Repository: GabrielOlvH/Industrial-Revolution Branch: master Commit: 012a1b83f39a Files: 1883 Total size: 1.9 MB Directory structure: gitextract_2rmcmr4g/ ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── bugs.md │ └── requests.md ├── .gitignore ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src/ └── main/ ├── java/ │ └── me/ │ └── steven/ │ └── indrev/ │ ├── AprilFools.java │ ├── FabricRecipeRemainder.java │ ├── IREnergyStorage.java │ ├── WCustomTabPanel.java │ └── mixin/ │ ├── aprilfools/ │ │ ├── AprilFoolsMixinConfigPlugin.java │ │ └── MixinTranslatableText.java │ ├── client/ │ │ ├── MixinBuiltChunk.java │ │ ├── MixinClientPlayerInteractionManager.java │ │ ├── MixinGameRenderer.java │ │ ├── MixinItemRenderer.java │ │ ├── MixinLivingEntityClient.java │ │ ├── MixinMinecraftClient.java │ │ └── MixinWItemSlot.java │ └── common/ │ ├── MixinAbstractCookingRecipe.java │ ├── MixinEnchantmentHelper.java │ ├── MixinEntity.java │ ├── MixinItemPredicate.java │ ├── MixinItemStack.java │ ├── MixinLivingEntity.java │ ├── MixinPiglinBrain.java │ ├── MixinPlayerEntity.java │ ├── MixinPlayerInventory.java │ ├── MixinServerPlayerEntity.java │ └── MixinServerWorld.java ├── kotlin/ │ └── me/ │ └── steven/ │ └── indrev/ │ ├── IndustrialRevolution.kt │ ├── IndustrialRevolutionClient.kt │ ├── api/ │ │ ├── AttributeModifierProvider.kt │ │ ├── CustomEnchantmentProvider.kt │ │ ├── IREntityExtension.kt │ │ ├── IRPlayerEntityExtension.kt │ │ ├── IRServerPlayerEntityExtension.kt │ │ ├── OreDataCards.kt │ │ ├── ServerWorldExtension.kt │ │ ├── machines/ │ │ │ ├── Tier.kt │ │ │ └── TransferMode.kt │ │ └── sideconfigs/ │ │ ├── Configurable.kt │ │ ├── ConfigurationType.kt │ │ └── SideConfiguration.kt │ ├── armor/ │ │ ├── IRArmorMaterial.kt │ │ ├── ModuleFeatureRenderer.kt │ │ └── ReinforcedElytraFeatureRenderer.kt │ ├── blockentities/ │ │ ├── BaseBlockEntity.kt │ │ ├── BaseMachineBlockEntity.kt │ │ ├── GlobalStateController.kt │ │ ├── MachineBlockEntity.kt │ │ ├── Syncable.kt │ │ ├── cables/ │ │ │ └── BasePipeBlockEntity.kt │ │ ├── crafters/ │ │ │ ├── CompressorBlockEntity.kt │ │ │ ├── CompressorFactoryBlockEntity.kt │ │ │ ├── CondenserBlockEntity.kt │ │ │ ├── CondenserBlockEntityRenderer.kt │ │ │ ├── CraftingMachineBlockEntity.kt │ │ │ ├── ElectricFurnaceBlockEntity.kt │ │ │ ├── ElectricFurnaceFactoryBlockEntity.kt │ │ │ ├── ElectrolyticSeparatorBlockEntity.kt │ │ │ ├── FluidInfuserBlockEntity.kt │ │ │ ├── FluidInfuserBlockEntityRenderer.kt │ │ │ ├── PulverizerBlockEntity.kt │ │ │ ├── PulverizerFactoryBlockEntity.kt │ │ │ ├── RecyclerBlockEntity.kt │ │ │ ├── SawmillBlockEntity.kt │ │ │ ├── SmelterBlockEntity.kt │ │ │ ├── SolidInfuserBlockEntity.kt │ │ │ └── SolidInfuserFactoryBlockEntity.kt │ │ ├── farms/ │ │ │ ├── AOEMachineBlockEntity.kt │ │ │ ├── AOEMachineBlockEntityRenderer.kt │ │ │ ├── BiomassComposterBlockEntity.kt │ │ │ ├── BiomassComposterBlockEntityRenderer.kt │ │ │ ├── ChopperBlockEntity.kt │ │ │ ├── ChopperBlockEntityRenderer.kt │ │ │ ├── DirtOxygenatorBlockEntity.kt │ │ │ ├── DrainBlockEntity.kt │ │ │ ├── FarmerBlockEntity.kt │ │ │ ├── FisherBlockEntity.kt │ │ │ ├── PumpBlockEntity.kt │ │ │ ├── PumpBlockEntityRenderer.kt │ │ │ ├── RancherBlockEntity.kt │ │ │ └── SlaughterBlockEntity.kt │ │ ├── generators/ │ │ │ ├── BiomassGeneratorBlockEntity.kt │ │ │ ├── CoalGeneratorBlockEntity.kt │ │ │ ├── GasBurningGeneratorBlockEntity.kt │ │ │ ├── GeneratorBlockEntity.kt │ │ │ ├── HeatGeneratorBlockEntity.kt │ │ │ ├── HeatGeneratorBlockEntityRenderer.kt │ │ │ ├── SolarGeneratorBlockEntity.kt │ │ │ ├── SolidFuelGeneratorBlockEntity.kt │ │ │ ├── SteamTurbineBlockEntity.kt │ │ │ └── SteamTurbineSteamInputValveBlockEntity.kt │ │ ├── laser/ │ │ │ ├── CapsuleBlockEntity.kt │ │ │ ├── CapsuleBlockEntityRenderer.kt │ │ │ ├── LaserBlockEntity.kt │ │ │ └── LaserBlockEntityRenderer.kt │ │ ├── miningrig/ │ │ │ ├── DataCardWriterBlockEntity.kt │ │ │ ├── DrillBlockEntity.kt │ │ │ ├── DrillBlockEntityRenderer.kt │ │ │ ├── MiningRigBlockEntity.kt │ │ │ └── MiningRigBlockEntityRenderer.kt │ │ ├── modularworkbench/ │ │ │ ├── ModularWorkbenchBlockEntity.kt │ │ │ └── ModularWorkbenchBlockEntityRenderer.kt │ │ ├── solarpowerplant/ │ │ │ ├── HeliostatBlockEntity.kt │ │ │ ├── HeliostatBlockEntityRenderer.kt │ │ │ ├── SolarPowerPlantTowerBlockEntity.kt │ │ │ └── SolarReceiverBlockEntity.kt │ │ └── storage/ │ │ ├── CabinetBlockEntity.kt │ │ ├── ChargePadBlockEntity.kt │ │ ├── ChargePadBlockEntityRenderer.kt │ │ ├── LazuliFluxContainerBlockEntity.kt │ │ ├── LazuliFluxContainerBlockEntityRenderer.kt │ │ ├── TankBlockEntity.kt │ │ └── TankBlockEntityRenderer.kt │ ├── blocks/ │ │ ├── HeliostatBlock.kt │ │ ├── machine/ │ │ │ ├── CapsuleBlock.kt │ │ │ ├── ChargePadBlock.kt │ │ │ ├── DirtOxygenatorBlock.kt │ │ │ ├── DrillBlock.kt │ │ │ ├── DrillHeadModel.kt │ │ │ ├── ElectrolyticSeparatorBlock.kt │ │ │ ├── FacingMachineBlock.kt │ │ │ ├── HorizontalFacingMachineBlock.kt │ │ │ ├── LaserBlock.kt │ │ │ ├── LazuliFluxContainerBlock.kt │ │ │ ├── MachineBlock.kt │ │ │ ├── MiningRigBlock.kt │ │ │ ├── PumpBlock.kt │ │ │ ├── pipes/ │ │ │ │ ├── BasePipeBlock.kt │ │ │ │ ├── CableBlock.kt │ │ │ │ ├── FluidPipeBlock.kt │ │ │ │ └── ItemPipeBlock.kt │ │ │ └── solarpowerplant/ │ │ │ ├── FluidValveBlock.kt │ │ │ ├── SolarPowerPlantFluidOutputBlock.kt │ │ │ ├── SolarPowerPlantTowerBlock.kt │ │ │ ├── SolarReceiverBlock.kt │ │ │ ├── SteamTurbineBlock.kt │ │ │ └── SteamTurbineSteamInputValveBlock.kt │ │ ├── misc/ │ │ │ ├── AcidFluidBlock.kt │ │ │ ├── BiomassComposterBlock.kt │ │ │ ├── CabinetBlock.kt │ │ │ ├── DuctBlock.kt │ │ │ ├── HorizontalFacingBlock.kt │ │ │ ├── NikoliteOreBlock.kt │ │ │ ├── PlankBlock.kt │ │ │ ├── SulfurCrystalBlock.kt │ │ │ ├── TankBlock.kt │ │ │ ├── VerticalFacingBlock.kt │ │ │ └── WarningStrobeBlock.kt │ │ └── models/ │ │ ├── LazuliFluxContainerBakedModel.kt │ │ ├── MachineBakedModel.kt │ │ ├── MinerBakedModel.kt │ │ ├── PumpPipeBakedModel.kt │ │ └── pipes/ │ │ ├── BasePipeModel.kt │ │ ├── CableModel.kt │ │ ├── FluidPipeModel.kt │ │ └── ItemPipeModel.kt │ ├── compat/ │ │ ├── dashloader/ │ │ │ └── models/ │ │ │ ├── DashCableModel.kt │ │ │ ├── DashFluidPipeModel.kt │ │ │ ├── DashItemPipeModel.kt │ │ │ ├── DashLazuliFluxContainerModel.kt │ │ │ ├── DashMachineModel.kt │ │ │ ├── DashMinerModel.kt │ │ │ └── DashTankModel.kt │ │ └── rei/ │ │ ├── REIPlugin.kt │ │ ├── categories/ │ │ │ ├── IRMachineRecipeCategory.kt │ │ │ ├── IRModuleCraftingRecipeCategory.kt │ │ │ └── IRSawmillRecipeCategory.kt │ │ └── plugins/ │ │ └── IRMachinePlugin.kt │ ├── components/ │ │ ├── CraftingComponent.kt │ │ ├── EnhancerComponent.kt │ │ ├── FluidComponent.kt │ │ ├── GuiSyncableComponent.kt │ │ ├── InventoryComponent.kt │ │ ├── TemperatureComponent.kt │ │ └── multiblock/ │ │ ├── BlockStateFilter.kt │ │ ├── MultiBlockComponent.kt │ │ ├── MultiblockBlockEntityRenderer.kt │ │ ├── MultiblockMatcher.kt │ │ ├── StructureDefinition.kt │ │ ├── StructureHolder.kt │ │ ├── StructureIdentifier.kt │ │ └── definitions/ │ │ ├── FactoryStructureDefinition.kt │ │ ├── SolarPowerPlantTowerStructureDefinition.kt │ │ └── SteamTurbineStructureDefinition.kt │ ├── config/ │ │ └── IRConfig.kt │ ├── datagen/ │ │ ├── DataFactory.kt │ │ ├── DataGenerator.kt │ │ ├── DataGeneratorManager.kt │ │ ├── ImageFactory.kt │ │ ├── IndustrialRevolutionDatagen.kt │ │ ├── JsonFactory.kt │ │ ├── generators/ │ │ │ ├── BlockModelGenerator.kt │ │ │ ├── ItemModelGenerator.kt │ │ │ ├── LootTableGenerator.kt │ │ │ ├── MaterialRecipeGenerator.kt │ │ │ ├── MaterialTagGenerator.kt │ │ │ └── MetalSpriteGenerator.kt │ │ └── utils/ │ │ ├── MaterialColors.kt │ │ ├── MetalModel.kt │ │ ├── MetalSpriteRegistry.kt │ │ └── SpriteColorHolder.kt │ ├── events/ │ │ ├── client/ │ │ │ ├── IRClientTickEvents.kt │ │ │ ├── IRHudRenderCallback.kt │ │ │ ├── IRLivingEntityFeatureRendererCallback.kt │ │ │ ├── IRTooltipComponentsCallback.kt │ │ │ ├── IRWorldRenderer.kt │ │ │ ├── MatterProjectorPreviewRenderer.kt │ │ │ └── MiningRigInfoTooltipCallback.kt │ │ └── common/ │ │ └── IRLootTableCallback.kt │ ├── fluids/ │ │ ├── BaseFluid.kt │ │ └── FluidType.kt │ ├── gui/ │ │ ├── IRInventoryScreen.kt │ │ ├── IRModularControllerScreen.kt │ │ ├── IRScreenHandlerFactory.kt │ │ ├── ScrewdriverScreenHandlerFactory.kt │ │ ├── guiutils.kt │ │ ├── properties/ │ │ │ └── SyncableProperty.kt │ │ ├── screenhandlers/ │ │ │ ├── IRGuiScreenHandler.kt │ │ │ ├── blockblacklister/ │ │ │ │ └── BlockBlacklisterScreenHandler.kt │ │ │ ├── machines/ │ │ │ │ ├── BiomassGeneratorScreenHandler.kt │ │ │ │ ├── ChopperScreenHandler.kt │ │ │ │ ├── CoalGeneratorScreenHandler.kt │ │ │ │ ├── CompressorFactoryScreenHandler.kt │ │ │ │ ├── CompressorScreenHandler.kt │ │ │ │ ├── CondenserScreenHandler.kt │ │ │ │ ├── DataCardWriterScreenHandler.kt │ │ │ │ ├── ElectricFurnaceFactoryScreenHandler.kt │ │ │ │ ├── ElectricFurnaceScreenHandler.kt │ │ │ │ ├── ElectrolyticSeparatorScreenHandler.kt │ │ │ │ ├── FarmerScreenHandler.kt │ │ │ │ ├── FisherScreenHandler.kt │ │ │ │ ├── FluidInfuserScreenHandler.kt │ │ │ │ ├── GasBurningGeneratorScreenHandler.kt │ │ │ │ ├── HeatGeneratorScreenHandler.kt │ │ │ │ ├── LaserEmitterScreenHandler.kt │ │ │ │ ├── LazuliFluxContainerScreenHandler.kt │ │ │ │ ├── MiningRigComputerScreenHandler.kt │ │ │ │ ├── MiningRigDrillScreenHandler.kt │ │ │ │ ├── ModularWorkbenchScreenHandler.kt │ │ │ │ ├── PulverizerFactoryScreenHandler.kt │ │ │ │ ├── PulverizerScreenHandler.kt │ │ │ │ ├── PumpScreenHandler.kt │ │ │ │ ├── RancherScreenHandler.kt │ │ │ │ ├── RecyclerScreenHandler.kt │ │ │ │ ├── SawmillScreenHandler.kt │ │ │ │ ├── SlaughterScreenHandler.kt │ │ │ │ ├── SmelterScreenHandler.kt │ │ │ │ ├── SolarGeneratorScreenHandler.kt │ │ │ │ ├── SolarPowerPlantTowerScreenHandler.kt │ │ │ │ ├── SolidInfuserFactoryScreenHandler.kt │ │ │ │ ├── SolidInfuserScreenHandler.kt │ │ │ │ └── SteamTurbineScreenHandler.kt │ │ │ ├── modular/ │ │ │ │ └── ModularItemConfigurationScreenHandler.kt │ │ │ ├── pipes/ │ │ │ │ ├── PipeFilterScreen.kt │ │ │ │ ├── PipeFilterScreenFactory.kt │ │ │ │ └── PipeFilterScreenHandler.kt │ │ │ ├── screenhandlers.kt │ │ │ ├── storage/ │ │ │ │ └── CabinetScreenHandler.kt │ │ │ └── wrench/ │ │ │ └── ScrewdriverScreenHandler.kt │ │ ├── tooltip/ │ │ │ ├── energy/ │ │ │ │ ├── EnergyTooltipComponent.kt │ │ │ │ └── EnergyTooltipData.kt │ │ │ ├── modular/ │ │ │ │ ├── ModularTooltipComponent.kt │ │ │ │ └── ModularTooltipData.kt │ │ │ └── oredatacards/ │ │ │ ├── OreDataCardTooltipComponent.kt │ │ │ └── OreDataCardTooltipData.kt │ │ └── widgets/ │ │ ├── machines/ │ │ │ ├── WCustomBar.kt │ │ │ └── WMachineSideDisplay.kt │ │ └── misc/ │ │ ├── WBookEntryShortcut.kt │ │ ├── WCircleProgressBar.kt │ │ ├── WDynamicSprite.kt │ │ ├── WKnob.kt │ │ ├── WPlayerRender.kt │ │ ├── WStaticTooltip.kt │ │ ├── WText.kt │ │ └── WTooltipedItemSlot.kt │ ├── inventories/ │ │ ├── IRInventory.kt │ │ └── IRInventoryDSL.kt │ ├── items/ │ │ ├── armor/ │ │ │ ├── IRColorModuleItem.kt │ │ │ ├── IRModularArmorItem.kt │ │ │ ├── IRModuleItem.kt │ │ │ ├── JetpackHandler.kt │ │ │ ├── JetpackItem.kt │ │ │ └── ReinforcedElytraItem.kt │ │ ├── energy/ │ │ │ ├── IRBatteryItem.kt │ │ │ ├── IREnergyItem.kt │ │ │ ├── IRGamerAxeItem.kt │ │ │ ├── IRMiningDrillItem.kt │ │ │ ├── IRModularDrillItem.kt │ │ │ ├── IRPortableChargerItem.kt │ │ │ └── MachineBlockItem.kt │ │ ├── misc/ │ │ │ ├── IRCraftingToolItem.kt │ │ │ ├── IREnergyReaderItem.kt │ │ │ ├── IRGuideBookItem.kt │ │ │ ├── IRMachineUpgradeItem.kt │ │ │ ├── IRServoItem.kt │ │ │ ├── OreDataCardItem.kt │ │ │ └── Tools.kt │ │ ├── models/ │ │ │ └── TankItemBakedModel.kt │ │ └── upgrade/ │ │ ├── Enhancer.kt │ │ └── IREnhancerItem.kt │ ├── networks/ │ │ ├── EndpointData.kt │ │ ├── Network.kt │ │ ├── NetworkEvents.kt │ │ ├── NetworkState.kt │ │ ├── Node.kt │ │ ├── ServoNetworkState.kt │ │ ├── client/ │ │ │ ├── ClientNetworkInfo.kt │ │ │ ├── ClientNetworkState.kt │ │ │ ├── ClientServoNetworkInfo.kt │ │ │ └── node/ │ │ │ ├── ClientNodeInfo.kt │ │ │ └── ClientServoNodeInfo.kt │ │ ├── energy/ │ │ │ ├── CableEnergyIo.kt │ │ │ ├── EnergyNetwork.kt │ │ │ └── EnergyNetworkState.kt │ │ ├── factory/ │ │ │ ├── NetworkFactory.kt │ │ │ └── factories.kt │ │ ├── fluid/ │ │ │ ├── FluidNetwork.kt │ │ │ └── FluidNetworkState.kt │ │ └── item/ │ │ ├── ItemFilterData.kt │ │ ├── ItemNetwork.kt │ │ └── ItemNetworkState.kt │ ├── packets/ │ │ ├── PacketRegistry.kt │ │ ├── client/ │ │ │ ├── ClientItemPipePackets.kt │ │ │ ├── GuiPropertySyncPacket.kt │ │ │ ├── MachineStateUpdatePacket.kt │ │ │ ├── MiningRigSpawnBlockParticlesPacket.kt │ │ │ ├── SyncAppliedModulesPacket.kt │ │ │ ├── SyncConfigPacket.kt │ │ │ └── SyncNetworkServosPacket.kt │ │ └── common/ │ │ ├── ConfigureIOPackets.kt │ │ ├── DataCardWriteStartPacket.kt │ │ ├── FluidGuiHandInteractionPacket.kt │ │ ├── ItemPipePackets.kt │ │ ├── SelectModuleOnWorkbenchPacket.kt │ │ ├── ToggleFactoryStackSplittingPacket.kt │ │ ├── ToggleGamerAxePacket.kt │ │ ├── UpdateAOEMachineRangePacket.kt │ │ ├── UpdateKnobValue.kt │ │ ├── UpdateMiningDrillBlockBlacklistPacket.kt │ │ ├── UpdateModularToolLevelPacket.kt │ │ └── UpdateRancherConfigPacket.kt │ ├── recipes/ │ │ ├── IRecipeGetter.kt │ │ ├── SelfRemainderRecipe.kt │ │ └── machines/ │ │ ├── CompressorRecipe.kt │ │ ├── CondenserRecipe.kt │ │ ├── DistillerRecipe.kt │ │ ├── ElectrolysisRecipe.kt │ │ ├── FluidInfuserRecipe.kt │ │ ├── IRFluidRecipe.kt │ │ ├── IRRecipe.kt │ │ ├── IRRecipeType.kt │ │ ├── InfuserRecipe.kt │ │ ├── LaserRecipe.kt │ │ ├── ModuleRecipe.kt │ │ ├── PulverizerRecipe.kt │ │ ├── RecyclerRecipe.kt │ │ ├── SawmillRecipe.kt │ │ ├── SmelterRecipe.kt │ │ ├── VanillaCookingRecipeCachedGetter.kt │ │ └── entries/ │ │ ├── InputEntry.kt │ │ └── OutputEntry.kt │ ├── registry/ │ │ ├── IRBlockRegistry.kt │ │ ├── IRFluidFuelRegistry.kt │ │ ├── IRFluidRegistry.kt │ │ ├── IRItemRegistry.kt │ │ ├── IRModelManagers.kt │ │ ├── MachineRegistry.kt │ │ ├── MaterialHelper.kt │ │ └── WorldGeneration.kt │ ├── tools/ │ │ ├── IRToolMaterial.kt │ │ └── modular/ │ │ ├── ArmorModule.kt │ │ ├── DrillModule.kt │ │ ├── GamerAxeModule.kt │ │ ├── IRModularItem.kt │ │ ├── MiningToolModule.kt │ │ └── Module.kt │ ├── utils/ │ │ ├── EmptyModel.kt │ │ ├── EnergyDamageHandler.kt │ │ ├── IRFluidTank.kt │ │ ├── ReusableArrayDeque.kt │ │ ├── accessorextensions.kt │ │ ├── clientutils.kt │ │ ├── energyutils.kt │ │ ├── fluidutils.kt │ │ ├── helperextensions.kt │ │ ├── hiddenitems.kt │ │ ├── interactions.kt │ │ ├── itemutils.kt │ │ ├── machineinteractions.kt │ │ └── utils.kt │ └── world/ │ └── features/ │ ├── IRConfiguredFeature.kt │ └── SulfurCrystalFeature.kt └── resources/ ├── assets/ │ └── indrev/ │ ├── blockstates/ │ │ ├── biomass_composter.json │ │ ├── block_base.json │ │ ├── block_highlight.json │ │ ├── block_shadow.json │ │ ├── bronze_block.json │ │ ├── cabinet.json │ │ ├── capsule.json │ │ ├── charge_pad_mk4.json │ │ ├── composting.json │ │ ├── controller.json │ │ ├── coolant.json │ │ ├── deepslate_lead_ore.json │ │ ├── deepslate_nikolite_ore.json │ │ ├── deepslate_silver_ore.json │ │ ├── deepslate_tin_ore.json │ │ ├── deepslate_tungsten_ore.json │ │ ├── drain_mk1.json │ │ ├── drill_bottom.json │ │ ├── drill_middle.json │ │ ├── drill_top.json │ │ ├── duct.json │ │ ├── electrum_block.json │ │ ├── fisher_mk2.json │ │ ├── fisher_mk3.json │ │ ├── fisher_mk4.json │ │ ├── frame.json │ │ ├── heat_generator_mk4.json │ │ ├── hydrogen.json │ │ ├── intake.json │ │ ├── laser_emitter_mk4.json │ │ ├── lead_block.json │ │ ├── lead_ore.json │ │ ├── machine_block.json │ │ ├── methane.json │ │ ├── modular_workbench_mk4.json │ │ ├── molten_copper.json │ │ ├── molten_gold.json │ │ ├── molten_iron.json │ │ ├── molten_lead.json │ │ ├── molten_netherite.json │ │ ├── molten_silver.json │ │ ├── molten_tin.json │ │ ├── nikolite_ore.json │ │ ├── ore_base.json │ │ ├── ore_highlight.json │ │ ├── oxygen.json │ │ ├── plank_block.json │ │ ├── planks.json │ │ ├── pump_mk1.json │ │ ├── raw_lead_block.json │ │ ├── raw_silver_block.json │ │ ├── raw_tin_block.json │ │ ├── raw_tungsten_block.json │ │ ├── silo.json │ │ ├── silver_block.json │ │ ├── silver_ore.json │ │ ├── steel_block.json │ │ ├── sulfur_crystal.json │ │ ├── sulfuric_acid.json │ │ ├── tank.json │ │ ├── tin_block.json │ │ ├── tin_ore.json │ │ ├── toxic_mud.json │ │ ├── tungsten_block.json │ │ ├── tungsten_ore.json │ │ ├── warning_strobe.json │ │ └── wither_proof_obsidian.json │ ├── lang/ │ │ ├── en_us.json │ │ ├── pt_br.json │ │ ├── ru_ru.json │ │ ├── tr_tr.json │ │ └── zh_cn.json │ ├── models/ │ │ ├── block/ │ │ │ ├── block_base.json │ │ │ ├── block_highlight.json │ │ │ ├── block_shadow.json │ │ │ ├── bronze_block.json │ │ │ ├── cabinet.json │ │ │ ├── cable_center_mk1.json │ │ │ ├── cable_center_mk2.json │ │ │ ├── cable_center_mk3.json │ │ │ ├── cable_center_mk4.json │ │ │ ├── cable_side_mk1.json │ │ │ ├── cable_side_mk2.json │ │ │ ├── cable_side_mk3.json │ │ │ ├── cable_side_mk4.json │ │ │ ├── capsule.json │ │ │ ├── charge_pad_mk4.json │ │ │ ├── controller.json │ │ │ ├── deepslate_lead_ore.json │ │ │ ├── deepslate_nikolite_ore.json │ │ │ ├── deepslate_silver_ore.json │ │ │ ├── deepslate_tin_ore.json │ │ │ ├── deepslate_tungsten_ore.json │ │ │ ├── diamond_drill_head.json │ │ │ ├── drain_mk1.json │ │ │ ├── drill.json │ │ │ ├── drill_bottom.json │ │ │ ├── drill_middle.json │ │ │ ├── drill_top.json │ │ │ ├── drill_top_on.json │ │ │ ├── duct.json │ │ │ ├── electrum_block.json │ │ │ ├── fishing_farm_mk2.json │ │ │ ├── fishing_farm_mk3.json │ │ │ ├── fishing_farm_mk4.json │ │ │ ├── fluid_pipe_center_mk1.json │ │ │ ├── fluid_pipe_center_mk2.json │ │ │ ├── fluid_pipe_center_mk3.json │ │ │ ├── fluid_pipe_center_mk4.json │ │ │ ├── fluid_pipe_side_mk1.json │ │ │ ├── fluid_pipe_side_mk2.json │ │ │ ├── fluid_pipe_side_mk3.json │ │ │ ├── fluid_pipe_side_mk4.json │ │ │ ├── frame.json │ │ │ ├── gray_lava.json │ │ │ ├── heat_generator_mk4.json │ │ │ ├── intake.json │ │ │ ├── iron_drill_head.json │ │ │ ├── item_pipe_center_mk1.json │ │ │ ├── item_pipe_center_mk2.json │ │ │ ├── item_pipe_center_mk3.json │ │ │ ├── item_pipe_center_mk4.json │ │ │ ├── item_pipe_side_mk1.json │ │ │ ├── item_pipe_side_mk2.json │ │ │ ├── item_pipe_side_mk3.json │ │ │ ├── item_pipe_side_mk4.json │ │ │ ├── laser.json │ │ │ ├── laser_on.json │ │ │ ├── lazuli_flux_container.json │ │ │ ├── lazuli_flux_container_input.json │ │ │ ├── lazuli_flux_container_item_lf_level.json │ │ │ ├── lazuli_flux_container_mk1_overlay.json │ │ │ ├── lazuli_flux_container_mk2_overlay.json │ │ │ ├── lazuli_flux_container_mk3_overlay.json │ │ │ ├── lazuli_flux_container_mk4_overlay.json │ │ │ ├── lazuli_flux_container_output.json │ │ │ ├── lead_block.json │ │ │ ├── lead_ore.json │ │ │ ├── machine.json │ │ │ ├── machine_block.json │ │ │ ├── modular_workbench_mk4.json │ │ │ ├── netherite_drill_head.json │ │ │ ├── nikolite_ore.json │ │ │ ├── ore_base.json │ │ │ ├── ore_highlight.json │ │ │ ├── plank_block.json │ │ │ ├── planks_height10.json │ │ │ ├── planks_height12.json │ │ │ ├── planks_height14.json │ │ │ ├── planks_height2.json │ │ │ ├── planks_height4.json │ │ │ ├── planks_height6.json │ │ │ ├── planks_height8.json │ │ │ ├── pump_mk1.json │ │ │ ├── pump_pipe.json │ │ │ ├── raw_lead_block.json │ │ │ ├── raw_silver_block.json │ │ │ ├── raw_tin_block.json │ │ │ ├── raw_tungsten_block.json │ │ │ ├── servo_output.json │ │ │ ├── servo_output_item.json │ │ │ ├── servo_retriever.json │ │ │ ├── servo_retriever_item.json │ │ │ ├── silo.json │ │ │ ├── silver_block.json │ │ │ ├── silver_ore.json │ │ │ ├── smelter_mk4.json │ │ │ ├── solar_generator_mk1.json │ │ │ ├── solar_generator_mk1_on.json │ │ │ ├── solar_generator_mk3.json │ │ │ ├── solar_generator_mk3_on.json │ │ │ ├── steel_block.json │ │ │ ├── stone_drill_head.json │ │ │ ├── sulfur_crystal.json │ │ │ ├── tank.json │ │ │ ├── tank_both.json │ │ │ ├── tank_down.json │ │ │ ├── tank_up.json │ │ │ ├── tin_block.json │ │ │ ├── tin_ore.json │ │ │ ├── tungsten_block.json │ │ │ ├── tungsten_ore.json │ │ │ ├── warning_strobe.json │ │ │ └── wither_proof_obsidian.json │ │ └── item/ │ │ ├── axe_base.json │ │ ├── axe_highlight.json │ │ ├── axe_outline.json │ │ ├── battery.json │ │ ├── biomass.json │ │ ├── blast_furnace_enhancer.json │ │ ├── block_base.json │ │ ├── block_highlight.json │ │ ├── boots_base.json │ │ ├── boots_highlight.json │ │ ├── boots_outline.json │ │ ├── broken_reinforced_elytra.json │ │ ├── bronze_axe.json │ │ ├── bronze_block.json │ │ ├── bronze_boots.json │ │ ├── bronze_chestplate.json │ │ ├── bronze_dust.json │ │ ├── bronze_helmet.json │ │ ├── bronze_hoe.json │ │ ├── bronze_ingot.json │ │ ├── bronze_leggings.json │ │ ├── bronze_nugget.json │ │ ├── bronze_pickaxe.json │ │ ├── bronze_plate.json │ │ ├── bronze_shovel.json │ │ ├── bronze_sword.json │ │ ├── buffer_enhancer.json │ │ ├── cabinet.json │ │ ├── cable_mk1.json │ │ ├── cable_mk2.json │ │ ├── cable_mk3.json │ │ ├── cable_mk4.json │ │ ├── capsule.json │ │ ├── charge_pad_mk4.json │ │ ├── chestplate_base.json │ │ ├── chestplate_highlight.json │ │ ├── chestplate_outline.json │ │ ├── chunk_base.json │ │ ├── chunk_highlight.json │ │ ├── chunk_outline.json │ │ ├── chunk_scanner.json │ │ ├── circuit_mk1.json │ │ ├── circuit_mk2.json │ │ ├── circuit_mk3.json │ │ ├── circuit_mk4.json │ │ ├── coal_dust.json │ │ ├── controller.json │ │ ├── coolant_bucket.json │ │ ├── cooler_cell.json │ │ ├── copper_axe.json │ │ ├── copper_block.json │ │ ├── copper_boots.json │ │ ├── copper_chestplate.json │ │ ├── copper_chunk.json │ │ ├── copper_dust.json │ │ ├── copper_gear.json │ │ ├── copper_helmet.json │ │ ├── copper_hoe.json │ │ ├── copper_leggings.json │ │ ├── copper_nether_ore.json │ │ ├── copper_nugget.json │ │ ├── copper_ore.json │ │ ├── copper_pickaxe.json │ │ ├── copper_plate.json │ │ ├── copper_purified_ore.json │ │ ├── copper_shovel.json │ │ ├── copper_sword.json │ │ ├── damage_enhancer.json │ │ ├── deepslate_lead_ore.json │ │ ├── deepslate_nikolite_ore.json │ │ ├── deepslate_silver_ore.json │ │ ├── deepslate_tin_ore.json │ │ ├── deepslate_tungsten_ore.json │ │ ├── diamond_drill_head.json │ │ ├── diamond_dust.json │ │ ├── drain_mk1.json │ │ ├── drill_bottom.json │ │ ├── duct.json │ │ ├── dust_base.json │ │ ├── dust_highlight.json │ │ ├── dust_outline.json │ │ ├── electrum_block.json │ │ ├── electrum_dust.json │ │ ├── electrum_ingot.json │ │ ├── electrum_nugget.json │ │ ├── electrum_plate.json │ │ ├── empty_enhancer.json │ │ ├── energy_enhancer.json │ │ ├── energy_reader.json │ │ ├── enriched_nikolite_dust.json │ │ ├── enriched_nikolite_ingot.json │ │ ├── fan.json │ │ ├── fisher_mk2.json │ │ ├── fisher_mk3.json │ │ ├── fisher_mk4.json │ │ ├── fluid_pipe_mk1.json │ │ ├── fluid_pipe_mk2.json │ │ ├── fluid_pipe_mk3.json │ │ ├── fluid_pipe_mk4.json │ │ ├── frame.json │ │ ├── gamer_axe.json │ │ ├── gamer_axe1.json │ │ ├── gamer_axe10.json │ │ ├── gamer_axe2.json │ │ ├── gamer_axe3.json │ │ ├── gamer_axe4.json │ │ ├── gamer_axe5.json │ │ ├── gamer_axe6.json │ │ ├── gamer_axe7.json │ │ ├── gamer_axe8.json │ │ ├── gamer_axe9.json │ │ ├── gold_chunk.json │ │ ├── gold_dust.json │ │ ├── gold_plate.json │ │ ├── gold_purified_ore.json │ │ ├── guide_book.json │ │ ├── hammer.json │ │ ├── heat_coil.json │ │ ├── heat_generator_mk4.json │ │ ├── heatsink.json │ │ ├── helmet_base.json │ │ ├── helmet_highlight.json │ │ ├── helmet_outline.json │ │ ├── hoe_base.json │ │ ├── hoe_highlight.json │ │ ├── hoe_outline.json │ │ ├── ingot_base.json │ │ ├── ingot_highlight.json │ │ ├── ingot_outline.json │ │ ├── intake.json │ │ ├── iron_chunk.json │ │ ├── iron_drill_head.json │ │ ├── iron_dust.json │ │ ├── iron_gear.json │ │ ├── iron_plate.json │ │ ├── iron_purified_ore.json │ │ ├── jetpack_mk1.json │ │ ├── jetpack_mk2.json │ │ ├── jetpack_mk3.json │ │ ├── jetpack_mk4.json │ │ ├── laser_emitter_mk4.json │ │ ├── lazuli_flux_container_creative.json │ │ ├── lazuli_flux_container_mk1.json │ │ ├── lazuli_flux_container_mk2.json │ │ ├── lazuli_flux_container_mk3.json │ │ ├── lazuli_flux_container_mk4.json │ │ ├── lead_axe.json │ │ ├── lead_block.json │ │ ├── lead_boots.json │ │ ├── lead_chestplate.json │ │ ├── lead_chunk.json │ │ ├── lead_dust.json │ │ ├── lead_helmet.json │ │ ├── lead_hoe.json │ │ ├── lead_ingot.json │ │ ├── lead_leggings.json │ │ ├── lead_nugget.json │ │ ├── lead_ore.json │ │ ├── lead_pickaxe.json │ │ ├── lead_plate.json │ │ ├── lead_purified_ore.json │ │ ├── lead_shovel.json │ │ ├── lead_sword.json │ │ ├── leggings_base.json │ │ ├── leggings_highlight.json │ │ ├── leggings_outline.json │ │ ├── machine_block.json │ │ ├── miner_mk4.json │ │ ├── mining_drill_mk1.json │ │ ├── mining_drill_mk2.json │ │ ├── mining_drill_mk3.json │ │ ├── mining_drill_mk4.json │ │ ├── modular_armor_boots.json │ │ ├── modular_armor_chest.json │ │ ├── modular_armor_helmet.json │ │ ├── modular_armor_legs.json │ │ ├── modular_core.json │ │ ├── modular_core_activated.json │ │ ├── modular_workbench_mk4.json │ │ ├── module_auto_feeder.json │ │ ├── module_breathing.json │ │ ├── module_charger.json │ │ ├── module_color_black.json │ │ ├── module_color_blue.json │ │ ├── module_color_brown.json │ │ ├── module_color_cyan.json │ │ ├── module_color_green.json │ │ ├── module_color_orange.json │ │ ├── module_color_pink.json │ │ ├── module_color_purple.json │ │ ├── module_color_red.json │ │ ├── module_color_yellow.json │ │ ├── module_efficiency.json │ │ ├── module_elytra.json │ │ ├── module_feather_falling.json │ │ ├── module_fire_aspect.json │ │ ├── module_fire_resistance.json │ │ ├── module_fortune.json │ │ ├── module_jetpack.json │ │ ├── module_jump_boost.json │ │ ├── module_looting.json │ │ ├── module_magnet.json │ │ ├── module_night_vision.json │ │ ├── module_piglin_tricker.json │ │ ├── module_protection.json │ │ ├── module_range.json │ │ ├── module_reach.json │ │ ├── module_sharpness.json │ │ ├── module_silk_touch.json │ │ ├── module_solar_panel.json │ │ ├── module_speed.json │ │ ├── module_water_affinity.json │ │ ├── molten_bucket_base.json │ │ ├── molten_bucket_highlight.json │ │ ├── molten_bucket_outline.json │ │ ├── molten_copper_bucket.json │ │ ├── molten_gold_bucket.json │ │ ├── molten_iron_bucket.json │ │ ├── molten_lead_bucket.json │ │ ├── molten_netherite_bucket.json │ │ ├── molten_silver_bucket.json │ │ ├── molten_tin_bucket.json │ │ ├── netherite_drill_head.json │ │ ├── netherite_scrap_chunk.json │ │ ├── netherite_scrap_dust.json │ │ ├── netherite_scrap_purified_ore.json │ │ ├── nikolite_dust.json │ │ ├── nikolite_ingot.json │ │ ├── nikolite_nether_ore.json │ │ ├── nikolite_ore.json │ │ ├── nugget_base.json │ │ ├── nugget_highlight.json │ │ ├── nugget_outline.json │ │ ├── ore_base.json │ │ ├── ore_data_card.json │ │ ├── ore_data_card_not_empty.json │ │ ├── ore_highlight.json │ │ ├── pickaxe_base.json │ │ ├── pickaxe_highlight.json │ │ ├── pickaxe_outline.json │ │ ├── plank_block.json │ │ ├── planks.json │ │ ├── plate_base.json │ │ ├── plate_highlight.json │ │ ├── plate_outline.json │ │ ├── portable_charger.json │ │ ├── pump_mk1.json │ │ ├── purified_ore_base.json │ │ ├── purified_ore_highlight.json │ │ ├── purified_ore_outline.json │ │ ├── raw_lead.json │ │ ├── raw_lead_block.json │ │ ├── raw_silver.json │ │ ├── raw_silver_block.json │ │ ├── raw_tin.json │ │ ├── raw_tin_block.json │ │ ├── raw_tungsten.json │ │ ├── raw_tungsten_block.json │ │ ├── reinforced_elytra.json │ │ ├── sawdust.json │ │ ├── scan_output.json │ │ ├── screwdriver.json │ │ ├── servo_output.json │ │ ├── servo_retriever.json │ │ ├── shovel_base.json │ │ ├── shovel_highlight.json │ │ ├── shovel_outline.json │ │ ├── silo.json │ │ ├── silver_axe.json │ │ ├── silver_block.json │ │ ├── silver_boots.json │ │ ├── silver_chestplate.json │ │ ├── silver_chunk.json │ │ ├── silver_dust.json │ │ ├── silver_helmet.json │ │ ├── silver_hoe.json │ │ ├── silver_ingot.json │ │ ├── silver_leggings.json │ │ ├── silver_nugget.json │ │ ├── silver_ore.json │ │ ├── silver_pickaxe.json │ │ ├── silver_plate.json │ │ ├── silver_purified_ore.json │ │ ├── silver_shovel.json │ │ ├── silver_sword.json │ │ ├── smelter_mk4.json │ │ ├── smoker_enhancer.json │ │ ├── soot.json │ │ ├── speed_enhancer.json │ │ ├── steel_axe.json │ │ ├── steel_block.json │ │ ├── steel_boots.json │ │ ├── steel_chestplate.json │ │ ├── steel_dust.json │ │ ├── steel_gear.json │ │ ├── steel_helmet.json │ │ ├── steel_hoe.json │ │ ├── steel_ingot.json │ │ ├── steel_leggings.json │ │ ├── steel_nugget.json │ │ ├── steel_pickaxe.json │ │ ├── steel_plate.json │ │ ├── steel_shovel.json │ │ ├── steel_sword.json │ │ ├── stone_drill_head.json │ │ ├── sulfur_crystal.json │ │ ├── sulfur_dust.json │ │ ├── sulfuric_acid_bucket.json │ │ ├── sword_base.json │ │ ├── sword_highlight.json │ │ ├── sword_outline.json │ │ ├── tank.json │ │ ├── tech_soup.json │ │ ├── tier_upgrade_mk2.json │ │ ├── tier_upgrade_mk3.json │ │ ├── tier_upgrade_mk4.json │ │ ├── tin_axe.json │ │ ├── tin_block.json │ │ ├── tin_boots.json │ │ ├── tin_chestplate.json │ │ ├── tin_chunk.json │ │ ├── tin_dust.json │ │ ├── tin_gear.json │ │ ├── tin_helmet.json │ │ ├── tin_hoe.json │ │ ├── tin_ingot.json │ │ ├── tin_leggings.json │ │ ├── tin_nugget.json │ │ ├── tin_ore.json │ │ ├── tin_pickaxe.json │ │ ├── tin_plate.json │ │ ├── tin_purified_ore.json │ │ ├── tin_shovel.json │ │ ├── tin_sword.json │ │ ├── tool_stick.json │ │ ├── toxic_mud_bucket.json │ │ ├── tungsten_block.json │ │ ├── tungsten_chunk.json │ │ ├── tungsten_dust.json │ │ ├── tungsten_ingot.json │ │ ├── tungsten_nugget.json │ │ ├── tungsten_ore.json │ │ ├── tungsten_plate.json │ │ ├── tungsten_purified_ore.json │ │ ├── untanned_leather.json │ │ ├── warning_strobe.json │ │ ├── wither_proof_obsidian.json │ │ └── wrench.json │ ├── particles/ │ │ └── laser_particle.json │ ├── sounds/ │ │ └── laser.ogg │ ├── sounds.json │ └── textures/ │ ├── block/ │ │ ├── biomass_generator_on.png.mcmeta │ │ ├── cable_center_emissive_mk1.png.mcmeta │ │ ├── cable_center_emissive_mk2.png.mcmeta │ │ ├── cable_center_emissive_mk3.png.mcmeta │ │ ├── cable_center_emissive_mk4.png.mcmeta │ │ ├── cable_wire_emissive_mk1.png.mcmeta │ │ ├── cable_wire_emissive_mk2.png.mcmeta │ │ ├── cable_wire_emissive_mk3.png.mcmeta │ │ ├── cable_wire_emissive_mk4.png.mcmeta │ │ ├── chopper_on.png.mcmeta │ │ ├── coal_generator_on.png.mcmeta │ │ ├── compressor_on.png.mcmeta │ │ ├── condenser_on.png.mcmeta │ │ ├── data_card_writer_on.png.mcmeta │ │ ├── drill_top_emissive_on.png.mcmeta │ │ ├── drill_top_tracer_emissive_on.png.mcmeta │ │ ├── electric_furnace_emissive_on.png.mcmeta │ │ ├── fluid_infuser_on.png.mcmeta │ │ ├── gray_lava_flow.png.mcmeta │ │ ├── gray_lava_still.png.mcmeta │ │ ├── mining_rig_screen_emissive.png.mcmeta │ │ ├── pulverizer_on.png.mcmeta │ │ ├── rancher_on.png.mcmeta │ │ ├── recycler_on.png.mcmeta │ │ ├── sawmill_on.png.mcmeta │ │ └── solid_infuser_emissive_on.png.mcmeta │ ├── entity/ │ │ └── laser.png.mcmeta │ ├── gui/ │ │ ├── hud_damaged.png.mcmeta │ │ ├── hud_regenerating.png.mcmeta │ │ └── hud_warning.png.mcmeta │ └── item/ │ ├── enriched_nikolite_dust.png.mcmeta │ ├── enriched_nikolite_ingot.png.mcmeta │ └── gamer_axe.png.mcmeta ├── data/ │ ├── c/ │ │ └── tags/ │ │ ├── blocks/ │ │ │ ├── bronze_blocks.json │ │ │ ├── copper_blocks.json │ │ │ ├── electrum_blocks.json │ │ │ ├── lead_blocks.json │ │ │ ├── lead_ores.json │ │ │ ├── silver_blocks.json │ │ │ ├── silver_ores.json │ │ │ ├── tin_blocks.json │ │ │ ├── tin_ores.json │ │ │ ├── tungsten_blocks.json │ │ │ └── tungsten_ores.json │ │ └── items/ │ │ ├── ancient_debris_ores.json │ │ ├── bronze_blocks.json │ │ ├── bronze_dusts.json │ │ ├── bronze_ingots.json │ │ ├── bronze_nuggets.json │ │ ├── bronze_plates.json │ │ ├── coal_dusts.json │ │ ├── coal_ores.json │ │ ├── copper_blocks.json │ │ ├── copper_dusts.json │ │ ├── copper_ingots.json │ │ ├── copper_nuggets.json │ │ ├── copper_ores.json │ │ ├── copper_plates.json │ │ ├── diamond_dusts.json │ │ ├── diamond_ores.json │ │ ├── electrum_blocks.json │ │ ├── electrum_dusts.json │ │ ├── electrum_ingots.json │ │ ├── electrum_nuggets.json │ │ ├── electrum_plates.json │ │ ├── emerald_ores.json │ │ ├── gold_dusts.json │ │ ├── gold_ingots.json │ │ ├── gold_ores.json │ │ ├── gold_plates.json │ │ ├── iron_dusts.json │ │ ├── iron_ingots.json │ │ ├── iron_ores.json │ │ ├── iron_plates.json │ │ ├── lead_blocks.json │ │ ├── lead_dusts.json │ │ ├── lead_ingots.json │ │ ├── lead_nuggets.json │ │ ├── lead_ores.json │ │ ├── lead_plates.json │ │ ├── netherite_scrap_dusts.json │ │ ├── nikolite_ores.json │ │ ├── raw_copper_ores.json │ │ ├── raw_gold_ores.json │ │ ├── raw_iron_ores.json │ │ ├── raw_lead_ores.json │ │ ├── raw_silver_ores.json │ │ ├── raw_tin_ores.json │ │ ├── raw_tungsten_ores.json │ │ ├── redstone_ores.json │ │ ├── screwdrivers.json │ │ ├── silver_blocks.json │ │ ├── silver_dusts.json │ │ ├── silver_ingots.json │ │ ├── silver_nuggets.json │ │ ├── silver_ores.json │ │ ├── silver_plates.json │ │ ├── steel_blocks.json │ │ ├── steel_boots.json │ │ ├── steel_chestplates.json │ │ ├── steel_dusts.json │ │ ├── steel_helmets.json │ │ ├── steel_ingots.json │ │ ├── steel_leggings.json │ │ ├── steel_nuggets.json │ │ ├── steel_plates.json │ │ ├── sulfur_dusts.json │ │ ├── sulfurs.json │ │ ├── tin_blocks.json │ │ ├── tin_dusts.json │ │ ├── tin_ingots.json │ │ ├── tin_nuggets.json │ │ ├── tin_ores.json │ │ ├── tin_plates.json │ │ ├── tungsten_blocks.json │ │ ├── tungsten_dusts.json │ │ ├── tungsten_ingots.json │ │ ├── tungsten_nuggets.json │ │ ├── tungsten_ores.json │ │ ├── tungsten_plates.json │ │ └── wrenches.json │ ├── fabric/ │ │ └── tags/ │ │ └── items/ │ │ ├── axes.json │ │ ├── hoes.json │ │ ├── pickaxes.json │ │ ├── shovels.json │ │ └── swords.json │ ├── indrev/ │ │ ├── advancements/ │ │ │ ├── advanced_solar_generator.json │ │ │ ├── basic_solar_generator.json │ │ │ ├── biomass.json │ │ │ ├── biomass_generator.json │ │ │ ├── blast_furnace_enhancer.json │ │ │ ├── buffer_enhancer.json │ │ │ ├── cable_mk1.json │ │ │ ├── cable_mk2.json │ │ │ ├── cable_mk3.json │ │ │ ├── cable_mk4.json │ │ │ ├── chopper_mk4.json │ │ │ ├── circuit_mk1.json │ │ │ ├── circuit_mk2.json │ │ │ ├── circuit_mk3.json │ │ │ ├── circuit_mk4.json │ │ │ ├── coal_generator.json │ │ │ ├── compressor.json │ │ │ ├── condenser.json │ │ │ ├── electric_furnace.json │ │ │ ├── empty_enhancer.json │ │ │ ├── enriched_nikolite_dust.json │ │ │ ├── enriched_nikolite_ingot.json │ │ │ ├── farmer_mk1.json │ │ │ ├── gamer_axe.json │ │ │ ├── heat_generator.json │ │ │ ├── industrial_smelter.json │ │ │ ├── lazuli_flux_container_mk1.json │ │ │ ├── lazuli_flux_container_mk2.json │ │ │ ├── lazuli_flux_container_mk3.json │ │ │ ├── lazuli_flux_container_mk4.json │ │ │ ├── machine_block.json │ │ │ ├── mk2_upgrade.json │ │ │ ├── mk3_upgrade.json │ │ │ ├── mk4_upgrade.json │ │ │ ├── modular_armor.json │ │ │ ├── modular_workbench.json │ │ │ ├── nikolite.json │ │ │ ├── nikolite_ingot.json │ │ │ ├── pulverizer.json │ │ │ ├── rancher_mk4.json │ │ │ ├── recycler.json │ │ │ ├── slaughter_mk1.json │ │ │ ├── smoker_enhancer.json │ │ │ ├── solid_infuser.json │ │ │ └── speed_enhancer.json │ │ ├── loot_tables/ │ │ │ └── blocks/ │ │ │ ├── biomass_generator_mk3.json │ │ │ ├── bronze_block.json │ │ │ ├── cabinet.json │ │ │ ├── cable_mk1.json │ │ │ ├── cable_mk2.json │ │ │ ├── cable_mk3.json │ │ │ ├── cable_mk4.json │ │ │ ├── capsule.json │ │ │ ├── charge_pad_mk4.json │ │ │ ├── chopper_creative.json │ │ │ ├── chopper_mk1.json │ │ │ ├── chopper_mk2.json │ │ │ ├── chopper_mk3.json │ │ │ ├── chopper_mk4.json │ │ │ ├── coal_generator_mk1.json │ │ │ ├── compressor_creative.json │ │ │ ├── compressor_factory_mk4.json │ │ │ ├── compressor_mk1.json │ │ │ ├── compressor_mk2.json │ │ │ ├── compressor_mk3.json │ │ │ ├── compressor_mk4.json │ │ │ ├── condenser_mk4.json │ │ │ ├── controller.json │ │ │ ├── deepslate_lead_ore.json │ │ │ ├── deepslate_nikolite_ore.json │ │ │ ├── deepslate_silver_ore.json │ │ │ ├── deepslate_tin_ore.json │ │ │ ├── deepslate_tungsten_ore.json │ │ │ ├── drain_mk1.json │ │ │ ├── drill_bottom.json │ │ │ ├── duct.json │ │ │ ├── electric_furnace_creative.json │ │ │ ├── electric_furnace_factory_mk4.json │ │ │ ├── electric_furnace_mk1.json │ │ │ ├── electric_furnace_mk2.json │ │ │ ├── electric_furnace_mk3.json │ │ │ ├── electric_furnace_mk4.json │ │ │ ├── electrum_block.json │ │ │ ├── farmer_creative.json │ │ │ ├── farmer_mk1.json │ │ │ ├── farmer_mk2.json │ │ │ ├── farmer_mk3.json │ │ │ ├── farmer_mk4.json │ │ │ ├── fisher_mk2.json │ │ │ ├── fisher_mk3.json │ │ │ ├── fisher_mk4.json │ │ │ ├── fluid_infuser_creative.json │ │ │ ├── fluid_infuser_mk1.json │ │ │ ├── fluid_infuser_mk2.json │ │ │ ├── fluid_infuser_mk3.json │ │ │ ├── fluid_infuser_mk4.json │ │ │ ├── fluid_pipe_mk1.json │ │ │ ├── fluid_pipe_mk2.json │ │ │ ├── fluid_pipe_mk3.json │ │ │ ├── fluid_pipe_mk4.json │ │ │ ├── frame.json │ │ │ ├── heat_generator_mk4.json │ │ │ ├── intake.json │ │ │ ├── item_pipe_mk1.json │ │ │ ├── item_pipe_mk2.json │ │ │ ├── item_pipe_mk3.json │ │ │ ├── item_pipe_mk4.json │ │ │ ├── laser_emitter_mk4.json │ │ │ ├── lazuli_flux_container_creative.json │ │ │ ├── lazuli_flux_container_mk1.json │ │ │ ├── lazuli_flux_container_mk2.json │ │ │ ├── lazuli_flux_container_mk3.json │ │ │ ├── lazuli_flux_container_mk4.json │ │ │ ├── lead_block.json │ │ │ ├── lead_ore.json │ │ │ ├── machine_block.json │ │ │ ├── mining_rig_mk4.json │ │ │ ├── modular_workbench_mk4.json │ │ │ ├── nikolite_ore.json │ │ │ ├── plank_block.json │ │ │ ├── planks.json │ │ │ ├── pulverizer_creative.json │ │ │ ├── pulverizer_factory_mk4.json │ │ │ ├── pulverizer_mk1.json │ │ │ ├── pulverizer_mk2.json │ │ │ ├── pulverizer_mk3.json │ │ │ ├── pulverizer_mk4.json │ │ │ ├── pump_mk1.json │ │ │ ├── rancher_creative.json │ │ │ ├── rancher_mk1.json │ │ │ ├── rancher_mk2.json │ │ │ ├── rancher_mk3.json │ │ │ ├── rancher_mk4.json │ │ │ ├── raw_lead_block.json │ │ │ ├── raw_silver_block.json │ │ │ ├── raw_tin_block.json │ │ │ ├── raw_tungsten_block.json │ │ │ ├── recycler_mk2.json │ │ │ ├── sawmill_creative.json │ │ │ ├── sawmill_mk1.json │ │ │ ├── sawmill_mk2.json │ │ │ ├── sawmill_mk3.json │ │ │ ├── sawmill_mk4.json │ │ │ ├── silo.json │ │ │ ├── silver_block.json │ │ │ ├── silver_ore.json │ │ │ ├── slaughter_creative.json │ │ │ ├── slaughter_mk1.json │ │ │ ├── slaughter_mk2.json │ │ │ ├── slaughter_mk3.json │ │ │ ├── slaughter_mk4.json │ │ │ ├── smelter_mk4.json │ │ │ ├── solar_generator_mk1.json │ │ │ ├── solar_generator_mk3.json │ │ │ ├── solid_infuser_creative.json │ │ │ ├── solid_infuser_factory_mk4.json │ │ │ ├── solid_infuser_mk1.json │ │ │ ├── solid_infuser_mk2.json │ │ │ ├── solid_infuser_mk3.json │ │ │ ├── solid_infuser_mk4.json │ │ │ ├── steel_block.json │ │ │ ├── sulfur_crystal.json │ │ │ ├── tank.json │ │ │ ├── tin_block.json │ │ │ ├── tin_ore.json │ │ │ ├── tungsten_block.json │ │ │ ├── tungsten_ore.json │ │ │ ├── warning_strobe.json │ │ │ └── wither_proof_obsidian.json │ │ ├── patchouli_books/ │ │ │ └── indrev/ │ │ │ ├── book.json │ │ │ ├── en_us/ │ │ │ │ ├── categories/ │ │ │ │ │ ├── enhanced_ore_processing.json │ │ │ │ │ ├── machines.json │ │ │ │ │ ├── natural_resources.json │ │ │ │ │ ├── temperature.json │ │ │ │ │ ├── tools.json │ │ │ │ │ ├── transportation.json │ │ │ │ │ └── upgrades.json │ │ │ │ └── entries/ │ │ │ │ ├── enhanced_ore_processing/ │ │ │ │ │ ├── doubling.json │ │ │ │ │ ├── quadrupling.json │ │ │ │ │ └── tripling.json │ │ │ │ ├── machines/ │ │ │ │ │ ├── basic_machines.json │ │ │ │ │ ├── batteries.json │ │ │ │ │ ├── cables.json │ │ │ │ │ ├── chopper.json │ │ │ │ │ ├── factories.json │ │ │ │ │ ├── farmer.json │ │ │ │ │ ├── fisher.json │ │ │ │ │ ├── generators.json │ │ │ │ │ ├── miner.json │ │ │ │ │ ├── pump.json │ │ │ │ │ └── rancher.json │ │ │ │ ├── natural_resources/ │ │ │ │ │ ├── lead_ore.json │ │ │ │ │ ├── nikolite_ore.json │ │ │ │ │ ├── silver_ore.json │ │ │ │ │ ├── sulfur_crystals.json │ │ │ │ │ ├── tin_ore.json │ │ │ │ │ └── tungsten_ore.json │ │ │ │ ├── temperature/ │ │ │ │ │ ├── coolers_and_fans.json │ │ │ │ │ └── info.json │ │ │ │ ├── tools/ │ │ │ │ │ ├── energy_reader.json │ │ │ │ │ ├── gamer_axe.json │ │ │ │ │ ├── hammer.json │ │ │ │ │ ├── mining_drills.json │ │ │ │ │ ├── modular_armor.json │ │ │ │ │ ├── modular_core.json │ │ │ │ │ ├── screwdriver.json │ │ │ │ │ └── wrench.json │ │ │ │ ├── transportation/ │ │ │ │ │ ├── energy.json │ │ │ │ │ ├── fluid_pipes.json │ │ │ │ │ ├── item_pipes.json │ │ │ │ │ └── servos.json │ │ │ │ └── upgrades/ │ │ │ │ ├── enhancement_upgrades.json │ │ │ │ └── tier_upgrades.json │ │ │ ├── ru_ru/ │ │ │ │ ├── book.json │ │ │ │ ├── categories/ │ │ │ │ │ ├── enhanced_ore_processing.json │ │ │ │ │ ├── machines.json │ │ │ │ │ ├── natural_resources.json │ │ │ │ │ ├── temperature.json │ │ │ │ │ ├── tools.json │ │ │ │ │ ├── transportation.json │ │ │ │ │ └── upgrades.json │ │ │ │ └── entries/ │ │ │ │ ├── enhanced_ore_processing/ │ │ │ │ │ ├── doubling.json │ │ │ │ │ ├── quadrupling.json │ │ │ │ │ └── tripling.json │ │ │ │ ├── machines/ │ │ │ │ │ ├── basic_machines.json │ │ │ │ │ ├── batteries.json │ │ │ │ │ ├── cables.json │ │ │ │ │ ├── chopper.json │ │ │ │ │ ├── factories.json │ │ │ │ │ ├── farmer.json │ │ │ │ │ ├── fisher.json │ │ │ │ │ ├── generators.json │ │ │ │ │ ├── miner.json │ │ │ │ │ ├── pump.json │ │ │ │ │ └── rancher.json │ │ │ │ ├── natural_resources/ │ │ │ │ │ ├── lead_ore.json │ │ │ │ │ ├── nikolite_ore.json │ │ │ │ │ ├── silver_ore.json │ │ │ │ │ ├── sulfur_crystals.json │ │ │ │ │ ├── tin_ore.json │ │ │ │ │ └── tungsten_ore.json │ │ │ │ ├── temperature/ │ │ │ │ │ ├── coolers_and_fans.json │ │ │ │ │ └── info.json │ │ │ │ ├── tools/ │ │ │ │ │ ├── energy_reader.json │ │ │ │ │ ├── gamer_axe.json │ │ │ │ │ ├── hammer.json │ │ │ │ │ ├── mining_drills.json │ │ │ │ │ ├── modular_armor.json │ │ │ │ │ ├── modular_core.json │ │ │ │ │ ├── screwdriver.json │ │ │ │ │ └── wrench.json │ │ │ │ ├── transportation/ │ │ │ │ │ ├── energy.json │ │ │ │ │ ├── fluid_pipes.json │ │ │ │ │ ├── item_pipes.json │ │ │ │ │ └── servos.json │ │ │ │ └── upgrades/ │ │ │ │ ├── enhancement_upgrades.json │ │ │ │ └── tier_upgrades.json │ │ │ └── zh_cn/ │ │ │ ├── categories/ │ │ │ │ ├── enhanced_ore_processing.json │ │ │ │ ├── machines.json │ │ │ │ ├── natural_resources.json │ │ │ │ ├── temperature.json │ │ │ │ ├── tools.json │ │ │ │ ├── transportation.json │ │ │ │ └── upgrades.json │ │ │ └── entries/ │ │ │ ├── enhanced_ore_processing/ │ │ │ │ ├── doubling.json │ │ │ │ ├── quadrupling.json │ │ │ │ └── tripling.json │ │ │ ├── machines/ │ │ │ │ ├── basic_machines.json │ │ │ │ ├── batteries.json │ │ │ │ ├── cables.json │ │ │ │ ├── chopper.json │ │ │ │ ├── factories.json │ │ │ │ ├── farmer.json │ │ │ │ ├── fisher.json │ │ │ │ ├── generators.json │ │ │ │ ├── miner.json │ │ │ │ ├── pump.json │ │ │ │ └── rancher.json │ │ │ ├── natural_resources/ │ │ │ │ ├── lead_ore.json │ │ │ │ ├── nikolite_ore.json │ │ │ │ ├── silver_ore.json │ │ │ │ ├── sulfur_crystals.json │ │ │ │ ├── tin_ore.json │ │ │ │ └── tungsten_ore.json │ │ │ ├── temperature/ │ │ │ │ ├── coolers_and_fans.json │ │ │ │ └── info.json │ │ │ ├── tools/ │ │ │ │ ├── energy_reader.json │ │ │ │ ├── gamer_axe.json │ │ │ │ ├── hammer.json │ │ │ │ ├── mining_drills.json │ │ │ │ ├── modular_armor.json │ │ │ │ ├── modular_core.json │ │ │ │ ├── screwdriver.json │ │ │ │ └── wrench.json │ │ │ ├── transportation/ │ │ │ │ ├── energy.json │ │ │ │ ├── fluid_pipes.json │ │ │ │ ├── item_pipes.json │ │ │ │ └── servos.json │ │ │ └── upgrades/ │ │ │ ├── enhancement_upgrades.json │ │ │ └── tier_upgrades.json │ │ ├── recipes/ │ │ │ ├── blasting/ │ │ │ │ ├── bronze_ingot_from_smelting.json │ │ │ │ ├── copper_ingot_from_ore.json │ │ │ │ ├── copper_ingot_from_smelting.json │ │ │ │ ├── electrum_ingot_from_smelting.json │ │ │ │ ├── gold_ingot.json │ │ │ │ ├── iron_ingot_from_dust.json │ │ │ │ ├── lead_ingot_from_ore.json │ │ │ │ ├── lead_ingot_from_raw_ore.json │ │ │ │ ├── lead_ingot_from_smelting.json │ │ │ │ ├── netherite_scrap.json │ │ │ │ ├── silver_ingot_from_ore.json │ │ │ │ ├── silver_ingot_from_raw_ores.json │ │ │ │ ├── silver_ingot_from_smelting.json │ │ │ │ ├── steel_ingot.json │ │ │ │ ├── tin_ingot_from_ore.json │ │ │ │ ├── tin_ingot_from_raw_ores.json │ │ │ │ ├── tin_ingot_from_smelting.json │ │ │ │ ├── tungsten_ingot_from_ore.json │ │ │ │ ├── tungsten_ingot_from_raw_ores.json │ │ │ │ └── tungsten_ingot_from_smelting.json │ │ │ ├── compressing/ │ │ │ │ ├── bronze_plate_from_compressor.json │ │ │ │ ├── carbon_fiber_plate.json │ │ │ │ ├── copper_plate_from_compressor.json │ │ │ │ ├── electrum_plate_from_compressor.json │ │ │ │ ├── empty_upgrade.json │ │ │ │ ├── gold_plate_from_compressor.json │ │ │ │ ├── iron_plate_from_compressor.json │ │ │ │ ├── lead_plate_from_compressor.json │ │ │ │ ├── plank_block.json │ │ │ │ ├── planks.json │ │ │ │ ├── silver_plate_from_compressor.json │ │ │ │ ├── steel_plate.json │ │ │ │ ├── tin_plate_from_compressor.json │ │ │ │ └── tungsten_plate_from_compressor.json │ │ │ ├── condensing/ │ │ │ │ ├── copper_chunk_from_molten_copper.json │ │ │ │ ├── gold_chunk_from_molten_gold.json │ │ │ │ ├── iron_chunk_from_molten_iron.json │ │ │ │ ├── lead_chunk_from_molten_lead.json │ │ │ │ ├── netherite_chunk_from_molten_netherite.json │ │ │ │ ├── silver_chunk_from_molten_silver.json │ │ │ │ └── tin_chunk_from_molten_tin.json │ │ │ ├── distiller/ │ │ │ │ └── salt_from_water.json │ │ │ ├── electrolysis/ │ │ │ │ └── electrolyze_water.json │ │ │ ├── fluid_infusing/ │ │ │ │ ├── clay.json │ │ │ │ ├── coolant_fluid.json │ │ │ │ ├── copper_purified_ore.json │ │ │ │ ├── gold_purified_ore.json │ │ │ │ ├── harden_black_concrete_powder.json │ │ │ │ ├── harden_blue_concrete_powder.json │ │ │ │ ├── harden_brown_concrete_powder.json │ │ │ │ ├── harden_cyan_concrete_powder.json │ │ │ │ ├── harden_gray_concrete_powder.json │ │ │ │ ├── harden_green_concrete_powder.json │ │ │ │ ├── harden_light_blue_concrete_powder.json │ │ │ │ ├── harden_light_gray_concrete_powder.json │ │ │ │ ├── harden_lime_concrete_powder.json │ │ │ │ ├── harden_magenta_concrete_powder.json │ │ │ │ ├── harden_orange_concrete_powder.json │ │ │ │ ├── harden_pink_concrete_powder.json │ │ │ │ ├── harden_purple_concrete_powder.json │ │ │ │ ├── harden_red_concrete_powder.json │ │ │ │ ├── harden_white_concrete_powder.json │ │ │ │ ├── harden_yellow_concrete_powder.json │ │ │ │ ├── iron_purified_ore.json │ │ │ │ ├── lead_purified_ore.json │ │ │ │ ├── netherite_purified_ore.json │ │ │ │ ├── paper.json │ │ │ │ ├── silver_purified_ore.json │ │ │ │ ├── sulfuric_acid.json │ │ │ │ ├── tin_purified_ore.json │ │ │ │ ├── tungsten_purified_ore.json │ │ │ │ └── wither_proof_obsidian.json │ │ │ ├── infusing/ │ │ │ │ ├── allium.json │ │ │ │ ├── azure_bluet.json │ │ │ │ ├── big_dripleaf.json │ │ │ │ ├── blue_orchid.json │ │ │ │ ├── bronze_dust.json │ │ │ │ ├── brown_mushroom.json │ │ │ │ ├── cornflower.json │ │ │ │ ├── crimson_fungus.json │ │ │ │ ├── crimson_roots.json │ │ │ │ ├── dandelion.json │ │ │ │ ├── dead_bush.json │ │ │ │ ├── electrum_dust.json │ │ │ │ ├── enriched_nikolite.json │ │ │ │ ├── enriched_nikolite_ingot.json │ │ │ │ ├── fern.json │ │ │ │ ├── glow_lichen.json │ │ │ │ ├── grass.json │ │ │ │ ├── kelp.json │ │ │ │ ├── large_fern.json │ │ │ │ ├── lilac.json │ │ │ │ ├── lily_of_the_valley.json │ │ │ │ ├── lily_pad.json │ │ │ │ ├── nether_sprouts.json │ │ │ │ ├── nikolite_ingot.json │ │ │ │ ├── orange_tulip.json │ │ │ │ ├── oxeye_daisy.json │ │ │ │ ├── pink_tulip.json │ │ │ │ ├── poppy.json │ │ │ │ ├── red_mushroom.json │ │ │ │ ├── red_tulip.json │ │ │ │ ├── sea_pickle.json │ │ │ │ ├── seagrass.json │ │ │ │ ├── small_dripleaf.json │ │ │ │ ├── spore_blossom.json │ │ │ │ ├── steel_dust.json │ │ │ │ ├── tall_grass.json │ │ │ │ ├── twisting_vines.json │ │ │ │ ├── untanned_leather.json │ │ │ │ ├── vine.json │ │ │ │ ├── warped_fungus.json │ │ │ │ ├── warped_roots.json │ │ │ │ ├── weeping_vines.json │ │ │ │ └── white_tulip.json │ │ │ ├── laser.json │ │ │ ├── modules/ │ │ │ │ ├── module_auto_feeder.json │ │ │ │ ├── module_breathing.json │ │ │ │ ├── module_charger.json │ │ │ │ ├── module_efficiency.json │ │ │ │ ├── module_elytra.json │ │ │ │ ├── module_feather_falling.json │ │ │ │ ├── module_fire_aspect.json │ │ │ │ ├── module_fire_resistance.json │ │ │ │ ├── module_fortune.json │ │ │ │ ├── module_jump_boost.json │ │ │ │ ├── module_looting.json │ │ │ │ ├── module_night_vision.json │ │ │ │ ├── module_piglin_tricker.json │ │ │ │ ├── module_protection.json │ │ │ │ ├── module_range.json │ │ │ │ ├── module_sharpness.json │ │ │ │ ├── module_silk_touch.json │ │ │ │ ├── module_solar_panel.json │ │ │ │ └── module_speed.json │ │ │ ├── pulverizer/ │ │ │ │ ├── blaze_power.json │ │ │ │ ├── bone_meal.json │ │ │ │ ├── bronze_dust_from_ingot.json │ │ │ │ ├── coal_dust.json │ │ │ │ ├── coal_dust_from_ore.json │ │ │ │ ├── cobblestone_pulverizer.json │ │ │ │ ├── copper_dust_from_chunk.json │ │ │ │ ├── copper_dust_from_ingot.json │ │ │ │ ├── copper_dust_from_ore.json │ │ │ │ ├── copper_dust_from_purified_ore.json │ │ │ │ ├── copper_dust_from_raw.json │ │ │ │ ├── diamond_dust.json │ │ │ │ ├── diamond_dust_from_ore.json │ │ │ │ ├── electrum_dust_from_ingot.json │ │ │ │ ├── gold_dust_from_chunk.json │ │ │ │ ├── gold_dust_from_ingot.json │ │ │ │ ├── gold_dust_from_ore.json │ │ │ │ ├── gold_dust_from_raw.json │ │ │ │ ├── gravel_pulverizer.json │ │ │ │ ├── iron_dust_from_chunk.json │ │ │ │ ├── iron_dust_from_ingot.json │ │ │ │ ├── iron_dust_from_ore.json │ │ │ │ ├── iron_dust_from_purified_ore.json │ │ │ │ ├── iron_dust_from_raw.json │ │ │ │ ├── lead_dust_from_chunk.json │ │ │ │ ├── lead_dust_from_ingot.json │ │ │ │ ├── lead_dust_from_ore.json │ │ │ │ ├── lead_dust_from_purified_ore.json │ │ │ │ ├── lead_dust_from_raw.json │ │ │ │ ├── netherite_dust.json │ │ │ │ ├── netherite_dust_from_chunk.json │ │ │ │ ├── nikolite.json │ │ │ │ ├── sand_pulverizer.json │ │ │ │ ├── silver_dust_from_chunk.json │ │ │ │ ├── silver_dust_from_ingot.json │ │ │ │ ├── silver_dust_from_ore.json │ │ │ │ ├── silver_dust_from_purified_ore.json │ │ │ │ ├── silver_dust_from_raw.json │ │ │ │ ├── sulfur_dust.json │ │ │ │ ├── sulfur_dust_from_gunpowder.json │ │ │ │ ├── tin_dust_from_chunk.json │ │ │ │ ├── tin_dust_from_ingot.json │ │ │ │ ├── tin_dust_from_ore.json │ │ │ │ ├── tin_dust_from_purified_ore.json │ │ │ │ ├── tin_dust_from_raw.json │ │ │ │ ├── tungsten_dust_from_ingot.json │ │ │ │ ├── tungsten_dust_from_ore.json │ │ │ │ └── tungsten_dust_from_purified_ore.json │ │ │ ├── recycling/ │ │ │ │ ├── recycle_baked_potato.json │ │ │ │ ├── recycle_bamboo.json │ │ │ │ ├── recycle_beetroot.json │ │ │ │ ├── recycle_beetrot_seeds.json │ │ │ │ ├── recycle_bread.json │ │ │ │ ├── recycle_brown_mushroom.json │ │ │ │ ├── recycle_cactus.json │ │ │ │ ├── recycle_carrot.json │ │ │ │ ├── recycle_cookie.json │ │ │ │ ├── recycle_dirt.json │ │ │ │ ├── recycle_flowers.json │ │ │ │ ├── recycle_grass.json │ │ │ │ ├── recycle_grass_block.json │ │ │ │ ├── recycle_hay_block.json │ │ │ │ ├── recycle_logs.json │ │ │ │ ├── recycle_melon.json │ │ │ │ ├── recycle_melon_seeds.json │ │ │ │ ├── recycle_melon_slice.json │ │ │ │ ├── recycle_nether_wart.json │ │ │ │ ├── recycle_planks.json │ │ │ │ ├── recycle_potato.json │ │ │ │ ├── recycle_red_mushroom.json │ │ │ │ ├── recycle_saplings.json │ │ │ │ ├── recycle_wheat.json │ │ │ │ └── recycle_wheat_seeds.json │ │ │ ├── sawmill/ │ │ │ │ ├── acacia_planks.json │ │ │ │ ├── birch_planks.json │ │ │ │ ├── crimson_planks.json │ │ │ │ ├── dark_oak_planks.json │ │ │ │ ├── jungle_planks.json │ │ │ │ ├── oak_planks.json │ │ │ │ ├── spruce_planks.json │ │ │ │ └── warped_planks.json │ │ │ ├── shaped/ │ │ │ │ ├── battery.json │ │ │ │ ├── biomass_generator_mk3.json │ │ │ │ ├── blast_furnace_enhancer.json │ │ │ │ ├── bronze_axe.json │ │ │ │ ├── bronze_block.json │ │ │ │ ├── bronze_boots.json │ │ │ │ ├── bronze_chestplate.json │ │ │ │ ├── bronze_helmet.json │ │ │ │ ├── bronze_hoe.json │ │ │ │ ├── bronze_ingot_from_nugget.json │ │ │ │ ├── bronze_leggings.json │ │ │ │ ├── bronze_pickaxe.json │ │ │ │ ├── bronze_shovel.json │ │ │ │ ├── bronze_sword.json │ │ │ │ ├── buffer_enhancer.json │ │ │ │ ├── cabinet.json │ │ │ │ ├── cable_mk1.json │ │ │ │ ├── cable_mk2.json │ │ │ │ ├── cable_mk3.json │ │ │ │ ├── cable_mk4.json │ │ │ │ ├── capsule.json │ │ │ │ ├── carbon_fiber_boots_frame.json │ │ │ │ ├── carbon_fiber_chest_frame.json │ │ │ │ ├── carbon_fiber_helmet_frame.json │ │ │ │ ├── carbon_fiber_legs_frame.json │ │ │ │ ├── carbon_fiber_rod.json │ │ │ │ ├── charge_pad_mk4.json │ │ │ │ ├── chopper_mk1.json │ │ │ │ ├── circuit_mk1.json │ │ │ │ ├── circuit_mk2.json │ │ │ │ ├── circuit_mk3.json │ │ │ │ ├── circuit_mk4.json │ │ │ │ ├── coal_generator_mk1.json │ │ │ │ ├── compressor_factory_mk4.json │ │ │ │ ├── compressor_mk1.json │ │ │ │ ├── condenser_mk4.json │ │ │ │ ├── controller.json │ │ │ │ ├── cooler_cell.json │ │ │ │ ├── copper_axe.json │ │ │ │ ├── copper_boots.json │ │ │ │ ├── copper_chestplate.json │ │ │ │ ├── copper_helmet.json │ │ │ │ ├── copper_hoe.json │ │ │ │ ├── copper_ingot_from_nuggets.json │ │ │ │ ├── copper_leggings.json │ │ │ │ ├── copper_pickaxe.json │ │ │ │ ├── copper_shovel.json │ │ │ │ ├── copper_sword.json │ │ │ │ ├── damage_upgrade.json │ │ │ │ ├── data_card_writer_mk4.json │ │ │ │ ├── diamond_drill_head.json │ │ │ │ ├── drain_mk1.json │ │ │ │ ├── drill_bottom.json │ │ │ │ ├── duct.json │ │ │ │ ├── electric_furnace_factory_mk4.json │ │ │ │ ├── electric_furnace_mk1.json │ │ │ │ ├── electrum_block.json │ │ │ │ ├── electrum_block_from_nugget.json │ │ │ │ ├── energy_reader.json │ │ │ │ ├── fan.json │ │ │ │ ├── farmer_mk1.json │ │ │ │ ├── fisher_mk2.json │ │ │ │ ├── fisher_mk3.json │ │ │ │ ├── fisher_mk4.json │ │ │ │ ├── fluid_infuser_mk1.json │ │ │ │ ├── fluid_pipe_mk1.json │ │ │ │ ├── fluid_pipe_mk2.json │ │ │ │ ├── fluid_pipe_mk3.json │ │ │ │ ├── fluid_pipe_mk4.json │ │ │ │ ├── frame.json │ │ │ │ ├── gamer_axe.json │ │ │ │ ├── hammer.json │ │ │ │ ├── heat_coil.json │ │ │ │ ├── heat_generator.json │ │ │ │ ├── heatsink.json │ │ │ │ ├── intake.json │ │ │ │ ├── iron_drill_head.json │ │ │ │ ├── item_pipe_mk1.json │ │ │ │ ├── item_pipe_mk2.json │ │ │ │ ├── item_pipe_mk3.json │ │ │ │ ├── item_pipe_mk4.json │ │ │ │ ├── laser_emitter_mk4.json │ │ │ │ ├── lazuli_flux_container_mk1.json │ │ │ │ ├── lazuli_flux_container_mk2.json │ │ │ │ ├── lazuli_flux_container_mk3.json │ │ │ │ ├── lazuli_flux_container_mk4.json │ │ │ │ ├── lead_axe.json │ │ │ │ ├── lead_block.json │ │ │ │ ├── lead_boots.json │ │ │ │ ├── lead_chestplate.json │ │ │ │ ├── lead_helmet.json │ │ │ │ ├── lead_hoe.json │ │ │ │ ├── lead_ingot_from_nuggets.json │ │ │ │ ├── lead_leggings.json │ │ │ │ ├── lead_pickaxe.json │ │ │ │ ├── lead_shovel.json │ │ │ │ ├── lead_sword.json │ │ │ │ ├── machine_block.json │ │ │ │ ├── mining_drill_mk1.json │ │ │ │ ├── mining_drill_mk2.json │ │ │ │ ├── mining_drill_mk3.json │ │ │ │ ├── mining_drill_mk4.json │ │ │ │ ├── mining_rig_mk4.json │ │ │ │ ├── modular_armor_boots.json │ │ │ │ ├── modular_armor_chest.json │ │ │ │ ├── modular_armor_helmet.json │ │ │ │ ├── modular_armor_legs.json │ │ │ │ ├── modular_core.json │ │ │ │ ├── modular_workbench.json │ │ │ │ ├── netherite_drill_head.json │ │ │ │ ├── ore_data_card.json │ │ │ │ ├── paper.json │ │ │ │ ├── portable_charger.json │ │ │ │ ├── pulverizer_factory_mk4.json │ │ │ │ ├── pulverizer_mk1.json │ │ │ │ ├── pump_mk1.json │ │ │ │ ├── rancher_mk1.json │ │ │ │ ├── raw_lead_block.json │ │ │ │ ├── raw_silver_block.json │ │ │ │ ├── raw_tin_block.json │ │ │ │ ├── raw_tungsten_block.json │ │ │ │ ├── recycler_mk2.json │ │ │ │ ├── reinforced_elytra.json │ │ │ │ ├── sawmill_mk1.json │ │ │ │ ├── screwdriver.json │ │ │ │ ├── servo_output.json │ │ │ │ ├── servo_retriever.json │ │ │ │ ├── silo.json │ │ │ │ ├── silver_axe.json │ │ │ │ ├── silver_block.json │ │ │ │ ├── silver_boots.json │ │ │ │ ├── silver_chestplate.json │ │ │ │ ├── silver_helmet.json │ │ │ │ ├── silver_hoe.json │ │ │ │ ├── silver_ingot_from_nuggets.json │ │ │ │ ├── silver_leggings.json │ │ │ │ ├── silver_pickaxe.json │ │ │ │ ├── silver_shovel.json │ │ │ │ ├── silver_sword.json │ │ │ │ ├── slaughter_mk1.json │ │ │ │ ├── smelter_mk4.json │ │ │ │ ├── smoker_enhancer.json │ │ │ │ ├── solar_generator_mk1.json │ │ │ │ ├── solar_generator_mk3.json │ │ │ │ ├── solid_infuser_factory_mk4.json │ │ │ │ ├── solid_infuser_mk1.json │ │ │ │ ├── speed_enhancer.json │ │ │ │ ├── steel_axe.json │ │ │ │ ├── steel_block.json │ │ │ │ ├── steel_boots.json │ │ │ │ ├── steel_chestplate.json │ │ │ │ ├── steel_helmet.json │ │ │ │ ├── steel_hoe.json │ │ │ │ ├── steel_ingot_from_nuggets.json │ │ │ │ ├── steel_leggings.json │ │ │ │ ├── steel_pickaxe.json │ │ │ │ ├── steel_shovel.json │ │ │ │ ├── steel_sword.json │ │ │ │ ├── stone_drill_head.json │ │ │ │ ├── tank.json │ │ │ │ ├── tier_upgrade_mk2.json │ │ │ │ ├── tier_upgrade_mk3.json │ │ │ │ ├── tier_upgrade_mk4.json │ │ │ │ ├── tin_axe.json │ │ │ │ ├── tin_block.json │ │ │ │ ├── tin_boots.json │ │ │ │ ├── tin_chestplate.json │ │ │ │ ├── tin_helmet.json │ │ │ │ ├── tin_hoe.json │ │ │ │ ├── tin_ingot_from_nuggets.json │ │ │ │ ├── tin_leggings.json │ │ │ │ ├── tin_pickaxe.json │ │ │ │ ├── tin_shovel.json │ │ │ │ ├── tin_sword.json │ │ │ │ ├── tungsten_block.json │ │ │ │ ├── tungsten_ingot_from_nuggets.json │ │ │ │ ├── warning_strobe.json │ │ │ │ └── wrench.json │ │ │ ├── shapeless/ │ │ │ │ ├── bronze_ingot_from_block.json │ │ │ │ ├── bronze_nugget.json │ │ │ │ ├── copper_ingot_from_block.json │ │ │ │ ├── copper_nugget.json │ │ │ │ ├── copper_plate_from_hammer.json │ │ │ │ ├── electrum_ingot_from_block.json │ │ │ │ ├── electrum_nugget.json │ │ │ │ ├── gold_plate_from_hammer.json │ │ │ │ ├── guide_book.json │ │ │ │ ├── iron_plate_from_hammer.json │ │ │ │ ├── lead_ingot_from_block.json │ │ │ │ ├── lead_nugget.json │ │ │ │ ├── planks.json │ │ │ │ ├── raw_lead.json │ │ │ │ ├── raw_silver.json │ │ │ │ ├── raw_tin.json │ │ │ │ ├── raw_tungsten.json │ │ │ │ ├── silver_ingot_from_block.json │ │ │ │ ├── silver_nugget.json │ │ │ │ ├── steel_ingot_from_block.json │ │ │ │ ├── steel_nugget.json │ │ │ │ ├── tin_ingot_from_block.json │ │ │ │ ├── tin_nugget.json │ │ │ │ ├── tin_plate_from_hammer.json │ │ │ │ ├── tungsten_ingot_from_block.json │ │ │ │ └── tungsten_nugget.json │ │ │ ├── smelter/ │ │ │ │ ├── molten_copper_from_block.json │ │ │ │ ├── molten_copper_from_dust.json │ │ │ │ ├── molten_copper_from_ingot.json │ │ │ │ ├── molten_copper_from_nugget.json │ │ │ │ ├── molten_copper_from_ore.json │ │ │ │ ├── molten_copper_from_purified_ore.json │ │ │ │ ├── molten_gold_from_block.json │ │ │ │ ├── molten_gold_from_dust.json │ │ │ │ ├── molten_gold_from_ingot.json │ │ │ │ ├── molten_gold_from_nugget.json │ │ │ │ ├── molten_gold_from_ore.json │ │ │ │ ├── molten_gold_from_purified_ore.json │ │ │ │ ├── molten_iron_from_block.json │ │ │ │ ├── molten_iron_from_dust.json │ │ │ │ ├── molten_iron_from_ingot.json │ │ │ │ ├── molten_iron_from_nugget.json │ │ │ │ ├── molten_iron_from_ore.json │ │ │ │ ├── molten_iron_from_purified_ore.json │ │ │ │ ├── molten_lead_from_block.json │ │ │ │ ├── molten_lead_from_dust.json │ │ │ │ ├── molten_lead_from_ingot.json │ │ │ │ ├── molten_lead_from_nugget.json │ │ │ │ ├── molten_lead_from_ore.json │ │ │ │ ├── molten_lead_from_purified_ore.json │ │ │ │ ├── molten_netherite_from_dust.json │ │ │ │ ├── molten_netherite_from_ore.json │ │ │ │ ├── molten_netherite_from_purified_ore.json │ │ │ │ ├── molten_silver_from_block.json │ │ │ │ ├── molten_silver_from_dust.json │ │ │ │ ├── molten_silver_from_ingot.json │ │ │ │ ├── molten_silver_from_nugget.json │ │ │ │ ├── molten_silver_from_ore.json │ │ │ │ ├── molten_silver_from_purified_ore.json │ │ │ │ ├── molten_tin_from_block.json │ │ │ │ ├── molten_tin_from_dust.json │ │ │ │ ├── molten_tin_from_ingot.json │ │ │ │ ├── molten_tin_from_nugget.json │ │ │ │ ├── molten_tin_from_ore.json │ │ │ │ └── molten_tin_from_purified_ore.json │ │ │ ├── smelting/ │ │ │ │ ├── bronze_ingot_from_smelting.json │ │ │ │ ├── copper_ingot_from_smelting.json │ │ │ │ ├── electrum_ingot_from_smelting.json │ │ │ │ ├── gold_ingot.json │ │ │ │ ├── iron_ingot_from_dust.json │ │ │ │ ├── lead_ingot_from_ore.json │ │ │ │ ├── lead_ingot_from_raw_ore.json │ │ │ │ ├── lead_ingot_from_smelting.json │ │ │ │ ├── leather.json │ │ │ │ ├── netherite_scrap.json │ │ │ │ ├── silver_ingot_from_ore.json │ │ │ │ ├── silver_ingot_from_raw_ores.json │ │ │ │ ├── silver_ingot_from_smelting.json │ │ │ │ ├── steel_ingot.json │ │ │ │ ├── tin_ingot_from_ore.json │ │ │ │ ├── tin_ingot_from_raw_ores.json │ │ │ │ ├── tin_ingot_from_smelting.json │ │ │ │ ├── tungsten_ingot_from_ore.json │ │ │ │ ├── tungsten_ingot_from_raw_ores.json │ │ │ │ └── tungsten_ingot_from_smelting.json │ │ │ └── upgrade/ │ │ │ ├── chopper_mk1_to_mk2.json │ │ │ ├── chopper_mk2_to_mk3.json │ │ │ ├── chopper_mk3_to_mk4.json │ │ │ ├── compressor_mk1_to_mk2.json │ │ │ ├── compressor_mk2_to_mk3.json │ │ │ ├── compressor_mk3_to_mk4.json │ │ │ ├── electric_furnace_mk1_to_mk2.json │ │ │ ├── electric_furnace_mk2_to_mk3.json │ │ │ ├── electric_furnace_mk3_to_mk4.json │ │ │ ├── electrolytic_separator_mk1_to_mk2.json │ │ │ ├── electrolytic_separator_mk2_to_mk3.json │ │ │ ├── electrolytic_separator_mk3_to_mk4.json │ │ │ ├── farmer_mk1_to_mk2.json │ │ │ ├── farmer_mk2_to_mk3.json │ │ │ ├── farmer_mk3_to_mk4.json │ │ │ ├── fluid_infuser_mk1_to_mk2.json │ │ │ ├── fluid_infuser_mk2_to_mk3.json │ │ │ ├── fluid_infuser_mk3_to_mk4.json │ │ │ ├── pulverizer_mk1_to_mk2.json │ │ │ ├── pulverizer_mk2_to_mk3.json │ │ │ ├── pulverizer_mk3_to_mk4.json │ │ │ ├── rancher_mk1_to_mk2.json │ │ │ ├── rancher_mk2_to_mk3.json │ │ │ ├── rancher_mk3_to_mk4.json │ │ │ ├── sawmill_mk1_to_mk2.json │ │ │ ├── sawmill_mk2_to_mk3.json │ │ │ ├── sawmill_mk3_to_mk4.json │ │ │ ├── slaughter_mk1_to_mk2.json │ │ │ ├── slaughter_mk2_to_mk3.json │ │ │ ├── slaughter_mk3_to_mk4.json │ │ │ ├── solid_infuser_mk1_to_mk2.json │ │ │ ├── solid_infuser_mk2_to_mk3.json │ │ │ └── solid_infuser_mk3_to_mk4.json │ │ └── tags/ │ │ └── items/ │ │ └── coolers.json │ └── minecraft/ │ └── tags/ │ ├── blocks/ │ │ ├── mineable/ │ │ │ ├── axe.json │ │ │ └── pickaxe.json │ │ └── wither_immune.json │ └── fluids/ │ ├── lava.json │ └── water.json ├── fabric.mod.json ├── indrev.accesswidener └── indrev.mixins.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bugs.md ================================================ --- name: Bug report about: Something isn't working right title: '[BUG] ' labels: bug assignees: '' --- **Description:** - Give a detailed explanation of the issue **Steps to Reproduce:** - Steps to reproduce this behaviour **Technical Information:** - Minecraft: - Fabric API: - Industrial Revolution: - Modpack: **Logs:** - Use a paste service, do not paste the logs directly here. **Details:** Extra information that might be helpful like screenshots or technical information about your computer. ================================================ FILE: .github/ISSUE_TEMPLATE/requests.md ================================================ --- name: Request about: I have a really cool idea! title: '[REQUEST] ' labels: enhancement assignees: '' --- **Description:** - Describe your suggestion with as many details as possible. **Reason:** - Why do you think your suggestion should be added? ================================================ FILE: .gitignore ================================================ # gradle .gradle/ build/ out/ # idea .idea/ *.iml *.ipr *.iws # fabric run/ ================================================ FILE: LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================

Industrial Revolution

An Industrial mod made for Fabric.

## Features For a complete list of features please check the mods [official page](https://www.curseforge.com/minecraft/mc-mods/industrial-revolution) ## License Distributed under the Apache License, Version 2.0. See `LICENSE` for more information. ## Build If you want to build this yourself, please clone the repository and execute `gradlew build` in the projects folder. Artifacts will be generated at `/build/libs` ================================================ FILE: build.gradle ================================================ plugins { id "fabric-loom" id "maven-publish" id "org.jetbrains.kotlin.jvm" } sourceCompatibility = JavaVersion.VERSION_16 targetCompatibility = JavaVersion.VERSION_16 archivesBaseName = project.archives_base_name version = project.mod_version group = project.maven_group repositories { maven { name = "Modmuss50" url = "https://maven.modmuss50.me/" content { includeGroup "RebornCore" includeGroup "TechReborn" includeGroup "teamreborn" } } maven { url "https://maven.shedaniel.me/" content { includeGroup "dev.architectury" includeGroup "me.shedaniel" includeGroup "me.shedaniel.cloth" includeGroup "me.shedaniel.cloth.api" } } maven { url = "https://maven.fabricmc.net/" content { includeGroup "net.fabricmc" } } maven { name = "BuildCraft" url = "https://mod-buildcraft.com/maven" content { includeGroup "alexiil.mc.lib" } } maven { name = "CottonMC" url = "https://server.bbkr.space/artifactory/libs-release" content { includeGroup "io.github.cottonmc" } } maven { url = "https://maven.terraformersmc.com/releases/" content { includeGroup "com.terraformersmc" } } maven { name = "Patchouli" url = "https://maven.blamejared.com" content { includeGroup "vazkii.patchouli" } } maven { url = "https://jitpack.io" content { includeGroup "com.github.Technici4n" includeGroup "com.github.Draylar" includeGroup "com.github.Draylar.omega-config" includeGroup "com.github.emilyploszaj" } } maven { url "https://oskarstrom.net/maven" content { includeGroup "net.oskarstrom" } } maven { name = "Cafeteria Development" url = 'https://maven.cafeteria.dev' content { includeGroup 'dev.cafeteria' includeGroup 'me.luligabi' includeGroup 'net.adriantodt.fabricmc' } } maven { name = 'Ladysnake Mods' url = 'https://ladysnake.jfrog.io/artifactory/mods' content { includeGroup 'io.github.ladysnake' includeGroupByRegex 'io\\.github\\.onyxstudios.*' } } } dependencies { dependencies.ext.lib = { dep, optional = false -> modImplementation(dep) { exclude group: "net.fabricmc.fabric-api" exclude group: "com.jamieswhiteshirt" exclude group: "io.github.prospector" } if (!optional) { include(dep) { exclude group: "net.fabricmc.fabric-api" exclude group: "com.jamieswhiteshirt" exclude group: "io.github.prospector" } } } minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}" dependencies.ext.lib("teamreborn:energy:${project.tr_energy_version}") dependencies.ext.lib("io.github.cottonmc:LibGui:${project.libgui_version}") dependencies.ext.lib("vazkii.patchouli:Patchouli:${project.patchouli_version}") dependencies.ext.lib("alexiil.mc.lib:libblockattributes-core:${project.lba_version}") dependencies.ext.lib("alexiil.mc.lib:libblockattributes-fluids:${project.lba_version}") dependencies.ext.lib("alexiil.mc.lib:libblockattributes-items:${project.lba_version}") dependencies.ext.lib("com.github.Draylar:magna:${project.magna_version}") dependencies.ext.lib("com.github.emilyploszaj:step-height-entity-attribute:v1.0.1") dependencies.ext.lib("dev.cafeteria:fake-player-api:${project.fakeplayerapi_version}") dependencies.ext.lib("me.luligabi:NoIndium:1.0") dependencies.ext.lib("me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}", true) modRuntimeOnly("com.terraformersmc:modmenu:${project.modmenu_version}") { exclude group: "net.fabricmc.fabric-api", module: "fabric-api" } // DashLoader Stuff modCompileOnly("net.oskarstrom:DashLoader:2.0-dev12") dependencies.ext.lib("io.activej:activej-serializer:${project.activej_version}", true) modRuntimeOnly("org.yaml:snakeyaml:1.27") } loom { accessWidenerPath = file("src/main/resources/indrev.accesswidener") } processResources { inputs.property "version", project.version filesMatching("fabric.mod.json") { expand "version": project.version } } // ensure that the encoding is set to UTF-8, no matter what the system default is // this fixes some edge cases with special characters not displaying correctly // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html tasks.withType(JavaCompile) { it.options.encoding = "UTF-8" it.options.release = 16 } java { // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task // if it is present. // If you remove this line, sources will not be generated. withSourcesJar() } jar { from("LICENSE") { rename { "${it}_${project.archivesBaseName}"} } exclude("me/steven/indrev/datagen") } // configure the maven publication publishing { publications { mavenJava(MavenPublication) { // add all the jars that should be included when publishing to maven artifact(remapJar) { builtBy remapJar } artifact(sourcesJar) { builtBy remapSourcesJar } } } // select the repositories you want to publish to repositories { maven { url "https://maven.cafeteria.dev/releases" credentials { username = project.property("mcdUsername") password = project.property("mcdPassword") } authentication { basic(BasicAuthentication) } } } } compileKotlin.kotlinOptions.jvmTarget = "16" ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ kotlin.code.style=official org.gradle.jvmargs=-Xmx2G # Fabric Properties minecraft_version=1.18.2 yarn_mappings=1.18.2+build.3 loader_version=0.13.3 #Fabric api fabric_version=0.51.1+1.18.2 loom_version=0.11-SNAPSHOT # Mod Properties mod_version=1.14.0-BETA maven_group=me.steven archives_base_name=indrev # Kotlin kotlin_version=1.6.20 fabric_kotlin_version=1.7.2+kotlin.1.6.20 # LibGui libgui_version=5.3.2+1.18.2 # Mod Menu modmenu_version=3.1.0 # Roughly Enough Items rei_version=8.0.442 # Patchouli patchouli_version=1.18.2-66-FABRIC # LBA lba_version=0.10.0 # Magna magna_version=1.7.0-1.18 # TR Energy tr_energy_version=2.0.0-beta1 # Fake Player API fakeplayerapi_version=0.3.0 # ActiveJ activej_version=4.3 ================================================ FILE: gradlew ================================================ #!/usr/bin/env sh # # Copyright 2015 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin or MSYS, switch paths to Windows format before running java if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=`expr $i + 1` done case $i in 0) set -- ;; 1) set -- "$args0" ;; 2) set -- "$args0" "$args1" ;; 3) set -- "$args0" "$args1" "$args2" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: settings.gradle ================================================ pluginManagement { repositories { jcenter() maven { name = 'Fabric' url = 'https://maven.fabricmc.net/' } gradlePluginPortal() } plugins { id 'fabric-loom' version loom_version id "org.jetbrains.kotlin.jvm" version kotlin_version } } ================================================ FILE: src/main/java/me/steven/indrev/AprilFools.java ================================================ package me.steven.indrev; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.text.LiteralText; import net.minecraft.util.Formatting; import net.minecraft.util.registry.Registry; import java.io.File; import java.io.IOException; import java.time.LocalDate; @Environment(EnvType.CLIENT) public final class AprilFools { private static final File CHECK = new File(FabricLoader.getInstance().getGameDir().toString(), ".indrev_" + LocalDate.now().getYear()); public static void init() { if (isToday()) { ItemTooltipCallback.EVENT.register((itemStack, tooltipContext, list) -> { String itemNamespace = Registry.ITEM.getId(itemStack.getItem()).getNamespace(); if (itemNamespace.equals(IndustrialRevolution.MOD_ID) && list.size() > 1) { list.add(new LiteralText("")); // break line list.add(new LiteralText("every good modpack uses forge...").formatted(Formatting.ITALIC)); } }); try { CHECK.createNewFile(); } catch (IOException ex) { ex.printStackTrace(); } } } public static boolean isToday() { return false; /*LocalDate now = LocalDate.now(); return now.getDayOfMonth() == 1 && now.getMonthValue() == 4 && !CHECK.exists();*/ } } ================================================ FILE: src/main/java/me/steven/indrev/FabricRecipeRemainder.java ================================================ package me.steven.indrev; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.CraftingInventory; import net.minecraft.item.ItemStack; public interface FabricRecipeRemainder { ItemStack getRemainder(ItemStack stack, CraftingInventory craftingInventory, PlayerEntity playerEntity); } ================================================ FILE: src/main/java/me/steven/indrev/IREnergyStorage.java ================================================ package me.steven.indrev; import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions; import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant; import net.minecraft.util.math.Direction; import org.jetbrains.annotations.Nullable; import team.reborn.energy.api.EnergyStorage; /** * Copied from SimpleSidedEnergyContainer and modified to fit IR's needs */ public abstract class IREnergyStorage extends SnapshotParticipant { private final SideStorage[] sideStorages = new SideStorage[7]; public IREnergyStorage() { for (int i = 0; i < 7; ++i) { sideStorages[i] = new SideStorage(i == 6 ? null : Direction.byId(i)); } } /** * @return The current capacity of this storage. */ public abstract long getCapacity(); /** * @return The maximum amount of energy that can be inserted in a single operation from the passed side. */ public abstract long getMaxInsert(@Nullable Direction side); /** * @return The maximum amount of energy that can be extracted in a single operation from the passed side. */ public abstract long getMaxExtract(@Nullable Direction side); /** * @return An {@link EnergyStorage} implementation for the passed side. */ public EnergyStorage getSideStorage(@Nullable Direction side) { return sideStorages[side == null ? 6 : side.getId()]; } public long getAmount() { return 0; } public void setAmount(long v) { } @Override protected Long createSnapshot() { return getAmount(); } @Override protected void readSnapshot(Long snapshot) { setAmount(snapshot); } private class SideStorage implements EnergyStorage { private final Direction side; private SideStorage(Direction side) { this.side = side; } @Override public boolean supportsInsertion() { return getMaxInsert(side) > 0; } @Override public long insert(long maxAmount, TransactionContext transaction) { StoragePreconditions.notNegative(maxAmount); long inserted = Math.min(getMaxInsert(side), Math.min(maxAmount, getCapacity() - getAmount())); if (inserted > 0) { updateSnapshots(transaction); setAmount(getAmount() + inserted); return inserted; } return 0; } @Override public boolean supportsExtraction() { return getMaxExtract(side) > 0; } @Override public long extract(long maxAmount, TransactionContext transaction) { StoragePreconditions.notNegative(maxAmount); long extracted = Math.min(getMaxExtract(side), Math.min(maxAmount, getAmount())); if (extracted > 0) { updateSnapshots(transaction); setAmount(getAmount() - extracted); return extracted; } return 0; } @Override public long getAmount() { return IREnergyStorage.this.getAmount(); } @Override public long getCapacity() { return IREnergyStorage.this.getCapacity(); } } } ================================================ FILE: src/main/java/me/steven/indrev/WCustomTabPanel.java ================================================ /* Original code by Juuxel, part of https://github.com/CottonMC/LibGui MIT License Copyright (c) 2018 The Cotton Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package me.steven.indrev; import io.github.cottonmc.cotton.gui.client.BackgroundPainter; import io.github.cottonmc.cotton.gui.client.LibGui; import io.github.cottonmc.cotton.gui.client.ScreenDrawing; import io.github.cottonmc.cotton.gui.widget.*; import io.github.cottonmc.cotton.gui.widget.data.Axis; import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment; import io.github.cottonmc.cotton.gui.widget.data.InputResult; import io.github.cottonmc.cotton.gui.widget.icon.Icon; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.sound.PositionedSoundInstance; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.Consumer; public class WCustomTabPanel extends WPanel { private static final int TAB_PADDING = 4; private static final int TAB_WIDTH = 28; private static final int TAB_HEIGHT = 25; private static final int PANEL_PADDING = 8; // The padding of BackgroundPainter.VANILLA private static final int ICON_SIZE = 16; private final WBox tabRibbon = new WBox(Axis.HORIZONTAL).setSpacing(1); private final List tabWidgets = new ArrayList<>(); private final WCardPanel mainPanel = new WCardPanel(); /** * Constructs a new tab panel. */ public WCustomTabPanel() { add(tabRibbon, 0, 0); add(mainPanel, PANEL_PADDING, TAB_HEIGHT + PANEL_PADDING); } public void add(WWidget widget, int x, int y) { children.add(widget); widget.setParent(this); widget.setLocation(x, y); expandToFit(widget); } @Override public WPanel setBackgroundPainter(BackgroundPainter painter) { return super.setBackgroundPainter( BackgroundPainter.createLightDarkVariants( BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/panel_light.png")).setPadding(0).setTopPadding(-25), BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/panel_dark.png")).setPadding(8).setTopPadding(-25) )); } // dont bother it's just bad code ik public WPanel setForceBackgroundPainter(BackgroundPainter painter) { return super.setBackgroundPainter(painter); } /** * Adds a tab to this panel. * * @param tab the added tab */ public void add(WCustomTabPanel.Tab tab) { WTab tabWidget = new WTab(tab); if (tabWidgets.isEmpty()) { tabWidget.selected = true; } tabWidgets.add(tabWidget); tabRibbon.add(tabWidget, TAB_WIDTH, TAB_HEIGHT + TAB_PADDING); mainPanel.add(tab.getWidget()); } /** * Configures and adds a tab to this panel. * * @param widget the contained widget * @param configurator the tab configurator */ public void add(WWidget widget, Consumer configurator) { WCustomTabPanel.Tab.Builder builder = new WCustomTabPanel.Tab.Builder(widget); configurator.accept(builder); add(builder.build()); } @Override public void setSize(int x, int y) { super.setSize(x, y); tabRibbon.setSize(x, TAB_HEIGHT); } @Environment(EnvType.CLIENT) @Override public void addPainters() { super.addPainters(); } /** * The data of a tab. */ public static class Tab { private final Text title; private final Icon icon; private final WWidget widget; private final Consumer tooltip; /** * Constructs a tab. * * @param title the tab title * @param icon the tab icon * @param widget the widget contained in the tab * @param tooltip the tab tooltip * @throws IllegalArgumentException if both the title and the icon are null * @throws NullPointerException if either the widget or the tooltip is null */ public Tab(Text title, Icon icon, WWidget widget, Consumer tooltip) { if (title == null && icon == null) { throw new IllegalArgumentException("A tab must have a title or an icon"); } this.title = title; this.icon = icon; this.widget = Objects.requireNonNull(widget, "widget"); this.tooltip = tooltip; } /** * Gets the title of this tab. * * @return the title, or null if there's no title */ public Text getTitle() { return title; } /** * Gets the icon of this tab. * * @return the icon, or null if there's no title */ public Icon getIcon() { return icon; } /** * Gets the contained widget of this tab. * * @return the contained widget */ public WWidget getWidget() { return widget; } /** * Adds this widget's tooltip to the {@code tooltip} builder. * * @param tooltip the tooltip builder */ public void addTooltip(TooltipBuilder tooltip) { if (this.tooltip != null) this.tooltip.accept(tooltip); } /** * A builder for tab data. */ public static final class Builder { @Nullable private Text title; @Nullable private Icon icon; private final WWidget widget; private final List tooltip = new ArrayList<>(); /** * Constructs a new tab data builder. * * @param widget the contained widget * @throws NullPointerException if the widget is null */ public Builder(WWidget widget) { this.widget = Objects.requireNonNull(widget, "widget"); } /** * Sets the tab title. * * @param title the new title * @return this builder * @throws NullPointerException if the title is null */ public Builder title(Text title) { this.title = Objects.requireNonNull(title, "title"); return this; } /** * Sets the tab icon. * * @param icon the new icon * @return this builder * @throws NullPointerException if the icon is null */ public Builder icon(Icon icon) { this.icon = Objects.requireNonNull(icon, "icon"); return this; } /** * Adds lines to the tab's tooltip. * * @param lines the added lines * @return this builder * @throws NullPointerException if the line array is null */ public Builder tooltip(Text... lines) { Objects.requireNonNull(lines, "lines"); Collections.addAll(tooltip, lines); return this; } /** * Adds lines to the tab's tooltip. * * @param lines the added lines * @return this builder * @throws NullPointerException if the line collection is null */ public Builder tooltip(Collection lines) { Objects.requireNonNull(lines, "lines"); tooltip.addAll(lines); return this; } /** * Builds a tab from this builder. * * @return the built tab */ public Tab build() { Consumer tooltip = null; if (!this.tooltip.isEmpty()) { //noinspection Convert2Lambda tooltip = new Consumer() { @Environment(EnvType.CLIENT) @Override public void accept(TooltipBuilder builder) { builder.add(WCustomTabPanel.Tab.Builder.this.tooltip.toArray(new Text[0])); } }; } return new Tab(title, icon, widget, tooltip); } } } private final class WTab extends WWidget { private final WCustomTabPanel.Tab data; boolean selected = false; WTab(WCustomTabPanel.Tab data) { this.data = data; } @Override public boolean canFocus() { return true; } @Environment(EnvType.CLIENT) @Override public InputResult onClick(int x, int y, int button) { super.onClick(x, y, button); MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F)); for (WTab tab : tabWidgets) { tab.selected = (tab == this); } mainPanel.setSelectedCard(data.getWidget()); WCustomTabPanel.this.layout(); return InputResult.PROCESSED; } @Environment(EnvType.CLIENT) @Override public void onKeyPressed(int ch, int key, int modifiers) { if (isActivationKey(ch)) { onClick(0, 0, 0); } } @Environment(EnvType.CLIENT) @Override public void paint(MatrixStack matrices, int x, int y, int mouseX, int mouseY) { TextRenderer renderer = MinecraftClient.getInstance().textRenderer; Text title = data.getTitle(); Icon icon = data.getIcon(); if (title != null) { int width = TAB_WIDTH + renderer.getWidth(title); if (icon == null) width = Math.max(TAB_WIDTH, width - ICON_SIZE); if (this.width != width) { setSize(width, this.height); getParent().layout(); } } (selected ? WCustomTabPanel.Painters.SELECTED_TAB : WCustomTabPanel.Painters.UNSELECTED_TAB).paintBackground(matrices, x, y, this); if (isFocused()) { (selected ? WCustomTabPanel.Painters.SELECTED_TAB_FOCUS_BORDER : Painters.UNSELECTED_TAB_FOCUS_BORDER).paintBackground(matrices, x, y, this); } int iconX = 6; if (title != null) { int titleX = (icon != null) ? iconX + ICON_SIZE + 1 : 0; int titleY = (height - TAB_PADDING - renderer.fontHeight) / 2 + 1; int width = (icon != null) ? this.width - iconX - ICON_SIZE : this.width; HorizontalAlignment align = (icon != null) ? HorizontalAlignment.LEFT : HorizontalAlignment.CENTER; int color; if (LibGui.isDarkMode()) { color = WLabel.DEFAULT_DARKMODE_TEXT_COLOR; } else { color = selected ? WLabel.DEFAULT_TEXT_COLOR : 0xEEEEEE; } ScreenDrawing.drawString(matrices, title.asOrderedText(), align, x + titleX, y + titleY, width, color); } if (icon != null) { icon.paint(matrices, x + iconX, (y + (height - TAB_PADDING - ICON_SIZE) / 2) + 1, ICON_SIZE); } } @Override public void addTooltip(TooltipBuilder tooltip) { data.addTooltip(tooltip); } } /** * Internal background painter instances for tabs. */ @Environment(EnvType.CLIENT) final static class Painters { static final BackgroundPainter SELECTED_TAB = BackgroundPainter.createLightDarkVariants( BackgroundPainter.createNinePatch(new Identifier("indrev", "textures/gui/selected_light.png")).setTopPadding(2), BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/selected_dark.png")).setTopPadding(2) ); static final BackgroundPainter UNSELECTED_TAB = BackgroundPainter.createLightDarkVariants( BackgroundPainter.createNinePatch(new Identifier("indrev", "textures/gui/unselected_light.png")), BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/unselected_dark.png")) ); static final BackgroundPainter SELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/focus.png")).setTopPadding(2); static final BackgroundPainter UNSELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier("libgui", "textures/widget/tab/focus.png")); } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/aprilfools/AprilFoolsMixinConfigPlugin.java ================================================ package me.steven.indrev.mixin.aprilfools; import me.steven.indrev.AprilFools; import net.fabricmc.api.EnvType; import net.fabricmc.loader.api.FabricLoader; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; import java.util.ArrayList; import java.util.List; import java.util.Set; public class AprilFoolsMixinConfigPlugin implements IMixinConfigPlugin { private static final ArrayList mixins = new ArrayList<>(); static { mixins.add("aprilfools.MixinTranslatableText"); } @Override public void onLoad(String mixinPackage) { } @Override public String getRefMapperConfig() { return null; } @Override public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { return true; } @Override public void acceptTargets(Set myTargets, Set otherTargets) { } @Override public List getMixins() { return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT && AprilFools.isToday() ? mixins : null; } @Override public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/aprilfools/MixinTranslatableText.java ================================================ package me.steven.indrev.mixin.aprilfools; import me.steven.indrev.IndustrialRevolution; import net.minecraft.text.TranslatableText; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyVariable; @Mixin(TranslatableText.class) public class MixinTranslatableText { @Shadow @Final private String key; @ModifyVariable(name = "string", at = @At("STORE"), method = "updateTranslations") private String owo(String original) { if (this.key.contains(IndustrialRevolution.MOD_ID)) { if (!original.contains(".")) { String[] words = original.split(" "); for (int i = 0; i < words.length; i++) { if (words[i].endsWith("ger") || words[i].endsWith("der")) words[i] = words[i].substring(0, words[i].length() - 2) + "ah"; else if (words[i].endsWith("er") || words[i].endsWith("re")) words[i] = words[i].substring(0, words[i].length() - 2) + 'y'; else if (words[i].endsWith("xe")) words[i] = words[i].substring(0, words[i].length() - 2) + "xie"; else if (words[i].endsWith("et")) words[i] = words[i].substring(0, words[i].length() - 2) + "ie"; words[i] = words[i].replaceFirst("est", "ess"); // Chest -> Chess words[i] = words[i].replaceFirst("O", "Ow").replaceFirst("o", "ow"); } return String.join(" ", words); } } return original; } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/client/MixinBuiltChunk.java ================================================ package me.steven.indrev.mixin.client; import me.steven.indrev.blockentities.GlobalStateController; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.chunk.ChunkBuilder; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ChunkBuilder.BuiltChunk.class) public abstract class MixinBuiltChunk { @Shadow public abstract BlockPos getOrigin(); @Inject(method = "rebuild", at = @At("INVOKE")) private void indrev_removeBuiltChunk(CallbackInfo ci) { long chunkPos = ChunkPos.toLong(getOrigin().getX() >> 4, getOrigin().getZ() >> 4); MinecraftClient.getInstance().execute(() -> GlobalStateController.INSTANCE.getChunksToUpdate().remove(chunkPos)); } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/client/MixinClientPlayerInteractionManager.java ================================================ package me.steven.indrev.mixin.client; import me.steven.indrev.registry.IRItemRegistry; import net.minecraft.client.network.ClientPlayerInteractionManager; import net.minecraft.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(ClientPlayerInteractionManager.class) public class MixinClientPlayerInteractionManager { @Redirect(method = "isCurrentlyBreaking", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;areNbtEqual(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Z")) private boolean indrev_fixGamerAxe(ItemStack left, ItemStack right) { if (left.getItem() == IRItemRegistry.INSTANCE.getGAMER_AXE_ITEM() && left.getItem() == right.getItem()) return true; return ItemStack.areNbtEqual(left, right); } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/client/MixinGameRenderer.java ================================================ package me.steven.indrev.mixin.client; import me.steven.indrev.api.IRPlayerEntityExtension; import me.steven.indrev.tools.modular.ArmorModule; import net.minecraft.client.render.GameRenderer; import net.minecraft.entity.LivingEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(value = GameRenderer.class, priority = 999) public class MixinGameRenderer { @Inject(method = "getNightVisionStrength", at = @At("INVOKE"), cancellable = true) private static void indrev_nightVisionStrength(LivingEntity livingEntity, float f, CallbackInfoReturnable cir) { if (livingEntity instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) livingEntity).isApplied(ArmorModule.NIGHT_VISION)) { cir.setReturnValue(1f); } } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/client/MixinItemRenderer.java ================================================ package me.steven.indrev.mixin.client; import com.mojang.blaze3d.systems.RenderSystem; import me.steven.indrev.items.armor.IRModularArmorItem; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ItemRenderer.class) public abstract class MixinItemRenderer { @Shadow protected abstract void renderGuiQuad(BufferBuilder buffer, int x, int y, int width, int height, int red, int green, int blue, int alpha); @Inject( method = "renderGuiItemOverlay(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At( value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemBarVisible()Z", shift = At.Shift.BEFORE ) ) private void indrev_renderModularArmorFluidTank(TextRenderer renderer, ItemStack stack, int x, int y, String countLabel, CallbackInfo ci) { if (stack.getItem() instanceof IRModularArmorItem armor && armor.isFluidItemBarVisible(stack)) { RenderSystem.disableDepthTest(); RenderSystem.disableTexture(); RenderSystem.disableBlend(); Tessellator tessellator = Tessellator.getInstance(); BufferBuilder bufferBuilder = tessellator.getBuffer(); int i = armor.getFluidItemBarStep(stack); int j = armor.getFluidItemBarColor(stack); this.renderGuiQuad(bufferBuilder, x + 2, y + 11, 13, 2, 0, 0, 0, 255); this.renderGuiQuad(bufferBuilder, x + 2, y + 11, i, 1, j >> 16 & 255, j >> 8 & 255, j & 255, 255); RenderSystem.enableBlend(); RenderSystem.enableTexture(); RenderSystem.enableDepthTest(); } } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/client/MixinLivingEntityClient.java ================================================ package me.steven.indrev.mixin.client; import me.steven.indrev.api.IRPlayerEntityExtension; import me.steven.indrev.tools.modular.ArmorModule; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.effect.StatusEffect; import net.minecraft.entity.effect.StatusEffects; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(LivingEntity.class) public abstract class MixinLivingEntityClient { @Inject(method = "hasStatusEffect", at = @At("INVOKE"), cancellable = true) private void indrev_nightVision(StatusEffect effect, CallbackInfoReturnable cir) { if ( effect.equals(StatusEffects.NIGHT_VISION) && this instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) this).isApplied(ArmorModule.NIGHT_VISION) ) { cir.setReturnValue(true); } } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/client/MixinMinecraftClient.java ================================================ package me.steven.indrev.mixin.client; import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler; import me.steven.indrev.packets.client.GuiPropertySyncPacket; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider; import net.minecraft.network.PacketByteBuf; import net.minecraft.screen.ScreenHandler; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(MinecraftClient.class) public class MixinMinecraftClient { @Inject(method = "setScreen", at = @At("RETURN")) private void indrev_requestProperties(Screen screen, CallbackInfo ci) { if (screen instanceof ScreenHandlerProvider provider) { ScreenHandler handler = provider.getScreenHandler(); if (handler instanceof IRGuiScreenHandler) { PacketByteBuf buf = PacketByteBufs.create(); buf.writeInt(handler.syncId); ClientPlayNetworking.send(GuiPropertySyncPacket.INSTANCE.getC2S_REQUEST_PROPERTIES(), buf); } } } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/client/MixinWItemSlot.java ================================================ package me.steven.indrev.mixin.client; import io.github.cottonmc.cotton.gui.client.BackgroundPainter; import io.github.cottonmc.cotton.gui.widget.WItemSlot; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = WItemSlot.class, remap = false) public class MixinWItemSlot { @Shadow @Nullable private BackgroundPainter backgroundPainter; @Inject(method = "addPainters", at = @At("HEAD"), cancellable = true, remap = false) private void indrev_dontOverridePainters(CallbackInfo ci) { if (backgroundPainter != null) ci.cancel(); } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinAbstractCookingRecipe.java ================================================ package me.steven.indrev.mixin.common; import alexiil.mc.lib.attributes.fluid.volume.FluidVolume; import me.steven.indrev.components.CraftingComponent; import me.steven.indrev.recipes.machines.IRRecipe; import me.steven.indrev.recipes.machines.entries.InputEntry; import me.steven.indrev.recipes.machines.entries.OutputEntry; import me.steven.indrev.utils.IRFluidTank; import net.minecraft.item.ItemStack; import net.minecraft.recipe.AbstractCookingRecipe; import net.minecraft.recipe.Ingredient; import net.minecraft.recipe.Recipe; import net.minecraft.recipe.RecipeType; import net.minecraft.util.Identifier; import net.minecraft.util.collection.DefaultedList; import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.Collections; import java.util.List; import java.util.Random; @Mixin(AbstractCookingRecipe.class) public abstract class MixinAbstractCookingRecipe implements IRRecipe { @Shadow @Final protected Identifier id; @Shadow @Final protected int cookTime; @Shadow @Final protected ItemStack output; @Shadow @Final protected Ingredient input; private InputEntry[] indrev_inputEntries; private OutputEntry[] indrev_outputEntries; @Inject(method = "", at = @At("TAIL")) private void a(RecipeType type, Identifier id, String group, Ingredient input, ItemStack output, float experience, int cookTime, CallbackInfo ci) { this.indrev_inputEntries = new InputEntry[] { new InputEntry(input, 1) }; this.indrev_outputEntries = new OutputEntry[] { new OutputEntry(output, 1d) }; } @NotNull @Override public Identifier getIdentifier() { return id; } @NotNull @Override public InputEntry @NotNull [] getInput() { return indrev_inputEntries; } @NotNull @Override public OutputEntry @NotNull [] getOutputs() { return indrev_outputEntries; } @Override public int getTicks() { return cookTime; } @Override public boolean matches(@NotNull List inv, @NotNull List fluidVolume) { return this.input.test(inv.get(0)); } @NotNull @Override public List craft(Random random) { return Collections.singletonList(output.copy()); } @NotNull @Override public Identifier getId() { return id; } @Override public boolean isIgnoredInRecipeBook() { return false; } @NotNull @Override public DefaultedList getIngredients() { DefaultedList defaultedList = DefaultedList.of(); defaultedList.add(this.input); return defaultedList; } @Override public boolean isEmpty() { DefaultedList defaultedList = this.getIngredients(); return defaultedList.isEmpty() || defaultedList.stream().anyMatch((ingredient) -> ingredient.getMatchingStacks().length == 0); } @Override public boolean canStart(@NotNull CraftingComponent component) { return component.fits(output); } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinEnchantmentHelper.java ================================================ package me.steven.indrev.mixin.common; import me.steven.indrev.api.CustomEnchantmentProvider; import me.steven.indrev.api.IRPlayerEntityExtension; import me.steven.indrev.tools.modular.ArmorModule; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(EnchantmentHelper.class) public class MixinEnchantmentHelper { @Inject(method = "getLevel", at = @At("HEAD"), cancellable = true) private static void indrev_customEnchantProvider(Enchantment enchantment, ItemStack stack, CallbackInfoReturnable cir) { if (stack.getItem() instanceof CustomEnchantmentProvider) { int level = ((CustomEnchantmentProvider) stack.getItem()).getLevel(enchantment, stack); if (level > -1) cir.setReturnValue(level); } } @Inject(method = "hasAquaAffinity", at = @At("HEAD"), cancellable = true) private static void indrev_waterAffinityChest(LivingEntity entity, CallbackInfoReturnable cir) { //specifically checks for water affinity on chestplate if (entity instanceof PlayerEntity player && ArmorModule.WATER_AFFINITY.getLevel(player.getInventory().getArmorStack(EquipmentSlot.CHEST.getEntitySlotId())) > 0 && entity instanceof IRPlayerEntityExtension ext && ext.isApplied(ArmorModule.WATER_AFFINITY) ) cir.setReturnValue(true); } @Inject(method = "getDepthStrider", at = @At("HEAD"), cancellable = true) private static void indrev_waterAffinityLegs(LivingEntity entity, CallbackInfoReturnable cir) { //specifically checks for water affinity on leggings if (entity instanceof PlayerEntity player && ArmorModule.WATER_AFFINITY.getLevel(player.getInventory().getArmorStack(EquipmentSlot.LEGS.getEntitySlotId())) > 0 && entity instanceof IRPlayerEntityExtension ext && ext.isApplied(ArmorModule.WATER_AFFINITY) ) cir.setReturnValue(3); } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinEntity.java ================================================ package me.steven.indrev.mixin.common; import me.steven.indrev.api.IREntityExtension; import me.steven.indrev.api.IRPlayerEntityExtension; import me.steven.indrev.inventories.IRInventory; import me.steven.indrev.tools.modular.ArmorModule; import net.minecraft.entity.Entity; import net.minecraft.entity.ItemEntity; import net.minecraft.item.ItemStack; import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(Entity.class) public abstract class MixinEntity implements IREntityExtension { @Shadow public World world; @Shadow public abstract double getX(); @Shadow public abstract double getY(); @Shadow public abstract double getZ(); private IRInventory machineInv = null; @Inject(method = "setAir", at = @At("INVOKE"), cancellable = true) private void indrev_breathingModule(CallbackInfo ci) { if (this instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) this).isApplied(ArmorModule.BREATHING)) { ci.cancel(); } } @Inject(method = "getJumpVelocityMultiplier", at = @At(value = "RETURN"), cancellable = true) private void indrev_jumpBoostModule(CallbackInfoReturnable cir) { if (this instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) this).isApplied(ArmorModule.JUMP_BOOST)) { cir.setReturnValue((float) ((IRPlayerEntityExtension) this).getAppliedLevel(ArmorModule.JUMP_BOOST)); } } @Inject(method = "dropStack(Lnet/minecraft/item/ItemStack;F)Lnet/minecraft/entity/ItemEntity;", at = @At("HEAD"), cancellable = true) private void indrev_onDropItem(ItemStack stack, float yOffset, CallbackInfoReturnable cir) { if (!world.isClient && machineInv != null) { if (!machineInv.output(stack)) { ItemEntity itemEntity = new ItemEntity(world, getX(), getY() + yOffset, getZ(), stack); itemEntity.setToDefaultPickupDelay(); this.world.spawnEntity(itemEntity); } cir.cancel(); } } @Nullable @Override public IRInventory getMachineInv() { return machineInv; } @Override public void setMachineInv(IRInventory machineInv) { this.machineInv = machineInv; } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinItemPredicate.java ================================================ package me.steven.indrev.mixin.common; import me.steven.indrev.api.CustomEnchantmentProvider; import net.minecraft.item.ItemStack; import net.minecraft.predicate.item.EnchantmentPredicate; import net.minecraft.predicate.item.ItemPredicate; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(ItemPredicate.class) public class MixinItemPredicate { @Shadow @Final private EnchantmentPredicate[] enchantments; @Inject( method = "test", slice = @Slice( from = @At(value = "INVOKE", target = "Lnet/minecraft/predicate/item/EnchantmentPredicate;test(Ljava/util/Map;)Z") ), at = @At(value = "RETURN"), cancellable = true) private void indrev_customEnchantProvider(ItemStack stack, CallbackInfoReturnable cir) { if (stack.getItem() instanceof CustomEnchantmentProvider && enchantments.length > 0) { for (EnchantmentPredicate predicate : enchantments) { if (predicate.levels != null && predicate.enchantment != null) { int level = ((CustomEnchantmentProvider) stack.getItem()).getLevel(predicate.enchantment, stack); if (level > -1 && predicate.levels.test(level)) cir.setReturnValue(true); } } } } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinItemStack.java ================================================ package me.steven.indrev.mixin.common; import com.google.common.collect.Multimap; import me.steven.indrev.api.AttributeModifierProvider; import me.steven.indrev.items.energy.IREnergyItem; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.attribute.EntityAttribute; import net.minecraft.entity.attribute.EntityAttributeModifier; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.server.network.ServerPlayerEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Random; @Mixin(ItemStack.class) public abstract class MixinItemStack { @Shadow public abstract Item getItem(); @Inject(method = "getAttributeModifiers", at = @At("TAIL"), cancellable = true) private void indrev_modifiableAttributeModifiers(EquipmentSlot equipmentSlot, CallbackInfoReturnable> cir) { ItemStack stack = (ItemStack) (Object) this; if (stack.getItem() instanceof AttributeModifierProvider) { cir.setReturnValue(((AttributeModifierProvider) stack.getItem()).getAttributeModifiers(stack, equipmentSlot)); } } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinLivingEntity.java ================================================ package me.steven.indrev.mixin.common; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(LivingEntity.class) public abstract class MixinLivingEntity extends Entity { public MixinLivingEntity(EntityType type, World world) { super(type, world); } /* @Inject(method = "knockDownwards", at = @At("HEAD"), cancellable = true) private void indrev_waterAffinityDownwards(CallbackInfo ci) { this.setVelocity(this.getVelocity().add(0.0D, -0.03999999910593033D * 2, 0.0D)); ci.cancel(); } @Inject(method = "swimUpward", at = @At("HEAD"), cancellable = true) private void indrev_waterAffinityUpwards(CallbackInfo ci) { this.setVelocity(this.getVelocity().add(0.0D, 0.03999999910593033D * 2, 0.0D)); ci.cancel(); }*/ } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinPiglinBrain.java ================================================ package me.steven.indrev.mixin.common; import me.steven.indrev.api.IRPlayerEntityExtension; import me.steven.indrev.tools.modular.ArmorModule; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.mob.PiglinBrain; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(PiglinBrain.class) public class MixinPiglinBrain { @Inject(method = "wearsGoldArmor", at = @At("RETURN"), cancellable = true) private static void indrev_hasPiglinTrickerModule(LivingEntity entity, CallbackInfoReturnable cir) { if (entity instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) entity).isApplied(ArmorModule.PIGLIN_TRICKER)) { cir.setReturnValue(true); } } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinPlayerEntity.java ================================================ package me.steven.indrev.mixin.common; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import me.steven.indrev.api.IRPlayerEntityExtension; import me.steven.indrev.items.energy.IREnergyItem; import me.steven.indrev.items.energy.IRGamerAxeItem; import me.steven.indrev.tools.modular.ArmorModule; import me.steven.indrev.utils.EnergyutilsKt; import net.minecraft.block.BlockState; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.world.World; import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import team.reborn.energy.api.EnergyStorage; import java.util.Iterator; import java.util.Map; @Mixin(PlayerEntity.class) public abstract class MixinPlayerEntity extends LivingEntity implements IRPlayerEntityExtension { private boolean indrev_regenerating = false; private double indrev_shield = 0.0; private final Object2IntMap appliedEffects = new Object2IntOpenHashMap<>(); protected MixinPlayerEntity(EntityType entityType, World world) { super(entityType, world); } @Inject(method = "getBlockBreakingSpeed", at = @At("HEAD"), cancellable = true) private void indrev_checkEnergyTool(BlockState block, CallbackInfoReturnable cir) { PlayerEntity player = (PlayerEntity) (Object) this; PlayerInventory inventory = player.getInventory(); ItemStack itemStack = inventory.main.get(inventory.selectedSlot); Item item = itemStack.getItem(); EnergyStorage itemIo = EnergyutilsKt.energyOf(itemStack); if (itemIo != null && item instanceof IREnergyItem) { long amount = itemIo.getAmount(); if (item instanceof IRGamerAxeItem) { NbtCompound tag = itemStack.getOrCreateNbt(); if (tag.contains("Active") && !tag.getBoolean("Active")) { cir.setReturnValue(0.2F); return; } } if (amount < 1) cir.setReturnValue(0.2F); } } @Inject(method = "writeCustomDataToNbt", at = @At("RETURN")) private void indrev_writeShieldToPlayerTag(NbtCompound tag, CallbackInfo ci) { tag.putDouble("indrev:shield", indrev_shield); } @Inject(method = "readCustomDataFromNbt", at = @At("RETURN")) private void indrev_readShieldToPlayerTag(NbtCompound tag, CallbackInfo ci) { indrev_shield = tag.getDouble("indrev:shield"); } @Override public double getShieldDurability() { return indrev_shield; } @Override public void setShieldDurability(double shieldDurability) { this.indrev_shield = shieldDurability; } @Override public double getMaxShieldDurability() { Iterator iterator = getArmorItems().iterator(); double shield = 0.0; while (iterator.hasNext()) { ItemStack next = iterator.next(); shield += ArmorModule.PROTECTION.getLevel(next) * 25; } return shield; } @Override public @NotNull Map getAppliedModules() { return appliedEffects; } @Override public boolean isApplied(@NotNull ArmorModule module) { return appliedEffects.containsKey(module); } @Override public void applyModule(@NotNull ArmorModule module, int level) { appliedEffects.put(module, level); } @Override public int getAppliedLevel(@NotNull ArmorModule module) { return appliedEffects.getOrDefault(module, 0); } @Override public void setRegenerating(boolean isRegenerating) { this.indrev_regenerating = isRegenerating; } @Override public boolean isRegenerating() { return indrev_regenerating; } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinPlayerInventory.java ================================================ package me.steven.indrev.mixin.common; import me.steven.indrev.items.armor.JetpackHandler; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(PlayerInventory.class) public abstract class MixinPlayerInventory { @Shadow public abstract ItemStack getArmorStack(int slot); @Shadow @Final public PlayerEntity player; @Inject(method = "updateItems", at = @At("TAIL")) private void indrev_tickJetpack(CallbackInfo ci) { ItemStack armorStack = getArmorStack(EquipmentSlot.CHEST.getEntitySlotId()); if (armorStack.getItem() instanceof JetpackHandler handler && handler.isUsable(armorStack)) { handler.tickJetpack(armorStack, player); } } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinServerPlayerEntity.java ================================================ package me.steven.indrev.mixin.common; import com.mojang.authlib.GameProfile; import io.netty.buffer.Unpooled; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import me.steven.indrev.api.IRServerPlayerEntityExtension; import me.steven.indrev.items.armor.IRModularArmorItem; import me.steven.indrev.items.energy.IRPortableChargerItem; import me.steven.indrev.packets.client.SyncAppliedModulesPacket; import me.steven.indrev.tools.modular.ArmorModule; import me.steven.indrev.utils.AccessorextensionsKt; import me.steven.indrev.utils.EnergyutilsKt; import me.steven.indrev.utils.HelperextensionsKt; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.minecraft.entity.ExperienceOrbEntity; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.HungerManager; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.FoodComponent; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketByteBuf; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvents; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import team.reborn.energy.api.EnergyStorage; import java.util.List; import java.util.Map; @Mixin(ServerPlayerEntity.class) public abstract class MixinServerPlayerEntity extends PlayerEntity implements IRServerPlayerEntityExtension { @Shadow public abstract boolean isInvulnerableTo(DamageSource damageSource); @Shadow public abstract void playSound(SoundEvent event, SoundCategory category, float volume, float pitch); private int ticks = 0; private int lastDamageTick = 0; private float lastDmg = 0f; private double lastShield = 0.0; private final Object2IntMap oldAppliedModules = new Object2IntOpenHashMap<>(); public MixinServerPlayerEntity(World world, BlockPos pos, float yaw, GameProfile profile) { super(world, pos, yaw, profile); } @Inject(method = "tick", at = @At("TAIL")) private void indrev_applyEffects(CallbackInfo ci) { ticks++; if (ticks % 15 == 0) { applyArmorEffects(); } indrev_tickMagnet(); setShieldDurability(Math.min(getShieldDurability(), getMaxShieldDurability())); } @ModifyVariable(method = "damage(Lnet/minecraft/entity/damage/DamageSource;F)Z", at = @At("HEAD"), argsOnly = true) private float indrev_absorbDamage(float amount, DamageSource source) { final float initial = amount; if (isInvulnerableTo(source)) return amount; if (lastDamageTick + 10 > ticks) { if (amount <= lastDmg) return 0f; amount = amount - lastDmg; } lastDamageTick = ticks; lastDmg = initial; if (shouldApplyToShield(source)) { float leftover = (float) applyDamageToShield(amount); if (amount > leftover) world.playSoundFromEntity(null, this, SoundEvents.BLOCK_CHAIN_BREAK, SoundCategory.PLAYERS, 1f, 0.0001f); return leftover; } else return amount; } @Inject(method = "worldChanged", at = @At("TAIL")) private void indrev_syncOnDimChange(ServerWorld origin, CallbackInfo ci) { sync(); AccessorextensionsKt.getFluidNetworkState(origin).onDimChange(this); AccessorextensionsKt.getItemNetworkState(origin).onDimChange(this); } private boolean shouldApplyToShield(DamageSource source) { if (source.equals(DamageSource.FALL)) return isApplied(ArmorModule.FEATHER_FALLING); else if (source.isFire()) return isApplied(ArmorModule.FIRE_RESISTANCE); else return !source.equals(DamageSource.STARVE) && !source.equals(DamageSource.DROWN); } private void applyArmorEffects() { ServerPlayerEntity player = (ServerPlayerEntity) (Object) this; PlayerInventory inventory = player.getInventory(); getAppliedModules().clear(); for (int i = 0; i < inventory.armor.size(); i++) { int cSlot = 36+i; ItemStack itemStack = inventory.getStack(cSlot); if (itemStack.getItem() instanceof IRModularArmorItem) { List modules = ((IRModularArmorItem) itemStack.getItem()).getInstalled(itemStack); for (ArmorModule module : modules) { int level = module.getLevel(itemStack); if (level <= 0) continue; switch (module) { case SPEED: case BREATHING: case JUMP_BOOST: case NIGHT_VISION: case FIRE_RESISTANCE: case PIGLIN_TRICKER: case FEATHER_FALLING: case WATER_AFFINITY: if (EnergyutilsKt.extract(inventory, cSlot, 20)) applyModule(module, level); break; case AUTO_FEEDER: HungerManager hunger = player.getHungerManager(); if (hunger.isNotFull()) { for (int slot = 0; slot <= inventory.size(); slot++) { ItemStack stack = inventory.getStack(slot); FoodComponent food = stack.getItem().getFoodComponent(); if (food != null && !food.isAlwaysEdible() && !HelperextensionsKt.hasNegativeEffects(food) && food.getHunger() <= 20 - hunger.getFoodLevel() && EnergyutilsKt.extract(inventory, cSlot, 30)) { stack.finishUsing(world, player); player.eatFood(world, stack); } if (!hungerManager.isNotFull()) break; } } break; case CHARGER: IRPortableChargerItem.Companion.chargeItemsInInv(cSlot, player.getInventory()); break; case SOLAR_PANEL: if (world.isDay() && world.isSkyVisible(player.getBlockPos().up(2))) { for (int x = 0; x < 4; x++) { EnergyutilsKt.insert(inventory, cSlot - x, 75L * level); } } break; case PROTECTION: if (ticks - 120 > lastDamageTick && getShieldDurability() < getMaxShieldDurability() && EnergyutilsKt.extract(inventory, cSlot, 30)) { regenerateShield(); } break; default: break; } } } } } private void indrev_tickMagnet() { ServerPlayerEntity player = (ServerPlayerEntity) (Object) this; PlayerInventory inventory = player.getInventory(); for (ItemStack itemStack : inventory.armor) { if (itemStack.getItem() instanceof IRModularArmorItem) { int level = ArmorModule.MAGNET.getLevel(itemStack); if (level > 0) { Vec3i offset = new Vec3i(8, 8, 8); Box area = new Box(getBlockPos().subtract(offset), getBlockPos().add(offset)); Vec3d blockCenter = HelperextensionsKt.toVec3d(getBlockPos()).add(0.5, 0.5, 0.5); world.getOtherEntities(this, area, (entity) -> entity instanceof ItemEntity || entity instanceof ExperienceOrbEntity).forEach(entity -> { if ((entity instanceof ItemEntity itemEntity && !itemEntity.cannotPickup()) || (entity instanceof ExperienceOrbEntity xpEntity && xpEntity.age > 40)) { Vec3d v = entity.getPos().relativize(blockCenter).normalize().multiply(0.2); entity.addVelocity(v.x, v.y, v.z); //applyModule(ArmorModule.MAGNET, 1); } }); return; } } } //getAppliedModules().remove(ArmorModule.MAGNET); } private void regenerateShield() { setShieldDurability(Math.min(getShieldDurability() + 0.5, getMaxShieldDurability())); } private double applyDamageToShield(double damage) { double absorbed = Math.min(damage, getShieldDurability()); setShieldDurability(getShieldDurability() - absorbed); return damage - absorbed; } @Override public boolean shouldSync() { return !oldAppliedModules.equals(getAppliedModules()) || lastShield != getShieldDurability(); } @Override public void sync() { lastShield = getShieldDurability(); oldAppliedModules.clear(); oldAppliedModules.putAll(getAppliedModules()); PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); Map appliedModules = getAppliedModules(); buf.writeInt(appliedModules.size()); appliedModules.forEach((module, level) -> { buf.writeInt(module.ordinal()); buf.writeInt(level); }); buf.writeDouble(getShieldDurability()); buf.writeBoolean(ticks - 120 > lastDamageTick); ServerPlayNetworking.send((ServerPlayerEntity) (Object) this, SyncAppliedModulesPacket.INSTANCE.getSYNC_MODULE_PACKET(), buf); } @Override public boolean isRegenerating() { return ticks - 120 > lastDamageTick; } } ================================================ FILE: src/main/java/me/steven/indrev/mixin/common/MixinServerWorld.java ================================================ package me.steven.indrev.mixin.common; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import me.steven.indrev.api.ServerWorldExtension; import me.steven.indrev.networks.Network; import me.steven.indrev.networks.NetworkState; import me.steven.indrev.networks.ServoNetworkState; import me.steven.indrev.networks.energy.EnergyNetwork; import me.steven.indrev.networks.energy.EnergyNetworkState; import me.steven.indrev.networks.fluid.FluidNetworkState; import me.steven.indrev.networks.item.ItemNetworkState; import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.Direction; import net.minecraft.world.PersistentStateManager; import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import team.reborn.energy.api.EnergyStorage; @Mixin(ServerWorld.class) public abstract class MixinServerWorld implements ServerWorldExtension { private final Long2ObjectOpenHashMap> indrev_energyIoCache = new Long2ObjectOpenHashMap<>(); private ItemNetworkState indrev_itemNetworkState = null; private FluidNetworkState indrev_fluidNetworkState = null; private EnergyNetworkState indrev_energyNetworkState = null; @Shadow public abstract PersistentStateManager getPersistentStateManager(); @NotNull @Override public Long2ObjectOpenHashMap> indrev_getEnergyCache() { return indrev_energyIoCache; } @NotNull @Override public EnergyNetworkState indrev_getEnergyNetworkState() { ServerWorld world = (ServerWorld) (Object) this; if (indrev_energyNetworkState == null) { indrev_energyNetworkState = getPersistentStateManager() .getOrCreate( nbt -> EnergyNetworkState.Companion.readNbt(nbt, () -> new EnergyNetworkState(world)), () -> new EnergyNetworkState(world), Network.Type.Companion.getENERGY().getKey()); } return indrev_energyNetworkState; } @NotNull @Override public FluidNetworkState indrev_getFluidNetworkState() { ServerWorld world = (ServerWorld) (Object) this; if (indrev_fluidNetworkState == null) { indrev_fluidNetworkState = getPersistentStateManager() .getOrCreate( nbt -> ServoNetworkState.Companion.readNbt(nbt, () -> new FluidNetworkState(world)), () -> new FluidNetworkState(world), Network.Type.Companion.getFLUID().getKey()); } return indrev_fluidNetworkState; } @NotNull @Override public ItemNetworkState indrev_getItemNetworkState() { ServerWorld world = (ServerWorld) (Object) this; if (indrev_itemNetworkState == null) { indrev_itemNetworkState = getPersistentStateManager() .getOrCreate( nbt -> ItemNetworkState.Companion.readNbt(nbt, () -> new ItemNetworkState(world)), () -> new ItemNetworkState(world), Network.Type.Companion.getITEM().getKey()); } return indrev_itemNetworkState; } } ================================================ FILE: src/main/kotlin/me/steven/indrev/IndustrialRevolution.kt ================================================ package me.steven.indrev import dev.cafeteria.fakeplayerapi.server.FakePlayerBuilder import dev.cafeteria.fakeplayerapi.server.FakeServerPlayer import me.steven.indrev.api.IRServerPlayerEntityExtension import me.steven.indrev.api.machines.Tier import me.steven.indrev.config.IRConfig import me.steven.indrev.datagen.DataGeneratorManager import me.steven.indrev.events.common.IRLootTableCallback import me.steven.indrev.gui.screenhandlers.COAL_GENERATOR_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.networks.NetworkEvents import me.steven.indrev.packets.PacketRegistry import me.steven.indrev.packets.client.SyncConfigPacket import me.steven.indrev.recipes.SelfRemainderRecipe import me.steven.indrev.recipes.machines.* import me.steven.indrev.registry.* import me.steven.indrev.utils.identifier import net.fabricmc.api.ModInitializer import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes import net.fabricmc.fabric.impl.datagen.FabricDataGenHelper import net.fabricmc.loader.api.FabricLoader import net.minecraft.item.Item import net.minecraft.item.ItemGroup import net.minecraft.item.ItemStack import net.minecraft.sound.SoundCategory import net.minecraft.sound.SoundEvent import net.minecraft.tag.TagKey import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger object IndustrialRevolution : ModInitializer { override fun onInitialize() { //load the screenhandlers.kt class COAL_GENERATOR_HANDLER IRConfig IRItemRegistry.registerAll() IRBlockRegistry.registerAll() IRFluidRegistry.registerAll() Registry.register(Registry.SOUND_EVENT, LASER_SOUND_ID, LASER_SOUND_EVENT) Registry.register(Registry.PARTICLE_TYPE, identifier("laser_particle"), LASER_PARTICLE) WorldGeneration.init() WorldGeneration.addFeatures() LootTableLoadingCallback.EVENT.register(IRLootTableCallback) MachineRegistry Registry.register(Registry.RECIPE_SERIALIZER, PulverizerRecipe.IDENTIFIER, PulverizerRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, PulverizerRecipe.IDENTIFIER, PulverizerRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, CompressorRecipe.IDENTIFIER, CompressorRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, CompressorRecipe.IDENTIFIER, CompressorRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, InfuserRecipe.IDENTIFIER, InfuserRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, InfuserRecipe.IDENTIFIER, InfuserRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, FluidInfuserRecipe.IDENTIFIER, FluidInfuserRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, FluidInfuserRecipe.IDENTIFIER, FluidInfuserRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, RecyclerRecipe.IDENTIFIER, RecyclerRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, RecyclerRecipe.IDENTIFIER, RecyclerRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, SmelterRecipe.IDENTIFIER, SmelterRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, SmelterRecipe.IDENTIFIER, SmelterRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, CondenserRecipe.IDENTIFIER, CondenserRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, CondenserRecipe.IDENTIFIER, CondenserRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, DistillerRecipe.IDENTIFIER, DistillerRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, DistillerRecipe.IDENTIFIER, DistillerRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, SawmillRecipe.IDENTIFIER, SawmillRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, SawmillRecipe.IDENTIFIER, SawmillRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, ModuleRecipe.IDENTIFIER, ModuleRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, ModuleRecipe.IDENTIFIER, ModuleRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, LaserRecipe.IDENTIFIER, LaserRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, LaserRecipe.IDENTIFIER, LaserRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, ElectrolysisRecipe.IDENTIFIER, ElectrolysisRecipe.SERIALIZER) Registry.register(Registry.RECIPE_TYPE, ElectrolysisRecipe.IDENTIFIER, ElectrolysisRecipe.TYPE) Registry.register(Registry.RECIPE_SERIALIZER, SelfRemainderRecipe.IDENTIFIER, SelfRemainderRecipe.SERIALIZER) PacketRegistry.registerServer() ServerTickEvents.END_WORLD_TICK.register(NetworkEvents) ServerBlockEntityEvents.BLOCK_ENTITY_LOAD.register(NetworkEvents) ServerLifecycleEvents.SERVER_STOPPING.register(NetworkEvents) ServerPlayConnectionEvents.JOIN.register { handler, _, _ -> val player = handler.player SyncConfigPacket.sendConfig(player) if (player is IRServerPlayerEntityExtension) { (player as IRServerPlayerEntityExtension).sync() } } ServerTickEvents.START_SERVER_TICK.register { server -> server.playerManager.playerList.forEach { player -> if (player is IRServerPlayerEntityExtension && player.shouldSync()) { player.sync() } } } ServerTickEvents.END_SERVER_TICK.register { server -> server.playerManager.playerList.forEach { player -> val currentScreenHandler = player.currentScreenHandler as? IRGuiScreenHandler ?: return@forEach currentScreenHandler.syncProperties() } } ServerLifecycleEvents.END_DATA_PACK_RELOAD.register { s, _, _ -> s.recipeManager.recipes.keys.filterIsInstance>().forEach { it.clearCache() } } if (FabricLoader.getInstance().getLaunchArguments(true).contains("-dataGen")) { FabricDataGenHelper.run() ClientLifecycleEvents.CLIENT_STARTED.register(ClientLifecycleEvents.ClientStarted { DataGeneratorManager("indrev").generate() }) } LOGGER.info("Industrial Revolution has initialized.") } val LOGGER: Logger = LogManager.getLogger("Industrial Revolution") const val MOD_ID = "indrev" val MOD_GROUP: ItemGroup = FabricItemGroupBuilder.build(identifier("indrev_group")) { ItemStack { MachineRegistry.PULVERIZER_REGISTRY.block(Tier.MK4).asItem() } } val COOLERS_TAG: TagKey = TagKey.of(Registry.ITEM_KEY, identifier("coolers")) val WRENCH_TAG: TagKey = TagKey.of(Registry.ITEM_KEY, Identifier("c:wrenches")) val SCREWDRIVER_TAG: TagKey = TagKey.of(Registry.ITEM_KEY, Identifier("c:screwdrivers")) val NIKOLITE_ORES: TagKey = TagKey.of(Registry.ITEM_KEY, Identifier("c:nikolite_ores")) val TIN_ORES: TagKey = TagKey.of(Registry.ITEM_KEY, Identifier("c:tin_ores")) val LEAD_ORES: TagKey = TagKey.of(Registry.ITEM_KEY, Identifier("c:lead_ores")) val SILVER_ORES: TagKey = TagKey.of(Registry.ITEM_KEY, Identifier("c:silver_ores")) val TUNGSTEN_ORES: TagKey = TagKey.of(Registry.ITEM_KEY, Identifier("c:tungsten_ores")) val ANCIENT_DEBRIS_ORES: TagKey = TagKey.of(Registry.ITEM_KEY, Identifier("c:ancient_debris_ores")) val LASER_SOUND_ID = identifier("laser") val LASER_SOUND_EVENT = SoundEvent(LASER_SOUND_ID) val LASER_PARTICLE = FabricParticleTypes.simple() val FAKE_PLAYER_BUILDER = FakePlayerBuilder(identifier("default_fake_player")) { builder, server, world, profile -> object : FakeServerPlayer(builder, server, world, profile) { override fun isCreative(): Boolean = false override fun isSpectator(): Boolean = false override fun playSound(sound: SoundEvent?, volume: Float, pitch: Float) {} override fun playSound(event: SoundEvent?, category: SoundCategory?, volume: Float, pitch: Float) {} } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/IndustrialRevolutionClient.kt ================================================ package me.steven.indrev import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import me.steven.indrev.api.OreDataCards import me.steven.indrev.blockentities.GlobalStateController import me.steven.indrev.blockentities.crafters.* import me.steven.indrev.blockentities.miningrig.DrillBlockEntityRenderer import me.steven.indrev.blockentities.farms.* import me.steven.indrev.blockentities.generators.HeatGeneratorBlockEntityRenderer import me.steven.indrev.blockentities.generators.SteamTurbineBlockEntity import me.steven.indrev.blockentities.laser.CapsuleBlockEntityRenderer import me.steven.indrev.blockentities.laser.LaserBlockEntityRenderer import me.steven.indrev.blockentities.miningrig.MiningRigBlockEntityRenderer import me.steven.indrev.blockentities.modularworkbench.ModularWorkbenchBlockEntityRenderer import me.steven.indrev.blockentities.solarpowerplant.HeliostatBlockEntityRenderer import me.steven.indrev.blockentities.storage.ChargePadBlockEntityRenderer import me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntityRenderer import me.steven.indrev.blockentities.storage.TankBlockEntityRenderer import me.steven.indrev.components.multiblock.MultiblockBlockEntityRenderer import me.steven.indrev.config.IRConfig import me.steven.indrev.events.client.* import me.steven.indrev.fluids.FluidType import me.steven.indrev.gui.IRInventoryScreen import me.steven.indrev.gui.screenhandlers.* import me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreen import me.steven.indrev.networks.Network import me.steven.indrev.networks.client.ClientNetworkState import me.steven.indrev.packets.PacketRegistry import me.steven.indrev.registry.* import me.steven.indrev.events.client.IRWorldRenderer import me.steven.indrev.utils.identifier import net.fabricmc.api.ClientModInitializer import net.fabricmc.fabric.api.`object`.builder.v1.client.model.FabricModelPredicateProviderRegistry import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback import net.fabricmc.fabric.api.client.rendering.v1.TooltipComponentCallback import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback import net.minecraft.client.option.KeyBinding import net.minecraft.client.particle.FlameParticle import net.minecraft.client.render.RenderLayer import net.minecraft.client.util.InputUtil import net.minecraft.item.ElytraItem import net.minecraft.screen.PlayerScreenHandler import org.lwjgl.glfw.GLFW @Suppress("UNCHECKED_CAST") object IndustrialRevolutionClient : ClientModInitializer { override fun onInitializeClient() { FluidType.WATER.registerReloadListener() FluidType.LAVA.registerReloadListener() FluidType.GAS.registerReloadListener() arrayOf( IRFluidRegistry.COOLANT_STILL, IRFluidRegistry.SULFURIC_ACID_STILL, IRFluidRegistry.TOXIC_MUD_STILL, IRFluidRegistry.STEAM_STILL ).forEach { it.registerRender(FluidType.WATER) } arrayOf( IRFluidRegistry.MOLTEN_NETHERITE_STILL, IRFluidRegistry.MOLTEN_IRON_STILL, IRFluidRegistry.MOLTEN_GOLD_STILL, IRFluidRegistry.MOLTEN_COPPER_STILL, IRFluidRegistry.MOLTEN_TIN_STILL, IRFluidRegistry.MOLTEN_LEAD_STILL, IRFluidRegistry.MOLTEN_SILVER_STILL ).forEach { it.registerRender(FluidType.LAVA) } arrayOf( IRFluidRegistry.HYDROGEN_STILL, IRFluidRegistry.OXYGEN_STILL, IRFluidRegistry.METHANE_STILL, ).forEach { it.registerRender(FluidType.GAS) } arrayOf( COAL_GENERATOR_HANDLER, SOLAR_GENERATOR_HANDLER, BIOMASS_GENERATOR_HANDLER, HEAT_GENERATOR_HANDLER, GAS_BURNING_GENERATOR_HANDLER, BATTERY_HANDLER, ELECTRIC_FURNACE_HANDLER, PULVERIZER_HANDLER, COMPRESSOR_HANDLER, SOLID_INFUSER_HANDLER, RECYCLER_HANDLER, CHOPPER_HANDLER, RANCHER_HANDLER, MINING_RIG_HANDLER, MODULAR_WORKBENCH_HANDLER, FISHER_HANDLER, SCREWDRIVER_HANDLER, SMELTER_HANDLER, CONDENSER_HANDLER, FLUID_INFUSER_HANDLER, FARMER_HANDLER, SLAUGHTER_HANDLER, SAWMILL_HANDLER, ELECTRIC_FURNACE_FACTORY_HANDLER, PULVERIZER_FACTORY_HANDLER, COMPRESSOR_FACTORY_HANDLER, SOLID_INFUSER_FACTORY_HANDLER, CABINET_HANDLER, DRILL_HANDLER, LASER_HANDLER, ELECTROLYTIC_SEPARATOR_HANDLER, STEAM_TURBINE_HANDLER, SOLAR_POWER_PLANT_TOWER_HANDLER, DATA_CARD_WRITER_HANDLER, PUMP_HANDLER ).forEach { handler -> ScreenRegistry.register(handler) { controller, inv, _ -> IRInventoryScreen(controller, inv.player) } } ScreenRegistry.register(PIPE_FILTER_HANDLER) { controller, inv, _ -> PipeFilterScreen(controller, inv.player) } MachineRegistry.CHOPPER_REGISTRY.registerBlockEntityRenderer(::ChopperBlockEntityRenderer) MachineRegistry.RANCHER_REGISTRY.registerBlockEntityRenderer(::AOEMachineBlockEntityRenderer) MachineRegistry.FARMER_REGISTRY.registerBlockEntityRenderer(::AOEMachineBlockEntityRenderer) MachineRegistry.SLAUGHTER_REGISTRY.registerBlockEntityRenderer(::AOEMachineBlockEntityRenderer) MachineRegistry.MODULAR_WORKBENCH_REGISTRY.registerBlockEntityRenderer(::ModularWorkbenchBlockEntityRenderer) MachineRegistry.CHARGE_PAD_REGISTRY.registerBlockEntityRenderer(::ChargePadBlockEntityRenderer) MachineRegistry.CONDENSER_REGISTRY.registerBlockEntityRenderer(::CondenserBlockEntityRenderer) MachineRegistry.FLUID_INFUSER_REGISTRY.registerBlockEntityRenderer(::FluidInfuserBlockEntityRenderer) MachineRegistry.SOLID_INFUSER_FACTORY_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer { be -> be.multiblockComponent!! } } MachineRegistry.COMPRESSOR_FACTORY_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer { be -> be.multiblockComponent!! } } MachineRegistry.PULVERIZER_FACTORY_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer { be -> be.multiblockComponent!! } } MachineRegistry.ELECTRIC_FURNACE_FACTORY_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer { be -> be.multiblockComponent!! } } MachineRegistry.MINING_RIG_REGISTRY.registerBlockEntityRenderer(::MiningRigBlockEntityRenderer) MachineRegistry.PUMP_REGISTRY.registerBlockEntityRenderer(::PumpBlockEntityRenderer) MachineRegistry.LAZULI_FLUX_CONTAINER_REGISTRY.registerBlockEntityRenderer(::LazuliFluxContainerBlockEntityRenderer) MachineRegistry.HEAT_GENERATOR_REGISTRY.registerBlockEntityRenderer(::HeatGeneratorBlockEntityRenderer) MachineRegistry.LASER_EMITTER_REGISTRY.registerBlockEntityRenderer(::LaserBlockEntityRenderer) MachineRegistry.STEAM_TURBINE_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer { be -> be.multiblockComponent!! } } BlockEntityRendererRegistry.register(IRBlockRegistry.TANK_BLOCK_ENTITY) { TankBlockEntityRenderer() } BlockEntityRendererRegistry.register(IRBlockRegistry.DRILL_BLOCK_ENTITY_TYPE) { DrillBlockEntityRenderer() } BlockEntityRendererRegistry.register(IRBlockRegistry.CAPSULE_BLOCK_ENTITY) { CapsuleBlockEntityRenderer() } BlockEntityRendererRegistry.register(IRBlockRegistry.BIOMASS_COMPOSTER_BLOCK_ENTITY) { BiomassComposterBlockEntityRenderer() } BlockEntityRendererRegistry.register(IRBlockRegistry.SOLAR_POWER_PLANT_TOWER_BLOCK_ENTITY) { MultiblockBlockEntityRenderer { be -> be.multiblockComponent } } BlockEntityRendererRegistry.register(IRBlockRegistry.HELIOSTAT_BLOCK_ENTITY) { HeliostatBlockEntityRenderer() } MachineRegistry.MODULAR_WORKBENCH_REGISTRY.setRenderLayer(RenderLayer.getTranslucent()) MachineRegistry.PUMP_REGISTRY.setRenderLayer(RenderLayer.getTranslucent()) MachineRegistry.HEAT_GENERATOR_REGISTRY.setRenderLayer(RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.TANK_BLOCK, RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.SULFUR_CRYSTAL_CLUSTER, RenderLayer.getTranslucent()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.DRILL_TOP, RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.DRILL_MIDDLE, RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.DRILL_BOTTOM, RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CAPSULE_BLOCK, RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CABLE_MK1, RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CABLE_MK2, RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CABLE_MK3, RenderLayer.getCutout()) BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CABLE_MK4, RenderLayer.getCutout()) ModelLoadingRegistry.INSTANCE.registerModelProvider(IRModelManagers) ModelLoadingRegistry.INSTANCE.registerVariantProvider { IRModelManagers } FabricModelPredicateProviderRegistry.register( IRItemRegistry.GAMER_AXE_ITEM, identifier("activate") ) { stack, _, _, _ -> stack?.orCreateNbt?.getFloat("Progress") ?: 0f } FabricModelPredicateProviderRegistry.register(IRItemRegistry.REINFORCED_ELYTRA, identifier("broken")) { stack, _, _, _ -> if (ElytraItem.isUsable(stack)) 0.0f else 1.0f } FabricModelPredicateProviderRegistry.register(IRItemRegistry.ORE_DATA_CARD, identifier("empty")) { stack, _, _, _ -> if (OreDataCards.readNbt(stack) == null) 0.0f else 1.0f } PacketRegistry.registerClient() GlobalStateController.initClient() HudRenderCallback.EVENT.register(IRHudRenderCallback) WorldRenderEvents.BEFORE_BLOCK_OUTLINE.register(IRWorldRenderer) WorldRenderEvents.BEFORE_ENTITIES.register(MatterProjectorPreviewRenderer) ClientTickEvents.END_CLIENT_TICK.register(IRClientTickEvents) ClientSpriteRegistryCallback.event(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).register( ClientSpriteRegistryCallback { _, registry -> registry.register(identifier("block/lazuli_flux_container_lf_level")) registry.register(identifier("gui/hud_damaged")) registry.register(identifier("gui/hud_regenerating")) registry.register(identifier("gui/hud_warning")) registry.register(identifier("gui/hud_default")) registry.register(identifier("particle/laser_particle_1")) registry.register(identifier("particle/laser_particle_2")) registry.register(identifier("particle/laser_particle_3")) }) ParticleFactoryRegistry.getInstance().register(IndustrialRevolution.LASER_PARTICLE) { spriteProvider -> FlameParticle.Factory(spriteProvider) } ClientPlayConnectionEvents.DISCONNECT.register { _, _ -> IRConfig.readConfigs() } LivingEntityFeatureRendererRegistrationCallback.EVENT.register(IRLivingEntityFeatureRendererCallback) TooltipComponentCallback.EVENT.register(IRTooltipComponentsCallback) ItemTooltipCallback.EVENT.register(MiningRigInfoTooltipCallback) AprilFools.init() } val MODULAR_CONTROLLER_KEYBINDING: KeyBinding = KeyBindingHelper.registerKeyBinding( KeyBinding( "key.indrev.modular", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_K, "category.indrev" ) ) val GAMER_AXE_TOGGLE_KEYBINDING: KeyBinding = KeyBindingHelper.registerKeyBinding( KeyBinding( "key.indrev.gamer_axe_toggle", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_G, "category.indrev" ) ) val CLIENT_NETWORK_STATE = Object2ObjectOpenHashMap, ClientNetworkState<*>>() } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/AttributeModifierProvider.kt ================================================ package me.steven.indrev.api import com.google.common.collect.Multimap import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.attribute.EntityAttribute import net.minecraft.entity.attribute.EntityAttributeModifier import net.minecraft.item.ItemStack interface AttributeModifierProvider { fun getAttributeModifiers(itemStack: ItemStack, equipmentSlot: EquipmentSlot): Multimap } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/CustomEnchantmentProvider.kt ================================================ package me.steven.indrev.api import net.minecraft.enchantment.Enchantment import net.minecraft.item.ItemStack interface CustomEnchantmentProvider { fun getLevel(enchantment: Enchantment, itemStack: ItemStack): Int } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/IREntityExtension.kt ================================================ package me.steven.indrev.api import me.steven.indrev.inventories.IRInventory interface IREntityExtension { var machineInv: IRInventory? } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/IRPlayerEntityExtension.kt ================================================ package me.steven.indrev.api import me.steven.indrev.tools.modular.ArmorModule interface IRPlayerEntityExtension { var shieldDurability: Double var isRegenerating: Boolean fun getMaxShieldDurability(): Double fun getAppliedModules(): Map fun applyModule(module: ArmorModule, level: Int) fun isApplied(module: ArmorModule): Boolean fun getAppliedLevel(module: ArmorModule): Int } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/IRServerPlayerEntityExtension.kt ================================================ package me.steven.indrev.api interface IRServerPlayerEntityExtension : IRPlayerEntityExtension { fun shouldSync(): Boolean fun sync() } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/OreDataCards.kt ================================================ package me.steven.indrev.api import me.steven.indrev.config.IRConfig import me.steven.indrev.registry.IRItemRegistry import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.item.Items import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtList import net.minecraft.tag.TagKey import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import java.util.* import kotlin.math.pow object OreDataCards { const val MAX_SIZE = 2048 const val MAX_RICNHESS = 1.0 const val MAX_PER_CYCLE = 8 val INVALID_DATA = Data(emptyList(), mutableMapOf(), -1.0, -1, -1, -1, -1, -1) fun isAllowed(stack: ItemStack): Boolean { return IRConfig.miningRigConfig.allowedTags.any { stack.isIn(TagKey.of(Registry.ITEM_KEY, Identifier(it.key))) } } fun getCost(stack: ItemStack): Int { return IRConfig.miningRigConfig.allowedTags.firstNotNullOfOrNull { if (stack.isIn(TagKey.of(Registry.ITEM_KEY, Identifier(it.key)))) it.value else null } ?: 0 } fun readNbt(stack: ItemStack): Data? { if (!stack.isOf(IRItemRegistry.ORE_DATA_CARD)) return null val nbt = stack.getSubNbt("CardData") ?: return null val entriesNbt = nbt.getList("Entries", 10) val entries = mutableListOf() entriesNbt.forEach { element -> val itemId = (element as NbtCompound).getString("ItemId") val optional = Registry.ITEM.getOrEmpty(Identifier(itemId)) if (!optional.isPresent) { return INVALID_DATA } val count = element.getInt("Count") entries.add(OreEntry(optional.get(), count)) } val modifiersNbt = nbt.getList("ModifiersUsed", 10) val modifiers = mutableMapOf() modifiersNbt.forEach { element -> val modifierId = (element as NbtCompound).getInt("Modifier") val level = element.getInt("Level") modifiers[Modifier.values()[modifierId]] = level } val richness = nbt.getDouble("Richness") val size = nbt.getInt("Size") val used = nbt.getInt("Used") val speed = nbt.getInt("Speed") val rng = nbt.getInt("Rng") val energy = nbt.getInt("Energy") return Data(entries.toList(), modifiers, richness, speed, rng, energy, size, used) } fun writeNbt(stack: ItemStack, data: Data) { if (!stack.isOf(IRItemRegistry.ORE_DATA_CARD)) return val nbt = stack.getOrCreateSubNbt("CardData") val entriesNbt = NbtList() data.entries.forEach { entry -> val entryNbt = NbtCompound() entryNbt.putString("ItemId", Registry.ITEM.getId(entry.item).toString()) entryNbt.putInt("Count", entry.count) entriesNbt.add(entryNbt) } nbt.put("Entries", entriesNbt) val modifiersNbt = NbtList() data.modifiersUsed.forEach { (modifier, level) -> val modifierNbt = NbtCompound() modifierNbt.putInt("Modifier", modifier.ordinal) modifierNbt.putInt("Level", level) modifiersNbt.add(modifierNbt) } nbt.put("ModifiersUsed", modifiersNbt) nbt.putDouble("Richness", data.richness) nbt.putInt("Size", data.maxCycles) nbt.putInt("Used", data.used) nbt.putInt("Speed", data.speed) nbt.putInt("Rng", data.rng) nbt.putInt("Energy", data.energyRequired) } data class Data(val entries: List, val modifiersUsed: MutableMap, val richness: Double, val speed: Int, val rng: Int, val energyRequired: Int, val maxCycles: Int, var used: Int) { fun isValid(): Boolean { return this != INVALID_DATA && entries.isNotEmpty() && richness > 0 && maxCycles > 0 && maxCycles < MAX_SIZE } fun isEmpty(): Boolean { return used >= maxCycles } fun pickRandom(random: Random): Item { entries.forEach { entry -> entry.order = -random.nextFloat().pow(1.0f / entry.count.toFloat()) } return entries.minByOrNull { a -> a.order }!!.item } } data class OreEntry(val item: Item, val count: Int, var order: Float = 0.0f) enum class Modifier(val item: Item) { RICHNESS(IRItemRegistry.ENRICHED_NIKOLITE_DUST()), SPEED(Items.REDSTONE), SIZE(Items.STONE), RNG(Items.EMERALD); val translationKey = "item.indrev.ore_data_card.modifier.${name.lowercase()}" companion object { fun isModifierItem(item: Item): Boolean = values().any { it.item == item } fun byItem(item: Item): Modifier? = values().firstOrNull { it.item == item } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/ServerWorldExtension.kt ================================================ package me.steven.indrev.api import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import me.steven.indrev.networks.energy.EnergyNetworkState import me.steven.indrev.networks.fluid.FluidNetworkState import me.steven.indrev.networks.item.ItemNetworkState import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache import net.minecraft.util.math.Direction import team.reborn.energy.api.EnergyStorage interface ServerWorldExtension { fun indrev_getEnergyCache(): Long2ObjectOpenHashMap> fun indrev_getEnergyNetworkState(): EnergyNetworkState fun indrev_getFluidNetworkState(): FluidNetworkState fun indrev_getItemNetworkState(): ItemNetworkState } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/machines/Tier.kt ================================================ package me.steven.indrev.api.machines enum class Tier(val io: Long, val id: String) { MK1(64, "mk1"), MK2(128, "mk2"), MK3(512, "mk3"), MK4(4096, "mk4"), CREATIVE(4096, "creative"); fun next(): Tier { return when (this) { MK1 -> MK2 MK2 -> MK3 MK3 -> MK4 else -> error("no tier after $this") } } companion object { val VALUES = arrayOf(MK1, MK2, MK3, MK4) val ALL_VALUES = values() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/machines/TransferMode.kt ================================================ package me.steven.indrev.api.machines enum class TransferMode(val rgb: Long, val input: Boolean, val output: Boolean) { INPUT(0x997e75ff, true, false), INPUT_FIRST(0x9975ff8e, true, false), INPUT_SECOND(0x9975ff8e, true, false), OUTPUT_FIRST(0x9975ff8e, false, true), OUTPUT_SECOND(0x9975ff8e, false, true), OUTPUT(0x99ffb175, false, true), INPUT_OUTPUT(0x99d875ff, true, true), NONE(-1, false, false); fun next(): TransferMode = when (this) { INPUT -> INPUT_FIRST INPUT_FIRST -> INPUT_SECOND INPUT_SECOND -> OUTPUT_FIRST OUTPUT_FIRST -> OUTPUT_SECOND OUTPUT_SECOND -> OUTPUT OUTPUT -> INPUT_OUTPUT INPUT_OUTPUT -> NONE NONE -> INPUT } fun next(available: Array): TransferMode { var current = this for (i in values().indices) { val possible = current.next() if (available.contains(possible)) { return possible } current = possible } return this } companion object { val DEFAULT = arrayOf(INPUT, OUTPUT, INPUT_OUTPUT, NONE) val SOLID_INFUSER = arrayOf(INPUT, OUTPUT, INPUT_OUTPUT, INPUT_FIRST, INPUT_SECOND, NONE) val ELECTROLYTIC_SEPARATOR = arrayOf(TransferMode.INPUT, TransferMode.OUTPUT_FIRST, TransferMode.OUTPUT_SECOND, TransferMode.NONE) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/sideconfigs/Configurable.kt ================================================ package me.steven.indrev.api.sideconfigs import me.steven.indrev.api.machines.TransferMode import net.minecraft.block.BlockState import net.minecraft.util.math.Direction interface Configurable { fun isConfigurable(type: ConfigurationType): Boolean fun isFixed(type: ConfigurationType): Boolean fun getValidConfigurations(type: ConfigurationType): Array fun getCurrentConfiguration(type: ConfigurationType): SideConfiguration fun applyDefault(state: BlockState, type: ConfigurationType, configuration: MutableMap) } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/sideconfigs/ConfigurationType.kt ================================================ package me.steven.indrev.api.sideconfigs import me.steven.indrev.api.machines.TransferMode import net.minecraft.text.Text import net.minecraft.text.TranslatableText enum class ConfigurationType(val title: Text, vararg val validModes: TransferMode) { ITEM(TranslatableText("item.indrev.wrench.item"), TransferMode.INPUT, TransferMode.OUTPUT, TransferMode.INPUT_OUTPUT, TransferMode.INPUT_FIRST, TransferMode.INPUT_SECOND, TransferMode.NONE), FLUID(TranslatableText("item.indrev.wrench.fluid"), TransferMode.INPUT, TransferMode.OUTPUT, TransferMode.INPUT_OUTPUT, TransferMode.NONE), ENERGY(TranslatableText("item.indrev.wrench.energy"), TransferMode.INPUT, TransferMode.OUTPUT, TransferMode.NONE); fun next(): ConfigurationType { return when (this) { ITEM -> FLUID FLUID -> ENERGY ENERGY -> ITEM } } fun next(available: Array): ConfigurationType { var current = this for (i in values().indices) { val possible = current.next() if (available.contains(possible)) { return possible } current = possible } return this } companion object { fun getTypes(blockEntity: Configurable) = values().filter { type -> blockEntity.isConfigurable(type) && !blockEntity.isFixed(type) }.toTypedArray() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/api/sideconfigs/SideConfiguration.kt ================================================ package me.steven.indrev.api.sideconfigs import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WToggleButton import io.github.cottonmc.cotton.gui.widget.WWidget import io.netty.buffer.Unpooled import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.blocks.machine.FacingMachineBlock import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.gui.widgets.machines.WMachineSideDisplay import me.steven.indrev.packets.common.ConfigureIOPackets import me.steven.indrev.utils.add import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.minecraft.entity.player.PlayerInventory import net.minecraft.nbt.NbtCompound import net.minecraft.network.PacketByteBuf import net.minecraft.text.TranslatableText import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World import java.util.* import java.util.function.Consumer data class SideConfiguration(val type: ConfigurationType, private val transferConfig: EnumMap = EnumMap(Direction::class.java)) : MutableMap by transferConfig { var autoPush = true var autoPull = true init { Direction.values().forEach { dir -> this[dir] = TransferMode.NONE } } fun canInput(direction: Direction): Boolean { return this[direction]?.input == true } fun canOutput(direction: Direction): Boolean { return this[direction]?.output == true } fun writeNbt(tag: NbtCompound?) { var transferConfigTag = tag?.getCompound("TransferConfig") if (tag?.contains("TransferConfig") == false) { transferConfigTag = NbtCompound() tag.put("TransferConfig", transferConfigTag) } val configTag = NbtCompound() forEach { (dir, mode) -> configTag.putString(dir.toString(), mode.toString()) } transferConfigTag?.put(type.toString().lowercase(Locale.getDefault()), configTag) configTag.putBoolean("AutoPush", autoPush) configTag.putBoolean("AutoPull", autoPull) } fun readNbt(tag: NbtCompound?) { if (tag?.contains("TransferConfig") == true) { val transferConfigTag = tag.getCompound("TransferConfig") val configTag = transferConfigTag.getCompound(type.toString().lowercase(Locale.getDefault())) Direction.values().forEach { dir -> val value = configTag.getString(dir.toString()).uppercase(Locale.getDefault()) if (value.isNotEmpty()) { val mode = TransferMode.valueOf(value) this[dir] = mode } } if (configTag.contains("AutoPush")) autoPush = configTag.getBoolean("AutoPush") if (configTag.contains("AutoPull")) autoPull = configTag.getBoolean("AutoPull") } } fun writeBuf(buf: PacketByteBuf) { buf.writeBoolean(autoPush) buf.writeBoolean(autoPull) forEach { dir, mode -> buf.writeByte(dir.ordinal) buf.writeByte(mode.ordinal) } } fun readBuf(buf: PacketByteBuf) { autoPush = buf.readBoolean() autoPull = buf.readBoolean() repeat(6) { val direction = Direction.values()[buf.readByte().toInt()] val mode = TransferMode.values()[buf.readByte().toInt()] this[direction] = mode } } fun getConfigurationPanel(world: World, pos: BlockPos, configurable: Configurable, playerInventory: PlayerInventory, type: ConfigurationType): WWidget { val root = WGridPanel() root.setSize(100, 128) val configuration = this if (configuration.type == ConfigurationType.ITEM) { val autoPushBtn = WToggleButton(TranslatableText("item.indrev.wrench.autopush")) autoPushBtn.toggle = configuration.autoPush autoPushBtn.onToggle = Consumer { v -> configuration.autoPush = v val buf = PacketByteBuf(Unpooled.buffer()) buf.writeEnumConstant(type) buf.writeByte(0) buf.writeBlockPos(pos) buf.writeBoolean(v) ClientPlayNetworking.send(ConfigureIOPackets.UPDATE_AUTO_OPERATION_PACKET_ID, buf) } root.add(autoPushBtn, 0, 4) val autoPullBtn = WToggleButton(TranslatableText("item.indrev.wrench.autopull")) autoPullBtn.toggle = configuration.autoPull autoPullBtn.onToggle = Consumer { v -> configuration.autoPull = v val buf = PacketByteBuf(Unpooled.buffer()) buf.writeEnumConstant(type) buf.writeByte(1) buf.writeBlockPos(pos) buf.writeBoolean(v) ClientPlayNetworking.send(ConfigureIOPackets.UPDATE_AUTO_OPERATION_PACKET_ID, buf) } root.add(autoPullBtn, 0.0, 4.8) } val blockState = world.getBlockState(pos) val machineVisualizerPanel = WGridPanel() MachineSide.values().forEach { side -> var facing = when { blockState.contains(HorizontalFacingMachineBlock.HORIZONTAL_FACING) -> blockState[HorizontalFacingMachineBlock.HORIZONTAL_FACING] blockState.contains(FacingMachineBlock.FACING) -> blockState[FacingMachineBlock.FACING] else -> Direction.UP } if (facing.axis.isVertical) { facing = playerInventory.player.horizontalFacing.opposite } val direction = offset(facing, side.direction) val mode = configuration[direction]!! val widget = WMachineSideDisplay(side, direction, mode, world, pos) widget.setOnClick { widget.mode = widget.mode.next(configurable.getValidConfigurations(type)) configuration[direction] = widget.mode val buf = PacketByteBuf(Unpooled.buffer()) buf.writeEnumConstant(type) buf.writeBlockPos(pos) buf.writeInt(direction.id) buf.writeInt(widget.mode.ordinal) ClientPlayNetworking.send(ConfigureIOPackets.UPDATE_MACHINE_SIDE_PACKET_ID, buf) } machineVisualizerPanel.add(widget, (side.x) * 1.2, (side.y) * 1.2) } root.add(machineVisualizerPanel, 0.5, 0.0) return root } private fun offset(facing: Direction, side: Direction): Direction { return if (side.axis.isVertical) side else when (facing) { Direction.NORTH -> side Direction.SOUTH -> side.opposite Direction.WEST -> side.rotateYCounterclockwise() Direction.EAST -> side.rotateYClockwise() else -> side } } enum class MachineSide(val x: Int, val y: Int, val direction: Direction, val u1: Float, val v1: Float, val u2: Float, val v2: Float) { FRONT(1, 1, Direction.NORTH, 5.333f, 5.333f, 10.666f, 10.666f), LEFT(0, 1, Direction.EAST, 0.0f, 5.333f, 5.332f, 10.666f), BACK(2, 2, Direction.SOUTH, 10.667f, 10.667f, 16.0f, 16f), RIGHT(2, 1, Direction.WEST, 10.667f, 5.333f, 16.0f, 10.665f), TOP(1, 0, Direction.UP, 5.333f, 0.0f, 10.666f, 5.333f), BOTTOM(1, 2, Direction.DOWN, 5.333f, 10.667f, 10.666f, 15.998f) } companion object { val EMPTY_ITEM = SideConfiguration(ConfigurationType.ITEM) val EMPTY_FLUID = SideConfiguration(ConfigurationType.FLUID) val EMPTY_ENERGY = SideConfiguration(ConfigurationType.ENERGY) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/armor/IRArmorMaterial.kt ================================================ package me.steven.indrev.armor import me.steven.indrev.registry.IRItemRegistry import net.minecraft.entity.EquipmentSlot import net.minecraft.item.ArmorMaterial import net.minecraft.recipe.Ingredient import net.minecraft.sound.SoundEvent import net.minecraft.sound.SoundEvents enum class IRArmorMaterial( private val armorName: String, private val durabilityMultiplier: Int, private val armorValues: IntArray, private val enchantability: Int, private val equipSound: SoundEvent, private val toughness: Float, private val knockbackResistance: Float, private val repairIngredient: () -> Ingredient?) : ArmorMaterial { MODULAR( "modular", 0, intArrayOf(1, 3, 2, 1), 15, SoundEvents.ITEM_ARMOR_EQUIP_IRON, 1.0F, 1.0F, { null } ), STEEL_ELYTRA( "reinforced_elytra", 30, intArrayOf(1, 1, 1, 1), 15, SoundEvents.ITEM_ARMOR_EQUIP_IRON, 1.0F, 0F, { Ingredient.ofItems(IRItemRegistry.STEEL_PLATE()) } ), JETPACK( "jetpack", 30, intArrayOf(1, 1, 1, 1), 15, SoundEvents.ITEM_ARMOR_EQUIP_IRON, 1.0F, 0F, { null } ), STEEL( "steel", 30, intArrayOf(2, 6, 7, 2), 15, SoundEvents.ITEM_ARMOR_EQUIP_IRON, 1.0F, 0F, { Ingredient.ofItems(IRItemRegistry.STEEL_INGOT()) } ), COPPER( "copper", 14, intArrayOf(2, 4, 5, 2), 15, SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.COPPER_INGOT()) } ), TIN( "tin", 10, intArrayOf(1, 4, 5, 2), 15, SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.TIN_INGOT()) } ), BRONZE( "bronze", 15, intArrayOf(2, 5, 6, 2), 15, SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.BRONZE_INGOT()) } ), LEAD( "lead", 20, intArrayOf(2, 4, 4, 1), 15, SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.LEAD_INGOT()) } ), SILVER( "silver", 30, intArrayOf(1, 4, 4, 1), 15, SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.SILVER_INGOT()) } ); override fun getName(): String = armorName override fun getEquipSound(): SoundEvent = equipSound override fun getRepairIngredient(): Ingredient? = repairIngredient() override fun getEnchantability(): Int = enchantability override fun getProtectionAmount(slot: EquipmentSlot): Int = this.armorValues[slot.entitySlotId] override fun getDurability(slot: EquipmentSlot): Int = BASE_DURABILITY[slot.entitySlotId] * durabilityMultiplier override fun getKnockbackResistance(): Float = knockbackResistance override fun getToughness(): Float = toughness companion object { private val BASE_DURABILITY = intArrayOf(13, 15, 16, 11) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/armor/ModuleFeatureRenderer.kt ================================================ package me.steven.indrev.armor import me.steven.indrev.items.armor.IRModularArmorItem import me.steven.indrev.utils.identifier import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer import net.minecraft.client.render.entity.feature.FeatureRendererContext import net.minecraft.client.render.entity.model.BipedEntityModel import net.minecraft.client.render.item.ItemRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity import net.minecraft.item.ArmorItem import net.minecraft.util.Identifier class ModuleFeatureRenderer, A : BipedEntityModel>( context: FeatureRendererContext, private val leggingsModel: A, private val bodyModel: A ) : ArmorFeatureRenderer(context, leggingsModel, bodyModel) { override fun render(matrixStack: MatrixStack, vertexConsumerProvider: VertexConsumerProvider, i: Int, livingEntity: T, f: Float, g: Float, h: Float, j: Float, k: Float, l: Float) { renderArmor(matrixStack, vertexConsumerProvider, livingEntity, EquipmentSlot.CHEST, i, getArmor(EquipmentSlot.CHEST)) renderArmor(matrixStack, vertexConsumerProvider, livingEntity, EquipmentSlot.LEGS, i, getArmor(EquipmentSlot.LEGS)) renderArmor(matrixStack, vertexConsumerProvider, livingEntity, EquipmentSlot.FEET, i, getArmor(EquipmentSlot.FEET)) renderArmor(matrixStack, vertexConsumerProvider, livingEntity, EquipmentSlot.HEAD, i, getArmor(EquipmentSlot.HEAD)) } private fun renderArmor(matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, livingEntity: T, equipmentSlot: EquipmentSlot, light: Int, bipedEntityModel: A) { val itemStack = livingEntity.getEquippedStack(equipmentSlot) val item = itemStack.item as? IRModularArmorItem ?: return if (item.slotType == equipmentSlot) { (this.contextModel as? BipedEntityModel)?.setAttributes(bipedEntityModel) ?: return setVisible(bipedEntityModel, equipmentSlot) val rgb = item.getColor(itemStack) val r = (rgb and 0xFF0000 shr 16) / 255f val g = (rgb and 0xFF00 shr 8) / 255f val b = (rgb and 0xFF) / 255f item.getInstalled(itemStack).filter { it.slots.contains(equipmentSlot) }.forEach { module -> if (module.hasTexture) { renderArmorParts( matrices, vertexConsumers, light, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(equipmentSlot), r, g, b, module.key ) if (module.hasOverlay) { renderArmorParts( matrices, vertexConsumers, 15728880, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(equipmentSlot), r, g, b, "${module.key}_overlay" ) } } } } } private fun renderArmorParts( matrixStack: MatrixStack, vertexConsumerProvider: VertexConsumerProvider, light: Int, armorItem: ArmorItem, hasGlint: Boolean, bipedEntityModel: A, secondLayer: Boolean, r: Float, g: Float, b: Float, overlay: String?) { val vertexConsumer = ItemRenderer.getArmorGlintConsumer(vertexConsumerProvider, RenderLayer.getArmorCutoutNoCull(getArmorTexture(armorItem, secondLayer, overlay)), false, hasGlint) bipedEntityModel.render(matrixStack, vertexConsumer, light, OverlayTexture.DEFAULT_UV, r, g, b, 1.0f) } private fun getArmor(slot: EquipmentSlot): A { return if (usesSecondLayer(slot)) leggingsModel else bodyModel } private fun usesSecondLayer(slot: EquipmentSlot): Boolean { return slot == EquipmentSlot.LEGS } private fun getArmorTexture(armorItem: ArmorItem, secondLayer: Boolean, overlay: String?): Identifier { val path = "textures/models/armor/" + armorItem.material.name + "_layer_" + (if (secondLayer) 2 else 1) + (if (overlay == null) "" else "_$overlay") + ".png" return MODULAR_ARMOR_TEXTURE_CACHE.computeIfAbsent(path) { id -> identifier(id) } } companion object { val MODULAR_ARMOR_TEXTURE_CACHE = mutableMapOf() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/armor/ReinforcedElytraFeatureRenderer.kt ================================================ package me.steven.indrev.armor import me.steven.indrev.items.armor.ReinforcedElytraItem import me.steven.indrev.utils.identifier import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.client.network.AbstractClientPlayerEntity import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.entity.PlayerModelPart import net.minecraft.client.render.entity.feature.FeatureRenderer import net.minecraft.client.render.entity.feature.FeatureRendererContext import net.minecraft.client.render.entity.model.ElytraEntityModel import net.minecraft.client.render.entity.model.EntityModel import net.minecraft.client.render.entity.model.EntityModelLayers import net.minecraft.client.render.entity.model.EntityModelLoader import net.minecraft.client.render.item.ItemRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity @Environment(EnvType.CLIENT) class ReinforcedElytraFeatureRenderer>( context: FeatureRendererContext, loader: EntityModelLoader ) : FeatureRenderer(context) { private val elytraModel: ElytraEntityModel = ElytraEntityModel(loader.getModelPart(EntityModelLayers.ELYTRA)) override fun render( matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, entity: T, limbAngle: Float, limbDistance: Float, tickDelta: Float, animationProgress: Float, headYaw: Float, headPitch: Float ) { val itemStack = entity.getEquippedStack(EquipmentSlot.CHEST) if (ReinforcedElytraItem.hasValidElytra(itemStack)) { val textureId = when { entity !is AbstractClientPlayerEntity -> REINFORCED_ELYTRA_SKIN entity.canRenderElytraTexture() && entity.elytraTexture != null -> entity.elytraTexture!! entity.canRenderCapeTexture() && entity.capeTexture != null && entity.isPartVisible(PlayerModelPart.CAPE) -> entity.capeTexture!! else -> REINFORCED_ELYTRA_SKIN } matrices.push() matrices.translate(0.0, 0.0, 0.125) this.contextModel.copyStateTo(elytraModel) elytraModel.setAngles(entity, limbAngle, limbDistance, animationProgress, headYaw, headPitch) val vertexConsumer = ItemRenderer.getArmorGlintConsumer( vertexConsumers, RenderLayer.getArmorCutoutNoCull(textureId), false, itemStack.hasGlint() ) elytraModel.render(matrices, vertexConsumer, light, OverlayTexture.DEFAULT_UV, 1.0f, 1.0f, 1.0f, 1.0f) matrices.pop() } } companion object { private val REINFORCED_ELYTRA_SKIN = identifier("textures/entity/reinforced_elytra.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/BaseBlockEntity.kt ================================================ package me.steven.indrev.blockentities import com.google.common.base.Preconditions import me.steven.indrev.components.GuiSyncableComponent import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityType import net.minecraft.nbt.NbtCompound import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos abstract class BaseBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : BlockEntity(type, pos, state) { open val guiSyncableComponent: GuiSyncableComponent? = null fun sync() { Preconditions.checkNotNull(world) // Maintain distinct failure case from below check(world is ServerWorld) { "Cannot call sync() on the logical client! Did you check world.isClient first?" } (world as ServerWorld).chunkManager.markForUpdate(getPos()) } abstract fun toTag(tag: NbtCompound) abstract fun fromTag(tag: NbtCompound) open fun toClientTag(tag: NbtCompound) { toTag(tag) } open fun fromClientTag(tag: NbtCompound) { fromTag(tag) } override fun toUpdatePacket(): BlockEntityUpdateS2CPacket { return BlockEntityUpdateS2CPacket.create(this) } override fun toInitialChunkDataNbt(): NbtCompound { val nbt = super.toInitialChunkDataNbt() toClientTag(nbt) nbt.putBoolean("#c", true) return nbt } override fun writeNbt(nbt: NbtCompound) { super.writeNbt(nbt) toTag(nbt) } override fun readNbt(nbt: NbtCompound) { super.readNbt(nbt) if (nbt.contains("#c")) { fromClientTag(nbt) } else { fromTag(nbt) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/BaseMachineBlockEntity.kt ================================================ package me.steven.indrev.blockentities import me.steven.indrev.api.sideconfigs.Configurable import net.minecraft.block.BlockState import net.minecraft.block.InventoryProvider import net.minecraft.block.entity.BlockEntityType import net.minecraft.util.math.BlockPos abstract class BaseMachineBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : BaseBlockEntity(type, pos, state), InventoryProvider, Configurable, Syncable { /** * Whether the machine should call #sync or not */ open val syncToWorld = false var isMarkedForUpdate: Boolean = true override fun markForUpdate(condition: () -> Boolean) { isMarkedForUpdate = isMarkedForUpdate || condition() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/GlobalStateController.kt ================================================ package me.steven.indrev.blockentities import it.unimi.dsi.fastutil.longs.Long2BooleanOpenHashMap import it.unimi.dsi.fastutil.longs.Long2ObjectMap import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import me.steven.indrev.packets.client.MachineStateUpdatePacket import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.component3 import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.client.MinecraftClient import net.minecraft.util.math.BlockPos import net.minecraft.util.math.ChunkPos import net.minecraft.world.World import java.util.function.LongFunction object GlobalStateController { @Environment(EnvType.CLIENT) val chunksToUpdate: Long2ObjectMap> = Long2ObjectOpenHashMap() @Environment(EnvType.CLIENT) val workingStateTracker = Long2BooleanOpenHashMap() @Environment(EnvType.CLIENT) fun queueUpdate(pos: BlockPos) { val chunkPos = ChunkPos.toLong(pos.x shr 4, pos.z shr 4) if (MinecraftClient.getInstance().isOnThread) chunksToUpdate.computeIfAbsent(chunkPos, LongFunction { hashSetOf() }).add(pos) else MinecraftClient.getInstance().execute { chunksToUpdate.computeIfAbsent(chunkPos, LongFunction { hashSetOf() }).add(pos) } } fun update(world: World, pos: BlockPos, workingState: Boolean) { val (x, y, z) = pos val players = world.server!!.playerManager.playerList for (i in players.indices) { val player = players[i] if (player.world.registryKey === world.registryKey) { val xOffset = x - player.x val yOffset = y - player.y val zOffset = z - player.z if (xOffset * xOffset + yOffset * yOffset + zOffset * zOffset < 64 * 64) { val buf = PacketByteBufs.create() buf.writeLong(pos.asLong()) buf.writeBoolean(workingState) ServerPlayNetworking.send(player, MachineStateUpdatePacket.UPDATE_PACKET_ID, buf) } } } } @Environment(EnvType.CLIENT) fun initClient() { var ticks = 0 ClientTickEvents.END_CLIENT_TICK.register { client -> ticks++ val world = client.world if (world != null && ticks % 15 == 0) { chunksToUpdate.values.removeIf { positions -> positions.forEach { (x, y, z) -> client.worldRenderer.scheduleBlockRenders(x , y, z, x, y,z) } true } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/MachineBlockEntity.kt ================================================ package me.steven.indrev.blockentities import me.steven.indrev.IREnergyStorage import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.components.* import me.steven.indrev.components.multiblock.MultiBlockComponent import me.steven.indrev.config.IConfig import me.steven.indrev.config.IRConfig import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import me.steven.indrev.utils.transferEnergy import me.steven.indrev.utils.transferFluids import me.steven.indrev.utils.transferItems import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.block.BlockState import net.minecraft.client.MinecraftClient import net.minecraft.inventory.SidedInventory import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.WorldAccess import kotlin.collections.set abstract class MachineBlockEntity(val tier: Tier, val registry: MachineRegistry, pos: BlockPos, state: BlockState) : BaseMachineBlockEntity(registry.blockEntityType(tier), pos, state) { val validConnections = Direction.values().toMutableList() override var guiSyncableComponent: GuiSyncableComponent? = GuiSyncableComponent() var energy: Long by autosync(ENERGY_ID, 0L) { value -> when (tier) { Tier.CREATIVE -> energyCapacity else -> value.coerceIn(0, energyCapacity) } } var inventoryComponent: InventoryComponent? = null var temperatureComponent: TemperatureComponent? = null var fluidComponent: FluidComponent? = null var multiblockComponent: MultiBlockComponent? = null var enhancerComponent: EnhancerComponent? = null var itemTransferCooldown = 0 var workingState: Boolean = false set(value) { val update = value != field field = value if (update && world?.isClient == false) GlobalStateController.update(world!!, pos, value) } get() { if (world?.isClient == true && GlobalStateController.workingStateTracker.contains(pos.asLong())) { MinecraftClient.getInstance().execute { field = GlobalStateController.workingStateTracker.remove(pos.asLong()) } } return field } var ticks = 0 @Suppress("UNCHECKED_CAST") val config: T by lazy { registry.config(tier) as T } open val maxInput: Long = tier.io open val maxOutput: Long = tier.io open val energyCapacity: Long get() = config.maxEnergyStored + (IRConfig.upgrades.bufferUpgradeModifier * (enhancerComponent?.getCount(Enhancer.BUFFER) ?: 0)) init { trackLong(MAX_ENERGY_ID) { energyCapacity } } open val storage = MachineEnergyStorage() protected open fun machineTick() {} @Environment(EnvType.CLIENT) open fun machineClientTick() {} fun tick() { inventoryComponent?.run { enhancerComponent?.updateEnhancers(inventory) } ticks++ multiblockComponent?.tick(world!!, pos, cachedState) if (multiblockComponent?.isBuilt(world!!, pos, cachedState) == false) return transferEnergy() transferItems() transferFluids() machineTick() if (isMarkedForUpdate) { markDirty() if (syncToWorld) sync() isMarkedForUpdate = false } } open fun getProcessingSpeed(): Double { return 1.0 + (IRConfig.upgrades.speedUpgradeModifier * (enhancerComponent?.getCount(Enhancer.SPEED) ?: 0)) } open fun getEnergyCost(): Long = 0 // internal consumption fun use(amount: Long): Boolean { val extracted = amount.coerceAtMost(energy) if (extracted == amount) { this.energy -= extracted return true } return false } fun canUse(amount: Long): Boolean { val extracted = amount.coerceAtMost(energy) return extracted == amount } override fun getInventory(state: BlockState?, world: WorldAccess?, pos: BlockPos?): SidedInventory? = inventoryComponent?.inventory override fun isConfigurable(type: ConfigurationType): Boolean { return when (type) { ConfigurationType.ITEM -> inventoryComponent != null && (inventoryComponent?.inventory?.inputSlots?.isNotEmpty() == true || inventoryComponent?.inventory?.outputSlots?.isNotEmpty() == true) ConfigurationType.FLUID -> fluidComponent != null ConfigurationType.ENERGY -> false } } override fun applyDefault(state: BlockState, type: ConfigurationType, configuration: MutableMap) { val direction = (state.block as MachineBlock).getFacing(state) when (type) { ConfigurationType.ITEM -> { configuration[direction.rotateYClockwise()] = TransferMode.INPUT configuration[direction.rotateYCounterclockwise()] = TransferMode.OUTPUT } ConfigurationType.ENERGY -> throw IllegalArgumentException("cannot apply energy configuration to $this") else -> return } } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.ITEM -> TransferMode.DEFAULT ConfigurationType.FLUID, ConfigurationType.ENERGY -> arrayOf(TransferMode.INPUT, TransferMode.OUTPUT, TransferMode.NONE) } } override fun getCurrentConfiguration(type: ConfigurationType): SideConfiguration { return when (type) { ConfigurationType.ITEM -> inventoryComponent!!.itemConfig ConfigurationType.FLUID -> fluidComponent!!.transferConfig ConfigurationType.ENERGY -> error("nope") } } override fun isFixed(type: ConfigurationType): Boolean = false open fun getFluidTransferRate(): Long = when (tier) { Tier.MK1 -> bucket / 3 Tier.MK2 -> bucket * 2 / 3 else -> bucket } override fun fromTag(tag: NbtCompound) { inventoryComponent?.readNbt(tag) inventoryComponent?.run { enhancerComponent?.updateEnhancers(inventory) } temperatureComponent?.readNbt(tag) fluidComponent?.fromTag(tag) multiblockComponent?.readNbt(tag) energy = tag.getLong("Energy") } override fun toTag(tag: NbtCompound) { tag.putLong("Energy", energy) inventoryComponent?.writeNbt(tag) temperatureComponent?.writeNbt(tag) fluidComponent?.toTag(tag) multiblockComponent?.writeNbt(tag) } override fun fromClientTag(tag: NbtCompound) { } override fun toClientTag(tag: NbtCompound) { } open inner class MachineEnergyStorage : IREnergyStorage() { override fun getAmount(): Long = energy override fun setAmount(v: Long) { energy = v } override fun getCapacity(): Long = energyCapacity override fun getMaxExtract(side: Direction?): Long = maxOutput override fun getMaxInsert(side: Direction?): Long = maxInput override fun onFinalCommit() { super.onFinalCommit() markForUpdate() } } companion object { const val ENERGY_ID = 0 const val MAX_ENERGY_ID = 1 const val TEMPERATURE_ID = 2 const val MAX_TEMPERATURE_ID = 3 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/Syncable.kt ================================================ package me.steven.indrev.blockentities interface Syncable { fun markForUpdate(condition: () -> Boolean = { true }) } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/cables/BasePipeBlockEntity.kt ================================================ package me.steven.indrev.blockentities.cables import com.google.common.base.Preconditions import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.machine.pipes.BasePipeBlock import me.steven.indrev.networks.Network import me.steven.indrev.registry.IRBlockRegistry import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.client.MinecraftClient import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtList import net.minecraft.nbt.NbtOps import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class BasePipeBlockEntity(val pipeType: Network.Type<*>, tier: Tier, pos: BlockPos, state: BlockState) : BlockEntity(when (tier) { Tier.MK1 -> IRBlockRegistry.COVERABLE_BLOCK_ENTITY_TYPE_MK1 Tier.MK2 -> IRBlockRegistry.COVERABLE_BLOCK_ENTITY_TYPE_MK2 Tier.MK3 -> IRBlockRegistry.COVERABLE_BLOCK_ENTITY_TYPE_MK3 Tier.MK4 -> IRBlockRegistry.COVERABLE_BLOCK_ENTITY_TYPE_MK4 Tier.CREATIVE -> error("no creative cable") }, pos, state), RenderAttachmentBlockEntity { val connections = Object2ObjectOpenHashMap() init { connections.defaultReturnValue(BasePipeBlock.ConnectionType.NONE) } var coverState: BlockState? = null override fun getRenderAttachmentData(): Any { return PipeRenderData( coverState, connections.filterValues { type -> type == BasePipeBlock.ConnectionType.CONNECTED }.keys.toTypedArray() ) } override fun readNbt(tag: NbtCompound) { this.coverState = null if (tag.contains("coverState")) { BlockState.CODEC.decode(NbtOps.INSTANCE, tag.getCompound("coverState")).result().ifPresent { pair -> this.coverState = pair.first } } val list = tag.getList("connections", 10) connections.clear() list?.forEach { t -> t as NbtCompound val dir = Direction.byId(t.getByte("d").toInt()) val type = BasePipeBlock.ConnectionType.byId(t.getByte("c").toInt()) connections[dir] = type } super.readNbt(tag) if (world != null && world!!.isClient) { MinecraftClient.getInstance().worldRenderer.updateBlock(world!!, pos, null, null, 0) } } override fun writeNbt(tag: NbtCompound) { if (this.coverState != null) { BlockState.CODEC.encode(this.coverState, NbtOps.INSTANCE, NbtCompound()).result().ifPresent { t -> tag.put("coverState", t) } } val list = NbtList() connections.forEach { (dir, conn) -> if (conn != BasePipeBlock.ConnectionType.NONE) { val t = NbtCompound() t.putByte("d", dir.id.toByte()) t.putByte("c", conn.id.toByte()) list.add(t) } } tag.put("connections", list) } fun sync() { Preconditions.checkNotNull(world) // Maintain distinct failure case from below check(world is ServerWorld) { "Cannot call sync() on the logical client! Did you check world.isClient first?" } (world as ServerWorld).chunkManager.markForUpdate(getPos()) } override fun toUpdatePacket(): BlockEntityUpdateS2CPacket { return BlockEntityUpdateS2CPacket.create(this) } override fun toInitialChunkDataNbt(): NbtCompound { val nbt = super.toInitialChunkDataNbt() writeNbt(nbt) return nbt } data class PipeRenderData(val cover: BlockState?, val connections: Array) } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/CompressorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.CompressorRecipe import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos class CompressorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.COMPRESSOR_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1500) this.enhancerComponent = EnhancerComponent(intArrayOf(4, 5, 6, 7), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slot = 2 } output { slot = 3 } } trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) } override val type: IRRecipeType = CompressorRecipe.TYPE companion object { const val CRAFTING_COMPONENT_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/CompressorFactoryBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.CraftingComponent import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.multiblock.MultiBlockComponent import me.steven.indrev.components.multiblock.definitions.FactoryStructureDefinition import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.CompressorRecipe import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos class CompressorFactoryBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.COMPRESSOR_FACTORY_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400) this.enhancerComponent = EnhancerComponent(intArrayOf(2, 3, 4, 5), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slots = intArrayOf(6, 8, 10, 12, 14) } output { slots = intArrayOf(7, 9, 11, 13, 15) } } this.craftingComponents = Array(5) { index -> val component = CraftingComponent(index, this).apply { inputSlots = intArrayOf(6 + (index * 2)) outputSlots = intArrayOf(6 + (index * 2) + 1) } trackObject(CRAFTING_COMPONENT_START_ID + index, component) component } this.multiblockComponent = MultiBlockComponent(FactoryStructureDefinition.SELECTOR) } override val syncToWorld: Boolean = true override val type: IRRecipeType = CompressorRecipe.TYPE override fun fromClientTag(tag: NbtCompound) { multiblockComponent?.readNbt(tag) } override fun toClientTag(tag: NbtCompound) { multiblockComponent?.writeNbt(tag) } companion object { const val CRAFTING_COMPONENT_START_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/CondenserBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.FluidComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.CondenserRecipe import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class CondenserBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.CONDENSER_REGISTRY, pos, state) { init { this.enhancerComponent = EnhancerComponent(intArrayOf(3, 4, 5, 6), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { output { slot = 2 } coolerSlot = 1 } this.fluidComponent = object : FluidComponent({ this }, bucket * 8) { init { this.inputTanks = intArrayOf(0) } } trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) trackObject(INPUT_TANK_ID, fluidComponent!![0]) } override val syncToWorld: Boolean = true override val type: IRRecipeType = CondenserRecipe.TYPE override fun getMaxCount(enhancer: Enhancer): Int { return if (enhancer == Enhancer.SPEED) 4 else super.getMaxCount(enhancer) } override fun applyDefault( state: BlockState, type: ConfigurationType, configuration: MutableMap ) { val direction = (state.block as MachineBlock).getFacing(state) when (type) { ConfigurationType.ITEM -> { configuration[direction.rotateYCounterclockwise()] = TransferMode.OUTPUT } else -> super.applyDefault(state, type, configuration) } } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.ITEM -> arrayOf(TransferMode.OUTPUT, TransferMode.NONE) else -> return super.getValidConfigurations(type) } } override fun fromClientTag(tag: NbtCompound) { fluidComponent!!.fromTag(tag) } override fun toClientTag(tag: NbtCompound) { fluidComponent!!.toTag(tag) } companion object { const val CRAFTING_COMPONENT_ID = 2 const val INPUT_TANK_ID = 3 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/CondenserBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.crafters import alexiil.mc.lib.attributes.fluid.render.FluidRenderFace import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Direction class CondenserBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: CondenserBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { val fluidComponent = entity?.fluidComponent ?: return val faces = when (entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) { Direction.NORTH -> NORTH_FACE Direction.SOUTH -> SOUTH_FACE Direction.WEST -> WEST_FACE Direction.EAST -> EAST_FACE else -> return } val volume = fluidComponent[0] if (!volume.isEmpty) { volume.render(faces, vertexConsumers, matrices) } } companion object { private val NORTH_FACE = listOf(FluidRenderFace.createFlatFaceZ(0.815, 0.625, -0.005, 0.19, 0.815, -0.005, 1.0, true, false)) private val SOUTH_FACE = listOf(FluidRenderFace.createFlatFaceZ(0.185, 0.625, 1.005, 0.81, 0.815, 1.005, 1.0, true, false)) private val WEST_FACE = listOf(FluidRenderFace.createFlatFaceX(-0.005, 0.625, 0.185, -0.005, 0.815, 0.81, 1.0, false, false)) private val EAST_FACE = listOf(FluidRenderFace.createFlatFaceX(1.005, 0.625, 0.815, 1.005, 0.815, 0.19, 1.0, false, false)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/CraftingMachineBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import it.unimi.dsi.fastutil.objects.Object2IntArrayMap import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.components.CraftingComponent import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.config.HeatMachineConfig import me.steven.indrev.config.IRConfig import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.IRecipeGetter import me.steven.indrev.recipes.machines.IRRecipe import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.entity.ExperienceOrbEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtList import net.minecraft.recipe.SmeltingRecipe import net.minecraft.server.world.ServerWorld import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import net.minecraft.world.World import java.util.function.IntBinaryOperator import kotlin.math.floor abstract class CraftingMachineBlockEntity(tier: Tier, registry: MachineRegistry, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, registry, pos, state) { override val maxOutput: Long = 0 private var currentRecipe: T? = null val usedRecipes = Object2IntOpenHashMap() abstract val type: IRecipeGetter var craftingComponents = Array(1) { CraftingComponent(0, this) } var isSplitOn = false override fun machineTick() { ticks++ craftingComponents.forEach { it.tick() } workingState = craftingComponents.any { it.isCrafting } if (ticks % 20 == 0 && isSplitOn) { splitStacks() } } override fun getProcessingSpeed(): Double { val isFullEfficiency = temperatureComponent?.isFullEfficiency() == true val baseSpeed = if (isFullEfficiency) ((config as? HeatMachineConfig)?.processTemperatureBoost ?: 1.0) * config.processSpeed else config.processSpeed return baseSpeed + (IRConfig.upgrades.speedUpgradeModifier * (enhancerComponent?.getCount(Enhancer.SPEED) ?: 0)) } override fun getEnergyCost(): Long { val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1) return (if (temperatureComponent?.isFullEfficiency() == true) config.energyCost * 1.5 else config.energyCost).toLong() * speedEnhancers } open fun getMaxCount(enhancer: Enhancer): Int { return when (enhancer) { Enhancer.SPEED -> return 1 Enhancer.BUFFER -> 4 else -> 1 } } open fun splitStacks() { if (craftingComponents.size <= 1) return val inventory = inventoryComponent!!.inventory splitStacks(inventory.inputSlots) } fun splitStacks(inputSlots: IntArray) { if (craftingComponents.size <= 1) return val inventory = inventoryComponent!!.inventory val (item, sum) = inputSlots.associateStacks { inventory.getStack(it) }.maxByOrNull { it.value } ?: return if (sum <= 0) return val freeSlots = inputSlots.filter { inventory.fits(item, it) } var remaining = sum val slotsUsed = freeSlots.size.coerceAtMost(sum) val rem = sum % slotsUsed val isBelowLimit = slotsUsed > freeSlots.size val baseAmount = Math.floorDiv(sum, slotsUsed) freeSlots.forEachIndexed { index, slot -> if (isBelowLimit && index + 1 > slotsUsed) { inventory.setStack(slot, ItemStack.EMPTY) return@forEachIndexed } var set = baseAmount if (rem != 0 && rem > index && remaining > 0) set++ if (index == slotsUsed - 1) set += remaining remaining -= set if (remaining < 0) set += remaining inventory.setStack(slot, ItemStack(item, set)) } } private inline fun IntArray.associateStacks(transform: (Int) -> ItemStack): Map { return associateToStacks(Object2IntArrayMap(5), transform) } private inline fun > IntArray.associateToStacks(destination: M, transform: (Int) -> ItemStack): M { for (element in this) { val stack = transform(element) if (!stack.isEmpty && stack.nbt?.isEmpty != false) destination.mergeInt(stack.item, stack.count, IntBinaryOperator { old, new -> old + new }) } return destination } override fun fromTag(tag: NbtCompound) { val craftTags = tag.getList("craftingComponents", 10) craftTags?.forEach { craftTag -> val index = (craftTag as NbtCompound).getInt("index") craftingComponents[index].readNbt(craftTag) } isSplitOn = tag.getBoolean("split") super.fromTag(tag) } override fun toTag(tag: NbtCompound) { val craftTags = NbtList() craftingComponents.forEachIndexed { index, crafting -> val craftTag = NbtCompound() craftTags.add(crafting.writeNbt(craftTag)) craftTag.putInt("index", index) } tag.put("craftingComponents", craftTags) tag.putBoolean("split", isSplitOn) return super.toTag(tag) } @Suppress("UNCHECKED_CAST") fun dropExperience(player: PlayerEntity) { val list = mutableListOf() usedRecipes.forEach { (id, amount) -> for (recipes in world!!.recipeManager.recipes.values) { val recipe = recipes[id] ?: continue list.add(recipe as? T ?: continue) if (recipe is SmeltingRecipe) spawnOrbs(world!!, player.pos, amount, recipe.experience) break } } player.unlockRecipes(list.toList()) usedRecipes.clear() } private fun spawnOrbs(world: World, pos: Vec3d, amount: Int, experience: Float) { val xp = amount.toFloat() * experience var n = floor(xp).toInt() val decimal = xp % 1 if (decimal != 0.0f && Math.random() < decimal.toDouble()) { ++n } ExperienceOrbEntity.spawn(world as ServerWorld, pos, n) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/ElectricFurnaceBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.mixin.common.MixinAbstractCookingRecipe import me.steven.indrev.recipes.IRecipeGetter import me.steven.indrev.recipes.machines.VanillaCookingRecipeCachedGetter import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos class ElectricFurnaceBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.ELECTRIC_FURNACE_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.1, 1300..1700, 2000) this.enhancerComponent = EnhancerComponent(intArrayOf(4, 5, 6, 7), Enhancer.FURNACE, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slot = 2 } output { slot = 3 } } trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) } @Suppress("UNCHECKED_CAST") override val type: IRecipeGetter get() { val upgrades = enhancerComponent!!.enhancers return when (upgrades.keys.firstOrNull { it == Enhancer.BLAST_FURNACE || it == Enhancer.SMOKER }) { Enhancer.BLAST_FURNACE -> VanillaCookingRecipeCachedGetter.BLASTING Enhancer.SMOKER -> VanillaCookingRecipeCachedGetter.SMOKING else -> VanillaCookingRecipeCachedGetter.SMELTING } as IRecipeGetter } companion object { const val CRAFTING_COMPONENT_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/ElectricFurnaceFactoryBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.CraftingComponent import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.multiblock.MultiBlockComponent import me.steven.indrev.components.multiblock.definitions.FactoryStructureDefinition import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.mixin.common.MixinAbstractCookingRecipe import me.steven.indrev.recipes.IRecipeGetter import me.steven.indrev.recipes.machines.VanillaCookingRecipeCachedGetter import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos class ElectricFurnaceFactoryBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.ELECTRIC_FURNACE_FACTORY_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.1, 1300..1700, 2000) this.enhancerComponent = EnhancerComponent(intArrayOf(2, 3, 4, 5), Enhancer.FURNACE, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slots = intArrayOf(6, 8, 10, 12, 14) } output { slots = intArrayOf(7, 9, 11, 13, 15) } } this.craftingComponents = Array(5) { index -> val component = CraftingComponent(index, this).apply { inputSlots = intArrayOf(6 + (index * 2)) outputSlots = intArrayOf(6 + (index * 2) + 1) } trackObject(CRAFTING_COMPONENT_START_ID + index, component) component } this.multiblockComponent = MultiBlockComponent(FactoryStructureDefinition.SELECTOR) } override val syncToWorld: Boolean = true @Suppress("UNCHECKED_CAST") override val type: IRecipeGetter get() { val upgrades = enhancerComponent!!.enhancers return when (upgrades.keys.firstOrNull { it == Enhancer.BLAST_FURNACE || it == Enhancer.SMOKER }) { Enhancer.BLAST_FURNACE -> VanillaCookingRecipeCachedGetter.BLASTING Enhancer.SMOKER -> VanillaCookingRecipeCachedGetter.SMOKING else -> VanillaCookingRecipeCachedGetter.SMELTING } as IRecipeGetter } override fun fromClientTag(tag: NbtCompound) { multiblockComponent?.readNbt(tag) } override fun toClientTag(tag: NbtCompound) { multiblockComponent?.writeNbt(tag) } companion object { const val CRAFTING_COMPONENT_START_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/ElectrolyticSeparatorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.FluidComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.ElectrolysisRecipe import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class ElectrolyticSeparatorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.ELECTROLYTIC_SEPARATOR_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 500..700, 900) this.enhancerComponent = EnhancerComponent(intArrayOf(1, 2, 3, 4), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { coolerSlot = 0 } this.fluidComponent = ElectrolyticSeparatorFluidComponent() trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) trackObject(INPUT_TANK_ID, fluidComponent!![0]) trackObject(FIRST_OUTPUT_TANK_ID, fluidComponent!![1]) trackObject(SECOND_OUTPUT_TANK_ID, fluidComponent!![2]) } override val type: IRRecipeType = ElectrolysisRecipe.TYPE override fun applyDefault( state: BlockState, type: ConfigurationType, configuration: MutableMap ) { if (type != ConfigurationType.ITEM) super.applyDefault(state, type, configuration) } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.FLUID -> TransferMode.ELECTROLYTIC_SEPARATOR else -> return super.getValidConfigurations(type) } } inner class ElectrolyticSeparatorFluidComponent : FluidComponent({ this }, bucket * 4, 3) { init { this.inputTanks = intArrayOf(0) this.outputTanks = intArrayOf(1, 2) } override fun getValidTanks(dir: Direction): IntArray { return when (transferConfig[dir]!!) { TransferMode.OUTPUT_FIRST -> intArrayOf(1) TransferMode.OUTPUT_SECOND -> intArrayOf(2) else -> super.getValidTanks(dir) } } } companion object { const val CRAFTING_COMPONENT_ID = 4 const val INPUT_TANK_ID = 5 const val FIRST_OUTPUT_TANK_ID = 6 const val SECOND_OUTPUT_TANK_ID = 7 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/FluidInfuserBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.FluidComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.FluidInfuserRecipe import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos class FluidInfuserBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.FLUID_INFUSER_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400) this.enhancerComponent = EnhancerComponent(intArrayOf(4, 5, 6, 7), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slot = 2 } output { slot = 3 } } this.fluidComponent = FluidInfuserFluidComponent() trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) trackObject(INPUT_TANK_ID, fluidComponent!![0]) trackObject(OUTPUT_TANK_ID, fluidComponent!![1]) } override val syncToWorld: Boolean = true override val type: IRRecipeType = FluidInfuserRecipe.TYPE override fun fromClientTag(tag: NbtCompound) { fluidComponent!!.fromTag(tag) } override fun toClientTag(tag: NbtCompound) { fluidComponent!!.toTag(tag) } inner class FluidInfuserFluidComponent : FluidComponent({ this }, bucket * 8 , 2) { init { this.inputTanks = intArrayOf(0) this.outputTanks = intArrayOf(1) } } companion object { const val CRAFTING_COMPONENT_ID = 4 const val INPUT_TANK_ID = 5 const val OUTPUT_TANK_ID = 6 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/FluidInfuserBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.crafters import alexiil.mc.lib.attributes.fluid.render.FluidRenderFace import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Direction class FluidInfuserBlockEntityRenderer : BlockEntityRenderer{ override fun render( entity: FluidInfuserBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { val fluidComponent = entity?.fluidComponent ?: return val inputFace = when (entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) { Direction.NORTH -> INPUT_NORTH_FACE Direction.SOUTH -> INPUT_SOUTH_FACE Direction.WEST -> INPUT_WEST_FACE Direction.EAST -> INPUT_EAST_FACE else -> return } val inputVolume = fluidComponent[0] if (!inputVolume.isEmpty) { inputVolume.render(inputFace, vertexConsumers, matrices) } val outputFace = when (entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) { Direction.NORTH -> OUTPUT_NORTH_FACE Direction.SOUTH -> OUTPUT_SOUTH_FACE Direction.WEST -> OUTPUT_WEST_FACE Direction.EAST -> OUTPUT_EAST_FACE else -> return } val outputVolume = fluidComponent[1] if (!outputVolume.isEmpty) { outputVolume.render(outputFace, vertexConsumers, matrices) } } companion object { private val INPUT_NORTH_FACE = listOf(FluidRenderFace.createFlatFaceZ(0.815, 0.690, -0.005, 0.19, 0.815, -0.005, 1.0, true, false)) private val INPUT_SOUTH_FACE = listOf(FluidRenderFace.createFlatFaceZ(0.185, 0.690, 1.005, 0.81, 0.815, 1.005, 1.0, true, false)) private val INPUT_WEST_FACE = listOf(FluidRenderFace.createFlatFaceX(-0.005, 0.690, 0.185, -0.005, 0.815, 0.81, 1.0, false, false)) private val INPUT_EAST_FACE = listOf(FluidRenderFace.createFlatFaceX(1.005, 0.690, 0.815, 1.005, 0.815, 0.19, 1.0, false, false)) private val OUTPUT_NORTH_FACE = listOf(FluidRenderFace.createFlatFaceZ(0.815, 0.190, -0.005, 0.19, 0.32, -0.005, 1.0, true, false)) private val OUTPUT_SOUTH_FACE = listOf(FluidRenderFace.createFlatFaceZ(0.185, 0.190, 1.005, 0.81, 0.32, 1.005, 1.0, true, false)) private val OUTPUT_WEST_FACE = listOf(FluidRenderFace.createFlatFaceX(-0.005, 0.190, 0.185, -0.005, 0.32, 0.81, 1.0, false, false)) private val OUTPUT_EAST_FACE = listOf(FluidRenderFace.createFlatFaceX(1.005, 0.190, 0.815, 1.005, 0.32, 0.19, 1.0, false, false)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/PulverizerBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.recipes.machines.PulverizerRecipe import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos class PulverizerBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.PULVERIZER_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400) this.enhancerComponent = EnhancerComponent(intArrayOf(5, 6, 7, 8), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slot = 2 } output { slots = intArrayOf(3, 4) } } trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) } override val type: IRRecipeType = PulverizerRecipe.TYPE companion object { const val CRAFTING_COMPONENT_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/PulverizerFactoryBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.CraftingComponent import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.multiblock.MultiBlockComponent import me.steven.indrev.components.multiblock.definitions.FactoryStructureDefinition import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.recipes.machines.PulverizerRecipe import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos class PulverizerFactoryBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.PULVERIZER_FACTORY_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400) this.enhancerComponent = EnhancerComponent(intArrayOf(2, 3, 4, 5), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slots = intArrayOf(6, 8, 10, 12, 14) } output { slots = intArrayOf(7, 9, 11, 13, 15) } } this.craftingComponents = Array(5) { index -> val component = CraftingComponent(index, this).apply { inputSlots = intArrayOf(6 + (index * 2)) outputSlots = intArrayOf(6 + (index * 2) + 1) } trackObject(CRAFTING_COMPONENT_START_ID + index, component) component } this.multiblockComponent = MultiBlockComponent(FactoryStructureDefinition.SELECTOR) } override val syncToWorld: Boolean = true override val type: IRRecipeType = PulverizerRecipe.TYPE override fun fromClientTag(tag: NbtCompound) { multiblockComponent?.readNbt(tag) } override fun toClientTag(tag: NbtCompound) { multiblockComponent?.writeNbt(tag) } companion object { const val CRAFTING_COMPONENT_START_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/RecyclerBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.recipes.machines.RecyclerRecipe import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos class RecyclerBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.RECYCLER_REGISTRY, pos, state) { init { this.enhancerComponent = object : EnhancerComponent(intArrayOf(4, 5, 6, 7), Enhancer.DEFAULT, this::getMaxCount) { override fun isLocked(slot: Int, tier: Tier): Boolean = false } this.inventoryComponent = inventory(this) { input { slot = 2 } output { slot = 3 } coolerSlot = 1 } trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) } override val type: IRRecipeType = RecyclerRecipe.TYPE companion object { const val CRAFTING_COMPONENT_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/SawmillBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.recipes.machines.SawmillRecipe import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos class SawmillBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.SAWMILL_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400) this.enhancerComponent = EnhancerComponent(intArrayOf(7, 8, 9, 10), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slot = 2 } output { slots = intArrayOf(3, 4, 5, 6) } } trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) } override val type: IRRecipeType = SawmillRecipe.TYPE companion object { const val CRAFTING_COMPONENT_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/SmelterBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.FluidComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.recipes.machines.SmelterRecipe import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class SmelterBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.SMELTER_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.2, 1700..2500, 2700) this.enhancerComponent = EnhancerComponent(intArrayOf(3, 4, 5, 6), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slot = 2 } } this.fluidComponent = object : FluidComponent({ this }, bucket * 8) { init { this.outputTanks = intArrayOf(0) } } trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) trackObject(TANK_ID, fluidComponent!![0]) } override val type: IRRecipeType = SmelterRecipe.TYPE override fun getMaxCount(enhancer: Enhancer): Int { return if (enhancer == Enhancer.SPEED) 4 else super.getMaxCount(enhancer) } override fun applyDefault( state: BlockState, type: ConfigurationType, configuration: MutableMap ) { val direction = (state.block as MachineBlock).getFacing(state) when (type) { ConfigurationType.ITEM -> { configuration[direction.rotateYClockwise()] = TransferMode.INPUT } else -> super.applyDefault(state, type, configuration) } } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.ITEM -> arrayOf(TransferMode.INPUT, TransferMode.NONE) else -> return super.getValidConfigurations(type) } } companion object { const val CRAFTING_COMPONENT_ID = 4 const val TANK_ID = 5 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/SolidInfuserBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.recipes.machines.InfuserRecipe import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class SolidInfuserBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.SOLID_INFUSER_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400) this.enhancerComponent = EnhancerComponent(intArrayOf(5, 6, 7, 8), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slots = intArrayOf(2, 3) filter { _, dir, slot -> canInput(dir, slot) } } output { slot = 4 } } trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0]) } private fun canInput(side: Direction?, slot: Int): Boolean { if (side == null) return true return when (inventoryComponent!!.itemConfig[side]) { TransferMode.INPUT_FIRST -> slot == 2 TransferMode.INPUT_SECOND -> slot == 3 else -> true } } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.ITEM -> TransferMode.SOLID_INFUSER else -> super.getValidConfigurations(type) } } override val type: IRRecipeType = InfuserRecipe.TYPE companion object { const val CRAFTING_COMPONENT_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/crafters/SolidInfuserFactoryBlockEntity.kt ================================================ package me.steven.indrev.blockentities.crafters import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.components.CraftingComponent import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.multiblock.MultiBlockComponent import me.steven.indrev.components.multiblock.definitions.FactoryStructureDefinition import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.machines.IRRecipeType import me.steven.indrev.recipes.machines.InfuserRecipe import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class SolidInfuserFactoryBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : CraftingMachineBlockEntity(tier, MachineRegistry.SOLID_INFUSER_FACTORY_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400) this.enhancerComponent = EnhancerComponent(intArrayOf(2, 3, 4, 5), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slots = intArrayOf(6, 7, 9, 10, 12, 13, 15, 16, 18, 19) filter { _, dir, slot -> canInput(dir, slot) } } output { slots = intArrayOf(8, 11, 14, 17, 20) } } this.craftingComponents = Array(5) { index -> val component = CraftingComponent(index, this).apply { inputSlots = intArrayOf(6 + (index * 3), 6 + (index * 3) + 1) outputSlots = intArrayOf(6 + (index * 3) + 2) } trackObject(CRAFTING_COMPONENT_START_ID + index, component) component } this.multiblockComponent = MultiBlockComponent(FactoryStructureDefinition.SELECTOR) } override val syncToWorld: Boolean = true override fun splitStacks() { splitStacks(TOP_SLOTS) splitStacks(BOTTOM_SLOTS) } private fun canInput(side: Direction?, slot: Int): Boolean { if (side == null) return true return when (inventoryComponent!!.itemConfig[side]) { TransferMode.INPUT_FIRST -> TOP_SLOTS.contains(slot) TransferMode.INPUT_SECOND -> BOTTOM_SLOTS.contains(slot) else -> true } } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.ITEM -> TransferMode.SOLID_INFUSER else -> super.getValidConfigurations(type) } } override val type: IRRecipeType = InfuserRecipe.TYPE override fun fromClientTag(tag: NbtCompound) { multiblockComponent?.readNbt(tag) } override fun toClientTag(tag: NbtCompound) { multiblockComponent?.writeNbt(tag) } companion object { val TOP_SLOTS = intArrayOf(6, 9, 12, 15, 18) val BOTTOM_SLOTS = intArrayOf(7, 10, 13, 16, 19) const val CRAFTING_COMPONENT_START_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/AOEMachineBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import io.netty.buffer.Unpooled import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.config.IConfig import me.steven.indrev.packets.common.UpdateAOEMachineRangePacket import me.steven.indrev.registry.MachineRegistry import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandlerContext import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box abstract class AOEMachineBlockEntity(tier: Tier, registry: MachineRegistry, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, registry, pos, state) { override val syncToWorld: Boolean = true var renderWorkingArea = false abstract var range: Int open fun getWorkingArea(): Box { val box = Box(pos) return box.expand(range.toDouble(), 0.0, range.toDouble()).stretch(0.0, range.toDouble() * 2, 0.0) } override fun toTag(tag: NbtCompound) { tag.putInt("range", range) super.toTag(tag) } override fun toClientTag(tag: NbtCompound) { tag.putInt("range", range) } override fun fromTag(tag: NbtCompound) { super.fromTag(tag) range = tag.getInt("range") } override fun fromClientTag(tag: NbtCompound) { range = tag.getInt("range") } companion object { fun sendValueUpdatePacket(value: Int, ctx: ScreenHandlerContext) { if (value > 0) { val packet = PacketByteBuf(Unpooled.buffer()) packet.writeInt(value) ctx.run { _, pos -> packet.writeBlockPos(pos) } ClientPlayNetworking.send(UpdateAOEMachineRangePacket.UPDATE_VALUE_PACKET_ID, packet) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/AOEMachineBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.farms import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.math.MatrixStack open class AOEMachineBlockEntityRenderer : BlockEntityRenderer> { override fun render( blockEntity: AOEMachineBlockEntity<*>, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int ) { if (blockEntity.renderWorkingArea) { val pos = blockEntity.pos val area = blockEntity.getWorkingArea().offset(-pos.x.toDouble(), -pos.y.toDouble(), -pos.z.toDouble()) val vc = vertexConsumers.getBuffer(RenderLayer.getLines()) WorldRenderer.drawBox(matrices, vc, area, 1f, 0f, 1f, 1f) } } override fun rendersOutsideBoundingBox(blockEntity: AOEMachineBlockEntity<*>?): Boolean = true } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/BiomassComposterBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import alexiil.mc.lib.attributes.fluid.amount.FluidAmount import alexiil.mc.lib.attributes.fluid.render.FluidRenderFace import alexiil.mc.lib.attributes.fluid.volume.FluidKeys import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.blocks.misc.BiomassComposterBlock import me.steven.indrev.registry.IRBlockRegistry import me.steven.indrev.registry.IRFluidRegistry import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.bucket import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext import net.minecraft.block.BlockState import net.minecraft.block.ComposterBlock import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.util.math.MatrixStack import net.minecraft.fluid.Fluids import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos class BiomassComposterBlockEntity(pos: BlockPos, state: BlockState) : BaseBlockEntity(IRBlockRegistry.BIOMASS_COMPOSTER_BLOCK_ENTITY, pos, state) { var ticks = 0 var level = 0 val fluidInv = BiomassComposterFluidInv() val itemInv = BiomassComposterItemInv() companion object { fun tick(state: BlockState, blockEntity: BiomassComposterBlockEntity) { val vol = blockEntity.fluidInv if (blockEntity.isInProgress()) { blockEntity.ticks++ } if (blockEntity.isReady() && state[BiomassComposterBlock.CLOSED] && vol.amount > 0 && vol.resource.isOf(Fluids.WATER)) { blockEntity.fluidInv.variant = FluidVariant.of(IRFluidRegistry.METHANE_STILL) blockEntity.reset() } else if (blockEntity.isReady() && !blockEntity.hasFluids()) { blockEntity.itemInv.amount = 1 blockEntity.itemInv.variant = ItemVariant.of(IRItemRegistry.BIOMASS) blockEntity.reset() } blockEntity.doneInsertionThisTick = false } } private var doneInsertionThisTick = false private fun hasFluids() = !fluidInv.isResourceBlank && fluidInv.amount > 0 fun isReady() = level >= 7 && ticks >= getProgressTime() fun reset() { level = 0 ticks = 0 markDirty() if (!world!!.isClient) sync() } fun isInProgress(): Boolean { return when { level < 7 -> false cachedState[BiomassComposterBlock.CLOSED] -> fluidInv.variant.isOf(Fluids.WATER) else -> !hasFluids() } && ticks < getProgressTime() } fun getProgressTime() = if (!cachedState[BiomassComposterBlock.CLOSED]) 120 else 440 inner class BiomassComposterFluidInv : SingleVariantStorage() { override fun canInsert(variant: FluidVariant): Boolean = variant.isOf(Fluids.WATER) override fun canExtract(variant: FluidVariant): Boolean = variant.isOf(IRFluidRegistry.METHANE_STILL) override fun getCapacity(variant: FluidVariant?): Long = bucket override fun getBlankVariant(): FluidVariant = FluidVariant.blank() fun render(faces: List?, vcp: VertexConsumerProvider?, matrices: MatrixStack?) { if (!variant.isBlank) FluidKeys.get(variant.fluid).withAmount(FluidAmount.BUCKET).render(faces, vcp, matrices) } } inner class BiomassComposterItemInv : SingleVariantStorage() { override fun canInsert(variant: ItemVariant): Boolean = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.contains(variant.item) && level < 7 && !doneInsertionThisTick override fun insert(insertedVariant: ItemVariant, maxAmount: Long, transaction: TransactionContext?): Long { StoragePreconditions.notBlankNotNegative(insertedVariant, maxAmount) if ((insertedVariant == variant || variant.isBlank) && canInsert(insertedVariant)) { val chance = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.getValue(variant.item) val insertedAmount = maxAmount.coerceAtMost(getCapacity(insertedVariant) - amount) var actuallyInserted = 0L for (x in 0 until insertedAmount) { if (world!!.random.nextDouble() < chance) { actuallyInserted++ } } if (insertedAmount > 0) { updateSnapshots(transaction) if (variant.isBlank) { variant = insertedVariant amount = actuallyInserted } else { amount += actuallyInserted } doneInsertionThisTick = true } return insertedAmount } return 0 } override fun getCapacity(variant: ItemVariant): Long = 7 - level.toLong() override fun canExtract(variant: ItemVariant): Boolean = variant.isOf(IRItemRegistry.BIOMASS) override fun getBlankVariant(): ItemVariant = ItemVariant.blank() } override fun toTag(tag: NbtCompound) { tag.putInt("ticks", ticks) tag.putInt("level", level) tag.put("fluidVariant", fluidInv.variant.toNbt()) tag.putLong("fluidAmt", fluidInv.amount) tag.put("itemVariant", itemInv.variant.toNbt()) tag.putLong("itemAmt", itemInv.amount) } override fun fromTag(tag: NbtCompound) { ticks = tag.getInt("ticks") level = tag.getInt("level") fluidInv.variant = FluidVariant.fromNbt(tag.getCompound("fluidVariant")) fluidInv.amount = tag.getLong("fluidAmt") itemInv.variant = ItemVariant.fromNbt(tag.getCompound("itemVariant")) itemInv.amount = tag.getLong("itemAmt") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/BiomassComposterBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.farms import alexiil.mc.lib.attributes.fluid.render.FluidRenderFace import me.steven.indrev.blocks.misc.BiomassComposterBlock import me.steven.indrev.utils.bucket import me.steven.indrev.utils.identifier import net.minecraft.client.MinecraftClient import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.ModelIdentifier import net.minecraft.client.util.math.MatrixStack class BiomassComposterBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: BiomassComposterBlockEntity, tickDelta: Float, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int ) { if (entity.cachedState[BiomassComposterBlock.CLOSED]) return matrices.run { if (entity.level > 0) { push() translate(0.0, ((((entity.level - 1) * 2)) / 16.0), 0.0) val model = MinecraftClient.getInstance().bakedModelManager.getModel(ModelIdentifier(identifier("composting"), "")) MinecraftClient.getInstance().blockRenderManager.modelRenderer.render( matrices.peek(), vertexConsumers.getBuffer(RenderLayer.getTranslucent()), null, model, -1f, -1f, -1f, WorldRenderer.getLightmapCoordinates(entity.world, entity.pos.up()), OverlayTexture.DEFAULT_UV ) pop() } push() val vol = entity.fluidInv.amount / bucket.toDouble() translate(0.0, vol, 0.0) entity.fluidInv.render(FACE, vertexConsumers, matrices) pop() } } companion object { private val FACE = listOf(FluidRenderFace.createFlatFaceY(0.125, 0.0, 0.125, 0.875, 0.0, 0.875, 1.0, true, false)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/ChopperBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.* import net.minecraft.block.* import net.minecraft.item.AxeItem import net.minecraft.item.BlockItem import net.minecraft.item.BoneMealItem import net.minecraft.item.ItemStack import net.minecraft.item.SwordItem import net.minecraft.loot.context.LootContext import net.minecraft.loot.context.LootContextParameters import net.minecraft.server.world.ServerWorld import net.minecraft.tag.BlockTags import net.minecraft.tag.ItemTags import net.minecraft.util.ItemScatterer import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.world.chunk.Chunk class ChopperBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : AOEMachineBlockEntity(tier, MachineRegistry.CHOPPER_REGISTRY, pos, state) { init { this.enhancerComponent = EnhancerComponent(intArrayOf(15, 16, 17, 18), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slots = intArrayOf(2, 3, 4, 5) //todo tool tags? 2 filter { stack -> stack.item is AxeItem || stack.item is SwordItem } 3 filter { (_, item) -> item is BoneMealItem } 4..5 filter { (stack, item), _ -> stack.isIn(ItemTags.SAPLINGS) || (item is BlockItem && (item.block is MushroomPlantBlock || item.block is BambooBlock)) } } output { slots = intArrayOf(6, 7, 8, 9, 10, 11, 12, 13, 14) } coolerSlot = 1 } } override val maxInput: Long = config.maxInput override val maxOutput: Long = 0 private var scheduledBlocks = mutableListOf().iterator() private val scannedBlocks = mutableSetOf() override var range = 5 var cooldown = 0.0 override fun machineTick() { if (world?.isClient == true) return val inventory = inventoryComponent?.inventory ?: return cooldown += getProcessingSpeed() if (cooldown < config.processSpeed || ticks % 15 != 0 || !canUse(getEnergyCost())) return val area = getWorkingArea() if (!scheduledBlocks.hasNext()) { // includes tree branches that goes outside the actual area val fullArea = area.expand(4.0).shrink(0.0, -4.0, 0.0) scheduledBlocks = fullArea.map(::BlockPos).iterator() scannedBlocks.clear() } else { var currentChunk: Chunk? = null var performedActions = 0 val axeStack = inventory.getStack(2) val brokenBlocks = hashMapOf() while (scheduledBlocks.hasNext() && cooldown >= config.processSpeed) { val pos = scheduledBlocks.next() if (!scannedBlocks.add(pos)) continue if (pos.x shr 4 != currentChunk?.pos?.x || pos.z shr 4 != currentChunk.pos.z) { currentChunk = world?.getChunk(pos) } val blockState = currentChunk?.getBlockState(pos) ?: continue if (axeStack != null && !axeStack.isEmpty && tryChop(axeStack, pos, blockState, currentChunk) ) { cooldown -= config.processSpeed if (!use(getEnergyCost())) break brokenBlocks[pos] = blockState performedActions++ } if (pos.y == this.pos.y && pos in area) { for (slot in 3..5) { val stack = inventory.getStack(slot) if (stack.isEmpty || !tryUse(blockState, stack, pos)) continue cooldown -= config.processSpeed if (!use(getEnergyCost())) break brokenBlocks[pos] = blockState performedActions++ } } } brokenBlocks.forEach { (blockPos, blockState) -> val droppedStacks = blockState.getDroppedStacks( LootContext.Builder(world as ServerWorld).random(world?.random) .parameter(LootContextParameters.ORIGIN, blockPos.toVec3d()) .parameter(LootContextParameters.TOOL, axeStack) ) droppedStacks.forEach { if (!inventory.output(it)) ItemScatterer.spawn(world, blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(), it) } } temperatureComponent?.tick(performedActions > 0) workingState = performedActions > 0 } cooldown = 0.0 } private fun tryChop( toolStack: ItemStack, blockPos: BlockPos, blockState: BlockState, chunk: Chunk ): Boolean { fun damageTool(amount: Int): Boolean { return when { energyOf(toolStack) != null -> energyOf(toolStack)!!.use(amount.toLong()) toolStack.isEmpty -> false toolStack.isDamageable -> { toolStack.damage(amount, world?.random, null) if (toolStack.damage >= toolStack.maxDamage) toolStack.decrement(1) true } else -> true } } val block = blockState.block when { toolStack.item is AxeItem && (blockState.isIn(BlockTags.LOGS) || block is MushroomBlock || block == Blocks.MUSHROOM_STEM) -> { if (!damageTool(1)) return false world?.setBlockState(blockPos, Blocks.AIR.defaultState, 3) } block is LeavesBlock -> { world?.setBlockState(blockPos, Blocks.AIR.defaultState, 3) } toolStack.item is SwordItem && block is BambooBlock && blockPos.y > pos.y -> { val upPos = blockPos.up() val up = chunk.getBlockState(upPos) scannedBlocks.add(upPos) if (up.isOf(block)) tryChop(toolStack, upPos, blockState, chunk) if (!damageTool(2)) return false world?.setBlockState(blockPos, Blocks.AIR.defaultState, 3) } else -> return false } return true } private fun tryUse(blockState: BlockState, itemStack: ItemStack, pos: BlockPos): Boolean { val item = itemStack.item val block = blockState.block when { item is BoneMealItem && itemStack.count > 1 && (blockState.isIn(BlockTags.SAPLINGS) || block is MushroomPlantBlock || block is BambooBlock || block is BambooSaplingBlock) && block is Fertilizable && block.isFertilizable(world, pos, blockState, false) && block.canGrow(world, world?.random, pos, blockState) -> { block.grow(world as ServerWorld, world?.random, pos, blockState) world?.syncWorldEvent(2005, pos, 0) itemStack.decrement(1) } block is AirBlock && item is BlockItem && (itemStack.isIn(ItemTags.SAPLINGS) || item.block is MushroomPlantBlock || item.block is BambooBlock) && item.block.defaultState.canPlaceAt(world, pos) && itemStack.count > 1 -> { if (item.block is BambooBlock) world?.setBlockState(pos, Blocks.BAMBOO_SAPLING.defaultState, 3) else world?.setBlockState(pos, item.block.defaultState, 3) itemStack.decrement(1) } else -> return false } return true } override fun getEnergyCost(): Long { val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1) return config.energyCost * speedEnhancers } override fun getWorkingArea(): Box { val box = Box(pos) return box.expand(range.toDouble(), 0.0, range.toDouble()).stretch(0.0, 40.0, 0.0) } fun getMaxCount(enhancer: Enhancer): Int { return when (enhancer) { Enhancer.SPEED -> return 12 Enhancer.BUFFER -> 4 else -> 1 } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/ChopperBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.farms import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.client.util.math.MatrixStack class ChopperBlockEntityRenderer : AOEMachineBlockEntityRenderer() { override fun render( blockEntity: AOEMachineBlockEntity<*>, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int ) { super.render(blockEntity, tickDelta, matrices, vertexConsumers, light, overlay) if (blockEntity.renderWorkingArea) { val pos = blockEntity.pos val area = blockEntity.getWorkingArea().offset(-pos.x.toDouble(), -pos.y.toDouble(), -pos.z.toDouble()).expand(4.0, 0.0, 4.0) val vc = vertexConsumers.getBuffer(RenderLayer.getLines()) WorldRenderer.drawBox(matrices, vc, area, 1f, 0.2f, 0.2f, 1f) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/DirtOxygenatorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blocks.machine.FacingMachineBlock import me.steven.indrev.config.MachineConfig import me.steven.indrev.registry.IRFluidRegistry import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage import net.minecraft.block.BlockState import net.minecraft.block.Fertilizable import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos class DirtOxygenatorBlockEntity(pos: BlockPos, state: BlockState) : MachineBlockEntity(Tier.MK1, MachineRegistry.DIRT_OXYGENATOR_REGISTRY, pos, state) { private var streak = 0 private var ticksUntilReset = 40 val fluidInv = FluidStorage() override fun machineTick() { val target = pos.offset(cachedState[FacingMachineBlock.FACING]).up() val targetState = world!!.getBlockState(target) val block = targetState.block if (block !is Fertilizable || !targetState.canPlaceAt(world, target)) return if (fluidInv.amount <= 0) { ticksUntilReset-- if (ticksUntilReset <= 0) streak = 0 return } else { streak++ ticksUntilReset = 40 } val chance = (streak / 300.0).coerceIn(0.0, 1.0) * 0.6 if (world!!.random.nextDouble() < chance) { if ( block.isFertilizable(world, pos, targetState, false) && block.canGrow(world, world!!.random, target, targetState) ) { block.grow(world as ServerWorld, world!!.random, target, targetState) } } fluidInv.variant = FluidVariant.blank() fluidInv.amount = 0 } inner class FluidStorage : SingleVariantStorage() { override fun canExtract(variant: FluidVariant): Boolean = false override fun canInsert(variant: FluidVariant): Boolean = variant.isOf(IRFluidRegistry.OXYGEN_STILL) override fun getCapacity(variant: FluidVariant): Long = bucket override fun getBlankVariant(): FluidVariant = FluidVariant.blank() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/DrainBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.components.FluidComponent import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import me.steven.indrev.utils.contains import me.steven.indrev.utils.drainFluid import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.block.BlockState import net.minecraft.block.FluidBlock import net.minecraft.block.FluidDrainable import net.minecraft.fluid.FlowableFluid import net.minecraft.fluid.Fluid import net.minecraft.fluid.Fluids import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction class DrainBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, MachineRegistry.DRAIN_REGISTRY, pos, state) { init { this.fluidComponent = object : FluidComponent({ this }, bucket) { init { this.outputTanks = intArrayOf(0) } } } override val maxInput: Long = config.maxInput override fun machineTick() { val world = world ?: return val fluidComponent = fluidComponent ?: return if (ticks % 20 == 0 || !fluidComponent[0].isEmpty) return val fluidState = world.getFluidState(pos.up()) if (fluidState?.isEmpty == false) { val range = getWorkingArea() // DOWN is intentionally excluded val directions = arrayOf(Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) val mutablePos = pos.mutableCopy() val fluid = getStill(fluidState.fluid) val bfs = mutableListOf(pos.up()) var i = 0 while (i < bfs.size) { val current = bfs[i++] for (dir in directions) { mutablePos.set(current, dir) if (mutablePos !in bfs && mutablePos in range && getStill(world.getFluidState(mutablePos).fluid) === fluid) { bfs.add(mutablePos.toImmutable()) } } } bfs.sortByDescending { it.y } for (pos in bfs) { val blockState = world.getBlockState(pos) val block = blockState?.block if (block is FluidDrainable && block is FluidBlock) { val drained = block.drainFluid(world, pos, blockState) if (drained != Fluids.EMPTY) { fluidComponent[0].insert(FluidVariant.of(drained), bucket, true) return } } } } } private fun getStill(fluid: Fluid): Fluid = if (fluid is FlowableFluid) fluid.still else fluid fun getWorkingArea(): Box = Box(pos.up()).expand(8.0, 0.0, 8.0).stretch(0.0, 4.0, 0.0) override fun applyDefault( state: BlockState, type: ConfigurationType, configuration: MutableMap ) { Direction.values().forEach { dir -> configuration[dir] = TransferMode.OUTPUT } } override fun isFixed(type: ConfigurationType): Boolean = true } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/FarmerBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.map import me.steven.indrev.utils.toVec3d import net.minecraft.block.* import net.minecraft.item.BlockItem import net.minecraft.item.BoneMealItem import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.loot.context.LootContext import net.minecraft.loot.context.LootContextParameters import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction class FarmerBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : AOEMachineBlockEntity(tier, MachineRegistry.FARMER_REGISTRY, pos, state) { init { this.enhancerComponent = EnhancerComponent(intArrayOf(14, 15, 16, 17), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slots = intArrayOf(1, 2, 3, 4) } output { slots = intArrayOf(5, 6, 7, 8, 9, 10, 11, 12, 13) } } } override val maxInput: Long = config.maxInput override val maxOutput: Long = 0 override var range: Int = 5 var cooldown = 0.0 private var nextBlocks = emptyList().iterator() override fun machineTick() { cooldown += getProcessingSpeed() if (cooldown < config.processSpeed) return val world = world as ServerWorld val energyCost = config.energyCost if (!canUse(energyCost)) return if (nextBlocks.hasNext()) { while (nextBlocks.hasNext()) { val pos = nextBlocks.next() val state = world.getBlockState(pos) if (state.block != Blocks.SUGAR_CANE) tryHarvest(state, pos, world) else { for (i in 5 downTo 1) { val posToHarvest = pos.up(i) val toHarvest = world.getBlockState(posToHarvest) if (toHarvest.block == Blocks.SUGAR_CANE) { tryHarvest(state, posToHarvest, world) } } } } } else { nextBlocks = getWorkingArea().map(::BlockPos).iterator() } cooldown = 0.0 } private fun tryHarvest(state: BlockState, pos: BlockPos, world: ServerWorld): Boolean { val block = state.block val inventory = inventoryComponent?.inventory val performedAction = inventory?.inputSlots?.any { slot -> val stack = inventory.getStack(slot) val item = stack.item val isCropBlock = block is CropBlock || block is StemBlock || block is SweetBerryBushBlock || block is CocoaBlock || block is NetherWartBlock when { item is BoneMealItem && isCropBlock && (block as? Fertilizable)?.isFertilizable(world, pos, state, false) == true && block.canGrow(world, world.random, pos, state) -> { stack.decrement(1) block.grow(world, world.random, pos, state) world.syncWorldEvent(2005, pos, 0) true } canHarvest(slot, state, block, item) -> { if ((block is CropBlock || block is SweetBerryBushBlock) && stack.count > 1) { world.setBlockState(pos, block.defaultState) stack.decrement(1) } else { world.setBlockState(pos, Blocks.AIR.defaultState) } val droppedStacks = state.getDroppedStacks(LootContext.Builder(world) .random(world.random) .parameter(LootContextParameters.ORIGIN, pos.toVec3d()) .parameter(LootContextParameters.BLOCK_STATE, state) .parameter(LootContextParameters.TOOL, ItemStack.EMPTY)) droppedStacks.forEach { inventory.output(it) } true } block is AirBlock && canPlant(item) && stack.count > 1 -> { var cropState = (item as BlockItem).block.defaultState if (item.block is CocoaBlock) { arrayOf(Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST).firstOrNull { cropState = cropState.with(CocoaBlock.FACING, it) cropState.canPlaceAt(world, pos) } ?: return@any false } if (cropState.canPlaceAt(world, pos) && world.isAir(pos)) { world.setBlockState(pos, cropState) stack.count-- true } else false } else -> false } } ?: false if (performedAction) use(getEnergyCost()) return performedAction } override fun getEnergyCost(): Long { val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1) return config.energyCost * speedEnhancers } private fun canPlant(item: Item) = item is BlockItem && ( item.block is CropBlock || item.block is StemBlock || item.block == Blocks.SUGAR_CANE || item.block is SweetBerryBushBlock || item.block is CocoaBlock || item.block is NetherWartBlock ) private fun canHarvest(slot: Int, state: BlockState, block: Block, item: Item): Boolean = (((block is CropBlock && block.isMature(state)) || (block is SweetBerryBushBlock && state[SweetBerryBushBlock.AGE] == 2)) && (item is BlockItem && item.block == block || slot == 4)) || block is GourdBlock || block == Blocks.SUGAR_CANE || (block is CocoaBlock && state[CocoaBlock.AGE] == 2) || (block is NetherWartBlock && state[NetherWartBlock.AGE] == 3) override fun getWorkingArea(): Box = Box(pos).expand(range.toDouble(), 0.0, range.toDouble()) fun getMaxCount(enhancer: Enhancer): Int { return when (enhancer) { Enhancer.SPEED -> return 1 Enhancer.BUFFER -> 4 else -> 1 } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/FisherBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.toVec3d import net.minecraft.block.BlockState import net.minecraft.item.FishingRodItem import net.minecraft.loot.context.LootContext import net.minecraft.loot.context.LootContextParameters import net.minecraft.loot.context.LootContextTypes import net.minecraft.server.world.ServerWorld import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class FisherBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, MachineRegistry.FISHER_REGISTRY, pos, state) { init { this.enhancerComponent = EnhancerComponent(intArrayOf(6, 7, 8, 9), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slot = 1 filter { (_, item), _ -> item is FishingRodItem } } output { slots = intArrayOf(2, 3, 4, 5) } } } private var cooldown = config.processSpeed override val maxInput: Long = config.maxInput override val maxOutput: Long = 0 override fun machineTick() { if (!canUse(getEnergyCost())) return val rodStack = inventoryComponent!!.inventory.getStack(1) if (rodStack.isEmpty || rodStack.item !is FishingRodItem || !use(getEnergyCost())) return cooldown += getProcessingSpeed() if (cooldown < config.processSpeed) return cooldown = 0.0 Direction.values().forEach { direction -> val pos = pos.offset(direction) if (world?.isWater(pos) == true) { val identifiers = getIdentifiers(tier) val id = identifiers[world!!.random!!.nextInt(identifiers.size)] val lootTable = (world as ServerWorld).server.lootManager.getTable(id) val ctx = LootContext.Builder(world as ServerWorld).random(world!!.random) .parameter(LootContextParameters.ORIGIN, pos.toVec3d()) .parameter(LootContextParameters.TOOL, rodStack) .build(LootContextTypes.FISHING) val loot = lootTable.generateLoot(ctx) loot.forEach { stack -> inventoryComponent?.inventory?.output(stack) } rodStack?.apply { damage++ if (damage >= maxDamage) decrement(1) } } } } override fun getEnergyCost(): Long { val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1) return config.energyCost * speedEnhancers } private fun getIdentifiers(tier: Tier) = when (tier) { Tier.MK2 -> arrayOf(FISH_IDENTIFIER) Tier.MK3 -> arrayOf(FISH_IDENTIFIER, FISH_IDENTIFIER, JUNK_IDENTIFIER, JUNK_IDENTIFIER, TREASURE_IDENTIFIER) else -> arrayOf(FISH_IDENTIFIER, FISH_IDENTIFIER, FISH_IDENTIFIER, TREASURE_IDENTIFIER) } fun getMaxCount(enhancer: Enhancer): Int { return when (enhancer) { Enhancer.SPEED, Enhancer.BUFFER -> 4 else -> 1 } } companion object { private val FISH_IDENTIFIER = Identifier("gameplay/fishing/fish") private val JUNK_IDENTIFIER = Identifier("gameplay/fishing/junk") private val TREASURE_IDENTIFIER = Identifier("gameplay/fishing/treasure") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/PumpBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue import it.unimi.dsi.fastutil.longs.LongOpenHashSet import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.components.* import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.config.IRConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import me.steven.indrev.utils.drainFluid import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.block.BlockState import net.minecraft.block.FluidBlock import net.minecraft.block.FluidDrainable import net.minecraft.fluid.FlowableFluid import net.minecraft.fluid.Fluid import net.minecraft.fluid.FluidState import net.minecraft.fluid.Fluids import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import kotlin.math.floor import kotlin.math.roundToInt class PumpBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, MachineRegistry.PUMP_REGISTRY, pos, state) { init { this.fluidComponent = FluidComponent({ this }, bucket) this.enhancerComponent = object : EnhancerComponent(intArrayOf(0, 1, 2, 3), Enhancer.DEFAULT, this::getMaxCount) { override fun isLocked(slot: Int, tier: Tier): Boolean = false } this.inventoryComponent = inventory(this) { } trackObject(TANK_ID, fluidComponent!![0]) trackDouble(SPEED_ID) { config.processSpeed - enhancerComponent!!.getCount(Enhancer.SPEED) * 10 } } override val syncToWorld: Boolean = true override val maxInput: Long = config.maxInput override val maxOutput: Long = 0 var pipePosition = 0.0 private var isSearchingDown = false private var currentTarget: BlockPos = pos private val scanned = LongOpenHashSet() private var queue = LongArrayFIFOQueue() private var cooldown = 20.0 override fun machineTick() { if (pipePosition == 0.0) { isSearchingDown = true } if (!isSearchingDown && currentTarget == pos) { searchFluid() } if (queue.isEmpty) { isSearchingDown = true } if (currentTarget != pos) { cooldown-- if (cooldown <= 0) { drainTargetFluid() } } if (isSearchingDown) { searchDown() } } private fun searchFluid() { val currentLevel = floor(pipePosition).toInt() val lookLevel = pos.offset(Direction.DOWN, currentLevel) val currentFluid = world!!.getFluidState(lookLevel) val targetFluid = world!!.getFluidState(currentTarget) if (currentFluid.isEmpty) { isSearchingDown = true return } if ((targetFluid.isEmpty || !targetFluid.isStill) && !currentFluid.isEmpty) { scanned.clear() val searching = BlockPos.Mutable() val neighbor = BlockPos.Mutable() while (!queue.isEmpty) { val packedPos = queue.dequeue() searching.set(BlockPos.unpackLongX(packedPos), BlockPos.unpackLongY(packedPos), BlockPos.unpackLongZ(packedPos)) SEARCH_DIRECTIONS.forEach { dir -> neighbor.set(searching.x + dir.offsetX, searching.y + dir.offsetY, searching.z + dir.offsetZ) if (neighbor.getSquaredDistance(lookLevel) < IRConfig.machines.pumpMaxRange * IRConfig.machines.pumpMaxRange && scanned.add(neighbor.asLong()) && getStill(world!!.getFluidState(neighbor).fluid) == getStill(currentFluid.fluid)) { queue.enqueue(neighbor.asLong()) } } val fluidState = world!!.getFluidState(searching) if (fluidState.isStill && !fluidState.isEmpty && fluidState.fluid == getStill(currentFluid.fluid)) { currentTarget = searching break } } } } private fun getFluidTouchingPipe(): FluidState { val currentLevel = floor(pipePosition).toInt() val lookLevel = pos.offset(Direction.DOWN, currentLevel) return world!!.getFluidState(lookLevel) } private fun drainTargetFluid() { val fluidToPump = world!!.getFluidState(currentTarget) if (!fluidToPump.isEmpty && canUse(config.energyCost) && fluidComponent!![0].isEmpty) { val blockState = world!!.getBlockState(currentTarget) val block = blockState?.block if (block is FluidDrainable && block is FluidBlock) { val drained = block.drainFluid(world!!, currentTarget, blockState) if (drained != Fluids.EMPTY) { fluidComponent!![0].insert(FluidVariant.of(drained), bucket, true) use(getEnergyCost()) cooldown = config.processSpeed - enhancerComponent!!.getCount(Enhancer.SPEED) * 10 } currentTarget = pos } } } override fun getEnergyCost(): Long { return config.energyCost * (enhancerComponent!!.getCount(Enhancer.SPEED) + 1) } private fun searchDown() { if (use(2)) { pipePosition += 0.01 sync() val fluid = getFluidTouchingPipe() if (!fluid.isEmpty) { isSearchingDown = false pipePosition = pipePosition.roundToInt().toDouble() queue = LongArrayFIFOQueue() val currentLevel = floor(pipePosition).toInt() queue.enqueue(pos.offset(Direction.DOWN, currentLevel).asLong()) } } } private fun getStill(fluid: Fluid): Fluid = if (fluid is FlowableFluid) fluid.still else fluid override fun applyDefault( state: BlockState, type: ConfigurationType, configuration: MutableMap ) { assert(type == ConfigurationType.FLUID) val facing = state[HorizontalFacingMachineBlock.HORIZONTAL_FACING] configuration[facing] = TransferMode.OUTPUT } override fun isFixed(type: ConfigurationType): Boolean = true fun getMaxCount(enhancer: Enhancer): Int { return when (enhancer) { Enhancer.SPEED -> return 1 Enhancer.BUFFER -> 4 else -> 1 } } override fun toTag(tag: NbtCompound) { tag.putDouble("MovingTicks", pipePosition) super.toTag(tag) } override fun fromTag(tag: NbtCompound) { pipePosition = tag.getDouble("MovingTicks") super.fromTag(tag) } override fun fromClientTag(tag: NbtCompound) { pipePosition = tag.getDouble("MovingTicks") } override fun toClientTag(tag: NbtCompound) { tag.putDouble("MovingTicks", pipePosition) } companion object { const val TANK_ID = 2 const val SPEED_ID = 3 val SEARCH_DIRECTIONS = mutableListOf(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/PumpBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.farms import alexiil.mc.lib.attributes.fluid.render.FluidRenderFace import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.utils.IRFluidTank import me.steven.indrev.utils.identifier import net.minecraft.client.MinecraftClient import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.ModelIdentifier import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3f import kotlin.math.floor class PumpBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: PumpBlockEntity, tickDelta: Float, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int ) { matrices.run { push() val inputVolume = entity.fluidComponent!![0] if (!inputVolume.isEmpty) { translate(0.5, 0.5, 0.5) var direction = entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING] if (direction.axis == Direction.Axis.X) direction = direction.opposite multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(direction.asRotation())) translate(-0.5, -0.5, -0.5) push() multiply(Vec3f.NEGATIVE_X.getDegreesQuaternion(22.5f)) renderFluid(inputVolume, vertexConsumers) pop() push() multiply(Vec3f.NEGATIVE_Z.getDegreesQuaternion(22.5f)) translate(0.5, 0.5, 0.5) multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(90f)) translate(-0.5, -0.5, -0.5) matrices.translate(0.0, 0.38, 0.0765) renderFluid(inputVolume, vertexConsumers) pop() push() multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(22.5f)) translate(0.5, 0.5, 0.5) multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(180f)) translate(-0.5, -0.5, -0.5) matrices.translate(0.1284, 0.0, 0.1285) renderFluid(inputVolume, vertexConsumers) pop() } pop() val currentY = floor(entity.pipePosition).toInt() for (y in 1..currentY) { push() translate(0.0, -y.toDouble(), 0.0) renderModel(vertexConsumers, entity) pop() } if (currentY.toDouble() != entity.pipePosition) { push() scale(1.01f, 1f, 1.01f) translate(-0.005, -entity.pipePosition, -0.005) renderModel(vertexConsumers, entity) pop() } } } private fun MatrixStack.renderModel(vertexConsumers: VertexConsumerProvider, entity: PumpBlockEntity) { val model = MinecraftClient.getInstance().bakedModelManager.getModel(ModelIdentifier(identifier("pump_pipe"), "")) val buffer = vertexConsumers.getBuffer(RenderLayer.getSolid()) val light = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos) MinecraftClient.getInstance().blockRenderManager.modelRenderer.render( peek(), buffer, null, model, -1f, -1f, -1f, light, OverlayTexture.DEFAULT_UV ) } private fun MatrixStack.renderFluid(inputVolume: IRFluidTank, vcp: VertexConsumerProvider) { inputVolume.render(FACES, vcp, this) } override fun rendersOutsideBoundingBox(blockEntity: PumpBlockEntity?): Boolean = true companion object { val FACES = listOf( FluidRenderFace.createFlatFaceZ(0.443, 0.15, 0.32, 0.556, 0.31, 0.32, 2.0, false, false), FluidRenderFace.createFlatFaceZ(0.443, 0.15, 0.32+ (0.55-0.443), 0.556, 0.31, 0.32+ (0.55-0.443), 2.0, true, false), FluidRenderFace.createFlatFaceX(0.443, 0.15, 0.323, 0.443, 0.31, 0.426, 2.0, false, false), FluidRenderFace.createFlatFaceX(0.443+ (0.55-0.443), 0.15, 0.323, 0.443+ (0.55-0.443), 0.31, 0.426, 2.0, true, false) ) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/RancherBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.components.autosync import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.redirectDrops import net.minecraft.block.BlockState import net.minecraft.entity.damage.DamageSource import net.minecraft.entity.passive.AnimalEntity import net.minecraft.item.ItemStack import net.minecraft.item.SwordItem import net.minecraft.nbt.NbtCompound import net.minecraft.server.world.ServerWorld import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos class RancherBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : AOEMachineBlockEntity(tier, MachineRegistry.RANCHER_REGISTRY, pos, state) { init { this.enhancerComponent = EnhancerComponent(intArrayOf(15, 16, 17, 18), Enhancer.DEFAULT, this::getMaxCount) this.inventoryComponent = inventory(this) { input { slots = intArrayOf(2, 3, 4, 5) } output { slots = intArrayOf(6, 7, 8, 9, 10, 11, 12, 13, 14) } coolerSlot = 1 } } override val maxInput: Long = config.maxInput override val maxOutput: Long = 0 var cooldown = 0.0 override var range = 5 private val fakePlayer by lazy { IndustrialRevolution.FAKE_PLAYER_BUILDER.create(world!!.server, world as ServerWorld, "rancher") } var feedBabies: Boolean by autosync(FEED_BABIES_ID, true) var mateAdults: Boolean by autosync(MATE_ADULTS, true) var matingLimit: Int by autosync(MATING_LIMIT, 16) var killAfter: Int by autosync(KILL_AFTER, 8) override fun machineTick() { if (world?.isClient == true) return val inventory = inventoryComponent?.inventory ?: return cooldown += getProcessingSpeed() if (cooldown < config.processSpeed) return val animals = world?.getEntitiesByClass(AnimalEntity::class.java, getWorkingArea()) { true } ?: emptyList() if (animals.isEmpty() || !canUse(getEnergyCost())) { workingState = false return } else workingState = true val swordStack = inventory.inputSlots.map { inventory.getStack(it) }.firstOrNull { it.item is SwordItem } fakePlayer.inventory.selectedSlot = 0 if (swordStack != null && !swordStack.isEmpty && swordStack.damage < swordStack.maxDamage) { val swordItem = swordStack.item as SwordItem val kill = filterAnimalsToKill(animals) if (kill.isNotEmpty()) use(getEnergyCost()) kill.forEach { animal -> animal.redirectDrops(inventory) { if (!animal.isAlive || !animal.damage(DamageSource.player(fakePlayer), swordItem.attackDamage)) return@forEach swordStack.damage(1, world?.random, null) if (swordStack.damage >= swordStack.maxDamage) swordStack.decrement(1) } } } for (animal in animals) { inventory.inputSlots.forEach { slot -> val stack = inventory.getStack(slot).copy() animal.redirectDrops(inventory) { if (tryFeed(animals.size, animal, inventory.getStack(slot)).isAccepted) return@forEach fakePlayer.inventory.selectedSlot = 8 fakePlayer.setStackInHand(Hand.MAIN_HAND, stack) if (animal.interactMob(fakePlayer, Hand.MAIN_HAND).isAccepted) use(getEnergyCost()) val inserted = inventory.output(fakePlayer.inventory.getStack(0)) val handStack = fakePlayer.getStackInHand(Hand.MAIN_HAND) if (!handStack.isEmpty && handStack.item != stack.item) { inventory.output(handStack) fakePlayer.setStackInHand(Hand.MAIN_HAND, ItemStack.EMPTY) } if (inserted) inventory.setStack(slot, stack) fakePlayer.inventory.clear() } } } fakePlayer.inventory.clear() cooldown = 0.0 } private fun tryFeed(size: Int, animalEntity: AnimalEntity, stack: ItemStack): ActionResult { if (animalEntity.isBreedingItem(stack)) { val breedingAge: Int = animalEntity.breedingAge if (!world!!.isClient && breedingAge == 0 && animalEntity.canEat() && size <= matingLimit && mateAdults) { animalEntity.eat(fakePlayer, Hand.MAIN_HAND, stack) animalEntity.lovePlayer(fakePlayer) } if (animalEntity.isBaby && feedBabies) { animalEntity.eat(fakePlayer, Hand.MAIN_HAND, stack) animalEntity.growUp(((-breedingAge / 20f) * 0.1f).toInt(), true) } return ActionResult.SUCCESS } return ActionResult.PASS } private fun filterAnimalsToKill(entities: List): List { val adults = entities.filter { !it.isBaby } val types = adults.map { it.type }.associateWith { mutableListOf() } adults.forEach { types[it.type]?.add(it) } return types.values.let { values -> values.map { animals -> animals.dropLast((animals.size - killAfter).coerceAtLeast(killAfter)) } }.flatten() } override fun getEnergyCost(): Long { val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1) return config.energyCost * speedEnhancers } fun getMaxCount(enhancer: Enhancer): Int { return when (enhancer) { Enhancer.SPEED -> return 1 Enhancer.BUFFER -> 4 else -> 1 } } override fun toTag(tag: NbtCompound) { super.toTag(tag) tag.putBoolean("feedBabies", feedBabies) tag.putBoolean("mateAdults", mateAdults) tag.putInt("matingLimit", matingLimit) tag.putInt("killAfter", killAfter) } override fun fromTag(tag: NbtCompound) { super.fromTag(tag) feedBabies = tag.getBoolean("feedBabies") mateAdults = tag.getBoolean("mateAdults") matingLimit = tag.getInt("matingLimit") killAfter = tag.getInt("killAfter") } companion object { const val FEED_BABIES_ID = 2 const val MATE_ADULTS = 3 const val MATING_LIMIT = 4 const val KILL_AFTER = 5 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/farms/SlaughterBlockEntity.kt ================================================ package me.steven.indrev.blockentities.farms import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.redirectDrops import net.minecraft.block.BlockState import net.minecraft.entity.LivingEntity import net.minecraft.entity.boss.WitherEntity import net.minecraft.entity.damage.DamageSource import net.minecraft.entity.decoration.ArmorStandEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.SwordItem import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos class SlaughterBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : AOEMachineBlockEntity(tier, MachineRegistry.SLAUGHTER_REGISTRY, pos, state) { init { this.enhancerComponent = EnhancerComponent( intArrayOf(11, 12, 13, 14), arrayOf(Enhancer.SPEED, Enhancer.BUFFER, Enhancer.DAMAGE), this::getMaxCount ) this.inventoryComponent = inventory(this) { input { slot = 1 } output { slots = intArrayOf(2, 3, 4, 5, 6, 7, 8, 9, 10) } } } override val maxInput: Long = config.maxInput override val maxOutput: Long = 0 var cooldown = 0.0 override var range = 5 private val fakePlayer by lazy { IndustrialRevolution.FAKE_PLAYER_BUILDER.create(world!!.server, world as ServerWorld, "slaughter") } override fun machineTick() { if (world?.isClient == true) return val inventory = inventoryComponent?.inventory ?: return val enhancers = enhancerComponent!!.enhancers cooldown += getProcessingSpeed() if (cooldown < config.processSpeed) return val source = DamageSource.player(fakePlayer) val mobs = world?.getEntitiesByClass(LivingEntity::class.java, getWorkingArea()) { e -> e !is PlayerEntity && e !is ArmorStandEntity && !e.isDead && !e.isInvulnerableTo(source) && (e !is WitherEntity || e.invulnerableTimer <= 0) } ?: emptyList() if (mobs.isEmpty() || !canUse(getEnergyCost())) { workingState = false return } else workingState = true val swordStack = inventory.inputSlots.map { inventory.getStack(it) }.firstOrNull { it.item is SwordItem } fakePlayer.inventory.selectedSlot = 0 if (swordStack != null && !swordStack.isEmpty && swordStack.damage < swordStack.maxDamage) { val swordItem = swordStack.item as SwordItem use(getEnergyCost()) mobs.forEach { mob -> swordStack.damage(1, world?.random, null) if (swordStack.damage >= swordStack.maxDamage) swordStack.decrement(1) if (mob.isAlive) { mob.redirectDrops(inventory) { mob.damage(source, (swordItem.attackDamage * Enhancer.getDamageMultiplier(enhancers)).toFloat()) } } } } fakePlayer.inventory.clear() cooldown = 0.0 } override fun getEnergyCost(): Long { val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1) val dmgEnhancers = (enhancerComponent!!.getCount(Enhancer.DAMAGE) * 8).coerceAtLeast(1) return config.energyCost * speedEnhancers * dmgEnhancers } fun getMaxCount(enhancer: Enhancer): Int { return when (enhancer) { Enhancer.SPEED, Enhancer.DAMAGE -> return 1 Enhancer.BUFFER -> 4 else -> 1 } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/BiomassGeneratorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.inventories.inventory import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.util.math.BlockPos class BiomassGeneratorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : SolidFuelGeneratorBlockEntity(tier, MachineRegistry.BIOMASS_GENERATOR_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.08, 900..2000, 2500) this.inventoryComponent = inventory(this) { input { slot = 2 } } } override fun getFuelMap(): Map = BURN_TIME_MAP companion object { private val BURN_TIME_MAP = hashMapOf() init { BURN_TIME_MAP[IRItemRegistry.BIOMASS] = 150 } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/CoalGeneratorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.inventories.inventory import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.block.entity.AbstractFurnaceBlockEntity import net.minecraft.item.Item import net.minecraft.util.math.BlockPos class CoalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : SolidFuelGeneratorBlockEntity(Tier.MK1, MachineRegistry.COAL_GENERATOR_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.08, 900..2000, 2500) this.inventoryComponent = inventory(this) { input { slot = 2 2 filter { stack -> BURN_TIME_MAP.containsKey(stack.item) } } } } override fun getFuelMap(): Map = BURN_TIME_MAP companion object { private val BURN_TIME_MAP = AbstractFurnaceBlockEntity.createFuelTimeMap() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/GasBurningGeneratorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.components.FluidComponent import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.components.autosync import me.steven.indrev.components.trackObject import me.steven.indrev.inventories.inventory import me.steven.indrev.registry.IRFluidFuelRegistry import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.block.BlockState import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos class GasBurningGeneratorBlockEntity(pos: BlockPos, state: BlockState) : GeneratorBlockEntity(Tier.MK4, MachineRegistry.GAS_BURNING_GENERATOR_REGISTRY, pos, state) { private var burnTime by autosync(BURN_TIME_ID, 0) private var maxBurnTime by autosync(TOTAL_BURN_TIME_ID, 0) init { this.temperatureComponent = TemperatureComponent(this, 0.3, 2200..2400, 2500) this.inventoryComponent = inventory(this) { coolerSlot = 0 output { slot = 1 } } this.fluidComponent = GasBurningGeneratorFluidComponent() } var generatingTicks = 0 override fun machineTick() { super.machineTick() if (workingState) { generatingTicks++ if (generatingTicks % 100 == 0 && world!!.random.nextDouble() < 0.6) { inventoryComponent!!.inventory.output(ItemStack(IRItemRegistry.SOOT)) } } } override fun shouldGenerate(): Boolean { if (burnTime > 0) burnTime-- else if (energyCapacity > energy) { val invFluid = fluidComponent!![0] val fluid = invFluid.resource.fluid if (invFluid.isEmpty || !IRFluidFuelRegistry.isFuel(fluid)) return false val fuel = IRFluidFuelRegistry.get(fluid)!! if (fluidComponent!![0].extract(fuel.consumptionRatio) == fuel.consumptionRatio) { burnTime = fuel.burnTime maxBurnTime = burnTime } } return burnTime > 0 && energy < energyCapacity } override fun getGenerationRatio(): Long { val invFluid = fluidComponent!![0] val fluid = invFluid.resource.fluid val modifier = if (temperatureComponent!!.isFullEfficiency()) config.temperatureBoost else 1.0 return ((IRFluidFuelRegistry.get(fluid)?.generationRatio ?: 0) * modifier).toLong() } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.ITEM -> arrayOf(TransferMode.OUTPUT, TransferMode.NONE) ConfigurationType.FLUID -> arrayOf(TransferMode.INPUT, TransferMode.NONE) else -> super.getValidConfigurations(type) } } inner class GasBurningGeneratorFluidComponent : FluidComponent({ this }, bucket * 2, 1) { init { trackObject(TANK_ID, this[0]) } override fun isFluidValidForTank(index: Int, variant: FluidVariant): Boolean { return IRFluidFuelRegistry.isFuel(variant.fluid) } } companion object { const val BURN_TIME_ID = 4 const val TOTAL_BURN_TIME_ID = 5 const val TANK_ID = 6 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/GeneratorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.config.GeneratorConfig import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos abstract class GeneratorBlockEntity(tier: Tier, registry: MachineRegistry, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, registry, pos, state) { override fun machineTick() { if (world?.isClient == false) { val ratio = getGenerationRatio() if (shouldGenerate() && energyCapacity > energy + ratio) { this.energy += ratio this.temperatureComponent?.tick(true) workingState = true } else { workingState = false this.temperatureComponent?.tick(false) } } } override val maxInput: Long = 0 override val maxOutput: Long = config.maxOutput abstract fun shouldGenerate(): Boolean open fun getGenerationRatio(): Long = (config.ratio * if (this.temperatureComponent?.isFullEfficiency() == true) config.temperatureBoost else 1.0).toLong() } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/HeatGeneratorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.* import me.steven.indrev.inventories.inventory import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import net.minecraft.block.BlockState import net.minecraft.fluid.Fluids import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos class HeatGeneratorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : GeneratorBlockEntity(tier, MachineRegistry.HEAT_GENERATOR_REGISTRY, pos, state) { private var burnTime by autosync(GasBurningGeneratorBlockEntity.BURN_TIME_ID, 0) private var maxBurnTime by autosync(GasBurningGeneratorBlockEntity.TOTAL_BURN_TIME_ID, 0) init { this.temperatureComponent = TemperatureComponent(this, 0.8, 7000..9000, 10000) this.inventoryComponent = inventory(this) { input { slot = 2 } } this.fluidComponent = FluidComponent({ this }, bucket * 4) fluidComponent!!.inputTanks = intArrayOf(0) trackObject(TANK_ID, fluidComponent!![0]) trackLong(GENERATION_RATIO_ID) { getGenerationRatio() } trackLong(CONSUMPTION_RATIO_ID) { getConsumptionRate() } } override val syncToWorld: Boolean = true override fun shouldGenerate(): Boolean { if (burnTime > 0) burnTime-- else if (energyCapacity > energy) { val tank = fluidComponent!![0] val consume = getConsumptionRate() if (tank.variant.isOf(Fluids.LAVA) && tank.tryExtract(consume)) { burnTime = 10 maxBurnTime = burnTime tank.extract(consume, true) } markDirty() } return burnTime > 0 && energy < energyCapacity } override fun getGenerationRatio(): Long { return (config.ratio * (temperatureComponent!!.temperature / temperatureComponent!!.optimalRange.first).coerceAtMost(1.0)).toLong() } fun getConsumptionRate(temperature: Double = temperatureComponent!!.temperature): Long { return ((temperature / temperatureComponent!!.optimalRange.first).coerceIn(0.001, 1.0) * 810).toLong() } override fun fromTag(tag: NbtCompound) { super.fromTag(tag) burnTime = tag.getInt("BurnTime") maxBurnTime = tag.getInt("MaxBurnTime") } override fun toTag(tag: NbtCompound) { tag.putInt("BurnTime", burnTime) tag.putInt("MaxBurnTime", maxBurnTime) super.toTag(tag) } override fun toClientTag(tag: NbtCompound) { fluidComponent!!.toTag(tag) } override fun fromClientTag(tag: NbtCompound) { fluidComponent!!.fromTag(tag) } companion object { const val TANK_ID = 4 const val GENERATION_RATIO_ID = 5 const val CONSUMPTION_RATIO_ID = 6 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/HeatGeneratorBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.generators import alexiil.mc.lib.attributes.fluid.render.FluidRenderFace import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.utils.IRFluidTank import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3f class HeatGeneratorBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: HeatGeneratorBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int ) { val volume = entity?.fluidComponent?.get(0) ?: return if (!volume.isEmpty) { matrices?.run { push() val direction = entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING] .let { if (it.axis == Direction.Axis.X) it.opposite else it } translate(0.5, 0.5, 0.5) multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(direction.asRotation())) translate(-0.5, -0.5, -0.5) matrices.renderFluid(volume, vertexConsumers) pop() } } } private fun MatrixStack.renderFluid(inputVolume: IRFluidTank, vcp: VertexConsumerProvider) { val yMax = (((inputVolume.amount / (4f*81000)) * 10.0) / 16.0).coerceAtLeast(0.1) val face = listOf( FluidRenderFace.createFlatFaceZ(0.01, 0.1, 0.99, 0.99, yMax, 0.99, 2.0, true, false), FluidRenderFace.createFlatFaceY(0.01, yMax, 0.8, 0.99, yMax, 0.99, 2.0, true, false), FluidRenderFace.createFlatFaceX(0.01, 0.1, 0.8, 0.01, yMax, 0.99, 2.0, false, false), FluidRenderFace.createFlatFaceX(0.99, 0.1, 0.8, 0.99, yMax, 0.99, 2.0, true, false) ) inputVolume.render(face, vcp, this) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/SolarGeneratorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.TemperatureComponent import me.steven.indrev.inventories.inventory import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos class SolarGeneratorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : GeneratorBlockEntity(tier, MachineRegistry.SOLAR_GENERATOR_REGISTRY, pos, state) { init { this.temperatureComponent = TemperatureComponent(this, 0.1, 500..700, 1000) this.inventoryComponent = inventory(this) {} } override fun shouldGenerate(): Boolean = this.world?.isSkyVisible(pos.up()) == true && this.world?.isDay == true && energy < energyCapacity } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/SolidFuelGeneratorBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.components.autosync import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction abstract class SolidFuelGeneratorBlockEntity(tier: Tier, registry: MachineRegistry, pos: BlockPos, state: BlockState) : GeneratorBlockEntity(tier, registry, pos, state) { private var burnTime by autosync(BURN_TIME_ID, 0) private var maxBurnTime by autosync(TOTAL_BURN_TIME_ID, 0) override fun shouldGenerate(): Boolean { if (burnTime > 0) burnTime-- else if (energyCapacity > energy) { val inventory = inventoryComponent?.inventory ?: return false val invStack = inventory.getStack(2) val item = invStack.item if (!invStack.isEmpty && getFuelMap().containsKey(invStack.item)) { burnTime = getFuelMap()[invStack.item] ?: return false maxBurnTime = burnTime invStack.decrement(1) if (item.hasRecipeRemainder()) inventory.setStack(2, ItemStack(item.recipeRemainder)) } markDirty() } return burnTime > 0 && energy < energyCapacity } override fun applyDefault( state: BlockState, type: ConfigurationType, configuration: MutableMap ) { val direction = (state.block as MachineBlock).getFacing(state) when (type) { ConfigurationType.ITEM -> { configuration[direction.rotateYClockwise()] = TransferMode.INPUT } else -> super.applyDefault(state, type, configuration) } } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.ITEM -> arrayOf(TransferMode.INPUT, TransferMode.NONE) else -> return super.getValidConfigurations(type) } } override fun fromTag(tag: NbtCompound) { super.fromTag(tag) burnTime = tag?.getInt("BurnTime") ?: 0 maxBurnTime = tag?.getInt("MaxBurnTime") ?: 0 } override fun toTag(tag: NbtCompound) { tag?.putInt("BurnTime", burnTime) tag?.putInt("MaxBurnTime", maxBurnTime) super.toTag(tag) } abstract fun getFuelMap(): Map companion object { const val BURN_TIME_ID = 4 const val TOTAL_BURN_TIME_ID = 5 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/SteamTurbineBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import alexiil.mc.lib.attributes.fluid.amount.FluidAmount import me.steven.indrev.api.machines.Tier import me.steven.indrev.components.FluidComponent import me.steven.indrev.components.autosync import me.steven.indrev.components.multiblock.MultiBlockComponent import me.steven.indrev.components.multiblock.definitions.SteamTurbineStructureDefinition import me.steven.indrev.components.trackLong import me.steven.indrev.registry.IRFluidRegistry import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.bucket import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.world.World class SteamTurbineBlockEntity(pos: BlockPos, state: BlockState) : GeneratorBlockEntity(Tier.MK4, MachineRegistry.STEAM_TURBINE_REGISTRY, pos, state) { init { this.multiblockComponent = SteamTurbineMultiblockComponent() this.fluidComponent = SteamTurbineFluidComponent() } override val maxInput: Long = 0 var efficiency by autosync(EFFICIENCY, 1.0) var generatingTicks = 0 var totalInserted: FluidAmount = FluidAmount.ZERO init { trackLong(GENERATING) { getGenerationRatio() * 100 } } // used for the screen handler @Environment(EnvType.CLIENT) var consuming: FluidAmount = FluidAmount.ZERO override fun getGenerationRatio(): Long { val radius = getRadius() val eff = totalInserted.div(20).asInexactDouble() return (((eff * 2) / (radius.toDouble() / 7)) * 2048).toLong() } override fun shouldGenerate(): Boolean { if (generatingTicks <= 0) { if (!totalInserted.isZero) { generatingTicks = 20 fluidComponent!![0].extract(Long.MAX_VALUE, true) } else return false } generatingTicks-- if (generatingTicks <= 0) totalInserted = FluidAmount.ZERO return true } private fun getConsumptionRatio(): FluidAmount { return totalInserted.div(20) } private fun getRadius(): Int { //return 7 val matcher = multiblockComponent!!.getSelectedMatcher(world ?: return 0, pos, cachedState) return SteamTurbineStructureDefinition.getRadius(matcher.builtId ?: return 0) } private inner class SteamTurbineFluidComponent : FluidComponent({ this }, bucket, 1) { override fun getTankCapacity(index: Int): Long { return (((getRadius() * getRadius().toLong()) * efficiency) * 81L).toLong() } override fun isFluidValidForTank(index: Int, variant: FluidVariant): Boolean { return variant.isOf(IRFluidRegistry.STEAM_STILL) } } private inner class SteamTurbineMultiblockComponent : MultiBlockComponent({ _, _, _ -> SteamTurbineStructureDefinition }) { override fun tick(world: World, pos: BlockPos, blockState: BlockState) { super.tick(world, pos, blockState) SteamTurbineStructureDefinition .getInputValvePositions(pos, blockState, getSelectedMatcher(world, pos, blockState)) .forEach { valvePos -> val valveBlockEntity = world.getBlockEntity(valvePos) if (valveBlockEntity is SteamTurbineSteamInputValveBlockEntity) valveBlockEntity.steamTurbinePos = pos } if (!isBuilt(world, pos, blockState)) { fluidComponent!![0].amount = 0 } } } override fun toTag(tag: NbtCompound) { tag.putDouble("Efficiency", efficiency) super.toTag(tag) } override fun fromTag(tag: NbtCompound) { efficiency = tag.getDouble("Efficiency") super.fromTag(tag) } override fun toClientTag(tag: NbtCompound) { tag.put("Consuming", getConsumptionRatio().toNbt()) multiblockComponent?.writeNbt(tag) } override fun fromClientTag(tag: NbtCompound) { consuming = FluidAmount.fromNbt(tag.getCompound("Consuming")) ?: consuming multiblockComponent?.readNbt(tag) } companion object { const val EFFICIENCY = 2 const val GENERATING = 3 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/generators/SteamTurbineSteamInputValveBlockEntity.kt ================================================ package me.steven.indrev.blockentities.generators import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.util.math.BlockPos class SteamTurbineSteamInputValveBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(IRBlockRegistry.STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ENTITY, pos, state) { var steamTurbinePos: BlockPos = BlockPos(-1, -1, -1) var inserted = false fun getSteamTurbine(): SteamTurbineBlockEntity? { val blockEntity = world!!.getBlockEntity(steamTurbinePos) as? SteamTurbineBlockEntity ?: return null return if (blockEntity.multiblockComponent?.isBuilt(world!!, pos, blockEntity.cachedState) == true) blockEntity else null } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/laser/CapsuleBlockEntity.kt ================================================ package me.steven.indrev.blockentities.laser import com.google.common.base.Preconditions import me.steven.indrev.blocks.machine.LaserBlock import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.inventory.Inventories import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.server.world.ServerWorld import net.minecraft.util.collection.DefaultedList import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World class CapsuleBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(IRBlockRegistry.CAPSULE_BLOCK_ENTITY, pos, state) { var inventory: DefaultedList = DefaultedList.ofSize(1, ItemStack.EMPTY) var lastProgress = 0 fun getActiveLasersCount(): Int { return Direction.values().count { val laser = world?.getBlockEntity(pos.offset(it, 4)) as? LaserBlockEntity ?: return@count false laser.cachedState[LaserBlock.POWERED] } } override fun readNbt(tag: NbtCompound?) { super.readNbt(tag) inventory = DefaultedList.ofSize(1, ItemStack.EMPTY) Inventories.readNbt(tag, inventory) } override fun writeNbt(tag: NbtCompound) { super.writeNbt(tag) Inventories.writeNbt(tag, inventory) } open fun sync() { Preconditions.checkNotNull(world) // Maintain distinct failure case from below check(world is ServerWorld) { "Cannot call sync() on the logical client! Did you check world.isClient first?" } (world as ServerWorld).chunkManager.markForUpdate(getPos()) } companion object { fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: CapsuleBlockEntity) { if (world.isClient) return val tag = blockEntity.inventory[0].orCreateNbt val progress = tag.getInt("Progress") if (tag.contains("Progress") && progress == blockEntity.lastProgress) { tag.remove("Progress") } blockEntity.lastProgress = progress } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/laser/CapsuleBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.laser import net.minecraft.client.MinecraftClient import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.render.model.json.ModelTransformation import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Vec3f class CapsuleBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: CapsuleBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { entity ?: return val itemStack = entity.inventory[0] if (itemStack.isEmpty) return val lightCoord = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos) matrices?.run { push() val time = entity.world?.time ?: 1 translate(0.5, 0.35, 0.5) scale(1.2f, 1.2f, 1.2f) multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion((time + tickDelta) * 16 * entity.getActiveLasersCount())) MinecraftClient.getInstance().itemRenderer.renderItem(itemStack, ModelTransformation.Mode.GROUND, lightCoord, overlay, matrices, vertexConsumers, 0) pop() } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/laser/LaserBlockEntity.kt ================================================ package me.steven.indrev.blockentities.laser import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blocks.machine.FacingMachineBlock import me.steven.indrev.blocks.machine.LaserBlock import me.steven.indrev.config.MachineConfig import me.steven.indrev.recipes.machines.LaserRecipe import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.component3 import me.steven.indrev.utils.toVec3d import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.block.BlockState import net.minecraft.entity.Entity import net.minecraft.item.ItemStack import net.minecraft.particle.ParticleTypes import net.minecraft.server.world.ServerWorld import net.minecraft.sound.SoundCategory import net.minecraft.sound.SoundEvents import net.minecraft.util.math.* import net.minecraft.world.World import net.minecraft.world.explosion.Explosion import java.util.* class LaserBlockEntity(pos: BlockPos, state: BlockState) : MachineBlockEntity(Tier.MK4, MachineRegistry.LASER_EMITTER_REGISTRY, pos, state) { override val maxInput: Long = config.maxInput override val maxOutput: Long = 0 private var ticksUntilExplode = 100 private var recipe: LaserRecipe? = null override fun machineTick() { if (!cachedState[LaserBlock.POWERED]) { ticksUntilExplode = 100 return } val facing = cachedState[FacingMachineBlock.FACING] val containerPos = pos.offset(facing, 4) val container = world?.getBlockEntity(containerPos) as? CapsuleBlockEntity if (container == null) { ticksUntilExplode-- if (ticksUntilExplode < 0) explode() return } else { ticksUntilExplode = 100 BlockPos.iterate(pos.offset(facing), pos.offset(facing, 3)).forEach { world?.breakBlock(it, true) } } val stack = container.inventory[0] if (recipe == null) { recipe = LaserRecipe.TYPE.getMatchingRecipe(world as ServerWorld, stack) .firstOrNull { it.matches(stack, emptyList()) } } else if (ItemStack.areItemsEqual(recipe!!.outputs.first().stack, stack)) { return } if (recipe?.matches(stack, emptyList()) != true) { world?.breakBlock(containerPos, false) recipe = null return } if (!use(config.energyCost)) { world?.setBlockState(pos, cachedState.with(LaserBlock.POWERED, false)) return } val (x, y, z) = scale(facing.vec3f(), 3.0f) val damageArea = Box(pos).stretch(x.toDouble(), y.toDouble(), z.toDouble()).let { when { facing.axis.isVertical -> it.shrink(0.3, 0.0, 0.3) facing.axis == Direction.Axis.X -> it.shrink(0.0, 0.3, 0.3) else -> it.shrink(0.3, 0.3, 0.0) } } world?.getEntitiesByClass(Entity::class.java, damageArea) { true }?.forEach { it.setOnFireFor(1000) it.damage(LaserBlock.LASER_DAMAGE_SOURCE, 2f) } val tag = stack.orCreateNbt val progress = tag.getDouble("Progress") if (progress >= recipe!!.ticks) { container.inventory[0] = recipe!!.craft(null as Random?).first() container.markDirty() container.sync() world?.updateNeighbors(containerPos, container.cachedState.block) } else tag.putDouble("Progress", progress + config.energyCost) } @Environment(EnvType.CLIENT) override fun machineClientTick() { if (cachedState[LaserBlock.POWERED]) { if (!isEmittingLaser()) { if (world!!.random.nextDouble() > 0.7) { spawnParticles(world!!, pos, isFire = true) world!!.playSound( pos.x.toDouble() + 0.5, pos.y.toDouble() + 0.5, pos.z.toDouble() + 0.5, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.6f, 0.8f, false ) } return } spawnParticles(world!!, pos, 0.4) } } private fun spawnParticles(world: World, pos: BlockPos, width: Double = 0.5625, isFire: Boolean = false) { val random = world.random val facing = cachedState[FacingMachineBlock.FACING] repeat(6) { val axis = facing.axis val x = if (axis != Direction.Axis.X) 0.7 - random.nextDouble() * width else 0.0 val y = if (axis != Direction.Axis.Y) 0.7 - random.nextDouble() * width else 0.0 val z = if (axis != Direction.Axis.Z) 0.7 - random.nextDouble() * width else 0.0 val source = Vec3d(pos.x + x, pos.y + y, pos.z + z).let { val (x, y, z) = facing.unitVector if (x > 0 || y > 0 || z > 0) it.add(Vec3d(facing.unitVector)) else it } val velocity = pos.offset(facing, 4).toVec3d().add(x, y, z).subtract(source).normalize().multiply(0.18) world.addParticle( if (isFire) ParticleTypes.SMOKE else IndustrialRevolution.LASER_PARTICLE, true, source.x, source.y, source.z, if (isFire) 0.0 else velocity.x, if (isFire) 0.05 else velocity.y, if (isFire) 0.0 else velocity.z ) } } fun isEmittingLaser(): Boolean { val facing = cachedState[FacingMachineBlock.FACING] val containerPos = pos.offset(facing, 4) return cachedState[LaserBlock.POWERED] && world?.getBlockEntity(containerPos) as? CapsuleBlockEntity != null } private fun explode() { world?.createExplosion( null, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), 3f, true, Explosion.DestructionType.DESTROY ) } companion object { private fun Direction.vec3f() = Vec3f(offsetX.toFloat(), offsetY.toFloat(), offsetZ.toFloat()) private fun scale(v: Vec3f, scale: Float): Vec3f { return Vec3f(v.x * scale, v.y * scale, v.z * scale) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/laser/LaserBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.laser import me.steven.indrev.blocks.machine.FacingMachineBlock import me.steven.indrev.utils.identifier import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BeaconBlockEntityRenderer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.Identifier import net.minecraft.util.math.Direction class LaserBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: LaserBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { entity ?: return if (!entity.isEmittingLaser()) return val direction = entity.cachedState[FacingMachineBlock.FACING] matrices?.run { push() translate(direction.unitVector.x.toDouble(), direction.unitVector.y.toDouble(), direction.unitVector.z.toDouble()) translate(0.5, 0.5, 0.5) val rotation = direction.let { when { it.axis.isHorizontal -> it.rotateYCounterclockwise().unitVector.getDegreesQuaternion(90f) it == Direction.DOWN -> Direction.EAST.unitVector.getDegreesQuaternion(180f) else -> it.unitVector.getDegreesQuaternion(90f) } } multiply(rotation) translate(-0.5, -0.5, -0.5) BeaconBlockEntityRenderer.renderBeam( matrices, vertexConsumers, TEXTURE, tickDelta, 1.0f, entity.world!!.time, 0, 3, floatArrayOf(1f, 1f, 1f), 0.085f, 0.1f ) pop() } } override fun rendersOutsideBoundingBox(blockEntity: LaserBlockEntity?): Boolean = true companion object { val TEXTURE: Identifier = identifier("textures/entity/laser.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/miningrig/DataCardWriterBlockEntity.kt ================================================ package me.steven.indrev.blockentities.miningrig import me.steven.indrev.api.OreDataCards import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.components.autosync import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.config.IRConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos class DataCardWriterBlockEntity (tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, MachineRegistry.DATA_CARD_WRITER_REGISTRY, pos, state) { init { this.inventoryComponent = inventory(this) { input { 0 filter { itemStack, _ -> itemStack.item == IRItemRegistry.ORE_DATA_CARD } 1 until 13 filter { stack, _ -> OreDataCards.isAllowed(stack) } 13 until 16 filter { stack, _ -> OreDataCards.Modifier.isModifierItem(stack.item) } } } } var processTime by autosync(PROCESS_ID, 0) var totalProcessTime by autosync(TOTAL_PROCESS_ID, 0) private val modifiersToAdd = mutableMapOf() private val toWrite = mutableListOf() override fun machineTick() { if (totalProcessTime > 0 && use(getEnergyCost())) { if (processTime >= totalProcessTime) finish() else { processTime++ workingState = true } } else { workingState = false } } fun start() { val inventory = inventoryComponent!!.inventory val cardStack = inventory.getStack(0) val oldData = OreDataCards.readNbt(cardStack) ORES_SLOTS.forEach { slot -> val stack = inventory.getStack(slot) if (!stack.isEmpty && stack.count == 64) { toWrite.add(stack) inventory.setStack(slot, ItemStack.EMPTY) } } if (toWrite.isEmpty() && oldData == null) { return } MODIFIERS_SLOTS.forEach { slot -> val stack = inventory.getStack(slot) val modifier = OreDataCards.Modifier.byItem(stack.item) var level = (modifiersToAdd[modifier] ?: 0) + (oldData?.modifiersUsed?.get(modifier) ?: 0) when (modifier) { OreDataCards.Modifier.RICHNESS -> { while (stack.count >= 16 && level < 40) { stack.decrement(16) level++ } } OreDataCards.Modifier.SPEED, OreDataCards.Modifier.SIZE -> { while (stack.count >= 64) { stack.decrement(64) level++ } } OreDataCards.Modifier.RNG -> { if (level == 0) { stack.decrement(1) val r = world!!.random.nextDouble() if (r > 0.95 && r <= 0.98) { level = -1 } else if (r > 0.98) { level = 1 } } } else -> return@forEach } modifiersToAdd[modifier] = level - (oldData?.modifiersUsed?.get(modifier) ?: 0) } processTime = 0 totalProcessTime = 20*10 + (toWrite.size * (5*modifiersToAdd.map { it.value }.sum())) } private fun finish() { val inventory = inventoryComponent!!.inventory val cardStack = inventory.getStack(0) val oldData = OreDataCards.readNbt(cardStack) val oreTypes = toWrite.map { it.item }.distinct().count() val richnessDecrease = if (oreTypes == 1) 0.02 else 0.04 val richnessModifier = ((modifiersToAdd[OreDataCards.Modifier.RICHNESS] ?: 0) * 0.01).coerceAtMost(0.2) val richness = ((oldData?.richness ?: 1.0) - (richnessDecrease * toWrite.size) + richnessModifier).coerceIn(richnessDecrease, 1.0) val speedModifier = ((oldData?.modifiersUsed?.get(OreDataCards.Modifier.SPEED) ?: 0) + (modifiersToAdd[OreDataCards.Modifier.SPEED] ?: 0)) * 20 val speed = 100 + (richness * 1100) - speedModifier + (modifiersToAdd[OreDataCards.Modifier.SIZE] ?: 0) * 2 val rng = oldData?.rng ?: modifiersToAdd[OreDataCards.Modifier.RNG] ?: 0 val oreEnergyRequired = toWrite.sumOf { OreDataCards.getCost(it) * 16 } val energyRequired = (oldData?.energyRequired ?: 32) + 8 * (modifiersToAdd[OreDataCards.Modifier.SPEED] ?: 0) + oreEnergyRequired val cyclesModifiers = (modifiersToAdd[OreDataCards.Modifier.SIZE] ?: 0) * 128 val maxCycles = (oldData?.maxCycles ?: 0) + (toWrite.size * 64) + cyclesModifiers val items = mutableMapOf() oldData?.entries?.forEach { entry -> items[entry.item] = entry.count } toWrite.forEach { stack -> items[stack.item] = items.getOrDefault(stack.item, 0) + stack.count } val entries = items.keys.map { OreDataCards.OreEntry(it, items[it]!!) } val modifiersMap = mutableMapOf() modifiersToAdd.forEach { (modifier, level) -> modifiersMap[modifier] = (oldData?.modifiersUsed?.get(modifier)?: 0) + level } val data = OreDataCards.Data(entries, modifiersMap, richness, speed.toInt(), rng, energyRequired, maxCycles, oldData?.used ?: 0) OreDataCards.writeNbt(cardStack, data) modifiersToAdd.clear() toWrite.clear() processTime = 0 totalProcessTime = 0 } override fun getEnergyCost(): Long { return IRConfig.machines.dataCardWriter.energyCost } companion object { const val PROCESS_ID = 2 const val TOTAL_PROCESS_ID = 3 val MODIFIERS_SLOTS = 13 until 16 val ORES_SLOTS = 1 until 13 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/miningrig/DrillBlockEntity.kt ================================================ package me.steven.indrev.blockentities.miningrig import com.google.common.base.Preconditions import io.netty.buffer.Unpooled import me.steven.indrev.api.OreDataCards import me.steven.indrev.blocks.machine.DrillBlock import me.steven.indrev.gui.screenhandlers.machines.MiningRigDrillScreenHandler import me.steven.indrev.packets.client.MiningRigSpawnBlockParticlesPacket import me.steven.indrev.registry.IRBlockRegistry import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.component3 import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.entity.LootableContainerBlockEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.Inventories import net.minecraft.item.BlockItem import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.network.PacketByteBuf import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerContext import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.world.ServerWorld import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.collection.DefaultedList import net.minecraft.util.math.BlockPos import net.minecraft.util.registry.Registry import net.minecraft.world.World class DrillBlockEntity(pos: BlockPos, state: BlockState) : LootableContainerBlockEntity(IRBlockRegistry.DRILL_BLOCK_ENTITY_TYPE, pos, state), ExtendedScreenHandlerFactory { var inventory: DefaultedList = DefaultedList.ofSize(1, ItemStack.EMPTY) var position: Double = 1.0 var miningProgress: Double = 0.0 fun setWorkingState(working: Boolean) { if (cachedState[DrillBlock.WORKING] != working) world?.setBlockState(pos, cachedState.with(DrillBlock.WORKING, working)) } fun tickMining(miningRig: MiningRigBlockEntity, data: OreDataCards.Data) { setWorkingState(true) miningProgress += getSpeedMultiplier() if (miningProgress >= data.speed) { miningProgress = 0.0 val item = data.pickRandom(world!!.random) val rng = if (data.rng == 1 && world!!.random.nextDouble() < 0.1) 2 else 1 val count = ((5 * data.richness) + world!!.random.nextInt((OreDataCards.MAX_PER_CYCLE * data.richness).toInt().coerceAtLeast(1))).toInt() * rng var stack = ItemStack(item, count) if (data.rng == 1 && world!!.random.nextDouble() > 0.9) { stack.count *= 2 } else if (data.rng == -1 && world!!.random.nextDouble() > 0.9) { stack = ItemStack.EMPTY } miningRig.output(stack) data.used++ miningRig.lastMinedItem = ItemStack(item) miningRig.sync() val drillStack = inventory[0] drillStack.damage(1, world!!.random, null) if (drillStack.damage >= drillStack.maxDamage) { inventory[0] = ItemStack.EMPTY } if (item is BlockItem) sendBlockBreakPacket(item.block) } } private fun sendBlockBreakPacket(block: Block) { val (x, y, z) = pos val players = (world as ServerWorld).server.playerManager.playerList for (i in players.indices) { val serverPlayerEntity = players[i] if (serverPlayerEntity.world.registryKey === world!!.registryKey) { val xOffset = x - serverPlayerEntity.x val yOffset = y - serverPlayerEntity.y val zOffset = z - serverPlayerEntity.z if (xOffset * xOffset + yOffset * yOffset + zOffset * zOffset < 64 * 64) { val buf = PacketByteBuf(Unpooled.buffer()) buf.writeBlockPos(pos) buf.writeInt(Registry.BLOCK.getRawId(block)) ServerPlayNetworking.send(serverPlayerEntity, MiningRigSpawnBlockParticlesPacket.BLOCK_BREAK_PACKET, buf) } } } } override fun size(): Int = 1 override fun getContainerName(): Text = TranslatableText("block.indrev.drill") override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory): ScreenHandler { return MiningRigDrillScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos)) } override fun getInvStackList(): DefaultedList = inventory override fun setInvStackList(list: DefaultedList) { inventory = list } override fun readNbt(tag: NbtCompound) { super.readNbt(tag) inventory = DefaultedList.ofSize(size(), ItemStack.EMPTY) if (!deserializeLootTable(tag)) { Inventories.readNbt(tag, inventory) } position = tag.getDouble("Position") miningProgress = tag.getDouble("Mining") } override fun writeNbt(tag: NbtCompound) { super.writeNbt(tag) if (!serializeLootTable(tag)) { Inventories.writeNbt(tag, inventory) } tag.putDouble("Position", position) tag.putDouble("Mining", miningProgress) } override fun toUpdatePacket(): BlockEntityUpdateS2CPacket { return BlockEntityUpdateS2CPacket.create(this) } override fun toInitialChunkDataNbt(): NbtCompound { val nbt = super.toInitialChunkDataNbt() writeNbt(nbt) return nbt } fun sync() { Preconditions.checkNotNull(world) // Maintain distinct failure case from below check(world is ServerWorld) { "Cannot call sync() on the logical client! Did you check world.isClient first?" } (world as ServerWorld).chunkManager.markForUpdate(getPos()) } override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) { buf.writeBlockPos(pos) } fun getSpeedMultiplier(): Double { val item = inventory[0].item return if (position > 0) 0.0 else when (item) { IRItemRegistry.STONE_DRILL_HEAD -> 1.0 IRItemRegistry.IRON_DRILL_HEAD -> 2.0 IRItemRegistry.DIAMOND_DRILL_HEAD -> 4.0 IRItemRegistry.NETHERITE_DRILL_HEAD -> 8.0 else -> 0.0 } } companion object { fun isValidDrill(item: Item) = item == IRItemRegistry.STONE_DRILL_HEAD || item == IRItemRegistry.IRON_DRILL_HEAD || item == IRItemRegistry.DIAMOND_DRILL_HEAD || item == IRItemRegistry.NETHERITE_DRILL_HEAD fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: DrillBlockEntity) { if (blockEntity.inventory[0].isEmpty) { blockEntity.position = 1.0 blockEntity.markDirty() blockEntity.sync() } else if (state[DrillBlock.WORKING]) { if (blockEntity.position > 0) blockEntity.position -= 0.01 else return blockEntity.markDirty() blockEntity.sync() } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/miningrig/DrillBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.miningrig import me.steven.indrev.blocks.machine.DrillBlock import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.identifier import net.minecraft.client.MinecraftClient import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.RenderLayers import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.ModelIdentifier import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Vec3f class DrillBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: DrillBlockEntity, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int ) { val variant = when (entity.inventory[0].item) { IRItemRegistry.STONE_DRILL_HEAD -> "stone" IRItemRegistry.IRON_DRILL_HEAD -> "iron" IRItemRegistry.DIAMOND_DRILL_HEAD -> "diamond" IRItemRegistry.NETHERITE_DRILL_HEAD -> "netherite" else -> return } val model = MinecraftClient.getInstance().bakedModelManager.getModel(ModelIdentifier(identifier("drill_head"), variant)) matrices?.run { push() val entry = peek() translate(0.5, 0.0, 0.5) if (entity.position <= 0.0 && entity.cachedState[DrillBlock.WORKING]) { multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion( (entity.world!!.time + tickDelta) * 12)) } translate(-0.5, entity.position, -0.5) MinecraftClient.getInstance().blockRenderManager.modelRenderer.render( entry, vertexConsumers.getBuffer(RenderLayers.getBlockLayer(entity.cachedState)), null, model, -1f, -1f, -1f, WorldRenderer.getLightmapCoordinates(entity.world, entity.pos), OverlayTexture.DEFAULT_UV ) pop() } } override fun rendersOutsideBoundingBox(blockEntity: DrillBlockEntity?): Boolean = true } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/miningrig/MiningRigBlockEntity.kt ================================================ package me.steven.indrev.blockentities.miningrig import me.steven.indrev.api.OreDataCards import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blocks.machine.DrillBlock import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.components.* import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.config.IRConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.items.misc.OreDataCardItem import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.* import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction import net.minecraft.block.BlockState import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import java.util.EnumSet class MiningRigBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, MachineRegistry.MINING_RIG_REGISTRY, pos, state) { init { this.inventoryComponent = inventory(this) { input { 0 filter { itemStack, _ -> itemStack.item is OreDataCardItem } } } trackLong(ENERGY_REQUIRED_ID) { getEnergyCost() } trackInt(MAX_SPEED_ID) { OreDataCards.readNbt(inventoryComponent!!.inventory.getStack(0))?.speed ?: 0 } VALID_DRILL_POSITIONS.forEachIndexed { index, offset -> val id = START_DRILL_ID + index val drillPos = pos.add(offset) trackDouble(id) { val block = world?.getBlockState(drillPos)?.block as? DrillBlock ?: return@trackDouble 0.0 val drillBlockEntity = world?.getBlockEntity(block.part.getBlockEntityPos(drillPos)) as? DrillBlockEntity ?: return@trackDouble 0.0 drillBlockEntity.miningProgress } } } override val syncToWorld: Boolean = true override val maxInput: Long = config.maxInput override val maxOutput: Long = 0 var lastMinedItem: ItemStack = ItemStack.EMPTY val storageDirections = Direction.values().toMutableList() private val remainingStacks = mutableListOf() override fun machineTick() { val inventory = inventoryComponent?.inventory ?: return if (remainingStacks.isNotEmpty()) { val copy = ArrayList(remainingStacks) remainingStacks.clear() copy.forEach { output(it) } getActiveDrills().forEach { drill -> drill.setWorkingState(false) } workingState = false return } val cardStack = inventory.getStack(0) val data = OreDataCards.readNbt(cardStack) if (data != null && data.isValid() && !data.isEmpty() && use(getEnergyCost())) { workingState = true val before = data.used getActiveDrills().forEach { drill -> drill.tickMining(this, data) } if (before != data.used) OreDataCards.writeNbt(cardStack, data) } else { workingState = false getActiveDrills().forEach { drill -> drill.setWorkingState(false) drill.miningProgress = 0.0 } } } fun output(stack: ItemStack) { val variant = ItemVariant.of(stack) var count = stack.count.toLong() storageDirections.removeIf { dir -> val itemStorage = itemStorageOf(world!!, pos.offset(dir), dir.opposite) ?: return@removeIf true if (count > 0) { transaction { tx -> val inserted = itemStorage.insert(variant, count, tx) tx.commit() count -= inserted } } false } if (count > 0) { stack.count = count.toInt() remainingStacks.add(stack) } } fun getActiveDrills(): List { return VALID_DRILL_POSITIONS.map { pos.add(it) }.mapNotNull { pos -> val blockState = world?.getBlockState(pos) val block = blockState?.block if (block is DrillBlock) { val blockEntity = world?.getBlockEntity(block.part.getBlockEntityPos(pos)) as? DrillBlockEntity ?: return@mapNotNull null val itemStack = blockEntity.inventory[0] if (!itemStack.isEmpty && DrillBlockEntity.isValidDrill(itemStack.item)) { blockEntity } else { blockEntity.setWorkingState(false) null } } else null } } override fun getEnergyCost(): Long { return config.energyCost + (IRConfig.machines.drill * getActiveDrills().size) + (OreDataCards.readNbt(inventoryComponent!!.inventory.getStack(0))?.energyRequired ?: 0) } override fun applyDefault( state: BlockState, type: ConfigurationType, configuration: MutableMap ) { val direction = (state.block as MachineBlock).getFacing(state) when (type) { ConfigurationType.ITEM -> { configuration[direction.rotateYCounterclockwise()] = TransferMode.OUTPUT } else -> super.applyDefault(state, type, configuration) } } override fun getValidConfigurations(type: ConfigurationType): Array { return when (type) { ConfigurationType.ITEM -> arrayOf(TransferMode.OUTPUT, TransferMode.NONE) else -> return super.getValidConfigurations(type) } } override fun toClientTag(tag: NbtCompound) { tag.put("LastMinedBlock", lastMinedItem.writeNbt(NbtCompound())) } override fun fromClientTag(tag: NbtCompound) { lastMinedItem = ItemStack.fromNbt(tag.getCompound("LastMinedBlock")) } companion object { val VALID_DRILL_POSITIONS = arrayOf( BlockPos(-1, 0, 0), BlockPos(1, 0, 0), BlockPos(0, 0, -1), BlockPos(0, 0, 1), BlockPos(-1, 0, -1), BlockPos(-1, 0, 1), BlockPos(1, 0, 1), BlockPos(1, 0, -1) ) const val ENERGY_REQUIRED_ID = 2 const val MAX_SPEED_ID = 3 const val START_DRILL_ID = 4 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/miningrig/MiningRigBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.miningrig import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import net.minecraft.client.MinecraftClient import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.render.model.json.ModelTransformation import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3f class MiningRigBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: MiningRigBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { if (entity == null || !entity.workingState) return matrices?.run { push() val direction = entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING] when (direction) { Direction.NORTH -> translate(0.34, 0.6, -0.01) Direction.SOUTH -> translate(0.66, 0.6, 1.01) Direction.WEST -> translate(-0.01, 0.6, 0.66) Direction.EAST -> translate(1.01, 0.6, 0.34) else -> { pop() return } } multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(direction.asRotation())) scale(0.3f, 0.3f, 0.01f) MinecraftClient.getInstance().itemRenderer.renderItem(entity.lastMinedItem, ModelTransformation.Mode.GUI, 15728880, overlay, this, vertexConsumers, 0) pop() } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/modularworkbench/ModularWorkbenchBlockEntity.kt ================================================ package me.steven.indrev.blockentities.modularworkbench import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.components.autosync import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.items.armor.IRColorModuleItem import me.steven.indrev.items.armor.IRModularArmorItem import me.steven.indrev.items.armor.IRModuleItem import me.steven.indrev.recipes.machines.ModuleRecipe import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.tools.modular.ArmorModule import me.steven.indrev.tools.modular.IRModularItem import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.getRecipes import net.minecraft.block.BlockState import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos class ModularWorkbenchBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, MachineRegistry.MODULAR_WORKBENCH_REGISTRY, pos, state) { init { this.inventoryComponent = inventory(this) { maxStackCount = 1 0 filter { (_, item) -> item !is IRModularItem<*> } 1 filter { stack -> stack.item is IRModuleItem } 2 filter { stack -> stack.item is IRModularItem<*> } 3 until 15 filter { stack, index -> recipe != null && recipe!!.input.size > index - 3 && recipe!!.input[index - 3].ingredient.test(stack) } input { slots = (1 until 15).map { it }.toIntArray() } output { slot = 15 } } } override val syncToWorld: Boolean = true override val maxOutput: Long = 0 var moduleProcessTime by autosync(PROCESS_TIME_ID, 0) var moduleMaxProcessTime by autosync(MAX_PROCESS_TIME_ID, 0) private var processTime by autosync(INSTALL_TIME_ID, 0) private var maxProcessTime by autosync(MAX_INSTALL_TIME_ID, 0) private var state: State by autosync(STATE_ID, State.IDLE, State.values()) var selectedRecipe: Identifier? = null var recipe: ModuleRecipe? = null get() { if (selectedRecipe != null) field = world!!.recipeManager.getRecipes(ModuleRecipe.TYPE)[selectedRecipe]!! return field } override fun machineTick() { tickModuleInstall() tickModuleCraft() } private fun tickModuleCraft() { val inventory = inventoryComponent?.inventory ?: return if (!inventory.getStack(15).isEmpty) return val inputStacks = inventory.inputSlots.map { inventory.getStack(it) } when { recipe?.matches(inputStacks, emptyList()) != true -> { moduleMaxProcessTime = 0 moduleProcessTime = 0 } moduleMaxProcessTime in 1..moduleProcessTime -> { (3 until 15).forEach { slot -> inventory.setStack(slot, ItemStack.EMPTY) } inventory.setStack(15, recipe!!.outputs[0].stack.copy()) moduleMaxProcessTime = 0 moduleProcessTime = 0 } else -> { moduleMaxProcessTime = recipe!!.ticks moduleProcessTime++ } } } private fun tickModuleInstall() { val inventory = inventoryComponent?.inventory ?: return val targetStack = inventory.getStack(2) val moduleStack = inventory.getStack(1) if (moduleStack.item !is IRModuleItem || targetStack.item !is IRModularItem<*>) { processTime = 0 state = State.IDLE return } val targetItem = targetStack.item as IRModularItem<*> val moduleItem = moduleStack.item as IRModuleItem val module = moduleItem.module val compatible = targetItem.getCompatibleModules(targetStack) if (inventory.isEmpty) { processTime = 0 workingState = false state = State.IDLE } else { if (isProcessing() && compatible.contains(module) && use(config.energyCost)) { workingState = true processTime += config.processSpeed.toInt() if (processTime >= maxProcessTime) { inventory.setStack(1, ItemStack.EMPTY) val tag = targetStack.orCreateNbt when { module == ArmorModule.COLOR -> { if (targetItem !is IRModularArmorItem) return val colorModuleItem = moduleItem as IRColorModuleItem targetItem.setColor(targetStack, colorModuleItem.color) } tag.contains(module.key) -> { val level = tag.getInt(module.key) + 1 tag.putInt(module.key, level.coerceAtMost(module.maxLevel)) } else -> tag.putInt(module.key, 1) } processTime = 0 state = State.IDLE } } else if (energy > 0 && !targetStack.isEmpty && !moduleStack.isEmpty && compatible.contains(module)) { val tag = targetStack.orCreateNbt if (tag.contains(module.key)) { val level = module.getMaxInstalledLevel(targetStack) if (module != ArmorModule.COLOR && level >= module.maxLevel) { state = State.MAX_LEVEL return } } processTime = 1 maxProcessTime = 1200 workingState = true state = State.INSTALLING } else { state = State.INCOMPATIBLE } } } private fun isProcessing(): Boolean = processTime > 0 && energy > 0 override fun fromTag(tag: NbtCompound) { processTime = tag.getInt("ProcessTime") if (tag.contains("SelectedRecipe")) selectedRecipe = Identifier(tag.getString("SelectedRecipe")) super.fromTag(tag) } override fun toTag(tag: NbtCompound) { tag.putInt("ProcessTime", processTime) if (selectedRecipe != null) tag.putString("SelectedRecipe", selectedRecipe!!.toString()) super.toTag(tag) } override fun fromClientTag(tag: NbtCompound) { if (tag.contains("SelectedRecipe")) selectedRecipe = Identifier(tag.getString("SelectedRecipe")) inventoryComponent!!.readNbt(tag) } override fun toClientTag(tag: NbtCompound) { if (selectedRecipe != null) tag.putString("SelectedRecipe", selectedRecipe!!.toString()) inventoryComponent!!.writeNbt(tag) } enum class State { IDLE, INSTALLING, INCOMPATIBLE, MAX_LEVEL; } companion object { const val PROCESS_TIME_ID = 2 const val MAX_PROCESS_TIME_ID = 3 const val INSTALL_TIME_ID = 4 const val MAX_INSTALL_TIME_ID = 5 const val STATE_ID = 6 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/modularworkbench/ModularWorkbenchBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.modularworkbench import me.steven.indrev.armor.ModuleFeatureRenderer import me.steven.indrev.components.multiblock.MultiblockBlockEntityRenderer import me.steven.indrev.items.armor.IRModularArmorItem import me.steven.indrev.tools.modular.IRModularItem import me.steven.indrev.utils.identifier import net.minecraft.client.MinecraftClient import net.minecraft.client.model.Dilation import net.minecraft.client.model.TexturedModelData import net.minecraft.client.network.AbstractClientPlayerEntity import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.render.entity.model.BipedEntityModel import net.minecraft.client.render.entity.model.ElytraEntityModel import net.minecraft.client.render.entity.model.PlayerEntityModel import net.minecraft.client.render.item.ItemRenderer import net.minecraft.client.render.model.json.ModelTransformation import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.EquipmentSlot import net.minecraft.item.ArmorItem import net.minecraft.item.ItemStack import net.minecraft.util.Identifier import net.minecraft.util.math.Vec3f class ModularWorkbenchBlockEntityRenderer : BlockEntityRenderer { private val bodyModel = BipedEntityModel(TexturedModelData.of(PlayerEntityModel.getTexturedModelData(Dilation.NONE, false), 64, 64).createModel()) private val leggingsModel = BipedEntityModel(TexturedModelData.of(PlayerEntityModel.getTexturedModelData(Dilation.NONE, false), 64, 64).createModel()) init { bodyModel.setVisible(false) leggingsModel.setVisible(false) } override fun render( entity: ModularWorkbenchBlockEntity, tickDelta: Float, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int ) { ElytraEntityModel.getTexturedModelData().createModel() val itemStack = entity.inventoryComponent?.inventory?.getStack(2) if (itemStack?.item is IRModularArmorItem) { matrices.run { push() val yOffset = if (itemStack.item is IRModularArmorItem) { when ((itemStack.item as IRModularArmorItem).slotType) { EquipmentSlot.HEAD -> 1.0 EquipmentSlot.CHEST -> 1.5 EquipmentSlot.LEGS -> 1.7 EquipmentSlot.FEET -> 2.0 else -> -1.0 } } else 0.0 val time = entity.world?.time ?: 1 translate(0.5, yOffset, 0.5) multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion((time + tickDelta) * 4)) multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(180f)) val lightMapCoords = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos) renderArmor(this, vertexConsumers, itemStack, lightMapCoords) pop() } } else if (itemStack?.item is IRModularItem<*>) { val lightCoord = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos) matrices.run { push() val time = entity.world?.time ?: 1 translate(0.5, 0.35, 0.5) multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion((time + tickDelta) * 4)) MinecraftClient.getInstance().itemRenderer.renderItem(itemStack, ModelTransformation.Mode.GROUND, lightCoord, overlay, matrices, vertexConsumers, 0) pop() } } } private fun renderArmor( matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, itemStack: ItemStack, light: Int ) { val item = itemStack.item as? IRModularArmorItem ?: return val slotType = item.slotType val bipedEntityModel = getArmor(slotType) setVisible(slotType) val rgb = item.getColor(itemStack) val r = (rgb and 0xFF0000 shr 16) / 255f val g = (rgb and 0xFF00 shr 8) / 255f val b = (rgb and 0xFF) / 255f renderArmorParts( matrices, vertexConsumers, light, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(slotType), r, g, b, null ) item.getInstalled(itemStack).filter { it.slots.contains(slotType) }.forEach { module -> if (module.hasTexture) { renderArmorParts( matrices, vertexConsumers, light, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(slotType), r, g, b, module.key ) if (module.hasOverlay) { renderArmorParts( matrices, vertexConsumers, 15728880, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(slotType), r, g, b, "${module.key}_overlay" ) } } } } private fun renderArmorParts( matrixStack: MatrixStack, vertexConsumerProvider: VertexConsumerProvider, light: Int, armorItem: ArmorItem, hasGlint: Boolean, bipedEntityModel: BipedEntityModel, secondLayer: Boolean, r: Float, g: Float, b: Float, overlay: String? ) { val vertexConsumer = ItemRenderer.getArmorGlintConsumer( vertexConsumerProvider, RenderLayer.getArmorCutoutNoCull(getArmorTexture(armorItem, secondLayer, overlay)), false, hasGlint ) bipedEntityModel.render(matrixStack, vertexConsumer, light, OverlayTexture.DEFAULT_UV, r, g, b, 1.0f) } private fun getArmor(slot: EquipmentSlot): BipedEntityModel { return if (usesSecondLayer(slot)) leggingsModel else bodyModel } private fun usesSecondLayer(slot: EquipmentSlot): Boolean { return slot == EquipmentSlot.LEGS } private fun setVisible(slot: EquipmentSlot) { bodyModel.head.visible = slot == EquipmentSlot.HEAD bodyModel.hat.visible = slot == EquipmentSlot.HEAD bodyModel.body.visible = slot == EquipmentSlot.CHEST bodyModel.rightArm.visible = slot == EquipmentSlot.CHEST bodyModel.leftArm.visible = slot == EquipmentSlot.CHEST leggingsModel.body.visible = slot == EquipmentSlot.LEGS leggingsModel.rightLeg.visible = slot == EquipmentSlot.LEGS leggingsModel.leftLeg.visible = slot == EquipmentSlot.LEGS bodyModel.rightLeg.visible = slot == EquipmentSlot.FEET bodyModel.leftLeg.visible = slot == EquipmentSlot.FEET } private fun getArmorTexture(armorItem: ArmorItem, bl: Boolean, string: String?): Identifier { val path = "textures/models/armor/" + armorItem.material.name + "_layer_" + (if (bl) 2 else 1) + (if (string == null) "" else "_$string") + ".png" return ModuleFeatureRenderer.MODULAR_ARMOR_TEXTURE_CACHE.computeIfAbsent(path) { id -> if (string == null) Identifier(id) else identifier(id) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/solarpowerplant/HeliostatBlockEntity.kt ================================================ package me.steven.indrev.blockentities.solarpowerplant import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.blocks.HeliostatBlock import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.world.World class HeliostatBlockEntity(pos: BlockPos, state: BlockState) : BaseBlockEntity(IRBlockRegistry.HELIOSTAT_BLOCK_ENTITY, pos, state) { var targetBlock: BlockPos = BlockPos.ORIGIN var pitch: Float = 0.0f var yaw: Float = 0.0f companion object { fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: HeliostatBlockEntity) { //this doesn't work, i need another way to check if there's no obstruction //val hit = world!!.raycastBlock(pos.toVec3d().add(0.5, 1.5, 0.5), targetBlock.toVec3d().add(0.5, 0.5, 0.5), pos, cachedState.getOutlineShape(world, pos), cachedState) //if (hit !is BlockHitResult || hit.blockPos != targetBlock) return if (!world.isSkyVisible(pos.up())) return val receiver = world.getBlockEntity(blockEntity.targetBlock) as? SolarReceiverBlockEntity ?: return val controller = world.getBlockEntity(receiver.controllerPos) as? SolarPowerPlantTowerBlockEntity ?: return controller.heliostats++ } } override fun toTag(tag: NbtCompound) { tag.putLong("target", targetBlock.asLong()) } override fun fromTag(tag: NbtCompound) { targetBlock = BlockPos.fromLong(tag.getLong("target")) yaw = HeliostatBlock.getYaw(pos, targetBlock) pitch = HeliostatBlock.getPitch(pos, targetBlock) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/solarpowerplant/HeliostatBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.solarpowerplant import net.minecraft.block.Blocks import net.minecraft.block.HorizontalConnectingBlock import net.minecraft.client.MinecraftClient import net.minecraft.client.render.RenderLayers import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Vec3f class HeliostatBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: HeliostatBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { if (entity == null) return matrices?.run { push() val state = Blocks.GLASS_PANE.defaultState.with(HorizontalConnectingBlock.WEST, true).with(HorizontalConnectingBlock.EAST, true) matrices.translate(0.5, 0.5, 0.5) matrices.multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(entity.yaw)) matrices.multiply(Vec3f.POSITIVE_X.getDegreesQuaternion(entity.pitch)) matrices.translate(-0.5, -0.5, -0.5) val buffer = vertexConsumers?.getBuffer(RenderLayers.getBlockLayer(state)) MinecraftClient.getInstance().blockRenderManager.renderBlock(state, entity.pos, entity.world, this, buffer, false, entity.world!!.random) pop() } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/solarpowerplant/SolarPowerPlantTowerBlockEntity.kt ================================================ package me.steven.indrev.blockentities.solarpowerplant import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.blockentities.Syncable import me.steven.indrev.components.* import me.steven.indrev.components.multiblock.MultiBlockComponent import me.steven.indrev.components.multiblock.definitions.SolarPowerPlantTowerStructureDefinition import me.steven.indrev.registry.IRBlockRegistry import me.steven.indrev.utils.bucket import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.world.World class SolarPowerPlantTowerBlockEntity(pos: BlockPos, state: BlockState) : BaseBlockEntity(IRBlockRegistry.SOLAR_POWER_PLANT_TOWER_BLOCK_ENTITY, pos, state), Syncable { override val guiSyncableComponent = GuiSyncableComponent() val temperatureComponent = TemperatureComponent(this, 0.09, 1100..1300, 1500) val multiblockComponent = SolarPowerPlantMultiblockComponent() val fluidComponent = object : FluidComponent({ this }, bucket * 16, 2) { init { this.inputTanks = intArrayOf(0) this.outputTanks = intArrayOf(1) this.unsided = true } } var isMarkedForUpdate = false init { trackObject(INPUT_TANK_ID, fluidComponent[0]) trackObject(OUTPUT_TANK_ID, fluidComponent[1]) } var heliostats = 0 companion object { const val INPUT_TANK_ID = 4 const val OUTPUT_TANK_ID = 5 fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: SolarPowerPlantTowerBlockEntity) { blockEntity.multiblockComponent.tick(world, pos, state) if (blockEntity.multiblockComponent.isBuilt(world, pos, state)) { val limit = blockEntity.heliostats * 6 blockEntity.temperatureComponent.tick(blockEntity.temperatureComponent.temperature < limit + (world.random.nextFloat() * 2 - 1) * 10) blockEntity.markForUpdate() blockEntity.heliostats = 0 } } } override fun fromTag(tag: NbtCompound) { temperatureComponent.readNbt(tag) fluidComponent.fromTag(tag) multiblockComponent.readNbt(tag) } override fun toTag(tag: NbtCompound) { temperatureComponent.writeNbt(tag) fluidComponent.toTag(tag) multiblockComponent.writeNbt(tag) } override fun fromClientTag(tag: NbtCompound) { multiblockComponent.readNbt(tag) } override fun toClientTag(tag: NbtCompound) { multiblockComponent.writeNbt(tag) } override fun markForUpdate(condition: () -> Boolean) { isMarkedForUpdate = isMarkedForUpdate || condition() } inner class SolarPowerPlantMultiblockComponent : MultiBlockComponent({ _, _, _ -> SolarPowerPlantTowerStructureDefinition }) { override fun tick(world: World, pos: BlockPos, blockState: BlockState) { super.tick(world, pos, blockState) SolarPowerPlantTowerStructureDefinition.getSolarReceiverPositions(pos, blockState).forEach { receiverPos -> val blockEntity = world.getBlockEntity(receiverPos) as? SolarReceiverBlockEntity ?: return@forEach blockEntity.controllerPos = pos } SolarPowerPlantTowerStructureDefinition.getFluidInputPositions(pos, blockState).forEach { inputPos -> } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/solarpowerplant/SolarReceiverBlockEntity.kt ================================================ package me.steven.indrev.blockentities.solarpowerplant import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos class SolarReceiverBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(IRBlockRegistry.SOLAR_RECEIVER_BLOCK_ENTITY, pos, state) { var controllerPos: BlockPos = BlockPos.ORIGIN override fun writeNbt(tag: NbtCompound?) { tag?.putLong("controller", controllerPos.asLong()) return super.writeNbt(tag) } override fun readNbt(tag: NbtCompound) { super.readNbt(tag) controllerPos = BlockPos.fromLong(tag.getLong("controller")) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/storage/CabinetBlockEntity.kt ================================================ package me.steven.indrev.blockentities.storage import me.steven.indrev.gui.screenhandlers.storage.CabinetScreenHandler import me.steven.indrev.registry.IRBlockRegistry import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory import net.minecraft.block.BlockState import net.minecraft.block.entity.LootableContainerBlockEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.Inventories import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerContext import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.collection.DefaultedList import net.minecraft.util.math.BlockPos class CabinetBlockEntity(pos: BlockPos, state: BlockState) : LootableContainerBlockEntity(IRBlockRegistry.CABINET_BLOCK_ENTITY_TYPE, pos, state), ExtendedScreenHandlerFactory { private var inventory: DefaultedList = DefaultedList.ofSize(27, ItemStack.EMPTY) override fun size(): Int = 27 override fun getContainerName(): Text = TranslatableText("block.indrev.cabinet") override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory): ScreenHandler { return CabinetScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos)) } override fun getInvStackList(): DefaultedList = inventory override fun setInvStackList(list: DefaultedList) { inventory = list } override fun writeScreenOpeningData(player: ServerPlayerEntity?, buf: PacketByteBuf) { buf.writeBlockPos(pos) } override fun readNbt(tag: NbtCompound?) { super.readNbt(tag) inventory = DefaultedList.ofSize(size(), ItemStack.EMPTY) if (!deserializeLootTable(tag)) { Inventories.readNbt(tag, inventory) } } override fun writeNbt(tag: NbtCompound) { super.writeNbt(tag) if (!serializeLootTable(tag)) { Inventories.writeNbt(tag, inventory) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/storage/ChargePadBlockEntity.kt ================================================ package me.steven.indrev.blockentities.storage import com.google.common.collect.Iterables import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.asMutableList import me.steven.indrev.utils.energyOf import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext import net.minecraft.block.BlockState import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import team.reborn.energy.api.EnergyStorage class ChargePadBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, MachineRegistry.CHARGE_PAD_REGISTRY, pos, state) { init { this.inventoryComponent = inventory(this) { input { slot = 0 } output { slot = 0 } } } override val syncToWorld: Boolean = true override val maxOutput: Long = 16384 override val maxInput: Long = 16384 override val energyCapacity: Long = 0L val energyIo = ChargePadEnergyStorage() var hasCollided = false private fun getItemEnergyIo() = energyOf(inventoryComponent!!.inventory, 0) override fun fromClientTag(tag: NbtCompound) { inventoryComponent!!.readNbt(tag) } override fun toClientTag(tag: NbtCompound) { inventoryComponent!!.writeNbt(tag) } inner class ChargePadEnergyStorage : EnergyStorage { override fun getAmount(): Long = getItemEnergyIo()?.amount ?: 0 override fun getCapacity(): Long = getItemEnergyIo()?.capacity ?: 0 override fun insert(amount: Long, transaction: TransactionContext?): Long { val handlers = if (hasCollided) { val collidedEntityItems = collectItemIos() if (collidedEntityItems.isEmpty()) hasCollided = false getItemEnergyIo()?.also { collidedEntityItems.add(it) } collidedEntityItems } else getItemEnergyIo().let { if (it == null) emptyList() else listOf(it) } if (handlers.isEmpty()) return 0 val max = amount.coerceAtMost(maxOutput) var remainder = max handlers.forEach { handler -> if (remainder > 0) remainder -= handler.insert(remainder, transaction) else return 0 } return max - remainder } override fun extract(maxAmount: Long, transaction: TransactionContext?): Long = 0 override fun supportsExtraction(): Boolean = false override fun supportsInsertion(): Boolean = true private fun collectItemIos(): MutableList { return world!!.getEntitiesByClass(LivingEntity::class.java, Box(pos)) { true }.flatMap { entity -> when (entity) { is PlayerEntity -> Iterables.concat(entity.inventory.armor.mapIndexed { index, _ -> energyOf(entity.inventory, 36 + index) }, listOf(energyOf(entity.inventory, entity.inventory.selectedSlot), energyOf(entity.inventory, 40))) else -> emptyList() } }.filterNotNull().asMutableList() } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/storage/ChargePadBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.storage import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import net.minecraft.client.MinecraftClient import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.render.model.json.ModelTransformation import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3f import kotlin.math.abs import kotlin.math.sin class ChargePadBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: ChargePadBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { val inventory = entity?.inventoryComponent?.inventory val stack = inventory?.getStack(0) if (stack?.isEmpty == false) { val facing = entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING].rotateYClockwise() var x = abs(facing.offsetX.toFloat()) * 0.5 var z = abs(facing.offsetZ.toFloat()) * 0.5 if (facing.offsetX == -1) z = x else if (facing.offsetZ == 1) x = z when (facing.axis) { Direction.Axis.X -> z += 0.2 Direction.Axis.Z -> x += 0.2 else -> return } matrices?.run { push() val time = entity.world?.time ?: 1 val offset = sin((time + tickDelta) / 16.0) / 32.0 translate(x, 1.1 + offset, z) multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion((time + tickDelta) * 4)) scale(0.5f, 0.5f, 0.5f) val lightAbove = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos.up()) MinecraftClient.getInstance().itemRenderer.renderItem(stack, ModelTransformation.Mode.GROUND, lightAbove, overlay, this, vertexConsumers, 0) pop() } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/storage/LazuliFluxContainerBlockEntity.kt ================================================ package me.steven.indrev.blockentities.storage import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blocks.machine.FacingMachineBlock import me.steven.indrev.config.LFCConfig import me.steven.indrev.inventories.inventory import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.energyOf import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import team.reborn.energy.api.EnergyStorageUtil import kotlin.math.floor class LazuliFluxContainerBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity(tier, MachineRegistry.LAZULI_FLUX_CONTAINER_REGISTRY, pos, state) { init { this.inventoryComponent = inventory(this) { input { 0 filter { stack -> energyOf(stack) != null } } } } override val syncToWorld: Boolean = true override val maxInput: Long = config.maxInput override val maxOutput: Long = config.maxOutput val transferConfig: SideConfiguration = SideConfiguration(ConfigurationType.ENERGY) override val storage = LazuliFluxContainerEnergyStorage() private var clientLastRenderWidth = 0f override fun machineTick() { if (world?.isClient == true) return val inventory = inventoryComponent?.inventory ?: return val itemIo = energyOf(inventory, 0) if (itemIo != null) EnergyStorageUtil.move(storage.getSideStorage(null), itemIo, maxOutput, null) } private fun update() { val width = floor((((energy.toFloat() / energyCapacity.toFloat()) * 0.5f) + 0.25f) * 16) if (width != clientLastRenderWidth) { sync() clientLastRenderWidth = width } } override fun isConfigurable(type: ConfigurationType): Boolean { return type == ConfigurationType.ENERGY } override fun getCurrentConfiguration(type: ConfigurationType): SideConfiguration { return transferConfig } override fun applyDefault(state: BlockState, type: ConfigurationType, configuration: MutableMap) { val facing = state[FacingMachineBlock.FACING] Direction.values().forEach { dir -> if (dir == facing && tier != Tier.CREATIVE) configuration[dir] = TransferMode.INPUT else configuration[dir] = TransferMode.OUTPUT } } override fun toTag(tag: NbtCompound) { transferConfig.writeNbt(tag) super.toTag(tag) } override fun fromTag(tag: NbtCompound) { super.fromTag(tag) transferConfig.readNbt(tag) } override fun toClientTag(tag: NbtCompound) { tag.putLong("energy", energy) transferConfig.writeNbt(tag) } override fun fromClientTag(tag: NbtCompound) { transferConfig.readNbt(tag) energy = tag.getLong("energy") } inner class LazuliFluxContainerEnergyStorage : MachineEnergyStorage() { override fun getMaxInsert(side: Direction?): Long { return if (side == null || transferConfig.canInput(side)) maxInput else 0 } override fun getMaxExtract(side: Direction?): Long { return if (side == null || transferConfig.canOutput(side)) maxOutput else 0 } override fun onFinalCommit() { super.onFinalCommit() update() } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/storage/LazuliFluxContainerBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.storage import me.steven.indrev.api.machines.Tier import me.steven.indrev.utils.identifier import net.minecraft.client.MinecraftClient import net.minecraft.client.render.OverlayTexture import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.texture.Sprite import net.minecraft.client.util.math.MatrixStack import net.minecraft.screen.PlayerScreenHandler import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3f import kotlin.math.absoluteValue import kotlin.math.floor import kotlin.math.sin class LazuliFluxContainerBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: LazuliFluxContainerBlockEntity?, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { entity ?: return val width = ((entity.energy.toFloat() / entity.energyCapacity.toFloat()) * 0.5f) + 0.25f val sprite = MinecraftClient.getInstance().getSpriteAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).apply(identifier("block/lazuli_flux_container_lf_level")) val color: Long = (255 shl 24 or when (entity.tier) { Tier.MK1 -> 0xffbb19 Tier.MK2 -> 0x5d3dff Tier.MK3 -> 0xfd47ff else -> 0xff4070 }).toLong() val maxX = floor((width * 16)) / 16f val time = entity.world!!.time matrices?.run { val offset = 0.001 push() translate(-0.0, 0.0, -offset) drawOverlay(this, 0.25f, 0f, maxX, 1f, color, sprite, vertexConsumers, Direction.NORTH, width, tickDelta, time) translate(0.0, 0.0, offset) translate(0.5, 0.5, 0.5) multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(90f)) translate(-0.5, -0.5, -0.5) translate(0.0, 0.0, -offset) drawOverlay(this, 0.25f, 0f, maxX, 1f, color, sprite, vertexConsumers, Direction.EAST, width, tickDelta, time) translate(0.0, 0.0, offset) translate(0.5, 0.5, 0.5) multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(90f)) translate(-0.5, -0.5, -0.5) translate(-0.0, 0.0, -offset) drawOverlay(this, 0.25f, 0f, maxX, 1f, color, sprite, vertexConsumers, Direction.SOUTH, width, tickDelta, time) translate(0.0, 0.0, offset) translate(0.5, 0.5, 0.5) multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(90f)) translate(-0.5, -0.5, -0.5) translate(0.0, 0.0, -offset) drawOverlay(this, 0.25f, 0f, maxX, 1f, color, sprite, vertexConsumers, Direction.WEST, width, tickDelta, time) translate(0.0, 0.0, offset) pop() } } private fun drawOverlay(matrices: MatrixStack, x1: Float, y1: Float, x2: Float, y2: Float, color: Long, sprite: Sprite, vertexConsumers: VertexConsumerProvider?, direction: Direction, width: Float, tickDelta: Float, time: Long) { val matrix = matrices.peek().positionMatrix var xx1 = x1 var xx2 = x2 var yy1 = x1 var yy2 = x2 if (x1 < x2) { xx1 = x2 xx2 = x1 } if (y1 < y2) { yy1 = y2 yy2 = y1 } val a = (color shr 24 and 255) / 255.0f val r = (color shr 16 and 255) / 255.0f val g = (color shr 8 and 255) / 255.0f val b = (color and 255) / 255.0f val vec = direction.unitVector var maxU = sprite.getFrameU(4.0) var minU = sprite.getFrameU(floor(width.toDouble() * 16)) val normal = matrices.peek().normalMatrix vertexConsumers?.getBuffer(RenderLayer.getEntityTranslucent(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE))?.run { fun vertex(x: Float, y: Float, u: Float, v: Float, alpha: Float = a) { this@run.vertex(matrix, x, y, 0.0f).color(r, g, b, alpha).texture(u, v).overlay(OverlayTexture.DEFAULT_UV).light(EMISSIVE_LIGHT).normal(normal, vec.x, vec.y, vec.z).next() } vertex(xx1, yy1, minU, sprite.minV) vertex(xx1, yy2, minU, sprite.maxV) vertex(xx2, yy2, maxU, sprite.maxV) vertex(xx1, yy1, minU, sprite.minV) vertex(xx1, yy1, minU, sprite.minV) vertex(xx2, yy2, maxU, sprite.maxV) vertex(xx2, yy1, maxU, sprite.minV) vertex(xx1, yy1, minU, sprite.minV) if (width < 0.75 && width > 0.25) { xx2 = xx1 xx1 = (floor(width * 16) + 1) / 16f val opacity = sin((time + tickDelta) / 8).absoluteValue maxU = minU minU = sprite.getFrameU(floor(width.toDouble() * 16) + 1) vertex(xx1, yy1, minU, sprite.minV, opacity) vertex(xx1, yy2, minU, sprite.maxV, opacity) vertex(xx2, yy2, maxU, sprite.maxV, opacity) vertex(xx1, yy1, minU, sprite.minV, opacity) vertex(xx1, yy1, minU, sprite.minV, opacity) vertex(xx2, yy2, maxU, sprite.maxV, opacity) vertex(xx2, yy1, maxU, sprite.minV, opacity) vertex(xx1, yy1, minU, sprite.minV, opacity) } } } companion object { private const val EMISSIVE_LIGHT = 15728880 } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/storage/TankBlockEntity.kt ================================================ package me.steven.indrev.blockentities.storage import me.steven.indrev.IndustrialRevolution import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.blockentities.Syncable import me.steven.indrev.blocks.misc.TankBlock import me.steven.indrev.components.FluidComponent import me.steven.indrev.registry.IRBlockRegistry import me.steven.indrev.utils.bucket import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil import net.fabricmc.fabric.api.transfer.v1.storage.base.CombinedStorage import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.world.World class TankBlockEntity(pos: BlockPos, state: BlockState) : BaseBlockEntity(IRBlockRegistry.TANK_BLOCK_ENTITY, pos, state), Syncable { val fluidComponent = object : FluidComponent({ this }, bucket * 8) { init { this.unsided = true } } var isMarkedForUpdate: Boolean = true companion object { fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: TankBlockEntity) { if (world.isClient) return if (blockEntity.isMarkedForUpdate) { blockEntity.markDirty() blockEntity.sync() blockEntity.isMarkedForUpdate = false } if (!state[TankBlock.DOWN]) return val down = world.getBlockEntity(pos.down()) as? TankBlockEntity ?: return val volume = StorageUtil.move(blockEntity.fluidComponent, down.fluidComponent, { true }, Long.MAX_VALUE, null) if (volume > 0) { down.isMarkedForUpdate = true blockEntity.isMarkedForUpdate = true } } } override fun toTag(tag: NbtCompound) { fluidComponent.toTag(tag) } override fun fromTag(tag: NbtCompound) { fluidComponent.fromTag(tag) } override fun toClientTag(tag: NbtCompound) { fluidComponent.toTag(tag) } override fun fromClientTag(tag: NbtCompound) { fluidComponent.fromTag(tag) } override fun markDirty() { if (world != null) { world!!.markDirty(pos) } } override fun markForUpdate(condition: () -> Boolean) { isMarkedForUpdate = isMarkedForUpdate || condition() } class CombinedTankStorage : CombinedStorage(mutableListOf()) { var initialFluid = FluidVariant.blank() fun add(tank: TankBlockEntity): Boolean { val invFluid = tank.fluidComponent[0] if (initialFluid.isBlank && !invFluid.isEmpty) { initialFluid = invFluid.variant } else if (!invFluid.isEmpty && initialFluid != invFluid.variant) { IndustrialRevolution.LOGGER.debug("Found connected tanks with mismatching fluids @ ${tank.pos}") return false } parts.add(tank.fluidComponent) return true } override fun insert(resource: FluidVariant?, maxAmount: Long, transaction: TransactionContext?): Long { if (!initialFluid.isBlank && resource != initialFluid) return 0 return super.insert(resource, maxAmount, transaction) } override fun extract(resource: FluidVariant?, maxAmount: Long, transaction: TransactionContext?): Long { if (!initialFluid.isBlank && resource != initialFluid) return 0 return super.extract(resource, maxAmount, transaction) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blockentities/storage/TankBlockEntityRenderer.kt ================================================ package me.steven.indrev.blockentities.storage import alexiil.mc.lib.attributes.fluid.render.FluidRenderFace import me.steven.indrev.blocks.misc.TankBlock import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.math.MatrixStack class TankBlockEntityRenderer : BlockEntityRenderer { override fun render( entity: TankBlockEntity, tickDelta: Float, matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, light: Int, overlay: Int ) { val fluidComponent = entity.fluidComponent val volume = fluidComponent[0] if (volume.isEmpty) return val fluid = volume.amount val maxFluid = fluidComponent.limit var percent = fluid.toFloat() / maxFluid.toFloat() val maxHeight = if (entity.cachedState[TankBlock.UP]) 16 else 14 percent = (percent * maxHeight).toInt() / 16f val yHeight = percent.toDouble().coerceAtLeast(0.1) val faces = mutableListOf( FluidRenderFace.createFlatFaceZ(0.9, 0.0, 0.1, 0.1, yHeight, 0.1, 1.0, true, false), FluidRenderFace.createFlatFaceZ(0.1, 0.0, 0.9, 0.9, yHeight, 0.9, 1.0, true, false), FluidRenderFace.createFlatFaceX(0.1, 0.0, 0.1, 0.1, yHeight, 0.9, 1.0, false, false), FluidRenderFace.createFlatFaceX(0.9, 0.0, 0.9, 0.9, yHeight, 0.1, 1.0, false, false), ) var renderFluidTop = true if (entity.cachedState[TankBlock.UP]) { val aboveTank = entity.world!!.getBlockEntity(entity.pos.up()) as? TankBlockEntity renderFluidTop = aboveTank?.fluidComponent?.get(0)?.variant != volume.variant } if (renderFluidTop) { faces.add(FluidRenderFace.createFlatFaceY(0.1, yHeight, 0.1, 0.9, yHeight, 0.9, 1.0, true, false)) } for (face in faces) { face.light = light; } volume.render(faces, vertexConsumers, matrices); } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/HeliostatBlock.kt ================================================ package me.steven.indrev.blocks import me.steven.indrev.blockentities.solarpowerplant.HeliostatBlockEntity import net.minecraft.block.Block import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockRenderType import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.MathHelper import net.minecraft.world.World class HeliostatBlock(settings: Settings) : Block(settings), BlockEntityProvider { override fun getRenderType(state: BlockState?): BlockRenderType = BlockRenderType.ENTITYBLOCK_ANIMATED override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = HeliostatBlockEntity(pos, state) override fun getTicker( world: World, state: BlockState?, type: BlockEntityType? ): BlockEntityTicker? { return if (world.isClient) null else return BlockEntityTicker { world, pos, state, blockEntity -> HeliostatBlockEntity.tick(world, pos, state, blockEntity as? HeliostatBlockEntity ?: return@BlockEntityTicker) } } companion object { private const val RAD2DEG = 57.2957763671875 fun getYaw(origin: BlockPos, target: BlockPos): Float { val xOffset = target.x + 0.0 - origin.x.toDouble() + 0.0 val zOffset = target.z + 0.0 - origin.z.toDouble() + 0.0 return MathHelper.wrapDegrees((MathHelper.atan2(zOffset, xOffset) * RAD2DEG).toFloat() - 90.0f) } fun getPitch(origin: BlockPos, target: BlockPos): Float { val xOffset = target.x + 0.0f - origin.x.toFloat() + 0.0f val yOffset = target.y + 0.0f - origin.y.toFloat() + 0.0f val zOffset = target.z + 0.0f - origin.z.toFloat() + 0.0f val g = MathHelper.sqrt(xOffset * xOffset + zOffset * zOffset).toDouble() return MathHelper.wrapDegrees((-(MathHelper.atan2(yOffset.toDouble(), g) * RAD2DEG)).toFloat()) } fun findConnectingHeliostats(origin: BlockPos, world: World, scanned: MutableSet, positions: MutableSet) { Direction.values().forEach { dir -> val off = origin.offset(dir) if (scanned.add(off.asLong()) && world.testBlockState(off) { state -> state.block is HeliostatBlock }) { positions.add(off.asLong()) findConnectingHeliostats(off, world, scanned, positions) } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/CapsuleBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.blockentities.laser.CapsuleBlockEntity import me.steven.indrev.recipes.machines.LaserRecipe import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.minecraft.block.Block import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.Material import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.ItemScatterer import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockView import net.minecraft.world.World class CapsuleBlock : Block(FabricBlockSettings.of(Material.GLASS).requiresTool().nonOpaque().strength(1f, 1f)), BlockEntityProvider { override fun onUse( state: BlockState?, world: World, pos: BlockPos?, player: PlayerEntity, hand: Hand?, hit: BlockHitResult? ): ActionResult { if (!world.isClient && hand == Hand.MAIN_HAND) { val stack = player.getStackInHand(hand) val recipe = LaserRecipe.TYPE.getMatchingRecipe(world as ServerWorld, stack) .firstOrNull { it.matches(stack, emptyList()) } val blockEntity = world.getBlockEntity(pos) as? CapsuleBlockEntity ?: return ActionResult.PASS if (recipe != null && blockEntity.inventory[0].isEmpty) { player.setStackInHand(hand, ItemStack.EMPTY) blockEntity.inventory[0] = stack } else if (!blockEntity.inventory[0].isEmpty) { val itemStack = blockEntity.inventory[0] itemStack.nbt = null player.inventory?.insertStack(itemStack) blockEntity.inventory[0] = ItemStack.EMPTY } else return ActionResult.PASS world.updateNeighbors(pos, this) blockEntity.markDirty() blockEntity.sync() return ActionResult.SUCCESS } return ActionResult.PASS } override fun onStateReplaced( state: BlockState?, world: World, pos: BlockPos, newState: BlockState?, moved: Boolean ) { val blockEntity = world.getBlockEntity(pos) as? CapsuleBlockEntity if (blockEntity != null) ItemScatterer.spawn(world, pos, blockEntity.inventory) super.onStateReplaced(state, world, pos, newState, moved) } override fun emitsRedstonePower(state: BlockState?): Boolean = true override fun getWeakRedstonePower( state: BlockState?, world: BlockView, pos: BlockPos, direction: Direction? ): Int { val blockEntity = world.getBlockEntity(pos) as? CapsuleBlockEntity ?: return 0 if (blockEntity.world!!.isClient) return 0 val stack = blockEntity.inventory[0] val recipe = LaserRecipe.TYPE.getMatchingRecipe(world as ServerWorld, stack) .firstOrNull { it.matches(stack, emptyList()) } return if (recipe == null) 15 else 0 } override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = CapsuleBlockEntity(pos, state) override fun getTicker( world: World, state: BlockState?, type: BlockEntityType? ): BlockEntityTicker? { return if (world.isClient) null else BlockEntityTicker { world, pos, state, blockEntity -> CapsuleBlockEntity.tick(world, pos, state, blockEntity as CapsuleBlockEntity) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/ChargePadBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.storage.ChargePadBlockEntity import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.energyOf import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.block.BlockState import net.minecraft.block.ShapeContext import net.minecraft.client.item.TooltipContext import net.minecraft.entity.Entity import net.minecraft.entity.decoration.ArmorStandEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.Formatting import net.minecraft.util.Hand import net.minecraft.util.function.BooleanBiFunction import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView import net.minecraft.world.World import java.util.* import java.util.stream.Stream class ChargePadBlock(registry: MachineRegistry, settings: Settings, tier: Tier) : HorizontalFacingMachineBlock(registry, settings, tier, null, null) { override fun getOutlineShape( state: BlockState, world: BlockView?, pos: BlockPos?, context: ShapeContext? ): VoxelShape = when (state[HORIZONTAL_FACING]) { Direction.NORTH -> FACING_NORTH Direction.SOUTH -> FACING_SOUTH Direction.EAST -> FACING_EAST Direction.WEST -> FACING_WEST else -> FACING_NORTH } override fun onUse(state: BlockState?, world: World, pos: BlockPos?, player: PlayerEntity?, hand: Hand?, hit: BlockHitResult?): ActionResult { val blockEntity = world.getBlockEntity(pos) as? ChargePadBlockEntity ?: return ActionResult.PASS val inventory = blockEntity.inventoryComponent?.inventory ?: return ActionResult.PASS val machineStack = inventory.getStack(0) if (!machineStack.isEmpty) { player?.inventory?.insertStack(machineStack) return ActionResult.SUCCESS } val handStack = player?.mainHandStack if (energyOf(player?.inventory, player?.inventory?.selectedSlot?: return ActionResult.PASS) != null) { inventory.setStack(0, handStack) player.setStackInHand(Hand.MAIN_HAND, ItemStack.EMPTY) return ActionResult.SUCCESS } return ActionResult.PASS } override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { super.getPlacementState(ctx) return this.defaultState.with(HORIZONTAL_FACING, ctx?.playerFacing) } override fun onEntityCollision(state: BlockState?, world: World?, pos: BlockPos?, entity: Entity?) { if (entity is PlayerEntity || entity is ArmorStandEntity) { val blockEntity = world?.getBlockEntity(pos) as? ChargePadBlockEntity ?: return blockEntity.hasCollided = true } } override fun appendTooltip(stack: ItemStack?, view: BlockView?, tooltip: MutableList?, options: TooltipContext?) { super.appendTooltip(stack, view, tooltip, options) tooltip?.add(TranslatableText("block.indrev.charge_pad_mk4.tooltip").formatted(Formatting.BLUE, Formatting.ITALIC)) } @Environment(EnvType.CLIENT) override fun randomDisplayTick(state: BlockState?, world: World, pos: BlockPos, random: Random?) { } companion object { private val FACING_NORTH = Stream.of( createCuboidShape(1.0, 0.0, 1.0, 15.0, 0.1, 15.0), createCuboidShape(1.25, 0.0, 1.25, 14.75, 0.3, 14.75), createCuboidShape(7.0, 0.0, 2.0, 9.0, 15.0, 4.0), createCuboidShape(8.0, 15.0, 2.0, 9.0, 15.5, 3.0), createCuboidShape(7.0, 15.0, 2.0, 8.0, 15.5, 3.0), createCuboidShape(7.0, 15.0, 3.0, 8.0, 15.2, 4.0), createCuboidShape(8.0, 15.0, 3.0, 9.0, 15.2, 4.0) ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) private val FACING_SOUTH: VoxelShape = Stream.of( createCuboidShape(1.0, 0.0, 1.0, 15.0, 0.1, 15.0), createCuboidShape(1.25, 0.0, 1.25, 14.75, 0.3, 14.75), createCuboidShape(7.0, 0.0, 12.0, 9.0, 15.0, 14.0), createCuboidShape(7.0, 15.0, 13.0, 8.0, 15.5, 14.0), createCuboidShape(8.0, 15.0, 13.0, 9.0, 15.5, 14.0), createCuboidShape(8.0, 15.0, 12.0, 9.0, 15.2, 13.0), createCuboidShape(7.0, 15.0, 12.0, 8.0, 15.2, 13.0) ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) private val FACING_WEST = Stream.of( createCuboidShape(1.0, 0.0, 1.0, 15.0, 0.1, 15.0), createCuboidShape(1.25, 0.0, 1.25, 14.75, 0.3, 14.75), createCuboidShape(2.0, 0.0, 7.0, 4.0, 15.0, 9.0), createCuboidShape(2.0, 15.0, 7.0, 3.0, 15.5, 8.0), createCuboidShape(2.0, 15.0, 8.0, 3.0, 15.5, 9.0), createCuboidShape(3.0, 15.0, 8.0, 4.0, 15.2, 9.0), createCuboidShape(3.0, 15.0, 7.0, 4.0, 15.2, 8.0) ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) private val FACING_EAST = Stream.of( createCuboidShape(1.0, 0.0, 1.0, 15.0, 0.1, 15.0), createCuboidShape(1.25, 0.0, 1.25, 14.75, 0.3, 14.75), createCuboidShape(12.0, 0.0, 7.0, 14.0, 15.0, 9.0), createCuboidShape(13.0, 15.0, 8.0, 14.0, 15.5, 9.0), createCuboidShape(13.0, 15.0, 7.0, 14.0, 15.5, 8.0), createCuboidShape(12.0, 15.0, 7.0, 13.0, 15.2, 8.0), createCuboidShape(12.0, 15.0, 8.0, 13.0, 15.2, 9.0) ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/DirtOxygenatorBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.api.machines.Tier import me.steven.indrev.config.IRConfig import me.steven.indrev.registry.MachineRegistry import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.minecraft.block.BlockState import net.minecraft.item.ItemPlacementContext class DirtOxygenatorBlock(registry: MachineRegistry, settings: FabricBlockSettings) : FacingMachineBlock(registry, settings, Tier.MK1, IRConfig.machines.dirtOxygenator, null) { override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { return this.defaultState.with(FACING, ctx?.playerLookDirection) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/DrillBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.blockentities.miningrig.DrillBlockEntity import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.Block import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack import net.minecraft.state.StateManager import net.minecraft.state.property.BooleanProperty import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.ItemScatterer import net.minecraft.util.StringIdentifiable import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockView import net.minecraft.world.World import net.minecraft.world.WorldAccess import net.minecraft.world.WorldView open class DrillBlock private constructor(settings: Settings, val part: DrillPart) : Block(settings) { init { this.defaultState = stateManager.defaultState.with(WORKING, false) } override fun getPlacementState(ctx: ItemPlacementContext): BlockState? { val middle = ctx.world.getBlockState(ctx.blockPos.up()) val top = ctx.world.getBlockState(ctx.blockPos.up(2)) return if (middle.canReplace(ctx) && top.canReplace(ctx)) defaultState else null } override fun onPlaced( world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack? ) { world.setBlockState(pos.up(2), DRILL_TOP.defaultState) world.setBlockState(pos.up(), DRILL_MIDDLE.defaultState) } override fun onUse( state: BlockState, world: World, pos: BlockPos, player: PlayerEntity?, hand: Hand?, hit: BlockHitResult? ): ActionResult { if (!world.isClient) { val blockEntity = world.getBlockEntity(part.getBlockEntityPos(pos)) as? DrillBlockEntity ?: return ActionResult.PASS player?.openHandledScreen(blockEntity) } return ActionResult.CONSUME } override fun getStateForNeighborUpdate( state: BlockState, direction: Direction?, newState: BlockState, world: WorldAccess, pos: BlockPos, posFrom: BlockPos? ): BlockState { return if (newState.block is DrillBlock) state.with(WORKING, newState[WORKING]) else if (!part.test(world, pos)) Blocks.AIR.defaultState else state } override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) { if (!world.isClient && player.isCreative && part != DrillPart.BOTTOM) { val bottom = part.getBlockEntityPos(pos) world.setBlockState(bottom, Blocks.AIR.defaultState) } super.onBreak(world, pos, state, player) } override fun getPickStack(world: BlockView, pos: BlockPos, state: BlockState): ItemStack = ItemStack(DRILL_BOTTOM) override fun appendProperties(builder: StateManager.Builder?) { builder?.add(WORKING) } enum class DrillPart(val str: String) : StringIdentifiable { TOP("top") { override fun test(world: WorldView, pos: BlockPos): Boolean { return world.getBlockState(pos.down()).isOf(DRILL_MIDDLE) && world.getBlockState(pos.down(2)).isOf(DRILL_BOTTOM) } override fun getBlockEntityPos(pos: BlockPos): BlockPos = pos.down(2) }, MIDDLE("middle") { override fun test(world: WorldView, pos: BlockPos): Boolean { return world.getBlockState(pos.up()).isOf(DRILL_TOP) && world.getBlockState(pos.down()).isOf(DRILL_BOTTOM) } override fun getBlockEntityPos(pos: BlockPos): BlockPos = pos.down() }, BOTTOM("bottom") { override fun test(world: WorldView, pos: BlockPos): Boolean { return world.getBlockState(pos.up()).isOf(DRILL_MIDDLE) && world.getBlockState(pos.up(2)).isOf(DRILL_TOP) } override fun getBlockEntityPos(pos: BlockPos): BlockPos = pos }; abstract fun test(world: WorldView, pos: BlockPos): Boolean abstract fun getBlockEntityPos(pos: BlockPos): BlockPos override fun asString(): String = str } class TopDrillBlock(settings: Settings) : DrillBlock(settings, DrillPart.TOP) class MiddleDrillBlock(settings: Settings) : DrillBlock(settings, DrillPart.MIDDLE) class BottomDrillBlock(settings: Settings) : DrillBlock(settings, DrillPart.BOTTOM), BlockEntityProvider { override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = DrillBlockEntity(pos, state) override fun getTicker( world: World, state: BlockState?, type: BlockEntityType? ): BlockEntityTicker? { return if (world.isClient) null else BlockEntityTicker { world, pos, state, blockEntity -> DrillBlockEntity.tick(world, pos, state, blockEntity as DrillBlockEntity) } } override fun onStateReplaced( state: BlockState, world: World?, pos: BlockPos?, newState: BlockState, moved: Boolean ) { if (!newState.isOf(this)) { (world?.getBlockEntity(pos) as? DrillBlockEntity)?.let { ItemScatterer.spawn(world, pos, it) } } super.onStateReplaced(state, world, pos, newState, moved) } } companion object { private val DRILL_TOP by lazy { IRBlockRegistry.DRILL_TOP } private val DRILL_MIDDLE by lazy { IRBlockRegistry.DRILL_MIDDLE } private val DRILL_BOTTOM by lazy { IRBlockRegistry.DRILL_BOTTOM } val WORKING: BooleanProperty = BooleanProperty.of("working") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/DrillHeadModel.kt ================================================ package me.steven.indrev.blocks.machine import com.mojang.datafixers.util.Pair import me.steven.indrev.utils.identifier import net.minecraft.client.render.model.BakedModel import net.minecraft.client.render.model.ModelBakeSettings import net.minecraft.client.render.model.ModelLoader import net.minecraft.client.render.model.UnbakedModel import net.minecraft.client.texture.Sprite import net.minecraft.client.util.SpriteIdentifier import net.minecraft.screen.PlayerScreenHandler import net.minecraft.util.Identifier import java.util.function.Function class DrillHeadModel(val variant: String) : UnbakedModel { private val spriteIdCollection = mutableListOf( SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier("item/${variant}_drill_head")) ) private val modelIdentifier = identifier("block/${variant}_drill_head") private var bakedModel: BakedModel? = null override fun bake( loader: ModelLoader, textureGetter: Function, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel? { bakedModel = loader.getOrLoadModel(modelIdentifier).bake(loader, textureGetter, rotationContainer, modelId) return bakedModel } override fun getModelDependencies(): MutableCollection = mutableListOf() override fun getTextureDependencies( unbakedModelGetter: Function?, unresolvedTextureReferences: MutableSet>? ): MutableCollection { return spriteIdCollection } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/ElectrolyticSeparatorBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.api.machines.Tier import me.steven.indrev.config.IRConfig import me.steven.indrev.gui.screenhandlers.machines.ElectrolyticSeparatorScreenHandler import me.steven.indrev.registry.MachineRegistry import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings class ElectrolyticSeparatorBlock(registry: MachineRegistry, settings: FabricBlockSettings, tier: Tier) : HorizontalFacingMachineBlock(registry, settings, tier, when (tier) { Tier.MK1 -> IRConfig.machines.electrolyticSeparatorMk1 Tier.MK2 -> IRConfig.machines.electrolyticSeparatorMk2 Tier.MK3 -> IRConfig.machines.electrolyticSeparatorMk3 else -> IRConfig.machines.electrolyticSeparatorMk4 }, ::ElectrolyticSeparatorScreenHandler ) { } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/FacingMachineBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.api.machines.Tier import me.steven.indrev.config.IConfig import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemPlacementContext import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerContext import net.minecraft.state.StateManager import net.minecraft.state.property.DirectionProperty import net.minecraft.state.property.Properties import net.minecraft.util.BlockRotation import net.minecraft.util.math.Direction open class FacingMachineBlock( registry: MachineRegistry, settings: Settings, tier: Tier, config: IConfig?, screenHandler: ((Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler)?, ) : MachineBlock(registry, settings, tier, config, screenHandler) { override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { super.getPlacementState(ctx) return this.defaultState.with(FACING, ctx?.playerLookDirection?.opposite) } override fun appendProperties(builder: StateManager.Builder?) { super.appendProperties(builder) builder?.add(FACING) } override fun rotate(state: BlockState, rotation: BlockRotation): BlockState { return state.with(FACING, HorizontalFacingMachineBlock.getRotated(state[FACING], rotation)) } override fun getFacing(state: BlockState): Direction = state[FACING] companion object { val FACING: DirectionProperty = Properties.FACING } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/HorizontalFacingMachineBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.api.machines.Tier import me.steven.indrev.config.IConfig import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemPlacementContext import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerContext import net.minecraft.state.StateManager import net.minecraft.state.property.DirectionProperty import net.minecraft.state.property.Properties import net.minecraft.util.BlockRotation import net.minecraft.util.math.Direction open class HorizontalFacingMachineBlock( registry: MachineRegistry, settings: Settings, tier: Tier, config: IConfig?, screenHandler: ((Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler)? ) : MachineBlock(registry, settings, tier, config, screenHandler) { override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { super.getPlacementState(ctx) return this.defaultState.with(HORIZONTAL_FACING, ctx?.playerFacing?.opposite) } override fun appendProperties(builder: StateManager.Builder?) { super.appendProperties(builder) builder?.add(HORIZONTAL_FACING) } override fun rotate(state: BlockState, rotation: BlockRotation): BlockState { return state.with(HORIZONTAL_FACING, getRotated(state[HORIZONTAL_FACING], rotation)) } override fun getFacing(state: BlockState): Direction = state[HORIZONTAL_FACING] companion object { val HORIZONTAL_FACING: DirectionProperty = Properties.HORIZONTAL_FACING fun getRotated(direction: Direction, rotation: BlockRotation): Direction = if (direction.axis.isVertical) direction else when (rotation) { BlockRotation.NONE -> direction BlockRotation.CLOCKWISE_90 -> direction.rotateYClockwise() BlockRotation.CLOCKWISE_180 -> direction.opposite BlockRotation.COUNTERCLOCKWISE_90 -> direction.rotateYCounterclockwise() } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/LaserBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.config.IRConfig import me.steven.indrev.gui.screenhandlers.machines.LaserEmitterScreenHandler import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.component3 import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.entity.damage.DamageSource import net.minecraft.item.ItemPlacementContext import net.minecraft.sound.SoundCategory import net.minecraft.state.StateManager import net.minecraft.state.property.BooleanProperty import net.minecraft.state.property.Properties import net.minecraft.util.math.BlockPos import net.minecraft.world.World import java.util.* class LaserBlock(registry: MachineRegistry, settings: Settings) : FacingMachineBlock( registry, settings, Tier.MK4, IRConfig.machines.laser, ::LaserEmitterScreenHandler ) { override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { return super.getPlacementState(ctx)?.with(POWERED, false) } override fun appendProperties(builder: StateManager.Builder?) { super.appendProperties(builder) builder?.add(POWERED) } @Suppress("DEPRECATION") override fun neighborUpdate( state: BlockState?, world: World?, pos: BlockPos?, block: Block?, fromPos: BlockPos?, notify: Boolean ) { super.neighborUpdate(state, world, pos, block, fromPos, notify) world?.setBlockState(pos, state?.with(POWERED, world.isReceivingRedstonePower(pos))) } override fun randomDisplayTick(state: BlockState?, world: World, pos: BlockPos, random: Random?) { if (state!![POWERED] && random!!.nextDouble() > 0.9) { val (x, y, z) = pos world.playSound(x.toDouble() + 0.5, y.toDouble(), z.toDouble() + 0.5, IndustrialRevolution.LASER_SOUND_EVENT, SoundCategory.BLOCKS, 0.4f, 1f, false ) } } companion object { val POWERED: BooleanProperty = Properties.POWERED val LASER_DAMAGE_SOURCE = object : DamageSource("laser") { init { setFire() setBypassesArmor() } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/LazuliFluxContainerBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity import me.steven.indrev.config.IRConfig import me.steven.indrev.gui.screenhandlers.machines.LazuliFluxContainerScreenHandler import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos import net.minecraft.world.World class LazuliFluxContainerBlock(registry: MachineRegistry, settings: Settings, tier: Tier) : FacingMachineBlock( registry, settings, tier, when (tier) { Tier.MK1 -> IRConfig.machines.lazuliFluxContainerMk1 Tier.MK2 -> IRConfig.machines.lazuliFluxContainerMk2 Tier.MK3 -> IRConfig.machines.lazuliFluxContainerMk3 else -> IRConfig.machines.lazuliFluxContainerMk4 }, ::LazuliFluxContainerScreenHandler ) { override fun onPlaced( world: World?, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack? ) { super.onPlaced(world, pos, state, placer, itemStack) if (world?.isClient == true) { val blockEntity = world.getBlockEntity(pos) as? LazuliFluxContainerBlockEntity ?: return ConfigurationType.values().forEach { type -> if (blockEntity.isConfigurable(type)) blockEntity.applyDefault(state, type, blockEntity.getCurrentConfiguration(type)) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/MachineBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity import me.steven.indrev.config.IConfig import me.steven.indrev.gui.IRScreenHandlerFactory import me.steven.indrev.items.misc.IRMachineUpgradeItem import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.energyOf import me.steven.indrev.utils.screwdriver import me.steven.indrev.utils.wrench import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil import net.minecraft.block.Block import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.InventoryProvider import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.SidedInventory import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack import net.minecraft.particle.ParticleTypes import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerContext import net.minecraft.server.world.ServerWorld import net.minecraft.stat.Stats import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.BlockRotation import net.minecraft.util.Hand import net.minecraft.util.ItemScatterer import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World import net.minecraft.world.WorldAccess import java.util.* open class MachineBlock( val registry: MachineRegistry, settings: Settings, val tier: Tier, val config: IConfig?, private val screenHandler: ((Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler)?, ) : Block(settings), BlockEntityProvider, InventoryProvider{ override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? = registry.blockEntityType(tier).instantiate(pos, state) override fun getTicker( world: World, state: BlockState?, type: BlockEntityType? ): BlockEntityTicker? { return if (world.isClient) BlockEntityTicker { _, _, _, blockEntity -> (blockEntity as? MachineBlockEntity<*>)?.machineClientTick() } else BlockEntityTicker { _, _, _, blockEntity -> (blockEntity as? MachineBlockEntity<*>)?.tick() } } override fun onUse( state: BlockState?, world: World, pos: BlockPos?, player: PlayerEntity?, hand: Hand?, hit: BlockHitResult? ): ActionResult? { if (world.isClient) return ActionResult.CONSUME val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return ActionResult.FAIL if (blockEntity.fluidComponent != null) { val result = StorageUtil.move(ContainerItemContext.ofPlayerHand(player, hand).find(FluidStorage.ITEM), blockEntity.fluidComponent, { true }, Long.MAX_VALUE, null) if (result > 0) return ActionResult.SUCCESS } val stack = player?.mainHandStack!! val item = stack.item if (item is IRMachineUpgradeItem) { return ActionResult.PASS } else if (stack.isIn(IndustrialRevolution.WRENCH_TAG)) { return wrench(world, pos!!, state!!, blockEntity, player, stack) } else if (stack.isIn(IndustrialRevolution.SCREWDRIVER_TAG)) { return screwdriver(world, pos!!, state!!, blockEntity, player, stack) } else if (blockEntity.multiblockComponent != null && !blockEntity.multiblockComponent!!.isBuilt(world, pos!!, blockEntity.cachedState, true)) { player.sendMessage(TranslatableText("text.multiblock.not_built"), true) blockEntity.multiblockComponent?.toggleRender(player.isSneaking) blockEntity.markDirty() blockEntity.sync() } else if (screenHandler != null) { player.openHandledScreen(IRScreenHandlerFactory(screenHandler, pos!!)) } else return ActionResult.PASS return ActionResult.SUCCESS } @Suppress("DEPRECATION") override fun onStateReplaced(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean) { val oldBlockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> super.onStateReplaced(state, world, pos, newState, moved) if (world.isClient) return if (newState.isOf(this)) { val oldFacing = getFacing(state) val newFacing = getFacing(newState) if (oldFacing == newFacing) return val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return val rotation = offset(oldFacing, newFacing) blockEntity.inventoryComponent?.run { update(EnumMap(itemConfig).clone(), itemConfig, rotation) } blockEntity.fluidComponent?.run { update(EnumMap(transferConfig).clone(), transferConfig, rotation) } if (blockEntity is LazuliFluxContainerBlockEntity) { update(EnumMap(blockEntity.transferConfig).clone(), blockEntity.transferConfig, rotation) } blockEntity.markDirty() } else if (oldBlockEntity?.inventoryComponent != null) { ItemScatterer.spawn(world, pos, oldBlockEntity.inventoryComponent!!.inventory) world.updateComparators(pos, this) } } private fun update(original: EnumMap, config: SideConfiguration, rotation: BlockRotation) { Direction.values().forEach { side -> config[side] = original[rotation.rotate(side)]!! } } private fun offset(old: Direction, new: Direction): BlockRotation { return when (old) { new.rotateYClockwise() -> BlockRotation.CLOCKWISE_90 new.rotateYCounterclockwise() -> BlockRotation.COUNTERCLOCKWISE_90 new.opposite -> BlockRotation.CLOCKWISE_180 else -> BlockRotation.NONE } } override fun afterBreak(world: World?, player: PlayerEntity?, pos: BlockPos?, state: BlockState?, blockEntity: BlockEntity?, toolStack: ItemStack?) { player?.incrementStat(Stats.MINED.getOrCreateStat(this)) player?.addExhaustion(0.005f) writeNbtComponents(world, player, pos, state, blockEntity, toolStack) } fun writeNbtComponents(world: World?, player: PlayerEntity?, pos: BlockPos?, state: BlockState?, blockEntity: BlockEntity?, toolStack: ItemStack?) { if (world is ServerWorld) { getDroppedStacks(state, world, pos, blockEntity, player, toolStack).forEach { stack -> val item = stack.item if (blockEntity is MachineBlockEntity<*> && item is BlockItem && item.block is MachineBlock) { val itemIo = energyOf(stack) if (itemIo != null) { stack.orCreateNbt.putLong("energy", blockEntity.energy) } val tag = stack.getOrCreateSubNbt("MachineInfo") val temperatureController = blockEntity.temperatureComponent if (temperatureController != null) tag.putDouble("Temperature", temperatureController.temperature) } dropStack(world, pos, stack) } state!!.onStacksDropped(world, pos, toolStack) } } override fun onPlaced(world: World?, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack?) { super.onPlaced(world, pos, state, placer, itemStack) if (world?.isClient == true) return val blockEntity = world?.getBlockEntity(pos) if (blockEntity is MachineBlockEntity<*>) { val tag = itemStack?.getSubNbt("MachineInfo") val temperatureController = blockEntity.temperatureComponent val itemIo = energyOf(itemStack) if (itemIo != null) { blockEntity.energy = itemIo.amount } if (temperatureController != null) { val temperature = tag?.getDouble("Temperature") if (temperature != null) temperatureController.temperature = temperature } ConfigurationType.values().forEach { type -> if (blockEntity.isConfigurable(type)) blockEntity.applyDefault(state, type, blockEntity.getCurrentConfiguration(type)) } world.updateNeighbors(pos, this) blockEntity.markDirty() } } override fun neighborUpdate( state: BlockState?, world: World?, pos: BlockPos?, block: Block?, fromPos: BlockPos?, notify: Boolean ) { val blockEntity = world?.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return blockEntity.validConnections.clear() blockEntity.validConnections.addAll(Direction.values()) } open fun getFacing(state: BlockState): Direction = Direction.UP override fun getInventory(state: BlockState?, world: WorldAccess?, pos: BlockPos?): SidedInventory? { val blockEntity = world?.getBlockEntity(pos) as? InventoryProvider ?: return null return blockEntity.getInventory(state, world, pos) } @Environment(EnvType.CLIENT) override fun randomDisplayTick(state: BlockState?, world: World, pos: BlockPos, random: Random?) { val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return if (blockEntity.workingState) { val d = pos.x.toDouble() + 0.5 val e = pos.y.toDouble() + 1.0 val f = pos.z.toDouble() + 0.5 world.addParticle(ParticleTypes.SMOKE, d, e, f, 0.0, 0.0, 0.0) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/MiningRigBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.miningrig.MiningRigBlockEntity import me.steven.indrev.config.IRConfig import me.steven.indrev.gui.screenhandlers.machines.MiningRigComputerScreenHandler import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.itemStorageOf import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.client.item.TooltipContext import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockView import net.minecraft.world.World class MiningRigBlock(registry: MachineRegistry, settings: Settings, tier: Tier) : HorizontalFacingMachineBlock( registry, settings, tier, IRConfig.machines.miner, ::MiningRigComputerScreenHandler, ) { override fun appendTooltip( stack: ItemStack?, view: BlockView?, tooltip: MutableList?, options: TooltipContext? ) { super.appendTooltip(stack, view, tooltip, options) tooltip?.add( TranslatableText("block.indrev.mining_rig.tooltip").formatted(Formatting.BLUE, Formatting.ITALIC) ) } override fun neighborUpdate( state: BlockState?, world: World?, pos: BlockPos?, block: Block?, fromPos: BlockPos?, notify: Boolean ) { super.neighborUpdate(state, world, pos, block, fromPos, notify) if (world is ServerWorld) { val dir = Direction.fromVector(fromPos!!.subtract(pos)) ?: return if (itemStorageOf(world, fromPos, dir) != null) { val blockEntity = world.getBlockEntity(pos) as? MiningRigBlockEntity ?: return blockEntity.storageDirections.addAll(Direction.values()) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/PumpBlock.kt ================================================ package me.steven.indrev.blocks.machine import me.steven.indrev.api.machines.Tier import me.steven.indrev.config.IRConfig import me.steven.indrev.gui.screenhandlers.machines.PumpScreenHandler import me.steven.indrev.registry.MachineRegistry import net.minecraft.block.BlockState import net.minecraft.block.ShapeContext import net.minecraft.item.ItemPlacementContext import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView class PumpBlock(registry: MachineRegistry, settings: Settings) : HorizontalFacingMachineBlock( registry, settings, Tier.MK1, IRConfig.machines.pump, ::PumpScreenHandler) { override fun getOutlineShape( state: BlockState?, world: BlockView?, pos: BlockPos?, context: ShapeContext? ): VoxelShape = when (state?.get(HORIZONTAL_FACING)) { Direction.NORTH -> SHAPE_NORTH Direction.SOUTH -> SHAPE_SOUTH Direction.WEST -> SHAPE_WEST Direction.EAST -> SHAPE_EAST else -> VoxelShapes.fullCube() } override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { return this.defaultState.with(HORIZONTAL_FACING, ctx?.playerFacing) } companion object { private val SHAPE_NORTH = createCuboidShape(2.5, 0.0, 0.0, 14.5, 16.0, 14.5) private val SHAPE_SOUTH = createCuboidShape(2.5, 0.0, 2.5, 14.5, 16.0, 16.0) private val SHAPE_WEST = createCuboidShape(0.0, 0.0, 2.5, 14.5, 16.0, 14.5) private val SHAPE_EAST = createCuboidShape(2.5, 0.0, 2.5, 16.0, 16.0, 14.5) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/pipes/BasePipeBlock.kt ================================================ package me.steven.indrev.blocks.machine.pipes import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import me.steven.indrev.items.misc.IRServoItem import me.steven.indrev.networks.EndpointData import me.steven.indrev.networks.Network import me.steven.indrev.networks.ServoNetworkState import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.component3 import me.steven.indrev.utils.toVec3d import net.minecraft.block.Block import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.ShapeContext import net.minecraft.block.entity.BlockEntity import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.BlockItem import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.ItemScatterer import net.minecraft.util.collection.DefaultedList import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView import net.minecraft.world.World abstract class BasePipeBlock(settings: Settings, val tier: Tier, val type: Network.Type<*>) : Block(settings), BlockEntityProvider { init { this.defaultState = stateManager.defaultState } abstract fun getShape(blockEntity: BasePipeBlockEntity): VoxelShape override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? = BasePipeBlockEntity(type, tier, pos, state) override fun getOutlineShape( state: BlockState, view: BlockView, pos: BlockPos?, context: ShapeContext? ): VoxelShape { val blockEntity = view.getBlockEntity(pos) as? BasePipeBlockEntity ?: return VoxelShapes.empty() return if (blockEntity.coverState != null) VoxelShapes.fullCube() else getShape(blockEntity) } override fun hasDynamicBounds(): Boolean = true override fun onBlockBreakStart(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity) { if (!world.isClient) { val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return if (blockEntity.coverState != null && player.mainHandStack.isOf(IRItemRegistry.WRENCH)) { val cover = blockEntity.coverState ?: return ItemScatterer.spawn(world, pos, DefaultedList.ofSize(1, ItemStack(cover.block))) blockEntity.coverState = null blockEntity.markDirty() blockEntity.sync() } } } override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult { val handStack = player.getStackInHand(hand) ?: return ActionResult.FAIL val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return ActionResult.FAIL val tryWrench = onWrench(state, world, pos, player, hand, hit) if (tryWrench.isAccepted) return tryWrench else if (handStack.item is IRServoItem) return ActionResult.PASS val item = handStack.item if ( blockEntity.coverState == null && !player.isSneaking && isValidCover(handStack, world, pos) ) { val result = (item as BlockItem).block.getPlacementState(ItemPlacementContext(player, hand, handStack, hit)) blockEntity.coverState = result if (!world.isClient) { blockEntity.markDirty() blockEntity.sync() } if (!player.abilities.creativeMode) handStack.decrement(1) return ActionResult.success(world.isClient) } return ActionResult.PASS } private fun isValidCover(stack: ItemStack, world: World, pos: BlockPos): Boolean { val item = stack.item return !stack.isEmpty && item is BlockItem && item.block !is BlockEntityProvider && item.block.defaultState.isFullCube(world, pos) } private fun onWrench(state: BlockState,world: World, pos: BlockPos,player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult { val handStack = player.getStackInHand(hand) ?: return ActionResult.FAIL val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return ActionResult.FAIL if (handStack.isIn(IndustrialRevolution.WRENCH_TAG) && world is ServerWorld) { val dir = getSideFromHit(hit.pos, pos) val (x, y, z) = hit.pos val networkState = type.getNetworkState(world) if (networkState is ServoNetworkState<*>) { if (dir != null) { val data = networkState.removeEndpointData(pos, dir) when (data?.type) { EndpointData.Type.OUTPUT -> { ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_OUTPUT)) return ActionResult.SUCCESS } EndpointData.Type.RETRIEVER -> { ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_RETRIEVER)) return ActionResult.SUCCESS } else -> { } } } } if (blockEntity.connections[dir]!!.isConnected()) blockEntity.connections[dir] = ConnectionType.WRENCHED else blockEntity.connections[hit.side] = if (isConnectable(world, pos, hit.side)) ConnectionType.CONNECTED else ConnectionType.NONE blockEntity.markDirty() blockEntity.sync() world.updateNeighbors(pos, state.block) Network.handleUpdate(networkState, pos) return ActionResult.success(world.isClient) } return ActionResult.PASS } @Suppress("DEPRECATION") override fun onStateReplaced( blockState: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean ) { super.onStateReplaced(blockState, world, pos, newState, moved) if (!world.isClient) { val networkState = type.getNetworkState(world as ServerWorld) if (networkState is ServoNetworkState<*>) { if (blockState.isOf(newState.block)) { Network.handleUpdate(networkState, pos) } else { Direction.values().forEach { dir -> val data = networkState.removeEndpointData(pos, dir) val (x, y, z) = pos.toVec3d() when (data?.type) { EndpointData.Type.OUTPUT -> ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_OUTPUT)) EndpointData.Type.RETRIEVER -> ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_RETRIEVER)) else -> { } } } } } Network.handleBreak(networkState, pos) } } abstract fun isConnectable(world: ServerWorld, pos: BlockPos, dir: Direction): Boolean override fun onPlaced( world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack? ) { super.onPlaced(world, pos, state, placer, itemStack) if (!world.isClient) { DIRECTIONS.forEach { facing -> updateConnection(world as ServerWorld, pos, pos.offset(facing), facing) } val networkState = type.getNetworkState(world as ServerWorld) Network.handleUpdate(networkState, pos) } } override fun neighborUpdate( state: BlockState, world: World?, pos: BlockPos, block: Block?, fromPos: BlockPos, notify: Boolean ) { val (x, y, z) = pos.subtract(fromPos) val facing = Direction.fromVector(x, y, z)?.opposite ?: return if (world is ServerWorld) { updateConnection(world, pos, fromPos, facing) } } private fun updateConnection(world: ServerWorld, pos: BlockPos, neighborPos: BlockPos, facing: Direction) { val networkState = type.getNetworkState(world) val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return val before = blockEntity.connections[facing] val new = ConnectionType.getType(isConnectable(world, neighborPos, facing)) val neighborBlockEntity = world.getBlockEntity(neighborPos) as? BasePipeBlockEntity if (before != new && before?.isConnectable() != false && new.isConnectable()) { neighborBlockEntity?.connections?.put(facing.opposite, new) neighborBlockEntity?.markDirty() neighborBlockEntity?.sync() blockEntity.connections[facing] = new blockEntity.markDirty() blockEntity.sync() Network.handleUpdate(networkState, pos) } } enum class ConnectionType(val id: Int) { NONE(-1), CONNECTED(0), WRENCHED(1); fun isConnected() = this == CONNECTED fun isConnectable() = this != WRENCHED companion object { fun getType(connects: Boolean) = if (connects) CONNECTED else NONE fun byId(id: Int): ConnectionType { return when (id) { 0 -> CONNECTED 1 -> WRENCHED else -> NONE } } } } companion object { fun getSideFromHit(hit: Vec3d, pos: BlockPos): Direction? { val x = hit.x - pos.x val y = hit.y - pos.y val z = hit.z - pos.z return when { y > 0.6625 -> Direction.UP y < 0.3375 -> Direction.DOWN x > 0.6793 -> Direction.EAST x < 0.3169 -> Direction.WEST z < 0.3169 -> Direction.NORTH z > 0.6625 -> Direction.SOUTH else -> null } } val DIRECTIONS = Direction.values() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/pipes/CableBlock.kt ================================================ package me.steven.indrev.blocks.machine.pipes import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import me.steven.indrev.config.IRConfig import me.steven.indrev.networks.Network import me.steven.indrev.networks.energy.CableEnergyIo import me.steven.indrev.utils.energyOf import me.steven.indrev.utils.pack import net.minecraft.client.item.TooltipContext import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView import java.util.function.IntFunction class CableBlock(settings: Settings, tier: Tier) : BasePipeBlock(settings, tier, Network.Type.ENERGY) { override fun appendTooltip( stack: ItemStack?, world: BlockView?, tooltip: MutableList?, options: TooltipContext? ) { tooltip?.add( TranslatableText("gui.indrev.tooltip.maxTransferRate").formatted(Formatting.AQUA) .append(TranslatableText("gui.indrev.tooltip.lftick", getMaxTransferRate()).formatted(Formatting.GRAY)) ) } override fun isConnectable(world: ServerWorld, pos: BlockPos, dir: Direction): Boolean { val handler = energyOf(world, pos, dir.opposite) if (handler != null && handler !is CableEnergyIo) return true val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return false if (!blockEntity.cachedState.isOf(this)) return false return blockEntity.connections[dir.opposite]!!.isConnectable() } private fun getMaxTransferRate() = when(tier) { Tier.MK1 -> IRConfig.cables.cableMk1 Tier.MK2 -> IRConfig.cables.cableMk2 Tier.MK3 -> IRConfig.cables.cableMk3 else -> IRConfig.cables.cableMk4 } override fun getShape(blockEntity: BasePipeBlockEntity): VoxelShape { val directions = DIRECTIONS.filter { dir -> blockEntity.connections[dir] == ConnectionType.CONNECTED } return SHAPE_CACHE.get().computeIfAbsent(pack(directions).toInt(), IntFunction { var shape = CENTER_SHAPE directions.forEach { direction -> shape = VoxelShapes.union(shape, getShape(direction)) } shape }) } companion object { val SHAPE_CACHE: ThreadLocal> = ThreadLocal.withInitial { object : Int2ObjectOpenHashMap(64, 0.25f) { override fun rehash(newN: Int) { } } } val DOWN_SHAPE: VoxelShape = createCuboidShape(6.0, 0.0, 6.0, 10.0, 6.0, 10.0) val UP_SHAPE: VoxelShape = createCuboidShape(6.0, 10.5, 6.0, 10.0, 16.0, 10.0) val SOUTH_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 10.5, 10.0, 10.0, 16.0) val NORTH_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 0.0, 10.0, 10.0, 5.5) val EAST_SHAPE: VoxelShape = createCuboidShape(10.5, 6.0, 6.0, 16.0, 10.0, 10.0) val WEST_SHAPE: VoxelShape = createCuboidShape(0.0, 6.0, 6.0, 5.5, 10.0, 10.0) val CENTER_SHAPE: VoxelShape = createCuboidShape(5.5, 5.5, 5.5, 10.5, 10.5, 10.5) private fun getShape(direction: Direction): VoxelShape { var shape = VoxelShapes.empty() if (direction == Direction.NORTH) shape = NORTH_SHAPE if (direction == Direction.SOUTH) shape = SOUTH_SHAPE if (direction == Direction.EAST) shape = EAST_SHAPE if (direction == Direction.WEST) shape = WEST_SHAPE if (direction == Direction.UP) shape = UP_SHAPE if (direction == Direction.DOWN) shape = DOWN_SHAPE return shape } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/pipes/FluidPipeBlock.kt ================================================ package me.steven.indrev.blocks.machine.pipes import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import me.steven.indrev.config.IRConfig import me.steven.indrev.networks.Network import me.steven.indrev.networks.ServoNetworkState import me.steven.indrev.utils.fluidStorageOf import me.steven.indrev.utils.pack import net.minecraft.client.item.TooltipContext import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView import java.util.function.IntFunction class FluidPipeBlock(settings: Settings, tier: Tier) : BasePipeBlock(settings, tier, Network.Type.FLUID) { override fun appendTooltip( stack: ItemStack?, world: BlockView?, tooltip: MutableList?, options: TooltipContext? ) { tooltip?.add( TranslatableText("gui.indrev.tooltip.maxTransferRate").formatted(Formatting.AQUA) .append(TranslatableText("gui.indrev.tooltip.fluidsec", getMaxTransferRate()).formatted(Formatting.GRAY)) ) } override fun isConnectable(world: ServerWorld, pos: BlockPos, dir: Direction): Boolean { if (fluidStorageOf(world, pos, dir.opposite) != null) return true if ((type.getNetworkState(world) as ServoNetworkState<*>).hasServo(pos.offset(dir.opposite), dir)) return true val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return false if (!blockEntity.cachedState.isOf(this)) return false return blockEntity.connections[dir.opposite]!!.isConnectable() } private fun getMaxTransferRate() = when(tier) { Tier.MK1 -> IRConfig.cables.fluidPipeMk1 Tier.MK2 -> IRConfig.cables.fluidPipeMk2 Tier.MK3 -> IRConfig.cables.fluidPipeMk3 else -> IRConfig.cables.fluidPipeMk4 } override fun getShape(blockEntity: BasePipeBlockEntity): VoxelShape { val directions = DIRECTIONS.filter { dir -> blockEntity.connections[dir] == ConnectionType.CONNECTED } return SHAPE_CACHE.get().computeIfAbsent(pack(directions).toInt(), IntFunction { var shape = CENTER_SHAPE directions.forEach { direction -> shape = VoxelShapes.union(shape, getShape(direction)) } shape }) } companion object { val SHAPE_CACHE: ThreadLocal> = ThreadLocal.withInitial { object : Int2ObjectOpenHashMap(64, 0.25f) { override fun rehash(newN: Int) { } } } val DOWN_SHAPE: VoxelShape = createCuboidShape(6.0, 0.0, 6.0, 10.0, 6.0, 10.0) val UP_SHAPE: VoxelShape = createCuboidShape(6.0, 10.0, 6.0, 10.0, 16.0, 10.0) val SOUTH_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 10.5, 10.0, 10.0, 16.0) val NORTH_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 0.0, 10.0, 10.0, 6.0) val EAST_SHAPE: VoxelShape = createCuboidShape(10.0, 6.0, 6.0, 16.0, 10.0, 10.0) val WEST_SHAPE: VoxelShape = createCuboidShape(0.0, 6.0, 6.0, 6.0, 10.0, 10.0) val CENTER_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 6.0, 10.0, 10.0, 10.0) private fun getShape(direction: Direction): VoxelShape { var shape = VoxelShapes.empty() if (direction == Direction.NORTH) shape = NORTH_SHAPE if (direction == Direction.SOUTH) shape = SOUTH_SHAPE if (direction == Direction.EAST) shape = EAST_SHAPE if (direction == Direction.WEST) shape = WEST_SHAPE if (direction == Direction.UP) shape = UP_SHAPE if (direction == Direction.DOWN) shape = DOWN_SHAPE return shape } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/pipes/ItemPipeBlock.kt ================================================ package me.steven.indrev.blocks.machine.pipes import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import me.steven.indrev.config.IRConfig import me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreenFactory import me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreenHandler import me.steven.indrev.networks.Network import me.steven.indrev.networks.ServoNetworkState import me.steven.indrev.utils.itemStorageOf import me.steven.indrev.utils.pack import net.minecraft.block.BlockState import net.minecraft.client.item.TooltipContext import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.Formatting import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView import net.minecraft.world.World import java.util.function.IntFunction class ItemPipeBlock(settings: Settings, tier: Tier) : BasePipeBlock(settings, tier, Network.Type.ITEM) { override fun appendTooltip( stack: ItemStack?, world: BlockView?, tooltip: MutableList?, options: TooltipContext? ) { tooltip?.add( TranslatableText("gui.indrev.tooltip.maxTransferRate").formatted(Formatting.AQUA) .append(TranslatableText("gui.indrev.tooltip.itemsec", getMaxTransferRate()).formatted(Formatting.GRAY)) ) } override fun onUse( state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult ): ActionResult { val dir = getSideFromHit(hit.pos, pos) val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return ActionResult.PASS if (hand == Hand.MAIN_HAND && !world.isClient && player.getStackInHand(hand).isEmpty && dir != null && blockEntity.connections[dir]!!.isConnected()) { val type = Network.Type.ITEM val networkState = type.getNetworkState(world as ServerWorld) if (networkState.networksByPos.get(pos.asLong())?.containers?.containsKey(pos.offset(dir)) == true) { player.openHandledScreen(PipeFilterScreenFactory(::PipeFilterScreenHandler, pos, dir)) return ActionResult.SUCCESS } } return super.onUse(state, world, pos, player, hand, hit) } override fun isConnectable(world: ServerWorld, pos: BlockPos, dir: Direction): Boolean { if (itemStorageOf(world, pos, dir.opposite) != null) return true if ((type.getNetworkState(world) as ServoNetworkState<*>).hasServo(pos.offset(dir.opposite), dir)) return true val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return false if (!blockEntity.cachedState.isOf(this)) return false return blockEntity.connections[dir.opposite]!!.isConnectable() } private fun getMaxTransferRate() = when(tier) { Tier.MK1 -> IRConfig.cables.itemPipeMk1 Tier.MK2 -> IRConfig.cables.itemPipeMk2 Tier.MK3 -> IRConfig.cables.itemPipeMk3 else -> IRConfig.cables.itemPipeMk4 } override fun getShape(blockEntity: BasePipeBlockEntity): VoxelShape { val directions = DIRECTIONS.filter { dir -> blockEntity.connections[dir] == ConnectionType.CONNECTED } return SHAPE_CACHE.get().computeIfAbsent(pack(directions).toInt(), IntFunction { var shape = CENTER_SHAPE directions.forEach { direction -> shape = VoxelShapes.union(shape, getShape(direction)) } shape }) } companion object { val SHAPE_CACHE: ThreadLocal> = ThreadLocal.withInitial { object : Int2ObjectOpenHashMap(64, 0.25f) { override fun rehash(newN: Int) { } } } val DOWN_SHAPE: VoxelShape = createCuboidShape(6.5, 0.0, 6.5, 9.5, 6.5, 9.5) val UP_SHAPE: VoxelShape = createCuboidShape(6.5, 9.5, 6.5, 9.5, 16.0, 9.5) val SOUTH_SHAPE: VoxelShape = createCuboidShape(6.5, 6.5, 9.5, 9.5, 9.5, 16.0) val NORTH_SHAPE: VoxelShape = createCuboidShape(6.5, 6.5, 0.0, 9.5, 9.5, 6.5) val EAST_SHAPE: VoxelShape = createCuboidShape(9.5, 6.5, 6.5, 16.0, 9.5, 9.5) val WEST_SHAPE: VoxelShape = createCuboidShape(0.0, 6.5, 6.5, 6.5, 9.5, 9.5) val CENTER_SHAPE: VoxelShape = createCuboidShape(6.5, 6.5, 6.5, 9.5, 9.5, 9.5) private fun getShape(direction: Direction): VoxelShape { var shape = VoxelShapes.empty() if (direction == Direction.NORTH) shape = NORTH_SHAPE if (direction == Direction.SOUTH) shape = SOUTH_SHAPE if (direction == Direction.EAST) shape = EAST_SHAPE if (direction == Direction.WEST) shape = WEST_SHAPE if (direction == Direction.UP) shape = UP_SHAPE if (direction == Direction.DOWN) shape = DOWN_SHAPE return shape } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/FluidValveBlock.kt ================================================ package me.steven.indrev.blocks.machine.solarpowerplant import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.FacingBlock import net.minecraft.item.ItemPlacementContext import net.minecraft.state.StateManager import net.minecraft.util.BlockMirror import net.minecraft.util.BlockRotation import net.minecraft.util.math.Direction open class FluidValveBlock(settings: Settings) : FacingBlock(settings) { init { this.defaultState = stateManager.defaultState.with(FACING, Direction.NORTH) } override fun appendProperties(builder: StateManager.Builder?) { builder?.add(FACING) } override fun rotate(state: BlockState, rotation: BlockRotation): BlockState? { return state.with(FACING, rotation.rotate(state[FACING])) } override fun mirror(state: BlockState, mirror: BlockMirror): BlockState? { return state.rotate(mirror.getRotation(state[FACING])) } override fun getPlacementState(ctx: ItemPlacementContext): BlockState? { return defaultState.with(FACING, ctx.playerLookDirection.opposite) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SolarPowerPlantFluidOutputBlock.kt ================================================ package me.steven.indrev.blocks.machine.solarpowerplant import me.steven.indrev.blocks.misc.HorizontalFacingBlock class SolarPowerPlantFluidOutputBlock(settings: Settings) : HorizontalFacingBlock(settings) ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SolarPowerPlantTowerBlock.kt ================================================ package me.steven.indrev.blocks.machine.solarpowerplant import me.steven.indrev.IndustrialRevolution import me.steven.indrev.blockentities.solarpowerplant.HeliostatBlockEntity import me.steven.indrev.blockentities.solarpowerplant.SolarPowerPlantTowerBlockEntity import me.steven.indrev.blocks.misc.HorizontalFacingBlock import me.steven.indrev.components.multiblock.definitions.SolarPowerPlantTowerStructureDefinition import me.steven.indrev.gui.IRScreenHandlerFactory import me.steven.indrev.gui.screenhandlers.machines.SolarPowerPlantTowerScreenHandler import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.entity.player.PlayerEntity import net.minecraft.nbt.NbtLong import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.world.World class SolarPowerPlantTowerBlock(settings: Settings) : HorizontalFacingBlock(settings), BlockEntityProvider { override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = SolarPowerPlantTowerBlockEntity(pos, state) override fun getTicker( world: World, state: BlockState?, type: BlockEntityType? ): BlockEntityTicker? { return if (world.isClient) null else return BlockEntityTicker { world, pos, state, blockEntity -> SolarPowerPlantTowerBlockEntity.tick(world, pos, state, blockEntity as? SolarPowerPlantTowerBlockEntity ?: return@BlockEntityTicker) } } override fun onUse( state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult? ): ActionResult { if (!world.isClient) { val stack = player.getStackInHand(hand) if (stack.isIn(IndustrialRevolution.SCREWDRIVER_TAG) && stack.orCreateNbt.contains("SelectedHeliostats")) { val positions = stack.nbt!!.getList("SelectedHeliostats", 4)?.map { BlockPos.fromLong((it as NbtLong).longValue()) } val receivers = SolarPowerPlantTowerStructureDefinition.getSolarReceiverPositions(pos, state) positions?.forEach { p -> val heliostat = world.getBlockEntity(p) as HeliostatBlockEntity val target = receivers.minByOrNull { it.getManhattanDistance(p) } ?: return@forEach heliostat.targetBlock = target heliostat.markDirty() heliostat.sync() } player.sendMessage(LiteralText("Linked Heliostats!"), true) } else { val blockEntity = world.getBlockEntity(pos) as? SolarPowerPlantTowerBlockEntity ?: return ActionResult.PASS if (!blockEntity.multiblockComponent.isBuilt(world, pos, state)) { player.sendMessage(TranslatableText("text.multiblock.not_built"), true) blockEntity.multiblockComponent.toggleRender(player.isSneaking) blockEntity.markDirty() blockEntity.sync() } else player.openHandledScreen(IRScreenHandlerFactory(::SolarPowerPlantTowerScreenHandler, pos)) } } return ActionResult.success(world.isClient) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SolarReceiverBlock.kt ================================================ package me.steven.indrev.blocks.machine.solarpowerplant import me.steven.indrev.blockentities.solarpowerplant.SolarReceiverBlockEntity import net.minecraft.block.Block import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.util.math.BlockPos class SolarReceiverBlock(settings: Settings) : Block(settings), BlockEntityProvider { override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = SolarReceiverBlockEntity(pos, state) } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SteamTurbineBlock.kt ================================================ package me.steven.indrev.blocks.machine.solarpowerplant import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.config.IRConfig import me.steven.indrev.gui.screenhandlers.machines.SteamTurbineScreenHandler import me.steven.indrev.registry.MachineRegistry class SteamTurbineBlock(registry: MachineRegistry, settings: Settings) : HorizontalFacingMachineBlock(registry, settings, Tier.MK4, IRConfig.generators.steamTurbine, ::SteamTurbineScreenHandler) ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SteamTurbineSteamInputValveBlock.kt ================================================ package me.steven.indrev.blocks.machine.solarpowerplant import me.steven.indrev.blockentities.generators.SteamTurbineSteamInputValveBlockEntity import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.util.math.BlockPos import net.minecraft.world.World class SteamTurbineSteamInputValveBlock(settings: Settings) : FluidValveBlock(settings), BlockEntityProvider { override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = SteamTurbineSteamInputValveBlockEntity(pos, state) override fun getTicker( world: World, state: BlockState?, type: BlockEntityType? ): BlockEntityTicker? { return if (world.isClient) null else BlockEntityTicker { _, _, _, blockEntity -> (blockEntity as? SteamTurbineSteamInputValveBlockEntity)?.inserted = false } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/AcidFluidBlock.kt ================================================ package me.steven.indrev.blocks.misc import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.block.FluidBlock import net.minecraft.entity.Entity import net.minecraft.entity.damage.DamageSource import net.minecraft.fluid.FlowableFluid import net.minecraft.particle.ParticleTypes import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World import java.util.* class AcidFluidBlock(fluid: FlowableFluid, settings: Settings) : FluidBlock(fluid, settings) { override fun randomTick(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?) { Direction.values().forEach { dir -> val neighbor = pos?.offset(dir) val blockState = world?.getBlockState(neighbor) val block = blockState?.block if (block == Blocks.DIRT || block == Blocks.GRASS_BLOCK || block == Blocks.FARMLAND || block == Blocks.DIRT_PATH) world?.setBlockState(pos, Blocks.COARSE_DIRT.defaultState) } } override fun randomDisplayTick(state: BlockState?, world: World, pos: BlockPos, random: Random) { if (!world.isAir(pos.up()) || random.nextInt(10) < 5) return (0..1).forEach { a -> (0..1).forEach { b -> world.addParticle( ParticleTypes.SNEEZE, pos.x + a / 2.0 + (random.nextFloat() / 5), pos.y + 1.0, pos.z + b / 2.0 + (random.nextFloat() / 5), 0.0, 0.005, 0.0) } } } override fun onEntityCollision(state: BlockState?, world: World, pos: BlockPos?, entity: Entity?) { if (world.time % 15 == 0L) entity?.damage(ACID_DAMAGE_SOURCE, 4f) } companion object { val ACID_DAMAGE_SOURCE = object : DamageSource("acid") { init { setBypassesArmor() setUnblockable() } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/BiomassComposterBlock.kt ================================================ package me.steven.indrev.blocks.misc import me.steven.indrev.blockentities.farms.BiomassComposterBlockEntity import me.steven.indrev.registry.IRBlockRegistry import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.bucket import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil import net.minecraft.block.* import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.state.StateManager import net.minecraft.state.property.BooleanProperty import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.world.World class BiomassComposterBlock : Block(FabricBlockSettings.copyOf(Blocks.COMPOSTER)), BlockEntityProvider { init { this.defaultState = stateManager.defaultState.with(CLOSED, false) } override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = BiomassComposterBlockEntity(pos, state) override fun getTicker( world: World, state: BlockState, type: BlockEntityType ): BlockEntityTicker? { return if (world.isClient) null else BlockEntityTicker { _, _, state, blockEntity -> BiomassComposterBlockEntity.tick( state, blockEntity as? BiomassComposterBlockEntity ?: return@BlockEntityTicker ) } } override fun appendProperties(builder: StateManager.Builder?) { builder?.add(CLOSED) } override fun onUse( state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult? ): ActionResult { val stack = player.getStackInHand(hand) val blockEntity = world.getBlockEntity(pos) as? BiomassComposterBlockEntity ?: return ActionResult.PASS val inHand = ContainerItemContext.ofPlayerHand(player, hand).find(FluidStorage.ITEM) val result = StorageUtil.move(inHand, blockEntity.fluidInv, { true }, bucket, null) if (result > 0) { blockEntity.markDirty() if (!world.isClient) blockEntity.sync() return ActionResult.success(world.isClient) } else if (ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.contains(stack.item) || stack.isEmpty) { compost(stack, player, blockEntity, world) } else if (state[CLOSED]) { world.setBlockState(pos, state.with(CLOSED, false)) if (!player.isCreative) player.inventory.offerOrDrop(ItemStack(IRBlockRegistry.PLANKS)) } else if (stack.isOf(IRBlockRegistry.PLANKS.asItem())) { world.setBlockState(pos, state.with(CLOSED, true)) if (!player.isCreative) stack.decrement(1) } else return ActionResult.FAIL return ActionResult.success(world.isClient) } private fun compost(stack: ItemStack, player: PlayerEntity, blockEntity: BiomassComposterBlockEntity, world: World) { if (world.isClient) return if (blockEntity.itemInv.variant.isOf(IRItemRegistry.BIOMASS)) { player.inventory.offerOrDrop(ItemStack(IRItemRegistry.BIOMASS)) blockEntity.itemInv.variant = ItemVariant.blank() blockEntity.itemInv.amount = 0 blockEntity.markDirty() blockEntity.sync() } else if (blockEntity.level < 7 && !stack.isEmpty) { val chance = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.getValue(stack.item) if (!player.isCreative) stack.decrement(1) var leveledUp = false if (world.random.nextInt() < chance) { blockEntity.level++ blockEntity.markDirty() blockEntity.sync() leveledUp = true } world.syncWorldEvent(1500, blockEntity.pos, if (leveledUp) 1 else 0) } } companion object { val CLOSED: BooleanProperty = BooleanProperty.of("closed") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/CabinetBlock.kt ================================================ package me.steven.indrev.blocks.misc import me.steven.indrev.blockentities.storage.CabinetBlockEntity import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.world.World class CabinetBlock(settings: Settings) : HorizontalFacingBlock(settings), BlockEntityProvider { override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = CabinetBlockEntity(pos, state) override fun onUse( state: BlockState?, world: World, pos: BlockPos?, player: PlayerEntity, hand: Hand?, hit: BlockHitResult? ): ActionResult { if (!world.isClient) { val blockEntity = world.getBlockEntity(pos) as? CabinetBlockEntity ?: return ActionResult.PASS player.openHandledScreen(blockEntity) } return ActionResult.success(world.isClient) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/DuctBlock.kt ================================================ package me.steven.indrev.blocks.misc import net.minecraft.block.BlockState import net.minecraft.block.ShapeContext import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.world.BlockView class DuctBlock(settings: Settings) : HorizontalFacingBlock(settings) { override fun getOutlineShape( state: BlockState, world: BlockView?, pos: BlockPos?, context: ShapeContext? ): VoxelShape = when (state[FACING]) { Direction.NORTH -> FACING_NORTH Direction.SOUTH -> FACING_SOUTH Direction.WEST -> FACING_WEST Direction.EAST -> FACING_EAST else -> FACING_NORTH } companion object { private val FACING_SOUTH: VoxelShape = createCuboidShape(2.0, 0.0, 0.0, 14.0, 14.0, 14.0) private val FACING_NORTH: VoxelShape = createCuboidShape(2.0, 0.0, 2.0, 14.0, 14.0, 16.0) private val FACING_EAST: VoxelShape = createCuboidShape(0.0, 0.0, 2.0, 14.0, 14.0, 14.0) private val FACING_WEST: VoxelShape = createCuboidShape(2.0, 0.0, 2.0, 16.0, 14.0, 14.0) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/HorizontalFacingBlock.kt ================================================ package me.steven.indrev.blocks.misc import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.HorizontalFacingBlock import net.minecraft.item.ItemPlacementContext import net.minecraft.state.StateManager import net.minecraft.util.math.Direction open class HorizontalFacingBlock(settings: Settings) : HorizontalFacingBlock(settings) { init { this.defaultState = stateManager.defaultState.with(FACING, Direction.NORTH) } override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { return defaultState.with(FACING, ctx?.playerFacing?.opposite) } override fun appendProperties(builder: StateManager.Builder?) { builder?.add(FACING) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/NikoliteOreBlock.kt ================================================ package me.steven.indrev.blocks.misc import net.minecraft.block.OreBlock import net.minecraft.util.math.intprovider.UniformIntProvider class NikoliteOreBlock(settings: Settings) : OreBlock(settings, UniformIntProvider.create(1, 6)) ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/PlankBlock.kt ================================================ package me.steven.indrev.blocks.misc import net.minecraft.block.BlockState import net.minecraft.block.ShapeContext import net.minecraft.block.SnowBlock import net.minecraft.util.math.BlockPos import net.minecraft.util.shape.VoxelShape import net.minecraft.world.BlockView import net.minecraft.world.WorldView class PlankBlock(settings: Settings) : SnowBlock(settings) { override fun canPlaceAt(state: BlockState, world: WorldView?, pos: BlockPos): Boolean = true override fun getCollisionShape( state: BlockState, world: BlockView?, pos: BlockPos?, context: ShapeContext? ): VoxelShape = LAYERS_TO_SHAPE[state.get(LAYERS)] } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/SulfurCrystalBlock.kt ================================================ package me.steven.indrev.blocks.misc import me.steven.indrev.registry.IRItemRegistry import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.ShapeContext import net.minecraft.item.Item import net.minecraft.item.ItemPlacementContext import net.minecraft.state.StateManager import net.minecraft.state.property.DirectionProperty import net.minecraft.state.property.Properties import net.minecraft.util.function.BooleanBiFunction import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView import net.minecraft.world.World import net.minecraft.world.WorldView import java.util.stream.Stream class SulfurCrystalBlock(settings: Settings) : Block(settings) { init { defaultState = stateManager.defaultState.with(FACING, Direction.UP) } override fun getOutlineShape( state: BlockState, world: BlockView?, pos: BlockPos?, context: ShapeContext? ): VoxelShape = when (state[FACING]) { Direction.DOWN -> DOWN_SHAPE Direction.NORTH -> NORTH_SHAPE Direction.SOUTH -> SOUTH_SHAPE Direction.WEST -> WEST_SHAPE Direction.EAST -> EAST_SHAPE else -> UP_SHAPE } override fun neighborUpdate( state: BlockState, world: World?, pos: BlockPos, block: Block?, fromPos: BlockPos, notify: Boolean ) { val vec = pos.subtract(fromPos) val dir = Direction.fromVector(vec.x, vec.y, vec.z) if (state[FACING] == dir) { world?.breakBlock(pos, true) } } override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean { val direction = state.get(FACING) val blockPos = pos.offset(direction.opposite) val blockState = world.getBlockState(blockPos) return blockState.isSideSolidFullSquare(world, blockPos, direction) } override fun appendProperties(builder: StateManager.Builder?) { builder?.add(FACING) } override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { return defaultState.with(FACING, ctx?.playerLookDirection?.opposite) } override fun asItem(): Item { return IRItemRegistry.SULFUR_CRYSTAL_ITEM } companion object { val FACING: DirectionProperty = Properties.FACING private val UP_SHAPE = Stream.of( createCuboidShape(2.0, 5.0, 4.0, 3.0, 6.0, 5.0), createCuboidShape(2.0, 4.0, 4.0, 4.0, 5.0, 6.0), createCuboidShape(1.0, 0.0, 3.0, 4.0, 4.0, 6.0), createCuboidShape(3.0, 0.0, 2.0, 5.0, 1.0, 4.0), createCuboidShape(0.0, 0.0, 5.0, 2.0, 2.0, 7.0), createCuboidShape(10.0, 0.0, 1.0, 12.0, 1.0, 3.0), createCuboidShape(11.0, 0.0, 2.0, 14.0, 4.0, 5.0), createCuboidShape(11.0, 4.0, 2.0, 13.0, 5.0, 4.0), createCuboidShape(12.0, 5.0, 3.0, 13.0, 6.0, 4.0), createCuboidShape(13.0, 0.0, 4.0, 15.0, 2.0, 6.0), createCuboidShape(6.0, 0.0, 7.0, 8.0, 2.0, 10.0), createCuboidShape(7.0, 0.0, 8.0, 12.0, 5.0, 13.0), createCuboidShape(8.0, 5.0, 9.0, 11.0, 8.0, 12.0), createCuboidShape(9.0, 8.0, 10.0, 10.0, 10.0, 11.0), createCuboidShape(11.0, 0.0, 12.0, 14.0, 3.0, 15.0) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) private val DOWN_SHAPE = Stream.of( createCuboidShape(13.0, 11.0, 4.0, 14.0, 12.0, 5.0), createCuboidShape(12.0, 12.0, 4.0, 14.0, 13.0, 6.0), createCuboidShape(12.0, 13.0, 3.0, 15.0, 17.0, 6.0), createCuboidShape(11.0, 16.0, 2.0, 13.0, 17.0, 4.0), createCuboidShape(14.0, 15.0, 5.0, 16.0, 17.0, 7.0), createCuboidShape(4.0, 16.0, 1.0, 6.0, 17.0, 3.0), createCuboidShape(2.0, 13.0, 2.0, 5.0, 17.0, 5.0), createCuboidShape(3.0, 12.0, 2.0, 5.0, 13.0, 4.0), createCuboidShape(3.0, 11.0, 3.0, 4.0, 12.0, 4.0), createCuboidShape(1.0, 15.0, 4.0, 3.0, 17.0, 6.0), createCuboidShape(8.0, 15.0, 7.0, 10.0, 17.0, 10.0), createCuboidShape(4.0, 12.0, 8.0, 9.0, 17.0, 13.0), createCuboidShape(5.0, 9.0, 9.0, 8.0, 12.0, 12.0), createCuboidShape(6.0, 7.0, 10.0, 7.0, 9.0, 11.0), createCuboidShape(2.0, 14.0, 12.0, 5.0, 17.0, 15.0) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) private val EAST_SHAPE = Stream.of( createCuboidShape(5.0, 11.0, 13.0, 6.0, 12.0, 14.0), createCuboidShape(4.0, 10.0, 12.0, 5.0, 12.0, 14.0), createCuboidShape(0.0, 10.0, 12.0, 4.0, 13.0, 15.0), createCuboidShape(0.0, 12.0, 11.0, 1.0, 14.0, 13.0), createCuboidShape(0.0, 9.0, 14.0, 2.0, 11.0, 16.0), createCuboidShape(0.0, 13.0, 4.0, 1.0, 15.0, 6.0), createCuboidShape(0.0, 11.0, 2.0, 4.0, 14.0, 5.0), createCuboidShape(4.0, 12.0, 3.0, 5.0, 14.0, 5.0), createCuboidShape(5.0, 12.0, 3.0, 6.0, 13.0, 4.0), createCuboidShape(0.0, 10.0, 1.0, 2.0, 12.0, 3.0), createCuboidShape(0.0, 6.0, 8.0, 2.0, 9.0, 10.0), createCuboidShape(0.0, 3.0, 4.0, 5.0, 8.0, 9.0), createCuboidShape(5.0, 4.0, 5.0, 8.0, 7.0, 8.0), createCuboidShape(8.0, 5.0, 6.0, 10.0, 6.0, 7.0), createCuboidShape(0.0, 1.0, 2.0, 3.0, 4.0, 5.0) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) private val SOUTH_SHAPE = Stream.of( createCuboidShape(1.0, 4.0, 0.0, 3.0, 6.0, 2.0), createCuboidShape(3.0, 3.0, 5.0, 4.0, 4.0, 6.0), createCuboidShape(3.0, 2.0, 4.0, 5.0, 4.0, 5.0), createCuboidShape(2.0, 2.0, 0.0, 5.0, 5.0, 4.0), createCuboidShape(4.0, 1.0, 0.0, 6.0, 3.0, 1.0), createCuboidShape(11.0, 2.0, 0.0, 13.0, 4.0, 1.0), createCuboidShape(12.0, 3.0, 0.0, 15.0, 6.0, 4.0), createCuboidShape(12.0, 4.0, 4.0, 14.0, 6.0, 5.0), createCuboidShape(13.0, 4.0, 5.0, 14.0, 5.0, 6.0), createCuboidShape(14.0, 5.0, 0.0, 16.0, 7.0, 2.0), createCuboidShape(8.0, 7.0, 0.0, 10.0, 10.0, 2.0), createCuboidShape(4.0, 8.0, 0.0, 9.0, 13.0, 5.0), createCuboidShape(5.0, 9.0, 5.0, 8.0, 12.0, 8.0), createCuboidShape(6.0, 10.0, 8.0, 7.0, 11.0, 10.0), createCuboidShape(2.0, 12.0, 0.0, 5.0, 15.0, 3.0) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) private val NORTH_SHAPE = Stream.of( createCuboidShape(13.0, 4.0, 14.0, 15.0, 6.0, 16.0), createCuboidShape(12.0, 3.0, 10.0, 13.0, 4.0, 11.0), createCuboidShape(11.0, 2.0, 11.0, 13.0, 4.0, 12.0), createCuboidShape(11.0, 2.0, 12.0, 14.0, 5.0, 16.0), createCuboidShape(10.0, 1.0, 15.0, 12.0, 3.0, 16.0), createCuboidShape(3.0, 2.0, 15.0, 5.0, 4.0, 16.0), createCuboidShape(1.0, 3.0, 12.0, 4.0, 6.0, 16.0), createCuboidShape(2.0, 4.0, 11.0, 4.0, 6.0, 12.0), createCuboidShape(2.0, 4.0, 10.0, 3.0, 5.0, 11.0), createCuboidShape(0.0, 5.0, 14.0, 2.0, 7.0, 16.0), createCuboidShape(6.0, 7.0, 14.0, 8.0, 10.0, 16.0), createCuboidShape(7.0, 8.0, 11.0, 12.0, 13.0, 16.0), createCuboidShape(8.0, 9.0, 8.0, 11.0, 12.0, 11.0), createCuboidShape(9.0, 10.0, 6.0, 10.0, 11.0, 8.0), createCuboidShape(11.0, 12.0, 13.0, 14.0, 15.0, 16.0) ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) private val WEST_SHAPE = Stream.of( createCuboidShape(10.0, 11.0, 2.0, 11.0, 12.0, 3.0), createCuboidShape(11.0, 10.0, 2.0, 12.0, 12.0, 4.0), createCuboidShape(12.0, 10.0, 1.0, 16.0, 13.0, 4.0), createCuboidShape(15.0, 12.0, 3.0, 16.0, 14.0, 5.0), createCuboidShape(14.0, 9.0, 0.0, 16.0, 11.0, 2.0), createCuboidShape(15.0, 13.0, 10.0, 16.0, 15.0, 12.0), createCuboidShape(12.0, 11.0, 11.0, 16.0, 14.0, 14.0), createCuboidShape(11.0, 12.0, 11.0, 12.0, 14.0, 13.0), createCuboidShape(10.0, 12.0, 12.0, 11.0, 13.0, 13.0), createCuboidShape(14.0, 10.0, 13.0, 16.0, 12.0, 15.0), createCuboidShape(14.0, 6.0, 6.0, 16.0, 9.0, 8.0), createCuboidShape(11.0, 3.0, 7.0, 16.0, 8.0, 12.0), createCuboidShape(8.0, 4.0, 8.0, 11.0, 7.0, 11.0), createCuboidShape(6.0, 5.0, 9.0, 8.0, 6.0, 10.0), createCuboidShape(13.0, 1.0, 11.0, 16.0, 4.0, 14.0) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/TankBlock.kt ================================================ package me.steven.indrev.blocks.misc import alexiil.mc.lib.attributes.fluid.volume.FluidVolume import me.steven.indrev.blockentities.Syncable import me.steven.indrev.blockentities.storage.TankBlockEntity import me.steven.indrev.components.FluidComponent import me.steven.indrev.registry.IRBlockRegistry import me.steven.indrev.utils.bucket import me.steven.indrev.utils.fluidStorageOf import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil import net.minecraft.block.Block import net.minecraft.block.BlockEntityProvider import net.minecraft.block.BlockState import net.minecraft.block.ShapeContext import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType import net.minecraft.client.item.TooltipContext import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.stat.Stats import net.minecraft.state.StateManager import net.minecraft.state.property.BooleanProperty import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.util.ActionResult import net.minecraft.util.Hand import net.minecraft.util.function.BooleanBiFunction import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView import net.minecraft.world.World import net.minecraft.world.WorldAccess import net.minecraft.world.chunk.Chunk import java.util.stream.Stream class TankBlock(settings: Settings) : Block(settings), BlockEntityProvider { init { this.defaultState = stateManager.defaultState.with(UP, false).with(DOWN, false) } override fun appendProperties(builder: StateManager.Builder?) { builder?.add(UP, DOWN) } override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = TankBlockEntity(pos, state) override fun getTicker( world: World, state: BlockState?, type: BlockEntityType? ): BlockEntityTicker? { return if (world.isClient) null else BlockEntityTicker { world, pos, state, blockEntity -> TankBlockEntity.tick(world, pos, state, blockEntity as TankBlockEntity) } } override fun getOutlineShape( state: BlockState, world: BlockView?, pos: BlockPos?, context: ShapeContext? ): VoxelShape { val isDown = state[DOWN] val isUp = state[UP] return when { !isUp && !isDown -> SINGLE_TANK_SHAPE isUp && !isDown -> TANK_UP_SHAPE !isUp && isDown -> TANK_DOWN_SHAPE else -> TANK_BOTH_SHAPE } } override fun onUse( state: BlockState?, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult ): ActionResult { if (world is ServerWorld) { val storage = fluidStorageOf(world, pos, hit.side) val inHand = ContainerItemContext.ofPlayerHand(player, hand).find(FluidStorage.ITEM) var res = StorageUtil.move(storage, inHand, { true }, Long.MAX_VALUE, null) if (res == 0L) res = StorageUtil.move(inHand, storage, { true }, Long.MAX_VALUE, null) return if (res > 0) ActionResult.SUCCESS else ActionResult.PASS } return ActionResult.CONSUME } override fun afterBreak( world: World?, player: PlayerEntity?, pos: BlockPos?, state: BlockState?, blockEntity: BlockEntity?, toolStack: ItemStack? ) { if (world?.isClient == true) return player?.incrementStat(Stats.MINED.getOrCreateStat(this)) player?.addExhaustion(0.005f) writeNbtComponents(world, player, pos, state, blockEntity, toolStack) } override fun onStateReplaced( state: BlockState?, world: World, pos: BlockPos, newState: BlockState?, moved: Boolean ) { super.onStateReplaced(state, world, pos, newState, moved) } fun writeNbtComponents( world: World?, player: PlayerEntity?, pos: BlockPos?, state: BlockState?, blockEntity: BlockEntity?, toolStack: ItemStack? ) { if (world is ServerWorld) { getDroppedStacks(state, world, pos, blockEntity, player, toolStack).forEach { stack -> if (blockEntity is TankBlockEntity) { val tag = stack.orCreateNbt blockEntity.fluidComponent.toTag(tag) } dropStack(world, pos, stack) } state!!.onStacksDropped(world, pos, toolStack) } } override fun getStateForNeighborUpdate( state: BlockState, direction: Direction?, newState: BlockState, world: WorldAccess, pos: BlockPos, posFrom: BlockPos ): BlockState { if (world.isClient) return state val connects = isConnectable(world as World, pos, posFrom) return when (direction) { Direction.UP -> state.with(UP, newState.isOf(this) && newState[DOWN] && connects) Direction.DOWN -> state.with(DOWN, newState.isOf(this) && newState[UP] && connects) else -> state } } override fun onPlaced( world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack? ) { val tag = itemStack?.nbt if (world.isClient) return val tankEntity = world.getBlockEntity(pos) as? TankBlockEntity ?: return if (tag?.isEmpty == false) { tankEntity.fluidComponent.fromTag(tag) } } override fun appendTooltip( stack: ItemStack?, world: BlockView?, tooltip: MutableList?, options: TooltipContext? ) { val tag = stack?.nbt val tanksTag = tag?.getCompound("tanks") ?: return val volume = tanksTag.keys?.map { key -> val tankTag = tanksTag.getCompound(key) FluidVolume.fromTag(tankTag.getCompound("fluids")) }?.firstOrNull() ?: return val fluid = volume.amount().asInt(1000) tooltip?.addAll(volume.fluidKey.fullTooltip.toTypedArray()) tooltip?.add(LiteralText("$fluid / 8000 mB")) } override fun getPlacementState(ctx: ItemPlacementContext): BlockState? { val blockPos = ctx.blockPos val world = ctx.world val fluidComponent = FluidComponent({ object : Syncable{ override fun markForUpdate(condition: () -> Boolean) { TODO("Not yet implemented") } } }, bucket, 1) if (ctx.stack.nbt != null && !ctx.stack.nbt!!.isEmpty) fluidComponent.fromTag(ctx.stack.nbt) val connectsUp = isConnectable(world, fluidComponent, blockPos.up()) val connectsDown = isConnectable(world, fluidComponent, blockPos.down()) return defaultState .with(UP, connectsUp && (!connectsDown || isConnectable(world, blockPos.down(), blockPos.up()))) .with(DOWN, connectsDown && (!connectsUp || isConnectable(world, blockPos.up(), blockPos.down()))) } private fun isConnectable(world: World, pos: BlockPos, other: BlockPos): Boolean { if (world.isClient) return false val firstInv = fluidStorageOf(world, pos, Direction.UP) as? TankBlockEntity.CombinedTankStorage ?: return false val secondInv = fluidStorageOf(world, pos, Direction.DOWN) as? TankBlockEntity.CombinedTankStorage ?: return false return if (firstInv.initialFluid.isBlank || secondInv.initialFluid.isBlank) true else firstInv.initialFluid == secondInv.initialFluid } private fun isConnectable(world: World, firstInv: FluidComponent, other: BlockPos): Boolean { if (world.isClient) return false val secondInv = fluidStorageOf(world, other, Direction.UP) as? TankBlockEntity.CombinedTankStorage ?: return false return if (firstInv[0].isEmpty || secondInv.initialFluid.isBlank) true else firstInv[0].variant == secondInv.initialFluid } override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack { val stack = super.getPickStack(world, pos, state) val blockEntity = world?.getBlockEntity(pos) as? TankBlockEntity ?: return stack blockEntity.fluidComponent.toTag(stack.orCreateNbt) return stack } companion object { val UP: BooleanProperty = BooleanProperty.of("up") val DOWN: BooleanProperty = BooleanProperty.of("down") val SINGLE_TANK_SHAPE = Stream.of( createCuboidShape(1.0, 14.0, 1.0, 15.0, 15.0, 15.0), createCuboidShape(1.0, 0.0, 1.0, 15.0, 1.0, 15.0), createCuboidShape(14.0, 1.0, 14.0, 15.0, 14.0, 15.0), createCuboidShape(1.0, 1.0, 14.0, 2.0, 14.0, 15.0), createCuboidShape(1.0, 1.0, 1.0, 2.0, 14.0, 2.0), createCuboidShape(14.0, 1.0, 1.0, 15.0, 14.0, 2.0), createCuboidShape(14.5, 1.0, 1.5, 14.7, 14.0, 14.5), createCuboidShape(1.5, 1.0, 1.5, 1.7, 14.0, 14.5), createCuboidShape(1.5, 1.0, 1.5, 14.5, 14.0, 1.7), createCuboidShape(1.5, 1.0, 14.5, 14.5, 14.0, 14.7) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.get() val TANK_DOWN_SHAPE = Stream.of( createCuboidShape(1.0, 14.0, 1.0, 15.0, 15.0, 15.0), createCuboidShape(14.0, 0.0, 14.0, 15.0, 15.0, 15.0), createCuboidShape(1.0, 0.0, 14.0, 2.0, 15.0, 15.0), createCuboidShape(1.0, 0.0, 1.0, 2.0, 15.0, 2.0), createCuboidShape(14.0, 0.0, 1.0, 15.0, 15.0, 2.0), createCuboidShape(14.5, 0.0, 1.5, 14.7, 15.0, 14.5), createCuboidShape(1.5, 0.0, 1.5, 1.7, 15.0, 14.5), createCuboidShape(1.5, 0.0, 1.5, 14.5, 15.0, 1.7), createCuboidShape(1.5, 0.0, 14.5, 14.5, 15.0, 14.7) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.get() val TANK_UP_SHAPE = Stream.of( createCuboidShape(1.0, 0.0, 1.0, 15.0, 1.0, 15.0), createCuboidShape(14.0, 1.0, 14.0, 15.0, 16.0, 15.0), createCuboidShape(1.0, 1.0, 14.0, 2.0, 16.0, 15.0), createCuboidShape(1.0, 1.0, 1.0, 2.0, 16.0, 2.0), createCuboidShape(1.04, 1.0, 1.0, 15.0, 16.0, 2.0), createCuboidShape(14.5, 1.0, 1.5, 14.7, 16.0, 14.5), createCuboidShape(1.5, 1.0, 1.5, 1.7, 16.0, 14.5), createCuboidShape(1.5, 1.0, 1.5, 14.5, 16.0, 1.7), createCuboidShape(1.5, 1.0, 14.5, 14.5, 16.0, 14.7) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.get() val TANK_BOTH_SHAPE = Stream.of( createCuboidShape(14.0, 0.0, 14.0, 15.0, 16.0, 15.0), createCuboidShape(1.0, 0.0, 14.0, 2.0, 16.0, 15.0), createCuboidShape(1.0, 0.0, 1.0, 2.0, 16.0, 2.0), createCuboidShape(14.0, 0.0, 1.0, 15.0, 16.0, 2.0), createCuboidShape(14.5, 0.0, 1.5, 14.7, 16.0, 14.5), createCuboidShape(1.5, 0.0, 1.5, 1.7, 16.0, 14.5), createCuboidShape(1.5, 0.0, 1.5, 14.5, 16.0, 1.7), createCuboidShape(1.5, 0.0, 14.5, 14.5, 16.0, 14.7) ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.get() fun findAllTanks(chunk: Chunk, blockState: BlockState, pos: BlockPos, scanned: MutableSet, to: TankBlockEntity.CombinedTankStorage) { if (blockState.isOf(IRBlockRegistry.TANK_BLOCK) && scanned.add(pos)) { to.add((chunk.getBlockEntity(pos) as TankBlockEntity)) if (blockState[UP]) findAllTanks(chunk, chunk.getBlockState(pos.up()), pos.up(), scanned, to) if (blockState[DOWN]) findAllTanks(chunk, chunk.getBlockState(pos.down()), pos.down(), scanned, to) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/VerticalFacingBlock.kt ================================================ package me.steven.indrev.blocks.misc import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.item.ItemPlacementContext import net.minecraft.state.StateManager import net.minecraft.state.property.EnumProperty import net.minecraft.util.math.Direction open class VerticalFacingBlock(settings: Settings) : Block(settings) { init { this.defaultState = stateManager.defaultState.with(FACING, Direction.UP) } override fun appendProperties(builder: StateManager.Builder?) { builder?.add(FACING) } override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? { return defaultState.with(FACING, Direction.getEntityFacingOrder(ctx?.player).firstOrNull { it.axis.isVertical }?.opposite ?: Direction.UP) } companion object { val FACING = EnumProperty.of("facing", Direction::class.java, Direction.UP, Direction.DOWN) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/misc/WarningStrobeBlock.kt ================================================ package me.steven.indrev.blocks.misc import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.block.ShapeContext import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.world.BlockView import net.minecraft.world.WorldAccess import net.minecraft.world.WorldView class WarningStrobeBlock(settings: Settings) : Block(settings) { override fun getOutlineShape( state: BlockState?, world: BlockView?, pos: BlockPos?, context: ShapeContext? ): VoxelShape = SHAPE override fun canPlaceAt(state: BlockState?, world: WorldView, pos: BlockPos): Boolean { val blockBelow = world.getBlockState(pos.down()) return isFaceFullSquare(blockBelow.getCollisionShape(world, pos.down()), Direction.UP) } override fun getStateForNeighborUpdate( state: BlockState, direction: Direction?, newState: BlockState?, world: WorldAccess?, pos: BlockPos?, posFrom: BlockPos? ): BlockState? { return if (!state.canPlaceAt(world, pos)) Blocks.AIR.defaultState else state } companion object { private val SHAPE = createCuboidShape(5.0, 0.0, 5.0, 11.0, 6.0, 11.0) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/models/LazuliFluxContainerBakedModel.kt ================================================ package me.steven.indrev.blocks.models import me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity import me.steven.indrev.utils.blockSpriteId import net.fabricmc.fabric.api.renderer.v1.Renderer import net.fabricmc.fabric.api.renderer.v1.RendererAccess import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.minecraft.block.BlockState import net.minecraft.client.texture.Sprite import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockRenderView import java.util.* import java.util.function.Supplier class LazuliFluxContainerBakedModel(id: String) : MachineBakedModel("lazuli_flux_container") { init { overlayIds.addAll( arrayOf( blockSpriteId("block/lazuli_flux_container"), blockSpriteId("block/lazuli_flux_container_input"), blockSpriteId("block/lazuli_flux_container_output"), blockSpriteId("block/lazuli_flux_container_item_lf_level"), blockSpriteId("block/${id}_overlay") ) ) } private val color: Int = when (id.last()) { '1' -> 0xffbb19 '2' -> 0x5d3dff '3' -> 0xfd47ff else -> 0xff4070 } override fun buildDefaultMesh() { val renderer: Renderer = RendererAccess.INSTANCE.renderer!! val builder: MeshBuilder = renderer.meshBuilder() val emitter = builder.emitter for (direction in Direction.values()) { emitter.draw(direction, baseSprite!!, -1) emitter.draw(direction, overlays[4]!!, -1) } defaultMesh = builder.build() } override fun emitBlockQuads( blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier, ctx: RenderContext ) { super.emitBlockQuads(blockView, state, pos, randomSupplier, ctx) val blockEntity = blockView.getBlockEntity(pos) as? LazuliFluxContainerBlockEntity ?: return val emitter = ctx.emitter blockEntity.transferConfig.forEach { side, mode -> if (mode.input) { emitter.draw(side, overlays[1]!!) } else if (mode.output) { emitter.draw(side, overlays[2]!!) } } } private fun emitHorizontalQuads(sprite: Sprite, ctx: RenderContext) { ctx.emitter.run { draw(Direction.NORTH, sprite, 255 shl 24 or color) draw(Direction.SOUTH, sprite, 255 shl 24 or color) draw(Direction.EAST, sprite, 255 shl 24 or color) draw(Direction.WEST, sprite, 255 shl 24 or color) } } override fun emitItemQuads(stack: ItemStack?, randomSupplier: Supplier?, ctx: RenderContext) { super.emitItemQuads(stack, randomSupplier, ctx) emitHorizontalQuads(overlays[3]!!, ctx) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/models/MachineBakedModel.kt ================================================ package me.steven.indrev.blocks.models import com.mojang.datafixers.util.Pair import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.utils.blockSpriteId import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.renderer.v1.Renderer import net.fabricmc.fabric.api.renderer.v1.RendererAccess import net.fabricmc.fabric.api.renderer.v1.material.BlendMode import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.minecraft.block.BlockState import net.minecraft.client.MinecraftClient import net.minecraft.client.render.model.* import net.minecraft.client.render.model.json.ModelOverrideList import net.minecraft.client.render.model.json.ModelTransformation import net.minecraft.client.texture.MissingSprite import net.minecraft.client.texture.Sprite import net.minecraft.client.util.ModelIdentifier import net.minecraft.client.util.SpriteIdentifier import net.minecraft.item.ItemStack import net.minecraft.screen.PlayerScreenHandler import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3f import net.minecraft.world.BlockRenderView import java.util.* import java.util.function.Function import java.util.function.Supplier open class MachineBakedModel(val id: String) : UnbakedModel, BakedModel, FabricBakedModel { var baseSpriteId = SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier("block/${id.replace(Regex("_mk[0-4]"), "")}")) var overlayIds: MutableList = mutableListOf() var workingOverlayIds: MutableList = mutableListOf() var baseSprite: Sprite? = null val overlays: Array by lazy { arrayOfNulls(overlayIds.size) } val workingOverlays: Array by lazy { arrayOfNulls(workingOverlayIds.size) } val emissives = hashSetOf() var defaultMesh: Mesh? = null var workingStateMesh: Mesh? = null fun factoryOverlay() { overlayIds.add(blockSpriteId("block/factory_overlay")) } fun tierOverlay(tier: Tier) { overlayIds.add(blockSpriteId("block/${tier.toString().lowercase(Locale.getDefault())}_overlay")) } override fun bake( loader: ModelLoader, textureGetter: Function, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel { baseSprite = textureGetter.apply(baseSpriteId) overlayIds.processSprites(overlays, textureGetter) workingOverlayIds.processSprites(workingOverlays, textureGetter) if (isEmissive(baseSprite)) emissives.add(baseSprite!!) buildDefaultMesh() buildWorkingStateMesh() return this } open fun buildDefaultMesh() { val renderer: Renderer = RendererAccess.INSTANCE.renderer!! val builder: MeshBuilder = renderer.meshBuilder() val emitter = builder.emitter for (direction in Direction.values()) { emitter.draw(direction, baseSprite!!, -1) overlays.forEach { overlay -> emitter.draw(direction, overlay!!, -1) } } defaultMesh = builder.build() } fun buildWorkingStateMesh() { val renderer: Renderer = RendererAccess.INSTANCE.renderer!! val builder: MeshBuilder = renderer.meshBuilder() val emitter = builder.emitter for (direction in Direction.values()) { emitter.draw(direction, baseSprite!!, -1) workingOverlays.forEach { overlay -> emitter.draw(direction, overlay!!, -1) } overlays.forEach { overlay -> emitter.draw(direction, overlay!!, -1) } } workingStateMesh = builder.build() } private fun List.processSprites(arr: Array, textureGetter: Function) { forEachIndexed { index, id -> val sprite = textureGetter.apply(id) if (sprite.id != MissingSprite.getMissingSpriteId()) { arr[index] = sprite if (isEmissive(sprite)) emissives.add(sprite) } } } //don't judge me fun isEmissive(sprite: Sprite?) = sprite?.id?.toString()?.contains("emissive") == true override fun getModelDependencies(): MutableCollection = mutableListOf() override fun getTextureDependencies( unbakedModelGetter: Function?, unresolvedTextureReferences: MutableSet>? ): MutableCollection { val list = mutableListOf(baseSpriteId) list.addAll(overlayIds) list.addAll(workingOverlayIds) return list } override fun getQuads(state: BlockState?, face: Direction?, random: Random?): MutableList = mutableListOf() override fun useAmbientOcclusion(): Boolean = true override fun hasDepth(): Boolean = false override fun isSideLit(): Boolean = true override fun isBuiltin(): Boolean = false override fun getParticleSprite(): Sprite? = baseSprite override fun getTransformation(): ModelTransformation = TRANSFORM override fun getOverrides(): ModelOverrideList = ModelOverrideList.EMPTY override fun isVanillaAdapter(): Boolean = false override fun emitBlockQuads( blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier, ctx: RenderContext ) { val block = state.block as? MachineBlock ?: return val direction = block.getFacing(state) val blockEntity = blockView.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return ctx.pushTransform(rotateQuads(direction)) val m = if (blockEntity.workingState) workingStateMesh else defaultMesh ctx.meshConsumer().accept(m) ctx.popTransform() } override fun emitItemQuads(stack: ItemStack?, randomSupplier: Supplier?, ctx: RenderContext) { ctx.meshConsumer().accept(defaultMesh) } protected fun QuadEmitter.draw( side: Direction, sprite: Sprite, color: Int = -1 ) { square(side, 0f, 0f, 1f, 1f, 0f) spriteBake(0, sprite, MutableQuadView.BAKE_LOCK_UV) spriteColor(0, color, color, color, color) if (emissives.contains(sprite)) material(MATERIAL) val uv = if (sprite.width == 16 && sprite.height == 16) MachineTextureUV.FULL else MachineTextureUV.BY_DIRECTION[side]!! sprite(0, 0, sprite.getFrameU(uv.u1.toDouble()), sprite.getFrameV(uv.v1.toDouble())) sprite(1, 0, sprite.getFrameU(uv.u1.toDouble()), sprite.getFrameV(uv.v2.toDouble())) sprite(2, 0, sprite.getFrameU(uv.u2.toDouble()), sprite.getFrameV(uv.v2.toDouble())) sprite(3, 0, sprite.getFrameU(uv.u2.toDouble()), sprite.getFrameV(uv.v1.toDouble())) emit() } /** * Original code belongs to Haven-King. * Source: https://github.com/Haven-King/Automotion */ fun rotateQuads(direction: Direction): RenderContext.QuadTransform = RenderContext.QuadTransform { q -> val rotate = Vec3f.POSITIVE_Y.getDegreesQuaternion( when (direction) { Direction.NORTH -> 0f Direction.EAST -> 270f Direction.SOUTH -> 180f Direction.WEST -> 90f else -> 0f } ) val tmp = Vec3f() for (i in 0..3) { q.copyPos(i, tmp) tmp.add(-0.5f, -0.5f, -0.5f) tmp.rotate(rotate) tmp.add(0.5f, 0.5f, 0.5f) q.pos(i, tmp) if (q.hasNormal(i)) { q.copyNormal(i, tmp) tmp.rotate(rotate) q.normal(i, tmp) } } q.cullFace(null) q.nominalFace(direction) true } enum class MachineTextureUV(val direction: Direction?, val u1: Float, val v1: Float, val u2: Float, val v2: Float) { FRONT(Direction.NORTH, 16f / 48f * 16f, 16f / 48f * 16f, 32f / 48f * 16f, 32f / 48f * 16f), LEFT(Direction.EAST, 0.0f, 16f / 48f * 16f, 16f / 48f * 16f, 32f / 48f * 16f), BACK(Direction.SOUTH, 32f / 48f * 16f, 32f / 48f * 16f, 16.0f, 16f), RIGHT(Direction.WEST, 32f / 48f * 16f, 16f / 48f * 16f, 16.0f, 32f / 48f * 16f), TOP(Direction.UP, 16f / 48f * 16f, 0.0f, 32f / 48f * 16f, 16f / 48f * 16f), BOTTOM(Direction.DOWN, 16f / 48f * 16f, 32f / 48f * 16f, 32f / 48f * 16f, 16.0f), FULL(null, 0f, 0f, 16f, 16f); companion object { val BY_DIRECTION = values().associateBy { it.direction } } } companion object { val MATERIAL by lazy { RendererAccess.INSTANCE.renderer?.materialFinder()!!.clear() .spriteDepth(1) .blendMode(0, BlendMode.CUTOUT) .disableAo(0, true) .disableDiffuse(0, true) .emissive(0, true) ?.find() } val TRANSFORM: ModelTransformation by lazy { MinecraftClient.getInstance().bakedModelManager.getModel( ModelIdentifier(Identifier("stone"), "") ).transformation } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/models/MinerBakedModel.kt ================================================ package me.steven.indrev.blocks.models import com.mojang.datafixers.util.Pair import me.steven.indrev.blockentities.miningrig.MiningRigBlockEntity import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.utils.blockSpriteId import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.minecraft.block.BlockState import net.minecraft.client.render.model.BakedModel import net.minecraft.client.render.model.ModelBakeSettings import net.minecraft.client.render.model.ModelLoader import net.minecraft.client.render.model.UnbakedModel import net.minecraft.client.texture.Sprite import net.minecraft.client.util.SpriteIdentifier import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.world.BlockRenderView import java.util.* import java.util.function.Function import java.util.function.Supplier class MinerBakedModel(id: String) : MachineBakedModel(id) { private val screenSpriteId = blockSpriteId("block/mining_rig_screen_emissive") var screenSprite: Sprite? = null override fun bake( loader: ModelLoader, textureGetter: Function, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel { screenSprite = textureGetter.apply(screenSpriteId) emissives.add(screenSprite!!) return super.bake(loader, textureGetter, rotationContainer, modelId) } override fun getTextureDependencies( unbakedModelGetter: Function?, unresolvedTextureReferences: MutableSet>? ): MutableCollection { val deps = super.getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences) deps.add(screenSpriteId) return deps } override fun emitBlockQuads( blockView: BlockRenderView, state: BlockState, pos: BlockPos, randomSupplier: Supplier, ctx: RenderContext ) { super.emitBlockQuads(blockView, state, pos, randomSupplier, ctx) val block = state.block as? MachineBlock ?: return val direction = block.getFacing(state) val blockEntity = blockView.getBlockEntity(pos) as? MiningRigBlockEntity ?: return if (blockEntity.workingState) ctx.emitter.draw(direction, screenSprite!!) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/models/PumpPipeBakedModel.kt ================================================ package me.steven.indrev.blocks.models import com.mojang.datafixers.util.Pair import me.steven.indrev.utils.identifier import net.minecraft.client.render.model.BakedModel import net.minecraft.client.render.model.ModelBakeSettings import net.minecraft.client.render.model.ModelLoader import net.minecraft.client.render.model.UnbakedModel import net.minecraft.client.texture.Sprite import net.minecraft.client.util.SpriteIdentifier import net.minecraft.screen.PlayerScreenHandler import net.minecraft.util.Identifier import java.util.function.Function class PumpPipeBakedModel : UnbakedModel { private val spriteIdCollection = mutableListOf( SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier("block/pump_pipe")) ) private val modelIdentifier = identifier("block/pump_pipe") private var bakedModel: BakedModel? = null override fun bake( loader: ModelLoader, textureGetter: Function, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel? { bakedModel = loader.getOrLoadModel(modelIdentifier).bake(loader, textureGetter, rotationContainer, modelId) return bakedModel } override fun getModelDependencies(): MutableCollection = mutableListOf() override fun getTextureDependencies( unbakedModelGetter: Function?, unresolvedTextureReferences: MutableSet>? ): MutableCollection { return spriteIdCollection } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/models/pipes/BasePipeModel.kt ================================================ package me.steven.indrev.blocks.models.pipes import com.mojang.datafixers.util.Pair import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry import net.fabricmc.fabric.api.renderer.v1.Renderer import net.fabricmc.fabric.api.renderer.v1.RendererAccess import net.fabricmc.fabric.api.renderer.v1.material.BlendMode import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView import net.minecraft.block.BlockState import net.minecraft.client.MinecraftClient import net.minecraft.client.render.RenderLayers import net.minecraft.client.render.model.* import net.minecraft.client.render.model.json.ModelOverrideList import net.minecraft.client.render.model.json.ModelTransformation import net.minecraft.client.texture.Sprite import net.minecraft.client.util.SpriteIdentifier import net.minecraft.item.ItemStack import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockRenderView import java.util.* import java.util.function.Function import java.util.function.Supplier abstract class BasePipeModel(val tier: Tier, val type: String) : BakedModel, FabricBakedModel, UnbakedModel { abstract val spriteIdCollection: MutableList private val modelIdCollection = mutableListOf( identifier("block/${type}_center_${tier.toString().lowercase()}"), identifier("block/${type}_side_${tier.toString().lowercase()}") ) val modelArray = arrayOfNulls(7) val spriteArray = arrayOfNulls(4) protected val meshArray = arrayOfNulls(7) lateinit var transform: ModelTransformation override fun bake( loader: ModelLoader, textureGetter: Function, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel { spriteIdCollection.forEachIndexed { idx, spriteIdentifier -> spriteArray[idx] = textureGetter.apply(spriteIdentifier) } val center = loader.getOrLoadModel(modelIdCollection[0]).bake(loader, textureGetter, rotationContainer, modelId)!! meshArray[0] = buildDefaultMesh(0, center) transform = center.transformation val sideModel = loader.getOrLoadModel(modelIdCollection[1]) meshArray[1] = buildDefaultMesh(1, sideModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId)!!) // NORTH meshArray[2] = buildDefaultMesh(2, sideModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId)!!) // EAST meshArray[3] = buildDefaultMesh(3, sideModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId)!!)// SOUTH meshArray[4] = buildDefaultMesh(4, sideModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId)!!)// WEST meshArray[5] = buildDefaultMesh(5, sideModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId)!!) // UP meshArray[6] = buildDefaultMesh(6, sideModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId)!!) // DOWN return this } open fun buildDefaultMesh(index: Int, model: BakedModel): Mesh { modelArray[index] = model val renderer: Renderer = RendererAccess.INSTANCE.renderer!! val builder: MeshBuilder = renderer.meshBuilder() val emitter = builder.emitter model.getQuads(null, null, null).forEach { q -> emitter.fromVanilla(q, null, null) emitter.emit() } return builder.build() } /** * Used for DashLoader compat */ fun buildMeshes() { modelArray.forEachIndexed { index, model -> meshArray[index] = buildDefaultMesh(index, model!!) } } override fun getModelDependencies(): MutableCollection = modelIdCollection override fun getTextureDependencies( unbakedModelGetter: Function?, unresolvedTextureReferences: MutableSet>? ): MutableCollection = spriteIdCollection override fun getQuads(state: BlockState?, face: Direction?, random: Random?): MutableList = mutableListOf() override fun useAmbientOcclusion(): Boolean = true override fun hasDepth(): Boolean = false override fun isSideLit(): Boolean = true override fun isBuiltin(): Boolean = false override fun getParticleSprite(): Sprite = spriteArray[0]!! override fun getTransformation(): ModelTransformation = transform override fun getOverrides(): ModelOverrideList = ModelOverrideList.EMPTY override fun isVanillaAdapter(): Boolean = false override fun emitBlockQuads( world: BlockRenderView, state: BlockState, pos: BlockPos, randSupplier: Supplier, context: RenderContext ) { val renderData = (world as RenderAttachedBlockView).getBlockEntityRenderAttachment(pos) as? BasePipeBlockEntity.PipeRenderData ?: return if (renderData.cover != null) { val coverState = renderData.cover val model = MinecraftClient.getInstance().bakedModelManager.blockModels.getModel(coverState) val color = 255 shl 24 or (ColorProviderRegistry.BLOCK[coverState.block]?.getColor(coverState, world, pos, 0) ?: -1) val emitter = context.emitter val renderLayer = BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(coverState)) val material = RENDER_LAYER_MATERIAL_MAP.computeIfAbsent(renderLayer) { RendererAccess.INSTANCE.renderer!!.materialFinder().blendMode(0, renderLayer).find() } DIRECTIONS.forEach { dir -> model.getQuads(coverState, dir, randSupplier.get()).forEach { quad -> emitter.fromVanilla(quad, material, dir) if (quad.hasColor()) { emitter.spriteColor(0, color, color, color, color) } emitter.emit() } } if (coverState.isOpaque) return } context.meshConsumer().accept(meshArray[0]) if (renderData.connections.contains(Direction.NORTH)) context.meshConsumer().accept(meshArray[1]) if (renderData.connections.contains(Direction.EAST)) context.meshConsumer().accept(meshArray[2]) if (renderData.connections.contains(Direction.SOUTH)) context.meshConsumer().accept(meshArray[3]) if (renderData.connections.contains(Direction.WEST)) context.meshConsumer().accept(meshArray[4]) if (renderData.connections.contains(Direction.UP)) context.meshConsumer().accept(meshArray[5]) if (renderData.connections.contains(Direction.DOWN)) context.meshConsumer().accept(meshArray[6]) } override fun emitItemQuads(stack: ItemStack?, p1: Supplier, context: RenderContext) { context.meshConsumer().accept(meshArray[0]) } companion object { val RENDER_LAYER_MATERIAL_MAP = hashMapOf() val DIRECTIONS = Direction.values() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/models/pipes/CableModel.kt ================================================ package me.steven.indrev.blocks.models.pipes import me.steven.indrev.api.machines.Tier import me.steven.indrev.utils.blockSpriteId import net.fabricmc.fabric.api.renderer.v1.Renderer import net.fabricmc.fabric.api.renderer.v1.RendererAccess import net.fabricmc.fabric.api.renderer.v1.material.BlendMode import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView import net.minecraft.client.render.model.BakedModel class CableModel(tier: Tier) : BasePipeModel(tier, "cable") { override val spriteIdCollection = mutableListOf( blockSpriteId("block/cable_center"), blockSpriteId("block/cable_center_emissive_${tier.toString().lowercase()}"), blockSpriteId("block/cable_wrap"), blockSpriteId("block/cable_wire_emissive_${tier.toString().lowercase()}") ) override fun buildDefaultMesh(index: Int, model: BakedModel): Mesh { val renderer: Renderer = RendererAccess.INSTANCE.renderer!! val builder: MeshBuilder = renderer.meshBuilder() val emitter = builder.emitter if (index == 0) { modelArray[index] = model val sprite = spriteArray[1]!! model.getQuads(null, null, null).forEach { q -> emitter.fromVanilla(q, null, null) emitter.emit() emitter.fromVanilla(q, null, null) emitter.spriteBake(0, sprite, MutableQuadView.BAKE_LOCK_UV) emitter.sprite(0, 0, sprite.getFrameU(3.0), sprite.getFrameV(3.0)) emitter.sprite(1, 0, sprite.getFrameU(3.0), sprite.getFrameV(13.0)) emitter.sprite(2, 0, sprite.getFrameU(13.0), sprite.getFrameV(13.0)) emitter.sprite(3, 0, sprite.getFrameU(13.0), sprite.getFrameV(3.0)) emitter.material(CENTER_MATERIAL) emitter.emit() } return builder.build() } return super.buildDefaultMesh(index, model) } companion object { val CENTER_MATERIAL by lazy { RendererAccess.INSTANCE.renderer?.materialFinder()!!.clear() .spriteDepth(1) .blendMode(0, BlendMode.CUTOUT) .disableAo(0, true) .disableDiffuse(0, true) .emissive(0, true) ?.find() } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/models/pipes/FluidPipeModel.kt ================================================ package me.steven.indrev.blocks.models.pipes import me.steven.indrev.IndustrialRevolutionClient import me.steven.indrev.api.machines.Tier import me.steven.indrev.networks.EndpointData import me.steven.indrev.networks.Network import me.steven.indrev.networks.client.node.ClientServoNodeInfo import me.steven.indrev.networks.client.node.to import me.steven.indrev.utils.blockSpriteId import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.minecraft.block.BlockState import net.minecraft.client.render.model.BakedModel import net.minecraft.client.render.model.ModelBakeSettings import net.minecraft.client.render.model.ModelLoader import net.minecraft.client.render.model.ModelRotation import net.minecraft.client.texture.Sprite import net.minecraft.client.util.SpriteIdentifier import net.minecraft.item.ItemStack import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockRenderView import java.util.* import java.util.function.Function import java.util.function.Supplier class FluidPipeModel(tier: Tier) : BasePipeModel(tier, "fluid_pipe") { override val spriteIdCollection: MutableList = mutableListOf( blockSpriteId("block/fluid_pipe_center_${tier.toString().lowercase()}"), blockSpriteId("block/fluid_pipe_side_${tier.toString().lowercase()}"), blockSpriteId("block/servo_retriever"), blockSpriteId("block/servo_output") ) val retrieverServoModels = arrayOfNulls(6) val outputServoModels = arrayOfNulls(6) override fun bake( loader: ModelLoader, textureGetter: Function, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel { super.bake(loader, textureGetter, rotationContainer, modelId) val retrieverModel = loader.getOrLoadModel( identifier("block/servo_retriever")) retrieverServoModels[0] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId) // NORTH retrieverServoModels[1] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId) // EAST retrieverServoModels[2] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId) // SOUTH retrieverServoModels[3] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId) // WEST retrieverServoModels[4] = retrieverModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId) // UP retrieverServoModels[5] = retrieverModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId) // DOWN val outputModel = loader.getOrLoadModel( identifier("block/servo_output")) outputServoModels[0] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId) // NORTH outputServoModels[1] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId) // EAST outputServoModels[2] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId) // SOUTH outputServoModels[3] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId) // WEST outputServoModels[4] = outputModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId) // UP outputServoModels[5] = outputModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId) // DOWN return this } override fun emitBlockQuads( world: BlockRenderView, state: BlockState, pos: BlockPos, randSupplier: Supplier, context: RenderContext ) { super.emitBlockQuads(world, state, pos, randSupplier, context) IndustrialRevolutionClient.CLIENT_NETWORK_STATE[Network.Type.FLUID]?.get(pos)?.to()?.servos?.forEach { (dir, type) -> val index = when (dir!!) { Direction.DOWN -> 5 Direction.UP -> 4 Direction.NORTH -> 0 Direction.SOUTH -> 2 Direction.WEST -> 3 Direction.EAST -> 1 } val model = when (type) { EndpointData.Type.RETRIEVER -> retrieverServoModels EndpointData.Type.OUTPUT -> outputServoModels else -> return }[index] context.fallbackConsumer().accept(model) } } override fun emitItemQuads(stack: ItemStack?, p1: Supplier, context: RenderContext) { super.emitItemQuads(stack, p1, context) context.meshConsumer().accept(meshArray[1]) context.meshConsumer().accept(meshArray[3]) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/blocks/models/pipes/ItemPipeModel.kt ================================================ package me.steven.indrev.blocks.models.pipes import me.steven.indrev.IndustrialRevolutionClient import me.steven.indrev.api.machines.Tier import me.steven.indrev.networks.EndpointData import me.steven.indrev.networks.Network import me.steven.indrev.networks.client.node.ClientServoNodeInfo import me.steven.indrev.networks.client.node.to import me.steven.indrev.utils.blockSpriteId import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.minecraft.block.BlockState import net.minecraft.client.render.model.BakedModel import net.minecraft.client.render.model.ModelBakeSettings import net.minecraft.client.render.model.ModelLoader import net.minecraft.client.render.model.ModelRotation import net.minecraft.client.texture.Sprite import net.minecraft.client.util.SpriteIdentifier import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockRenderView import java.util.* import java.util.function.Function import java.util.function.Supplier class ItemPipeModel(tier: Tier) : BasePipeModel(tier, "item_pipe") { override val spriteIdCollection: MutableList = mutableListOf( blockSpriteId("block/item_pipe_center_${tier.toString().lowercase()}"), blockSpriteId("block/item_pipe_side_${tier.toString().lowercase()}"), blockSpriteId("block/servo_retriever"), blockSpriteId("block/servo_output") ) val retrieverServoModels = arrayOfNulls(6) val outputServoModels = arrayOfNulls(6) override fun bake( loader: ModelLoader, textureGetter: Function, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel { super.bake(loader, textureGetter, rotationContainer, modelId) val retrieverModel = loader.getOrLoadModel(identifier("block/servo_retriever_item")) retrieverServoModels[0] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId) // NORTH retrieverServoModels[1] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId) // EAST retrieverServoModels[2] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId) // SOUTH retrieverServoModels[3] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId) // WEST retrieverServoModels[4] = retrieverModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId) // UP retrieverServoModels[5] = retrieverModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId) // DOWN val outputModel = loader.getOrLoadModel(identifier("block/servo_output_item")) outputServoModels[0] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId) // NORTH outputServoModels[1] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId) // EAST outputServoModels[2] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId) // SOUTH outputServoModels[3] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId) // WEST outputServoModels[4] = outputModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId) // UP outputServoModels[5] = outputModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId) // DOWN return this } override fun emitBlockQuads( world: BlockRenderView, state: BlockState, pos: BlockPos, randSupplier: Supplier, context: RenderContext ) { super.emitBlockQuads(world, state, pos, randSupplier, context) IndustrialRevolutionClient.CLIENT_NETWORK_STATE[Network.Type.ITEM]?.get(pos)?.to()?.servos?.forEach { (dir, type) -> val index = when (dir!!) { Direction.DOWN -> 5 Direction.UP -> 4 Direction.NORTH -> 0 Direction.SOUTH -> 2 Direction.WEST -> 3 Direction.EAST -> 1 } val model = when (type) { EndpointData.Type.RETRIEVER -> retrieverServoModels EndpointData.Type.OUTPUT -> outputServoModels else -> return }[index] context.fallbackConsumer().accept(model) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashCableModel.kt ================================================ package me.steven.indrev.compat.dashloader.models import io.activej.serializer.annotations.Deserialize import io.activej.serializer.annotations.Serialize import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.models.pipes.CableModel import net.minecraft.client.render.model.BakedModel import net.oskarstrom.dashloader.DashRegistry import net.oskarstrom.dashloader.api.annotation.DashObject import net.oskarstrom.dashloader.model.DashModel @DashObject(CableModel::class) class DashCableModel : DashModel { var tier: Int @Serialize(order = 0) get var models: IntArray @Serialize(order = 1) get var sprites: IntArray @Serialize(order = 2) get constructor(model: CableModel, registry: DashRegistry) { this.tier = model.tier.ordinal this.models = model.modelArray.map { m -> registry.createModelPointer(m) }.toIntArray() this.sprites = model.spriteArray.map { s -> registry.createSpritePointer(s) }.toIntArray() } constructor( @Deserialize("tier") tier: Int, @Deserialize("models") models: IntArray, @Deserialize("sprites") sprites: IntArray ) { this.tier = tier this.models = models this.sprites = sprites } override fun toUndash(registry: DashRegistry): BakedModel { val model = CableModel(Tier.ALL_VALUES[tier]) this.models.forEachIndexed { index, pointer -> model.modelArray[index] = registry.getModel(pointer) } this.sprites.forEachIndexed { index, pointer -> model.spriteArray[index] = registry.getSprite(pointer) } model.transform = model.modelArray[0]!!.transformation model.buildMeshes() return model } override fun getStage(): Int = 3 } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashFluidPipeModel.kt ================================================ package me.steven.indrev.compat.dashloader.models import io.activej.serializer.annotations.Deserialize import io.activej.serializer.annotations.Serialize import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.models.pipes.FluidPipeModel import net.minecraft.client.render.model.BakedModel import net.oskarstrom.dashloader.DashRegistry import net.oskarstrom.dashloader.api.annotation.DashObject import net.oskarstrom.dashloader.model.DashModel @DashObject(FluidPipeModel::class) class DashFluidPipeModel : DashModel { var tier: Int @Serialize(order = 0) get var models: IntArray @Serialize(order = 1) get var sprites: IntArray @Serialize(order = 2) get var retrieverServos: IntArray @Serialize(order = 3) get var outputServos: IntArray @Serialize(order = 4) get constructor(model: FluidPipeModel, registry: DashRegistry) { this.tier = model.tier.ordinal this.models = model.modelArray.map { m -> registry.createModelPointer(m) }.toIntArray() this.sprites = model.spriteArray.map { s -> registry.createSpritePointer(s) }.toIntArray() this.retrieverServos = model.retrieverServoModels.map { m -> registry.createModelPointer(m) }.toIntArray() this.outputServos = model.outputServoModels.map { m -> registry.createModelPointer(m) }.toIntArray() } constructor( @Deserialize("tier") tier: Int, @Deserialize("models") models: IntArray, @Deserialize("sprites") sprites: IntArray, @Deserialize("retrieverServos") retrieverServos: IntArray, @Deserialize("outputServos") outputServos: IntArray ) { this.tier = tier this.models = models this.sprites = sprites this.retrieverServos = retrieverServos this.outputServos = outputServos } override fun toUndash(registry: DashRegistry): BakedModel { val model = FluidPipeModel(Tier.ALL_VALUES[tier]) this.models.forEachIndexed { index, pointer -> model.modelArray[index] = registry.getModel(pointer) } this.sprites.forEachIndexed { index, pointer -> model.spriteArray[index] = registry.getSprite(pointer) } this.retrieverServos.forEachIndexed { index, pointer -> model.retrieverServoModels[index] = registry.getModel(pointer) } this.outputServos.forEachIndexed { index, pointer -> model.outputServoModels[index] = registry.getModel(pointer) } model.transform = model.modelArray[0]!!.transformation model.buildMeshes() return model } override fun getStage(): Int = 3 } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashItemPipeModel.kt ================================================ package me.steven.indrev.compat.dashloader.models import io.activej.serializer.annotations.Deserialize import io.activej.serializer.annotations.Serialize import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.models.pipes.ItemPipeModel import net.minecraft.client.render.model.BakedModel import net.oskarstrom.dashloader.DashRegistry import net.oskarstrom.dashloader.api.annotation.DashObject import net.oskarstrom.dashloader.model.DashModel @DashObject(ItemPipeModel::class) class DashItemPipeModel: DashModel { var tier: Int @Serialize(order = 0) get var models: IntArray @Serialize(order = 1) get var sprites: IntArray @Serialize(order = 2) get var retrieverServos: IntArray @Serialize(order = 3) get var outputServos: IntArray @Serialize(order = 4) get constructor(model: ItemPipeModel, registry: DashRegistry) { this.tier = model.tier.ordinal this.models = model.modelArray.map { m -> registry.createModelPointer(m) }.toIntArray() this.sprites = model.spriteArray.map { s -> registry.createSpritePointer(s) }.toIntArray() this.retrieverServos = model.retrieverServoModels.map { m -> registry.createModelPointer(m) }.toIntArray() this.outputServos = model.outputServoModels.map { m -> registry.createModelPointer(m) }.toIntArray() } constructor( @Deserialize("tier") tier: Int, @Deserialize("models") models: IntArray, @Deserialize("sprites") sprites: IntArray, @Deserialize("retrieverServos") retrieverServos: IntArray, @Deserialize("outputServos") outputServos: IntArray ) { this.tier = tier this.models = models this.sprites = sprites this.retrieverServos = retrieverServos this.outputServos = outputServos } override fun toUndash(registry: DashRegistry): BakedModel { val model = ItemPipeModel(Tier.ALL_VALUES[tier]) this.models.forEachIndexed { index, pointer -> model.modelArray[index] = registry.getModel(pointer) } this.sprites.forEachIndexed { index, pointer -> model.spriteArray[index] = registry.getSprite(pointer) } this.retrieverServos.forEachIndexed { index, pointer -> model.retrieverServoModels[index] = registry.getModel(pointer) } this.outputServos.forEachIndexed { index, pointer -> model.outputServoModels[index] = registry.getModel(pointer) } model.transform = model.modelArray[0]!!.transformation model.buildMeshes() return model } override fun getStage(): Int = 3 } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashLazuliFluxContainerModel.kt ================================================ package me.steven.indrev.compat.dashloader.models import io.activej.serializer.annotations.Deserialize import io.activej.serializer.annotations.Serialize import me.steven.indrev.blocks.models.LazuliFluxContainerBakedModel import net.minecraft.client.render.model.BakedModel import net.oskarstrom.dashloader.DashRegistry import net.oskarstrom.dashloader.api.annotation.DashObject import net.oskarstrom.dashloader.model.DashModel @DashObject(LazuliFluxContainerBakedModel::class) class DashLazuliFluxContainerModel : DashModel { var id: String @Serialize(order = 0) get var defaultSprite: Int @Serialize(order = 1) get var overlays: IntArray @Serialize(order = 2) get constructor(model: LazuliFluxContainerBakedModel, registry: DashRegistry) { this.id = model.id this.defaultSprite = registry.createSpritePointer(model.baseSprite) this.overlays = model.overlays.map { registry.createSpritePointer(it) }.toIntArray() } constructor( @Deserialize("id") id: String, @Deserialize("defaultSprite") defaultSprite: Int, @Deserialize("overlays") overlays: IntArray ) { this.id = id this.defaultSprite = defaultSprite this.overlays = overlays } override fun toUndash(registry: DashRegistry): BakedModel { val model = LazuliFluxContainerBakedModel(id) model.baseSprite = registry.getSprite(defaultSprite) model.overlays.indices.forEach { index -> val sprite = registry.getSprite(overlays[index]) model.overlays[index] = sprite if (model.isEmissive(sprite)) model.emissives.add(sprite) } model.buildDefaultMesh() return model } override fun getStage(): Int = 0 } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashMachineModel.kt ================================================ package me.steven.indrev.compat.dashloader.models import io.activej.serializer.annotations.Deserialize import io.activej.serializer.annotations.Serialize import me.steven.indrev.blocks.models.MachineBakedModel import me.steven.indrev.utils.blockSpriteId import net.minecraft.client.render.model.BakedModel import net.oskarstrom.dashloader.DashRegistry import net.oskarstrom.dashloader.api.annotation.DashObject import net.oskarstrom.dashloader.model.DashModel @DashObject(MachineBakedModel::class) class DashMachineModel : DashModel { var id: String @Serialize(order = 0) get var defaultSprite: Int @Serialize(order = 1) get var overlays: IntArray @Serialize(order = 2) get var workingOverlays: IntArray @Serialize(order = 3) get constructor(model: MachineBakedModel, registry: DashRegistry) { this.id = model.id this.defaultSprite = registry.createSpritePointer(model.baseSprite) this.overlays = model.overlays.map { registry.createSpritePointer(it) }.toIntArray() this.workingOverlays = model.workingOverlays.map { registry.createSpritePointer(it) }.toIntArray() } constructor( @Deserialize("id") id: String, @Deserialize("defaultSprite") defaultSprite: Int, @Deserialize("overlays") overlays: IntArray, @Deserialize("workingOverlays") workingOverlays: IntArray ) { this.id = id this.defaultSprite = defaultSprite this.overlays = overlays this.workingOverlays = workingOverlays } override fun toUndash(registry: DashRegistry): BakedModel { val model = MachineBakedModel(id) model.baseSprite = registry.getSprite(defaultSprite) overlays.indices.forEach { _ -> model.overlayIds.add(blockSpriteId("")) } workingOverlays.indices.forEach { _ -> model.workingOverlayIds.add(blockSpriteId("")) } model.overlays.indices.forEach { index -> val sprite = registry.getSprite(overlays[index]) model.overlays[index] = sprite if (model.isEmissive(sprite)) model.emissives.add(sprite) } model.workingOverlays.indices.forEach { index -> val sprite = registry.getSprite(workingOverlays[index]) model.workingOverlays[index] = sprite if (model.isEmissive(sprite)) model.emissives.add(sprite) } model.buildDefaultMesh() model.buildWorkingStateMesh() return model } override fun getStage(): Int = 0 } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashMinerModel.kt ================================================ package me.steven.indrev.compat.dashloader.models import io.activej.serializer.annotations.Deserialize import io.activej.serializer.annotations.Serialize import me.steven.indrev.blocks.models.MinerBakedModel import me.steven.indrev.utils.blockSpriteId import net.minecraft.client.render.model.BakedModel import net.oskarstrom.dashloader.DashRegistry import net.oskarstrom.dashloader.api.annotation.DashObject import net.oskarstrom.dashloader.model.DashModel @DashObject(MinerBakedModel::class) class DashMinerModel : DashModel { var id: String @Serialize(order = 0) get var defaultSprite: Int @Serialize(order = 1) get var overlays: IntArray @Serialize(order = 2) get var workingOverlays: IntArray @Serialize(order = 3) get var screenSprite: Int @Serialize(order = 4) get constructor(model: MinerBakedModel, registry: DashRegistry) { this.id = model.id this.defaultSprite = registry.createSpritePointer(model.baseSprite) this.overlays = model.overlays.map { registry.createSpritePointer(it) }.toIntArray() this.workingOverlays = model.workingOverlays.map { registry.createSpritePointer(it) }.toIntArray() this.screenSprite = registry.createSpritePointer(model.screenSprite) } constructor( @Deserialize("id") id: String, @Deserialize("defaultSprite") defaultSprite: Int, @Deserialize("overlays") overlays: IntArray, @Deserialize("workingOverlays") workingOverlays: IntArray, @Deserialize("screenSprite") screenSprite: Int ) { this.id = id this.defaultSprite = defaultSprite this.overlays = overlays this.workingOverlays = workingOverlays this.screenSprite = screenSprite } override fun toUndash(registry: DashRegistry): BakedModel { val model = MinerBakedModel(id) model.baseSprite = registry.getSprite(defaultSprite) model.screenSprite = registry.getSprite(screenSprite) overlays.indices.forEach { _ -> model.overlayIds.add(blockSpriteId("")) } workingOverlays.indices.forEach { _ -> model.workingOverlayIds.add(blockSpriteId("")) } model.overlays.indices.forEach { index -> val sprite = registry.getSprite(overlays[index]) model.overlays[index] = sprite if (model.isEmissive(sprite)) model.emissives.add(sprite) } model.workingOverlays.indices.forEach { index -> val sprite = registry.getSprite(workingOverlays[index]) model.workingOverlays[index] = sprite if (model.isEmissive(sprite)) model.emissives.add(sprite) } model.buildDefaultMesh() model.buildWorkingStateMesh() return model } override fun getStage(): Int = 0 } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashTankModel.kt ================================================ package me.steven.indrev.compat.dashloader.models import me.steven.indrev.items.models.TankItemBakedModel import net.minecraft.client.render.model.BakedModel import net.oskarstrom.dashloader.DashRegistry import net.oskarstrom.dashloader.api.annotation.DashConstructor import net.oskarstrom.dashloader.api.annotation.DashObject import net.oskarstrom.dashloader.api.enums.ConstructorMode import net.oskarstrom.dashloader.model.DashModel @DashObject(TankItemBakedModel::class) class DashTankModel @DashConstructor(ConstructorMode.EMPTY) constructor() : DashModel { override fun toUndash(registry: DashRegistry): BakedModel { return TankItemBakedModel() } override fun getStage(): Int = 3 } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/rei/REIPlugin.kt ================================================ package me.steven.indrev.compat.rei import me.shedaniel.rei.api.client.plugins.REIClientPlugin import me.shedaniel.rei.api.client.registry.category.CategoryRegistry import me.shedaniel.rei.api.client.registry.display.DisplayRegistry import me.shedaniel.rei.api.client.registry.entry.EntryRegistry import me.shedaniel.rei.api.common.util.EntryStacks import me.shedaniel.rei.plugin.common.displays.DefaultInformationDisplay import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.compat.rei.categories.IRMachineRecipeCategory import me.steven.indrev.compat.rei.categories.IRModuleCraftingRecipeCategory import me.steven.indrev.compat.rei.categories.IRSawmillRecipeCategory import me.steven.indrev.compat.rei.plugins.IRMachinePlugin import me.steven.indrev.recipes.machines.* import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.energyOf import me.steven.indrev.utils.hide import net.minecraft.block.Block import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.recipe.AbstractCookingRecipe import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import java.util.* import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.distinctBy import kotlin.collections.forEach import kotlin.collections.indexOf import kotlin.collections.mutableMapOf import kotlin.collections.sumOf import kotlin.math.roundToInt object REIPlugin : REIClientPlugin { override fun registerEntries(entryRegistry: EntryRegistry?) { fun registerCharged(vararg items: Item) { items.forEach { item -> entryRegistry?.addEntriesAfter(EntryStacks.of(item), EntryStacks.of(ItemStack(item).also { it.orCreateNbt.putLong("energy", energyOf(it)!!.capacity) })) } } registerCharged( IRItemRegistry.MINING_DRILL_MK1, IRItemRegistry.MINING_DRILL_MK2, IRItemRegistry.MINING_DRILL_MK3, IRItemRegistry.MINING_DRILL_MK4, IRItemRegistry.MODULAR_ARMOR_HELMET, IRItemRegistry.MODULAR_ARMOR_CHEST, IRItemRegistry.MODULAR_ARMOR_LEGGINGS, IRItemRegistry.MODULAR_ARMOR_BOOTS, IRItemRegistry.PORTABLE_CHARGER_ITEM, IRItemRegistry.BATTERY ) entryRegistry?.addEntriesAfter(EntryStacks.of(IRItemRegistry.GAMER_AXE_ITEM), EntryStacks.of(ItemStack(IRItemRegistry.GAMER_AXE_ITEM).also { val tag = it.orCreateNbt tag.putLong("energy", energyOf(it)!!.capacity) tag.putBoolean("Active", true) tag.putFloat("Progress", 1f) })) entryRegistry?.removeEntryIf { e -> hide(e.identifier ?: return@removeEntryIf false) } } override fun registerCategories(registry: CategoryRegistry) { registry.add( IRMachineRecipeCategory( PulverizerRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.PULVERIZER_REGISTRY.block(Tier.MK1)), "indrev.category.rei.pulverizing" ) ) registry.add( IRMachineRecipeCategory( InfuserRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.SOLID_INFUSER_REGISTRY.block(Tier.MK1)), "indrev.category.rei.infusing" ) ) registry.add( IRMachineRecipeCategory( CompressorRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.COMPRESSOR_REGISTRY.block(Tier.MK1)), "indrev.category.rei.compressing" ) ) registry.add( IRMachineRecipeCategory( RecyclerRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.RECYCLER_REGISTRY.block(Tier.MK2)), "indrev.category.rei.recycling" ) ) registry.add( IRMachineRecipeCategory( FluidInfuserRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.FLUID_INFUSER_REGISTRY.block(Tier.MK1)), "indrev.category.rei.fluid_infusing" ) ) registry.add( IRMachineRecipeCategory( CondenserRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.CONDENSER_REGISTRY.block(Tier.MK4)), "indrev.category.rei.condensing" ) ) registry.add( IRMachineRecipeCategory( SmelterRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.SMELTER_REGISTRY.block(Tier.MK4)), "indrev.category.rei.smelting" ) ) registry.add( IRSawmillRecipeCategory( SawmillRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.SAWMILL_REGISTRY.block(Tier.MK4)), "indrev.category.rei.sawmill" ) ) registry.add( IRModuleCraftingRecipeCategory( ModuleRecipe.IDENTIFIER, EntryStacks.of(MachineRegistry.MODULAR_WORKBENCH_REGISTRY.block(Tier.MK4)), "indrev.category.rei.module" ) ) } override fun registerDisplays(registry: DisplayRegistry) { registry.recipeManager.recipes.keys.forEach { type -> if (type is IRRecipeType<*> && type.id.namespace == IndustrialRevolution.MOD_ID) registry.registerFiller(IRRecipe::class.java, { r -> r is IRRecipe && r !is AbstractCookingRecipe && r.type == type }) { recipe -> IRMachinePlugin(recipe) } } MachineRegistry.MAP.entries.distinctBy { (_, v) -> v }.forEach { (_, machineRegistry) -> if (machineRegistry.upgradeable && machineRegistry.tiers.size > 1) { machineRegistry.forEachBlock { tier, block -> val entryStack = EntryStacks.of(block) if (tier != Tier.CREATIVE && tier != Tier.MK1) { val info = DefaultInformationDisplay.createFromEntry(entryStack, TranslatableText(block.translationKey)) info.lines(TranslatableText("indrev.category.rei.upgrading", TranslatableText("item.indrev.tier_upgrade_" + tier.toString() .lowercase(Locale.getDefault())).formatted(Formatting.DARK_GRAY), TranslatableText(machineRegistry.block(machineRegistry.tiers[machineRegistry.tiers.indexOf(tier) - 1]).translationKey).formatted(Formatting.DARK_GRAY), tier.toString())) registry.add(info) } } } } } /*override fun registerOthers(recipeHelper: RecipeHelper?) { MachineRegistry.PULVERIZER_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( PULVERIZING, EntryStacks.of(block) ) } MachineRegistry.SOLID_INFUSER_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( INFUSING, EntryStacks.of(block) ) } MachineRegistry.COMPRESSOR_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( COMPRESSING, EntryStacks.of(block) ) } MachineRegistry.RECYCLER_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( RECYCLING, EntryStacks.of(block) ) } MachineRegistry.FLUID_INFUSER_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( FLUID_INFUSER, EntryStacks.of(block) ) } MachineRegistry.CONDENSER_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( CONDENSER, EntryStacks.of(block) ) } MachineRegistry.SMELTER_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( SMELTER, EntryStacks.of(block) ) } MachineRegistry.SAWMILL_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( SAWMILL, EntryStacks.of(block) ) } MachineRegistry.MODULAR_WORKBENCH_REGISTRY.forEachBlock { _, block -> recipeHelper?.registerWorkingStations( MODULE, EntryStacks.of(block) ) } }*/ } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/rei/categories/IRMachineRecipeCategory.kt ================================================ package me.steven.indrev.compat.rei.categories import me.shedaniel.math.Point import me.shedaniel.math.Rectangle import me.shedaniel.rei.api.client.gui.DisplayRenderer import me.shedaniel.rei.api.client.gui.Renderer import me.shedaniel.rei.api.client.gui.SimpleDisplayRenderer import me.shedaniel.rei.api.client.gui.widgets.Widget import me.shedaniel.rei.api.client.gui.widgets.Widgets import me.shedaniel.rei.api.client.registry.display.DisplayCategory import me.shedaniel.rei.api.common.category.CategoryIdentifier import me.shedaniel.rei.api.common.entry.EntryStack import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes import me.steven.indrev.compat.rei.plugins.IRMachinePlugin import me.steven.indrev.recipes.machines.IRFluidRecipe import me.steven.indrev.utils.createREIFluidWidget import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Identifier open class IRMachineRecipeCategory( private val identifier: Identifier, private val logo: EntryStack<*>, private val categoryName: String ) : DisplayCategory { override fun setupDisplay(recipeDisplay: IRMachinePlugin, bounds: Rectangle): MutableList { val recipe = recipeDisplay.recipe val startPoint = Point(bounds.centerX - 41, bounds.centerY - 27) val widgets = super.setupDisplay(recipeDisplay, bounds).toMutableList() widgets.add(Widgets.createArrow(Point(startPoint.x + 24, startPoint.y + 18))) if (recipe.input.isNotEmpty()) { val input = recipeDisplay.inputEntries.filter { it.all { it.type == VanillaEntryTypes.ITEM } } widgets.add(Widgets.createSlot(Point(startPoint.x + 1, startPoint.y + 19)).entries(input[0])) if (recipe.input.size > 1) widgets.add( Widgets.createSlot(Point(startPoint.x - 17, startPoint.y + 19)).entries(input[1]) ) } if (recipe.outputs.isNotEmpty()) { widgets.add(Widgets.createResultSlotBackground(Point(startPoint.x + 61, startPoint.y + 19))) widgets.add( Widgets.createSlot(Point(startPoint.x + 61, startPoint.y + 19)).entries(recipeDisplay.outputEntries.filter { it.all { it.type == VanillaEntryTypes.ITEM } }[0]).disableBackground().markOutput() ) } if (recipe is IRFluidRecipe) { if (recipe.fluidOutput.isNotEmpty() && recipe.fluidOutput[0].amount > 0) { val outputFluidPoint = Point(startPoint.x + 83, startPoint.y) createREIFluidWidget(widgets, outputFluidPoint, recipe.fluidOutput[0]) } if (recipe.fluidInput.isNotEmpty() && recipe.fluidInput[0].amount > 0) { val inputFluidPoint = Point(startPoint.x - 20, startPoint.y) createREIFluidWidget(widgets, inputFluidPoint, recipe.fluidInput[0]) } } return widgets } override fun getDisplayRenderer(display: IRMachinePlugin): DisplayRenderer { return SimpleDisplayRenderer.from(listOf(display.inputEntries[0]), display.outputEntries) } override fun getDisplayHeight(): Int = 66 override fun getIdentifier(): Identifier = identifier override fun getIcon(): Renderer { return logo } override fun getTitle(): Text = TranslatableText(categoryName) override fun getCategoryIdentifier(): CategoryIdentifier { return CategoryIdentifier.of(identifier) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/rei/categories/IRModuleCraftingRecipeCategory.kt ================================================ package me.steven.indrev.compat.rei.categories import me.shedaniel.math.Point import me.shedaniel.math.Rectangle import me.shedaniel.rei.api.client.gui.widgets.Widget import me.shedaniel.rei.api.client.gui.widgets.Widgets import me.shedaniel.rei.api.common.entry.EntryStack import me.shedaniel.rei.api.common.util.EntryStacks import me.steven.indrev.compat.rei.plugins.IRMachinePlugin import net.minecraft.util.Identifier class IRModuleCraftingRecipeCategory( identifier: Identifier, logo: EntryStack<*>, categoryName: String ) : IRMachineRecipeCategory(identifier, logo, categoryName) { private val slotLayout = hashMapOf>() init { slotLayout[1] = arrayOf(Point(2 * 18, 0)) slotLayout[2] = arrayOf( Point(0, 2 * 18), Point(4 * 18, 2 * 18) ) slotLayout[3] = arrayOf( Point(2 * 18, 0), Point(0, 4 * 17), Point(4 * 18, 4 * 17) ) slotLayout[4] = arrayOf( Point(2 * 18, 0), Point(0, 2 * 18), Point(4 * 18, 2 * 18), Point(2 * 18, 4 * 18) ) slotLayout[5] = arrayOf( Point(2 * 18, 0), Point(0, 2 * 18), Point(4 * 18, 2 * 18), Point(1 * 14, 4 * 18), Point(3 * 20, 4 * 18) ) slotLayout[6] = arrayOf( Point(2 * 18, 0), Point(0 * 18, 1 * 18), Point(4 * 18, 1 * 18), Point(0 * 14, 3 * 18), Point(4 * 18, 3 * 18), Point(2 * 18, 4 * 18) ) } override fun setupDisplay(recipeDisplay: IRMachinePlugin, bounds: Rectangle): MutableList { val recipe = recipeDisplay.recipe val startPoint = Point(bounds.centerX - 43, bounds.centerY - 45) val widgets: MutableList = listOf(Widgets.createCategoryBase(bounds)).toMutableList() val input = recipeDisplay.inputEntries val outputs = recipe.outputs.map { EntryStacks.of(it.stack) } slotLayout[input.size]?.forEachIndexed { index, point -> widgets.add(Widgets.createSlot(startPoint + point).entries(input[index])) } widgets.add(Widgets.createResultSlotBackground(startPoint + Point(2 * 18, 2 * 18))) widgets.add(Widgets.createSlot(startPoint + Point(2 * 18, 2 * 18)).entry(outputs[0]).disableBackground().markOutput()) return widgets } private operator fun Point.plus(other: Point) = Point(x + other.x, y + other.y) override fun getDisplayHeight(): Int = 120 } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/rei/categories/IRSawmillRecipeCategory.kt ================================================ package me.steven.indrev.compat.rei.categories import me.shedaniel.math.Point import me.shedaniel.math.Rectangle import me.shedaniel.rei.api.client.gui.widgets.Widget import me.shedaniel.rei.api.client.gui.widgets.Widgets import me.shedaniel.rei.api.common.entry.EntryStack import me.shedaniel.rei.api.common.util.EntryStacks import me.steven.indrev.compat.rei.plugins.IRMachinePlugin import net.minecraft.item.ItemStack import net.minecraft.util.Identifier class IRSawmillRecipeCategory( identifier: Identifier, logo: EntryStack<*>, categoryName: String ) : IRMachineRecipeCategory(identifier, logo, categoryName) { override fun setupDisplay(recipeDisplay: IRMachinePlugin, bounds: Rectangle): MutableList { val recipe = recipeDisplay.recipe val startPoint = Point(bounds.centerX - 41, bounds.centerY - 27) val widgets: MutableList = listOf(Widgets.createCategoryBase(bounds)).toMutableList() widgets.add(Widgets.createArrow(Point(startPoint.x + 24, startPoint.y + 18))) val input = recipeDisplay.inputEntries widgets.add(Widgets.createSlot(Point(startPoint.x + 1, startPoint.y + 19)).entries(input[0])) val outputs = recipe.outputs.map { EntryStacks.of(it.stack) } widgets.add( Widgets.createSlot(Point(startPoint.x + 61, startPoint.y + 6)) .entry(outputs.getOrElse(0) { EntryStacks.of(ItemStack.EMPTY) }) ) widgets.add( Widgets.createSlot(Point(startPoint.x + 61, startPoint.y + 24)) .entry(outputs.getOrElse(1) { EntryStacks.of(ItemStack.EMPTY) }) ) widgets.add( Widgets.createSlot(Point(startPoint.x + 79, startPoint.y + 6)) .entry(outputs.getOrElse(2) { EntryStacks.of(ItemStack.EMPTY) }) ) widgets.add( Widgets.createSlot(Point(startPoint.x + 79, startPoint.y + 24)) .entry(outputs.getOrElse(3) { EntryStacks.of(ItemStack.EMPTY) }) ) return widgets } } ================================================ FILE: src/main/kotlin/me/steven/indrev/compat/rei/plugins/IRMachinePlugin.kt ================================================ package me.steven.indrev.compat.rei.plugins import me.shedaniel.rei.api.common.category.CategoryIdentifier import me.shedaniel.rei.api.common.display.Display import me.shedaniel.rei.api.common.entry.EntryIngredient import me.shedaniel.rei.api.common.util.EntryIngredients import me.shedaniel.rei.api.common.util.EntryStacks import me.steven.indrev.recipes.machines.IRFluidRecipe import me.steven.indrev.recipes.machines.IRRecipe import net.minecraft.item.ItemStack import net.minecraft.util.Identifier import java.util.* open class IRMachinePlugin(val recipe: IRRecipe) : Display { override fun getInputEntries(): List = getInputs(recipe) override fun getOutputEntries(): List = getOutputs(recipe) override fun getCategoryIdentifier(): CategoryIdentifier<*> { return CategoryIdentifier.of(recipe.type.id) } override fun getDisplayLocation(): Optional { return Optional.of(recipe.id) } companion object { fun getInputs(recipe: IRRecipe): List { val list = mutableListOf() if (recipe is IRFluidRecipe) { val fluidInput = recipe.fluidInput //TODO properly support multiple inputs if (fluidInput.isNotEmpty()) list.addAll(mutableListOf(EntryIngredients.of(fluidInput[0].resource.fluid, 81000))) } list.addAll(recipe.input.map { (ingredient, count) -> val builder = EntryIngredient.builder() ingredient.matchingStacks.map { stack -> builder.add(EntryStacks.of(ItemStack(stack.item, count))) } builder.build() }) return list } fun getOutputs(recipe: IRRecipe): List { val list = mutableListOf() list.addAll(recipe.outputs.map { (stack, _) -> EntryIngredients.of(stack) }.toMutableList()) if (recipe is IRFluidRecipe) { val fluidOutput = recipe.fluidOutput //TODO properly support multiple outputs if (fluidOutput.isNotEmpty()) list.add(EntryIngredients.of(fluidOutput[0].resource.fluid, 81000)) } return list } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/CraftingComponent.kt ================================================ package me.steven.indrev.components import me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity import me.steven.indrev.inventories.IRInventory import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.recipes.IRecipeGetter import me.steven.indrev.recipes.machines.IRFluidRecipe import me.steven.indrev.recipes.machines.IRRecipe import me.steven.indrev.utils.IRFluidAmount import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.network.PacketByteBuf import net.minecraft.server.world.ServerWorld import net.minecraft.world.World import kotlin.math.ceil open class CraftingComponent(private val index: Int, val machine: CraftingMachineBlockEntity) : DefaultSyncableObject() { var processTime: Int = 0 var totalProcessTime: Int = 0 val fluidComponent: FluidComponent? get() = machine.fluidComponent val inventoryComponent: InventoryComponent get() = machine.inventoryComponent!! val temperatureComponent: TemperatureComponent? get() = machine.temperatureComponent val type: IRecipeGetter get() = machine.type val world: World? get() = machine.world var isCrafting: Boolean = false var inputSlots: IntArray? = null get() = if (field == null) inventoryComponent.inventory.inputSlots else field var outputSlots: IntArray? = null get() = if (field == null) inventoryComponent.inventory.outputSlots else field private var currentRecipe: T? = null fun tick() { val inventory = inventoryComponent.inventory val inputInventory = inputSlots!!.map { inventory.getStack(it) } val inputTanks = fluidComponent?.inputTanks?.map { fluidComponent!![it] } ?: emptyList() when { isProcessing() -> { val recipe = currentRecipe val upgrades = machine.enhancerComponent!!.enhancers if (recipe?.matches(inputInventory, inputTanks) != true) { tryStartRecipe(inventory) ?: reset().also { markDirty() } } else if (machine.use(machine.getEnergyCost())) { isCrafting = true processTime = (processTime + ceil(machine.getProcessingSpeed())).coerceAtLeast(0.0).toInt() markDirty() if (processTime >= totalProcessTime) { handleInventories(inventory, inputInventory, recipe) machine.usedRecipes.addTo(recipe.id, 1) reset() } } else isCrafting = false } machine.energy > 0 -> { reset() if (tryStartRecipe(inventory) == null) isCrafting = false } else -> { reset() isCrafting = false } } temperatureComponent?.tick(isProcessing() && isCrafting) } protected open fun handleInventories(inventory: IRInventory, inputInventory: List, recipe: IRRecipe) { val output = recipe.craft(machine.world!!.random) inputSlots!!.forEachIndexed { index, _ -> recipe.input.forEach { (ingredient, count) -> val stack = inputInventory[index] if (!ingredient.test(stack)) return@forEach stack.decrement(count) return@forEachIndexed } } output.forEach { stack -> craft(stack) } if (recipe is IRFluidRecipe) { fluidComponent!!.inputTanks.forEach outer@{ slot -> recipe.fluidInput.forEach { volume -> val tank = fluidComponent!![slot] if (tank.resource != volume.resource) return@forEach val amount = tank.amount - volume.amount if (amount <= 0) tank.variant = FluidVariant.blank() tank.amount = amount tank.markDirty() return@forEach } } recipe.fluidOutput.forEach { craft(it) } } } fun craft(fluid: IRFluidAmount) { val fluidComponent = fluidComponent!! for (outputSlot in fluidComponent.outputTanks) { val tank = fluidComponent[outputSlot] if (tank.isEmpty) { tank.variant = fluid.resource tank.amount = tank.amount + fluid.amount tank.markDirty() } else if (fluid.resource == tank.resource && fluid.amount() + tank.amount <= tank.capacity) { tank.amount = tank.amount + fluid.amount tank.markDirty() } else continue break } } fun craft(stack: ItemStack) { val inventory = inventoryComponent.inventory for (outputSlot in outputSlots!!) { val outStack = inventory.getStack(outputSlot) if (stack.item == outStack.item && stack.nbt == outStack.nbt && stack.count + outStack.count <= stack.maxCount) outStack.increment(stack.count) else if (outStack.isEmpty) inventory.setStack(outputSlot, stack) else continue break } } fun fits(stack: ItemStack): Boolean { for (outputSlot in outputSlots!!) { val outStack = inventoryComponent.inventory.getStack(outputSlot) if (outStack.isEmpty || (stack.item == outStack.item && stack.nbt == outStack.nbt && stack.count + outStack.count <= stack.maxCount)) return true } return false } private fun tryStartRecipe(inventory: IRInventory): T? { val inputStacks = inputSlots!!.map { inventory.getStack(it) }.filter { !it.isEmpty } val inputFluids = fluidComponent?.inputTanks?.map { fluidComponent!![it] }?.filter { !it.isEmpty } ?: emptyList() val recipe = type.getMatchingRecipe(world as ServerWorld, inputStacks, inputFluids) .firstOrNull { it.matches(inputStacks, inputFluids) } ?: return null if (!recipe.canStart(this)) return null processTime = 0 totalProcessTime = recipe.ticks currentRecipe = recipe return recipe } private fun reset() { processTime = 0 totalProcessTime = 0 } private fun isProcessing() = totalProcessTime > 0 && processTime < totalProcessTime fun readNbt(tag: NbtCompound?) { processTime = tag?.getInt("ProcessTime") ?: 0 totalProcessTime = tag?.getInt("MaxProcessTime") ?: 0 } fun writeNbt(tag: NbtCompound): NbtCompound { tag.putInt("ProcessTime", processTime) tag.putInt("MaxProcessTime", totalProcessTime) return tag } override fun toPacket(buf: PacketByteBuf) { buf.writeInt(processTime) buf.writeInt(totalProcessTime) } @Environment(EnvType.CLIENT) override fun fromPacket(buf: PacketByteBuf) { this.processTime = buf.readInt() this.totalProcessTime = buf.readInt() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/EnhancerComponent.kt ================================================ package me.steven.indrev.components import it.unimi.dsi.fastutil.ints.IntBinaryOperator import it.unimi.dsi.fastutil.objects.Object2IntMap import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import me.steven.indrev.api.machines.Tier import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.items.upgrade.IREnhancerItem import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import net.minecraft.inventory.Inventory open class EnhancerComponent( val slots: IntArray, val compatible: Array, val maxSlotCount: (Enhancer) -> Int ) { val enhancers: Object2IntMap = Object2IntOpenHashMap() fun updateEnhancers(inventory: Inventory) { enhancers.clear() slots .forEach { slot -> val (stack, item) = inventory.getStack(slot) if (item is IREnhancerItem && compatible.contains(item.enhancer)) enhancers.mergeInt(item.enhancer, stack.count, IntBinaryOperator { i, j -> i + j }) } } fun getCount(enhancer: Enhancer) = enhancers.getInt(enhancer) open fun isLocked(slot: Int, tier: Tier) = slots.indexOf(slot) > tier.ordinal } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/FluidComponent.kt ================================================ package me.steven.indrev.components import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.blockentities.Syncable import me.steven.indrev.utils.IRFluidTank import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.storage.StorageView import net.fabricmc.fabric.api.transfer.v1.storage.base.CombinedStorage import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.Direction import java.util.* open class FluidComponent(val syncable: () -> Syncable, val limit: Long, val tankCount: Int = 1) : CombinedStorage(mutableListOf()) { init { parts.addAll((0 until tankCount).map { IRFluidTank(it) { this } }) } var inputTanks = intArrayOf() var outputTanks = intArrayOf() var unsided = false val transferConfig: SideConfiguration = SideConfiguration(ConfigurationType.FLUID) private val exposedSides = EnumMap(Direction::class.java) fun getCachedSide(dir: Direction): ExposedFluidComponent { val exposed = exposedSides[dir] if (exposed == null || (!unsided && exposed.mode != transferConfig[dir])) { exposedSides[dir] = ExposedFluidComponent(dir, transferConfig[dir]!!) } return exposedSides[dir]!! } open fun getValidTanks(dir: Direction): IntArray = if (unsided) IntArray(tankCount) { it } else if (transferConfig[dir]!!.input) inputTanks else if (transferConfig[dir]!!.output) outputTanks else IntArray(0) open fun getTankCapacity(index: Int): Long = limit open fun isFluidValidForTank(index: Int, variant: FluidVariant): Boolean = true operator fun set(tank: Int, volume: Long) { this.parts[tank].amount = volume } operator fun get(tank: Int): IRFluidTank = this.parts[tank] fun toTag(tag: NbtCompound): NbtCompound { val tanksTag = NbtCompound() parts.forEachIndexed { index, tank -> val tankTag = NbtCompound() tankTag.put("fluids", tank.toTag()) tanksTag.put(index.toString(), tankTag) } tag.put("tanks", tanksTag) transferConfig.writeNbt(tag) return tag } fun fromTag(tag: NbtCompound?) { val tanksTag = tag?.getCompound("tanks") tanksTag?.keys?.forEach { key -> val index = key.toInt() val tankTag = tanksTag.getCompound(key) parts[index].fromTag(tankTag.getCompound("fluids")) } transferConfig.readNbt(tag) } inner class ExposedFluidComponent(val dir: Direction, val mode: TransferMode) : CombinedStorage(mutableListOf()) { init { parts.addAll(getValidTanks(dir).map { this@FluidComponent.parts[it].exposed }) } override fun insert(resource: FluidVariant?, maxAmount: Long, transaction: TransactionContext?): Long { return if (unsided || transferConfig[dir]!!.input) { super.insert(resource, maxAmount, transaction) } else 0 } override fun extract(resource: FluidVariant?, maxAmount: Long, transaction: TransactionContext?): Long { return if (unsided || transferConfig[dir]!!.output) super.extract(resource, maxAmount, transaction) else 0 } override fun supportsExtraction(): Boolean = this@FluidComponent.supportsExtraction() override fun supportsInsertion(): Boolean = this@FluidComponent.supportsInsertion() override fun getVersion(): Long = this@FluidComponent.version override fun exactView(transaction: TransactionContext?, resource: FluidVariant?): StorageView? { return super.exactView(transaction, resource) } override fun iterable(transaction: TransactionContext?): MutableIterable> { return super.iterable(transaction) } override fun iterator(transaction: TransactionContext?): MutableIterator> { return super.iterator(transaction) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/GuiSyncableComponent.kt ================================================ package me.steven.indrev.components import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.gui.properties.* import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.network.PacketByteBuf class GuiSyncableComponent { val properties = mutableListOf>() @Suppress("UNCHECKED_CAST") operator fun get(index: Int) = properties[index].value as T fun add(index: Int, prop: SyncableProperty<*>) { val r = properties.size until index + 1 for (x in r) { properties.add(NullSyncableProperty) } properties[index] = prop } } interface SyncableObject { var isDirty: Boolean fun markDirty(condition: () -> Boolean = { true }) fun toPacket(buf: PacketByteBuf) @Environment(EnvType.CLIENT) fun fromPacket(buf: PacketByteBuf) } open class DefaultSyncableObject : SyncableObject { override var isDirty: Boolean = false override fun markDirty(condition: () -> Boolean) { if (isDirty || condition()) isDirty = true } override fun toPacket(buf: PacketByteBuf) { } override fun fromPacket(buf: PacketByteBuf) { } } fun wrapTrackedObject(index: Int, obj: T): SyncableProperty { return object : SyncableProperty(index, obj) { override var isDirty: Boolean get() = obj.isDirty set(value) { obj.isDirty = value } override fun toPacket(buf: PacketByteBuf) = obj.toPacket(buf) override fun fromPacket(buf: PacketByteBuf) = obj.fromPacket(buf) } } fun BaseBlockEntity.autosync(index: Int, value: Int, setter: (Int) -> Int = { it }): IntSyncableProperty { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") return IntSyncableProperty(index, value, setter) .also { component.add(index, it) } } fun BaseBlockEntity.trackInt(index: Int, provider: () -> Int) { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") component.add(index, object : IntSyncableProperty(index, provider()) { override var value: Int get() = if (world!!.isClient) clientValue else provider() set(value) { clientValue = value } var clientValue: Int = 0 override var isDirty: Boolean get() = clientValue != value set(value) { clientValue = if (!value) this.value else -1 } }) } fun BaseBlockEntity.autosync(index: Int, value: Long, setter: (Long) -> Long = { it }) : LongSyncableProperty { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") return LongSyncableProperty(index, value, setter) .also { component.add(index, it) } } fun BaseBlockEntity.trackLong(index: Int, provider: () -> Long) { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") component.add(index, object : LongSyncableProperty(index, provider()) { override var value: Long get() = if (world!!.isClient) clientValue else provider() set(value) { clientValue = value } private var clientValue = 0L override var isDirty: Boolean get() = clientValue != value set(value) { clientValue = if (!value) this.value else -1 } }) } fun BaseBlockEntity.autosync(index: Int, value: Double, setter: (Double) -> Double = { it }) : DoubleSyncableProperty { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") return DoubleSyncableProperty(index, value, setter) .also { component.add(index, it) } } fun BaseBlockEntity.trackDouble(index: Int, provider: () -> Double) { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") component.add(index, object : DoubleSyncableProperty(index, provider()) { override var value: Double get() = if (world!!.isClient) clientValue else provider() set(value) { clientValue = value } private var clientValue = 0.0 override var isDirty: Boolean get() = clientValue != value set(value) { clientValue = if (!value) this.value else -1.0 } }) } fun BaseBlockEntity.autosync(index: Int, value: Boolean, setter: (Boolean) -> Boolean = { it }) : BooleanSyncableProperty { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") return BooleanSyncableProperty(index, value, setter) .also { component.add(index, it) } } fun BaseBlockEntity.trackBoolean(index: Int, provider: () -> Boolean) { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") component.add(index, object : BooleanSyncableProperty(index, provider()) { override var value: Boolean get() = if (world!!.isClient) clientValue else provider() set(value) { clientValue = value } private var clientValue = false override var isDirty: Boolean get() = clientValue != value set(value) { clientValue = if (!value) this.value else !clientValue } }) } fun > BaseBlockEntity.autosync(index: Int, value: T, values: Array, setter: (T) -> T = { it }) : EnumSyncableProperty { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") return EnumSyncableProperty(index, value, values, setter) .also { component.add(index, it) } } fun > BaseBlockEntity.trackEnum(index: Int, values: Array, provider: () -> T) { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") component.add(index, object : EnumSyncableProperty(index, provider(), values) { override var value: T get() = if (world!!.isClient) clientValue else provider() set(value) { clientValue = value } private var clientValue = defaultValue override var isDirty: Boolean get() = clientValue != value set(value) { clientValue = if (!value) this.value else values[0] } }) } fun BaseBlockEntity.trackObject(index: Int, value: T) { val component = this.guiSyncableComponent ?: error("$this does not provide gui_syncable component") component.add(index, wrapTrackedObject(index, value)) } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/InventoryComponent.kt ================================================ package me.steven.indrev.components import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.inventories.IRInventory import net.minecraft.inventory.Inventory import net.minecraft.inventory.InventoryChangedListener import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtList class InventoryComponent(val syncable: MachineBlockEntity<*>, supplier: InventoryComponent.() -> IRInventory) : InventoryChangedListener { val inventory: IRInventory = supplier() init { inventory.addListener(this) inventory.component = this } val itemConfig: SideConfiguration = SideConfiguration(ConfigurationType.ITEM) override fun onInventoryChanged(sender: Inventory?) { syncable.markForUpdate() } fun readNbt(tag: NbtCompound?) { val tagList = tag?.get("Inventory") as NbtList? ?: NbtList() tagList.indices.forEach { i -> val stackTag = tagList.getCompound(i) val slot = stackTag.getInt("Slot") inventory.setStack(slot, ItemStack.fromNbt(stackTag)) } itemConfig.readNbt(tag) } fun writeNbt(tag: NbtCompound): NbtCompound { val tagList = NbtList() for (i in 0 until inventory.size()) { val stackTag = NbtCompound() stackTag.putInt("Slot", i) tagList.add(inventory.getStack(i).writeNbt(stackTag)) } tag.put("Inventory", tagList) itemConfig.writeNbt(tag) return tag } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/TemperatureComponent.kt ================================================ package me.steven.indrev.components import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import net.minecraft.block.entity.BlockEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound class TemperatureComponent( private val blockEntity: BaseBlockEntity, private val heatingSpeed: Double, val optimalRange: IntRange, val limit: Int ) { var cooling = true var temperature: Double = 25.0 init { blockEntity.trackInt(MachineBlockEntity.TEMPERATURE_ID) { temperature.toInt() } blockEntity.trackInt(MachineBlockEntity.MAX_TEMPERATURE_ID) { limit } } private var ticks = 0 fun readNbt(tag: NbtCompound?) { temperature = tag?.getDouble("Temperature") ?: 0.0 cooling = tag?.getBoolean("Cooling") ?: false } fun writeNbt(tag: NbtCompound): NbtCompound { tag.putDouble("Temperature", temperature) tag.putBoolean("Cooling", cooling) return tag } fun isFullEfficiency(): Boolean { val machine = blockEntity as? MachineBlockEntity<*> val inventoryComponent = machine?.inventoryComponent return (!cooling || inventoryComponent?.inventory?.coolerStack?.isEmpty != true) && temperature.toInt() in optimalRange } fun tick(shouldHeatUp: Boolean) { ticks++ val machine = blockEntity as? MachineBlockEntity<*> val random = blockEntity.world!!.random val inv = machine?.inventoryComponent?.inventory val (coolerStack, coolerItem) = inv?.coolerStack ?: ItemStack.EMPTY val isHeatingUp = shouldHeatUp || (machine != null && coolerItem == IRItemRegistry.HEAT_COIL && machine.use(16)) if (cooling) { val modifier = (blockEntity as? CraftingMachineBlockEntity<*>)?.craftingComponents?.size ?: 0 temperature -= heatingSpeed / if (isHeatingUp) 3 + modifier else 1 if (coolerStack.isDamageable && ticks % 120 == 0) coolerStack.damage(1, random, null) if (coolerStack.damage >= coolerStack.maxDamage) { coolerStack.decrement(1) } if (temperature <= optimalRange.first + (2 * random.nextFloat() - 1) * 10) { cooling = false } } else if (isHeatingUp) { temperature += heatingSpeed val n = (optimalRange.last + optimalRange.first) / 2.0 if (temperature >= n + (2 * random.nextFloat() - 1) * 15) { cooling = true } } else if (temperature > 35.0) { temperature -= heatingSpeed / 1.5 } else if (ticks % 15 == 0) { temperature = (temperature + (2 * random.nextFloat() - 1) / 2).coerceIn(20.0, 35.0) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/BlockStateFilter.kt ================================================ package me.steven.indrev.components.multiblock import net.minecraft.block.BlockState import net.minecraft.block.Blocks class BlockStateFilter(private val filter: (BlockState) -> Boolean, val display: BlockState = Blocks.AIR.defaultState) { constructor(blockState: BlockState) : this({ b -> b == blockState }, blockState) operator fun invoke(state: BlockState) = filter(state) } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/MultiBlockComponent.kt ================================================ package me.steven.indrev.components.multiblock import net.minecraft.block.BlockState import net.minecraft.nbt.NbtCompound import net.minecraft.util.math.BlockPos import net.minecraft.world.World open class MultiBlockComponent( val structureDecider: (BlockState, World, BlockPos) -> StructureDefinition ) { var shouldRenderHologram = false var variant = 0 private var ticks = 0 private var cachedMatchers: MutableMap = hashMapOf() open fun tick(world: World, pos: BlockPos, blockState: BlockState) { ticks++ if (ticks % 15 != 0) return getSelectedMatcher(world, pos, blockState).update(world, pos, blockState) } fun getSelectedMatcher(world: World, pos: BlockPos, blockState: BlockState): MultiblockMatcher { val selected = structureDecider(blockState, world, pos) return cachedMatchers.computeIfAbsent(selected.identifier) { selected.toMatcher() } } fun isBuilt(world: World, pos: BlockPos, blockState: BlockState, forceUpdate: Boolean = false): Boolean { if (forceUpdate) { getSelectedMatcher(world, pos, blockState).update(world, pos, blockState) } return getSelectedMatcher(world, pos, blockState).matches } fun toggleRender(isSneaking: Boolean) { if (!isSneaking) shouldRenderHologram = !shouldRenderHologram else variant++ } fun readNbt(tag: NbtCompound?) { shouldRenderHologram = tag?.getBoolean("ShouldRenderHologram") ?: false variant = tag?.getInt("Variant") ?: 0 } fun writeNbt(tag: NbtCompound): NbtCompound { tag.putBoolean("ShouldRenderHologram", shouldRenderHologram) tag.putInt("Variant", variant) return tag } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/MultiblockBlockEntityRenderer.kt ================================================ package me.steven.indrev.components.multiblock import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import net.minecraft.block.entity.BlockEntity import net.minecraft.client.MinecraftClient import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.BlockRotation class MultiblockBlockEntityRenderer(val provider: (T) -> MultiBlockComponent) : BlockEntityRenderer { override fun render( entity: T, tickDelta: Float, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int ) { val multiblock = provider(entity) if (!multiblock.shouldRenderHologram) return val rotation = MultiblockMatcher.rotateBlock(entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING].opposite) val def = multiblock.getSelectedMatcher(entity.world!!, entity.pos, entity.cachedState).definition def.holder.variants.values.toList()[multiblock.variant % def.holder.variants.size].forEach { (offset, state) -> matrices.push() val rotated = offset.rotate(rotation) val blockPos = entity.pos.subtract(rotated) val blockState = entity.world!!.getBlockState(blockPos) if (blockState.material.isReplaceable) { matrices.translate(-rotated.x.toDouble() + 0.25, -rotated.y.toDouble() + 0.25, -rotated.z.toDouble() + 0.25) matrices.scale(0.5f, 0.5f, 0.5f) MinecraftClient.getInstance().blockRenderManager .renderBlockAsEntity(state.display.rotate(rotation.rotate(BlockRotation.CLOCKWISE_180)), matrices, vertexConsumers, 15728880, overlay) } matrices.pop() } } override fun rendersOutsideBoundingBox(blockEntity: T): Boolean = true } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/MultiblockMatcher.kt ================================================ package me.steven.indrev.components.multiblock import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import net.minecraft.block.BlockState import net.minecraft.util.BlockRotation import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World import net.minecraft.world.chunk.Chunk class MultiblockMatcher(val definition: StructureDefinition) { var builtId: StructureIdentifier? = null val matches get() = builtId != null fun update(world: World, pos: BlockPos, state: BlockState) { if (builtId == null || !isBuilt(world, pos, state, definition.holder.variants[builtId]!!)) { builtId = getBuiltStructureId(world, pos, state) } } fun getBuiltStructureId(world: World, pos: BlockPos, state: BlockState): StructureIdentifier? { return definition.holder.variants .filter { (_, structure) -> isBuilt(world, pos, state, structure) } .map { it.key } .firstOrNull() } fun isBuilt(world: World, pos: BlockPos, state: BlockState, map: Map): Boolean { var currentChunk: Chunk = world.getChunk(pos) if (world.isOutOfHeightLimit(pos)) return false val rotation = rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) return map.all { (offset, statePredicate) -> val statePos = pos.subtract(offset.rotate(rotation).rotate(BlockRotation.CLOCKWISE_180)) if (currentChunk.pos.startX < statePos.x || currentChunk.pos.endX > statePos.x || currentChunk.pos.startZ < statePos.z || currentChunk.pos.endZ > statePos.z) { currentChunk = world.getChunk(statePos) } val blockState = currentChunk.getBlockState(statePos) .rotate(rotateBlock0(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING])) statePredicate(blockState) } } fun getBuiltStructure(): Map { return if (builtId == null) emptyMap() else definition.holder.variants[builtId] ?: emptyMap() } companion object { fun rotateBlock(direction: Direction): BlockRotation { return when (direction) { Direction.NORTH -> BlockRotation.NONE Direction.SOUTH -> BlockRotation.CLOCKWISE_180 Direction.WEST -> BlockRotation.COUNTERCLOCKWISE_90 Direction.EAST -> BlockRotation.CLOCKWISE_90 else -> return BlockRotation.NONE } } // this is because there is something fundamentally wrong in the rotation/position of the multiblock structures // I don't want to fix it now so this is a workaround and yes I am aware of the problems. fun rotateBlock0(direction: Direction): BlockRotation { return when (direction) { Direction.NORTH -> BlockRotation.NONE Direction.SOUTH -> BlockRotation.CLOCKWISE_180 Direction.WEST -> BlockRotation.CLOCKWISE_90 Direction.EAST -> BlockRotation.COUNTERCLOCKWISE_90 else -> return BlockRotation.NONE } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/StructureDefinition.kt ================================================ package me.steven.indrev.components.multiblock import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import net.minecraft.block.BlockState import net.minecraft.util.BlockRotation import net.minecraft.util.math.BlockPos import net.minecraft.world.World import net.minecraft.world.chunk.Chunk interface StructureDefinition { val identifier: String val isOptional: Boolean val holder: StructureHolder fun toMatcher(): MultiblockMatcher = MultiblockMatcher(this) } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/StructureHolder.kt ================================================ package me.steven.indrev.components.multiblock import com.google.common.collect.ImmutableMap import net.minecraft.block.BlockState import net.minecraft.util.BlockRotation import net.minecraft.util.math.BlockPos class StructureHolder(val variants: Map> = hashMapOf()) { class Builder(private val definition: StructureDefinition, private val structure: MutableMap = HashMap()) { private val createdStructures: MutableMap> = hashMapOf() fun add(blockPos: BlockPos, blockState: (BlockState) -> Boolean): Builder { structure[blockPos] = BlockStateFilter(blockState) return this } fun from(structure: Map): Builder { this.structure.clear() this.structure.putAll(structure) return this } fun corners( center: BlockPos, radius: Int, state: (BlockState) -> Boolean, rotation: BlockRotation = BlockRotation.NONE ): Builder { add(center.add(radius, radius, 0).rotate(rotation), state) add(center.add(-radius, radius, 0).rotate(rotation), state) add(center.add(radius, -radius, 0).rotate(rotation), state) add(center.add(-radius, -radius, 0).rotate(rotation), state) return this } fun horizontalCorners( center: BlockPos, radius: Int, state: (BlockState) -> Boolean, rotation: BlockRotation = BlockRotation.NONE ): Builder { add(center.add(radius, 0, radius).rotate(rotation), state) add(center.add(-radius, 0, radius).rotate(rotation), state) add(center.add(radius, 0, -radius).rotate(rotation), state) add(center.add(-radius, 0, -radius).rotate(rotation), state) return this } fun diamond(center: BlockPos, radius: Int, state: (BlockState) -> Boolean): Builder { add(center.add(0, radius, 0), state) add(center.add(0, -radius, 0), state) add(center.add(radius, 0, 0), state) add(center.add(-radius, 0, 0), state) return this } fun cube(start: BlockPos, width: Int, depth: Int, height: Int, state: (BlockState) -> Boolean): Builder { for (x in 0 until width) { for (y in 0 until height) { for (z in 0 until depth) { add(start.add(BlockPos(x, y, z)), state) } } } return this } fun add(blockPos: BlockPos, blockState: BlockState): Builder { structure[blockPos] = BlockStateFilter(blockState) return this } fun corners( center: BlockPos, radius: Int, state: BlockState, rotation: BlockRotation = BlockRotation.NONE ): Builder { add(center.add(radius, radius, 0).rotate(rotation), state) add(center.add(-radius, radius, 0).rotate(rotation), state) add(center.add(radius, -radius, 0).rotate(rotation), state) add(center.add(-radius, -radius, 0).rotate(rotation), state) return this } fun horizontalCorners( center: BlockPos, radius: Int, state: BlockState, rotation: BlockRotation = BlockRotation.NONE ): Builder { add(center.add(radius, 0, radius).rotate(rotation), state) add(center.add(-radius, 0, radius).rotate(rotation), state) add(center.add(radius, 0, -radius).rotate(rotation), state) add(center.add(-radius, 0, -radius).rotate(rotation), state) return this } fun diamond(center: BlockPos, radius: Int, state: BlockState): Builder { add(center.add(0, radius, 0), state) add(center.add(0, -radius, 0), state) add(center.add(radius, 0, 0), state) add(center.add(-radius, 0, 0), state) return this } fun cube(start: BlockPos, width: Int, depth: Int, height: Int, state: BlockState): Builder { for (x in 0 until width) { for (y in 0 until height) { for (z in 0 until depth) { add(start.add(BlockPos(x, y, z)), state) } } } return this } fun remove(pos: BlockPos): Builder { structure.remove(pos) return this } fun create(variant: String): Builder { createdStructures[StructureIdentifier("indrev", definition.identifier, variant)] = ImmutableMap.copyOf(structure) structure.clear() return this } fun build(): StructureHolder = StructureHolder(ImmutableMap.copyOf(createdStructures)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/StructureIdentifier.kt ================================================ package me.steven.indrev.components.multiblock import net.minecraft.util.Identifier class StructureIdentifier(val modId: String, val structure: String, val variant: String): Identifier(modId, "$structure/$variant") ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/definitions/FactoryStructureDefinition.kt ================================================ package me.steven.indrev.components.multiblock.definitions import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.components.multiblock.MultiblockMatcher import me.steven.indrev.components.multiblock.StructureDefinition import me.steven.indrev.components.multiblock.StructureHolder import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.BlockState import net.minecraft.state.property.Properties import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World /** * Multiple definitions rather than Structure variations were used here to avoid useless block state calls */ object FactoryStructureDefinition : StructureDefinition { private val CONTROLLER_STATE = IRBlockRegistry.CONTROLLER.defaultState.with(Properties.HORIZONTAL_FACING, Direction.NORTH) private val DUCT_STATE = IRBlockRegistry.DUCT.defaultState.with(Properties.HORIZONTAL_FACING, Direction.WEST) private val CABINET_STATE = IRBlockRegistry.CABINET.defaultState.with(Properties.HORIZONTAL_FACING, Direction.WEST) private val INTAKE_STATE = IRBlockRegistry.INTAKE.defaultState.with(Properties.HORIZONTAL_FACING, Direction.WEST) override val identifier: String = "factory" override val isOptional: Boolean = false override val holder: StructureHolder = StructureHolder.Builder(this) .cube(BlockPos(-1, 1, 1), 3, 3, 1, IRBlockRegistry.FRAME.defaultState) .cube(BlockPos(0, 0, 1), 2, 3, 1, IRBlockRegistry.SILO.defaultState) .cube(BlockPos(1, -1, 1), 1, 3, 1, INTAKE_STATE) .cube(BlockPos(0, -1, 1), 1, 3, 1, DUCT_STATE) .cube(BlockPos(-1, 0, 2), 1, 2, 1, CABINET_STATE) .add(BlockPos(-1, 0, 1), CONTROLLER_STATE) .add(BlockPos(-1, -1, 1), IRBlockRegistry.WARNING_STROBE.defaultState) .create("factory") .build() object Inverted : StructureDefinition { private val DUCT_STATE_INVERTED = IRBlockRegistry.DUCT.defaultState.with(Properties.HORIZONTAL_FACING, Direction.EAST) private val CABINET_STATE_INVERTED = IRBlockRegistry.CABINET.defaultState.with(Properties.HORIZONTAL_FACING, Direction.EAST) private val INTAKE_STATE_INVERTED = IRBlockRegistry.INTAKE.defaultState.with(Properties.HORIZONTAL_FACING, Direction.EAST) override val identifier: String = "factory_inverted" override val isOptional: Boolean = false override val holder: StructureHolder = StructureHolder.Builder(this) .cube(BlockPos(-1, 1, 1), 3, 3, 1, IRBlockRegistry.FRAME.defaultState) .cube(BlockPos(-1, 0, 1), 2, 3, 1, IRBlockRegistry.SILO.defaultState) .cube(BlockPos(-1, -1, 1), 1, 3, 1, INTAKE_STATE_INVERTED) .cube(BlockPos(0, -1, 1), 1, 3, 1, DUCT_STATE_INVERTED) .cube(BlockPos(1, 0, 2), 1, 2, 1, CABINET_STATE_INVERTED) .add(BlockPos(1, 0, 1), CONTROLLER_STATE) .add(BlockPos(1, -1, 1), IRBlockRegistry.WARNING_STROBE.defaultState) .create("factory") .build() } val SELECTOR: (BlockState, World, BlockPos) -> StructureDefinition = { state, world, pos -> val rotation = MultiblockMatcher.rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING].opposite) val ductState = world.getBlockState(pos.subtract(BlockPos(0, -1, 1).rotate(rotation))) if (!ductState.isOf(IRBlockRegistry.DUCT)) FactoryStructureDefinition else { val ductFacing = ductState[HorizontalFacingMachineBlock.HORIZONTAL_FACING] if (ductFacing == DUCT_STATE.rotate(rotation)[HorizontalFacingMachineBlock.HORIZONTAL_FACING].opposite) FactoryStructureDefinition else Inverted } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/definitions/SolarPowerPlantTowerStructureDefinition.kt ================================================ package me.steven.indrev.components.multiblock.definitions import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.components.multiblock.* import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.block.FacingBlock import net.minecraft.util.BlockRotation import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import kotlin.math.abs object SolarPowerPlantTowerStructureDefinition : StructureDefinition { private val CASING = IRBlockRegistry.STEAM_TURBINE_CASING_BLOCK private val RESISTANT_GLASS = Blocks.GLASS.defaultState private val FLUID_INPUT = IRBlockRegistry.FLUID_VALVE.defaultState private val FLUID_OUTPUT = IRBlockRegistry.FLUID_VALVE.defaultState.with(FacingBlock.FACING, Direction.NORTH) override val identifier: String = "solar_power_plant" override val isOptional: Boolean = false override val holder: StructureHolder = StructureHolder.Builder(this) .from(createStructureMap()) .create("default") .build() fun getFluidInputPositions(pos: BlockPos, state: BlockState): List { val rotation = MultiblockMatcher.rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) val radius = 3 val positions = hashSetOf() for (x in -radius..radius) { for (z in -radius..radius) { if ((abs(z) == radius) || (abs(x) == radius)) positions.add(BlockPos(x, 0, z + radius)) } } return positions .map { offset -> pos.subtract(offset.rotate(rotation).rotate(BlockRotation.CLOCKWISE_180)) }.toList() } fun getSolarReceiverPositions(pos: BlockPos, state: BlockState): List { val rotation = MultiblockMatcher.rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) val radius = 3 val positions = hashSetOf() for (x in -radius + 1 until radius) { for (y in -10..-4) { for (z in -radius + 1 until radius) { if (y == -8 && (abs(x) == radius-1 || abs(z) == radius-1)) positions.add(BlockPos(x, y, z + 3)) } } } return positions .map { offset -> pos.subtract(offset.rotate(rotation).rotate(BlockRotation.CLOCKWISE_180)) }.toList() } private fun createStructureMap(): Map { val map = hashMapOf() val radius = 3 for (x in -radius..radius) { for (y in -3..1) { for (z in -radius..radius) { if (arrayOf(abs(x), abs(y), abs(z)).count { it == radius } >= 2 || y == -3 || y == 1) map[BlockPos(x, y, z + radius)] = BlockStateFilter(Blocks.IRON_BLOCK.defaultState) else if ((y == 0 && abs(z) == radius) || (y == 0 && abs(x) == radius)) map[BlockPos(x, y, z + radius)] = BlockStateFilter({state -> state == Blocks.IRON_BLOCK.defaultState || state == FLUID_INPUT }, FLUID_INPUT) else if (arrayOf(abs(x), abs(y), abs(z)).count { it == radius } != 0) map[BlockPos(x, y, z + radius)] = BlockStateFilter({ state -> state == CASING || state == RESISTANT_GLASS }, RESISTANT_GLASS) } } } map[BlockPos(0, 1, 0)] = BlockStateFilter(FLUID_OUTPUT) for (x in -radius + 1 until radius) { for (y in -10..-4) { for (z in -radius + 1 until radius) { if (y == -8 && (abs(x) == radius-1 || abs(z) == radius-1)) map[BlockPos(x, y, z + 3)] = BlockStateFilter(IRBlockRegistry.SOLAR_RECEIVER_BLOCK.defaultState) else if (abs(x) == radius-1 || abs(z) == radius-1 || y == -10) map[BlockPos(x, y, z + 3)] = BlockStateFilter(Blocks.IRON_BLOCK.defaultState) } } } map.remove(BlockPos.ORIGIN) return map } } ================================================ FILE: src/main/kotlin/me/steven/indrev/components/multiblock/definitions/SteamTurbineStructureDefinition.kt ================================================ package me.steven.indrev.components.multiblock.definitions import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.blocks.misc.VerticalFacingBlock import me.steven.indrev.components.multiblock.* import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.block.FacingBlock import net.minecraft.block.HorizontalFacingBlock import net.minecraft.util.BlockRotation import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import kotlin.math.abs object SteamTurbineStructureDefinition : StructureDefinition { private val ROTOR_UP = IRBlockRegistry.STEAM_TURBINE_ROTOR_BLOCK.defaultState.with(VerticalFacingBlock.FACING, Direction.UP) private val ROTOR_DOWN = IRBlockRegistry.STEAM_TURBINE_ROTOR_BLOCK.defaultState.with(VerticalFacingBlock.FACING, Direction.DOWN) private val PRESSURE_VALVE_SOUTH = IRBlockRegistry.STEAM_TURBINE_PRESSURE_VALVE_BLOCK.defaultState.with(HorizontalFacingBlock.FACING, Direction.SOUTH) private val STEAM_INPUT_VALVE_NORTH = IRBlockRegistry.STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK.defaultState.with(FacingBlock.FACING, Direction.NORTH) private val ENERGY_OUTPUT_NORTH = IRBlockRegistry.STEAM_TURBINE_ENERGY_OUTPUT.defaultState.with(HorizontalFacingBlock.FACING, Direction.NORTH) private val CASING = IRBlockRegistry.STEAM_TURBINE_CASING_BLOCK private val RESISTANT_GLASS = Blocks.GLASS.defaultState override val identifier: String = "steam_turbine" override val isOptional: Boolean = false override val holder: StructureHolder = StructureHolder.Builder(this) .from(createStructureMap(2)) .create("5x5x5") .from(createStructureMap(3)) .create("7x7x7") .from(createStructureMap(4)) .create("9x9x9") .from(createStructureMap(5)) .create("11x11x11") .from(createStructureMap(6)) .create("13x13x13") .from(createStructureMap(7)) .create("15x15x15") .build() fun getInputValvePositions(pos: BlockPos, state: BlockState, matcher: MultiblockMatcher): List { val rotation = MultiblockMatcher.rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) matcher.builtId?.also { id -> val radius = getRadius(id) return arrayOf( BlockPos(-radius + 1, 0, 0), BlockPos(radius - 1, 0, 0), BlockPos(0, -radius + 1, 0), BlockPos(0, radius - 1, 0) ) .map { offset -> pos.subtract(offset.rotate(rotation).rotate(BlockRotation.CLOCKWISE_180)) }.toList() } return emptyList() } fun getRadius(id: StructureIdentifier) = (id.variant.substring(0, id.variant.indexOf("x")).toInt() - 1) / 2 private fun createStructureMap(radius: Int): Map { val map = hashMapOf() for (x in -radius..radius) { for (y in -radius..radius) { for (z in -radius..radius) { if (arrayOf(abs(x), abs(y), abs(z)).count { it == radius } >= 2 || (y == 0 && abs(z) == radius) || (x == 0 && abs(z) == radius) || abs(y) == radius) map[BlockPos(x, y, z + radius)] = BlockStateFilter(Blocks.IRON_BLOCK.defaultState) else if (arrayOf(abs(x), abs(y), abs(z)).count { it == radius } != 0) map[BlockPos(x, y, z + radius)] = BlockStateFilter({ state -> state == CASING || state == RESISTANT_GLASS }, RESISTANT_GLASS) } } } map[BlockPos(0, -radius, radius)] = BlockStateFilter(ROTOR_UP) map[BlockPos(0, radius, radius)] = BlockStateFilter(ROTOR_DOWN) map[BlockPos(-radius + 1, 0, 0)] = BlockStateFilter(STEAM_INPUT_VALVE_NORTH) map[BlockPos(radius - 1, 0, 0)] = BlockStateFilter(STEAM_INPUT_VALVE_NORTH) map[BlockPos(0, -radius + 1, 0)] = BlockStateFilter(STEAM_INPUT_VALVE_NORTH) map[BlockPos(0, radius - 1, 0)] = BlockStateFilter(STEAM_INPUT_VALVE_NORTH) map[BlockPos(-radius + 1, 0, radius * 2)] = BlockStateFilter(PRESSURE_VALVE_SOUTH) map[BlockPos(radius - 1, 0, radius * 2)] = BlockStateFilter(PRESSURE_VALVE_SOUTH) map[BlockPos(0, -radius + 1, radius * 2)] = BlockStateFilter(PRESSURE_VALVE_SOUTH) map[BlockPos(0, radius - 1, radius * 2)] = BlockStateFilter(PRESSURE_VALVE_SOUTH) for (i in -radius + 1 until radius) map[BlockPos(0, i, radius)] = BlockStateFilter(Blocks.ACACIA_FENCE.defaultState) map[BlockPos(0, 1, 0)] = BlockStateFilter(ENERGY_OUTPUT_NORTH) map.remove(BlockPos(0, 0, 0)) return map } } ================================================ FILE: src/main/kotlin/me/steven/indrev/config/IRConfig.kt ================================================ package me.steven.indrev.config import com.google.gson.GsonBuilder import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.registry.IRItemRegistry import net.fabricmc.loader.api.FabricLoader import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.tag.ItemTags import net.minecraft.tag.TagKey import net.minecraft.util.Identifier import java.io.File object IRConfig { private val gson = GsonBuilder().setPrettyPrinting().create() lateinit var generators: Generators lateinit var machines: Machines lateinit var cables: Cables lateinit var upgrades: Upgrades lateinit var oregen: OreGen lateinit var hud: Hud lateinit var miningRigConfig: MiningRigConfig init { readConfigs() } fun readConfigs() { generators = readOrCreate("generators.json") { Generators() } machines = readOrCreate("machines.json") { Machines() } cables = readOrCreate("cables.json") { Cables() } upgrades = readOrCreate("upgrades.json") { Upgrades() } oregen = readOrCreate("oregen.json") { OreGen() } hud = readOrCreate("hud.json") { Hud() } miningRigConfig = readOrCreate("mining_rig_config.json") { MiningRigConfig() } } private inline fun readOrCreate(file: String, default: () -> T): T { val dir = File(FabricLoader.getInstance().configDir.toFile(), "indrev") if (!dir.exists() && !dir.mkdirs()) { IndustrialRevolution.LOGGER.error("Could not create directory, using default configs.") return default() } val f = File(dir, file) try { if (f.exists()) return gson.fromJson(f.readLines().joinToString(""), T::class.java) else if (!f.createNewFile()) IndustrialRevolution.LOGGER.error("Failed to create default config file ($file), using default config.") else f.writeText(gson.toJson(default())) return default() } catch (e: Exception) { IndustrialRevolution.LOGGER.error("Failed to read config file! Using default values.", e) return default() } } fun writeToClient(buf: PacketByteBuf) { val gson = GsonBuilder().create() buf.writeString(gson.toJson(generators)) buf.writeString(gson.toJson(machines)) buf.writeString(gson.toJson(cables)) buf.writeString(gson.toJson(upgrades)) buf.writeString(gson.toJson(oregen)) buf.writeString(gson.toJson(miningRigConfig)) } fun readFromServer(buf: PacketByteBuf) { generators = gson.fromJson(buf.readString(), Generators::class.java) machines = gson.fromJson(buf.readString(), Machines::class.java) cables = gson.fromJson(buf.readString(), Cables::class.java) upgrades = gson.fromJson(buf.readString(), Upgrades::class.java) oregen = gson.fromJson(buf.readString(), OreGen::class.java) miningRigConfig = gson.fromJson(buf.readString(), MiningRigConfig::class.java) } } class Generators { val coalGenerator: GeneratorConfig = GeneratorConfig(16.0, 1.5, 1000, Tier.MK1.io) val solarGeneratorMk1: GeneratorConfig = GeneratorConfig(8.0, 1.5, 500, Tier.MK1.io) val solarGeneratorMk3: GeneratorConfig = GeneratorConfig(12.0, 1.5, 1500, Tier.MK3.io) val heatGenerator: GeneratorConfig = GeneratorConfig(128.0, -1.0, 10000, Tier.MK4.io) val gasGenerator: GeneratorConfig = GeneratorConfig(1.0, 1.5, 10000, Tier.MK4.io) val biomassGenerator: GeneratorConfig = GeneratorConfig(64.0, 1.5, 10000, Tier.MK3.io) val steamTurbine: GeneratorConfig = GeneratorConfig(4.0, -1.0, 20000, Tier.MK4.io) } class GeneratorConfig( val ratio: Double = 16.0, val temperatureBoost: Double = 1.5, override val maxEnergyStored: Long, val maxOutput: Long ) : IConfig class Machines { val electricFurnaceMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io) val electricFurnaceMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0, 5000, Tier.MK2.io) val electricFurnaceMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0, 10000, Tier.MK3.io) val electricFurnaceMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, Tier.MK4.io) val electricFurnaceFactory: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, 16384) val pulverizerMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io) val pulverizerMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0, 5000, Tier.MK2.io) val pulverizerMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0, 10000, Tier.MK3.io) val pulverizerMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, Tier.MK4.io) val pulverizerFactory: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, 16384) val compressorMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io) val compressorMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0, 5000, Tier.MK2.io) val compressorMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0, 10000, Tier.MK3.io) val compressorMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, Tier.MK4.io) val compressorFactory: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, 16384) val sawmillMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io) val sawmillMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0, 5000, Tier.MK2.io) val sawmillMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0, 10000, Tier.MK3.io) val sawmillMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, Tier.MK4.io) val infuserMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io) val infuserMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0,5000, Tier.MK2.io) val infuserMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0,10000, Tier.MK3.io) val infuserMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0,100000, Tier.MK4.io) val infuserFactory: HeatMachineConfig = HeatMachineConfig(64, 4.5, 2.0, 100000, 16384) val recycler: MachineConfig = MachineConfig(8, 2.5, 50000, Tier.MK2.io) val chopperMk1: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io) val chopperMk2: MachineConfig = MachineConfig(32, 40.0, 5000, Tier.MK2.io) val chopperMk3: MachineConfig = MachineConfig(64, 30.0, 25000, Tier.MK3.io) val chopperMk4: MachineConfig = MachineConfig(128, 20.0, 50000, Tier.MK4.io) val farmerMk1: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io) val farmerMk2: MachineConfig = MachineConfig(32, 40.0, 5000, Tier.MK2.io) val farmerMk3: MachineConfig = MachineConfig(64, 30.0, 25000, Tier.MK3.io) val farmerMk4: MachineConfig = MachineConfig(128, 20.0, 50000, Tier.MK4.io) val slaughterMk1: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io) val slaughterMk2: MachineConfig = MachineConfig(32, 40.0, 5000, Tier.MK2.io) val slaughterMk3: MachineConfig = MachineConfig(64, 30.0, 25000, Tier.MK3.io) val slaughterMk4: MachineConfig = MachineConfig(128, 20.0, 50000, Tier.MK4.io) val rancherMk1: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io) val rancherMk2: MachineConfig = MachineConfig(32, 40.0, 5000, Tier.MK2.io) val rancherMk3: MachineConfig = MachineConfig(64, 30.0, 25000, Tier.MK3.io) val rancherMk4: MachineConfig = MachineConfig(128, 20.0, 50000, Tier.MK4.io) val miner: MachineConfig = MachineConfig(64, 100.0, 50000, Tier.MK4.io) val dataCardWriter: MachineConfig = MachineConfig(128, 100.0, 2500, Tier.MK4.io) val drill: Long = 256 val fishingMk2: MachineConfig = MachineConfig(8, 400.0, 50000, Tier.MK2.io) val fishingMk3: MachineConfig = MachineConfig(16, 400.0, 50000, Tier.MK3.io) val fishingMk4: MachineConfig = MachineConfig(64, 500.0, 50000, Tier.MK4.io) val dirtOxygenator: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io) val drain: MachineConfig = MachineConfig(4, 20.0, 1000, Tier.MK1.io) val pump: MachineConfig = MachineConfig(32, 50.0, 50, Tier.MK1.io) val pumpMaxRange: Int = 64 val smelter: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0,50000, Tier.MK4.io) val condenser: MachineConfig = MachineConfig(64, 4.0, 50000, Tier.MK4.io) val fluidInfuserMk1: MachineConfig = MachineConfig(4, 1.0, 1000, Tier.MK1.io) val fluidInfuserMk2: MachineConfig = MachineConfig(8, 2.0, 5000, Tier.MK2.io) val fluidInfuserMk3: MachineConfig = MachineConfig(16, 3.0, 10000, Tier.MK3.io) val fluidInfuserMk4: MachineConfig = MachineConfig(64, 4.0, 100000, Tier.MK4.io) val electrolyticSeparatorMk1: MachineConfig = MachineConfig(4, 1.0, 1000, Tier.MK1.io) val electrolyticSeparatorMk2: MachineConfig = MachineConfig(8, 2.0, 5000, Tier.MK2.io) val electrolyticSeparatorMk3: MachineConfig = MachineConfig(16, 3.0, 10000, Tier.MK3.io) val electrolyticSeparatorMk4: MachineConfig = MachineConfig(64, 4.0, 100000, Tier.MK4.io) val modularWorkbench: MachineConfig = MachineConfig(64, 1.0, 5000, Tier.MK4.io) val laser: MachineConfig = MachineConfig(4096, 1.0, 2500000, 16384) val distiller: MachineConfig = MachineConfig(64, 4.0, 100000, Tier.MK4.io) val lazuliFluxContainerMk1: LFCConfig = LFCConfig(10000, 128, 128) val lazuliFluxContainerMk2: LFCConfig = LFCConfig(100000, 512, 512) val lazuliFluxContainerMk3: LFCConfig = LFCConfig(1000000, 4096, 4096) val lazuliFluxContainerMk4: LFCConfig = LFCConfig(10000000, 16384, 16384) } class HeatMachineConfig( override val energyCost: Long, override val processSpeed: Double, val processTemperatureBoost: Double, override val maxEnergyStored: Long, override val maxInput: Long ) : BasicMachineConfig open class MachineConfig( override val energyCost: Long, override val processSpeed: Double, override val maxEnergyStored: Long, override val maxInput: Long ) : BasicMachineConfig interface BasicMachineConfig : IConfig { val energyCost: Long val processSpeed: Double val maxInput: Long } class LFCConfig( override val maxEnergyStored: Long, val maxInput: Long, val maxOutput: Long ): IConfig interface IConfig { val maxEnergyStored: Long } class Cables { val cableMk1 = 128 val cableMk2 = 512 val cableMk3 = 4096 val cableMk4 = 16384 val itemPipeMk1 = 32 val itemPipeMk2 = 64 val itemPipeMk3 = 128 val itemPipeMk4 = 256 val fluidPipeMk1 = 1 val fluidPipeMk2 = 2 val fluidPipeMk3 = 4 val fluidPipeMk4 = 8 } class Upgrades { val speedUpgradeModifier = 6.5 val energyUpgradeModifier = 1.02 val bufferUpgradeModifier = 25000 val damageUpgradeModifier = 3.0 } class OreGen { val tin = true val nikolite = true val lead = true val silver = true val tungsten = true val sulfuricAcidLake = true val sulfurCrystals = true } class MiningRigConfig { val allowedTags = mutableMapOf( ItemTags.COPPER_ORES.id.toString() to 1, ItemTags.COAL_ORES.id.toString() to 1, ItemTags.GOLD_ORES.id.toString() to 2, ItemTags.IRON_ORES.id.toString() to 2, IndustrialRevolution.NIKOLITE_ORES.id.toString() to 2, IndustrialRevolution.TIN_ORES.id.toString() to 2, ItemTags.REDSTONE_ORES.id.toString() to 2, IndustrialRevolution.LEAD_ORES.id.toString() to 3, IndustrialRevolution.SILVER_ORES.id.toString() to 3, IndustrialRevolution.TUNGSTEN_ORES.id.toString() to 3, ItemTags.DIAMOND_ORES.id.toString() to 4, ItemTags.EMERALD_ORES.id.toString() to 4, IndustrialRevolution.ANCIENT_DEBRIS_ORES.id.toString() to 4 ) } class Hud { val renderPosX = 0 val renderPosY = 0 } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/DataFactory.kt ================================================ package me.steven.indrev.datagen import net.minecraft.util.Identifier import java.io.File interface DataFactory { val extension: String fun getFileName(t: T, id: Identifier): String = id.path fun generate(): P? fun write(file: File, t: P) } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/DataGenerator.kt ================================================ package me.steven.indrev.datagen import net.minecraft.util.Identifier import java.io.File abstract class DataGenerator(val dir: File, val namespace: String, val fallback: (T) -> DataFactory) { protected val generators = HashMap>() init { dir.mkdirs() } operator fun get(obj: T): DataFactory = generators.getOrDefault(obj, fallback(obj)) fun register(obj: T, factory: DataFactory) { generators[obj] = factory } fun generate(identifier: Identifier, obj: T): Boolean { val jsonFactory = this[obj] return generate(identifier, obj, jsonFactory) } fun generate(identifier: Identifier, obj: T, factory: DataFactory): Boolean { val file = File(dir, "${factory.getFileName(obj, identifier)}.${factory.extension}") file.parentFile.mkdirs() val output = factory.generate() if (output != null) { file.createNewFile() factory.write(file, output) return true } return false } abstract fun generate(): Int } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/DataGeneratorManager.kt ================================================ package me.steven.indrev.datagen import com.google.gson.JsonArray import com.google.gson.JsonObject import me.steven.indrev.IndustrialRevolution import me.steven.indrev.api.machines.Tier import me.steven.indrev.datagen.generators.* import me.steven.indrev.datagen.utils.MetalModel import me.steven.indrev.datagen.utils.MetalSpriteRegistry import me.steven.indrev.items.misc.IRMachineUpgradeItem import me.steven.indrev.registry.IRBlockRegistry import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.registry.MachineRegistry import net.minecraft.item.BlockItem import net.minecraft.item.Items import net.minecraft.util.DyeColor import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import java.io.File import kotlin.system.exitProcess class DataGeneratorManager(namespace: String) { val root = File("generated") val lootTableGenerator = LootTableGenerator(root, namespace, LootTableGenerator.SELF_DROP) val itemModelGenerator = ItemModelGenerator(root, namespace, ItemModelGenerator.DEFAULT_ITEM) val blockModelGenerator = BlockModelGenerator(root, namespace) { JsonFactory.nullFactory() } val materialRecipeGenerator = MaterialRecipeGenerator(root, namespace) { JsonFactory.nullFactory() } val materialTagGenerator = MaterialTagGenerator(root, namespace) { JsonFactory.nullFactory() } val metalSpriteGenerator = MetalSpriteGenerator(root, namespace) { ImageFactory.nullFactory() } init { root.mkdir() arrayOf("tin", "lead", "tungsten", "silver").forEach { material -> materialRecipeGenerator.register("raw_${material}", rawOreIntoBlock(material)) materialRecipeGenerator.register("raw_${material}_block", rawOreBlockIntoRawItem(material)) } arrayOf("copper", "tin", "lead", "tungsten", "silver").forEach { material -> materialRecipeGenerator.register( "${material}_ore", pulverizeOre("c:${material}_ores", "indrev:${material}_dust") ) materialRecipeGenerator.register( "${material}_ingot", pulverizeIngot("c:${material}_ingots", "indrev:${material}_dust") ) arrayOf("ore", "plate", "dust", "ingot").forEach { suffix -> materialTagGenerator.register("${material}_$suffix", createTag("indrev:${material}_$suffix")) } } arrayOf("iron", "gold").forEach { material -> materialRecipeGenerator.register( "${material}_ore", pulverizeOre("c:${material}_ores", "indrev:${material}_dust") ) materialRecipeGenerator.register( "${material}_ingot", pulverizeIngot("c:${material}_ingots", "indrev:${material}_dust") ) materialTagGenerator.register("${material}_ore", createTag("minecraft:${material}_ore")) } arrayOf("diamond", "coal").forEach { material -> materialRecipeGenerator.register( "${material}_ore", pulverizeOre("c:${material}_ores", "minecraft:${material}") ) materialRecipeGenerator.register( material, pulverizeIngot("minecraft:$material", "indrev:${material}_dust", fileSuffix = "dust") ) materialTagGenerator.register("${material}_dust", createTag("indrev:${material}_dust")) materialTagGenerator.register("${material}_ore", createTag("minecraft:${material}_ore")) } DyeColor.values().forEach { val name = it.getName() materialRecipeGenerator.register("harden_${name}_concrete_powder", hardenConcretePowder(name)) } MachineRegistry.MAP.values.distinct().forEach { registry -> if (registry.upgradeable) { arrayOf(Tier.MK1, Tier.MK2, Tier.MK3).forEach { tier -> materialRecipeGenerator.register("${Registry.ITEM.getId(registry.block(Tier.MK1).asItem()).toString().replace("_mk1", "")}_${tier.id}_to_${tier.next().id}", upgradeMachineRecipe(registry, tier, tier.next())) } } } itemModelGenerator.register(IRItemRegistry.GAMER_AXE_ITEM, JsonFactory.nullFactory()) itemModelGenerator.register(IRBlockRegistry.DRILL_BOTTOM.asItem(), JsonFactory.nullFactory()) Registry.BLOCK.forEach { block -> val id = Registry.BLOCK.getId(block) if (id.namespace == namespace && id.path.contains("ore") && !id.path.contains("purified")) lootTableGenerator.register(block, LootTableGenerator.ORE_DROP(block)) } MetalSpriteRegistry.MATERIAL_PROVIDERS.forEach { (id, model) -> val itemId = Identifier(id.namespace, id.path) val item = Registry.ITEM.get(itemId) if (item != Items.AIR) { if (item is BlockItem) { blockModelGenerator.register(item.block, BlockModelGenerator.CUBE_ALL(item.block)) } if (id.toString().contains("sword")) metalSpriteGenerator.register(id, ImageFactory.simpleFactory0()(id)) else metalSpriteGenerator.register(id, ImageFactory.simpleFactory()(id)) val factory = if (model.type == MetalModel.TransformationType.HANDHELD) ItemModelGenerator.HANDHELD else ItemModelGenerator.DEFAULT_ITEM itemModelGenerator.register(item, factory(item)) } } } fun generate() { val lootTablesGenerated = lootTableGenerator.generate() IndustrialRevolution.LOGGER.info("Generated $lootTablesGenerated loot tables.") val itemModelsGenerated = itemModelGenerator.generate() IndustrialRevolution.LOGGER.info("Generated $itemModelsGenerated item models.") val blockModelsGenerated = blockModelGenerator.generate() IndustrialRevolution.LOGGER.info("Generated $blockModelsGenerated block models.") val recipesGenerated = materialRecipeGenerator.generate() IndustrialRevolution.LOGGER.info("Generated $recipesGenerated recipes.") val tagsGenerated = materialTagGenerator.generate() IndustrialRevolution.LOGGER.info("Generated $tagsGenerated tags.") val spritesGenerated = metalSpriteGenerator.generate() IndustrialRevolution.LOGGER.info("Generated $spritesGenerated sprites.") IndustrialRevolution.LOGGER.info("Generated ${lootTablesGenerated + itemModelsGenerated + blockModelsGenerated + recipesGenerated + tagsGenerated + spritesGenerated } files in total.") exitProcess(0) } operator fun Int.plus(boolean: Boolean): Int { return if (boolean) this + 1 else this } private fun pulverizeTagged(inputId: String, outputId: String, count: Int, time: Int, fileSuffix: String) : JsonFactory { return object : JsonFactory { override fun generate(): JsonObject { val json = JsonObject() json.addProperty("type", "indrev:pulverize") val ingredients = JsonObject() ingredients.addProperty("tag", inputId) json.add("ingredients", ingredients) val output = JsonObject() output.addProperty("item", outputId) output.addProperty("count", count) json.add("output", output) json.addProperty("processTime", time) return json } override fun getFileName(t: String, id: Identifier): String { var fileName = super.getFileName(t, id) val index = fileName.indexOf("_") if (index != -1) fileName = fileName.substring(0, index) return "pulverizer/${fileName}_$fileSuffix" } } } private fun pulverizeItem(inputId: String, outputId: String, count: Int, time: Int, fileSuffix: String) : JsonFactory { return object : JsonFactory { override fun generate(): JsonObject { val json = JsonObject() json.addProperty("type", "indrev:pulverize") val ingredients = JsonObject() ingredients.addProperty("item", inputId) json.add("ingredients", ingredients) val output = JsonObject() output.addProperty("item", outputId) output.addProperty("count", count) json.add("output", output) json.addProperty("processTime", time) return json } override fun getFileName(t: String, id: Identifier): String = "pulverizer/${super.getFileName(t, id)}_$fileSuffix" } } private fun pulverizeOre(inputId: String, outputId: String, count: Int = 2, time: Int = 200, fileSuffix: String = "dust_from_ore") : JsonFactory { return pulverizeTagged(inputId, outputId, count, time, fileSuffix) } private fun pulverizeIngot(inputId: String, outputId: String, count: Int = 1, time: Int = 150, fileSuffix: String = "dust_from_ingot") : JsonFactory { return pulverizeTagged(inputId, outputId, count, time, fileSuffix) } private fun rawOreIntoBlock(ore: String): JsonFactory { return object : JsonFactory { override fun generate(): JsonObject { val json = JsonObject() json.addProperty("type", "crafting_shaped") val pattern = JsonArray() repeat(3) { pattern.add("###") } json.add("pattern", pattern) val key = JsonObject() key.add("#", JsonObject().also { it.addProperty("item", "indrev:raw_${ore}") }) json.add("key", key) val result = JsonObject() result.addProperty("item", "indrev:raw_${ore}_block") json.add("result", result) return json } override fun getFileName(t: String, id: Identifier): String = "shaped/raw_${ore}_block" } } private fun rawOreBlockIntoRawItem(ore: String): JsonFactory { return object : JsonFactory { override fun generate(): JsonObject { val json = JsonObject() json.addProperty("type", "crafting_shapeless") val ingredients = JsonObject() ingredients.addProperty("item", "indrev:raw_${ore}_block") json.add("ingredients", ingredients) val output = JsonObject() output.addProperty("item", "indrev:raw_${ore}") output.addProperty("count", 9) json.add("result", output) return json } override fun getFileName(t: String, id: Identifier): String = "shapeless/raw_${ore}" } } private fun createTag(item: String): JsonFactory { return object : JsonFactory { override fun generate(): JsonObject { val obj = JsonObject() obj.addProperty("replace", false) val values = JsonArray() values.add(item) obj.add("values", values) return obj } override fun getFileName(t: String, id: Identifier): String = "${super.getFileName(t, id)}s" } } private fun hardenConcretePowder(color: String): JsonFactory { return object : JsonFactory { override fun generate(): JsonObject { val json = JsonObject() json.addProperty("type", "indrev:fluid_infuse") val ingredients = JsonObject() ingredients.addProperty("item", "minecraft:${color}_concrete_powder") json.add("ingredients", ingredients) val fluidInput = JsonObject() fluidInput.addProperty("fluid", "minecraft:water") fluidInput.addProperty("type", "mb") fluidInput.addProperty("count", 100) json.add("fluidInput", fluidInput) val output = JsonObject() output.addProperty("item", "minecraft:${color}_concrete") output.addProperty("count", 1) json.add("output", output) json.addProperty("processTime", 200) return json } override fun getFileName(t: String, id: Identifier): String = "fluid_infusing/harden_${color}_concrete_powder" } } private fun upgradeMachineRecipe(registry: MachineRegistry, from: Tier, to: Tier) : JsonFactory { return object : JsonFactory { override fun generate(): JsonObject { val json = JsonObject() json.addProperty("type", "crafting_shapeless") val ingredients = JsonArray() ingredients.add(JsonObject().also { it.addProperty("item", Registry.ITEM.getId(registry.block(from).asItem()).toString()) }) ingredients.add(JsonObject().also { it.addProperty("item", Registry.ITEM.getId(IRMachineUpgradeItem.fromTier(from)).toString()) }) json.add("ingredients", ingredients) val output = JsonObject() output.addProperty("item", Registry.ITEM.getId(registry.block(to).asItem()).toString()) json.add("result", output) return json } override fun getFileName(t: String, id: Identifier): String = "upgrade/${super.getFileName(t, id)}" } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/ImageFactory.kt ================================================ package me.steven.indrev.datagen import me.steven.indrev.datagen.utils.MetalModel import me.steven.indrev.datagen.utils.MetalSpriteRegistry import net.minecraft.client.MinecraftClient import net.minecraft.client.util.ModelIdentifier import net.minecraft.util.Identifier import java.awt.image.BufferedImage import java.io.File import javax.imageio.ImageIO interface ImageFactory : DataFactory { override val extension: String get() = "png" override fun write(file: File, t: BufferedImage) { ImageIO.write(t, "png", file) } companion object { fun simpleFactory(): (ModelIdentifier) -> ImageFactory = { string -> object : ImageFactory { override fun generate(): BufferedImage { val img = BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB) val baseGraphics = img.createGraphics() val model = MetalSpriteRegistry.MATERIAL_PROVIDERS[string] (model as MetalModel).holders.forEach { modelWithColor -> val type = if (model.type == MetalModel.TransformationType.BLOCK) "block" else "item" val resourceId = Identifier(modelWithColor.id.namespace, "textures/$type/${modelWithColor.id.path}.png") val inputStream = MinecraftClient.getInstance().resourceManager.getResource(resourceId).inputStream val overlay = ImageIO.read(inputStream) for (x in 0 until 16) { for (y in 0 until 16) { val overlayHex = overlay.getRGB(x, y) val a = ((overlayHex shr 24) and 255).toFloat() / 255f val r = ((overlayHex shr 16) and 255).toFloat() / 255f val g = ((overlayHex shr 8) and 255).toFloat() / 255f val b = ((overlayHex shr 0) and 255).toFloat() / 255f val hex = modelWithColor.color val oA = ((hex shr 24) and 255).toFloat() / 255f val oR = ((hex shr 16) and 255).toFloat() / 255f val oG = ((hex shr 8) and 255).toFloat() / 255f val oB = ((hex shr 0) and 255).toFloat() / 255f val nR = mix(r, r * oR, oA) val nG = mix(g, g * oG, oA) val nB = mix(b, b * oB, oA) val nHex = toHex(a, nR, nG, nB) overlay.setRGB(x, y, nHex) } } baseGraphics.drawImage(overlay, 0, 0, null) } baseGraphics.dispose() return img } } } fun simpleFactory0(): (ModelIdentifier) -> ImageFactory = { string -> object : ImageFactory { override fun generate(): BufferedImage { val img = BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB) val baseGraphics = img.createGraphics() val model = MetalSpriteRegistry.MATERIAL_PROVIDERS[string] (model as MetalModel).holders.forEach { modelWithColor -> val type = if (model.type == MetalModel.TransformationType.BLOCK) "block" else "item" val resourceId = Identifier(modelWithColor.id.namespace, "textures/$type/${modelWithColor.id.path}.png") val inputStream = MinecraftClient.getInstance().resourceManager.getResource(resourceId).inputStream val overlay = ImageIO.read(inputStream) for (x in 0 until 16) { for (y in 0 until 16) { val overlayHex = overlay.getRGB(x, y) val a = ((overlayHex shr 24) and 255).toFloat() / 255f val r = ((overlayHex shr 16) and 255).toFloat() / 255f val g = ((overlayHex shr 8) and 255).toFloat() / 255f val b = ((overlayHex shr 0) and 255).toFloat() / 255f val hex = modelWithColor.color val oA = ((hex shr 24) and 255).toFloat() / 255f val oR = ((hex shr 16) and 255).toFloat() / 255f val oG = ((hex shr 8) and 255).toFloat() / 255f val oB = ((hex shr 0) and 255).toFloat() / 255f val nR = mix(r, r * oR, oA) val nG = mix(g, g * oG, oA) val nB = mix(b, b * oB, oA) val nHex = toHex(a, nR, nG, nB) overlay.setRGB(x, y, nHex) } } if (modelWithColor.id.path == "tool_stick") baseGraphics.drawImage(overlay, 0, -1, null) else baseGraphics.drawImage(overlay, 0, 0, null) } baseGraphics.dispose() return img } } } private fun mix(x: Float, y: Float, a: Float): Float = x + (y - x) * a private fun toHex(a: Float, r: Float, g: Float, b: Float): Int = (a * 255).toInt() and 0xFF shl 24 or ((r * 255).toInt() and 0xFF shl 16) or ((g * 255).toInt() shl 8) or ((b * 255).toInt() shl 0) fun nullFactory(): ImageFactory = object : ImageFactory { override fun generate(): BufferedImage? = null } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/IndustrialRevolutionDatagen.kt ================================================ package me.steven.indrev.datagen import me.steven.indrev.IndustrialRevolution import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider import net.minecraft.block.Material import net.minecraft.tag.BlockTags import net.minecraft.util.registry.Registry class IndustrialRevolutionDatagen : DataGeneratorEntrypoint { override fun onInitializeDataGenerator(datagen: FabricDataGenerator) { datagen.addProvider(IndustrialRevolutionDatagen::IRBlockTagProvider) } class IRBlockTagProvider(datagen: FabricDataGenerator) : FabricTagProvider.BlockTagProvider(datagen) { override fun generateTags() { Registry.BLOCK.ids.forEach { id -> if (id.namespace == IndustrialRevolution.MOD_ID) { val block = Registry.BLOCK.get(id) if (block.defaultState.material == Material.METAL || block.defaultState.material == Material.STONE || block.defaultState.material == Material.GLASS) { getOrCreateTagBuilder(BlockTags.PICKAXE_MINEABLE).add(block) } if (block.defaultState.material == Material.WOOD) { getOrCreateTagBuilder(BlockTags.AXE_MINEABLE).add(block) } } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/JsonFactory.kt ================================================ package me.steven.indrev.datagen import com.google.gson.GsonBuilder import com.google.gson.JsonObject import java.io.File interface JsonFactory : DataFactory { override val extension: String get() = "json" override fun write(file: File, t: JsonObject?) { file.writeText(GsonBuilder().setPrettyPrinting().create().toJson(t)) } companion object { fun nullFactory(): JsonFactory = object : JsonFactory { override fun generate(): JsonObject? = null } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/generators/BlockModelGenerator.kt ================================================ package me.steven.indrev.datagen.generators import com.google.gson.JsonObject import me.steven.indrev.datagen.DataGenerator import me.steven.indrev.datagen.JsonFactory import net.minecraft.block.Block import net.minecraft.util.registry.Registry import java.io.File class BlockModelGenerator(val root: File, namespace: String, fallback: (Block) -> JsonFactory) : DataGenerator(File(root, "models/block"), namespace, fallback) { override fun generate(): Int { var count = 0 generators.forEach { (block, _) -> if (generate(Registry.BLOCK.getId(block), block)) count++ } return count } companion object { val CUBE_ALL: (Block) -> JsonFactory = { item -> object : JsonFactory { override fun generate(): JsonObject { val id = Registry.BLOCK.getId(item) val obj = JsonObject() obj.addProperty("parent", "block/cube_all") val texturesObj = JsonObject() texturesObj.addProperty("all", "${id.namespace}:block/${id.path}") obj.add("textures", texturesObj) return obj } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/generators/ItemModelGenerator.kt ================================================ package me.steven.indrev.datagen.generators import com.google.gson.JsonObject import me.steven.indrev.datagen.DataGenerator import me.steven.indrev.datagen.JsonFactory import net.minecraft.item.BlockItem import net.minecraft.item.Item import net.minecraft.util.registry.Registry import java.io.File class ItemModelGenerator(val root: File, namespace: String, fallback: (Item) -> JsonFactory) : DataGenerator(File(root, "models/item"), namespace, fallback) { override fun generate(): Int { var count = 0 Registry.ITEM.ids.filter { id -> id.namespace == namespace }.forEach { val item = Registry.ITEM.get(it) if (item.asItem() != null && generate(it, item)) { count++ } } return count } companion object { val DEFAULT_ITEM: (Item) -> JsonFactory = { item -> object : JsonFactory { override fun generate(): JsonObject { val id = Registry.ITEM.getId(item) val obj = JsonObject() if (item is BlockItem) { obj.addProperty("parent", "${id.namespace}:block/${id.path}") } else { obj.addProperty("parent", "item/generated") val texturesObj = JsonObject() texturesObj.addProperty("layer0", "${id.namespace}:item/${id.path}") obj.add("textures", texturesObj) } return obj } } } val HANDHELD: (Item) -> JsonFactory = { item -> object : JsonFactory { override fun generate(): JsonObject { val id = Registry.ITEM.getId(item) val obj = JsonObject() obj.addProperty("parent", "item/handheld") val texturesObj = JsonObject() texturesObj.addProperty("layer0", "${id.namespace}:item/${id.path}") obj.add("textures", texturesObj) return obj } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/generators/LootTableGenerator.kt ================================================ package me.steven.indrev.datagen.generators import com.google.gson.JsonObject import me.steven.indrev.datagen.DataGenerator import me.steven.indrev.datagen.JsonFactory import net.minecraft.block.Block import net.minecraft.enchantment.Enchantments import net.minecraft.item.Items import net.minecraft.loot.LootManager import net.minecraft.loot.LootPool import net.minecraft.loot.LootTable import net.minecraft.loot.condition.MatchToolLootCondition import net.minecraft.loot.condition.SurvivesExplosionLootCondition import net.minecraft.loot.context.LootContextTypes import net.minecraft.loot.entry.AlternativeEntry import net.minecraft.loot.entry.ItemEntry import net.minecraft.loot.function.ApplyBonusLootFunction import net.minecraft.loot.function.ExplosionDecayLootFunction import net.minecraft.loot.provider.number.ConstantLootNumberProvider import net.minecraft.predicate.NumberRange import net.minecraft.predicate.item.EnchantmentPredicate import net.minecraft.predicate.item.ItemPredicate import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import java.io.File class LootTableGenerator(val root: File, namespace: String, fallback: (Block) -> JsonFactory) : DataGenerator(File(root, "loot_tables/blocks"), namespace, fallback) { override fun generate(): Int { var count = 0 Registry.BLOCK.ids.filter { id -> id.namespace == namespace }.forEach { val block = Registry.BLOCK.get(it) if (block.asItem() != null && block.asItem() != Items.AIR && generate(it, block)) { count++ } } return count } companion object { val SELF_DROP: (Block) -> JsonFactory = { block -> object : JsonFactory { override fun generate(): JsonObject? { val lootTable = LootTable.builder() .pool( LootPool.builder().rolls(ConstantLootNumberProvider.create(1f)).with(ItemEntry.builder(block)) .conditionally(SurvivesExplosionLootCondition.builder()) ) .type(LootContextTypes.BLOCK) .build() return LootManager.toJson(lootTable).asJsonObject } } } val ORE_DROP: (Block) -> JsonFactory = { block -> object : JsonFactory { override fun generate(): JsonObject? { val blockId = Registry.BLOCK.getId(block) val rawOreId = "raw_${blockId.path.replace("deepslate_", "").replace("_ore", "")}" val rawOre = Registry.ITEM.get(Identifier(blockId.namespace, rawOreId)) val lootTable = LootTable.builder() .pool( LootPool.builder() .bonusRolls(ConstantLootNumberProvider.create(0f)) .rolls(ConstantLootNumberProvider.create(1f)) .with( AlternativeEntry.builder( ItemEntry.builder(block) .conditionally( MatchToolLootCondition.builder( ItemPredicate.Builder.create().enchantment( EnchantmentPredicate( Enchantments.SILK_TOUCH, NumberRange.IntRange.atLeast(1) ) ) ) ), ItemEntry.builder(rawOre) .apply(ApplyBonusLootFunction.oreDrops(Enchantments.FORTUNE)) .apply(ExplosionDecayLootFunction.builder()) ) ) ) .type(LootContextTypes.BLOCK) .build() return LootManager.toJson(lootTable).asJsonObject } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/generators/MaterialRecipeGenerator.kt ================================================ package me.steven.indrev.datagen.generators import com.google.gson.JsonObject import me.steven.indrev.datagen.DataGenerator import me.steven.indrev.datagen.JsonFactory import net.minecraft.util.Identifier import java.io.File class MaterialRecipeGenerator( val root: File, namespace: String, fallback: (String) -> JsonFactory ) : DataGenerator(File(root, "recipes"), namespace, fallback) { fun register(id: Identifier, factory: JsonFactory) { generators[id.toString()] = factory } override fun generate(): Int { var count = 0 generators.forEach { (tag, _) -> if (generate(Identifier(tag), tag)) count++ } return count } companion object } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/generators/MaterialTagGenerator.kt ================================================ package me.steven.indrev.datagen.generators import com.google.gson.JsonArray import com.google.gson.JsonObject import me.steven.indrev.datagen.DataGenerator import me.steven.indrev.datagen.JsonFactory import net.minecraft.util.Identifier import java.io.File class MaterialTagGenerator(val root: File, namespace: String, fallback: (String) -> JsonFactory) : DataGenerator(File(root, "tags/items"), namespace, fallback) { override fun generate(): Int { var count = 0 generators.forEach { (id, _) -> if (generate(Identifier(id), id)) count++ } return count } companion object { val DEFAULT_ITEM: (String) -> JsonFactory = { tag -> object : JsonFactory { override fun generate(): JsonObject { val obj = JsonObject() obj.addProperty("replace", false) val values = JsonArray() values.add(tag) obj.add("values", values) return obj } override fun getFileName(t: String, id: Identifier): String = "${super.getFileName(t, id)}s" } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/generators/MetalSpriteGenerator.kt ================================================ package me.steven.indrev.datagen.generators import me.steven.indrev.datagen.DataGenerator import me.steven.indrev.datagen.ImageFactory import net.minecraft.util.Identifier import java.awt.image.BufferedImage import java.io.File class MetalSpriteGenerator( val root: File, namespace: String, fallback: (Identifier) -> ImageFactory ) : DataGenerator(File(root, "textures"), namespace, fallback) { override fun generate(): Int { var count = 0 generators.forEach { (tag, _) -> if (generate(tag, tag)) count++ } return count } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/utils/MaterialColors.kt ================================================ package me.steven.indrev.datagen.utils const val LEAD_BASE = 0xFF575d61 const val LEAD_HIGHLIGHT = 0xFF77859a //const val LEAD_SHADOW = 0xFF343539 const val SILVER_BASE = 0xFFc6e9e3 const val SILVER_HIGHLIGHT = 0xFFd9f2e3 //const val SILVER_SHADOW = 0xFF989890 const val COPPER_BASE = 0xFFe77c56 const val COPPER_HIGHLIGHT = 0xFFfc9982 //const val COPPER_SHADOW = 0xFFd1613f const val TUNGSTEN_BASE = 0xFF686b5b const val TUNGSTEN_HIGHLIGHT = 0xFF838961 //const val TUNGSTEN_SHADOW = 0xFF58564a const val BRONZE_BASE = 0xFF914e3b const val BRONZE_HIGHLIGHT = 0xFFa86234 //const val BRONZE_SHADOW = 0xFF815735 const val ELECTRUM_BASE = 0xFFecd36f const val ELECTRUM_HIGHLIGHT = 0xFFf5e875 //const val ELECTRUM_SHADOW = 0xFFc17a3d const val STEEL_BASE = 0xFF3b3b3b const val STEEL_HIGHLIGHT = 0xFF444341 const val NIKOLITE_BASE = 0xFF008f8c const val NIKOLITE_HIGHLIGHT = 0xFF00ad88 const val ENRICHED_NIKOLITE_BASE = 0xFF5300de const val ENRICHED_NIKOLITE_HIGHLIGHT = 0xFF3740d4 const val IRON_BASE = 0xFFfdfdfd const val IRON_HIGHLIGHT = 0xFFffffff const val TIN_BASE = 0xFFc9c9c9 const val TIN_HIGHLIGHT = 0xFFc1c7c7 const val TIN_OUTLINE = 0xFF9ca1aa const val DIAMOND_BASE = 0xFF4aedd9 const val DIAMOND_HIGHLIGHT = 0xFFa1fbe8 const val DIAMOND_OUTLINE = 0xFF11727a const val SULFUR_BASE = 0xFFf5da68 const val SULFUR_HIGHLIGHT = 0xFFffffff const val SULFUR_OUTLINE = 0xFFbda547 const val COAL_BASE = 0xFF1f1721 const val COAL_HIGHLIGHT = 0xFF261e24 const val NETHERITE_SCRAP_BASE = 0xFF603a32 const val NETHERITE_SCRAP_HIGHLIGHT = 0xFF7e6059 const val NETHERITE_SCRAP_OUTLINE = 0xFF24110b ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/utils/MetalModel.kt ================================================ package me.steven.indrev.datagen.utils import me.steven.indrev.utils.identifier import net.minecraft.util.Identifier class MetalModel private constructor(val holders: Array, val type: TransformationType) { class Builder { private val holders: MutableList = mutableListOf() private var transformationType: TransformationType = TransformationType.AUTO fun with(id: Identifier, color: Long): Builder { holders.add(SpriteColorHolder(id, color.toInt())) return this } fun ingotBase(color: Long): Builder { return with(identifier("ingot_base"), color) } fun ingotOutline(color: Long): Builder { return with(identifier("ingot_outline"), color) } fun ingotHighlight(color: Long): Builder { return with(identifier("ingot_highlight"), color) } fun plateBase(color: Long): Builder { return with(identifier("plate_base"), color) } fun plateOutline(color: Long): Builder { return with(identifier("plate_outline"), color) } fun plateHighlight(color: Long): Builder { return with(identifier("plate_highlight"), color) } fun pickaxeBase(color: Long): Builder { return with(identifier("pickaxe_base"), color) } fun pickaxeOutline(color: Long): Builder { return with(identifier("pickaxe_outline"), color) } fun pickaxeHighlight(color: Long): Builder { return with(identifier("pickaxe_highlight"), color) } fun axeBase(color: Long): Builder { return with(identifier("axe_base"), color) } fun axeOutline(color: Long): Builder { return with(identifier("axe_outline"), color) } fun axeHighlight(color: Long): Builder { return with(identifier("axe_highlight"), color) } fun helmetBase(color: Long): Builder { return with(identifier("helmet_base"), color) } fun helmetOutline(color: Long): Builder { return with(identifier("helmet_outline"), color) } fun helmetHighlight(color: Long): Builder { return with(identifier("helmet_highlight"), color) } fun dustBase(color: Long): Builder { return with(identifier("dust_base"), color) } fun dustOutline(color: Long): Builder { return with(identifier("dust_outline"), color) } fun dustHighlight(color: Long): Builder { return with(identifier("dust_highlight"), color) } fun chestplateBase(color: Long): Builder { return with(identifier("chestplate_base"), color) } fun chestplateOutline(color: Long): Builder { return with(identifier("chestplate_outline"), color) } fun chestplateHighlight(color: Long): Builder { return with(identifier("chestplate_highlight"), color) } fun bootsBase(color: Long): Builder { return with(identifier("boots_base"), color) } fun bootsOutline(color: Long): Builder { return with(identifier("boots_outline"), color) } fun bootsHighlight(color: Long): Builder { return with(identifier("boots_highlight"), color) } fun hoeBase(color: Long): Builder { return with(identifier("hoe_base"), color) } fun hoeOutline(color: Long): Builder { return with(identifier("hoe_outline"), color) } fun hoeHighlight(color: Long): Builder { return with(identifier("hoe_highlight"), color) } fun leggingsBase(color: Long): Builder { return with(identifier("leggings_base"), color) } fun leggingsOutline(color: Long): Builder { return with(identifier("leggings_outline"), color) } fun leggingsHighlight(color: Long): Builder { return with(identifier("leggings_highlight"), color) } fun nuggetBase(color: Long): Builder { return with(identifier("nugget_base"), color) } fun nuggetOutline(color: Long): Builder { return with(identifier("nugget_outline"), color) } fun nuggetHighlight(color: Long): Builder { return with(identifier("nugget_highlight"), color) } fun oreBase(color: Long): Builder { return with(identifier("ore_base"), color) } fun oreHighlight(color: Long): Builder { return with(identifier("ore_highlight"), color) } fun blockBase(color: Long): Builder { return with(identifier("block_base"), color) } fun blockOutline(color: Long): Builder { return with(identifier("block_outline"), color) } fun blockHighlight(color: Long): Builder { return with(identifier("block_highlight"), color) } fun purifiedOreBase(color: Long): Builder { return with(identifier("purified_ore_base"), color) } fun purifiedOreOutline(color: Long): Builder { return with(identifier("purified_ore_outline"), color) } fun purifiedOreHighlight(color: Long): Builder { return with(identifier("purified_ore_highlight"), color) } fun chunkBase(color: Long): Builder { return with(identifier("chunk_base"), color) } fun chunkOutline(color: Long): Builder { return with(identifier("chunk_outline"), color) } fun chunkHighlight(color: Long): Builder { return with(identifier("chunk_highlight"), color) } fun shovelBase(color: Long): Builder { return with(identifier("shovel_base"), color) } fun shovelOutline(color: Long): Builder { return with(identifier("shovel_outline"), color) } fun shovelHighlight(color: Long): Builder { return with(identifier("shovel_highlight"), color) } fun swordBase(color: Long): Builder { return with(identifier("sword_base"), color) } fun swordOutline(color: Long): Builder { return with(identifier("sword_outline"), color) } fun swordHighlight(color: Long): Builder { return with(identifier("sword_highlight"), color) } fun moltenBucketBase(color: Long): Builder { return with(identifier("molten_bucket_base"), color) } fun moltenBucketHighlight(color: Long): Builder { return with(identifier("molten_bucket_highlight"), color) } fun rawOre(color: Long): Builder { return with(identifier("raw_ore"), color) } fun rawOreBlock(color: Long): Builder { transformationType = TransformationType.BLOCK return with(identifier("raw_ore_block"), color) } fun moltenBucketOutline(color: Long): Builder { return with(identifier("molten_bucket_outline"), color) } fun toolStick(): Builder { transformationType = TransformationType.HANDHELD return with(identifier("tool_stick"), -1) } fun bucket(): Builder { transformationType = TransformationType.HANDHELD return with(Identifier("bucket"), -1) } fun ore(): Builder { transformationType = TransformationType.BLOCK return with(Identifier("stone"), -1) } fun deepslateOre(): Builder { transformationType = TransformationType.BLOCK return with(Identifier("deepslate"), -1) } fun block(): Builder { transformationType = TransformationType.BLOCK return this } fun build() = MetalModel(holders.toTypedArray(), transformationType) } enum class TransformationType { AUTO, HANDHELD, BLOCK; val variant = if (this.toString() == "BLOCK") "" else "inventory" } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/utils/MetalSpriteRegistry.kt ================================================ package me.steven.indrev.datagen.utils import me.steven.indrev.utils.identifier import net.minecraft.client.util.ModelIdentifier object MetalSpriteRegistry { val MATERIAL_PROVIDERS: HashMap = hashMapOf() init { put("lead_ingot", MetalModel.Builder().ingotBase(LEAD_BASE).ingotHighlight(LEAD_HIGHLIGHT).build()) put("lead_plate", MetalModel.Builder().plateBase(LEAD_BASE).plateHighlight(LEAD_HIGHLIGHT).build()) put("lead_pickaxe", MetalModel.Builder().toolStick().pickaxeBase(LEAD_BASE).pickaxeHighlight(LEAD_HIGHLIGHT).build()) put("lead_axe", MetalModel.Builder().toolStick().axeBase(LEAD_BASE).axeHighlight(LEAD_HIGHLIGHT).build()) put("lead_sword", MetalModel.Builder().toolStick().swordBase(LEAD_BASE).swordHighlight(LEAD_HIGHLIGHT).build()) put("lead_hoe", MetalModel.Builder().toolStick().hoeBase(LEAD_BASE).hoeHighlight(LEAD_HIGHLIGHT).build()) put("lead_shovel", MetalModel.Builder().toolStick().shovelBase(LEAD_BASE).shovelHighlight(LEAD_HIGHLIGHT).build()) put("lead_helmet", MetalModel.Builder().helmetBase(LEAD_BASE).helmetHighlight(LEAD_HIGHLIGHT).build()) put("lead_dust", MetalModel.Builder().dustBase(LEAD_BASE).dustHighlight(LEAD_HIGHLIGHT).build()) put("lead_chestplate", MetalModel.Builder().chestplateBase(LEAD_BASE).chestplateHighlight(LEAD_HIGHLIGHT).build()) put("lead_boots", MetalModel.Builder().bootsBase(LEAD_BASE).bootsHighlight(LEAD_HIGHLIGHT).build()) put("lead_leggings", MetalModel.Builder().leggingsBase(LEAD_BASE).leggingsHighlight(LEAD_HIGHLIGHT).build()) put("lead_nugget", MetalModel.Builder().nuggetBase(LEAD_BASE).nuggetHighlight(LEAD_HIGHLIGHT).build()) putBlock("lead_ore", MetalModel.Builder().ore().oreBase(LEAD_BASE).oreHighlight(LEAD_HIGHLIGHT).build()) putBlock("deepslate_lead_ore", MetalModel.Builder().deepslateOre().oreBase(LEAD_BASE).oreHighlight(LEAD_HIGHLIGHT).build()) put("raw_lead", MetalModel.Builder().rawOre(LEAD_BASE).build()) putBlock("raw_lead_block", MetalModel.Builder().rawOreBlock(LEAD_BASE).build()) putBlock("lead_block", MetalModel.Builder().block().blockBase(LEAD_BASE).build()) put("lead_purified_ore", MetalModel.Builder().purifiedOreBase(LEAD_BASE).purifiedOreHighlight(LEAD_HIGHLIGHT).build()) put("lead_chunk", MetalModel.Builder().chunkBase(LEAD_BASE).chunkHighlight(LEAD_HIGHLIGHT).build()) put("molten_lead_bucket", MetalModel.Builder().bucket().moltenBucketBase(LEAD_BASE).moltenBucketHighlight(LEAD_HIGHLIGHT).build()) put("silver_ingot", MetalModel.Builder().ingotBase(SILVER_BASE).ingotHighlight(SILVER_HIGHLIGHT).build()) put("silver_plate", MetalModel.Builder().plateBase(SILVER_BASE).plateHighlight(SILVER_HIGHLIGHT).build()) put("silver_pickaxe", MetalModel.Builder().toolStick().pickaxeBase(SILVER_BASE).pickaxeHighlight( SILVER_HIGHLIGHT ).build()) put("silver_axe", MetalModel.Builder().toolStick().axeBase(SILVER_BASE).axeHighlight( SILVER_HIGHLIGHT ).build()) put("silver_sword", MetalModel.Builder().toolStick().swordBase(SILVER_BASE).swordHighlight( SILVER_HIGHLIGHT ).build()) put("silver_hoe", MetalModel.Builder().toolStick().hoeBase(SILVER_BASE).hoeHighlight( SILVER_HIGHLIGHT ).build()) put("silver_shovel", MetalModel.Builder().toolStick().shovelBase(SILVER_BASE).shovelHighlight( SILVER_HIGHLIGHT ).build()) put("silver_helmet", MetalModel.Builder().helmetBase(SILVER_BASE).helmetHighlight(SILVER_HIGHLIGHT).build()) put("silver_dust", MetalModel.Builder().dustBase(SILVER_BASE).dustHighlight(SILVER_HIGHLIGHT).build()) put("silver_chestplate", MetalModel.Builder().chestplateBase(SILVER_BASE).chestplateHighlight( SILVER_HIGHLIGHT ).build()) put("silver_boots", MetalModel.Builder().bootsBase(SILVER_BASE).bootsHighlight(SILVER_HIGHLIGHT).build()) put("silver_leggings", MetalModel.Builder().leggingsBase(SILVER_BASE).leggingsHighlight( SILVER_HIGHLIGHT ).build()) put("silver_nugget", MetalModel.Builder().nuggetBase(SILVER_BASE).nuggetHighlight(SILVER_HIGHLIGHT).build()) putBlock("silver_ore", MetalModel.Builder().ore().oreBase(SILVER_BASE).oreHighlight(SILVER_HIGHLIGHT).build()) putBlock("deepslate_silver_ore", MetalModel.Builder().deepslateOre().oreBase(SILVER_BASE).oreHighlight(SILVER_HIGHLIGHT).build()) put("raw_silver", MetalModel.Builder().rawOre(SILVER_BASE).build()) putBlock("raw_silver_block", MetalModel.Builder().rawOreBlock(SILVER_BASE).build()) putBlock("silver_block", MetalModel.Builder().block().blockBase(SILVER_BASE).build()) put("silver_purified_ore", MetalModel.Builder().purifiedOreBase(SILVER_BASE).purifiedOreHighlight( SILVER_HIGHLIGHT ).build()) put("silver_chunk", MetalModel.Builder().chunkBase(SILVER_BASE).chunkHighlight(SILVER_HIGHLIGHT).build()) put("molten_silver_bucket", MetalModel.Builder().bucket().moltenBucketBase(SILVER_BASE).moltenBucketHighlight(SILVER_HIGHLIGHT).build()) put("copper_ingot", MetalModel.Builder().ingotBase(COPPER_BASE).ingotHighlight(COPPER_HIGHLIGHT).build()) put("copper_plate", MetalModel.Builder().plateBase(COPPER_BASE).plateHighlight(COPPER_HIGHLIGHT).build()) put("copper_pickaxe", MetalModel.Builder().toolStick().pickaxeBase(COPPER_BASE).pickaxeHighlight( COPPER_HIGHLIGHT ).build()) put("copper_axe", MetalModel.Builder().toolStick().axeBase(COPPER_BASE).axeHighlight( COPPER_HIGHLIGHT ).build()) put("copper_sword", MetalModel.Builder().toolStick().swordBase(COPPER_BASE).swordHighlight( COPPER_HIGHLIGHT ).build()) put("copper_hoe", MetalModel.Builder().toolStick().hoeBase(COPPER_BASE).hoeHighlight( COPPER_HIGHLIGHT ).build()) put("copper_shovel", MetalModel.Builder().toolStick().shovelBase(COPPER_BASE).shovelHighlight( COPPER_HIGHLIGHT ).build()) put("copper_helmet", MetalModel.Builder().helmetBase(COPPER_BASE).helmetHighlight(COPPER_HIGHLIGHT).build()) put("copper_dust", MetalModel.Builder().dustBase(COPPER_BASE).dustHighlight(COPPER_HIGHLIGHT).build()) put("copper_chestplate", MetalModel.Builder().chestplateBase(COPPER_BASE).chestplateHighlight( COPPER_HIGHLIGHT ).build()) put("copper_boots", MetalModel.Builder().bootsBase(COPPER_BASE).bootsHighlight(COPPER_HIGHLIGHT).build()) put("copper_leggings", MetalModel.Builder().leggingsBase(COPPER_BASE).leggingsHighlight( COPPER_HIGHLIGHT ).build()) put("copper_nugget", MetalModel.Builder().nuggetBase(COPPER_BASE).nuggetHighlight(COPPER_HIGHLIGHT).build()) putBlock("copper_ore", MetalModel.Builder().ore().oreBase(COPPER_BASE).oreHighlight(COPPER_HIGHLIGHT).build()) putBlock("copper_block", MetalModel.Builder().block().blockBase(COPPER_BASE).build()) put("copper_purified_ore", MetalModel.Builder().purifiedOreBase(COPPER_BASE).purifiedOreHighlight( COPPER_HIGHLIGHT ).build()) put("copper_chunk", MetalModel.Builder().chunkBase(COPPER_BASE).chunkHighlight(COPPER_HIGHLIGHT).build()) put("molten_copper_bucket", MetalModel.Builder().bucket().moltenBucketBase(COPPER_BASE).moltenBucketHighlight(COPPER_HIGHLIGHT).build()) put("tungsten_ingot", MetalModel.Builder().ingotBase(TUNGSTEN_BASE).ingotHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_plate", MetalModel.Builder().plateBase(TUNGSTEN_BASE).plateHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_pickaxe", MetalModel.Builder().toolStick().pickaxeBase(TUNGSTEN_BASE).pickaxeHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_axe", MetalModel.Builder().toolStick().axeBase(TUNGSTEN_BASE).axeHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_sword", MetalModel.Builder().toolStick().swordBase(TUNGSTEN_BASE).swordHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_hoe", MetalModel.Builder().toolStick().hoeBase(TUNGSTEN_BASE).hoeHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_shovel", MetalModel.Builder().toolStick().shovelBase(TUNGSTEN_BASE).shovelHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_helmet", MetalModel.Builder().helmetBase(TUNGSTEN_BASE).helmetHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_dust", MetalModel.Builder().dustBase(TUNGSTEN_BASE).dustHighlight(TUNGSTEN_HIGHLIGHT).build()) put("tungsten_chestplate", MetalModel.Builder().chestplateBase(TUNGSTEN_BASE).chestplateHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_boots", MetalModel.Builder().bootsBase(TUNGSTEN_BASE).bootsHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_leggings", MetalModel.Builder().leggingsBase(TUNGSTEN_BASE).leggingsHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_nugget", MetalModel.Builder().nuggetBase(TUNGSTEN_BASE).nuggetHighlight( TUNGSTEN_HIGHLIGHT ).build()) putBlock("deepslate_tungsten_ore", MetalModel.Builder().deepslateOre().oreBase(TUNGSTEN_BASE).oreHighlight( TUNGSTEN_HIGHLIGHT ).build()) putBlock("tungsten_ore", MetalModel.Builder().ore().oreBase(TUNGSTEN_BASE).oreHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("raw_tungsten", MetalModel.Builder().rawOre(TUNGSTEN_BASE).build()) putBlock("raw_tungsten_block", MetalModel.Builder().rawOreBlock(TUNGSTEN_BASE).build()) putBlock("tungsten_block", MetalModel.Builder().block().blockBase(TUNGSTEN_BASE).build()) put("tungsten_purified_ore", MetalModel.Builder().purifiedOreBase(TUNGSTEN_BASE).purifiedOreHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("tungsten_chunk", MetalModel.Builder().chunkBase(TUNGSTEN_BASE).chunkHighlight( TUNGSTEN_HIGHLIGHT ).build()) put("bronze_ingot", MetalModel.Builder().ingotBase(BRONZE_BASE).ingotHighlight(BRONZE_HIGHLIGHT).build()) put("bronze_plate", MetalModel.Builder().plateBase(BRONZE_BASE).plateHighlight(BRONZE_HIGHLIGHT).build()) put("bronze_pickaxe", MetalModel.Builder().toolStick().pickaxeBase(BRONZE_BASE).pickaxeHighlight( BRONZE_HIGHLIGHT ).build()) put("bronze_axe", MetalModel.Builder().toolStick().axeBase(BRONZE_BASE).axeHighlight( BRONZE_HIGHLIGHT ).build()) put("bronze_sword", MetalModel.Builder().toolStick().swordBase(BRONZE_BASE).swordHighlight( BRONZE_HIGHLIGHT ).build()) put("bronze_hoe", MetalModel.Builder().toolStick().hoeBase(BRONZE_BASE).hoeHighlight( BRONZE_HIGHLIGHT ).build()) put("bronze_shovel", MetalModel.Builder().toolStick().shovelBase(BRONZE_BASE).shovelHighlight( BRONZE_HIGHLIGHT ).build()) put("bronze_helmet", MetalModel.Builder().helmetBase(BRONZE_BASE).helmetHighlight(BRONZE_HIGHLIGHT).build()) put("bronze_dust", MetalModel.Builder().dustBase(BRONZE_BASE).dustHighlight(BRONZE_HIGHLIGHT).build()) put("bronze_chestplate", MetalModel.Builder().chestplateBase(BRONZE_BASE).chestplateHighlight( BRONZE_HIGHLIGHT ).build()) put("bronze_boots", MetalModel.Builder().bootsBase(BRONZE_BASE).bootsHighlight(BRONZE_HIGHLIGHT).build()) put("bronze_leggings", MetalModel.Builder().leggingsBase(BRONZE_BASE).leggingsHighlight( BRONZE_HIGHLIGHT ).build()) put("bronze_nugget", MetalModel.Builder().nuggetBase(BRONZE_BASE).nuggetHighlight(BRONZE_HIGHLIGHT).build()) putBlock("bronze_ore", MetalModel.Builder().ore().oreBase(BRONZE_BASE).oreHighlight(BRONZE_HIGHLIGHT).build()) putBlock("bronze_block", MetalModel.Builder().block().blockBase(BRONZE_BASE).build()) put("bronze_purified_ore", MetalModel.Builder().purifiedOreBase(BRONZE_BASE).purifiedOreHighlight( BRONZE_HIGHLIGHT ).build()) put("bronze_chunk", MetalModel.Builder().chunkBase(BRONZE_BASE).chunkHighlight(BRONZE_HIGHLIGHT).build()) put("electrum_ingot", MetalModel.Builder().ingotBase(ELECTRUM_BASE).ingotHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_plate", MetalModel.Builder().plateBase(ELECTRUM_BASE).plateHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_pickaxe", MetalModel.Builder().toolStick().pickaxeBase(ELECTRUM_BASE).pickaxeHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_axe", MetalModel.Builder().toolStick().axeBase(ELECTRUM_BASE).axeHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_sword", MetalModel.Builder().toolStick().swordBase(ELECTRUM_BASE).swordHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_hoe", MetalModel.Builder().toolStick().hoeBase(ELECTRUM_BASE).hoeHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_shovel", MetalModel.Builder().toolStick().shovelBase(ELECTRUM_BASE).shovelHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_helmet", MetalModel.Builder().helmetBase(ELECTRUM_BASE).helmetHighlight(ELECTRUM_HIGHLIGHT).build()) put("electrum_dust", MetalModel.Builder().dustBase(ELECTRUM_BASE).dustHighlight(ELECTRUM_HIGHLIGHT).build()) put("electrum_chestplate", MetalModel.Builder().chestplateBase(ELECTRUM_BASE).chestplateHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_boots", MetalModel.Builder().bootsBase(ELECTRUM_BASE).bootsHighlight(ELECTRUM_HIGHLIGHT).build()) put("electrum_leggings", MetalModel.Builder().leggingsBase(ELECTRUM_BASE).leggingsHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_nugget", MetalModel.Builder().nuggetBase(ELECTRUM_BASE).nuggetHighlight(ELECTRUM_HIGHLIGHT).build()) putBlock("electrum_ore", MetalModel.Builder().ore().oreBase(ELECTRUM_BASE).oreHighlight(ELECTRUM_HIGHLIGHT).build()) putBlock("electrum_block", MetalModel.Builder().block().blockBase(ELECTRUM_BASE).build()) put("electrum_purified_ore", MetalModel.Builder().purifiedOreBase(ELECTRUM_BASE).purifiedOreHighlight( ELECTRUM_HIGHLIGHT ).build()) put("electrum_chunk", MetalModel.Builder().chunkBase(ELECTRUM_BASE).chunkHighlight(ELECTRUM_HIGHLIGHT).build()) put("steel_ingot", MetalModel.Builder().ingotBase(STEEL_BASE).ingotHighlight(STEEL_HIGHLIGHT).build()) put("steel_plate", MetalModel.Builder().plateBase(STEEL_BASE).plateHighlight(STEEL_HIGHLIGHT).build()) put("steel_pickaxe", MetalModel.Builder().toolStick().pickaxeBase(STEEL_BASE).pickaxeHighlight( STEEL_HIGHLIGHT ).build()) put("steel_axe", MetalModel.Builder().toolStick().axeBase(STEEL_BASE).axeHighlight(STEEL_HIGHLIGHT).build()) put("steel_sword", MetalModel.Builder().toolStick().swordBase(STEEL_BASE).swordHighlight(STEEL_HIGHLIGHT).build()) put("steel_hoe", MetalModel.Builder().toolStick().hoeBase(STEEL_BASE).hoeHighlight(STEEL_HIGHLIGHT).build()) put("steel_shovel", MetalModel.Builder().toolStick().shovelBase(STEEL_BASE).shovelHighlight(STEEL_HIGHLIGHT).build()) put("steel_helmet", MetalModel.Builder().helmetBase(STEEL_BASE).helmetHighlight(STEEL_HIGHLIGHT).build()) put("steel_dust", MetalModel.Builder().dustBase(STEEL_BASE).dustHighlight(STEEL_HIGHLIGHT).build()) put("steel_chestplate", MetalModel.Builder().chestplateBase(STEEL_BASE).chestplateHighlight(STEEL_HIGHLIGHT).build()) put("steel_boots", MetalModel.Builder().bootsBase(STEEL_BASE).bootsHighlight(STEEL_HIGHLIGHT).build()) put("steel_leggings", MetalModel.Builder().leggingsBase(STEEL_BASE).leggingsHighlight(STEEL_HIGHLIGHT).build()) put("steel_nugget", MetalModel.Builder().nuggetBase(STEEL_BASE).nuggetHighlight(STEEL_HIGHLIGHT).build()) putBlock("steel_ore", MetalModel.Builder().ore().oreBase(STEEL_BASE).oreHighlight(STEEL_HIGHLIGHT).build()) putBlock("steel_block", MetalModel.Builder().block().blockBase(STEEL_BASE).build()) put("steel_purified_ore", MetalModel.Builder().purifiedOreBase(STEEL_BASE).purifiedOreHighlight( STEEL_HIGHLIGHT ).build()) put("steel_chunk", MetalModel.Builder().chunkBase(STEEL_BASE).chunkHighlight(STEEL_HIGHLIGHT).build()) put("tin_ingot", MetalModel.Builder().ingotBase(TIN_BASE).ingotHighlight(TIN_HIGHLIGHT).build()) put("tin_plate", MetalModel.Builder().plateBase(TIN_BASE).plateHighlight(TIN_HIGHLIGHT).build()) put("tin_pickaxe", MetalModel.Builder().toolStick().pickaxeBase(TIN_BASE).pickaxeHighlight(TIN_HIGHLIGHT).build()) put("tin_axe", MetalModel.Builder().toolStick().axeBase(TIN_BASE).axeHighlight(TIN_HIGHLIGHT).build()) put("tin_sword", MetalModel.Builder().toolStick().swordBase(TIN_BASE).swordHighlight(TIN_HIGHLIGHT).build()) put("tin_hoe", MetalModel.Builder().toolStick().hoeBase(TIN_BASE).hoeHighlight(TIN_HIGHLIGHT).build()) put("tin_shovel", MetalModel.Builder().toolStick().shovelBase(TIN_BASE).shovelHighlight(TIN_HIGHLIGHT).build()) put("tin_helmet", MetalModel.Builder().helmetBase(TIN_BASE).helmetHighlight(TIN_HIGHLIGHT).build()) put("tin_dust", MetalModel.Builder().dustBase(TIN_BASE).dustHighlight(TIN_HIGHLIGHT).build()) put("tin_chestplate", MetalModel.Builder().chestplateBase(TIN_BASE).chestplateHighlight(TIN_HIGHLIGHT).build()) put("tin_boots", MetalModel.Builder().bootsBase(TIN_BASE).bootsHighlight(TIN_HIGHLIGHT).build()) put("tin_leggings", MetalModel.Builder().leggingsBase(TIN_BASE).leggingsHighlight(TIN_HIGHLIGHT).build()) put("tin_nugget", MetalModel.Builder().nuggetBase(TIN_BASE).nuggetHighlight(TIN_HIGHLIGHT).build()) putBlock("deepslate_tin_ore", MetalModel.Builder().deepslateOre().oreBase(0xFFFFFFFF).oreHighlight(0xFFdedede).build()) putBlock("tin_ore", MetalModel.Builder().ore().oreBase(0xFFFFFFFF).oreHighlight(0xFFdedede).build()) putBlock("tin_block", MetalModel.Builder().block().blockBase(TIN_BASE).build()) put("raw_tin", MetalModel.Builder().rawOre(TIN_BASE).build()) putBlock("raw_tin_block", MetalModel.Builder().rawOreBlock(TIN_BASE).build()) put("tin_purified_ore", MetalModel.Builder().purifiedOreBase(TIN_BASE).purifiedOreHighlight(TIN_HIGHLIGHT).build()) put("tin_chunk", MetalModel.Builder().chunkBase(TIN_BASE).chunkHighlight(TIN_HIGHLIGHT).build()) put("molten_tin_bucket", MetalModel.Builder().bucket().moltenBucketBase(TIN_BASE).moltenBucketHighlight(TIN_HIGHLIGHT).build()) put("nikolite_ingot", MetalModel.Builder().ingotBase(NIKOLITE_BASE).ingotHighlight(NIKOLITE_HIGHLIGHT).build()) put("nikolite_dust", MetalModel.Builder().dustBase(NIKOLITE_BASE).dustHighlight(NIKOLITE_HIGHLIGHT).build()) putBlock("deepslate_nikolite_ore", MetalModel.Builder().deepslateOre().oreBase(NIKOLITE_BASE).oreHighlight(NIKOLITE_HIGHLIGHT).build()) putBlock("nikolite_ore", MetalModel.Builder().ore().oreBase(NIKOLITE_BASE).oreHighlight(NIKOLITE_HIGHLIGHT).build()) put("iron_plate", MetalModel.Builder().plateBase(IRON_BASE).plateHighlight(IRON_HIGHLIGHT).build()) put("iron_dust", MetalModel.Builder().dustBase(IRON_BASE).dustHighlight(IRON_HIGHLIGHT).build()) put("iron_purified_ore", MetalModel.Builder().purifiedOreBase(IRON_BASE).purifiedOreHighlight(IRON_HIGHLIGHT).build()) put("iron_chunk", MetalModel.Builder().chunkBase(IRON_BASE).chunkHighlight(IRON_HIGHLIGHT).build()) put("diamond_dust", MetalModel.Builder().dustBase(DIAMOND_BASE).dustHighlight(DIAMOND_HIGHLIGHT).dustOutline( DIAMOND_OUTLINE ).build()) put("sulfur_dust", MetalModel.Builder().dustBase(SULFUR_BASE).dustHighlight(SULFUR_HIGHLIGHT).dustOutline( SULFUR_OUTLINE ).build()) put("coal_dust", MetalModel.Builder().dustBase(COAL_BASE).dustHighlight(COAL_HIGHLIGHT).build()) put("netherite_scrap_dust", MetalModel.Builder().dustBase(NETHERITE_SCRAP_BASE).dustHighlight( NETHERITE_SCRAP_HIGHLIGHT ).dustOutline(NETHERITE_SCRAP_OUTLINE).build()) put("netherite_scrap_chunk", MetalModel.Builder().chunkBase(NETHERITE_SCRAP_BASE).chunkHighlight( NETHERITE_SCRAP_HIGHLIGHT ).chunkOutline(NETHERITE_SCRAP_OUTLINE).build()) put("netherite_scrap_purified_ore", MetalModel.Builder().purifiedOreBase(NETHERITE_SCRAP_BASE).purifiedOreHighlight( NETHERITE_SCRAP_HIGHLIGHT ).purifiedOreOutline(NETHERITE_SCRAP_OUTLINE).build()) put("molten_netherite_bucket", MetalModel.Builder().bucket().moltenBucketBase(NETHERITE_SCRAP_BASE).moltenBucketHighlight(NETHERITE_SCRAP_HIGHLIGHT).build()) } private fun put(id: String, model: MetalModel) { MATERIAL_PROVIDERS[ModelIdentifier(identifier(id), "inventory")] = model } private fun putBlock(id: String, model: MetalModel) { MATERIAL_PROVIDERS[ModelIdentifier(identifier(id), "inventory")] = model MATERIAL_PROVIDERS[ModelIdentifier(identifier(id), "")] = model } } ================================================ FILE: src/main/kotlin/me/steven/indrev/datagen/utils/SpriteColorHolder.kt ================================================ package me.steven.indrev.datagen.utils import net.minecraft.util.Identifier class SpriteColorHolder(val id: Identifier, val color: Int) ================================================ FILE: src/main/kotlin/me/steven/indrev/events/client/IRClientTickEvents.kt ================================================ package me.steven.indrev.events.client import me.steven.indrev.IndustrialRevolutionClient import me.steven.indrev.gui.IRModularControllerScreen import me.steven.indrev.gui.screenhandlers.modular.ModularItemConfigurationScreenHandler import me.steven.indrev.packets.common.ToggleGamerAxePacket import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.tools.modular.IRModularItem import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.minecraft.client.MinecraftClient object IRClientTickEvents : ClientTickEvents.EndTick { override fun onEndTick(client: MinecraftClient) { while (IndustrialRevolutionClient.MODULAR_CONTROLLER_KEYBINDING.wasPressed()) { val playerInventory = MinecraftClient.getInstance().player?.inventory ?: break val hasModularItem = (0 until playerInventory.size()) .associateWith { slot -> playerInventory.getStack(slot) } .filter { (_, stack) -> stack.item is IRModularItem<*> } .isNotEmpty() if (hasModularItem) MinecraftClient.getInstance() .setScreen(IRModularControllerScreen(ModularItemConfigurationScreenHandler(client.player!!.inventory))) } while (IndustrialRevolutionClient.GAMER_AXE_TOGGLE_KEYBINDING.wasPressed()) { val itemStack = client.player?.mainHandStack ?: break if (itemStack.isOf(IRItemRegistry.GAMER_AXE_ITEM)) { ClientPlayNetworking.send(ToggleGamerAxePacket.PACKET_ID, PacketByteBufs.empty()) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/events/client/IRHudRenderCallback.kt ================================================ package me.steven.indrev.events.client import com.mojang.blaze3d.systems.RenderSystem import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.api.IRPlayerEntityExtension import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.config.IRConfig import me.steven.indrev.items.armor.IRModularArmorItem import me.steven.indrev.items.misc.IRMachineUpgradeItem import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback import net.minecraft.client.MinecraftClient import net.minecraft.client.render.GameRenderer import net.minecraft.client.render.Tessellator import net.minecraft.client.render.VertexFormat import net.minecraft.client.render.VertexFormats import net.minecraft.client.texture.Sprite import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.EquipmentSlot.* import net.minecraft.item.ArmorItem import net.minecraft.item.ItemStack import net.minecraft.screen.PlayerScreenHandler import net.minecraft.util.hit.BlockHitResult object IRHudRenderCallback : HudRenderCallback { override fun onHudRender(matrices: MatrixStack, tickDelta: Float) { renderModularArmorHud(matrices) renderTierUpgrade(matrices) } private fun renderModularArmorHud(matrices: MatrixStack) { val client = MinecraftClient.getInstance() val player = client.player if (player is IRPlayerEntityExtension && player.getMaxShieldDurability() > 0) { val color = player.inventory.getArmorStack(HEAD.entitySlotId).let { (stack, item) -> if ((item as? IRModularArmorItem)?.slotType != HEAD) -1 else item.getColor(stack) } val x = IRConfig.hud.renderPosX + 2 val y = IRConfig.hud.renderPosY + 2 val spriteId = when { player.shieldDurability == player.getMaxShieldDurability() -> DEFAULT player.isRegenerating -> REGENERATING player.shieldDurability <= player.getMaxShieldDurability() * 0.25 -> WARNING else -> DAMAGED } val sprite = client.getSpriteAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).apply(spriteId) texturedRect(x + 5, y + 50, 16, 16, sprite, color, 0.8f) ScreenDrawing.texturedRect(matrices, x, y, 90, 62, HUD_MAIN, color, 0.8f) ScreenDrawing.texturedRect(matrices, x + 7, y + 33, 83, 20, HOLDER, color, 0.3f) val shieldText = "${player.shieldDurability.toInt()}/${player.getMaxShieldDurability().toInt()}" ScreenDrawing.drawStringWithShadow(matrices, shieldText, HorizontalAlignment.CENTER, x + 20, y + 56, client.textRenderer.getWidth(shieldText), color) player.armorItems.forEach { stack -> val item = stack.item as? ArmorItem ?: return@forEach val xOffset = 21 * when (item.slotType) { FEET -> 3 LEGS -> 2 CHEST -> 1 HEAD -> 0 else -> return@forEach } client.itemRenderer.renderInGui(stack, x + 9 + xOffset, y + 35) client.itemRenderer.renderGuiItemOverlay(client.textRenderer, stack, x + 9 + xOffset, y + 35) } } } private fun renderTierUpgrade(matrices: MatrixStack) { val client = MinecraftClient.getInstance() val world = client.world ?: return val player = client.player ?: return val hit = MinecraftClient.getInstance().crosshairTarget as? BlockHitResult ?: return val x = client.window.scaledWidth / 2 val y = (client.window.scaledHeight / 2) + 8 val stack = player.mainHandStack val item = stack.item as? IRMachineUpgradeItem ?: return val state = world.getBlockState(hit.blockPos) val block = state?.block as? MachineBlock client.itemRenderer.renderInGui(stack, x, y) if (block != null && block.registry.upgradeable && block.tier == item.from) { matrices.push() matrices.scale(0.5f, 0.5f, 0.5f) client.textRenderer.draw(matrices, "Right Click to upgrade machine", x.toFloat() + 16 + 64 + 64 + 64 + 42, y.toFloat()+ 64 + 64 + 13, -1) matrices.pop() client.itemRenderer.renderInGui(ItemStack(block), x + 8, y + 16) RenderSystem.disableDepthTest() ScreenDrawing.texturedRect(matrices, x + 8 + 16 + 2, y + 16, 16, 16, ARROW, -1) ScreenDrawing.texturedRect(matrices, x + 8 + 16 + 6, y + 18, 16, 16, CHECKMARK, -1) client.itemRenderer.renderInGui(ItemStack(block.registry.block(item.to)), x + 8 + 32 + 4, y + 16) } else { RenderSystem.disableDepthTest() ScreenDrawing.texturedRect(matrices, x + 5, y + 5, 16, 16, X, -1) RenderSystem.enableDepthTest() matrices.push() matrices.scale(0.5f, 0.5f, 0.5f) val txt = when { block == null -> "This is not a machine" !block.registry.upgradeable -> "This machine does not accept tier upgrades" block.tier != item.from -> "This is not the right tier" else -> "This should not happen!" } client.textRenderer.draw(matrices, txt, x.toFloat() + 16 + 64 + 64 + 64 + 42, y.toFloat()+ 64 + 64 + 13, -1) matrices.pop() } } fun texturedRect( x: Int, y: Int, width: Int, height: Int, sprite: Sprite, color: Int, opacity: Float ) { val r = (color shr 16 and 255).toFloat() / 255.0f val g = (color shr 8 and 255).toFloat() / 255.0f val b = (color and 255).toFloat() / 255.0f RenderSystem.disableDepthTest() RenderSystem.depthMask(false) RenderSystem.enableBlend() RenderSystem.defaultBlendFunc() RenderSystem.setShader { GameRenderer.getPositionTexShader() } RenderSystem.setShaderColor(r, g, b, opacity) RenderSystem.setShaderTexture(0, PlayerScreenHandler.BLOCK_ATLAS_TEXTURE) Tessellator.getInstance().run { buffer.run { begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE) vertex(x.toDouble(), y + height.toDouble(), -90.0).texture(sprite.minU, sprite.maxV).next() vertex(x + width.toDouble(), y + height.toDouble(), -90.0).texture(sprite.maxU, sprite.maxV).next() vertex(x + width.toDouble(), y.toDouble(), -90.0).texture(sprite.maxU, sprite.minV).next() vertex(x.toDouble(), y.toDouble(), -90.0).texture(sprite.minU, sprite.minV).next() } draw() } RenderSystem.depthMask(true) RenderSystem.enableDepthTest() RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) } private val WARNING = identifier("gui/hud_warning") private val REGENERATING = identifier("gui/hud_regenerating") private val DAMAGED = identifier("gui/hud_damaged") private val DEFAULT = identifier("gui/hud_default") private val HUD_MAIN = identifier("textures/gui/hud_main.png") private val HOLDER = identifier("textures/gui/hud_armor_holder.png") private val CHECKMARK = identifier("textures/gui/checkmark.png") private val X = identifier("textures/gui/x.png") private val ARROW = identifier("textures/gui/widget_processing_empty.png") } ================================================ FILE: src/main/kotlin/me/steven/indrev/events/client/IRLivingEntityFeatureRendererCallback.kt ================================================ package me.steven.indrev.events.client import me.steven.indrev.armor.ModuleFeatureRenderer import me.steven.indrev.armor.ReinforcedElytraFeatureRenderer import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback import net.minecraft.client.render.entity.EntityRendererFactory import net.minecraft.client.render.entity.LivingEntityRenderer import net.minecraft.client.render.entity.model.BipedEntityModel import net.minecraft.client.render.entity.model.EntityModelLayers import net.minecraft.entity.EntityType import net.minecraft.entity.LivingEntity object IRLivingEntityFeatureRendererCallback : LivingEntityFeatureRendererRegistrationCallback { override fun registerRenderers( entityType: EntityType, renderer: LivingEntityRenderer<*, *>, helper: LivingEntityFeatureRendererRegistrationCallback.RegistrationHelper, ctx: EntityRendererFactory.Context ) { val slim = false helper.register( ModuleFeatureRenderer( renderer as LivingEntityRenderer>, BipedEntityModel(ctx.getPart(if (slim) EntityModelLayers.PLAYER_SLIM_INNER_ARMOR else EntityModelLayers.PLAYER_INNER_ARMOR)), BipedEntityModel(ctx.getPart(if (slim) EntityModelLayers.PLAYER_SLIM_OUTER_ARMOR else EntityModelLayers.PLAYER_OUTER_ARMOR)) ) ) helper.register(ReinforcedElytraFeatureRenderer(renderer, ctx.modelLoader)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/events/client/IRTooltipComponentsCallback.kt ================================================ package me.steven.indrev.events.client import me.steven.indrev.gui.tooltip.energy.EnergyTooltipComponent import me.steven.indrev.gui.tooltip.energy.EnergyTooltipData import me.steven.indrev.gui.tooltip.modular.ModularTooltipComponent import me.steven.indrev.gui.tooltip.modular.ModularTooltipData import me.steven.indrev.gui.tooltip.oredatacards.OreDataCardTooltipComponent import me.steven.indrev.gui.tooltip.oredatacards.OreDataCardTooltipData import net.fabricmc.fabric.api.client.rendering.v1.TooltipComponentCallback import net.minecraft.client.gui.tooltip.TooltipComponent import net.minecraft.client.item.TooltipData object IRTooltipComponentsCallback : TooltipComponentCallback { override fun getComponent(data: TooltipData?): TooltipComponent? { return when (data) { is ModularTooltipData -> ModularTooltipComponent(data) is EnergyTooltipData -> EnergyTooltipComponent(data) is OreDataCardTooltipData -> OreDataCardTooltipComponent(data) else -> null } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/events/client/IRWorldRenderer.kt ================================================ package me.steven.indrev.events.client import me.steven.indrev.IndustrialRevolution import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.component3 import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents import net.minecraft.client.MinecraftClient import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.WorldRenderer import net.minecraft.nbt.NbtLong import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.HitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box object IRWorldRenderer : WorldRenderEvents.BeforeBlockOutline { override fun beforeBlockOutline(context: WorldRenderContext, hitResult: HitResult?): Boolean { val player = MinecraftClient.getInstance().player ?: return true val stack = player.mainHandStack ?: return true if (!stack.isIn(IndustrialRevolution.SCREWDRIVER_TAG) || stack.nbt?.contains("SelectedHeliostats") != true) return true val positions = stack.nbt?.getList("SelectedHeliostats", 4)?.map { BlockPos.fromLong((it as NbtLong).longValue()) } ?: return true val vcp = context.consumers() as VertexConsumerProvider.Immediate val vc = vcp.getBuffer(RenderLayer.getLines()) ?: return true val (cX, cY, cZ) = MinecraftClient.getInstance().gameRenderer.camera.pos context.matrixStack().run { positions.forEach { (x, y, z) -> push() translate(x.toDouble() - cX, y.toDouble() - cY, z.toDouble() - cZ) WorldRenderer.drawBox(this, vc, Box(BlockPos.ORIGIN), 1f, 0.6f, 1f, 1f) pop() } } vcp.draw() return !positions.contains((hitResult as? BlockHitResult)?.blockPos) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/events/client/MatterProjectorPreviewRenderer.kt ================================================ package me.steven.indrev.events.client import draylar.magna.api.MagnaTool import me.steven.indrev.tools.modular.DrillModule import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents import net.minecraft.client.MinecraftClient import net.minecraft.client.render.OverlayTexture import net.minecraft.util.hit.BlockHitResult object MatterProjectorPreviewRenderer : WorldRenderEvents.BeforeEntities { override fun beforeEntities(context: WorldRenderContext) { val player = MinecraftClient.getInstance().player ?: return val target = MinecraftClient.getInstance().crosshairTarget val stack = player.mainHandStack val item = stack.item if (player.isSneaking && DrillModule.MATTER_PROJECTOR.isInstalled(stack) && item is MagnaTool && target is BlockHitResult) { item.blockFinder.findPositions(context.world(), player, item.getRadius(stack)).forEach { pos -> val blockState = context.world().getBlockState(pos) val offset = pos.offset(target.side) if (context.world().getBlockState(offset).material.isReplaceable) { val cameraPos = MinecraftClient.getInstance().gameRenderer.camera.pos val x = offset.x - cameraPos.x val y = offset.y - cameraPos.y val z = offset.z - cameraPos.z context.matrixStack().push() context.matrixStack().translate(x, y, z) context.matrixStack().scale(0.6f, 0.6f, 0.6f) MinecraftClient.getInstance().blockRenderManager.renderBlockAsEntity(blockState, context.matrixStack(), context.consumers(), 0xFF, OverlayTexture.DEFAULT_UV) context.matrixStack().pop() } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/events/client/MiningRigInfoTooltipCallback.kt ================================================ package me.steven.indrev.events.client import me.steven.indrev.api.OreDataCards import me.steven.indrev.blockentities.miningrig.DataCardWriterBlockEntity import me.steven.indrev.gui.screenhandlers.machines.DataCardWriterScreenHandler import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback import net.minecraft.client.MinecraftClient import net.minecraft.client.item.TooltipContext import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting object MiningRigInfoTooltipCallback : ItemTooltipCallback { override fun getTooltip(stack: ItemStack, ctx: TooltipContext, lines: MutableList) { val handler = MinecraftClient.getInstance().player?.currentScreenHandler if (handler is DataCardWriterScreenHandler) { val index = lines.size - if (ctx.isAdvanced) 1 else 0 handler.ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? DataCardWriterBlockEntity ?: return@run DataCardWriterBlockEntity.ORES_SLOTS.forEach { slot -> val oreStack = blockEntity.inventoryComponent!!.inventory.getStack(slot) if (oreStack.equals(stack) && stack.count < 64) { lines.add(index, LiteralText("Missing ${64-stack.count} blocks.").formatted(Formatting.RED)) lines.add(index, LiteralText("Not enough blocks to collect data.").formatted(Formatting.RED)) } } } val data: OreDataCards.Data = handler.ctx.get { world, pos -> val blockEntity = world.getBlockEntity(pos) as? DataCardWriterBlockEntity ?: return@get OreDataCards.INVALID_DATA val cardStack = blockEntity.inventoryComponent!!.inventory.getStack(0) OreDataCards.readNbt(cardStack) ?: OreDataCards.INVALID_DATA }.orElse(OreDataCards.INVALID_DATA) val modifier = OreDataCards.Modifier.byItem(stack.item) var remainingLevels = 0 var level = 0 when (modifier) { OreDataCards.Modifier.RICHNESS -> { level = stack.count / 16 remainingLevels = 40 - (data.modifiersUsed[modifier] ?: 0) } OreDataCards.Modifier.SPEED, OreDataCards.Modifier.SIZE -> { level = stack.count / 64 remainingLevels = 1 } OreDataCards.Modifier.RNG -> {} else -> return } val modifierName = TranslatableText(modifier.translationKey) if (remainingLevels <= 0) lines.add(index, LiteralText("Cannot increase ").append(modifierName).append(" level anymore").formatted( Formatting.RED)) else if (level > 0) lines.add(index, LiteralText("+$level ").append(modifierName).append(" modifiers").formatted(Formatting.GREEN)) else lines.add(index, LiteralText("Not enough to increase ").append(modifierName).formatted(Formatting.RED)) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/events/common/IRLootTableCallback.kt ================================================ package me.steven.indrev.events.common import me.steven.indrev.registry.IRItemRegistry import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback import net.minecraft.loot.LootManager import net.minecraft.loot.LootPool import net.minecraft.loot.condition.RandomChanceLootCondition import net.minecraft.loot.entry.ItemEntry import net.minecraft.loot.provider.number.ConstantLootNumberProvider import net.minecraft.resource.ResourceManager import net.minecraft.util.Identifier object IRLootTableCallback : LootTableLoadingCallback { override fun onLootTableLoading( resourceManager: ResourceManager, manager: LootManager, id: Identifier, supplier: FabricLootSupplierBuilder, setter: LootTableLoadingCallback.LootTableSetter ) { val chance = when (id) { abandonedMineshaft, simpleDungeon -> 0.3f buriedTreasure -> 0.4f woodlandMansion -> 0.5f endCityTreasure -> 0.6f else -> return } val builder = LootPool.builder() builder.rolls(ConstantLootNumberProvider.create(1f)) colorModules.forEach { builder.with(ItemEntry.builder(it)) } builder.conditionally(RandomChanceLootCondition.builder(chance)) supplier.withPool(builder.build()) } private val abandonedMineshaft = Identifier("chests/abandoned_mineshaft") private val buriedTreasure = Identifier("chests/buried_treasure") private val simpleDungeon = Identifier("chests/simple_dungeon") private val woodlandMansion = Identifier("chests/woodland_mansion") private val endCityTreasure = Identifier("chests/end_city_treasure") private val colorModules = arrayOf( IRItemRegistry.PINK_MODULE_ITEM, IRItemRegistry.RED_MODULE_ITEM, IRItemRegistry.PURPLE_MODULE_ITEM, IRItemRegistry.BLUE_MODULE_ITEM, IRItemRegistry.CYAN_MODULE_ITEM, IRItemRegistry.GREEN_MODULE_ITEM, IRItemRegistry.YELLOW_MODULE_ITEM, IRItemRegistry.ORANGE_MODULE_ITEM, IRItemRegistry.BLUE_MODULE_ITEM, IRItemRegistry.BLACK_MODULE_ITEM, IRItemRegistry.BROWN_MODULE_ITEM ) } ================================================ FILE: src/main/kotlin/me/steven/indrev/fluids/BaseFluid.kt ================================================ package me.steven.indrev.fluids import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.FluidBlock import net.minecraft.client.render.RenderLayer import net.minecraft.client.texture.Sprite import net.minecraft.fluid.FlowableFluid import net.minecraft.fluid.Fluid import net.minecraft.fluid.FluidState import net.minecraft.item.BucketItem import net.minecraft.item.Item import net.minecraft.state.StateManager import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockRenderView import net.minecraft.world.BlockView import net.minecraft.world.WorldAccess import net.minecraft.world.WorldView abstract class BaseFluid( val identifier: Identifier, val block: () -> FluidBlock, private val bucketItem: () -> BucketItem?, private val color: Int ) : FlowableFluid() { override fun toBlockState(state: FluidState?): BlockState? = block().defaultState.with( FluidBlock.LEVEL, getBlockStateLevel(state) ) override fun getBucketItem(): Item? = bucketItem() override fun getLevelDecreasePerBlock(world: WorldView?): Int = 1 override fun getTickRate(world: WorldView?): Int = 5 override fun isInfinite(): Boolean = false override fun getFlowSpeed(world: WorldView?): Int = 2 override fun canBeReplacedWith( state: FluidState?, world: BlockView?, pos: BlockPos?, fluid: Fluid, direction: Direction ): Boolean = false override fun getBlastResistance(): Float = 100F override fun beforeBreakingBlock(world: WorldAccess, pos: BlockPos?, state: BlockState) { val blockEntity = if (state.hasBlockEntity()) world.getBlockEntity(pos) else null Block.dropStacks(state, world, pos, blockEntity) } override fun matchesType(fluid: Fluid?): Boolean = fluid == flowing || fluid == still fun registerRender(fluidType: FluidType) { val fluidRenderHandler = object : FluidRenderHandler { override fun getFluidSprites(view: BlockRenderView?, pos: BlockPos?, state: FluidState?): Array = fluidType.sprites override fun getFluidColor(view: BlockRenderView?, pos: BlockPos?, state: FluidState?): Int = color } FluidRenderHandlerRegistry.INSTANCE.register(still, fluidRenderHandler) FluidRenderHandlerRegistry.INSTANCE.register(flowing, fluidRenderHandler) BlockRenderLayerMap.INSTANCE.putFluids( RenderLayer.getTranslucent(), still, flowing ) } class Flowing( identifier: Identifier, block: () -> FluidBlock, bucketItem: () -> BucketItem?, color: Int, val still: () -> Still ) : BaseFluid(identifier, block, bucketItem, color) { override fun appendProperties(builder: StateManager.Builder?) { super.appendProperties(builder) builder?.add(LEVEL) } override fun getFlowing(): Fluid = this override fun getStill(): Fluid = still() override fun getLevel(state: FluidState): Int = state[LEVEL] override fun isStill(state: FluidState?): Boolean = false } class Still( identifier: Identifier, block: () -> FluidBlock, bucketItem: () -> BucketItem?, color: Int, val flowing: () -> Flowing ) : BaseFluid(identifier, block, bucketItem, color) { override fun getLevel(state: FluidState?): Int = 8 override fun getFlowing(): Fluid = flowing() override fun getStill(): Fluid = this override fun isStill(state: FluidState?): Boolean = true } } ================================================ FILE: src/main/kotlin/me/steven/indrev/fluids/FluidType.kt ================================================ package me.steven.indrev.fluids import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback import net.fabricmc.fabric.api.resource.ResourceManagerHelper import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener import net.minecraft.client.MinecraftClient import net.minecraft.client.texture.Sprite import net.minecraft.resource.ResourceManager import net.minecraft.resource.ResourceType import net.minecraft.screen.PlayerScreenHandler import net.minecraft.util.Identifier import java.util.* enum class FluidType(val stillId: Identifier, val flowId: Identifier) { LAVA(identifier("block/gray_lava_still"), identifier("block/gray_lava_flow")), WATER(Identifier("block/water_still"), Identifier("block/water_flow")), GAS(identifier("block/gas"), identifier("block/gas")); var sprites = arrayOfNulls(2) fun registerReloadListener() { ClientSpriteRegistryCallback.event(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE) .register(ClientSpriteRegistryCallback { _, registry -> registry.register(stillId) registry.register(flowId) }) ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES) .registerReloadListener(object : SimpleSynchronousResourceReloadListener { override fun reload(manager: ResourceManager?) { val atlas = MinecraftClient.getInstance().getSpriteAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE) sprites[0] = atlas.apply(stillId) sprites[1] = atlas.apply(flowId) } override fun getFabricId(): Identifier = identifier("${this@FluidType.name.lowercase(Locale.getDefault())}_reload_listener") }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/IRInventoryScreen.kt ================================================ package me.steven.indrev.gui import io.github.cottonmc.cotton.gui.SyncedGuiDescription import io.github.cottonmc.cotton.gui.client.CottonInventoryScreen import net.minecraft.entity.player.PlayerEntity class IRInventoryScreen(controller: T, playerEntity: PlayerEntity) : CottonInventoryScreen(controller, playerEntity) { } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/IRModularControllerScreen.kt ================================================ package me.steven.indrev.gui import io.github.cottonmc.cotton.gui.GuiDescription import io.github.cottonmc.cotton.gui.client.CottonClientScreen class IRModularControllerScreen(desc: GuiDescription) : CottonClientScreen(desc) ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/IRScreenHandlerFactory.kt ================================================ package me.steven.indrev.gui import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerContext import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.Text import net.minecraft.util.math.BlockPos open class IRScreenHandlerFactory( private val handlerFactory: (Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler, private val pos: BlockPos ) : ExtendedScreenHandlerFactory { override fun createMenu(syncId: Int, inv: PlayerInventory?, player: PlayerEntity?): ScreenHandler { return handlerFactory(syncId, inv!!, ScreenHandlerContext.create(inv.player.world, pos)) } override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) { buf.writeBlockPos(pos) } override fun getDisplayName(): Text? = null } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/ScrewdriverScreenHandlerFactory.kt ================================================ package me.steven.indrev.gui import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blockentities.MachineBlockEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerContext import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.util.math.BlockPos class ScrewdriverScreenHandlerFactory( handlerFactory: (Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler, pos: BlockPos, val blockEntity: MachineBlockEntity<*> ) : IRScreenHandlerFactory(handlerFactory, pos) { override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) { super.writeScreenOpeningData(player, buf) buf.writeBoolean(blockEntity.isConfigurable(ConfigurationType.ITEM)) if (blockEntity.isConfigurable(ConfigurationType.ITEM)) { blockEntity.getCurrentConfiguration(ConfigurationType.ITEM).writeBuf(buf) } buf.writeBoolean(blockEntity.isConfigurable(ConfigurationType.FLUID)) if (blockEntity.isConfigurable(ConfigurationType.FLUID)) { blockEntity.getCurrentConfiguration(ConfigurationType.FLUID).writeBuf(buf) } buf.writeBoolean(blockEntity.isConfigurable(ConfigurationType.ENERGY)) if (blockEntity.isConfigurable(ConfigurationType.ENERGY)) { blockEntity.getCurrentConfiguration(ConfigurationType.ENERGY).writeBuf(buf) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/guiutils.kt ================================================ package me.steven.indrev.utils import io.github.cottonmc.cotton.gui.SyncedGuiDescription import io.github.cottonmc.cotton.gui.ValidatedSlot import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.* import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import io.github.cottonmc.cotton.gui.widget.icon.Icon import io.netty.buffer.Unpooled import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity import me.steven.indrev.blockentities.farms.AOEMachineBlockEntity import me.steven.indrev.gui.widgets.machines.energyBar import me.steven.indrev.gui.widgets.machines.temperatureBar import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot import me.steven.indrev.items.upgrade.IREnhancerItem import me.steven.indrev.packets.common.ToggleFactoryStackSplittingPacket import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.Inventory import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.world.World fun WGridPanel.add(w: WWidget, x: Double, y: Double, width: Double, height: Double) { this.add(w, x.toInt(), y.toInt(), width.toInt(), height.toInt()) w.setLocation((x * 18).toInt(), (y * 18).toInt()) } fun WGridPanel.add(w: WWidget, x: Double, y: Double) { this.add(w, x.toInt(), y.toInt()) w.setLocation((x * 18).toInt(), (y * 18).toInt()) } val SPLIT_ON_ICON = identifier("textures/gui/split_on.png") val SPLIT_OFF_ICON = identifier("textures/gui/split_off.png") fun SyncedGuiDescription.configure( titleId: String, ctx: ScreenHandlerContext, playerInventory: PlayerInventory, blockInventory: Inventory, panel: WGridPanel = rootPanel as WGridPanel, invPos: Double = 4.0, widgetPos: Double = 0.15 ) { panel.setSize(150, 120) panel.add(createPlayerInventoryPanel(), 0.0, invPos) val title = WText(TranslatableText(titleId), HorizontalAlignment.CENTER, 0x404040) var titlePos = 4.7 ctx.run { world, blockPos -> val blockEntity = world.getBlockEntity(blockPos) as BaseBlockEntity val energyWidget = energyBar(blockEntity) panel.add(energyWidget, 0.1, widgetPos) if (blockEntity is MachineBlockEntity<*> && blockEntity.enhancerComponent != null) { addUpgradeSlots(blockEntity, blockInventory, world, panel) } if (blockEntity is MachineBlockEntity<*> && blockEntity.temperatureComponent != null) { titlePos += 0.5 addTemperatureWidget(blockEntity, panel, blockInventory, world, widgetPos) } if (blockEntity is AOEMachineBlockEntity<*>) { addAOEWidgets(world, blockEntity, panel) } if (blockEntity is CraftingMachineBlockEntity<*> && blockEntity.craftingComponents.size > 1) { addSplitStackButton(blockEntity, blockPos, world, panel) } } panel.add(title, titlePos, 0.0) } fun addSplitStackButton(blockEntity: CraftingMachineBlockEntity<*>, blockPos: BlockPos, world: World, panel: WGridPanel) { val buttonPanel = WGridPanel() val button = object : WButton() { init { if (world.isClient) { icon = Icon { matrices, x, y, size -> val id = if (blockEntity.isSplitOn) SPLIT_ON_ICON else SPLIT_OFF_ICON ScreenDrawing.texturedRect(matrices, x + 1, y + 1, size, size, id, -1) } } } override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add(TranslatableText("gui.indrev.button.auto_split")) } } button.setOnClick { blockEntity.isSplitOn = !blockEntity.isSplitOn val buf = PacketByteBuf(Unpooled.buffer()) buf.writeBlockPos(blockPos) ClientPlayNetworking.send(ToggleFactoryStackSplittingPacket.SPLIT_STACKS_PACKET, buf) } if (world.isClient) buttonPanel.backgroundPainter = UPGRADE_SLOT_PANEL_PAINTER buttonPanel.add(button, 0, 0) panel.add(buttonPanel, 9.7, 4.2) button.setSize(20, 20) } fun addUpgradeSlots(blockEntity: MachineBlockEntity<*>, blockInventory: Inventory, world: World, panel: WGridPanel) { val enhancerComponent = blockEntity.enhancerComponent!! val slotPanel = WGridPanel() for ((i, slot) in enhancerComponent.slots.withIndex()) { val s = object : WTooltipedItemSlot(inventory = blockInventory, startIndex = slot, emptyTooltip = mutableListOf(TranslatableText("gui.indrev.upgrade_slot_type"))) { override fun createSlotPeer(inventory: Inventory?, index: Int, x: Int, y: Int): ValidatedSlot { return object : ValidatedSlot(inventory, index, x, y) { override fun getMaxItemCount(stack: ItemStack): Int { val upgrade = (stack.item as? IREnhancerItem)?.enhancer ?: return 0 return enhancerComponent.maxSlotCount(upgrade) } } } } if (world.isClient) s.backgroundPainter = if (enhancerComponent.isLocked(slot, blockEntity.tier)) getLockedSlotPainter( blockInventory, slot ) else getUpgradeSlotPainter(blockInventory, slot) slotPanel.add(s, 0, i) } if (world.isClient) slotPanel.backgroundPainter = UPGRADE_SLOT_PANEL_PAINTER panel.add(slotPanel, 9.7, -0.25) } fun addTemperatureWidget(blockEntity: MachineBlockEntity<*>, panel: WGridPanel, blockInventory: Inventory, world: World, widgetPos: Double) { panel.add(temperatureBar(blockEntity), 0.95, widgetPos) val coolerSlot = WTooltipedItemSlot.of(blockInventory, blockEntity.inventoryComponent!!.inventory.coolerSlot!!, TranslatableText("gui.indrev.cooler_slot_type")) if (world.isClient) coolerSlot.backgroundPainter = getCoolerSlotPainter(blockInventory, 1) panel.add(coolerSlot, 0.75, widgetPos + 2.6) } fun addAOEWidgets(world: World, blockEntity: AOEMachineBlockEntity<*>, panel: WGridPanel) { val buttonPanel = WGridPanel() val button = object : WButton() { override fun addTooltip(information: TooltipBuilder?) { information?.add(TranslatableText("block.indrev.aoe.toggle.${blockEntity.renderWorkingArea}")) } } button.setOnClick { blockEntity.renderWorkingArea = !blockEntity.renderWorkingArea } if (world.isClient) { button.icon = Icon { matrices, x, y, _ -> ScreenDrawing.texturedRect(matrices,x + 1, y + 1, 16, 16, identifier("textures/gui/range_icon.png"), -1) } buttonPanel.backgroundPainter = UPGRADE_SLOT_PANEL_PAINTER } buttonPanel.add(button, 0, 0) panel.add(buttonPanel, 9.7, 4.2) button.setSize(20, 20) } fun WItemSlot.setPainterSafe(ctx: ScreenHandlerContext, painter: () -> BackgroundPainter) { ctx.run { world, _ -> if (world.isClient) this.backgroundPainter = painter() } } fun WItemSlot.setIcon(ctx: ScreenHandlerContext, inventory: Inventory, slot: Int, identifier: Identifier) { setPainterSafe(ctx) { BackgroundPainter { matrices, left, top, widget -> BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget) if (inventory.getStack(slot).isEmpty) ScreenDrawing.texturedRect(matrices, left + 1, top + 1, 16, 16, identifier, -1) } } } val POWER_ICON_ID = identifier("textures/gui/power_icon.png") fun getEnergySlotPainter(inventory: Inventory, slot: Int) = BackgroundPainter { matrices, left, top, widget -> BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget) if (inventory.getStack(slot).isEmpty) ScreenDrawing.texturedRect(matrices, left, top, 18, 18, POWER_ICON_ID, -1) } val VENT_ICON_ID = identifier("textures/gui/vent_icon.png") fun getCoolerSlotPainter(inventory: Inventory, slot: Int) = BackgroundPainter { matrices, left, top, widget -> BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget) if (inventory.getStack(slot).isEmpty) ScreenDrawing.texturedRect(matrices, left, top, 18, 18, VENT_ICON_ID, -1) } val UPGRADE_ICON_ID = identifier("textures/gui/upgrade_icon.png") fun getUpgradeSlotPainter(inventory: Inventory, slot: Int) = BackgroundPainter { matrices, left, top, widget -> BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget) if (inventory.getStack(slot).isEmpty) ScreenDrawing.texturedRect(matrices, left, top, 18, 18, UPGRADE_ICON_ID, -1) } val LOCKED_ICON_ID = identifier("textures/gui/locked_icon.png") fun getLockedSlotPainter(inventory: Inventory, slot: Int) = BackgroundPainter { matrices, left, top, widget -> BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget) if (inventory.getStack(slot).isEmpty) ScreenDrawing.texturedRect(matrices, left, top, 18, 18, LOCKED_ICON_ID, -1) } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/properties/SyncableProperty.kt ================================================ package me.steven.indrev.gui.properties import me.steven.indrev.components.DefaultSyncableObject import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.network.PacketByteBuf import kotlin.reflect.KProperty abstract class SyncableProperty(val id: Int, val defaultValue: T, val setter: (T) -> T = { it }) : DefaultSyncableObject() { open var value = defaultValue fun set(value: Any?) { this.value = value as T } operator fun getValue(thisRef: Any?, property: KProperty<*>): T { return value } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { val before = this.value this.value = setter(value) markDirty { before != value } } } object NullSyncableProperty : SyncableProperty(-1, -1, { it }) { override fun toPacket(buf: PacketByteBuf) { } override fun fromPacket(buf: PacketByteBuf) { } } open class IntSyncableProperty(id: Int, defaultValue: Int, setter: (Int) -> Int = { it }) : SyncableProperty(id, defaultValue, setter) { @Environment(EnvType.CLIENT) override fun fromPacket(buf: PacketByteBuf) { value = buf.readInt() } override fun toPacket(buf: PacketByteBuf) { buf.writeInt(value) } } open class LongSyncableProperty(id: Int, defaultValue: Long, setter: (Long) -> Long = { it }) : SyncableProperty(id, defaultValue, setter) { @Environment(EnvType.CLIENT) override fun fromPacket(buf: PacketByteBuf) { value = buf.readLong() } override fun toPacket(buf: PacketByteBuf) { buf.writeLong(value) } } open class DoubleSyncableProperty(id: Int, defaultValue: Double, setter: (Double) -> Double = { it }) : SyncableProperty(id, defaultValue, setter) { @Environment(EnvType.CLIENT) override fun fromPacket(buf: PacketByteBuf) { value = buf.readDouble() } override fun toPacket(buf: PacketByteBuf) { buf.writeDouble(value) } } open class BooleanSyncableProperty(id: Int, defaultValue: Boolean, setter: (Boolean) -> Boolean = { it }) : SyncableProperty(id, defaultValue, setter) { @Environment(EnvType.CLIENT) override fun fromPacket(buf: PacketByteBuf) { value = buf.readBoolean() } override fun toPacket(buf: PacketByteBuf) { buf.writeBoolean(value) } } open class EnumSyncableProperty>(id: Int, defaultValue: T, val values: Array, setter: (T) -> T = { it }) : SyncableProperty(id, defaultValue, setter) { @Environment(EnvType.CLIENT) override fun fromPacket(buf: PacketByteBuf) { value = values[buf.readInt()] } override fun toPacket(buf: PacketByteBuf) { buf.writeInt(value.ordinal) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/IRGuiScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers import io.github.cottonmc.cotton.gui.SyncedGuiDescription import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.netty.buffer.Unpooled import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.components.GuiSyncableComponent import me.steven.indrev.gui.properties.SyncableProperty import me.steven.indrev.packets.client.GuiPropertySyncPacket import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.block.entity.BlockEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandlerContext import net.minecraft.screen.ScreenHandlerType import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.util.Identifier import java.util.function.BiFunction open class IRGuiScreenHandler( type: ScreenHandlerType<*>?, syncId: Int, playerInventory: PlayerInventory, val ctx: ScreenHandlerContext ) : SyncedGuiDescription(type, syncId, playerInventory, getBlockInventory(ctx), getBlockPropertyDelegate(ctx)) { var component: GuiSyncableComponent? = null init { properties.clear() trackedPropertyValues.clear() ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? BaseBlockEntity ?: return@run component = blockEntity.guiSyncableComponent } } @Environment(EnvType.CLIENT) override fun addPainters() { super.addPainters() val offset = 170 - rootPanel.width rootPanel.backgroundPainter = BackgroundPainter.createLightDarkVariants( BackgroundPainter.createNinePatch(Identifier("libgui", "textures/widget/panel_light.png")).setPadding(8) .setRightPadding(offset), BackgroundPainter.createNinePatch(Identifier("libgui", "textures/widget/panel_dark.png")).setPadding(8) .setRightPadding(offset) ) } @Suppress("UNCHECKED_CAST") inline fun withBlockEntity(block: (T) -> Unit) { val be = ctx.get(BiFunction { world, pos -> world.getBlockEntity(pos) as? T ?: return@BiFunction null }).orElse(null) block(be ?: return) } @Suppress("UNCHECKED_CAST") inline fun query(block: (T) -> U): U { val be = ctx.get(BiFunction { world, pos -> world.getBlockEntity(pos) as? T ?: return@BiFunction null }).orElse(null) return block(be ?: error("burh")) } fun syncProperties() { val props = component?.properties ?: return val player = playerInventory.player if (player is ServerPlayerEntity) { for (i in props.indices) { if (props[i].isDirty) { val buf = PacketByteBuf(Unpooled.buffer()) buf.writeInt(syncId) buf.writeInt(i) props[i].toPacket(buf) ServerPlayNetworking.send(player, GuiPropertySyncPacket.SYNC_PROPERTY, buf) props[i].isDirty = false onSyncedProperty(i, props[i]) } } } } open fun onSyncedProperty(index: Int, property: SyncableProperty<*>) { } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/blockblacklister/BlockBlacklisterScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.blockblacklister import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.impl.LibGuiCommon import io.github.cottonmc.cotton.gui.widget.WButton import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WLabel import io.github.cottonmc.cotton.gui.widget.WWidget import io.github.cottonmc.cotton.gui.widget.data.InputResult import io.github.cottonmc.cotton.gui.widget.data.Insets import me.steven.indrev.packets.common.UpdateMiningDrillBlockBlacklistPacket import me.steven.indrev.tools.modular.DrillModule import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.minecraft.client.MinecraftClient import net.minecraft.client.util.math.MatrixStack import net.minecraft.item.ItemStack import net.minecraft.text.TranslatableText import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import kotlin.random.Random class BlockBlacklisterScreenHandler : LightweightGuiDescription() { init { val panel = WGridPanel() this.rootPanel = panel panel.insets = Insets.ROOT_PANEL val stack = { MinecraftClient.getInstance().player!!.mainHandStack } val range = DrillModule.RANGE.getLevel(stack()) if (range <= 0) { panel.add(WLabel("Install at least one Range Module to unlock functionality."), 0, 0) } else { for (x in -range..range) { for (y in -range..range) { val pos = BlockPos(x, -y, 0) panel.add(WToggleBlock(pos, stack), x + range, y + range) } } } val buttonFlipY = WButton(TranslatableText("Mirror vertically")) buttonFlipY.onClick = Runnable { val buf = PacketByteBufs.create() buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.FLIP_Y.ordinal) ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf) } panel.add(buttonFlipY, range * 2 + 2, 0) buttonFlipY.setSize(20, 20) val buttonFlipX = WButton(TranslatableText("Mirror horizontally")) buttonFlipX.onClick = Runnable { val buf = PacketByteBufs.create() buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.FLIP_X.ordinal) ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf) } panel.add(buttonFlipX, range * 2 + 2, 2) buttonFlipX.setSize(20, 20) val buttonRotX90 = WButton(TranslatableText("Rotate 90º")) buttonRotX90.onClick = Runnable { val buf = PacketByteBufs.create() buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.ROT_X_90_CLOCKWISE.ordinal) ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf) } panel.add(buttonRotX90, range * 2 + 2, 4) buttonRotX90.setSize(20, 20) val buttonRotX90CCW = WButton(TranslatableText("Rotate 90º Counterclockwise")) buttonRotX90CCW.onClick = Runnable { val buf = PacketByteBufs.create() buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.ROT_X_90_COUNTERCLOCKWISE.ordinal) ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf) } panel.add(buttonRotX90CCW, range * 2 + 2, 6) buttonRotX90CCW.setSize(20, 20) val buttonClear = WButton(TranslatableText("Reset")) buttonClear.onClick = Runnable { val buf = PacketByteBufs.create() buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.CLEAR.ordinal) ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf) } panel.add(buttonClear, range * 2 + 2, 8) buttonClear.setSize(20, 20) panel.validate(this) } override fun addPainters() { rootPanel.backgroundPainter = BackgroundPainter.createLightDarkVariants( BackgroundPainter.createNinePatch(Identifier(LibGuiCommon.MOD_ID, "textures/widget/panel_light.png")).setRightPadding(-35), BackgroundPainter.createNinePatch(Identifier(LibGuiCommon.MOD_ID, "textures/widget/panel_dark.png")).setRightPadding(-35) ) } class WToggleBlock(private val pos: BlockPos, private val stack: () -> ItemStack) : WWidget() { private val isSelected: Boolean get() = !DrillModule.getBlacklistedPositions(stack()).contains(pos) private val texture = run { val r = Random.nextFloat() when { r < 0.0003 -> Identifier("textures/block/emerald_ore.png") r < 0.0008 -> Identifier("textures/block/diamond_ore.png") r < 0.025 -> Identifier("textures/block/gold_ore.png") r < 0.05 -> Identifier("textures/block/iron_ore.png") r < 0.1 -> Identifier("textures/block/coal_ore.png") r < 0.2 -> Identifier("textures/block/cobblestone.png") else -> Identifier("textures/block/stone.png") } } override fun paint(matrices: MatrixStack, x: Int, y: Int, mouseX: Int, mouseY: Int) { ScreenDrawing.coloredRect(matrices, x - 1, y - 1, width + 2, height + 2, 0xFF000000.toInt()) ScreenDrawing.texturedRect(matrices, x, y, width, height, texture, -1) if (!isSelected) { ScreenDrawing.coloredRect(matrices, x, y, width, height, 0xAA000000.toInt()) } } override fun onClick(x: Int, y: Int, button: Int): InputResult { val buf = PacketByteBufs.create() buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.SINGLE.ordinal) buf.writeBlockPos(pos) ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf) return InputResult.PROCESSED } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/BiomassGeneratorScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.generators.BiomassGeneratorBlockEntity import me.steven.indrev.gui.screenhandlers.BIOMASS_GENERATOR_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.fuelBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class BiomassGeneratorScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( BIOMASS_GENERATOR_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.biomass_generator", ctx, playerInventory, blockInventory) // Fuel input root.add(WItemSlot.of(blockInventory, 2), 4, 2) // Burning widget val wFuel = query { be -> fuelBar(be) } root.add(wFuel, 4.1, 1.1) wFuel.setSize(14, 14) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("biomass_generator") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ChopperScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WSlider import io.github.cottonmc.cotton.gui.widget.WSprite import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.blockentities.farms.AOEMachineBlockEntity import me.steven.indrev.gui.screenhandlers.CHOPPER_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import me.steven.indrev.utils.setIcon import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText class ChopperScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( CHOPPER_HANDLER, syncId, playerInventory, ctx ) { var value = -1 init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.chopper", ctx, playerInventory, blockInventory, invPos = 4.45) val inputFrame = WSprite(identifier("textures/gui/input_frame.png")) root.add(inputFrame, 1.9, 0.7) inputFrame.setSize(40, 44) val outputFrame = WSprite(identifier("textures/gui/output_frame.png")) root.add(outputFrame, 5.1, 0.7) outputFrame.setSize(58, 62) val outputSlot = WTooltipedItemSlot.of(blockInventory, 6, 3, 3, TranslatableText("gui.indrev.output_slot_type")) outputSlot.isInsertingAllowed = false root.add(outputSlot, 5.2, 1.0) val axeSlot = WTooltipedItemSlot.of(blockInventory, 2, TranslatableText("gui.indrev.chopper_input_axe")) axeSlot.setIcon(ctx, blockInventory, 2, AXE_ICON) root.add(axeSlot, 2.0, 1.0) val boneMealSlot = WTooltipedItemSlot.of(blockInventory, 3, TranslatableText("gui.indrev.chopper_input_bone_meal")) boneMealSlot.setIcon(ctx, blockInventory, 3, BONE_MEAL_ICON) root.add(boneMealSlot, 3.0, 1.0) val saplingSlot = WTooltipedItemSlot.of(blockInventory, 4, TranslatableText("gui.indrev.chopper_input_sapling")) saplingSlot.setIcon(ctx, blockInventory, 4, SAPLING_ICON) root.add(saplingSlot, 2.0, 2.0) val otherSaplingSlot = WTooltipedItemSlot.of(blockInventory, 5, TranslatableText("gui.indrev.chopper_input_sapling")) otherSaplingSlot.setIcon(ctx, blockInventory, 5, SAPLING_ICON) root.add(otherSaplingSlot, 3.0, 2.0) val slider = WSlider(1, 9, Axis.HORIZONTAL) root.add(slider, 1.6, 3.6) slider.setSize(50, 20) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@run slider.value = blockEntity.range } slider.setValueChangeListener { newValue -> this.value = newValue } val text = WText({ TranslatableText("block.indrev.aoe.range", slider.value) }, HorizontalAlignment.LEFT) root.add(text, 1.8, 3.3) root.validate(this) } override fun close(player: PlayerEntity?) { super.close(player) AOEMachineBlockEntity.sendValueUpdatePacket(value, ctx) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("chopper_controller") val AXE_ICON = identifier("textures/gui/axe_icon.png") val BONE_MEAL_ICON = identifier("textures/gui/bone_meal_icon.png") val SAPLING_ICON = identifier("textures/gui/sapling_icon.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/CoalGeneratorScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.generators.CoalGeneratorBlockEntity import me.steven.indrev.gui.screenhandlers.COAL_GENERATOR_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.fuelBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.util.Identifier class CoalGeneratorScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( COAL_GENERATOR_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.coal_generator", ctx, playerInventory, blockInventory) val itemSlot = WItemSlot.of(blockInventory, 2) root.add(itemSlot, 4, 2) val wFuel = query { be -> fuelBar(be) } root.add(wFuel, 4.1, 1.1) wFuel.setSize(14, 14) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID: Identifier = identifier("coal_generator_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/CompressorFactoryScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.CompressorFactoryBlockEntity import me.steven.indrev.gui.screenhandlers.COMPRESSOR_FACTORY_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.upProcessBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class CompressorFactoryScreenHandler ( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( COMPRESSOR_FACTORY_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.compressor_factory", ctx, playerInventory, blockInventory, widgetPos = 0.15) withBlockEntity { blockEntity -> val slotsAmount = 5 val offset = 2.2 for ((index, slot) in blockEntity.inventoryComponent!!.inventory.inputSlots.withIndex()) { val inputSlot = WItemSlot.of(blockInventory, slot) root.add(inputSlot, offset + (index * 1.4), 0.6) } for (i in 0 until slotsAmount) { val processWidget = upProcessBar(blockEntity, CompressorFactoryBlockEntity.CRAFTING_COMPONENT_START_ID + i) root.add(processWidget, offset + (i * 1.4), 1.7) } for ((index, slot) in blockEntity.inventoryComponent!!.inventory.outputSlots.withIndex()) { val outputSlot = WItemSlot.of(blockInventory, slot) root.add(outputSlot, offset + (index * 1.4), 2.9) outputSlot.addChangeListener { _, _, _, _ -> val player = playerInventory.player if (!player.world.isClient) { blockEntity.dropExperience(player) } } outputSlot.isInsertingAllowed = false } } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("compressor_factory_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/CompressorScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.CompressorBlockEntity import me.steven.indrev.gui.screenhandlers.COMPRESSOR_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class CompressorScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( COMPRESSOR_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.compressor", ctx, playerInventory, blockInventory) val inputSlot = WItemSlot.of(blockInventory, 2) root.add(inputSlot, 3.3, 1.8) val processWidget = query { be -> processBar(be, CompressorBlockEntity.CRAFTING_COMPONENT_ID) } root.add(processWidget, 4.45, 1.8) val outputSlot = WItemSlot.outputOf(blockInventory, 3) outputSlot.isInsertingAllowed = false root.add(outputSlot, 5.94, 1.8) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("compressor_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/CondenserScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.CondenserBlockEntity import me.steven.indrev.gui.screenhandlers.CONDENSER_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.fluidTank import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class CondenserScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( CONDENSER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.condenser", ctx, playerInventory, blockInventory) withBlockEntity { be -> val fluid = fluidTank(be, CondenserBlockEntity.INPUT_TANK_ID) root.add(fluid, 2.8, 1.0) val processWidget = processBar(be, CondenserBlockEntity.CRAFTING_COMPONENT_ID) root.add(processWidget, 4.0, 1.8) } val outputSlot = WItemSlot.outputOf(blockInventory, 2) root.add(outputSlot, 5.7, 1.8) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("condenser") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/DataCardWriterScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WButton import io.github.cottonmc.cotton.gui.widget.WDynamicLabel import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import io.github.cottonmc.cotton.gui.widget.WLabel import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.blockentities.miningrig.DataCardWriterBlockEntity import me.steven.indrev.gui.screenhandlers.DATA_CARD_WRITER_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.packets.common.DataCardWriteStartPacket import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.minecraft.client.MinecraftClient import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.LiteralText class DataCardWriterScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( DATA_CARD_WRITER_HANDLER, syncId, playerInventory, ctx ) { init { val startButton = WButton(LiteralText("Write")) val root = object : WGridPanel() { override fun tick() { super.tick() startButton.isEnabled = component!!.get(DataCardWriterBlockEntity.TOTAL_PROCESS_ID) <= 0 } } setRootPanel(root) configure("block.indrev.data_card_writer", ctx, playerInventory, blockInventory, invPos = 5.7, widgetPos = 0.85) val cardSlot = WItemSlot.of(blockInventory, 0) root.add(cardSlot, 4.0, 1.0) val inputOres = WItemSlot.of(blockInventory, 1, 6, 2) root.add(inputOres, 1.5, 3.5) val modifierSlots = WItemSlot.of(blockInventory, 13, 1, 3) root.add(modifierSlots, 8.0, 0.5) startButton.onClick = Runnable { val buf = PacketByteBufs.create() ctx.run { _, pos -> buf.writeBlockPos(pos) } ClientPlayNetworking.send(DataCardWriteStartPacket.START_PACKET, buf) } root.add(startButton, 7.65, 4.5) startButton.setSize(28, 20) val dataLabel = WLabel("Data") root.add(dataLabel, 1.5, 3.0) val timeLabel = WDynamicLabel { val processTime = component!!.get(DataCardWriterBlockEntity.PROCESS_ID) val totalProcessTime = component!!.get(DataCardWriterBlockEntity.TOTAL_PROCESS_ID) val remaining = (totalProcessTime - processTime) / 20 if (remaining > 0) "${remaining}s" else "" } timeLabel.setAlignment(HorizontalAlignment.CENTER) root.add(timeLabel, 4.1, 2.1) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("data_card_writer_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ElectricFurnaceFactoryScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.ElectricFurnaceFactoryBlockEntity import me.steven.indrev.gui.screenhandlers.ELECTRIC_FURNACE_FACTORY_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.upProcessBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class ElectricFurnaceFactoryScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( ELECTRIC_FURNACE_FACTORY_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.electric_furnace_factory", ctx, playerInventory, blockInventory, widgetPos = 0.15) withBlockEntity { blockEntity -> val slotsAmount = 5 val offset = 2.2 for ((index, slot) in blockEntity.inventoryComponent!!.inventory.inputSlots.withIndex()) { val inputSlot = WItemSlot.of(blockInventory, slot) root.add(inputSlot, offset + (index * 1.4), 0.6) } for (i in 0 until slotsAmount) { val processWidget = upProcessBar(blockEntity, ElectricFurnaceFactoryBlockEntity.CRAFTING_COMPONENT_START_ID + i) root.add(processWidget, offset + (i * 1.4), 1.7) } for ((index, slot) in blockEntity.inventoryComponent!!.inventory.outputSlots.withIndex()) { val outputSlot = WItemSlot.of(blockInventory, slot) root.add(outputSlot, offset + (index * 1.4), 2.9) outputSlot.addChangeListener { _, _, _, _ -> val player = playerInventory.player if (!player.world.isClient) { blockEntity.dropExperience(player) } } outputSlot.isInsertingAllowed = false } } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("electric_furnace_factory_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ElectricFurnaceScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity import me.steven.indrev.blockentities.crafters.ElectricFurnaceBlockEntity import me.steven.indrev.gui.screenhandlers.ELECTRIC_FURNACE_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class ElectricFurnaceScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( ELECTRIC_FURNACE_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.electric_furnace", ctx, playerInventory, blockInventory) val inputSlot = WItemSlot.of(blockInventory, 2) root.add(inputSlot, 3.3, 1.8) val processWidget = query { be -> processBar(be, ElectricFurnaceBlockEntity.CRAFTING_COMPONENT_ID) } root.add(processWidget, 4.45, 1.8) val outputSlot = WItemSlot.outputOf(blockInventory, 3) outputSlot.addChangeListener { _, _, _, _ -> val player = playerInventory.player if (!player.world.isClient) { ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? CraftingMachineBlockEntity<*> ?: return@run blockEntity.dropExperience(player) } } } outputSlot.isInsertingAllowed = false root.add(outputSlot, 5.94, 1.8) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("electric_furnace_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ElectrolyticSeparatorScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import me.steven.indrev.blockentities.crafters.ElectrolyticSeparatorBlockEntity import me.steven.indrev.gui.screenhandlers.ELECTROLYTIC_SEPARATOR_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.fluidTank import me.steven.indrev.gui.widgets.machines.leftProcessBar import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class ElectrolyticSeparatorScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( ELECTROLYTIC_SEPARATOR_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.electrolytic_separator", ctx, playerInventory, blockInventory) withBlockEntity { be -> val fluid = fluidTank(be, ElectrolyticSeparatorBlockEntity.INPUT_TANK_ID) root.add(fluid, 5.0, 1.0) val processWidget = processBar(be, ElectrolyticSeparatorBlockEntity.CRAFTING_COMPONENT_ID) root.add(processWidget, 6.2, 1.8) val leftProcessWidget = leftProcessBar(be, ElectrolyticSeparatorBlockEntity.CRAFTING_COMPONENT_ID) root.add(leftProcessWidget, 3.7, 1.8) val firstOutputFluid = fluidTank(be, ElectrolyticSeparatorBlockEntity.FIRST_OUTPUT_TANK_ID) root.add(firstOutputFluid, 2.5, 1.0) val secondOutputFluid = fluidTank(be, ElectrolyticSeparatorBlockEntity.SECOND_OUTPUT_TANK_ID) root.add(secondOutputFluid, 7.5, 1.0) } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("electrolytic_separator") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/FarmerScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WSlider import io.github.cottonmc.cotton.gui.widget.WSprite import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.blockentities.farms.AOEMachineBlockEntity import me.steven.indrev.gui.screenhandlers.FARMER_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot import me.steven.indrev.inventories.IRInventory import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText class FarmerScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( FARMER_HANDLER, syncId, playerInventory, ctx ) { private var value = -1 init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.farmer", ctx, playerInventory, blockInventory, invPos = 4.45) val inputFrame = WSprite(identifier("textures/gui/input_frame.png")) root.add(inputFrame, 1.9, 0.7) inputFrame.setSize(40, 44) val outputFrame = WSprite(identifier("textures/gui/output_frame.png")) root.add(outputFrame, 5.1, 0.7) outputFrame.setSize(58, 62) val outputSlot = WTooltipedItemSlot.of(blockInventory, (blockInventory as IRInventory).outputSlots.first(), 3, 3, TranslatableText("gui.indrev.output_slot_type")) outputSlot.isInsertingAllowed = false root.add(outputSlot, 5.2, 1.0) val inputSlot = WTooltipedItemSlot.of(blockInventory, (blockInventory as IRInventory).inputSlots.first(), 2, 2, TranslatableText("gui.indrev.farmer_input_slot_type")) root.add(inputSlot, 2.0, 1.0) val slider = WSlider(1, 10, Axis.HORIZONTAL) root.add(slider, 1.6, 3.6) slider.setSize(50, 20) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@run slider.value = blockEntity.range } slider.setValueChangeListener { newValue -> this.value = newValue } val text = WText({ TranslatableText("block.indrev.aoe.range", slider.value) }, HorizontalAlignment.LEFT) root.add(text, 1.8, 3.3) root.validate(this) } override fun close(player: PlayerEntity?) { super.close(player) AOEMachineBlockEntity.sendValueUpdatePacket(value, ctx) } companion object { val SCREEN_ID = identifier("farmer_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/FisherScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.gui.screenhandlers.FISHER_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import me.steven.indrev.utils.setIcon import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText class FisherScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( FISHER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.fisher", ctx, playerInventory, blockInventory) root.add(WItemSlot.of(blockInventory, 2, 2, 2), 3.7, 0.7) val fishingRodSlot = WTooltipedItemSlot.of(blockInventory, 1, TranslatableText("gui.indrev.fishingrod")) fishingRodSlot.setIcon(ctx, blockInventory, 1, FISHING_ROD_ICON) root.add(fishingRodSlot, 4.2, 3.0) root.validate(this) } companion object { val SCREEN_ID = identifier("fishing_farm_screen") val FISHING_ROD_ICON = identifier("textures/gui/fishing_rod_icon.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/FluidInfuserScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.FluidInfuserBlockEntity import me.steven.indrev.gui.screenhandlers.FLUID_INFUSER_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.fluidTank import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class FluidInfuserScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( FLUID_INFUSER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.fluid_infuser", ctx, playerInventory, blockInventory) val firstInput = WItemSlot.of(blockInventory, 2) root.add(firstInput, 3.7, 1.8) withBlockEntity { be -> val fluid = fluidTank(be, FluidInfuserBlockEntity.INPUT_TANK_ID) root.add(fluid, 2.5, 1.0) val processWidget = processBar(be, FluidInfuserBlockEntity.CRAFTING_COMPONENT_ID) root.add(processWidget, 5.0, 1.8) val outputStack = WItemSlot.of(blockInventory, 3) root.add(outputStack, 6.4, 1.8) val outputFluid = fluidTank(be, FluidInfuserBlockEntity.OUTPUT_TANK_ID) root.add(outputFluid, 7.7, 1.0) } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("fluid_infuser") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/GasBurningGeneratorScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import io.github.cottonmc.cotton.gui.widget.WSprite import me.steven.indrev.blockentities.generators.GasBurningGeneratorBlockEntity import me.steven.indrev.gui.screenhandlers.GAS_BURNING_GENERATOR_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.fluidTank import me.steven.indrev.gui.widgets.machines.fuelBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class GasBurningGeneratorScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( GAS_BURNING_GENERATOR_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.gas_generator", ctx, playerInventory, blockInventory) withBlockEntity { be -> val wFuel = fuelBar(be) root.add(wFuel, 3.5, 3.2) wFuel.setSize(14, 14) val fluid = fluidTank(be, GasBurningGeneratorBlockEntity.TANK_ID) root.add(fluid, 3.5, 0.8) } val processSprite = WSprite(identifier("textures/gui/widget_processing_empty.png")) root.add(processSprite, 4.9, 1.5) val ashSlot = WItemSlot.of(blockInventory, 1) root.add(ashSlot, 6.5, 1.5) root.validate(this) } companion object { val SCREEN_ID = identifier("gas_burning_generator_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/HeatGeneratorScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.blockentities.generators.HeatGeneratorBlockEntity import me.steven.indrev.gui.screenhandlers.HEAT_GENERATOR_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.fluidTank import me.steven.indrev.gui.widgets.misc.WStaticTooltip import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting class HeatGeneratorScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( HEAT_GENERATOR_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.heat_generator", ctx, playerInventory, blockInventory, invPos = 4.25) val info = WStaticTooltip() root.add(info, 2.3, 0.9) info.setSize(90, 55) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? HeatGeneratorBlockEntity ?: return@run val generatingText = WText({ val consumptionRate = (blockEntity.getConsumptionRate(component!![HeatGeneratorBlockEntity.CONSUMPTION_RATIO_ID])).toString() TranslatableText("gui.indrev.heatgen.title", LiteralText(consumptionRate).formatted(Formatting.DARK_RED)).formatted(Formatting.RED) }, HorizontalAlignment.LEFT) root.add(generatingText, 2.5, 1.0) } root.add(WText(TranslatableText("gui.indrev.heatgen.pertick").formatted(Formatting.RED), HorizontalAlignment.LEFT), 2.5, 1.6) val amount = WText({ val ratio = component!!.get(HeatGeneratorBlockEntity.GENERATION_RATIO_ID) TranslatableText("gui.indrev.heatgen.generating", LiteralText(ratio.toString()).formatted(Formatting.WHITE)).formatted(Formatting.BLUE) }, HorizontalAlignment.LEFT) root.add(amount, 2.5, 2.6) root.add(WText(TranslatableText("gui.indrev.heatgen.pertick").formatted(Formatting.BLUE), HorizontalAlignment.LEFT), 2.5, 3.2) withBlockEntity { be -> val fluid = fluidTank(be, HeatGeneratorBlockEntity.TANK_ID) root.add(fluid, 8.0, 0.6) } root.validate(this) } companion object { val SCREEN_ID = identifier("heat_generator_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/LaserEmitterScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WLabel import io.github.cottonmc.cotton.gui.widget.data.Insets import me.steven.indrev.blockentities.laser.LaserBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.LASER_HANDLER import me.steven.indrev.gui.widgets.machines.energyBar import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText class LaserEmitterScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( LASER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) root.insets = Insets.ROOT_PANEL val label = WLabel(TranslatableText("block.indrev.laser_emitter_mk4")) root.add(label, 0, 0) label.setSize(75, 0) withBlockEntity { be -> val energy = energyBar(be) root.add(energy, 1, 1) energy.setLocation(9 + 16 + root.insets.left, 16 + root.insets.top) } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true override fun addPainters() { if (rootPanel != null && !fullscreen) { rootPanel.backgroundPainter = BackgroundPainter.VANILLA } } companion object { val SCREEN_ID = identifier("laser") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/LazuliFluxContainerScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity import me.steven.indrev.gui.screenhandlers.BATTERY_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.machines.energyBar import me.steven.indrev.gui.widgets.misc.WPlayerRender import me.steven.indrev.gui.widgets.misc.WStaticTooltip import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.utils.add import me.steven.indrev.utils.getEnergySlotPainter import me.steven.indrev.utils.identifier import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ArmorItem import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText import net.minecraft.util.Identifier import java.util.function.Predicate class LazuliFluxContainerScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( BATTERY_HANDLER, syncId, playerInventory, ctx ) { val shieldPainter by lazy { getSlotPainter(40, Identifier("minecraft", "textures/item/empty_armor_slot_shield.png")) } val helmetPainter by lazy { getSlotPainter(39, Identifier("minecraft", "textures/item/empty_armor_slot_helmet.png")) } val chestplatePainter by lazy { getSlotPainter(38, Identifier("minecraft", "textures/item/empty_armor_slot_chestplate.png")) } val leggingsPainter by lazy { getSlotPainter(37, Identifier("minecraft", "textures/item/empty_armor_slot_leggings.png")) } val bootsPainter by lazy { getSlotPainter(36, Identifier("minecraft", "textures/item/empty_armor_slot_boots.png")) } init { val root = WGridPanel() setRootPanel(root) root.setSize(150, 120) root.add( WText(TranslatableText("block.indrev.lazuli_flux_container_1"), HorizontalAlignment.CENTER, 0x404040), 5.95 + 0.3, 0.0 ) root.add( WText(TranslatableText("block.indrev.lazuli_flux_container_2"), HorizontalAlignment.CENTER, 0x404040), 5.95 + 0.3, 0.7 ) withBlockEntity { be -> val wEnergy = energyBar(be) root.add(wEnergy, 8.4, 0.4) } ctx.run { world, _ -> val itemSlot = WItemSlot.of(blockInventory, 0) if (world.isClient) itemSlot.backgroundPainter = getEnergySlotPainter(blockInventory, 0) root.add(itemSlot, 5.7, 1.3) root.add(createPlayerInventoryPanel(), 0.0, 4.2) val boots = WItemSlot.of(playerInventory, 36) if (world.isClient) boots.backgroundPainter = bootsPainter boots.filter = Predicate { stack -> val item = stack.item item is ArmorItem && item.slotType == EquipmentSlot.FEET } root.add(boots, 0, 3) val leggings = WItemSlot.of(playerInventory, 37) if (world.isClient) leggings.backgroundPainter = leggingsPainter leggings.filter = Predicate { stack -> val item = stack.item item is ArmorItem && item.slotType == EquipmentSlot.LEGS } root.add(leggings, 0, 2) val chestplate = WItemSlot.of(playerInventory, 38) if (world.isClient) chestplate.backgroundPainter = chestplatePainter chestplate.filter = Predicate { stack -> val item = stack.item item is ArmorItem && item.slotType == EquipmentSlot.CHEST } root.add(chestplate, 0, 1) val helmet = WItemSlot.of(playerInventory, 39) if (world.isClient) helmet.backgroundPainter = helmetPainter helmet.filter = Predicate { stack -> val item = stack.item item is ArmorItem && item.slotType == EquipmentSlot.HEAD } root.add(helmet, 0, 0) val shield = WItemSlot.of(playerInventory, 40) if (world.isClient) shield.backgroundPainter = shieldPainter root.add(shield, 3.8, 3.0) } val playerBg = WStaticTooltip() root.add(playerBg, 1.3, 0.2) playerBg.setSize(40, 65) val playerWidget = WPlayerRender() root.add(playerWidget, 2.4, 3.5) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true private fun getSlotPainter(slot: Int, identifier: Identifier) = BackgroundPainter { matrices, left, top, panel -> BackgroundPainter.SLOT.paintBackground(matrices, left, top, panel) if (playerInventory.getStack(slot).isEmpty) ScreenDrawing.texturedRect(matrices, left + 1, top + 1, 16, 16, identifier, -1) } companion object { val SCREEN_ID = identifier("battery_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/MiningRigComputerScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.* import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.api.OreDataCards import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blockentities.miningrig.DrillBlockEntity import me.steven.indrev.blockentities.miningrig.MiningRigBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.MINING_RIG_HANDLER import me.steven.indrev.gui.widgets.misc.WCircleProgressBar import me.steven.indrev.gui.widgets.misc.WStaticTooltip import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting class MiningRigComputerScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( MINING_RIG_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.mining_rig", ctx, playerInventory, blockInventory, invPos = 6.0, widgetPos = 1.5) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? MiningRigBlockEntity ?: return@run val activeDrills = blockEntity.getActiveDrills() val bg = WStaticTooltip() root.add(bg, 1.0, 0.9) bg.setSize(142, 85) root.add(WText(LiteralText("Insert"), HorizontalAlignment.CENTER, 0x8080), 7.5, 1.9) root.add(WText(LiteralText("data card"), HorizontalAlignment.CENTER, 0x8080), 7.5, 2.6) val cardSlot = WTooltipedItemSlot.of(blockInventory, 0, TranslatableText("gui.indrev.scan_output_slo1t_type")) root.add(cardSlot, 7.0, 3.3) root.add(WText(TranslatableText("block.indrev.drill.active"), HorizontalAlignment.CENTER, 0x8080), 3.35, 1.0) val requiredPower = component!!.get(MiningRigBlockEntity.ENERGY_REQUIRED_ID).toDouble() when { component!!.get(MachineBlockEntity.ENERGY_ID) < requiredPower -> { val sprite = object : WSprite(identifier("textures/gui/not_enough_power.png")) { override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add( TranslatableText("block.indrev.drill.not_enough_power").formatted(Formatting.DARK_RED), TranslatableText("block.indrev.drill.power_required", requiredPower) .formatted(Formatting.DARK_RED) ) } } root.add(sprite, 3.0, 1.5) sprite.setSize(16, 16) } activeDrills.isEmpty() -> { val noDrillsText = TranslatableText("block.indrev.drill.no_drills") root.add(WText(noDrillsText, HorizontalAlignment.CENTER, 0x404040), 3.35, 1.75) } else -> { activeDrills.forEachIndexed { index, drill -> val panel = getDrillInfo(drill) root.add(panel, 1.4 + if (index > 3) index - 4 else index, 1.9 + if (index > 3) 1.1 else 0.0) } } } } root.add(WText({ val data = OreDataCards.readNbt(blockInventory.getStack(0)) ?: return@WText LiteralText.EMPTY val remaining = data.maxCycles - data.used LiteralText("$remaining") }, HorizontalAlignment.CENTER, 0x8080), 3.35, 4.4) root.add(WText(TranslatableText("block.indrev.mining_rig.mined"), HorizontalAlignment.CENTER, 0x8080), 3.35, 5.0) root.validate(this) } private fun getDrillInfo(blockEntity: DrillBlockEntity): WGridPanel { val itemStack = blockEntity.inventory[0] val panel = WGridPanel() panel.add(WItem(itemStack), 0, 0) ctx.run { world, pos -> val miningRig = world.getBlockEntity(pos) as? MiningRigBlockEntity ?: return@run val tmpPos = miningRig.pos.mutableCopy() tmpPos.y = blockEntity.pos.y val offset = blockEntity.pos.subtract(tmpPos) val index = MiningRigBlockEntity.VALID_DRILL_POSITIONS.indexOf(offset) val progress = object : WCircleProgressBar({ component!!.get(MiningRigBlockEntity.START_DRILL_ID + index).toInt() }, { component!![MiningRigBlockEntity.MAX_SPEED_ID] }, { _, _ -> 0xFF007E7E.toInt()}) { override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add(itemStack.name) val seconds = blockEntity.getSpeedMultiplier() tooltip?.add(TranslatableText("block.indrev.drill.faster", seconds).formatted(Formatting.DARK_GRAY)) if (blockEntity.position > 0) tooltip?.add(TranslatableText("block.indrev.drill.activating").formatted(Formatting.DARK_GRAY)) } } panel.add(progress, 0, 0) } return panel } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("mining_rig") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/MiningRigDrillScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.blockentities.miningrig.DrillBlockEntity import me.steven.indrev.gui.screenhandlers.DRILL_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.utils.add import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText import java.util.function.Predicate class MiningRigDrillScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( DRILL_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) root.add(WText(TranslatableText("block.indrev.drill"), HorizontalAlignment.LEFT, 0x404040), 0.0, -0.1) val slot = WItemSlot.of(blockInventory, 0) slot.filter = Predicate { stack -> DrillBlockEntity.isValidDrill(stack.item) } root.add(slot, 4, 2) root.add(createPlayerInventoryPanel(), 0.0, 3.8) root.validate(this) } companion object { val SCREEN_ID = identifier("drill") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ModularWorkbenchScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import com.mojang.blaze3d.systems.RenderSystem import io.github.cottonmc.cotton.gui.ValidatedSlot import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.* import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import io.github.cottonmc.cotton.gui.widget.data.InputResult import io.github.cottonmc.cotton.gui.widget.icon.ItemIcon import me.steven.indrev.WCustomTabPanel import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.modularworkbench.ModularWorkbenchBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.MODULAR_WORKBENCH_HANDLER import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.upProcessBar import me.steven.indrev.gui.widgets.misc.WStaticTooltip import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot import me.steven.indrev.items.armor.IRModularArmorItem import me.steven.indrev.items.armor.IRModuleItem import me.steven.indrev.packets.common.SelectModuleOnWorkbenchPacket import me.steven.indrev.recipes.machines.ModuleRecipe import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.tools.modular.ArmorModule import me.steven.indrev.tools.modular.IRModularItem import me.steven.indrev.utils.* import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.fabricmc.fabric.api.util.TriState import net.fabricmc.loader.api.FabricLoader import net.minecraft.client.MinecraftClient import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.player.PlayerInventory import net.minecraft.inventory.Inventory import net.minecraft.item.ItemStack import net.minecraft.screen.ScreenHandlerContext import net.minecraft.sound.SoundEvents import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.Identifier import kotlin.math.floor import kotlin.math.sin // Careful, here be dragons. // All the `index + 3` are because the slot numbers changed and it was easier to do this, if you don't like it don't read it :) class ModularWorkbenchScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( MODULAR_WORKBENCH_HANDLER, syncId, playerInventory, ctx ) { private val slotLayout = hashMapOf>() private var slotsPanel = WPlainPanel() private val selected: ModuleRecipe? get() { var r: ModuleRecipe? = null ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? ModularWorkbenchBlockEntity r = blockEntity?.recipe } return r } init { slotLayout[1] = arrayOf(WToggleableItemSlot(0, 2 * 18, 0, false)) slotLayout[2] = arrayOf( WToggleableItemSlot(0, 0,2 * 18,false), WToggleableItemSlot(1, 4 * 18, 2 * 18, false) ) slotLayout[3] = arrayOf( WToggleableItemSlot(0, 2 * 18, 0, false), WToggleableItemSlot(1, 0, 4 * 17, false), WToggleableItemSlot(2, 4 * 18, 4 * 17, false) ) slotLayout[4] = arrayOf( WToggleableItemSlot(0, 2 * 18, 0, false), WToggleableItemSlot(1, 0, 2 * 18, false), WToggleableItemSlot(2, 4 * 18, 2 * 18, false), WToggleableItemSlot(3, 2 * 18, 4 * 18, false) ) slotLayout[5] = arrayOf( WToggleableItemSlot(0, 2 * 18, 0, false), WToggleableItemSlot(1, 0, 2 * 18, false), WToggleableItemSlot(2, 4 * 18, 2 * 18, false), WToggleableItemSlot(3, 1 * 14, 4 * 18, false), WToggleableItemSlot(4, 3 * 20, 4 * 18, false) ) slotLayout[6] = arrayOf( WToggleableItemSlot(0, 2 * 18, 0, false), WToggleableItemSlot(1, 0 * 18, 1 * 18, false), WToggleableItemSlot(2, 4 * 18, 1 * 18, false), WToggleableItemSlot(3, 0 * 14, 3 * 18, false), WToggleableItemSlot(4, 4 * 18, 3 * 18, false), WToggleableItemSlot(5, 2 * 18, 4 * 18, false) ) slotLayout.forEach { (_, slots) -> slots.forEach { slotsPanel.add(it, it.x, it.y) } } if (selected != null) layoutSlots(selected!!) val root = WCustomTabPanel() setRootPanel(root) root.add(buildInstallPanel()) { it.icon(ItemIcon(MachineRegistry.MODULAR_WORKBENCH_REGISTRY.block(Tier.MK4).asItem())) } root.add(buildCraftPanel()) { it.icon(ItemIcon(IRItemRegistry.PROTECTION_MODULE_ITEM)) } root.validate(this) } private fun buildCraftPanel(): WGridPanel { val panel = WGridPanel() val modules = WGridPanel() ctx.run { world, pos -> world.recipeManager.getRecipes(ModuleRecipe.TYPE).entries.forEachIndexed { index, (id, recipe) -> val button = WModule(recipe.outputs.first().stack) button.clickAction = { val buf = PacketByteBufs.create() buf.writeInt(syncId) buf.writeIdentifier(id) buf.writeBlockPos(pos) ClientPlayNetworking.send(SelectModuleOnWorkbenchPacket.MODULE_SELECT_PACKET, buf) layoutSlots(recipe) slotsPanel.addPainters() } modules.add(button, index % 3, floor(index / 3.0).toInt()) } } val outputSlot = WCraftingItemSlot(blockInventory, 15, true) slotsPanel.add(outputSlot, 2 * 18, 2 * 18) panel.add(slotsPanel, 4, 0) val scrollPanel = WScrollPanel(modules) scrollPanel.isScrollingVertically = TriState.TRUE scrollPanel.isScrollingHorizontally = TriState.FALSE panel.add(scrollPanel, 0, 0, 4, 5) scrollPanel.setSize(3 * 18 + 8, 5 * 18 - 4) panel.add(createPlayerInventoryPanel(), 0, 5) return panel } fun layoutSlots(recipe: ModuleRecipe) { val inputs = recipe.input slotLayout.forEach { (size, slots) -> slots.forEachIndexed { index, slot -> slot.hidden = size != inputs.size if (!slot.hidden && FabricLoader.getInstance().environmentType == EnvType.CLIENT) slot.preview = inputs[index].ingredient.matchingStacks[0] } } } private fun buildInstallPanel(): WGridPanel { val root = WGridPanel() configure("block.indrev.modular_workbench", ctx, playerInventory, blockInventory, root, invPos = 5.0, widgetPos = 0.9) val armorSlot = WTooltipedItemSlot.of(blockInventory, 2, TranslatableText("gui.indrev.modular_armor_slot_type")) root.add(armorSlot, 1.5, 3.5) val moduleSlot = WTooltipedItemSlot.of(blockInventory, 1, TranslatableText("gui.indrev.module_slot_type")) root.add(moduleSlot, 1.5, 1.0) val process = query { upProcessBar(it, ModularWorkbenchBlockEntity.INSTALL_TIME_ID, ModularWorkbenchBlockEntity.MAX_INSTALL_TIME_ID) } root.add(process, 1.5, 2.2) val info = WStaticTooltip() info.setSize(100, 60) root.add(info, 3, 1) addTextInfo(root) return root } // Are you seriously still going? private fun addTextInfo(panel: WGridPanel) { val armorInfoText = WText({ val stack = blockInventory.getStack(2) if (!stack.isEmpty) TranslatableText(stack.item.translationKey).formatted(Formatting.DARK_PURPLE, Formatting.UNDERLINE) else LiteralText.EMPTY }, HorizontalAlignment.LEFT) val moduleToInstall = WText({ val (stack, item) = blockInventory.getStack(1) if (!stack.isEmpty && item is IRModuleItem) { TranslatableText(item.translationKey).formatted(Formatting.GRAY, Formatting.ITALIC) } else LiteralText.EMPTY }, HorizontalAlignment.LEFT) val modulesInstalled = WText({ val (stack, item) = blockInventory.getStack(2) if (!stack.isEmpty && item is IRModularItem<*>) { val modules = item.getCount(stack).toString() MODULE_COUNT().append(LiteralText(modules).formatted(Formatting.WHITE)) } else LiteralText.EMPTY }, HorizontalAlignment.LEFT) val shield = WText({ val (stack, item) = blockInventory.getStack(2) if (!stack.isEmpty && item is IRModularArmorItem) { val shield = item.getMaxShield(ArmorModule.PROTECTION.getLevel(stack)).toString() SHIELD_TEXT().append(LiteralText(shield).formatted(Formatting.WHITE)) } else LiteralText.EMPTY }, HorizontalAlignment.LEFT) val installing = WText({ val state = component!!.get(ModularWorkbenchBlockEntity.STATE_ID) if (state == ModularWorkbenchBlockEntity.State.INSTALLING) { INSTALLING_TEXT() } else LiteralText.EMPTY }, HorizontalAlignment.LEFT) val progress = WText({ val stack = blockInventory.getStack(1) val item = stack.item if (!stack.isEmpty && item is IRModuleItem) { val progress = component!!.get(ModularWorkbenchBlockEntity.INSTALL_TIME_ID) when (component!!.get(ModularWorkbenchBlockEntity.STATE_ID)) { ModularWorkbenchBlockEntity.State.INCOMPATIBLE -> INCOMPATIBLE_TEXT() ModularWorkbenchBlockEntity.State.MAX_LEVEL -> MAX_LEVEL_TEXT() else -> { val percent = ((progress / component!!.get(ModularWorkbenchBlockEntity.MAX_INSTALL_TIME_ID).toDouble().coerceAtLeast(1.0)) * 100).toInt() PROGRESS_TEXT().append(LiteralText("$percent%")) } } } else LiteralText.EMPTY }, HorizontalAlignment.LEFT) panel.add(installing, 3, 3) panel.add(progress, 3, 4) panel.add(armorInfoText, 3, 1) panel.add(modulesInstalled, 3.0, 1.5) panel.add(shield, 3, 2) panel.add(moduleToInstall, 3.0, 3.5) } @Environment(EnvType.CLIENT) override fun addPainters() { val offset = 178 - rootPanel.width (rootPanel as WCustomTabPanel).setForceBackgroundPainter( BackgroundPainter.createLightDarkVariants( BackgroundPainter.createNinePatch(Identifier("libgui", "textures/widget/panel_light.png")).setPadding(8).setLeftPadding(0) .setRightPadding(offset).setTopPadding(-25), BackgroundPainter.createNinePatch(Identifier("libgui", "textures/widget/panel_dark.png")).setPadding(8) .setRightPadding(offset) )) } inner class WModule(private val itemStack: ItemStack) : WWidget() { var clickAction: () -> Unit = {} override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { val hovered = mouseX >= 0 && mouseY >= 0 && mouseX < width && mouseY < height ScreenDrawing.drawBeveledPanel(matrices, x, y, width, height) if (hovered) ScreenDrawing.coloredRect(matrices, x, y, width, height, 0x887d88ff.toInt()) MinecraftClient.getInstance().itemRenderer.renderInGui(itemStack, x + 1, y + 1) } override fun addTooltip(tooltip: TooltipBuilder?) { val texts = itemStack.getTooltip(MinecraftClient.getInstance().player) { MinecraftClient.getInstance().options.advancedItemTooltips } tooltip?.add(*texts.toTypedArray()) } override fun onClick(x: Int, y: Int, button: Int): InputResult { MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0f)) clickAction() return InputResult.PROCESSED } } inner class WToggleableItemSlot(private val index: Int, x: Int, y: Int, big: Boolean) : WItemSlot(blockInventory, index + 3, 1, 1, big) { var preview: ItemStack? = null init { this.x = x this.y = y } var hidden = true override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { if (!hidden) { super.paint(matrices, x, y, mouseX, mouseY) if (preview != null) { val renderer = MinecraftClient.getInstance().itemRenderer renderer.renderInGui(preview, x + 1, y + 1) RenderSystem.disableDepthTest() ScreenDrawing.coloredRect(matrices, x + 1, y + 1, 16, 16, 0xb08b8b8b.toInt()) } } } override fun addTooltip(tooltip: TooltipBuilder?) { if (!blockInventory.getStack(index + 3).isEmpty) return val texts = preview?.getTooltip(MinecraftClient.getInstance().player) { MinecraftClient.getInstance().options.advancedItemTooltips } ?: return tooltip?.add(*texts.toTypedArray()) } override fun createSlotPeer(inventory: Inventory, index: Int, x: Int, y: Int): ValidatedSlot { return WToggleableSlot(inventory, index, x, y) { hidden } } } inner class WCraftingItemSlot(inventory: Inventory, index: Int, big: Boolean) : WItemSlot(inventory, index, 1, 1, big) { init { isInsertingAllowed = false } override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { super.paint(matrices, x, y, mouseX, mouseY) if (selected != null) { val cur = component!!.get(ModularWorkbenchBlockEntity.PROCESS_TIME_ID) val max = component!!.get(ModularWorkbenchBlockEntity.MAX_PROCESS_TIME_ID) val renderer = MinecraftClient.getInstance().itemRenderer renderer.renderInGui(selected!!.outputs[0].stack, x + 1, y + 1) RenderSystem.disableDepthTest() val a = 255 - ((cur / max.toDouble()) * 255).toInt() ScreenDrawing.coloredRect(matrices, x + 1, y + 1, 16, 16, a shl 24 or 0x8b8b8b) RenderSystem.enableDepthTest() if (cur > 0 && max > 0 && cur != max) { val txt = "${(cur / max.toDouble() * 100).toInt() }%" val width = MinecraftClient.getInstance().textRenderer.getWidth(txt) MinecraftClient.getInstance().textRenderer.draw(matrices, txt, x.toFloat() + 10 - (width / 2), y.toFloat() + 25, 0x404040) val s = sin(world.time.toDouble() / 5) * 10 ScreenDrawing.coloredRect(matrices, x - 3, (y + s).toInt() + 8, this.width + 6, 2, 0x99578bfa.toInt()) } } } override fun addTooltip(tooltip: TooltipBuilder?) { val texts = selected?.outputs?.get(0)?.stack?.getTooltip(MinecraftClient.getInstance().player) { MinecraftClient.getInstance().options.advancedItemTooltips } ?: return val cur = component!!.get(ModularWorkbenchBlockEntity.PROCESS_TIME_ID) val max = component!!.get(ModularWorkbenchBlockEntity.MAX_PROCESS_TIME_ID) if (max > 0 && cur != max) { tooltip?.add(LiteralText("Crafting: ")) tooltip?.add(LiteralText.EMPTY) } tooltip?.add(*texts.toTypedArray()) } } inner class WToggleableSlot(inventory: Inventory, index: Int, x: Int, y: Int, val hidden: () -> Boolean) : ValidatedSlot(inventory, index, x, y) { override fun getMaxItemCount(): Int { return selected?.input?.get(index - 3)?.count ?: 0 } override fun canInsert(stack: ItemStack?): Boolean { return !hidden() && super.canInsert(stack) } override fun isEnabled(): Boolean { return !hidden() && super.isEnabled() } } companion object { val SCREEN_ID = identifier("modular_workbench_screen") val SHIELD_TEXT = { TranslatableText("gui.indrev.shield").formatted(Formatting.BLUE) } val PROGRESS_TEXT = { TranslatableText("gui.indrev.progress").formatted(Formatting.BLUE) } val MODULE_COUNT = { TranslatableText("gui.indrev.modules_installed").formatted(Formatting.BLUE) } val INSTALLING_TEXT = { TranslatableText("gui.indrev.installing").formatted(Formatting.DARK_PURPLE, Formatting.UNDERLINE) } val INCOMPATIBLE_TEXT = { TranslatableText("gui.indrev.incompatible").formatted(Formatting.RED) } val MAX_LEVEL_TEXT = { TranslatableText("gui.indrev.max_level").formatted(Formatting.RED) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/PulverizerFactoryScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.PulverizerFactoryBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.PULVERIZER_FACTORY_HANDLER import me.steven.indrev.gui.widgets.machines.upProcessBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class PulverizerFactoryScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( PULVERIZER_FACTORY_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.pulverizer_factory", ctx, playerInventory, blockInventory, widgetPos = 0.15) withBlockEntity { blockEntity -> val slotsAmount = 5 val offset = 2.2 for ((index, slot) in blockEntity.inventoryComponent!!.inventory.inputSlots.withIndex()) { val inputSlot = WItemSlot.of(blockInventory, slot) root.add(inputSlot, offset + (index * 1.4), 0.6) } for (i in 0 until slotsAmount) { val processWidget = upProcessBar(blockEntity, PulverizerFactoryBlockEntity.CRAFTING_COMPONENT_START_ID + i) root.add(processWidget, offset + (i * 1.4), 1.7) } for ((index, slot) in blockEntity.inventoryComponent!!.inventory.outputSlots.withIndex()) { val outputSlot = WItemSlot.of(blockInventory, slot) root.add(outputSlot, offset + (index * 1.4), 2.9) outputSlot.addChangeListener { _, _, _, _ -> val player = playerInventory.player if (!player.world.isClient) { blockEntity.dropExperience(player) } } outputSlot.isInsertingAllowed = false } } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("pulverizer_factory_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/PulverizerScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.PulverizerBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.PULVERIZER_HANDLER import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class PulverizerScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( PULVERIZER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.pulverizer", ctx, playerInventory, blockInventory) val inputSlot = WItemSlot.of(blockInventory, 2) root.add(inputSlot, 3.3, 1.2) val processWidget = query { be -> processBar(be, PulverizerBlockEntity.CRAFTING_COMPONENT_ID) } root.add(processWidget, 4.4, 1.2) val outputSlot = WItemSlot.outputOf(blockInventory, 3) outputSlot.isInsertingAllowed = false root.add(outputSlot, 5.84, 1.2) val extraOutputSlot = WItemSlot.of(blockInventory, 4) extraOutputSlot.isInsertingAllowed = false root.add(extraOutputSlot, 5.84, 2.5) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("pulverizer_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/PumpScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WDynamicLabel import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import io.github.cottonmc.cotton.gui.widget.WLabel import me.steven.indrev.blockentities.crafters.PulverizerBlockEntity import me.steven.indrev.blockentities.farms.PumpBlockEntity import me.steven.indrev.blockentities.generators.GasBurningGeneratorBlockEntity import me.steven.indrev.blocks.machine.PumpBlock import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.PULVERIZER_HANDLER import me.steven.indrev.gui.screenhandlers.PUMP_HANDLER import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.fluidTank import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.LiteralText class PumpScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( PUMP_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.pump", ctx, playerInventory, blockInventory) withBlockEntity { be -> val fluid = fluidTank(be, PumpBlockEntity.TANK_ID) root.add(fluid, 8, 1) } root.add(WLabel("1 bucket every" ), 1, 1) root.add(WDynamicLabel { "${component!!.get(PumpBlockEntity.SPEED_ID)} ticks" }, 1, 2) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("pump_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/RancherScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.github.cottonmc.cotton.gui.widget.* import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import io.github.cottonmc.cotton.gui.widget.icon.ItemIcon import io.netty.buffer.Unpooled import me.steven.indrev.WCustomTabPanel import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.farms.AOEMachineBlockEntity import me.steven.indrev.blockentities.farms.RancherBlockEntity import me.steven.indrev.gui.properties.SyncableProperty import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.RANCHER_HANDLER import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.packets.common.UpdateRancherConfigPacket import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.registry.MachineRegistry import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText import net.minecraft.util.Identifier class RancherScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( RANCHER_HANDLER, syncId, playerInventory, ctx ) { var value = -1 val feedBabyText = WToggleButton() var feedBabies: Boolean = false val mateAdultsText = WToggleButton() var mateAdults: Boolean = false val matingLimitText = WTextField() var matingLimit: Int = 0 val killAfterText = WTextField() var killAfter: Int = 0 init { val root = WCustomTabPanel() setRootPanel(root) feedBabies = component!![RancherBlockEntity.FEED_BABIES_ID] mateAdults = component!![RancherBlockEntity.MATE_ADULTS] // matingLimit = properties[RancherBlockEntity.MATING_LIMIT] //killAfter = properties[RancherBlockEntity.KILL_AFTER] root.add(buildMainPanel()) { it.icon(ItemIcon(RANCHER_MK4.asItem())) } root.add(buildConfigPanel()) { it.icon(ItemIcon(IRItemRegistry.WRENCH)) } root.validate(this) } private fun buildMainPanel(): WWidget { val mainPanel = WGridPanel() configure("block.indrev.rancher", ctx, playerInventory, blockInventory, mainPanel, invPos = 4.45) val inputFrame = WSprite(identifier("textures/gui/input_frame.png")) mainPanel.add(inputFrame, 1.9, 0.7) inputFrame.setSize(40, 44) val outputFrame = WSprite(identifier("textures/gui/output_frame.png")) mainPanel.add(outputFrame, 5.1, 0.7) outputFrame.setSize(58, 62) mainPanel.add( WItemSlot.of(blockInventory, 6, 3, 3), 5.2, 1.0 ) mainPanel.add( WItemSlot.of(blockInventory, 2, 2, 2), 2.0, 1.0 ) val slider = WSlider(1, 9, Axis.HORIZONTAL) mainPanel.add(slider, 1.6, 3.6) slider.setSize(50, 20) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@run slider.value = blockEntity.range } slider.setValueChangeListener { newValue -> this.value = newValue } val text = WText({ TranslatableText("block.indrev.aoe.range", slider.value) }, HorizontalAlignment.LEFT) mainPanel.add(text, 1.8, 3.3) return mainPanel } private fun buildConfigPanel(): WWidget { val configPanel = WGridPanel() val feedBabyInput = WGridPanel() feedBabyInput.add(WLabel("Feed babies"), 0.0, 0.25) feedBabyText.toggle = feedBabies feedBabyText.setOnToggle { v -> feedBabies = v } feedBabyInput.add(feedBabyText, 4.4, 0.0) configPanel.add(feedBabyInput, 1, 1) val mateAdultsInput = WGridPanel() mateAdultsInput.add(WLabel("Mate adults"), 0.0, 0.25) mateAdultsText.toggle = mateAdults mateAdultsText.setOnToggle { v -> mateAdults = v } mateAdultsInput.add(mateAdultsText, 4.4, 0.0) configPanel.add(mateAdultsInput, 1, 3) val matingLimitInput = WGridPanel() matingLimitInput.add(WLabel("Mating limit"), 0.0, 0.25) matingLimitInput.add(matingLimitText, 4, 0) matingLimitText.setSize(34, 18) configPanel.add(matingLimitInput, 1, 5) val killAfterInput = WGridPanel() killAfterInput.add(WLabel("Kill after"), 0.0, 0.25) killAfterInput.add(killAfterText, 4, 0) killAfterText.setSize(34, 18) configPanel.add(killAfterInput, 1, 7) return configPanel } override fun onSyncedProperty(index: Int, property: SyncableProperty<*>) { when (index) { RancherBlockEntity.KILL_AFTER -> { killAfter = property.value as Int killAfterText.text = killAfter.toString() } RancherBlockEntity.MATING_LIMIT -> { matingLimit = property.value as Int matingLimitText.text = matingLimit.toString() } RancherBlockEntity.MATE_ADULTS -> { mateAdults = property.value as Boolean mateAdultsText.toggle = mateAdults } RancherBlockEntity.FEED_BABIES_ID -> { feedBabies = property.value as Boolean feedBabyText.toggle = feedBabies } } } @Environment(EnvType.CLIENT) override fun addPainters() { val offset = 178 - rootPanel.width (rootPanel as WCustomTabPanel).setForceBackgroundPainter( BackgroundPainter.createLightDarkVariants( BackgroundPainter.createNinePatch(Identifier("libgui", "textures/widget/panel_light.png")).setPadding(8).setLeftPadding(0) .setRightPadding(offset).setTopPadding(-25), BackgroundPainter.createNinePatch(Identifier("libgui", "textures/widget/panel_dark.png")).setPadding(8) .setRightPadding(offset) )) } override fun close(player: PlayerEntity?) { super.close(player) AOEMachineBlockEntity.sendValueUpdatePacket(value, ctx) ctx.run { world, pos -> if (world.isClient) { val buf = PacketByteBuf(Unpooled.buffer()) buf.writeBlockPos(pos) buf.writeBoolean(feedBabies) buf.writeBoolean(mateAdults) buf.writeInt(matingLimitText.text.toIntOrNull() ?: matingLimit) buf.writeInt(killAfterText.text.toIntOrNull() ?: killAfter) ClientPlayNetworking.send(UpdateRancherConfigPacket.SYNC_RANCHER_CONFIG, buf) } } } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("rancher_screen") val RANCHER_MK4 by lazy { MachineRegistry.RANCHER_REGISTRY.block(Tier.MK4) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/RecyclerScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.RecyclerBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.RECYCLER_HANDLER import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class RecyclerScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( RECYCLER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.recycler", ctx, playerInventory, blockInventory) val inputSlot = WItemSlot.of(blockInventory, 2) root.add(inputSlot, 2.8, 1.8) withBlockEntity { be -> val processWidget = processBar(be, RecyclerBlockEntity.CRAFTING_COMPONENT_ID) root.add(processWidget, 3.95, 1.8) } val outputSlot = WItemSlot.outputOf(blockInventory, 3) outputSlot.isInsertingAllowed = false root.add(outputSlot, 5.44, 1.8) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("recycler") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SawmillScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.SawmillBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.SAWMILL_HANDLER import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class SawmillScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( SAWMILL_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.sawmill", ctx, playerInventory, blockInventory) val inputSlot = WItemSlot.of(blockInventory, 2) root.add(inputSlot, 3.0, 1.8) val processWidget = query { be -> processBar(be, SawmillBlockEntity.CRAFTING_COMPONENT_ID) } root.add(processWidget, 4.15, 1.8) val outputSlots = WItemSlot.of(blockInventory, 3, 2, 2) outputSlots.isInsertingAllowed = false root.add(outputSlots, 5.5, 1.3) root.validate(this) } companion object { val SCREEN_ID = identifier("sawmill_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SlaughterScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WSlider import io.github.cottonmc.cotton.gui.widget.WSprite import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.blockentities.farms.AOEMachineBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.SLAUGHTER_HANDLER import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot import me.steven.indrev.inventories.IRInventory import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import me.steven.indrev.utils.setIcon import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText class SlaughterScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( SLAUGHTER_HANDLER, syncId, playerInventory, ctx ) { private var value = -1 init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.slaughter", ctx, playerInventory, blockInventory) val outputFrame = WSprite(identifier("textures/gui/output_frame.png")) root.add(outputFrame, 5.1, 0.7) outputFrame.setSize(58, 62) val outputSlot = WTooltipedItemSlot.of( blockInventory, (blockInventory as IRInventory).outputSlots.first(), 3, 3, TranslatableText("gui.indrev.output_slot_type") ) outputSlot.isInsertingAllowed = false root.add(outputSlot, 5.2, 1.0) val swordSlot = WTooltipedItemSlot.of(blockInventory, 1, TranslatableText("gui.indrev.slaughter_input_sword")) swordSlot.setIcon(ctx, blockInventory, 1, SWORD_ICON) root.add(swordSlot, 2.5, 1.5) val slider = WSlider(1, 10, Axis.HORIZONTAL) root.add(slider, 1.6, 3.1) slider.setSize(50, 20) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@run slider.value = blockEntity.range } slider.setValueChangeListener { newValue -> this.value = newValue } val text = WText({ TranslatableText("block.indrev.aoe.range", slider.value) }, HorizontalAlignment.LEFT) root.add(text, 1.8, 2.8) root.validate(this) } override fun close(player: PlayerEntity?) { super.close(player) AOEMachineBlockEntity.sendValueUpdatePacket(value, ctx) } companion object { val SCREEN_ID = identifier("slaughter_screen") val SWORD_ICON = identifier("textures/gui/sword_icon.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SmelterScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.SmelterBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.SMELTER_HANDLER import me.steven.indrev.gui.widgets.machines.fluidTank import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class SmelterScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( SMELTER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.smelter", ctx, playerInventory, blockInventory) val inputSlot = WItemSlot.of(blockInventory, 2) root.add(inputSlot, 3.5, 1.8) withBlockEntity { be -> val processWidget = processBar(be, SmelterBlockEntity.CRAFTING_COMPONENT_ID) root.add(processWidget, 4.8, 1.8) val fluid = fluidTank(be, SmelterBlockEntity.TANK_ID) root.add(fluid, 6.2, 1.0) } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("smelter") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SolarGeneratorScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.blockentities.generators.SolarGeneratorBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.SOLAR_GENERATOR_HANDLER import me.steven.indrev.gui.widgets.misc.WDynamicSprite import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText class SolarGeneratorScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( SOLAR_GENERATOR_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.solar_generator", ctx, playerInventory, blockInventory) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? SolarGeneratorBlockEntity ?: return@run val sprite = WDynamicSprite { if (blockEntity.shouldGenerate()) GENERATING_ICON else NOT_GENERATING_ICON } root.add(sprite, 4.5, 2.0) sprite.setSize(19, 10) val text = WText({ if (blockEntity.shouldGenerate()) TranslatableText("gui.indrev.solar.on") else TranslatableText("gui.indrev.heatgen.idle") }, HorizontalAlignment.CENTER, 0x404040) root.add(text, 5.0, 2.7) } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("solar_generator") private val GENERATING_ICON = identifier("textures/gui/sun_icon.png") private val NOT_GENERATING_ICON = identifier("textures/gui/sun_unlit_icon.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SolarPowerPlantTowerScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WLabel import me.steven.indrev.blockentities.solarpowerplant.SolarPowerPlantTowerBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.SOLAR_POWER_PLANT_TOWER_HANDLER import me.steven.indrev.gui.widgets.machines.fluidTank import me.steven.indrev.gui.widgets.machines.temperatureBar import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class SolarPowerPlantTowerScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( SOLAR_POWER_PLANT_TOWER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) root.add(WLabel("Solar Power Plant Tower"), 0, 0) withBlockEntity { be -> val wFluid = fluidTank(be, SolarPowerPlantTowerBlockEntity.INPUT_TANK_ID) root.add(wFluid, 8, 0) wFluid.setLocation(8 * 18, 8) val wTemp = temperatureBar(be) root.add(wTemp, 0, 0) wTemp.setLocation(0, 8) } val inventoryPanel = createPlayerInventoryPanel() root.add(inventoryPanel, 0, 4) inventoryPanel.setLocation(0, 4 * 18 + 9) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("solar_power_plant_tower") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SolidInfuserFactoryScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.SolidInfuserFactoryBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.SOLID_INFUSER_FACTORY_HANDLER import me.steven.indrev.gui.widgets.machines.upProcessBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class SolidInfuserFactoryScreenHandler( syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext ) : IRGuiScreenHandler( SOLID_INFUSER_FACTORY_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.solid_infuser_factory", ctx, playerInventory, blockInventory, invPos = 4.85, widgetPos = 0.5) withBlockEntity { blockEntity -> val offset = 2.2 for (index in blockEntity.inventoryComponent!!.inventory.inputSlots.indices step 2) { val slot = blockEntity.inventoryComponent!!.inventory.inputSlots[index] val inputSlot = WItemSlot.of(blockInventory, slot, 1, 2) root.add(inputSlot, offset + ((index * 1.4) / 2), 0.6) } for (i in 0 until 5) { val processWidget = upProcessBar(blockEntity, SolidInfuserFactoryBlockEntity.CRAFTING_COMPONENT_START_ID + i) root.add(processWidget, offset + (i * 1.4), 2.7) } for ((index, slot) in blockEntity.inventoryComponent!!.inventory.outputSlots.withIndex()) { val outputSlot = WItemSlot.of(blockInventory, slot) root.add(outputSlot, offset + (index * 1.4), 3.8) outputSlot.addChangeListener { _, _, _, _ -> val player = playerInventory.player if (!player.world.isClient) { blockEntity.dropExperience(player) } } outputSlot.isInsertingAllowed = false } } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("infuser_factory_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SolidInfuserScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import me.steven.indrev.blockentities.crafters.SolidInfuserBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.SOLID_INFUSER_HANDLER import me.steven.indrev.gui.widgets.machines.WCustomBar import me.steven.indrev.gui.widgets.machines.processBar import me.steven.indrev.utils.add import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class SolidInfuserScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( SOLID_INFUSER_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.solid_infuser", ctx, playerInventory, blockInventory) val firstInput = WItemSlot.of(blockInventory, 2) root.add(firstInput, 2.9, 1.8) val secondInput = WItemSlot.of(blockInventory, 3) root.add(secondInput, 4.0, 1.8) val processWidget = query { be -> processBar(be, SolidInfuserBlockEntity.CRAFTING_COMPONENT_ID) } root.add(processWidget, 5.15, 1.8) val outputSlot = WItemSlot.outputOf(blockInventory, 4) outputSlot.isInsertingAllowed = false root.add(outputSlot, 6.6, 1.8) root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("infuser") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SteamTurbineScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.machines import io.github.cottonmc.cotton.gui.widget.WDynamicLabel import io.github.cottonmc.cotton.gui.widget.WGridPanel import me.steven.indrev.blockentities.generators.SteamTurbineBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.STEAM_TURBINE_HANDLER import me.steven.indrev.gui.widgets.misc.WKnob import me.steven.indrev.utils.configure import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext class SteamTurbineScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( STEAM_TURBINE_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) configure("block.indrev.steam_turbine_mk4", ctx, playerInventory, blockInventory) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? SteamTurbineBlockEntity ?: return@run root.add(WDynamicLabel { " Efficiency: " + String.format("%.2f", blockEntity.efficiency) }, 1, 1) root.add(WDynamicLabel { val a = blockEntity.consuming.asInt(1000).coerceAtLeast(1) val inexact = blockEntity.consuming.asInt(1000) val prefix = if (inexact > a) ">" else if (inexact < a) "<" else "" " Consuming: $prefix$a mB" }, 1, 2) root.add(WDynamicLabel { " Generating: ${propertyDelegate[SteamTurbineBlockEntity.GENERATING] / 100.0}" }, 1, 3) //root.add(WFluid(ctx, 0), -1, 0) val wKnob = WKnob((propertyDelegate[SteamTurbineBlockEntity.EFFICIENCY] * 3f) + 30f, pos) root.add(wKnob, 7, 4) wKnob.setSize(32, 32) wKnob.setLocation(7 * 18, 2 * 18 + 9) } root.validate(this) } override fun canUse(player: PlayerEntity?): Boolean = true companion object { val SCREEN_ID = identifier("steam_turbine") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/modular/ModularItemConfigurationScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.modular import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.TooltipBuilder import io.github.cottonmc.cotton.gui.widget.WBox import io.github.cottonmc.cotton.gui.widget.WSlider import io.github.cottonmc.cotton.gui.widget.WWidget import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.cottonmc.cotton.gui.widget.icon.Icon import io.netty.buffer.Unpooled import me.steven.indrev.WCustomTabPanel import me.steven.indrev.gui.widgets.misc.WPlayerRender import me.steven.indrev.gui.widgets.misc.WStaticTooltip import me.steven.indrev.items.energy.IRGamerAxeItem import me.steven.indrev.packets.common.UpdateModularToolLevelPacket import me.steven.indrev.tools.modular.IRModularItem import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.Item import net.minecraft.network.PacketByteBuf import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry class ModularItemConfigurationScreenHandler(playerInventory: PlayerInventory) : LightweightGuiDescription() { val iconProvider: (Item) -> Icon? = { item -> val id = Registry.ITEM.getId(item) val textureId = Identifier(id.namespace, "textures/item/${id.path}.png") Icon { _, x, y, size -> if (item is IRGamerAxeItem) ScreenDrawing.texturedRect(MatrixStack(), x, y, size, size, textureId, 0f, 0f, 1f, 0.14285714f, -1) else ScreenDrawing.texturedRect(MatrixStack(), x, y, size, size, textureId, -1) } } init { val root = WCustomTabPanel() this.rootPanel = root (playerInventory.size() - 1 downTo 0) .associateWith { slot -> playerInventory.getStack(slot) } .filter { (_, stack) -> stack.item is IRModularItem<*> } .forEach { (slot, stack) -> val tabPanel = WBox(Axis.HORIZONTAL) tabPanel.spacing = 10 val item = stack.item as IRModularItem<*> item.getInstalled(stack).forEach { module -> val moduleItem = module.item.asItem() val icon = iconProvider(moduleItem!!) val moduleBox = WBox(Axis.VERTICAL) moduleBox.add(object : WWidget() { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { icon?.paint(matrices, x, y, width) } override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add(TranslatableText(moduleItem.translationKey)) } }) val maxLevel = module.getMaxInstalledLevel(stack) val slider = object : WSlider(0, maxLevel, Axis.HORIZONTAL) { override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add(LiteralText("${value * 100 / maxLevel}%")) } } slider.value = module.getLevel(stack) slider.setDraggingFinishedListener { value -> val buf = PacketByteBuf(Unpooled.buffer()) buf.writeString(module.key) buf.writeInt(value) buf.writeInt(slot) ClientPlayNetworking.send(UpdateModularToolLevelPacket.UPDATE_MODULAR_TOOL_LEVEL, buf) } moduleBox.add(slider) tabPanel.add(moduleBox) } tabPanel.setSize(tabPanel.width + 65, tabPanel.height) root.add( WCustomTabPanel.Tab( null, iconProvider(stack.item), tabPanel, { it.add(TranslatableText(stack.item.translationKey)) }) ) } val playerBg = WStaticTooltip() root.add(playerBg, -50, 4) playerBg.setSize(40, 65) val playerWidget = WPlayerRender() root.add(playerWidget, -30, 64) root.validate(this) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/pipes/PipeFilterScreen.kt ================================================ package me.steven.indrev.gui.screenhandlers.pipes import io.github.cottonmc.cotton.gui.client.CottonInventoryScreen import net.minecraft.entity.player.PlayerEntity import net.minecraft.text.LiteralText class PipeFilterScreen(val screenHandler: PipeFilterScreenHandler, val player: PlayerEntity) : CottonInventoryScreen(screenHandler, player, LiteralText("Item Pipe Filter")) ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/pipes/PipeFilterScreenFactory.kt ================================================ package me.steven.indrev.gui.screenhandlers.pipes import me.steven.indrev.networks.Network import me.steven.indrev.networks.item.ItemNetworkState import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.network.PacketByteBuf import net.minecraft.screen.ScreenHandler import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.world.ServerWorld import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class PipeFilterScreenFactory( private val handlerFactory: (Int, PlayerInventory) -> ScreenHandler, private val pos: BlockPos, private val dir: Direction ) : ExtendedScreenHandlerFactory { override fun createMenu(syncId: Int, inv: PlayerInventory?, player: PlayerEntity?): ScreenHandler { return handlerFactory(syncId, inv!!) } override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) { (Network.Type.ITEM.getNetworkState(player.world as ServerWorld) as? ItemNetworkState)?.let { state -> val data = state.getEndpointData(pos, dir, false) val filterData = state.getFilterData(pos, dir, true) buf.writeEnumConstant(dir) buf.writeBlockPos(pos) filterData.filter.forEach { buf.writeItemStack(it) } buf.writeBoolean(filterData.whitelist) buf.writeBoolean(filterData.matchDurability) buf.writeBoolean(filterData.matchTag) buf.writeBoolean(data != null) if (data != null) { buf.writeEnumConstant(data.type) buf.writeEnumConstant(data.mode) } } } override fun getDisplayName(): Text = LiteralText("Item Filter") } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/pipes/PipeFilterScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.pipes import io.github.cottonmc.cotton.gui.SyncedGuiDescription import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.* import io.github.cottonmc.cotton.gui.widget.data.InputResult import io.github.cottonmc.cotton.gui.widget.data.Insets import me.steven.indrev.gui.screenhandlers.PIPE_FILTER_HANDLER import me.steven.indrev.networks.EndpointData import me.steven.indrev.packets.common.ItemPipePackets import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.minecraft.client.MinecraftClient import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.sound.PositionedSoundInstance import net.minecraft.client.util.math.MatrixStack import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.sound.SoundEvents import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.collection.DefaultedList import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import java.util.* import java.util.function.Consumer class PipeFilterScreenHandler( syncId: Int, playerInventory: PlayerInventory, whitelist: Boolean = false, matchDurability: Boolean = false, matchTag: Boolean = false, var mode: EndpointData.Mode? = null, val type: EndpointData.Type? = null ) : SyncedGuiDescription(PIPE_FILTER_HANDLER, syncId, playerInventory) { val backingList = DefaultedList.ofSize(9, ItemStack.EMPTY) lateinit var direction: Direction lateinit var blockPos: BlockPos init { val root = WGridPanel() this.rootPanel = root root.insets = Insets.ROOT_PANEL (0 until backingList.size).forEach { index -> val slot = WFilterSlot(index) root.add(slot, 1 * index, 1) } val whitelistButton = object : WToggleButton(WHITELIST_ICON, BLACKLIST_ICON) { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { WButton().also { it.setSize(20, 20) }.paint(matrices, x, y, mouseX, mouseY) super.paint(matrices, x + 1, y + 1, mouseX, mouseY) } override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add(TranslatableText("gui.indrev.whitelist.$isOn")) } } whitelistButton.onToggle = Consumer { value -> val buf = PacketByteBufs.create() writeIdentifyingData(buf) buf.writeInt(0) buf.writeBoolean(value) ClientPlayNetworking.send(ItemPipePackets.CHANGE_FILTER_MODE_PACKET, buf) } whitelistButton.toggle = whitelist root.add(whitelistButton, 2, 2) whitelistButton.setLocation(2 * 18 + root.insets.left, 3 * 18) val matchDurabilityButton = object : WToggleButton(MATCH_DURABILITY_ICON, IGNORE_DURABILITY_ICON) { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { WButton().also { it.setSize(20, 20) }.paint(matrices, x, y, mouseX, mouseY) super.paint(matrices, x + 1, y + 1, mouseX, mouseY) } override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add(TranslatableText("gui.indrev.matchDurability.$isOn")) } } matchDurabilityButton.onToggle = Consumer { value -> val buf = PacketByteBufs.create() writeIdentifyingData(buf) buf.writeInt(1) buf.writeBoolean(value) ClientPlayNetworking.send(ItemPipePackets.CHANGE_FILTER_MODE_PACKET, buf) } matchDurabilityButton.toggle = matchDurability root.add(matchDurabilityButton, 4, 2) matchDurabilityButton.setLocation(4 * 18 + root.insets.left, 3 * 18) val matchTagButton = object : WToggleButton(MATCH_NBT_ICON, IGNORE_NBT_ICON) { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { WButton().also { it.setSize(20, 20) }.paint(matrices, x, y, mouseX, mouseY) super.paint(matrices, x + 1, y + 1, mouseX, mouseY) } override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add(TranslatableText("gui.indrev.matchTag.$isOn")) } } matchTagButton.onToggle = Consumer { value -> val buf = PacketByteBufs.create() writeIdentifyingData(buf) buf.writeInt(2) buf.writeBoolean(value) ClientPlayNetworking.send(ItemPipePackets.CHANGE_FILTER_MODE_PACKET, buf) } matchTagButton.toggle = matchTag root.add(matchTagButton, 6, 3) matchTagButton.setLocation(6 * 18 + root.insets.left, 3 * 18) if (mode != null && type != null) { val modeWidget = WServoMode() root.add(modeWidget, 8, 0) modeWidget.setSize(18, 9) } val panel = createPlayerInventoryPanel() root.add(panel, 0, 4) root.validate(this) } private fun writeIdentifyingData(buf: PacketByteBuf) { buf.writeEnumConstant(direction) buf.writeBlockPos(blockPos) } inner class WFilterSlot(val index: Int) : WWidget() { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { ScreenDrawing.drawBeveledPanel(matrices, x, y, height, width, -1207959552, 1275068416, -1191182337) MinecraftClient.getInstance().itemRenderer.renderInGui(backingList[index], x + 1, y + 1) MinecraftClient.getInstance().itemRenderer.renderGuiItemOverlay(MinecraftClient.getInstance().textRenderer, backingList[index], x + 1, y + 1) if (mouseX >= 0 && mouseY >= 0 && mouseX < width && mouseY < height) DrawableHelper.fill(matrices, x + 1, y + 1, x + 17, y + 17, -2130706433) } override fun onClick(x: Int, y: Int, button: Int): InputResult { val buf = PacketByteBufs.create() buf.writeInt(index) writeIdentifyingData(buf) ClientPlayNetworking.send(ItemPipePackets.CLICK_FILTER_SLOT_PACKET, buf) return InputResult.PROCESSED } override fun addTooltip(tooltip: TooltipBuilder?) { val itemStack = backingList[index] if (!itemStack.isEmpty) tooltip?.add(*itemStack.getTooltip(playerInventory.player) { MinecraftClient.getInstance().options.advancedItemTooltips }.toTypedArray()) } } inner class WServoMode : WWidget() { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { if (type == EndpointData.Type.OUTPUT) MinecraftClient.getInstance().itemRenderer.renderInGui(ItemStack(IRItemRegistry.SERVO_OUTPUT), x + 1, y - 2) else if (type == EndpointData.Type.RETRIEVER) MinecraftClient.getInstance().itemRenderer.renderInGui(ItemStack(IRItemRegistry.SERVO_RETRIEVER), x + 1, y - 2) } override fun onClick(x: Int, y: Int, button: Int): InputResult { mode = mode?.next() ?: return InputResult.IGNORED MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0f)) val buf = PacketByteBufs.create() writeIdentifyingData(buf) buf.writeEnumConstant(mode) ClientPlayNetworking.send(ItemPipePackets.CHANGE_SERVO_MODE_PACKET, buf) return InputResult.PROCESSED } override fun addTooltip(tooltip: TooltipBuilder?) { tooltip?.add( TranslatableText("item.indrev.servo.mode") .append(TranslatableText("item.indrev.servo.mode.${mode.toString().lowercase(Locale.getDefault())}").formatted(Formatting.BLUE))) } } companion object { val WHITELIST_ICON = identifier("textures/gui/filter_whitelist.png") val BLACKLIST_ICON = identifier("textures/gui/filter_blacklist.png") val IGNORE_DURABILITY_ICON = identifier("textures/gui/filter_ignore_durability.png") val MATCH_DURABILITY_ICON = identifier("textures/gui/filter_match_durability.png") val IGNORE_NBT_ICON = identifier("textures/gui/filter_ignore_nbt.png") val MATCH_NBT_ICON = identifier("textures/gui/filter_match_nbt.png") val SCREEN_ID = identifier("pipe_filter_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/screenhandlers.kt ================================================ package me.steven.indrev.gui.screenhandlers import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.gui.screenhandlers.machines.* import me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreenHandler import me.steven.indrev.gui.screenhandlers.storage.CabinetScreenHandler import me.steven.indrev.gui.screenhandlers.wrench.ScrewdriverScreenHandler import me.steven.indrev.networks.EndpointData import me.steven.indrev.utils.registerScreenHandler import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry import net.minecraft.screen.ScreenHandlerContext import net.minecraft.util.math.Direction import java.util.* val COAL_GENERATOR_HANDLER = CoalGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::CoalGeneratorScreenHandler) val SOLAR_GENERATOR_HANDLER = SolarGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::SolarGeneratorScreenHandler) val BIOMASS_GENERATOR_HANDLER = BiomassGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::BiomassGeneratorScreenHandler) val HEAT_GENERATOR_HANDLER = HeatGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::HeatGeneratorScreenHandler) val GAS_BURNING_GENERATOR_HANDLER = GasBurningGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::GasBurningGeneratorScreenHandler) val BATTERY_HANDLER = LazuliFluxContainerScreenHandler.SCREEN_ID.registerScreenHandler(::LazuliFluxContainerScreenHandler) val ELECTRIC_FURNACE_HANDLER = ElectricFurnaceScreenHandler.SCREEN_ID.registerScreenHandler(::ElectricFurnaceScreenHandler) val PULVERIZER_HANDLER = PulverizerScreenHandler.SCREEN_ID.registerScreenHandler(::PulverizerScreenHandler) val PUMP_HANDLER = PumpScreenHandler.SCREEN_ID.registerScreenHandler(::PumpScreenHandler) val COMPRESSOR_HANDLER = CompressorScreenHandler.SCREEN_ID.registerScreenHandler(::CompressorScreenHandler) val SOLID_INFUSER_HANDLER = SolidInfuserScreenHandler.SCREEN_ID.registerScreenHandler(::SolidInfuserScreenHandler) val RECYCLER_HANDLER = RecyclerScreenHandler.SCREEN_ID.registerScreenHandler(::RecyclerScreenHandler) val CHOPPER_HANDLER = ChopperScreenHandler.SCREEN_ID.registerScreenHandler(::ChopperScreenHandler) val RANCHER_HANDLER = RancherScreenHandler.SCREEN_ID.registerScreenHandler(::RancherScreenHandler) val MINING_RIG_HANDLER = MiningRigComputerScreenHandler.SCREEN_ID.registerScreenHandler(::MiningRigComputerScreenHandler) val FISHER_HANDLER = FisherScreenHandler.SCREEN_ID.registerScreenHandler(::FisherScreenHandler) val MODULAR_WORKBENCH_HANDLER = ModularWorkbenchScreenHandler.SCREEN_ID.registerScreenHandler(::ModularWorkbenchScreenHandler) val SMELTER_HANDLER = SmelterScreenHandler.SCREEN_ID.registerScreenHandler(::SmelterScreenHandler) val CONDENSER_HANDLER = CondenserScreenHandler.SCREEN_ID.registerScreenHandler(::CondenserScreenHandler) val FLUID_INFUSER_HANDLER = FluidInfuserScreenHandler.SCREEN_ID.registerScreenHandler(::FluidInfuserScreenHandler) val FARMER_HANDLER = FarmerScreenHandler.SCREEN_ID.registerScreenHandler(::FarmerScreenHandler) val SLAUGHTER_HANDLER = SlaughterScreenHandler.SCREEN_ID.registerScreenHandler(::SlaughterScreenHandler) val SAWMILL_HANDLER = SawmillScreenHandler.SCREEN_ID.registerScreenHandler(::SawmillScreenHandler) val LASER_HANDLER = LaserEmitterScreenHandler.SCREEN_ID.registerScreenHandler(::LaserEmitterScreenHandler) val ELECTROLYTIC_SEPARATOR_HANDLER = ElectrolyticSeparatorScreenHandler.SCREEN_ID.registerScreenHandler(::ElectrolyticSeparatorScreenHandler) val DATA_CARD_WRITER_HANDLER = DataCardWriterScreenHandler.SCREEN_ID.registerScreenHandler(::DataCardWriterScreenHandler) val ELECTRIC_FURNACE_FACTORY_HANDLER = ElectricFurnaceFactoryScreenHandler.SCREEN_ID.registerScreenHandler(::ElectricFurnaceFactoryScreenHandler) val PULVERIZER_FACTORY_HANDLER = PulverizerFactoryScreenHandler.SCREEN_ID.registerScreenHandler(::PulverizerFactoryScreenHandler) val COMPRESSOR_FACTORY_HANDLER = CompressorFactoryScreenHandler.SCREEN_ID.registerScreenHandler(::CompressorFactoryScreenHandler) val SOLID_INFUSER_FACTORY_HANDLER = SolidInfuserFactoryScreenHandler.SCREEN_ID.registerScreenHandler(::SolidInfuserFactoryScreenHandler) val STEAM_TURBINE_HANDLER = SteamTurbineScreenHandler.SCREEN_ID.registerScreenHandler(::SteamTurbineScreenHandler) val SOLAR_POWER_PLANT_TOWER_HANDLER = SolarPowerPlantTowerScreenHandler.SCREEN_ID.registerScreenHandler(::SolarPowerPlantTowerScreenHandler) val PIPE_FILTER_HANDLER = ScreenHandlerRegistry.registerExtended(PipeFilterScreenHandler.SCREEN_ID) { syncId, inv, buf -> val dir = buf.readEnumConstant(Direction::class.java) val pos = buf.readBlockPos() val list = (0 until 9).map { buf.readItemStack() } val whitelist = buf.readBoolean() val matchDurability = buf.readBoolean() val matchTag = buf.readBoolean() val hasServo = buf.readBoolean() val type = if (hasServo) buf.readEnumConstant(EndpointData.Type::class.java) else null val mode = if (hasServo) buf.readEnumConstant(EndpointData.Mode::class.java) else null val controller = PipeFilterScreenHandler(syncId, inv, whitelist, matchDurability, matchTag, mode, type) controller.direction = dir controller.blockPos = pos list.forEachIndexed { index, itemStack -> controller.backingList[index] = itemStack } controller } as ExtendedScreenHandlerType val DRILL_HANDLER = MiningRigDrillScreenHandler.SCREEN_ID.registerScreenHandler(::MiningRigDrillScreenHandler) val SCREWDRIVER_HANDLER = ScreenHandlerRegistry.registerExtended(ScrewdriverScreenHandler.SCREEN_ID) { syncId, inv, buf -> val pos = buf.readBlockPos() val itemConfig = SideConfiguration(ConfigurationType.ITEM) if (buf.readBoolean()) itemConfig.readBuf(buf) val fluidConfig = SideConfiguration(ConfigurationType.FLUID) if (buf.readBoolean()) fluidConfig.readBuf(buf) val energyConfig = SideConfiguration(ConfigurationType.ENERGY) if (buf.readBoolean()) energyConfig.readBuf(buf) val map = EnumMap(ConfigurationType::class.java) map[ConfigurationType.ITEM] = itemConfig map[ConfigurationType.FLUID] = fluidConfig map[ConfigurationType.ENERGY] = energyConfig ScrewdriverScreenHandler(syncId, inv, ScreenHandlerContext.create(inv.player.world, pos), map) } as ExtendedScreenHandlerType val CABINET_HANDLER = CabinetScreenHandler.SCREEN_ID.registerScreenHandler(::CabinetScreenHandler) ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/storage/CabinetScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.storage import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.WItemSlot import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import me.steven.indrev.gui.screenhandlers.CABINET_HANDLER import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.utils.add import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.TranslatableText class CabinetScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) : IRGuiScreenHandler( CABINET_HANDLER, syncId, playerInventory, ctx ) { init { val root = WGridPanel() setRootPanel(root) root.add(WText(TranslatableText("block.indrev.cabinet"), HorizontalAlignment.LEFT, 0x404040), 0.0, -0.1) root.add(WItemSlot.of(blockInventory, 0, 9, 3), 0.0, 0.6) root.add(createPlayerInventoryPanel(), 0.0, 3.8) root.validate(this) } companion object { val SCREEN_ID = identifier("cabinet") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/screenhandlers/wrench/ScrewdriverScreenHandler.kt ================================================ package me.steven.indrev.gui.screenhandlers.wrench import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.github.cottonmc.cotton.gui.widget.WButton import io.github.cottonmc.cotton.gui.widget.WGridPanel import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import io.github.cottonmc.cotton.gui.widget.data.Insets import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.gui.screenhandlers.SCREWDRIVER_HANDLER import me.steven.indrev.gui.widgets.misc.WText import me.steven.indrev.utils.add import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerInventory import net.minecraft.screen.ScreenHandlerContext import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.TranslatableText import java.util.* class ScrewdriverScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext, val configs: EnumMap) : IRGuiScreenHandler( SCREWDRIVER_HANDLER, syncId, playerInventory, ctx ) { private lateinit var currentType: ConfigurationType init { val root = WGridPanel() setRootPanel(root) root.setSize(100, 128) root.insets = Insets.ROOT_PANEL val titleText = WText(TranslatableText("item.indrev.wrench.title"), HorizontalAlignment.LEFT, 0x404040) root.add(titleText, 0.3, 0.4) ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@run val availableTypes = ConfigurationType.getTypes(blockEntity) currentType = availableTypes.first() var widget = configs[currentType]?.getConfigurationPanel(world, pos, blockEntity, playerInventory, currentType) ?: return@run val configY = if (availableTypes.size > 1) 2 else 1 root.add(widget, 0, configY) val configTypeButton = WButton(currentType.title) configTypeButton.setOnClick { currentType = currentType.next(availableTypes) configTypeButton.label = currentType.title root.remove(widget) widget = configs[currentType]?.getConfigurationPanel(world, pos, blockEntity, playerInventory, currentType) ?: return@setOnClick root.add(widget, 0, configY) root.validate(this) } if (availableTypes.size > 1) root.add(configTypeButton, 1.6, 1.0) configTypeButton.setSize(45, 20) } root.validate(this) } override fun close(player: PlayerEntity?) { super.close(player) if (player is ServerPlayerEntity) { ctx.run { world, pos -> val blockEntity = world.getBlockEntity(pos) as? LazuliFluxContainerBlockEntity ?: return@run blockEntity.sync() world.updateNeighbors(pos, blockEntity.cachedState.block) } } } override fun addPainters() { super.addPainters() rootPanel.backgroundPainter = BackgroundPainter.VANILLA } companion object { val SCREEN_ID = identifier("screwdriver_config_screen") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/tooltip/energy/EnergyTooltipComponent.kt ================================================ package me.steven.indrev.gui.tooltip.energy import com.mojang.blaze3d.systems.RenderSystem import me.steven.indrev.utils.getEnergyString import me.steven.indrev.utils.identifier import net.minecraft.client.font.TextRenderer import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.gui.tooltip.TooltipComponent import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.item.ItemRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.text.LiteralText import net.minecraft.util.Formatting import net.minecraft.util.math.Matrix4f open class EnergyTooltipComponent(private val data: EnergyTooltipData) : TooltipComponent { override fun getHeight(): Int = 18 override fun getWidth(textRenderer: TextRenderer): Int { val percentage = if (data.maxEnergy > 0) data.energy * 100 / data.maxEnergy else 0 val text = LiteralText("${getEnergyString(data.energy)} LF (${percentage.toInt()}%)").formatted(Formatting.GRAY) return textRenderer.getWidth(text) + 20 } override fun drawItems( textRenderer: TextRenderer, x: Int, y: Int, matrices: MatrixStack, itemRenderer: ItemRenderer, z: Int ) { RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f) RenderSystem.setShaderTexture(0, identifier("textures/gui/energy_icon.png")) DrawableHelper.drawTexture(matrices, x, y, z, 0f, 0f, 18, 18, 18, 18) } override fun drawText( textRenderer: TextRenderer, x: Int, y: Int, matrix4f: Matrix4f, immediate: VertexConsumerProvider.Immediate ) { val percentage = if (data.maxEnergy > 0) data.energy * 100 / data.maxEnergy else 0 val text = LiteralText("${getEnergyString(data.energy)} LF (${percentage.toInt()}%)").formatted(Formatting.GRAY) textRenderer.draw(text, x.toFloat() + 19, (y.toFloat() + 9) - textRenderer.fontHeight / 2, -1, true, matrix4f, immediate, false, 0, 15728880) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/tooltip/energy/EnergyTooltipData.kt ================================================ package me.steven.indrev.gui.tooltip.energy import net.minecraft.client.item.TooltipData open class EnergyTooltipData(val energy: Long, val maxEnergy: Long) : TooltipData ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/tooltip/modular/ModularTooltipComponent.kt ================================================ package me.steven.indrev.gui.tooltip.modular import me.steven.indrev.gui.tooltip.energy.EnergyTooltipComponent import net.minecraft.client.font.TextRenderer import net.minecraft.client.gui.screen.Screen import net.minecraft.client.render.item.ItemRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.item.ItemStack import kotlin.math.ceil class ModularTooltipComponent(private val data: ModularTooltipData) : EnergyTooltipComponent(data) { override fun getHeight(): Int = if (Screen.hasShiftDown()) 18 else 18 + 18 * (ceil(data.modules.size / 5.0).toInt()) override fun getWidth(textRenderer: TextRenderer): Int { val energyWidth = super.getWidth(textRenderer) var cX = 0 data.modules.forEachIndexed { index, module -> val level = data.levelProvider(module) cX += (level * 5) + 18 if (index + 1 % 5 == 0) return cX } return cX.coerceAtLeast(energyWidth) } override fun drawItems( textRenderer: TextRenderer, x: Int, y: Int, matrices: MatrixStack, itemRenderer: ItemRenderer, z: Int ) { super.drawItems(textRenderer, x, y, matrices, itemRenderer, z) if (Screen.hasShiftDown()) return var cX = x var cY = y + 18 data.modules.sortedByDescending { data.levelProvider(it) }.forEachIndexed { index, module -> val level = data.levelProvider(module) cX += level * 5 repeat(level) { cX -= 5 itemRenderer.renderInGui(ItemStack(module.item.asItem()), cX, cY) } cX += (level * 5) + 18 if (index + 1 % 5 == 0) { cY += 18 cX = x } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/tooltip/modular/ModularTooltipData.kt ================================================ package me.steven.indrev.gui.tooltip.modular import me.steven.indrev.gui.tooltip.energy.EnergyTooltipData import me.steven.indrev.tools.modular.Module class ModularTooltipData(energy: Long, maxEnergy: Long, val modules: List, val levelProvider: (Module) -> Int) : EnergyTooltipData(energy, maxEnergy) { } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/tooltip/oredatacards/OreDataCardTooltipComponent.kt ================================================ package me.steven.indrev.gui.tooltip.oredatacards import me.steven.indrev.api.OreDataCards import me.steven.indrev.utils.drawCircle import net.minecraft.client.font.TextRenderer import net.minecraft.client.gui.tooltip.TooltipComponent import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.render.item.ItemRenderer import net.minecraft.client.util.math.MatrixStack import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.util.Formatting import net.minecraft.util.math.Matrix4f class OreDataCardTooltipComponent(val data: OreDataCardTooltipData) : TooltipComponent { override fun getHeight(): Int { return 18 * (if (data.cardData.rng == 0) 3 else 4) + (18* data.cardData.entries.size / 4) + 18 } override fun getWidth(textRenderer: TextRenderer): Int { val text = LiteralText("Cycle: ").styled { s -> s.withColor(0x007E7E) }.append(LiteralText("${data.cardData.speed} ticks (${data.cardData.energyRequired} LF/tick)").formatted(Formatting.WHITE)) return textRenderer.getWidth(text) + 9 } override fun drawText( textRenderer: TextRenderer, x: Int, y: Int, matrix: Matrix4f?, vertexConsumers: VertexConsumerProvider.Immediate? ) { val speed = LiteralText("Cycle: ").styled { s -> s.withColor(0x007E7E) }.append(LiteralText("${data.cardData.speed} ticks (${data.cardData.energyRequired} LF/tick)").formatted(Formatting.WHITE)) textRenderer.draw(speed, x.toFloat() + 3, y.toFloat() + 4 + 18 * 3 + (18 * (data.cardData.entries.size / 4)), -1, false, matrix, vertexConsumers, false, 0, 15728880) val dataTxt = "${(data.cardData.maxCycles - data.cardData.used) * 100 / OreDataCards.MAX_SIZE}%" val width = textRenderer.getWidth(dataTxt) textRenderer.draw(dataTxt, x.toFloat() + textRenderer.getWidth(speed) - 17- width / 2, y.toFloat() + 15, -1, false, matrix, vertexConsumers, false, 0, 15728880) textRenderer.draw("Types", x.toFloat() + 3, y.toFloat() + 5, 0x007E7E, false, matrix, vertexConsumers, false, 0, 15728880) val richness = LiteralText("Richness: ").styled { s -> s.withColor(0x007E7E) }.append(LiteralText("${(data.cardData.richness * 100).toInt()}%").formatted(Formatting.WHITE)) textRenderer.draw(richness, x.toFloat() + 3, y.toFloat() + 4 + 18 * 2 + (18 * (data.cardData.entries.size / 4)), -1, false, matrix, vertexConsumers, false, 0, 15728880) if (data.cardData.rng == -1) { textRenderer.draw("-RNG", x.toFloat(), y.toFloat() + 16 * 5, -1, false, matrix, vertexConsumers, false, 0, 15728880) } else if (data.cardData.rng == 1) { textRenderer.draw("+RNG", x.toFloat(), y.toFloat() + 16 * 5, -1, false, matrix, vertexConsumers, false, 0, 15728880) } } override fun drawItems( textRenderer: TextRenderer, x: Int, y: Int, matrices: MatrixStack, itemRenderer: ItemRenderer, z: Int ) { val text = LiteralText("Cycle: ").styled { s -> s.withColor(0x007E7E) }.append(LiteralText("${data.cardData.speed} ticks (${data.cardData.energyRequired} LF/tick)").formatted(Formatting.WHITE)) data.cardData.entries.forEachIndexed { index, entry -> itemRenderer.renderInGui(ItemStack(entry.item), x + 6 + (index % 3 * 18), y + 17 + (18 * (index / 3))) } matrices.push() matrices.translate(0.0, 0.0, z.toDouble()) val width = 18 * 2 val offset = textRenderer.getWidth(text) - width drawCircle(matrices, 1,1, x + offset, y, width) { _, _ -> 0x22FFFFFF.toInt() } drawCircle(matrices, data.cardData.maxCycles, OreDataCards.MAX_SIZE, x + offset, y, width) { _, _ -> 0xFF0000FF.toInt() } drawCircle(matrices, data.cardData.used, OreDataCards.MAX_SIZE, x + offset, y, width) { _, _ -> 0xFFFF0000.toInt() } matrices.pop() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/tooltip/oredatacards/OreDataCardTooltipData.kt ================================================ package me.steven.indrev.gui.tooltip.oredatacards import me.steven.indrev.api.OreDataCards import net.minecraft.client.item.TooltipData class OreDataCardTooltipData(val cardData: OreDataCards.Data) : TooltipData ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/machines/WCustomBar.kt ================================================ package me.steven.indrev.gui.widgets.machines import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.TooltipBuilder import io.github.cottonmc.cotton.gui.widget.WWidget import io.github.cottonmc.cotton.gui.widget.data.InputResult import io.github.cottonmc.cotton.gui.widget.data.Texture import io.netty.buffer.Unpooled import me.steven.indrev.blockentities.BaseBlockEntity import me.steven.indrev.components.CraftingComponent import me.steven.indrev.packets.common.FluidGuiHandInteractionPacket import me.steven.indrev.utils.IRFluidTank import me.steven.indrev.utils.getEnergyString import me.steven.indrev.utils.getTooltip import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.minecraft.client.util.math.MatrixStack import net.minecraft.network.PacketByteBuf import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.math.MathHelper open class WCustomBar(val bg: Texture, val bar: Texture, val value: () -> Int, val max: () -> Int, val direction: Direction) : WWidget() { override fun canResize(): Boolean = false override fun paint(matrices: MatrixStack, x: Int, y: Int, mouseX: Int, mouseY: Int) { ScreenDrawing.texturedRect(matrices, x, y, getWidth(), getHeight(), bg, -0x1) val maxVal: Int = max() var percent: Float = (value() / maxVal.toFloat()).coerceIn(0f, 1f) var barMax = getWidth() if (direction == Direction.DOWN || direction == Direction.UP) barMax = getHeight() percent = (percent * barMax).toInt() / barMax.toFloat() //Quantize to bar size val barSize = (barMax * percent).toInt() if (barSize <= 0) return when (direction) { Direction.UP -> { val left = x; var top = y + getHeight(); top -= barSize; drawBar(matrices, left, top, getWidth(), barSize, bar.u1(), MathHelper.lerp(percent, bar.v2(), bar.v1()), bar.u2(), bar.v2()); } Direction.RIGHT -> { drawBar(matrices, x, y, barSize, getHeight(), bar.u1(), bar.v1(), MathHelper.lerp(percent, bar.u1(), bar.u2()), bar.v2()) } Direction.DOWN -> { drawBar(matrices, x, y, getWidth(), barSize,bar.u1(), bar.v1(), bar.u2(), MathHelper.lerp(percent, bar.v1(), bar.v2())) } Direction.LEFT -> { var left = x + getWidth() left -= barSize drawBar(matrices, left, y, barSize, getHeight(), MathHelper.lerp(percent, bar.u2(), bar.u1()), bar.v1(), bar.u2(), bar.v2()) } } } open fun drawBar(matrices: MatrixStack, left: Int, top: Int, width: Int, height: Int, u1: Float, v1: Float, u2: Float, v2: Float) { ScreenDrawing.texturedRect(matrices, left, top, width, height, bar.image(), u1, v1, u2, v2, -1); } enum class Direction { UP, RIGHT, DOWN, LEFT } } private val LIT_TEXTURE_ID = Texture(identifier("textures/gui/widget_fuel_burning.png")) private val UNLIT_TEXTURE_ID = Texture(identifier("textures/gui/widget_fuel_not_burning.png")) fun fuelBar(blockEntity: BaseBlockEntity, valueIndex: Int = 4, maxIndex: Int = 5): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val fuel = WCustomBar(UNLIT_TEXTURE_ID, LIT_TEXTURE_ID, { properties[valueIndex] }, { properties[maxIndex] }, WCustomBar.Direction.UP) fuel.setSize(14, 14) return fuel } val TANK_BOTTOM = Texture(identifier("textures/gui/tank_bottom.png")) val TANK_TOP = Texture(identifier("textures/gui/tank_top.png")) fun fluidTank(blockEntity: BaseBlockEntity, index: Int): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val tank = object : WCustomBar(TANK_BOTTOM, TANK_TOP, { 1 }, { 1 }, Direction.UP) { override fun drawBar(matrices: MatrixStack, left: Int, top: Int, width: Int, height: Int, u1: Float, v1: Float, u2: Float, v2: Float) { val maxVal: Long = properties.get(index).capacity var percent: Float = (properties.get(index).amount / maxVal.toFloat()).coerceIn(0f, 1f) val barMax = getHeight() percent = (percent * barMax).toInt() / barMax.toFloat() //Quantize to bar size val barSize = (barMax * percent).toInt() if (barSize > 0) { var t = top t -= barSize val tank = properties.get(index) tank .renderGuiRect( left.toDouble() + 1, t.toDouble() + 1 + height, left + width.toDouble() - 1, t + barSize.toDouble() - 1 + height ) } ScreenDrawing.texturedRect(matrices, left, top, this.width, this.height, TANK_TOP, -1) } override fun addTooltip(tooltip: TooltipBuilder?) { val tank = properties.get(index) if (!tank.isEmpty) tooltip?.add(*getTooltip(tank.variant, tank.amount, tank.capacity).toTypedArray()) } override fun onClick(x: Int, y: Int, button: Int): InputResult { val packet = PacketByteBuf(Unpooled.buffer()) packet.writeInt(properties.get(index).index) ClientPlayNetworking.send(FluidGuiHandInteractionPacket.FLUID_CLICK_PACKET, packet) return InputResult.PROCESSED } } tank.setSize(16, 43) return tank } val ENERGY_EMPTY = Texture(identifier("textures/gui/widget_energy_empty.png")) val ENERGY_FULL = Texture(identifier("textures/gui/widget_energy_full.png")) fun energyBar(blockEntity: BaseBlockEntity): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val energy = object : WCustomBar(ENERGY_EMPTY, ENERGY_FULL, { properties.get(0).toInt() }, { properties.get(1).toInt() }, Direction.UP) { override fun addTooltip(tooltip: TooltipBuilder?) { val energy = getEnergyString(properties[0]) val maxEnergy = getEnergyString(properties[1]) tooltip?.add(TranslatableText("gui.widget.energy").formatted(Formatting.BLUE)) tooltip?.add(LiteralText("$energy / $maxEnergy LF")) } } energy.setSize(10, 64) return energy } private val EMPTY_HEAT = Texture(identifier("textures/gui/widget_temperature_empty.png")) private val FULL_HEAT = Texture(identifier("textures/gui/widget_temperature_full.png")) fun temperatureBar(blockEntity: BaseBlockEntity): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val temp = object : WCustomBar(EMPTY_HEAT, FULL_HEAT, { properties.get(2).toInt() }, { properties.get(3).toInt() }, Direction.UP) { override fun addTooltip(tooltip: TooltipBuilder?) { val temperature = properties.get(2).toInt() val maxTemperature = properties.get(3).toInt() //TODO /* val info = when { temperature > temperatureComponent.optimalRange.last -> TranslatableText("gui.widget.temperature_info.high").formatted(Formatting.DARK_RED, Formatting.ITALIC) temperature in temperatureComponent.optimalRange -> TranslatableText("gui.widget.temperature_info.medium").formatted(Formatting.YELLOW, Formatting.ITALIC) else -> TranslatableText("gui.widget.temperature_info.low").formatted(Formatting.GREEN, Formatting.ITALIC) } tooltip?.add(TranslatableText("gui.widget.temperature").formatted(Formatting.BLUE))*/ tooltip?.add(LiteralText("$temperature / $maxTemperature °C")) //tooltip?.add(info) } } temp.setSize(10, 43) return temp } val RIGHT_PROCESS_EMPTY = Texture(identifier("textures/gui/widget_processing_empty.png")) val RIGHT_PROCESS_FULL = Texture(identifier("textures/gui/widget_processing_full.png")) val LEFT_PROCESS_EMPTY = Texture(identifier("textures/gui/widget_processing_empty_left.png")) val LEFT_PROCESS_FULL = Texture(identifier("textures/gui/widget_processing_full_left.png")) val UP_PROCESS_EMPTY = Texture(identifier("textures/gui/widget_processing_empty_vertical.png")) val UP_PROCESS_FULL = Texture(identifier("textures/gui/widget_processing_full_vertical.png")) fun processBar(blockEntity: BaseBlockEntity, index: Int): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val process = object : WCustomBar(RIGHT_PROCESS_EMPTY, RIGHT_PROCESS_FULL, { properties.get>(index).processTime }, { properties.get>(index).totalProcessTime }, Direction.RIGHT) { override fun addTooltip(tooltip: TooltipBuilder?) { val progress = properties.get>(index).processTime val max = properties.get>(index).totalProcessTime if (max <= 0) return val percentage = progress * 100 / max tooltip?.add(TranslatableText("gui.widget.process", percentage).append(LiteralText("%"))) } } return process } fun processBar(blockEntity: BaseBlockEntity, progress: Int, max: Int): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val process = object : WCustomBar(RIGHT_PROCESS_EMPTY, RIGHT_PROCESS_FULL, { properties[progress] }, { properties[max] }, Direction.RIGHT) { override fun addTooltip(tooltip: TooltipBuilder?) { if (properties.get(max) <= 0) return val percentage = properties.get(progress) * 100 / properties.get(max) tooltip?.add(TranslatableText("gui.widget.process", percentage).append(LiteralText("%"))) } } return process } fun upProcessBar(blockEntity: BaseBlockEntity, index: Int): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val process = object : WCustomBar(UP_PROCESS_EMPTY, UP_PROCESS_FULL, { properties.get>(index).processTime }, { properties.get>(index).totalProcessTime }, Direction.DOWN) { override fun addTooltip(tooltip: TooltipBuilder?) { val progress = properties.get>(index).processTime val max = properties.get>(index).totalProcessTime if (max <= 0) return val percentage = progress * 100 / max tooltip?.add(TranslatableText("gui.widget.process", percentage).append(LiteralText("%"))) } } return process } fun upProcessBar(blockEntity: BaseBlockEntity, progress: Int, max: Int): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val process = object : WCustomBar(UP_PROCESS_EMPTY, UP_PROCESS_FULL, { properties[progress] }, { properties[max] }, Direction.DOWN) { override fun addTooltip(tooltip: TooltipBuilder?) { if (properties.get(max) <= 0) return val percentage = properties.get(progress) * 100 / properties.get(max) tooltip?.add(TranslatableText("gui.widget.process", percentage).append(LiteralText("%"))) } } return process } fun leftProcessBar(blockEntity: BaseBlockEntity, index: Int): WCustomBar { val properties = blockEntity.guiSyncableComponent ?: error("$blockEntity does not provide gui_syncable component") val process = object : WCustomBar(LEFT_PROCESS_EMPTY, LEFT_PROCESS_FULL, { properties.get>(index).processTime }, { properties.get>(index).totalProcessTime }, Direction.LEFT) { override fun addTooltip(tooltip: TooltipBuilder?) { val progress = properties.get>(index).processTime val max = properties.get>(index).totalProcessTime if (max <= 0) return val percentage = progress * 100 / max tooltip?.add(TranslatableText("gui.widget.process", percentage).append(LiteralText("%"))) } } return process } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/machines/WMachineSideDisplay.kt ================================================ package me.steven.indrev.gui.widgets.machines import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.TooltipBuilder import io.github.cottonmc.cotton.gui.widget.WButton import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.utils.draw2Colors import me.steven.indrev.utils.identifier import net.minecraft.client.gui.DrawableHelper import net.minecraft.client.util.math.MatrixStack import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World import java.util.* class WMachineSideDisplay( private val side: SideConfiguration.MachineSide, private val direction: Direction, var mode: TransferMode, private val world: World, private val blockPos: BlockPos ) : WButton() { init { this.setSize(16, 16) } override fun setSize(x: Int, y: Int) { this.width = x this.height = y } override fun paint(matrices: MatrixStack, x: Int, y: Int, mouseX: Int, mouseY: Int) { ScreenDrawing.texturedRect(matrices, x, y, width, height, TEXTURE_ID, side.u1 / 16f, side.v1 / 16f, side.u2 / 16f, side.v2 / 16f, -1) if (mode == TransferMode.INPUT_OUTPUT) draw2Colors(matrices, x, y, x + width, y + height, TransferMode.INPUT.rgb, TransferMode.OUTPUT.rgb) else if (mode != TransferMode.NONE) DrawableHelper.fill(matrices, x, y, x + width, y + height, mode.rgb.toInt()) if (isWithinBounds(mouseX, mouseY)) DrawableHelper.fill(matrices, x, y, x + width, y + height, -2130706433) } override fun addTooltip(tooltip: TooltipBuilder?) { val modeText = TranslatableText("item.indrev.wrench.mode", TranslatableText("item.indrev.wrench.${mode.toString().lowercase(Locale.getDefault())}").formatted(Formatting.WHITE) ).formatted(Formatting.BLUE) val side = TranslatableText("item.indrev.wrench.side.${side.toString().lowercase(Locale.getDefault())}") .append(LiteralText(" (") .append(TranslatableText("item.indrev.wrench.side.${direction.toString().lowercase(Locale.getDefault())}")) .append(LiteralText(")"))).formatted(Formatting.WHITE) tooltip?.add(modeText, side) val blockState = world.getBlockState(blockPos.offset(direction)) if (!blockState.isAir) { val neighbor = TranslatableText("item.indrev.wrench.connected", blockState.block.name) tooltip?.add(neighbor) } } companion object { val TEXTURE_ID = identifier("textures/block/machine_block.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/misc/WBookEntryShortcut.kt ================================================ package me.steven.indrev.gui.widgets.misc import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.WButton import io.github.cottonmc.cotton.gui.widget.data.InputResult import me.steven.indrev.utils.identifier import net.minecraft.client.util.math.MatrixStack open class WBookEntryShortcut : WButton() { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { ScreenDrawing.texturedRect(matrices, x, y, width, height, ICON_IDENTIFIER, -1) } override fun onMouseDown(x: Int, y: Int, button: Int): InputResult { onClick(x, y, button) return InputResult.PROCESSED } override fun setSize(x: Int, y: Int) { this.width = x this.height = y } companion object { private val ICON_IDENTIFIER = identifier("textures/gui/help.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/misc/WCircleProgressBar.kt ================================================ package me.steven.indrev.gui.widgets.misc import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.WWidget import me.steven.indrev.utils.drawCircle import net.minecraft.client.util.math.MatrixStack import kotlin.math.* open class WCircleProgressBar(val value: () -> Int, val max: () -> Int, val colorProvider: (Int, Int) -> Int) : WWidget() { override fun paint(matrices: MatrixStack, x: Int, y: Int, mouseX: Int, mouseY: Int) { matrices.push() matrices.translate(0.0, 0.0, 300.0) drawCircle(matrices, value(), max(), x, y, width, colorProvider) matrices.pop() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/misc/WDynamicSprite.kt ================================================ package me.steven.indrev.gui.widgets.misc import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.WWidget import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.Identifier class WDynamicSprite(val provider: () -> Identifier) : WWidget() { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { ScreenDrawing.texturedRect(matrices, x, y, width, height, provider(), -1) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/misc/WKnob.kt ================================================ package me.steven.indrev.gui.widgets.misc import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.WWidget import io.github.cottonmc.cotton.gui.widget.data.InputResult import me.steven.indrev.packets.common.UpdateKnobValue.UPDATE_EFFICIENCY_PACKET import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.minecraft.client.MinecraftClient import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3f import kotlin.math.atan2 class WKnob(var angle: Float = 30.0f, val pos: BlockPos) : WWidget() { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { matrices?.run { push() translate(x.toDouble() + width / 2.0, y.toDouble() + width / 2.0, 0.0) multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(angle)) translate(-x.toDouble() - width / 2.0, -y.toDouble() - width / 2.0, 0.0) ScreenDrawing.texturedRect(matrices, x, y, width, height, KNOB_TEXTURE_ID, -1) pop() } val textRenderer = MinecraftClient.getInstance().textRenderer val text = String.format("%.1f", ((angle - 30) / 300f) * 100) + "%" ScreenDrawing.drawString( matrices, text, (x - textRenderer.getWidth(text) / 2) + width / 2, (y - textRenderer.fontHeight / 2) + height / 2, -1 ) } private fun calculateAngle(x: Float, y: Float): Float { val px = (x / width.toFloat()) - 0.5 val py = (1 - y / height.toFloat()) - 0.5 var angle = -(Math.toDegrees(atan2(py, px))).toFloat() + 180 if (angle > 360) angle -= 360 return angle.coerceIn(30f, 330f) } override fun onMouseDrag(x: Int, y: Int, button: Int, deltaX: Double, deltaY: Double): InputResult { angle = calculateAngle(x.toFloat(), y.toFloat()) return InputResult.PROCESSED } override fun onMouseUp(x: Int, y: Int, button: Int): InputResult { val buf = PacketByteBufs.create() buf.writeBlockPos(pos) buf.writeFloat((angle - 30) / 300f) ClientPlayNetworking.send(UPDATE_EFFICIENCY_PACKET, buf) return InputResult.PROCESSED } companion object { val KNOB_TEXTURE_ID = identifier("textures/gui/knob.png") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/misc/WPlayerRender.kt ================================================ package me.steven.indrev.gui.widgets.misc import io.github.cottonmc.cotton.gui.widget.WWidget import net.minecraft.client.MinecraftClient import net.minecraft.client.gui.screen.ingame.InventoryScreen import net.minecraft.client.util.math.MatrixStack class WPlayerRender : WWidget() { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { InventoryScreen.drawEntity(x, y, 30, -mouseX.toFloat(), -mouseY.toFloat(), MinecraftClient.getInstance().player) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/misc/WStaticTooltip.kt ================================================ package me.steven.indrev.gui.widgets.misc import com.mojang.blaze3d.systems.RenderSystem import io.github.cottonmc.cotton.gui.widget.WWidget import net.minecraft.client.render.* import net.minecraft.client.render.VertexFormat.DrawMode import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.Matrix4f class WStaticTooltip : WWidget() { override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { drawTooltipBackground(matrices ?: return, x, y, width, height) } @Suppress("DEPRECATION") private fun drawTooltipBackground(matrices: MatrixStack, x: Int, y: Int, width: Int, height: Int) { val tessellator = Tessellator.getInstance() val bufferBuilder = tessellator.buffer RenderSystem.setShader { GameRenderer.getPositionColorShader() } bufferBuilder.begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR) val matrix4f = matrices.peek().positionMatrix val z = 0 fillGradient(matrix4f, bufferBuilder, x - 3, y - 4, x + width + 3, y - 3, z, -267386864, -267386864) fillGradient(matrix4f, bufferBuilder, x - 3, y + height + 3, x + width + 3, y + height + 4, z, -267386864, -267386864) fillGradient(matrix4f, bufferBuilder, x - 3, y - 3, x + width + 3, y + height + 3, z, -267386864, -267386864) fillGradient(matrix4f, bufferBuilder, x - 4, y - 3, x - 3, y + height + 3, z, -267386864, -267386864) fillGradient(matrix4f, bufferBuilder, x + width + 3, y - 3, x + width + 4, y + height + 3, z, -267386864, -267386864) fillGradient(matrix4f, bufferBuilder, x - 3, y - 3 + 1, x - 3 + 1, y + height + 3 - 1, z, 1347420415, 1344798847) fillGradient(matrix4f, bufferBuilder, x + width + 2, y - 3 + 1, x + width + 3, y + height + 3 - 1, z, 1347420415, 1344798847) fillGradient(matrix4f, bufferBuilder, x - 3, y - 3, x + width + 3, y - 3 + 1, z, 1347420415, 1347420415) fillGradient(matrix4f, bufferBuilder, x - 3, y + height + 2, x + width + 3, y + height + 3, z, 1344798847, 1344798847) RenderSystem.enableDepthTest() RenderSystem.disableTexture() RenderSystem.enableBlend() RenderSystem.defaultBlendFunc() bufferBuilder.end() BufferRenderer.draw(bufferBuilder) RenderSystem.disableBlend() RenderSystem.enableTexture() } private fun fillGradient(matrix4f: Matrix4f?, bufferBuilder: BufferBuilder, xStart: Int, yStart: Int, xEnd: Int, yEnd: Int, i: Int, j: Int, k: Int) { val f = (j shr 24 and 255).toFloat() / 255.0f val g = (j shr 16 and 255).toFloat() / 255.0f val h = (j shr 8 and 255).toFloat() / 255.0f val l = (j and 255).toFloat() / 255.0f val m = (k shr 24 and 255).toFloat() / 255.0f val n = (k shr 16 and 255).toFloat() / 255.0f val o = (k shr 8 and 255).toFloat() / 255.0f val p = (k and 255).toFloat() / 255.0f bufferBuilder.vertex(matrix4f, xEnd.toFloat(), yStart.toFloat(), i.toFloat()).color(g, h, l, f).next() bufferBuilder.vertex(matrix4f, xStart.toFloat(), yStart.toFloat(), i.toFloat()).color(g, h, l, f).next() bufferBuilder.vertex(matrix4f, xStart.toFloat(), yEnd.toFloat(), i.toFloat()).color(n, o, p, m).next() bufferBuilder.vertex(matrix4f, xEnd.toFloat(), yEnd.toFloat(), i.toFloat()).color(n, o, p, m).next() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/misc/WText.kt ================================================ package me.steven.indrev.gui.widgets.misc import io.github.cottonmc.cotton.gui.client.ScreenDrawing import io.github.cottonmc.cotton.gui.widget.WWidget import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment import net.minecraft.client.MinecraftClient import net.minecraft.client.util.math.MatrixStack import net.minecraft.text.Text class WText( private val string: () -> Text, private val alignment: HorizontalAlignment = HorizontalAlignment.CENTER, private val color: Int = -1 ) : WWidget() { constructor(string: Text, alignment: HorizontalAlignment = HorizontalAlignment.CENTER, color: Int = -1) : this( { string }, alignment, color ) override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) { var alignedX = x.toDouble() val text = string() if (alignment == HorizontalAlignment.CENTER) { alignedX -= MinecraftClient.getInstance().textRenderer.getWidth(text) / 2.0 MinecraftClient.getInstance().textRenderer.draw(matrices, text, alignedX.toFloat(), y.toFloat(), color) return } ScreenDrawing.drawString(matrices, text.asOrderedText(), alignment, alignedX.toInt(), y, this.width, color) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/gui/widgets/misc/WTooltipedItemSlot.kt ================================================ package me.steven.indrev.gui.widgets.misc import io.github.cottonmc.cotton.gui.widget.WItemSlot import net.minecraft.client.MinecraftClient import net.minecraft.client.util.math.MatrixStack import net.minecraft.inventory.Inventory import net.minecraft.text.Text open class WTooltipedItemSlot( private val emptyTooltip: MutableList, private val inventory: Inventory, private val startIndex: Int = 0, private val slotsWide: Int = 1, private val slotsHigh: Int = 1, big: Boolean = false ) : WItemSlot(inventory, startIndex, slotsWide, slotsHigh, big) { override fun renderTooltip(matrices: MatrixStack?, x: Int, y: Int, tX: Int, tY: Int) { val slots = startIndex until startIndex + (slotsHigh * slotsWide) if (emptyTooltip.size != 0 && slots.all { inventory.getStack(it).isEmpty }) { val screen = MinecraftClient.getInstance().currentScreen screen?.renderTooltip(matrices, emptyTooltip, tX + x, tY + y) } } companion object { fun of(inventory: Inventory, index: Int, vararg emptyTooltip: Text): WTooltipedItemSlot = WTooltipedItemSlot(emptyTooltip.toMutableList(), inventory, index) fun of( inventory: Inventory, startIndex: Int, slotsWide: Int, slotsHigh: Int, vararg emptyTooltip: Text ): WTooltipedItemSlot = WTooltipedItemSlot(emptyTooltip.toMutableList(), inventory, startIndex, slotsWide, slotsHigh) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/inventories/IRInventory.kt ================================================ package me.steven.indrev.inventories import me.steven.indrev.IndustrialRevolution import me.steven.indrev.components.InventoryComponent import net.minecraft.inventory.SidedInventory import net.minecraft.inventory.SimpleInventory import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.math.Direction class IRInventory( dsl: IRInventoryDSL, size: Int, val inputSlots: IntArray, val outputSlots: IntArray, val slotPredicate: (Int, ItemStack?, Direction?) -> Boolean = { _, _, _ -> true } ) : SimpleInventory(size), SidedInventory { var component: InventoryComponent? = null private var availableSlots = inputSlots.plus(outputSlots) val coolerSlot = dsl.coolerSlot private val maxCount = dsl.maxStackCount init { if (dsl.coolerSlot != null) availableSlots = availableSlots.plus(coolerSlot!!) availableSlots = availableSlots.distinct().toIntArray() } val coolerStack: ItemStack get() = if (coolerSlot == null) ItemStack.EMPTY else getStack(coolerSlot) override fun getAvailableSlots(var1: Direction?): IntArray = availableSlots override fun canExtract(slot: Int, stack: ItemStack?, direction: Direction?): Boolean = outputSlots.contains(slot) && component?.itemConfig?.get(direction)?.output == true override fun canInsert(slot: Int, stack: ItemStack?, direction: Direction?): Boolean = (if (slot == coolerSlot) stack?.isIn(IndustrialRevolution.COOLERS_TAG) == true else stack?.isIn(IndustrialRevolution.COOLERS_TAG) == false) && (inputSlots.contains(slot) || slot == coolerSlot) && component?.itemConfig?.get(direction)?.input == true && slotPredicate(slot, stack, direction) || stack?.isEmpty == true override fun getMaxCountPerStack(): Int = maxCount override fun isValid(slot: Int, stack: ItemStack?): Boolean = slotPredicate(slot, stack, null) || stack?.isEmpty == true fun fits(stack: Item, outputSlot: Int): Boolean { val outStack = getStack(outputSlot) if (outStack.isEmpty || (stack == outStack.item && outStack.nbt?.isEmpty != false)) return true return false } fun output(stack: ItemStack): Boolean { for (i in 0 until size()) { val itemStack = getStack(i) if (canCombine(itemStack, stack)) { transfer(stack, itemStack) if (stack.isEmpty) { return true } } } return addToOutputSlot(stack) } private fun canCombine(one: ItemStack, two: ItemStack): Boolean = one.item === two.item && ItemStack.areNbtEqual(one, two) private fun transfer(source: ItemStack, target: ItemStack) { val i = this.maxCountPerStack.coerceAtMost(target.maxCount) val j = source.count.coerceAtMost(i - target.count) if (j > 0) { target.increment(j) source.decrement(j) markDirty() } } private fun addToOutputSlot(stack: ItemStack): Boolean { for (i in outputSlots) { val itemStack = getStack(i) if (itemStack.isEmpty) { setStack(i, stack.copy()) stack.count = 0 return true } } return false } } ================================================ FILE: src/main/kotlin/me/steven/indrev/inventories/IRInventoryDSL.kt ================================================ package me.steven.indrev.inventories import me.steven.indrev.IndustrialRevolution import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.components.InventoryComponent import me.steven.indrev.items.upgrade.IREnhancerItem import me.steven.indrev.utils.EMPTY_INT_ARRAY import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import net.minecraft.item.ItemStack import net.minecraft.util.math.Direction open class Filterable { var filters: MutableMap Boolean> = hashMapOf() infix fun Int.filter(filter: (ItemStack, Direction?) -> Boolean) { filters[this] = filter } infix fun IntRange.filter(filter: (ItemStack, Direction?, Int) -> Boolean) { forEach { slot -> filters[slot] = { stack, direction -> filter(stack, direction, slot) } } } infix fun Int.filter(filter: (ItemStack) -> Boolean) { filters[this] = { stack, _ -> filter(stack) } } infix fun IntRange.filter(filter: (ItemStack, Int) -> Boolean) { forEach { slot -> filters[slot] = { stack, _ -> filter(stack, slot) } } } } open class IRInventoryDSL : Filterable() { var coolerSlot: Int? = null private var input: FilteredSlots = FilteredSlots.EMPTY_FILTER private var output: FilteredSlots = FilteredSlots.EMPTY_FILTER var enhancerSlots: IntRange? = null var maxStackCount = 64 fun input(block: FilteredSlots.() -> Unit) { input = FilteredSlots() block(input) input.filters.forEach { (key, value) -> filters[key] = value } } fun output(block: FilteredSlots.() -> Unit) { output = FilteredSlots() block(output) output.filters.forEach { (key, value) -> filters[key] = value } } fun build(blockEntity: MachineBlockEntity<*>): IRInventory { var size = input.slots.plus(output.slots).plus(filters.keys).distinct().size + 1 if (coolerSlot == null && blockEntity.temperatureComponent != null) coolerSlot = 1 if (coolerSlot != null) size++ val enhancerComponent = blockEntity.enhancerComponent if (enhancerComponent != null) size += enhancerComponent.slots.size return IRInventory(this, size, input.slots, output.slots) { slot, stack, dir -> if (stack == null) false else filters.computeIfAbsent(slot) { { (stack, item), _ -> when { coolerSlot != null && slot == coolerSlot -> stack.isIn(IndustrialRevolution.COOLERS_TAG) input.slots.contains(slot) -> true enhancerComponent != null -> item is IREnhancerItem && slot in enhancerComponent.slots && !enhancerComponent.isLocked(slot, blockEntity.tier) && enhancerComponent.compatible.contains(item.enhancer) else -> false } } }(stack, dir) } } class FilteredSlots : Filterable() { var slots: IntArray = EMPTY_INT_ARRAY var slot: Int? = null set(value) { if (value != null) slots = intArrayOf(value) field = value } fun filter(filter: (ItemStack, Direction?, Int) -> Boolean) { slots.forEach { slot -> filters[slot] = { stack, dir -> filter(stack, dir, slot) } } } fun filter(filter: (ItemStack, Int) -> Boolean) { slots.forEach { slot -> filters[slot] = { stack, _ -> filter(stack, slot) } } } companion object { val EMPTY_FILTER = FilteredSlots() } } } fun inventory(blockEntity: MachineBlockEntity<*>, block: IRInventoryDSL.() -> Unit): InventoryComponent { val dsl = IRInventoryDSL() block(dsl) val inv = dsl.build(blockEntity) return InventoryComponent(blockEntity) { inv } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/armor/IRColorModuleItem.kt ================================================ package me.steven.indrev.items.armor import me.steven.indrev.tools.modular.ArmorModule class IRColorModuleItem(val color: Int, settings: Settings) : IRModuleItem(ArmorModule.COLOR, settings) ================================================ FILE: src/main/kotlin/me/steven/indrev/items/armor/IRModularArmorItem.kt ================================================ package me.steven.indrev.items.armor import com.google.common.collect.ImmutableMultimap import com.google.common.collect.Multimap import dev.emi.stepheightentityattribute.StepHeightEntityAttributeMain import me.steven.indrev.IndustrialRevolutionClient import me.steven.indrev.api.AttributeModifierProvider import me.steven.indrev.api.machines.Tier import me.steven.indrev.armor.IRArmorMaterial import me.steven.indrev.gui.tooltip.modular.ModularTooltipData import me.steven.indrev.items.energy.IREnergyItem import me.steven.indrev.registry.IRFluidRegistry import me.steven.indrev.tools.modular.ArmorModule import me.steven.indrev.tools.modular.IRModularItem import me.steven.indrev.utils.bucket import me.steven.indrev.utils.energyOf import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry import net.fabricmc.fabric.api.entity.event.v1.FabricElytraItem import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.client.gui.screen.Screen import net.minecraft.client.item.TooltipContext import net.minecraft.client.item.TooltipData import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity import net.minecraft.entity.attribute.EntityAttribute import net.minecraft.entity.attribute.EntityAttributeModifier import net.minecraft.entity.attribute.EntityAttributes import net.minecraft.item.ArmorItem import net.minecraft.item.DyeableArmorItem import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.world.World import team.reborn.energy.api.EnergyStorage import team.reborn.energy.impl.SimpleItemEnergyStorageImpl import java.util.* import kotlin.math.roundToInt class IRModularArmorItem(slot: EquipmentSlot, maxStored: Long, settings: Settings) : DyeableArmorItem(IRArmorMaterial.MODULAR, slot, settings), IRModularItem, AttributeModifierProvider, IREnergyItem, JetpackHandler, FabricElytraItem { init { EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, maxStored, Tier.MK4.io, Tier.MK4.io) }, this) } override val fluidFilter: (FluidVariant) -> Boolean = { it.isOf(IRFluidRegistry.HYDROGEN_STILL) } override val limit: Long = bucket / 20 override fun isUsable(stack: ItemStack): Boolean = ArmorModule.JETPACK.getLevel(stack) > 0 fun getFluidItemBarStep(stack: ItemStack): Int { val volume = getFuelStored(stack) return (13.0 - (((limit - volume.amount()) * 13) / limit)).roundToInt() } fun getFluidItemBarColor(stack: ItemStack): Int = FluidRenderHandlerRegistry.INSTANCE.get(getFuelStored(stack).resource.fluid).getFluidColor(null, null, null) fun isFluidItemBarVisible(stack: ItemStack): Boolean = getFuelStored(stack).amount > 0 override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack) override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack) override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack) override fun isEnchantable(stack: ItemStack?): Boolean = false override fun appendTooltip(stack: ItemStack, world: World?, tooltip: MutableList?, context: TooltipContext?) { if (Screen.hasShiftDown()) getInstalledTooltip(getInstalled(stack), stack, tooltip) tooltip?.add( TranslatableText("item.indrev.modular_item.tooltip", LiteralText("").append( IndustrialRevolutionClient.MODULAR_CONTROLLER_KEYBINDING.boundKeyLocalizedText).formatted(Formatting.AQUA)).formatted( Formatting.GRAY)) } override fun canRepair(stack: ItemStack?, ingredient: ItemStack?): Boolean = false override fun getColor(stack: ItemStack?): Int { val nbt = stack!!.getSubNbt("display") return if (nbt != null && nbt.contains("color", 99)) nbt.getInt("color") else -1 } fun getMaxShield(protectionLevel: Int) = protectionLevel * 100.0 override fun getCompatibleModules(itemStack: ItemStack): Array { val armor = itemStack.item as? ArmorItem ?: return emptyArray() return when (armor.slotType) { EquipmentSlot.HEAD -> ArmorModule.COMPATIBLE_HELMET EquipmentSlot.CHEST -> ArmorModule.COMPATIBLE_CHEST EquipmentSlot.LEGS -> ArmorModule.COMPATIBLE_LEGS EquipmentSlot.FEET -> ArmorModule.COMPATIBLE_BOOTS else -> return emptyArray() } } override fun getInstalled(stack: ItemStack): List { val tag = stack.nbt ?: return emptyList() return getCompatibleModules(stack).filter { module -> module != ArmorModule.COLOR }.mapNotNull { module -> if (tag.contains(module.key)) module else null } } override fun useCustomElytra(entity: LivingEntity, chestStack: ItemStack, tickElytra: Boolean): Boolean { if (ReinforcedElytraItem.canFallFly(chestStack)) { if (tickElytra) { doVanillaElytraTick(entity, chestStack) } return true } return false } override fun getAttributeModifiers( itemStack: ItemStack, equipmentSlot: EquipmentSlot ): Multimap { val item = itemStack.item as IRModularArmorItem val itemIo = energyOf(itemStack) if (itemIo == null || itemIo.amount <= 0) { return ImmutableMultimap.of() } else if (equipmentSlot == item.slotType) { val level = ArmorModule.PROTECTION.getLevel(itemStack).toDouble() val uUID = MODIFIERS[equipmentSlot.entitySlotId] val attr = ImmutableMultimap.builder() if (level > 0) { val toughnessModifier = EntityAttributeModifier(uUID, "Armor toughness", item.toughness * level, EntityAttributeModifier.Operation.ADDITION) attr.put( EntityAttributes.GENERIC_ARMOR_TOUGHNESS, toughnessModifier ) val armorModifier = EntityAttributeModifier(uUID, "Armor modifier", item.protection * level, EntityAttributeModifier.Operation.ADDITION) attr.put( EntityAttributes.GENERIC_ARMOR, armorModifier ) } val speedLevel = ArmorModule.SPEED.getLevel(itemStack) * 0.9 if (speedLevel > 0) { attr.put( EntityAttributes.GENERIC_MOVEMENT_SPEED, EntityAttributeModifier(SPEED_MODIFIER, "Speed", speedLevel, EntityAttributeModifier.Operation.MULTIPLY_TOTAL) ) attr.put( StepHeightEntityAttributeMain.STEP_HEIGHT, EntityAttributeModifier(STEP_HEIGHT_MODIFIER, "Step-Height", 1.0, EntityAttributeModifier.Operation.ADDITION) ) } return attr.build() } return getAttributeModifiers(equipmentSlot) } override fun getTooltipData(stack: ItemStack): Optional { val handler = energyOf(stack) ?: return Optional.empty() val modules = getInstalled(stack) return Optional.of(ModularTooltipData(handler.amount, handler.capacity, modules) { it.getLevel(stack) }) } companion object { private val MODIFIERS = arrayOf( UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150") ) private val SPEED_MODIFIER = UUID.randomUUID() private val STEP_HEIGHT_MODIFIER = UUID.randomUUID() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/armor/IRModuleItem.kt ================================================ package me.steven.indrev.items.armor import me.steven.indrev.tools.modular.Module import net.minecraft.client.gui.screen.Screen import net.minecraft.client.item.TooltipContext import net.minecraft.client.option.KeyBinding import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.world.World open class IRModuleItem(val module: Module, settings: Settings) : Item(settings) { override fun appendTooltip( stack: ItemStack, world: World?, tooltip: MutableList?, context: TooltipContext? ) { if (Screen.hasShiftDown()) { module.getTooltip(stack, tooltip) } else { tooltip?.add(TranslatableText("gui.indrev.tooltip.press_shift", LiteralText("").append(KeyBinding.getLocalizedName("key.keyboard.left.shift").get()).formatted(Formatting.BLUE, Formatting.ITALIC)).formatted(Formatting.BLUE, Formatting.ITALIC)) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/armor/JetpackHandler.kt ================================================ package me.steven.indrev.items.armor import me.steven.indrev.registry.IRFluidRegistry import me.steven.indrev.utils.bucket import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.fabricmc.fabric.api.transfer.v1.storage.base.ResourceAmount import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantItemStorage import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.Inventory import net.minecraft.item.ItemConvertible import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound interface JetpackHandler : ItemConvertible { val fluidFilter: (FluidVariant) -> Boolean val limit: Long fun isUsable(stack: ItemStack): Boolean = true fun tickJetpack(stack: ItemStack, playerEntity: PlayerEntity) { if (!playerEntity.jumping) return val max = 0.5 // TODO oxyhydrogen val vel = playerEntity.velocity if (playerEntity.isFallFlying && useFuel(stack, playerEntity.inventory, 38)) { val facing = playerEntity.rotationVector playerEntity.velocity = vel.multiply(0.3).add(facing.multiply(max)) } else if (vel.y < max && useFuel(stack, playerEntity.inventory, 38)) { playerEntity.setVelocity(vel.x, max.coerceAtMost(vel.y + 0.15), vel.z) } } fun getFuelStored(stack: ItemStack): ResourceAmount { val nbt = stack.getSubNbt("Fluid") ?: return ResourceAmount(FluidVariant.blank(), 0) val variant = FluidVariant.fromNbt(nbt) val amt = nbt.getLong("Amount") return ResourceAmount(variant, amt) } //TODO oxyhydrogen private fun getConsumptionRatio(stack: ItemStack) = bucket / 81 / 3 private fun useFuel(stack: ItemStack, inv: Inventory, slot: Int): Boolean { val consumption = getConsumptionRatio(stack) val storage = FluidStorage.ITEM.find(stack, ContainerItemContext.ofSingleSlot(InventoryStorage.of(inv, null).getSlot(slot))) Transaction.openOuter().use { tx -> val extracted = storage?.extract(FluidVariant.of(IRFluidRegistry.HYDROGEN_STILL), consumption, tx) return if (extracted == consumption) { tx.commit() true } else { tx.abort() false } } } class JetpackFluidStorage(val handler: JetpackHandler, ctx: ContainerItemContext) : SingleVariantItemStorage(ctx) { override fun getCapacity(variant: FluidVariant?): Long = handler.limit override fun getBlankResource(): FluidVariant = FluidVariant.blank() override fun getResource(currentVariant: ItemVariant): FluidVariant { val compound = currentVariant.nbt?.getCompound("Fluid") ?: return blankResource return FluidVariant.fromNbt(compound) } override fun getAmount(currentVariant: ItemVariant): Long { val compound = currentVariant.nbt?.getCompound("Fluid") ?: return 0 return compound.getLong("Amount") } override fun getUpdatedVariant( currentVariant: ItemVariant, newResource: FluidVariant, newAmount: Long ): ItemVariant { val nbt = NbtCompound() return if (newAmount > 0 && !newResource.isBlank) { val fluidNbt = newResource.toNbt() fluidNbt.putLong("Amount", newAmount) nbt.put("Fluid", fluidNbt) ItemVariant.of(handler, nbt) } else ItemVariant.of(handler) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/armor/JetpackItem.kt ================================================ package me.steven.indrev.items.armor import me.steven.indrev.api.machines.Tier import me.steven.indrev.armor.IRArmorMaterial import me.steven.indrev.registry.IRFluidRegistry import me.steven.indrev.utils.bucket import me.steven.indrev.utils.itemSettings import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.entity.EquipmentSlot import net.minecraft.item.ArmorItem import net.minecraft.item.ItemStack import kotlin.math.roundToInt class JetpackItem(tier: Tier) : ArmorItem(IRArmorMaterial.JETPACK, EquipmentSlot.CHEST, itemSettings().maxCount(1)), JetpackHandler { init { FluidStorage.ITEM.registerForItems({ stack, ctx -> JetpackHandler.JetpackFluidStorage(this, ctx) }, this) } override val limit = bucket * (10L * (tier.ordinal + 1)) override val fluidFilter: (FluidVariant) -> Boolean = { it.isOf(IRFluidRegistry.HYDROGEN_STILL) } private fun isActivated(stack: ItemStack) = stack.isOf(this) && stack.orCreateNbt.getBoolean("Activated") fun toggle(stack: ItemStack) { if (stack.isOf(this)) { val tag = stack.orCreateNbt tag.putBoolean("Activated", tag.getBoolean("Activated")) } } override fun getItemBarStep(stack: ItemStack): Int { val volume = getFuelStored(stack) return (13.0 - (((limit - volume.amount()) * 13) / limit).toDouble()).roundToInt() } override fun getItemBarColor(stack: ItemStack): Int = FluidRenderHandlerRegistry.INSTANCE.get(getFuelStored(stack).resource.fluid).getFluidColor(null, null, getFuelStored(stack).resource.fluid.defaultState) override fun isItemBarVisible(stack: ItemStack): Boolean = getFuelStored(stack).amount > 0 } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/armor/ReinforcedElytraItem.kt ================================================ package me.steven.indrev.items.armor import me.steven.indrev.armor.IRArmorMaterial import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.tools.modular.ArmorModule import me.steven.indrev.utils.energyOf import me.steven.indrev.utils.itemSettings import net.fabricmc.fabric.api.entity.event.v1.FabricElytraItem import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity import net.minecraft.item.ArmorItem import net.minecraft.item.ElytraItem import net.minecraft.item.ItemStack class ReinforcedElytraItem : ArmorItem(IRArmorMaterial.STEEL_ELYTRA, EquipmentSlot.CHEST, itemSettings().maxDamage(800)), FabricElytraItem { override fun useCustomElytra(entity: LivingEntity, chestStack: ItemStack, tickElytra: Boolean): Boolean { return canFallFly(chestStack) && super.useCustomElytra(entity, chestStack, tickElytra) } companion object { fun canFallFly(itemStack: ItemStack): Boolean { return (itemStack.isOf(IRItemRegistry.REINFORCED_ELYTRA) && ElytraItem.isUsable(itemStack)) || (itemStack.isOf(IRItemRegistry.MODULAR_ARMOR_CHEST) && ArmorModule.ELYTRA.getLevel(itemStack) > 0 && energyOf(itemStack)!!.amount > 0) } fun hasValidElytra(itemStack: ItemStack): Boolean { return (itemStack.isOf(IRItemRegistry.REINFORCED_ELYTRA)) || (itemStack.isOf(IRItemRegistry.MODULAR_ARMOR_CHEST) && IRItemRegistry.MODULAR_ARMOR_CHEST.getInstalled(itemStack).contains(ArmorModule.ELYTRA)) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/energy/IRBatteryItem.kt ================================================ package me.steven.indrev.items.energy import me.steven.indrev.api.machines.Tier import me.steven.indrev.gui.tooltip.energy.EnergyTooltipData import me.steven.indrev.utils.energyOf import net.minecraft.client.item.TooltipData import net.minecraft.item.Item import net.minecraft.item.ItemStack import team.reborn.energy.api.EnergyStorage import team.reborn.energy.impl.SimpleItemEnergyStorageImpl import java.util.* class IRBatteryItem(settings: Settings, maxStored: Long) : Item(settings), IREnergyItem { init { EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, maxStored, Tier.MK1.io, Tier.MK1.io) }, this) } override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack) override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack) override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack) override fun canRepair(stack: ItemStack?, ingredient: ItemStack?): Boolean = false override fun getTooltipData(stack: ItemStack): Optional { val handler = energyOf(stack) ?: return Optional.empty() return Optional.of(EnergyTooltipData(handler.amount, handler.capacity)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/energy/IREnergyItem.kt ================================================ package me.steven.indrev.items.energy import me.steven.indrev.utils.energyOf import net.minecraft.item.ItemStack import kotlin.math.roundToInt interface IREnergyItem { fun getDurabilityBarProgress(stack: ItemStack?): Int { val energyIo = energyOf(stack) ?: return 0 return (13.0f - (energyIo.capacity - energyIo.amount) * 13.0f / energyIo.capacity).roundToInt() } fun hasDurabilityBar(stack: ItemStack?): Boolean = (energyOf(stack)?.amount ?: 0) > 0 fun getDurabilityBarColor(stack: ItemStack?): Int { val durability = getDurabilityBarProgress(stack) / 13f val r = (149 - ((149 - 55) * durability)).toInt() and 255 shl 16 val g = (122 - ((122) * durability)).toInt() shl 8 val b = 255 return r or g or b } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/energy/IRGamerAxeItem.kt ================================================ package me.steven.indrev.items.energy import com.google.common.collect.ImmutableMultimap import com.google.common.collect.Multimap import me.steven.indrev.IndustrialRevolutionClient import me.steven.indrev.api.AttributeModifierProvider import me.steven.indrev.api.CustomEnchantmentProvider import me.steven.indrev.api.machines.Tier import me.steven.indrev.gui.tooltip.modular.ModularTooltipData import me.steven.indrev.tools.modular.GamerAxeModule import me.steven.indrev.tools.modular.IRModularItem import me.steven.indrev.tools.modular.MiningToolModule import me.steven.indrev.tools.modular.Module import me.steven.indrev.utils.energyOf import net.minecraft.block.BlockState import net.minecraft.client.gui.screen.Screen import net.minecraft.client.item.TooltipContext import net.minecraft.client.item.TooltipData import net.minecraft.enchantment.Enchantment import net.minecraft.enchantment.Enchantments import net.minecraft.entity.Entity import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity import net.minecraft.entity.attribute.EntityAttribute import net.minecraft.entity.attribute.EntityAttributeModifier import net.minecraft.entity.attribute.EntityAttributes import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.AxeItem import net.minecraft.item.ItemStack import net.minecraft.item.ItemUsageContext import net.minecraft.item.ToolMaterial import net.minecraft.tag.BlockTags import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.Formatting import net.minecraft.util.Hand import net.minecraft.util.TypedActionResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.world.World import team.reborn.energy.api.EnergyStorage import team.reborn.energy.api.base.SimpleBatteryItem import team.reborn.energy.impl.SimpleItemEnergyStorageImpl import java.util.* class IRGamerAxeItem( material: ToolMaterial, maxStored: Long, tier: Tier, attackDamage: Float, attackSpeed: Float, settings: Settings ) : AxeItem(material, attackDamage, attackSpeed, settings), IREnergyItem, IRModularItem, AttributeModifierProvider, CustomEnchantmentProvider { init { EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, maxStored, tier.io, tier.io) }, this) } override fun appendTooltip( stack: ItemStack, world: World?, tooltip: MutableList, context: TooltipContext? ) { if (Screen.hasShiftDown()) getInstalledTooltip(getInstalled(stack), stack, tooltip) val active = stack.nbt?.getBoolean("Active") ?: false tooltip.add(TranslatableText("item.indrev.gamer_axe.tooltip.$active", LiteralText("").append(IndustrialRevolutionClient.GAMER_AXE_TOGGLE_KEYBINDING.boundKeyLocalizedText).formatted(Formatting.AQUA)).formatted(Formatting.GRAY)) tooltip.add(TranslatableText("item.indrev.modular_item.tooltip", LiteralText("").append(IndustrialRevolutionClient.MODULAR_CONTROLLER_KEYBINDING.boundKeyLocalizedText).formatted(Formatting.AQUA)).formatted(Formatting.GRAY)) } override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack) override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack) override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack) override fun isEnchantable(stack: ItemStack?): Boolean = false override fun getTooltipData(stack: ItemStack): Optional { val handler = energyOf(stack) ?: return Optional.empty() val modules = getInstalled(stack) return Optional.of(ModularTooltipData(handler.amount, handler.capacity, modules) { it.getLevel(stack) }) } override fun useOnBlock(context: ItemUsageContext?): ActionResult { return ActionResult.PASS } override fun getMiningSpeedMultiplier(stack: ItemStack, state: BlockState?): Float { val speedMultiplier = MiningToolModule.EFFICIENCY.getLevel(stack) + 1 return if (!isActive(stack) || (energyOf(stack)?.amount ?: 0) <= 0) 0f else 8f * speedMultiplier } override fun hasGlint(stack: ItemStack?): Boolean { return stack?.nbt?.getBoolean("Active") == true } override fun postMine( stack: ItemStack, world: World, state: BlockState, pos: BlockPos, miner: LivingEntity ): Boolean { if (!isActive(stack) || miner !is PlayerEntity) return false val canStart = unsafeUse(stack, 1) if (canStart && !miner.isSneaking && state.isIn(BlockTags.LOGS)) { val scanned = mutableSetOf() Direction.values().forEach { dir -> scanTree(scanned, world, stack, pos.offset(dir)) } } return canStart } fun scanTree(scanned: MutableSet, world: World, stack: ItemStack, pos: BlockPos) { if (!scanned.add(pos)) return val state = world.getBlockState(pos) if (state.isIn(BlockTags.LOGS) || state.isIn(BlockTags.LEAVES)) { if (unsafeUse(stack, 1)) { world.breakBlock(pos, true) if (scanned.size < 40) Direction.values().forEach { dir -> scanTree(scanned, world, stack, pos.offset(dir)) } } } } override fun postHit(stack: ItemStack, target: LivingEntity?, attacker: LivingEntity): Boolean { if (attacker !is PlayerEntity) return false val level = GamerAxeModule.REACH.getLevel(stack) if (isActive(stack) && level > 0) { target?.world?.getEntitiesByClass(LivingEntity::class.java, Box(target.blockPos).expand(level.toDouble())) { true }?.forEach { entity -> if (unsafeUse(stack, 1)) { attacker.resetLastAttackedTicks() attacker.attack(entity) } } } return unsafeUse(stack, 1) } override fun canRepair(stack: ItemStack?, ingredient: ItemStack?): Boolean = false override fun getCompatibleModules(itemStack: ItemStack): Array = GamerAxeModule.COMPATIBLE fun isActive(stack: ItemStack): Boolean { val tag = stack.orCreateNbt ?: return false return tag.contains("Active") && tag.getBoolean("Active") } override fun inventoryTick(stack: ItemStack?, world: World?, entity: Entity, slot: Int, selected: Boolean) { val tag = stack?.orCreateNbt ?: return tickAnimations(stack) if (isActive(stack) && !unsafeUse(stack, 5)) { tag.putBoolean("Active", false) } } fun unsafeUse(stack: ItemStack, amount: Long): Boolean { val itemIo = energyOf(stack)!! if (itemIo.amount > amount) { SimpleBatteryItem.setStoredEnergyUnchecked(stack, itemIo.amount - amount) return true } return false } private fun tickAnimations(stack: ItemStack) { val tag = stack.orCreateNbt if (!tag.contains("Active") || !tag.contains("Progress")) return val active = tag.getBoolean("Active") var progress = tag.getFloat("Progress") if (active && progress < 1) { progress += 0.12f if (progress >= 1) tag.putBoolean("Active", true) } else if (!active && progress > 0) { progress -= 0.12f if (progress <= 0) tag.putBoolean("Active", false) } tag.putFloat("Progress", progress.coerceIn(0f, 1f)) } override fun getAttributeModifiers( itemStack: ItemStack, equipmentSlot: EquipmentSlot ): Multimap { val itemIo = energyOf(itemStack) if (!isActive(itemStack) || itemIo == null || itemIo.amount <= 0) return ImmutableMultimap.of() else if (equipmentSlot == EquipmentSlot.MAINHAND) { val builder = ImmutableMultimap.builder() builder.put( EntityAttributes.GENERIC_ATTACK_DAMAGE, EntityAttributeModifier( ATTACK_DAMAGE_MODIFIER_ID, "Tool modifier", (itemStack.item as IRGamerAxeItem).attackDamage * (GamerAxeModule.SHARPNESS.getLevel(itemStack) / 2.0 + 1), EntityAttributeModifier.Operation.ADDITION ) ) builder.put( EntityAttributes.GENERIC_ATTACK_SPEED, EntityAttributeModifier( ATTACK_SPEED_MODIFIER_ID, "Tool modifier", 1.0, EntityAttributeModifier.Operation.ADDITION ) ) return builder.build() } return getAttributeModifiers(equipmentSlot) } override fun getLevel(enchantment: Enchantment, itemStack: ItemStack): Int { val module = when { Enchantments.LOOTING == enchantment -> GamerAxeModule.LOOTING Enchantments.FIRE_ASPECT == enchantment -> GamerAxeModule.FIRE_ASPECT else -> return 0 } return module.getLevel(itemStack) } companion object { private val ATTACK_DAMAGE_MODIFIER_ID = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF") private val ATTACK_SPEED_MODIFIER_ID = UUID.fromString("FA233E1C-4180-4865-B01B-BCCE9785ACA3") } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/energy/IRMiningDrillItem.kt ================================================ package me.steven.indrev.items.energy import me.steven.indrev.api.machines.Tier import me.steven.indrev.gui.tooltip.energy.EnergyTooltipData import me.steven.indrev.utils.energyOf import me.steven.indrev.utils.use import net.minecraft.block.BlockState import net.minecraft.block.Material import net.minecraft.client.item.TooltipContext import net.minecraft.client.item.TooltipData import net.minecraft.entity.LivingEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.item.PickaxeItem import net.minecraft.item.ToolMaterial import net.minecraft.text.Text import net.minecraft.util.math.BlockPos import net.minecraft.world.World import java.util.* open class IRMiningDrillItem( toolMaterial: ToolMaterial, private val tier: Tier, private val maxStored: Double, val baseMiningSpeed: Float, settings: Settings ) : PickaxeItem(toolMaterial, 0, 0F, settings.maxDamage(-1)), IREnergyItem { override fun getMiningSpeedMultiplier(stack: ItemStack, state: BlockState?): Float { val material = state?.material val hasEnergy = (energyOf(stack)?.amount ?: 0) > 0 return when { SUPPORTED_MATERIALS.contains(material) && hasEnergy -> baseMiningSpeed !hasEnergy -> 0F else -> super.getMiningSpeedMultiplier(stack, state) } } override fun postHit(stack: ItemStack, target: LivingEntity?, attacker: LivingEntity): Boolean { if (attacker !is PlayerEntity) return false energyOf(attacker.inventory, attacker.inventory.selectedSlot)?.use(2) ?: return false return true } override fun postMine( stack: ItemStack, world: World, state: BlockState, pos: BlockPos?, miner: LivingEntity ): Boolean { if (miner !is PlayerEntity) return false energyOf(miner.inventory, miner.inventory.selectedSlot)?.use(1) ?: return false return true } override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack) override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack) override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack) override fun isEnchantable(stack: ItemStack?): Boolean = false override fun appendTooltip( stack: ItemStack, world: World?, tooltip: MutableList?, context: TooltipContext? ) { } override fun canRepair(stack: ItemStack?, ingredient: ItemStack?): Boolean = false override fun getTooltipData(stack: ItemStack): Optional { val handler = energyOf(stack) ?: return Optional.empty() return Optional.of(EnergyTooltipData(handler.amount, handler.capacity)) } companion object { val SUPPORTED_MATERIALS = arrayOf( Material.METAL, Material.STONE, Material.WOOD, Material.BAMBOO, Material.COBWEB, Material.PISTON, Material.GOURD, Material.SOIL, Material.SOLID_ORGANIC, Material.LEAVES, Material.AGGREGATE ) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/energy/IRModularDrillItem.kt ================================================ package me.steven.indrev.items.energy import draylar.magna.api.BlockFinder import draylar.magna.api.BlockProcessor import draylar.magna.api.MagnaTool import draylar.magna.api.reach.ReachDistanceHelper import io.github.cottonmc.cotton.gui.client.CottonClientScreen import me.steven.indrev.IndustrialRevolutionClient import me.steven.indrev.api.CustomEnchantmentProvider import me.steven.indrev.api.machines.Tier import me.steven.indrev.gui.screenhandlers.blockblacklister.BlockBlacklisterScreenHandler import me.steven.indrev.gui.tooltip.modular.ModularTooltipData import me.steven.indrev.tools.modular.DrillModule import me.steven.indrev.tools.modular.IRModularItem import me.steven.indrev.tools.modular.MiningToolModule import me.steven.indrev.tools.modular.Module import me.steven.indrev.utils.energyOf import net.minecraft.block.BlockState import net.minecraft.client.MinecraftClient import net.minecraft.client.gui.screen.Screen import net.minecraft.client.item.TooltipContext import net.minecraft.client.item.TooltipData import net.minecraft.enchantment.Enchantment import net.minecraft.enchantment.Enchantments import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.item.ItemUsageContext import net.minecraft.item.ToolMaterial import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.Formatting import net.minecraft.util.Hand import net.minecraft.util.TypedActionResult import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.RaycastContext import net.minecraft.world.World import java.util.* class IRModularDrillItem( toolMaterial: ToolMaterial, tier: Tier, maxStored: Double, baseMiningSpeed: Float, settings: Settings ) : IRMiningDrillItem(toolMaterial, tier, maxStored, baseMiningSpeed, settings), MagnaTool, IRModularItem, CustomEnchantmentProvider { override fun getMiningSpeedMultiplier(stack: ItemStack, state: BlockState?): Float { val material = state?.material val hasEnergy = (energyOf(stack)?.amount ?: 0) > 0 val level = MiningToolModule.EFFICIENCY.getLevel(stack) var speedMultiplier = (level + 1) * 2 if (level == 5) speedMultiplier *= 50 return when { SUPPORTED_MATERIALS.contains(material) && hasEnergy -> baseMiningSpeed + speedMultiplier.toFloat() !hasEnergy -> 0F else -> super.getMiningSpeedMultiplier(stack, state) } } override fun appendTooltip(stack: ItemStack, world: World?, tooltip: MutableList?, context: TooltipContext?) { if (Screen.hasShiftDown()) getInstalledTooltip(getInstalled(stack), stack, tooltip) tooltip?.add( TranslatableText("item.indrev.modular_item.tooltip", LiteralText("").append( IndustrialRevolutionClient.MODULAR_CONTROLLER_KEYBINDING.boundKeyLocalizedText).formatted(Formatting.AQUA)).formatted( Formatting.GRAY)) } override fun use(world: World?, user: PlayerEntity, hand: Hand): TypedActionResult { val stack = user.getStackInHand(hand) if (world!!.isClient && (DrillModule.CONTROLLED_DESTRUCTION.getLevel(stack) > 0 || DrillModule.MATTER_PROJECTOR.getLevel(stack) > 0)) { MinecraftClient.getInstance().setScreen(object : CottonClientScreen(BlockBlacklisterScreenHandler()) { override fun shouldPause(): Boolean = false }) return TypedActionResult.success(stack) } return TypedActionResult.pass(stack) } override fun useOnBlock(context: ItemUsageContext): ActionResult { if (DrillModule.MATTER_PROJECTOR.getLevel(context.stack) <= 0) return ActionResult.PASS val world = context.world val player = context.player!! blockFinder.findPositions(world, context.player, getRadius(context.stack)).forEach { pos -> val blockState = world.getBlockState(pos) val offset = pos.offset(context.side) if (world.getBlockState(offset).material.isReplaceable) { val stackToRemove = ItemStack(blockState.block) val slot = player.inventory.getSlotWithStack(stackToRemove) if (slot >= 0) { val removed = player.inventory.removeStack(slot, 1) if (removed.item == stackToRemove.item && removed.count == 1) { world.setBlockState(offset, blockState) } } } } return ActionResult.success(world.isClient) } override fun getBlockFinder(): BlockFinder { return object : BlockFinder { override fun findPositions( world: World, playerEntity: PlayerEntity, radius: Int, depth: Int ): MutableList { val cameraPos = playerEntity.getCameraPosVec(1f) val rotation = playerEntity.getRotationVec(1f) val reachDistance = ReachDistanceHelper.getReachDistance(playerEntity) val combined = cameraPos.add(rotation.x * reachDistance, rotation.y * reachDistance, rotation.z * reachDistance) val blockHitResult = world.raycast(RaycastContext(cameraPos, combined, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, playerEntity)) val handStack = playerEntity.getStackInHand(Hand.MAIN_HAND) val blocks = super.findPositions(world, playerEntity, radius, depth) val center = getCenterPosition(world, playerEntity, blockHitResult, handStack) filterBlacklistedBlocks(center, blockHitResult, playerEntity, handStack, blocks) return blocks } } } override fun getCompatibleModules(itemStack: ItemStack): Array = DrillModule.COMPATIBLE override fun getLevel(enchantment: Enchantment, itemStack: ItemStack): Int { val module = when { Enchantments.FORTUNE == enchantment -> DrillModule.FORTUNE Enchantments.SILK_TOUCH == enchantment -> DrillModule.SILK_TOUCH else -> return -1 } return module.getLevel(itemStack) } override fun getRadius(stack: ItemStack): Int = DrillModule.RANGE.getLevel(stack) override fun playBreakEffects(): Boolean = false override fun getCenterPosition( world: World, player: PlayerEntity, blockHitResult: BlockHitResult, toolStack: ItemStack ): BlockPos { val pos = blockHitResult.blockPos val radius = getRadius(toolStack) return if (blockHitResult.side.axis == Direction.Axis.Y || radius < 1) pos else pos.up(radius - 1) } override fun attemptBreak( world: World?, pos: BlockPos?, player: PlayerEntity, breakRadius: Int, processor: BlockProcessor? ): Boolean { val mainHandStack = player.mainHandStack return if (getRadius(mainHandStack) > 0) super.attemptBreak(world, pos, player, breakRadius, processor) else false } override fun getTooltipData(stack: ItemStack): Optional { val handler = energyOf(stack) ?: return Optional.empty() val modules = getInstalled(stack) return Optional.of(ModularTooltipData(handler.amount, handler.capacity, modules) { it.getLevel(stack) }) } companion object { fun filterBlacklistedBlocks(center: BlockPos, blockHitResult: BlockHitResult, playerEntity: PlayerEntity, stack: ItemStack, blocks: MutableList) { blocks.removeIf { pos -> var offset = pos.subtract(center) if (blockHitResult.side.axis.isVertical) { offset = BlockPos(offset.x, offset.z, offset.y) if (playerEntity.horizontalFacing.axis == Direction.Axis.Z) { offset = BlockPos(offset.x* -playerEntity.horizontalFacing.offsetZ, offset.y* playerEntity.horizontalFacing.offsetZ, offset.z) } else if (playerEntity.horizontalFacing.axis == Direction.Axis.X) { offset = BlockPos(offset.y *playerEntity.horizontalFacing.offsetX, offset.x* playerEntity.horizontalFacing.offsetX, offset.z) } } else if (blockHitResult.side.axis == Direction.Axis.X) { offset = BlockPos(offset.z * -blockHitResult.side.offsetX, offset.y, offset.x) } else if (blockHitResult.side.axis == Direction.Axis.Z) { offset = BlockPos(offset.x * blockHitResult.side.offsetZ, offset.y, offset.z) } DrillModule.getBlacklistedPositions(stack).contains(offset) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/energy/IRPortableChargerItem.kt ================================================ package me.steven.indrev.items.energy import me.steven.indrev.gui.tooltip.energy.EnergyTooltipData import me.steven.indrev.utils.energyOf import net.minecraft.client.item.TooltipData import net.minecraft.entity.Entity import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.Inventory import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.world.World import team.reborn.energy.api.EnergyStorage import team.reborn.energy.api.EnergyStorageUtil import team.reborn.energy.impl.SimpleItemEnergyStorageImpl import java.util.* class IRPortableChargerItem( settings: Settings, maxStored: Long ) : Item(settings), IREnergyItem { init { EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, maxStored, 16384, 16384) }, this) } override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack) override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack) override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack) override fun inventoryTick(stack: ItemStack, world: World?, entity: Entity?, slot: Int, selected: Boolean) { val player = entity as? PlayerEntity ?: return if (player.offHandStack != stack && player.mainHandStack != stack) return chargeItemsInInv(slot, player.inventory) } override fun getTooltipData(stack: ItemStack): Optional { val handler = energyOf(stack) ?: return Optional.empty() return Optional.of(EnergyTooltipData(handler.amount, handler.capacity)) } companion object { fun chargeItemsInInv(slot: Int, inventory: Inventory) { val items = (0 until inventory.size()) .filter { s -> s != slot } .mapNotNull { s -> energyOf(inventory, s) } var rem = 16384L items.forEach { h -> if (rem <= 0) return rem -= EnergyStorageUtil.move(energyOf(inventory, slot) ?: return, h, rem, null) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/energy/MachineBlockItem.kt ================================================ package me.steven.indrev.items.energy import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.gui.tooltip.energy.EnergyTooltipData import me.steven.indrev.utils.buildMachineTooltip import me.steven.indrev.utils.energyOf import net.minecraft.block.Block import net.minecraft.client.item.TooltipContext import net.minecraft.client.item.TooltipData import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack import net.minecraft.text.Text import net.minecraft.world.World import team.reborn.energy.api.EnergyStorage import team.reborn.energy.impl.SimpleItemEnergyStorageImpl import java.util.* class MachineBlockItem(private val machineBlock: Block, settings: Settings) : BlockItem(machineBlock, settings) { init { val capacity = ((machineBlock as? MachineBlock)?.config?.maxEnergyStored ?: 0).toLong() EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, capacity, Tier.MK4.io, 0) }, this) } override fun appendTooltip( stack: ItemStack?, world: World?, tooltip: MutableList?, options: TooltipContext? ) { val config = (machineBlock as? MachineBlock)?.config buildMachineTooltip(config ?: return, tooltip) } override fun getTooltipData(stack: ItemStack): Optional { val handler = energyOf(stack) ?: return Optional.empty() return Optional.of(EnergyTooltipData(handler.amount, handler.capacity)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/misc/IRCraftingToolItem.kt ================================================ package me.steven.indrev.items.misc import me.steven.indrev.FabricRecipeRemainder import net.minecraft.entity.player.PlayerEntity import net.minecraft.inventory.CraftingInventory import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.server.network.ServerPlayerEntity import kotlin.random.Random import kotlin.random.asJavaRandom class IRCraftingToolItem(settings: Settings) : Item(settings), FabricRecipeRemainder { override fun getRemainder(stack: ItemStack?, craftingInventory: CraftingInventory?, playerEntity: PlayerEntity?): ItemStack? { return if (stack != null && stack.isDamageable) { val stackCopy = stack.copy().also { it.damage(1, Random.asJavaRandom(), playerEntity as? ServerPlayerEntity) } if (stackCopy.damage >= stackCopy.maxDamage) ItemStack.EMPTY else stackCopy } else stack } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/misc/IREnergyReaderItem.kt ================================================ package me.steven.indrev.items.misc import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.utils.energyOf import net.minecraft.item.Item import net.minecraft.item.ItemUsageContext import net.minecraft.server.world.ServerWorld import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.Formatting class IREnergyReaderItem(settings: Settings) : Item(settings) { override fun useOnBlock(context: ItemUsageContext?): ActionResult { if (context?.world?.isClient == true) return ActionResult.SUCCESS val blockPos = context?.blockPos val blockEntity = context?.world?.getBlockEntity(blockPos) val machineIo = energyOf(context!!.world as ServerWorld, blockPos!!, context.side) if (machineIo != null) { val energy = machineIo.amount val text = TranslatableText("item.indrev.energy_reader.use") .formatted(Formatting.BLUE) .append(LiteralText(" $energy LF").formatted(Formatting.WHITE)) if (blockEntity is MachineBlockEntity<*>) { val energyCost = when { blockEntity !is LazuliFluxContainerBlockEntity && blockEntity.config is BasicMachineConfig -> blockEntity.getEnergyCost() else -> -1 } if (energyCost > 0) { val energyCostText = TranslatableText( "item.indrev.energy_reader.use1", LiteralText(energyCost.toString()).formatted(Formatting.WHITE) ).formatted(Formatting.BLUE) text .append(LiteralText(" | ").formatted(Formatting.BLACK, Formatting.BOLD)) .append(energyCostText) } } context.player?.sendMessage(text, true) } return ActionResult.FAIL } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/misc/IRGuideBookItem.kt ================================================ package me.steven.indrev.items.misc import me.steven.indrev.utils.identifier import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.util.Hand import net.minecraft.util.TypedActionResult import net.minecraft.world.World import vazkii.patchouli.api.PatchouliAPI class IRGuideBookItem(settings: Settings) : Item(settings) { override fun use(world: World, user: PlayerEntity, hand: Hand?): TypedActionResult { if (!world.isClient) { PatchouliAPI.get().openBookGUI(user as ServerPlayerEntity, identifier("indrev")) return TypedActionResult.success(user.getStackInHand(hand)) } return TypedActionResult.consume(user.getStackInHand(hand)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/misc/IRMachineUpgradeItem.kt ================================================ package me.steven.indrev.items.misc import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blocks.machine.FacingMachineBlock import me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.registry.IRItemRegistry import net.minecraft.client.item.TooltipContext import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.item.ItemUsageContext import net.minecraft.nbt.NbtCompound import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.Formatting import net.minecraft.world.World import java.util.* class IRMachineUpgradeItem(settings: Settings, val from: Tier, val to: Tier) : Item(settings) { override fun appendTooltip(stack: ItemStack?, world: World?, tooltip: MutableList?, context: TooltipContext?) { tooltip?.add(TranslatableText("item.indrev.tier_upgrade_${to.toString().lowercase(Locale.getDefault())}.tooltip").formatted(Formatting.GREEN)) super.appendTooltip(stack, world, tooltip, context) } override fun useOnBlock(context: ItemUsageContext?): ActionResult { val world = context?.world if (world?.isClient == true) return ActionResult.PASS val blockPos = context?.blockPos val state = world?.getBlockState(blockPos) val block = state?.block as? MachineBlock ?: return ActionResult.PASS val blockEntity = world.getBlockEntity(blockPos) as? MachineBlockEntity<*> ?: return ActionResult.PASS if (block.tier == from) { if (!blockEntity.registry.upgradeable) return ActionResult.PASS val inventoryTag = blockEntity.inventoryComponent?.writeNbt(NbtCompound()) blockEntity.inventoryComponent?.inventory?.clear() val fluidTag = blockEntity.fluidComponent?.toTag(NbtCompound()) val temperatureTag = blockEntity.temperatureComponent?.writeNbt(NbtCompound()) val energy = blockEntity.energy var newState = blockEntity.registry.block(to).defaultState if (state.contains(FacingMachineBlock.FACING)) newState = newState.with(FacingMachineBlock.FACING, state[FacingMachineBlock.FACING]) else if (state.contains(HorizontalFacingMachineBlock.HORIZONTAL_FACING)) newState = newState.with(HorizontalFacingMachineBlock.HORIZONTAL_FACING, state[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) world.setBlockState(blockPos, newState) val upgradedBlockEntity = world.getBlockEntity(blockPos) as? MachineBlockEntity<*> ?: throw RuntimeException("This should never happen, what the fuck") upgradedBlockEntity.energy = energy upgradedBlockEntity.inventoryComponent?.readNbt(inventoryTag) upgradedBlockEntity.fluidComponent?.fromTag(fluidTag) upgradedBlockEntity.temperatureComponent?.readNbt(temperatureTag) context.player?.getStackInHand(context.hand)?.decrement(1) return ActionResult.SUCCESS } return super.useOnBlock(context) } companion object { fun fromTier(tier: Tier): IRMachineUpgradeItem { return when (tier) { Tier.MK1 -> IRItemRegistry.TIER_UPGRADE_MK2 Tier.MK2 -> IRItemRegistry.TIER_UPGRADE_MK3 Tier.MK3 -> IRItemRegistry.TIER_UPGRADE_MK4 else -> error("no upgrade available") } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/misc/IRServoItem.kt ================================================ package me.steven.indrev.items.misc import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import me.steven.indrev.blocks.machine.pipes.BasePipeBlock import me.steven.indrev.networks.EndpointData import me.steven.indrev.networks.ServoNetworkState import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.component3 import net.minecraft.client.item.TooltipContext import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.item.ItemUsageContext import net.minecraft.server.world.ServerWorld import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.* import net.minecraft.world.World class IRServoItem(settings: Settings, val type: EndpointData.Type) : Item(settings) { override fun appendTooltip( stack: ItemStack, world: World?, tooltip: MutableList, context: TooltipContext? ) { tooltip.add(TranslatableText("$translationKey.tooltip")) tooltip.add(LiteralText.EMPTY) val modeString = getMode(stack).toString().lowercase() tooltip.add(TranslatableText("item.indrev.servo.mode") .append(TranslatableText("item.indrev.servo.mode.$modeString").formatted(Formatting.BLUE))) tooltip.add(TranslatableText("item.indrev.servo.mode.$modeString.tooltip").formatted(Formatting.DARK_GRAY)) } override fun use(world: World?, user: PlayerEntity, hand: Hand?): TypedActionResult { if (world?.isClient == true) return TypedActionResult.pass(user.getStackInHand(hand)) val stack = user.getStackInHand(hand) val newMode = getMode(stack).next() stack.orCreateNbt.putString("mode", newMode.toString()) user.sendMessage(TranslatableText("item.indrev.servo.mode") .append(TranslatableText("item.indrev.servo.mode.${newMode.toString().lowercase()}").formatted(Formatting.BLUE)), true) return TypedActionResult.consume(stack) } override fun useOnBlock(context: ItemUsageContext): ActionResult { val world = context.world if (world.isClient) return ActionResult.CONSUME val hand = context.hand val stack = context.stack val hit = context.hitPos val pos = context.blockPos val state = world.getBlockState(pos) val block = state.block if (block !is BasePipeBlock) return ActionResult.PASS if (world is ServerWorld && hand == Hand.MAIN_HAND) { val dir = BasePipeBlock.getSideFromHit(hit, pos!!) val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return ActionResult.PASS if (dir != null && blockEntity.connections[dir]!!.isConnected()) { val network = block.type.getNetworkState(world) as? ServoNetworkState? network?.also { networkState -> if (networkState.networksByPos.get(pos.asLong())?.containers?.containsKey(pos.offset(dir)) == true) { val (x, y, z) = hit if (networkState.hasServo(pos, dir)) { when (networkState.getEndpointData(pos, dir)?.type) { EndpointData.Type.OUTPUT -> ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_OUTPUT)) EndpointData.Type.RETRIEVER -> ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_RETRIEVER)) else -> {} } } val data = networkState.getEndpointData(pos, dir, true) ?: return@also context.player!!.sendMessage(LiteralText("Failed to put servo"), true) data.type = type data.mode = getMode(stack) networkState.version++ stack.decrement(1) networkState.markDirty() return ActionResult.SUCCESS } } } } return ActionResult.PASS } companion object { fun getMode(itemStack: ItemStack): EndpointData.Mode { val m = itemStack.orCreateNbt.getString("mode") if (m.isNullOrEmpty()) return EndpointData.Mode.NEAREST_FIRST return EndpointData.Mode.valueOf(m.uppercase()) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/misc/OreDataCardItem.kt ================================================ package me.steven.indrev.items.misc import me.steven.indrev.api.OreDataCards import me.steven.indrev.gui.tooltip.oredatacards.OreDataCardTooltipData import me.steven.indrev.utils.itemSettings import net.minecraft.client.item.TooltipContext import net.minecraft.client.item.TooltipData import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.world.World import java.util.* import kotlin.math.roundToInt class OreDataCardItem : Item(itemSettings().maxCount(1)) { override fun appendTooltip( stack: ItemStack, world: World?, tooltip: MutableList, context: TooltipContext? ) { val data = OreDataCards.readNbt(stack) ?: return if (!data.isValid()) { tooltip.add(LiteralText("Invalid data card!").formatted(Formatting.RED)) } } override fun getName(stack: ItemStack): Text { return if (OreDataCards.readNbt(stack) == null) TranslatableText("item.indrev.empty_ore_data_card") else super.getName(stack) } override fun isItemBarVisible(stack: ItemStack): Boolean { return OreDataCards.readNbt(stack) != null } override fun getItemBarColor(stack: ItemStack): Int { val data = OreDataCards.readNbt(stack) ?: return -1 return if (!data.isValid()) return 0xff0000 else 0xffffff } override fun getItemBarStep(stack: ItemStack): Int { val data = OreDataCards.readNbt(stack) ?: return 0 if (!data.isValid()) return 13 return 13-((OreDataCards.MAX_SIZE - (data.maxCycles - data.used)) * 13.0f / OreDataCards.MAX_SIZE.toDouble()).roundToInt() } override fun getTooltipData(stack: ItemStack): Optional { val data = OreDataCards.readNbt(stack) if (data?.isValid() != true) return Optional.empty() return Optional.of(OreDataCardTooltipData(data)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/misc/Tools.kt ================================================ package me.steven.indrev.items.misc import net.minecraft.item.* //fuck Mojang with protected shit class IRBasicSword(material: ToolMaterial, attackDamage: Int, attackSpeed: Float, settings: Settings) : SwordItem(material, attackDamage, attackSpeed, settings) class IRBasicShovel(material: ToolMaterial, attackDamage: Float, attackSpeed: Float, settings: Settings) : ShovelItem(material, attackDamage, attackSpeed, settings) class IRBasicPickaxe(material: ToolMaterial, attackDamage: Int, attackSpeed: Float, settings: Settings) : PickaxeItem(material, attackDamage, attackSpeed, settings) class IRBasicHoe(material: ToolMaterial, attackDamage: Int, attackSpeed: Float, settings: Settings) : HoeItem(material, attackDamage, attackSpeed, settings) class IRBasicAxe(material: ToolMaterial, attackDamage: Float, attackSpeed: Float, settings: Settings) : AxeItem(material, attackDamage, attackSpeed, settings) ================================================ FILE: src/main/kotlin/me/steven/indrev/items/models/TankItemBakedModel.kt ================================================ package me.steven.indrev.items.models import alexiil.mc.lib.attributes.fluid.volume.FluidVolume import com.mojang.datafixers.util.Pair import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.minecraft.block.BlockState import net.minecraft.client.MinecraftClient import net.minecraft.client.render.model.* import net.minecraft.client.render.model.json.ModelOverrideList import net.minecraft.client.render.model.json.ModelTransformation import net.minecraft.client.texture.Sprite import net.minecraft.client.util.ModelIdentifier import net.minecraft.client.util.SpriteIdentifier import net.minecraft.fluid.Fluids import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockRenderView import java.util.* import java.util.function.Function import java.util.function.Supplier class TankItemBakedModel : UnbakedModel, BakedModel, FabricBakedModel { private val modelIdentifier = ModelIdentifier( identifier("tank"), "down=false,up=false" ) private val transform: ModelTransformation by lazy { MinecraftClient.getInstance().bakedModelManager.getModel( ModelIdentifier(Identifier("stone"), "") ).transformation } override fun isVanillaAdapter(): Boolean = false override fun emitItemQuads(stack: ItemStack, randSupplier: Supplier, context: RenderContext) { val tankModel = MinecraftClient.getInstance().bakedModelManager.getModel( modelIdentifier ) context.fallbackConsumer().accept(tankModel) val stackTag = stack.orCreateNbt val volume = readNbt(stackTag) ?: return val player = MinecraftClient.getInstance().player val world = player?.world val pos = player?.blockPos val fluid = volume.rawFluid ?: Fluids.EMPTY val fluidRenderHandler = FluidRenderHandlerRegistry.INSTANCE.get(fluid) ?: return val fluidColor = fluidRenderHandler.getFluidColor(world, pos, fluid.defaultState) val fluidSprite = fluidRenderHandler.getFluidSprites(world, pos, fluid.defaultState)[0] val color = 255 shl 24 or fluidColor context.pushTransform { quad -> quad.spriteColor(0, color, color, color, color) true } val emitter = context.emitter val p = (volume.amount().asLong(1L) / 8f).coerceAtMost(0.9f) emitter.draw(Direction.UP, fluidSprite, 0.09375f, 0.09f, 0.9f, 0.90625f, (0.9f - p) + 0.09575f) emitter.draw(Direction.NORTH, fluidSprite, 0.09375f, 0.06f, 0.9f, p, 0.09575f) emitter.draw(Direction.SOUTH, fluidSprite, 0.09375f, 0.06f, 0.9f, p, 0.09575f) emitter.draw(Direction.EAST, fluidSprite, 0.09375f, 0.06f, 0.9f, p, 0.09575f) emitter.draw(Direction.WEST, fluidSprite, 0.09375f, 0.06f, 0.9f, p, 0.09575f) context.popTransform() } private fun QuadEmitter.draw( side: Direction, sprite: Sprite, left: Float, bottom: Float, right: Float, top: Float, depth: Float ) { square(side, left, bottom, right, top, depth) spriteBake(0, sprite, MutableQuadView.BAKE_LOCK_UV) spriteColor(0, -1, -1, -1, -1) emit() } override fun emitBlockQuads( p0: BlockRenderView?, p1: BlockState?, p2: BlockPos?, p3: Supplier?, p4: RenderContext? ) { } override fun getOverrides(): ModelOverrideList = ModelOverrideList.EMPTY override fun getQuads(state: BlockState?, face: Direction?, random: Random?): MutableList = mutableListOf() override fun getParticleSprite() = null override fun hasDepth(): Boolean = false override fun getTransformation(): ModelTransformation = transform override fun useAmbientOcclusion(): Boolean = true override fun isSideLit(): Boolean = false override fun isBuiltin(): Boolean = false private fun readNbt(tag: NbtCompound?): FluidVolume? { val tanksTag = tag?.getCompound("tanks") tanksTag?.keys?.forEach { key -> val tankTag = tanksTag.getCompound(key) return FluidVolume.fromTag(tankTag.getCompound("fluids")) } return null } override fun getModelDependencies(): MutableCollection = mutableListOf() override fun getTextureDependencies( unbakedModelGetter: Function?, unresolvedTextureReferences: MutableSet>? ): MutableCollection = mutableListOf() override fun bake( loader: ModelLoader?, textureGetter: Function?, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel = this } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/upgrade/Enhancer.kt ================================================ package me.steven.indrev.items.upgrade import me.steven.indrev.components.EnhancerComponent import me.steven.indrev.config.IRConfig enum class Enhancer { SPEED, BUFFER, BLAST_FURNACE, SMOKER, DAMAGE; companion object { val DEFAULT = arrayOf(SPEED, BUFFER) val FURNACE = arrayOf(SPEED, BUFFER, BLAST_FURNACE, SMOKER) fun getDamageMultiplier(enhancers: Map): Double { return (IRConfig.upgrades.damageUpgradeModifier * (enhancers[DAMAGE] ?: 0).toDouble()).coerceAtLeast(1.0) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/items/upgrade/IREnhancerItem.kt ================================================ package me.steven.indrev.items.upgrade import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.gui.IRInventoryScreen import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import net.minecraft.client.MinecraftClient import net.minecraft.client.item.TooltipContext import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.world.World import java.util.* class IREnhancerItem(settings: Settings, val enhancer: Enhancer) : Item(settings) { override fun appendTooltip(stack: ItemStack?, world: World?, tooltip: MutableList?, context: TooltipContext?) { tooltip?.add(TranslatableText("item.indrev.${enhancer.toString().lowercase(Locale.getDefault())}_enhancer.tooltip").formatted(Formatting.GREEN)) tooltip?.add(LiteralText.EMPTY) val currentScreen = MinecraftClient.getInstance().currentScreen if (currentScreen is IRInventoryScreen<*>) { val handler = currentScreen.screenHandler as? IRGuiScreenHandler ?: return handler.ctx.run { _, pos -> val blockEntity = world?.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@run val enhancerComponent = blockEntity.enhancerComponent ?: return@run if (!enhancerComponent.compatible.contains(enhancer)) tooltip?.add(TranslatableText("item.indrev.enhancers.incompatible").formatted(Formatting.DARK_RED)) else tooltip?.add(TranslatableText("item.indrev.enhancers.count", enhancerComponent.maxSlotCount(enhancer)).formatted(Formatting.AQUA)) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/EndpointData.kt ================================================ package me.steven.indrev.networks import me.steven.indrev.utils.ItemFilter import me.steven.indrev.utils.fluidStorageOf import me.steven.indrev.utils.itemStorageOf import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction import net.minecraft.nbt.NbtCompound import net.minecraft.server.world.ServerWorld import java.util.* import kotlin.random.Random data class EndpointData(var type: Type, var mode: Mode?) { fun writeNbt(tag: NbtCompound): NbtCompound { tag.putInt("t", type.ordinal) if (mode != null) tag.putInt("m", mode!!.ordinal) return tag } fun readNbt(tag: NbtCompound): EndpointData { val type = Type.VALUES[tag.getInt("t")] val mode = if (tag.contains("m")) Mode.VALUES[tag.getInt("m")] else null return EndpointData(type, mode) } enum class Type { RETRIEVER, OUTPUT, INPUT; companion object { val VALUES = values() } } enum class Mode { ROUND_ROBIN { override fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array) -> Unit { return { array -> Transaction.openOuter().use { tx -> Arrays.sort(array, if (type == Type.RETRIEVER) Comparator.comparing { node -> node as Node fluidStorageOf(world, node.target, node.direction)?.iterable(tx)?.sumOf { v -> if (!filter(v.resource)) 0 else v.amount } ?: 0 }.reversed() else Comparator.comparing { node -> node as Node fluidStorageOf(world, node.target, node.direction)?.iterable(tx)?.sumOf { v -> if (!filter(v.resource)) 0 else v.amount } ?: 0 }) tx.abort() } } } override fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array) -> Unit { return { array -> Transaction.openOuter().use { tx -> Arrays.sort(array, (if (type == Type.RETRIEVER) Comparator.comparing { node -> node as Node itemStorageOf(world, node.target, node.direction)?.iterable(tx)?.sumOf { v -> if (!filter(v.resource)) 0 else v.amount } ?: 0 }.reversed() else Comparator.comparing { node -> node as Node itemStorageOf(world, node.target, node.direction)?.iterable(tx)?.sumOf { v -> if (!filter(v.resource)) 0 else v.amount } ?: 0 }) ) } } } }, FURTHEST_FIRST { override fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array) -> Unit { return { array -> Arrays.sort(array) { first, second -> first as Node second as Node when { first.dist > second.dist -> -1 first.dist < second.dist -> 1 else -> 0 } } } } override fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array) -> Unit { return { array -> Arrays.sort(array) { first, second -> first as Node second as Node when { first.dist > second.dist -> -1 first.dist < second.dist -> 1 else -> 0 } } } } }, NEAREST_FIRST { override fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array) -> Unit { return { array -> Arrays.sort(array) } } override fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array) -> Unit { return { array -> Arrays.sort(array) } } }, RANDOM { override fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array) -> Unit { return { array -> array.shuffle() } } override fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array) -> Unit { return { array -> array.shuffle() } } }; abstract fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array) -> Unit abstract fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array) -> Unit fun next(): Mode { return when (this) { ROUND_ROBIN -> FURTHEST_FIRST FURTHEST_FIRST -> NEAREST_FIRST NEAREST_FIRST -> RANDOM RANDOM -> ROUND_ROBIN } } companion object { val R = Random(System.currentTimeMillis()) val VALUES = values() } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/Network.kt ================================================ package me.steven.indrev.networks import it.unimi.dsi.fastutil.longs.LongOpenHashSet import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import me.steven.indrev.networks.client.ClientNetworkInfo import me.steven.indrev.networks.client.ClientServoNetworkInfo import me.steven.indrev.networks.client.node.ClientServoNodeInfo import me.steven.indrev.networks.energy.EnergyNetwork import me.steven.indrev.networks.factory.ENERGY_NET_FACTORY import me.steven.indrev.networks.factory.FLUID_NET_FACTORY import me.steven.indrev.networks.factory.ITEM_NET_FACTORY import me.steven.indrev.networks.factory.NetworkFactory import me.steven.indrev.networks.fluid.FluidNetwork import me.steven.indrev.networks.fluid.FluidNetworkState import me.steven.indrev.networks.item.ItemNetwork import me.steven.indrev.networks.item.ItemNetworkState import me.steven.indrev.utils.energyNetworkState import me.steven.indrev.utils.fluidNetworkState import me.steven.indrev.utils.itemNetworkState import net.minecraft.block.Block import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import java.util.* abstract class Network( val type: Type<*>, val world: ServerWorld, val pipes: MutableSet = ObjectOpenHashSet(), val containers: MutableMap> = Object2ObjectOpenHashMap() ) { val state = type.getNetworkState(world) protected val queue = Object2ObjectOpenHashMap>(containers.size) protected fun isQueueValid(): Boolean { if (queue.isEmpty() && containers.isNotEmpty()) { buildQueue() } return queue.isNotEmpty() } protected fun buildQueue() { queue.clear() containers.forEach { (pos, _) -> find(pos, pos, 0, LongOpenHashSet()) } } protected fun find(source: BlockPos, blockPos: BlockPos, count: Int, s: LongOpenHashSet) { DIRECTIONS.forEach { dir -> val offset = blockPos.offset(dir.opposite) if (pipes.contains(offset) && s.add(offset.asLong())) { find(source, offset, count + 1, s) } if (source != offset && containers.contains(offset) && containers[offset]!!.contains(dir)) { queue.computeIfAbsent(source, Object2ObjectFunction { ArrayList(containers.size) }).add(Node(source, offset, count, dir)) } } } abstract fun tick(world: ServerWorld) open fun remove() { state.remove(this) } @Suppress("UNCHECKED_CAST") open fun appendPipe(block: Block, blockPos: BlockPos) { pipes.add(blockPos) state[blockPos] = this } open fun appendContainer(blockPos: BlockPos, direction: Direction) { containers.computeIfAbsent(blockPos) { EnumSet.noneOf(Direction::class.java) }.add(direction) state[blockPos] = this } companion object { val DIRECTIONS = Direction.values() fun handleBreak(state: NetworkState, pos: BlockPos) { state.networksByPos[pos.asLong()]?.remove() DIRECTIONS.forEach { val offset = pos.offset(it) handleUpdate(state, offset) } } fun handleUpdate(state: NetworkState, pos: BlockPos) { state.networksByPos[pos.asLong()]?.remove() state.queueUpdate(pos.asLong(), true) } } abstract class Type(val key: String) { abstract val factory: NetworkFactory abstract fun createEmpty(world: ServerWorld): T abstract fun getNetworkState(world: ServerWorld): NetworkState open fun createClientNetworkInfo(world: ServerWorld): ClientNetworkInfo<*>? { val state = getNetworkState(world) if (state !is ServoNetworkState<*>) return null return ClientServoNetworkInfo().also { state.endpointData.forEach { (pos, data) -> val info = ClientServoNodeInfo(pos, Object2ObjectOpenHashMap()) data.forEach { (dir, endpointData) -> info.servos[dir] = endpointData.type } it.pipes[pos] = info } } } companion object { val ENERGY = object : Type(NetworkState.ENERGY_KEY) { override val factory: NetworkFactory = ENERGY_NET_FACTORY override fun createEmpty(world: ServerWorld): EnergyNetwork = EnergyNetwork(world) override fun getNetworkState(world: ServerWorld): NetworkState = world.energyNetworkState } val FLUID = object : Type(NetworkState.FLUID_KEY) { override val factory: NetworkFactory = FLUID_NET_FACTORY override fun createEmpty(world: ServerWorld): FluidNetwork = FluidNetwork(world) override fun getNetworkState(world: ServerWorld): FluidNetworkState = world.fluidNetworkState } val ITEM = object : Type(NetworkState.ITEM_KEY) { override val factory: NetworkFactory = ITEM_NET_FACTORY override fun createEmpty(world: ServerWorld): ItemNetwork = ItemNetwork(world) override fun getNetworkState(world: ServerWorld): ItemNetworkState = world.itemNetworkState } fun valueOf(string: String): Type<*> { return when (string) { NetworkState.ENERGY_KEY -> ENERGY NetworkState.FLUID_KEY -> FLUID NetworkState.ITEM_KEY -> ITEM else -> throw IllegalArgumentException("Unknown network type $string") } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/NetworkEvents.kt ================================================ package me.steven.indrev.networks import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents import net.minecraft.block.entity.BlockEntity import net.minecraft.server.MinecraftServer import net.minecraft.server.world.ServerWorld object NetworkEvents : ServerTickEvents.EndWorldTick, ServerBlockEntityEvents.Load, ServerLifecycleEvents.ServerStopping { override fun onEndTick(world: ServerWorld) { Network.Type.ENERGY.getNetworkState(world).tick(world) Network.Type.FLUID.getNetworkState(world).tick(world) Network.Type.ITEM.getNetworkState(world).tick(world) } override fun onServerStopping(server: MinecraftServer) { server.worlds.forEach { world -> Network.Type.ENERGY.getNetworkState(world).markDirty() } } override fun onLoad(blockEntity: BlockEntity, world: ServerWorld) { if (blockEntity is BasePipeBlockEntity) { val networkState = blockEntity.pipeType.getNetworkState(world) networkState.queueUpdate(blockEntity.pos.asLong()) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/NetworkState.kt ================================================ package me.steven.indrev.networks import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import it.unimi.dsi.fastutil.longs.LongOpenHashSet import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import net.minecraft.nbt.NbtCompound import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.world.PersistentState open class NetworkState(val type: Network.Type, val world: ServerWorld) : PersistentState() { val networksByPos = Long2ObjectOpenHashMap() val networks = ObjectOpenHashSet() private val queuedUpdates = LongOpenHashSet() val updatedPositions = LongOpenHashSet() fun queueUpdate(pos: Long, force: Boolean = false) { if (!networksByPos.contains(pos) || force) queuedUpdates.add(pos) } operator fun set(pos: BlockPos, network: Network) { networksByPos[pos.asLong()] = network as T } open fun remove(network: Network) { network.pipes.forEach { pos -> networksByPos.remove(pos.asLong()) onRemoved(pos) } network.containers.forEach { (pos, _) -> networksByPos.remove(pos.asLong()) onRemoved(pos) } networks.remove(network) } open fun add(network: Network) { this.networks.add(network) } open fun tick(world: ServerWorld) { // this is to avoid a weird CME I cannot reproduce // something is queueing an update while going through the other ones (nothing is ever removed from it until the .clear()) // so I copy and clear the original before iterating so if anything is added while iterating, it will be processed in the next tick. // TODO find this CME and fix it :) val copy = LongOpenHashSet(queuedUpdates) queuedUpdates.clear() copy.forEach { pos -> if (!updatedPositions.contains(pos)) { val network = type.factory.deepScan(type, world, BlockPos.fromLong(pos)) if (network.pipes.isNotEmpty()) add(network) else remove(network) } } updatedPositions.clear() world.profiler.push("indrev_${type.key}NetworkTick") networks.forEach { network -> network.tick(world) } world.profiler.pop() } open fun onRemoved(pos: BlockPos) { } open fun onSet(blockPos: BlockPos, network: T) { } override fun writeNbt(tag: NbtCompound): NbtCompound { return tag } companion object { const val ENERGY_KEY = "indrev_networks" const val FLUID_KEY = "indrev_fluid_networks" const val ITEM_KEY = "indrev_item_networks" } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/Node.kt ================================================ package me.steven.indrev.networks import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction data class Node(val origin: BlockPos, val target: BlockPos, val dist: Int, val direction: Direction) : Comparable { override fun compareTo(other: Node): Int { return when { this.dist > other.dist -> 1 this.dist < other.dist -> -1 else -> 0 } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/ServoNetworkState.kt ================================================ package me.steven.indrev.networks import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import me.steven.indrev.packets.client.SyncNetworkServosPacket import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.entity.player.PlayerEntity import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtList import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import java.util.* import java.util.function.LongFunction abstract class ServoNetworkState(type: Network.Type, world: ServerWorld) : NetworkState(type, world) { val endpointData = Long2ObjectOpenHashMap>() private val recentlyRemoved = Long2ObjectOpenHashMap>() private val syncedMaps = Object2IntOpenHashMap() init { syncedMaps.defaultReturnValue(-1) } var version = 1 override fun tick(world: ServerWorld) { super.tick(world) sync(world) clearCachedData(false) } fun sync(world: ServerWorld) { world.players.forEach { player -> val v = syncedMaps.getInt(player.uuid) if (version > v) { val buf = PacketByteBufs.create() buf.writeString(type.key) type.createClientNetworkInfo(world)?.write(buf) ServerPlayNetworking.send(player, SyncNetworkServosPacket.SYNC_NETWORK_SERVOS, buf) syncedMaps[player.uuid] =version } } } fun onDimChange(playerEntity: PlayerEntity) { syncedMaps.removeInt(playerEntity.uuid) } override fun onRemoved(pos: BlockPos) { version++ super.onRemoved(pos) if (endpointData.containsKey(pos.asLong())) recentlyRemoved[pos.asLong()] = endpointData.remove(pos.asLong()) } override fun onSet(blockPos: BlockPos, network: T) { version++ if (recentlyRemoved.containsKey(blockPos.asLong())) { endpointData[blockPos.asLong()] = recentlyRemoved.remove(blockPos.asLong()) } } fun hasServo(blockPos: BlockPos, direction: Direction): Boolean { return endpointData.get(blockPos.asLong())?.get(direction)?.type?.let { it != EndpointData.Type.INPUT } == true } fun getEndpointData(pos: BlockPos, direction: Direction, createIfAbsent: Boolean = false): EndpointData? { return getEndpointData(pos.asLong(), direction, createIfAbsent) } fun getEndpointData(pos: Long, direction: Direction, createIfAbsent: Boolean = false): EndpointData? { return if (createIfAbsent) endpointData.computeIfAbsent(pos, LongFunction { Object2ObjectOpenHashMap() }) .computeIfAbsent(direction, Object2ObjectFunction { EndpointData(EndpointData.Type.INPUT, null) }) else endpointData.get(pos)?.get(direction) } fun removeEndpointData(pos: BlockPos, direction: Direction): EndpointData? { version++ val datas = endpointData.get(pos.asLong()) ?: return null val d = datas.remove(direction) if (datas.isEmpty()) endpointData.remove(pos.asLong()) return d } open fun clearCachedData(importCache: Boolean) { if (importCache) { recentlyRemoved.forEach { e -> endpointData[e.key] = e.value } } this.recentlyRemoved.clear() } override fun writeNbt(tag: NbtCompound): NbtCompound { val modesTag = NbtList() endpointData.forEach { (pos, modes) -> val sidesTag = NbtList() modes.forEach { (dir, mode) -> val t = NbtCompound() t.put(dir.ordinal.toString(), mode.writeNbt(NbtCompound())) sidesTag.add(t) } val posTag = NbtCompound() posTag.putLong("pos", pos) posTag.put("sides", sidesTag) modesTag.add(posTag) } tag.put("modes", modesTag) return super.writeNbt(tag) } companion object { fun > readNbt(tag: NbtCompound, supplier: () -> P): P { val state = supplier() val modesTag = tag.getList("modes", 10) modesTag.forEach { posTag -> posTag as NbtCompound val pos = posTag.getLong("pos") val map = Object2ObjectOpenHashMap() val sidesTag = posTag.getList("sides", 10) sidesTag.forEach { t -> t as NbtCompound t.keys.forEach { id -> val data = EndpointData(EndpointData.Type.INPUT, EndpointData.Mode.NEAREST_FIRST).readNbt(t.getCompound(id)) val dir = Direction.values()[id.toInt()] map[dir] = data } } state.endpointData[pos] = map } return state } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/client/ClientNetworkInfo.kt ================================================ package me.steven.indrev.networks.client import me.steven.indrev.networks.client.node.ClientNodeInfo import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.network.PacketByteBuf interface ClientNetworkInfo { fun write(buf: PacketByteBuf) @Environment(EnvType.CLIENT) fun read(buf: PacketByteBuf) fun createNodes(): List } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/client/ClientNetworkState.kt ================================================ package me.steven.indrev.networks.client import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import me.steven.indrev.networks.Network import me.steven.indrev.networks.client.node.ClientNodeInfo import me.steven.indrev.utils.component1 import me.steven.indrev.utils.component2 import me.steven.indrev.utils.component3 import net.minecraft.client.MinecraftClient import net.minecraft.network.PacketByteBuf import net.minecraft.util.math.BlockPos class ClientNetworkState(val type: Network.Type) { private val nodes = Long2ObjectOpenHashMap() fun processPacket(buf: PacketByteBuf, client: MinecraftClient) { val positions = hashSetOf() val info = ClientServoNetworkInfo() info.read(buf) info.pipes.forEach { (pos, info) -> val oldInfo = nodes[pos] if (info != oldInfo) { positions.add(BlockPos.fromLong(pos)) } } client.execute { val before = nodes.clone() nodes.clear() nodes.putAll(info.pipes) positions.forEach { (x, y, z) -> MinecraftClient.getInstance().worldRenderer.scheduleBlockRenders(x, y, z, x, y, z) } before.filterKeys { !positions.contains(BlockPos.fromLong(it)) }.forEach { val (x, y, z) = BlockPos.fromLong(it.key) MinecraftClient.getInstance().worldRenderer.scheduleBlockRenders(x, y, z, x, y, z) } } } fun get(pos: BlockPos): ClientNodeInfo? = nodes[pos.asLong()] fun clear() { nodes.clear() } fun add(info: ClientNetworkInfo<*>) { info.createNodes().forEach { nodes[it.pos] = it } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/client/ClientServoNetworkInfo.kt ================================================ package me.steven.indrev.networks.client import com.google.common.collect.ImmutableList import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import me.steven.indrev.networks.EndpointData import me.steven.indrev.networks.client.node.ClientServoNodeInfo import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.network.PacketByteBuf import net.minecraft.util.math.Direction class ClientServoNetworkInfo : ClientNetworkInfo { val pipes = Long2ObjectOpenHashMap() override fun write(buf: PacketByteBuf) { buf.writeInt(pipes.size) pipes.forEach { (pos, info) -> buf.writeLong(pos) buf.writeByte(info.size) info.forEach { dir, data -> buf.writeByte(dir.id) buf.writeByte(data.ordinal) } } } @Environment(EnvType.CLIENT) override fun read(buf: PacketByteBuf) { pipes.clear() val size = buf.readInt() repeat(size) { val pos = buf.readLong() val info = ClientServoNodeInfo(pos, Object2ObjectOpenHashMap()) val infoSize = buf.readByte() repeat(infoSize.toInt()) { val dir = Direction.byId(buf.readByte().toInt()) val type = EndpointData.Type.VALUES[buf.readByte().toInt()] info.servos[dir] = type } pipes[pos] = info } } override fun createNodes(): List { val list = ImmutableList.builder() pipes.forEach { (_, servos) -> list.add(servos) } return list.build() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/client/node/ClientNodeInfo.kt ================================================ package me.steven.indrev.networks.client.node interface ClientNodeInfo { val pos: Long } inline fun ClientNodeInfo.to(): T { return this as T } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/client/node/ClientServoNodeInfo.kt ================================================ package me.steven.indrev.networks.client.node import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import me.steven.indrev.networks.EndpointData import net.minecraft.util.math.Direction open class ClientServoNodeInfo(override val pos: Long, val servos: Object2ObjectOpenHashMap) : ClientNodeInfo { val size: Int get() = servos.size inline fun forEach(f: (Direction, EndpointData.Type) -> Unit) = servos.forEach { (dir, type) -> f(dir, type) } override fun equals(other: Any?): Boolean { return when { other !is ClientServoNodeInfo -> false other.pos != pos -> false other.servos.size != servos.size -> false other.servos != servos -> false else -> true } } override fun hashCode(): Int { var result = pos.hashCode() result = 31 * result + servos.hashCode() return result } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/energy/CableEnergyIo.kt ================================================ package me.steven.indrev.networks.energy import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import team.reborn.energy.api.EnergyStorage class CableEnergyIo(private val network: EnergyNetwork?, val pos: BlockPos, val direction: Direction?) : EnergyStorage, SnapshotParticipant() { override fun getAmount(): Long = network?.energy ?: 0 override fun getCapacity(): Long = network?.capacity ?: 0 override fun insert(maxAmount: Long, transaction: TransactionContext?): Long { if (network == null || direction == null) return 0 StoragePreconditions.notNegative(maxAmount) val inserted = maxAmount.coerceAtMost(network.maxCableTransfer).coerceAtMost(capacity - amount) if (inserted > 0) { updateSnapshots(transaction) network.energy += inserted return inserted } return 0 } override fun extract(maxAmount: Long, transaction: TransactionContext?): Long = 0 override fun supportsInsertion(): Boolean = true override fun supportsExtraction(): Boolean = false override fun createSnapshot(): Long { return network?.energy ?: 0 } override fun readSnapshot(snapshot: Long) { network?.energy = snapshot } companion object { val NO_NETWORK = CableEnergyIo(null, BlockPos.ORIGIN, null) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/energy/EnergyNetwork.kt ================================================ package me.steven.indrev.networks.energy import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.machine.pipes.CableBlock import me.steven.indrev.config.IRConfig import me.steven.indrev.networks.Network import me.steven.indrev.utils.energyOf import me.steven.indrev.utils.insert import me.steven.indrev.utils.isLoaded import me.steven.indrev.utils.transaction import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction import net.minecraft.block.Block import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import team.reborn.energy.api.EnergyStorage import java.util.* open class EnergyNetwork( world: ServerWorld, val cables: MutableSet = ObjectOpenHashSet(), val machines: MutableMap> = Object2ObjectOpenHashMap() ) : Network(Type.ENERGY, world, cables, machines) { var tier = Tier.MK1 val maxCableTransfer: Long get() = when (tier) { Tier.MK1 -> IRConfig.cables.cableMk1 Tier.MK2 -> IRConfig.cables.cableMk2 Tier.MK3 -> IRConfig.cables.cableMk3 else -> IRConfig.cables.cableMk4 }.toLong() val insertables = ObjectOpenHashSet() var energy = 0L val capacity: Long get() = pipes.size * maxCableTransfer private val maxInputs = Object2LongOpenHashMap() private val storages = mutableListOf() override fun tick(world: ServerWorld) { if (energy <= 0) return maxInputs.clear() storages.clear() insertables.forEach { pos -> if (world.isLoaded(pos)) { machines[pos]?.forEach { dir -> val storage = energyOf(world, pos, dir) if (storage != null) storages.add(storage) } } } val totalInput = transaction { tx -> storages.sumOf { energyStorage -> val maxInput = energyStorage.insert(MAX_VALUE, tx) if (maxInput > 0) maxInputs[energyStorage] = maxInput maxInput }.toDouble() } if (totalInput <= 0) return transaction { tx -> storages.forEach { energyStorage -> val maxInput = maxInputs.getLong(energyStorage) if (maxInput <= 0) return@forEach val toTransfer = ((maxInput / totalInput) * energy).toLong().coerceAtMost(maxCableTransfer).coerceAtMost(energy) energy -= energyStorage.insert(toTransfer, tx) if (energy <= 0) { tx.commit() return } } tx.commit() } } override fun appendPipe(block: Block, blockPos: BlockPos) { val cable = block as? CableBlock ?: return this.tier = cable.tier super.appendPipe(block, blockPos) } companion object { private const val MAX_VALUE = Long.MAX_VALUE - 1L } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/energy/EnergyNetworkState.kt ================================================ package me.steven.indrev.networks.energy import me.steven.indrev.networks.Network import me.steven.indrev.networks.NetworkState import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtHelper import net.minecraft.nbt.NbtList import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos class EnergyNetworkState(world: ServerWorld) : NetworkState(Network.Type.ENERGY, world) { private var destroyedEnergy = 0L val savedEnergy = mutableMapOf() override fun add(network: Network) { super.add(network) if (network is EnergyNetwork) { network.energy += destroyedEnergy.coerceAtMost(network.capacity) destroyedEnergy -= network.energy if (destroyedEnergy <= 0) destroyedEnergy = 0 } } override fun remove(network: Network) { super.remove(network) if (network is EnergyNetwork) { destroyedEnergy += network.energy } } override fun tick(world: ServerWorld) { super.tick(world) destroyedEnergy = 0 } override fun writeNbt(tag: NbtCompound): NbtCompound { val list = NbtList() networks.forEach { network -> val pos = network.pipes.minByOrNull { it } ?: return@forEach val networkTag = NbtCompound() networkTag.put("Pos", NbtHelper.fromBlockPos(pos)) networkTag.putLong("Energy", (network as EnergyNetwork).energy) list.add(networkTag) } tag.put("SavedEnergy", list) return super.writeNbt(tag) } companion object { fun readNbt(tag: NbtCompound, supplier: () -> EnergyNetworkState): EnergyNetworkState { val state = supplier() val list = tag.getList("SavedEnergy", 10) list.forEach { t -> val networkTag = t as NbtCompound val pos = NbtHelper.toBlockPos(networkTag.getCompound("Pos")) val energy = networkTag.getLong("Energy") state.savedEnergy[pos] = energy } return state } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/factory/NetworkFactory.kt ================================================ package me.steven.indrev.networks.factory import it.unimi.dsi.fastutil.longs.LongOpenHashSet import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import me.steven.indrev.networks.Network import me.steven.indrev.networks.NetworkState import net.minecraft.block.BlockState import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.chunk.Chunk interface NetworkFactory { fun process( network: T, state: NetworkState, world: ServerWorld, pos: BlockPos, direction: Direction, blockState: () -> BlockState ): Boolean fun deepScan( scanned: LongOpenHashSet, type: Network.Type, state: NetworkState, network: T, chunk: Chunk, world: ServerWorld, blockPos: BlockPos, source: BlockPos, direction: Direction ) { val blockState by lazy { chunk.getBlockState(blockPos) } val shouldContinue = process(network, state, world, blockPos, direction) { blockState } val longPos = blockPos.asLong() if (blockPos != source && !scanned.add(longPos)) return if (shouldContinue) { if (state.networksByPos.containsKey(longPos)) { val oldNetwork = state.networksByPos[longPos] if (oldNetwork != network) oldNetwork?.remove() } state.updatedPositions.add(longPos) val blockEntity = chunk.getBlockEntity(blockPos) as? BasePipeBlockEntity ?: return DIRECTIONS.forEach { dir -> if (blockEntity.connections[dir]!!.isConnected()) { val nPos = blockPos.offset(dir) if (nPos.x shr 4 == chunk.pos.x && nPos.z shr 4 == chunk.pos.z) deepScan(scanned, type, state, network, chunk, world, nPos, source, dir) else deepScan(scanned, type, state, network, world.getChunk(nPos), world, nPos, source, dir) } } } } fun deepScan( type: Network.Type, world: ServerWorld, source: BlockPos ): T { val network = type.createEmpty(world) DIRECTIONS.forEach { direction -> deepScan(LongOpenHashSet(), type, type.getNetworkState(world), network, world.getChunk(source), world, source, source, direction) } return network } companion object { val DIRECTIONS = Direction.values() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/factory/factories.kt ================================================ package me.steven.indrev.networks.factory import me.steven.indrev.blocks.machine.pipes.CableBlock import me.steven.indrev.blocks.machine.pipes.FluidPipeBlock import me.steven.indrev.blocks.machine.pipes.ItemPipeBlock import me.steven.indrev.networks.NetworkState import me.steven.indrev.networks.energy.EnergyNetwork import me.steven.indrev.networks.energy.EnergyNetworkState import me.steven.indrev.networks.fluid.FluidNetwork import me.steven.indrev.networks.item.ItemNetwork import me.steven.indrev.utils.energyOf import me.steven.indrev.utils.fluidStorageOf import me.steven.indrev.utils.itemStorageOf import net.minecraft.block.BlockState import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction val ENERGY_NET_FACTORY: NetworkFactory = object : NetworkFactory { override fun process( network: EnergyNetwork, state: NetworkState, world: ServerWorld, pos: BlockPos, direction: Direction, blockState: () -> BlockState ): Boolean { if (blockState().block is CableBlock) { network.appendPipe(blockState().block, pos.toImmutable()) if ((state as EnergyNetworkState).savedEnergy.containsKey(pos)) { val energy = state.savedEnergy.remove(pos)!! network.energy += energy } return true } else { val energyOf = energyOf(world, pos, direction.opposite) if (energyOf != null) { network.appendContainer(pos, direction.opposite) if (energyOf.supportsInsertion()) network.insertables.add(pos) } } return false } } val FLUID_NET_FACTORY: NetworkFactory = object : NetworkFactory { override fun process( network: FluidNetwork, state: NetworkState, world: ServerWorld, pos: BlockPos, direction: Direction, blockState: () -> BlockState ): Boolean { if (blockState().block is FluidPipeBlock) { network.appendPipe(blockState().block, pos.toImmutable()) state.onSet(pos, network) return true } else if (fluidStorageOf(world, pos, direction.opposite) != null) { network.appendContainer(pos, direction.opposite) } return false } } val ITEM_NET_FACTORY: NetworkFactory = object : NetworkFactory { override fun process( network: ItemNetwork, state: NetworkState, world: ServerWorld, pos: BlockPos, direction: Direction, blockState: () -> BlockState ): Boolean { if (blockState().block is ItemPipeBlock) { network.appendPipe(blockState().block, pos.toImmutable()) state.onSet(pos, network) return true } else if (itemStorageOf(world, pos, direction.opposite) != null) { network.appendContainer(pos, direction.opposite) } return false } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/fluid/FluidNetwork.kt ================================================ package me.steven.indrev.networks.fluid import alexiil.mc.lib.attributes.fluid.filter.FluidFilter import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.machine.pipes.FluidPipeBlock import me.steven.indrev.config.IRConfig import me.steven.indrev.networks.EndpointData import me.steven.indrev.networks.Network import me.steven.indrev.networks.Node import me.steven.indrev.utils.ReusableArrayDeque import me.steven.indrev.utils.bucket import me.steven.indrev.utils.fluidStorageOf import me.steven.indrev.utils.isLoaded import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.storage.Storage import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil import net.minecraft.block.Block import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import java.util.* import kotlin.collections.component1 import kotlin.collections.component2 class FluidNetwork( world: ServerWorld, pipes: MutableSet = ObjectOpenHashSet(), containers: MutableMap> = Object2ObjectOpenHashMap() ) : Network(Type.FLUID, world, pipes, containers) { var tier = Tier.MK1 private val maxCableTransfer: Long get() = when (tier) { Tier.MK1 -> IRConfig.cables.fluidPipeMk1 Tier.MK2 -> IRConfig.cables.fluidPipeMk2 Tier.MK3 -> IRConfig.cables.fluidPipeMk3 else -> IRConfig.cables.fluidPipeMk4 }.toLong() * bucket var lastTransferred: FluidVariant = FluidVariant.blank() private val deques = Object2ObjectOpenHashMap>>() private var ticks = 0 override fun tick(world: ServerWorld) { ticks++ if (ticks % 20 != 0) return val state = Type.FLUID.getNetworkState(world) as FluidNetworkState if (isQueueValid()) { containers.forEach { (pos, directions) -> if (!world.isLoaded(pos)) return@forEach val nodes = queue[pos] ?: return@forEach directions.forEach inner@{ dir -> val data = state.getEndpointData(pos.offset(dir), dir.opposite) ?: return@inner val filter: (FluidVariant) -> Boolean = { v -> !lastTransferred.isBlank && v == lastTransferred } val deque = getQueue(pos, data, filter, nodes) if (data.type == EndpointData.Type.OUTPUT) tickOutput(pos, dir, deque, state, filter) else if (data.type == EndpointData.Type.RETRIEVER) tickRetriever(pos, dir, deque, state, filter) deque.resetHead() } } } lastTransferred = FluidVariant.blank() } private fun getQueue(pos: BlockPos, data: EndpointData, filter: (FluidVariant) -> Boolean, nodes: List): ReusableArrayDeque { var queuesByNodes = deques[pos] if (queuesByNodes == null) { queuesByNodes = EnumMap(EndpointData.Mode::class.java) this.deques[pos] = queuesByNodes } var queue = queuesByNodes[data.mode] if (queue == null) { queue = ReusableArrayDeque(nodes) queue.apply(data.mode!!.getFluidSorter(world, data.type) { filter(it) }) queuesByNodes[data.mode] = queue } if (data.mode == EndpointData.Mode.ROUND_ROBIN || data.mode == EndpointData.Mode.RANDOM) { queue.apply(data.mode!!.getFluidSorter(world, data.type) { filter(it) }) } return queue } private fun tickOutput(pos: BlockPos, dir: Direction, queue: ReusableArrayDeque, state: FluidNetworkState, fluidFilter: (FluidVariant) -> Boolean) { val extractable = fluidStorageOf(world, pos, dir) var remaining = maxCableTransfer updateLastTransferred(extractable) while (queue.isNotEmpty() && remaining > 0) { val (_, targetPos, _, targetDir) = queue.removeFirst() if (!world.isLoaded(targetPos)) continue val targetData = state.getEndpointData(targetPos.offset(targetDir), targetDir.opposite) val input = targetData == null || targetData.type == EndpointData.Type.INPUT if (!input) continue val insertable = fluidStorageOf(world, targetPos, targetDir) val moved = StorageUtil.move(extractable, insertable, fluidFilter, remaining, null) remaining -= moved } } private fun tickRetriever(pos: BlockPos, dir: Direction, queue: ReusableArrayDeque, state: FluidNetworkState, fluidFilter: (FluidVariant) -> Boolean) { val insertable = fluidStorageOf(world, pos, dir) var remaining = maxCableTransfer while (queue.isNotEmpty() && remaining > 0) { val (_, targetPos, _, targetDir) = queue.removeFirst() if (!world.isLoaded(targetPos)) continue val targetData = state.getEndpointData(targetPos.offset(targetDir), targetDir.opposite) val isRetriever = targetData?.type == EndpointData.Type.RETRIEVER if (isRetriever) continue val extractable = fluidStorageOf(world, targetPos, targetDir) updateLastTransferred(extractable) val moved = StorageUtil.move(extractable, insertable, fluidFilter, remaining, null) remaining -= moved } } private fun updateLastTransferred(extractable: Storage?) { if (lastTransferred.isBlank) { val content = StorageUtil.findExtractableContent(extractable, null) if (content != null) { lastTransferred = content.resource } } } override fun appendPipe(block: Block, blockPos: BlockPos) { val cable = block as? FluidPipeBlock ?: return this.tier = cable.tier super.appendPipe(block, blockPos) } companion object { private val NO_FLUID_FILTER = FluidFilter { true } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/fluid/FluidNetworkState.kt ================================================ package me.steven.indrev.networks.fluid import me.steven.indrev.networks.Network import me.steven.indrev.networks.ServoNetworkState import net.minecraft.server.world.ServerWorld class FluidNetworkState(world: ServerWorld) : ServoNetworkState(Network.Type.FLUID, world) ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/item/ItemFilterData.kt ================================================ package me.steven.indrev.networks.item import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.minecraft.inventory.Inventories import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.util.collection.DefaultedList class ItemFilterData( var whitelist: Boolean, var matchDurability: Boolean, var matchTag: Boolean, val filter: DefaultedList = DefaultedList.ofSize(9, ItemStack.EMPTY) ) { constructor() : this(false, false, false) fun matches(itemStack: ItemVariant): Boolean { if (filter.isEmpty()) return !whitelist val findMatches = filter.filter { it.item == itemStack.item } if (findMatches.isEmpty()) return !whitelist var match = true if (matchDurability) match = findMatches.any { it.damage == itemStack.toStack().damage } if (match && matchTag) match = findMatches.any { it.nbt == itemStack.nbt } if (match) return whitelist return !whitelist } fun writeNbt(tag: NbtCompound): NbtCompound { tag.put("filter", Inventories.writeNbt(NbtCompound(), filter)) tag.putBoolean("w", whitelist) tag.putBoolean("d", matchDurability) tag.putBoolean("mt", matchTag) return tag } fun readNbt(tag: NbtCompound): ItemFilterData { Inventories.readNbt(tag.getCompound("filter"), filter) whitelist = tag.getBoolean("w") matchDurability = tag.getBoolean("d") matchTag = tag.getBoolean("mt") return this } companion object { val ACCEPTING_FILTER_DATA = ItemFilterData() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/item/ItemNetwork.kt ================================================ package me.steven.indrev.networks.item import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.machine.pipes.ItemPipeBlock import me.steven.indrev.config.IRConfig import me.steven.indrev.networks.EndpointData import me.steven.indrev.networks.Network import me.steven.indrev.networks.Node import me.steven.indrev.utils.ReusableArrayDeque import me.steven.indrev.utils.isLoaded import me.steven.indrev.utils.itemStorageOf import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil import net.minecraft.block.Block import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import java.util.* import kotlin.collections.List import kotlin.collections.MutableMap import kotlin.collections.MutableSet import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.forEach import kotlin.collections.isNotEmpty import kotlin.collections.set class ItemNetwork( world: ServerWorld, pipes: MutableSet = ObjectOpenHashSet(), containers: MutableMap> = Object2ObjectOpenHashMap() ) : Network(Type.ITEM, world, pipes, containers) { var tier = Tier.MK1 private val maxCableTransfer: Long get() = when (tier) { Tier.MK1 -> IRConfig.cables.itemPipeMk1 Tier.MK2 -> IRConfig.cables.itemPipeMk2 Tier.MK3 -> IRConfig.cables.itemPipeMk3 else -> IRConfig.cables.itemPipeMk4 }.toLong() private val deques = Object2ObjectOpenHashMap>>() private var ticks = 0 override fun tick(world: ServerWorld) { ticks++ if (ticks % 20 != 0) return val state = Type.ITEM.getNetworkState(world) as ItemNetworkState if (containers.isEmpty()) return else if (queue.isEmpty()) buildQueue() if (queue.isNotEmpty()) { containers.forEach { (pos, directions) -> if (!world.isLoaded(pos)) return@forEach val nodes = queue[pos] ?: return@forEach directions.forEach inner@{ dir -> val data = state.getEndpointData(pos.offset(dir), dir.opposite) ?: return@inner val filterData = state.getFilterData(pos.offset(dir), dir.opposite) if (data.type == EndpointData.Type.INPUT) return@inner val deque = getQueue(pos, data, filterData, nodes) if (data.type == EndpointData.Type.OUTPUT) tickOutput(pos, dir, deque, state, data, filterData) else if (data.type == EndpointData.Type.RETRIEVER) tickRetriever(pos, dir, deque, state, data, filterData) deque.resetHead() } } } } private fun getQueue(pos: BlockPos, data: EndpointData, filter: ItemFilterData, nodes: List): ReusableArrayDeque { var queuesByNodes = deques[pos] if (queuesByNodes == null) { queuesByNodes = EnumMap(EndpointData.Mode::class.java) this.deques[pos] = queuesByNodes } var queue = queuesByNodes[data.mode] if (queue == null) { queue = ReusableArrayDeque(nodes) queue.apply(data.mode!!.getItemSorter(world, data.type) { filter.matches(it) }) queuesByNodes[data.mode] = queue } if (data.mode == EndpointData.Mode.ROUND_ROBIN || data.mode == EndpointData.Mode.RANDOM) { queue.apply(data.mode!!.getItemSorter(world, data.type) { filter.matches(it) }) } return queue } private fun tickOutput(pos: BlockPos, dir: Direction, queue: ReusableArrayDeque, state: ItemNetworkState, data: EndpointData, filterData: ItemFilterData) { val extractable = itemStorageOf(world, pos, dir) var remaining = maxCableTransfer while (queue.isNotEmpty() && remaining > 0) { val node = queue.removeFirst() val (_, targetPos, _, targetDir) = node if (!world.isLoaded(targetPos)) continue val targetData = state.getEndpointData(targetPos.offset(targetDir), targetDir.opposite) val input = targetData == null || targetData.type == EndpointData.Type.INPUT if (!input) continue val targetFilterData = state.getFilterData(targetPos.offset(targetDir), targetDir.opposite) fun doMove() { val insertable = itemStorageOf(world, targetPos, targetDir) val moved = StorageUtil.move(extractable, insertable, { filterData.matches(it) && targetFilterData.matches(it) }, remaining, null) remaining -= moved if (moved > 0 && remaining > 0) { doMove() } } doMove() } } private fun tickRetriever(pos: BlockPos, dir: Direction, queue: ReusableArrayDeque, state: ItemNetworkState, data: EndpointData, filterData: ItemFilterData) { val insertable = itemStorageOf(world, pos, dir) var remaining = maxCableTransfer while (queue.isNotEmpty() && remaining > 0) { val node = queue.removeFirst() val (_, targetPos, _, targetDir) = node if (!world.isLoaded(targetPos)) continue val targetData = state.getEndpointData(targetPos.offset(targetDir), targetDir.opposite) val isRetriever = targetData?.type == EndpointData.Type.RETRIEVER if (isRetriever) continue val targetFilterData = state.getFilterData(targetPos.offset(targetDir), targetDir.opposite) fun doMove() { val extractable = itemStorageOf(world, targetPos, targetDir) val moved = StorageUtil.move(extractable, insertable, { filterData.matches(it) && targetFilterData.matches(it) }, remaining, null) remaining -= moved if (moved > 0 && remaining > 0) doMove() } doMove() } } override fun appendPipe(block: Block, blockPos: BlockPos) { val cable = block as? ItemPipeBlock ?: return this.tier = cable.tier super.appendPipe(block, blockPos) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/networks/item/ItemNetworkState.kt ================================================ package me.steven.indrev.networks.item import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import me.steven.indrev.networks.Network import me.steven.indrev.networks.ServoNetworkState import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtList import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import java.util.function.LongFunction class ItemNetworkState(world: ServerWorld) : ServoNetworkState(Network.Type.ITEM, world) { val filters = Long2ObjectOpenHashMap>() private val recentlyRemovedFilters = Long2ObjectOpenHashMap>() override fun onRemoved(pos: BlockPos) { super.onRemoved(pos) if (filters.containsKey(pos.asLong())) recentlyRemovedFilters[pos.asLong()] = filters.remove(pos.asLong()) } override fun onSet(blockPos: BlockPos, network: ItemNetwork) { super.onSet(blockPos, network) if (recentlyRemovedFilters.containsKey(blockPos.asLong())) { filters[blockPos.asLong()] = recentlyRemovedFilters.remove(blockPos.asLong()) } } override fun clearCachedData(importCache: Boolean) { super.clearCachedData(importCache) if (importCache) { recentlyRemovedFilters.forEach { e -> filters[e.key] = e.value } } recentlyRemovedFilters.clear() } fun getFilterData(pos: BlockPos, direction: Direction, createIfAbsent: Boolean = false): ItemFilterData { return if (createIfAbsent) filters.computeIfAbsent(pos.asLong(), LongFunction { Object2ObjectOpenHashMap() }).computeIfAbsent(direction, Object2ObjectFunction { ItemFilterData() }) else filters.get(pos.asLong())?.get(direction) ?: ItemFilterData.ACCEPTING_FILTER_DATA } override fun writeNbt(tag: NbtCompound): NbtCompound { val filtersTag = NbtList() filters.forEach { (pos, modes) -> val sidesTag = NbtList() modes.forEach { (dir, filterData) -> if (filterData != ItemFilterData.ACCEPTING_FILTER_DATA) { val t = NbtCompound() t.put(dir.ordinal.toString(), filterData.writeNbt(NbtCompound())) sidesTag.add(t) } } val posTag = NbtCompound() posTag.putLong("pos", pos) posTag.put("sides", sidesTag) filtersTag.add(posTag) } tag.put("filters", filtersTag) return super.writeNbt(tag) } companion object { fun readNbt(tag: NbtCompound, supplier: () -> ItemNetworkState): ItemNetworkState { val state = supplier() val modesTag = tag.getList("filters", 10) modesTag.forEach { posTag -> posTag as NbtCompound val pos = posTag.getLong("pos") val map = Object2ObjectOpenHashMap() val sidesTag = posTag.getList("sides", 10) sidesTag.forEach { t -> t as NbtCompound t.keys.forEach { id -> val data = ItemFilterData().readNbt(t.getCompound(id)) val dir = Direction.values()[id.toInt()] map[dir] = data } } state.filters[pos] = map } ServoNetworkState.readNbt(tag) { state } return state } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/PacketRegistry.kt ================================================ package me.steven.indrev.packets import me.steven.indrev.packets.client.* import me.steven.indrev.packets.common.* object PacketRegistry { fun registerServer() { ConfigureIOPackets.register() DataCardWriteStartPacket.register() GuiPropertySyncPacket.registerServer() FluidGuiHandInteractionPacket.register() ItemPipePackets.register() SelectModuleOnWorkbenchPacket.register() ToggleFactoryStackSplittingPacket.register() ToggleGamerAxePacket.register() UpdateAOEMachineRangePacket.register() UpdateMiningDrillBlockBlacklistPacket.register() UpdateKnobValue.register() UpdateModularToolLevelPacket.register() UpdateRancherConfigPacket.register() } fun registerClient() { ClientItemPipePackets.register() GuiPropertySyncPacket.register() MachineStateUpdatePacket.register() MiningRigSpawnBlockParticlesPacket.register() SyncAppliedModulesPacket.register() SyncConfigPacket.register() SyncNetworkServosPacket.register() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/client/ClientItemPipePackets.kt ================================================ package me.steven.indrev.packets.client import me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreen import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking object ClientItemPipePackets { val UPDATE_FILTER_SLOT_S2C_PACKET = identifier("update_filter_s2c") fun register() { ClientPlayNetworking.registerGlobalReceiver(UPDATE_FILTER_SLOT_S2C_PACKET) { client, _, buf, _ -> val slotIndex = buf.readInt() val stack = buf.readItemStack() client.execute { val screen = client.currentScreen as? PipeFilterScreen ?: return@execute val controller = screen.screenHandler controller.backingList[slotIndex] = stack } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/client/GuiPropertySyncPacket.kt ================================================ package me.steven.indrev.packets.client import me.steven.indrev.IndustrialRevolution import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking object GuiPropertySyncPacket { val SYNC_PROPERTY = identifier("sync_property") val C2S_REQUEST_PROPERTIES = identifier("client_request") fun register() { ClientPlayNetworking.registerGlobalReceiver(SYNC_PROPERTY) { client, _, buf, _ -> val syncId = buf.readInt() val property = buf.readInt() val handler = client.player!!.currentScreenHandler if (handler.syncId == syncId && handler is IRGuiScreenHandler) { val prop = handler.component?.properties?.get(property) ?: return@registerGlobalReceiver prop.fromPacket(buf) handler.onSyncedProperty(property, prop) } else { IndustrialRevolution.LOGGER.warn("Received sync packet for unknown screen type @ $handler") } } } fun registerServer() { ServerPlayNetworking.registerGlobalReceiver(C2S_REQUEST_PROPERTIES) { server, player, _, buf, _ -> val syncId = buf.readInt() val handler = player!!.currentScreenHandler if (handler.syncId == syncId && handler is IRGuiScreenHandler) { handler.component?.properties?.forEach { it.markDirty() } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/client/MachineStateUpdatePacket.kt ================================================ package me.steven.indrev.packets.client import me.steven.indrev.blockentities.GlobalStateController import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking object MachineStateUpdatePacket { val UPDATE_PACKET_ID = identifier("global_state_update") fun register() { ClientPlayNetworking.registerGlobalReceiver(UPDATE_PACKET_ID) { client, _, buf, _ -> val pos = buf.readBlockPos() val workingState = buf.readBoolean() client.execute { GlobalStateController.workingStateTracker[pos.asLong()] = workingState GlobalStateController.queueUpdate(pos) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/client/MiningRigSpawnBlockParticlesPacket.kt ================================================ package me.steven.indrev.packets.client import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.minecraft.client.MinecraftClient import net.minecraft.client.world.ClientWorld import net.minecraft.sound.SoundCategory import net.minecraft.util.registry.Registry object MiningRigSpawnBlockParticlesPacket { val BLOCK_BREAK_PACKET = identifier("miner_drill_block_particle") fun register() { ClientPlayNetworking.registerGlobalReceiver(BLOCK_BREAK_PACKET) { client, _, buf, _ -> val pos = buf.readBlockPos().down() val blockRawId = buf.readInt() val block = Registry.BLOCK.get(blockRawId) client.execute { MinecraftClient.getInstance().particleManager.addBlockBreakParticles(pos, block.defaultState) val blockSoundGroup = block.getSoundGroup(block.defaultState) (client.player!!.world as ClientWorld).playSound( pos, blockSoundGroup.breakSound, SoundCategory.BLOCKS, (blockSoundGroup.getVolume() + 1.0f) / 4.0f, blockSoundGroup.getPitch() * 0.8f, false ) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/client/SyncAppliedModulesPacket.kt ================================================ package me.steven.indrev.packets.client import me.steven.indrev.api.IRPlayerEntityExtension import me.steven.indrev.tools.modular.ArmorModule import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking object SyncAppliedModulesPacket { val SYNC_MODULE_PACKET = identifier("sync_module") fun register() { ClientPlayNetworking.registerGlobalReceiver(SYNC_MODULE_PACKET) { client, _, buf, _ -> val size = buf.readInt() val modules = hashMapOf() for (index in 0 until size) { val ordinal = buf.readInt() val module = ArmorModule.values()[ordinal] val level = buf.readInt() modules[module] = level } val durability = buf.readDouble() val isRegenerating = buf.readBoolean() client.execute { val player = client.player!! if (player is IRPlayerEntityExtension) { (player.getAppliedModules() as MutableMap<*, *>).clear() modules.forEach(player::applyModule) player.shieldDurability = durability player.isRegenerating = isRegenerating } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/client/SyncConfigPacket.kt ================================================ package me.steven.indrev.packets.client import me.steven.indrev.config.IRConfig import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.server.network.ServerPlayerEntity object SyncConfigPacket { val SYNC_CONFIG_PACKET = identifier("sync_config_packet") fun register() { ClientPlayNetworking.registerGlobalReceiver(SYNC_CONFIG_PACKET) { _, _, buf, _ -> IRConfig.readFromServer(buf) } } fun sendConfig(playerEntity: ServerPlayerEntity) { val buf = PacketByteBufs.create() IRConfig.writeToClient(buf) ServerPlayNetworking.send(playerEntity, SYNC_CONFIG_PACKET, buf) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/client/SyncNetworkServosPacket.kt ================================================ package me.steven.indrev.packets.client import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import me.steven.indrev.IndustrialRevolutionClient import me.steven.indrev.networks.Network import me.steven.indrev.networks.client.ClientNetworkState import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking object SyncNetworkServosPacket { val SYNC_NETWORK_SERVOS = identifier("sync_network_servos") fun register() { ClientPlayNetworking.registerGlobalReceiver(SYNC_NETWORK_SERVOS) { client, _, buf, _ -> val type = Network.Type.valueOf(buf.readString()) val state = IndustrialRevolutionClient.CLIENT_NETWORK_STATE.computeIfAbsent(type, Object2ObjectFunction { ClientNetworkState(type) }) state.processPacket(buf, client) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/ConfigureIOPackets.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.blockentities.GlobalStateController import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.util.math.Direction object ConfigureIOPackets { val UPDATE_MACHINE_SIDE_PACKET_ID = identifier("update_machine_side") val UPDATE_AUTO_OPERATION_PACKET_ID = identifier("update_auto_pull_push") fun register() { ServerPlayNetworking.registerGlobalReceiver(UPDATE_MACHINE_SIDE_PACKET_ID) { server, player, _, buf, _ -> val type = buf.readEnumConstant(ConfigurationType::class.java) val pos = buf.readBlockPos() val dir = Direction.byId(buf.readInt()) val mode = TransferMode.values()[buf.readInt()] server.execute { val world = player.world val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@execute blockEntity.getCurrentConfiguration(type)[dir] = mode blockEntity.markDirty() GlobalStateController.update(world, pos, false) world.updateNeighbors(pos, blockEntity.cachedState.block) } } ServerPlayNetworking.registerGlobalReceiver(UPDATE_AUTO_OPERATION_PACKET_ID) { server, player, _, buf, _ -> val type = buf.readEnumConstant(ConfigurationType::class.java) val opType = buf.readByte() val pos = buf.readBlockPos() val value = buf.readBoolean() server.execute { val world = player.world val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@execute if (opType.toInt() == 0) blockEntity.getCurrentConfiguration(type).autoPush = value else blockEntity.getCurrentConfiguration(type).autoPull = value blockEntity.markDirty() } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/DataCardWriteStartPacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.blockentities.miningrig.DataCardWriterBlockEntity import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking object DataCardWriteStartPacket { val START_PACKET = identifier("write_data_card_start") fun register() { ServerPlayNetworking.registerGlobalReceiver(START_PACKET) { server, player, _, buf, _ -> val pos = buf.readBlockPos() server.execute { val blockEntity = player.world.getBlockEntity(pos) as? DataCardWriterBlockEntity ?: return@execute blockEntity.start() } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/FluidGuiHandInteractionPacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler import me.steven.indrev.utils.identifier import me.steven.indrev.utils.isLoaded import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil object FluidGuiHandInteractionPacket { val FLUID_CLICK_PACKET = identifier("fluid_widget_click") fun register() { ServerPlayNetworking.registerGlobalReceiver(FLUID_CLICK_PACKET) { server, player, _, buf, _ -> val tank = buf.readInt() val world = player.world val screenHandler = player.currentScreenHandler as? IRGuiScreenHandler ?: return@registerGlobalReceiver server.execute { screenHandler.ctx.run { _, pos -> if (world.isLoaded(pos)) { val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@run val fluidComponent = blockEntity.fluidComponent ?: return@run val handStorage = ContainerItemContext.ofPlayerCursor(player, screenHandler).find(FluidStorage.ITEM) val res = StorageUtil.move(handStorage, fluidComponent[tank], { true }, Long.MAX_VALUE, null) if (res == 0L) StorageUtil.move(fluidComponent[tank], handStorage, { true }, Long.MAX_VALUE, null) } } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/ItemPipePackets.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.networks.EndpointData import me.steven.indrev.networks.Network import me.steven.indrev.networks.item.ItemNetworkState import me.steven.indrev.packets.client.ClientItemPipePackets import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.networking.v1.PacketByteBufs import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.Direction object ItemPipePackets { val CLICK_FILTER_SLOT_PACKET = identifier("click_filter_slot") val CHANGE_FILTER_MODE_PACKET = identifier("change_whitelist_mode") val CHANGE_SERVO_MODE_PACKET = identifier("change_servo_mode") fun register() { ServerPlayNetworking.registerGlobalReceiver(CLICK_FILTER_SLOT_PACKET) { server, player, _, buf, _ -> val slotIndex = buf.readInt() val dir = buf.readEnumConstant(Direction::class.java) val pos = buf.readBlockPos() server.execute { val cursorStack = player.currentScreenHandler.cursorStack val state = Network.Type.ITEM.getNetworkState(player.world as ServerWorld) as? ItemNetworkState ?: return@execute val data = state.getFilterData(pos, dir) if (cursorStack.isEmpty) data.filter[slotIndex] = ItemStack.EMPTY else data.filter[slotIndex] = cursorStack.copy().also { it.count = 1 } state.markDirty() val syncPacket = PacketByteBufs.create() syncPacket.writeInt(slotIndex) syncPacket.writeItemStack(data.filter[slotIndex]) ServerPlayNetworking.send(player, ClientItemPipePackets.UPDATE_FILTER_SLOT_S2C_PACKET, syncPacket) } } ServerPlayNetworking.registerGlobalReceiver(CHANGE_FILTER_MODE_PACKET) { server, player, _, buf, _ -> val dir = buf.readEnumConstant(Direction::class.java) val pos = buf.readBlockPos() val field = buf.readInt() val value = buf.readBoolean() server.execute { val state = Network.Type.ITEM.getNetworkState(player.world as ServerWorld) as? ItemNetworkState ?: return@execute val data = state.getFilterData(pos, dir, true) when (field) { 0 -> data.whitelist = value 1 -> data.matchDurability = value 2 -> data.matchTag = value else -> return@execute } state.markDirty() } } ServerPlayNetworking.registerGlobalReceiver(CHANGE_SERVO_MODE_PACKET) { server, player, _, buf, _ -> val dir = buf.readEnumConstant(Direction::class.java) val pos = buf.readBlockPos() val mode = buf.readEnumConstant(EndpointData.Mode::class.java) server.execute { val state = Network.Type.ITEM.getNetworkState(player.world as ServerWorld) as? ItemNetworkState ?: return@execute val data = state.getEndpointData(pos, dir, true) ?: return@execute data.mode = mode state.markDirty() } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/SelectModuleOnWorkbenchPacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.blockentities.modularworkbench.ModularWorkbenchBlockEntity import me.steven.indrev.gui.screenhandlers.machines.ModularWorkbenchScreenHandler import me.steven.indrev.recipes.machines.ModuleRecipe import me.steven.indrev.utils.getRecipes import me.steven.indrev.utils.identifier import me.steven.indrev.utils.isLoaded import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking object SelectModuleOnWorkbenchPacket { val MODULE_SELECT_PACKET = identifier("module_select_packet") fun register() { ServerPlayNetworking.registerGlobalReceiver(MODULE_SELECT_PACKET) { server, player, _, buf, _ -> val syncId = buf.readInt() val recipeId = buf.readIdentifier() val pos = buf.readBlockPos() val screenHandler = player.currentScreenHandler as? ModularWorkbenchScreenHandler ?: return@registerGlobalReceiver if (syncId != screenHandler.syncId) return@registerGlobalReceiver server.execute { val world = player.world if (world.isLoaded(pos)) { val recipe = server.recipeManager.getRecipes(ModuleRecipe.TYPE)[recipeId]!! screenHandler.layoutSlots(recipe) val blockEntity = world.getBlockEntity(pos) as? ModularWorkbenchBlockEntity ?: return@execute blockEntity.selectedRecipe = recipeId blockEntity.markDirty() blockEntity.sync() } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/ToggleFactoryStackSplittingPacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity import me.steven.indrev.utils.identifier import me.steven.indrev.utils.isLoaded import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking object ToggleFactoryStackSplittingPacket { val SPLIT_STACKS_PACKET = identifier("split_stacks_packet") fun register() { ServerPlayNetworking.registerGlobalReceiver(SPLIT_STACKS_PACKET) { server, player, _, buf, _ -> val pos = buf.readBlockPos() server.execute { val world = player.world if (world.isLoaded(pos)) { val blockEntity = world.getBlockEntity(pos) as? CraftingMachineBlockEntity<*> ?: return@execute blockEntity.isSplitOn = !blockEntity.isSplitOn if (blockEntity.isSplitOn) blockEntity.splitStacks() } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/ToggleGamerAxePacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.registry.IRItemRegistry import me.steven.indrev.utils.energyOf import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.text.LiteralText import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.TypedActionResult object ToggleGamerAxePacket { val PACKET_ID = identifier("toggle_gamer_axe") fun register() { ServerPlayNetworking.registerGlobalReceiver(PACKET_ID) { server, player, _, _, _ -> server.execute { val stack = player.mainHandStack if (stack.isOf(IRItemRegistry.GAMER_AXE_ITEM)) { val tag = stack?.orCreateNbt if (tag?.contains("Active") == false || tag?.contains("Progress") == false) { tag.putBoolean("Active", true) tag.putFloat("Progress", 0f) } else if (tag?.contains("Active") == true) { val active = !tag.getBoolean("Active") val itemIo = energyOf(stack)!! if (itemIo.amount > 0) { stack.orCreateNbt.putBoolean("Active", active) val color = if (active) Formatting.GREEN else Formatting.RED player.sendMessage(LiteralText("").append(stack.name).formatted(stack.rarity.formatting).append(": ").append(TranslatableText("item.indrev.gamer_axe.$active").formatted(color, Formatting.BOLD)), true) } else { player.sendMessage(LiteralText("Not enough energy!"), true) } } } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/UpdateAOEMachineRangePacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.blockentities.farms.AOEMachineBlockEntity import me.steven.indrev.utils.identifier import me.steven.indrev.utils.isLoaded import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking object UpdateAOEMachineRangePacket { val UPDATE_VALUE_PACKET_ID = identifier("update_value_packet") fun register() { ServerPlayNetworking.registerGlobalReceiver(UPDATE_VALUE_PACKET_ID) { server, player, _, buf, _ -> val value = buf.readInt() val pos = buf.readBlockPos() val world = player.world server.execute { if (world.isLoaded(pos)) { val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@execute blockEntity.range = value blockEntity.markDirty() blockEntity.sync() } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/UpdateKnobValue.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.blockentities.generators.SteamTurbineBlockEntity import me.steven.indrev.utils.identifier import me.steven.indrev.utils.isLoaded import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking object UpdateKnobValue { val UPDATE_EFFICIENCY_PACKET = identifier("update_steam_turbine_efficiency") fun register() { ServerPlayNetworking.registerGlobalReceiver(UPDATE_EFFICIENCY_PACKET) { server, player, _, buf, _ -> val pos = buf.readBlockPos() val efficiency = buf.readFloat() server.execute { val world = player.world if (world.isLoaded(pos)) { val blockEntity = world.getBlockEntity(pos) as? SteamTurbineBlockEntity ?: return@execute blockEntity.efficiency = efficiency.toDouble() blockEntity.markDirty() } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/UpdateMiningDrillBlockBlacklistPacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.tools.modular.DrillModule import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtHelper import net.minecraft.nbt.NbtList import net.minecraft.network.PacketByteBuf import net.minecraft.server.MinecraftServer import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.util.math.BlockPos object UpdateMiningDrillBlockBlacklistPacket { val UPDATE_BLACKLIST_PACKET = identifier("update_drill_blacklist") fun register() { ServerPlayNetworking.registerGlobalReceiver(UPDATE_BLACKLIST_PACKET) { server, player, _, buf, _ -> val mode = Mode.values()[buf.readInt()] mode.process(buf, server, player) } } enum class Mode(val process: (PacketByteBuf, MinecraftServer, ServerPlayerEntity) -> Unit) { SINGLE({ buf, server, player -> val pos = buf.readBlockPos() server.execute { val stack = player.mainHandStack val nbt = stack.orCreateNbt.getList("BlacklistedPositions", 10) val posNbt = NbtHelper.fromBlockPos(pos) if (nbt.contains(posNbt)) nbt.remove(posNbt) else nbt.add(posNbt) stack.nbt!!.put("BlacklistedPositions", nbt) } }), FLIP_Y({ _, server, player -> server.execute { val stack = player.mainHandStack val flipped = DrillModule.getBlacklistedPositions(stack).map { BlockPos(it.x, -it.y, it.z) } update(stack, flipped) } }), FLIP_X({ _, server, player -> server.execute { val stack = player.mainHandStack val flipped = DrillModule.getBlacklistedPositions(stack).map { BlockPos(-it.x, it.y, it.z) } update(stack, flipped) } }), ROT_X_90_CLOCKWISE({ _, server, player -> server.execute { val stack = player.mainHandStack val flipped = DrillModule.getBlacklistedPositions(stack).map { BlockPos(it.y, -it.x, it.z) } update(stack, flipped) } }), ROT_X_90_COUNTERCLOCKWISE({ _, server, player -> server.execute { val stack = player.mainHandStack val flipped = DrillModule.getBlacklistedPositions(stack).map { BlockPos(-it.y, it.x, it.z) } update(stack, flipped) } }), CLEAR({ _, server, player -> server.execute { val stack = player.mainHandStack update(stack, emptyList()) } }), } private fun update(stack: ItemStack, blacklist: List) { if (blacklist.isEmpty()) { stack.removeSubNbt("BlacklistedPositions") return } val tagList = NbtList() blacklist.map { pos -> NbtHelper.fromBlockPos(pos) }.forEach { tagList.add(it) } stack.nbt!!.put("BlacklistedPositions", tagList) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/UpdateModularToolLevelPacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking object UpdateModularToolLevelPacket { val UPDATE_MODULAR_TOOL_LEVEL = identifier("update_modular_level") fun register() { ServerPlayNetworking.registerGlobalReceiver(UPDATE_MODULAR_TOOL_LEVEL) { server, player, _, buf, _ -> val key = buf.readString(32767) val value = buf.readInt() val slot = buf.readInt() server.execute { val stack = player.inventory.getStack(slot) if (!stack.isEmpty) { val tag = stack.getOrCreateSubNbt("selected") tag.putInt(key, value) } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/packets/common/UpdateRancherConfigPacket.kt ================================================ package me.steven.indrev.packets.common import me.steven.indrev.blockentities.farms.RancherBlockEntity import me.steven.indrev.utils.identifier import me.steven.indrev.utils.isLoaded import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking object UpdateRancherConfigPacket { val SYNC_RANCHER_CONFIG = identifier("rancher_sync_config") fun register() { ServerPlayNetworking.registerGlobalReceiver(SYNC_RANCHER_CONFIG) { server, player, _, buf, _ -> val pos = buf.readBlockPos() val feedBabies = buf.readBoolean() val mateAdults = buf.readBoolean() val matingLimit = buf.readInt() val killAfter = buf.readInt() server.execute { val world = player.world if (world.isLoaded(pos)) { val blockEntity = world.getBlockEntity(pos) as? RancherBlockEntity ?: return@execute blockEntity.feedBabies = feedBabies blockEntity.mateAdults = mateAdults blockEntity.matingLimit = matingLimit blockEntity.killAfter = killAfter blockEntity.markDirty() } } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/IRecipeGetter.kt ================================================ package me.steven.indrev.recipes import me.steven.indrev.utils.IRFluidTank import me.steven.indrev.utils.asMutableList import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.inventory.Inventory import net.minecraft.item.ItemStack import net.minecraft.recipe.Recipe import net.minecraft.server.world.ServerWorld interface IRecipeGetter> { fun getMatchingRecipe(world: ServerWorld, itemStack: ItemStack): Collection fun getMatchingRecipe(world: ServerWorld, fluidInput: FluidVariant): Collection fun getMatchingRecipe(world: ServerWorld, stacks: List, fluids: List): Collection { return when { stacks.isEmpty() && fluids.isEmpty() -> emptyList() stacks.isEmpty() -> fluids.flatMap { getMatchingRecipe(world, it.resource) } fluids.isEmpty() -> stacks.flatMap { getMatchingRecipe(world, it) } else -> stacks.flatMap { getMatchingRecipe(world, it) }.asMutableList().also { results -> results.addAll(fluids.flatMap { getMatchingRecipe(world, it.resource) }) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/SelfRemainderRecipe.kt ================================================ package me.steven.indrev.recipes import com.google.gson.JsonObject import me.steven.indrev.FabricRecipeRemainder import me.steven.indrev.utils.identifier import net.minecraft.inventory.CraftingInventory import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.recipe.Ingredient import net.minecraft.recipe.RecipeSerializer import net.minecraft.recipe.ShapelessRecipe import net.minecraft.util.Identifier import net.minecraft.util.JsonHelper import net.minecraft.util.collection.DefaultedList class SelfRemainderRecipe(id: Identifier, group: String, output: ItemStack, ingredients: DefaultedList) : ShapelessRecipe(id, group, output, ingredients) { override fun craft(input: CraftingInventory): ItemStack { return output.copy() } override fun getRemainder(input: CraftingInventory): DefaultedList { val defaultedList = DefaultedList.ofSize(input.size(), ItemStack.EMPTY) for (i in 0 until input.size()) { val invStack: ItemStack = input.getStack(i) if (invStack.item is FabricRecipeRemainder) { val remainder = (invStack.item as FabricRecipeRemainder).getRemainder(invStack.copy(), input, null) defaultedList[i] = remainder } } return defaultedList } override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("selfremainder") val SERIALIZER = Serializer() class Serializer : ShapelessRecipe.Serializer() { override fun read(identifier: Identifier, jsonObject: JsonObject): SelfRemainderRecipe { val shaped = super.read(identifier, jsonObject)!! val group = JsonHelper.getString(jsonObject, "group", "") return SelfRemainderRecipe(shaped.id, group!!, shaped.output, shaped.ingredients) } override fun read(identifier: Identifier?, packetByteBuf: PacketByteBuf?): SelfRemainderRecipe { val shaped = super.read(identifier, packetByteBuf) return SelfRemainderRecipe(shaped.id, shaped.group, shaped.output, shaped.ingredients) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/CompressorRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class CompressorRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val ticks: Int ) : IRRecipe { override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("compress") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRRecipe.IRRecipeSerializer({ id, input, output, ticks -> CompressorRecipe(id, input, output, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/CondenserRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.IRFluidAmount import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class CondenserRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val fluidInput: Array, override val ticks: Int, ) : IRFluidRecipe() { override val fluidOutput: Array = emptyArray() override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("condenser") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRFluidRecipeSerializer({ id, ingredients, output, fluidInput, _, ticks -> CondenserRecipe(id, ingredients, output, fluidInput, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/DistillerRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.IRFluidAmount import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class DistillerRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val fluidInput: Array, override val ticks: Int, ) : IRFluidRecipe() { override val fluidOutput: Array = emptyArray() override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("distiller") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRFluidRecipeSerializer({ id, ingredients, output, fluidInput, _, ticks -> DistillerRecipe(id, ingredients, output, fluidInput!!, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/ElectrolysisRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.IRFluidAmount import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class ElectrolysisRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val fluidInput: Array, override val fluidOutput: Array, override val ticks: Int ) : IRFluidRecipe() { override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("electrolysis") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRFluidRecipeSerializer({ id, ingredients, output, fluidInput, fluidOutput, ticks -> ElectrolysisRecipe(id, ingredients, output, fluidInput, fluidOutput, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/FluidInfuserRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.IRFluidAmount import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class FluidInfuserRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val fluidInput: Array, override val fluidOutput: Array, override val ticks: Int, ) : IRFluidRecipe() { override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("fluid_infuse") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRFluidRecipeSerializer({ id, ingredients, output, fluidInput, fluidOutput, ticks -> FluidInfuserRecipe(id, ingredients, output, fluidInput, fluidOutput, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/IRFluidRecipe.kt ================================================ package me.steven.indrev.recipes.machines import com.google.gson.JsonObject import me.steven.indrev.components.CraftingComponent import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.* import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.recipe.Ingredient import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier abstract class IRFluidRecipe : IRRecipe { abstract val fluidInput: Array abstract val fluidOutput: Array override fun matches(inv: List, fluidVolume: List): Boolean { return when { fluidInput.isNotEmpty() -> fluidVolume.isNotEmpty() && fluidInput.indices.all { index -> val vol = fluidVolume[index] vol.resource == fluidInput[index].resource && vol.amount >= fluidInput[index].amount() } && super.matches(inv, fluidVolume) else -> super.matches(inv, fluidVolume) } } override fun canStart(component: CraftingComponent<*>): Boolean { val fluidComponent = component.fluidComponent!! val outputTankVolume = fluidComponent.outputTanks if (fluidOutput.isNotEmpty() && fluidOutput.indices.any { index -> val vol = fluidComponent[outputTankVolume[index]] !vol.isEmpty && (vol.resource != fluidOutput[index].resource || vol.amount + fluidOutput[index].amount() > fluidComponent.limit) } ) return false return super.canStart(component) } open class IRFluidRecipeSerializer(private val factory: (Identifier, Array, Array, Array, Array, Int) -> T) : RecipeSerializer { override fun read(id: Identifier, buf: PacketByteBuf): T { val ticks = buf.readInt() val inputFluidsSize = buf.readInt() val inputFluids = mutableListOf() (0 until inputFluidsSize).forEach { _ -> inputFluids.add(fromPacket(buf)) } val outputFluidsSize = buf.readInt() val outputFluids = mutableListOf() (0 until outputFluidsSize).forEach { _ -> outputFluids.add(fromPacket(buf)) } val size = buf.readInt() val ingredients = mutableListOf() (0 until size).forEach { _ -> val ingredient = Ingredient.fromPacket(buf) val count = buf.readInt() ingredients.add(InputEntry(ingredient, count)) } val outputSize = buf.readInt() val output = mutableListOf() (0 until outputSize).forEach { _ -> val stack = buf.readItemStack() val chance = buf.readDouble() output.add(OutputEntry(stack, chance)) } return factory(id, ingredients.toTypedArray(), output.toTypedArray(), inputFluids.toTypedArray(), outputFluids.toTypedArray(), ticks) } override fun read(id: Identifier, json: JsonObject): T { val ingredients = IRRecipe.ingredientsFromElement(json["ingredients"]) val ticks = json.get("processTime").asInt val output = IRRecipe.itemStacksFromElement(json["output"]) val fluidInputJson = json.get("fluidInput") val fluidInput = if (fluidInputJson == null) emptyArray() else getFluidFromJson(fluidInputJson) val fluidOutputJson = json.get("fluidOutput") val fluidOutput = if (fluidOutputJson == null) emptyArray() else getFluidFromJson(fluidOutputJson) return factory(id, ingredients, output, fluidInput, fluidOutput, ticks) } override fun write(buf: PacketByteBuf, recipe: T) { buf.writeInt(recipe.ticks) buf.writeInt(recipe.fluidInput.size) recipe.fluidInput.forEach { fluidInput -> fluidInput.toPacket(buf) } buf.writeInt(recipe.fluidOutput.size) recipe.fluidOutput.forEach { fluidOutput -> fluidOutput.toPacket(buf) } buf.writeInt(recipe.input.size) recipe.input.forEach { (ingredient, count) -> ingredient.write(buf) buf.writeInt(count) } buf.writeInt(recipe.outputs.size) recipe.outputs.forEach { (stack, chance) -> buf.writeItemStack(stack) buf.writeDouble(chance) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/IRRecipe.kt ================================================ package me.steven.indrev.recipes.machines import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject import me.steven.indrev.components.CraftingComponent import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.IRFluidTank import me.steven.indrev.utils.asMutableList import net.minecraft.inventory.Inventory import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.recipe.Ingredient import net.minecraft.recipe.Recipe import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier import net.minecraft.util.JsonHelper import net.minecraft.util.collection.DefaultedList import net.minecraft.util.registry.Registry import net.minecraft.world.World import java.util.* interface IRRecipe : Recipe { val identifier: Identifier val input: Array val outputs: Array val ticks: Int override fun getId(): Identifier = identifier @Deprecated("Unsupported method for Industrial Revolution's recipes", replaceWith = ReplaceWith("craft(Random)"), DeprecationLevel.ERROR) override fun craft(inv: Inventory?): ItemStack = throw IllegalArgumentException("Unsupported method for Industrial Revolution's recipes") @Deprecated("Unsupported method for Industrial Revolution's recipes", replaceWith = ReplaceWith("output"), DeprecationLevel.ERROR) override fun getOutput(): ItemStack = outputs.firstOrNull()?.stack ?: ItemStack.EMPTY @Deprecated("Unsupported method for Industrial Revolution's recipes", replaceWith = ReplaceWith("input"), DeprecationLevel.ERROR) override fun getIngredients(): DefaultedList = DefaultedList.of() @Deprecated("Unsupported method for Industrial Revolution's recipes", replaceWith = ReplaceWith("matches(Inventory, FluidVolume?)"), DeprecationLevel.ERROR) override fun matches(inv: Inventory?, world: World?): Boolean = throw IllegalArgumentException("Unsupported method for Industrial Revolution's recipes") override fun getType(): IRRecipeType<*> fun canStart(component: CraftingComponent<*>): Boolean { return component.outputSlots!!.isEmpty() || outputs.all { component.fits(it.stack) } } override fun isEmpty(): Boolean { return input.isEmpty() || input.any { entry -> entry.ingredient.matchingStacks.isEmpty() } } override fun isIgnoredInRecipeBook(): Boolean = true fun craft(random: Random?): List { val produced = ArrayList(outputs.size) outputs.forEach { (stack, chance) -> if (chance >= 1.0 || random != null && random.nextDouble() > chance) produced.add(stack.copy()) } return produced } fun matches(inv: List, fluidVolume: List): Boolean { if (inv.isEmpty()) return true else if (inv.size == 1 && input.size == 1) return matches(inv.first(), fluidVolume) val remainder = input.map { it.copy() }.asMutableList() for (stack in inv) { val result = remainder.firstOrNull { (ingredient, count) -> ingredient.test(stack) && stack.count >= count } ?: continue result.count -= stack.count if (result.count <= 0) remainder.remove(result) } return remainder.isEmpty() } fun matches(stack: ItemStack, fluidVolume: List): Boolean { assert(input.size == 1) val (ingredient, count) = input.first() if (ingredient.test(stack) && stack.count >= count) return true return false } open class IRRecipeSerializer(private val factory: (Identifier, Array, Array, Int) -> T) : RecipeSerializer { override fun read(id: Identifier, json: JsonObject): T { val ingredients = ingredientsFromElement(json["ingredients"]) val ticks = json.get("processTime").asInt val output = itemStacksFromElement(json["output"]) return factory(id, ingredients, output, ticks) } override fun read(id: Identifier, buf: PacketByteBuf): T { val size = buf.readInt() val ingredients = mutableListOf() (0 until size).forEach { _ -> val ingredient = Ingredient.fromPacket(buf) val count = buf.readInt() ingredients.add(InputEntry(ingredient, count)) } val ticks = buf.readInt() val outputSize = buf.readInt() val output = mutableListOf() (0 until outputSize).forEach { _ -> val stack = buf.readItemStack() val chance = buf.readDouble() output.add(OutputEntry(stack, chance)) } return factory(id, ingredients.toTypedArray(), output.toTypedArray(), ticks) } override fun write(buf: PacketByteBuf, recipe: T) { buf.writeInt(recipe.input.size) recipe.input.forEach { (ingredient, count) -> ingredient.write(buf) buf.writeInt(count) } buf.writeInt(recipe.ticks) buf.writeInt(recipe.outputs.size) recipe.outputs.forEach { (stack, chance) -> buf.writeItemStack(stack) buf.writeDouble(chance) } } } companion object { fun ingredientsFromElement(jsonElement: JsonElement?): Array { return when (jsonElement) { is JsonArray -> jsonElement.map { ingredientFromJson(it.asJsonObject) }.toTypedArray() is JsonObject -> arrayOf(ingredientFromJson(jsonElement)) null -> return emptyArray() else -> throw IllegalArgumentException("expected json object or array at 'ingredients', received $jsonElement") } } fun itemStacksFromElement(jsonElement: JsonElement?): Array { return when (jsonElement) { is JsonArray -> jsonElement.map { itemStackFromJson(it.asJsonObject)!! }.sortedByDescending { it.chance }.toTypedArray() is JsonObject -> listOf(itemStackFromJson(jsonElement) ?: return emptyArray()).toTypedArray() null -> return emptyArray() else -> throw IllegalArgumentException("expected json object or array at 'ingredients', received $jsonElement") } } fun ingredientFromJson(json: JsonObject): InputEntry { val ing = Ingredient.fromJson(json) val count = JsonHelper.getInt(json, "count", 1) return InputEntry(ing, count) } fun itemStackFromJson(json: JsonObject): OutputEntry? { val itemId = json.get("item").asString if (itemId == "empty") return null val item = Registry.ITEM.get(Identifier(itemId)) val output = ItemStack { item } if (output.isEmpty) println("empty $itemId") output.count = JsonHelper.getInt(json, "count", 1) val chance = JsonHelper.getFloat(json, "chance", 1f).toDouble() return OutputEntry(output, chance) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/IRRecipeType.kt ================================================ package me.steven.indrev.recipes.machines import com.google.common.collect.HashMultimap import com.google.common.collect.ImmutableList import com.google.common.collect.Multimap import me.steven.indrev.recipes.IRecipeGetter import me.steven.indrev.utils.getRecipes import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.recipe.RecipeType import net.minecraft.server.world.ServerWorld import net.minecraft.util.Identifier class IRRecipeType(val id: Identifier) : IRecipeGetter, RecipeType { private val recipeCache: Multimap = HashMultimap.create() private val fluidOnlyRecipeCache: Multimap = HashMultimap.create() override fun getMatchingRecipe(world: ServerWorld, itemStack: ItemStack): Collection { if (itemStack.isEmpty) return emptyList() else if (recipeCache.containsKey(itemStack.item)) return recipeCache[itemStack.item] val matches = ImmutableList.copyOf( world.recipeManager.getRecipes(this).values .filter { recipe -> recipe.input.any { it.ingredient.test(itemStack) } } ) recipeCache.putAll(itemStack.item, matches) return matches } override fun getMatchingRecipe(world: ServerWorld, fluidInput: FluidVariant): Collection { if (fluidOnlyRecipeCache.containsKey(fluidInput)) return fluidOnlyRecipeCache[fluidInput] val matches = ImmutableList.copyOf( world.recipeManager.getRecipes(this).values .filter { recipe -> recipe is IRFluidRecipe && recipe.fluidInput.any { it.resource == fluidInput } } ) fluidOnlyRecipeCache.putAll(fluidInput, matches) return matches } fun clearCache() { recipeCache.clear() fluidOnlyRecipeCache.clear() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/InfuserRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class InfuserRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val ticks: Int ) : IRRecipe { override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("infuse") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRRecipe.IRRecipeSerializer({ id, input, output, ticks -> InfuserRecipe(id, input, output, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/LaserRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class LaserRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val ticks: Int ) : IRRecipe { override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("laser") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRRecipe.IRRecipeSerializer({ id, input, output, ticks -> LaserRecipe(id, input, output, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/ModuleRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class ModuleRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val ticks: Int ) : IRRecipe { override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("modules") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRRecipe.IRRecipeSerializer({ id, input, output, ticks -> ModuleRecipe(id, input, output, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/PulverizerRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class PulverizerRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val ticks: Int ) : IRRecipe { override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("pulverize") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRRecipe.IRRecipeSerializer({ id, input, output, ticks -> PulverizerRecipe(id, input, output, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/RecyclerRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class RecyclerRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val ticks: Int ) : IRRecipe { override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("recycle") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRRecipe.IRRecipeSerializer({ id, input, output, ticks -> RecyclerRecipe(id, input, output, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/SawmillRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class SawmillRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val ticks: Int ) : IRRecipe { override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER override fun getType(): IRRecipeType<*> = TYPE companion object { val IDENTIFIER = identifier("sawmill") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = IRRecipe.IRRecipeSerializer(::SawmillRecipe) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/SmelterRecipe.kt ================================================ package me.steven.indrev.recipes.machines import me.steven.indrev.recipes.machines.entries.InputEntry import me.steven.indrev.recipes.machines.entries.OutputEntry import me.steven.indrev.utils.IRFluidAmount import me.steven.indrev.utils.identifier import net.minecraft.recipe.RecipeSerializer import net.minecraft.util.Identifier class SmelterRecipe( override val identifier: Identifier, override val input: Array, override val outputs: Array, override val fluidOutput: Array, override val ticks: Int, ) : IRFluidRecipe() { override val fluidInput: Array = emptyArray() override fun getType(): IRRecipeType<*> = TYPE override fun fits(width: Int, height: Int): Boolean = true override fun getSerializer(): RecipeSerializer<*> = SERIALIZER companion object { val IDENTIFIER = identifier("smelter") val TYPE = IRRecipeType(IDENTIFIER) val SERIALIZER = Serializer() class Serializer : IRFluidRecipeSerializer({ id, ingredients, output, _, fluidOutput, ticks -> SmelterRecipe(id, ingredients, output, fluidOutput, ticks) }) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/VanillaCookingRecipeCachedGetter.kt ================================================ package me.steven.indrev.recipes.machines import com.google.common.collect.HashMultimap import com.google.common.collect.Multimap import me.steven.indrev.recipes.IRecipeGetter import me.steven.indrev.utils.getRecipes import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.recipe.AbstractCookingRecipe import net.minecraft.recipe.RecipeType import net.minecraft.server.world.ServerWorld class VanillaCookingRecipeCachedGetter(private val type: RecipeType) : IRecipeGetter { private val recipeCache: Multimap = HashMultimap.create() override fun getMatchingRecipe(world: ServerWorld, itemStack: ItemStack): Collection { if (itemStack.isEmpty) return emptyList() else if (recipeCache.containsKey(itemStack.item)) return recipeCache[itemStack.item] val matches = world.recipeManager.getRecipes(type).values .filter { recipe -> recipe.input.test(itemStack) } recipeCache.putAll(itemStack.item, matches) return matches } override fun getMatchingRecipe(world: ServerWorld, fluidInput: FluidVariant): Collection = emptyList() companion object { val SMELTING = VanillaCookingRecipeCachedGetter(RecipeType.SMELTING) val SMOKING = VanillaCookingRecipeCachedGetter(RecipeType.SMOKING) val BLASTING = VanillaCookingRecipeCachedGetter(RecipeType.BLASTING) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/entries/InputEntry.kt ================================================ package me.steven.indrev.recipes.machines.entries import net.minecraft.recipe.Ingredient data class InputEntry(val ingredient: Ingredient, var count: Int) ================================================ FILE: src/main/kotlin/me/steven/indrev/recipes/machines/entries/OutputEntry.kt ================================================ package me.steven.indrev.recipes.machines.entries import net.minecraft.item.ItemStack data class OutputEntry(val stack: ItemStack, val chance: Double) ================================================ FILE: src/main/kotlin/me/steven/indrev/registry/IRBlockRegistry.kt ================================================ package me.steven.indrev.registry import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.cables.BasePipeBlockEntity import me.steven.indrev.blockentities.miningrig.DrillBlockEntity import me.steven.indrev.blockentities.farms.BiomassComposterBlockEntity import me.steven.indrev.blockentities.generators.SteamTurbineBlockEntity import me.steven.indrev.blockentities.generators.SteamTurbineSteamInputValveBlockEntity import me.steven.indrev.blockentities.laser.CapsuleBlockEntity import me.steven.indrev.blockentities.solarpowerplant.* import me.steven.indrev.blockentities.storage.CabinetBlockEntity import me.steven.indrev.blockentities.storage.TankBlockEntity import me.steven.indrev.blocks.HeliostatBlock import me.steven.indrev.blocks.machine.CapsuleBlock import me.steven.indrev.blocks.machine.DrillBlock import me.steven.indrev.blocks.machine.pipes.BasePipeBlock import me.steven.indrev.blocks.machine.pipes.CableBlock import me.steven.indrev.blocks.machine.pipes.FluidPipeBlock import me.steven.indrev.blocks.machine.pipes.ItemPipeBlock import me.steven.indrev.blocks.machine.solarpowerplant.* import me.steven.indrev.blocks.misc.* import me.steven.indrev.networks.energy.CableEnergyIo import me.steven.indrev.utils.* import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder import net.fabricmc.fabric.api.registry.FlammableBlockRegistry import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage import net.minecraft.block.Block import net.minecraft.block.Blocks import net.minecraft.block.MapColor import net.minecraft.block.Material import net.minecraft.block.entity.BlockEntityType import net.minecraft.item.BlockItem import net.minecraft.server.world.ServerWorld import net.minecraft.sound.BlockSoundGroup import net.minecraft.util.math.Direction import net.minecraft.util.registry.Registry import team.reborn.energy.api.EnergyStorage @Suppress("MemberVisibilityCanBePrivate") object IRBlockRegistry { fun registerAll() { identifier("sulfur_crystal").block(SULFUR_CRYSTAL_CLUSTER).item(IRItemRegistry.SULFUR_CRYSTAL_ITEM) identifier("planks").block(PLANKS).item(BlockItem(PLANKS, itemSettings())) identifier("plank_block").block(PLANK_BLOCK).item(BlockItem(PLANK_BLOCK, itemSettings())) FlammableBlockRegistry.getDefaultInstance().add(PLANKS, 10, 40) FlammableBlockRegistry.getDefaultInstance().add(PLANK_BLOCK, 10, 40) identifier("biomass_composter").block(BIOMASS_COMPOSTER_BLOCK).item(BIOMASS_COMPOSTER_ITEM).blockEntityType(BIOMASS_COMPOSTER_BLOCK_ENTITY) FluidStorage.SIDED.registerForBlockEntity({ be, c -> if (c != Direction.UP) be.fluidInv else null }, BIOMASS_COMPOSTER_BLOCK_ENTITY) ItemStorage.SIDED.registerForBlockEntity({ be, c -> if (c != Direction.UP) be.itemInv else null }, BIOMASS_COMPOSTER_BLOCK_ENTITY) identifier("wither_proof_obsidian").block(WITHER_PROOF_OBSIDIAN).item(BlockItem(WITHER_PROOF_OBSIDIAN, itemSettings())) identifier("machine_block").block(MACHINE_BLOCK).item(BlockItem(MACHINE_BLOCK, itemSettings())) identifier("controller").block(CONTROLLER).item(BlockItem(CONTROLLER, itemSettings())) identifier("frame").block(FRAME).item(BlockItem(FRAME, itemSettings())) identifier("duct").block(DUCT).item(BlockItem(DUCT, itemSettings())) identifier("silo").block(SILO).item(BlockItem(SILO, itemSettings())) identifier("warning_strobe").block(WARNING_STROBE).item(BlockItem(WARNING_STROBE, itemSettings())) identifier("intake").block(INTAKE).item(BlockItem(INTAKE, itemSettings())) identifier("cabinet").block(CABINET).item(BlockItem(CABINET, itemSettings())).blockEntityType(CABINET_BLOCK_ENTITY_TYPE) identifier("drill_top").block(DRILL_TOP) identifier("drill_middle").block(DRILL_MIDDLE) identifier("drill_bottom").block(DRILL_BOTTOM).item(BlockItem(DRILL_BOTTOM, itemSettings())) identifier("drill").blockEntityType(DRILL_BLOCK_ENTITY_TYPE) identifier("tank").block(TANK_BLOCK).item(IRItemRegistry.TANK_BLOCK_ITEM).blockEntityType(TANK_BLOCK_ENTITY) FluidStorage.SIDED.registerForBlockEntity({ be, _ -> val combinedStorage = TankBlockEntity.CombinedTankStorage() TankBlock.findAllTanks(be.world!!.getChunk(be.pos), be.world!!.getBlockState(be.pos), be.pos, mutableSetOf(), combinedStorage) combinedStorage }, TANK_BLOCK_ENTITY) identifier("capsule").block(CAPSULE_BLOCK).item(IRItemRegistry.CAPSULE_BLOCK_ITEM).blockEntityType(CAPSULE_BLOCK_ENTITY) identifier("fluid_pipe_mk1").block(FLUID_PIPE_MK1) identifier("fluid_pipe_mk2").block(FLUID_PIPE_MK2) identifier("fluid_pipe_mk3").block(FLUID_PIPE_MK3) identifier("fluid_pipe_mk4").block(FLUID_PIPE_MK4) identifier("item_pipe_mk1").block(ITEM_PIPE_MK1) identifier("item_pipe_mk2").block(ITEM_PIPE_MK2) identifier("item_pipe_mk3").block(ITEM_PIPE_MK3) identifier("item_pipe_mk4").block(ITEM_PIPE_MK4) identifier("cable_mk1").block(CABLE_MK1).blockEntityType(COVERABLE_BLOCK_ENTITY_TYPE_MK1) identifier("cable_mk2").block(CABLE_MK2).blockEntityType(COVERABLE_BLOCK_ENTITY_TYPE_MK2) identifier("cable_mk3").block(CABLE_MK3).blockEntityType(COVERABLE_BLOCK_ENTITY_TYPE_MK3) identifier("cable_mk4").block(CABLE_MK4).blockEntityType(COVERABLE_BLOCK_ENTITY_TYPE_MK4) EnergyStorage.SIDED.registerForBlocks({ world, pos, _, be, dir -> if (world is ServerWorld && be is BasePipeBlockEntity) { val energyNetwork = world.energyNetworkState.networksByPos[pos.asLong()] if (energyNetwork != null && be.connections[dir]?.isConnected() == true) { return@registerForBlocks CableEnergyIo(energyNetwork, pos, dir) } } CableEnergyIo.NO_NETWORK }, CABLE_MK1, CABLE_MK2, CABLE_MK3, CABLE_MK4) identifier("heliostat") .block(HELIOSTAT_BLOCK) .item(HELIOSTAT_BLOCK_ITEM) .blockEntityType(HELIOSTAT_BLOCK_ENTITY) identifier("resistant_glass") .block(RESISTANT_GLASS_BLOCK) .item(RESISTANT_GLASS_BLOCK_ITEM) identifier("solar_receiver") .block(SOLAR_RECEIVER_BLOCK) .item(SOLAR_RECEIVER_BLOCK_ITEM) .blockEntityType(SOLAR_RECEIVER_BLOCK_ENTITY) identifier("fluid_valve").block(FLUID_VALVE).item(FLUID_VALVE_ITEM) FluidStorage.SIDED.registerForBlocks({ world, pos, _, _, dir -> val aboveBlockEntity = world.getBlockEntity(pos.up()) as? SolarPowerPlantTowerBlockEntity return@registerForBlocks aboveBlockEntity?.fluidComponent?.getCachedSide(dir) }, FLUID_VALVE) identifier("steam_turbine_steam_input_valve") .block(STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK) .item(STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ITEM) .blockEntityType(STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ENTITY) FluidStorage.SIDED.registerForBlockEntity({ be, _ -> be.getSteamTurbine()?.fluidComponent }, STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ENTITY) identifier("steam_turbine_energy_output").block(STEAM_TURBINE_ENERGY_OUTPUT).item(STEAM_TURBINE_ENERGY_OUTPUT_ITEM) EnergyStorage.SIDED.registerForBlocks({ world, pos, _, _, s -> val turbineBlockEntity = world.getBlockEntity(pos.up()) as? SteamTurbineBlockEntity if (turbineBlockEntity?.multiblockComponent?.isBuilt(world, pos.up(), turbineBlockEntity.cachedState) == true) turbineBlockEntity.storage.getSideStorage(s) else null }, STEAM_TURBINE_ENERGY_OUTPUT) identifier("steam_turbine_casing").block(STEAM_TURBINE_CASING_BLOCK).item(STEAM_TURBINE_CASING_BLOCK_ITEM) identifier("steam_turbine_rotor").block(STEAM_TURBINE_ROTOR_BLOCK).item(STEAM_TURBINE_ROTOR_BLOCK_ITEM) identifier("steam_turbine_pressure_valve").block(STEAM_TURBINE_PRESSURE_VALVE_BLOCK).item(STEAM_TURBINE_PRESSURE_VALVE_BLOCK_ITEM) identifier("solar_power_plant_tower") .block(SOLAR_POWER_PLANT_TOWER_BLOCK) .item(SOLAR_POWER_PLANT_TOWER_BLOCK_ITEM) .blockEntityType(SOLAR_POWER_PLANT_TOWER_BLOCK_ENTITY) } val SULFUR_CRYSTAL_CLUSTER = SulfurCrystalBlock(FabricBlockSettings.of(Material.METAL).sounds(BlockSoundGroup.GLASS).requiresTool().strength(3f, 3f)) val NIKOLITE_ORE = { Registry.BLOCK.get(identifier("nikolite_ore")) } val TIN_ORE = { Registry.BLOCK.get(identifier("tin_ore")) } val LEAD_ORE = { Registry.BLOCK.get(identifier("lead_ore")) } val SILVER_ORE = { Registry.BLOCK.get(identifier("silver_ore")) } val TUNGSTEN_ORE = { Registry.BLOCK.get(identifier("tungsten_ore")) } val DEEPSLATE_NIKOLITE_ORE = { Registry.BLOCK.get(identifier("deepslate_nikolite_ore")) } val DEEPSLATE_TIN_ORE = { Registry.BLOCK.get(identifier("deepslate_tin_ore")) } val DEEPSLATE_LEAD_ORE = { Registry.BLOCK.get(identifier("deepslate_lead_ore")) } val DEEPSLATE_SILVER_ORE = { Registry.BLOCK.get(identifier("deepslate_silver_ore")) } val DEEPSLATE_TUNGSTEN_ORE = { Registry.BLOCK.get(identifier("deepslate_tungsten_ore")) } val MACHINE_BLOCK = Block( FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F) ) val PLANKS = PlankBlock( FabricBlockSettings.of(Material.WOOD, MapColor.BROWN).strength(2.0f).sounds(BlockSoundGroup.WOOD) ) val PLANK_BLOCK = Block( FabricBlockSettings.of(Material.WOOD, MapColor.BROWN).strength(3F, 6F).sounds(BlockSoundGroup.WOOD) ) val BIOMASS_COMPOSTER_BLOCK = BiomassComposterBlock() val BIOMASS_COMPOSTER_ITEM = BlockItem(BIOMASS_COMPOSTER_BLOCK, itemSettings()) val BIOMASS_COMPOSTER_BLOCK_ENTITY = FabricBlockEntityTypeBuilder.create(::BiomassComposterBlockEntity, BIOMASS_COMPOSTER_BLOCK).build() val WITHER_PROOF_OBSIDIAN = Block( FabricBlockSettings.of(Material.STONE, MapColor.BLACK).requiresTool().strength(50.0F, 1200.0F).sounds(BlockSoundGroup.STONE) ) val CONTROLLER = HorizontalFacingBlock( FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F) ) val DUCT = DuctBlock( FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F) ) val FRAME = Block( FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F) ) val SILO = Block( FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F) ) val WARNING_STROBE = WarningStrobeBlock( FabricBlockSettings.of(Material.METAL).requiresTool().luminance(15).nonOpaque().strength(3F, 6F) ) val INTAKE = HorizontalFacingBlock( FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F) ) val CABINET = CabinetBlock( FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F) ) val CABINET_BLOCK_ENTITY_TYPE: BlockEntityType = FabricBlockEntityTypeBuilder.create(::CabinetBlockEntity, CABINET).build(null) val DRILL_TOP = DrillBlock.TopDrillBlock( FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F) ) val DRILL_MIDDLE = DrillBlock.MiddleDrillBlock( FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F) ) val DRILL_BOTTOM = DrillBlock.BottomDrillBlock( FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F) ) val DRILL_BLOCK_ENTITY_TYPE: BlockEntityType = BlockEntityType.Builder.create(::DrillBlockEntity, DRILL_BOTTOM).build(null) val TANK_BLOCK = TankBlock(FabricBlockSettings.of(Material.GLASS).nonOpaque().requiresTool().strength(1f, 1f)) val TANK_BLOCK_ENTITY: BlockEntityType = BlockEntityType.Builder.create(::TankBlockEntity, TANK_BLOCK).build(null) val CAPSULE_BLOCK = CapsuleBlock() val CAPSULE_BLOCK_ENTITY: BlockEntityType = BlockEntityType.Builder.create(::CapsuleBlockEntity, CAPSULE_BLOCK).build(null) val FLUID_PIPE_MK1 = FluidPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK1) val FLUID_PIPE_MK2 = FluidPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK2) val FLUID_PIPE_MK3 = FluidPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK3) val FLUID_PIPE_MK4 = FluidPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK4) val ITEM_PIPE_MK1 = ItemPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK1) val ITEM_PIPE_MK2 = ItemPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK2) val ITEM_PIPE_MK3 = ItemPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK3) val ITEM_PIPE_MK4 = ItemPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK4) val CABLE_MK1 = CableBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK1) val CABLE_MK2 = CableBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK2) val CABLE_MK3 = CableBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK3) val CABLE_MK4 = CableBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK4) val COVERABLE_BLOCK_ENTITY_TYPE_MK1 = FabricBlockEntityTypeBuilder.create({ pos, state -> BasePipeBlockEntity((state.block as BasePipeBlock).type, Tier.MK1, pos, state) }, FLUID_PIPE_MK1, ITEM_PIPE_MK1, CABLE_MK1).build(null) val COVERABLE_BLOCK_ENTITY_TYPE_MK2 = FabricBlockEntityTypeBuilder.create({ pos, state -> BasePipeBlockEntity((state.block as BasePipeBlock).type, Tier.MK2, pos, state) }, FLUID_PIPE_MK2, ITEM_PIPE_MK2, CABLE_MK2).build(null) val COVERABLE_BLOCK_ENTITY_TYPE_MK3 = FabricBlockEntityTypeBuilder.create({ pos, state -> BasePipeBlockEntity((state.block as BasePipeBlock).type, Tier.MK3, pos, state) }, FLUID_PIPE_MK3, ITEM_PIPE_MK3, CABLE_MK3).build(null) val COVERABLE_BLOCK_ENTITY_TYPE_MK4 = FabricBlockEntityTypeBuilder.create({ pos, state -> BasePipeBlockEntity((state.block as BasePipeBlock).type, Tier.MK4, pos, state) }, FLUID_PIPE_MK4, ITEM_PIPE_MK4, CABLE_MK4).build(null) val HELIOSTAT_BLOCK = HeliostatBlock( FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F) ) val HELIOSTAT_BLOCK_ITEM = BlockItem(HELIOSTAT_BLOCK, itemSettings()) val HELIOSTAT_BLOCK_ENTITY = BlockEntityType.Builder.create(::HeliostatBlockEntity, HELIOSTAT_BLOCK).build(null) val SOLAR_RECEIVER_BLOCK = SolarReceiverBlock( FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F) ) val SOLAR_RECEIVER_BLOCK_ITEM = BlockItem(SOLAR_RECEIVER_BLOCK, itemSettings()) val SOLAR_RECEIVER_BLOCK_ENTITY = BlockEntityType.Builder.create(::SolarReceiverBlockEntity, SOLAR_RECEIVER_BLOCK).build(null) val STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK = SteamTurbineSteamInputValveBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F)) val STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ITEM = BlockItem(STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK, itemSettings()) val STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ENTITY = BlockEntityType.Builder.create(::SteamTurbineSteamInputValveBlockEntity, STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK).build(null) val RESISTANT_GLASS_BLOCK = Block( FabricBlockSettings.of(Material.GLASS).requiresTool().nonOpaque().strength(3F, 6F) ) val RESISTANT_GLASS_BLOCK_ITEM = BlockItem(RESISTANT_GLASS_BLOCK, itemSettings()) val FLUID_VALVE = FluidValveBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F)) val FLUID_VALVE_ITEM = BlockItem(FLUID_VALVE, itemSettings()) val STEAM_TURBINE_ENERGY_OUTPUT = HorizontalFacingBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F)) val STEAM_TURBINE_ENERGY_OUTPUT_ITEM = BlockItem(STEAM_TURBINE_ENERGY_OUTPUT, itemSettings()) val STEAM_TURBINE_CASING_BLOCK = Block(FabricBlockSettings.of(Material.METAL).strength(3F, 6F)) val STEAM_TURBINE_CASING_BLOCK_ITEM = BlockItem(STEAM_TURBINE_CASING_BLOCK, itemSettings()) val STEAM_TURBINE_ROTOR_BLOCK = VerticalFacingBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F)) val STEAM_TURBINE_ROTOR_BLOCK_ITEM = BlockItem(STEAM_TURBINE_ROTOR_BLOCK, itemSettings()) val STEAM_TURBINE_PRESSURE_VALVE_BLOCK = HorizontalFacingBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F)) val STEAM_TURBINE_PRESSURE_VALVE_BLOCK_ITEM = BlockItem(STEAM_TURBINE_PRESSURE_VALVE_BLOCK, itemSettings()) val SOLAR_POWER_PLANT_TOWER_BLOCK = SolarPowerPlantTowerBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F)) val SOLAR_POWER_PLANT_TOWER_BLOCK_ITEM = BlockItem(SOLAR_POWER_PLANT_TOWER_BLOCK, itemSettings()) val SOLAR_POWER_PLANT_TOWER_BLOCK_ENTITY = BlockEntityType.Builder.create(::SolarPowerPlantTowerBlockEntity, SOLAR_POWER_PLANT_TOWER_BLOCK).build(null) } ================================================ FILE: src/main/kotlin/me/steven/indrev/registry/IRFluidFuelRegistry.kt ================================================ package me.steven.indrev.registry import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import net.minecraft.fluid.Fluid object IRFluidFuelRegistry { private val registry = Object2ObjectOpenHashMap() fun register(fluid: Fluid, info: FluidFuelInfo) { registry[fluid] = info } fun register(fluid: Fluid, burnTime: Int, combustionTemperature: Int, ratio: Int, consumptionRatio: Long) { registry[fluid] = FluidFuelInfo(burnTime, combustionTemperature, ratio, consumptionRatio) } fun isFuel(fluid: Fluid) = registry.contains(fluid) fun get(fluid: Fluid) = registry[fluid] data class FluidFuelInfo(val burnTime: Int, val combustionTemperature: Int, val generationRatio: Int, val consumptionRatio: Long) } ================================================ FILE: src/main/kotlin/me/steven/indrev/registry/IRFluidRegistry.kt ================================================ package me.steven.indrev.registry import me.steven.indrev.blocks.misc.AcidFluidBlock import me.steven.indrev.datagen.utils.* import me.steven.indrev.fluids.BaseFluid import me.steven.indrev.utils.* import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricMaterialBuilder import net.minecraft.block.FluidBlock import net.minecraft.block.MapColor import net.minecraft.block.Material import net.minecraft.item.BucketItem import net.minecraft.item.Items @Suppress("MemberVisibilityCanBePrivate") object IRFluidRegistry { fun registerAll() { COOLANT_IDENTIFIER.block(COOLANT) identifier("${COOLANT_IDENTIFIER.path}_still").fluid(COOLANT_STILL) identifier("${COOLANT_IDENTIFIER.path}_flowing").fluid(COOLANT_FLOWING) identifier("${COOLANT_IDENTIFIER.path}_bucket").item(COOLANT_BUCKET) MOLTEN_NETHERITE_IDENTIFIER.block(MOLTEN_NETHERITE) identifier("${MOLTEN_NETHERITE_IDENTIFIER.path}_still").fluid(MOLTEN_NETHERITE_STILL) identifier("${MOLTEN_NETHERITE_IDENTIFIER.path}_flowing").fluid(MOLTEN_NETHERITE_FLOWING) identifier("${MOLTEN_NETHERITE_IDENTIFIER.path}_bucket").item(MOLTEN_NETHERITE_BUCKET) MOLTEN_IRON_IDENTIFIER.block(MOLTEN_IRON) identifier("${MOLTEN_IRON_IDENTIFIER.path}_still").fluid(MOLTEN_IRON_STILL) identifier("${MOLTEN_IRON_IDENTIFIER.path}_flowing").fluid(MOLTEN_IRON_FLOWING) identifier("${MOLTEN_IRON_IDENTIFIER.path}_bucket").item(MOLTEN_IRON_BUCKET) MOLTEN_GOLD_IDENTIFIER.block(MOLTEN_GOLD) identifier("${MOLTEN_GOLD_IDENTIFIER.path}_still").fluid(MOLTEN_GOLD_STILL) identifier("${MOLTEN_GOLD_IDENTIFIER.path}_flowing").fluid(MOLTEN_GOLD_FLOWING) identifier("${MOLTEN_GOLD_IDENTIFIER.path}_bucket").item(MOLTEN_GOLD_BUCKET) MOLTEN_COPPER_IDENTIFIER.block(MOLTEN_COPPER) identifier("${MOLTEN_COPPER_IDENTIFIER.path}_still").fluid(MOLTEN_COPPER_STILL) identifier("${MOLTEN_COPPER_IDENTIFIER.path}_flowing").fluid(MOLTEN_COPPER_FLOWING) identifier("${MOLTEN_COPPER_IDENTIFIER.path}_bucket").item(MOLTEN_COPPER_BUCKET) MOLTEN_TIN_IDENTIFIER.block(MOLTEN_TIN) identifier("${MOLTEN_TIN_IDENTIFIER.path}_still").fluid(MOLTEN_TIN_STILL) identifier("${MOLTEN_TIN_IDENTIFIER.path}_flowing").fluid(MOLTEN_TIN_FLOWING) identifier("${MOLTEN_TIN_IDENTIFIER.path}_bucket").item(MOLTEN_TIN_BUCKET) MOLTEN_LEAD_IDENTIFIER.block(MOLTEN_LEAD) identifier("${MOLTEN_LEAD_IDENTIFIER.path}_still").fluid(MOLTEN_LEAD_STILL) identifier("${MOLTEN_LEAD_IDENTIFIER.path}_flowing").fluid(MOLTEN_LEAD_FLOWING) identifier("${MOLTEN_LEAD_IDENTIFIER.path}_bucket").item(MOLTEN_LEAD_BUCKET) MOLTEN_SILVER_IDENTIFIER.block(MOLTEN_SILVER) identifier("${MOLTEN_SILVER_IDENTIFIER.path}_still").fluid(MOLTEN_SILVER_STILL) identifier("${MOLTEN_SILVER_IDENTIFIER.path}_flowing").fluid(MOLTEN_SILVER_FLOWING) identifier("${MOLTEN_SILVER_IDENTIFIER.path}_bucket").item(MOLTEN_SILVER_BUCKET) SULFURIC_ACID_IDENTIFIER.block(SULFURIC_ACID) identifier("${SULFURIC_ACID_IDENTIFIER.path}_still").fluid(SULFURIC_ACID_STILL) identifier("${SULFURIC_ACID_IDENTIFIER.path}_flowing").fluid(SULFURIC_ACID_FLOWING) identifier("${SULFURIC_ACID_IDENTIFIER.path}_bucket").item(SULFURIC_ACID_BUCKET) TOXIC_MUD_IDENTIFIER.block(TOXIC_MUD) identifier("${TOXIC_MUD_IDENTIFIER.path}_still").fluid(TOXIC_MUD_STILL) identifier("${TOXIC_MUD_IDENTIFIER.path}_flowing").fluid(TOXIC_MUD_FLOWING) identifier("${TOXIC_MUD_IDENTIFIER.path}_bucket").item(TOXIC_MUD_BUCKET) HYDROGEN_IDENTIFIER.block(HYDROGEN) identifier("${HYDROGEN_IDENTIFIER.path}_still").fluid(HYDROGEN_STILL) identifier("${HYDROGEN_IDENTIFIER.path}_flowing").fluid(HYDROGEN_FLOWING) identifier("${HYDROGEN_IDENTIFIER.path}_bucket").item(HYDROGEN_BUCKET) IRFluidFuelRegistry.register(HYDROGEN_STILL, 10, 900, 4, 100 * 81) OXYGEN_IDENTIFIER.block(OXYGEN) identifier("${OXYGEN_IDENTIFIER.path}_still").fluid(OXYGEN_STILL) identifier("${OXYGEN_IDENTIFIER.path}_flowing").fluid(OXYGEN_FLOWING) identifier("${OXYGEN_IDENTIFIER.path}_bucket").item(OXYGEN_BUCKET) METHANE_IDENTIFIER.block(METHANE) identifier("${METHANE_IDENTIFIER.path}_still").fluid(METHANE_STILL) identifier("${METHANE_IDENTIFIER.path}_flowing").fluid(METHANE_FLOWING) identifier("${METHANE_IDENTIFIER.path}_bucket").item(METHANE_BUCKET) IRFluidFuelRegistry.register(METHANE_STILL, 60, 900, 128, 250 * 81) STEAM_IDENTIFIER.block(STEAM) identifier("${STEAM_IDENTIFIER.path}_still").fluid(STEAM_STILL) identifier("${STEAM_IDENTIFIER.path}_flowing").fluid(STEAM_FLOWING) identifier("${STEAM_IDENTIFIER.path}_bucket").item(STEAM_BUCKET) } val COOLANT_IDENTIFIER = identifier("coolant") val COOLANT_STILL: BaseFluid.Still = BaseFluid.Still(COOLANT_IDENTIFIER, { COOLANT }, { COOLANT_BUCKET }, 0x0C2340) { COOLANT_FLOWING } val COOLANT_FLOWING = BaseFluid.Flowing(COOLANT_IDENTIFIER, { COOLANT }, { COOLANT_BUCKET }, 0x0C2340) { COOLANT_STILL } val COOLANT_BUCKET = BucketItem(COOLANT_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val COOLANT = object : FluidBlock(COOLANT_STILL, FabricBlockSettings.of(Material.LAVA)) {} val MOLTEN_NETHERITE_IDENTIFIER = identifier("molten_netherite") val MOLTEN_NETHERITE_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_NETHERITE_IDENTIFIER, { MOLTEN_NETHERITE }, { MOLTEN_NETHERITE_BUCKET }, NETHERITE_SCRAP_BASE.toInt()) { MOLTEN_NETHERITE_FLOWING } val MOLTEN_NETHERITE_FLOWING = BaseFluid.Flowing(MOLTEN_NETHERITE_IDENTIFIER, { MOLTEN_NETHERITE }, { MOLTEN_NETHERITE_BUCKET }, NETHERITE_SCRAP_BASE.toInt()) { MOLTEN_NETHERITE_STILL } val MOLTEN_NETHERITE_BUCKET = BucketItem(MOLTEN_NETHERITE_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val MOLTEN_NETHERITE = object : FluidBlock(MOLTEN_NETHERITE_STILL, FabricBlockSettings.of(Material.LAVA)) {} val MOLTEN_IRON_IDENTIFIER = identifier("molten_iron") val MOLTEN_IRON_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_IRON_IDENTIFIER, { MOLTEN_IRON }, { MOLTEN_IRON_BUCKET }, 0x7A0019) { MOLTEN_IRON_FLOWING } val MOLTEN_IRON_FLOWING = BaseFluid.Flowing(MOLTEN_IRON_IDENTIFIER, { MOLTEN_IRON }, { MOLTEN_IRON_BUCKET }, 0x7A0019) { MOLTEN_IRON_STILL } val MOLTEN_IRON_BUCKET = BucketItem(MOLTEN_IRON_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val MOLTEN_IRON = object : FluidBlock(MOLTEN_IRON_STILL, FabricBlockSettings.of(Material.LAVA)) {} val MOLTEN_GOLD_IDENTIFIER = identifier("molten_gold") val MOLTEN_GOLD_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_GOLD_IDENTIFIER, { MOLTEN_GOLD }, { MOLTEN_GOLD_BUCKET }, 0xFFCC00) { MOLTEN_GOLD_FLOWING } val MOLTEN_GOLD_FLOWING = BaseFluid.Flowing(MOLTEN_GOLD_IDENTIFIER, { MOLTEN_GOLD }, { MOLTEN_GOLD_BUCKET }, 0xFFCC00) { MOLTEN_GOLD_STILL } val MOLTEN_GOLD_BUCKET = BucketItem(MOLTEN_GOLD_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val MOLTEN_GOLD = object : FluidBlock(MOLTEN_GOLD_STILL, FabricBlockSettings.of(Material.LAVA)) {} val MOLTEN_COPPER_IDENTIFIER = identifier("molten_copper") val MOLTEN_COPPER_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_COPPER_IDENTIFIER, { MOLTEN_COPPER }, { MOLTEN_COPPER_BUCKET }, COPPER_BASE.toInt()) { MOLTEN_COPPER_FLOWING } val MOLTEN_COPPER_FLOWING = BaseFluid.Flowing(MOLTEN_COPPER_IDENTIFIER, { MOLTEN_COPPER }, { MOLTEN_COPPER_BUCKET }, COPPER_BASE.toInt()) { MOLTEN_COPPER_STILL } val MOLTEN_COPPER_BUCKET = BucketItem(MOLTEN_COPPER_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val MOLTEN_COPPER = object : FluidBlock(MOLTEN_COPPER_STILL, FabricBlockSettings.of(Material.LAVA)) {} val MOLTEN_TIN_IDENTIFIER = identifier("molten_tin") val MOLTEN_TIN_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_TIN_IDENTIFIER, { MOLTEN_TIN }, { MOLTEN_TIN_BUCKET }, TIN_BASE.toInt()) { MOLTEN_TIN_FLOWING } val MOLTEN_TIN_FLOWING = BaseFluid.Flowing(MOLTEN_TIN_IDENTIFIER, { MOLTEN_TIN }, { MOLTEN_TIN_BUCKET }, TIN_BASE.toInt()) { MOLTEN_TIN_STILL } val MOLTEN_TIN_BUCKET = BucketItem(MOLTEN_TIN_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val MOLTEN_TIN = object : FluidBlock(MOLTEN_TIN_STILL, FabricBlockSettings.of(Material.LAVA)) {} val MOLTEN_LEAD_IDENTIFIER = identifier("molten_lead") val MOLTEN_LEAD_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_LEAD_IDENTIFIER, { MOLTEN_LEAD }, { MOLTEN_LEAD_BUCKET }, LEAD_BASE.toInt()) { MOLTEN_LEAD_FLOWING } val MOLTEN_LEAD_FLOWING = BaseFluid.Flowing(MOLTEN_LEAD_IDENTIFIER, { MOLTEN_LEAD }, { MOLTEN_LEAD_BUCKET }, LEAD_BASE.toInt()) { MOLTEN_LEAD_STILL } val MOLTEN_LEAD_BUCKET = BucketItem(MOLTEN_LEAD_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val MOLTEN_LEAD = object : FluidBlock(MOLTEN_LEAD_STILL, FabricBlockSettings.of(Material.LAVA)) {} val MOLTEN_SILVER_IDENTIFIER = identifier("molten_silver") val MOLTEN_SILVER_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_SILVER_IDENTIFIER, { MOLTEN_SILVER }, { MOLTEN_SILVER_BUCKET }, SILVER_BASE.toInt()) { MOLTEN_SILVER_FLOWING } val MOLTEN_SILVER_FLOWING = BaseFluid.Flowing(MOLTEN_SILVER_IDENTIFIER, { MOLTEN_SILVER }, { MOLTEN_SILVER_BUCKET }, SILVER_BASE.toInt()) { MOLTEN_SILVER_STILL } val MOLTEN_SILVER_BUCKET = BucketItem(MOLTEN_SILVER_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val MOLTEN_SILVER = object : FluidBlock(MOLTEN_SILVER_STILL, FabricBlockSettings.of(Material.LAVA)) {} val ACID_MATERIAL: Material = FabricMaterialBuilder(MapColor.GREEN).allowsMovement().lightPassesThrough().notSolid().replaceable() .liquid().build() val MUD_MATERIAL: Material = FabricMaterialBuilder(MapColor.BROWN).allowsMovement().lightPassesThrough().notSolid().replaceable() .liquid().build() val SULFURIC_ACID_IDENTIFIER = identifier("sulfuric_acid") val SULFURIC_ACID_STILL: BaseFluid.Still = BaseFluid.Still(SULFURIC_ACID_IDENTIFIER, { SULFURIC_ACID }, { SULFURIC_ACID_BUCKET }, 0x003D1E) { SULFURIC_ACID_FLOWING } val SULFURIC_ACID_FLOWING = BaseFluid.Flowing(SULFURIC_ACID_IDENTIFIER, { SULFURIC_ACID }, { SULFURIC_ACID_BUCKET }, 0x003D1E) { SULFURIC_ACID_STILL } val SULFURIC_ACID_BUCKET = BucketItem(SULFURIC_ACID_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val SULFURIC_ACID = AcidFluidBlock(SULFURIC_ACID_STILL, FabricBlockSettings.of(ACID_MATERIAL).ticksRandomly()) val TOXIC_MUD_IDENTIFIER = identifier("toxic_mud") val TOXIC_MUD_STILL: BaseFluid.Still = BaseFluid.Still(TOXIC_MUD_IDENTIFIER, { TOXIC_MUD }, { TOXIC_MUD_BUCKET }, 0x5c3b0e) { TOXIC_MUD_FLOWING } val TOXIC_MUD_FLOWING = BaseFluid.Flowing(TOXIC_MUD_IDENTIFIER, { TOXIC_MUD }, { TOXIC_MUD_BUCKET }, 0x5c3b0e) { TOXIC_MUD_STILL } val TOXIC_MUD_BUCKET = BucketItem(TOXIC_MUD_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val TOXIC_MUD = object : FluidBlock(TOXIC_MUD_STILL, FabricBlockSettings.of(MUD_MATERIAL)) {} val HYDROGEN_IDENTIFIER = identifier("hydrogen") val HYDROGEN_STILL: BaseFluid.Still = BaseFluid.Still(HYDROGEN_IDENTIFIER, { HYDROGEN }, { HYDROGEN_BUCKET }, -1) { HYDROGEN_FLOWING } val HYDROGEN_FLOWING = BaseFluid.Flowing(HYDROGEN_IDENTIFIER, { HYDROGEN }, { HYDROGEN_BUCKET }, -1) { HYDROGEN_STILL } val HYDROGEN_BUCKET = BucketItem(HYDROGEN_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val HYDROGEN = object : FluidBlock(HYDROGEN_STILL, FabricBlockSettings.of(Material.LAVA)) {} val OXYGEN_IDENTIFIER = identifier("oxygen") val OXYGEN_STILL: BaseFluid.Still = BaseFluid.Still(OXYGEN_IDENTIFIER, { OXYGEN }, { OXYGEN_BUCKET }, 0xfcfccf) { OXYGEN_FLOWING } val OXYGEN_FLOWING = BaseFluid.Flowing(OXYGEN_IDENTIFIER, { OXYGEN }, { OXYGEN_BUCKET }, 0xfcfccf) { OXYGEN_STILL } val OXYGEN_BUCKET = BucketItem(OXYGEN_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val OXYGEN = object : FluidBlock(OXYGEN_STILL, FabricBlockSettings.of(Material.LAVA)) {} val METHANE_IDENTIFIER = identifier("methane") val METHANE_STILL: BaseFluid.Still = BaseFluid.Still(METHANE_IDENTIFIER, { METHANE }, { METHANE_BUCKET }, 0xe8ffbf) { METHANE_FLOWING } val METHANE_FLOWING = BaseFluid.Flowing(METHANE_IDENTIFIER, { METHANE }, { METHANE_BUCKET }, 0xe8ffbf) { METHANE_STILL } val METHANE_BUCKET = BucketItem(METHANE_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val METHANE = object : FluidBlock(METHANE_STILL, FabricBlockSettings.of(Material.LAVA)) {} val STEAM_IDENTIFIER = identifier("steam") val STEAM_STILL: BaseFluid.Still = BaseFluid.Still(STEAM_IDENTIFIER, { STEAM }, { STEAM_BUCKET }, -1) { STEAM_FLOWING } val STEAM_FLOWING = BaseFluid.Flowing(STEAM_IDENTIFIER, { STEAM }, { STEAM_BUCKET }, -1) { STEAM_STILL } val STEAM_BUCKET = BucketItem(STEAM_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1)) val STEAM = object : FluidBlock(STEAM_STILL, FabricBlockSettings.of(Material.WATER)) {} } ================================================ FILE: src/main/kotlin/me/steven/indrev/registry/IRItemRegistry.kt ================================================ package me.steven.indrev.registry import me.steven.indrev.api.machines.Tier import me.steven.indrev.armor.IRArmorMaterial import me.steven.indrev.blocks.misc.NikoliteOreBlock import me.steven.indrev.items.armor.* import me.steven.indrev.items.energy.* import me.steven.indrev.items.misc.* import me.steven.indrev.items.upgrade.Enhancer import me.steven.indrev.items.upgrade.IREnhancerItem import me.steven.indrev.networks.EndpointData import me.steven.indrev.tools.IRToolMaterial import me.steven.indrev.tools.modular.ArmorModule import me.steven.indrev.tools.modular.DrillModule import me.steven.indrev.tools.modular.GamerAxeModule import me.steven.indrev.tools.modular.MiningToolModule import me.steven.indrev.utils.* import net.minecraft.client.item.TooltipContext import net.minecraft.entity.EquipmentSlot import net.minecraft.item.* import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.ActionResult import net.minecraft.util.Formatting import net.minecraft.util.Rarity import net.minecraft.util.registry.Registry import net.minecraft.world.World import team.reborn.energy.api.EnergyStorage import team.reborn.energy.impl.SimpleItemEnergyStorageImpl @Suppress("MemberVisibilityCanBePrivate") object IRItemRegistry { fun registerAll() { identifier("guide_book").item(GUIDE_BOOK) MaterialHelper("tin") { withItems("dust", "ingot", "plate", "nugget", "chunk", "purified_ore") withBlock() withOre() withTools( IRBasicPickaxe(IRToolMaterial.TIN, 1, -1.0f, itemSettings()), IRBasicAxe(IRToolMaterial.TIN, 4.5f, -2.0f, itemSettings()), IRBasicShovel(IRToolMaterial.TIN, 1.5f, -0.7f, itemSettings()), IRBasicSword(IRToolMaterial.TIN, 3, -1.5f, itemSettings()), IRBasicHoe(IRToolMaterial.TIN, 2, -0.5f, itemSettings()) ) withArmor(IRArmorMaterial.TIN) }.register() MaterialHelper("copper") { withItems("dust", "plate", "nugget", "chunk", "purified_ore") withTools( IRBasicPickaxe(IRToolMaterial.COPPER, 1, -1.0f, itemSettings()), IRBasicAxe(IRToolMaterial.COPPER, 4.5f, -2.0f, itemSettings()), IRBasicShovel(IRToolMaterial.COPPER, 1.5f, -0.7f, itemSettings()), IRBasicSword(IRToolMaterial.COPPER, 3, -1.5f, itemSettings()), IRBasicHoe(IRToolMaterial.COPPER, 2, -0.5f, itemSettings()) ) withArmor(IRArmorMaterial.COPPER) }.register() MaterialHelper("steel") { withItems("dust", "ingot", "plate", "nugget") withBlock() withTools( IRBasicPickaxe(IRToolMaterial.STEEL, 2, -1.8f, itemSettings()), IRBasicAxe(IRToolMaterial.STEEL, 7f, -3.1f, itemSettings()), IRBasicShovel(IRToolMaterial.STEEL, 1.0f, -1.0f, itemSettings()), IRBasicSword(IRToolMaterial.STEEL, 5, -2.0f, itemSettings()), IRBasicHoe(IRToolMaterial.STEEL, 3, -1.5f, itemSettings()) ) withArmor(IRArmorMaterial.STEEL) }.register() MaterialHelper("iron") { withItems("dust", "plate", "chunk", "purified_ore") }.register() MaterialHelper("netherite_scrap") { withItems("dust", "chunk", "purified_ore") }.register() MaterialHelper("nikolite") { withItems("dust", "ingot") withOre(false) { settings -> NikoliteOreBlock(settings) } }.register() MaterialHelper("enriched_nikolite") { withItems("dust", "ingot") }.register() MaterialHelper("diamond") { withItems("dust") }.register() MaterialHelper("gold") { withItems("dust", "plate", "chunk", "purified_ore") }.register() MaterialHelper("coal") { withItems("dust") }.register() MaterialHelper("sulfur") { withItems("dust") }.register() MaterialHelper("lead") { withItems("dust", "ingot", "plate", "nugget", "chunk", "purified_ore") withBlock() withOre() withTools( IRBasicPickaxe(IRToolMaterial.LEAD, 1, -1.0f, itemSettings()), IRBasicAxe(IRToolMaterial.LEAD, 3.8f, -2.9f, itemSettings()), IRBasicShovel(IRToolMaterial.LEAD, 1.5f, -0.7f, itemSettings()), IRBasicSword(IRToolMaterial.LEAD, 3, -1.5f, itemSettings()), IRBasicHoe(IRToolMaterial.LEAD, 2, -0.5f, itemSettings()) ) withArmor(IRArmorMaterial.LEAD) }.register() MaterialHelper("bronze") { withItems("dust", "ingot", "plate", "nugget") withBlock() withTools( IRBasicPickaxe(IRToolMaterial.BRONZE, 1, -1.0f, itemSettings()), IRBasicAxe(IRToolMaterial.BRONZE, 5f, -2.0f, itemSettings()), IRBasicShovel(IRToolMaterial.BRONZE, 1.5f, -0.7f, itemSettings()), IRBasicSword(IRToolMaterial.BRONZE, 3, -1.5f, itemSettings()), IRBasicHoe(IRToolMaterial.BRONZE, 2, -0.5f, itemSettings()) ) withArmor(IRArmorMaterial.BRONZE) }.register() MaterialHelper("silver") { withItems("dust", "ingot", "plate", "nugget", "chunk", "purified_ore") withBlock() withOre() withTools( IRBasicPickaxe(IRToolMaterial.SILVER, 1, -1.0f, itemSettings()), IRBasicAxe(IRToolMaterial.SILVER, 3.5f, -2.5f, itemSettings()), IRBasicShovel(IRToolMaterial.SILVER, 1.5f, -0.7f, itemSettings()), IRBasicSword(IRToolMaterial.SILVER, 3, -1.5f, itemSettings()), IRBasicHoe(IRToolMaterial.SILVER, 2, -0.5f, itemSettings()) ) withArmor(IRArmorMaterial.SILVER) }.register() MaterialHelper("tungsten") { withItems("dust", "ingot", "plate", "nugget", "purified_ore") withBlock() withOre() }.register() MaterialHelper("electrum") { withItems("dust", "ingot", "plate", "nugget") withBlock() }.register() MaterialHelper.register() identifier("soot").item(SOOT) identifier("carbon_fiber_plate").item(DEFAULT_ITEM()) identifier("carbon_fiber_rod").item(DEFAULT_ITEM()) identifier("sawdust").item(DEFAULT_ITEM()) identifier("hammer").item(HAMMER) identifier("stone_drill_head").item(STONE_DRILL_HEAD) identifier("iron_drill_head").item(IRON_DRILL_HEAD) identifier("diamond_drill_head").item(DIAMOND_DRILL_HEAD) identifier("netherite_drill_head").item(NETHERITE_DRILL_HEAD) identifier("mining_drill_mk1").item(MINING_DRILL_MK1) identifier("mining_drill_mk2").item(MINING_DRILL_MK2) identifier("mining_drill_mk3").item(MINING_DRILL_MK3) identifier("mining_drill_mk4").item(MINING_DRILL_MK4) EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, 4000, Tier.MK1.io, Tier.MK1.io) }, MINING_DRILL_MK1) EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, 8000, Tier.MK2.io, Tier.MK2.io) }, MINING_DRILL_MK2) EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, 16000, Tier.MK3.io, Tier.MK3.io) }, MINING_DRILL_MK3) EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, 32000, Tier.MK4.io, Tier.MK4.io) }, MINING_DRILL_MK4) identifier("battery").item(BATTERY) identifier("circuit_mk1").item(DEFAULT_ITEM()) identifier("circuit_mk2").item(DEFAULT_ITEM()) identifier("circuit_mk3").item(DEFAULT_ITEM()) identifier("circuit_mk4").item(DEFAULT_ITEM()) identifier("fan").item(FAN) identifier("cooler_cell").item(COOLER_CELL) identifier("heatsink").item(HEATSINK) identifier("heat_coil").item(HEAT_COIL) identifier("ore_data_card").item(ORE_DATA_CARD) identifier("empty_enhancer").item(DEFAULT_ITEM()) identifier("buffer_enhancer").item(BUFFER_ENHANCER) identifier("speed_enhancer").item(SPEED_UPGRADE) identifier("blast_furnace_enhancer").item(BLAST_FURNACE_UPGRADE) identifier("smoker_enhancer").item(SMOKER_UPGRADE) identifier("damage_enhancer").item(DAMAGE_UPGRADE) identifier("energy_reader").item(ENERGY_READER) identifier("tier_upgrade_mk2").item(TIER_UPGRADE_MK2) identifier("tier_upgrade_mk3").item(TIER_UPGRADE_MK3) identifier("tier_upgrade_mk4").item(TIER_UPGRADE_MK4) identifier("biomass").item(BIOMASS) identifier("untanned_leather").item(DEFAULT_ITEM()) identifier("wrench").item(WRENCH) identifier("screwdriver").item(SCREWDRIVER) identifier("jetpack_mk1").item(JETPACK_MK1) identifier("jetpack_mk2").item(JETPACK_MK2) identifier("jetpack_mk3").item(JETPACK_MK3) identifier("jetpack_mk4").item(JETPACK_MK4) identifier("carbon_fiber_helmet_frame").item(DEFAULT_ITEM()) identifier("carbon_fiber_chest_frame").item(DEFAULT_ITEM()) identifier("carbon_fiber_legs_frame").item(DEFAULT_ITEM()) identifier("carbon_fiber_boots_frame").item(DEFAULT_ITEM()) identifier("modular_armor_helmet").item(MODULAR_ARMOR_HELMET) identifier("modular_armor_chest").item(MODULAR_ARMOR_CHEST) identifier("modular_armor_legs").item(MODULAR_ARMOR_LEGGINGS) identifier("modular_armor_boots").item(MODULAR_ARMOR_BOOTS) identifier("module_protection").item(PROTECTION_MODULE_ITEM) identifier("module_speed").item(SPEED_MODULE_ITEM) identifier("module_jump_boost").item(JUMP_BOOST_MODULE_ITEM) identifier("module_night_vision").item(NIGHT_VISION_MODULE_ITEM) identifier("module_breathing").item(BREATHING_MODULE_ITEM) identifier("module_feather_falling").item(FEATHER_FALLING_MODULE_ITEM) identifier("module_auto_feeder").item(AUTO_FEEDER_MODULE_ITEM) identifier("module_charger").item(CHARGER_MODULE_ITEM) identifier("module_solar_panel").item(SOLAR_PANEL_MODULE_ITEM) identifier("module_piglin_tricker").item(PIGLIN_TRICKER_MODULE_ITEM) identifier("module_elytra").item(ELYTRA_MODULE_ITEM) identifier("module_jetpack").item(JETPACK_MODULE_ITEM) identifier("module_magnet").item(MAGNET_MODULE) identifier("module_water_affinity").item(WATER_AFFINITY_MODULE) identifier("module_fire_resistance").item(FIRE_RESISTANCE_MODULE_ITEM) identifier("module_range").item(RANGE_MODULE_ITEM) identifier("module_efficiency").item(EFFICIENCY_MODULE_ITEM) identifier("module_fortune").item(FORTUNE_MODULE_ITEM) identifier("module_silk_touch").item(SILK_TOUCH_MODULE_ITEM) identifier("module_controlled_destruction").item(CONTROLLED_DESTRUCTION_MODULE_ITEM) identifier("module_matter_projector").item(MATTER_PROJECTOR_MODULE_ITEM) identifier("module_looting").item(LOOTING_MODULE_ITEM) identifier("module_fire_aspect").item(FIRE_ASPECT_MODULE_ITEM) identifier("module_sharpness").item(SHARPNESS_MODULE_ITEM) //identifier("module_reach").item(REACH_MODULE_ITEM) identifier("module_color_pink").item(PINK_MODULE_ITEM) identifier("module_color_red").item(RED_MODULE_ITEM) identifier("module_color_purple").item(PURPLE_MODULE_ITEM) identifier("module_color_blue").item(BLUE_MODULE_ITEM) identifier("module_color_cyan").item(CYAN_MODULE_ITEM) identifier("module_color_green").item(GREEN_MODULE_ITEM) identifier("module_color_yellow").item(YELLOW_MODULE_ITEM) identifier("module_color_orange").item(ORANGE_MODULE_ITEM) identifier("module_color_black").item(BLACK_MODULE_ITEM) identifier("module_color_brown").item(BROWN_MODULE_ITEM) identifier("portable_charger").item(PORTABLE_CHARGER_ITEM) identifier("gamer_axe").item(GAMER_AXE_ITEM) identifier("modular_core").item(MODULAR_CORE) identifier("modular_core_activated").item(MODULAR_CORE_ACTIVATED) identifier("fluid_pipe_mk1").item(FLUID_PIPE_ITEM_MK1) identifier("fluid_pipe_mk2").item(FLUID_PIPE_ITEM_MK2) identifier("fluid_pipe_mk3").item(FLUID_PIPE_ITEM_MK3) identifier("fluid_pipe_mk4").item(FLUID_PIPE_ITEM_MK4) identifier("item_pipe_mk1").item(ITEM_PIPE_ITEM_MK1) identifier("item_pipe_mk2").item(ITEM_PIPE_ITEM_MK2) identifier("item_pipe_mk3").item(ITEM_PIPE_ITEM_MK3) identifier("item_pipe_mk4").item(ITEM_PIPE_ITEM_MK4) identifier("cable_mk1").item(CABLE_ITEM_MK1) identifier("cable_mk2").item(CABLE_ITEM_MK2) identifier("cable_mk3").item(CABLE_ITEM_MK3) identifier("cable_mk4").item(CABLE_ITEM_MK4) identifier("servo_retriever").item(SERVO_RETRIEVER) identifier("servo_output").item(SERVO_OUTPUT) identifier("reinforced_elytra").item(REINFORCED_ELYTRA) } private val DEFAULT_ITEM: () -> Item = { Item(itemSettings()) } val GUIDE_BOOK = IRGuideBookItem(itemSettings()) val BATTERY = IRBatteryItem(itemSettings(), 4096) val HAMMER = IRCraftingToolItem(itemSettings().maxDamage(32)) val STEEL_INGOT = { Registry.ITEM.get(identifier("steel_ingot")) } val STEEL_PLATE = { Registry.ITEM.get(identifier("steel_plate")) } val COPPER_INGOT = { Registry.ITEM.get(identifier("copper_ingot")) } val TIN_INGOT = { Registry.ITEM.get(identifier("tin_ingot")) } val LEAD_INGOT = { Registry.ITEM.get(identifier("lead_ingot")) } val BRONZE_INGOT = { Registry.ITEM.get(identifier("bronze_ingot")) } val SILVER_INGOT = { Registry.ITEM.get(identifier("silver_ingot")) } val ENRICHED_NIKOLITE_DUST = { Registry.ITEM.get(identifier("enriched_nikolite_dust")) } val BIOMASS = DEFAULT_ITEM() val FAN = Item(itemSettings().maxDamage(128)) val COOLER_CELL = Item(itemSettings().maxDamage(512)) val HEATSINK = Item(itemSettings().maxDamage(1536)) val HEAT_COIL = object : Item(itemSettings().maxDamage(128)) { override fun appendTooltip( stack: ItemStack?, world: World?, tooltip: MutableList?, context: TooltipContext? ) { tooltip?.add(TranslatableText("item.indrev.heat_coil.tooltip").formatted(Formatting.BLUE)) } } val TIER_UPGRADE_MK2 = IRMachineUpgradeItem(itemSettings(), Tier.MK1, Tier.MK2) val TIER_UPGRADE_MK3 = IRMachineUpgradeItem(itemSettings(), Tier.MK2, Tier.MK3) val TIER_UPGRADE_MK4 = IRMachineUpgradeItem(itemSettings(), Tier.MK3, Tier.MK4) val MINING_DRILL_MK1 = IRMiningDrillItem(ToolMaterials.STONE, Tier.MK1, 4000.0, 6f, itemSettings()) val MINING_DRILL_MK2 = IRMiningDrillItem(ToolMaterials.IRON, Tier.MK2, 8000.0, 10f, itemSettings()) val MINING_DRILL_MK3 = IRMiningDrillItem(ToolMaterials.DIAMOND, Tier.MK3, 16000.0, 14f, itemSettings()) val MINING_DRILL_MK4 = IRModularDrillItem( ToolMaterials.NETHERITE, Tier.MK4, 32000.0, 16f, itemSettings().fireproof() ) val ORE_DATA_CARD = OreDataCardItem() val ENERGY_READER = IREnergyReaderItem(itemSettings()) val SOOT = DEFAULT_ITEM() val SULFUR_CRYSTAL_ITEM = DEFAULT_ITEM() val STONE_DRILL_HEAD = Item(itemSettings().maxDamage(256)) val IRON_DRILL_HEAD = Item(itemSettings().maxDamage(1024)) val DIAMOND_DRILL_HEAD = Item(itemSettings().maxDamage(2048)) val NETHERITE_DRILL_HEAD = Item(itemSettings().maxDamage(4096)) val BUFFER_ENHANCER = IREnhancerItem(itemSettings().maxCount(32), Enhancer.BUFFER) val SPEED_UPGRADE = IREnhancerItem(itemSettings().maxCount(32), Enhancer.SPEED) val BLAST_FURNACE_UPGRADE = IREnhancerItem(itemSettings().maxCount(1), Enhancer.BLAST_FURNACE) val SMOKER_UPGRADE = IREnhancerItem(itemSettings().maxCount(1), Enhancer.SMOKER) val DAMAGE_UPGRADE = IREnhancerItem(itemSettings().maxCount(1), Enhancer.DAMAGE) val WRENCH = object : Item(itemSettings().maxCount(1)) { override fun useOnBlock(context: ItemUsageContext): ActionResult { val state = context.world.getBlockState(context.blockPos) val blockEntity = context.world.getBlockEntity(context.blockPos) return wrench(context.world, context.blockPos, state, blockEntity, context.player, context.stack) } } val SCREWDRIVER = object : Item(itemSettings().maxCount(1)) { override fun useOnBlock(context: ItemUsageContext): ActionResult { val blockEntity = context.world.getBlockEntity(context.blockPos) val state = context.world.getBlockState(context.blockPos) return screwdriver(context.world, context.blockPos, state, blockEntity, context.player, context.stack) } } val JETPACK_MK1 = JetpackItem(Tier.MK1) val JETPACK_MK2 = JetpackItem(Tier.MK2) val JETPACK_MK3 = JetpackItem(Tier.MK3) val JETPACK_MK4 = JetpackItem(Tier.MK4) val MODULAR_ARMOR_HELMET = IRModularArmorItem(EquipmentSlot.HEAD, 250000, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler)) val MODULAR_ARMOR_CHEST = IRModularArmorItem(EquipmentSlot.CHEST, 250000, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler)) val MODULAR_ARMOR_LEGGINGS = IRModularArmorItem(EquipmentSlot.LEGS, 250000, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler)) val MODULAR_ARMOR_BOOTS = IRModularArmorItem(EquipmentSlot.FEET, 250000, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler)) val PROTECTION_MODULE_ITEM = IRModuleItem(ArmorModule.PROTECTION, itemSettings().maxCount(1)) val SPEED_MODULE_ITEM = IRModuleItem(ArmorModule.SPEED, itemSettings().maxCount(1)) val JUMP_BOOST_MODULE_ITEM = IRModuleItem(ArmorModule.JUMP_BOOST, itemSettings().maxCount(1)) val BREATHING_MODULE_ITEM = IRModuleItem(ArmorModule.BREATHING, itemSettings().maxCount(1)) val NIGHT_VISION_MODULE_ITEM = IRModuleItem(ArmorModule.NIGHT_VISION, itemSettings().maxCount(1)) val FEATHER_FALLING_MODULE_ITEM = IRModuleItem(ArmorModule.FEATHER_FALLING, itemSettings().maxCount(1)) val AUTO_FEEDER_MODULE_ITEM = IRModuleItem(ArmorModule.AUTO_FEEDER, itemSettings().maxCount(1)) val CHARGER_MODULE_ITEM = IRModuleItem(ArmorModule.CHARGER, itemSettings().maxCount(1)) val SOLAR_PANEL_MODULE_ITEM = IRModuleItem(ArmorModule.SOLAR_PANEL, itemSettings().maxCount(1)) val PIGLIN_TRICKER_MODULE_ITEM = IRModuleItem(ArmorModule.PIGLIN_TRICKER, itemSettings().maxCount(1)) val ELYTRA_MODULE_ITEM = IRModuleItem(ArmorModule.ELYTRA, itemSettings().maxCount(1)) val JETPACK_MODULE_ITEM = IRModuleItem(ArmorModule.JETPACK, itemSettings().maxCount(1)) val MAGNET_MODULE = IRModuleItem(ArmorModule.MAGNET, itemSettings().maxCount(1)) val WATER_AFFINITY_MODULE = IRModuleItem(ArmorModule.WATER_AFFINITY, itemSettings().maxCount(1)) val FIRE_RESISTANCE_MODULE_ITEM = IRModuleItem(ArmorModule.FIRE_RESISTANCE, itemSettings().maxCount(1)) val SILK_TOUCH_MODULE_ITEM = IRModuleItem(DrillModule.SILK_TOUCH, itemSettings().maxCount(1)) val CONTROLLED_DESTRUCTION_MODULE_ITEM = IRModuleItem(DrillModule.CONTROLLED_DESTRUCTION, itemSettings().maxCount(1)) val MATTER_PROJECTOR_MODULE_ITEM = IRModuleItem(DrillModule.MATTER_PROJECTOR, itemSettings().maxCount(1)) val FORTUNE_MODULE_ITEM = IRModuleItem(DrillModule.FORTUNE, itemSettings().maxCount(1)) val RANGE_MODULE_ITEM = IRModuleItem(DrillModule.RANGE, itemSettings().maxCount(1)) // val REACH_MODULE_ITEM = IRModuleItem(GamerAxeModule.REACH, itemSettings().maxCount(1)) val EFFICIENCY_MODULE_ITEM = IRModuleItem(MiningToolModule.EFFICIENCY, itemSettings().maxCount(1)) val LOOTING_MODULE_ITEM = IRModuleItem(GamerAxeModule.LOOTING, itemSettings().maxCount(1)) val FIRE_ASPECT_MODULE_ITEM = IRModuleItem(GamerAxeModule.FIRE_ASPECT, itemSettings().maxCount(1)) val SHARPNESS_MODULE_ITEM = IRModuleItem(GamerAxeModule.SHARPNESS, itemSettings().maxCount(1)) val PINK_MODULE_ITEM = IRColorModuleItem(0xFF74DD, itemSettings().maxCount(1)) val RED_MODULE_ITEM = IRColorModuleItem(0xFF747C, itemSettings().maxCount(1)) val PURPLE_MODULE_ITEM = IRColorModuleItem(0xD174FF, itemSettings().maxCount(1)) val BLUE_MODULE_ITEM = IRColorModuleItem(0x7974FF, itemSettings().maxCount(1)) val CYAN_MODULE_ITEM = IRColorModuleItem(0x74FFFC, itemSettings().maxCount(1)) val GREEN_MODULE_ITEM = IRColorModuleItem(0x7BFF74, itemSettings().maxCount(1)) val YELLOW_MODULE_ITEM = IRColorModuleItem(0xF7FF74, itemSettings().maxCount(1)) val ORANGE_MODULE_ITEM = IRColorModuleItem(0xFFA674, itemSettings().maxCount(1)) val BLACK_MODULE_ITEM = IRColorModuleItem(0x424242, itemSettings().maxCount(1)) val BROWN_MODULE_ITEM = IRColorModuleItem(0x935F42, itemSettings().maxCount(1)) val PORTABLE_CHARGER_ITEM = IRPortableChargerItem(itemSettings(), 250000) val GAMER_AXE_ITEM = IRGamerAxeItem(ToolMaterials.NETHERITE, 10000, Tier.MK4, 4f, -2f, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler)) val TANK_BLOCK_ITEM = BlockItem(IRBlockRegistry.TANK_BLOCK, itemSettings()) val CAPSULE_BLOCK_ITEM = BlockItem(IRBlockRegistry.CAPSULE_BLOCK, itemSettings().maxCount(1)) val MODULAR_CORE: Item = object : Item(itemSettings().maxCount(1)), IREnergyItem { override fun appendTooltip( stack: ItemStack?, world: World?, tooltip: MutableList?, context: TooltipContext? ) { buildEnergyTooltip(stack, tooltip) } } val MODULAR_CORE_ACTIVATED = object : Item(itemSettings().maxCount(1)) { override fun hasGlint(stack: ItemStack?): Boolean = true } val FLUID_PIPE_ITEM_MK1 = BlockItem(IRBlockRegistry.FLUID_PIPE_MK1, itemSettings()) val FLUID_PIPE_ITEM_MK2 = BlockItem(IRBlockRegistry.FLUID_PIPE_MK2, itemSettings()) val FLUID_PIPE_ITEM_MK3 = BlockItem(IRBlockRegistry.FLUID_PIPE_MK3, itemSettings()) val FLUID_PIPE_ITEM_MK4 = BlockItem(IRBlockRegistry.FLUID_PIPE_MK4, itemSettings()) val ITEM_PIPE_ITEM_MK1 = BlockItem(IRBlockRegistry.ITEM_PIPE_MK1, itemSettings()) val ITEM_PIPE_ITEM_MK2 = BlockItem(IRBlockRegistry.ITEM_PIPE_MK2, itemSettings()) val ITEM_PIPE_ITEM_MK3 = BlockItem(IRBlockRegistry.ITEM_PIPE_MK3, itemSettings()) val ITEM_PIPE_ITEM_MK4 = BlockItem(IRBlockRegistry.ITEM_PIPE_MK4, itemSettings()) val CABLE_ITEM_MK1 = BlockItem(IRBlockRegistry.CABLE_MK1, itemSettings()) val CABLE_ITEM_MK2 = BlockItem(IRBlockRegistry.CABLE_MK2, itemSettings()) val CABLE_ITEM_MK3 = BlockItem(IRBlockRegistry.CABLE_MK3, itemSettings()) val CABLE_ITEM_MK4 = BlockItem(IRBlockRegistry.CABLE_MK4, itemSettings()) val SERVO_RETRIEVER = IRServoItem(itemSettings().maxCount(16), EndpointData.Type.RETRIEVER) val SERVO_OUTPUT = IRServoItem(itemSettings().maxCount(16), EndpointData.Type.OUTPUT) val REINFORCED_ELYTRA = ReinforcedElytraItem() } ================================================ FILE: src/main/kotlin/me/steven/indrev/registry/IRModelManagers.kt ================================================ package me.steven.indrev.registry import me.steven.indrev.api.machines.Tier import me.steven.indrev.blocks.machine.DrillHeadModel import me.steven.indrev.blocks.models.PumpPipeBakedModel import me.steven.indrev.blocks.models.pipes.CableModel import me.steven.indrev.blocks.models.pipes.FluidPipeModel import me.steven.indrev.blocks.models.pipes.ItemPipeModel import me.steven.indrev.items.models.TankItemBakedModel import me.steven.indrev.utils.EmptyModel import me.steven.indrev.utils.hide import me.steven.indrev.utils.identifier import net.fabricmc.fabric.api.client.model.ExtraModelProvider import net.fabricmc.fabric.api.client.model.ModelProviderContext import net.fabricmc.fabric.api.client.model.ModelVariantProvider import net.minecraft.client.render.model.UnbakedModel import net.minecraft.client.util.ModelIdentifier import net.minecraft.resource.ResourceManager import net.minecraft.util.Identifier import java.util.function.Consumer object IRModelManagers : ModelVariantProvider, ExtraModelProvider { private val CABLE_MODELS = arrayOf( CableModel(Tier.MK1), CableModel(Tier.MK2), CableModel(Tier.MK3), CableModel(Tier.MK4) ) private val ITEM_PIPE_MODELS = arrayOf( ItemPipeModel(Tier.MK1), ItemPipeModel(Tier.MK2), ItemPipeModel(Tier.MK3), ItemPipeModel(Tier.MK4) ) private val FLUID_PIPE_MODELS = arrayOf( FluidPipeModel(Tier.MK1), FluidPipeModel(Tier.MK2), FluidPipeModel(Tier.MK3), FluidPipeModel(Tier.MK4) ) override fun loadModelVariant(resourceId: ModelIdentifier, ctx: ModelProviderContext?): UnbakedModel? { if (resourceId.namespace != "indrev") return null val path = resourceId.path val variant = resourceId.variant val id = Identifier(resourceId.namespace, resourceId.path) return when { hide(id) -> EmptyModel path == "drill_head" -> DrillHeadModel(resourceId.variant) path == "pump_pipe" -> PumpPipeBakedModel() path == "tank" && variant == "inventory" -> TankItemBakedModel() path.startsWith("cable_mk") -> CABLE_MODELS[path.last().toString().toInt() - 1] path.startsWith("item_pipe_mk") -> ITEM_PIPE_MODELS[path.last().toString().toInt() - 1] path.startsWith("fluid_pipe_mk") -> FLUID_PIPE_MODELS[path.last().toString().toInt() - 1] MachineRegistry.MAP.containsKey(id) -> MachineRegistry.MAP[id]?.modelProvider?.get(Tier.values()[(path.last().toString().toIntOrNull() ?: 4) - 1])?.invoke(id.path.replace("_creative", "_mk4")) else -> return null } } override fun provideExtraModels(manager: ResourceManager?, out: Consumer) { out.accept(ModelIdentifier(identifier("drill_head"), "stone")) out.accept(ModelIdentifier(identifier("drill_head"), "iron")) out.accept(ModelIdentifier(identifier("drill_head"), "diamond")) out.accept(ModelIdentifier(identifier("drill_head"), "netherite")) out.accept(ModelIdentifier(identifier("pump_pipe"), "")) out.accept(ModelIdentifier(identifier("composting"), "")) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/registry/MachineRegistry.kt ================================================ package me.steven.indrev.registry import me.steven.indrev.api.machines.Tier import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blockentities.crafters.* import me.steven.indrev.blockentities.farms.* import me.steven.indrev.blockentities.generators.* import me.steven.indrev.blockentities.laser.LaserBlockEntity import me.steven.indrev.blockentities.miningrig.DataCardWriterBlockEntity import me.steven.indrev.blockentities.miningrig.MiningRigBlockEntity import me.steven.indrev.blockentities.modularworkbench.ModularWorkbenchBlockEntity import me.steven.indrev.blockentities.storage.ChargePadBlockEntity import me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity import me.steven.indrev.blocks.machine.* import me.steven.indrev.blocks.machine.solarpowerplant.SteamTurbineBlock import me.steven.indrev.blocks.models.LazuliFluxContainerBakedModel import me.steven.indrev.blocks.models.MachineBakedModel import me.steven.indrev.blocks.models.MinerBakedModel import me.steven.indrev.config.IConfig import me.steven.indrev.config.IRConfig import me.steven.indrev.gui.screenhandlers.machines.* import me.steven.indrev.items.energy.MachineBlockItem import me.steven.indrev.utils.* import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap import net.fabricmc.fabric.api.client.rendereregistry.v1.BlockEntityRendererRegistry import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.storage.Storage import net.fabricmc.loader.api.FabricLoader import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Material import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityType import net.minecraft.client.render.RenderLayer import net.minecraft.client.render.block.entity.BlockEntityRenderer import net.minecraft.client.render.model.UnbakedModel import net.minecraft.client.util.SpriteIdentifier import net.minecraft.item.BlockItem import net.minecraft.screen.PlayerScreenHandler import net.minecraft.sound.BlockSoundGroup import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import team.reborn.energy.api.EnergyStorage import java.util.* class MachineRegistry(private val key: String, val upgradeable: Boolean = true, vararg val tiers: Tier = Tier.values()) { private val configs: MutableMap = EnumMap(Tier::class.java) private val blocks: MutableMap = EnumMap(Tier::class.java) val blockEntities: MutableMap> = EnumMap(Tier::class.java) @Environment(EnvType.CLIENT) val modelProvider: MutableMap UnbakedModel?> = EnumMap(Tier::class.java) fun blockProvider(blockProvider: MachineRegistry.(Tier) -> Block): MachineRegistry { tiers.forEach { tier -> val block = blockProvider(this, tier) if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) BlockRenderLayerMap.INSTANCE.putBlock(block, RenderLayer.getCutout()) val blockItem = if (block is MachineBlock) MachineBlockItem(block, itemSettings()) else BlockItem(block, itemSettings()) identifier("${key}_${tier.id}").apply { block(block) item(blockItem) if (block is MachineBlock) { MAP[this] = this@MachineRegistry if (block.config != null) configs[tier] = block.config } } blocks[tier] = block } return this } fun blockEntityProvider(entityProvider: (Tier) -> (BlockPos, BlockState) -> BlockEntity): MachineRegistry { tiers.forEach { tier -> val blockEntityType = FabricBlockEntityTypeBuilder.create(entityProvider(tier), block(tier)).build(null) identifier("${key}_${tier.id}").apply { blockEntityType(blockEntityType) } blockEntities[tier] = blockEntityType } return this } fun forEachBlockEntity(action: (Tier, BlockEntityType<*>) -> Unit) = blockEntities.forEach(action) fun forEachBlock(action: (Tier, Block) -> Unit) = blocks.forEach(action) fun blockEntityType(tier: Tier) = blockEntities[tier] ?: throw IllegalStateException("invalid tier for machine $key") fun config(tier: Tier) = configs[tier] ?: throw java.lang.IllegalStateException("invalid tier for machine $key") fun block(tier: Tier) = blocks[tier] ?: throw java.lang.IllegalStateException("invalid tier for machine $key") fun modelProvider(provider: (Tier) -> (String) -> UnbakedModel?): MachineRegistry { if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) { tiers.forEach { tier -> modelProvider[tier] = provider(tier) } } return this } fun defaultModelProvider(hasWorkingState: Boolean = true): MachineRegistry { if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) modelProvider { tier -> { id -> MachineBakedModel(id).also { it.tierOverlay(tier) if (hasWorkingState) it.workingOverlayIds.add( SpriteIdentifier( PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier("block/${id.replace(Regex("_mk[0-4]"), "")}_on") ) ) } } } return this } fun noModelProvider(): MachineRegistry { if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) modelProvider { { null } } return this } fun energyProvider(provider: (Tier) -> (BlockEntity, Direction) -> EnergyStorage?): MachineRegistry { blockEntities.forEach { (tier, type) -> EnergyStorage.SIDED.registerForBlockEntities(provider(tier), type) } return this } fun defaultEnergyProvider(): MachineRegistry = energyProvider { { be, side -> (be as? MachineBlockEntity<*>)?.storage?.getSideStorage(side) } } fun fluidStorageProvider(provider: (Tier) -> (BlockEntity, Direction) -> Storage?): MachineRegistry { blockEntities.forEach { (tier, type) -> FluidStorage.SIDED.registerForBlockEntities(provider(tier), type) } return this } fun defaultFluidStorageProvider(): MachineRegistry { blockEntities.forEach { (_, type) -> FluidStorage.SIDED.registerForBlockEntities({ be, dir -> (be as MachineBlockEntity<*>).fluidComponent?.getCachedSide(dir) }, type) } return this } @Suppress("UNCHECKED_CAST") @Environment(EnvType.CLIENT) fun registerBlockEntityRenderer(renderer: () -> BlockEntityRenderer) { blockEntities.forEach { (_, type) -> BlockEntityRendererRegistry.INSTANCE.register(type as BlockEntityType) { _ -> renderer() } } } @Environment(EnvType.CLIENT) fun setRenderLayer(layer: RenderLayer) { blocks.forEach { (_, block) -> BlockRenderLayerMap.INSTANCE.putBlock(block, layer) } } companion object { val MAP = hashMapOf() private val SETTINGS = { FabricBlockSettings.of(Material.METAL) .sounds(BlockSoundGroup.METAL) .requiresTool() .strength(5.0f, 6.0f) } val COAL_GENERATOR_REGISTRY = MachineRegistry("coal_generator", false, Tier.MK1) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.generators.coalGenerator, ::CoalGeneratorScreenHandler ) } .blockEntityProvider { { pos, state -> CoalGeneratorBlockEntity(pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val SOLAR_GENERATOR_REGISTRY = MachineRegistry("solar_generator", false, Tier.MK1, Tier.MK3) .blockProvider { tier -> MachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.generators.solarGeneratorMk1 else -> IRConfig.generators.solarGeneratorMk3 }, ::SolarGeneratorScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> SolarGeneratorBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val BIOMASS_GENERATOR_REGISTRY = MachineRegistry("biomass_generator", false, Tier.MK3) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.generators.biomassGenerator, ::BiomassGeneratorScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> BiomassGeneratorBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val HEAT_GENERATOR_REGISTRY = MachineRegistry("heat_generator", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS().nonOpaque(), tier, IRConfig.generators.heatGenerator, ::HeatGeneratorScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> HeatGeneratorBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultFluidStorageProvider() .noModelProvider() val GAS_BURNING_GENERATOR_REGISTRY = MachineRegistry("gas_generator", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS().nonOpaque(), tier, IRConfig.generators.gasGenerator, ::GasBurningGeneratorScreenHandler ) } .blockEntityProvider { { pos, state -> GasBurningGeneratorBlockEntity(pos, state) } } .defaultEnergyProvider() .defaultFluidStorageProvider() .defaultModelProvider() val LAZULI_FLUX_CONTAINER_REGISTRY = MachineRegistry("lazuli_flux_container", false) .blockProvider { tier -> LazuliFluxContainerBlock(this, SETTINGS(), tier) } .blockEntityProvider { tier -> { pos, state -> LazuliFluxContainerBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .modelProvider { { id -> LazuliFluxContainerBakedModel(id) } } val ELECTRIC_FURNACE_REGISTRY = MachineRegistry("electric_furnace") .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.electricFurnaceMk1 Tier.MK2 -> IRConfig.machines.electricFurnaceMk2 Tier.MK3 -> IRConfig.machines.electricFurnaceMk3 else -> IRConfig.machines.electricFurnaceMk4 }, ::ElectricFurnaceScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> ElectricFurnaceBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .modelProvider { tier -> { id -> MachineBakedModel(id).also { it.workingOverlayIds.add(blockSpriteId("block/electric_furnace_emissive_on")) it.tierOverlay(tier) } } } val PULVERIZER_REGISTRY = MachineRegistry("pulverizer") .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.pulverizerMk1 Tier.MK2 -> IRConfig.machines.pulverizerMk2 Tier.MK3 -> IRConfig.machines.pulverizerMk3 else -> IRConfig.machines.pulverizerMk4 }, ::PulverizerScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> PulverizerBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val COMPRESSOR_REGISTRY = MachineRegistry("compressor") .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.compressorMk1 Tier.MK2 -> IRConfig.machines.compressorMk2 Tier.MK3 -> IRConfig.machines.compressorMk3 else -> IRConfig.machines.compressorMk4 }, ::CompressorScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> CompressorBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val SOLID_INFUSER_REGISTRY = MachineRegistry("solid_infuser") .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.infuserMk1 Tier.MK2 -> IRConfig.machines.infuserMk2 Tier.MK3 -> IRConfig.machines.infuserMk3 else -> IRConfig.machines.infuserMk4 }, ::SolidInfuserScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> SolidInfuserBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .modelProvider { tier -> { id -> MachineBakedModel(id).also { it.workingOverlayIds.add(blockSpriteId("block/solid_infuser_emissive_on")) it.tierOverlay(tier) } } } val SAWMILL_REGISTRY = MachineRegistry("sawmill") .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.sawmillMk1 Tier.MK2 -> IRConfig.machines.sawmillMk2 Tier.MK3 -> IRConfig.machines.sawmillMk3 else -> IRConfig.machines.sawmillMk4 }, ::SawmillScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> SawmillBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val RECYCLER_REGISTRY = MachineRegistry("recycler", false, Tier.MK2) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.machines.recycler, ::RecyclerScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> RecyclerBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val SMELTER_REGISTRY = MachineRegistry("smelter", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.machines.smelter, ::SmelterScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> SmelterBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultFluidStorageProvider() .defaultModelProvider(hasWorkingState = false) val CONDENSER_REGISTRY = MachineRegistry("condenser", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.machines.condenser, ::CondenserScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> CondenserBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultFluidStorageProvider() .defaultModelProvider() val ELECTRIC_FURNACE_FACTORY_REGISTRY = MachineRegistry("electric_furnace_factory", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.machines.electricFurnaceFactory, ::ElectricFurnaceFactoryScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> ElectricFurnaceFactoryBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .modelProvider { { id -> MachineBakedModel(id).also { it.baseSpriteId = blockSpriteId("block/electric_furnace") it.factoryOverlay() } } } val PULVERIZER_FACTORY_REGISTRY = MachineRegistry("pulverizer_factory", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.machines.pulverizerFactory, ::PulverizerFactoryScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> PulverizerFactoryBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .modelProvider { { id -> MachineBakedModel(id).also { it.baseSpriteId = blockSpriteId("block/pulverizer") it.factoryOverlay() } } } val COMPRESSOR_FACTORY_REGISTRY = MachineRegistry("compressor_factory", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.machines.compressorFactory, ::CompressorFactoryScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> CompressorFactoryBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .modelProvider { { id -> MachineBakedModel(id).also { it.baseSpriteId = blockSpriteId("block/compressor") it.overlayIds.add(blockSpriteId("block/factory_overlay_compressor")) } } } val SOLID_INFUSER_FACTORY_REGISTRY = MachineRegistry("solid_infuser_factory", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.machines.infuserFactory, ::SolidInfuserFactoryScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> SolidInfuserFactoryBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .modelProvider { { id -> MachineBakedModel(id).also { it.baseSpriteId = blockSpriteId("block/solid_infuser") it.factoryOverlay() } } } val DRAIN_REGISTRY = MachineRegistry("drain", false, Tier.MK1) .blockProvider { tier -> MachineBlock( this, SETTINGS(), tier, IRConfig.machines.drain, null ) } .blockEntityProvider { tier -> { pos, state -> DrainBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultFluidStorageProvider() .defaultModelProvider(hasWorkingState = false) val PUMP_REGISTRY = MachineRegistry("pump", false, Tier.MK1) .blockProvider { PumpBlock(this, SETTINGS().nonOpaque()) } .blockEntityProvider { tier -> { pos, state -> PumpBlockEntity(tier, pos, state) } } .energyProvider { { be, dir -> if (dir == Direction.UP) (be as? MachineBlockEntity<*>)?.storage?.getSideStorage(dir) else null } } .fluidStorageProvider { { be, dir -> if (be.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING] == dir) (be as PumpBlockEntity).fluidComponent else null } } .noModelProvider() val FLUID_INFUSER_REGISTRY = MachineRegistry("fluid_infuser", true) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.fluidInfuserMk1 Tier.MK2 -> IRConfig.machines.fluidInfuserMk2 Tier.MK3 -> IRConfig.machines.fluidInfuserMk3 else -> IRConfig.machines.fluidInfuserMk4 }, ::FluidInfuserScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> FluidInfuserBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultFluidStorageProvider() .defaultModelProvider() val ELECTROLYTIC_SEPARATOR_REGISTRY = MachineRegistry("electrolytic_separator", true) .blockProvider { tier -> ElectrolyticSeparatorBlock(this, SETTINGS(), tier) } .blockEntityProvider { tier -> { pos, state -> ElectrolyticSeparatorBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultFluidStorageProvider() .defaultModelProvider() val CHOPPER_REGISTRY = MachineRegistry("chopper", true) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.chopperMk1 Tier.MK2 -> IRConfig.machines.chopperMk2 Tier.MK3 -> IRConfig.machines.chopperMk3 else -> IRConfig.machines.chopperMk4 }, ::ChopperScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> ChopperBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val FARMER_REGISTRY = MachineRegistry("farmer", true) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.farmerMk1 Tier.MK2 -> IRConfig.machines.farmerMk2 Tier.MK3 -> IRConfig.machines.farmerMk3 else -> IRConfig.machines.farmerMk4 }, ::FarmerScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> FarmerBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider(hasWorkingState = false) val SLAUGHTER_REGISTRY = MachineRegistry("slaughter", true) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.slaughterMk1 Tier.MK2 -> IRConfig.machines.slaughterMk2 Tier.MK3 -> IRConfig.machines.slaughterMk3 else -> IRConfig.machines.slaughterMk4 }, ::SlaughterScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> SlaughterBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider(hasWorkingState = false) val RANCHER_REGISTRY = MachineRegistry("rancher", true) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK1 -> IRConfig.machines.rancherMk1 Tier.MK2 -> IRConfig.machines.rancherMk2 Tier.MK3 -> IRConfig.machines.rancherMk3 else -> IRConfig.machines.rancherMk4 }, ::RancherScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> RancherBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val MINING_RIG_REGISTRY = MachineRegistry("mining_rig", false, Tier.MK4) .blockProvider { tier -> MiningRigBlock(this, SETTINGS(), tier) } .blockEntityProvider { tier -> { pos, state -> MiningRigBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .modelProvider { { id -> MinerBakedModel(id) } } val DATA_CARD_WRITER_REGISTRY = MachineRegistry("data_card_writer", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, IRConfig.machines.dataCardWriter, ::DataCardWriterScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> DataCardWriterBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .defaultModelProvider() val FISHER_REGISTRY = MachineRegistry("fisher", false, Tier.MK2, Tier.MK3, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS(), tier, when (tier) { Tier.MK2 -> IRConfig.machines.fishingMk2 Tier.MK3 -> IRConfig.machines.fishingMk3 else -> IRConfig.machines.fishingMk4 }, ::FisherScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> FisherBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .noModelProvider() val DIRT_OXYGENATOR_REGISTRY = MachineRegistry("dirt_oxygenator", false, Tier.MK1) .blockProvider { DirtOxygenatorBlock(this, SETTINGS()) } .blockEntityProvider { { pos, state -> DirtOxygenatorBlockEntity(pos, state) } } .defaultEnergyProvider() .defaultFluidStorageProvider() .defaultModelProvider(hasWorkingState = false) val MODULAR_WORKBENCH_REGISTRY = MachineRegistry("modular_workbench", false, Tier.MK4) .blockProvider { tier -> HorizontalFacingMachineBlock( this, SETTINGS().nonOpaque(), tier, IRConfig.machines.modularWorkbench, ::ModularWorkbenchScreenHandler ) } .blockEntityProvider { tier -> { pos, state -> ModularWorkbenchBlockEntity(tier, pos, state) } } .defaultEnergyProvider() .noModelProvider() val CHARGE_PAD_REGISTRY = MachineRegistry("charge_pad", false, Tier.MK4) .blockProvider { tier -> ChargePadBlock(this, SETTINGS(), tier) } .blockEntityProvider { tier -> { pos, state -> ChargePadBlockEntity(tier, pos, state) } } .energyProvider { { be, dir -> if (dir == Direction.DOWN) (be as? ChargePadBlockEntity)?.energyIo else null } } .noModelProvider() val LASER_EMITTER_REGISTRY = MachineRegistry("laser_emitter", false, Tier.MK4) .blockProvider { LaserBlock(this, SETTINGS().nonOpaque()) } .blockEntityProvider { { pos, state -> LaserBlockEntity(pos, state) } } .energyProvider { { be, dir -> if (dir.opposite == be.cachedState[FacingMachineBlock.FACING]) (be as LaserBlockEntity).storage.getSideStorage(dir) else null } } .noModelProvider() val STEAM_TURBINE_REGISTRY = MachineRegistry("steam_turbine", false, Tier.MK4) .blockProvider { SteamTurbineBlock(this, SETTINGS().nonOpaque()) } .blockEntityProvider { { pos, state -> SteamTurbineBlockEntity(pos, state) } } .defaultFluidStorageProvider() .defaultModelProvider(true) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/registry/MaterialHelper.kt ================================================ package me.steven.indrev.registry import me.steven.indrev.utils.identifier import me.steven.indrev.utils.itemSettings import net.fabricmc.api.EnvType import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap import net.fabricmc.loader.api.FabricLoader import net.minecraft.block.Block import net.minecraft.block.Material import net.minecraft.client.render.RenderLayer import net.minecraft.entity.EquipmentSlot import net.minecraft.item.* import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry class MaterialHelper(private val id: String, private val block: MaterialHelper.() -> Unit) { fun withItems(vararg variants: String): MaterialHelper { variants.forEach { variant -> val identifier = identifier("${id}_$variant") map[identifier] = { Registry.register(Registry.ITEM, identifier, Item(itemSettings())) } } return this } fun withItem(): MaterialHelper { Registry.register(Registry.ITEM, identifier(id), Item(itemSettings())) return this } fun withOre(rawOre: Boolean = true, supplier: (FabricBlockSettings) -> Block = { Block(it) }): MaterialHelper { val ore = supplier(FabricBlockSettings.of(Material.STONE).requiresTool().strength(3f, 3f)) val identifier = identifier("${id}_ore") map[identifier] = { Registry.register(Registry.BLOCK, identifier, ore) Registry.register(Registry.ITEM, identifier, BlockItem(ore, itemSettings())) } val deepslateOre = supplier(FabricBlockSettings.of(Material.STONE).requiresTool().strength(3f, 3f)) val deepslateId = identifier("deepslate_${id}_ore") map[deepslateId] = { Registry.register(Registry.BLOCK, deepslateId, deepslateOre) Registry.register(Registry.ITEM, deepslateId, BlockItem(deepslateOre, itemSettings())) } if (rawOre) { val rawOreBlock = supplier(FabricBlockSettings.of(Material.STONE).requiresTool().strength(3f, 3f)) val rawOreId = identifier("raw_${id}") val rawOreBlockId = identifier("raw_${id}_block") map[rawOreId] = { Registry.register(Registry.BLOCK, rawOreBlockId, rawOreBlock) Registry.register(Registry.ITEM, rawOreBlockId, BlockItem(rawOreBlock, itemSettings())) Registry.register(Registry.ITEM, rawOreId, Item(itemSettings())) } } return this } fun withTools(pickaxe: PickaxeItem, axe: AxeItem, shovel: ShovelItem, sword: SwordItem, hoe: HoeItem) { map[identifier("${id}_pickaxe")] = { Registry.register(Registry.ITEM, identifier("${id}_pickaxe"), pickaxe) } map[identifier("${id}_axe")] = { Registry.register(Registry.ITEM, identifier("${id}_axe"), axe) } map[identifier("${id}_shovel")] = { Registry.register(Registry.ITEM, identifier("${id}_shovel"), shovel) } map[identifier("${id}_sword")] = { Registry.register(Registry.ITEM, identifier("${id}_sword"), sword) } map[identifier("${id}_hoe")] = { Registry.register(Registry.ITEM, identifier("${id}_hoe"), hoe) } } fun withArmor(material: ArmorMaterial) { map[identifier("${id}_helmet")] = { Registry.register(Registry.ITEM, identifier("${id}_helmet"), ArmorItem(material, EquipmentSlot.HEAD, itemSettings())) } map[identifier("${id}_chestplate")] = { Registry.register(Registry.ITEM, identifier("${id}_chestplate"), ArmorItem(material, EquipmentSlot.CHEST, itemSettings())) } map[identifier("${id}_leggings")] = { Registry.register(Registry.ITEM, identifier("${id}_leggings"), ArmorItem(material, EquipmentSlot.LEGS, itemSettings())) } map[identifier("${id}_boots")] = { Registry.register(Registry.ITEM, identifier("${id}_boots"), ArmorItem(material, EquipmentSlot.FEET, itemSettings())) } } fun withBlock(): MaterialHelper { val block = Block(FabricBlockSettings.of(Material.METAL).requiresTool().strength(5f, 6f)) val id = identifier("${id}_block") map[id] = { Registry.register(Registry.BLOCK, id, block) Registry.register(Registry.ITEM, id, BlockItem(block, itemSettings())) } if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) { BlockRenderLayerMap.INSTANCE.putBlock(block, RenderLayer.getCutout()) } return this } fun register() = block() companion object { val map = HashMap Unit>() fun register() { map.entries.sortedWith( compareBy Unit>> { id -> id.key.path.contains("ore") && !id.key.path.contains("purified") } .then(compareBy { id -> id.key.path.contains("raw") }) .then(compareBy { id -> id.key.path.contains("block") }) .then(compareBy { id -> id.key.path.contains("ingot") }) .then(compareBy { id -> id.key.path.contains("chunk") }) .then(compareBy { id -> id.key.path.contains("dust") }) .then(compareBy { id -> id.key.path.contains("purified") }) .then(compareBy { id -> id.key.path.contains("plate") && !id.key.path.contains("chestplate") }) .then(compareBy { id -> id.key.path.contains("nugget") }) .then(compareBy Unit>> { id -> id.key.path.substring(0, id.key.path.indexOf("_")) } .then(compareBy { id -> id.key.path.contains("sword") }) .then(compareBy { id -> id.key.path.contains("pickaxe") }) .then(compareBy { id -> id.key.path.contains("axe") && !id.key.path.contains("pickaxe") }) .then(compareBy { id -> id.key.path.contains("shovel") }) .then(compareBy { id -> id.key.path.contains("hoe") }) .then(compareBy { id -> id.key.path.contains("helmet") }) .then(compareBy { id -> id.key.path.contains("chestplate") }) .then(compareBy { id -> id.key.path.contains("leggings") }) .then(compareBy { id -> id.key.path.contains("boots") }) ) ).asReversed().forEach { it.value() } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/registry/WorldGeneration.kt ================================================ package me.steven.indrev.registry import com.google.common.collect.ImmutableList import me.steven.indrev.config.IRConfig import me.steven.indrev.utils.identifier import me.steven.indrev.world.features.IRConfiguredFeature import me.steven.indrev.world.features.SulfurCrystalFeature import net.fabricmc.fabric.api.biome.v1.BiomeModifications import net.minecraft.block.Blocks import net.minecraft.util.math.intprovider.UniformIntProvider import net.minecraft.util.registry.Registry import net.minecraft.util.registry.RegistryEntry import net.minecraft.world.biome.BiomeKeys import net.minecraft.world.gen.GenerationStep import net.minecraft.world.gen.YOffset import net.minecraft.world.gen.feature.* import net.minecraft.world.gen.placementmodifier.CountPlacementModifier import net.minecraft.world.gen.placementmodifier.HeightRangePlacementModifier import net.minecraft.world.gen.placementmodifier.SquarePlacementModifier import net.minecraft.world.gen.stateprovider.BlockStateProvider object WorldGeneration { fun init() { val config = IRConfig.oregen if (config.tin) { configuredFeatures.add(tinFeature) } if (config.nikolite) { configuredFeatures.add(nikoliteFeature) } if (config.lead) { configuredFeatures.add(leadFeature) } if (config.tungsten) { configuredFeatures.add(tungstenFeature) } if (config.silver) { configuredFeatures.add(silverFeature) } if (config.sulfurCrystals) { configuredFeatures.add(sulfurFeatureOverworld) configuredFeatures.add(sulfurFeatureNether) } if (config.sulfuricAcidLake) { configuredFeatures.add(acidLakesFeature) } } private val configuredFeatures = mutableListOf() fun addFeatures() { configuredFeatures.forEach { feature -> BiomeModifications.addFeature( { ctx -> feature.biomePredicate(ctx) }, feature.step, feature.placedFeatureKey ) } } private val tinTargets = ImmutableList.of( OreFeatureConfig.createTarget( OreConfiguredFeatures.STONE_ORE_REPLACEABLES, IRBlockRegistry.TIN_ORE().defaultState ), OreFeatureConfig.createTarget( OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES, IRBlockRegistry.DEEPSLATE_TIN_ORE().defaultState ) ) private val tinFeature = IRConfiguredFeature( identifier("tin_ore"), GenerationStep.Feature.UNDERGROUND_ORES, ConfiguredFeature(Feature.ORE, OreFeatureConfig(tinTargets, 10)), { feature -> PlacedFeature( RegistryEntry.of(feature), listOf( CountPlacementModifier.of(14), SquarePlacementModifier.of(), HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-48), YOffset.fixed(48)) ) ) }, IRConfiguredFeature.IS_OVERWORLD ) private val leadTargets = ImmutableList.of( OreFeatureConfig.createTarget( OreConfiguredFeatures.STONE_ORE_REPLACEABLES, IRBlockRegistry.LEAD_ORE().defaultState ), OreFeatureConfig.createTarget( OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES, IRBlockRegistry.DEEPSLATE_LEAD_ORE().defaultState ) ) private val leadFeature = IRConfiguredFeature( identifier("lead_ore"), GenerationStep.Feature.UNDERGROUND_ORES, ConfiguredFeature(Feature.ORE, OreFeatureConfig(leadTargets, 6)), { feature -> PlacedFeature( RegistryEntry.of(feature), listOf( CountPlacementModifier.of(11), SquarePlacementModifier.of(), HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-32), YOffset.fixed(32)) ) ) }, IRConfiguredFeature.IS_OVERWORLD ) private val silverTargets = ImmutableList.of( OreFeatureConfig.createTarget( OreConfiguredFeatures.STONE_ORE_REPLACEABLES, IRBlockRegistry.SILVER_ORE().defaultState ), OreFeatureConfig.createTarget( OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES, IRBlockRegistry.DEEPSLATE_SILVER_ORE().defaultState ) ) private val silverFeature = IRConfiguredFeature( identifier("silver_ore"), GenerationStep.Feature.UNDERGROUND_ORES, ConfiguredFeature(Feature.ORE, OreFeatureConfig(silverTargets, 8)), { feature -> PlacedFeature( RegistryEntry.of(feature), listOf( CountPlacementModifier.of(9), SquarePlacementModifier.of(), HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-32), YOffset.fixed(32)) ) ) }, IRConfiguredFeature.IS_OVERWORLD ) private val tungstenTargets = ImmutableList.of( OreFeatureConfig.createTarget( OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES, IRBlockRegistry.DEEPSLATE_TUNGSTEN_ORE().defaultState ) ) private val tungstenFeature = IRConfiguredFeature( identifier("tungsten_ore"), GenerationStep.Feature.UNDERGROUND_ORES, ConfiguredFeature(Feature.ORE, OreFeatureConfig(tungstenTargets, 5)), { feature -> PlacedFeature( RegistryEntry.of(feature), listOf( CountPlacementModifier.of(8), SquarePlacementModifier.of(), HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-16), YOffset.fixed(16)) ) ) }, IRConfiguredFeature.IS_OVERWORLD ) private val nikoliteTargets = ImmutableList.of( OreFeatureConfig.createTarget( OreConfiguredFeatures.STONE_ORE_REPLACEABLES, IRBlockRegistry.NIKOLITE_ORE().defaultState ), OreFeatureConfig.createTarget( OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES, IRBlockRegistry.DEEPSLATE_NIKOLITE_ORE().defaultState ) ) private val nikoliteFeature = IRConfiguredFeature( identifier("nikolite_ore"), GenerationStep.Feature.UNDERGROUND_ORES, ConfiguredFeature(Feature.ORE, OreFeatureConfig(nikoliteTargets, 7)), { feature -> PlacedFeature( RegistryEntry.of(feature), listOf( CountPlacementModifier.of(8), SquarePlacementModifier.of(), HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-16), YOffset.fixed(16)) ) ) }, IRConfiguredFeature.IS_OVERWORLD ) private val sulfurCrystalFeature: SulfurCrystalFeature = Registry.register( Registry.FEATURE, identifier("sulfur_crystal"), SulfurCrystalFeature(DefaultFeatureConfig.CODEC) ) private val sulfurFeatureOverworld = IRConfiguredFeature( identifier("sulfur_crystal_overworld"), GenerationStep.Feature.UNDERGROUND_DECORATION, ConfiguredFeature(sulfurCrystalFeature, DefaultFeatureConfig.INSTANCE), { feature -> PlacedFeature( RegistryEntry.of(feature), listOf( CountPlacementModifier.of(12), HeightRangePlacementModifier.uniform(YOffset.getBottom(), YOffset.fixed(16)) ) ) }, IRConfiguredFeature.IS_OVERWORLD ) private val sulfurFeatureNether = IRConfiguredFeature( identifier("sulfur_crystal_nether"), GenerationStep.Feature.UNDERGROUND_DECORATION, ConfiguredFeature(sulfurCrystalFeature, DefaultFeatureConfig.INSTANCE), { feature -> PlacedFeature( RegistryEntry.of(feature), listOf( CountPlacementModifier.of(20), HeightRangePlacementModifier.uniform(YOffset.getBottom(), YOffset.getTop()) ) ) }, IRConfiguredFeature.IS_NETHER ) private val acidLakesFeature = IRConfiguredFeature( identifier("sulfuric_acid_lake"), GenerationStep.Feature.LAKES, ConfiguredFeature( Feature.LAKE, LakeFeature.Config( BlockStateProvider.of(IRFluidRegistry.SULFURIC_ACID.defaultState), BlockStateProvider.of(Blocks.COARSE_DIRT.defaultState) ) ), { feature -> PlacedFeature( RegistryEntry.of(feature), listOf( CountPlacementModifier.of(UniformIntProvider.create(0, 60)) ) ) } ) { ctx -> ctx.biomeKey == BiomeKeys.SWAMP } } ================================================ FILE: src/main/kotlin/me/steven/indrev/tools/IRToolMaterial.kt ================================================ package me.steven.indrev.tools import me.steven.indrev.registry.IRItemRegistry import net.minecraft.item.ToolMaterial import net.minecraft.recipe.Ingredient enum class IRToolMaterial( private val miningLevel: Int, private val itemDurability: Int, private val miningSpeed: Float, private val attackDamage: Float, private val enchantability: Int, private val repairIngredient: () -> Ingredient? ) : ToolMaterial { TIN(1, 200, 4.0f, 1.0f, 14, { Ingredient.ofItems(IRItemRegistry.TIN_INGOT()) }), COPPER(2, 300, 4.5f, 1.0f, 14, { Ingredient.ofItems(IRItemRegistry.COPPER_INGOT()) }), STEEL(3, 600, 4.5f, 2.0f, 14, { Ingredient.ofItems(IRItemRegistry.STEEL_INGOT()) }), LEAD(2, 900, 3.0f, 2.0f, 8, { Ingredient.ofItems(IRItemRegistry.LEAD_INGOT()) }), BRONZE(2, 500, 3.5f, 2.5f, 12, { Ingredient.ofItems(IRItemRegistry.BRONZE_INGOT()) }), SILVER(2, 500, 5.0f, 1.0f, 24, { Ingredient.ofItems(IRItemRegistry.SILVER_INGOT()) }); override fun getAttackDamage(): Float = attackDamage override fun getDurability(): Int = itemDurability override fun getEnchantability(): Int = enchantability override fun getMiningLevel(): Int = miningLevel override fun getMiningSpeedMultiplier(): Float = miningSpeed override fun getRepairIngredient(): Ingredient? = repairIngredient() } ================================================ FILE: src/main/kotlin/me/steven/indrev/tools/modular/ArmorModule.kt ================================================ package me.steven.indrev.tools.modular import me.steven.indrev.registry.IRItemRegistry import net.minecraft.client.gui.screen.Screen import net.minecraft.entity.EquipmentSlot import net.minecraft.item.ItemConvertible import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting enum class ArmorModule( override val key: String, val slots: Array, override val maxLevel: Int, override val item: ItemConvertible, val hasTexture: Boolean, val hasOverlay: Boolean, ) : Module { NIGHT_VISION("night_vision", arrayOf(EquipmentSlot.HEAD), 1, { IRItemRegistry.NIGHT_VISION_MODULE_ITEM }, true, true), SPEED("speed", arrayOf(EquipmentSlot.LEGS), 3, { IRItemRegistry.SPEED_MODULE_ITEM }, false, false), JUMP_BOOST("jump_boost", arrayOf(EquipmentSlot.FEET), 3, { IRItemRegistry.JUMP_BOOST_MODULE_ITEM },false, false), BREATHING("breathing", arrayOf(EquipmentSlot.HEAD), 1, { IRItemRegistry.BREATHING_MODULE_ITEM }, false, false), FEATHER_FALLING("feather_falling", arrayOf(EquipmentSlot.FEET), 1, { IRItemRegistry.FEATHER_FALLING_MODULE_ITEM }, false, false), PROTECTION("protection", arrayOf(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET), 3, { IRItemRegistry.PROTECTION_MODULE_ITEM }, true, true), AUTO_FEEDER("auto_feeder", arrayOf(EquipmentSlot.HEAD), 1, { IRItemRegistry.AUTO_FEEDER_MODULE_ITEM }, false, false), CHARGER("charger", arrayOf(EquipmentSlot.CHEST), 1, { IRItemRegistry.CHARGER_MODULE_ITEM }, false, false), SOLAR_PANEL("solar_panel", arrayOf(EquipmentSlot.HEAD), 2, { IRItemRegistry.SOLAR_PANEL_MODULE_ITEM }, false, false), FIRE_RESISTANCE("fire_resistance", arrayOf(EquipmentSlot.CHEST),1, { IRItemRegistry.FIRE_RESISTANCE_MODULE_ITEM }, false, false), PIGLIN_TRICKER("piglin_tricker", arrayOf(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET), 1, { IRItemRegistry.PIGLIN_TRICKER_MODULE_ITEM },false, false), ELYTRA("elytra", arrayOf(EquipmentSlot.CHEST), 1, { IRItemRegistry.ELYTRA_MODULE_ITEM }, false, false), MAGNET("magnet", arrayOf(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET), 1, { IRItemRegistry.MAGNET_MODULE }, false, false), JETPACK("jetpack", arrayOf(EquipmentSlot.CHEST), 1, { IRItemRegistry.JETPACK_MODULE_ITEM }, false, false), WATER_AFFINITY("water_affinity", arrayOf(EquipmentSlot.CHEST, EquipmentSlot.LEGS), 1, { IRItemRegistry.WATER_AFFINITY_MODULE }, false, false) { override fun getTooltip(stack: ItemStack, tooltip: MutableList?) { val chestplate = TranslatableText("item.indrev.module_water_affinity.on", TranslatableText("item.indrev.module_water_affinity.chestplate").formatted(Formatting.GOLD)) tooltip?.add(chestplate.formatted(Formatting.BLUE, Formatting.ITALIC)) tooltip?.add(LiteralText(" ") .append(TranslatableText("item.indrev.module_water_affinity.tooltip") .formatted(Formatting.BLUE, Formatting.ITALIC))) val legs = TranslatableText("item.indrev.module_water_affinity.on", TranslatableText("item.indrev.module_water_affinity.leggings").formatted(Formatting.GOLD)) tooltip?.add(legs.formatted(Formatting.BLUE, Formatting.ITALIC)) tooltip?.add(LiteralText(" ") .append(TranslatableText("item.indrev.module_water_affinity.tooltip1") .formatted(Formatting.BLUE, Formatting.ITALIC))) tooltip?.add(LiteralText(" ")) if (Screen.hasShiftDown()) { val maxLevelText = TranslatableText( "item.indrev.module_max_level", LiteralText(maxLevel.toString()).formatted(Formatting.GOLD) ) tooltip?.add(maxLevelText.formatted(Formatting.BLUE)) } tooltip?.add(TranslatableText("item.indrev.module_parts").formatted(Formatting.BLUE)) slots.forEach { tooltip?.add(TranslatableText("item.indrev.module_parts_${it.toString().lowercase()}").formatted(Formatting.GOLD)) } } }, COLOR("color", arrayOf(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET), -1, { null },false, false); override fun getTooltip(stack: ItemStack, tooltip: MutableList?) { super.getTooltip(stack, tooltip) tooltip?.add(TranslatableText("item.indrev.module_parts").formatted(Formatting.BLUE)) slots.forEach { tooltip?.add(TranslatableText("item.indrev.module_parts_${it.toString().lowercase()}").formatted(Formatting.GOLD)) } } companion object { val COMPATIBLE: Array = values() val COMPATIBLE_HELMET: Array = COMPATIBLE.filter { it.slots.contains(EquipmentSlot.HEAD) }.toTypedArray() val COMPATIBLE_CHEST: Array = COMPATIBLE.filter { it.slots.contains(EquipmentSlot.CHEST) }.toTypedArray() val COMPATIBLE_LEGS: Array = COMPATIBLE.filter { it.slots.contains(EquipmentSlot.LEGS) }.toTypedArray() val COMPATIBLE_BOOTS: Array = COMPATIBLE.filter { it.slots.contains(EquipmentSlot.FEET) }.toTypedArray() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/tools/modular/DrillModule.kt ================================================ package me.steven.indrev.tools.modular import me.steven.indrev.registry.IRItemRegistry import net.minecraft.item.ItemConvertible import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtHelper import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.math.BlockPos enum class DrillModule( override val key: String, override val maxLevel: Int, override val item: ItemConvertible ) : Module { RANGE("range", 5, { IRItemRegistry.RANGE_MODULE_ITEM }), FORTUNE("fortune", 3, { IRItemRegistry.FORTUNE_MODULE_ITEM }), SILK_TOUCH("silk_touch", 1, { IRItemRegistry.SILK_TOUCH_MODULE_ITEM }), CONTROLLED_DESTRUCTION("controlled_destruction", 1, { IRItemRegistry.CONTROLLED_DESTRUCTION_MODULE_ITEM }), MATTER_PROJECTOR("matter_projector", 1, { IRItemRegistry.MATTER_PROJECTOR_MODULE_ITEM }); override fun getTooltip(stack: ItemStack, tooltip: MutableList?) { super.getTooltip(stack, tooltip) tooltip?.add(TranslatableText("item.indrev.module_parts").formatted(Formatting.BLUE)) tooltip?.add(TranslatableText("item.indrev.module_parts_drill").formatted(Formatting.GOLD)) } companion object { val COMPATIBLE: Array = arrayOf(RANGE, FORTUNE, SILK_TOUCH, MiningToolModule.EFFICIENCY, CONTROLLED_DESTRUCTION, MATTER_PROJECTOR) fun getBlacklistedPositions(stack: ItemStack): List { val nbt = stack.nbt ?: return emptyList() if ( !nbt.contains("BlacklistedPositions") || (CONTROLLED_DESTRUCTION.getLevel(stack) <= 0 && MATTER_PROJECTOR.getLevel(stack) <= 0) || RANGE.getLevel(stack) <= 0 ) return emptyList() val list = nbt.getList("BlacklistedPositions", 10) return list.map { element -> NbtHelper.toBlockPos(element as NbtCompound) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/tools/modular/GamerAxeModule.kt ================================================ package me.steven.indrev.tools.modular import me.steven.indrev.registry.IRItemRegistry import net.minecraft.item.ItemConvertible import net.minecraft.item.ItemStack import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting enum class GamerAxeModule( override val key: String, override val maxLevel: Int, override val item: ItemConvertible ) : Module { LOOTING("looting", 3, { IRItemRegistry.LOOTING_MODULE_ITEM }), FIRE_ASPECT("fire_aspect", 1, { IRItemRegistry.FIRE_ASPECT_MODULE_ITEM }), SHARPNESS("sharpness", 5, { IRItemRegistry.SHARPNESS_MODULE_ITEM }), REACH("reach", 4, { IRItemRegistry.SHARPNESS_MODULE_ITEM }); // NOT IMPLEMENTED override fun getTooltip(stack: ItemStack, tooltip: MutableList?) { super.getTooltip(stack, tooltip) tooltip?.add(TranslatableText("item.indrev.module_parts").formatted(Formatting.BLUE)) tooltip?.add(TranslatableText("item.indrev.module_parts_gamer_axe").formatted(Formatting.GOLD)) } companion object { val COMPATIBLE: Array = arrayOf(LOOTING, FIRE_ASPECT, SHARPNESS, REACH, MiningToolModule.EFFICIENCY) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/tools/modular/IRModularItem.kt ================================================ package me.steven.indrev.tools.modular import net.minecraft.item.ItemStack import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting interface IRModularItem { fun getCompatibleModules(itemStack: ItemStack): Array fun getInstalled(stack: ItemStack): List { val tag = stack.nbt ?: return emptyList() return getCompatibleModules(stack).mapNotNull { module -> if (tag.contains(module.key)) module else null } } fun getInstalledTooltip(upgrades: List, stack: ItemStack, tooltip: MutableList?) { if (upgrades.isNotEmpty()) { tooltip?.add(TranslatableText("item.indrev.modular.upgrade").formatted(Formatting.GOLD)) upgrades.forEach { upgrade -> val level = upgrade.getLevel(stack) val text = TranslatableText("item.indrev.modular.upgrade.${upgrade.key}", level) if (upgrade.getMaxInstalledLevel(stack) != level) text.formatted(Formatting.ITALIC) tooltip?.add(text.formatted(Formatting.BLUE)) } } } fun getCount(stack: ItemStack): Int { return getCompatibleModules(stack).map { module -> val tag = stack.nbt ?: return@map 0 if (tag.contains(module.key)) tag.getInt(module.key) else 0 }.sum() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/tools/modular/MiningToolModule.kt ================================================ package me.steven.indrev.tools.modular import me.steven.indrev.registry.IRItemRegistry import net.minecraft.item.ItemConvertible import net.minecraft.item.ItemStack import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting enum class MiningToolModule( override val key: String, override val maxLevel: Int, override val item: ItemConvertible ) : Module { EFFICIENCY("efficiency", 5, { IRItemRegistry.EFFICIENCY_MODULE_ITEM }); override fun getTooltip(stack: ItemStack, tooltip: MutableList?) { super.getTooltip(stack, tooltip) tooltip?.add(TranslatableText("item.indrev.module_parts").formatted(Formatting.BLUE)) tooltip?.add(TranslatableText("item.indrev.module_parts_drill").formatted(Formatting.GOLD)) tooltip?.add(TranslatableText("item.indrev.module_parts_gamer_axe").formatted(Formatting.GOLD)) } } ================================================ FILE: src/main/kotlin/me/steven/indrev/tools/modular/Module.kt ================================================ package me.steven.indrev.tools.modular import net.minecraft.client.gui.screen.Screen import net.minecraft.item.ItemConvertible import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting interface Module { val key: String val maxLevel: Int val item: ItemConvertible fun isInstalled(itemStack: ItemStack): Boolean { return !itemStack.isEmpty && itemStack.orCreateNbt.contains(key) } fun getLevel(itemStack: ItemStack): Int { if (itemStack.isEmpty) return 0 val tag = itemStack.getOrCreateSubNbt("selected") return if (tag.contains(key)) tag.getInt(key) else getMaxInstalledLevel(itemStack) } fun getMaxInstalledLevel(itemStack: ItemStack): Int { if (itemStack.isEmpty) return 0 val tag = itemStack.orCreateNbt return if (tag.contains(key)) tag.getInt(key) else 0 } fun getTooltip(stack: ItemStack, tooltip: MutableList?) { val titleText = TranslatableText("item.indrev.module_${key}.tooltip") tooltip?.add(titleText.formatted(Formatting.BLUE, Formatting.ITALIC)) tooltip?.add(LiteralText(" ")) if (Screen.hasShiftDown()) { if (this != ArmorModule.COLOR) { val maxLevelText = TranslatableText("item.indrev.module_max_level", LiteralText(maxLevel.toString()).formatted(Formatting.GOLD)) tooltip?.add(maxLevelText.formatted(Formatting.BLUE)) } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/EmptyModel.kt ================================================ package me.steven.indrev.utils import com.mojang.datafixers.util.Pair import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel import net.fabricmc.fabric.api.renderer.v1.render.RenderContext import net.minecraft.block.BlockState import net.minecraft.client.render.model.* import net.minecraft.client.render.model.json.ModelOverrideList import net.minecraft.client.render.model.json.ModelTransformation import net.minecraft.client.texture.Sprite import net.minecraft.client.util.SpriteIdentifier import net.minecraft.item.ItemStack import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.BlockRenderView import java.util.* import java.util.function.Function import java.util.function.Supplier object EmptyModel : UnbakedModel, BakedModel, FabricBakedModel { override fun getModelDependencies(): MutableCollection = mutableListOf() override fun getTextureDependencies( unbakedModelGetter: Function?, unresolvedTextureReferences: MutableSet>? ): MutableCollection = mutableListOf() override fun bake( loader: ModelLoader?, textureGetter: Function?, rotationContainer: ModelBakeSettings?, modelId: Identifier? ): BakedModel = this override fun getQuads(state: BlockState?, face: Direction?, random: Random?): MutableList = mutableListOf() override fun useAmbientOcclusion(): Boolean = false override fun hasDepth(): Boolean = false override fun isSideLit(): Boolean = false override fun isBuiltin(): Boolean = false override fun getParticleSprite(): Sprite? = null override fun getTransformation(): ModelTransformation = ModelTransformation.NONE override fun getOverrides(): ModelOverrideList = ModelOverrideList.EMPTY override fun isVanillaAdapter(): Boolean = false override fun emitBlockQuads( blockView: BlockRenderView?, state: BlockState?, pos: BlockPos?, randomSupplier: Supplier?, context: RenderContext? ) { } override fun emitItemQuads(stack: ItemStack?, randomSupplier: Supplier?, context: RenderContext?) { } } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/EnergyDamageHandler.kt ================================================ package me.steven.indrev.utils import net.fabricmc.fabric.api.item.v1.CustomDamageHandler import net.minecraft.entity.LivingEntity import net.minecraft.item.ItemStack import java.util.function.Consumer object EnergyDamageHandler : CustomDamageHandler { override fun damage(stack: ItemStack, amount: Int, entity: LivingEntity?, breakCallback: Consumer?): Int { /*val itemIo = energyOf(stack) Transaction.openOuter().use { itemIo?.extract(amount.toLong(), it) it.commit() } return 0*/ return 0//TODO figure thsi out } } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/IRFluidTank.kt ================================================ package me.steven.indrev.utils import alexiil.mc.lib.attributes.fluid.amount.FluidAmount import alexiil.mc.lib.attributes.fluid.render.FluidRenderFace import alexiil.mc.lib.attributes.fluid.volume.FluidKeys import me.steven.indrev.components.DefaultSyncableObject import me.steven.indrev.components.FluidComponent import me.steven.indrev.components.SyncableObject import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.storage.base.ResourceAmount import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant import net.minecraft.client.render.VertexConsumerProvider import net.minecraft.client.util.math.MatrixStack import net.minecraft.nbt.NbtCompound import net.minecraft.network.PacketByteBuf class IRFluidTank(val index: Int, val component: () -> FluidComponent) : SingleVariantStorage(), SyncableObject by DefaultSyncableObject() { val isEmpty: Boolean get() = variant.isBlank || amount == 0L val exposed = ExposedIRFluidTank() override fun getCapacity(variant: FluidVariant): Long = component().getTankCapacity(index) override fun getBlankVariant(): FluidVariant = FluidVariant.blank() fun toTag(): NbtCompound { val nbt = NbtCompound() nbt.put("variant", variant.toNbt()) nbt.putLong("amt", amount) return nbt } fun render(faces: List?, vcp: VertexConsumerProvider?, matrices: MatrixStack?) { if (!variant.isBlank) FluidKeys.get(variant.fluid).withAmount(FluidAmount.BUCKET).render(faces, vcp, matrices) } fun renderGuiRect(x0: Double, y0: Double, x1: Double, y1: Double) { if (!variant.isBlank) FluidKeys.get(variant.fluid).withAmount(FluidAmount.BUCKET).renderGuiRect(x0, y0, x1, y1) } override fun onFinalCommit() { super.onFinalCommit() component().syncable().markForUpdate() markDirty() } fun extract(amount: Long, act: Boolean = false): Long { Transaction.openOuter().use { val extracted = extract(variant, amount, it) if (act) it.commit() else it.abort() return extracted } } fun tryExtract(amount: Long): Boolean { Transaction.openOuter().use { val extracted = extract(variant, amount, it) it.abort() return extracted == amount } } fun insert(variant: FluidVariant, amount: Long, act: Boolean = false): Long { if (!this.variant.isBlank && variant != this.variant) return 0 Transaction.openOuter().use { val inserted = insert(variant, amount, it) if (act) it.commit() else it.abort() return inserted } } fun tryInsert(variant: FluidVariant, amount: Long): Boolean { if (!this.variant.isBlank && variant != this.variant) return false Transaction.openOuter().use { val inserted = insert(variant, amount, it) it.abort() return inserted == amount } } override fun toPacket(buf: PacketByteBuf) { resource.toPacket(buf) buf.writeLong(amount) } @Environment(EnvType.CLIENT) override fun fromPacket(buf: PacketByteBuf) { this.variant = FluidVariant.fromPacket(buf) this.amount = buf.readLong() } fun fromTag(nbt: NbtCompound) { this.variant = FluidVariant.fromNbt(nbt.getCompound("variant")) this.amount = nbt.getLong("amt") } inner class ExposedIRFluidTank : SnapshotParticipant>(), SingleSlotStorage by this { override fun createSnapshot(): ResourceAmount = this@IRFluidTank.createSnapshot() override fun readSnapshot(snapshot: ResourceAmount) = this@IRFluidTank.readSnapshot(snapshot) override fun insert(resource: FluidVariant, maxAmount: Long, transaction: TransactionContext): Long { return if (component().inputTanks.contains(index) && component().isFluidValidForTank(index, resource)) this@IRFluidTank.insert(resource, maxAmount, transaction) else 0 } override fun extract(resource: FluidVariant, maxAmount: Long, transaction: TransactionContext): Long { return if (component().outputTanks.contains(index)) this@IRFluidTank.extract(resource, maxAmount, transaction) else 0 } override fun onFinalCommit() = this@IRFluidTank.onFinalCommit() } } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/ReusableArrayDeque.kt ================================================ package me.steven.indrev.utils /** * Limited simple re-implementation of a reusable ArrayDeque * When polling, instead of reducing size and setting elements on the array to null, it will just increase the head * When finished, you can reset the head and start again * Used by IR's networks */ class ReusableArrayDeque>(elements: Collection) : AbstractMutableList() { private var head: Int = 0 var elementData: Array override var size: Int = 0 private set init { elementData = elements.toTypedArray() size = elementData.size if (elementData.isEmpty()) elementData = emptyElementData } inline fun apply(transform: (Array) -> Unit) { @Suppress("UNCHECKED_CAST") transform(elementData) } override fun add(index: Int, element: E) = throw NotImplementedError() override fun removeAt(index: Int): E = throw NotImplementedError() override fun set(index: Int, element: E): E = throw NotImplementedError() override fun get(index: Int): E { checkElementIndex(index, size) return internalGet(internalIndex(index)) } fun removeFirst(): E { if (isEmpty()) throw NoSuchElementException("ArrayDeque is empty.") val element = internalGet(head) head = incremented(head) return element } private fun incremented(index: Int): Int = index + 1 private fun internalGet(internalIndex: Int): E { @Suppress("UNCHECKED_CAST") return elementData[internalIndex] as E } private fun internalIndex(index: Int): Int = positiveMod(head + index) private fun positiveMod(index: Int): Int = if (index >= elementData.size) index - elementData.size else index fun resetHead() { head = 0 } override fun isEmpty(): Boolean = head >= size companion object { private val emptyElementData = emptyArray() fun checkElementIndex(index: Int, size: Int) { if (index < 0 || index >= size) { throw IndexOutOfBoundsException("index: $index, size: $size") } } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/accessorextensions.kt ================================================ package me.steven.indrev.utils import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import me.steven.indrev.api.ServerWorldExtension import me.steven.indrev.networks.energy.EnergyNetworkState import me.steven.indrev.networks.fluid.FluidNetworkState import me.steven.indrev.networks.item.ItemNetworkState import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.Direction import team.reborn.energy.api.EnergyStorage val ServerWorld.energyIoCache: Long2ObjectOpenHashMap> get() = (this as ServerWorldExtension).indrev_getEnergyCache() val ServerWorld.energyNetworkState: EnergyNetworkState get() = (this as ServerWorldExtension).indrev_getEnergyNetworkState() val ServerWorld.fluidNetworkState: FluidNetworkState get() = (this as ServerWorldExtension).indrev_getFluidNetworkState() val ServerWorld.itemNetworkState: ItemNetworkState get() = (this as ServerWorldExtension).indrev_getItemNetworkState() ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/clientutils.kt ================================================ package me.steven.indrev.utils import com.mojang.blaze3d.systems.RenderSystem import io.github.cottonmc.cotton.gui.client.BackgroundPainter import io.github.cottonmc.cotton.gui.client.ScreenDrawing import me.steven.indrev.config.BasicMachineConfig import me.steven.indrev.config.GeneratorConfig import me.steven.indrev.config.HeatMachineConfig import me.steven.indrev.config.LFCConfig import net.fabricmc.api.EnvType import net.fabricmc.loader.api.FabricLoader import net.minecraft.client.gui.screen.Screen import net.minecraft.client.option.KeyBinding import net.minecraft.client.render.* import net.minecraft.client.util.math.MatrixStack import net.minecraft.item.ItemStack import net.minecraft.text.LiteralText import net.minecraft.text.Text import net.minecraft.text.TranslatableText import net.minecraft.util.Formatting import net.minecraft.util.Identifier import kotlin.math.atan2 val isClient = FabricLoader.getInstance().environmentType == EnvType.CLIENT val UPGRADE_SLOT_PANEL_PAINTER: BackgroundPainter = BackgroundPainter.createLightDarkVariants( BackgroundPainter.createNinePatch(Identifier("libgui", "textures/widget/panel_light.png")).setPadding(4), BackgroundPainter.createNinePatch(Identifier("libgui", "textures/widget/panel_dark.png")).setPadding(4) ) fun draw2Colors(matrices: MatrixStack, x1: Int, y1: Int, x2: Int, y2: Int, color1: Long, color2: Long) { val matrix = matrices.peek().positionMatrix var j: Int var xx1 = x1.toFloat() var xx2 = x2.toFloat() var yy1 = x1.toFloat() var yy2 = x2.toFloat() if (x1 < x2) { j = x1 xx1 = x2.toFloat() xx2 = j.toFloat() } if (y1 < y2) { j = y1 yy1 = y2.toFloat() yy2 = j.toFloat() } val f1 = (color1 shr 24 and 255) / 255.0f val g1 = (color1 shr 16 and 255) / 255.0f val h1 = (color1 shr 8 and 255) / 255.0f val k1 = (color1 and 255) / 255.0f val f2 = (color2 shr 24 and 255) / 255.0f val g2 = (color2 shr 16 and 255) / 255.0f val h2 = (color2 shr 8 and 255) / 255.0f val k2 = (color2 and 255) / 255.0f RenderSystem.setShader { GameRenderer.getPositionColorShader() } RenderSystem.enableBlend() RenderSystem.disableTexture() RenderSystem.defaultBlendFunc() Tessellator.getInstance().buffer.run { begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR) vertex(matrix, xx1, yy1, 0.0f).color(g1, h1, k1, f1).next() vertex(matrix, xx1, yy2, 0.0f).color(g1, h1, k1, f1).next() vertex(matrix, xx2, yy2, 0.0f).color(g1, h1, k1, f1).next() vertex(matrix, xx1, yy1, 0.0f).color(g1, h1, k1, f1).next() end() BufferRenderer.draw(this) begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR) vertex(matrix, xx1, yy1, 0.0f).color(g2, h2, k2, f2).next() vertex(matrix, xx2, yy2, 0.0f).color(g2, h2, k2, f2).next() vertex(matrix, xx2, yy1, 0.0f).color(g2, h2, k2, f2).next() vertex(matrix, xx1, yy1, 0.0f).color(g2, h2, k2, f2).next() end() BufferRenderer.draw(this) } RenderSystem.enableTexture() RenderSystem.disableBlend() } fun drawCircle(matrices: MatrixStack, value: Int, max: Int, x: Int, y: Int, width: Int, colorProvider: (Int, Int) -> Int) { val maxRadius = width / 2 - 1 val minRadius = maxRadius - 3 val maxAngle = value * 360 / max.toDouble() for (xOffset in -maxRadius until maxRadius) { for (yOffset in -maxRadius until maxRadius) { val squaredDist = xOffset * xOffset + yOffset * yOffset val angle = Math.toDegrees(atan2(-xOffset.toDouble(), yOffset.toDouble())) + 180 if (squaredDist >= minRadius * minRadius && squaredDist < maxRadius * maxRadius && angle < maxAngle) { val color = colorProvider(x + xOffset, y + yOffset) ScreenDrawing.coloredRect(matrices, x + xOffset + width / 2, y + yOffset + width / 2, 1, 1, color) } } } } fun buildEnergyTooltip(stack: ItemStack?, tooltip: MutableList?) { val handler = energyOf(stack) ?: return if (handler.amount > 0) { val percentage = handler.amount * 100 / handler.capacity tooltip?.add(LiteralText("${getEnergyString(handler.amount)} LF (${percentage.toInt()}%)").formatted(Formatting.GRAY)) } } fun buildMachineTooltip(config: Any, tooltip: MutableList?) { if (Screen.hasShiftDown()) { when (config) { is HeatMachineConfig -> { tooltip?.add(configText("maxInput", "lftick", config.maxInput)) tooltip?.add(configText("maxEnergyStored", "lf", getEnergyString(config.maxEnergyStored))) tooltip?.add(configText("energyCost", "lftick", config.energyCost)) val speed = config.processSpeed * 100 if (speed >= 1000) tooltip?.add(configText("processSpeed", "seconds", config.processSpeed / 20)) else tooltip?.add(configText("processSpeed", "${speed.toInt()}%")) tooltip?.add(configText("temperatureBoost", "${config.processTemperatureBoost * config.processSpeed * 100}%")) } is BasicMachineConfig -> { tooltip?.add(configText("maxInput", "lftick", config.maxInput)) tooltip?.add(configText("maxEnergyStored", "lf", getEnergyString(config.maxEnergyStored))) tooltip?.add(configText("energyCost", "lftick", config.energyCost)) val speed = config.processSpeed * 100 if (speed >= 1000) tooltip?.add(configText("processSpeed", "seconds", config.processSpeed / 20)) else tooltip?.add(configText("processSpeed", "${speed.toInt()}%")) } is GeneratorConfig -> { tooltip?.add(configText("maxOutput", "lftick", config.maxOutput)) tooltip?.add(configText("maxEnergyStored", "lf", getEnergyString(config.maxEnergyStored))) tooltip?.add(configText("ratio", "lftick", config.ratio)) if (config.temperatureBoost > 0) tooltip?.add(configText("temperatureBoost", "lftick", config.temperatureBoost * config.ratio)) } is LFCConfig -> { tooltip?.add(configText("maxInput", "lftick", config.maxInput)) tooltip?.add(configText("maxOutput", "lftick", config.maxOutput)) tooltip?.add(configText("maxEnergyStored", "lf", getEnergyString(config.maxEnergyStored))) } } } else { tooltip?.add( TranslatableText("gui.indrev.tooltip.press_shift", LiteralText("").append(KeyBinding.getLocalizedName("key.keyboard.left.shift").get()).formatted(Formatting.AQUA)).formatted(Formatting.GRAY) ) } } private fun configText(key: String, value: String): Text { return TranslatableText("gui.indrev.tooltip.$key").formatted(Formatting.AQUA) .append(LiteralText(value).formatted(Formatting.GRAY)) } private fun configText(key: String, unit: String, value: Any): Text { return TranslatableText("gui.indrev.tooltip.$key").formatted(Formatting.AQUA) .append(TranslatableText("gui.indrev.tooltip.$unit", value).formatted(Formatting.GRAY)) } fun getEnergyString(energy: Long): String = when { energy >= 1000000000000 -> "${"%.1f".format(energy / 1000000000000f)}T" energy >= 1000000000 -> "${"%.1f".format(energy / 1000000000f)}B" energy >= 1000000 -> "${"%.1f".format(energy / 1000000f)}M" energy >= 1000 -> "${"%.1f".format(energy / 1000f)}k" else -> "%.1f".format(energy.toFloat()) } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/energyutils.kt ================================================ package me.steven.indrev.utils import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction import net.minecraft.inventory.Inventory import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import team.reborn.energy.api.EnergyStorage import java.util.function.LongFunction fun energyOf(world: ServerWorld, blockPos: BlockPos, direction: Direction): EnergyStorage? { return world.energyIoCache.computeIfAbsent( blockPos.asLong(), LongFunction { BlockApiCache.create(EnergyStorage.SIDED, world, blockPos) }).find(direction) } fun energyOf(itemStack: ItemStack?): EnergyStorage? { return if (itemStack == null || itemStack.isEmpty) null else EnergyStorage.ITEM.find(itemStack, ContainerItemContext.withInitial(itemStack)) } fun energyOf(inv: Inventory?, slot: Int): EnergyStorage? { val itemStack = inv?.getStack(slot) return if (itemStack == null || itemStack.isEmpty) null else EnergyStorage.ITEM.find(itemStack, ContainerItemContext.ofSingleSlot(InventoryStorage.of(inv, null).getSlot(slot))) } fun extract(inv: Inventory?, slot: Int, amount: Long): Boolean { return energyOf(inv, slot)?.use(amount) == true } fun insert(inv: Inventory?, slot: Int, amount: Long): Boolean { val e = energyOf(inv, slot) ?: return false Transaction.openOuter().use { t -> val inserted = e.insert(amount, t) if (inserted == amount) { t.commit() return true } return false } } fun EnergyStorage.use(amount: Long): Boolean { Transaction.openOuter().use { t -> val extracted = this.extract(amount, t) if (extracted == amount) { t.commit() return true } return false } } fun EnergyStorage.insert(amount: Long, act: Boolean): Long { Transaction.openOuter().use { t -> val inserted = this.insert(amount, t) if (act) { t.commit() } return inserted } } fun EnergyStorage.extract(amount: Long, act: Boolean): Long { Transaction.openOuter().use { t -> val extracted = this.extract(amount, t) if (extracted == amount && act) { t.commit() } return extracted } } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/fluidutils.kt ================================================ package me.steven.indrev.utils import alexiil.mc.lib.attributes.fluid.amount.FluidAmount import alexiil.mc.lib.attributes.fluid.volume.FluidKeys import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.storage.Storage import net.fabricmc.fabric.api.transfer.v1.storage.base.ResourceAmount import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.block.FluidBlock import net.minecraft.client.gui.screen.Screen import net.minecraft.fluid.Fluid import net.minecraft.fluid.Fluids import net.minecraft.item.ItemStack import net.minecraft.network.PacketByteBuf import net.minecraft.server.world.ServerWorld import net.minecraft.text.OrderedText import net.minecraft.text.Style import net.minecraft.text.TranslatableText import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.registry.Registry import net.minecraft.world.World import java.util.* import java.util.function.LongFunction //these are just for reference, they're not used much val bucket = 81000L val bottle = bucket / 3 // 27000 droplets val half_bucket = bucket / 2 // 40500 droplets val block = bucket val ingot = block / 9 // 9000 droplets val nugget = ingot / 9 // 1000 droplets val scrap = ingot / 4 // 250 droplets val mb = bucket / 1000 fun FluidBlock.drainFluid(world: World, pos: BlockPos, state: BlockState): Fluid { return if (state.get(FluidBlock.LEVEL) as Int == 0) { world.setBlockState(pos, Blocks.AIR.defaultState, 11) fluid } else { Fluids.EMPTY } } //TODO fuck weakhashmaps val fluidApiCache = WeakHashMap, Direction>>>() fun fluidStorageOf(world: ServerWorld, blockPos: BlockPos, direction: Direction): Storage? { return fluidApiCache .computeIfAbsent(world) { Long2ObjectOpenHashMap() } .computeIfAbsent(blockPos.asLong(), LongFunction { BlockApiCache.create(FluidStorage.SIDED, world, blockPos) }) .find(direction) } fun fluidStorageOf(world: World, blockPos: BlockPos, direction: Direction): Storage? { if (world is ServerWorld) return fluidStorageOf(world, blockPos, direction) else return FluidStorage.SIDED.find(world, blockPos, direction) } fun fluidStorageOf(itemStack: ItemStack?): Storage? { return if (itemStack == null) null else FluidStorage.ITEM.find(itemStack, null) } typealias IRFluidAmount = ResourceAmount fun IRFluidAmount.toPacket(buf: PacketByteBuf) { resource.toPacket(buf) buf.writeLong(amount) } fun IRFluidAmount.renderGuiRect(x0: Double, y0: Double, x1: Double, y1: Double) { FluidKeys.get(resource.fluid).withAmount(FluidAmount.BUCKET).renderGuiRect(x0, y0, x1, y1) } fun fromPacket(buf: PacketByteBuf): IRFluidAmount { val res = FluidVariant.fromPacket(buf) val amt = buf.readLong() return amt of res } infix fun Long.of(variant: FluidVariant) = IRFluidAmount(variant, this) fun getTooltip(variant: FluidVariant, amount: Long, capacity: Long): List { val tooltips = mutableListOf() val id = Registry.BLOCK.getId(variant.fluid.defaultState.blockState.block) val color = FluidRenderHandlerRegistry.INSTANCE.get(variant.fluid)?.getFluidColor(null, null, variant.fluid.defaultState) ?: -1 tooltips.add(TranslatableText("block.${id.namespace}.${id.path}").setStyle(Style.EMPTY.withColor(color)).asOrderedText()) val asMb = amount / 81 val accurate = amount / 81.0 val prefix = when { accurate > asMb -> ">" accurate < asMb -> "<" else -> "" } if (capacity > 0) tooltips.add(TranslatableText("$prefix$asMb / ${capacity / 81} mB").asOrderedText()) else tooltips.add(TranslatableText("$prefix$asMb mB").asOrderedText()) if (Screen.hasShiftDown()) { if (capacity > 0) tooltips.add(TranslatableText("$amount / $capacity droplets").asOrderedText()) else tooltips.add(TranslatableText("$amount droplets").asOrderedText()) } return tooltips } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/helperextensions.kt ================================================ package me.steven.indrev.utils import me.steven.indrev.api.IREntityExtension import me.steven.indrev.inventories.IRInventory import net.minecraft.entity.Entity import net.minecraft.entity.effect.StatusEffectCategory import net.minecraft.item.FoodComponent import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.util.collection.WeightedList import net.minecraft.util.math.* import net.minecraft.util.thread.ThreadExecutor import net.minecraft.world.World import java.util.concurrent.CompletableFuture fun World.isLoaded(pos: BlockPos): Boolean { return chunkManager.isChunkLoaded(pos.x shr 4, pos.z shr 4) } fun WeightedList.pickRandom(): E { return this.shuffle().entries.first().element } fun FoodComponent.hasNegativeEffects(): Boolean { return statusEffects.any { it.first.effectType.category == StatusEffectCategory.HARMFUL } } inline fun Entity.redirectDrops(inv: IRInventory, run: () -> Unit) { this as IREntityExtension this.machineInv = inv run() this.machineInv = null } fun BlockPos.toVec3d() = Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) fun ThreadExecutor<*>.submitAndGet(task: () -> V): V { return (if (!this.isOnThread) CompletableFuture.supplyAsync(task, this) else CompletableFuture.completedFuture(task())).get() } inline fun Box.any(f: (Int, Int, Int) -> Boolean): Boolean { for (x in minX.toInt()..maxX.toInt()) for (y in minY.toInt()..maxY.toInt()) for (z in minZ.toInt()..maxZ.toInt()) if (f(x, y, z)) return true return false } inline fun Box.forEach(f: (Int, Int, Int) -> Unit) { for (x in minX.toInt() until maxX.toInt()) for (y in minY.toInt() until maxY.toInt()) for (z in minZ.toInt() until maxZ.toInt()) f(x, y, z) } inline fun Box.map(f: (Int, Int, Int) -> T): MutableList { val list = ArrayList((xLength * yLength * zLength).toInt()) for (x in minX.toInt() until maxX.toInt()) for (y in minY.toInt() until maxY.toInt()) for (z in minZ.toInt() until maxZ.toInt()) list.add(f(x, y, z)) return list } operator fun Box.contains(pos: BlockPos): Boolean { return contains(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) } inline fun Box.firstOrNull(f: (Int, Int, Int) -> Boolean): BlockPos? { for (x in minX.toInt()..maxX.toInt()) for (y in minY.toInt()..maxY.toInt()) for (z in minZ.toInt()..maxZ.toInt()) if (f(x, y, z)) return BlockPos(x, y, z) return null } operator fun Vec3i.component1() = x operator fun Vec3i.component2() = y operator fun Vec3i.component3() = z operator fun Vec3d.component1() = this.x operator fun Vec3d.component2() = this.y operator fun Vec3d.component3() = this.z operator fun Vec3f.component1() = this.x operator fun Vec3f.component2() = this.y operator fun Vec3f.component3() = this.z operator fun ItemStack.component1(): ItemStack = this operator fun ItemStack.component2(): Item = item fun Collection.asMutableList(): MutableList { return this as? MutableList ?: ArrayList(this) } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/hiddenitems.kt ================================================ package me.steven.indrev.utils import net.minecraft.util.Identifier val hiddenIds = arrayOf( "heliostat", "resistant_glass", "solar_receiver", "fluid_valve", "molten_salt", "steam_turbine_mk4", "steam_turbine_steam_input_valve", "steam_turbine_energy_output", "steam_turbine_casing", "steam_turbine_rotor", "steam_turbine_pressure_valve", "solar_power_plant_tower", "solar_power_plant_smelter", "boiler", "steam", "carbon_fiber_plate", "carbon_fiber_rod", "salt", "carbon_fiber_helmet_frame", "carbon_fiber_chest_frame", "carbon_fiber_legs_frame", "carbon_fiber_boots_frame", "module_controlled_destruction", "module_matter_projector", "biomass_composter", "hydrogen_bucket", "oxygen_bucket", "methane_bucket", "steam_bucket", "molten_salt_bucket", "jetpack_mk1", "jetpack_mk2", "jetpack_mk3", "jetpack_mk4", "distiller_mk4", "module_magnet", "gas_generator_mk4", "dirt_oxygenator_mk1", "soot", "electrolytic_separator_mk1", "electrolytic_separator_mk2", "electrolytic_separator_mk3", "electrolytic_separator_mk4", "electrolytic_separator_creative", "module_water_affinity", "module_jetpack", "reinforced_elytra", "module_reinforced_elytra" ) fun hide(id: Identifier) = id.namespace == "indrev" && hiddenIds.contains(id.path) ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/interactions.kt ================================================ package me.steven.indrev.utils import it.unimi.dsi.fastutil.longs.LongOpenHashSet import me.steven.indrev.api.sideconfigs.ConfigurationType import me.steven.indrev.api.sideconfigs.SideConfiguration import me.steven.indrev.blockentities.MachineBlockEntity import me.steven.indrev.blocks.HeliostatBlock import me.steven.indrev.blocks.machine.MachineBlock import me.steven.indrev.gui.ScrewdriverScreenHandlerFactory import me.steven.indrev.gui.screenhandlers.wrench.ScrewdriverScreenHandler import me.steven.indrev.registry.IRBlockRegistry import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtLong import net.minecraft.text.LiteralText import net.minecraft.util.ActionResult import net.minecraft.util.BlockRotation import net.minecraft.util.math.BlockPos import net.minecraft.world.World import java.util.* fun wrench( world: World, pos: BlockPos, blockState: BlockState, blockEntity: BlockEntity?, player: PlayerEntity?, stack: ItemStack ): ActionResult { val block = blockState.block if (player?.isSneaking == true && block is MachineBlock) { block.writeNbtComponents(world, player, pos, blockState, blockEntity, stack) world.breakBlock(pos, false, player) } else { val rotated = blockState.rotate(BlockRotation.CLOCKWISE_90) if (rotated == blockState) return ActionResult.PASS world.setBlockState(pos, rotated) } return ActionResult.success(world.isClient) } fun screwdriver( world: World, pos: BlockPos, blockState: BlockState, blockEntity: BlockEntity?, player: PlayerEntity?, stack: ItemStack ): ActionResult { if (blockState.isOf(IRBlockRegistry.HELIOSTAT_BLOCK)) { val positions = LongOpenHashSet() positions.add(pos.asLong()) HeliostatBlock.findConnectingHeliostats(pos, world, LongOpenHashSet(), positions) val tagList = stack.orCreateNbt.getList("SelectedHeliostats", 4) positions.forEach { long -> tagList.add(NbtLong.of(long)) } stack.orCreateNbt.put("SelectedHeliostats", tagList) player?.sendMessage(LiteralText("Click on Solar Power Plant Tower to link the Heliostats."), true) } else if (blockEntity is MachineBlockEntity<*>) { if (ConfigurationType.getTypes(blockEntity).isNotEmpty()) { val map = EnumMap(ConfigurationType::class.java) map[ConfigurationType.ITEM] = if (blockEntity.isConfigurable(ConfigurationType.ITEM)) blockEntity.getCurrentConfiguration(ConfigurationType.ITEM) else SideConfiguration.EMPTY_ITEM map[ConfigurationType.FLUID] = if (blockEntity.isConfigurable(ConfigurationType.FLUID)) blockEntity.getCurrentConfiguration(ConfigurationType.FLUID) else SideConfiguration.EMPTY_FLUID map[ConfigurationType.ENERGY] = if (blockEntity.isConfigurable(ConfigurationType.ENERGY)) blockEntity.getCurrentConfiguration(ConfigurationType.ENERGY) else SideConfiguration.EMPTY_ENERGY player?.openHandledScreen(ScrewdriverScreenHandlerFactory({ syncId, inv, ctx -> ScrewdriverScreenHandler(syncId, inv, ctx, map) }, pos, blockEntity)) return ActionResult.success(world.isClient) } } return ActionResult.PASS } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/itemutils.kt ================================================ package me.steven.indrev.utils import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant import net.fabricmc.fabric.api.transfer.v1.storage.Storage import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World import java.util.* import java.util.function.LongFunction typealias ItemFilter = (ItemVariant) -> Boolean val itemApiCache = WeakHashMap, Direction>>>() fun itemStorageOf(world: ServerWorld, blockPos: BlockPos, direction: Direction): Storage? { return itemApiCache .computeIfAbsent(world) { Long2ObjectOpenHashMap() } .computeIfAbsent(blockPos.asLong(), LongFunction { BlockApiCache.create(ItemStorage.SIDED, world, blockPos) }) .find(direction) } fun itemStorageOf(world: World, blockPos: BlockPos, direction: Direction): Storage? { return if (world is ServerWorld) itemStorageOf(world, blockPos, direction) else ItemStorage.SIDED.find(world, blockPos, direction) } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/machineinteractions.kt ================================================ package me.steven.indrev.utils import me.steven.indrev.api.machines.TransferMode import me.steven.indrev.blockentities.MachineBlockEntity import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.storage.Storage import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil import net.minecraft.block.ChestBlock import net.minecraft.block.InventoryProvider import net.minecraft.block.entity.ChestBlockEntity import net.minecraft.inventory.Inventory import net.minecraft.inventory.SidedInventory import net.minecraft.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.world.World import team.reborn.energy.api.EnergyStorageUtil fun MachineBlockEntity<*>.transferItems() { itemTransferCooldown-- if (itemTransferCooldown <= 0) { itemTransferCooldown = 0 inventoryComponent?.itemConfig?.forEach { (direction, mode) -> val pos = pos.offset(direction) val inventory = inventoryComponent?.inventory ?: return@forEach if (mode.output && inventoryComponent!!.itemConfig.autoPush) { val neighborInv = getInvAt(world!!, pos) if (neighborInv != null) { inventory.outputSlots.forEach { slot -> transferItems(inventory, neighborInv, slot, direction) } return@forEach } val insertable = itemStorageOf(world!!, pos, direction.opposite) val extractable = itemStorageOf(world!!, this.pos, direction) StorageUtil.move(extractable, insertable, { true }, 64, null) } if (mode.input && inventoryComponent!!.itemConfig.autoPull) { val neighborInv = getInvAt(world!!, pos) if (neighborInv != null) { getAvailableSlots(neighborInv, direction.opposite).forEach { slot -> transferItems(neighborInv, inventory, slot, direction.opposite) } return@forEach } val extractable = itemStorageOf(world!!, pos, direction.opposite) val insertable = itemStorageOf(world!!, this.pos, direction) StorageUtil.move(extractable, insertable, { true }, 64, null) } } } } fun MachineBlockEntity<*>.transferItems(from: Inventory, to: Inventory, slot: Int, direction: Direction) { val toTransfer = from.getStack(slot) while (!toTransfer.isEmpty) { val firstSlot = (0 until to.size()).firstOrNull { firstSlot -> val firstStack = to.getStack(firstSlot) (canMergeItems(firstStack, toTransfer) || firstStack.isEmpty) && (to !is SidedInventory || to.canInsert(firstSlot, toTransfer, direction.opposite)) } ?: break val targetStack = to.getStack(firstSlot) if (from is SidedInventory && !from.canExtract(slot, toTransfer, direction)) break val availableSize = (toTransfer.maxCount - targetStack.count).coerceAtMost(toTransfer.count) if (!targetStack.isEmpty) { toTransfer.count -= availableSize targetStack.count += availableSize } else { from.setStack(slot, ItemStack.EMPTY) to.setStack(firstSlot, toTransfer) break } itemTransferCooldown = 12 } } private fun getAvailableSlots(inventory: Inventory, side: Direction): IntArray = if (inventory is SidedInventory) inventory.getAvailableSlots(side) ?: EMPTY_INT_ARRAY else (0 until inventory.size()).map { it }.toIntArray() private fun canMergeItems(first: ItemStack, second: ItemStack): Boolean = first.item == second.item && first.damage == second.damage && first.count < first.maxCount && ItemStack.areNbtEqual(first, second) private fun getInvAt(world: World, pos: BlockPos): Inventory? { val blockState = world.getBlockState(pos) val block = blockState?.block return when { block is InventoryProvider -> block.getInventory(blockState, world, pos) blockState?.hasBlockEntity() == true -> { val blockEntity = world.getBlockEntity(pos) as? Inventory ?: return null if (blockEntity is ChestBlockEntity && block is ChestBlock) ChestBlock.getInventory(block, blockState, world, pos, true) else blockEntity } else -> null } } fun MachineBlockEntity<*>.transferFluids() { fluidComponent?.transferConfig?.forEach innerForEach@{ (direction, mode) -> if (mode == TransferMode.NONE) return@innerForEach var extractable: Storage? = null var insertable: Storage? = null if (mode.output) { insertable = fluidStorageOf(world as ServerWorld, pos.offset(direction), direction.opposite) extractable = fluidStorageOf(world as ServerWorld, pos, direction) } if (mode.input) { extractable = fluidStorageOf(world as ServerWorld, pos.offset(direction), direction.opposite) insertable = fluidStorageOf(world as ServerWorld, pos, direction) } if (extractable != null && insertable != null) StorageUtil.move(extractable, insertable, { true }, getFluidTransferRate(), null) } } fun MachineBlockEntity<*>.transferEnergy() { val world = world as ServerWorld Direction.values() .forEach { direction -> if (validConnections.contains(direction)) { val sourceIo = energyOf(world, pos, direction) val targetIo = energyOf(world, pos.offset(direction), direction.opposite) if (sourceIo == null || targetIo == null) validConnections.remove(direction) else if (sourceIo.supportsExtraction() && targetIo.supportsInsertion()) EnergyStorageUtil.move(sourceIo, targetIo, Long.MAX_VALUE, null) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/utils/utils.kt ================================================ package me.steven.indrev.utils import com.google.gson.JsonElement import me.shedaniel.math.Point import me.shedaniel.rei.api.client.gui.widgets.Widget import me.shedaniel.rei.api.client.gui.widgets.Widgets import me.steven.indrev.IndustrialRevolution import me.steven.indrev.gui.widgets.machines.TANK_BOTTOM import net.fabricmc.fabric.api.item.v1.FabricItemSettings import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction import net.minecraft.block.Block import net.minecraft.block.entity.BlockEntityType import net.minecraft.client.MinecraftClient import net.minecraft.client.util.SpriteIdentifier import net.minecraft.entity.player.PlayerInventory import net.minecraft.fluid.Fluid import net.minecraft.inventory.Inventory import net.minecraft.item.Item import net.minecraft.nbt.NbtCompound import net.minecraft.recipe.Recipe import net.minecraft.recipe.RecipeManager import net.minecraft.recipe.RecipeType import net.minecraft.screen.PlayerScreenHandler import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerContext import net.minecraft.text.OrderedText import net.minecraft.util.Identifier import net.minecraft.util.math.ChunkPos import net.minecraft.util.math.Direction import net.minecraft.util.registry.Registry val EMPTY_INT_ARRAY = intArrayOf() fun identifier(id: String) = Identifier(IndustrialRevolution.MOD_ID, id) fun blockSpriteId(id: String) = SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier(id)) fun Identifier.block(block: Block): Identifier { Registry.register(Registry.BLOCK, this, block) return this } fun Identifier.fluid(fluid: Fluid): Identifier { Registry.register(Registry.FLUID, this, fluid) return this } fun Identifier.item(item: Item): Identifier { Registry.register(Registry.ITEM, this, item) return this } fun Identifier.blockEntityType(entityType: BlockEntityType<*>): Identifier { Registry.register(Registry.BLOCK_ENTITY_TYPE, this, entityType) return this } fun itemSettings(): FabricItemSettings = FabricItemSettings().group(IndustrialRevolution.MOD_GROUP) fun Identifier.registerScreenHandler( f: (Int, PlayerInventory, ScreenHandlerContext) -> T ): ExtendedScreenHandlerType = ScreenHandlerRegistry.registerExtended(this) { syncId, inv, buf -> f(syncId, inv, ScreenHandlerContext.create(inv.player.world, buf.readBlockPos())) } as ExtendedScreenHandlerType fun ChunkPos.toNbt() = NbtCompound().also { it.putInt("x", x) it.putInt("z", z) } fun getChunkPos(nbt: NbtCompound) = ChunkPos(nbt.getInt("x"), nbt.getInt("z")) fun getFluidFromJson(json: JsonElement): Array { if (json.isJsonArray) { return json.asJsonArray.map { getFluidFromJson(it.asJsonObject).toList() }.flatten().toTypedArray() } else { val json = json.asJsonObject val fluidId = json.get("fluid").asString val fluidKey = FluidVariant.of(Registry.FLUID.get(Identifier(fluidId))) val fluidAmount = json.get("amount").asLong return arrayOf(fluidAmount of fluidKey) } } fun createREIFluidWidget(widgets: MutableList, startPoint: Point, fluid: IRFluidAmount) { widgets.add(Widgets.createTexturedWidget(TANK_BOTTOM.image, startPoint.x, startPoint.y, 0f, 0f, 16, 52, 16, 52)) widgets.add(Widgets.createDrawableWidget { _, matrices, mouseX, mouseY, _ -> fluid.renderGuiRect(startPoint.x + 2.0, startPoint.y.toDouble() + 1.5, startPoint.x.toDouble() + 14, startPoint.y.toDouble() + 50) if (mouseX > startPoint.x && mouseX < startPoint.x + 16 && mouseY > startPoint.y && mouseY < startPoint.y + 52) { val information = mutableListOf() information.addAll(getTooltip(fluid.resource, fluid.amount, -1)) MinecraftClient.getInstance().currentScreen?.renderOrderedTooltip(matrices, information, mouseX, mouseY) } }) } fun pack(dirs: Collection): Byte { var i = 0 dirs.forEach { dir -> i = i or (1 shl dir.id) } return i.toByte() } fun unpack(byte: Byte): List { val i = byte.toInt() return DIRECTIONS.filter { dir -> i and (1 shl dir.id) != 0 } } private val DIRECTIONS = Direction.values() val Fluid?.rawId: Int get() = Registry.FLUID.getRawId(this) inline fun transaction(block: (Transaction) -> T) = Transaction.openOuter().use(block) @Suppress("UNCHECKED_CAST") fun > RecipeManager.getRecipes(type: RecipeType) = getAllOfType(type) as Map ================================================ FILE: src/main/kotlin/me/steven/indrev/world/features/IRConfiguredFeature.kt ================================================ package me.steven.indrev.world.features import net.fabricmc.fabric.api.biome.v1.BiomeModification import net.fabricmc.fabric.api.biome.v1.BiomeSelectionContext import net.minecraft.tag.BiomeTags import net.minecraft.util.Identifier import net.minecraft.util.registry.BuiltinRegistries import net.minecraft.util.registry.Registry import net.minecraft.util.registry.RegistryEntry import net.minecraft.util.registry.RegistryKey import net.minecraft.world.World import net.minecraft.world.biome.Biome import net.minecraft.world.dimension.DimensionOptions import net.minecraft.world.gen.GenerationStep import net.minecraft.world.gen.feature.ConfiguredFeature import net.minecraft.world.gen.feature.PlacedFeature class IRConfiguredFeature( val identifier: Identifier, val step: GenerationStep.Feature, val configuredFeature: ConfiguredFeature<*, *>, val placedFeature: (ConfiguredFeature<*, *>) -> PlacedFeature, val biomePredicate: (BiomeSelectionContext) -> Boolean ) { val configuredFeatureKey = RegistryKey.of(Registry.CONFIGURED_FEATURE_KEY, identifier) val placedFeatureKey = RegistryKey.of(Registry.PLACED_FEATURE_KEY, identifier) init { BuiltinRegistries.add(BuiltinRegistries.CONFIGURED_FEATURE, configuredFeatureKey.value, configuredFeature) BuiltinRegistries.add(BuiltinRegistries.PLACED_FEATURE, placedFeatureKey.value, placedFeature(configuredFeature)) } companion object { //TODO val IS_OVERWORLD: (BiomeSelectionContext) -> Boolean = { ctx -> ctx.canGenerateIn(DimensionOptions.OVERWORLD) } val IS_NETHER: (BiomeSelectionContext) -> Boolean = { ctx -> ctx.canGenerateIn(DimensionOptions.NETHER) } } } ================================================ FILE: src/main/kotlin/me/steven/indrev/world/features/SulfurCrystalFeature.kt ================================================ package me.steven.indrev.world.features import com.mojang.serialization.Codec import me.steven.indrev.blocks.misc.SulfurCrystalBlock import me.steven.indrev.registry.IRBlockRegistry import me.steven.indrev.utils.any import me.steven.indrev.utils.forEach import net.minecraft.block.Blocks import net.minecraft.block.Material import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.world.gen.feature.DefaultFeatureConfig import net.minecraft.world.gen.feature.Feature import net.minecraft.world.gen.feature.util.FeatureContext class SulfurCrystalFeature(codec: Codec) : Feature(codec) { override fun generate( context: FeatureContext ): Boolean { val blockPos = context.origin val world = context.world val random = context.random val mutablePos = BlockPos.Mutable() val coveredArea = Box(blockPos).expand(8.0, 8.0, 8.0) val isNearLava = coveredArea.any { x, y, z -> if (context.world.isOutOfHeightLimit(y)) return@any false mutablePos.set(x, y, z) world?.getBlockState(mutablePos)?.isOf(Blocks.LAVA) == true } if (!isNearLava) return false coveredArea.forEach { x, y, z -> if (context.world.isOutOfHeightLimit(y)) return@forEach mutablePos.set(x, y, z) DIRECTIONS_LIST.shuffled(random).forEach { dir -> val blockState = world?.getBlockState(mutablePos) val pos = mutablePos.offset(dir) val airState = world?.getBlockState(pos) val state = IRBlockRegistry.SULFUR_CRYSTAL_CLUSTER.defaultState.with(SulfurCrystalBlock.FACING, dir) if (blockState?.material == Material.STONE && airState?.isAir == true && state.canPlaceAt(world, pos)) { world.setBlockState(pos, state, 2) return true } } } return false } companion object { private val DIRECTIONS_LIST = Direction.values().toMutableList() } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/biomass_composter.json ================================================ { "multipart": [ { "apply": { "model": "block/composter" } }, { "when": { "closed": true }, "apply": { "model": "indrev:block/plank_block" } } ] } ================================================ FILE: src/main/resources/assets/indrev/blockstates/block_base.json ================================================ { "variants": { "": { "model": "indrev:block/block_base" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/block_highlight.json ================================================ { "variants": { "": { "model": "indrev:block/block_highlight" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/block_shadow.json ================================================ { "variants": { "": { "model": "indrev:block/block_shadow" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/bronze_block.json ================================================ { "variants": { "": { "model": "indrev:block/bronze_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/cabinet.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/cabinet", "y": 90 }, "facing=south": { "model": "indrev:block/cabinet", "y": 270 }, "facing=west": { "model": "indrev:block/cabinet", "y": 180 }, "facing=east": { "model": "indrev:block/cabinet" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/capsule.json ================================================ { "variants": { "": { "model": "indrev:block/capsule" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/charge_pad_mk4.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/charge_pad_mk4" }, "facing=south": { "model": "indrev:block/charge_pad_mk4", "y": 180 }, "facing=west": { "model": "indrev:block/charge_pad_mk4", "y": 270 }, "facing=east": { "model": "indrev:block/charge_pad_mk4", "y": 90 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/composting.json ================================================ { "variants": { "": { "model": "block/composter_contents1" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/controller.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/controller" }, "facing=south": { "model": "indrev:block/controller", "y": 180 }, "facing=west": { "model": "indrev:block/controller", "y": 270 }, "facing=east": { "model": "indrev:block/controller", "y": 90 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/coolant.json ================================================ { "variants": { "": { "model": "minecraft:block/water" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/deepslate_lead_ore.json ================================================ { "variants": { "": { "model": "indrev:block/deepslate_lead_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/deepslate_nikolite_ore.json ================================================ { "variants": { "": { "model": "indrev:block/deepslate_nikolite_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/deepslate_silver_ore.json ================================================ { "variants": { "": { "model": "indrev:block/deepslate_silver_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/deepslate_tin_ore.json ================================================ { "variants": { "": { "model": "indrev:block/deepslate_tin_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/deepslate_tungsten_ore.json ================================================ { "variants": { "": { "model": "indrev:block/deepslate_tungsten_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/drain_mk1.json ================================================ { "variants": { "": { "model": "indrev:block/drain_mk1" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/drill_bottom.json ================================================ { "variants": { "": { "model": "indrev:block/drill_bottom" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/drill_middle.json ================================================ { "variants": { "": { "model": "indrev:block/drill_middle" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/drill_top.json ================================================ { "variants": { "working=false": { "model": "indrev:block/drill_top" }, "working=true": { "model": "indrev:block/drill_top_on" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/duct.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/duct" }, "facing=south": { "model": "indrev:block/duct", "y": 180 }, "facing=west": { "model": "indrev:block/duct", "y": 270 }, "facing=east": { "model": "indrev:block/duct", "y": 90 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/electrum_block.json ================================================ { "variants": { "": { "model": "indrev:block/electrum_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/fisher_mk2.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/fishing_farm_mk2" }, "facing=south": { "model": "indrev:block/fishing_farm_mk2", "y": 180 }, "facing=west": { "model": "indrev:block/fishing_farm_mk2", "y": 270 }, "facing=east": { "model": "indrev:block/fishing_farm_mk2", "y": 90 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/fisher_mk3.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/fishing_farm_mk3" }, "facing=south": { "model": "indrev:block/fishing_farm_mk3", "y": 180 }, "facing=west": { "model": "indrev:block/fishing_farm_mk3", "y": 270 }, "facing=east": { "model": "indrev:block/fishing_farm_mk3", "y": 90 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/fisher_mk4.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/fishing_farm_mk4" }, "facing=south": { "model": "indrev:block/fishing_farm_mk4", "y": 180 }, "facing=west": { "model": "indrev:block/fishing_farm_mk4", "y": 270 }, "facing=east": { "model": "indrev:block/fishing_farm_mk4", "y": 90 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/frame.json ================================================ { "variants": { "": { "model": "indrev:block/frame" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/heat_generator_mk4.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/heat_generator_mk4" }, "facing=south": { "model": "indrev:block/heat_generator_mk4", "y": 180 }, "facing=west": { "model": "indrev:block/heat_generator_mk4", "y": 270 }, "facing=east": { "model": "indrev:block/heat_generator_mk4", "y": 90 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/hydrogen.json ================================================ { "variants": { "": { "model": "minecraft:block/water" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/intake.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/intake" }, "facing=south": { "model": "indrev:block/intake", "y": 180 }, "facing=west": { "model": "indrev:block/intake", "y": 270 }, "facing=east": { "model": "indrev:block/intake", "y": 90 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/laser_emitter_mk4.json ================================================ { "variants": { "facing=north,powered=false": { "model": "indrev:block/laser" }, "facing=south,powered=false": { "model": "indrev:block/laser", "y": 180 }, "facing=west,powered=false": { "model": "indrev:block/laser", "y": 270 }, "facing=east,powered=false": { "model": "indrev:block/laser", "y": 90 }, "facing=down,powered=false": { "model": "indrev:block/laser", "x": 90 }, "facing=up,powered=false": { "model": "indrev:block/laser", "x": 270 }, "facing=north,powered=true": { "model": "indrev:block/laser_on" }, "facing=south,powered=true": { "model": "indrev:block/laser_on", "y": 180 }, "facing=west,powered=true": { "model": "indrev:block/laser_on", "y": 270 }, "facing=east,powered=true": { "model": "indrev:block/laser_on", "y": 90 }, "facing=down,powered=true": { "model": "indrev:block/laser_on", "x": 90 }, "facing=up,powered=true": { "model": "indrev:block/laser_on", "x": 270 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/lead_block.json ================================================ { "variants": { "": { "model": "indrev:block/lead_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/lead_ore.json ================================================ { "variants": { "": { "model": "indrev:block/lead_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/machine_block.json ================================================ { "variants": { "": { "model": "indrev:block/machine_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/methane.json ================================================ { "variants": { "": { "model": "minecraft:block/water" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/modular_workbench_mk4.json ================================================ { "variants": { "": { "model": "indrev:block/modular_workbench_mk4" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/molten_copper.json ================================================ { "variants": { "": { "model": "indrev:block/gray_lava" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/molten_gold.json ================================================ { "variants": { "": { "model": "indrev:block/gray_lava" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/molten_iron.json ================================================ { "variants": { "": { "model": "indrev:block/gray_lava" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/molten_lead.json ================================================ { "variants": { "": { "model": "indrev:block/gray_lava" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/molten_netherite.json ================================================ { "variants": { "": { "model": "indrev:block/gray_lava" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/molten_silver.json ================================================ { "variants": { "": { "model": "indrev:block/gray_lava" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/molten_tin.json ================================================ { "variants": { "": { "model": "indrev:block/gray_lava" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/nikolite_ore.json ================================================ { "variants": { "": { "model": "indrev:block/nikolite_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/ore_base.json ================================================ { "variants": { "": { "model": "indrev:block/ore_base" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/ore_highlight.json ================================================ { "variants": { "": { "model": "indrev:block/ore_highlight" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/oxygen.json ================================================ { "variants": { "": { "model": "minecraft:block/water" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/plank_block.json ================================================ { "variants": { "": { "model": "indrev:block/plank_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/planks.json ================================================ { "variants": { "layers=1": { "model": "indrev:block/planks_height2" }, "layers=2": { "model": "indrev:block/planks_height4" }, "layers=3": { "model": "indrev:block/planks_height6" }, "layers=4": { "model": "indrev:block/planks_height8" }, "layers=5": { "model": "indrev:block/planks_height10" }, "layers=6": { "model": "indrev:block/planks_height12" }, "layers=7": { "model": "indrev:block/planks_height14" }, "layers=8": { "model": "indrev:block/plank_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/pump_mk1.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/pump_mk1", "y": 270 }, "facing=south": { "model": "indrev:block/pump_mk1", "y": 90 }, "facing=west": { "model": "indrev:block/pump_mk1", "y": 180 }, "facing=east": { "model": "indrev:block/pump_mk1" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/raw_lead_block.json ================================================ { "variants": { "": { "model": "indrev:block/raw_lead_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/raw_silver_block.json ================================================ { "variants": { "": { "model": "indrev:block/raw_silver_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/raw_tin_block.json ================================================ { "variants": { "": { "model": "indrev:block/raw_tin_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/raw_tungsten_block.json ================================================ { "variants": { "": { "model": "indrev:block/raw_tungsten_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/silo.json ================================================ { "variants": { "": { "model": "indrev:block/silo" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/silver_block.json ================================================ { "variants": { "": { "model": "indrev:block/silver_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/silver_ore.json ================================================ { "variants": { "": { "model": "indrev:block/silver_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/steel_block.json ================================================ { "variants": { "": { "model": "indrev:block/steel_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/sulfur_crystal.json ================================================ { "variants": { "facing=north": { "model": "indrev:block/sulfur_crystal", "x": 90 }, "facing=south": { "model": "indrev:block/sulfur_crystal", "x": 90, "y": 180 }, "facing=west": { "model": "indrev:block/sulfur_crystal", "x": -90, "y": 90 }, "facing=east": { "model": "indrev:block/sulfur_crystal", "x": 270, "y": 270 }, "facing=up": { "model": "indrev:block/sulfur_crystal" }, "facing=down": { "model": "indrev:block/sulfur_crystal", "x": 180, "y": 180 } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/sulfuric_acid.json ================================================ { "variants": { "": { "model": "minecraft:block/water" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/tank.json ================================================ { "variants": { "up=false,down=false": { "model": "indrev:block/tank" }, "up=true,down=false": { "model": "indrev:block/tank_up" }, "up=false,down=true": { "model": "indrev:block/tank_down" }, "up=true,down=true": { "model": "indrev:block/tank_both" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/tin_block.json ================================================ { "variants": { "": { "model": "indrev:block/tin_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/tin_ore.json ================================================ { "variants": { "": { "model": "indrev:block/tin_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/toxic_mud.json ================================================ { "variants": { "": { "model": "minecraft:block/water" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/tungsten_block.json ================================================ { "variants": { "": { "model": "indrev:block/tungsten_block" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/tungsten_ore.json ================================================ { "variants": { "": { "model": "indrev:block/tungsten_ore" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/warning_strobe.json ================================================ { "variants": { "": { "model": "indrev:block/warning_strobe" } } } ================================================ FILE: src/main/resources/assets/indrev/blockstates/wither_proof_obsidian.json ================================================ { "variants": { "": { "model": "indrev:block/wither_proof_obsidian" } } } ================================================ FILE: src/main/resources/assets/indrev/lang/en_us.json ================================================ { "item.patchouli.industrial_revolution_book.name": "Revolutionary Guide", "item.indrev.guide_book": "Revolutionary Guide", "item.patchouli.industrial_revolution_book.landing": "This book will help you go through some of the important aspects of the mod.$(br2)If you find any issues, questions or suggestions, open an issue on $(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$() or join our $(l:https://discord.com/invite/G4PjhEf)Discord!$()$(br2)Thank you for playing!", "itemGroup.indrev.indrev_group": "Industrial Revolution", "block.machines.tooltip.io": "I/O: %s", "block.indrev.lazuli_flux_container_1": "Lazuli Flux", "block.indrev.lazuli_flux_container_2": "Container", "block.indrev.coal_generator": "Coal Generator", "block.indrev.coal_generator_mk1": "Coal Generator", "block.indrev.heat_generator": "Heat Generator", "block.indrev.heat_generator_mk4": "Heat Generator", "block.indrev.electric_furnace": "Electric Furnace", "block.indrev.electric_furnace_mk1": "Electric Furnace MK1", "block.indrev.electric_furnace_mk2": "Electric Furnace MK2", "block.indrev.electric_furnace_mk3": "Electric Furnace MK3", "block.indrev.electric_furnace_mk4": "Electric Furnace MK4", "block.indrev.electric_furnace_creative": "Electric Furnace Creative", "block.indrev.electric_furnace_factory": "Electric Furnace Factory", "block.indrev.electric_furnace_factory_mk4": "Electric Furnace Factory MK4", "block.indrev.smelter": "Industrial Smelter", "block.indrev.smelter_mk4": "Industrial Smelter", "block.indrev.condenser": "Condenser", "block.indrev.condenser_mk4": "Condenser", "block.indrev.pulverizer": "Pulverizer", "block.indrev.pulverizer_mk1": "Pulverizer MK1", "block.indrev.pulverizer_mk2": "Pulverizer MK2", "block.indrev.pulverizer_mk3": "Pulverizer MK3", "block.indrev.pulverizer_mk4": "Pulverizer MK4", "block.indrev.pulverizer_creative": "Pulverizer Creative", "block.indrev.pulverizer_factory": "Pulverizer Factory", "block.indrev.pulverizer_factory_mk4": "Pulverizer Factory MK4", "block.indrev.sawmill": "Sawmill", "block.indrev.sawmill_mk1": "Sawmill MK1", "block.indrev.sawmill_mk2": "Sawmill MK2", "block.indrev.sawmill_mk3": "Sawmill MK3", "block.indrev.sawmill_mk4": "Sawmill MK4", "block.indrev.sawmill_creative": "Sawmill Creative", "block.indrev.compressor": "Compressor", "block.indrev.compressor_mk1": "Compressor MK1", "block.indrev.compressor_mk2": "Compressor MK2", "block.indrev.compressor_mk3": "Compressor MK3", "block.indrev.compressor_mk4": "Compressor MK4", "block.indrev.compressor_creative": "Compressor Creative", "block.indrev.compressor_factory": "Compressor Factory", "block.indrev.compressor_factory_mk4": "Compressor Factory MK4", "block.indrev.solid_infuser_factory": "Solid Infuser Factory", "block.indrev.solid_infuser": "Solid Infuser", "block.indrev.solid_infuser_mk1": "Solid Infuser MK1", "block.indrev.solid_infuser_mk2": "Solid Infuser MK2", "block.indrev.solid_infuser_mk3": "Solid Infuser MK3", "block.indrev.solid_infuser_mk4": "Solid Infuser MK4", "block.indrev.solid_infuser_creative": "Solid Infuser Creative", "block.indrev.solid_infuser_factory_mk4": "Solid Infuser Factory MK4", "block.indrev.lazuli_flux_container_mk1": "Lazuli Flux Container MK1", "block.indrev.lazuli_flux_container_mk2": "Lazuli Flux Container MK2", "block.indrev.lazuli_flux_container_mk3": "Lazuli Flux Container MK3", "block.indrev.lazuli_flux_container_mk4": "Lazuli Flux Container MK4", "block.indrev.lazuli_flux_container_creative": "Lazuli Flux Container Creative", "block.indrev.cable_mk1": "Cable MK1", "block.indrev.cable_mk2": "Cable MK2", "block.indrev.cable_mk3": "Cable MK3", "block.indrev.cable_mk4": "Cable MK4", "block.indrev.fluid_pipe_mk1": "Fluid Pipe MK1", "block.indrev.fluid_pipe_mk2": "Fluid Pipe MK2", "block.indrev.fluid_pipe_mk3": "Fluid Pipe MK3", "block.indrev.fluid_pipe_mk4": "Fluid Pipe MK4", "block.indrev.item_pipe_mk1": "Item Pipe MK1", "block.indrev.item_pipe_mk2": "Item Pipe MK2", "block.indrev.item_pipe_mk3": "Item Pipe MK3", "block.indrev.item_pipe_mk4": "Item Pipe MK4", "block.indrev.fisher": "Fisher", "block.indrev.fisher_mk2": "Basic Fisher", "block.indrev.fisher_mk3": "Improved Fisher", "block.indrev.fisher_mk4": "Advanced Fisher", "block.indrev.data_card_writer": "Data Card Encoder", "block.indrev.data_card_writer_mk4": "Data Card Encoder", "block.indrev.mining_rig": "Mining Rig Computer", "block.indrev.mining_rig_mk4": "Mining Rig Computer", "block.indrev.mining_rig.tooltip": "You must use a Chunk Scanner in this chunk before using the miner", "block.indrev.mining_rig.mined": "cycles remaining", "block.indrev.drill": "Mining Rig Drill", "block.indrev.drill_bottom": "Mining Rig Drill", "block.indrev.drill_middle": "Mining Rig Drill", "block.indrev.drill_top": "Mining Rig Drill", "block.indrev.drill.faster": "%sx faster", "block.indrev.drill.no_drills": "Empty.", "block.indrev.drill.wrong_location": "Incorrect report for current location!", "block.indrev.drill.active": "Active drills", "block.indrev.drill.activating": "Not ready", "block.indrev.drill.not_enough_power": "Insufficient power", "block.indrev.drill.power_required": "Requires at least %sLF to operate", "block.indrev.recycler": "Recycler", "block.indrev.recycler_mk2": "Recycler", "block.indrev.cable": "Cable", "block.indrev.farmer_mk1": "Farmer MK1", "block.indrev.farmer_mk2": "Farmer MK2", "block.indrev.farmer_mk3": "Farmer MK3", "block.indrev.farmer_mk4": "Farmer MK4", "block.indrev.farmer_creative": "Creative Farmer", "block.indrev.farmer": "Farmer", "block.indrev.slaughter_mk1": "Slaughter MK1", "block.indrev.slaughter_mk2": "Slaughter MK2", "block.indrev.slaughter_mk3": "Slaughter MK3", "block.indrev.slaughter_mk4": "Slaughter MK4", "block.indrev.slaughter_creative": "Creative Slaughter", "block.indrev.slaughter": "Slaughter", "block.indrev.chopper": "Chopper", "block.indrev.chopper_mk1": "Chopper MK1", "block.indrev.chopper_mk2": "Chopper MK2", "block.indrev.chopper_mk3": "Chopper MK3", "block.indrev.chopper_mk4": "Chopper MK4", "block.indrev.chopper_creative": "Creative Chopper", "block.indrev.rancher": "Rancher", "block.indrev.rancher_mk1": "Rancher MK1", "block.indrev.rancher_mk2": "Rancher MK2", "block.indrev.rancher_mk3": "Rancher MK3", "block.indrev.rancher_mk4": "Rancher MK4", "block.indrev.rancher_creative": "Creative Rancher", "block.indrev.fluid_infuser": "Fluid Infuser", "block.indrev.fluid_infuser_mk1": "Fluid Infuser MK1", "block.indrev.fluid_infuser_mk2": "Fluid Infuser MK2", "block.indrev.fluid_infuser_mk3": "Fluid Infuser MK3", "block.indrev.fluid_infuser_mk4": "Fluid Infuser MK4", "block.indrev.fluid_infuser_creative": "Fluid Infuser Creative", "block.indrev.electrolytic_separator": "Electrolytic Separator", "block.indrev.electrolytic_separator_mk1": "Electrolytic Separator MK1", "block.indrev.electrolytic_separator_mk2": "Electrolytic Separator MK2", "block.indrev.electrolytic_separator_mk3": "Electrolytic Separator MK3", "block.indrev.electrolytic_separator_mk4": "Electrolytic Separator MK4", "block.indrev.electrolytic_separator_creative": "Electrolytic Separator Creative", "block.indrev.drain_mk1": "Drain", "block.indrev.tank": "Tank", "block.indrev.factory": "Factory", "block.indrev.biomass_generator": "Biomass Generator", "block.indrev.biomass_generator_mk3": "Biomass Generator", "block.indrev.solar_generator": "Solar Generator", "block.indrev.solar_generator_mk1": "Solar Generator", "block.indrev.solar_generator_mk3": "Solar Generator", "block.indrev.charge_pad_mk4": "Charge Pad", "block.indrev.charge_pad_mk4.tooltip": "Charges equipped items or anything you put in it!", "block.indrev.aoe.range": "Range: %s", "block.indrev.aoe.toggle.false": "Show range", "block.indrev.aoe.toggle.true": "Hide range", "block.indrev.planks": "Plank", "block.indrev.plank_block": "Plank Block", "block.indrev.wither_proof_obsidian": "Wither Proof Obsidian", "block.indrev.controller": "Controller", "block.indrev.frame": "Frame", "block.indrev.duct": "Duct", "block.indrev.warning_strobe": "Warning Strobe", "block.indrev.silo": "Silo", "block.indrev.intake": "Intake", "block.indrev.cabinet": "Cabinet", "block.indrev.coolant": "Coolant", "block.indrev.molten_iron": "Molten Iron", "block.indrev.molten_gold": "Molten Gold", "block.indrev.molten_copper": "Molten Copper", "block.indrev.molten_tin": "Molten Tin", "block.indrev.molten_netherite": "Molten Netherite", "item.indrev.ore_data_card": "Ore Data Card", "item.indrev.empty_ore_data_card": "Empty Ore Data Card", "item.indrev.ore_data_card.modifier.speed": "speed", "item.indrev.ore_data_card.modifier.size": "size", "item.indrev.ore_data_card.modifier.richness": "richness", "item.indrev.ore_data_card.modifier.rng": "RNG", "block.indrev.hydrogen": "Hydrogen", "item.indrev.hydrogen_bucket": "Hydrogen Bucket", "block.indrev.oxygen": "Oxygen", "item.indrev.oxygen_bucket": "Oxygen Bucket", "block.indrev.methane": "Methane", "item.indrev.methane_bucket": "Methane Bucket", "block.indrev.sulfuric_acid": "Sulfuric Acid", "item.indrev.sulfuric_acid_bucket": "Sulfuric Acid Bucket", "block.indrev.toxic_mud": "Toxic Mud", "item.indrev.toxic_mud_bucket": "Toxic Mud Bucket", "block.indrev.machine_block": "Machine Block", "item.indrev.modular_core": "Core of Modularity (Uncharged)", "item.indrev.modular_core_activated": "Core of Modularity", "item.indrev.sawdust": "Sawdust", "block.indrev.laser_emitter_mk4": "Laser Emitter", "block.indrev.capsule": "Capsule", "block.indrev.tin_ore": "Tin Ore", "block.indrev.tin_block": "Block of Tin", "item.indrev.tin_ingot": "Tin Ingot", "item.indrev.tin_chunk": "Tin Chunk", "item.indrev.tin_dust": "Tin Dust", "item.indrev.tin_plate": "Tin Plate", "item.indrev.tin_gear": "Tin Gear", "item.indrev.tin_nugget": "Tin Nugget", "item.indrev.molten_tin_bucket": "Molten Tin Bucket", "item.indrev.tin_purified_ore": "Purified Tin Ore", "item.indrev.copper_chunk": "Copper Chunk", "item.indrev.copper_dust": "Copper Dust", "item.indrev.copper_plate": "Copper Plate", "item.indrev.copper_gear": "Copper Gear", "item.indrev.copper_nugget": "Copper Nugget", "item.indrev.copper_purified_ore": "Purified Copper Ore", "item.indrev.molten_copper_bucket": "Molten Copper Bucket", "block.indrev.molten_lead": "Molten Lead", "item.indrev.molten_lead_bucket": "Molten Lead Bucket", "block.indrev.lead_ore": "Lead Ore", "block.indrev.lead_block": "Block of Lead", "item.indrev.lead_ingot": "Lead Ingot", "item.indrev.lead_chunk": "Lead Chunk", "item.indrev.lead_dust": "Lead Dust", "item.indrev.lead_plate": "Lead Plate", "item.indrev.lead_gear": "Lead Gear", "item.indrev.lead_nugget": "Lead Nugget", "item.indrev.lead_purified_ore": "Purified Lead Ore", "item.indrev.raw_lead": "Raw Lead", "item.indrev.raw_silver": "Raw Silver", "item.indrev.raw_tungsten": "Raw Tungsten", "item.indrev.raw_tin": "Raw Tin", "block.indrev.raw_lead_block": "Raw Lead Block", "block.indrev.raw_silver_block": "Raw Silver Block", "block.indrev.raw_tungsten_block": "Raw Tungsten Block", "block.indrev.raw_tin_block": "Raw Tin Block", "block.indrev.molten_silver": "Molten Silver", "item.indrev.molten_silver_bucket": "Molten Silver Bucket", "block.indrev.silver_ore": "Silver Ore", "block.indrev.silver_block": "Block of Silver", "item.indrev.silver_ingot": "Silver Ingot", "item.indrev.silver_chunk": "Silver Chunk", "item.indrev.silver_dust": "Silver Dust", "item.indrev.silver_plate": "Silver Plate", "item.indrev.silver_gear": "Silver Gear", "item.indrev.silver_nugget": "Silver Nugget", "item.indrev.silver_purified_ore": "Purified Silver Ore", "block.indrev.molten_tungsten": "Molten Tungsten", "item.indrev.molten_tungsten_bucket": "Molten Tungsten Bucket", "block.indrev.tungsten_ore": "Tungsten Ore", "block.indrev.tungsten_block": "Block of Tungsten", "item.indrev.tungsten_ingot": "Tungsten Ingot", "item.indrev.tungsten_chunk": "Tungsten Chunk", "item.indrev.tungsten_dust": "Tungsten Dust", "item.indrev.tungsten_plate": "Tungsten Plate", "item.indrev.tungsten_gear": "Tungsten Gear", "item.indrev.tungsten_nugget": "Tungsten Nugget", "item.indrev.tungsten_purified_ore": "Purified Tungsten Ore", "block.indrev.deepslate_tungsten_ore": "Deepslate Tungsten Ore", "block.indrev.deepslate_tin_ore": "Deepslate Tin Ore", "block.indrev.deepslate_silver_ore": "Deepslate Silver Ore", "block.indrev.deepslate_nikolite_ore": "Deepslate Nikolite Ore", "block.indrev.deepslate_lead_ore": "Deepslate Lead Ore", "block.indrev.electrum_block": "Block of Electrum", "item.indrev.electrum_ingot": "Electrum Ingot", "item.indrev.electrum_dust": "Electrum Dust", "item.indrev.electrum_plate": "Electrum Plate", "item.indrev.electrum_gear": "Electrum Gear", "item.indrev.electrum_nugget": "Electrum Nugget", "block.indrev.bronze_block": "Block of Bronze", "item.indrev.bronze_ingot": "Bronze Ingot", "item.indrev.bronze_chunk": "Bronze Chunk", "item.indrev.bronze_dust": "Bronze Dust", "item.indrev.bronze_plate": "Bronze Plate", "item.indrev.bronze_gear": "Bronze Gear", "item.indrev.bronze_nugget": "Bronze Nugget", "block.indrev.sulfur_crystal": "Sulfur Crystal", "item.indrev.sulfur_crystal": "Sulfur Crystal", "item.indrev.sulfur_dust": "Sulfur Dust", "block.indrev.steel_block": "Block of Steel", "item.indrev.steel_dust": "Steel Dust", "item.indrev.steel_ingot": "Steel Ingot", "item.indrev.steel_plate": "Steel Plate", "item.indrev.steel_gear": "Steel Gear", "item.indrev.steel_nugget": "Steel Nugget", "item.indrev.gold_dust": "Gold Dust", "item.indrev.gold_plate": "Gold Plate", "item.indrev.gold_chunk": "Gold Chunk", "item.indrev.gold_purified_ore": "Purified Gold Ore", "item.indrev.molten_gold_bucket": "Molten Gold Bucket", "item.indrev.iron_dust": "Iron Dust", "item.indrev.iron_plate": "Iron Plate", "item.indrev.iron_chunk": "Iron Chunk", "item.indrev.molten_iron_bucket": "Molten Iron Bucket", "item.indrev.iron_purified_ore": "Purified Iron Ore", "item.indrev.diamond_dust": "Diamond Dust", "item.indrev.coal_dust": "Coal Dust", "block.indrev.pump_mk1": "Pump", "item.indrev.empty_enhancer": "Empty Enhancer", "item.indrev.buffer_enhancer": "Buffer Enhancer", "item.indrev.buffer_enhancer.tooltip": "Increases energy capacity", "item.indrev.energy_enhancer": "Energy Efficiency Enhancer", "item.indrev.energy_enhancer.tooltip": "Decreases energy cost", "item.indrev.damage_enhancer": "Damage Enhancer", "item.indrev.damage_enhancer.tooltip": "Increases the damage done by a Slaughter", "item.indrev.speed_enhancer": "Speed Enhancer", "item.indrev.speed_enhancer.tooltip": "Increases processing speed and power consumption", "item.indrev.blast_furnace_enhancer": "Blasting Enhancer", "item.indrev.blast_furnace_enhancer.tooltip": "Allows the Electric Furnace to accept Blast Furnace recipes", "item.indrev.smoker_enhancer": "Smoking Enhancer", "item.indrev.smoker_enhancer.tooltip": "Allows Electric Furnace to accept Smoker recipes", "item.indrev.enhancers.incompatible": "This machine does not support this enhancer", "item.indrev.enhancers.count": "Limited by %s per slot by the machine", "item.indrev.rechargeable.tooltip": "Rechargeable", "item.indrev.chunk_scanner": "Chunk Scanner", "item.indrev.chunk_scanner.scanning": "Scanning...", "item.indrev.chunk_scanner.scanned1": "Finished Scanning! Check your inventory.", "item.indrev.chunk_scanner.scanned2": "Vein type: %s", "item.indrev.chunk_scanner.already_scanned": "This chunk is already scanned and it has a %s vein", "item.indrev.chunk_scanner.tooltip1": "Hold right click to start scanning this chunk", "item.indrev.chunk_scanner.tooltip2": "Vein type: %s", "item.indrev.chunk_scanner.tooltip3": "From %s to %s", "item.indrev.chunk_scanner.tooltip4": "Dimension: %s", "item.indrev.chunk_scanner.tooltip5": "Right Click to read more", "item.indrev.fan": "Fan", "item.indrev.cooler_cell": "Cooler Cell", "item.indrev.heatsink": "Heatsink", "item.indrev.heat_coil": "Heat Coil", "item.indrev.heat_coil.tooltip": "Converts Lazuli Flux into heat", "item.indrev.stone_drill_head": "Stone Drill Head", "item.indrev.iron_drill_head": "Iron Drill Head", "item.indrev.diamond_drill_head": "Diamond Drill Head", "item.indrev.netherite_drill_head": "Netherite Drill Head", "item.indrev.mining_drill_mk1": "Mining Drill MK1", "item.indrev.mining_drill_mk2": "Mining Drill MK2", "item.indrev.mining_drill_mk3": "Mining Drill MK3", "item.indrev.mining_drill_mk4": "Modular Mining Drill", "item.indrev.energy_reader": "Energy Reader", "item.indrev.energy_reader.use": "Energy stored:", "item.indrev.energy_reader.use1": "%s LF/tick", "item.indrev.enriched_nikolite_dust": "Enriched Nikolite Dust", "item.indrev.enriched_nikolite_ingot": "Enriched Nikolite Ingot", "item.indrev.battery": "Battery", "item.indrev.circuit_mk1": "MK1 Circuit", "item.indrev.circuit_mk2": "MK2 Circuit", "item.indrev.circuit_mk3": "MK3 Circuit", "item.indrev.circuit_mk4": "MK4 Circuit", "item.indrev.tier_upgrade_mk2": "MK2 Machine Upgrade", "item.indrev.tier_upgrade_mk2.tooltip": "Upgrades MK1 machines to MK2", "item.indrev.tier_upgrade_mk3": "MK3 Machine Upgrade", "item.indrev.tier_upgrade_mk3.tooltip": "Upgrades MK2 machines to MK3", "item.indrev.tier_upgrade_mk4": "MK4 Machine Upgrade", "item.indrev.tier_upgrade_mk4.tooltip": "Upgrades MK3 machines to MK4", "item.indrev.servo_output": "Output Servo", "item.indrev.servo_output.tooltip": "Pushes into connected inventories", "item.indrev.servo_retriever": "Retriever Servo", "item.indrev.servo_retriever.tooltip": "Pulls from connected inventories", "item.indrev.servo.mode": "Priority: ", "item.indrev.servo.mode.round_robin": "Round Robin", "item.indrev.servo.mode.round_robin.tooltip": "Prioritizes containers with least amount of the item/fluid", "item.indrev.servo.mode.nearest_first": "Nearest First", "item.indrev.servo.mode.nearest_first.tooltip": "Prioritizes closer containers", "item.indrev.servo.mode.furthest_first": "Furthest First", "item.indrev.servo.mode.furthest_first.tooltip": "Prioritizes farther containers", "item.indrev.servo.mode.random": "Random", "item.indrev.servo.mode.random.tooltip": "No prioritization", "item.indrev.biomass": "Biomass", "item.indrev.untanned_leather": "Untanned Leather", "item.indrev.coolant_bucket": "Coolant Bucket", "item.indrev.molten_netherite_bucket": "Molten Netherite Bucket", "item.indrev.netherite_scrap_purified_ore": "Purified Ancient Debris", "item.indrev.netherite_scrap_chunk": "Ancient Debris Chunk", "item.indrev.netherite_scrap_dust": "Netherite Scrap Dust", "item.indrev.hammer": "Hammer", "item.indrev.screwdriver": "Screwdriver", "item.indrev.wrench": "Wrench", "item.indrev.wrench.switch_mode": "Wrench Mode: %s", "item.indrev.wrench.title": "Configure Machine", "item.indrev.wrench.autopush": "Auto Push", "item.indrev.wrench.autopull": "Auto Pull", "item.indrev.wrench.item": "Item", "item.indrev.wrench.fluid": "Fluid", "item.indrev.wrench.energy": "Energy", "item.indrev.wrench.output": "Output", "item.indrev.wrench.input": "Input", "item.indrev.wrench.input_first": "Input (First Slot)", "item.indrev.wrench.input_second": "Input (Second Slot)", "item.indrev.wrench.input_output": "Input and Output", "item.indrev.wrench.none": "None", "item.indrev.wrench.tooltip": "Shift + Right Click to change mode", "item.indrev.wrench.tooltip1": "Mode: %s", "item.indrev.wrench.tooltip1.rotate": "Rotate", "item.indrev.wrench.tooltip1.configure": "Configure", "item.indrev.wrench.mode": "Mode: %s", "item.indrev.wrench.side.north": "North", "item.indrev.wrench.side.south": "South", "item.indrev.wrench.side.west": "West", "item.indrev.wrench.side.east": "East", "item.indrev.wrench.side.up": "Up", "item.indrev.wrench.side.down": "Down", "item.indrev.wrench.side.top": "Top", "item.indrev.wrench.side.bottom": "Bottom", "item.indrev.wrench.side.left": "Left", "item.indrev.wrench.side.right": "Right", "item.indrev.wrench.side.front": "Front", "item.indrev.wrench.side.back": "Back", "item.indrev.wrench.connected": "Connected to %s", "item.indrev.tech_soup": "Tech Soup", "item.indrev.scan_output": "Resource Report", "item.indrev.portable_charger": "Portable Charger", "item.indrev.gamer_axe": "Gamer Axe", "item.indrev.gamer_axe.true": "On", "item.indrev.gamer_axe.false": "Off", "item.indrev.gamer_axe.tooltip.true": "Press %s to toggle off", "item.indrev.gamer_axe.tooltip.false": "Press %s to toggle on", "item.indrev.modular_item.tooltip": "Press %s to toggle modules", "gui.indrev.button.auto_split": "Auto-Split Stacks", "gui.indrev.resourcereport.size": "Vein size: %s", "gui.indrev.resourcereport.size.tiny": "Tiny", "gui.indrev.resourcereport.size.small": "Small", "gui.indrev.resourcereport.size.average": "Average", "gui.indrev.resourcereport.size.big": "Big", "gui.indrev.resourcereport.size.very_big": "Very big", "gui.indrev.resourcereport.size.enormous": "Enormous", "gui.indrev.resourcereport.size.gigantic": "Gigantic", "gui.furnace.mode": "Mode", "gui.furnace.mode.furnace": "Furnace", "gui.furnace.mode.blast_furnace": "Blast Furnace", "gui.furnace.mode.smoker": "Smoker", "gui.widget.process": "Progress: %s", "gui.widget.energy": "Energy", "gui.widget.temperature": "Temperature", "gui.widget.temperature_info.high": "Too hot", "gui.widget.temperature_info.medium": "Full potential", "gui.widget.temperature_info.low": "Not heated up", "gui.indrev.solar.on": "Generating", "gui.indrev.heatgen.idle": "Idle", "gui.indrev.heatgen.title": "Consuming %s mB", "gui.indrev.heatgen.generating": "Generating %s LF", "gui.indrev.heatgen.pertick": "per tick", "gui.indrev.heatgen.extra": "Careful, it's hot!", "gui.indrev.guide_book_shortcut.contains": "Opens about page on the Revolutionary Guide", "gui.indrev.guide_book_shortcut.missing": "Missing the Revolutionary Guide!", "gui.indrev.tooltip.maxTransferRate": "Transfer capacity: ", "gui.indrev.tooltip.maxInput": "Input capacity: ", "gui.indrev.tooltip.maxOutput": "Output capacity: ", "gui.indrev.tooltip.maxEnergyStored": "Energy capacity: ", "gui.indrev.tooltip.seconds": "%s seconds", "gui.indrev.tooltip.energyCost": "Energy cost: ", "gui.indrev.tooltip.processSpeed": "Speed: ", "gui.indrev.tooltip.ratio": "Generation: ", "gui.indrev.tooltip.press_shift": "Hold %s for more information", "gui.indrev.tooltip.lftick": "%s LF/tick", "gui.indrev.tooltip.lf": "%s LF", "gui.indrev.tooltip.itemsec": "%s items/s", "gui.indrev.tooltip.fluidsec": "%s buckets/s", "gui.indrev.tooltip.temperatureBoost": "Heated up: ", "gui.indrev.locked": "Locked", "gui.indrev.modular_armor_slot_type": "Insert item here", "gui.indrev.module_slot_type": "Insert desired module here", "gui.indrev.scan_output_slot_type": "Insert data card", "gui.indrev.output_slot_type": "Collected items", "gui.indrev.chopper_input_axe": "Insert axe", "gui.indrev.chopper_input_bone_meal": "Insert bone meal", "gui.indrev.chopper_input_sapling": "Insert saplings", "gui.indrev.farmer_input_slot_type": "Seeds and bone meals", "gui.indrev.slaughter_input_sword": "Insert sword", "gui.indrev.cooler_slot_type": "Cooler", "gui.indrev.battery_slot_type": "Battery", "gui.indrev.upgrade_slot_type": "Upgrades", "gui.indrev.fishingrod": "Fishing rod", "indrev.category.rei.upgrading": "You must craft a %s and use it on a %s to upgrade it to %s", "indrev.category.rei.pulverizing": "Pulverizing", "indrev.category.rei.infusing": "Infusing", "indrev.category.rei.compressing": "Compressing", "indrev.category.rei.recycling": "Recycling", "indrev.category.rei.fluid_infusing": "Fluid Infusing", "indrev.category.rei.condensing": "Condensing", "indrev.category.rei.smelting": "Smelting", "indrev.category.rei.sawmill": "Sawmill", "indrev.category.rei.module": "Module Crafting", "item.indrev.nikolite_dust": "Nikolite Dust", "item.indrev.nikolite_ingot": "Nikolite Ingot", "block.indrev.nikolite_ore": "Nikolite Ore", "item.indrev.modular.upgrade": "Modules installed:", "item.indrev.modular.upgrade.night_vision": "Night Vision %s", "item.indrev.modular.upgrade.speed": "Speed %s", "item.indrev.modular.upgrade.jump_boost": "Jump Boost %s", "item.indrev.modular.upgrade.breathing": "Breathing %s", "item.indrev.modular.upgrade.protection": "Protection %s", "item.indrev.modular.upgrade.feather_falling": "Feather Falling %s", "item.indrev.modular.upgrade.auto_feeder": "Auto Feeder %s", "item.indrev.modular.upgrade.charger": "Charger %s", "item.indrev.modular.upgrade.solar_panel": "Solar Panel %s", "item.indrev.modular.upgrade.piglin_tricker": "Piglin Tricker %s", "item.indrev.modular.upgrade.elytra": "Builtin Elytra %s", "item.indrev.modular.upgrade.jetpack": "Builtin Jetpack %s", "item.indrev.modular.upgrade.magnet": "Magnet %s", "item.indrev.modular.upgrade.water_affinity": "Water Affinity %s", "item.indrev.modular.upgrade.fire_resistance": "Fire Resistance %s", "item.indrev.modular.upgrade.efficiency": "Efficiency %s", "item.indrev.modular.upgrade.range": "Range %s", "item.indrev.modular.upgrade.fortune": "Fortune %s", "item.indrev.modular.upgrade.silk_touch": "Silk Touch %s", "item.indrev.modular.upgrade.sharpness": "Sharpness %s", "item.indrev.modular.upgrade.looting": "Looting %s", "item.indrev.modular.upgrade.fire_aspect": "Fire Aspect %s", "item.indrev.modular.upgrade.reach": "Reach %s", "item.indrev.steel_helmet": "Steel Helmet", "item.indrev.steel_chestplate": "Steel Chestplate", "item.indrev.steel_leggings": "Steel Leggings", "item.indrev.steel_boots": "Steel Boots", "item.indrev.steel_sword": "Steel Sword", "item.indrev.steel_pickaxe": "Steel Pickaxe", "item.indrev.steel_axe": "Steel Axe", "item.indrev.steel_shovel": "Steel Shovel", "item.indrev.steel_hoe": "Steel Hoe", "item.indrev.bronze_helmet": "Bronze Helmet", "item.indrev.bronze_chestplate": "Bronze Chestplate", "item.indrev.bronze_leggings": "Bronze Leggings", "item.indrev.bronze_boots": "Bronze Boots", "item.indrev.bronze_sword": "Bronze Sword", "item.indrev.bronze_pickaxe": "Bronze Pickaxe", "item.indrev.bronze_axe": "Bronze Axe", "item.indrev.bronze_shovel": "Bronze Shovel", "item.indrev.bronze_hoe": "Bronze Hoe", "item.indrev.lead_helmet": "Lead Helmet", "item.indrev.lead_chestplate": "Lead Chestplate", "item.indrev.lead_leggings": "Lead Leggings", "item.indrev.lead_boots": "Lead Boots", "item.indrev.lead_sword": "Lead Sword", "item.indrev.lead_pickaxe": "Lead Pickaxe", "item.indrev.lead_axe": "Lead Axe", "item.indrev.lead_shovel": "Lead Shovel", "item.indrev.lead_hoe": "Lead Hoe", "item.indrev.silver_helmet": "Silver Helmet", "item.indrev.silver_chestplate": "Silver Chestplate", "item.indrev.silver_leggings": "Silver Leggings", "item.indrev.silver_boots": "Silver Boots", "item.indrev.silver_sword": "Silver Sword", "item.indrev.silver_pickaxe": "Silver Pickaxe", "item.indrev.silver_axe": "Silver Axe", "item.indrev.silver_shovel": "Silver Shovel", "item.indrev.silver_hoe": "Silver Hoe", "block.indrev.modular_workbench": "Modular Workbench", "block.indrev.modular_workbench_mk4": "Modular Workbench", "item.indrev.modular_armor_helmet": "Modular Helmet", "item.indrev.modular_armor_chest": "Modular Chestplate", "item.indrev.modular_armor_legs": "Modular Leggings", "item.indrev.modular_armor_boots": "Modular Boots", "item.indrev.module_parts": "Can be installed on", "item.indrev.module_parts_head": "Helmet", "item.indrev.module_parts_chest": "Chestplate", "item.indrev.module_parts_legs": "Legs", "item.indrev.module_parts_feet": "Boots", "item.indrev.module_parts_drill": "Mining Drill", "item.indrev.module_parts_gamer_axe": "Gamer Axe", "item.indrev.module_max_level": "Maximum level: %s", "item.indrev.module_protection": "Protection Module", "item.indrev.module_protection.tooltip": "Increases the Protection provided by the armor.", "item.indrev.module_feather_falling": "Feather Falling Module", "item.indrev.module_feather_falling.tooltip": "Fall damage is negated at cost of shield charge.", "item.indrev.module_speed": "Speed Module", "item.indrev.module_speed.tooltip": "Gives you Speed effect at cost of energy.", "item.indrev.module_jump_boost": "Jump Boost Module", "item.indrev.module_jump_boost.tooltip": "Gives you Jump Boost effect at cost of energy.", "item.indrev.module_night_vision": "Night Vision Module", "item.indrev.module_night_vision.tooltip": "Gives you Night Vision effect at cost of energy.", "item.indrev.module_breathing": "Breathing Module", "item.indrev.module_breathing.tooltip": "Gives you Breathing effect when underwater at cost of energy.", "item.indrev.module_auto_feeder": "Auto Feeder Module", "item.indrev.module_auto_feeder.tooltip": "Eats food from your inventory automatically at cost of energy.", "item.indrev.module_charger": "Charger Module", "item.indrev.module_charger.tooltip": "Recharges any items in your inventory.", "item.indrev.module_solar_panel": "Solar Panel Module", "item.indrev.module_solar_panel.tooltip": "Recharges your armor and held item when under sunlight.", "item.indrev.module_piglin_tricker": "Piglin Tricker Module", "item.indrev.module_piglin_tricker.tooltip": "Tricks Piglins into thinking you are wearing gold.", "item.indrev.module_elytra": "Builtin Elytra Module", "item.indrev.module_elytra.tooltip": "Installs a Reinforced Elytra on your chestplate.", "item.indrev.module_jetpack": "Builtin Jetpack Module", "item.indrev.module_jetpack.tooltip": "Installs a Jetpack on your chestplate.", "item.indrev.module_magnet": "Magnet Module", "item.indrev.module_magnet.tooltip": "Pulls items and experience towards you.", "item.indrev.module_water_affinity": "Water Affinity Module", "item.indrev.module_water_affinity.on": "When on %s", "item.indrev.module_water_affinity.chestplate": "Chestplate", "item.indrev.module_water_affinity.leggings": "Leggings", "item.indrev.module_water_affinity.tooltip": "mine faster underwater.", "item.indrev.module_water_affinity.tooltip1": "swim and walk faster underwater.", "item.indrev.module_fire_resistance": "Fire Resistance Module", "item.indrev.module_fire_resistance.tooltip": "Gives you Fire Resistance effect at cost of shield charge.", "item.indrev.module_efficiency": "Efficiency Module", "item.indrev.module_efficiency.tooltip": "Increases the speed of your mining tool.", "item.indrev.module_range": "Range Module", "item.indrev.module_range.tooltip": "Increases the Range of blocks your Drill can break at once.", "item.indrev.module_fortune": "Fortune Module", "item.indrev.module_fortune.tooltip": "Behaves like a Fortune enchantment.", "item.indrev.module_silk_touch": "Silk Touch Module", "item.indrev.module_silk_touch.tooltip": "Behaves like a Silk Touch enchantment.", "item.indrev.module_reach": "Reach Module", "item.indrev.module_reach.tooltip": "Damage multiple mobs at once.", "item.indrev.module_looting": "Looting Module", "item.indrev.module_looting.tooltip": "Behaves like a Looting enchantment.", "item.indrev.module_fire_aspect": "Fire Aspect Module", "item.indrev.module_fire_aspect.tooltip": "Behaves like a Fire Aspect enchantment.", "item.indrev.module_sharpness": "Sharpness Module", "item.indrev.module_sharpness.tooltip": "Increases the damage dealt.", "item.indrev.module_color.tooltip": "§cC§eo§al§9o§6§dr§1s", "item.indrev.module_color_pink": "Pink Color Module", "item.indrev.module_color_red": "Red Color Module", "item.indrev.module_color_purple": "Purple Color Module", "item.indrev.module_color_blue": "Blue Color Module", "item.indrev.module_color_cyan": "Cyan Color Module", "item.indrev.module_color_green": "Green Color Module", "item.indrev.module_color_yellow": "Yellow Color Module", "item.indrev.module_color_orange": "Orange Color Module", "item.indrev.module_color_black": "Black Color Module", "item.indrev.module_color_brown": "Brown Color Module", "item.indrev.copper_sword": "Copper Sword", "item.indrev.copper_pickaxe": "Copper Pickaxe", "item.indrev.copper_axe": "Copper Axe", "item.indrev.copper_shovel": "Copper Shovel", "item.indrev.copper_hoe": "Copper Hoe", "item.indrev.copper_helmet": "Copper Helmet", "item.indrev.copper_chestplate": "Copper Chestplate", "item.indrev.copper_leggings": "Copper Leggings", "item.indrev.copper_boots": "Copper Boots", "item.indrev.tin_sword": "Tin Sword", "item.indrev.tin_pickaxe": "Tin Pickaxe", "item.indrev.tin_axe": "Tin Axe", "item.indrev.tin_shovel": "Tin Shovel", "item.indrev.tin_hoe": "Tin Hoe", "item.indrev.tin_helmet": "Tin Helmet", "item.indrev.tin_chestplate": "Tin Chestplate", "item.indrev.tin_leggings": "Tin Leggings", "item.indrev.tin_boots": "Tin Boots", "category.indrev": "Industrial Revolution", "key.indrev.modular": "Modular item configuration", "key.indrev.gamer_axe_toggle": "Toggle Gamer Axe", "text.multiblock.not_built": "Structure not built!", "advancements.indrev.nikolite": "The Beginning", "advancements.indrev.nikolite.description": "Find Nikolite", "advancements.indrev.machine_block": "Foundations", "advancements.indrev.machine_block.description": "Get a Machine Block", "advancements.indrev.coal_generator": "First Steps", "advancements.indrev.coal_generator.description": "Get a Coal Generator", "advancements.indrev.basic_solar_generator": "Passive Energy", "advancements.indrev.basic_solar_generator.description": "Get a Basic Solar Generator", "advancements.indrev.advanced_solar_generator": "More Passive Energy", "advancements.indrev.advanced_solar_generator.description": "Get an Advanced Solar Generator", "advancements.indrev.heat_generator": "It's Getting Hotter", "advancements.indrev.heat_generator.description": "Get a Heat Generator", "advancements.indrev.biomass_generator": "Not so Eco-friendly", "advancements.indrev.biomass_generator.description": "Get a Biomass Generator", "advancements.indrev.pulverizer": "It's Getting Dusty", "advancements.indrev.pulverizer.description": "Get a Pulverizer", "advancements.indrev.electric_furnace": "Better Furnace", "advancements.indrev.electric_furnace.description": "Get an Electric Furnace", "advancements.indrev.compressor": "Watch Your Fingers", "advancements.indrev.compressor.description": "Get a Compressor", "advancements.indrev.recycler": "Eco-friendly", "advancements.indrev.recycler.description": "Get a Recycler", "advancements.indrev.solid_infuser": "Infusing", "advancements.indrev.solid_infuser.description": "Get a Solid Infuser", "advancements.indrev.mk2_upgrade": "Double Efficiency", "advancements.indrev.mk2_upgrade.description": "Upgrade a machine to MK2", "advancements.indrev.mk3_upgrade": "Triple Efficiency", "advancements.indrev.mk3_upgrade.description": "Upgrade a machine to MK3", "advancements.indrev.mk4_upgrade": "Factory. Must. Grow", "advancements.indrev.mk4_upgrade.description": "Upgrade a machine to MK4", "advancements.indrev.biomass": "Looks Gross...", "advancements.indrev.biomass.description": "Get Biomass", "advancements.indrev.nikolite_ingot": "New Horizons", "advancements.indrev.nikolite_ingot.description": "Get Nikolite Ingot", "advancements.indrev.enriched_nikolite_dust": "Powerful Electronics", "advancements.indrev.enriched_nikolite_dust.description": "Get Enriched Nikolite Dust", "advancements.indrev.enriched_nikolite_ingot": "More. Power.", "advancements.indrev.enriched_nikolite_ingot.description": "Get Enriched Nikolite Ingot", "advancements.indrev.lazuli_flux_container_mk1": "Basic Energy Storage", "advancements.indrev.lazuli_flux_container_mk1.description": "Get a Lazuli Flux Container MK1", "advancements.indrev.lazuli_flux_container_mk2": "Mediocre Energy Storage", "advancements.indrev.lazuli_flux_container_mk2.description": "Get a Lazuli Flux Container MK2", "advancements.indrev.lazuli_flux_container_mk3": "Not so Mediocre Energy Storage", "advancements.indrev.lazuli_flux_container_mk3.description": "Get a Lazuli Flux Container MK3", "advancements.indrev.lazuli_flux_container_mk4": "Ultimate Energy Storage", "advancements.indrev.lazuli_flux_container_mk4.description": "Get a Lazuli Flux Container MK4", "advancements.indrev.empty_enhancer": "A Pretty Plate", "advancements.indrev.empty_enhancer.description": "Get an Empty Enhancer", "advancements.indrev.speed_enhancer": "Machine go Brrrr", "advancements.indrev.speed_enhancer.description": "Get a Speed Upgrade", "advancements.indrev.buffer_enhancer": "Beyond the Limit", "advancements.indrev.buffer_enhancer.description": "Get a Buffer Upgrade", "advancements.indrev.energy_enhancer": "Always Efficient", "advancements.indrev.energy_enhancer.description": "Get an Energy Upgrade", "advancements.indrev.cable_mk1": "Yellow Cables!", "advancements.indrev.cable_mk1.description": "Get a Cable MK1", "advancements.indrev.cable_mk2": "Blue Cables!", "advancements.indrev.cable_mk2.description": "Get a Cable MK2", "advancements.indrev.cable_mk3": "Purple Cables!", "advancements.indrev.cable_mk3.description": "Get a Cable MK3", "advancements.indrev.cable_mk4": "Red Cables!", "advancements.indrev.cable_mk4.description": "Get a Cable MK4", "advancements.indrev.modular_workbench": "It Does not Look Like a Workbench", "advancements.indrev.modular_workbench.description": "Get a Modular Workbench", "advancements.indrev.modular_armor": "Modularity", "advancements.indrev.modular_armor.description": "Get a Modular Armor full set", "advancements.indrev.gamer_axe": "Not Enough RGB", "advancements.indrev.gamer_axe.description": "Get a Gamer Axe", "advancements.indrev.chopper_mk4": "Auto Wood Farm", "advancements.indrev.chopper_mk4.description": "Get a Chopper", "advancements.indrev.rancher_mk1": "I, Robot Farmer", "advancements.indrev.rancher_mk1.description": "Get a Farmer", "advancements.indrev.rancher_mk4": "Mechanized Production", "advancements.indrev.rancher_mk4.description": "Get a Rancher", "advancements.indrev.industrial_smelter": "Enhanced Ore Processing", "advancements.indrev.industrial_smelter.description": "Get an Industrial Smelter", "advancements.indrev.condenser": "I Ran Out of Ideas", "advancements.indrev.condenser.description": "Get a Condenser", "advancements.indrev.blast_furnace_enhancer": "Blast Electric Furnace", "advancements.indrev.blast_furnace_enhancer.description": "Get the Smoking Upgrade", "advancements.indrev.smoker_enhancer": "A Chef's Dream", "advancements.indrev.smoker_enhancer.description": "Get the Smoking Upgrade", "advancements.indrev.circuit_mk1": "First Machines", "advancements.indrev.circuit_mk1.description": "Get a MK1 Circuit", "advancements.indrev.circuit_mk2": "New Technology", "advancements.indrev.circuit_mk2.description": "Get a MK2 Circuit", "advancements.indrev.circuit_mk3": "Advanced Technology", "advancements.indrev.circuit_mk3.description": "Get a MK3 Circuit", "advancements.indrev.circuit_mk4": "Ultimate Technology", "advancements.indrev.circuit_mk4.description": "Get a MK4 Circuit", "advancements.indrev.farmer_mk1": "Happy Little Farm", "advancements.indrev.farmer_mk1.description": "Get a MK1 Farmer", "advancements.indrev.slaughter_mk1": "Ultimate Mob Killer", "advancements.indrev.slaughter_mk1.description": "Get a MK1 Slaughter", "gui.indrev.tip": "TIP ", "gui.indrev.tip_0": "Make sure to configure fluid and item input and output", "gui.indrev.tip_1": "Do not use cables above the input limit", "gui.indrev.tip_2": "Use coolers like a cool guy", "gui.indrev.tip_3": "Use coolers to never loose efficiency", "gui.indrev.tip_4": "Factory. Must. Grow.", "gui.indrev.tip_5": "You should check out the Modular Armor", "gui.indrev.tip_6": "Real gamers use the Gamer Axe", "gui.indrev.tip_7": "If you have issues or suggestions join our Discord!", "gui.indrev.tip_8": "Always backup your worlds before updating", "gui.indrev.tip_9": "Cover your toes at night", "gui.indrev.modules_installed": "Modules installed: ", "gui.indrev.shield": "Shield: ", "gui.indrev.installing": "Installing", "gui.indrev.incompatible": "Cannot be installed", "gui.indrev.max_level": "Maximum level hit", "gui.indrev.progress": "Progress: ", "gui.indrev.whitelist.true": "Whitelist", "gui.indrev.whitelist.false": "Blacklist", "gui.indrev.matchDurability.true": "Match item damage", "gui.indrev.matchDurability.false": "Ignore item damage", "gui.indrev.matchTag.true": "Match item NBT", "gui.indrev.matchTag.false": "Ignore item NBT", "death.attack.acid": "%s was dissolved in sulfuric acid", "death.attack.acid.player": "%s was dissolved in sulfuric acid while trying to escape %s", "death.attack.laser": "%s ran into the laser and got fried.", "death.attack.laser.player": "%s ran into the laser and got fried while trying to escape %s", "vein.indrev.bauxite": "Bauxite", "vein.indrev.certus_quartz": "Certus Quartz", "vein.indrev.peat": "Peat", "vein.indrev.lignite": "Lignite", "vein.indrev.bituminous": "Bituminous", "vein.indrev.anthracite": "Anthracite", "vein.indrev.cuprite": "Cuprite", "vein.indrev.calaverite": "Calaverite", "vein.indrev.calaverite_nether": "Calaverite (Nether)", "vein.indrev.iridium": "Iridium", "vein.indrev.siderite": "Siderite", "vein.indrev.limonite": "Limonite", "vein.indrev.hematite": "Hematite", "vein.indrev.magnetite": "Magnetite", "vein.indrev.chalcopryte": "Chalcopryte", "vein.indrev.nikolite": "Nikolite", "vein.indrev.quartz": "Quartz", "vein.indrev.argentite": "Argentite", "vein.indrev.chlorargyrite": "Chlorargyrite", "vein.indrev.cassiterite": "Cassiterite", "vein.indrev.stannite": "Stannite", "vein.indrev.scheelite": "Scheelite", "vein.indrev.wolframite": "Wolframite", "vein.indrev.ferberite": "Ferberite", "vein.indrev.galena": "Galena", "vein.indrev.glowstonedeposit": "Glowstone Deposit", "vein.indrev.soul_nether": "Nether Soul Deposit", "vein.indrev.sulfur_nether": "Sulfur", "vein.indrev.denseice": "Dense Ice Deposit", "attribute.indrev.shield": "Shield", "subtitles.indrev.laser": "Laser beam", "item.indrev.reinforced_elytra": "Reinforced Elytra", "item.indrev.jetpack_mk1": "Jetpack MK1", "item.indrev.jetpack_mk2": "Jetpack MK2", "item.indrev.jetpack_mk3": "Jetpack MK3", "item.indrev.jetpack_mk4": "Jetpack MK4", "block.indrev.gas_generator": "Gas Burning Generator", "block.indrev.gas_generator_mk4": "Gas Burning Generator", "item.indrev.soot": "Soot", "item.indrev.carbon_fiber_plate": "Carbon Fiber Plate", "item.indrev.carbon_fiber_rod": "Carbon Fiber Rod", "item.indrev.carbon_fiber_helmet_frame": "Carbon Fiber Chestplate Frame", "item.indrev.carbon_fiber_chest_frame": "Carbon Fiber Helmet Frame", "item.indrev.carbon_fiber_legs_frame": "Carbon Fiber Leggings Frame", "item.indrev.carbon_fiber_boots_frame": "Carbon Fiber Boots Frame", "subtitles.indrev.laser": "Laser beam", "block.indrev.boiler": "Boiler", "block.indrev.steam_turbine_mk4": "Steam Turbine" } ================================================ FILE: src/main/resources/assets/indrev/lang/pt_br.json ================================================ { "item.patchouli.industrial_revolution_book.name": "Guia do revolucionário", "item.patchouli.industrial_revolution_book.landing": "Este livro irá lhe ajudar em aspectos importantes do mod.$(br2) Se houver algum problema, dúvidas ou sugestões, abra uma issue em $(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$() ou entre no nosso $(l:https://discord.com/invite/G4PjhEf)Discord!$()$(br2)Obrigado por jogar!", "itemGroup.indrev.indrev_group": "Revolução industrial", "block.machines.tooltip.io": "I/O: %s", "block.indrev.coal_generator": "Gerador a carvão", "block.indrev.coal_generator_mk1": "Gerador a carvão", "block.indrev.heat_generator": "Gerador a calor", "block.indrev.heat_generator_mk4": "Gerador a calor", "block.indrev.electric_furnace": "Fornalha elétrica", "block.indrev.electric_furnace_mk1": "Fornalha elétrica MK1", "block.indrev.electric_furnace_mk2": "Fornalha elétrica MK2", "block.indrev.electric_furnace_mk3": "Fornalha elétrica MK3", "block.indrev.electric_furnace_mk4": "Fornalha elétrica MK4", "block.indrev.pulverizer": "Pulverizador", "block.indrev.pulverizer_mk1": "Pulverizador MK1", "block.indrev.pulverizer_mk2": "Pulverizador MK2", "block.indrev.pulverizer_mk3": "Pulverizador MK3", "block.indrev.pulverizer_mk4": "Pulverizador MK4", "block.indrev.compressor": "Compressor", "block.indrev.compressor_mk1": "Compressor MK1", "block.indrev.compressor_mk2": "Compressor MK2", "block.indrev.compressor_mk3": "Compressor MK3", "block.indrev.compressor_mk4": "Compressor MK4", "block.indrev.infuser": "Infusor", "block.indrev.infuser_mk1": "Infusor MK1", "block.indrev.infuser_mk2": "Infusor MK2", "block.indrev.infuser_mk3": "Infusor MK3", "block.indrev.infuser_mk4": "Infusor MK4", "block.indrev.lazuli_flux_container_mk1": "Reservatório de Lazuli Flux MK1", "block.indrev.lazuli_flux_container_mk2": "Reservatório de Lazuli Flux MK2", "block.indrev.lazuli_flux_container_mk3": "Reservatório de Lazuli Flux MK3", "block.indrev.lazuli_flux_container_mk4": "Reservatório de Lazuli Flux MK4", "block.indrev.cable_mk1": "Cabo MK1", "block.indrev.cable_mk2": "Cabo MK2", "block.indrev.cable_mk3": "Cabo MK3", "block.indrev.cable_mk4": "Cabo MK4", "block.indrev.fishing_farm": "Pescadora", "block.indrev.fishing_farm_mk2": "Pescadora simples", "block.indrev.fishing_farm_mk3": "Pescadora aprimorada", "block.indrev.fishing_farm_mk4": "Pescadora avançada", "block.indrev.miner": "Mineradora", "block.indrev.miner_mk4": "Mineradora", "block.indrev.ender_miner_mk4": "Mineradora de ender", "block.indrev.miner.tooltip": "Você deve escanear esse chunk com um Analisador de terreno antes de usar a mineradora.", "block.indrev.miner.mined": "Minerado: %d", "block.indrev.recycler": "Recicladora", "block.indrev.recycler_mk2": "Recicladora", "block.indrev.cable": "Cabo", "block.indrev.chopper": "Lenhadora", "block.indrev.chopper_mk4": "Lenhadora", "block.indrev.rancher": "Rancheiro", "block.indrev.rancher_mk4": "Rancheiro", "block.indrev.biomass_generator": "Gerador a biomassa", "block.indrev.biomass_generator_mk3": "Gerador a biomassa", "block.indrev.solar_generator": "Gerador solar", "block.indrev.solar_generator_mk1": "Gerador solar", "block.indrev.solar_generator_mk3": "Gerador solar", "block.indrev.charge_pad_mk4": "Pedestal de carregamento", "block.indrev.charge_pad_mk4.tooltip": "Recarrega itens equipados ou que forem colocados nele.", "block.indrev.aoe.toggle.btn": "R", "block.indrev.aoe.toggle.false": "Mostrar alcance", "block.indrev.aoe.toggle.true": "Esconder alcance", "block.indrev.coolant": "Refrigerante", "block.indrev.molten_netherite": "Netherita derretida", "block.indrev.machine_block": "Bloco de máquina", "block.indrev.tin_ore": "Minério de estanho", "block.indrev.tin_block": "Bloco de estanho", "item.indrev.tin_ingot": "Barra de estanho", "item.indrev.tin_dust": "Pó de estanho", "item.indrev.tin_plate": "Chapa de estanho", "item.indrev.tin_gear": "Engrenagem de estanho", "block.indrev.copper_ore": "Minério de cobre", "block.indrev.copper_block": "bloco de cobre", "item.indrev.copper_ingot": "Barra de cobre", "item.indrev.copper_dust": "Pó de cobre", "item.indrev.copper_plate": "Chapa de cobre", "item.indrev.copper_gear": "Engrenagem de cobre", "block.indrev.steel_block": "Bloco de aço", "item.indrev.steel_dust": "Pó de aço", "item.indrev.steel_ingot": "Barra de aço", "item.indrev.steel_plate": "Chapa de aço", "item.indrev.steel_gear": "Engrenagem de aço", "item.indrev.gold_dust": "Pó de ouro", "item.indrev.gold_plate": "Chapa de ouro", "item.indrev.iron_dust": "Pó de ferro", "item.indrev.iron_plate": "Chapa de ferro", "item.indrev.iron_gear": "Engrenagem de ferro", "item.indrev.diamond_dust": "Pó de diamante", "item.indrev.coal_dust": "Pó de carvão", "item.indrev.empty_upgrade": "Módulo de melhoria", "item.indrev.buffer_upgrade": "Melhoria de armazenamento", "item.indrev.buffer_upgrade.tooltip": "Aumenta o armazenamento de energia da máquina", "item.indrev.energy_upgrade": "Melhoria de eficiência de energia", "item.indrev.energy_upgrade.tooltip": "Faz com que a máquina gaste menos energia", "item.indrev.speed_upgrade": "Melhoria de velocidade", "item.indrev.speed_upgrade.tooltip": "Diminui o tempo de processamento da máquina", "item.indrev.rechargeable.tooltip": "Recarregável", "item.indrev.chunk_scanner": "Escaneador de terreno", "item.indrev.chunk_scanner.scanning": "Escaneando...", "item.indrev.chunk_scanner.scanned1": "Escaneamento concluído!", "item.indrev.chunk_scanner.scanned2": "Depósito de %s", "item.indrev.chunk_scanner.already_scanned": "Este chunk já foi escaneado. Ele contém %s", "item.indrev.chunk_scanner.tooltip1": "Segure clique direito para escanear este chunk", "item.indrev.chunk_scanner.tooltip2": "Depósito de %s", "item.indrev.chunk_scanner.tooltip3": "De %s até %s", "item.indrev.chunk_scanner.tooltip4": "Dimensão: %s", "item.indrev.fan": "Ventilador", "item.indrev.cooler_cell": "Célula de refrigeração", "item.indrev.heatsink": "Dissipador", "item.indrev.mining_drill_mk1": "Broca de mineração MK1", "item.indrev.mining_drill_mk2": "Broca de mineração MK2", "item.indrev.mining_drill_mk3": "Broca de mineração MK3", "item.indrev.mining_drill_mk4": "Broca de mineração MK4", "item.indrev.energy_reader": "Leitor de energia", "item.indrev.energy_reader.use": "Energia armazenada:", "item.indrev.enriched_nikolite_dust": "Pó de Nikolita enriquecida", "item.indrev.enriched_nikolite_ingot": "Barra Nikolita enriquecida", "item.indrev.battery": "Bateria", "item.indrev.circuit_mk1": "Circuito MK1", "item.indrev.circuit_mk2": "Circuito MK2", "item.indrev.circuit_mk3": "Circuito MK3", "item.indrev.circuit_mk4": "Circuito MK4", "item.indrev.tier_upgrade_mk2": "Melhoria de máquina MK2", "item.indrev.tier_upgrade_mk2.tooltip": "Atualiza uma máquina MK1 para MK2", "item.indrev.tier_upgrade_mk3": "Melhoria de máquina MK3", "item.indrev.tier_upgrade_mk3.tooltip": "Atualiza uma máquina MK2 para MK3", "item.indrev.tier_upgrade_mk4": "Melhoria de máquina MK4", "item.indrev.tier_upgrade_mk4.tooltip": "Atualiza uma máquina MK3 para MK4", "item.indrev.biomass": "Biomassa", "item.indrev.coolant_bucket": "Balde de líquido refrigerante", "item.indrev.molten_netherite_bucket": "Balde de Netherita derretida", "item.indrev.hammer": "Martelo", "item.indrev.wrench": "Chave inglesa", "item.indrev.wrench.switch_mode": "Modo da chave: %s", "item.indrev.wrench.title": "Configurar máquina", "item.indrev.wrench.output": "Saída", "item.indrev.wrench.input": "Entrada", "item.indrev.wrench.input_output": "Entrada e saída", "item.indrev.wrench.none": "Vazio", "item.indrev.wrench.tooltip": "Shift + clique direito para trocar de modo", "item.indrev.wrench.mode": "Modo: %s", "item.indrev.tech_soup": "Sopa eletrônica", "item.indrev.scan_output": "Escanear saída", "item.indrev.portable_charger": "Carregador portátil", "item.indrev.gamer_axe": "Machado gamer", "gui.widget.energy": "Energia", "gui.widget.temperature": "Temperatura", "gui.widget.temperature_info.high": "Muito quente!", "gui.widget.temperature_info.medium": "Potencial máximo", "gui.widget.temperature_info.low": "Baixa temperatura", "gui.indrev.guide_book_shortcut.contains": "Abre a página referente no livro de ajuda", "gui.indrev.guide_book_shortcut.missing": "É necessário ter o Guia do revolucionário!", "gui.indrev.tooltip.maxInput": "Limite de entrada: ", "gui.indrev.tooltip.maxOutput": "Limite de saída: ", "gui.indrev.tooltip.maxEnergyStored": "Capacidade de armazenamento: ", "gui.indrev.tooltip.energyCost": "Energia necessária: ", "gui.indrev.tooltip.processSpeed": "Velocidade: ", "gui.indrev.tooltip.ratio": "Geração: ", "gui.indrev.tooltip.press_shift": "Segure Shift para mais informações", "gui.indrev.tooltip.lftick": "%s LF/tick", "gui.indrev.tooltip.lf": "%s LF", "gui.indrev.modular_armor_slot_type": "Coloque aqui a peça da sua armadura modular", "gui.indrev.module_slot_type": "Coloque aqui o módulo desejado", "gui.indrev.scan_output_slot_type": "Coloque aqui os resultados do escanemanto", "gui.indrev.output_slot_type": "Slot de saída", "gui.indrev.chopper_input_slot_type": "Compartimento de machado, muda e/ou farinha de osso", "gui.indrev.cooler_slot_type": "Espaço para resfriadores", "gui.indrev.battery_slot_type": "Espaço para baterias", "gui.indrev.upgrade_slot_type": "Espaço para melhorias", "gui.indrev.fishingrod": "Compartimento de vara de pesca", "indrev.category.rei.pulverizing": "Pulverizando", "indrev.category.rei.infusing": "Infudindo", "indrev.category.rei.compressing": "Comprimindo", "indrev.category.rei.recycling": "Reciclando", "item.indrev.nikolite_dust": "Pó de Nikolita", "item.indrev.nikolite_ingot": "Barra de Nikolita", "block.indrev.nikolite_ore": "Minério de Nikolita", "item.indrev.modular_armor.upgrade": "Módulos instalados:", "item.indrev.modular_armor.upgrade.night_vision": "Visão noturna %s", "item.indrev.modular_armor.upgrade.speed": "Velocidade %s", "item.indrev.modular_armor.upgrade.jump_boost": "Supersalto %s", "item.indrev.modular_armor.upgrade.breathing": "Respiração %s", "item.indrev.modular_armor.upgrade.protection": "Proteção %s", "item.indrev.modular_armor.upgrade.feather_falling": "Peso-pena %s", "item.indrev.modular_armor.upgrade.auto_feeder": "Alimentação %s", "item.indrev.modular_armor.upgrade.charger": "Carregador %s", "item.indrev.modular_armor.upgrade.solar_panel": "Painel solar %s", "item.indrev.modular_armor.upgrade.piglin_tricker": "Enganar piglin %s", "item.indrev.modular_armor.upgrade.fire_resistance": "Resistência a fogo %s", "item.indrev.steel_helmet": "Capacete de aço", "item.indrev.steel_chestplate": "Peitoral de aço", "item.indrev.steel_leggings": "Calças de aço", "item.indrev.steel_boots": "Botas de aço", "item.indrev.steel_sword": "Espada de aço", "item.indrev.steel_pickaxe": "Picareta de aço", "item.indrev.steel_axe": "Machado de aço", "item.indrev.steel_shovel": "Pá de aço", "item.indrev.steel_hoe": "Enxada de aço", "block.indrev.modular_workbench": "Bancada modular", "block.indrev.modular_workbench_mk4": "Bancada modular", "item.indrev.modular_armor_helmet": "Capacete modular", "item.indrev.modular_armor_chest": "Peitoral modular", "item.indrev.modular_armor_legs": "Calças Modulares", "item.indrev.modular_armor_boots": "Botas Modulares", "item.indrev.module_parts": "Pode ser instalado em", "item.indrev.module_parts_head": "Capacete", "item.indrev.module_parts_chest": "Peitoral", "item.indrev.module_parts_legs": "Calças", "item.indrev.module_parts_feet": "Botas", "item.indrev.module_max_level": "Nível máximo: %s", "item.indrev.module_protection": "Módulo de proteção", "item.indrev.module_protection.tooltip": "Aumenta a proteção da armadura", "item.indrev.module_feather_falling": "Módulo peso-pena", "item.indrev.module_feather_falling.tooltip": "Absorve o dano de queda com um custo extra de energia", "item.indrev.module_speed": "Módulo de velocidade", "item.indrev.module_speed.tooltip": "Sua velocidade aumenta. O gasto de energia também aumenta durante o uso", "item.indrev.module_jump_boost": "Módulo de supersalto", "item.indrev.module_jump_boost.tooltip": "Concede superpulo com um custo extra de energia", "item.indrev.module_night_vision": "Módulo de visão noturna", "item.indrev.module_night_vision.tooltip": "Concede visão noturna. O gasto de energia aumenta durante o uso", "item.indrev.module_breathing": "Módulo de respiração", "item.indrev.module_breathing.tooltip": "Permite respirar debaixo d'agua. O gasto de energia aumenta durante o uso", "item.indrev.module_auto_feeder": "Módulo de alimentação", "item.indrev.module_auto_feeder.tooltip": "Come automaticamente do inventário com um custo extra de energia", "item.indrev.module_charger": "Módulo de carregamento", "item.indrev.module_charger.tooltip": "Recarrega itens do seu inventário (não inclui armaduras)", "item.indrev.module_solar_panel": "Módulo de painel solar", "item.indrev.module_solar_panel.tooltip": "Recarrega a armadura e o item sendo segurado quando sob a luz do sol", "item.indrev.module_piglin_tricker": "Módulo de enganar piglin", "item.indrev.module_piglin_tricker.tooltip": "Faz os piglins pensarem que você está vestindo ouro", "item.indrev.module_fire_resistance": "Módulo de resistência a fogo", "item.indrev.module_fire_resistance.tooltip": "Concede resistência a fogo. O gasto de energia aumenta durante o uso", "item.indrev.module_color.tooltip": "§cC§eo§ar§9e§6§ds", "item.indrev.module_color_pink": "Módulo de cor rosa", "item.indrev.module_color_red": "Módulo de cor vermelho", "item.indrev.module_color_purple": "Módulo de cor roxo", "item.indrev.module_color_blue": "Módulo de cor azul", "item.indrev.module_color_cyan": "Módulo de cor ciano", "item.indrev.module_color_green": "Módulo de cor verde", "item.indrev.module_color_yellow": "Módulo de cor amarelo", "item.indrev.module_color_orange": "Módulo de cor laranja", "item.indrev.module_color_black": "Módulo de cor preto", "item.indrev.module_color_brown": "Módulo de cor marrom", "item.indrev.copper_sword": "Espada de cobre", "item.indrev.copper_pickaxe": "Picareta de cobre", "item.indrev.copper_axe": "Machado de cobre", "item.indrev.copper_shovel": "Pá de cobre", "item.indrev.copper_hoe": "Enxada de cobre", "item.indrev.copper_helmet": "Capacete de cobre", "item.indrev.copper_chestplate": "Peitoral de cobre", "item.indrev.copper_leggings": "Calças de cobre", "item.indrev.copper_boots": "Botas de cobre", "item.indrev.tin_sword": "Espada de estanho", "item.indrev.tin_pickaxe": "Picareta de estanho", "item.indrev.tin_axe": "Machado de estanho", "item.indrev.tin_shovel": "Pá de estanho", "item.indrev.tin_hoe": "Enxada de estanho", "item.indrev.tin_helmet": "Capacete de estanho", "item.indrev.tin_chestplate": "Peitoral de estanho", "item.indrev.tin_leggings": "Calças de estanho", "item.indrev.tin_boots": "Botas de estanho", "advancements.indrev.nikolite": "O início", "advancements.indrev.nikolite.description": "Encontre nikolita", "advancements.indrev.machine_block": "Fundações", "advancements.indrev.machine_block.description": "Adquira um bloco de máquina", "advancements.indrev.coal_generator": "Temos que começar de algum lugar", "advancements.indrev.coal_generator.description": "Adquira um gerador a carvão", "advancements.indrev.basic_solar_generator": "Energia sustentável", "advancements.indrev.basic_solar_generator.description": "Adquira um gerador solar simples", "advancements.indrev.advanced_solar_generator": "Mais sustentabilidade", "advancements.indrev.advanced_solar_generator.description": "Adquira um gerador solar avançado", "advancements.indrev.heat_generator": "Tá pegando fogo, bicho!", "advancements.indrev.heat_generator.description": "Adquira um gerador a calor", "advancements.indrev.biomass_generator": "Quem liga pro ecossistema?", "advancements.indrev.biomass_generator.description": "Adquira um Gerador a biomassa", "advancements.indrev.pulverizer": "Ficou só o pó", "advancements.indrev.pulverizer.description": "Adquira um pulverizador", "advancements.indrev.electric_furnace": "É uma fornalha... Só que melhor!", "advancements.indrev.electric_furnace.description": "Adquira uma fornalha elétrica", "advancements.indrev.compressor": "Cuidado com os dedos", "advancements.indrev.compressor.description": "Adquira um compressor", "advancements.indrev.recycler": "Camarada do meio ambiente", "advancements.indrev.recycler.description": "Get a Recycler", "advancements.indrev.infuser": "Mistureba", "advancements.indrev.infuser.description": "Adquira um infusor", "advancements.indrev.mk2_upgrade": "Duas vezes melhor!", "advancements.indrev.mk2_upgrade.description": "Upgrade a machine to MK2", "advancements.indrev.mk3_upgrade": "TRÊS VEZES MELHOR!", "advancements.indrev.mk3_upgrade.description": "Upgrade a machine to MK3", "advancements.indrev.mk4_upgrade": "PRODUZIR! PRODUZIR! PRODUZIR!", "advancements.indrev.mk4_upgrade.description": "Upgrade a machine to MK4", "advancements.indrev.biomass": "É melhor não comer isso", "advancements.indrev.biomass.description": "Produza biomassa", "advancements.indrev.nikolite_ingot": "Novos horizontes", "advancements.indrev.nikolite_ingot.description": "Adquira Barra de nikolita", "advancements.indrev.enriched_nikolite_dust": "Eletrônicos poderosos", "advancements.indrev.enriched_nikolite_dust.description": "Adquira Pó de nikolita enriquecida", "advancements.indrev.enriched_nikolite_ingot": "É TANTO PODER!", "advancements.indrev.enriched_nikolite_ingot.description": "Adquira Barra de nikolita enriquecida", "advancements.indrev.lazuli_flux_container_mk1": "É melhor do que nada", "advancements.indrev.lazuli_flux_container_mk1.description": "Adquira um Reservatório de Lazuli Flux MK1", "advancements.indrev.lazuli_flux_container_mk2": "Isso deve dar conta", "advancements.indrev.lazuli_flux_container_mk2.description": "Adquira um Reservatório de Lazuli Flux MK2", "advancements.indrev.lazuli_flux_container_mk3": "Estamos chegando em algum lugar", "advancements.indrev.lazuli_flux_container_mk3.description": "Adquira um Reservatório de Lazuli Flux MK3", "advancements.indrev.lazuli_flux_container_mk4": "Energia ilimitada! Ou quase", "advancements.indrev.lazuli_flux_container_mk4.description": "Adquira um Reservatório de Lazuli Flux MK4", "advancements.indrev.empty_upgrade": "Acho que ficaria bonita na parede", "advancements.indrev.empty_upgrade.description": "Adquira um módulo de melhoria", "advancements.indrev.speed_upgrade": "Taca-le pau nessa maquininha", "advancements.indrev.speed_upgrade.description": "Adquira uma melhoria de velocidade", "advancements.indrev.buffer_upgrade": "Além do alcance", "advancements.indrev.buffer_upgrade.description": "Adquira uma melhoria de armazenamento", "advancements.indrev.energy_upgrade": "Eficiência sempre", "advancements.indrev.energy_upgrade.description": "Adquira uma melhoria de energia", "advancements.indrev.cable_mk1": "Sem risco de choque", "advancements.indrev.cable_mk1.description": "Adquira um cabo MK1", "advancements.indrev.cable_mk2": "Gerenciamento de cabos", "advancements.indrev.cable_mk2.description": "Adquira um cabo MK2", "advancements.indrev.cable_mk3": "O que poderia ser melhor do que cabos roxos?", "advancements.indrev.cable_mk3.description": "Adquira um cabo MK3", "advancements.indrev.cable_mk4": "Cabos vermelhos, claro!", "advancements.indrev.cable_mk4.description": "Adquira um cabo MK4", "advancements.indrev.modular_workbench": "Uma bancada diferenciada, eu diria", "advancements.indrev.modular_workbench.description": "Adquira uma bancada modular", "advancements.indrev.modular_armor": "Modularidade", "advancements.indrev.modular_armor.description": "Adquira uma armadura modular completa", "advancements.indrev.gamer_axe": "Preciso de mais luzes", "advancements.indrev.gamer_axe.description": "Adquira um Machado gamer", "gui.indrev.tip": "Dica: ", "gui.indrev.tip_0": "Lembre-se de configurar entrada e saída de itens.", "gui.indrev.tip_1": "Não coloque energia acima do limite, as coisas podem ficar quentes!", "gui.indrev.tip_2": "Refrigere suas máquinas, elas também se estressam.", "gui.indrev.tip_3": "Use refrigeradores para manter a eficiência", "gui.indrev.tip_4": "Cresça! Cresça sua fábrica imediatamente!", "gui.indrev.tip_5": "Você devia dar uma olhada na armadura modular, ela é irada!", "gui.indrev.tip_6": "Gamers de verdade utilizam o machado gamer! Sim, ele brilha.", "gui.indrev.tip_7": "Caso tenha algum problema ou sugestão, entre no nosso grupo no Discord!", "gui.indrev.tip_8": "Faça backup dos seus mundos antes de atualizar", "gui.indrev.tip_9": "Cubra seus pés na hora de dormir", "gui.indrev.modules_installed": "Módulos instalados: ", "gui.indrev.shield": "Proteção: ", "gui.indrev.installing": "Instalando", "gui.indrev.incompatible": "Módulo incompatível", "gui.indrev.progress": "Progresso: " } ================================================ FILE: src/main/resources/assets/indrev/lang/ru_ru.json ================================================ { "item.patchouli.industrial_revolution_book.name": "Руководство Industrial Revolution", "item.indrev.guide_book": "Руководство Industrial Revolution", "item.patchouli.industrial_revolution_book.landing": "Эта книга поможет тебе пройтись насквозь по некоторым важным аспектам мода.$(br2)Если ты найдёшь какие-либо проблемы, захочешь задать вопрос или предложить предложение, открой «Проблему» в $(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$() или присоединяйся в наш $(l:https://discord.com/invite/G4PjhEf)Discord!$()$(br2)Спасибо за игру!", "itemGroup.indrev.indrev_group": "Industrial Revolution", "block.machines.tooltip.io": "Ввод/Вывод: %s", "block.indrev.lazuli_flux_container_1": "Лазуритовый флакс", "block.indrev.lazuli_flux_container_2": "Контейнер", "block.indrev.coal_generator": "Угольный генератор", "block.indrev.coal_generator_mk1": "Угольный генератор мк. 1", "block.indrev.heat_generator": "Тепловой генератор", "block.indrev.heat_generator_mk4": "Тепловой генератор мк. 4", "block.indrev.electric_furnace": "Электропечь", "block.indrev.electric_furnace_mk1": "Электропечь мк. 1", "block.indrev.electric_furnace_mk2": "Электропечь мк. 2", "block.indrev.electric_furnace_mk3": "Электропечь мк. 3", "block.indrev.electric_furnace_mk4": "Электропечь мк. 4", "block.indrev.electric_furnace_creative": "Творческая электропечь", "block.indrev.electric_furnace_factory": "Промышленная электропечь", "block.indrev.electric_furnace_factory_mk4": "Промышленная электропечь мк. 4", "block.indrev.smelter": "Производственная плавильная печь", "block.indrev.smelter_mk4": "Производственная плавильная печь мк. 4", "block.indrev.condenser": "Конденсатор", "block.indrev.condenser_mk4": "Конденсатор мк. 4", "block.indrev.pulverizer": "Измельчитель", "block.indrev.pulverizer_mk1": "Измельчитель мк. 1", "block.indrev.pulverizer_mk2": "Измельчитель мк. 2", "block.indrev.pulverizer_mk3": "Измельчитель мк. 3", "block.indrev.pulverizer_mk4": "Измельчитель мк. 4", "block.indrev.pulverizer_creative": "Творческий измельчитель", "block.indrev.pulverizer_factory": "Промышленный измельчитель", "block.indrev.pulverizer_factory_mk4": "Промышленный измельчитель мк. 4", "block.indrev.sawmill": "Лесопильный завод", "block.indrev.sawmill_mk1": "Лесопильный завод мк. 1", "block.indrev.sawmill_mk2": "Лесопильный завод мк. 2", "block.indrev.sawmill_mk3": "Лесопильный завод мк. 3", "block.indrev.sawmill_mk4": "Лесопильный завод мк. 4", "block.indrev.sawmill_creative": "Творческий лесопильный завод", "block.indrev.compressor": "Компрессор", "block.indrev.compressor_mk1": "Компрессор мк. 1", "block.indrev.compressor_mk2": "Компрессор мк. 2", "block.indrev.compressor_mk3": "Компрессор мк. 3", "block.indrev.compressor_mk4": "Компрессор мк. 4", "block.indrev.compressor_creative": "Творческий компрессор", "block.indrev.compressor_factory": "Промышленный компрессор", "block.indrev.compressor_factory_mk4": "Промышленный компрессор мк. 4", "block.indrev.solid_infuser": "Твердотельный настойник мк.", "block.indrev.solid_infuser_mk1": "Твердотельный настойник мк. 1", "block.indrev.solid_infuser_mk2": "Твердотельный настойник мк. 2", "block.indrev.solid_infuser_mk3": "Твердотельный настойник мк. 3", "block.indrev.solid_infuser_mk4": "Твердотельный настойник мк. 4", "block.indrev.solid_infuser_creative": "Творческий твердотельный настойник", "block.indrev.solid_infuser_factory": "Промышленный твердотельный настойник", "block.indrev.solid_infuser_factory_mk4": "Промышленный твердотельный настойник мк. 4", "block.indrev.lazuli_flux_container_mk1": "Лазуритовый флаксовый резервуар мк. 1", "block.indrev.lazuli_flux_container_mk2": "Лазуритовый флаксовый резервуар мк. 2", "block.indrev.lazuli_flux_container_mk3": "Лазуритовый флаксовый резервуар мк. 3", "block.indrev.lazuli_flux_container_mk4": "Лазуритовый флаксовый резервуар мк. 4", "block.indrev.lazuli_flux_container_creative": "Творческий лазуритовый флаксовый резервуар", "block.indrev.cable_mk1": "Кабель мк. 1", "block.indrev.cable_mk2": "Кабель мк. 2", "block.indrev.cable_mk3": "Кабель мк. 3", "block.indrev.cable_mk4": "Кабель мк. 4", "block.indrev.fluid_pipe_mk1": "Жидкостная труба мк. 1", "block.indrev.fluid_pipe_mk2": "Жидкостная труба мк. 2", "block.indrev.fluid_pipe_mk3": "Жидкостная труба мк. 3", "block.indrev.fluid_pipe_mk4": "Жидкостная труба мк. 4", "block.indrev.item_pipe_mk1": "Предметная труба мк. 1", "block.indrev.item_pipe_mk2": "Предметная труба мк. 2", "block.indrev.item_pipe_mk3": "Предметная труба мк. 3", "block.indrev.item_pipe_mk4": "Предметная труба мк. 4", "block.indrev.fisher": "Рыболов", "block.indrev.fisher_mk2": "Базовый рыболов", "block.indrev.fisher_mk3": "Улучшенный рыболов", "block.indrev.fisher_mk4": "Продвинутый рыболов", "block.indrev.mining_rig": "Станочная буровая машина", "block.indrev.mining_rig_mk4": "Станочная буровая машина мк. 4", "block.indrev.mining_rig.tooltip": "Прежде чем использовать Станочную буровую машину, в этом чанке ты должен использовать Сканер чанка.", "block.indrev.mining_rig.mined": "%s добыто.", "block.indrev.drill": "Сверло буровой установки", "block.indrev.drill_top": "Сверло буровой установки", "block.indrev.drill_middle": "Сверло буровой установки", "block.indrev.drill_bottom": "Сверло буровой установки", "block.indrev.drill.faster": "%sх быстрее", "block.indrev.drill.no_drills": "Пустой.", "block.indrev.drill.wrong_location": "Неверный отчёт для текущего местоположения!", "block.indrev.drill.active": "Свёрла активны", "block.indrev.drill.activating": "Не готов", "block.indrev.drill.not_enough_power": "Недостаточно энергии", "block.indrev.drill.power_required": "Для работы требуется как минимум %sЛФ.", "block.indrev.recycler": "Переработчик", "block.indrev.recycler_mk2": "Переработчик мк. 2", "block.indrev.cable": "Кабель", "block.indrev.farmer": "Фермер", "block.indrev.farmer_mk1": "Фермер мк. 1", "block.indrev.farmer_mk2": "Фермер мк. 2", "block.indrev.farmer_mk3": "Фермер мк. 3", "block.indrev.farmer_mk4": "Фермер мк. 4", "block.indrev.farmer_creative": "Творческий фермер", "block.indrev.slaughter": "Забойник", "block.indrev.slaughter_mk1": "Забойник мк. 1", "block.indrev.slaughter_mk2": "Забойник мк. 2", "block.indrev.slaughter_mk3": "Забойник мк. 3", "block.indrev.slaughter_mk4": "Забойник мк. 4", "block.indrev.slaughter_creative": "Творческий забойник", "block.indrev.chopper": "Лесоруб", "block.indrev.chopper_mk1": "Лесоруб мк. 1", "block.indrev.chopper_mk2": "Лесоруб мк. 2", "block.indrev.chopper_mk3": "Лесоруб мк. 3", "block.indrev.chopper_mk4": "Лесоруб мк. 4", "block.indrev.chopper_creative": "Творческий лесоруб", "block.indrev.rancher": "Скотовод", "block.indrev.rancher_mk1": "Скотовод мк. 1", "block.indrev.rancher_mk2": "Скотовод мк. 2", "block.indrev.rancher_mk3": "Скотовод мк. 3", "block.indrev.rancher_mk4": "Скотовод мк. 4", "block.indrev.rancher_creative": "Творческий скотовод", "block.indrev.fluid_infuser": "Жидкостный настойник", "block.indrev.fluid_infuser_mk1": "Жидкостный настойник мк. 1", "block.indrev.fluid_infuser_mk2": "Жидкостный настойник мк. 2", "block.indrev.fluid_infuser_mk3": "Жидкостный настойник мк. 3", "block.indrev.fluid_infuser_mk4": "Жидкостный настойник мк. 4", "block.indrev.fluid_infuser_creative": "Творческий жидкостный настойник", "block.indrev.electrolytic_separator": "Электролитный отделитель", "block.indrev.electrolytic_separator_mk1": "Электролитный отделитель мк. 1", "block.indrev.electrolytic_separator_mk2": "Электролитный отделитель мк. 2", "block.indrev.electrolytic_separator_mk3": "Электролитный отделитель мк. 3", "block.indrev.electrolytic_separator_mk4": "Электролитный отделитель мк. 4", "block.indrev.electrolytic_separator_creative": "Творческий электролитный отделитель", "block.indrev.drain_mk1": "Дренаж мк. 1", "block.indrev.tank": "Бак", "block.indrev.factory": "Завод", "block.indrev.biomass_generator": "Генератор на биомассе", "block.indrev.biomass_generator_mk3": "Генератор на биомассе мк. 3", "block.indrev.solar_generator": "Солнечный генератор", "block.indrev.solar_generator_mk1": "Солнечный генератор мк. 1", "block.indrev.solar_generator_mk3": "Солнечный генератор мк. 3", "block.indrev.charge_pad_mk4": "Зарядная площадка мк. 4", "block.indrev.charge_pad_mk4.tooltip": "Заряжает экипированные предметы или всё, что ты в него положишь!", "block.indrev.aoe.range": "Диапазон: %s", "block.indrev.aoe.toggle.false": "Показать диапазон", "block.indrev.aoe.toggle.true": "Скрыть диапазон", "block.indrev.planks": "Обшивная доска", "block.indrev.plank_block": "Блок из обшивных досок", "block.indrev.wither_proof_obsidian": "Обсидиановая защита от визера", "block.indrev.controller": "Регулятор", "block.indrev.frame": "Каркас", "block.indrev.duct": "Воздуховод", "block.indrev.warning_strobe": "Предупреждающая лампа", "block.indrev.silo": "Силос", "block.indrev.intake": "Воздушный канал", "block.indrev.cabinet": "Корпус", "block.indrev.coolant": "Хладагент", "block.indrev.molten_iron": "Расплавленное железо", "block.indrev.molten_gold": "Расплавленное золото", "block.indrev.molten_copper": "Расплавленная медь", "block.indrev.molten_tin": "Расплавленное олово", "block.indrev.molten_netherite": "Расплавленный незерит", "block.indrev.hydrogen": "Водород", "item.indrev.hydrogen_bucket": "Ведро водорода", "block.indrev.oxygen": "Кислород", "item.indrev.oxygen_bucket": "Ведро кислорода", "block.indrev.methane": "Метан", "item.indrev.methane_bucket": "Ведро метана", "block.indrev.sulfuric_acid": "Серная кислота", "item.indrev.sulfuric_acid_bucket": "Ведро серной кислоты", "block.indrev.toxic_mud": "Токсичный раствор", "item.indrev.toxic_mud_bucket": "Ведро токсичного раствора", "block.indrev.machine_block": "Машинный блок", "item.indrev.modular_core": "Ядро модульности (Незаряженное)", "item.indrev.modular_core_activated": "Ядро модульности", "item.indrev.sawdust": "Опилки", "block.indrev.laser_emitter_mk4": "Лазерный излучатель", "block.indrev.capsule": "Капсула", "block.indrev.tin_ore": "Оловянная руда", "block.indrev.tin_block": "Блок олова", "item.indrev.tin_ingot": "Оловянный слиток", "item.indrev.tin_chunk": "Оловянный кусок", "item.indrev.tin_dust": "Оловянная пыль", "item.indrev.tin_plate": "Оловянная пластина", "item.indrev.tin_gear": "Оловянная шестерня", "item.indrev.tin_nugget": "Кусочек олова", "item.indrev.molten_tin_bucket": "Ведро расплавленного олова", "item.indrev.tin_purified_ore": "Очищенная оловянная руда", "item.indrev.copper_chunk": "Медный кусок", "item.indrev.copper_dust": "Медная пыль", "item.indrev.copper_plate": "Медная пластина", "item.indrev.copper_gear": "Медная шестерня", "item.indrev.copper_nugget": "Кусочек меди", "item.indrev.copper_purified_ore": "Очищенная медная руда", "item.indrev.molten_copper_bucket": "Ведро расплавленной меди", "block.indrev.molten_lead": "Расплавленный свинец", "item.indrev.molten_lead_bucket": "Ведро расплавленного свинца", "block.indrev.lead_ore": "Свинцовая руда", "block.indrev.lead_block": "Блок свинца", "item.indrev.lead_ingot": "Свинцовый слиток", "item.indrev.lead_chunk": "Кусок свинца", "item.indrev.lead_dust": "Свинцовая пыль", "item.indrev.lead_plate": "Свинцовая пластина", "item.indrev.lead_gear": "Свинцовая шестерня", "item.indrev.lead_nugget": "Кусочек свинца", "item.indrev.lead_purified_ore": "Очищенная свинцовая руда", "item.indrev.raw_lead": "Рудный свинец", "item.indrev.raw_silver": "Рудное серебро", "item.indrev.raw_tungsten": "Рудный вольфрам", "item.indrev.raw_tin": "Рудное олово", "block.indrev.raw_lead_block": "Блок рудного свинца", "block.indrev.raw_silver_block": "Блок рудного серебра", "block.indrev.raw_tungsten_block": "Блок рудного вольфрама", "block.indrev.raw_tin_block": "Блок рудного олова", "block.indrev.molten_silver": "Расплавленное серебро", "item.indrev.molten_silver_bucket": "Ведро расплавленного серебра", "block.indrev.silver_ore": "Серебряная руда", "block.indrev.silver_block": "Блок серебра", "item.indrev.silver_ingot": "Серебряный слиток", "item.indrev.silver_chunk": "Серебряный кусок", "item.indrev.silver_dust": "Серебряная пыль", "item.indrev.silver_plate": "Серебряная плита", "item.indrev.silver_gear": "Серебряная шестерня", "item.indrev.silver_nugget": "Кусочек серебра", "item.indrev.silver_purified_ore": "Очищенная серебряная руда", "block.indrev.molten_tungsten": "Расплавленный вольфрам", "item.indrev.molten_tungsten_bucket": "Ведро расплавленного вольфрама", "block.indrev.tungsten_ore": "Вольфрамовая руда", "block.indrev.tungsten_block": "Блок вольфрама", "item.indrev.tungsten_ingot": "Вольфрамовый слиток", "item.indrev.tungsten_chunk": "Вольфрамовый кусок", "item.indrev.tungsten_dust": "Вольфрамовая пыль", "item.indrev.tungsten_plate": "Вольфрамовая пластина", "item.indrev.tungsten_gear": "Вольфрамовая шестерня", "item.indrev.tungsten_nugget": "Кусочек вольфрама", "item.indrev.tungsten_purified_ore": "Очищенная вольфрамовая руда", "block.indrev.deepslate_tungsten_ore": "Вольфрамоносный глубинный сланец", "block.indrev.deepslate_tin_ore": "Оловоносный глубинный сланец", "block.indrev.deepslate_silver_ore": "Серебряноносный глубинный сланец", "block.indrev.deepslate_nikolite_ore": "Николитоносный глубинный сланец", "block.indrev.deepslate_lead_ore": "Свинцовоносный глубинный сланец", "block.indrev.electrum_block": "Блок электрума", "item.indrev.electrum_ingot": "Электрумовый слиток", "item.indrev.electrum_dust": "Электрумовая пыль", "item.indrev.electrum_plate": "Электрумовая пластина", "item.indrev.electrum_gear": "Электрумовая шестерня", "item.indrev.electrum_nugget": "Кусочек электрума", "block.indrev.bronze_block": "Блок бронзы", "item.indrev.bronze_ingot": "Бронзовый слиток", "item.indrev.bronze_chunk": "Бронзовый кусок", "item.indrev.bronze_dust": "Бронзовая пыль", "item.indrev.bronze_plate": "Бронзовая пластина", "item.indrev.bronze_gear": "Бронзовая шестерня", "item.indrev.bronze_nugget": "Кусочек бронзы", "block.indrev.sulfur_crystal": "Блок серного кристалла", "item.indrev.sulfur_crystal": "Серный кристалл", "item.indrev.sulfur_dust": "Серная пыль", "block.indrev.steel_block": "Блок стали", "item.indrev.steel_dust": "Стальная пыль", "item.indrev.steel_ingot": "Стальной слиток", "item.indrev.steel_plate": "Стальная пластина", "item.indrev.steel_gear": "Стальная шестерня", "item.indrev.steel_nugget": "Кусочек стали", "item.indrev.gold_dust": "Золотая пыль", "item.indrev.gold_plate": "Золотая пластина", "item.indrev.gold_chunk": "Золотой кусок", "item.indrev.gold_purified_ore": "Очищенная золотая руда", "item.indrev.molten_gold_bucket": "Ведро расплавленного золота", "item.indrev.iron_dust": "Железная пыль", "item.indrev.iron_plate": "Железная пластина", "item.indrev.iron_chunk": "Железный кусок", "item.indrev.gold_dust": "Золотая пыль", "item.indrev.gold_plate": "Золотая пластина", "item.indrev.gold_chunk": "Золотой кусок", "item.indrev.nikolite_dust": "Николитовая пыль", "item.indrev.nikolite_ingot": "Николитовый слиток", "block.indrev.nikolite_ore": "Николитовая руда", "item.indrev.molten_iron_bucket": "Ведро расплавленного железа", "item.indrev.iron_purified_ore": "Очищенная железная руда", "item.indrev.diamond_dust": "Алмазная пыль", "item.indrev.coal_dust": "Угольная пыль", "block.indrev.pump_mk1": "Насос мк. 1", "item.indrev.empty_enhancer": "Пустой усилитель", "item.indrev.buffer_enhancer": "Усилитель: Буфер", "item.indrev.buffer_enhancer.tooltip": "Увеличивает энергоёмкость.", "item.indrev.energy_enhancer": "Усилитель: Эффективное использование энергии", "item.indrev.energy_enhancer.tooltip": "Сокращает энергозатраты.", "item.indrev.damage_enhancer": "Усилитель: Урон", "item.indrev.damage_enhancer.tooltip": "Увеличивает урон, сделанный Забойником.", "item.indrev.speed_enhancer": "Усилитель: Скорость", "item.indrev.speed_enhancer.tooltip": "Увеличивает скорость переработки и потребление энергии.", "item.indrev.blast_furnace_enhancer": "Усилитель: Горячее дутьё", "item.indrev.blast_furnace_enhancer.tooltip": "Позволяет электропечи принимать рецепты от доменной печи.", "item.indrev.smoker_enhancer": "Усилитель: Коптильня", "item.indrev.smoker_enhancer.tooltip": "Позволяет электропечи принимать рецепты от коптильни.", "item.indrev.enhancers.incompatible": "Машина не поддерживает данный усилитель.", "item.indrev.enhancers.count": "Ограничивается %s слотами в машине.", "item.indrev.rechargeable.tooltip": "Перезаряжаемый", "item.indrev.chunk_scanner": "Сканер чанка", "item.indrev.chunk_scanner.scanning": "Сканирование...", "item.indrev.chunk_scanner.scanned1": "Сканирование окончено! Проверь инвентарь.", "item.indrev.chunk_scanner.scanned2": "Тип жилы: %s", "item.indrev.chunk_scanner.already_scanned": "Этот чанк уже просканирован и в нём есть жила %s.", "item.indrev.chunk_scanner.tooltip1": "Чтобы начать сканирование чанка удерживай пкм.", "item.indrev.chunk_scanner.tooltip2": "Тип жилы: %s", "item.indrev.chunk_scanner.tooltip3": "От %s до %s", "item.indrev.chunk_scanner.tooltip4": "Измерение: %s", "item.indrev.chunk_scanner.tooltip5": "Пкм для подробностей.", "item.indrev.fan": "Вентилятор", "item.indrev.cooler_cell": "Охладительная ячейка", "item.indrev.heatsink": "Теплоотвод", "item.indrev.heat_coil": "Термическая катушка", "item.indrev.heat_coil.tooltip": "Преобразует лазуритный флакс в тепло.", "item.indrev.stone_drill_head": "Каменная головка бура", "item.indrev.iron_drill_head": "Железная головка бура", "item.indrev.diamond_drill_head": "Алмазная головка бура", "item.indrev.netherite_drill_head": "Незеритовая головка бура", "item.indrev.mining_drill_mk1": "Шахтёрский бур мк. 1", "item.indrev.mining_drill_mk2": "Шахтёрский бур мк. 2", "item.indrev.mining_drill_mk3": "Шахтёрский бур мк. 3", "item.indrev.mining_drill_mk4": "Модульный шахтёрский бур мк. 4", "item.indrev.energy_reader": "Считыватель энергии", "item.indrev.energy_reader.use": "Запасённая энергия:", "item.indrev.energy_reader.use1": "%s ЛФ/тик", "item.indrev.enriched_nikolite_dust": "Обогащённая николитовая пыль", "item.indrev.enriched_nikolite_ingot": "Обогащённый николитовый слиток", "item.indrev.battery": "Батарейка", "item.indrev.circuit_mk1": "Схема мк. 1", "item.indrev.circuit_mk2": "Схема мк. 2", "item.indrev.circuit_mk3": "Схема мк. 3", "item.indrev.circuit_mk4": "Схема мк. 4", "item.indrev.tier_upgrade_mk2": "Улучшение машины мк. 2", "item.indrev.tier_upgrade_mk2.tooltip": "Улучшает машины мк. 1 на мк. 2.", "item.indrev.tier_upgrade_mk3": "Улучшение машины мк. 3", "item.indrev.tier_upgrade_mk3.tooltip": "Улучшает машины мк. 2 на мк. 3.", "item.indrev.tier_upgrade_mk4": "Улучшение машины мк. 4", "item.indrev.tier_upgrade_mk4.tooltip": "Улучшает машины мк. 3 на мк. 4.", "item.indrev.servo_output": "Сервопривод вывода", "item.indrev.servo_output.tooltip": "Проталкивает предметы в соединённые инвентари.", "item.indrev.servo_retriever": "Сервопривод-извлекатель", "item.indrev.servo_retriever.tooltip": "Выталкивает предметы из соединённых инвентарей.", "item.indrev.servo.mode": "Приоритет: ", "item.indrev.servo.mode.round_robin": "Циклически", "item.indrev.servo.mode.round_robin.tooltip": "Приоритизирует контейнеры с наименьшим количеством предметов/жидкостей.", "item.indrev.servo.mode.nearest_first": "Сначала ближайший", "item.indrev.servo.mode.nearest_first.tooltip": "Приоритизирует ближайшие контейнеры.", "item.indrev.servo.mode.furthest_first": "Сначала дальний", "item.indrev.servo.mode.furthest_first.tooltip": "Приоритизирует дальние контейнеры.", "item.indrev.servo.mode.random": "Случайный", "item.indrev.servo.mode.random.tooltip": "Без приоритетов", "item.indrev.biomass": "Биомасса", "item.indrev.untanned_leather": "Не дублёная кожа", "item.indrev.coolant_bucket": "Ведро хладагента", "item.indrev.molten_netherite_bucket": "Ведро расплавленного незерита", "item.indrev.netherite_scrap_purified_ore": "Очищенный древний обломок", "item.indrev.netherite_scrap_chunk": "Кусок древнего обломка", "item.indrev.netherite_scrap_dust": "Пыль от незеритового лома", "item.indrev.hammer": "Молот", "item.indrev.screwdriver": "Отвёртка", "item.indrev.wrench": "Гаечный ключ", "item.indrev.wrench.switch_mode": "Режим гаечного ключа: %s", "item.indrev.wrench.title": "Настраивает машину", "item.indrev.wrench.autopush": "Авто-толкание", "item.indrev.wrench.autopull": "Авто-выталкивание", "item.indrev.wrench.item": "Предмет", "item.indrev.wrench.fluid": "Жидкость", "item.indrev.wrench.energy": "Энергия", "item.indrev.wrench.output": "Выход", "item.indrev.wrench.input": "Вход", "item.indrev.wrench.input_first": "Вход (Первый слот)", "item.indrev.wrench.input_second": "Вход (Второй слот)", "item.indrev.wrench.input_output": "Вход/Выход", "item.indrev.wrench.none": "Никакой", "item.indrev.wrench.tooltip": "Для смены режима Shift + Пкм.", "item.indrev.wrench.tooltip1": "Режим: %s", "item.indrev.wrench.tooltip1.rotate": "вращение", "item.indrev.wrench.tooltip1.configure": "настройки", "item.indrev.wrench.mode": "Режим: %s", "item.indrev.wrench.side.north": "север", "item.indrev.wrench.side.south": "юг", "item.indrev.wrench.side.west": "запад", "item.indrev.wrench.side.east": "восток", "item.indrev.wrench.side.up": "Верх", "item.indrev.wrench.side.down": "Низ", "item.indrev.wrench.side.top": "Верхняя сторона", "item.indrev.wrench.side.bottom": "Нижняя сторона", "item.indrev.wrench.side.left": "Левая сторона", "item.indrev.wrench.side.right": "Правая сторона", "item.indrev.wrench.side.front": "Передняя сторона", "item.indrev.wrench.side.back": "Задняя сторона", "item.indrev.wrench.connected": "Подключён к %s", "item.indrev.tech_soup": "Техносуп", "item.indrev.scan_output": "Отчёт о ресурсах", "item.indrev.portable_charger": "Переносной зарядник", "item.indrev.gamer_axe": "Геймерский топор", "gui.indrev.button.auto_split": "Авто-разделение стэков", "gui.indrev.resourcereport.size": "Размер жилы: %s", "gui.indrev.resourcereport.size.tiny": "Крошечный", "gui.indrev.resourcereport.size.small": "Малый", "gui.indrev.resourcereport.size.average": "Средний", "gui.indrev.resourcereport.size.big": "Большой", "gui.indrev.resourcereport.size.very_big": "Очень большой", "gui.indrev.resourcereport.size.enormous": "Огромный", "gui.indrev.resourcereport.size.gigantic": "Гигантский", "gui.furnace.mode": "Режим", "gui.furnace.mode.furnace": "Печь", "gui.furnace.mode.blast_furnace": "Доменная печь", "gui.furnace.mode.smoker": "Коптильня", "gui.widget.process": "Прогресс: %s", "gui.widget.energy": "Энергия", "gui.widget.temperature": "°C", "gui.widget.temperature_info.high": "Слишком горячо", "gui.widget.temperature_info.medium": "Полный потенциал", "gui.widget.temperature_info.low": "Не нагретый", "gui.indrev.solar.on": "Генерирование", "gui.indrev.heatgen.idle": "Простаивает", "gui.indrev.heatgen.title": "Потребление %s мБ", "gui.indrev.heatgen.generating": "Генерирование %s ЛФ", "gui.indrev.heatgen.pertick": "в тик", "gui.indrev.heatgen.extra": "Осторожно, он горячий!", "gui.indrev.guide_book_shortcut.contains": "Открывает страницу в Руководстве Industrial Revolution.", "gui.indrev.guide_book_shortcut.missing": "Отсутствует руководство industrial revolution!", "gui.indrev.tooltip.maxTransferRate": "Пропускная способность: ", "gui.indrev.tooltip.maxInput": "Макс. вход: ", "gui.indrev.tooltip.maxOutput": "Макс. выход: ", "gui.indrev.tooltip.maxEnergyStored": "Энергоёмкость: ", "gui.indrev.tooltip.seconds": "%s секунд.", "gui.indrev.tooltip.energyCost": "Энергозатраты: ", "gui.indrev.tooltip.processSpeed": "Скорость: ", "gui.indrev.tooltip.ratio": "Генерация: ", "gui.indrev.tooltip.press_shift": "Держи %s для подробной информации.", "gui.indrev.tooltip.lftick": "%s ЛФ/тик", "gui.indrev.tooltip.lf": "%s ЛФ", "gui.indrev.tooltip.itemsec": "%s предметов/сек", "gui.indrev.tooltip.fluidsec": "%s вёдер/сек", "gui.indrev.tooltip.temperatureBoost": "Нагревание: ", "gui.indrev.locked": "Заблокировано", "gui.indrev.modular_armor_slot_type": "Помести предмет.", "gui.indrev.module_slot_type": "Помести желанный модуль.", "gui.indrev.scan_output_slot_type": "Отчёт о ресурсах", "gui.indrev.output_slot_type": "Собрано предметов", "gui.indrev.chopper_input_axe": "Помести топор.", "gui.indrev.chopper_input_bone_meal": "Помести костную муку.", "gui.indrev.chopper_input_sapling": "Помести саженцы.", "gui.indrev.farmer_input_slot_type": "Семена и костная мука.", "gui.indrev.slaughter_input_sword": "Помести меч.", "gui.indrev.cooler_slot_type": "Охладитель", "gui.indrev.battery_slot_type": "Батарейка", "gui.indrev.upgrade_slot_type": "Улучшения", "gui.indrev.fishingrod": "Удочка", "indrev.category.rei.upgrading": "Ты должен создать %s и использовать на нём %s, чтобы улучшить его до %s.", "indrev.category.rei.pulverizing": "Измельчение", "indrev.category.rei.infusing": "Вливание", "indrev.category.rei.compressing": "Сжатие", "indrev.category.rei.recycling": "Переработка", "indrev.category.rei.fluid_infusing": "Вливание жидкостью", "indrev.category.rei.condensing": "Конденсирование", "indrev.category.rei.smelting": "Плавка", "indrev.category.rei.sawmill": "Распиливание", "indrev.category.rei.module": "Создание модуля", "item.indrev.modular.upgrade": "Установлено модулей:", "item.indrev.modular.upgrade.night_vision": "Ночное зрение %s", "item.indrev.modular.upgrade.speed": "Скорость %s", "item.indrev.modular.upgrade.jump_boost": "Прыгучесть %s", "item.indrev.modular.upgrade.breathing": "Дыхание %s", "item.indrev.modular.upgrade.protection": "Защита %s", "item.indrev.modular.upgrade.feather_falling": "Невесомость %s", "item.indrev.modular.upgrade.auto_feeder": "Авто-кормушка %s", "item.indrev.modular.upgrade.charger": "Зарядник %s", "item.indrev.modular.upgrade.solar_panel": "Солнечный генератор %s", "item.indrev.modular.upgrade.piglin_tricker": "Обман пиглина %s", "item.indrev.modular.upgrade.elytra": "Встроенные элитры %s", "item.indrev.modular.upgrade.jetpack": "Встроенный реактивный ранец %s", "item.indrev.modular.upgrade.magnet": "Магнит %s", "item.indrev.modular.upgrade.water_affinity": "Подводник %s", "item.indrev.modular.upgrade.fire_resistance": "Огнестойкость %s", "item.indrev.modular.upgrade.efficiency": "Эффективность %s", "item.indrev.modular.upgrade.range": "Диапазон %s", "item.indrev.modular.upgrade.fortune": "Удача %s", "item.indrev.modular.upgrade.silk_touch": "Шёлковое касание %s", "item.indrev.modular.upgrade.sharpness": "Острота %s", "item.indrev.modular.upgrade.looting": "Добыча %s", "item.indrev.modular.upgrade.fire_aspect": "Заговор огня %s", "item.indrev.modular.upgrade.reach": "Досягаемость %s", "item.indrev.steel_helmet": "Стальной шлем", "item.indrev.steel_chestplate": "Стальной нагрудник", "item.indrev.steel_leggings": "Стальные поножи", "item.indrev.steel_boots": "Стальные ботинки", "item.indrev.steel_sword": "Стальной меч", "item.indrev.steel_pickaxe": "Стальная кирка", "item.indrev.steel_axe": "Стальной топор", "item.indrev.steel_shovel": "Стальная лопата", "item.indrev.steel_hoe": "Стальная мотыга", "item.indrev.bronze_helmet": "Бронзовый шлем", "item.indrev.bronze_chestplate": "Бронзовый нагрудник", "item.indrev.bronze_leggings": "Бронзовые поножи", "item.indrev.bronze_boots": "Бронзовые ботинки", "item.indrev.bronze_sword": "Бронзовый меч", "item.indrev.bronze_pickaxe": "Бронзовая кирка", "item.indrev.bronze_axe": "Бронзовый топор", "item.indrev.bronze_shovel": "Бронзовая лопата", "item.indrev.bronze_hoe": "Бронзовая мотыга", "item.indrev.lead_helmet": "Свинцовый шлем", "item.indrev.lead_chestplate": "Свинцовый нагрудник", "item.indrev.lead_leggings": "Свинцовые поножи", "item.indrev.lead_boots": "Свинцовые ботинки", "item.indrev.lead_sword": "Свинцовый меч", "item.indrev.lead_pickaxe": "Свинцовая кирка", "item.indrev.lead_axe": "Свинцовый топор", "item.indrev.lead_shovel": "Свинцовая лопата", "item.indrev.lead_hoe": "Свинцовая мотыга", "item.indrev.silver_helmet": "Серебряный шлем", "item.indrev.silver_chestplate": "Серебряный нагрудник", "item.indrev.silver_leggings": "Серебряные поножи", "item.indrev.silver_boots": "Серебряные ботинки", "item.indrev.silver_sword": "Серебряный меч", "item.indrev.silver_pickaxe": "Серебряная кирка", "item.indrev.silver_axe": "Серебряный топор", "item.indrev.silver_shovel": "Серебряная лопата", "item.indrev.silver_hoe": "Серебряная мотыга", "block.indrev.modular_workbench": "Модульный верстак", "block.indrev.modular_workbench_mk4": "Модульный верстак мк. 4", "item.indrev.modular_armor_helmet": "Модульный шлем", "item.indrev.modular_armor_chest": "Модульный нагрудник", "item.indrev.modular_armor_legs": "Модульные поножи", "item.indrev.modular_armor_boots": "Модульные ботинки", "item.indrev.module_parts": "Можно установить в:", "item.indrev.module_parts_head": "шлем", "item.indrev.module_parts_chest": "нагрудник", "item.indrev.module_parts_legs": "поножи", "item.indrev.module_parts_feet": "ботинки", "item.indrev.module_parts_drill": "буровую установку", "item.indrev.module_parts_gamer_axe": "Гаймерский топор", "item.indrev.module_max_level": "Максимальный уровень: %s", "item.indrev.module_protection": "Модуль: Защита", "item.indrev.module_protection.tooltip": "Увеличивает защиту, обеспечиваемой бронёй.", "item.indrev.module_feather_falling": "Модуль: Невесомость", "item.indrev.module_feather_falling.tooltip": "Нейтрализует урон от падения за стоимость заряда щита.", "item.indrev.module_speed": "Модуль: Скорость", "item.indrev.module_speed.tooltip": "Даёт скорость за стоимость энергии.", "item.indrev.module_jump_boost": "Модуль: Прыгучесть", "item.indrev.module_jump_boost.tooltip": "Даёт прыжок за стоимость энергии.", "item.indrev.module_night_vision": "Модуль: Ночное зрение", "item.indrev.module_night_vision.tooltip": "Даёт ночное зрение за стоимость энергии.", "item.indrev.module_breathing": "Модуль: Дыхание", "item.indrev.module_breathing.tooltip": "Даёт подводное дыхание за стоимость энергии.", "item.indrev.module_auto_feeder": "Модуль: Авто-кормушка", "item.indrev.module_auto_feeder.tooltip": "Автоматически кормит едой из инвентаря за стоимость энергии.", "item.indrev.module_charger": "Модуль: Зарядник", "item.indrev.module_charger.tooltip": "Заряжает любые предметы в инвентаре (не включая броню).", "item.indrev.module_solar_panel": "Модуль: Солнечный генератор", "item.indrev.module_solar_panel.tooltip": "Заряжает под солнцем броню и держащий в руке предмет.", "item.indrev.module_piglin_tricker": "Модуль: Обман пиглина", "item.indrev.module_piglin_tricker.tooltip": "Обманом заставит Пиглинов думать, что ты носишь золото.", "item.indrev.module_elytra": "Модуль: Встроенные элитры", "item.indrev.module_elytra.tooltip": "Устанавливает в нагрудник Укреплённые элитры.", "item.indrev.module_jetpack": "Модуль: Встроенный реактивный ранец", "item.indrev.module_jetpack.tooltip": "Устанавливает в нагрудник Реактивный ранец.", "item.indrev.module_magnet": "Модуль: Магнит", "item.indrev.module_magnet.tooltip": "Притягивает предметы и опыт.", "item.indrev.module_water_affinity": "Модуль: Подводник", "item.indrev.module_water_affinity.on": "Находясь в: %s", "item.indrev.module_water_affinity.chestplate": "нагруднике", "item.indrev.module_water_affinity.leggings": "поножах", "item.indrev.module_water_affinity.tooltip": "ускоряет копание под водой.", "item.indrev.module_water_affinity.tooltip1": "ускоряет плавание/передвижение под водой.", "item.indrev.module_fire_resistance": "Модуль: Огнестойкость", "item.indrev.module_fire_resistance.tooltip": "Даёт огнестойкость за стоимость заряда щита.", "item.indrev.module_efficiency": "Модуль: Эффективность", "item.indrev.module_efficiency.tooltip": "Увеличивает скорость добычи инструмента.", "item.indrev.module_range": "Модуль: Диапазон", "item.indrev.module_range.tooltip": "Увеличивает радиус блоков Бура, который может сломать за раз.", "item.indrev.module_fortune": "Модуль: Удача", "item.indrev.module_fortune.tooltip": "Ведёт себя как зачарование Удача.", "item.indrev.module_silk_touch": "Модуль: Шёлковое касание", "item.indrev.module_silk_touch.tooltip": "Ведёт себя как зачарование Шёлковое касание.", "item.indrev.module_reach": "Модуль: Досягаемость", "item.indrev.module_reach.tooltip": "Наносит урон сразу нескольким мобам.", "item.indrev.module_looting": "Модуль: Добыча", "item.indrev.module_looting.tooltip": "Ведёт себя как зачарование Добыча.", "item.indrev.module_fire_aspect": "Модуль: Заговор огня", "item.indrev.module_fire_aspect.tooltip": "Ведёт себя как зачарование Заговор огня.", "item.indrev.module_sharpness": "Модуль: Острота", "item.indrev.module_sharpness.tooltip": "Увеличивает наносимый урон.", "item.indrev.module_color.tooltip": "§cЦ§eв§aе§9т§r", "item.indrev.module_color_pink": "Модуль: Розовый краситель", "item.indrev.module_color_red": "Модуль: Красный краситель", "item.indrev.module_color_purple": "Модуль: Фиолетовый краситель", "item.indrev.module_color_blue": "Модуль: Синий краситель", "item.indrev.module_color_cyan": "Модуль: Бирюзовый краситель", "item.indrev.module_color_green": "Модуль: Зелёный краситель", "item.indrev.module_color_yellow": "Модуль: Жёлтый краситель", "item.indrev.module_color_orange": "Модуль: Оранжевый краситель", "item.indrev.module_color_black": "Модуль: Чёрный краситель", "item.indrev.module_color_brown": "Модуль: Коричневый краситель", "item.indrev.copper_sword": "Медный меч", "item.indrev.copper_pickaxe": "Медная кирка", "item.indrev.copper_axe": "Медный топор", "item.indrev.copper_shovel": "Медная лопата", "item.indrev.copper_hoe": "Медная мотыга", "item.indrev.copper_helmet": "Медный шлем", "item.indrev.copper_chestplate": "Медный нагрудник", "item.indrev.copper_leggings": "Медные поножи", "item.indrev.copper_boots": "Медные ботинки", "item.indrev.tin_sword": "Оловянный меч", "item.indrev.tin_pickaxe": "Оловянная кирка", "item.indrev.tin_axe": "Оловянный топор", "item.indrev.tin_shovel": "Оловянная лопата", "item.indrev.tin_hoe": "Оловянная мотыга", "item.indrev.tin_helmet": "Оловянный шлем", "item.indrev.tin_chestplate": "Оловянный нагрудник", "item.indrev.tin_leggings": "Оловянные поножи", "item.indrev.tin_boots": "Оловянные ботинки", "category.indrev": "Industrial Revolution", "key.indrev.modular": "Конфигурация модульных предметов", "text.multiblock.not_built": "Конструкция не построена!", "advancements.indrev.nikolite": "Начало", "advancements.indrev.nikolite.description": "Найди наколит", "advancements.indrev.machine_block": "Основы", "advancements.indrev.machine_block.description": "Создай машинный блок", "advancements.indrev.coal_generator": "Первые шаги", "advancements.indrev.coal_generator.description": "Создай угольный генератор", "advancements.indrev.basic_solar_generator": "Пассивная энергия", "advancements.indrev.basic_solar_generator.description": "Создай базовую солнечный генератор", "advancements.indrev.advanced_solar_generator": "Ещё пассивной энергии", "advancements.indrev.advanced_solar_generator.description": "Создай продвинутый солнечный генератор", "advancements.indrev.heat_generator": "Становится всё горячее", "advancements.indrev.heat_generator.description": "Создай тепловой генератор", "advancements.indrev.biomass_generator": "Не так экологически безвредный", "advancements.indrev.biomass_generator.description": "Создай генератор на биомассе", "advancements.indrev.pulverizer": "Становится пылью", "advancements.indrev.pulverizer.description": "Создай измельчитель", "advancements.indrev.electric_furnace": "Лучше печи", "advancements.indrev.electric_furnace.description": "Создай электропечь", "advancements.indrev.compressor": "Следи за своими пальцами", "advancements.indrev.compressor.description": "Создай компрессор", "advancements.indrev.recycler": "Экологически безвредный", "advancements.indrev.recycler.description": "Создай переработчик", "advancements.indrev.solid_infuser": "Вливание", "advancements.indrev.solid_infuser.description": "Создай твердотельный настойник", "advancements.indrev.mk2_upgrade": "Двойная эффективность", "advancements.indrev.mk2_upgrade.description": "Улучши машину до мк. 2", "advancements.indrev.mk3_upgrade": "Тройная эффективность", "advancements.indrev.mk3_upgrade.description": "Улучши машину до мк. 3", "advancements.indrev.mk4_upgrade": "Завод. Должен. Расти.", "advancements.indrev.mk4_upgrade.description": "Улучши машину до мк. 4", "advancements.indrev.biomass": "Выглядит неприлично...", "advancements.indrev.biomass.description": "Создай биомассу", "advancements.indrev.nikolite_ingot": "Новые горизонты", "advancements.indrev.nikolite_ingot.description": "Создай николитовый слиток", "advancements.indrev.enriched_nikolite_dust": "Мощная электроника", "advancements.indrev.enriched_nikolite_dust.description": "Создай обогащённый николитовую пыль", "advancements.indrev.enriched_nikolite_ingot": "Больше. Энергии.", "advancements.indrev.enriched_nikolite_ingot.description": "Создай обогащённый николитовый слиток", "advancements.indrev.lazuli_flux_container_mk1": "Базовый накопитель энергии", "advancements.indrev.lazuli_flux_container_mk1.description": "Создай лазуритовый флаксовый резервуар мк. 1", "advancements.indrev.lazuli_flux_container_mk2": "Заурядный накопитель энергии", "advancements.indrev.lazuli_flux_container_mk2.description": "Создай лазуритовый флаксовый резервуар мк. 2", "advancements.indrev.lazuli_flux_container_mk3": "Не такой уж заурядный накопитель энергии", "advancements.indrev.lazuli_flux_container_mk3.description": "Создай лазуритовый флаксовый резервуар мк. 3", "advancements.indrev.lazuli_flux_container_mk4": "Конечный накопитель энергии", "advancements.indrev.lazuli_flux_container_mk4.description": "Создай лазуритовый флаксовый резервуар мк. 4", "advancements.indrev.empty_enhancer": "Милая пластина", "advancements.indrev.empty_enhancer.description": "Создай пустой усилитель", "advancements.indrev.speed_enhancer": "Машина включена Бр-р-р-р", "advancements.indrev.speed_enhancer.description": "Создай улучшение: скорость", "advancements.indrev.buffer_enhancer": "Сверх предела", "advancements.indrev.buffer_enhancer.description": "Создай улучшение: буфер", "advancements.indrev.energy_enhancer": "Всегда эффективен", "advancements.indrev.energy_enhancer.description": "Создай улучшение: энергия", "advancements.indrev.cable_mk1": "Жёлтые кабели!", "advancements.indrev.cable_mk1.description": "Создай кабель мк. 1", "advancements.indrev.cable_mk2": "Синие кабели!", "advancements.indrev.cable_mk2.description": "Создай кабель мк. 2", "advancements.indrev.cable_mk3": "Фиолетовые кабели!", "advancements.indrev.cable_mk3.description": "Создай кабель мк. 3", "advancements.indrev.cable_mk4": "Красные кабели!", "advancements.indrev.cable_mk4.description": "Создай кабель мк. 4", "advancements.indrev.modular_workbench": "Он не выглядит как Верстак", "advancements.indrev.modular_workbench.description": "Создай модульный верстак", "advancements.indrev.modular_armor": "Модульность", "advancements.indrev.modular_armor.description": "Создай полный набор Модульной брони", "advancements.indrev.gamer_axe": "Не достаточно RGB", "advancements.indrev.gamer_axe.description": "Создай геймерский топор", "advancements.indrev.chopper_mk4": "Авто добыча дерева", "advancements.indrev.chopper_mk4.description": "Создай лесоруб", "advancements.indrev.rancher_mk1": "Я, робот-фермер", "advancements.indrev.rancher_mk1.description": "Создай фермер", "advancements.indrev.rancher_mk4": "Механизированное производство", "advancements.indrev.rancher_mk4.description": "Создай скотовод", "advancements.indrev.industrial_smelter": "Улучшенная переработка руды", "advancements.indrev.industrial_smelter.description": "Создай производственную плавильную печь", "advancements.indrev.condenser": "У меня закончились идеи", "advancements.indrev.condenser.description": "Создай конденсатор", "advancements.indrev.blast_furnace_enhancer": "Доменная электропечь", "advancements.indrev.blast_furnace_enhancer.description": "Создай улучшение: горячее дутьё", "advancements.indrev.smoker_enhancer": "Мечта шефа", "advancements.indrev.smoker_enhancer.description": "Создай улучшение: коптильня", "advancements.indrev.circuit_mk1": "Первые машины", "advancements.indrev.circuit_mk1.description": "Создай схему мк. 1", "advancements.indrev.circuit_mk2": "Новая технология", "advancements.indrev.circuit_mk2.description": "Создай схему мк. 2", "advancements.indrev.circuit_mk3": "Продвинутая технология", "advancements.indrev.circuit_mk3.description": "Создай схему мк. 3", "advancements.indrev.circuit_mk4": "Конечная технология", "advancements.indrev.circuit_mk4.description": "Создай схему мк. 4", "advancements.indrev.farmer_mk1": "Счастливая ферма", "advancements.indrev.farmer_mk1.description": "Создай фермер мк. 1", "advancements.indrev.slaughter_mk1": "Конечный убийца мобов", "advancements.indrev.slaughter_mk1.description": "Создай забойник мк. 1", "gui.indrev.tip": "ПОДСКАЗКА ", "gui.indrev.tip_0": "Убедись, что настроил вход/выход для предмета и жидкости.", "gui.indrev.tip_1": "Не использовать кабели, превышающие предел ввода.", "gui.indrev.tip_2": "Используй охладители, как Крутой(ая) парень/девушка.", "gui.indrev.tip_3": "Используй охладители, чтобы никогда не терять эффективность.", "gui.indrev.tip_4": "Завод. Должен. Расти.", "gui.indrev.tip_5": "Тебе следует проверить Модульную броню.", "gui.indrev.tip_6": "Реальные геймеры используют Геймерский топор.", "gui.indrev.tip_7": "Если у тебя есть проблемы или предложения, присоединяйся в наш Discord (Только для англоязычных игроков).", "gui.indrev.tip_8": "Прежде чем обновиться, всегда делай резервную копию миров.", "gui.indrev.tip_9": "Прикрой свои пальцы в ночное время.", "gui.indrev.modules_installed": "Установлено модулей: ", "gui.indrev.shield": "Щит: ", "gui.indrev.installing": "Установка", "gui.indrev.incompatible": "Нельзя установить.", "gui.indrev.max_level": "Макс. уровень модуля.", "gui.indrev.progress": "Прогресс: ", "gui.indrev.whitelist.true": "Белый список", "gui.indrev.whitelist.false": "Чёрный список", "gui.indrev.matchDurability.true": "Соответствовать по повреждению предмета.", "gui.indrev.matchDurability.false": "Игнорировать повреждение предмета.", "gui.indrev.matchTag.true": "Соответствовать предмет по NBT.", "gui.indrev.matchTag.false": "Игнорировать NBT предмета.", "death.attack.acid": "%s был растворён в серной кислоте.", "death.attack.acid.player": "%s был растворён в серной кислоте, при попытке к бегству от %s.", "death.attack.laser": "%s попал под лазер и поджарился.", "death.attack.laser.player": "%s попал под лазер и поджарился, при попытке к бегству от %s.", "vein.indrev.bauxite": "Боксит", "vein.indrev.certus_quartz": "Истинный кварц", "vein.indrev.peat": "Торф", "vein.indrev.lignite": "Бурый уголь", "vein.indrev.bituminous": "Каменный уголь", "vein.indrev.anthracite": "Антрацит", "vein.indrev.cuprite": "Куприт", "vein.indrev.calaverite": "Калаверит", "vein.indrev.calaverite_nether": "Незерский калаверит", "vein.indrev.iridium": "Иридий", "vein.indrev.siderite": "Сидерит", "vein.indrev.limonite": "Лимонит", "vein.indrev.hematite": "Гематит", "vein.indrev.magnetite": "Магнетит", "vein.indrev.chalcopryte": "Халькопирит", "vein.indrev.nikolite": "Николит", "vein.indrev.quartz": "Кварц", "vein.indrev.argentite": "Сернистое серебро", "vein.indrev.chlorargyrite": "Кераргирит", "vein.indrev.cassiterite": "Касситерит", "vein.indrev.stannite": "Станнит", "vein.indrev.scheelite": "Шеелит", "vein.indrev.wolframite": "Вольфрамит", "vein.indrev.ferberite": "Вольфрамовокислое железо", "vein.indrev.galena": "Галенит", "vein.indrev.glowstonedeposit": "Месторождение светокамня", "vein.indrev.soul_nether": "Месторождение незер-душ", "vein.indrev.sulfur_nether": "Сера", "vein.indrev.denseice": "Месторождение плотного льда", "attribute.indrev.shield": "Щит", "subtitles.indrev.laser": "Лазерный луч", "item.indrev.reinforced_elytra": "Укреплённые элитры", "item.indrev.jetpack_mk1": "Реактивный ранец мк. 1", "item.indrev.jetpack_mk2": "Реактивный ранец мк. 2", "item.indrev.jetpack_mk3": "Реактивный ранец мк. 3", "item.indrev.jetpack_mk4": "Реактивный ранец мк. 4", "block.indrev.gas_generator": "Газовый генератор", "block.indrev.gas_generator_mk4": "Газовый генератор марк. 4", "item.indrev.soot": "Копоть", "item.indrev.carbon_fiber_plate": "Углеволоконная пластина", "item.indrev.carbon_fiber_rod": "Углеволоконный стержень", "item.indrev.carbon_fiber_helmet_frame": "Углеволоконный шлемовый каркас", "item.indrev.carbon_fiber_chest_frame": "Углеволоконный нагрудный каркас", "item.indrev.carbon_fiber_legs_frame": "Углеволоконный поножный каркас", "item.indrev.carbon_fiber_boots_frame": "Углеволоконный ботиночный каркас", "block.indrev.boiler": "Котёл", "block.indrev.steam_turbine_mk4": "Паровая турбина" } ================================================ FILE: src/main/resources/assets/indrev/lang/tr_tr.json ================================================ { "item.patchouli.industrial_revolution_book.name": "Devrimci Rehber", "item.indrev.guide_book": "Devrimci Rehber", "item.patchouli.industrial_revolution_book.landing": "Bu kitap modun bazı önemli bakış açılarından gitmenizi sağlayacak.$(br2)Eğer bir problemle karşılaşırsanız, ya da sorunuz veya önerileriniz varsa, şurada sorun açın $(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$() ya da Discord'umuza $(l:https://discord.com/invite/G4PjhEf)Katılın!$()$(br2)Oynadığınız için teşekkürler!", "itemGroup.indrev.indrev_group": "Endüstriyel Devrim", "block.machines.tooltip.io": "I/O: %s", "block.indrev.lazuli_flux_container_1": "Lazuli Flux", "block.indrev.lazuli_flux_container_2": "Konteyner", "block.indrev.coal_generator": "Kömür Jeneratörü", "block.indrev.coal_generator_mk1": "Kömür Jeneratörü", "block.indrev.heat_generator": "Isı Jeneratörü", "block.indrev.heat_generator_mk4": "Isı Jeneratörü", "block.indrev.electric_furnace": "Elektrikli Fırın", "block.indrev.electric_furnace_mk1": "Elektrikli Fırın MK1", "block.indrev.electric_furnace_mk2": "Elektrikli Fırın MK2", "block.indrev.electric_furnace_mk3": "Elektrikli Fırın MK3", "block.indrev.electric_furnace_mk4": "Elektrikli Fırın MK4", "block.indrev.electric_furnace_creative": "Elektrikli Fırın Yaratıcı", "block.indrev.electric_furnace_factory": "Elektrikli Fırın Fabrikası", "block.indrev.electric_furnace_factory_mk4": "Elektrikli Fırın Fabrikası MK4", "block.indrev.smelter": "Endüstriyel Dökümcü", "block.indrev.smelter_mk4": "Endüstriyel Dökümcü", "block.indrev.condenser": "Yoğunlaştırıcı", "block.indrev.condenser_mk4": "Yoğunlaştırıcı", "block.indrev.pulverizer": "Öğütücü", "block.indrev.pulverizer_mk1": "Öğütücü MK1", "block.indrev.pulverizer_mk2": "Öğütücü MK2", "block.indrev.pulverizer_mk3": "Öğütücü MK3", "block.indrev.pulverizer_mk4": "Öğütücü MK4", "block.indrev.pulverizer_creative": "Yaratıcı Öğütücü", "block.indrev.pulverizer_factory": "Öğütücü Fabrikası", "block.indrev.pulverizer_factory_mk4": "Öğütücü Fabrikası MK4", "block.indrev.sawmill": "Keresteci", "block.indrev.sawmill_mk1": "Keresteci MK1", "block.indrev.sawmill_mk2": "Keresteci MK2", "block.indrev.sawmill_mk3": "Keresteci MK3", "block.indrev.sawmill_mk4": "Keresteci MK4", "block.indrev.sawmill_creative": "Keresteci Yaratıcı", "block.indrev.compressor": "Kompresör", "block.indrev.compressor_mk1": "Kompresör MK1", "block.indrev.compressor_mk2": "Kompresör MK2", "block.indrev.compressor_mk3": "Kompresör MK3", "block.indrev.compressor_mk4": "Kompresör MK4", "block.indrev.compressor_creative": "Kompresör Yaratıcı", "block.indrev.compressor_factory": "Kompresör Fabrikası", "block.indrev.compressor_factory_mk4": "Kompresör Fabrikası MK4", "block.indrev.solid_infuser_factory": "Katı İnfüzör Fabrikası", "block.indrev.solid_infuser": "Katı İnfüzör", "block.indrev.solid_infuser_mk1": "Katı İnfüzör MK1", "block.indrev.solid_infuser_mk2": "Katı İnfüzör MK2", "block.indrev.solid_infuser_mk3": "Katı İnfüzör MK3", "block.indrev.solid_infuser_mk4": "Katı İnfüzör MK4", "block.indrev.solid_infuser_creative": "Katı İnfüzör Yaratıcı", "block.indrev.solid_infuser_factory_mk4": "Katı İnfüzör Fabrikası MK4", "block.indrev.lazuli_flux_container_mk1": "Lazuli Flux Konteyneri MK1", "block.indrev.lazuli_flux_container_mk2": "Lazuli Flux Konteyneri MK2", "block.indrev.lazuli_flux_container_mk3": "Lazuli Flux Konteyneri MK3", "block.indrev.lazuli_flux_container_mk4": "Lazuli Flux Konteyneri MK4", "block.indrev.lazuli_flux_container_creative": "Yaratıcı Lazuli Flux Konteyneri", "block.indrev.cable_mk1": "Kablo MK1", "block.indrev.cable_mk2": "Kablo MK2", "block.indrev.cable_mk3": "Kablo MK3", "block.indrev.cable_mk4": "Kablo MK4", "block.indrev.fluid_pipe_mk1": "Sıvı Borusu MK1", "block.indrev.fluid_pipe_mk2": "Sıvı Borusu MK2", "block.indrev.fluid_pipe_mk3": "Sıvı Borusu MK3", "block.indrev.fluid_pipe_mk4": "Sıvı Borusu MK4", "block.indrev.item_pipe_mk1": "Eşya Borusu MK1", "block.indrev.item_pipe_mk2": "Eşya Borusu MK2", "block.indrev.item_pipe_mk3": "Eşya Borusu MK3", "block.indrev.item_pipe_mk4": "Eşya Borusu MK4", "block.indrev.fisher": "Balıkçı", "block.indrev.fisher_mk2": "Basit Balıkçı", "block.indrev.fisher_mk3": "İyileştirilmiş Balıkçı", "block.indrev.fisher_mk4": "Gelişmiş Balıkçı", "block.indrev.mining_rig": "Madenci Teçhizat Yöneticisi", "block.indrev.mining_rig_mk4": "Madenci Teçhizat Yöneticisi", "block.indrev.mining_rig.tooltip": " Madenciyi kullanmadan önce bu Chunk'da Chunk Tarayıcısını kullanmalısınız", "block.indrev.mining_rig.mined": "%s kazıldı", "block.indrev.drill": "madencilik teçhizatı matkabı", "block.indrev.drill_bottom": "madencilik teçhizatı matkabı", "block.indrev.drill_middle": "madencilik teçhizatı matkabı", "block.indrev.drill_top": "madencilik teçhizatı matkabı", "block.indrev.drill.faster": "%sx daha hızlı", "block.indrev.drill.no_drills": "Boş.", "block.indrev.drill.active": "Aktif matkaplar", "block.indrev.drill.activating": "Hazır değil", "block.indrev.drill.not_enough_power": "Yetersiz güç", "block.indrev.drill.power_required": "Çalışmak için en az %sLF gerektirir", "block.indrev.recycler": "Geri Dönüştürücü", "block.indrev.recycler_mk2": "Geri Dönüştürücü", "block.indrev.cable": "Kablo", "block.indrev.farmer_mk1": "Çiftçi MK1", "block.indrev.farmer_mk2": "Çiftçi MK2", "block.indrev.farmer_mk3": "Çiftçi MK3", "block.indrev.farmer_mk4": "Çiftçi MK4", "block.indrev.farmer_creative": "Yaratıcı Çiftçi", "block.indrev.farmer": "Çiftçi", "block.indrev.slaughter_mk1": "Kırımcı MK1", "block.indrev.slaughter_mk2": "Kırımcı MK2", "block.indrev.slaughter_mk3": "Kırımcı MK3", "block.indrev.slaughter_mk4": "Kırımcı MK4", "block.indrev.slaughter_creative": "Yaratıcı Kırımcı", "block.indrev.slaughter": "Kırımcı", "block.indrev.chopper": "Oduncu", "block.indrev.chopper_mk1": "Oduncu MK1", "block.indrev.chopper_mk2": "Oduncu MK2", "block.indrev.chopper_mk3": "Oduncu MK3", "block.indrev.chopper_mk4": "Oduncu MK4", "block.indrev.chopper_creative": "Yaratıcı Oduncu", "block.indrev.rancher": "Besici", "block.indrev.rancher_mk1": "Besici MK1", "block.indrev.rancher_mk2": "Besici MK2", "block.indrev.rancher_mk3": "Besici MK3", "block.indrev.rancher_mk4": "Besici MK4", "block.indrev.rancher_creative": "Yaratıcı Çiftlik", "block.indrev.fluid_infuser": "Sıvı İnfüzör", "block.indrev.fluid_infuser_mk1": "Sıvı İnfüzör MK1", "block.indrev.fluid_infuser_mk2": "Sıvı İnfüzör MK2", "block.indrev.fluid_infuser_mk3": "Sıvı İnfüzör MK3", "block.indrev.fluid_infuser_mk4": "Sıvı İnfüzör MK4", "block.indrev.fluid_infuser_creative": "Sıvı İnfüzör Yaratıcı", "block.indrev.drain_mk1": "Süzgeç", "block.indrev.tank": "Tank", "block.indrev.factory": "Fabrika", "block.indrev.biomass_generator": "Biyoyakıt Jeneratörü", "block.indrev.biomass_generator_mk3": "Biyoyakıt Jeneratörü", "block.indrev.solar_generator": "Güneş Jeneratörü", "block.indrev.solar_generator_mk1": "Güneş Jeneratörü", "block.indrev.solar_generator_mk3": "Güneş Jeneratörü", "block.indrev.charge_pad_mk4": "Şarj Pad'i", "block.indrev.charge_pad_mk4.tooltip": "Takılı ekipmanları ya da içine koyduğun herhangi birşeyi şarj eder!", "block.indrev.aoe.range": "Menzil: %s", "block.indrev.aoe.toggle.false": "Menzili göster", "block.indrev.aoe.toggle.true": "Menzili gizle", "block.indrev.planks": "Tahta", "block.indrev.plank_block": "Tahta bloğu", "block.indrev.wither_proof_obsidian": "Wither Geçirmez Obsidyen", "block.indrev.controller": "Yönetici", "block.indrev.frame": "İskelet", "block.indrev.duct": "Kanal", "block.indrev.warning_strobe": "Uyarı flaşı", "block.indrev.silo": "Silo", "block.indrev.intake": "Giriş", "block.indrev.cabinet": "Kabinet", "block.indrev.coolant": "Soğutucu", "block.indrev.molten_iron": "Erimiş Demir", "block.indrev.molten_gold": "Erimiş Altın", "block.indrev.molten_copper": "Erimiş Bakır", "block.indrev.molten_tin": "Erimiş Tin", "block.indrev.molten_netherite": "Erimiş Netherit", "block.indrev.sulfuric_acid": "Sülfürik Asit", "item.indrev.sulfuric_acid_bucket": "Sülfürik Asit Kovası", "block.indrev.toxic_mud": "Toksik çamur", "item.indrev.toxic_mud_bucket": "Toksik çamur Kovası", "block.indrev.machine_block": "Makine Bloğu", "item.indrev.modular_core": "Modülerliğin Merkezi (Şarj olmamış)", "item.indrev.modular_core_activated": "Modülerliğin Merkezi", "item.indrev.sawdust": "Talaş", "block.indrev.laser_emitter_mk4": "Laser Yayıcı", "block.indrev.capsule": "Kapsül", "block.indrev.tin_ore": "Tin Cevheri", "block.indrev.tin_block": "Tin bloğu", "item.indrev.tin_ingot": "Tin Külçesi", "item.indrev.tin_chunk": "Tin Yığını", "item.indrev.tin_dust": "Tin Tozu", "item.indrev.tin_plate": "Tin Levha", "item.indrev.tin_gear": "Tin Çark", "item.indrev.tin_nugget": "Tin Parçası", "item.indrev.molten_tin_bucket": "Erimiş Tin Kovası", "item.indrev.tin_purified_ore": "Saflaştırılmış Tin Cevheri", "item.indrev.copper_chunk": "Bakır Yığını", "item.indrev.copper_dust": "Bakır Tozu", "item.indrev.copper_plate": "Bakır Levha", "item.indrev.copper_gear": "Bakır Çark", "item.indrev.copper_nugget": "Bakır Parçası", "item.indrev.copper_purified_ore": "Saflaştırılmış Bakır Cevheri", "item.indrev.molten_copper_bucket": "Erimiş Bakır Kovası", "block.indrev.molten_lead": "Erimiş Kurşun", "item.indrev.molten_lead_bucket": "Erimiş Kurşun Kovası", "block.indrev.lead_ore": "Kurşun Cevheri", "block.indrev.lead_block": "Kurşun bloğu", "item.indrev.lead_ingot": "Kurşun Külçesi", "item.indrev.lead_chunk": "Kurşun Yığını", "item.indrev.lead_dust": "Kurşun Tozu", "item.indrev.lead_plate": "Kurşun Levha", "item.indrev.lead_gear": "Kurşun Çark", "item.indrev.lead_nugget": "Kurşun Parçası", "item.indrev.lead_purified_ore": "Saflaştırılmış Kurşun Cevheri", "item.indrev.raw_lead": "Ham Kurşun", "item.indrev.raw_silver": "Ham Gümüş", "item.indrev.raw_tungsten": "Ham Tungsten", "item.indrev.raw_tin": "Ham Kalay", "block.indrev.raw_lead_block": "Ham Kurşun Bloğu", "block.indrev.raw_silver_block": "Ham Gümüş Bloğu", "block.indrev.raw_tungsten_block": "Ham Tungsten Bloğu", "block.indrev.raw_tin_block": "Ham Kalay Bloğu", "block.indrev.molten_silver": "Erimiş Gümüş", "item.indrev.molten_silver_bucket": "Erimiş Gümüş Kovası", "block.indrev.silver_ore": "Gümüş Cevheri", "block.indrev.silver_block": "Gümüş Bloğu", "item.indrev.silver_ingot": "Gümüş Külçesi", "item.indrev.silver_chunk": "Gümüş Yığını", "item.indrev.silver_dust": "Gümüş Tozu", "item.indrev.silver_plate": "Gümüş Levha", "item.indrev.silver_gear": "Gümüş Çark", "item.indrev.silver_nugget": "Gümüs Parçası", "item.indrev.silver_purified_ore": "Saflaştırılmış Gümüş Cevheri", "block.indrev.molten_tungsten": "Erimiş Tungsten", "item.indrev.molten_tungsten_bucket": "Erimiş Tungsten Kovası", "block.indrev.tungsten_ore": "Tungsten Cevheri", "block.indrev.tungsten_block": "Tungsten Bloğu", "item.indrev.tungsten_ingot": "Tungsten Külçesi", "item.indrev.tungsten_chunk": "Tungsten Yığını", "item.indrev.tungsten_dust": "Tungsten Tozu", "item.indrev.tungsten_plate": "Tungsten Levha", "item.indrev.tungsten_gear": "Tungsten Çark", "item.indrev.tungsten_nugget": "Tungsten Parçası", "item.indrev.tungsten_purified_ore": "Saflaştırılmış Tungsten Cevheri", "block.indrev.deepslate_tungsten_ore": "Derin Kayrak Tungsten Cevheri", "block.indrev.deepslate_tin_ore": "Derin Kayrak Tin Cevheri", "block.indrev.deepslate_silver_ore": "Derin Kayrak Gümüş Cevheri", "block.indrev.deepslate_nikolite_ore": "Derin Kayrak Nikolit Cevheri", "block.indrev.deepslate_lead_ore": "Derim Kayrak Lead Cevheri", "block.indrev.electrum_block": "Elektrum Bloğu", "item.indrev.electrum_ingot": "Elektrum Külçesi", "item.indrev.electrum_dust": "Elektrum Tozu", "item.indrev.electrum_plate": "Elektrum Levha", "item.indrev.electrum_gear": "Elektrum Çark", "item.indrev.electrum_nugget": "Elektrum Parçası", "block.indrev.bronze_block": "Bronz Bloğu", "item.indrev.bronze_ingot": "Bronz Külçesi", "item.indrev.bronze_chunk": "Bronz Yığını", "item.indrev.bronze_dust": "Bronz Tozu", "item.indrev.bronze_plate": "Bronz Levha", "item.indrev.bronze_gear": "Bronz Çark", "item.indrev.bronze_nugget": "Bronz Parçası", "block.indrev.sulfur_crystal": "Sülfür Kristali", "item.indrev.sulfur_crystal": "Sülfür Kristali", "item.indrev.sulfur_dust": "Sülfür Tozu", "block.indrev.steel_block": "Çelik Bloğu", "item.indrev.steel_dust": "Çelik Tozu", "item.indrev.steel_ingot": "Çelik Külçesi", "item.indrev.steel_plate": "Çelik Levha", "item.indrev.steel_gear": "Çelik Çark", "item.indrev.steel_nugget": "Çelik Parçası", "item.indrev.gold_dust": "Altın Tozu", "item.indrev.gold_plate": "Altın Levha", "item.indrev.gold_chunk": "Altın Yığını", "item.indrev.gold_purified_ore": "Saflaştırılmış Altın Cevheri", "item.indrev.molten_gold_bucket": "Erimiş Altın Kovası", "item.indrev.iron_dust": "Demir Tozu", "item.indrev.iron_plate": "Demir Levha", "item.indrev.iron_chunk": "Demir Yığını", "item.indrev.molten_iron_bucket": "Erimiş Demir Kovası", "item.indrev.iron_purified_ore": "Saflaştırılmış Demir Cevheri", "item.indrev.diamond_dust": "Elmas Tozu", "item.indrev.coal_dust": "Kömür Tozu", "block.indrev.pump_mk1": "Pompa", "item.indrev.empty_enhancer": "Boş Güçlendirici", "item.indrev.buffer_enhancer": "Arabellek Güçlendiricisi", "item.indrev.buffer_enhancer.tooltip": "Enerji kapasitesini arttırır", "item.indrev.energy_enhancer": "Enerji Verimlilik Güçlendiricisi", "item.indrev.energy_enhancer.tooltip": "Enerji kullanımını azaltır", "item.indrev.damage_enhancer": "Hasar Güçlendiricisi", "item.indrev.damage_enhancer.tooltip": "Kırımcı'nın hasarını arttırır", "item.indrev.speed_enhancer": "Hız Güçlendiricisi", "item.indrev.speed_enhancer.tooltip": "Üretim hızını ve güç kullanımını arttırır", "item.indrev.blast_furnace_enhancer": "Maden Fırını Güçlendiricisi", "item.indrev.blast_furnace_enhancer.tooltip": "Elektrikli Fırın'ın Maden Fırını'nın tariflerini kabul etmesini sağlar", "item.indrev.smoker_enhancer": "Duman Fırını Güçlendiricisi", "item.indrev.smoker_enhancer.tooltip": "Elektrikli Fırın'ın Duman Fırını'nın tariflerini kabul etmesini sağlar", "item.indrev.enhancers.incompatible": "Bu makine bu Güçlendiricisi kabul etmiyor", "item.indrev.enhancers.count": "Makine tarafından her bölme için %s kadar sınırlandırılmıştır", "item.indrev.rechargeable.tooltip": "Yeniden şarj edilebilir", "item.indrev.chunk_scanner": "Chunk Tarayıcısı", "item.indrev.chunk_scanner.scanning": "Tarıyor...", "item.indrev.chunk_scanner.scanned1": "Tarama bitti!", "item.indrev.chunk_scanner.scanned2": "Maden damarı türü: %s", "item.indrev.chunk_scanner.already_scanned": "Bu Chunk zaten taranmış ve %s damarına sahip", "item.indrev.chunk_scanner.tooltip1": "Chunk'ı taramak için sağ tıka basılı tutun", "item.indrev.chunk_scanner.tooltip2": "Damar Türü: %s", "item.indrev.chunk_scanner.tooltip3": "%s'den %s'ye", "item.indrev.chunk_scanner.tooltip4": "Boyut: %s", "item.indrev.chunk_scanner.tooltip5": "Daha fazla okumak için sağ tıklayın", "item.indrev.fan": "Vantilatör", "item.indrev.cooler_cell": "Soğutucu Hücre", "item.indrev.heatsink": "Soğutucu", "item.indrev.heat_coil": "Isı Bobini", "item.indrev.heat_coil.tooltip": "Lazuli Flux'ı Isıya çevirir", "item.indrev.stone_drill_head": "Taş Matkap Ucu", "item.indrev.iron_drill_head": "Demir Matkap Ucu", "item.indrev.diamond_drill_head": "Elmas Matkap Ucu", "item.indrev.netherite_drill_head": "Netherit Matkap Ucu", "item.indrev.mining_drill_mk1": "Kazı Matkabı MK1", "item.indrev.mining_drill_mk2": "Kazı Matkabı MK2", "item.indrev.mining_drill_mk3": "Kazı Matkabı MK3", "item.indrev.mining_drill_mk4": "Modüler Kazı Matkabı", "item.indrev.energy_reader": "Enerji Okuyucu", "item.indrev.energy_reader.use": "Enerji Saklı:", "item.indrev.energy_reader.use1": "%s LF/tick", "item.indrev.enriched_nikolite_dust": "Zenginleştirilmiş Nikolit Tozu", "item.indrev.enriched_nikolite_ingot": "Zenginleştirilmiş Nikolit Cevheri", "item.indrev.battery": "Batarya", "item.indrev.circuit_mk1": "MK1 Devre", "item.indrev.circuit_mk2": "MK2 Devre", "item.indrev.circuit_mk3": "MK3 Devre", "item.indrev.circuit_mk4": "MK4 Devre", "item.indrev.tier_upgrade_mk2": "MK2 Makine Arttırması", "item.indrev.tier_upgrade_mk2.tooltip": "MK1 Makineleri MK2'ye yükseltir", "item.indrev.tier_upgrade_mk3": "MK3 Makine Arttırması", "item.indrev.tier_upgrade_mk3.tooltip": "MK2 Makineleri MK3'e yükseltir", "item.indrev.tier_upgrade_mk4": "MK4 Makine Arttırması", "item.indrev.tier_upgrade_mk4.tooltip": "MK3 Makineleri MK4'e yükseltir", "item.indrev.servo_output": "Çıkış servosu", "item.indrev.servo_output.tooltip": "Bağlı envanterlere iteler", "item.indrev.servo_retriever": "Kurtarıcı Servo", "item.indrev.servo_retriever.tooltip": "Bağlı envanterlerden çeker", "item.indrev.servo.mode": "Öncelik: ", "item.indrev.servo.mode.round_robin": "Round Robin", "item.indrev.servo.mode.round_robin.tooltip": "Önceliği en az eşya/sıvıdır", "item.indrev.servo.mode.nearest_first": "İlk en yakın", "item.indrev.servo.mode.nearest_first.tooltip": "Öncelik en yakındaki konteynerlerdedir", "item.indrev.servo.mode.furthest_first": "İlk en uzak", "item.indrev.servo.mode.furthest_first.tooltip": "Öncelik en uzaktaki konteynerlerdedir", "item.indrev.servo.mode.random": "Rastgele", "item.indrev.servo.mode.random.tooltip": "Öncelik yok", "item.indrev.biomass": "Biyoyakıt", "item.indrev.untanned_leather": "Bronzlaşmamış Deri", "item.indrev.coolant_bucket": "Soğutucu Kovası", "item.indrev.molten_netherite_bucket": "Erimiş Netherit Kovası", "item.indrev.netherite_scrap_purified_ore": "Saflaştırılmış Netherit Hurdası", "item.indrev.netherite_scrap_chunk": "Netherit Hurdası Yığını", "item.indrev.netherite_scrap_dust": "Netherit Hurdası Tozu", "item.indrev.hammer": "Çekiç", "item.indrev.wrench": "İngiliz Anahtarı", "item.indrev.wrench.switch_mode": "Anahtar Modu: %s", "item.indrev.wrench.title": "Makineyi ayarla", "item.indrev.wrench.autopush": "Oto itele", "item.indrev.wrench.autopull": "Oto çek", "item.indrev.wrench.item": "Eşya", "item.indrev.wrench.fluid": "Sıvı", "item.indrev.wrench.energy": "Enerji", "item.indrev.wrench.output": "Çıkış", "item.indrev.wrench.input": "Giriş", "item.indrev.wrench.input_first": "Giriş (İlk Bölme)", "item.indrev.wrench.input_second": "Giriş (İkinci Bölme)", "item.indrev.wrench.input_output": "Giriş ve Çıkış", "item.indrev.wrench.none": "Hiçbiri", "item.indrev.wrench.tooltip": "Mod değişimi için Shift + sağ tık", "item.indrev.wrench.tooltip1": "Mod: %s", "item.indrev.wrench.tooltip1.rotate": "Döndür", "item.indrev.wrench.tooltip1.configure": "Ayarla", "item.indrev.wrench.mode": "Mod: %s", "item.indrev.wrench.side.north": "Kuzey", "item.indrev.wrench.side.south": "Güney", "item.indrev.wrench.side.west": "Batı", "item.indrev.wrench.side.east": "Doğu", "item.indrev.wrench.side.up": "Yukarı", "item.indrev.wrench.side.down": "Aşağı", "item.indrev.wrench.side.top": "Yukarı", "item.indrev.wrench.side.bottom": "Alt", "item.indrev.wrench.side.left": "Sol", "item.indrev.wrench.side.right": "Sağ", "item.indrev.wrench.side.front": "Ön", "item.indrev.wrench.side.back": "Arka", "item.indrev.wrench.connected": "Bağlı olduğu: %s", "item.indrev.tech_soup": "Teknoloji çorbası", "item.indrev.scan_output": "Kaynak Raporu", "item.indrev.portable_charger": "Taşınabilir Şarj Cihazı", "item.indrev.gamer_axe": "Oyuncu Baltası", "gui.indrev.button.auto_split": "Stack'leri oto-ayır", "gui.indrev.resourcereport.size": "Damar Büyüklüğü: %s", "gui.indrev.resourcereport.size.tiny": "Minik", "gui.indrev.resourcereport.size.small": "Küçük", "gui.indrev.resourcereport.size.average": "Ortalama", "gui.indrev.resourcereport.size.big": "Büyük", "gui.indrev.resourcereport.size.very_big": "Bayağı Büyük", "gui.indrev.resourcereport.size.enormous": "Kocaman", "gui.indrev.resourcereport.size.gigantic": "Koskocaman", "gui.furnace.mode": "Mod", "gui.furnace.mode.furnace": "Fırın", "gui.furnace.mode.blast_furnace": "Maden Fırını", "gui.furnace.mode.smoker": "Duman Fırını", "gui.widget.process": "İlerleme: %s", "gui.widget.energy": "Enerji", "gui.widget.temperature": "Isı", "gui.widget.temperature_info.high": "Çok sıcak", "gui.widget.temperature_info.medium": "Tam potansiyel", "gui.widget.temperature_info.low": "Isınmadı", "gui.indrev.solar.on": "Üretiyor", "gui.indrev.heatgen.idle": "Boşta", "gui.indrev.heatgen.title": "%s mB harcıyor", "gui.indrev.heatgen.generating": "%s LF üretiyor", "gui.indrev.heatgen.pertick": "Tick başına", "gui.indrev.heatgen.extra": "Dikkatli ol, çok sıcak!", "gui.indrev.guide_book_shortcut.contains": "Devrimci Rehberi'nde Hakkında Sayfasını açar", "gui.indrev.guide_book_shortcut.missing": "Devrimci Rehberi eksik!", "gui.indrev.tooltip.maxTransferRate": "Aktarma kapasitesi: ", "gui.indrev.tooltip.maxInput": "Giriş kapasitesi: ", "gui.indrev.tooltip.maxOutput": "Çıkış kapasitesi: ", "gui.indrev.tooltip.maxEnergyStored": "Enerji kapasitesi: ", "gui.indrev.tooltip.seconds": "%s saniye", "gui.indrev.tooltip.energyCost": "Enerji maliyeti: ", "gui.indrev.tooltip.processSpeed": "Hız: ", "gui.indrev.tooltip.ratio": "Üretim: ", "gui.indrev.tooltip.press_shift": "Daha fazla bilgi için %s'ye basılı tutun", "gui.indrev.tooltip.lftick": "%s LF/tick", "gui.indrev.tooltip.lf": "%s LF", "gui.indrev.tooltip.itemsec": "%s eşyalar/s", "gui.indrev.tooltip.fluidsec": "%s kovalar/s", "gui.indrev.tooltip.temperatureBoost": "Isındı: ", "gui.indrev.locked": "Kilitli", "gui.indrev.modular_armor_slot_type": "Buraya eşya ekleyin", "gui.indrev.module_slot_type": "Buraya istenen modülü ekleyin", "gui.indrev.scan_output_slot_type": "Kaynak Raporu", "gui.indrev.output_slot_type": "Toplanan Eşyalar", "gui.indrev.chopper_input_axe": "Balta Ekleyin", "gui.indrev.chopper_input_bone_meal": "Kemik Tozu ekleyin", "gui.indrev.chopper_input_sapling": "Fidan ekleyin", "gui.indrev.farmer_input_slot_type": "Tohumlar ve kemik Tozları", "gui.indrev.slaughter_input_sword": "Kılıç ekleyin", "gui.indrev.cooler_slot_type": "Soğutucu", "gui.indrev.battery_slot_type": "Batarya", "gui.indrev.upgrade_slot_type": "Arttırmalar", "gui.indrev.fishingrod": "Balık Oltası", "indrev.category.rei.upgrading": "Bir %s oluşturmalı ve onu %s'e yükseltmek için %s üzerinde kullanmalısınız.", "indrev.category.rei.pulverizing": "Öğütülüyor", "indrev.category.rei.infusing": "İnfüzleniyor", "indrev.category.rei.compressing": "Sıkıştırılıyor", "indrev.category.rei.recycling": "Geri Dönüştürülüyor", "indrev.category.rei.fluid_infusing": "Sıvı İnfüzleniyor", "indrev.category.rei.condensing": "Yoğunlaştırılıyor", "indrev.category.rei.smelting": "Pişiriliyor", "indrev.category.rei.sawmill": "Kereste Fabrikası", "indrev.category.rei.module": "Modül Üretimi", "item.indrev.nikolite_dust": "Nikolit Tozu", "item.indrev.nikolite_ingot": "Nikolit Külçesi", "block.indrev.nikolite_ore": "Nikolite Cevheri", "item.indrev.modular.upgrade": "Modüller Kurulu:", "item.indrev.modular.upgrade.night_vision": "Gece Görüşü %s", "item.indrev.modular.upgrade.speed": "Hız %s", "item.indrev.modular.upgrade.jump_boost": "Zıplama Güçlendirmesi %s", "item.indrev.modular.upgrade.breathing": "Nefes alma %s", "item.indrev.modular.upgrade.protection": "Koruma %s", "item.indrev.modular.upgrade.feather_falling": "Tüy Düşüşü %s", "item.indrev.modular.upgrade.auto_feeder": "Oto Besleme %s", "item.indrev.modular.upgrade.charger": "Şarj edici %s", "item.indrev.modular.upgrade.solar_panel": "Güneş Paneli %s", "item.indrev.modular.upgrade.piglin_tricker": "Piglin Kandırıcı %s", "item.indrev.modular.upgrade.fire_resistance": "Ateş Koruması %s", "item.indrev.modular.upgrade.efficiency": "Verimlilik %s", "item.indrev.modular.upgrade.range": "Alan %s", "item.indrev.modular.upgrade.fortune": "Servet %s", "item.indrev.modular.upgrade.silk_touch": "İpeksi Dokunuş %s", "item.indrev.modular.upgrade.sharpness": "Keskinlik %s", "item.indrev.modular.upgrade.looting": "Yağma %s", "item.indrev.modular.upgrade.fire_aspect": "Ateşten Çehre %s", "item.indrev.modular.upgrade.reach": "Yetişme %s", "item.indrev.steel_helmet": "Çelik Kask", "item.indrev.steel_chestplate": "Çelik Göğüslük", "item.indrev.steel_leggings": "Çelik Pantolon", "item.indrev.steel_boots": "Çelik Botlar", "item.indrev.steel_sword": "Çelik Kılıç", "item.indrev.steel_pickaxe": "Çelik Kazma", "item.indrev.steel_axe": "Çelik Balta", "item.indrev.steel_shovel": "Çelik Kürek", "item.indrev.steel_hoe": "Çelik Çapa", "item.indrev.bronze_helmet": "Bronz Kask", "item.indrev.bronze_chestplate": "Bronz Göğüslük", "item.indrev.bronze_leggings": "Bronz Pantolon", "item.indrev.bronze_boots": "Bronz Botlar", "item.indrev.bronze_sword": "Bronz Kılıç", "item.indrev.bronze_pickaxe": "Bronz Kazma", "item.indrev.bronze_axe": "Bronz Balta", "item.indrev.bronze_shovel": "Bronz Kürek", "item.indrev.bronze_hoe": "Bronz Çapa", "item.indrev.lead_helmet": "Lead Kask", "item.indrev.lead_chestplate": "Lead Göğüslük", "item.indrev.lead_leggings": "Lead Pantolon", "item.indrev.lead_boots": "Lead Botlar", "item.indrev.lead_sword": "Lead Kılıç", "item.indrev.lead_pickaxe": "Lead Kazma", "item.indrev.lead_axe": "Lead Balta", "item.indrev.lead_shovel": "Lead Kürek", "item.indrev.lead_hoe": "Lead Çapa", "item.indrev.silver_helmet": "Gümüş Kask", "item.indrev.silver_chestplate": "Gümüş Göğüslük", "item.indrev.silver_leggings": "Gümüş Pantolon", "item.indrev.silver_boots": "Gümüş Botlar", "item.indrev.silver_sword": "Gümüş Kılıç", "item.indrev.silver_pickaxe": "Gümüş Kazma", "item.indrev.silver_axe": "Gümüş Balta", "item.indrev.silver_shovel": "Gümüş Kürek", "item.indrev.silver_hoe": "Gümüş Çapa", "block.indrev.modular_workbench": "Modüler ÇalışmaMasası", "block.indrev.modular_workbench_mk4": "Modüler ÇalışmaMasası", "item.indrev.modular_armor_helmet": "Modüler Kask", "item.indrev.modular_armor_chest": "Modüler Göğüslük", "item.indrev.modular_armor_legs": "Modüler Pantolon", "item.indrev.modular_armor_boots": "Modüler Botlar", "item.indrev.module_parts": "Şunlara kurulabilir:", "item.indrev.module_parts_head": "Kask", "item.indrev.module_parts_chest": "Göğüslük", "item.indrev.module_parts_legs": "Pantolon", "item.indrev.module_parts_feet": "Botlar", "item.indrev.module_parts_drill": "Kazı Matkabı", "item.indrev.module_parts_gamer_axe": "Oyuncu Baltası", "item.indrev.module_max_level": "En yüksek seviye: %s", "item.indrev.module_protection": "Koruma Modülü ", "item.indrev.module_protection.tooltip": "Zırhın sağladığı korumayı arttırır", "item.indrev.module_feather_falling": "Tüy Düşüşü Modülü", "item.indrev.module_feather_falling.tooltip": "Enerji karşılığında düşüş hasarını azaltır.", "item.indrev.module_speed": "Hız Modülü", "item.indrev.module_speed.tooltip": "Enerji karşılığında hız efekti verir.", "item.indrev.module_jump_boost": "Zıplama Desteği Modülü", "item.indrev.module_jump_boost.tooltip": "Enerji karşılığında Zıplama Desteği efekti verir.", "item.indrev.module_night_vision": "Gece Görüşü Modülü", "item.indrev.module_night_vision.tooltip": "Enerji karşılığında Gece Görüşü efekti verir.", "item.indrev.module_breathing": "Nefes Alma Modülü", "item.indrev.module_breathing.tooltip": "Enerji karşılığında su altında Nefes Alma efekti verir.", "item.indrev.module_auto_feeder": "Oto Besleyici Modülü", "item.indrev.module_auto_feeder.tooltip": "Enerji karşılığında envanterinde otomatik yemek yemeni sağlar.", "item.indrev.module_charger": "Şarj edici Modülü", "item.indrev.module_charger.tooltip": "Envanterindeki herhangi bir eşyayı şarj eder (zırh içermez).", "item.indrev.module_solar_panel": "Güneş Paneli Modülü", "item.indrev.module_solar_panel.tooltip": "Günışığında zırhını ve elindeki eşyayı şarj eder.", "item.indrev.module_piglin_tricker": "Piglin Kandırıcı Modülü", "item.indrev.module_piglin_tricker.tooltip": "Piglinleri altın giyiyormuşsun diye kandırır.", "item.indrev.module_fire_resistance": "Ateş Direnci Modülü", "item.indrev.module_fire_resistance.tooltip": "Enerji karşılığında gerektiğinde Ateş Direnci efekti verir.", "item.indrev.module_efficiency": "Verimlilik Modülü", "item.indrev.module_efficiency.tooltip": "Kazı aletininin hızını arttırır.", "item.indrev.module_range": "Alan Modülü", "item.indrev.module_range.tooltip": "Matkabının bir kerede kurabileceği blok sayısını arttırır.", "item.indrev.module_fortune": "Servet Modülü", "item.indrev.module_fortune.tooltip": "Servet büyüsü gibi davranır.", "item.indrev.module_silk_touch": "İpeksi Dokunuş Modülü", "item.indrev.module_silk_touch.tooltip": "İpeksi Dokunuş büyüsü gibi davranır.", "item.indrev.module_reach": "Yetişme Modülü", "item.indrev.module_reach.tooltip": "Aynı anda birden fazla canavara hasar vurur.", "item.indrev.module_looting": "Yağma Modülü", "item.indrev.module_looting.tooltip": "Yağma büyüsü gibi davranır.", "item.indrev.module_fire_aspect": "Ateşten Çehre Modülü", "item.indrev.module_fire_aspect.tooltip": "Ateşten Çehre büyüsü gibi davranır.", "item.indrev.module_sharpness": "Keskinlik Modülü", "item.indrev.module_sharpness.tooltip": "Vurduğun hasarı arttırır.", "item.indrev.module_color.tooltip": "§cR§ee§an§9k§6§dl§1e§2r", "item.indrev.module_color_pink": "Pembe Renk Modülü", "item.indrev.module_color_red": "Kırmızı Renk Modülü", "item.indrev.module_color_purple": "Mor Renk Modülü", "item.indrev.module_color_blue": "Mavi Renk Modülü", "item.indrev.module_color_cyan": "Camgöbeği Renk Modülü", "item.indrev.module_color_green": "Yeşil Renk Modülü", "item.indrev.module_color_yellow": "Sarı Renk Modülü", "item.indrev.module_color_orange": "Turuncu Renk Modülü", "item.indrev.module_color_black": "Siyah Renk Modülü", "item.indrev.module_color_brown": "Kahverengi Renk Modülü", "item.indrev.copper_sword": "Bakır Kılıç", "item.indrev.copper_pickaxe": "Bakır Kazma", "item.indrev.copper_axe": "Bakır Balta", "item.indrev.copper_shovel": "Bakır Kürek", "item.indrev.copper_hoe": "Bakır Çapa", "item.indrev.copper_helmet": "Bakır Kask", "item.indrev.copper_chestplate": "Bakır Göğüslük", "item.indrev.copper_leggings": "Bakır Pantolon", "item.indrev.copper_boots": "Bakır Botlar", "item.indrev.tin_sword": "Tin Kılıç", "item.indrev.tin_pickaxe": "Tin Kazma", "item.indrev.tin_axe": "Tin Balta", "item.indrev.tin_shovel": "Tin Kürek", "item.indrev.tin_hoe": "Tin Çapa", "item.indrev.tin_helmet": "Tin Kask", "item.indrev.tin_chestplate": "Tin Göğüslük", "item.indrev.tin_leggings": "Tin Pantolon", "item.indrev.tin_boots": "Tin Botlar", "category.indrev": "Endüstriyel Devrim", "key.indrev.modular": "Modüler eşya Yapılandırması", "text.multiblock.not_built": "Yapı inşa edilmedi!", "advancements.indrev.nikolite": "Başlangıç", "advancements.indrev.nikolite.description": "Nikolit Bulun", "advancements.indrev.machine_block": "Temeller", "advancements.indrev.machine_block.description": "Bir Makine Bloğu elde edin", "advancements.indrev.coal_generator": "İlk Adımlar", "advancements.indrev.coal_generator.description": "Bir Kömür Jeneratörü elde edin", "advancements.indrev.basic_solar_generator": "Pasif Enerji", "advancements.indrev.basic_solar_generator.description": "Bir Basit Güneş Paneli üretin", "advancements.indrev.advanced_solar_generator": "Daha çok Pasif Enerji", "advancements.indrev.advanced_solar_generator.description": "Gelişmiş Güneş Jeneratörü elde edin", "advancements.indrev.heat_generator": "Burası sıcak mı oldu ne", "advancements.indrev.heat_generator.description": "Bir Isı Jeneratörü elde edin", "advancements.indrev.biomass_generator": "Çok Çevre-Dostu değil", "advancements.indrev.biomass_generator.description": "Bir Biyoyakıt Jeneratörü elde edin", "advancements.indrev.pulverizer": "Burası tozlanıyor mu ne", "advancements.indrev.pulverizer.description": "Bir Öğütücü elde edin", "advancements.indrev.electric_furnace": "Daha iyi Fırın", "advancements.indrev.electric_furnace.description": "Bir Elektrikli Fırın elde edin", "advancements.indrev.compressor": "Parmaklarına Dikkat et", "advancements.indrev.compressor.description": "Bir Kompresör elde edin", "advancements.indrev.recycler": "Çevre-Dostu", "advancements.indrev.recycler.description": "Bir Geri Dönüştürücü elde edin", "advancements.indrev.solid_infuser": "İnfüzleniyor", "advancements.indrev.solid_infuser.description": "Bir Katı İnfüzör elde edin", "advancements.indrev.mk2_upgrade": "İki kat Verimlilik", "advancements.indrev.mk2_upgrade.description": "Bir makineyi MK2'ye yükseltin", "advancements.indrev.mk3_upgrade": "Üç kat Verimlilik", "advancements.indrev.mk3_upgrade.description": "Bir makineyi MK3'e yükseltin", "advancements.indrev.mk4_upgrade": "Fabrika. Büyü. Meli", "advancements.indrev.mk4_upgrade.description": "Bir makineyi MK4'e yükseltin", "advancements.indrev.biomass": "İğrenç görünüyor...", "advancements.indrev.biomass.description": "Biyoyakıt elde edin", "advancements.indrev.nikolite_ingot": "Yeni Ufuklar", "advancements.indrev.nikolite_ingot.description": "Nikolit Külçesi elde edin", "advancements.indrev.enriched_nikolite_dust": "Güçlü Elektronikler", "advancements.indrev.enriched_nikolite_dust.description": "Zenginleştirilmiş Nikolit Tozu elde edin", "advancements.indrev.enriched_nikolite_ingot": "Daha. Fazla. Güç.", "advancements.indrev.enriched_nikolite_ingot.description": "Zenginleştirilmiş Nikolit Kulcesi elde edin", "advancements.indrev.lazuli_flux_container_mk1": "Basit Enerji Deposu", "advancements.indrev.lazuli_flux_container_mk1.description": "Get a Lazuli Flux Container MK1", "advancements.indrev.lazuli_flux_container_mk2": "Ortalama Enerji Deposu", "advancements.indrev.lazuli_flux_container_mk2.description": "Get a Lazuli Flux Container MK2", "advancements.indrev.lazuli_flux_container_mk3": "Çok ortalama olmayan Enerji Deposu", "advancements.indrev.lazuli_flux_container_mk3.description": "Get a Lazuli Flux Container MK3", "advancements.indrev.lazuli_flux_container_mk4": "Nihai Enerji Deposu", "advancements.indrev.lazuli_flux_container_mk4.description": "Get a Lazuli Flux Container MK4", "advancements.indrev.empty_enhancer": "Güzel bir Tabak", "advancements.indrev.empty_enhancer.description": "Bir Boş Güçlendirici elde edin", "advancements.indrev.speed_enhancer": "Makine goes Brrrr", "advancements.indrev.speed_enhancer.description": "Bir hız Güçlendiricisi elde edin", "advancements.indrev.buffer_enhancer": "Sınırların Ötesinde", "advancements.indrev.buffer_enhancer.description": "Bir Arabellek Güçlendiricisi elde edin", "advancements.indrev.energy_enhancer": "Her zaman Verimli", "advancements.indrev.energy_enhancer.description": "Bir Enerji Güçlendiricisi elde edin", "advancements.indrev.cable_mk1": "Sarı Kablolar!", "advancements.indrev.cable_mk1.description": "Bir Kablo MK1 elde edin", "advancements.indrev.cable_mk2": "Mavi Kablolar!", "advancements.indrev.cable_mk2.description": "Bir Kablo MK1 elde edin", "advancements.indrev.cable_mk3": "Pembe Kablolar!", "advancements.indrev.cable_mk3.description": "Bir Kablo MK3 elde edin", "advancements.indrev.cable_mk4": "Kırmızı Kablolar!", "advancements.indrev.cable_mk4.description": "Bir Kablo MK4 elde edin", "advancements.indrev.modular_workbench": "Bir Çalışma Masasına benzemiyor", "advancements.indrev.modular_workbench.description": "Bir Modüler ÇalışmaMasası elde et", "advancements.indrev.modular_armor": "Modülerlik", "advancements.indrev.modular_armor.description": "Modüler Zırhını tamamla", "advancements.indrev.gamer_axe": "Yeterli RGB yok", "advancements.indrev.gamer_axe.description": "Oyuncu Baltası'nı elde et", "advancements.indrev.chopper_mk4": "Oto Odun Farmı", "advancements.indrev.chopper_mk4.description": "Oduncu elde edin", "advancements.indrev.rancher_mk1": "Ben, Robot Besici", "advancements.indrev.rancher_mk1.description": "Bir Besici elde et", "advancements.indrev.rancher_mk4": "Mekanize Üretim", "advancements.indrev.rancher_mk4.description": "Bir Besici elde et", "advancements.indrev.industrial_smelter": "Geliştirilmiş Cevher İşlemesi", "advancements.indrev.industrial_smelter.description": "Bir Endüstriyel Pişirici üret", "advancements.indrev.condenser": "Aklımda fikir kalmadı", "advancements.indrev.condenser.description": "Bir Yoğunlaştırıcı elde et", "advancements.indrev.blast_furnace_enhancer": "Maden Elektrikli Fırını", "advancements.indrev.blast_furnace_enhancer.description": "Maden Pişirici arttırmasını elde et", "advancements.indrev.smoker_enhancer": "Bir şefin rüyası", "advancements.indrev.smoker_enhancer.description": "Dumanlı Pişirici arttırmasını elde et", "advancements.indrev.circuit_mk1": "İlk Makineler", "advancements.indrev.circuit_mk1.description": "Bir MK1 Devresi elde et", "advancements.indrev.circuit_mk2": "Yeni Teknoloji", "advancements.indrev.circuit_mk2.description": "Bir MK2 Devresi elde et", "advancements.indrev.circuit_mk3": "Gelişmiş Teknoloji", "advancements.indrev.circuit_mk3.description": "Bir MK3 Devresi elde et", "advancements.indrev.circuit_mk4": "Nihai Teknoloji", "advancements.indrev.circuit_mk4.description": "Bir MK4 Devresi elde et", "advancements.indrev.farmer_mk1": "Multi Küçük Çiftlik", "advancements.indrev.farmer_mk1.description": "Bir MK1 Çiftçi elde et", "advancements.indrev.slaughter_mk1": "Nihai Canavar öldürücüsü", "advancements.indrev.slaughter_mk1.description": "Bir MK1 Kırımcı elde et", "gui.indrev.tip": "İPUCU ", "gui.indrev.tip_0": "Sıvı ve eşya girişleri ve çıkışlarını ayarlamayı unutmayın", "gui.indrev.tip_1": "Giriş limitinin üzerinde kablo kullanmayın", "gui.indrev.tip_2": "Havalı adamlar gibi soğutucu kullanın", "gui.indrev.tip_3": "Verimliliği kaybetmemek için soğutucu kullanın", "gui.indrev.tip_4": "Fabrika. Büyü. Meli.", "gui.indrev.tip_5": "Modüler Zırha bi göz atmalısın", "gui.indrev.tip_6": "Gerçek oyuncular Oyuncu Baltası kullanır", "gui.indrev.tip_7": "Eğer sorununuz ya da önerileriniz varsa Discord'umuza katılın!", "gui.indrev.tip_8": "Güncellemeden önce her zaman dünyalarınızı yedekleyin", "gui.indrev.tip_9": "Tırnaklarınızın üstünü gece kapatın", "gui.indrev.modules_installed": "Modüller Kurulu: ", "gui.indrev.shield": "Kalkan: ", "gui.indrev.installing": "Kuruluyor", "gui.indrev.incompatible": "Kurulamaz", "gui.indrev.max_level": "En yüksek seviyeye ulaşıldı", "gui.indrev.progress": "İlerleme: ", "gui.indrev.whitelist.true": "Beyaz Liste", "gui.indrev.whitelist.false": "Kara liste", "gui.indrev.matchDurability.true": "Eşya hasarını eşle", "gui.indrev.matchDurability.false": "Eşya hasarını görmezden gel", "gui.indrev.matchTag.true": "Eşya NBT'sini eşle", "gui.indrev.matchTag.false": "Eşya NBT'sini görmezden gel", "death.attack.acid": "%s Sülfürik Asit'te çözüldü", "death.attack.acid.player": "%s %s kişisinden kaçarken Sülfürik Asit'te çözüldü", "death.attack.laser": "%s lazere doğru koştu ve kızardı.", "death.attack.laser.player": "%s %s kişisinden kaçarken lazere doğru koştu ve kızardı", "vein.indrev.bauxite": "Bauksit", "vein.indrev.certus_quartz": "Sert Kuvars", "vein.indrev.peat": "Peat", "vein.indrev.lignite": "Lignit", "vein.indrev.bituminous": "Bituminous", "vein.indrev.anthracite": "Antrasit", "vein.indrev.cuprite": "Kuprot", "vein.indrev.calaverite": "Kalaverit", "vein.indrev.calaverite_nether": "Kalaverit (Nether)", "vein.indrev.iridium": "İridyum", "vein.indrev.siderite": "Siderit", "vein.indrev.limonite": "Limonit", "vein.indrev.hematite": "Hematit", "vein.indrev.magnetite": "Magnetit", "vein.indrev.chalcopryte": "Kalkoprit", "vein.indrev.nikolite": "Nikolit", "vein.indrev.quartz": "Kuvars", "vein.indrev.argentite": "Argentit", "vein.indrev.chlorargyrite": "Klorargirit", "vein.indrev.cassiterite": "Kasiterit", "vein.indrev.stannite": "Stannit", "vein.indrev.scheelite": "Şelit", "vein.indrev.wolframite": "Wolframit", "vein.indrev.ferberite": "Ferberit", "vein.indrev.galena": "Galena", "vein.indrev.glowstonedeposit": "Işıktaşı Yatağı", "vein.indrev.soul_nether": "Nether ruhu Yatağı", "vein.indrev.sulfur_nether": "Sülfür", "vein.indrev.denseice": "Yoğun Buz Yatağı", "attribute.indrev.shield": "Kalkan", "subtitles.indrev.laser": "Lazer Işını" } ================================================ FILE: src/main/resources/assets/indrev/lang/zh_cn.json ================================================ { "item.patchouli.industrial_revolution_book.name": "工业革命手册", "item.indrev.guide_book": "工业革命手册", "item.patchouli.industrial_revolution_book.landing": "这本书将带你了解本模组的一些重要内容$(br2)若发现漏洞,有什么问题或建议,请在我们的$(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$()上提出问题或加入我们的$(l:https://discord.com/invite/G4PjhEf)Discord!$()$(br2)感谢游玩!", "itemGroup.indrev.indrev_group": "工业革命", "block.machines.tooltip.io": "I/O:%s", "block.indrev.lazuli_flux_container_1": "青金石通量", "block.indrev.lazuli_flux_container_2": "容器", "block.indrev.coal_generator": "煤炭发电机", "block.indrev.coal_generator_mk1": "煤炭发电机", "block.indrev.heat_generator": "热力发电机", "block.indrev.heat_generator_mk4": "热力发电机", "block.indrev.electric_furnace": "电炉", "block.indrev.electric_furnace_mk1": "电炉MK1", "block.indrev.electric_furnace_mk2": "电炉MK2", "block.indrev.electric_furnace_mk3": "电炉MK3", "block.indrev.electric_furnace_mk4": "电炉MK4", "block.indrev.electric_furnace_creative": "创造模式电炉", "block.indrev.electric_furnace_factory": "电炉工厂", "block.indrev.electric_furnace_factory_mk4": "电炉工厂MK4", "block.indrev.smelter": "工业熔炉", "block.indrev.smelter_mk4": "工业熔炉", "block.indrev.condenser": "聚合机", "block.indrev.condenser_mk4": "聚合机", "block.indrev.pulverizer": "粉碎机", "block.indrev.pulverizer_mk1": "粉碎机MK1", "block.indrev.pulverizer_mk2": "粉碎机MK2", "block.indrev.pulverizer_mk3": "粉碎机MK3", "block.indrev.pulverizer_mk4": "粉碎机MK4", "block.indrev.pulverizer_creative": "创造模式粉碎机", "block.indrev.pulverizer_factory": "粉碎工厂", "block.indrev.pulverizer_factory_mk4": "粉碎工厂MK4", "block.indrev.sawmill": "锯木机", "block.indrev.sawmill_mk1": "锯木机MK1", "block.indrev.sawmill_mk2": "锯木机MK2", "block.indrev.sawmill_mk3": "锯木机MK3", "block.indrev.sawmill_mk4": "锯木机MK4", "block.indrev.sawmill_creative": "创造模式锯木机", "block.indrev.compressor": "压缩机", "block.indrev.compressor_mk1": "压缩机MK1", "block.indrev.compressor_mk2": "压缩机MK2", "block.indrev.compressor_mk3": "压缩机MK3", "block.indrev.compressor_mk4": "压缩机MK4", "block.indrev.compressor_creative": "创造模式压缩机", "block.indrev.compressor_factory": "压缩工厂", "block.indrev.compressor_factory_mk4": "压缩工厂MK4", "block.indrev.solid_infuser_factory": "固体注入工厂", "block.indrev.solid_infuser": "固体注入机", "block.indrev.solid_infuser_mk1": "固体注入机MK1", "block.indrev.solid_infuser_mk2": "固体注入机MK2", "block.indrev.solid_infuser_mk3": "固体注入机MK3", "block.indrev.solid_infuser_mk4": "固体注入机MK4", "block.indrev.solid_infuser_creative": "创造模式固体注入机", "block.indrev.solid_infuser_factory_mk4": "固体注入工厂MK4", "block.indrev.lazuli_flux_container_mk1": "青金石通量容器MK1", "block.indrev.lazuli_flux_container_mk2": "青金石通量容器MK2", "block.indrev.lazuli_flux_container_mk3": "青金石通量容器MK3", "block.indrev.lazuli_flux_container_mk4": "青金石通量容器MK4", "block.indrev.lazuli_flux_container_creative": "创造模式青金石通量容器", "block.indrev.cable_mk1": "线缆MK1", "block.indrev.cable_mk2": "线缆MK2", "block.indrev.cable_mk3": "线缆MK3", "block.indrev.cable_mk4": "线缆MK4", "block.indrev.fluid_pipe_mk1": "流体管道MK1", "block.indrev.fluid_pipe_mk2": "流体管道MK2", "block.indrev.fluid_pipe_mk3": "流体管道MK3", "block.indrev.fluid_pipe_mk4": "流体管道MK4", "block.indrev.item_pipe_mk1": "物品管道MK1", "block.indrev.item_pipe_mk2": "物品管道MK2", "block.indrev.item_pipe_mk3": "物品管道MK3", "block.indrev.item_pipe_mk4": "物品管道MK4", "block.indrev.fisher": "钓鱼机", "block.indrev.fisher_mk2": "基础钓鱼机", "block.indrev.fisher_mk3": "改进钓鱼机", "block.indrev.fisher_mk4": "高级钓鱼机", "block.indrev.mining_rig": "钻机计算机", "block.indrev.mining_rig_mk4": "钻机计算机", "block.indrev.mining_rig.tooltip": "在当前区块执行开采工作前,须首先执行扫描程序", "block.indrev.mining_rig.mined": "已挖掘%s", "block.indrev.drill": "钻机", "block.indrev.drill_bottom": "钻机", "block.indrev.drill_middle": "钻机", "block.indrev.drill_top": "钻机", "block.indrev.drill.faster": "%s倍速", "block.indrev.drill.no_drills": "空", "block.indrev.drill.wrong_location": "扫描报告汇报的地点不正确!", "block.indrev.drill.active": "钻头激活", "block.indrev.drill.activating": "未就绪", "block.indrev.drill.not_enough_power": "能量不足", "block.indrev.drill.power_required": "正常运作至少需要%sLF", "block.indrev.recycler": "回收机", "block.indrev.recycler_mk2": "回收机", "block.indrev.cable": "线缆", "block.indrev.farmer_mk1": "耕作机MK1", "block.indrev.farmer_mk2": "耕作机MK2", "block.indrev.farmer_mk3": "耕作机MK3", "block.indrev.farmer_mk4": "耕作机MK4", "block.indrev.farmer_creative": "创造模式耕作机", "block.indrev.farmer": "耕作机", "block.indrev.slaughter_mk1": "屠宰机MK1", "block.indrev.slaughter_mk2": "屠宰机MK2", "block.indrev.slaughter_mk3": "屠宰机MK3", "block.indrev.slaughter_mk4": "屠宰机MK4", "block.indrev.slaughter_creative": "创造模式屠宰机", "block.indrev.slaughter": "屠宰机", "block.indrev.chopper": "伐木机", "block.indrev.chopper_mk1": "伐木机MK1", "block.indrev.chopper_mk2": "伐木机MK2", "block.indrev.chopper_mk3": "伐木机MK3", "block.indrev.chopper_mk4": "伐木机MK4", "block.indrev.chopper_creative": "创造模式伐木机", "block.indrev.rancher": "放牧机", "block.indrev.rancher_mk1": "放牧机MK1", "block.indrev.rancher_mk2": "放牧机MK2", "block.indrev.rancher_mk3": "放牧机MK3", "block.indrev.rancher_mk4": "放牧机MK4", "block.indrev.rancher_creative": "创造模式放牧机", "block.indrev.fluid_infuser": "液体注入机", "block.indrev.fluid_infuser_mk1": "液体注入机MK1", "block.indrev.fluid_infuser_mk2": "液体注入机MK2", "block.indrev.fluid_infuser_mk3": "液体注入机MK3", "block.indrev.fluid_infuser_mk4": "液体注入机MK4", "block.indrev.fluid_infuser_creative": "创造模式液体注入机", "block.indrev.electrolytic_separator": "电解分离机", "block.indrev.electrolytic_separator_mk1": "电解分离机MK1", "block.indrev.electrolytic_separator_mk2": "电解分离机MK2", "block.indrev.electrolytic_separator_mk3": "电解分离机MK3", "block.indrev.electrolytic_separator_mk4": "电解分离机MK4", "block.indrev.electrolytic_separator_creative": "创造模式电解分离机", "block.indrev.drain_mk1": "排液口", "block.indrev.tank": "储罐", "block.indrev.factory": "工厂", "block.indrev.biomass_generator": "生物质发电机", "block.indrev.biomass_generator_mk3": "生物质发电机", "block.indrev.solar_generator": "太阳能发电机", "block.indrev.solar_generator_mk1": "太阳能发电机", "block.indrev.solar_generator_mk3": "太阳能发电机", "block.indrev.charge_pad_mk4": "充电台", "block.indrev.charge_pad_mk4.tooltip": "为你放上去的东西充电!", "block.indrev.aoe.range": "作用范围:%s", "block.indrev.aoe.toggle.false": "显示作用范围", "block.indrev.aoe.toggle.true": "隐藏作用范围", "block.indrev.wither_proof_obsidian": "防凋灵黑曜石", "block.indrev.planks": "板材", "block.indrev.plank_block": "板材方块", "block.indrev.controller": "控制器", "block.indrev.frame": "机器框架", "block.indrev.duct": "通风管道", "block.indrev.warning_strobe": "警示灯", "block.indrev.silo": "筒仓", "block.indrev.intake": "进风口", "block.indrev.cabinet": "柜子", "block.indrev.coolant": "制冷液", "block.indrev.molten_iron": "熔融铁", "block.indrev.molten_gold": "熔融金", "block.indrev.molten_copper": "熔融铜", "block.indrev.molten_tin": "熔融锡", "block.indrev.molten_netherite": "熔融下界合金", "block.indrev.hydrogen": "液氢", "item.indrev.hydrogen_bucket": "液氢桶", "block.indrev.oxygen": "液氧", "item.indrev.oxygen_bucket": "液氧桶", "block.indrev.methane": "甲烷", "item.indrev.methane_bucket": "甲烷桶", "block.indrev.sulfuric_acid": "硫酸", "item.indrev.sulfuric_acid_bucket": "硫酸桶", "block.indrev.toxic_mud": "毒浆", "item.indrev.toxic_mud_bucket": "毒浆桶", "block.indrev.machine_block": "机器方块", "item.indrev.modular_core": "模块化核心(未充能)", "item.indrev.modular_core_activated": "模块化核心", "item.indrev.sawdust": "锯末", "block.indrev.laser_emitter_mk4": "激光发射器", "block.indrev.capsule": "微仓", "block.indrev.tin_ore": "锡矿石", "block.indrev.tin_block": "锡块", "item.indrev.tin_ingot": "锡锭", "item.indrev.tin_chunk": "锡碎块", "item.indrev.tin_dust": "锡粉", "item.indrev.tin_plate": "锡板", "item.indrev.tin_gear": "锡齿轮", "item.indrev.tin_nugget": "锡粒", "item.indrev.molten_tin_bucket": "熔融锡桶", "item.indrev.tin_purified_ore": "精炼锡矿石", "item.indrev.copper_chunk": "铜碎块", "item.indrev.copper_dust": "铜粉", "item.indrev.copper_plate": "铜板", "item.indrev.copper_gear": "铜齿轮", "item.indrev.copper_nugget": "铜粒", "item.indrev.copper_purified_ore": "精炼铜矿石", "item.indrev.molten_copper_bucket": "熔融铜桶", "block.indrev.molten_lead": "熔融铅", "item.indrev.molten_lead_bucket": "熔融铅桶", "block.indrev.lead_ore": "铅矿石", "block.indrev.lead_block": "铅块", "item.indrev.lead_ingot": "铅锭", "item.indrev.lead_chunk": "铅碎块", "item.indrev.lead_dust": "铅粉", "item.indrev.lead_plate": "铅板", "item.indrev.lead_gear": "铅齿轮", "item.indrev.lead_nugget": "铅粒", "item.indrev.lead_purified_ore": "精炼铅矿石", "item.indrev.raw_lead": "粗铅矿", "item.indrev.raw_silver": "粗银矿", "item.indrev.raw_tungsten": "粗钨矿", "item.indrev.raw_tin": "粗锡矿", "block.indrev.raw_lead_block": "粗铅块", "block.indrev.raw_silver_block": "粗银块", "block.indrev.raw_tungsten_block": "粗钨块", "block.indrev.raw_tin_block": "粗锡块", "block.indrev.molten_silver": "熔融银", "item.indrev.molten_silver_bucket": "熔融银桶", "block.indrev.silver_ore": "银矿石", "block.indrev.silver_block": "银块", "item.indrev.silver_ingot": "银锭", "item.indrev.silver_chunk": "银碎块", "item.indrev.silver_dust": "银粉", "item.indrev.silver_plate": "银板", "item.indrev.silver_gear": "银齿轮", "item.indrev.silver_nugget": "银粒", "item.indrev.silver_purified_ore": "精炼银矿石", "block.indrev.molten_tungsten": "熔融钨", "item.indrev.molten_tungsten_bucket": "熔融钨桶", "block.indrev.tungsten_ore": "钨矿石", "block.indrev.tungsten_block": "钨块", "item.indrev.tungsten_ingot": "钨锭", "item.indrev.tungsten_chunk": "钨碎块", "item.indrev.tungsten_dust": "钨粉", "item.indrev.tungsten_plate": "钨板", "item.indrev.tungsten_gear": "钨齿轮", "item.indrev.tungsten_nugget": "钨粒", "item.indrev.tungsten_purified_ore": "精炼钨矿石", "block.indrev.deepslate_tungsten_ore": "深层钨矿石", "block.indrev.deepslate_tin_ore": "深层锡矿石", "block.indrev.deepslate_silver_ore": "深层银矿石", "block.indrev.deepslate_nikolite_ore": "深层蓝石矿石", "block.indrev.deepslate_lead_ore": "深层铅矿石", "block.indrev.electrum_block": "琥珀金块", "item.indrev.electrum_ingot": "琥珀金锭", "item.indrev.electrum_dust": "琥珀金粉", "item.indrev.electrum_plate": "琥珀金板", "item.indrev.electrum_gear": "琥珀金齿轮", "item.indrev.electrum_nugget": "琥珀金粒", "block.indrev.bronze_block": "青铜块", "item.indrev.bronze_ingot": "青铜锭", "item.indrev.bronze_chunk": "青铜碎块", "item.indrev.bronze_dust": "青铜粉", "item.indrev.bronze_plate": "青铜板", "item.indrev.bronze_gear": "青铜齿轮", "item.indrev.bronze_nugget": "青铜粒", "block.indrev.sulfur_crystal": "硫晶体", "item.indrev.sulfur_crystal": "硫晶体", "item.indrev.sulfur_dust": "硫粉", "block.indrev.steel_block": "钢块", "item.indrev.steel_dust": "钢粉", "item.indrev.steel_ingot": "钢锭", "item.indrev.steel_plate": "钢板", "item.indrev.steel_gear": "钢齿轮", "item.indrev.steel_nugget": "钢粒", "item.indrev.gold_dust": "金粉", "item.indrev.gold_plate": "金板", "item.indrev.gold_chunk": "金碎块", "item.indrev.gold_purified_ore": "精炼金矿石", "item.indrev.molten_gold_bucket": "熔融金桶", "item.indrev.iron_dust": "铁粉", "item.indrev.iron_plate": "铁板", "item.indrev.iron_chunk": "铁碎块", "item.indrev.molten_iron_bucket": "熔融铁桶", "item.indrev.iron_purified_ore": "精炼铁矿石", "item.indrev.diamond_dust": "钻石粉", "item.indrev.coal_dust": "煤粉", "block.indrev.pump_mk1": "液泵", "item.indrev.empty_enhancer": "空白增益模块", "item.indrev.buffer_enhancer": "缓存增益", "item.indrev.buffer_enhancer.tooltip": "增加机器内置缓存", "item.indrev.energy_enhancer": "能效增益", "item.indrev.energy_enhancer.tooltip": "降低机器能耗", "item.indrev.damage_enhancer": "伤害增益", "item.indrev.damage_enhancer.tooltip": "增加屠宰机造成的伤害", "item.indrev.speed_enhancer": "速率增益", "item.indrev.speed_enhancer.tooltip": "使机器更高效运转", "item.indrev.blast_furnace_enhancer": "高炉增益", "item.indrev.blast_furnace_enhancer.tooltip": "允许电炉处理高炉配方", "item.indrev.smoker_enhancer": "烟熏增益", "item.indrev.smoker_enhancer.tooltip": "允许电炉处理烟熏炉配方", "item.indrev.enhancers.incompatible": "这台机器不支持这一增益", "item.indrev.enhancers.count": "机器设置上限为%s每槽位", "item.indrev.rechargeable.tooltip": "可充电", "item.indrev.chunk_scanner": "区块扫描仪", "item.indrev.chunk_scanner.scanning": "扫描中…", "item.indrev.chunk_scanner.scanned1": "扫描完成!", "item.indrev.chunk_scanner.scanned2": "矿脉类型:%s", "item.indrev.chunk_scanner.already_scanned": "该区块已有扫描记录,一共有%s个矿脉", "item.indrev.chunk_scanner.tooltip1": "按住右键开始扫描区块", "item.indrev.chunk_scanner.tooltip2": "矿脉类型:%s", "item.indrev.chunk_scanner.tooltip3": "从%s到%s", "item.indrev.chunk_scanner.tooltip4": "维度:%s", "item.indrev.chunk_scanner.tooltip5": "右击阅读更多", "item.indrev.fan": "风扇", "item.indrev.cooler_cell": "冷却单元", "item.indrev.heatsink": "散热片", "item.indrev.heat_coil": "加热线圈", "item.indrev.heat_coil.tooltip": "将青金石通量转化为热能", "item.indrev.stone_drill_head": "石头钻机钻头", "item.indrev.iron_drill_head": "铁钻机钻头", "item.indrev.diamond_drill_head": "钻石钻机钻头", "item.indrev.netherite_drill_head": "下界合金钻机钻头", "item.indrev.mining_drill_mk1": "采矿钻机MK1", "item.indrev.mining_drill_mk2": "采矿钻机MK2", "item.indrev.mining_drill_mk3": "采矿钻机MK3", "item.indrev.mining_drill_mk4": "采矿钻机MK4", "item.indrev.energy_reader": "能量示数器", "item.indrev.energy_reader.use": "已存储能量:", "item.indrev.energy_reader.use1": "%s LF/刻", "item.indrev.enriched_nikolite_dust": "富集蓝石粉", "item.indrev.enriched_nikolite_ingot": "富集蓝石锭", "item.indrev.battery": "电池", "item.indrev.circuit_mk1": "电路MK1", "item.indrev.circuit_mk2": "电路MK2", "item.indrev.circuit_mk3": "电路MK3", "item.indrev.circuit_mk4": "电路MK4", "item.indrev.tier_upgrade_mk2": "MK2机器升级", "item.indrev.tier_upgrade_mk2.tooltip": "升级MK1机器至MK2", "item.indrev.tier_upgrade_mk3": "MK3机器升级", "item.indrev.tier_upgrade_mk3.tooltip": "升级MK2机器至MK3", "item.indrev.tier_upgrade_mk4": "MK4机器升级", "item.indrev.tier_upgrade_mk4.tooltip": "升级MK3机器至MK4", "item.indrev.servo_output": "推送机构", "item.indrev.servo_output.tooltip": "检索容器后提取,并推送至管道", "item.indrev.servo_retriever": "收取机构", "item.indrev.servo_retriever.tooltip": "检索管道后收取,并向容器输送", "item.indrev.servo.mode": "检索优先级:", "item.indrev.servo.mode.round_robin": "短板优先(轮询)", "item.indrev.servo.mode.round_robin.tooltip": "优先考虑物品/液体存量最少的容器", "item.indrev.servo.mode.nearest_first": "近处优先", "item.indrev.servo.mode.nearest_first.tooltip": "优先考虑近端的容器", "item.indrev.servo.mode.furthest_first": "远处优先", "item.indrev.servo.mode.furthest_first.tooltip": "优先考虑远端的容器", "item.indrev.servo.mode.random": "随机", "item.indrev.servo.mode.random.tooltip": "无优先级", "item.indrev.biomass": "生物质", "item.indrev.untanned_leather": "生皮革", "item.indrev.coolant_bucket": "制冷液桶", "item.indrev.molten_netherite_bucket": "熔融下界合金桶", "item.indrev.netherite_scrap_purified_ore": "精炼远古残骸", "item.indrev.netherite_scrap_chunk": "远古残骸碎块", "item.indrev.netherite_scrap_dust": "下界合金碎片粉", "item.indrev.hammer": "锤子", "item.indrev.screwdriver": "螺丝刀", "item.indrev.wrench": "扳手", "item.indrev.wrench.switch_mode": "扳手模式:%s", "item.indrev.wrench.title": " 配置机器", "item.indrev.wrench.autopush": "自动推入", "item.indrev.wrench.autopull": "自动拉取", "item.indrev.wrench.item": "物品", "item.indrev.wrench.fluid": "液体", "item.indrev.wrench.energy": "能量", "item.indrev.wrench.output": "输出", "item.indrev.wrench.input": "输入", "item.indrev.wrench.input_first": "输入(一号槽位)", "item.indrev.wrench.input_second": "输入(二号槽位)", "item.indrev.wrench.input_output": "输入和输出", "item.indrev.wrench.none": "无", "item.indrev.wrench.tooltip": "Shift+右击以更改模式", "item.indrev.wrench.tooltip1": "模式:%s", "item.indrev.wrench.tooltip1.rotate": "旋转机器", "item.indrev.wrench.tooltip1.configure": "配置机器", "item.indrev.wrench.mode": "模式:%s", "item.indrev.wrench.side.north": "北", "item.indrev.wrench.side.south": "南", "item.indrev.wrench.side.west": "西", "item.indrev.wrench.side.east": "东", "item.indrev.wrench.side.up": "上", "item.indrev.wrench.side.down": "下", "item.indrev.wrench.side.top": "顶端", "item.indrev.wrench.side.bottom": "底端", "item.indrev.wrench.side.left": "左", "item.indrev.wrench.side.right": "右", "item.indrev.wrench.side.front": "前", "item.indrev.wrench.side.back": "后", "item.indrev.wrench.connected": "已连接至%s", "item.indrev.tech_soup": "高科技乱炖", "item.indrev.scan_output": "扫描报告", "item.indrev.portable_charger": "便携充电器", "item.indrev.gamer_axe": "头号玩家斧", "gui.indrev.button.auto_split": "自动分离成组的物品", "gui.indrev.resourcereport.size": "矿脉大小:%s", "gui.indrev.resourcereport.size.tiny": "些微", "gui.indrev.resourcereport.size.small": "小", "gui.indrev.resourcereport.size.average": "一般", "gui.indrev.resourcereport.size.big": "大", "gui.indrev.resourcereport.size.very_big": "很大", "gui.indrev.resourcereport.size.enormous": "庞大", "gui.indrev.resourcereport.size.gigantic": "超巨大", "gui.furnace.mode": "模式", "gui.furnace.mode.furnace": "熔炉", "gui.furnace.mode.blast_furnace": "高炉", "gui.furnace.mode.smoker": "烟熏炉", "gui.widget.process": "进度:%s", "gui.widget.energy": "能量", "gui.widget.temperature": "温度", "gui.widget.temperature_info.high": "过热", "gui.widget.temperature_info.medium": "最适", "gui.widget.temperature_info.low": "过冷", "gui.indrev.solar.on": "发电中", "gui.indrev.heatgen.idle": "闲置", "gui.indrev.heatgen.title": "消耗%s mB", "gui.indrev.heatgen.generating": "发电%s LF", "gui.indrev.heatgen.pertick": "每刻", "gui.indrev.heatgen.extra": "小心烫手!", "gui.indrev.guide_book_shortcut.contains": "打开工业革命手册", "gui.indrev.guide_book_shortcut.missing": "缺少工业革命手册!", "gui.indrev.tooltip.maxInput": "最大输入:", "gui.indrev.tooltip.maxOutput": "最大输出:", "gui.indrev.tooltip.maxEnergyStored": "最大储能:", "gui.indrev.tooltip.seconds": "%s秒", "gui.indrev.tooltip.energyCost": "能耗:", "gui.indrev.tooltip.processSpeed": "速率:", "gui.indrev.tooltip.ratio": "发电:", "gui.indrev.tooltip.press_shift": "按住 Shift 查看详细信息", "gui.indrev.tooltip.maxTransferRate": "传输速率:", "gui.indrev.tooltip.lftick": "%s LF/刻", "gui.indrev.tooltip.lf": "%s LF", "gui.indrev.tooltip.itemsec": "%s 物品/秒", "gui.indrev.tooltip.fluidsec": "%s 桶/秒", "gui.indrev.tooltip.temperatureBoost": "升温速率", "gui.indrev.locked": "已锁定", "gui.indrev.modular_armor_slot_type": "在这里放置模块盔甲", "gui.indrev.module_slot_type": "在这里放置模块", "gui.indrev.scan_output_slot_type": "在这里放置扫描结果", "gui.indrev.output_slot_type": "输出格", "gui.indrev.chopper_input_axe": "在这里放置斧头", "gui.indrev.chopper_input_bone_meal": "在这里放置骨粉", "gui.indrev.chopper_input_sapling": "在这里放置树苗", "gui.indrev.farmer_input_slot_type": "在这里放置种子或骨粉", "gui.indrev.slaughter_input_sword": "在这里放置剑", "gui.indrev.cooler_slot_type": "在这里放置冷却单元", "gui.indrev.battery_slot_type": "在这里放置电池", "gui.indrev.upgrade_slot_type": "在这里放置升级", "gui.indrev.fishingrod": "在这里放置钓竿", "indrev.category.rei.upgrading": "你必须对%2$s使用%1$s以升级至%3$s", "indrev.category.rei.pulverizing": "粉碎", "indrev.category.rei.infusing": "固体注入", "indrev.category.rei.compressing": "压缩", "indrev.category.rei.recycling": "回收", "indrev.category.rei.fluid_infusing": "液体注入", "indrev.category.rei.condensing": "聚合", "indrev.category.rei.smelting": "熔炼", "indrev.category.rei.sawmill": "锯木机", "indrev.category.rei.module": "模块合成", "item.indrev.nikolite_dust": "蓝石粉", "item.indrev.nikolite_ingot": "蓝石锭", "block.indrev.nikolite_ore": "蓝石矿石", "item.indrev.modular.upgrade": "已安装模块:", "item.indrev.modular.upgrade.night_vision": "夜视%s", "item.indrev.modular.upgrade.speed": "速度%s", "item.indrev.modular.upgrade.jump_boost": "跳跃提升%s", "item.indrev.modular.upgrade.breathing": "水下呼吸%s", "item.indrev.modular.upgrade.protection": "保护%s", "item.indrev.modular.upgrade.feather_falling": "摔落保护%s", "item.indrev.modular.upgrade.auto_feeder": "自动进食%s", "item.indrev.modular.upgrade.charger": "充电器%s", "item.indrev.modular.upgrade.solar_panel": "太阳能板%s", "item.indrev.modular.upgrade.piglin_tricker": "欺骗猪灵%s", "item.indrev.modular.upgrade.elytra": "内置鞘翅%s", "item.indrev.modular.upgrade.jetpack": "内置喷气背包%s", "item.indrev.modular.upgrade.magnet": "磁铁%s", "item.indrev.modular.upgrade.water_affinity": "亲水%s", "item.indrev.modular.upgrade.fire_resistance": "抗火%s", "item.indrev.modular.upgrade.efficiency": "效率%s", "item.indrev.modular.upgrade.range": "范围采掘%s", "item.indrev.modular.upgrade.fortune": "时运%s", "item.indrev.modular.upgrade.silk_touch": "精准采集%s", "item.indrev.modular.upgrade.sharpness": "锋利%s", "item.indrev.modular.upgrade.looting": "抢夺%s", "item.indrev.modular.upgrade.fire_aspect": "火焰附加%s", "item.indrev.modular.upgrade.reach": "延伸%s", "item.indrev.steel_helmet": "钢头盔", "item.indrev.steel_chestplate": "钢胸甲", "item.indrev.steel_leggings": "钢护腿", "item.indrev.steel_boots": "钢靴子", "item.indrev.steel_sword": "钢剑", "item.indrev.steel_pickaxe": "钢镐", "item.indrev.steel_axe": "钢斧", "item.indrev.steel_shovel": "钢锹", "item.indrev.steel_hoe": "钢锄", "item.indrev.bronze_helmet": "青铜头盔", "item.indrev.bronze_chestplate": "青铜胸甲", "item.indrev.bronze_leggings": "青铜护腿", "item.indrev.bronze_boots": "青铜靴子", "item.indrev.bronze_sword": "青铜剑", "item.indrev.bronze_pickaxe": "青铜镐", "item.indrev.bronze_axe": "青铜斧", "item.indrev.bronze_shovel": "青铜锹", "item.indrev.bronze_hoe": "青铜锄", "item.indrev.lead_helmet": "铅头盔", "item.indrev.lead_chestplate": "铅胸甲", "item.indrev.lead_leggings": "铅护腿", "item.indrev.lead_boots": "铅靴子", "item.indrev.lead_sword": "铅剑", "item.indrev.lead_pickaxe": "铅镐", "item.indrev.lead_axe": "铅斧", "item.indrev.lead_shovel": "铅锹", "item.indrev.lead_hoe": "铅锄", "item.indrev.silver_helmet": "银头盔", "item.indrev.silver_chestplate": "银胸甲", "item.indrev.silver_leggings": "银护腿", "item.indrev.silver_boots": "银靴子", "item.indrev.silver_sword": "银剑", "item.indrev.silver_pickaxe": "银镐", "item.indrev.silver_axe": "银斧", "item.indrev.silver_shovel": "银锹", "item.indrev.silver_hoe": "银锄", "block.indrev.modular_workbench": "模块工作台", "block.indrev.modular_workbench_mk4": "模块工作台", "item.indrev.modular_armor_helmet": "模块头盔", "item.indrev.modular_armor_chest": "模块胸甲", "item.indrev.modular_armor_legs": "模块护腿", "item.indrev.modular_armor_boots": "模块靴子", "item.indrev.module_parts": "可安装到", "item.indrev.module_parts_head": "头盔", "item.indrev.module_parts_chest": "胸甲", "item.indrev.module_parts_legs": "护腿", "item.indrev.module_parts_feet": "靴子", "item.indrev.module_parts_drill": "采矿钻机", "item.indrev.module_parts_gamer_axe": "头号玩家斧", "item.indrev.module_max_level": "等级上限:%s", "item.indrev.module_protection": "保护模块", "item.indrev.module_protection.tooltip": "增强护甲的保护功能。", "item.indrev.module_feather_falling": "羽落模块", "item.indrev.module_feather_falling.tooltip": "消耗能量以免疫摔落伤害。", "item.indrev.module_speed": "速度模块", "item.indrev.module_speed.tooltip": "消耗能量以增加行进速度。", "item.indrev.module_jump_boost": "跳跃提升模块", "item.indrev.module_jump_boost.tooltip": "消耗能量以增加跳跃高度。", "item.indrev.module_night_vision": "夜视模块", "item.indrev.module_night_vision.tooltip": "消耗能量以长时夜视。", "item.indrev.module_breathing": "呼吸模块", "item.indrev.module_breathing.tooltip": "消耗能量以在水下呼吸。", "item.indrev.module_auto_feeder": "自动进食模块", "item.indrev.module_auto_feeder.tooltip": "消耗能量以自动喂食背包里的食物。", "item.indrev.module_charger": "充电器模块", "item.indrev.module_charger.tooltip": "为你背包里的物品充电(不含盔甲)。", "item.indrev.module_solar_panel": "太阳能板模块", "item.indrev.module_solar_panel.tooltip": "在阳光下给你的盔甲和手持物品充电。", "item.indrev.module_piglin_tricker": "欺骗猪灵模块", "item.indrev.module_piglin_tricker.tooltip": "让猪灵以为你穿着金盔甲。", "item.indrev.module_elytra": "内置鞘翅模块", "item.indrev.module_elytra.tooltip": "为你的胸甲安装一个强化鞘翅。", "item.indrev.module_jetpack": "内置喷气背包模块", "item.indrev.module_jetpack.tooltip": "为你的胸甲安装一个喷气背包。", "item.indrev.module_magnet": "磁铁模块", "item.indrev.module_magnet.tooltip": "吸附你身边的物品和经验值球。", "item.indrev.module_water_affinity": "亲水模块", "item.indrev.module_water_affinity.on": "装备在%s上时,", "item.indrev.module_water_affinity.chestplate": "胸甲", "item.indrev.module_water_affinity.leggings": "护腿", "item.indrev.module_water_affinity.tooltip": "获得水下速掘。", "item.indrev.module_water_affinity.tooltip1": "增加水下机动性。", "item.indrev.module_fire_resistance": "抗火模块", "item.indrev.module_fire_resistance.tooltip": "消耗能量以获得抗火效果。", "item.indrev.module_efficiency": "效率模块", "item.indrev.module_efficiency.tooltip": "增加工具的挖掘速度。", "item.indrev.module_range": "范围采掘模块", "item.indrev.module_range.tooltip": "增加工具的采掘范围。", "item.indrev.module_fortune": "时运模块", "item.indrev.module_fortune.tooltip": "同时运附魔。", "item.indrev.module_silk_touch": "精准采集模块", "item.indrev.module_silk_touch.tooltip": "同精准采集附魔。", "item.indrev.module_reach": "延伸模块", "item.indrev.module_reach.tooltip": "可以一次性伤害多个生物。", "item.indrev.module_looting": "抢夺模块", "item.indrev.module_looting.tooltip": "同抢夺附魔。", "item.indrev.module_fire_aspect": "火焰附加模块", "item.indrev.module_fire_aspect.tooltip": "同火焰附加附魔。", "item.indrev.module_sharpness": "锋利模块", "item.indrev.module_sharpness.tooltip": "增加武器伤害。", "item.indrev.module_color.tooltip": "§c颜§e色§4模§b块", "item.indrev.module_color_pink": "粉色模块", "item.indrev.module_color_red": "红色模块", "item.indrev.module_color_purple": "紫色模块", "item.indrev.module_color_blue": "蓝色模块", "item.indrev.module_color_cyan": "青色模块", "item.indrev.module_color_green": "绿色模块", "item.indrev.module_color_yellow": "黄色模块", "item.indrev.module_color_orange": "橙色模块", "item.indrev.module_color_black": "黑色模块", "item.indrev.module_color_brown": "棕色模块", "item.indrev.copper_sword": "铜剑", "item.indrev.copper_pickaxe": "铜镐", "item.indrev.copper_axe": "铜斧", "item.indrev.copper_shovel": "铜锹", "item.indrev.copper_hoe": "铜锄", "item.indrev.copper_helmet": "铜头盔", "item.indrev.copper_chestplate": "铜胸甲", "item.indrev.copper_leggings": "铜护腿", "item.indrev.copper_boots": "铜靴子", "item.indrev.tin_sword": "锡剑", "item.indrev.tin_pickaxe": "锡镐", "item.indrev.tin_axe": "锡斧", "item.indrev.tin_shovel": "锡锹", "item.indrev.tin_hoe": "锡锄", "item.indrev.tin_helmet": "锡头盔", "item.indrev.tin_chestplate": "锡胸甲", "item.indrev.tin_leggings": "锡护腿", "item.indrev.tin_boots": "锡靴子", "category.indrev": "工业革命", "key.indrev.modular": "模块化物品设置", "text.multiblock.not_built": "多方块结构不正确!", "advancements.indrev.nikolite": "工业革命的开端", "advancements.indrev.nikolite.description": "找到蓝石", "advancements.indrev.machine_block": "夯实基础", "advancements.indrev.machine_block.description": "获得一台机器", "advancements.indrev.coal_generator": "革命第一步", "advancements.indrev.coal_generator.description": "获得一台煤炭发电机", "advancements.indrev.basic_solar_generator": "被动能源", "advancements.indrev.basic_solar_generator.description": "获得一台太阳能发动机", "advancements.indrev.advanced_solar_generator": "更多被动能源", "advancements.indrev.advanced_solar_generator.description": "获得以太高级太阳能发动机", "advancements.indrev.heat_generator": "燃起来了!", "advancements.indrev.heat_generator.description": "获得一台热力发动机", "advancements.indrev.biomass_generator": "也没那么环保嘛……", "advancements.indrev.biomass_generator.description": "获得一台生物质发动机", "advancements.indrev.pulverizer": "粉尘弥漫", "advancements.indrev.pulverizer.description": "获得一台粉碎机", "advancements.indrev.electric_furnace": "更好的熔炉", "advancements.indrev.electric_furnace.description": "获得一台电炉", "advancements.indrev.compressor": "小心夹手", "advancements.indrev.compressor.description": "获得一台压缩机", "advancements.indrev.recycler": "保护大自然", "advancements.indrev.recycler.description": "获得一台回收机", "advancements.indrev.solid_infuser": "注入灵魂", "advancements.indrev.solid_infuser.description": "获得一台固体注入机", "advancements.indrev.mk2_upgrade": "效率加倍!!", "advancements.indrev.mk2_upgrade.description": "升级机器至MK2", "advancements.indrev.mk3_upgrade": "效率再加倍!!!", "advancements.indrev.mk3_upgrade.description": "升级机器至MK3", "advancements.indrev.mk4_upgrade": "工厂!给我!办起来!", "advancements.indrev.mk4_upgrade.description": "升级机器至MK4", "advancements.indrev.biomass": "噫呃……", "advancements.indrev.biomass.description": "获得生物质", "advancements.indrev.nikolite_ingot": "崭新黎明", "advancements.indrev.nikolite_ingot.description": "获得蓝石锭", "advancements.indrev.enriched_nikolite_dust": "强力电子元件", "advancements.indrev.enriched_nikolite_dust.description": "获得富集蓝石粉", "advancements.indrev.enriched_nikolite_ingot": "搞快点!", "advancements.indrev.enriched_nikolite_ingot.description": "获得富集蓝石锭", "advancements.indrev.lazuli_flux_container_mk1": "基础的能源存储", "advancements.indrev.lazuli_flux_container_mk1.description": "获得一台青金石通量容器MK1", "advancements.indrev.lazuli_flux_container_mk2": "一般的能源存储", "advancements.indrev.lazuli_flux_container_mk2.description": "获得一台青金石通量容器MK2", "advancements.indrev.lazuli_flux_container_mk3": "不那么一般的能源存储", "advancements.indrev.lazuli_flux_container_mk3.description": "获得一台青金石通量容器MK3", "advancements.indrev.lazuli_flux_container_mk4": "究极能源存储", "advancements.indrev.lazuli_flux_container_mk4.description": "获得一台青金石通量容器MK4", "advancements.indrev.empty_enhancer": "“平平”无奇", "advancements.indrev.empty_enhancer.description": "获得一个空白升级模块", "advancements.indrev.speed_enhancer": "哈哈,机器轰隆隆隆隆", "advancements.indrev.speed_enhancer.description": "获得速率升级", "advancements.indrev.buffer_enhancer": "超越极限", "advancements.indrev.buffer_enhancer.description": "获得缓冲升级", "advancements.indrev.energy_enhancer": "卓有成效", "advancements.indrev.energy_enhancer.description": "获得能效升级", "advancements.indrev.cable_mk1": "橙线缆!", "advancements.indrev.cable_mk1.description": "获得一根MK1线缆", "advancements.indrev.cable_mk2": "蓝线缆!", "advancements.indrev.cable_mk2.description": "获得一根MK2线缆", "advancements.indrev.cable_mk3": "紫线缆!", "advancements.indrev.cable_mk3.description": "获得一根MK3线缆", "advancements.indrev.cable_mk4": "红线缆!", "advancements.indrev.cable_mk4.description": "获得一根MK4线缆", "advancements.indrev.modular_workbench": "这看着可不像工作台", "advancements.indrev.modular_workbench.description": "获得模块工作台", "advancements.indrev.modular_armor": "模块主义", "advancements.indrev.modular_armor.description": "获得模块套装", "advancements.indrev.gamer_axe": "灯大灯亮灯能闪!", "advancements.indrev.gamer_axe.description": "获得游戏爱好者之斧", "advancements.indrev.chopper_mk4": "自动树场", "advancements.indrev.chopper_mk4.description": "获得一台伐木机", "advancements.indrev.rancher_mk1": "我,机器农", "advancements.indrev.rancher_mk1.description": "获得一台耕作机", "advancements.indrev.rancher_mk4": "机械牧民", "advancements.indrev.rancher_mk4.description": "获得一台放牧机", "advancements.indrev.industrial_smelter": "高级冶金工业", "advancements.indrev.industrial_smelter.description": "获得一台工业熔炉", "advancements.indrev.condenser": "救救孩子,不会起成就名了", "advancements.indrev.condenser.description": "获得一台聚合器", "advancements.indrev.blast_furnace_enhancer": "电动高炉", "advancements.indrev.blast_furnace_enhancer.description": "获得高炉升级", "advancements.indrev.smoker_enhancer": "新东方厨师梦", "advancements.indrev.smoker_enhancer.description": "获得烟熏炉升级", "advancements.indrev.circuit_mk1": "机器第一步", "advancements.indrev.circuit_mk1.description": "获得一个MK1电路", "advancements.indrev.circuit_mk2": "新新科技", "advancements.indrev.circuit_mk2.description": "获得一个MK2电路", "advancements.indrev.circuit_mk3": "高级科技", "advancements.indrev.circuit_mk3.description": "获得一个MK3电路", "advancements.indrev.circuit_mk4": "究极科技", "advancements.indrev.circuit_mk4.description": "获得一个MK4电路", "advancements.indrev.farmer_mk1": "农家乐", "advancements.indrev.farmer_mk1.description": "获得一台MK1耕作机", "advancements.indrev.slaughter_mk1": "究极杀手", "advancements.indrev.slaughter_mk1.description": "获得一台MK1屠宰机", "gui.indrev.tip": "小贴士:", "gui.indrev.tip_0": "请认真配置物品的输入和输出接口哦", "gui.indrev.tip_1": "请勿使用超过输入上限的线缆", "gui.indrev.tip_2": "酷酷地使用冷却单元才好呢", "gui.indrev.tip_3": "用冷却单元来维持机器高效运行吧", "gui.indrev.tip_4": "工厂!给我!办起来!!!", "gui.indrev.tip_5": "来试试模块盔甲吧", "gui.indrev.tip_6": "头号玩家就用头号玩家斧", "gui.indrev.tip_7": "如果你遇到问题或有建议,请加入我们的Discord!", "gui.indrev.tip_8": "在更新之前记得要好好备份你的世界", "gui.indrev.tip_9": "睡觉前记得盖好被子", "gui.indrev.modules_installed": "已安装模块:", "gui.indrev.shield": "护盾:", "gui.indrev.installing": "安装中", "gui.indrev.incompatible": "不兼容的模块", "gui.indrev.max_level": "已达最大升级", "gui.indrev.progress": "进度:", "gui.indrev.whitelist.true": "白名单", "gui.indrev.whitelist.false": "黑名单", "gui.indrev.matchDurability.true": "匹配物品耐久", "gui.indrev.matchDurability.false": "忽略物品耐久", "gui.indrev.matchTag.true": "匹配物品NBT", "gui.indrev.matchTag.false": "忽略物品NBT", "death.attack.acid": "%s掉进硫酸中融化了", "death.attack.acid.player": "%s在尝试逃脱%s时掉进硫酸中融化了", "death.attack.laser": "%s被激光烤焦了", "death.attack.laser.player": "%s在尝试逃脱%s时被激光烧焦了", "vein.indrev.bauxite": "铝土矿脉", "vein.indrev.certus_quartz": "赛特斯石英矿脉", "vein.indrev.peat": "泥煤矿脉", "vein.indrev.lignite": "褐煤矿脉", "vein.indrev.bituminous": "沥青矿脉", "vein.indrev.anthracite": "白煤矿脉", "vein.indrev.cuprite": "赤铜矿脉", "vein.indrev.calaverite": "碲金矿脉", "vein.indrev.calaverite_nether": "下界碲金矿脉", "vein.indrev.iridium": "铱矿脉", "vein.indrev.siderite": "菱铁矿脉", "vein.indrev.limonite": "褐铁矿脉", "vein.indrev.hematite": "赤铁矿脉", "vein.indrev.magnetite": "磁铁矿脉", "vein.indrev.chalcopryte": "黄铜矿脉", "vein.indrev.nikolite": "蓝石矿脉", "vein.indrev.quartz": "石英矿脉", "vein.indrev.argentite": "辉银矿脉", "vein.indrev.chlorargyrite": "角银矿脉", "vein.indrev.cassiterite": "锡石矿脉", "vein.indrev.stannite": "黄锡矿脉", "vein.indrev.scheelite": "白钨矿脉", "vein.indrev.wolframite": "黑钨矿脉", "vein.indrev.ferberite": "钨铁矿脉", "vein.indrev.galena": "方铅矿脉", "vein.indrev.glowstonedeposit": "荧石矿床", "vein.indrev.soul_nether": "下界灵魂矿床", "vein.indrev.sulfur_nether": "硫矿", "vein.indrev.denseice": "坚冰矿床", "attribute.indrev.shield": "护盾", "subtitles.indrev.laser": "激光:激发", "item.indrev.reinforced_elytra": "强化鞘翅", "item.indrev.jetpack_mk1": "喷气背包MK1", "item.indrev.jetpack_mk2": "喷气背包MK2", "item.indrev.jetpack_mk3": "喷气背包MK3", "item.indrev.jetpack_mk4": "喷气背包MK4", "block.indrev.gas_generator": "燃气发电机", "block.indrev.gas_generator_mk4": "燃气发电机", "item.indrev.soot": "烟灰", "item.indrev.carbon_fiber_plate": "碳纤维板", "item.indrev.carbon_fiber_rod": "碳纤维棒", "item.indrev.carbon_fiber_helmet_frame": "碳纤维胸甲框架", "item.indrev.carbon_fiber_chest_frame": "碳纤维头盔框架", "item.indrev.carbon_fiber_legs_frame": "碳纤维护腿框架", "item.indrev.carbon_fiber_boots_frame": "碳纤维靴子框架", "block.indrev.boiler": "锅炉", "block.indrev.steam_turbine_mk4": "蒸汽轮机" } ================================================ FILE: src/main/resources/assets/indrev/models/block/block_base.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "indrev:block/block_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/block_highlight.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "indrev:block/block_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/block_shadow.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "indrev:block/block_shadow" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/bronze_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/bronze_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/cabinet.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "texture_size": [64, 64], "textures": { "0": "indrev:block/cabinet", "particle": "indrev:block/cabinet" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 4, 4], "texture": "#0"}, "east": {"uv": [0, 4, 4, 8], "texture": "#0"}, "south": {"uv": [4, 4, 8, 8], "texture": "#0"}, "west": {"uv": [4, 0, 8, 4], "texture": "#0"}, "up": {"uv": [4, 12, 0, 8], "texture": "#0"}, "down": {"uv": [8, 8, 4, 12], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/cable_center_mk1.json ================================================ { "textures": { "texture": "indrev:block/cable_center_emissive_mk1", "1": "indrev:block/cable_center" }, "elements": [ { "from": [5.5, 5.5, 5.5], "to": [10.5, 10.5, 10.5], "faces": { "north": {"uv": [3, 3, 13, 13], "texture": "#1"}, "east": {"uv": [3, 3, 13, 13], "texture": "#1"}, "south": {"uv": [3, 3, 13, 13], "texture": "#1"}, "west": {"uv": [3, 3, 13, 13], "texture": "#1"}, "up": {"uv": [3, 3, 13, 13], "texture": "#1"}, "down": {"uv": [3, 3, 13, 13], "texture": "#1"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [1.25, 1.25, 1.25] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/cable_center_mk2.json ================================================ { "textures": { "texture": "indrev:block/cable_center_emissive_mk2", "1": "indrev:block/cable_center" }, "elements": [ { "from": [5.5, 5.5, 5.5], "to": [10.5, 10.5, 10.5], "faces": { "north": {"uv": [3, 3, 13, 13], "texture": "#1"}, "east": {"uv": [3, 3, 13, 13], "texture": "#1"}, "south": {"uv": [3, 3, 13, 13], "texture": "#1"}, "west": {"uv": [3, 3, 13, 13], "texture": "#1"}, "up": {"uv": [3, 3, 13, 13], "texture": "#1"}, "down": {"uv": [3, 3, 13, 13], "texture": "#1"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [1.25, 1.25, 1.25] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/cable_center_mk3.json ================================================ { "textures": { "texture": "indrev:block/cable_center_emissive_mk3", "1": "indrev:block/cable_center" }, "elements": [ { "from": [5.5, 5.5, 5.5], "to": [10.5, 10.5, 10.5], "faces": { "north": {"uv": [3, 3, 13, 13], "texture": "#1"}, "east": {"uv": [3, 3, 13, 13], "texture": "#1"}, "south": {"uv": [3, 3, 13, 13], "texture": "#1"}, "west": {"uv": [3, 3, 13, 13], "texture": "#1"}, "up": {"uv": [3, 3, 13, 13], "texture": "#1"}, "down": {"uv": [3, 3, 13, 13], "texture": "#1"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [1.25, 1.25, 1.25] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/cable_center_mk4.json ================================================ { "textures": { "texture": "indrev:block/cable_center_emissive_mk4", "1": "indrev:block/cable_center" }, "elements": [ { "from": [5.5, 5.5, 5.5], "to": [10.5, 10.5, 10.5], "faces": { "north": {"uv": [3, 3, 13, 13], "texture": "#1"}, "east": {"uv": [3, 3, 13, 13], "texture": "#1"}, "south": {"uv": [3, 3, 13, 13], "texture": "#1"}, "west": {"uv": [3, 3, 13, 13], "texture": "#1"}, "up": {"uv": [3, 3, 13, 13], "texture": "#1"}, "down": {"uv": [3, 3, 13, 13], "texture": "#1"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [1.25, 1.25, 1.25] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/cable_side_mk1.json ================================================ { "textures": { "texture": "indrev:block/cable_wire_emissive_mk1", "1": "indrev:block/cable_wrap" }, "elements": [ { "from": [5.99, 0, 5.99], "to": [10.01, 6.01, 10.01], "rotation": {"angle": 0, "axis": "y", "origin": [10, 4, 13]}, "faces": { "north": {"uv": [0, 0, 6, 6], "texture": "#1"}, "east": {"uv": [0, 0, 6, 6], "texture": "#1"}, "south": {"uv": [0, 0, 6, 6], "texture": "#1"}, "west": {"uv": [0, 0, 6, 6], "texture": "#1"}, "up": {"uv": [0, 0, 6, 6], "texture": "#1"} } }, { "from": [6.5, 0, 6.5], "to": [9.5, 6, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [17.3, 8, 16.2]}, "faces": { "north": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "east": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "south": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "west": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "up": {"uv": [3, 3, 0, 0], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "down": {"uv": [3, 6, 0, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"} } } ], "jmx": { "textures": { "1": "indrev:block/cable_wrap", "texture": "indrev:block/cable_wire_emissive_mk1", "particle": "indrev:block/cable_wire_emissive_mk1" }, "materials": { "jmx_0": { "layer0": "cutout", "emissive0": true, "ambient_occlusion0": false, "diffuse0": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/cable_side_mk2.json ================================================ { "textures": { "texture": "indrev:block/cable_wire_emissive_mk2", "1": "indrev:block/cable_wrap" }, "elements": [ { "from": [5.99, 0, 5.99], "to": [10.01, 6.01, 10.01], "rotation": {"angle": 0, "axis": "y", "origin": [10, 4, 13]}, "faces": { "north": {"uv": [0, 0, 6, 6], "texture": "#1"}, "east": {"uv": [0, 0, 6, 6], "texture": "#1"}, "south": {"uv": [0, 0, 6, 6], "texture": "#1"}, "west": {"uv": [0, 0, 6, 6], "texture": "#1"}, "up": {"uv": [0, 0, 6, 6], "texture": "#1"} } }, { "from": [6.5, 0, 6.5], "to": [9.5, 6, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [17.3, 8, 16.2]}, "faces": { "north": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "east": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "south": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "west": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "up": {"uv": [3, 3, 0, 0], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "down": {"uv": [3, 6, 0, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"} } } ], "jmx": { "textures": { "1": "indrev:block/cable_wrap", "texture": "indrev:block/cable_wire_emissive_mk2", "particle": "indrev:block/cable_wire_emissive_mk2" }, "materials": { "jmx_0": { "layer0": "cutout", "emissive0": true, "ambient_occlusion0": false, "diffuse0": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/cable_side_mk3.json ================================================ { "textures": { "texture": "indrev:block/cable_wire_emissive_mk3", "1": "indrev:block/cable_wrap" }, "elements": [ { "from": [5.99, 0, 5.99], "to": [10.01, 6.01, 10.01], "rotation": {"angle": 0, "axis": "y", "origin": [10, 4, 13]}, "faces": { "north": {"uv": [0, 0, 6, 6], "texture": "#1"}, "east": {"uv": [0, 0, 6, 6], "texture": "#1"}, "south": {"uv": [0, 0, 6, 6], "texture": "#1"}, "west": {"uv": [0, 0, 6, 6], "texture": "#1"}, "up": {"uv": [0, 0, 6, 6], "texture": "#1"} } }, { "from": [6.5, 0, 6.5], "to": [9.5, 6, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [17.3, 8, 16.2]}, "faces": { "north": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "east": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "south": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "west": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "up": {"uv": [3, 3, 0, 0], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "down": {"uv": [3, 6, 0, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"} } } ], "jmx": { "textures": { "1": "indrev:block/cable_wrap", "texture": "indrev:block/cable_wire_emissive_mk3", "particle": "indrev:block/cable_wire_emissive_mk3" }, "materials": { "jmx_0": { "layer0": "cutout", "emissive0": true, "ambient_occlusion0": false, "diffuse0": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/cable_side_mk4.json ================================================ { "textures": { "texture": "indrev:block/cable_wire_emissive_mk4", "1": "indrev:block/cable_wrap" }, "elements": [ { "from": [5.99, 0, 5.99], "to": [10.01, 6.01, 10.01], "rotation": {"angle": 0, "axis": "y", "origin": [10, 4, 13]}, "faces": { "north": {"uv": [0, 0, 6, 6], "texture": "#1"}, "east": {"uv": [0, 0, 6, 6], "texture": "#1"}, "south": {"uv": [0, 0, 6, 6], "texture": "#1"}, "west": {"uv": [0, 0, 6, 6], "texture": "#1"}, "up": {"uv": [0, 0, 6, 6], "texture": "#1"} } }, { "from": [6.5, 0, 6.5], "to": [9.5, 6, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [17.3, 8, 16.2]}, "faces": { "north": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "east": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "south": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "west": {"uv": [0, 3, 3, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "up": {"uv": [3, 3, 0, 0], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"}, "down": {"uv": [3, 6, 0, 9], "texture": "#texture", "jmx_tex0": "#texture", "jmx_material": "#jmx_0"} } } ], "jmx": { "textures": { "1": "indrev:block/cable_wrap", "texture": "indrev:block/cable_wire_emissive_mk4", "particle": "indrev:block/cable_wire_emissive_mk4" }, "materials": { "jmx_0": { "layer0": "cutout", "emissive0": true, "ambient_occlusion0": false, "diffuse0": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/capsule.json ================================================ { "parent": "block/block", "credit": "Made with Blockbench", "texture_size": [4, 4], "textures": { "0": "indrev:block/capsule", "particle": "indrev:block/capsule" }, "elements": [ { "from": [0, 16, 0], "to": [16, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 22, 8]}, "faces": { "north": {"uv": [48, 2, 64, 4], "texture": "#0"}, "east": {"uv": [48, 4, 64, 6], "texture": "#0"}, "south": {"uv": [48, 6, 64, 8], "texture": "#0"}, "west": {"uv": [48, 8, 64, 10], "texture": "#0"}, "up": {"uv": [16, 16, 0, 0], "texture": "#0"}, "down": {"uv": [9, 9, 7, 7], "rotation": 270, "texture": "#0"} } }, { "from": [16, 0, 0], "to": [16, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [22, 8, 8]}, "faces": { "north": {"uv": [64, 10, 48, 8], "rotation": 90, "texture": "#0"}, "east": {"uv": [16, 16, 0, 0], "rotation": 180, "texture": "#0"}, "south": {"uv": [64, 6, 48, 4], "rotation": 270, "texture": "#0"}, "west": {"uv": [9, 7, 7, 9], "texture": "#0"}, "up": {"uv": [48, 6, 64, 8], "rotation": 90, "texture": "#0"}, "down": {"uv": [48, 2, 64, 4], "rotation": 90, "texture": "#0"} } }, { "from": [0, 0, 0], "to": [16, 16, 0], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 0]}, "faces": { "north": {"uv": [16, 16, 0, 0], "rotation": 180, "texture": "#0"}, "east": {"uv": [48, 6, 64, 4], "rotation": 270, "texture": "#0"}, "south": {"uv": [9, 9, 7, 7], "texture": "#0"}, "west": {"uv": [48, 10, 64, 8], "rotation": 90, "texture": "#0"}, "up": {"uv": [48, 4, 64, 2], "rotation": 180, "texture": "#0"}, "down": {"uv": [48, 8, 64, 6], "texture": "#0"} } }, { "from": [0, 0, 0], "to": [0, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [0, 8, 8]}, "faces": { "north": {"uv": [64, 6, 48, 4], "rotation": 270, "texture": "#0"}, "east": {"uv": [9, 7, 7, 9], "texture": "#0"}, "south": {"uv": [64, 10, 48, 8], "rotation": 90, "texture": "#0"}, "west": {"uv": [16, 16, 0, 0], "rotation": 180, "texture": "#0"}, "up": {"uv": [48, 6, 64, 8], "rotation": 270, "texture": "#0"}, "down": {"uv": [48, 2, 64, 4], "rotation": 270, "texture": "#0"} } }, { "from": [0, 0, 16], "to": [16, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 16]}, "faces": { "north": {"uv": [9, 9, 7, 7], "texture": "#0"}, "east": {"uv": [48, 10, 64, 8], "rotation": 90, "texture": "#0"}, "south": {"uv": [16, 16, 0, 0], "rotation": 180, "texture": "#0"}, "west": {"uv": [48, 6, 64, 4], "rotation": 270, "texture": "#0"}, "up": {"uv": [48, 4, 64, 2], "texture": "#0"}, "down": {"uv": [48, 8, 64, 6], "rotation": 180, "texture": "#0"} } }, { "from": [0, 0, 0], "to": [16, 0, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, -6, 8]}, "faces": { "north": {"uv": [48, 4, 64, 2], "texture": "#0"}, "east": {"uv": [48, 6, 64, 4], "texture": "#0"}, "south": {"uv": [48, 8, 64, 6], "texture": "#0"}, "west": {"uv": [48, 10, 64, 8], "texture": "#0"}, "up": {"uv": [9, 9, 7, 7], "rotation": 270, "texture": "#0"}, "down": {"uv": [16, 16, 0, 0], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/charge_pad_mk4.json ================================================ { "parent": "minecraft:block/block", "credit": "Made with Blockbench", "textures": { "0": "indrev:block/machine_block", "1": "indrev:block/charger_top", "2": "indrev:block/charger_side", "3": "indrev:block/charge_base", "particle": "indrev:block/machine_block" }, "elements": [ { "name": "base", "from": [ 1, 0, 1 ], "to": [ 15, 0.1, 15 ], "rotation": { "angle": 0, "axis": "y", "origin": [ 9, 8, 9 ] }, "faces": { "north": { "uv": [ 0, 10.333, 13.5, 10.633 ], "texture": "#0" }, "east": { "uv": [ 0, 10.333, 13.5, 10.633 ], "texture": "#0" }, "south": { "uv": [ 0, 10.333, 13.5, 10.633 ], "texture": "#0" }, "west": { "uv": [ 0, 10.333, 13.5, 10.633 ], "texture": "#0" }, "up": { "uv": [ 5.333, 10.667, 10.666, 16 ], "texture": "#0" }, "down": { "uv": [ 5.333, 10.667, 10.666, 16 ], "texture": "#0" } } }, { "name": "base2", "from": [ 1.25, 0, 1.25 ], "to": [ 14.75, 0.3, 14.75 ], "rotation": { "angle": 0, "axis": "y", "origin": [ 9, 8, 9 ] }, "faces": { "north": { "uv": [ 0, 10.333, 13.5, 10.633 ], "texture": "#0" }, "east": { "uv": [ 0, 10.333, 13.5, 10.633 ], "texture": "#0" }, "south": { "uv": [ 0, 10.333, 13.5, 10.633 ], "texture": "#0" }, "west": { "uv": [ 0, 10.333, 13.5, 10.633 ], "texture": "#0" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#3" } } }, { "name": "arm", "from": [ 7, 0, 2 ], "to": [ 9, 15, 4 ], "rotation": { "angle": 0, "axis": "y", "origin": [ 15, 8, 10 ] }, "faces": { "north": { "uv": [ 0, 0, 4, 15 ], "texture": "#2" }, "east": { "uv": [ 0, 0, 4, 15 ], "texture": "#2" }, "south": { "uv": [ 0, 0, 4, 15 ], "texture": "#2" }, "west": { "uv": [ 0, 0, 4, 15 ], "texture": "#2" } } }, { "name": "arm2", "from": [ 8, 15, 2 ], "to": [ 9, 15.5, 3 ], "rotation": { "angle": 0, "axis": "y", "origin": [ 16, 23, 10 ] }, "faces": { "north": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "east": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "south": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "west": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "up": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "down": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" } } }, { "name": "arm2", "from": [ 7, 15, 2 ], "to": [ 8, 15.5, 3 ], "rotation": { "angle": 0, "axis": "y", "origin": [ 15, 23, 10 ] }, "faces": { "north": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "east": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "south": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "west": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "up": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "down": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" } } }, { "name": "arm2", "from": [ 7, 15, 3 ], "to": [ 8, 15.2, 4 ], "rotation": { "angle": 0, "axis": "y", "origin": [ 15, 23, 11 ] }, "faces": { "north": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "east": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "south": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "west": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "up": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" }, "down": { "uv": [ 0, 0, 1, 1 ], "texture": "#2" } } }, { "name": "arm2", "from": [ 8, 15, 3 ], "to": [ 9, 15.2, 4 ], "rotation": { "angle": 0, "axis": "y", "origin": [ 16, 23, 11 ] }, "faces": { "east": { "uv": [ 0, 0, 1, 0.2 ], "texture": "#2" } } }, { "name": "top", "from": [ 4.75, 13.1, 1.75 ], "to": [ 11.25, 13.4, 8.25 ], "rotation": { "angle": 22.5, "axis": "x", "origin": [ 13, 21, 10 ] }, "faces": { "north": { "uv": [ 0, 0, 6.5, 0.3 ], "texture": "#1" }, "east": { "uv": [ 0, 0, 6.5, 0.3 ], "texture": "#1" }, "south": { "uv": [ 0, 0, 6.5, 0.3 ], "texture": "#1" }, "west": { "uv": [ 0, 0, 6.5, 0.3 ], "texture": "#1" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#1" }, "down": { "uv": [ 0, 0, 6.5, 0.3 ], "texture": "#1" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/controller.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "texture_size": [64, 64], "textures": { "0": "indrev:block/controller", "particle": "indrev:block/controller" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 4, 4], "texture": "#0"}, "east": {"uv": [0, 4, 4, 8], "texture": "#0"}, "south": {"uv": [4, 0, 8, 4], "texture": "#0"}, "west": {"uv": [4, 4, 8, 8], "texture": "#0"}, "up": {"uv": [4, 12, 0, 8], "texture": "#0"}, "down": {"uv": [8, 8, 4, 12], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/deepslate_lead_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/deepslate_lead_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/deepslate_nikolite_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/deepslate_nikolite_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/deepslate_silver_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/deepslate_silver_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/deepslate_tin_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/deepslate_tin_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/deepslate_tungsten_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/deepslate_tungsten_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/diamond_drill_head.json ================================================ { "credit": "Made with Blockbench", "texture_size": [32, 32], "textures": { "0": "indrev:item/diamond_drill_head", "particle": "indrev:item/diamond_drill_head" }, "elements": [ { "from": [2.5, 6, 8.01], "to": [14.5, 18, 8.01], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 8.01]}, "faces": { "south": {"uv": [2, 3, 13, 14], "rotation": 270, "texture": "#0"} } }, { "from": [2.5, 6, 7.99], "to": [14.5, 18, 7.99], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 7.99]}, "faces": { "north": {"uv": [2, 3, 13, 14], "rotation": 180, "texture": "#0"} } }, { "from": [7.99, 6, 2.5], "to": [7.99, 18, 14.5], "rotation": {"angle": -45, "axis": "x", "origin": [7.99, 11.5, 8]}, "faces": { "west": {"uv": [2, 3, 13, 14], "rotation": 270, "texture": "#0"} } }, { "from": [8.01, 6, 2.5], "to": [8.01, 18, 14.5], "rotation": {"angle": -45, "axis": "x", "origin": [8.01, 11.5, 8]}, "faces": { "east": {"uv": [2, 3, 13, 14], "rotation": 180, "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/drain_mk1.json ================================================ { "parent": "indrev:block/machine", "textures": { "texture": "indrev:block/drain" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/drill.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "0": "indrev:block/drill_top", "1": "indrev:block/drill_middle", "2": "indrev:block/drill_bottom", "particle": "indrev:block/drill_top" }, "elements": [ { "from": [5, 10.6, 6], "to": [10.3, 15.9, 11.3], "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 13.25, 8.65]}, "faces": { "north": {"uv": [0, 4, 4, 8], "texture": "#0"}, "east": {"uv": [0, 4, 4, 8], "texture": "#0"}, "south": {"uv": [0, 4, 4, 8], "texture": "#0"}, "west": {"uv": [0, 4, 4, 8], "texture": "#0"}, "up": {"uv": [0, 0, 4, 4], "texture": "#0"}, "down": {"uv": [0, 8, 4, 12], "texture": "#0"} } }, { "from": [5, 5.3, 6], "to": [10.3, 10.6, 11.3], "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 7.95, 8.65]}, "faces": { "north": {"uv": [0, 4, 4, 8], "texture": "#1"}, "east": {"uv": [0, 4, 4, 8], "texture": "#1"}, "south": {"uv": [0, 4, 4, 8], "texture": "#1"}, "west": {"uv": [0, 4, 4, 8], "texture": "#1"}, "up": {"uv": [0, 0, 4, 4], "texture": "#1"}, "down": {"uv": [0, 8, 4, 12], "texture": "#1"} } }, { "from": [5, 3, 6], "to": [10.3, 5.3, 6], "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 2.65, 8.65]}, "faces": { "north": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "east": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "south": {"uv": [0, 5.75, 4, 7.5], "texture": "#2"}, "west": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "up": {"uv": [0, 0, 3.75, 4], "texture": "#2"}, "down": {"uv": [4, 0, 7.75, 4], "texture": "#2"} } }, { "from": [5, 3, 6], "to": [5, 5.3, 11.3], "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 2.65, 8.65]}, "faces": { "north": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "east": {"uv": [0, 5.75, 4, 7.5], "texture": "#2"}, "south": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "west": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "up": {"uv": [0, 0, 3.75, 4], "rotation": 270, "texture": "#2"}, "down": {"uv": [4, 0, 7.75, 4], "rotation": 90, "texture": "#2"} } }, { "from": [10.3, 3, 6], "to": [10.3, 5.3, 11.3], "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 2.65, 8.65]}, "faces": { "north": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "east": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "south": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "west": {"uv": [0, 5.75, 4, 7.5], "texture": "#2"}, "up": {"uv": [0, 0, 3.75, 4], "rotation": 90, "texture": "#2"}, "down": {"uv": [4, 0, 7.75, 4], "rotation": 270, "texture": "#2"} } }, { "from": [5, 3, 11.3], "to": [10.3, 5.3, 11.3], "rotation": {"angle": 0, "axis": "y", "origin": [7.65, 2.65, 13.95]}, "faces": { "north": {"uv": [0, 5.75, 4, 7.5], "texture": "#2"}, "east": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "south": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "west": {"uv": [0, 4, 4, 5.75], "texture": "#2"}, "up": {"uv": [0, 0, 3.75, 4], "texture": "#2"}, "down": {"uv": [4, 0, 7.75, 4], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [20.5, 0, 0], "translation": [0, 4.25, -2.5], "scale": [1.3, 1.3, 1.3] }, "thirdperson_lefthand": { "rotation": [20.5, 0, 0], "translation": [0, 4.25, -2.5], "scale": [1.3, 1.3, 1.3] }, "firstperson_righthand": { "rotation": [-11, 0, 8], "scale": [1.3, 1.3, 1.3] }, "firstperson_lefthand": { "rotation": [-11, 0, 8], "scale": [1.3, 1.3, 1.3] }, "gui": { "rotation": [30, 225, 0], "translation": [0.25, -1.25, 0], "scale": [0.95, 0.95, 0.95] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] }, "ground": { "translation": [0, 2, 0], "scale": [1.3, 1.3, 1.3] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/drill_bottom.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/drill_bottom", "particle": "indrev:block/drill_bottom" }, "elements": [ { "from": [0, 9, 0.01], "to": [16, 16, 0.01], "rotation": {"angle": 0, "axis": "y", "origin": [8, 23, 8]}, "faces": { "north": {"uv": [0, 4, 4, 5.75], "texture": "#1"}, "south": {"uv": [0, 5.75, 4, 7.5], "texture": "#1"} } }, { "from": [0, 9, 15.99], "to": [16, 16, 15.99], "rotation": {"angle": 0, "axis": "y", "origin": [8, 23, 24]}, "faces": { "north": {"uv": [0, 5.75, 4, 7.5], "texture": "#1"}, "south": {"uv": [0, 4, 4, 5.75], "texture": "#1"} } }, { "from": [0.01, 9, 0], "to": [0.01, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 17, 8]}, "faces": { "east": {"uv": [0, 5.75, 4, 7.5], "texture": "#1"}, "west": {"uv": [0, 4, 4, 5.75], "texture": "#1"} } }, { "from": [15.99, 9, 0], "to": [15.99, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [24, 17, 8]}, "faces": { "east": {"uv": [0, 4, 4, 5.75], "texture": "#1"}, "west": {"uv": [0, 5.75, 4, 7.5], "texture": "#1"} } }, { "from": [0.01, 16.01, 0.01], "to": [15.99, 15.99, 15.99], "rotation": {"angle": 0, "axis": "y", "origin": [8, 24, 8]}, "faces": { "north": {"uv": [0, 0, 4, 0.25], "texture": "#1"}, "east": {"uv": [0, 0, 4, 0.25], "texture": "#1"}, "south": {"uv": [0, 0, 4, 0.25], "texture": "#1"}, "west": {"uv": [0, 0, 4, 0.25], "texture": "#1"}, "up": {"uv": [0, 0, 4, 4], "texture": "#1"}, "down": {"uv": [0, 0, 4, 4], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/drill_middle.json ================================================ { "credit": "Made with Blockbench", "textures": { "0": "indrev:block/drill_middle", "particle": "indrev:block/drill_middle" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 4, 4, 8], "texture": "#0"}, "east": {"uv": [0, 4, 4, 8], "texture": "#0"}, "south": {"uv": [0, 4, 4, 8], "texture": "#0"}, "west": {"uv": [0, 4, 4, 8], "texture": "#0"}, "up": {"uv": [0, 0, 4, 4], "texture": "#0"}, "down": {"uv": [0, 8, 4, 12], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/drill_top.json ================================================ { "credit": "Made with Blockbench", "texture_size": [64, 64], "textures": { "0": "indrev:block/drill_top", "particle": "indrev:block/drill_top" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 4, 4, 8], "texture": "#0"}, "east": {"uv": [0, 4, 4, 8], "texture": "#0"}, "south": {"uv": [0, 4, 4, 8], "texture": "#0"}, "west": {"uv": [0, 4, 4, 8], "texture": "#0"}, "up": {"uv": [4, 4, 0, 0], "texture": "#0"}, "down": {"uv": [0, 8, 4, 12], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/drill_top_on.json ================================================ { "credit": "Made with Blockbench", "texture_size": [64, 64], "textures": { "0": "indrev:block/drill_top", "1": "indrev:block/drill_top_tracer_emissive_on", "2": "indrev:block/drill_top_emissive_on", "particle": "indrev:block/drill_top" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 4, 4, 8], "texture": "#0"}, "east": {"uv": [0, 4, 4, 8], "texture": "#0"}, "south": {"uv": [0, 4, 4, 8], "texture": "#0"}, "west": {"uv": [0, 4, 4, 8], "texture": "#0"}, "up": {"uv": [4, 4, 0, 0], "texture": "#0"}, "down": {"uv": [0, 8, 4, 12], "texture": "#0"} } }, { "from": [-0.1, 7.9, -0.1], "to": [16.1, 9.1, 16.1], "faces": { "north": {"uv": [0, 7, 16, 8], "texture": "#2", "jmx_tex0": "#2", "jmx_material": "#jmx_0"}, "east": {"uv": [0, 7, 16, 8], "texture": "#2", "jmx_tex0": "#2", "jmx_material": "#jmx_0"}, "south": {"uv": [0, 7, 16, 8], "texture": "#2", "jmx_tex0": "#2", "jmx_material": "#jmx_0"}, "west": {"uv": [0, 7, 16, 8], "texture": "#2", "jmx_tex0": "#2", "jmx_material": "#jmx_0"}, "up": {"uv": [0, 0, 2, 2], "texture": "#2", "jmx_tex0": "#2", "jmx_material": "#jmx_0"}, "down": {"uv": [0, 0, 2, 2], "texture": "#2", "jmx_tex0": "#2", "jmx_material": "#jmx_0"} } }, { "from": [-0.1, 12.9, -0.1], "to": [16.1, 16.1, 16.1], "faces": { "north": {"uv": [4, 4, 8, 4.75], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "east": {"uv": [0, 4, 4, 4.75], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "south": {"uv": [12, 4, 16, 4.75], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "west": {"uv": [8, 4, 12, 4.75], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "up": {"uv": [8, 4, 4, 0], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "down": {"uv": [4, 12, 0, 16], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"} } } ], "jmx": { "textures": { "0": "indrev:block/drill_top", "1": "indrev:block/drill_top_tracer_emissive_on", "2": "indrev:block/drill_top_emissive_on" }, "materials": { "jmx_0": { "layer0": "cutout", "emissive0": true, "ambient_occlusion0": false, "diffuse0": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/duct.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "texture_size": [32, 32], "textures": { "0": "indrev:block/duct-opening", "1": "indrev:block/duct-tube-upper", "2": "indrev:block/duct-tube-lower", "particle": "indrev:block/duct-opening" }, "elements": [ { "from": [2, 2, 15], "to": [14, 14, 16], "rotation": {"angle": 0, "axis": "y", "origin": [9, 9, 15.5]}, "faces": { "north": {"uv": [0, 6, 6, 12], "texture": "#0"}, "east": {"uv": [6, 0, 6.5, 6], "texture": "#0"}, "south": {"uv": [0, 0, 6, 6], "texture": "#0"}, "west": {"uv": [6, 0, 6.5, 6], "texture": "#0"}, "up": {"uv": [12.5, 0.5, 6.5, 0], "texture": "#0"}, "down": {"uv": [12.5, 0, 6.5, 0.5], "texture": "#0"} } }, { "from": [2, 0, 2], "to": [14, 1, 14], "rotation": {"angle": 0, "axis": "y", "origin": [9, 0.5, 9]}, "faces": { "north": {"uv": [6, 0, 6.5, 6.5], "texture": "#0"}, "east": {"uv": [6.5, 0, 12.5, 0.5], "texture": "#0"}, "south": {"uv": [6, 0, 6.5, 6.5], "texture": "#0"}, "west": {"uv": [6.5, 0, 12.5, 0.5], "texture": "#0"}, "up": {"uv": [0, 6, 6, 12], "texture": "#0"}, "down": {"uv": [0, 0, 6, 6], "texture": "#0"} } }, { "from": [3, 3, 3], "to": [13, 13, 15], "rotation": {"angle": 0, "axis": "y", "origin": [11, 11, 11]}, "faces": { "north": {"uv": [11, 11, 16, 16], "texture": "#1"}, "east": {"uv": [0, 0, 6, 5], "texture": "#1"}, "south": {"uv": [0, 11, 5, 16], "texture": "#1"}, "west": {"uv": [0, 5, 6, 10], "texture": "#1"}, "up": {"uv": [11, 12, 6, 6], "texture": "#1"}, "down": {"uv": [11, 0, 6, 6], "texture": "#1"} } }, { "from": [3.03, 1.03, 3.03], "to": [12.99, 3.99, 12.99], "rotation": {"angle": 0, "axis": "y", "origin": [11, 9, 11]}, "faces": { "north": {"uv": [5, 8.5, 10, 10], "texture": "#2"}, "east": {"uv": [5, 2.5, 10, 4], "texture": "#2"}, "south": {"uv": [5, 0.5, 10, 2], "texture": "#2"}, "west": {"uv": [5, 0.5, 10, 2], "texture": "#2"}, "up": {"uv": [5, 5, 0, 0], "texture": "#2"}, "down": {"uv": [5, 5, 0, 10], "texture": "#2"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/electrum_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/electrum_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/fishing_farm_mk2.json ================================================ { "credit": "Made with Blockbench", "texture_size": [32, 32], "parent": "minecraft:block/block", "textures": { "1": "indrev:block/machine_block", "wood_top": "indrev:block/wood_top", "particle": "indrev:block/machine_block" }, "elements": [ { "from": [1, 13, 7], "to": [2, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [9, 19, 17]}, "faces": { "east": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"}, "up": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"} } }, { "from": [14, 13, 7], "to": [15, 16, 14], "rotation": {"angle": 0, "axis": "y", "origin": [22, 19, 15]}, "faces": { "west": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"}, "up": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"} } }, { "from": [0, 13, 0], "to": [16, 16, 7], "rotation": {"angle": 0, "axis": "y", "origin": [9, 20, 12]}, "faces": { "north": {"uv": [8, 0, 16, 1.5], "texture": "#wood_top"}, "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "south": {"uv": [8, 0, 16, 1.5], "texture": "#wood_top"}, "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 0, 16, 3.5], "texture": "#wood_top"}, "down": {"uv": [0, 0, 5, 3.5], "texture": "#wood_top"} } }, { "from": [5, 15, 2], "to": [12, 17, 4], "rotation": {"angle": 0, "axis": "y", "origin": [12, 21, 10]}, "faces": { "north": {"uv": [0, 5, 3.5, 5.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "south": {"uv": [5.5, 15, 9, 15.5], "texture": "#wood_top"}, "up": {"uv": [4.5, 9, 8, 10], "texture": "#wood_top"} } }, { "from": [6, 15, 1], "to": [11, 17, 5], "rotation": {"angle": 0, "axis": "y", "origin": [13, 21, 10]}, "faces": { "north": {"uv": [1, 8, 3.5, 8.5], "texture": "#wood_top"}, "east": {"uv": [1.5, 11.5, 3.5, 12], "texture": "#wood_top"}, "south": {"uv": [1, 9, 3.5, 9.5], "texture": "#wood_top"}, "west": {"uv": [1.5, 11.5, 3.5, 12], "texture": "#wood_top"}, "up": {"uv": [1, 6, 3.5, 8], "texture": "#wood_top"} } }, { "from": [6, 16, 2], "to": [11, 18, 4], "rotation": {"angle": 0, "axis": "y", "origin": [14, 22, 10]}, "faces": { "north": {"uv": [7, 12, 9.5, 12.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "south": {"uv": [7, 11.5, 9.5, 12], "texture": "#wood_top"}, "west": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "up": {"uv": [0, 4, 2.5, 5], "texture": "#wood_top"} } }, { "from": [8, 15, 5], "to": [9, 17, 6], "rotation": {"angle": 0, "axis": "y", "origin": [16, 21, 13]}, "faces": { "north": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "east": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "south": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "west": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "up": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "down": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"} } }, { "from": [4, 15, 1], "to": [5, 17, 5], "rotation": {"angle": 0, "axis": "y", "origin": [12, 21, 12]}, "faces": { "north": {"uv": [4.5, 11, 5, 11.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 13, 6.5, 13.5], "texture": "#wood_top"}, "south": {"uv": [4.5, 11, 5, 11.5], "texture": "#wood_top"}, "west": {"uv": [4.5, 13.5, 6.5, 14], "texture": "#wood_top"}, "up": {"uv": [4.5, 11, 5, 13], "texture": "#wood_top"} } }, { "from": [2, 13, 14], "to": [15, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [10, 19, 22]}, "faces": { "north": {"uv": [8.5, 2.5, 15, 4], "texture": "#wood_top"}, "up": {"uv": [9, 0, 15.5, 0.5], "texture": "#wood_top"} } }, { "from": [0, 13, 7], "to": [1, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [8, 19, 15]}, "faces": { "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 3, 8.5, 7], "texture": "#wood_top"}, "down": {"uv": [0, 0, 0.5, 3.5], "texture": "#wood_top"} } }, { "from": [15, 13, 7], "to": [16, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [23, 20, 21]}, "faces": { "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 3, 8.5, 7], "texture": "#wood_top"}, "down": {"uv": [0, 0, 0.5, 3.5], "texture": "#wood_top"} } }, { "from": [0, 13, 15], "to": [16, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 19, 23]}, "faces": { "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "south": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 0, 16, 0.5], "texture": "#wood_top"}, "down": {"uv": [0, 0, 5, 0.5], "texture": "#wood_top"} } }, { "from": [0, 0, 0], "to": [16, 13, 16], "rotation": {"angle": 0, "axis": "y", "origin": [10, 8, 10]}, "faces": { "north": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "east": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "south": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "west": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "up": {"uv": [6, 0.333, 9.6665, 4.833], "texture": "#1"}, "down": {"uv": [5.6665, 10.9995, 10.333, 15.666], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/fishing_farm_mk3.json ================================================ { "credit": "Made with Blockbench", "texture_size": [32, 32], "parent": "minecraft:block/block", "textures": { "1": "indrev:block/machine_block", "wood_top": "indrev:block/wood_top", "particle": "indrev:block/machine_block" }, "elements": [ { "from": [1, 13, 7], "to": [2, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [9, 19, 17]}, "faces": { "east": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"}, "up": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"} } }, { "from": [14, 13, 7], "to": [15, 16, 14], "rotation": {"angle": 0, "axis": "y", "origin": [22, 19, 15]}, "faces": { "west": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"}, "up": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"} } }, { "from": [0, 13, 0], "to": [16, 16, 7], "rotation": {"angle": 0, "axis": "y", "origin": [9, 20, 12]}, "faces": { "north": {"uv": [8, 0, 16, 1.5], "texture": "#wood_top"}, "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "south": {"uv": [8, 0, 16, 1.5], "texture": "#wood_top"}, "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 0, 16, 3.5], "texture": "#wood_top"}, "down": {"uv": [0, 0, 5, 3.5], "texture": "#wood_top"} } }, { "from": [5, 15, 2], "to": [12, 17, 4], "rotation": {"angle": 0, "axis": "y", "origin": [12, 21, 10]}, "faces": { "north": {"uv": [0, 5, 3.5, 5.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "south": {"uv": [5.5, 15, 9, 15.5], "texture": "#wood_top"}, "up": {"uv": [4.5, 9, 8, 10], "texture": "#wood_top"} } }, { "from": [6, 15, 1], "to": [11, 17, 5], "rotation": {"angle": 0, "axis": "y", "origin": [13, 21, 10]}, "faces": { "north": {"uv": [1, 8, 3.5, 8.5], "texture": "#wood_top"}, "east": {"uv": [1.5, 11.5, 3.5, 12], "texture": "#wood_top"}, "south": {"uv": [1, 9, 3.5, 9.5], "texture": "#wood_top"}, "west": {"uv": [1.5, 11.5, 3.5, 12], "texture": "#wood_top"}, "up": {"uv": [1, 6, 3.5, 8], "texture": "#wood_top"} } }, { "from": [6, 16, 2], "to": [11, 18, 4], "rotation": {"angle": 0, "axis": "y", "origin": [14, 22, 10]}, "faces": { "north": {"uv": [7, 12, 9.5, 12.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "south": {"uv": [7, 11.5, 9.5, 12], "texture": "#wood_top"}, "west": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "up": {"uv": [0, 4, 2.5, 5], "texture": "#wood_top"} } }, { "from": [8, 15, 5], "to": [9, 17, 6], "rotation": {"angle": 0, "axis": "y", "origin": [16, 21, 13]}, "faces": { "north": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "east": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "south": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "west": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "up": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "down": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"} } }, { "from": [4, 15, 1], "to": [5, 17, 5], "rotation": {"angle": 0, "axis": "y", "origin": [12, 21, 12]}, "faces": { "north": {"uv": [4.5, 11, 5, 11.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 13, 6.5, 13.5], "texture": "#wood_top"}, "south": {"uv": [4.5, 11, 5, 11.5], "texture": "#wood_top"}, "west": {"uv": [4.5, 13.5, 6.5, 14], "texture": "#wood_top"}, "up": {"uv": [4.5, 11, 5, 13], "texture": "#wood_top"} } }, { "from": [2, 13, 14], "to": [15, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [10, 19, 22]}, "faces": { "north": {"uv": [8.5, 2.5, 15, 4], "texture": "#wood_top"}, "up": {"uv": [9, 0, 15.5, 0.5], "texture": "#wood_top"} } }, { "from": [0, 13, 7], "to": [1, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [8, 19, 15]}, "faces": { "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 3, 8.5, 7], "texture": "#wood_top"}, "down": {"uv": [0, 0, 0.5, 3.5], "texture": "#wood_top"} } }, { "from": [15, 13, 7], "to": [16, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [23, 20, 21]}, "faces": { "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 3, 8.5, 7], "texture": "#wood_top"}, "down": {"uv": [0, 0, 0.5, 3.5], "texture": "#wood_top"} } }, { "from": [0, 13, 15], "to": [16, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 19, 23]}, "faces": { "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "south": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 0, 16, 0.5], "texture": "#wood_top"}, "down": {"uv": [0, 0, 5, 0.5], "texture": "#wood_top"} } }, { "from": [0, 0, 0], "to": [16, 13, 16], "rotation": {"angle": 0, "axis": "y", "origin": [10, 8, 10]}, "faces": { "north": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "east": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "south": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "west": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "up": {"uv": [6, 0.333, 9.6665, 4.833], "texture": "#1"}, "down": {"uv": [5.6665, 10.9995, 10.333, 15.666], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/fishing_farm_mk4.json ================================================ { "credit": "Made with Blockbench", "texture_size": [32, 32], "parent": "minecraft:block/block", "textures": { "1": "indrev:block/machine_block", "wood_top": "indrev:block/wood_top", "particle": "indrev:block/machine_block" }, "elements": [ { "from": [1, 13, 7], "to": [2, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [9, 19, 17]}, "faces": { "east": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"}, "up": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"} } }, { "from": [14, 13, 7], "to": [15, 16, 14], "rotation": {"angle": 0, "axis": "y", "origin": [22, 19, 15]}, "faces": { "west": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"}, "up": {"uv": [8.5, 1, 9, 5], "texture": "#wood_top"} } }, { "from": [0, 13, 0], "to": [16, 16, 7], "rotation": {"angle": 0, "axis": "y", "origin": [9, 20, 12]}, "faces": { "north": {"uv": [8, 0, 16, 1.5], "texture": "#wood_top"}, "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "south": {"uv": [8, 0, 16, 1.5], "texture": "#wood_top"}, "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 0, 16, 3.5], "texture": "#wood_top"}, "down": {"uv": [0, 0, 5, 3.5], "texture": "#wood_top"} } }, { "from": [5, 15, 2], "to": [12, 17, 4], "rotation": {"angle": 0, "axis": "y", "origin": [12, 21, 10]}, "faces": { "north": {"uv": [0, 5, 3.5, 5.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "south": {"uv": [5.5, 15, 9, 15.5], "texture": "#wood_top"}, "up": {"uv": [4.5, 9, 8, 10], "texture": "#wood_top"} } }, { "from": [6, 15, 1], "to": [11, 17, 5], "rotation": {"angle": 0, "axis": "y", "origin": [13, 21, 10]}, "faces": { "north": {"uv": [1, 8, 3.5, 8.5], "texture": "#wood_top"}, "east": {"uv": [1.5, 11.5, 3.5, 12], "texture": "#wood_top"}, "south": {"uv": [1, 9, 3.5, 9.5], "texture": "#wood_top"}, "west": {"uv": [1.5, 11.5, 3.5, 12], "texture": "#wood_top"}, "up": {"uv": [1, 6, 3.5, 8], "texture": "#wood_top"} } }, { "from": [6, 16, 2], "to": [11, 18, 4], "rotation": {"angle": 0, "axis": "y", "origin": [14, 22, 10]}, "faces": { "north": {"uv": [7, 12, 9.5, 12.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "south": {"uv": [7, 11.5, 9.5, 12], "texture": "#wood_top"}, "west": {"uv": [4.5, 9, 5.5, 9.5], "texture": "#wood_top"}, "up": {"uv": [0, 4, 2.5, 5], "texture": "#wood_top"} } }, { "from": [8, 15, 5], "to": [9, 17, 6], "rotation": {"angle": 0, "axis": "y", "origin": [16, 21, 13]}, "faces": { "north": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "east": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "south": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "west": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "up": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"}, "down": {"uv": [0, 4, 0.5, 4.5], "texture": "#wood_top"} } }, { "from": [4, 15, 1], "to": [5, 17, 5], "rotation": {"angle": 0, "axis": "y", "origin": [12, 21, 12]}, "faces": { "north": {"uv": [4.5, 11, 5, 11.5], "texture": "#wood_top"}, "east": {"uv": [4.5, 13, 6.5, 13.5], "texture": "#wood_top"}, "south": {"uv": [4.5, 11, 5, 11.5], "texture": "#wood_top"}, "west": {"uv": [4.5, 13.5, 6.5, 14], "texture": "#wood_top"}, "up": {"uv": [4.5, 11, 5, 13], "texture": "#wood_top"} } }, { "from": [2, 13, 14], "to": [15, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [10, 19, 22]}, "faces": { "north": {"uv": [8.5, 2.5, 15, 4], "texture": "#wood_top"}, "up": {"uv": [9, 0, 15.5, 0.5], "texture": "#wood_top"} } }, { "from": [0, 13, 7], "to": [1, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [8, 19, 15]}, "faces": { "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 3, 8.5, 7], "texture": "#wood_top"}, "down": {"uv": [0, 0, 0.5, 3.5], "texture": "#wood_top"} } }, { "from": [15, 13, 7], "to": [16, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [23, 20, 21]}, "faces": { "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 3, 8.5, 7], "texture": "#wood_top"}, "down": {"uv": [0, 0, 0.5, 3.5], "texture": "#wood_top"} } }, { "from": [0, 13, 15], "to": [16, 16, 16], "rotation": {"angle": 0, "axis": "y", "origin": [8, 19, 23]}, "faces": { "east": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "south": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "west": {"uv": [15.5, 0, 16, 1.5], "texture": "#wood_top"}, "up": {"uv": [8, 0, 16, 0.5], "texture": "#wood_top"}, "down": {"uv": [0, 0, 5, 0.5], "texture": "#wood_top"} } }, { "from": [0, 0, 0], "to": [16, 13, 16], "rotation": {"angle": 0, "axis": "y", "origin": [10, 8, 10]}, "faces": { "north": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "east": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "south": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "west": {"uv": [5.333, 5.6665, 10.665, 10.832], "texture": "#1"}, "up": {"uv": [6, 0.333, 9.6665, 4.833], "texture": "#1"}, "down": {"uv": [5.6665, 10.9995, 10.333, 15.666], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/fluid_pipe_center_mk1.json ================================================ { "credit": "Made with Blockbench", "textures": { "2": "indrev:block/fluid_pipe_center_mk1", "particle": "indrev:block/fluid_pipe_center_mk1" }, "elements": [ { "from": [6, 6, 6], "to": [10, 10, 10], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#2"}, "east": {"uv": [0, 0, 16, 16], "texture": "#2"}, "south": {"uv": [0, 0, 16, 16], "texture": "#2"}, "west": {"uv": [0, 0, 16, 16], "texture": "#2"}, "up": {"uv": [0, 0, 16, 16], "texture": "#2"}, "down": {"uv": [0, 0, 16, 16], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [1, 1, 1] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/fluid_pipe_center_mk2.json ================================================ { "credit": "Made with Blockbench", "textures": { "2": "indrev:block/fluid_pipe_center_mk2", "particle": "indrev:block/fluid_pipe_center_mk2" }, "elements": [ { "from": [6, 6, 6], "to": [10, 10, 10], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#2"}, "east": {"uv": [0, 0, 16, 16], "texture": "#2"}, "south": {"uv": [0, 0, 16, 16], "texture": "#2"}, "west": {"uv": [0, 0, 16, 16], "texture": "#2"}, "up": {"uv": [0, 0, 16, 16], "texture": "#2"}, "down": {"uv": [0, 0, 16, 16], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [1, 1, 1] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/fluid_pipe_center_mk3.json ================================================ { "credit": "Made with Blockbench", "textures": { "2": "indrev:block/fluid_pipe_center_mk3", "particle": "indrev:block/fluid_pipe_center_mk3" }, "elements": [ { "from": [6, 6, 6], "to": [10, 10, 10], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#2"}, "east": {"uv": [0, 0, 16, 16], "texture": "#2"}, "south": {"uv": [0, 0, 16, 16], "texture": "#2"}, "west": {"uv": [0, 0, 16, 16], "texture": "#2"}, "up": {"uv": [0, 0, 16, 16], "texture": "#2"}, "down": {"uv": [0, 0, 16, 16], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [1, 1, 1] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/fluid_pipe_center_mk4.json ================================================ { "credit": "Made with Blockbench", "textures": { "2": "indrev:block/fluid_pipe_center_mk4", "particle": "indrev:block/fluid_pipe_center_mk4" }, "elements": [ { "from": [6, 6, 6], "to": [10, 10, 10], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#2"}, "east": {"uv": [0, 0, 16, 16], "texture": "#2"}, "south": {"uv": [0, 0, 16, 16], "texture": "#2"}, "west": {"uv": [0, 0, 16, 16], "texture": "#2"}, "up": {"uv": [0, 0, 16, 16], "texture": "#2"}, "down": {"uv": [0, 0, 16, 16], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [1.0, 1.0, 1.0] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/fluid_pipe_side_mk1.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/fluid_pipe_side_mk1", "particle": "indrev:block/fluid_pipe_side_mk1" }, "elements": [ { "from": [6, 0, 6], "to": [10, 6, 10], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [6, 0, 10, 6], "texture": "#1"}, "east": {"uv": [6, 0, 10, 6], "texture": "#1"}, "south": {"uv": [6, 0, 10, 6], "texture": "#1"}, "west": {"uv": [6, 0, 10, 6], "texture": "#1"}, "up": {"uv": [4, 10, 0, 6], "texture": "#1"}, "down": {"uv": [4, 6, 0, 10], "texture": "#1"} } }, { "from": [5, 4, 5], "to": [11, 5, 11], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [0, 12, 6, 13], "texture": "#1"}, "east": {"uv": [0, 13, 6, 14], "texture": "#1"}, "south": {"uv": [0, 10, 6, 11], "texture": "#1"}, "west": {"uv": [0, 11, 6, 12], "texture": "#1"}, "up": {"uv": [6, 6, 0, 0], "texture": "#1"}, "down": {"uv": [6, 0, 0, 6], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/fluid_pipe_side_mk2.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/fluid_pipe_side_mk2", "particle": "indrev:block/fluid_pipe_side_mk2" }, "elements": [ { "from": [6, 0, 6], "to": [10, 6, 10], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [6, 0, 10, 6], "texture": "#1"}, "east": {"uv": [6, 0, 10, 6], "texture": "#1"}, "south": {"uv": [6, 0, 10, 6], "texture": "#1"}, "west": {"uv": [6, 0, 10, 6], "texture": "#1"}, "up": {"uv": [4, 10, 0, 6], "texture": "#1"}, "down": {"uv": [4, 6, 0, 10], "texture": "#1"} } }, { "from": [5, 4, 5], "to": [11, 5, 11], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [0, 12, 6, 13], "texture": "#1"}, "east": {"uv": [0, 13, 6, 14], "texture": "#1"}, "south": {"uv": [0, 10, 6, 11], "texture": "#1"}, "west": {"uv": [0, 11, 6, 12], "texture": "#1"}, "up": {"uv": [6, 6, 0, 0], "texture": "#1"}, "down": {"uv": [6, 0, 0, 6], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/fluid_pipe_side_mk3.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/fluid_pipe_side_mk3", "particle": "indrev:block/fluid_pipe_side_mk3" }, "elements": [ { "from": [6, 0, 6], "to": [10, 6, 10], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [6, 0, 10, 6], "texture": "#1"}, "east": {"uv": [6, 0, 10, 6], "texture": "#1"}, "south": {"uv": [6, 0, 10, 6], "texture": "#1"}, "west": {"uv": [6, 0, 10, 6], "texture": "#1"}, "up": {"uv": [4, 10, 0, 6], "texture": "#1"}, "down": {"uv": [4, 6, 0, 10], "texture": "#1"} } }, { "from": [5, 4, 5], "to": [11, 5, 11], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [0, 12, 6, 13], "texture": "#1"}, "east": {"uv": [0, 13, 6, 14], "texture": "#1"}, "south": {"uv": [0, 10, 6, 11], "texture": "#1"}, "west": {"uv": [0, 11, 6, 12], "texture": "#1"}, "up": {"uv": [6, 6, 0, 0], "texture": "#1"}, "down": {"uv": [6, 0, 0, 6], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/fluid_pipe_side_mk4.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/fluid_pipe_side_mk4", "particle": "indrev:block/fluid_pipe_side_mk4" }, "elements": [ { "from": [6, 0, 6], "to": [10, 6, 10], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [6, 0, 10, 6], "texture": "#1"}, "east": {"uv": [6, 0, 10, 6], "texture": "#1"}, "south": {"uv": [6, 0, 10, 6], "texture": "#1"}, "west": {"uv": [6, 0, 10, 6], "texture": "#1"}, "up": {"uv": [4, 10, 0, 6], "texture": "#1"}, "down": {"uv": [4, 6, 0, 10], "texture": "#1"} } }, { "from": [5, 4, 5], "to": [11, 5, 11], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [0, 12, 6, 13], "texture": "#1"}, "east": {"uv": [0, 13, 6, 14], "texture": "#1"}, "south": {"uv": [0, 10, 6, 11], "texture": "#1"}, "west": {"uv": [0, 11, 6, 12], "texture": "#1"}, "up": {"uv": [6, 6, 0, 0], "texture": "#1"}, "down": {"uv": [6, 0, 0, 6], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/frame.json ================================================ { "parent": "minecraft:block/block", "texture_size": [64, 64], "textures": { "0": "indrev:block/frame", "particle": "indrev:block/frame" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 8, 8], "texture": "#0"}, "east": {"uv": [0, 0, 8, 8], "texture": "#0"}, "south": {"uv": [0, 0, 8, 8], "texture": "#0"}, "west": {"uv": [0, 0, 8, 8], "texture": "#0"}, "up": {"uv": [16, 8, 8, 0], "texture": "#0"}, "down": {"uv": [8, 8, 0, 16], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/gray_lava.json ================================================ { "textures": { "particle": "indrev:block/gray_lava_still" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/heat_generator_mk4.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "texture_size": [64, 64], "textures": { "2": "indrev:block/heat_generator_inside", "texture": "indrev:block/heat_generator", "particle": "indrev:block/heat_generator" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [5.33333, 5.33333, 10.66667, 10.66667], "texture": "#texture"}, "east": {"uv": [0, 5.33333, 5.33333, 10.66667], "texture": "#texture"}, "south": {"uv": [10.66667, 10.66667, 16, 16], "texture": "#texture"}, "west": {"uv": [10.66667, 5.33333, 16, 10.66667], "texture": "#texture"}, "up": {"uv": [5.33333, 0, 10.66667, 5.33333], "texture": "#texture"}, "down": {"uv": [5.33333, 10.66667, 10.66667, 16], "texture": "#texture"} } }, { "from": [0.01, 0.99, 0.01], "to": [15.99, 1.01, 4.99], "faces": { "north": {"uv": [0, 0, 4, 0], "texture": "#2"}, "east": {"uv": [0, 0, 1.25, 0], "texture": "#2"}, "south": {"uv": [0, 0, 4, 0], "texture": "#2"}, "west": {"uv": [0, 0, 1.25, 0], "texture": "#2"}, "up": {"uv": [8, 3.5, 4, 2.25], "texture": "#2"}, "down": {"uv": [4, 4.5, 0, 5.75], "texture": "#2"} } }, { "from": [0.01, 9.99, 0.01], "to": [15.99, 10.01, 4.99], "faces": { "north": {"uv": [0, 0, 4, 0], "texture": "#2"}, "east": {"uv": [0, 0, 1.25, 0], "texture": "#2"}, "south": {"uv": [0, 0, 4, 0], "texture": "#2"}, "west": {"uv": [0, 0, 1.25, 0], "texture": "#2"}, "up": {"uv": [4, 7, 0, 5.75], "texture": "#2"}, "down": {"uv": [8, 2.25, 4, 3.5], "texture": "#2"} } }, { "from": [0.02, 1.02, 5.02], "to": [15.98, 9.98, 4.98], "faces": { "north": {"uv": [0, 0, 4, 2.25], "texture": "#2"}, "east": {"uv": [0, 0, 0, 2.25], "texture": "#2"}, "south": {"uv": [0, 2.25, 4, 4.5], "texture": "#2"}, "west": {"uv": [0, 0, 0, 2.25], "texture": "#2"}, "up": {"uv": [4, 0, 0, 0], "texture": "#2"}, "down": {"uv": [4, 0, 0, 0], "texture": "#2"} } }, { "from": [0.01, 1.01, 0.01], "to": [15.99, 9.99, 0.0], "faces": { "north": {"uv": [4, 4, 8, 6.25], "texture": "#2"}, "east": {"uv": [0, 0, 0, 2.25], "texture": "#2"}, "south": {"uv": [4, 0, 8, 2.25], "texture": "#2"}, "west": {"uv": [0, 0, 0, 2.25], "texture": "#2"}, "up": {"uv": [4, 0, 0, 0], "texture": "#2"}, "down": {"uv": [4, 0, 0, 0], "texture": "#2"} } }, { "from": [0.02, 1.01, 0.01], "to": [0, 9.99, 4.99], "faces": { "north": {"uv": [0, 0, 0, 2.25], "texture": "#2"}, "east": {"uv": [6, 7.5, 7.25, 9.75], "texture": "#2"}, "south": {"uv": [0, 0, 0, 2.25], "texture": "#2"}, "west": {"uv": [7.25, 7.5, 8.5, 9.75], "texture": "#2"}, "up": {"uv": [0, 1.25, 0, 0], "texture": "#2"}, "down": {"uv": [0, 0, 0, 1.25], "texture": "#2"} } }, { "from": [16, 1.01, 0.01], "to": [15.98, 9.99, 4.99], "faces": { "north": {"uv": [0, 0, 0, 2.25], "texture": "#2"}, "east": {"uv": [8, 0, 9.25, 2.25], "texture": "#2"}, "south": {"uv": [0, 0, 0, 2.25], "texture": "#2"}, "west": {"uv": [8, 2.25, 9.25, 4.5], "texture": "#2"}, "up": {"uv": [0, 1.25, 0, 0], "texture": "#2"}, "down": {"uv": [0, 0, 0, 1.25], "texture": "#2"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/intake.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "texture_size": [64, 64], "textures": { "0": "indrev:block/intake", "particle": "indrev:block/intake" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 4, 4, 8], "texture": "#0"}, "east": {"uv": [4, 4, 8, 8], "texture": "#0"}, "south": {"uv": [4, 4, 8, 8], "texture": "#0"}, "west": {"uv": [4, 4, 8, 8], "texture": "#0"}, "up": {"uv": [4, 4, 0, 0], "texture": "#0"}, "down": {"uv": [8, 0, 4, 4], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/iron_drill_head.json ================================================ { "credit": "Made with Blockbench", "texture_size": [32, 32], "textures": { "0": "indrev:item/iron_drill_head", "particle": "indrev:item/iron_drill_head" }, "elements": [ { "from": [2.5, 6, 8.01], "to": [14.5, 18, 8.01], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 8.01]}, "faces": { "south": {"uv": [2, 3, 13, 14], "rotation": 270, "texture": "#0"} } }, { "from": [2.5, 6, 7.99], "to": [14.5, 18, 7.99], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 7.99]}, "faces": { "north": {"uv": [2, 3, 13, 14], "rotation": 180, "texture": "#0"} } }, { "from": [7.99, 6, 2.5], "to": [7.99, 18, 14.5], "rotation": {"angle": -45, "axis": "x", "origin": [7.99, 11.5, 8]}, "faces": { "west": {"uv": [2, 3, 13, 14], "rotation": 270, "texture": "#0"} } }, { "from": [8.01, 6, 2.5], "to": [8.01, 18, 14.5], "rotation": {"angle": -45, "axis": "x", "origin": [8.01, 11.5, 8]}, "faces": { "east": {"uv": [2, 3, 13, 14], "rotation": 180, "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/item_pipe_center_mk1.json ================================================ { "credit": "Made with Blockbench", "textures": { "2": "indrev:block/item_pipe_center_mk1", "particle": "indrev:block/item_pipe_center_mk1" }, "elements": [ { "from": [6.5, 6.5, 6.5], "to": [7.5, 7.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 2, 4, 3], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 1, 4, 2], "texture": "#2"}, "up": {"uv": [0, 2, 4, 3], "texture": "#2"}, "down": {"uv": [0, 1, 4, 2], "texture": "#2"} } }, { "from": [7.5, 6.5, 8.5], "to": [8.5, 7.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [0, 2, 2, 3], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [1, 1, 3, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [0, 2, 2, 3], "texture": "#2"}, "down": {"uv": [1, 1, 3, 2], "texture": "#2"} } }, { "from": [7.5, 6.5, 6.5], "to": [8.5, 7.5, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [1, 1, 3, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [0, 2, 2, 3], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [0, 2, 2, 3], "texture": "#2"}, "down": {"uv": [1, 1, 3, 2], "texture": "#2"} } }, { "from": [6.5, 8.5, 6.5], "to": [7.5, 9.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 2, 4, 3], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 1, 4, 2], "texture": "#2"}, "up": {"uv": [0, 1, 4, 2], "texture": "#2"}, "down": {"uv": [0, 2, 4, 3], "texture": "#2"} } }, { "from": [6.5, 7.5, 6.5], "to": [7.5, 8.5, 7.5], "faces": { "north": {"uv": [0, 0, 1, 2], "texture": "#2"}, "east": {"uv": [0, 3, 1, 5], "texture": "#2"}, "south": {"uv": [0, 3, 1, 5], "texture": "#2"}, "west": {"uv": [0, 0, 1, 2], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [8.5, 7.5, 6.5], "to": [9.5, 8.5, 7.5], "faces": { "north": {"uv": [0, 0, 1, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 2], "texture": "#2"}, "south": {"uv": [0, 3, 1, 5], "texture": "#2"}, "west": {"uv": [0, 3, 1, 5], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [8.5, 7.5, 8.5], "to": [9.5, 8.5, 9.5], "faces": { "north": {"uv": [0, 3, 1, 5], "texture": "#2"}, "east": {"uv": [0, 0, 1, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 2], "texture": "#2"}, "west": {"uv": [0, 3, 1, 5], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [6.5, 7.5, 8.5], "to": [7.5, 8.5, 9.5], "faces": { "north": {"uv": [0, 3, 1, 5], "texture": "#2"}, "east": {"uv": [0, 3, 1, 5], "texture": "#2"}, "south": {"uv": [0, 0, 1, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 2], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [7.5, 8.5, 8.5], "to": [8.5, 9.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [0, 2, 2, 3], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [1, 1, 3, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [1, 1, 3, 2], "texture": "#2"}, "down": {"uv": [0, 2, 2, 3], "texture": "#2"} } }, { "from": [7.5, 8.5, 6.5], "to": [8.5, 9.5, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [1, 1, 3, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [0, 2, 2, 3], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [1, 1, 3, 2], "texture": "#2"}, "down": {"uv": [0, 2, 2, 3], "texture": "#2"} } }, { "from": [8.5, 6.5, 6.5], "to": [9.5, 7.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 1, 4, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 2, 4, 3], "texture": "#2"}, "up": {"uv": [0, 2, 4, 3], "texture": "#2"}, "down": {"uv": [0, 1, 4, 2], "texture": "#2"} } }, { "from": [8.5, 8.5, 6.5], "to": [9.5, 9.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 1, 4, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 2, 4, 3], "texture": "#2"}, "up": {"uv": [0, 1, 4, 2], "texture": "#2"}, "down": {"uv": [0, 2, 4, 3], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [2, 2, 2] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/item_pipe_center_mk2.json ================================================ { "credit": "Made with Blockbench", "textures": { "2": "indrev:block/item_pipe_center_mk2", "particle": "indrev:block/item_pipe_center_mk2" }, "elements": [ { "from": [6.5, 6.5, 6.5], "to": [7.5, 7.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 2, 4, 3], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 1, 4, 2], "texture": "#2"}, "up": {"uv": [0, 2, 4, 3], "texture": "#2"}, "down": {"uv": [0, 1, 4, 2], "texture": "#2"} } }, { "from": [7.5, 6.5, 8.5], "to": [8.5, 7.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [0, 2, 2, 3], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [1, 1, 3, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [0, 2, 2, 3], "texture": "#2"}, "down": {"uv": [1, 1, 3, 2], "texture": "#2"} } }, { "from": [7.5, 6.5, 6.5], "to": [8.5, 7.5, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [1, 1, 3, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [0, 2, 2, 3], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [0, 2, 2, 3], "texture": "#2"}, "down": {"uv": [1, 1, 3, 2], "texture": "#2"} } }, { "from": [6.5, 8.5, 6.5], "to": [7.5, 9.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 2, 4, 3], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 1, 4, 2], "texture": "#2"}, "up": {"uv": [0, 1, 4, 2], "texture": "#2"}, "down": {"uv": [0, 2, 4, 3], "texture": "#2"} } }, { "from": [6.5, 7.5, 6.5], "to": [7.5, 8.5, 7.5], "faces": { "north": {"uv": [0, 0, 1, 2], "texture": "#2"}, "east": {"uv": [0, 3, 1, 5], "texture": "#2"}, "south": {"uv": [0, 3, 1, 5], "texture": "#2"}, "west": {"uv": [0, 0, 1, 2], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [8.5, 7.5, 6.5], "to": [9.5, 8.5, 7.5], "faces": { "north": {"uv": [0, 0, 1, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 2], "texture": "#2"}, "south": {"uv": [0, 3, 1, 5], "texture": "#2"}, "west": {"uv": [0, 3, 1, 5], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [8.5, 7.5, 8.5], "to": [9.5, 8.5, 9.5], "faces": { "north": {"uv": [0, 3, 1, 5], "texture": "#2"}, "east": {"uv": [0, 0, 1, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 2], "texture": "#2"}, "west": {"uv": [0, 3, 1, 5], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [6.5, 7.5, 8.5], "to": [7.5, 8.5, 9.5], "faces": { "north": {"uv": [0, 3, 1, 5], "texture": "#2"}, "east": {"uv": [0, 3, 1, 5], "texture": "#2"}, "south": {"uv": [0, 0, 1, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 2], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [7.5, 8.5, 8.5], "to": [8.5, 9.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [0, 2, 2, 3], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [1, 1, 3, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [1, 1, 3, 2], "texture": "#2"}, "down": {"uv": [0, 2, 2, 3], "texture": "#2"} } }, { "from": [7.5, 8.5, 6.5], "to": [8.5, 9.5, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [1, 1, 3, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [0, 2, 2, 3], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [1, 1, 3, 2], "texture": "#2"}, "down": {"uv": [0, 2, 2, 3], "texture": "#2"} } }, { "from": [8.5, 6.5, 6.5], "to": [9.5, 7.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 1, 4, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 2, 4, 3], "texture": "#2"}, "up": {"uv": [0, 2, 4, 3], "texture": "#2"}, "down": {"uv": [0, 1, 4, 2], "texture": "#2"} } }, { "from": [8.5, 8.5, 6.5], "to": [9.5, 9.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 1, 4, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 2, 4, 3], "texture": "#2"}, "up": {"uv": [0, 1, 4, 2], "texture": "#2"}, "down": {"uv": [0, 2, 4, 3], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [2, 2, 2] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/item_pipe_center_mk3.json ================================================ { "credit": "Made with Blockbench", "textures": { "2": "indrev:block/item_pipe_center_mk3", "particle": "indrev:block/item_pipe_center_mk3" }, "elements": [ { "from": [6.5, 6.5, 6.5], "to": [7.5, 7.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 2, 4, 3], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 1, 4, 2], "texture": "#2"}, "up": {"uv": [0, 2, 4, 3], "texture": "#2"}, "down": {"uv": [0, 1, 4, 2], "texture": "#2"} } }, { "from": [7.5, 6.5, 8.5], "to": [8.5, 7.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [0, 2, 2, 3], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [1, 1, 3, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [0, 2, 2, 3], "texture": "#2"}, "down": {"uv": [1, 1, 3, 2], "texture": "#2"} } }, { "from": [7.5, 6.5, 6.5], "to": [8.5, 7.5, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [1, 1, 3, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [0, 2, 2, 3], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [0, 2, 2, 3], "texture": "#2"}, "down": {"uv": [1, 1, 3, 2], "texture": "#2"} } }, { "from": [6.5, 8.5, 6.5], "to": [7.5, 9.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 2, 4, 3], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 1, 4, 2], "texture": "#2"}, "up": {"uv": [0, 1, 4, 2], "texture": "#2"}, "down": {"uv": [0, 2, 4, 3], "texture": "#2"} } }, { "from": [6.5, 7.5, 6.5], "to": [7.5, 8.5, 7.5], "faces": { "north": {"uv": [0, 0, 1, 2], "texture": "#2"}, "east": {"uv": [0, 3, 1, 5], "texture": "#2"}, "south": {"uv": [0, 3, 1, 5], "texture": "#2"}, "west": {"uv": [0, 0, 1, 2], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [8.5, 7.5, 6.5], "to": [9.5, 8.5, 7.5], "faces": { "north": {"uv": [0, 0, 1, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 2], "texture": "#2"}, "south": {"uv": [0, 3, 1, 5], "texture": "#2"}, "west": {"uv": [0, 3, 1, 5], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [8.5, 7.5, 8.5], "to": [9.5, 8.5, 9.5], "faces": { "north": {"uv": [0, 3, 1, 5], "texture": "#2"}, "east": {"uv": [0, 0, 1, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 2], "texture": "#2"}, "west": {"uv": [0, 3, 1, 5], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [6.5, 7.5, 8.5], "to": [7.5, 8.5, 9.5], "faces": { "north": {"uv": [0, 3, 1, 5], "texture": "#2"}, "east": {"uv": [0, 3, 1, 5], "texture": "#2"}, "south": {"uv": [0, 0, 1, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 2], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [7.5, 8.5, 8.5], "to": [8.5, 9.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [0, 2, 2, 3], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [1, 1, 3, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [1, 1, 3, 2], "texture": "#2"}, "down": {"uv": [0, 2, 2, 3], "texture": "#2"} } }, { "from": [7.5, 8.5, 6.5], "to": [8.5, 9.5, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [1, 1, 3, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [0, 2, 2, 3], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [1, 1, 3, 2], "texture": "#2"}, "down": {"uv": [0, 2, 2, 3], "texture": "#2"} } }, { "from": [8.5, 6.5, 6.5], "to": [9.5, 7.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 1, 4, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 2, 4, 3], "texture": "#2"}, "up": {"uv": [0, 2, 4, 3], "texture": "#2"}, "down": {"uv": [0, 1, 4, 2], "texture": "#2"} } }, { "from": [8.5, 8.5, 6.5], "to": [9.5, 9.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 1, 4, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 2, 4, 3], "texture": "#2"}, "up": {"uv": [0, 1, 4, 2], "texture": "#2"}, "down": {"uv": [0, 2, 4, 3], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [2, 2, 2] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/item_pipe_center_mk4.json ================================================ { "credit": "Made with Blockbench", "textures": { "2": "indrev:block/item_pipe_center_mk4", "particle": "indrev:block/item_pipe_center_mk4" }, "elements": [ { "from": [6.5, 6.5, 6.5], "to": [7.5, 7.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 2, 4, 3], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 1, 4, 2], "texture": "#2"}, "up": {"uv": [0, 2, 4, 3], "texture": "#2"}, "down": {"uv": [0, 1, 4, 2], "texture": "#2"} } }, { "from": [7.5, 6.5, 8.5], "to": [8.5, 7.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [0, 2, 2, 3], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [1, 1, 3, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [0, 2, 2, 3], "texture": "#2"}, "down": {"uv": [1, 1, 3, 2], "texture": "#2"} } }, { "from": [7.5, 6.5, 6.5], "to": [8.5, 7.5, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [1, 1, 3, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [0, 2, 2, 3], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [0, 2, 2, 3], "texture": "#2"}, "down": {"uv": [1, 1, 3, 2], "texture": "#2"} } }, { "from": [6.5, 8.5, 6.5], "to": [7.5, 9.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 2, 4, 3], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 1, 4, 2], "texture": "#2"}, "up": {"uv": [0, 1, 4, 2], "texture": "#2"}, "down": {"uv": [0, 2, 4, 3], "texture": "#2"} } }, { "from": [6.5, 7.5, 6.5], "to": [7.5, 8.5, 7.5], "faces": { "north": {"uv": [0, 0, 1, 2], "texture": "#2"}, "east": {"uv": [0, 3, 1, 5], "texture": "#2"}, "south": {"uv": [0, 3, 1, 5], "texture": "#2"}, "west": {"uv": [0, 0, 1, 2], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [8.5, 7.5, 6.5], "to": [9.5, 8.5, 7.5], "faces": { "north": {"uv": [0, 0, 1, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 2], "texture": "#2"}, "south": {"uv": [0, 3, 1, 5], "texture": "#2"}, "west": {"uv": [0, 3, 1, 5], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [8.5, 7.5, 8.5], "to": [9.5, 8.5, 9.5], "faces": { "north": {"uv": [0, 3, 1, 5], "texture": "#2"}, "east": {"uv": [0, 0, 1, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 2], "texture": "#2"}, "west": {"uv": [0, 3, 1, 5], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [6.5, 7.5, 8.5], "to": [7.5, 8.5, 9.5], "faces": { "north": {"uv": [0, 3, 1, 5], "texture": "#2"}, "east": {"uv": [0, 3, 1, 5], "texture": "#2"}, "south": {"uv": [0, 0, 1, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 2], "texture": "#2"}, "up": {"uv": [0, 0, 1, 1], "texture": "#2"}, "down": {"uv": [0, 0, 1, 1], "texture": "#2"} } }, { "from": [7.5, 8.5, 8.5], "to": [8.5, 9.5, 9.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [0, 2, 2, 3], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [1, 1, 3, 2], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [1, 1, 3, 2], "texture": "#2"}, "down": {"uv": [0, 2, 2, 3], "texture": "#2"} } }, { "from": [7.5, 8.5, 6.5], "to": [8.5, 9.5, 7.5], "rotation": {"angle": 0, "axis": "y", "origin": [6.5, 8, 8]}, "faces": { "north": {"uv": [1, 1, 3, 2], "texture": "#2"}, "east": {"uv": [0, 0, 1, 1], "texture": "#2"}, "south": {"uv": [0, 2, 2, 3], "texture": "#2"}, "west": {"uv": [0, 0, 1, 1], "texture": "#2"}, "up": {"uv": [1, 1, 3, 2], "texture": "#2"}, "down": {"uv": [0, 2, 2, 3], "texture": "#2"} } }, { "from": [8.5, 6.5, 6.5], "to": [9.5, 7.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 1, 4, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 2, 4, 3], "texture": "#2"}, "up": {"uv": [0, 2, 4, 3], "texture": "#2"}, "down": {"uv": [0, 1, 4, 2], "texture": "#2"} } }, { "from": [8.5, 8.5, 6.5], "to": [9.5, 9.5, 9.5], "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#2"}, "east": {"uv": [0, 1, 4, 2], "texture": "#2"}, "south": {"uv": [0, 0, 1, 1], "texture": "#2"}, "west": {"uv": [0, 2, 4, 3], "texture": "#2"}, "up": {"uv": [0, 1, 4, 2], "texture": "#2"}, "down": {"uv": [0, 2, 4, 3], "texture": "#2"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [2, 2, 2] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/item_pipe_side_mk1.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/item_pipe_side_mk1", "particle": "indrev:block/item_pipe_side_mk1" }, "elements": [ { "from": [6.51, 0, 8.51], "to": [7.49, 6.5, 9.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [6.51, 0, 6.51], "to": [7.49, 6.5, 7.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [8.51, 0, 8.51], "to": [9.49, 6.5, 9.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [8.51, 0, 6.51], "to": [9.49, 6.5, 7.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/item_pipe_side_mk2.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/item_pipe_side_mk2", "particle": "indrev:block/item_pipe_side_mk2" }, "elements": [ { "from": [6.51, 0, 8.51], "to": [7.49, 6.5, 9.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [6.51, 0, 6.51], "to": [7.49, 6.5, 7.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [8.51, 0, 8.51], "to": [9.49, 6.5, 9.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [8.51, 0, 6.51], "to": [9.49, 6.5, 7.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/item_pipe_side_mk3.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/item_pipe_side_mk3", "particle": "indrev:block/item_pipe_side_mk3" }, "elements": [ { "from": [6.51, 0, 8.51], "to": [7.49, 6.5, 9.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [6.51, 0, 6.51], "to": [7.49, 6.5, 7.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [8.51, 0, 8.51], "to": [9.49, 6.5, 9.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [8.51, 0, 6.51], "to": [9.49, 6.5, 7.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/item_pipe_side_mk4.json ================================================ { "credit": "Made with Blockbench", "textures": { "1": "indrev:block/item_pipe_side_mk4", "particle": "indrev:block/item_pipe_side_mk4" }, "elements": [ { "from": [6.51, 0, 8.51], "to": [7.49, 6.5, 9.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [6.51, 0, 6.51], "to": [7.49, 6.5, 7.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [8.51, 0, 8.51], "to": [9.49, 6.5, 9.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } }, { "from": [8.51, 0, 6.51], "to": [9.49, 6.5, 7.49], "rotation": {"angle": 0, "axis": "z", "origin": [8, 2.625, 8]}, "faces": { "north": {"uv": [1, 0, 2, 6], "texture": "#1"}, "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, "south": {"uv": [3, 0, 4, 6], "texture": "#1"}, "west": {"uv": [2, 0, 3, 6], "texture": "#1"}, "up": {"uv": [1, 1, 0, 0], "texture": "#1"}, "down": {"uv": [12, 4, 8, 8], "texture": "#1"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/laser.json ================================================ { "credit": "Made with Blockbench", "texture_size": [64, 64], "parent": "block/block", "textures": { "5": "indrev:block/laser", "particle": "indrev:block/laser" }, "elements": [ { "from": [0, 0, 3], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 4, 4], "texture": "#5"}, "east": {"uv": [4, 4, 7.25, 8], "texture": "#5"}, "south": {"uv": [0, 4, 4, 8], "texture": "#5"}, "west": {"uv": [4, 0, 7.25, 4], "texture": "#5"}, "up": {"uv": [11.25, 8, 7.25, 4.75], "texture": "#5"}, "down": {"uv": [11.25, 0, 7.25, 3.25], "texture": "#5"} } }, { "from": [6, 6, 0], "to": [10, 10, 2], "rotation": {"angle": 0, "axis": "y", "origin": [6, 8, 4]}, "faces": { "north": {"uv": [3.5, 11.5, 4.5, 12.5], "texture": "#5"}, "east": {"uv": [3, 11.5, 3.5, 12.5], "texture": "#5"}, "west": {"uv": [4.5, 11.5, 5, 12.5], "texture": "#5"}, "up": {"uv": [4.5, 11.5, 3.5, 11], "texture": "#5"}, "down": {"uv": [4.5, 11, 3.5, 11.5], "texture": "#5"} } }, { "from": [5, 11, 1], "to": [11, 15, 4], "rotation": {"angle": 22.5, "axis": "x", "origin": [8, 13.5, 2]}, "faces": { "north": {"uv": [3.25, 9.5, 4.75, 10.5], "texture": "#5"}, "east": {"uv": [2.5, 9.5, 3.25, 10.5], "texture": "#5"}, "west": {"uv": [4.75, 9.5, 5.5, 10.5], "texture": "#5"}, "up": {"uv": [4.75, 9.5, 3.25, 8.75], "texture": "#5"}, "down": {"uv": [4.75, 13, 3.25, 13.75], "texture": "#5"} } }, { "from": [5, 1, 1], "to": [11, 5, 4], "rotation": {"angle": -22.5, "axis": "x", "origin": [8, 2.5, 2]}, "faces": { "north": {"uv": [3.25, 13.5, 4.75, 14.5], "texture": "#5"}, "east": {"uv": [2.5, 13.5, 3.25, 14.5], "texture": "#5"}, "west": {"uv": [4.75, 13.5, 5.5, 14.5], "texture": "#5"}, "up": {"uv": [4.75, 13.75, 3.25, 13], "texture": "#5"}, "down": {"uv": [4.75, 14.75, 3.25, 15.5], "texture": "#5"} } }, { "from": [1, 5, 1], "to": [5, 11, 4], "rotation": {"angle": 22.5, "axis": "y", "origin": [2.5, 8, 2]}, "faces": { "north": {"uv": [5.75, 11.25, 6.75, 12.75], "texture": "#5"}, "east": {"uv": [5, 11.25, 5.75, 12.75], "texture": "#5"}, "west": {"uv": [6.75, 11.25, 7.5, 12.75], "texture": "#5"}, "up": {"uv": [6.5, 11.25, 5.5, 10.5], "texture": "#5"}, "down": {"uv": [6.5, 12.75, 5.5, 13.5], "texture": "#5"} } }, { "from": [11, 5, 1], "to": [15, 11, 4], "rotation": {"angle": -22.5, "axis": "y", "origin": [13.5, 8, 2]}, "faces": { "north": {"uv": [1.25, 11.25, 2.25, 12.75], "texture": "#5"}, "east": {"uv": [0.5, 11.25, 1.25, 12.75], "texture": "#5"}, "west": {"uv": [2.25, 11.25, 3, 12.75], "texture": "#5"}, "up": {"uv": [2.5, 11.25, 1.5, 10.5], "texture": "#5"}, "down": {"uv": [2.5, 12.75, 1.5, 13.5], "texture": "#5"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/laser_on.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "texture_size": [32, 32], "textures": { "1": "indrev:block/laser_emissive", "5": "indrev:block/laser_on", "particle": "indrev:block/laser_on" }, "elements": [ { "from": [0, 0, 3], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 4, 4], "texture": "#5"}, "east": {"uv": [4, 4, 7.25, 8], "texture": "#5"}, "south": {"uv": [0, 4, 4, 8], "texture": "#5"}, "west": {"uv": [4, 0, 7.25, 4], "texture": "#5"}, "up": {"uv": [11.25, 8, 7.25, 4.75], "texture": "#5"}, "down": {"uv": [11.25, 0, 7.25, 3.25], "texture": "#5"} } }, { "from": [6, 6, 0], "to": [10, 10, 2], "rotation": {"angle": 0, "axis": "y", "origin": [6, 8, 4]}, "faces": { "north": {"uv": [3, 11, 5, 13], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "east": {"uv": [2, 11, 3, 13], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "west": {"uv": [5, 11, 6, 13], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "up": {"uv": [5, 11, 3, 10], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"}, "down": {"uv": [5, 10, 3, 11], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"} } }, { "from": [5, 11, 1], "to": [11, 15, 4], "rotation": {"angle": 22.5, "axis": "x", "origin": [8, 13.5, 2]}, "faces": { "north": {"uv": [3.25, 9.5, 4.75, 10.5], "texture": "#5"}, "east": {"uv": [2.5, 9.5, 3.25, 10.5], "texture": "#5"}, "west": {"uv": [4.75, 9.5, 5.5, 10.5], "texture": "#5"}, "up": {"uv": [4.75, 9.5, 3.25, 8.75], "texture": "#5"}, "down": {"uv": [4.75, 13, 3.25, 13.75], "texture": "#5"} } }, { "from": [5, 1, 1], "to": [11, 5, 4], "rotation": {"angle": -22.5, "axis": "x", "origin": [8, 2.5, 2]}, "faces": { "north": {"uv": [3.25, 13.5, 4.75, 14.5], "texture": "#5"}, "east": {"uv": [2.5, 13.5, 3.25, 14.5], "texture": "#5"}, "west": {"uv": [4.75, 13.5, 5.5, 14.5], "texture": "#5"}, "up": {"uv": [4.75, 13.75, 3.25, 13], "texture": "#5"}, "down": {"uv": [4.75, 14.75, 3.25, 15.5], "texture": "#5"} } }, { "from": [1, 5, 1], "to": [5, 11, 4], "rotation": {"angle": 22.5, "axis": "y", "origin": [2.5, 8, 2]}, "faces": { "north": {"uv": [5.75, 11.25, 6.75, 12.75], "texture": "#5"}, "east": {"uv": [5, 11.25, 5.75, 12.75], "texture": "#5"}, "west": {"uv": [6.75, 11.25, 7.5, 12.75], "texture": "#5"}, "up": {"uv": [6.5, 11.25, 5.5, 10.5], "texture": "#5"}, "down": {"uv": [6.5, 12.75, 5.5, 13.5], "texture": "#5"} } }, { "from": [11, 5, 1], "to": [15, 11, 4], "rotation": {"angle": -22.5, "axis": "y", "origin": [13.5, 8, 2]}, "faces": { "north": {"uv": [1.25, 11.25, 2.25, 12.75], "texture": "#5"}, "east": {"uv": [0.5, 11.25, 1.25, 12.75], "texture": "#5"}, "west": {"uv": [2.25, 11.25, 3, 12.75], "texture": "#5"}, "up": {"uv": [2.5, 11.25, 1.5, 10.5], "texture": "#5"}, "down": {"uv": [2.5, 12.75, 1.5, 13.5], "texture": "#5"} } }, { "from": [4, 4, 2.95], "to": [12, 12, 2.95], "rotation": {"angle": 0, "axis": "y", "origin": [12, 12, 9.975]}, "faces": { "north": {"uv": [2, 2, 6, 6], "texture": "#1", "jmx_tex0": "#1", "jmx_material": "#jmx_0"} } } ], "jmx": { "textures": { "1": "indrev:block/laser_emissive", "5": "indrev:block/laser_on", "particle": "indrev:block/laser_on" }, "materials": { "jmx_0": { "layer0": "cutout", "emissive0": true, "ambient_occlusion0": false, "diffuse0": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/lazuli_flux_container.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "texture_size": [64, 64], "textures": { "particle": "indrev:block/lazuli_flux_container", "texture": "indrev:block/lazuli_flux_container" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 8, 8, 16], "texture": "#texture"}, "east": {"uv": [0, 8, 8, 16], "texture": "#texture"}, "south": {"uv": [0, 8, 8, 16], "texture": "#texture"}, "west": {"uv": [0, 8, 8, 16], "texture": "#texture"}, "up": {"uv": [8, 8, 0, 0], "texture": "#texture"}, "down": {"uv": [16, 0, 8, 8], "texture": "#texture"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/lazuli_flux_container_input.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "texture": "indrev:block/lazuli_flux_container_input" }, "elements": [ { "from": [0, 0, -0.01], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#texture"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/lazuli_flux_container_item_lf_level.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "texture": "indrev:block/lazuli_flux_container_item_lf_level" }, "elements": [ { "from": [0, 0, -0.01], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#texture"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/lazuli_flux_container_mk1_overlay.json ================================================ { "textures": { "texture": "indrev:block/lazuli_flux_container_mk1_overlay" }, "elements": [ { "from": [0, 0, -0.01], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#texture", "jmx_tex1": "#texture", "jmx_material": "#jmx_1"} } } ], "jmx": { "textures": { "texture": "indrev:block/lazuli_flux_container_mk1_overlay" }, "materials": { "jmx_1": { "layer1": "cutout", "emissive1": true, "ambient_occlusion1": false, "diffuse1": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/lazuli_flux_container_mk2_overlay.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "texture": "indrev:block/lazuli_flux_container_mk2_overlay" }, "elements": [ { "from": [0, 0, -0.01], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#texture", "jmx_tex1": "#texture", "jmx_material": "#jmx_1"} } } ], "jmx": { "textures": { "texture": "indrev:block/lazuli_flux_container_mk2_overlay" }, "materials": { "jmx_1": { "layer1": "cutout", "emissive1": true, "ambient_occlusion1": false, "diffuse1": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/lazuli_flux_container_mk3_overlay.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "texture": "indrev:block/lazuli_flux_container_mk3_overlay" }, "elements": [ { "from": [0, 0, -0.01], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#texture", "jmx_tex1": "#texture", "jmx_material": "#jmx_1"} } } ], "jmx": { "textures": { "texture": "indrev:block/lazuli_flux_container_mk3_overlay" }, "materials": { "jmx_1": { "layer1": "cutout", "emissive1": true, "ambient_occlusion1": false, "diffuse1": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/lazuli_flux_container_mk4_overlay.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "texture": "indrev:block/lazuli_flux_container_mk4_overlay" }, "elements": [ { "from": [0, 0, -0.01], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#texture", "jmx_tex1": "#texture", "jmx_material": "#jmx_1"} } } ], "jmx": { "textures": { "texture": "indrev:block/lazuli_flux_container_mk4_overlay" }, "materials": { "jmx_1": { "layer1": "cutout", "emissive1": true, "ambient_occlusion1": false, "diffuse1": false } } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/lazuli_flux_container_output.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "texture": "indrev:block/lazuli_flux_container_output" }, "elements": [ { "from": [0, 0, -0.01], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#texture"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/lead_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/lead_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/lead_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/lead_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/machine.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "textures": { "particle": "minecraft:block/iron_block" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 16, 16 ], "faces": { "north": { "uv": [ 5.333, 5.333, 10.666, 10.666 ], "texture": "#texture", "cullface": "north" }, "east": { "uv": [ 0, 5.333, 5.332, 10.666 ], "texture": "#texture", "cullface": "east" }, "south": { "uv": [ 10.667, 10.667, 16, 15.999 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 10.667, 5.333, 16, 10.665 ], "texture": "#texture", "cullface": "west" }, "up": { "uv": [ 5.333, 0, 10.666, 5.333 ], "texture": "#texture", "cullface": "up" }, "down": { "uv": [ 5.333, 10.667, 10.666, 15.998 ], "texture": "#texture", "cullface": "down" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/machine_block.json ================================================ { "parent": "indrev:block/machine", "textures": { "texture": "indrev:block/machine_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/modular_workbench_mk4.json ================================================ { "credit": "Made with Blockbench", "parent": "block/cube_all", "textures": { "0": "indrev:block/machine_block", "2": "indrev:block/capsule", "particle": "indrev:block/machine_block" }, "elements": [ { "from": [0, 0, 0], "to": [16, 1, 16], "faces": { "north": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "east": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "south": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "west": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "up": {"uv": [5.33333, 10.66667, 10.66667, 16], "texture": "#0"}, "down": {"uv": [5.33333, 10.66667, 10.66667, 16], "texture": "#0"} } }, { "from": [1, 1, 1], "to": [15, 2, 15], "faces": { "north": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "east": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "south": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "west": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "up": {"uv": [5.66667, 11, 10.33333, 15.66667], "texture": "#0"}, "down": {"uv": [5.66667, 11, 10.33333, 15.66667], "texture": "#0"} } }, { "from": [0, 15, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "east": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "south": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "west": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "up": {"uv": [5.33333, 10.66667, 10.66667, 16], "texture": "#0"}, "down": {"uv": [5.33333, 10.66667, 10.66667, 16], "texture": "#0"} } }, { "from": [1, 14, 1], "to": [15, 15, 15], "faces": { "north": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "east": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "south": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "west": {"uv": [5.33333, 15.66667, 10.66667, 16], "texture": "#0"}, "up": {"uv": [5.66667, 11, 10.33333, 15.66667], "texture": "#0"}, "down": {"uv": [5.66667, 11, 10.33333, 15.66667], "texture": "#0"} } }, { "from": [2, 2, 13], "to": [3, 14, 14], "faces": { "north": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "east": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "south": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "west": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "up": {"uv": [0, 0, 1, 1], "texture": "#0"}, "down": {"uv": [0, 0, 1, 1], "texture": "#0"} } }, { "from": [13, 2, 2], "to": [14, 14, 3], "faces": { "north": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "east": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "south": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "west": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "up": {"uv": [0, 0, 1, 1], "texture": "#0"}, "down": {"uv": [0, 0, 1, 1], "texture": "#0"} } }, { "from": [13, 2, 13], "to": [14, 14, 14], "faces": { "north": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "east": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "south": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "west": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "up": {"uv": [0, 0, 1, 1], "texture": "#0"}, "down": {"uv": [0, 0, 1, 1], "texture": "#0"} } }, { "from": [2, 2, 2], "to": [3, 14, 3], "faces": { "north": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "east": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "south": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "west": {"uv": [0, 10.33333, 5.33333, 10.66667], "texture": "#0"}, "up": {"uv": [0, 0, 1, 1], "texture": "#0"}, "down": {"uv": [0, 0, 1, 1], "texture": "#0"} } }, { "from": [3, 2, 3], "to": [13, 14, 13], "faces": { "north": {"uv": [3, 2, 13, 14], "texture": "#2"}, "east": {"uv": [3, 2, 13, 14], "texture": "#2"}, "south": {"uv": [3, 2, 13, 14], "texture": "#2"}, "west": {"uv": [3, 2, 13, 14], "texture": "#2"}, "up": {"uv": [3, 2, 13, 14], "texture": "#2"}, "down": {"uv": [3, 2, 13, 14], "texture": "#2"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/netherite_drill_head.json ================================================ { "credit": "Made with Blockbench", "texture_size": [32, 32], "textures": { "0": "indrev:item/netherite_drill_head", "particle": "indrev:item/netherite_drill_head" }, "elements": [ { "from": [2.5, 6, 8.01], "to": [14.5, 18, 8.01], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 8.01]}, "faces": { "south": {"uv": [2, 3, 13, 14], "rotation": 270, "texture": "#0"} } }, { "from": [2.5, 6, 7.99], "to": [14.5, 18, 7.99], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 7.99]}, "faces": { "north": {"uv": [2, 3, 13, 14], "rotation": 180, "texture": "#0"} } }, { "from": [7.99, 6, 2.5], "to": [7.99, 18, 14.5], "rotation": {"angle": -45, "axis": "x", "origin": [7.99, 11.5, 8]}, "faces": { "west": {"uv": [2, 3, 13, 14], "rotation": 270, "texture": "#0"} } }, { "from": [8.01, 6, 2.5], "to": [8.01, 18, 14.5], "rotation": {"angle": -45, "axis": "x", "origin": [8.01, 11.5, 8]}, "faces": { "east": {"uv": [2, 3, 13, 14], "rotation": 180, "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/nikolite_ore.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "indrev:block/nikolite_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/ore_base.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "indrev:block/ore_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/ore_highlight.json ================================================ { "parent": "minecraft:block/cube_all", "textures": { "all": "indrev:block/ore_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/plank_block.json ================================================ { "parent": "minecraft:block/cube", "textures": { "up": "block/oak_planks", "down": "block/oak_planks", "north": "indrev:block/plank_side", "south": "indrev:block/plank_side", "east": "indrev:block/plank_side", "west": "indrev:block/plank_side", "particle": "indrev:block/plank_side" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/planks_height10.json ================================================ { "parent": "block/thin_block", "textures": { "particle": "indrev:block/plank_side", "texture": "indrev:block/plank_side", "top": "block/oak_planks" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 10, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#top", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, "north": { "uv": [ 0, 6, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 6, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 6, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 6, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/planks_height12.json ================================================ { "parent": "block/thin_block", "textures": { "particle": "indrev:block/plank_side", "texture": "indrev:block/plank_side", "top": "block/oak_planks" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 12, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#top", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, "north": { "uv": [ 0, 4, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 4, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 4, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 4, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/planks_height14.json ================================================ { "parent": "block/thin_block", "textures": { "particle": "indrev:block/plank_side", "texture": "indrev:block/plank_side", "top": "block/oak_planks" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 14, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#top", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, "north": { "uv": [ 0, 2, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 2, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 2, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 2, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/planks_height2.json ================================================ { "parent": "block/thin_block", "textures": { "particle": "indrev:block/plank_side", "texture": "indrev:block/plank_side", "top": "block/oak_planks" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 2, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#top", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, "north": { "uv": [ 0, 14, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 14, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 14, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 14, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/planks_height4.json ================================================ { "parent": "block/thin_block", "textures": { "particle": "indrev:block/plank_side", "texture": "indrev:block/plank_side", "top": "block/oak_planks" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 4, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#top", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, "north": { "uv": [ 0, 12, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 12, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 12, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 12, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/planks_height6.json ================================================ { "parent": "block/thin_block", "textures": { "particle": "indrev:block/plank_side", "texture": "indrev:block/plank_side", "top": "block/oak_planks" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 6, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#top", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, "north": { "uv": [ 0, 10, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 10, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 10, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 10, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/planks_height8.json ================================================ { "parent": "block/thin_block", "textures": { "particle": "indrev:block/plank_side", "texture": "indrev:block/plank_side", "top": "block/oak_planks" }, "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 8, 16 ], "faces": { "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#top", "cullface": "down" }, "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, "north": { "uv": [ 0, 8, 16, 16 ], "texture": "#texture", "cullface": "north" }, "south": { "uv": [ 0, 8, 16, 16 ], "texture": "#texture", "cullface": "south" }, "west": { "uv": [ 0, 8, 16, 16 ], "texture": "#texture", "cullface": "west" }, "east": { "uv": [ 0, 8, 16, 16 ], "texture": "#texture", "cullface": "east" } } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/pump_mk1.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "particle": "indrev:block/pump", "texture": "indrev:block/pump" }, "elements": [ { "name": "body", "from": [6, 0, 6], "to": [10, 11, 10], "rotation": {"angle": 0, "axis": "y", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4, 0, 6, 5.5], "texture": "#texture"}, "east": {"uv": [4, 0, 6, 5.5], "texture": "#texture"}, "south": {"uv": [4, 0, 6, 5.5], "texture": "#texture"}, "west": {"uv": [4, 0, 6, 5.5], "texture": "#texture"}, "up": {"uv": [3.999, 1.999, 2.001, 0.001], "texture": "#texture"}, "down": {"uv": [4, 5.5, 2, 7.5], "texture": "#texture"} } }, { "name": "motor", "from": [5, 11, 5], "to": [11, 16, 11], "rotation": {"angle": 0, "axis": "y", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [6, 0, 9.5, 2.5], "texture": "#texture"}, "east": {"uv": [6, 0, 9.5, 2.5], "texture": "#texture"}, "south": {"uv": [6, 0, 9.5, 2.5], "texture": "#texture"}, "west": {"uv": [6, 0, 9.5, 2.5], "texture": "#texture"}, "up": {"uv": [9, 5.5, 6, 2.5], "texture": "#texture"}, "down": {"uv": [9, 5.5, 6, 8.5], "texture": "#texture"} } }, { "name": "outlet", "from": [10, 6, 6], "to": [15, 10, 10], "rotation": {"angle": 0, "axis": "y", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4.5, 8.5, 7, 10.5], "texture": "#texture"}, "east": {"uv": [5, 11.5, 7, 13.5], "texture": "#texture"}, "south": {"uv": [2, 8.5, 4.5, 10.5], "texture": "#texture"}, "west": {"uv": [7, 11.5, 9, 13.5], "texture": "#texture"}, "up": {"uv": [9.5, 10.5, 7, 8.5], "texture": "#texture"}, "down": {"uv": [4.5, 10.5, 2, 12.5], "texture": "#texture"} } }, { "name": "backplate", "from": [15, 5, 5], "to": [16, 11, 11], "rotation": {"angle": 0, "axis": "y", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [9.5, 2.5, 10, 5.5], "texture": "#texture"}, "east": {"uv": [9, 5.5, 6, 2.5], "texture": "#texture"}, "south": {"uv": [9.5, 2.5, 10, 5.5], "texture": "#texture"}, "west": {"uv": [6, 5.5, 9, 8.5], "texture": "#texture"}, "up": {"uv": [10.5, 5.5, 10, 2.5], "texture": "#texture"}, "down": {"uv": [11, 2.5, 10.5, 5.5], "texture": "#texture"} } }, { "name": "support", "from": [10, 3, 8], "to": [12, 6, 8], "rotation": {"angle": 0, "axis": "y", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [9, 5.5, 10, 7], "texture": "#texture"}, "east": {"uv": [0, 0, 0, 1.5], "texture": "#texture"}, "south": {"uv": [10, 5.5, 11, 7], "texture": "#texture"}, "west": {"uv": [0, 0, 0, 1.5], "texture": "#texture"}, "up": {"uv": [1, 0, 0, 0], "texture": "#texture"}, "down": {"uv": [1, 0, 0, 0], "texture": "#texture"} } }, { "from": [3.30395, 4.79883, 6.99134], "to": [5.30395, 9.79883, 6.99134], "rotation": {"angle": 22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [5, 5.5, 6, 8], "texture": "#texture"}, "south": {"uv": [4, 5.5, 5, 8], "texture": "#texture"} } }, { "from": [3.30395, 9.79883, 6.99134], "to": [5.30395, 9.79883, 8.99134], "rotation": {"angle": 22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "up": {"uv": [4.5, 1.5, 5.5, 2.5], "rotation": 90, "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "rotation": 270, "texture": "#texture"} } }, { "from": [3.30395, 4.89883, 6.99134], "to": [5.30395, 4.89883, 8.99134], "rotation": {"angle": 22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "up": {"uv": [4.5, 2, 5, 2.5], "rotation": 90, "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "rotation": 270, "texture": "#texture"} } }, { "from": [5.30395, 4.79883, 6.99134], "to": [5.30395, 9.79883, 8.99134], "rotation": {"angle": 22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "east": {"uv": [5, 5.5, 6, 8], "texture": "#texture"}, "west": {"uv": [4, 5.5, 5, 8], "texture": "#texture"} } }, { "from": [3.30395, 4.79883, 6.99134], "to": [3.30395, 9.79883, 8.99134], "rotation": {"angle": 22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "east": {"uv": [4, 5.5, 5, 8], "texture": "#texture"}, "west": {"uv": [5, 5.5, 6, 8], "texture": "#texture"} } }, { "from": [3.30395, 4.79883, 8.99134], "to": [5.30395, 9.79883, 8.99134], "rotation": {"angle": 22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4, 5.5, 5, 8], "texture": "#texture"}, "south": {"uv": [5, 5.5, 6, 8], "texture": "#texture"} } }, { "name": "inner", "from": [3.35395, 4.74883, 7.04134], "to": [5.25395, 9.64883, 8.94134], "rotation": {"angle": 22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "east": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "south": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "west": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "up": {"uv": [4.5, 10.5, 5.5, 11.5], "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "rotation": 270, "texture": "#texture"} } }, { "from": [9, 4.92858, 3.3211], "to": [9, 9.92858, 5.3211], "rotation": {"angle": -22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "east": {"uv": [5, 5.5, 6, 8], "texture": "#texture"}, "west": {"uv": [4, 5.5, 5, 8], "texture": "#texture"} } }, { "from": [7, 9.92858, 3.3211], "to": [9, 9.92858, 5.3211], "rotation": {"angle": -22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "up": {"uv": [4.5, 1.5, 5.5, 2.5], "rotation": 180, "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "rotation": 180, "texture": "#texture"} } }, { "from": [7, 5.02858, 3.3211], "to": [9, 5.02858, 5.3211], "rotation": {"angle": -22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "up": {"uv": [4.5, 2, 5, 2.5], "rotation": 180, "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "rotation": 180, "texture": "#texture"} } }, { "from": [7, 4.92858, 5.3211], "to": [9, 9.92858, 5.3211], "rotation": {"angle": -22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4, 5.5, 5, 8], "texture": "#texture"}, "south": {"uv": [5, 5.5, 6, 8], "texture": "#texture"} } }, { "from": [7, 4.92858, 3.3211], "to": [9, 9.92858, 3.3211], "rotation": {"angle": -22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [5, 5.5, 6, 8], "texture": "#texture"}, "south": {"uv": [4, 5.5, 5, 8], "texture": "#texture"} } }, { "from": [7, 4.92858, 3.3211], "to": [7, 9.92858, 5.3211], "rotation": {"angle": -22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "east": {"uv": [4, 5.5, 5, 8], "texture": "#texture"}, "west": {"uv": [5, 5.5, 6, 8], "texture": "#texture"} } }, { "name": "inner", "from": [7.05, 4.97858, 3.3711], "to": [8.95, 9.87858, 5.2711], "rotation": {"angle": -22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "east": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "south": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "west": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "up": {"uv": [4.5, 10.5, 5.5, 11.5], "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "rotation": 180, "texture": "#texture"} } }, { "from": [7, 4.92637, 10.66202], "to": [7, 9.92637, 12.66202], "rotation": {"angle": 22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "east": {"uv": [4, 5.5, 5, 8], "texture": "#texture"}, "west": {"uv": [5, 5.5, 6, 8], "texture": "#texture"} } }, { "from": [7, 9.92637, 10.66202], "to": [9, 9.92637, 12.66202], "rotation": {"angle": 22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "up": {"uv": [4.5, 1.5, 5.5, 2.5], "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "texture": "#texture"} } }, { "from": [7, 5.02637, 10.66202], "to": [9, 5.02637, 12.66202], "rotation": {"angle": 22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "up": {"uv": [4.5, 2, 5, 2.5], "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "texture": "#texture"} } }, { "from": [7, 4.92637, 10.66202], "to": [9, 9.92637, 10.66202], "rotation": {"angle": 22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [5, 5.5, 6, 8], "texture": "#texture"}, "south": {"uv": [4, 5.5, 5, 8], "texture": "#texture"} } }, { "from": [7, 4.92637, 12.66202], "to": [9, 9.92637, 12.66202], "rotation": {"angle": 22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4, 5.5, 5, 8], "texture": "#texture"}, "south": {"uv": [5, 5.5, 6, 8], "texture": "#texture"} } }, { "from": [9, 4.92637, 10.66202], "to": [9, 9.92637, 12.66202], "rotation": {"angle": 22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "east": {"uv": [5, 5.5, 6, 8], "texture": "#texture"}, "west": {"uv": [4, 5.5, 5, 8], "texture": "#texture"} } }, { "name": "inner", "from": [7.05, 4.97637, 10.71202], "to": [8.95, 9.87637, 12.61202], "rotation": {"angle": 22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "east": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "south": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "west": {"uv": [4.5, 10.5, 5.5, 12.5], "texture": "#texture"}, "up": {"uv": [4.5, 10.5, 5.5, 11.5], "texture": "#texture"}, "down": {"uv": [4.5, 1.5, 5.5, 2.5], "texture": "#texture"} } }, { "from": [6.01, 2.98652, 10.38077], "to": [9.99, 4.96652, 12.76077], "rotation": {"angle": 22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [4, 3, 6, 4], "texture": "#texture"}, "east": {"uv": [1, 7.5, 2, 8.5], "texture": "#texture"}, "south": {"uv": [0, 6.5, 2, 7.5], "texture": "#texture"}, "west": {"uv": [0, 7.5, 1, 8.5], "texture": "#texture"}, "up": {"uv": [2, 6.5, 0, 5.5], "texture": "#texture"}, "down": {"uv": [4, 7.5, 2, 8.5], "texture": "#texture"} } }, { "from": [3.2132, 2.85568, 6.01], "to": [5.5932, 4.83568, 9.99], "rotation": {"angle": 22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [0, 7.5, 1, 8.5], "texture": "#texture"}, "east": {"uv": [5, 13.5, 7, 14.5], "texture": "#texture"}, "south": {"uv": [1, 7.5, 2, 8.5], "texture": "#texture"}, "west": {"uv": [0, 6.5, 2, 7.5], "texture": "#texture"}, "up": {"uv": [1, 10.5, 0, 8.5], "texture": "#texture"}, "down": {"uv": [2, 8.5, 1, 10.5], "texture": "#texture"} } }, { "from": [6.01, 2.98211, 3.23835], "to": [9.99, 4.96211, 5.61835], "rotation": {"angle": -22.5, "axis": "x", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [0, 6.5, 2, 7.5], "texture": "#texture"}, "east": {"uv": [0, 7.5, 1, 8.5], "texture": "#texture"}, "south": {"uv": [14, 5, 16, 6], "texture": "#texture"}, "west": {"uv": [1, 7.5, 2, 8.5], "texture": "#texture"}, "up": {"uv": [2, 7, 0, 6], "texture": "#texture"}, "down": {"uv": [4, 7.5, 2, 8.5], "texture": "#texture"} } }, { "from": [10.36875, 3.11839, 6.01], "to": [12.74875, 5.09839, 9.99], "rotation": {"angle": -22.5, "axis": "z", "origin": [7.66385, 5.81363, 7.99423]}, "faces": { "north": {"uv": [1, 7.5, 2, 8.5], "texture": "#texture"}, "east": {"uv": [0, 6.5, 2, 7.5], "texture": "#texture"}, "south": {"uv": [0, 7.5, 1, 8.5], "texture": "#texture"}, "west": {"uv": [14, 10.5, 16, 11.5], "texture": "#texture"}, "up": {"uv": [1, 12.5, 0, 10.5], "texture": "#texture"}, "down": {"uv": [2, 8.5, 1, 10.5], "texture": "#texture"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 135, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0], "scale": [0.8, 0.8, 0.8] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } }, "groups": [0, 1, 2, 3, 4, { "name": "cylinder", "origin": [9, 5.5, 15], "children": [5, 6, 7, 8, 9, 10, 11] }, { "name": "cylinder", "origin": [9, 5.5, 15], "children": [12, 13, 14, 15, 16, 17, 18] }, { "name": "cylinder", "origin": [9, 5.5, 15], "children": [19, 20, 21, 22, 23, 24, 25] }, { "name": "wings", "origin": [9.6, 3.70711, 8.01421], "children": [26, 27, 28, 29] } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/pump_pipe.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "texture_size": [8, 8], "textures": { "particle": "indrev:block/pump_pipe", "texture": "indrev:block/pump_pipe" }, "elements": [ { "from": [6.1, 0, 6.1], "to": [9.9, 16, 9.9], "rotation": {"angle": 0, "axis": "y", "origin": [16, 12, 14]}, "faces": { "north": {"uv": [0, 0, 4, 16], "texture": "#texture"}, "east": {"uv": [4, 0, 8, 16], "texture": "#texture"}, "south": {"uv": [0, 0, 4, 16], "texture": "#texture"}, "west": {"uv": [4, 0, 8, 16], "texture": "#texture"}, "up": {"uv": [11, 3, 8, 0], "texture": "#texture"}, "down": {"uv": [11, 0, 8, 3], "texture": "#texture"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/raw_lead_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/raw_lead_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/raw_silver_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/raw_silver_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/raw_tin_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/raw_tin_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/raw_tungsten_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/raw_tungsten_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/servo_output.json ================================================ { "credit": "Made with Blockbench", "textures": { "0": "indrev:block/servo_output", "particle": "indrev:block/servo_output" }, "elements": [ { "from": [4.9, -0.9, 4.9], "to": [11.1, 2.1, 11.1], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, "faces": { "north": {"uv": [0, 0, 6, 2], "texture": "#0"}, "east": {"uv": [0, 0, 6, 2], "texture": "#0"}, "south": {"uv": [0, 0, 6, 2], "texture": "#0"}, "west": {"uv": [0, 0, 6, 2], "texture": "#0"}, "up": {"uv": [12, 6, 6, 0], "texture": "#0"}, "down": {"uv": [12, 0, 6, 6], "texture": "#0"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/servo_output_item.json ================================================ { "credit": "Made with Blockbench", "textures": { "0": "indrev:block/servo_output", "particle": "indrev:block/servo_output" }, "elements": [ { "from": [5.75, 0, 5.75], "to": [10.25, 1.5, 10.25], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, "faces": { "north": {"uv": [0, 0, 6, 2], "texture": "#0"}, "east": {"uv": [0, 0, 6, 2], "texture": "#0"}, "south": {"uv": [0, 0, 6, 2], "texture": "#0"}, "west": {"uv": [0, 0, 6, 2], "texture": "#0"}, "up": {"uv": [12, 6, 6, 0], "texture": "#0"}, "down": {"uv": [12, 0, 6, 6], "texture": "#0"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/servo_retriever.json ================================================ { "credit": "Made with Blockbench", "textures": { "0": "indrev:block/servo_retriever", "particle": "indrev:block/servo_retriever" }, "elements": [ { "from": [4.9, -0.9, 4.9], "to": [11.1, 2.1, 11.1], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, "faces": { "north": {"uv": [0, 0, 6, 2], "texture": "#0"}, "east": {"uv": [0, 0, 6, 2], "texture": "#0"}, "south": {"uv": [0, 0, 6, 2], "texture": "#0"}, "west": {"uv": [0, 0, 6, 2], "texture": "#0"}, "up": {"uv": [12, 6, 6, 0], "texture": "#0"}, "down": {"uv": [12, 0, 6, 6], "texture": "#0"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/servo_retriever_item.json ================================================ { "credit": "Made with Blockbench", "textures": { "0": "indrev:block/servo_retriever", "particle": "indrev:block/servo_retriever" }, "elements": [ { "from": [5.75, 0, 5.75], "to": [10.25, 1.5, 10.25], "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, "faces": { "north": {"uv": [0, 0, 6, 2], "texture": "#0"}, "east": {"uv": [0, 0, 6, 2], "texture": "#0"}, "south": {"uv": [0, 0, 6, 2], "texture": "#0"}, "west": {"uv": [0, 0, 6, 2], "texture": "#0"}, "up": {"uv": [12, 6, 6, 0], "texture": "#0"}, "down": {"uv": [12, 0, 6, 6], "texture": "#0"} } } ], "display": { "thirdperson_righthand": { "rotation": [75, 45, 0], "translation": [0, 2.5, 0] }, "firstperson_righthand": { "rotation": [0, 45, 0] }, "firstperson_lefthand": { "rotation": [0, 225, 0] }, "ground": { "translation": [0, 2, 0] }, "gui": { "rotation": [30, 225, 0] }, "head": { "rotation": [0, 180, 0], "translation": [0, 13, 7], "scale": [2, 2, 2] }, "fixed": { "rotation": [0, 180, 0], "scale": [2, 2, 2] } } } ================================================ FILE: src/main/resources/assets/indrev/models/block/silo.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "textures": { "0": "indrev:block/silo", "particle": "indrev:block/silo" }, "elements": [ { "from": [0, 0, 0], "to": [16, 16, 16], "faces": { "north": {"uv": [0, 0, 8, 8], "texture": "#0"}, "east": {"uv": [0, 0, 8, 8], "texture": "#0"}, "south": {"uv": [0, 0, 8, 8], "texture": "#0"}, "west": {"uv": [0, 0, 8, 8], "texture": "#0"}, "up": {"uv": [8, 0, 16, 8], "texture": "#0"}, "down": {"uv": [0, 8, 8, 16], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/silver_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/silver_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/silver_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/silver_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/smelter_mk4.json ================================================ { "parent": "indrev:block/machine_mk4", "textures": { "texture": "indrev:block/smelter" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/solar_generator_mk1.json ================================================ { "parent": "indrev:block/machine_mk1", "textures": { "texture": "indrev:block/solar_generator" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/solar_generator_mk1_on.json ================================================ { "parent": "indrev:block/machine_mk1", "textures": { "texture": "indrev:block/solar_generator_on" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/solar_generator_mk3.json ================================================ { "parent": "indrev:block/machine_mk3", "textures": { "texture": "indrev:block/solar_generator" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/solar_generator_mk3_on.json ================================================ { "parent": "indrev:block/machine_mk3", "textures": { "texture": "indrev:block/solar_generator_on" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/steel_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/steel_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/stone_drill_head.json ================================================ { "credit": "Made with Blockbench", "texture_size": [32, 32], "textures": { "0": "indrev:item/stone_drill_head", "particle": "indrev:item/stone_drill_head" }, "elements": [ { "from": [2.5, 6, 8.01], "to": [14.5, 18, 8.01], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 8.01]}, "faces": { "south": {"uv": [2, 3, 13, 14], "rotation": 270, "texture": "#0"} } }, { "from": [2.5, 6, 7.99], "to": [14.5, 18, 7.99], "rotation": {"angle": 45, "axis": "z", "origin": [8, 11.5, 7.99]}, "faces": { "north": {"uv": [2, 3, 13, 14], "rotation": 180, "texture": "#0"} } }, { "from": [7.99, 6, 2.5], "to": [7.99, 18, 14.5], "rotation": {"angle": -45, "axis": "x", "origin": [7.99, 11.5, 8]}, "faces": { "west": {"uv": [2, 3, 13, 14], "rotation": 270, "texture": "#0"} } }, { "from": [8.01, 6, 2.5], "to": [8.01, 18, 14.5], "rotation": {"angle": -45, "axis": "x", "origin": [8.01, 11.5, 8]}, "faces": { "east": {"uv": [2, 3, 13, 14], "rotation": 180, "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/sulfur_crystal.json ================================================ { "credit": "Made with Blockbench", "textures": { "0": "indrev:block/sulfur_crystal", "particle": "indrev:block/sulfur_crystal" }, "elements": [ { "from": [10, 0, 1], "to": [12, 1, 3], "rotation": {"angle": 0, "axis": "y", "origin": [18, 8, 9]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [11, 0, 2], "to": [14, 4, 5], "rotation": {"angle": 0, "axis": "y", "origin": [19, 9, 10]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [11, 4, 2], "to": [13, 5, 4], "rotation": {"angle": 0, "axis": "y", "origin": [19, 13, 10]}, "faces": { "north": {"uv": [0, 0, 2, 1], "texture": "#0"}, "east": {"uv": [0, 0, 2, 1], "texture": "#0"}, "south": {"uv": [0, 0, 2, 1], "texture": "#0"}, "west": {"uv": [0, 0, 2, 1], "texture": "#0"}, "up": {"uv": [0, 0, 2, 2], "texture": "#0"}, "down": {"uv": [0, 0, 2, 2], "texture": "#0"} } }, { "from": [12, 5, 3], "to": [13, 6, 4], "rotation": {"angle": 0, "axis": "y", "origin": [20, 14, 11]}, "faces": { "north": {"uv": [0, 0, 16, 3], "texture": "#0"}, "east": {"uv": [0, 0, 16, 3], "texture": "#0"}, "south": {"uv": [0, 0, 16, 3], "texture": "#0"}, "west": {"uv": [0, 0, 16, 3], "texture": "#0"}, "up": {"uv": [0, 0, 16, 3], "texture": "#0"}, "down": {"uv": [0, 0, 16, 3], "texture": "#0"} } }, { "from": [13, 0, 4], "to": [15, 2, 6], "rotation": {"angle": 0, "axis": "y", "origin": [21, 9, 12]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 2, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [3, 0, 2], "to": [5, 1, 4], "rotation": {"angle": 0, "axis": "y", "origin": [11, 8, 10]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [1, 0, 3], "to": [4, 4, 6], "rotation": {"angle": 0, "axis": "y", "origin": [9, 9, 11]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [2, 4, 4], "to": [4, 5, 6], "rotation": {"angle": 0, "axis": "y", "origin": [10, 13, 12]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 2, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [2, 5, 4], "to": [3, 6, 5], "rotation": {"angle": 0, "axis": "y", "origin": [10, 14, 12]}, "faces": { "north": {"uv": [0, 0, 16, 3], "texture": "#0"}, "east": {"uv": [0, 0, 16, 3], "texture": "#0"}, "south": {"uv": [0, 0, 16, 3], "texture": "#0"}, "west": {"uv": [0, 0, 16, 3], "texture": "#0"}, "up": {"uv": [0, 0, 16, 3], "texture": "#0"}, "down": {"uv": [0, 0, 16, 3], "texture": "#0"} } }, { "from": [0, 0, 5], "to": [2, 2, 7], "rotation": {"angle": 0, "axis": "y", "origin": [8, 9, 13]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [6, 0, 7], "to": [8, 2, 10], "rotation": {"angle": 0, "axis": "y", "origin": [14, 8, 15]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [7, 0, 8], "to": [12, 5, 13], "rotation": {"angle": 0, "axis": "y", "origin": [15, 9, 16]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [8, 5, 9], "to": [11, 8, 12], "rotation": {"angle": 0, "axis": "y", "origin": [16, 14, 17]}, "faces": { "north": {"uv": [0, 0, 16, 16], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } }, { "from": [9, 8, 10], "to": [10, 10, 11], "rotation": {"angle": 0, "axis": "y", "origin": [17, 17, 18]}, "faces": { "north": {"uv": [0, 0, 16, 3], "texture": "#0"}, "east": {"uv": [0, 0, 16, 3], "texture": "#0"}, "south": {"uv": [0, 0, 16, 3], "texture": "#0"}, "west": {"uv": [0, 0, 16, 3], "texture": "#0"}, "up": {"uv": [0, 0, 16, 3], "texture": "#0"}, "down": {"uv": [0, 0, 16, 3], "texture": "#0"} } }, { "from": [11, 0, 12], "to": [14, 3, 15], "rotation": {"angle": 0, "axis": "y", "origin": [19, 9, 20]}, "faces": { "north": {"uv": [0, 0, 1, 1], "texture": "#0"}, "east": {"uv": [0, 0, 16, 16], "texture": "#0"}, "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, "west": {"uv": [0, 0, 16, 16], "texture": "#0"}, "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, "down": {"uv": [0, 0, 16, 16], "texture": "#0"} } } ], "display": {}, "groups": [ { "name": "VoxelShapes", "origin": [19, 9, 20], "children": [ { "name": "group", "origin": [19, 9, 20], "children": [0, 1, 2, 3, 4] }, { "name": "group", "origin": [19, 9, 20], "children": [5, 6, 7, 8, 9] }, { "name": "group", "origin": [19, 9, 20], "children": [10, 11, 12, 13, 14] } ] } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/tank.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "textures": { "3": "minecraft:block/glass", "5": "indrev:block/machine_block", "particle": "minecraft:block/glass" }, "elements": [ { "from": [1, 14, 1], "to": [15, 15, 15], "rotation": {"angle": 0, "axis": "y", "origin": [17, 20, 14]}, "faces": { "north": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "east": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "south": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "west": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "up": {"uv": [5.333, 10.667, 10.666, 16], "texture": "#5"}, "down": {"uv": [5.333, 10.667, 10.666, 16], "texture": "#5"} } }, { "from": [1, 0, 1], "to": [15, 1, 15], "rotation": {"angle": 0, "axis": "y", "origin": [17, 6, 14]}, "faces": { "north": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "east": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "south": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "west": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "up": {"uv": [5.333, 10.667, 10.666, 16], "texture": "#5"}, "down": {"uv": [5.333, 10.667, 10.666, 16], "texture": "#5"} } }, { "from": [14, 1, 14], "to": [15, 14, 15], "rotation": {"angle": 0, "axis": "y", "origin": [22, 9, 23]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [1, 1, 14], "to": [2, 14, 15], "rotation": {"angle": 0, "axis": "y", "origin": [9, 9, 23]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [1, 1, 1], "to": [2, 14, 2], "rotation": {"angle": 0, "axis": "y", "origin": [9, 9, 10]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [14, 1, 1], "to": [15, 14, 2], "rotation": {"angle": 0, "axis": "y", "origin": [22, 9, 10]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [14.5, 1, 1.5], "to": [14.7, 14, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [23, 16, 11]}, "faces": { "east": {"uv": [1, 1, 14, 15], "texture": "#3"}, "west": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 1, 1.5], "to": [1.7, 14, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [10, 16, 11]}, "faces": { "east": {"uv": [1, 1, 14, 15], "texture": "#3"}, "west": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 1, 1.5], "to": [14.5, 14, 1.7], "rotation": {"angle": 0, "axis": "y", "origin": [10, 16, 11]}, "faces": { "north": {"uv": [1, 1, 14, 15], "texture": "#3"}, "south": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 1, 14.5], "to": [14.5, 14, 14.7], "rotation": {"angle": 0, "axis": "y", "origin": [10, 16, 24]}, "faces": { "north": {"uv": [1, 1, 14, 15], "texture": "#3"}, "south": {"uv": [1, 1, 14, 15], "texture": "#3"} } } ], "groups": [ { "name": "VoxelShapes", "origin": [17, 21, 14], "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/tank_both.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "textures": { "3": "minecraft:block/glass", "5": "indrev:block/machine_block", "particle": "minecraft:block/glass" }, "elements": [ { "from": [14, 0, 14], "to": [15, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [22, 8, 23]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [1, 0, 14], "to": [2, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [9, 8, 23]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [1, 0, 1], "to": [2, 16, 2], "rotation": {"angle": 0, "axis": "y", "origin": [9, 8, 10]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [14, 0, 1], "to": [15, 16, 2], "rotation": {"angle": 0, "axis": "y", "origin": [22, 8, 10]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [14.5, 0, 1.5], "to": [14.7, 16, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [23, 15, 11]}, "faces": { "east": {"uv": [1, 1, 14, 15], "texture": "#3"}, "west": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 0, 1.5], "to": [1.7, 16, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [10, 15, 11]}, "faces": { "east": {"uv": [1, 1, 14, 15], "texture": "#3"}, "west": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 0, 1.5], "to": [14.5, 16, 1.7], "rotation": {"angle": 0, "axis": "y", "origin": [10, 15, 11]}, "faces": { "north": {"uv": [1, 1, 14, 15], "texture": "#3"}, "south": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 0, 14.5], "to": [14.5, 16, 14.7], "rotation": {"angle": 0, "axis": "y", "origin": [10, 15, 24]}, "faces": { "north": {"uv": [1, 1, 14, 15], "texture": "#3"}, "south": {"uv": [1, 1, 14, 15], "texture": "#3"} } } ], "groups": [ { "name": "VoxelShapes", "origin": [17, 21, 14], "children": [0, 1, 2, 3, 4, 5, 6, 7] } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/tank_down.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "textures": { "3": "minecraft:block/glass", "5": "indrev:block/machine_block", "particle": "minecraft:block/glass" }, "elements": [ { "from": [1, 14, 1], "to": [15, 15, 15], "rotation": {"angle": 0, "axis": "y", "origin": [17, 21, 14]}, "faces": { "north": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "east": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "south": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "west": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "up": {"uv": [5.333, 10.667, 10.666, 16], "texture": "#5"}, "down": {"uv": [5.333, 10.667, 10.666, 16], "texture": "#5"} } }, { "from": [14, 0, 14], "to": [15, 15, 15], "rotation": {"angle": 0, "axis": "y", "origin": [22, 8, 23]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [1, 0, 14], "to": [2, 15, 15], "rotation": {"angle": 0, "axis": "y", "origin": [9, 8, 23]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [1, 0, 1], "to": [2, 15, 2], "rotation": {"angle": 0, "axis": "y", "origin": [9, 8, 10]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [14, 0, 1], "to": [15, 15, 2], "rotation": {"angle": 0, "axis": "y", "origin": [22, 8, 10]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [14.5, 0, 1.5], "to": [14.7, 15, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [23, 15, 11]}, "faces": { "east": {"uv": [1, 1, 14, 15], "texture": "#3"}, "west": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 0, 1.5], "to": [1.7, 15, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [10, 15, 11]}, "faces": { "east": {"uv": [1, 1, 14, 15], "texture": "#3"}, "west": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 0, 1.5], "to": [14.5, 15, 1.7], "rotation": {"angle": 0, "axis": "y", "origin": [10, 15, 11]}, "faces": { "north": {"uv": [1, 1, 14, 15], "texture": "#3"}, "south": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 0, 14.5], "to": [14.5, 15, 14.7], "rotation": {"angle": 0, "axis": "y", "origin": [10, 15, 24]}, "faces": { "north": {"uv": [1, 1, 14, 15], "texture": "#3"}, "south": {"uv": [1, 1, 14, 15], "texture": "#3"} } } ], "groups": [ { "name": "VoxelShapes", "origin": [17, 21, 14], "children": [0, 1, 2, 3, 4, 5, 6, 7, 8] } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/tank_up.json ================================================ { "credit": "Made with Blockbench", "parent": "minecraft:block/block", "textures": { "3": "minecraft:block/glass", "5": "indrev:block/machine_block", "particle": "minecraft:block/glass" }, "elements": [ { "from": [1, 0, 1], "to": [15, 1, 15], "rotation": {"angle": 0, "axis": "y", "origin": [17, 6, 14]}, "faces": { "north": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "east": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "south": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "west": {"uv": [5.667, 10.333, 16, 11], "texture": "#5"}, "up": {"uv": [5.333, 10.667, 10.666, 16], "texture": "#5"}, "down": {"uv": [5.333, 10.667, 10.666, 16], "texture": "#5"} } }, { "from": [14, 1, 14], "to": [15, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [22, 9, 23]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [1, 1, 14], "to": [2, 16, 15], "rotation": {"angle": 0, "axis": "y", "origin": [9, 9, 23]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [1, 1, 1], "to": [2, 16, 2], "rotation": {"angle": 0, "axis": "y", "origin": [9, 9, 10]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [14, 1, 1], "to": [15, 16, 2], "rotation": {"angle": 0, "axis": "y", "origin": [22, 9, 10]}, "faces": { "north": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "east": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "south": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"}, "west": {"uv": [10.333, 5.333, 11, 16], "texture": "#5"} } }, { "from": [14.5, 1, 1.5], "to": [14.7, 16, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [23, 16, 11]}, "faces": { "east": {"uv": [1, 1, 14, 15], "texture": "#3"}, "west": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 1, 1.5], "to": [1.7, 16, 14.5], "rotation": {"angle": 0, "axis": "y", "origin": [10, 16, 11]}, "faces": { "east": {"uv": [1, 1, 14, 15], "texture": "#3"}, "west": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 1, 1.5], "to": [14.5, 16, 1.7], "rotation": {"angle": 0, "axis": "y", "origin": [10, 16, 11]}, "faces": { "north": {"uv": [1, 1, 14, 15], "texture": "#3"}, "south": {"uv": [1, 1, 14, 15], "texture": "#3"} } }, { "from": [1.5, 1, 14.5], "to": [14.5, 16, 14.7], "rotation": {"angle": 0, "axis": "y", "origin": [10, 16, 24]}, "faces": { "north": {"uv": [1, 1, 14, 15], "texture": "#3"}, "south": {"uv": [1, 1, 14, 15], "texture": "#3"} } } ], "groups": [ { "name": "VoxelShapes", "origin": [17, 21, 14], "children": [0, 1, 2, 3, 4, 5, 6, 7, 8] } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/tin_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/tin_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/tin_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/tin_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/tungsten_block.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/tungsten_block" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/tungsten_ore.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/tungsten_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/block/warning_strobe.json ================================================ { "credit": "Made with Blockbench", "parent": "block/block", "textures": { "0": "indrev:block/strobe-light", "1": "indrev:block/strobe-base", "particle": "indrev:block/strobe-light" }, "elements": [ { "from": [5, 0, 5], "to": [11, 1, 11], "rotation": {"angle": 0, "axis": "y", "origin": [12, 8, 12]}, "faces": { "north": {"uv": [6, 0, 12, 1], "texture": "#1"}, "east": {"uv": [6, 0, 12, 1], "texture": "#1"}, "south": {"uv": [6, 0, 12, 1], "texture": "#1"}, "west": {"uv": [6, 0, 12, 1], "texture": "#1"}, "up": {"uv": [0, 0, 6, 6], "texture": "#1"}, "down": {"uv": [6, 0, 0, 6], "texture": "#1"} } }, { "from": [6, 1, 6], "to": [10, 6, 10], "rotation": {"angle": 0, "axis": "y", "origin": [13, 9, 13]}, "faces": { "north": {"uv": [0, 0, 4, 5], "texture": "#0"}, "east": {"uv": [0, 0, 4, 5], "texture": "#0"}, "south": {"uv": [0, 0, 4, 5], "texture": "#0"}, "west": {"uv": [0, 0, 4, 5], "texture": "#0"}, "up": {"uv": [8, 4, 4, 0], "texture": "#0"}, "down": {"uv": [8, 4, 4, 0], "texture": "#0"} } } ] } ================================================ FILE: src/main/resources/assets/indrev/models/block/wither_proof_obsidian.json ================================================ { "parent": "block/cube_all", "textures": { "all": "indrev:block/wither_proof_obsidian" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/axe_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/axe_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/axe_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/axe_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/axe_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/axe_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/battery.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/battery" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/biomass.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/biomass" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/blast_furnace_enhancer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/blast_furnace_upgrade" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/block_base.json ================================================ { "parent": "indrev:block/block_base" } ================================================ FILE: src/main/resources/assets/indrev/models/item/block_highlight.json ================================================ { "parent": "indrev:block/block_highlight" } ================================================ FILE: src/main/resources/assets/indrev/models/item/boots_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/boots_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/boots_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/boots_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/boots_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/boots_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/broken_reinforced_elytra.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/broken_reinforced_elytra" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_axe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/bronze_axe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_block.json ================================================ { "parent": "indrev:block/bronze_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_boots.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/bronze_boots" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_chestplate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/bronze_chestplate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/bronze_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_helmet.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/bronze_helmet" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_hoe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/bronze_hoe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/bronze_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_leggings.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/bronze_leggings" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_nugget.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/bronze_nugget" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_pickaxe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/bronze_pickaxe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/bronze_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_shovel.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/bronze_shovel" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/bronze_sword.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/bronze_sword" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/buffer_enhancer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/buffer_upgrade" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/cabinet.json ================================================ { "parent": "indrev:block/cabinet" } ================================================ FILE: src/main/resources/assets/indrev/models/item/cable_mk1.json ================================================ { "parent": "indrev:block/cable_mk1" } ================================================ FILE: src/main/resources/assets/indrev/models/item/cable_mk2.json ================================================ { "parent": "indrev:block/cable_mk2" } ================================================ FILE: src/main/resources/assets/indrev/models/item/cable_mk3.json ================================================ { "parent": "indrev:block/cable_mk3" } ================================================ FILE: src/main/resources/assets/indrev/models/item/cable_mk4.json ================================================ { "parent": "indrev:block/cable_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/capsule.json ================================================ { "parent": "indrev:block/capsule" } ================================================ FILE: src/main/resources/assets/indrev/models/item/charge_pad_mk4.json ================================================ { "parent": "indrev:block/charge_pad_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/chestplate_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/chestplate_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/chestplate_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/chestplate_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/chestplate_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/chestplate_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/chunk_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/chunk_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/chunk_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/chunk_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/chunk_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/chunk_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/chunk_scanner.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/chunk_scanner" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/circuit_mk1.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/circuit_mk1" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/circuit_mk2.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/circuit_mk2" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/circuit_mk3.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/circuit_mk3" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/circuit_mk4.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/circuit_mk4" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/coal_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/coal_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/controller.json ================================================ { "parent": "indrev:block/controller" } ================================================ FILE: src/main/resources/assets/indrev/models/item/coolant_bucket.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/coolant_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/cooler_cell.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/cooler_cell" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_axe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/copper_axe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_block.json ================================================ { "parent": "indrev:block/copper_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_boots.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_boots" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_chestplate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_chestplate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_chunk.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_chunk" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_gear.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_gear" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_helmet.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_helmet" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_hoe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/copper_hoe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_leggings.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_leggings" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_nether_ore.json ================================================ { "parent": "indrev:block/copper_nether_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_nugget.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_nugget" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_ore.json ================================================ { "parent": "indrev:block/copper_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_pickaxe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/copper_pickaxe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_purified_ore.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/copper_purified_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_shovel.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/copper_shovel" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/copper_sword.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/copper_sword" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/damage_enhancer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/damage_enhancer" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/deepslate_lead_ore.json ================================================ { "parent": "indrev:block/deepslate_lead_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/deepslate_nikolite_ore.json ================================================ { "parent": "indrev:block/deepslate_nikolite_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/deepslate_silver_ore.json ================================================ { "parent": "indrev:block/deepslate_silver_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/deepslate_tin_ore.json ================================================ { "parent": "indrev:block/deepslate_tin_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/deepslate_tungsten_ore.json ================================================ { "parent": "indrev:block/deepslate_tungsten_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/diamond_drill_head.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/diamond_drill_head" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/diamond_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/diamond_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/drain_mk1.json ================================================ { "parent": "indrev:block/drain_mk1" } ================================================ FILE: src/main/resources/assets/indrev/models/item/drill_bottom.json ================================================ { "parent": "indrev:block/drill" } ================================================ FILE: src/main/resources/assets/indrev/models/item/duct.json ================================================ { "parent": "indrev:block/duct" } ================================================ FILE: src/main/resources/assets/indrev/models/item/dust_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/dust_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/dust_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/dust_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/dust_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/dust_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/electrum_block.json ================================================ { "parent": "indrev:block/electrum_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/electrum_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/electrum_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/electrum_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/electrum_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/electrum_nugget.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/electrum_nugget" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/electrum_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/electrum_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/empty_enhancer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/empty_upgrade" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/energy_enhancer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/energy_upgrade" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/energy_reader.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/energy_reader" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/enriched_nikolite_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/enriched_nikolite_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/enriched_nikolite_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/enriched_nikolite_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/fan.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/fan" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/fisher_mk2.json ================================================ { "parent": "indrev:block/fishing_farm_mk2" } ================================================ FILE: src/main/resources/assets/indrev/models/item/fisher_mk3.json ================================================ { "parent": "indrev:block/fishing_farm_mk3" } ================================================ FILE: src/main/resources/assets/indrev/models/item/fisher_mk4.json ================================================ { "parent": "indrev:block/fishing_farm_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/fluid_pipe_mk1.json ================================================ { "parent": "indrev:block/fluid_pipe_mk1" } ================================================ FILE: src/main/resources/assets/indrev/models/item/fluid_pipe_mk2.json ================================================ { "parent": "indrev:block/fluid_pipe_mk2" } ================================================ FILE: src/main/resources/assets/indrev/models/item/fluid_pipe_mk3.json ================================================ { "parent": "indrev:block/fluid_pipe_mk3" } ================================================ FILE: src/main/resources/assets/indrev/models/item/fluid_pipe_mk4.json ================================================ { "parent": "indrev:block/fluid_pipe_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/frame.json ================================================ { "parent": "indrev:block/frame" } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } }, "overrides": [ { "predicate": { "indrev:activate": 0.0 }, "model": "indrev:item/gamer_axe10" }, { "predicate": { "indrev:activate": 0.1 }, "model": "indrev:item/gamer_axe9" }, { "predicate": { "indrev:activate": 0.2 }, "model": "indrev:item/gamer_axe8" }, { "predicate": { "indrev:activate": 0.3 }, "model": "indrev:item/gamer_axe7" }, { "predicate": { "indrev:activate": 0.4 }, "model": "indrev:item/gamer_axe6" }, { "predicate": { "indrev:activate": 0.5 }, "model": "indrev:item/gamer_axe5" }, { "predicate": { "indrev:activate": 0.6 }, "model": "indrev:item/gamer_axe4" }, { "predicate": { "indrev:activate": 0.7 }, "model": "indrev:item/gamer_axe3" }, { "predicate": { "indrev:activate": 0.8 }, "model": "indrev:item/gamer_axe2" }, { "predicate": { "indrev:activate": 0.9 }, "model": "indrev:item/gamer_axe1" }, { "predicate": { "indrev:activate": 1.0 }, "model": "indrev:item/gamer_axe" } ] } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe1.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe1" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe10.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe10" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe2.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe2" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe3.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe3" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe4.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe4" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe5.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe5" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe6.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe6" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe7.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe7" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe8.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe8" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gamer_axe9.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/gamer_axe9" }, "display": { "thirdperson_righthand": { "rotation": [ 0, -90, 30 ], "translation": [ 0, 7.0, 2.0 ], "scale": [ 1.7, 1.7, 1.7 ] }, "thirdperson_lefthand": { "rotation": [ 0, 90, -55 ], "translation": [ 0, 4.0, 0.5 ], "scale": [ 1.7, 1.7, 1.7 ] }, "firstperson_righthand": { "rotation": [ 0, -90, 25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] }, "firstperson_lefthand": { "rotation": [ 0, 90, -25 ], "translation": [ 1.13, 3.2, 1.13 ], "scale": [ 1.36, 1.36, 1.36 ] } } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gold_chunk.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/gold_chunk" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gold_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/gold_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gold_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/gold_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/gold_purified_ore.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/gold_purified_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/guide_book.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/guide_book" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/hammer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/hammer" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/heat_coil.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/heat_coil" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/heat_generator_mk4.json ================================================ { "parent": "indrev:block/heat_generator_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/heatsink.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/heatsink" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/helmet_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/helmet_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/helmet_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/helmet_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/helmet_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/helmet_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/hoe_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/hoe_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/hoe_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/hoe_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/hoe_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/hoe_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/ingot_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/ingot_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/ingot_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/ingot_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/ingot_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/ingot_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/intake.json ================================================ { "parent": "indrev:block/intake" } ================================================ FILE: src/main/resources/assets/indrev/models/item/iron_chunk.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/iron_chunk" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/iron_drill_head.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/iron_drill_head" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/iron_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/iron_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/iron_gear.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/iron_gear" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/iron_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/iron_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/iron_purified_ore.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/iron_purified_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/jetpack_mk1.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/jetpack_mk1" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/jetpack_mk2.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/jetpack_mk2" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/jetpack_mk3.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/jetpack_mk3" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/jetpack_mk4.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/jetpack_mk4" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/laser_emitter_mk4.json ================================================ { "parent": "indrev:block/laser" } ================================================ FILE: src/main/resources/assets/indrev/models/item/lazuli_flux_container_creative.json ================================================ { "parent": "indrev:block/lazuli_flux_container_mk1" } ================================================ FILE: src/main/resources/assets/indrev/models/item/lazuli_flux_container_mk1.json ================================================ { "parent": "indrev:block/lazuli_flux_container_mk1" } ================================================ FILE: src/main/resources/assets/indrev/models/item/lazuli_flux_container_mk2.json ================================================ { "parent": "indrev:block/lazuli_flux_container_mk2" } ================================================ FILE: src/main/resources/assets/indrev/models/item/lazuli_flux_container_mk3.json ================================================ { "parent": "indrev:block/lazuli_flux_container_mk3" } ================================================ FILE: src/main/resources/assets/indrev/models/item/lazuli_flux_container_mk4.json ================================================ { "parent": "indrev:block/lazuli_flux_container_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_axe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/lead_axe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_block.json ================================================ { "parent": "indrev:block/lead_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_boots.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_boots" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_chestplate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_chestplate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_chunk.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_chunk" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_helmet.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_helmet" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_hoe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/lead_hoe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_leggings.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_leggings" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_nugget.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_nugget" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_ore.json ================================================ { "parent": "indrev:block/lead_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_pickaxe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/lead_pickaxe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_purified_ore.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/lead_purified_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_shovel.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/lead_shovel" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/lead_sword.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/lead_sword" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/leggings_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/leggings_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/leggings_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/leggings_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/leggings_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/leggings_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/machine_block.json ================================================ { "parent": "indrev:block/machine_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/miner_mk4.json ================================================ { "parent": "indrev:block/miner_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/mining_drill_mk1.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/mining_drill_mk1" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/mining_drill_mk2.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/mining_drill_mk2" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/mining_drill_mk3.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/mining_drill_mk3" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/mining_drill_mk4.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/mining_drill_mk4" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/modular_armor_boots.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/modular_armor_boots" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/modular_armor_chest.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/modular_armor_chest" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/modular_armor_helmet.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/modular_armor_helmet" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/modular_armor_legs.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/modular_armor_legs" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/modular_core.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/modular_core" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/modular_core_activated.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/modular_core_activated" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/modular_workbench_mk4.json ================================================ { "parent": "indrev:block/modular_workbench_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_auto_feeder.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_auto_feeder" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_breathing.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_breathing" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_charger.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_charger" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_black.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_black" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_blue.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_blue" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_brown.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_brown" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_cyan.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_cyan" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_green.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_green" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_orange.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_orange" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_pink.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_pink" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_purple.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_purple" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_red.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_red" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_color_yellow.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_color_yellow" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_efficiency.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_efficiency" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_elytra.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_elytra" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_feather_falling.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_feather_falling" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_fire_aspect.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_fire_aspect" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_fire_resistance.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_fire_resistance" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_fortune.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_fortune" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_jetpack.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_jetpack" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_jump_boost.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_jump_boost" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_looting.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_looting" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_magnet.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_magnet" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_night_vision.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_night_vision" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_piglin_tricker.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_piglin_tricker" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_protection.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_protection" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_range.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_range" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_reach.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_reach" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_sharpness.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_sharpness" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_silk_touch.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_silk_touch" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_solar_panel.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_solar_panel" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_speed.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_speed" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/module_water_affinity.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/module_water_affinity" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_bucket_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/molten_bucket_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_bucket_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/molten_bucket_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_bucket_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/molten_bucket_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_copper_bucket.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/molten_copper_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_gold_bucket.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/molten_gold_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_iron_bucket.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/molten_iron_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_lead_bucket.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/molten_lead_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_netherite_bucket.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/molten_netherite_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_silver_bucket.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/molten_silver_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/molten_tin_bucket.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/molten_tin_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/netherite_drill_head.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/netherite_drill_head" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/netherite_scrap_chunk.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/netherite_scrap_chunk" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/netherite_scrap_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/netherite_scrap_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/netherite_scrap_purified_ore.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/netherite_scrap_purified_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/nikolite_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/nikolite_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/nikolite_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/nikolite_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/nikolite_nether_ore.json ================================================ { "parent": "indrev:block/nikolite_nether_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/nikolite_ore.json ================================================ { "parent": "indrev:block/nikolite_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/nugget_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/nugget_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/nugget_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/nugget_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/nugget_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/nugget_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/ore_base.json ================================================ { "parent": "indrev:block/ore_base" } ================================================ FILE: src/main/resources/assets/indrev/models/item/ore_data_card.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/ore_data_card_empty" }, "overrides": [ { "predicate": { "indrev:empty": 1 }, "model": "indrev:item/ore_data_card_not_empty" } ] } ================================================ FILE: src/main/resources/assets/indrev/models/item/ore_data_card_not_empty.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/ore_data_card_not_empty" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/ore_highlight.json ================================================ { "parent": "indrev:block/ore_highlight" } ================================================ FILE: src/main/resources/assets/indrev/models/item/pickaxe_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/pickaxe_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/pickaxe_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/pickaxe_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/pickaxe_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/pickaxe_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/plank_block.json ================================================ { "parent": "indrev:block/plank_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/planks.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/planks_item" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/plate_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/plate_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/plate_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/plate_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/plate_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/plate_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/portable_charger.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/portable_charger" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/pump_mk1.json ================================================ { "parent": "indrev:block/pump_mk1" } ================================================ FILE: src/main/resources/assets/indrev/models/item/purified_ore_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/purified_ore_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/purified_ore_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/purified_ore_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/purified_ore_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/purified_ore_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/raw_lead.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/raw_lead" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/raw_lead_block.json ================================================ { "parent": "indrev:block/raw_lead_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/raw_silver.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/raw_silver" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/raw_silver_block.json ================================================ { "parent": "indrev:block/raw_silver_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/raw_tin.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/raw_tin" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/raw_tin_block.json ================================================ { "parent": "indrev:block/raw_tin_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/raw_tungsten.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/raw_tungsten" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/raw_tungsten_block.json ================================================ { "parent": "indrev:block/raw_tungsten_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/reinforced_elytra.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/reinforced_elytra" }, "overrides": [ { "predicate": { "indrev:broken": 1 }, "model": "indrev:item/broken_reinforced_elytra" } ] } ================================================ FILE: src/main/resources/assets/indrev/models/item/sawdust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/sawdust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/scan_output.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/scan_output" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/screwdriver.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/screwdriver" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/servo_output.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/servo_output" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/servo_retriever.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/servo_retriever" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/shovel_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/shovel_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/shovel_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/shovel_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/shovel_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/shovel_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silo.json ================================================ { "parent": "indrev:block/silo" } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_axe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/silver_axe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_block.json ================================================ { "parent": "indrev:block/silver_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_boots.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_boots" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_chestplate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_chestplate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_chunk.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_chunk" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_helmet.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_helmet" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_hoe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/silver_hoe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_leggings.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_leggings" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_nugget.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_nugget" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_ore.json ================================================ { "parent": "indrev:block/silver_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_pickaxe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/silver_pickaxe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_purified_ore.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/silver_purified_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_shovel.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/silver_shovel" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/silver_sword.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/silver_sword" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/smelter_mk4.json ================================================ { "parent": "indrev:block/smelter_mk4" } ================================================ FILE: src/main/resources/assets/indrev/models/item/smoker_enhancer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/smoker_upgrade" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/soot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/soot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/speed_enhancer.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/speed_upgrade" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_axe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/steel_axe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_block.json ================================================ { "parent": "indrev:block/steel_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_boots.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_boots" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_chestplate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_chestplate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_gear.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_gear" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_helmet.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_helmet" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_hoe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/steel_hoe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_leggings.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_leggings" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_nugget.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_nugget" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_pickaxe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/steel_pickaxe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/steel_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_shovel.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/steel_shovel" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/steel_sword.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/steel_sword" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/stone_drill_head.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/stone_drill_head" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/sulfur_crystal.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/sulfur_crystal" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/sulfur_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/sulfur_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/sulfuric_acid_bucket.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/sulfuric_acid_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/sword_base.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/sword_base" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/sword_highlight.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/sword_highlight" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/sword_outline.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/sword_outline" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tank.json ================================================ { "parent": "indrev:block/tank" } ================================================ FILE: src/main/resources/assets/indrev/models/item/tech_soup.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tech_soup" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tier_upgrade_mk2.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tier_upgrade_mk2" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tier_upgrade_mk3.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tier_upgrade_mk3" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tier_upgrade_mk4.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tier_upgrade_mk4" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_axe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/tin_axe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_block.json ================================================ { "parent": "indrev:block/tin_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_boots.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_boots" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_chestplate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_chestplate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_chunk.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_chunk" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_gear.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_gear" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_helmet.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_helmet" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_hoe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/tin_hoe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_leggings.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_leggings" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_nugget.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_nugget" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_ore.json ================================================ { "parent": "indrev:block/tin_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_pickaxe.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/tin_pickaxe" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_purified_ore.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tin_purified_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_shovel.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/tin_shovel" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tin_sword.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/tin_sword" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tool_stick.json ================================================ { "parent": "item/handheld", "textures": { "layer0": "indrev:item/tool_stick" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/toxic_mud_bucket.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/toxic_mud_bucket" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tungsten_block.json ================================================ { "parent": "indrev:block/tungsten_block" } ================================================ FILE: src/main/resources/assets/indrev/models/item/tungsten_chunk.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tungsten_chunk" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tungsten_dust.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tungsten_dust" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tungsten_ingot.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tungsten_ingot" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tungsten_nugget.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tungsten_nugget" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tungsten_ore.json ================================================ { "parent": "indrev:block/tungsten_ore" } ================================================ FILE: src/main/resources/assets/indrev/models/item/tungsten_plate.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tungsten_plate" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/tungsten_purified_ore.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/tungsten_purified_ore" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/untanned_leather.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/untanned_leather" } } ================================================ FILE: src/main/resources/assets/indrev/models/item/warning_strobe.json ================================================ { "parent": "indrev:block/warning_strobe" } ================================================ FILE: src/main/resources/assets/indrev/models/item/wither_proof_obsidian.json ================================================ { "parent": "indrev:block/wither_proof_obsidian" } ================================================ FILE: src/main/resources/assets/indrev/models/item/wrench.json ================================================ { "parent": "item/generated", "textures": { "layer0": "indrev:item/wrench" } } ================================================ FILE: src/main/resources/assets/indrev/particles/laser_particle.json ================================================ { "textures": [ "indrev:laser_particle_1", "indrev:laser_particle_2", "indrev:laser_particle_3" ] } ================================================ FILE: src/main/resources/assets/indrev/sounds.json ================================================ { "laser": { "subtitle": "subtitles.indrev.laser", "sounds": [ "indrev:laser" ] } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/biomass_generator_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 2, "frames": [2, 3, 4, 1, 2, 0, 3, 2, 0, 1, 2, 4, 3] } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/cable_center_emissive_mk1.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/cable_center_emissive_mk2.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/cable_center_emissive_mk3.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/cable_center_emissive_mk4.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/cable_wire_emissive_mk1.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/cable_wire_emissive_mk2.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/cable_wire_emissive_mk3.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/cable_wire_emissive_mk4.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/chopper_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 6 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/coal_generator_on.png.mcmeta ================================================ { "animation": { "frametime": 6, "frames": [3, 2, 0, 1, 3, 1, 0, 2, 3, 1, 0, 2, 1, 3, 3, 2, 1] } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/compressor_on.png.mcmeta ================================================ { "animation": { "frametime": 6, "frames": [0, 1, 2, 3, 4, 4, 4, 4, 3, 2, 1] } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/condenser_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 5 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/data_card_writer_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1, "frames": [0,1,2,3,4,5,6,7,6,5,4,3,2,1] } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/drill_top_emissive_on.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 4 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/drill_top_tracer_emissive_on.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/electric_furnace_emissive_on.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 4 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/fluid_infuser_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 5 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/gray_lava_flow.png.mcmeta ================================================ { "animation": { "frametime": 3 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/gray_lava_still.png.mcmeta ================================================ { "animation": { "frametime": 2, "frames": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ] } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/mining_rig_screen_emissive.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 3 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/pulverizer_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 2 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/rancher_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 5 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/recycler_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 10, "frames": [0, 1, 2, 3, 2, 1] } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/sawmill_on.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/block/solid_infuser_emissive_on.png.mcmeta ================================================ { "animation": { "interpolate": false, "frametime": 8 } } ================================================ FILE: src/main/resources/assets/indrev/textures/entity/laser.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/gui/hud_damaged.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/gui/hud_regenerating.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/gui/hud_warning.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/item/enriched_nikolite_dust.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/item/enriched_nikolite_ingot.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1 } } ================================================ FILE: src/main/resources/assets/indrev/textures/item/gamer_axe.png.mcmeta ================================================ { "animation": { "interpolate": true, "frametime": 1 } } ================================================ FILE: src/main/resources/data/c/tags/blocks/bronze_blocks.json ================================================ { "replace": false, "values": [ "indrev:bronze_block" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/copper_blocks.json ================================================ { "replace": false, "values": [ "minecraft:copper_block" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/electrum_blocks.json ================================================ { "replace": false, "values": [ "indrev:electrum_block" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/lead_blocks.json ================================================ { "replace": false, "values": [ "indrev:lead_block" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/lead_ores.json ================================================ { "replace": false, "values": [ "indrev:lead_ore", "indrev:deepslate_lead_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/silver_blocks.json ================================================ { "replace": false, "values": [ "indrev:silver_block" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/silver_ores.json ================================================ { "replace": false, "values": [ "indrev:silver_ore", "indrev:deepslate_silver_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/tin_blocks.json ================================================ { "replace": false, "values": [ "indrev:tin_block" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/tin_ores.json ================================================ { "replace": false, "values": [ "indrev:tin_ore", "indrev:deepslate_tin_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/tungsten_blocks.json ================================================ { "replace": false, "values": [ "indrev:tungsten_block" ] } ================================================ FILE: src/main/resources/data/c/tags/blocks/tungsten_ores.json ================================================ { "replace": false, "values": [ "indrev:tungsten_ore", "indrev:deepslate_tungsten_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/ancient_debris_ores.json ================================================ { "replace": false, "values": [ "minecraft:ancient_debris" ] } ================================================ FILE: src/main/resources/data/c/tags/items/bronze_blocks.json ================================================ { "replace": false, "values": [ "indrev:bronze_block" ] } ================================================ FILE: src/main/resources/data/c/tags/items/bronze_dusts.json ================================================ { "replace": false, "values": [ "indrev:bronze_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/bronze_ingots.json ================================================ { "replace": false, "values": [ "indrev:bronze_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/bronze_nuggets.json ================================================ { "replace": false, "values": [ "indrev:bronze_nugget" ] } ================================================ FILE: src/main/resources/data/c/tags/items/bronze_plates.json ================================================ { "replace": false, "values": [ "indrev:bronze_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/coal_dusts.json ================================================ { "replace": false, "values": [ "indrev:coal_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/coal_ores.json ================================================ { "replace": false, "values": [ "minecraft:coal_ore", "minecraft:deepslate_coal_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/copper_blocks.json ================================================ { "replace": false, "values": [ "minecraft:copper_block" ] } ================================================ FILE: src/main/resources/data/c/tags/items/copper_dusts.json ================================================ { "replace": false, "values": [ "indrev:copper_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/copper_ingots.json ================================================ { "replace": false, "values": [ "minecraft:copper_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/copper_nuggets.json ================================================ { "replace": false, "values": [ "indrev:copper_nugget" ] } ================================================ FILE: src/main/resources/data/c/tags/items/copper_ores.json ================================================ { "replace": false, "values": [ "minecraft:copper_ore", "minecraft:deepslate_copper_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/copper_plates.json ================================================ { "replace": false, "values": [ "indrev:copper_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/diamond_dusts.json ================================================ { "replace": false, "values": [ "indrev:diamond_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/diamond_ores.json ================================================ { "replace": false, "values": [ "minecraft:diamond_ore", "minecraft:deepslate_diamond_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/electrum_blocks.json ================================================ { "replace": false, "values": [ "indrev:electrum_block" ] } ================================================ FILE: src/main/resources/data/c/tags/items/electrum_dusts.json ================================================ { "replace": false, "values": [ "indrev:electrum_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/electrum_ingots.json ================================================ { "replace": false, "values": [ "indrev:electrum_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/electrum_nuggets.json ================================================ { "replace": false, "values": [ "indrev:electrum_nugget" ] } ================================================ FILE: src/main/resources/data/c/tags/items/electrum_plates.json ================================================ { "replace": false, "values": [ "indrev:electrum_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/emerald_ores.json ================================================ { "replace": false, "values": [ "minecraft:emerald_ore", "minecraft:deepslate_emerald_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/gold_dusts.json ================================================ { "replace": false, "values": [ "indrev:gold_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/gold_ingots.json ================================================ { "replace": false, "values": [ "minecraft:gold_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/gold_ores.json ================================================ { "replace": false, "values": [ "minecraft:gold_ore", "minecraft:deepslate_gold_ore", "minecraft:nether_gold_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/gold_plates.json ================================================ { "replace": false, "values": [ "indrev:gold_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/iron_dusts.json ================================================ { "replace": false, "values": [ "indrev:iron_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/iron_ingots.json ================================================ { "replace": false, "values": [ "minecraft:iron_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/iron_ores.json ================================================ { "replace": false, "values": [ "minecraft:iron_ore", "minecraft:deepslate_iron_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/iron_plates.json ================================================ { "replace": false, "values": [ "indrev:iron_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/lead_blocks.json ================================================ { "replace": false, "values": [ "indrev:lead_block" ] } ================================================ FILE: src/main/resources/data/c/tags/items/lead_dusts.json ================================================ { "replace": false, "values": [ "indrev:lead_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/lead_ingots.json ================================================ { "replace": false, "values": [ "indrev:lead_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/lead_nuggets.json ================================================ { "replace": false, "values": [ "indrev:lead_nugget" ] } ================================================ FILE: src/main/resources/data/c/tags/items/lead_ores.json ================================================ { "replace": false, "values": [ "indrev:lead_ore", "indrev:deepslate_lead_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/lead_plates.json ================================================ { "replace": false, "values": [ "indrev:lead_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/netherite_scrap_dusts.json ================================================ { "replace": false, "values": [ "indrev:netherite_scrap_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/nikolite_ores.json ================================================ { "replace": false, "values": [ "indrev:nikolite_ore", "indrev:deepslate_nikolite_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/raw_copper_ores.json ================================================ { "replace": false, "values": [ "minecraft:raw_copper" ] } ================================================ FILE: src/main/resources/data/c/tags/items/raw_gold_ores.json ================================================ { "replace": false, "values": [ "minecraft:raw_gold" ] } ================================================ FILE: src/main/resources/data/c/tags/items/raw_iron_ores.json ================================================ { "replace": false, "values": [ "minecraft:raw_iron" ] } ================================================ FILE: src/main/resources/data/c/tags/items/raw_lead_ores.json ================================================ { "replace": false, "values": [ "indrev:raw_lead" ] } ================================================ FILE: src/main/resources/data/c/tags/items/raw_silver_ores.json ================================================ { "replace": false, "values": [ "indrev:raw_silver" ] } ================================================ FILE: src/main/resources/data/c/tags/items/raw_tin_ores.json ================================================ { "replace": false, "values": [ "indrev:raw_tin" ] } ================================================ FILE: src/main/resources/data/c/tags/items/raw_tungsten_ores.json ================================================ { "replace": false, "values": [ "indrev:raw_tungsten" ] } ================================================ FILE: src/main/resources/data/c/tags/items/redstone_ores.json ================================================ { "replace": false, "values": [ "minecraft:redstone_ore", "minecraft:deepslate_redstone_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/screwdrivers.json ================================================ { "replace": false, "values": [ "indrev:screwdriver" ] } ================================================ FILE: src/main/resources/data/c/tags/items/silver_blocks.json ================================================ { "replace": false, "values": [ "indrev:silver_block" ] } ================================================ FILE: src/main/resources/data/c/tags/items/silver_dusts.json ================================================ { "replace": false, "values": [ "indrev:silver_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/silver_ingots.json ================================================ { "replace": false, "values": [ "indrev:silver_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/silver_nuggets.json ================================================ { "replace": false, "values": [ "indrev:silver_nugget" ] } ================================================ FILE: src/main/resources/data/c/tags/items/silver_ores.json ================================================ { "replace": false, "values": [ "indrev:silver_ore", "indrev:deepslate_silver_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/silver_plates.json ================================================ { "replace": false, "values": [ "indrev:silver_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_blocks.json ================================================ { "replace": false, "values": [ "indrev:steel_block" ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_boots.json ================================================ { "replace": false, "values": [ "indrev:steel_boots", { "id": "astromine:steel_boots", "required": false } ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_chestplates.json ================================================ { "replace": false, "values": [ "indrev:steel_chestplate", { "id": "astromine:steel_chestplate", "required": false } ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_dusts.json ================================================ { "replace": false, "values": [ "indrev:steel_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_helmets.json ================================================ { "replace": false, "values": [ "indrev:steel_helmet", { "id": "astromine:steel_helmet", "required": false } ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_ingots.json ================================================ { "replace": false, "values": [ "indrev:steel_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_leggings.json ================================================ { "replace": false, "values": [ "indrev:steel_leggings", { "id": "astromine:steel_leggings", "required": false } ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_nuggets.json ================================================ { "replace": false, "values": [ "indrev:steel_nugget" ] } ================================================ FILE: src/main/resources/data/c/tags/items/steel_plates.json ================================================ { "replace": false, "values": [ "indrev:steel_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/sulfur_dusts.json ================================================ { "replace": false, "values": [ "indrev:sulfur_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/sulfurs.json ================================================ { "replace": false, "values": [ "indrev:sulfur_crystal" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tin_blocks.json ================================================ { "replace": false, "values": [ "indrev:tin_block" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tin_dusts.json ================================================ { "replace": false, "values": [ "indrev:tin_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tin_ingots.json ================================================ { "replace": false, "values": [ "indrev:tin_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tin_nuggets.json ================================================ { "replace": false, "values": [ "indrev:tin_nugget" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tin_ores.json ================================================ { "replace": false, "values": [ "indrev:tin_ore", "indrev:deepslate_tin_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tin_plates.json ================================================ { "replace": false, "values": [ "indrev:tin_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tungsten_blocks.json ================================================ { "replace": false, "values": [ "indrev:tungsten_block" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tungsten_dusts.json ================================================ { "replace": false, "values": [ "indrev:tungsten_dust" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tungsten_ingots.json ================================================ { "replace": false, "values": [ "indrev:tungsten_ingot" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tungsten_nuggets.json ================================================ { "replace": false, "values": [ "indrev:tungsten_nugget" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tungsten_ores.json ================================================ { "replace": false, "values": [ "indrev:tungsten_ore", "indrev:deepslate_tungsten_ore" ] } ================================================ FILE: src/main/resources/data/c/tags/items/tungsten_plates.json ================================================ { "replace": false, "values": [ "indrev:tungsten_plate" ] } ================================================ FILE: src/main/resources/data/c/tags/items/wrenches.json ================================================ { "replace": false, "values": [ "indrev:wrench" ] } ================================================ FILE: src/main/resources/data/fabric/tags/items/axes.json ================================================ { "replace": false, "values": [ "indrev:mining_drill_mk1", "indrev:mining_drill_mk2", "indrev:mining_drill_mk3", "indrev:mining_drill_mk4", "indrev:tin_axe", "indrev:copper_axe", "indrev:steel_axe", "indrev:gamer_axe", "indrev:lead_axe", "indrev:silver_axe", "indrev:bronze_axe" ] } ================================================ FILE: src/main/resources/data/fabric/tags/items/hoes.json ================================================ { "replace": false, "values": [ "indrev:tin_hoe", "indrev:copper_hoe", "indrev:steel_hoe", "indrev:lead_hoe", "indrev:silver_hoe", "indrev:bronze_hoe" ] } ================================================ FILE: src/main/resources/data/fabric/tags/items/pickaxes.json ================================================ { "replace": false, "values": [ "indrev:mining_drill_mk1", "indrev:mining_drill_mk2", "indrev:mining_drill_mk3", "indrev:mining_drill_mk4", "indrev:tin_pickaxe", "indrev:copper_pickaxe", "indrev:steel_pickaxe", "indrev:lead_pickaxe", "indrev:silver_pickaxe", "indrev:bronze_pickaxe" ] } ================================================ FILE: src/main/resources/data/fabric/tags/items/shovels.json ================================================ { "replace": false, "values": [ "indrev:mining_drill_mk1", "indrev:mining_drill_mk2", "indrev:mining_drill_mk3", "indrev:mining_drill_mk4", "indrev:tin_shovel", "indrev:copper_shovel", "indrev:steel_shovel", "indrev:lead_shovel", "indrev:silver_shovel", "indrev:bronze_shovel" ] } ================================================ FILE: src/main/resources/data/fabric/tags/items/swords.json ================================================ { "replace": false, "values": [ "indrev:tin_sword", "indrev:copper_sword", "indrev:steel_sword", "indrev:lead_sword", "indrev:silver_sword", "indrev:bronze_sword" ] } ================================================ FILE: src/main/resources/data/indrev/advancements/advanced_solar_generator.json ================================================ { "display": { "icon": { "item": "indrev:solar_generator_mk3" }, "title": { "translate": "advancements.indrev.advanced_solar_generator" }, "description": { "translate": "advancements.indrev.advanced_solar_generator.description" } }, "parent": "indrev:basic_solar_generator", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:solar_generator_mk3"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/basic_solar_generator.json ================================================ { "display": { "icon": { "item": "indrev:solar_generator_mk1" }, "title": { "translate": "advancements.indrev.basic_solar_generator" }, "description": { "translate": "advancements.indrev.basic_solar_generator.description" } }, "parent": "indrev:coal_generator", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:solar_generator_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/biomass.json ================================================ { "display": { "icon": { "item": "indrev:biomass" }, "title": { "translate": "advancements.indrev.biomass" }, "description": { "translate": "advancements.indrev.biomass.description" } }, "parent": "indrev:recycler", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:biomass"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/biomass_generator.json ================================================ { "display": { "icon": { "item": "indrev:biomass_generator_mk3" }, "title": { "translate": "advancements.indrev.biomass_generator" }, "description": { "translate": "advancements.indrev.biomass_generator.description" } }, "parent": "indrev:biomass", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:biomass_generator_mk3"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/blast_furnace_enhancer.json ================================================ { "display": { "icon": { "item": "indrev:blast_furnace_enhancer" }, "title": { "translate": "advancements.indrev.blast_furnace_enhancer" }, "description": { "translate": "advancements.indrev.blast_furnace_enhancer.description" } }, "parent": "indrev:empty_enhancer", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:blast_furnace_enhancer"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/buffer_enhancer.json ================================================ { "display": { "icon": { "item": "indrev:buffer_enhancer" }, "title": { "translate": "advancements.indrev.buffer_enhancer" }, "description": { "translate": "advancements.indrev.buffer_enhancer.description" } }, "parent": "indrev:empty_enhancer", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:buffer_enhancer"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/cable_mk1.json ================================================ { "display": { "icon": { "item": "indrev:cable_mk1" }, "title": { "translate": "advancements.indrev.cable_mk1" }, "description": { "translate": "advancements.indrev.cable_mk1.description" } }, "parent": "indrev:nikolite", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:cable_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/cable_mk2.json ================================================ { "display": { "icon": { "item": "indrev:cable_mk2" }, "title": { "translate": "advancements.indrev.cable_mk2" }, "description": { "translate": "advancements.indrev.cable_mk2.description" } }, "parent": "indrev:cable_mk1", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:cable_mk2"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/cable_mk3.json ================================================ { "display": { "icon": { "item": "indrev:cable_mk3" }, "title": { "translate": "advancements.indrev.cable_mk3" }, "description": { "translate": "advancements.indrev.cable_mk3.description" } }, "parent": "indrev:cable_mk2", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:cable_mk3"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/cable_mk4.json ================================================ { "display": { "icon": { "item": "indrev:cable_mk4" }, "title": { "translate": "advancements.indrev.cable_mk4" }, "description": { "translate": "advancements.indrev.cable_mk4.description" } }, "parent": "indrev:cable_mk3", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:cable_mk4"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/chopper_mk4.json ================================================ { "display": { "icon": { "item": "indrev:chopper_mk1" }, "title": { "translate": "advancements.indrev.chopper_mk4" }, "description": { "translate": "advancements.indrev.chopper_mk4.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:chopper_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/circuit_mk1.json ================================================ { "display": { "icon": { "item": "indrev:circuit_mk1" }, "title": { "translate": "advancements.indrev.circuit_mk1" }, "description": { "translate": "advancements.indrev.circuit_mk1.description" } }, "hidden": true, "parent": "indrev:nikolite", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:circuit_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/circuit_mk2.json ================================================ { "display": { "icon": { "item": "indrev:circuit_mk2" }, "title": { "translate": "advancements.indrev.circuit_mk2" }, "description": { "translate": "advancements.indrev.circuit_mk2.description" } }, "hidden": true, "parent": "indrev:circuit_mk1", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:circuit_mk2"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/circuit_mk3.json ================================================ { "display": { "icon": { "item": "indrev:circuit_mk3" }, "title": { "translate": "advancements.indrev.circuit_mk3" }, "description": { "translate": "advancements.indrev.circuit_mk3.description" } }, "hidden": true, "parent": "indrev:circuit_mk2", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:circuit_mk3"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/circuit_mk4.json ================================================ { "display": { "icon": { "item": "indrev:circuit_mk4" }, "title": { "translate": "advancements.indrev.circuit_mk4" }, "description": { "translate": "advancements.indrev.circuit_mk4.description" } }, "hidden": true, "parent": "indrev:circuit_mk3", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:circuit_mk4"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/coal_generator.json ================================================ { "display": { "icon": { "item": "indrev:coal_generator_mk1" }, "title": { "translate": "advancements.indrev.coal_generator" }, "description": { "translate": "advancements.indrev.coal_generator.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:coal_generator_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/compressor.json ================================================ { "display": { "icon": { "item": "indrev:compressor_mk1" }, "title": { "translate": "advancements.indrev.compressor" }, "description": { "translate": "advancements.indrev.compressor.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:compressor_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/condenser.json ================================================ { "display": { "icon": { "item": "indrev:condenser_mk4" }, "title": { "translate": "advancements.indrev.condenser" }, "description": { "translate": "advancements.indrev.condenser" } }, "parent": "indrev:circuit_mk4", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:condenser_mk4"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/electric_furnace.json ================================================ { "display": { "icon": { "item": "indrev:electric_furnace_mk1" }, "title": { "translate": "advancements.indrev.electric_furnace" }, "description": { "translate": "advancements.indrev.electric_furnace.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:electric_furnace_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/empty_enhancer.json ================================================ { "display": { "icon": { "item": "indrev:empty_enhancer" }, "title": { "translate": "advancements.indrev.empty_enhancer" }, "description": { "translate": "advancements.indrev.empty_enhancer.description" } }, "parent": "indrev:nikolite", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:empty_enhancer"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/enriched_nikolite_dust.json ================================================ { "display": { "icon": { "item": "indrev:enriched_nikolite_dust" }, "title": { "translate": "advancements.indrev.enriched_nikolite_dust" }, "description": { "translate": "advancements.indrev.enriched_nikolite_dust.description" } }, "parent": "indrev:nikolite_ingot", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:enriched_nikolite_dust"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/enriched_nikolite_ingot.json ================================================ { "display": { "icon": { "item": "indrev:enriched_nikolite_ingot" }, "title": { "translate": "advancements.indrev.enriched_nikolite_ingot" }, "description": { "translate": "advancements.indrev.enriched_nikolite_ingot.description" } }, "parent": "indrev:enriched_nikolite_dust", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:enriched_nikolite_ingot"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/farmer_mk1.json ================================================ { "display": { "icon": { "item": "indrev:farmer_mk1" }, "title": { "translate": "advancements.indrev.farmer_mk1" }, "description": { "translate": "advancements.indrev.farmer_mk1.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:farmer_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/gamer_axe.json ================================================ { "display": { "icon": { "item": "indrev:gamer_axe" }, "title": { "translate": "advancements.indrev.gamer_axe" }, "description": { "translate": "advancements.indrev.gamer_axe.description" }, "frame": "challenge" }, "parent": "indrev:nikolite", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:gamer_axe"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/heat_generator.json ================================================ { "display": { "icon": { "item": "indrev:heat_generator_mk4" }, "title": { "translate": "advancements.indrev.heat_generator" }, "description": { "translate": "advancements.indrev.heat_generator.description" } }, "parent": "indrev:coal_generator", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:heat_generator_mk4"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/industrial_smelter.json ================================================ { "display": { "icon": { "item": "indrev:smelter_mk4" }, "title": { "translate": "advancements.indrev.industrial_smelter" }, "description": { "translate": "advancements.indrev.industrial_smelter" } }, "parent": "indrev:circuit_mk4", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:smelter_mk4"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/lazuli_flux_container_mk1.json ================================================ { "display": { "icon": { "item": "indrev:lazuli_flux_container_mk1" }, "title": { "translate": "advancements.indrev.lazuli_flux_container_mk1" }, "description": { "translate": "advancements.indrev.lazuli_flux_container_mk1.description" } }, "parent": "indrev:circuit_mk1", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:lazuli_flux_container_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/lazuli_flux_container_mk2.json ================================================ { "display": { "icon": { "item": "indrev:lazuli_flux_container_mk2" }, "title": { "translate": "advancements.indrev.lazuli_flux_container_mk2" }, "description": { "translate": "advancements.indrev.lazuli_flux_container_mk2.description" } }, "parent": "indrev:circuit_mk2", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:lazuli_flux_container_mk2"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/lazuli_flux_container_mk3.json ================================================ { "display": { "icon": { "item": "indrev:lazuli_flux_container_mk3" }, "title": { "translate": "advancements.indrev.lazuli_flux_container_mk3" }, "description": { "translate": "advancements.indrev.lazuli_flux_container_mk3.description" } }, "parent": "indrev:circuit_mk3", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:lazuli_flux_container_mk3"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/lazuli_flux_container_mk4.json ================================================ { "display": { "icon": { "item": "indrev:lazuli_flux_container_mk4" }, "title": { "translate": "advancements.indrev.lazuli_flux_container_mk4" }, "description": { "translate": "advancements.indrev.lazuli_flux_container_mk4.description" } }, "parent": "indrev:circuit_mk4", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": [ "indrev:lazuli_flux_container_mk4" ] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/machine_block.json ================================================ { "display": { "icon": { "item": "indrev:machine_block" }, "title": { "translate": "advancements.indrev.machine_block" }, "description": { "translate": "advancements.indrev.machine_block.description" } }, "parent": "indrev:nikolite", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:machine_block"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/mk2_upgrade.json ================================================ { "display": { "icon": { "item": "indrev:tier_upgrade_mk2" }, "title": { "translate": "advancements.indrev.mk2_upgrade" }, "description": { "translate": "advancements.indrev.mk2_upgrade.description" } }, "parent": "indrev:circuit_mk2", "criteria": { "checkInv": { "trigger": "minecraft:item_used_on_block", "conditions": { "item": { "items": ["indrev:tier_upgrade_mk2"] } } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/mk3_upgrade.json ================================================ { "display": { "icon": { "item": "indrev:tier_upgrade_mk3" }, "title": { "translate": "advancements.indrev.mk3_upgrade" }, "description": { "translate": "advancements.indrev.mk3_upgrade.description" } }, "parent": "indrev:circuit_mk3", "criteria": { "checkInv": { "trigger": "minecraft:item_used_on_block", "conditions": { "item": { "items": ["indrev:tier_upgrade_mk3"] } } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/mk4_upgrade.json ================================================ { "display": { "icon": { "item": "indrev:tier_upgrade_mk4" }, "title": { "translate": "advancements.indrev.mk4_upgrade" }, "description": { "translate": "advancements.indrev.mk4_upgrade.description" } }, "parent": "indrev:circuit_mk4", "criteria": { "checkInv": { "trigger": "minecraft:item_used_on_block", "conditions": { "item": { "items": ["indrev:tier_upgrade_mk4"] } } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/modular_armor.json ================================================ { "parent": "indrev:modular_workbench", "display": { "icon": { "item": "indrev:modular_armor_chest", "nbt": "{Damage:0}" }, "title": { "translate": "advancements.indrev.modular_armor" }, "description": { "translate": "advancements.indrev.modular_armor.description" }, "frame": "challenge" }, "rewards": { "experience": 100 }, "criteria": { "netherite_armor": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:modular_armor_helmet","indrev:modular_armor_chest", "indrev:modular_armor_legs", "indrev:modular_armor_boots"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/modular_workbench.json ================================================ { "display": { "icon": { "item": "indrev:modular_workbench_mk4" }, "title": { "translate": "advancements.indrev.modular_workbench" }, "description": { "translate": "advancements.indrev.modular_workbench.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:modular_workbench_mk4"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/nikolite.json ================================================ { "display": { "icon": { "item": "indrev:nikolite_dust" }, "title": { "translate": "advancements.indrev.nikolite" }, "description": { "translate": "advancements.indrev.nikolite.description" }, "background": "minecraft:textures/gui/advancements/backgrounds/adventure.png" }, "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:nikolite_dust"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/nikolite_ingot.json ================================================ { "display": { "icon": { "item": "indrev:nikolite_ingot" }, "title": { "translate": "advancements.indrev.nikolite_ingot" }, "description": { "translate": "advancements.indrev.nikolite_ingot.description" } }, "parent": "indrev:solid_infuser", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:nikolite_ingot"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/pulverizer.json ================================================ { "display": { "icon": { "item": "indrev:pulverizer_mk1" }, "title": { "translate": "advancements.indrev.pulverizer" }, "description": { "translate": "advancements.indrev.pulverizer.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:pulverizer_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/rancher_mk4.json ================================================ { "display": { "icon": { "item": "indrev:rancher_mk1" }, "title": { "translate": "advancements.indrev.rancher_mk4" }, "description": { "translate": "advancements.indrev.rancher_mk4.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:rancher_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/recycler.json ================================================ { "display": { "icon": { "item": "indrev:recycler_mk2" }, "title": { "translate": "advancements.indrev.recycler" }, "description": { "translate": "advancements.indrev.recycler.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:recycler_mk2"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/slaughter_mk1.json ================================================ { "display": { "icon": { "item": "indrev:slaughter_mk1" }, "title": { "translate": "advancements.indrev.slaughter_mk1" }, "description": { "translate": "advancements.indrev.slaughter_mk1.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:slaughter_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/smoker_enhancer.json ================================================ { "display": { "icon": { "item": "indrev:smoker_enhancer" }, "title": { "translate": "advancements.indrev.smoker_enhancer" }, "description": { "translate": "advancements.indrev.smoker_enhancer.description" } }, "parent": "indrev:empty_enhancer", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:smoker_enhancer"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/solid_infuser.json ================================================ { "display": { "icon": { "item": "indrev:solid_infuser_mk1" }, "title": { "translate": "advancements.indrev.solid_infuser" }, "description": { "translate": "advancements.indrev.solid_infuser.description" } }, "parent": "indrev:machine_block", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:solid_infuser_mk1"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/advancements/speed_enhancer.json ================================================ { "display": { "icon": { "item": "indrev:speed_enhancer" }, "title": { "translate": "advancements.indrev.speed_enhancer" }, "description": { "translate": "advancements.indrev.speed_enhancer.description" } }, "parent": "indrev:empty_enhancer", "criteria": { "checkInv": { "trigger": "minecraft:inventory_changed", "conditions": { "items": [ { "items": ["indrev:speed_enhancer"] } ] } } } } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/biomass_generator_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:biomass_generator_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/bronze_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:bronze_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/cabinet.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:cabinet" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/cable_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:cable_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/cable_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:cable_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/cable_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:cable_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/cable_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:cable_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/capsule.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:capsule" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/charge_pad_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:charge_pad_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/chopper_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:chopper_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/chopper_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:chopper_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/chopper_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:chopper_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/chopper_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:chopper_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/chopper_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:chopper_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/coal_generator_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:coal_generator_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/compressor_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:compressor_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/compressor_factory_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:compressor_factory_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/compressor_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:compressor_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/compressor_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:compressor_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/compressor_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:compressor_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/compressor_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:compressor_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/condenser_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:condenser_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/controller.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:controller" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/deepslate_lead_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:deepslate_lead_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:raw_lead" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/deepslate_nikolite_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:deepslate_nikolite_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:set_count", "count": { "min": 4.0, "max": 5.0, "type": "minecraft:uniform" } }, { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:uniform_bonus_count", "parameters": { "bonusMultiplier": 1 } }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:nikolite_dust" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/deepslate_silver_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:deepslate_silver_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:raw_silver" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/deepslate_tin_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:deepslate_tin_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:raw_tin" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/deepslate_tungsten_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:deepslate_tungsten_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:raw_tungsten" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/drain_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:drain_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/drill_bottom.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:drill_bottom" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/duct.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:duct" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:electric_furnace_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_factory_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:electric_furnace_factory_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:electric_furnace_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:electric_furnace_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:electric_furnace_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:electric_furnace_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/electrum_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:electrum_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/farmer_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:farmer_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/farmer_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:farmer_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/farmer_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:farmer_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/farmer_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:farmer_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/farmer_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:farmer_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fisher_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fisher_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fisher_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fisher_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fisher_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fisher_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_infuser_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_infuser_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_infuser_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_infuser_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_infuser_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_pipe_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_pipe_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_pipe_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_pipe_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_pipe_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_pipe_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/fluid_pipe_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:fluid_pipe_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/frame.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:frame" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/heat_generator_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:heat_generator_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/intake.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:intake" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/item_pipe_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:item_pipe_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/item_pipe_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:item_pipe_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/item_pipe_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:item_pipe_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/item_pipe_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:item_pipe_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/laser_emitter_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:laser_emitter_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:lazuli_flux_container_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:lazuli_flux_container_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:lazuli_flux_container_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:lazuli_flux_container_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:lazuli_flux_container_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/lead_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:lead_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/lead_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:lead_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:raw_lead" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/machine_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:machine_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/mining_rig_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:mining_rig_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/modular_workbench_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:modular_workbench_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/nikolite_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:nikolite_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:set_count", "count": { "min": 4.0, "max": 5.0, "type": "minecraft:uniform" } }, { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:uniform_bonus_count", "parameters": { "bonusMultiplier": 1 } }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:nikolite_dust" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/plank_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:plank_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/planks.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:alternatives", "conditions": [ { "condition": "minecraft:inverted", "term": { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } } ], "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "1" } } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "2" } } ], "functions": [ { "function": "minecraft:set_count", "count": 2 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "3" } } ], "functions": [ { "function": "minecraft:set_count", "count": 3 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "4" } } ], "functions": [ { "function": "minecraft:set_count", "count": 4 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "5" } } ], "functions": [ { "function": "minecraft:set_count", "count": 5 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "6" } } ], "functions": [ { "function": "minecraft:set_count", "count": 6 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "7" } } ], "functions": [ { "function": "minecraft:set_count", "count": 7 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "name": "indrev:plank_block" } ] }, { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "1" } } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "2" } } ], "functions": [ { "function": "minecraft:set_count", "count": 2 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "3" } } ], "functions": [ { "function": "minecraft:set_count", "count": 3 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "4" } } ], "functions": [ { "function": "minecraft:set_count", "count": 4 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "5" } } ], "functions": [ { "function": "minecraft:set_count", "count": 5 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "6" } } ], "functions": [ { "function": "minecraft:set_count", "count": 6 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:block_state_property", "block": "indrev:planks", "properties": { "layers": "7" } } ], "functions": [ { "function": "minecraft:set_count", "count": 7 } ], "name": "indrev:planks" }, { "type": "minecraft:item", "name": "indrev:plank_block" } ] } ] } ], "conditions": [ { "condition": "minecraft:entity_properties", "predicate": {}, "entity": "this" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/pulverizer_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:pulverizer_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/pulverizer_factory_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:pulverizer_factory_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/pulverizer_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:pulverizer_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/pulverizer_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:pulverizer_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/pulverizer_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:pulverizer_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/pulverizer_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:pulverizer_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/pump_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:pump_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/rancher_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:rancher_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/rancher_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:rancher_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/rancher_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:rancher_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/rancher_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:rancher_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/rancher_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:rancher_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/raw_lead_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:item", "name": "indrev:raw_lead_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/raw_silver_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:item", "name": "indrev:raw_silver_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/raw_tin_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:item", "name": "indrev:raw_tin_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/raw_tungsten_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:item", "name": "indrev:raw_tungsten_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/recycler_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:recycler_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/sawmill_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:sawmill_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/sawmill_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:sawmill_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/sawmill_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:sawmill_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/sawmill_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:sawmill_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/sawmill_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:sawmill_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/silo.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:silo" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/silver_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:silver_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/silver_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:silver_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:raw_silver" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/slaughter_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:slaughter_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/slaughter_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:slaughter_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/slaughter_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:slaughter_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/slaughter_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:slaughter_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/slaughter_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:slaughter_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/smelter_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:smelter_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/solar_generator_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:solar_generator_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/solar_generator_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:solar_generator_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_creative.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "indrev:solid_infuser_creative" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_factory_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:solid_infuser_factory_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_mk1.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:solid_infuser_mk1" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_mk2.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:solid_infuser_mk2" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_mk3.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:solid_infuser_mk3" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_mk4.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:solid_infuser_mk4" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/steel_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:steel_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/sulfur_crystal.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "functions": [ { "function": "minecraft:set_count", "count": { "min": 1.0, "max": 3.0, "type": "minecraft:uniform" } }, { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:uniform_bonus_count", "parameters": { "bonusMultiplier": 2 } }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:sulfur_crystal" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/tank.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:tank" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/tin_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:tin_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/tin_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:tin_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:raw_tin" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/tungsten_block.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:tungsten_block" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/tungsten_ore.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:alternatives", "children": [ { "type": "minecraft:item", "conditions": [ { "condition": "minecraft:match_tool", "predicate": { "enchantments": [ { "enchantment": "minecraft:silk_touch", "levels": { "min": 1 } } ] } } ], "name": "indrev:tungsten_ore" }, { "type": "minecraft:item", "functions": [ { "function": "minecraft:apply_bonus", "enchantment": "minecraft:fortune", "formula": "minecraft:ore_drops" }, { "function": "minecraft:explosion_decay" } ], "name": "indrev:raw_tungsten" } ] } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/warning_strobe.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:warning_strobe" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/loot_tables/blocks/wither_proof_obsidian.json ================================================ { "type": "minecraft:block", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "indrev:wither_proof_obsidian" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/book.json ================================================ { "name": "item.patchouli.industrial_revolution_book.name", "landing_text": "item.patchouli.industrial_revolution_book.landing", "model": "indrev:guide_book", "version": 1, "subtitle": "Everything you, $(playername), need to know about $(l)$(o)Industrial Revolution", "creative_tab": "indrev.indrev_group", "show_progress": false, "pause_game": false } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/enhanced_ore_processing.json ================================================ { "name": "Enhanced Ore Processing", "sortnum": 6, "description": "One of the many features presented by Industrial Revolution is it's capability for even quadrupling ores.", "icon": "indrev:copper_chunk" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/machines.json ================================================ { "name": "Machines", "sortnum": 1, "description": "Machines are the main focus for this mod. The following pages will show you their recipes and provide a little more information about them.", "icon": "indrev:electric_furnace_mk1" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/natural_resources.json ================================================ { "name": "Natural Resources", "sortnum": 0, "description": "Information about needed natural resources to explore the Industrial Revolution.", "icon": "indrev:tin_ore" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/temperature.json ================================================ { "name": "Temperature", "sortnum": 4, "description": "Some machines will present features like temperature, which can be checked inside it's interface. This section will talk more about it.", "icon": "indrev:fan" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/tools.json ================================================ { "name": "Tools", "sortnum": 5, "description": "Tools are also important in this mod.", "icon": "indrev:hammer" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/transportation.json ================================================ { "name": "Transportation", "sortnum": 2, "description": "Industrial Revolution also provides you ways of transporting energy, items and fluids through your world!", "icon": "indrev:cable_mk1" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/upgrades.json ================================================ { "name": "Upgrades", "sortnum": 3, "description": "Upgrades can boost machines stats.", "icon": "indrev:speed_enhancer" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/enhanced_ore_processing/doubling.json ================================================ { "name": "Doubling Ores", "category": "indrev:enhanced_ore_processing", "sortnum": 0, "icon": "indrev:pulverizer_mk1", "pages": [ { "type": "text", "text": "Ore doubling is the first step for enhanced ore processing.$(br)To get started, you'll need a $(l:machines/basic_machines#pulverizer)$(t:Go to Pulverizer)$(l)$(o)Pulverizer$().$(br)The Pulverizer will turn 1 ore into 2 dusts which can then be smelted into 2 ingots." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/enhanced_ore_processing/quadrupling.json ================================================ { "name": "Quadrupling Ores", "category": "indrev:enhanced_ore_processing", "sortnum": 2, "icon": "indrev:fluid_infuser_mk4", "pages": [ { "type": "text", "text": "Ore quadrupling is the last step for enhanced ore processing. For this, you'll need a $(l:machines/basic_machines#fluid_infuser)$(t:Go to Fluid Infuser)$(l)$(o)Fluid Infuser$(), a $(l:machines/basic_machines#smelter)$(t:Go to Industrial Smelter)$(l)$(o)Industrial Smelter$(), a $(l:machines/basic_machines#condenser)$(t:Go to Condenser)$(l)$(o)Condenser$(), a $(l:machines/basic_machines#pulverizer)$(t:Go to Pulverizer)$(l)$(o)Pulverizer$() and plenty of Sulfuric Acid.$(br)$(#3ead36)Sulfuric Acid$() can be found in lake forms at Swamps and can be crafted from $(#b3b332)Sulfur Dust$() + $(#88adf2)Water$() inside a Fluid Infuser." }, { "type": "text", "text": "$(l)1)$() Insert your ore in a Fluid Infuser with Sulfuric Acid. It will purify them and give you the purified ore form. $(br)$(l)2)$() Insert your purified ores into an Industrial Smelter. It will smelt them into their fluid forms.$(br)$(l)3)$() Move your molten ore into a Condenser.$(br)$(l)4)$() Pulverize your Ore Chunk, which will then give you a dust.$(br)$(l)5)$() Smelt your dust.$(br2)Done! Now you've transformed 1 ore into 4 ingots!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/enhanced_ore_processing/tripling.json ================================================ { "name": "Tripling Ores", "category": "indrev:enhanced_ore_processing", "sortnum": 1, "icon": "indrev:smelter_mk4", "pages": [ { "type": "text", "text": "Ore tripling is the next step for enhanced ore processing. For this, you'll need a $(l:machines/basic_machines#smelter)$(t:Go to Industrial Smelter)$(l)$(o)Industrial Smelter$(), a $(l:machines/basic_machines#condenser)$(t:Go to Condenser)$(l)$(o)Condenser$() and a $(l:machines/basic_machines#pulverizer)$(t:Go to Pulverizer)$(l)$(o)Pulverizer$()." }, { "type": "text", "text": "$(l)1)$() Insert your ores into an Industrial Smelter. It will smelt them into their fluid forms.$(br)$(l)2)$() Move your molten ore into a Condenser. It will solidify them into a Ore Chunk.$(br)$(l)3)$() Pulverize your Ore Chunk, which will then give you a dust.$(br)$(l)4)$() Smelt your dust.$(br2)Done! Now you've transformed 1 ore into 3 ingots!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/basic_machines.json ================================================ { "name": "Basic Machines", "category": "indrev:machines", "sortnum": 1, "icon": "indrev:electric_furnace_mk1", "pages": [ { "type": "text", "text": "These machines are used to transform resources into other more complex resources." }, { "type": "crafting", "anchor": "electric_furnace", "recipe": "indrev:shaped/electric_furnace_mk1", "text": "The $(l)$(o)Electric Furnace $()does everything a normal furnace does but using Lazuli Flux instead of coal." }, { "type": "crafting", "anchor": "pulverizer", "recipe": "indrev:shaped/pulverizer_mk1", "text": "The $(l)$(o)Pulverizer $()is mainly used for processing ores into two dusts." }, { "type": "crafting", "anchor": "compressor", "recipe": "indrev:shaped/compressor_mk1", "text": "The $(l)$(o)Compressor $()is mainly used for compressing ingots into plates." }, { "type": "crafting", "anchor": "infuser", "recipe": "indrev:shaped/solid_infuser_mk1", "text": "The $(l)$(o)Solid Infuser $()has the capability of mixin two ingredients into a different one." }, { "type": "crafting", "anchor": "fluid_infuser", "recipe": "indrev:shaped/fluid_infuser_mk1", "text": "The $(l)$(o)Fluid Infuser $()is used for advanced infusing with fluids. It's main current use is for ore quadrupling." }, { "type": "crafting", "anchor": "condenser", "recipe": "indrev:shaped/condenser_mk4", "text": "The $(l)$(o)Condenser $()is used for transforming molten ores into chunks which can be pulverized and then smelted into ingots." }, { "type": "crafting", "anchor": "smelter", "recipe": "indrev:shaped/smelter_mk4", "text": "The $(l)$(o)Industrial Smelter $()is mainly used to smelt ores into liquid. It's mostly used for enhanced ore processing." }, { "type": "crafting", "anchor": "recycler", "recipe": "indrev:shaped/recycler_mk2", "text": "The $(l)$(o)Recycler $()can process organic resources into biomass which can be used for energy generation." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/batteries.json ================================================ { "name": "Lazuli Flux Containers", "category": "indrev:machines", "sortnum": 3, "icon": "indrev:lazuli_flux_container_mk1", "pages": [ { "type": "text", "text": "These machines are used to store Lazuli Flux.$(br)You can see blue and orange arrows on each side. Blue arrows represent energy input and orange arrows represent energy output. You can configure these arrows using a Screwdriver." }, { "type": "crafting", "recipe": "indrev:shaped/lazuli_flux_container_mk1", "recipe2": "indrev:shaped/lazuli_flux_container_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/lazuli_flux_container_mk3", "recipe2": "indrev:shaped/lazuli_flux_container_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/cables.json ================================================ { "name": "Cables", "category": "indrev:machines", "sortnum": 2, "icon": "indrev:cable_mk1", "pages": [ { "type": "text", "text": "These machines are used to transfer Lazuli Flux to great distances." }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk1", "recipe2": "indrev:shaped/cable_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk3", "recipe2": "indrev:shaped/cable_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/chopper.json ================================================ { "name": "Chopper", "category": "indrev:machines", "sortnum": 5, "icon": "indrev:chopper_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/chopper_mk1", "text": "The $(l)Chopper$() is a machine focused in automating wood farming, and it also supports enhancement upgrades.$(br)Note that it requires certain items for it to function: (put them in the 2x2 square)" }, { "type": "text", "text": "$(l)Axe:$() if present in the inventory, will retrieve any wood and leaves in range (leaves will not consume durability)$(br2)$(l)Sapling:$() if present in the inventory, will be planted in every block possible inside it's range.$(br2)$(l)Bone meal:$() will be applied to any saplings inside it's range." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/factories.json ================================================ { "name": "Factories", "category": "indrev:machines", "sortnum": 9, "icon": "indrev:electric_furnace_factory_mk4", "pages": [ { "type": "text", "text": "$(l)Factories$() are improved versions of machines and they can process 5 items at once. They are also smart enough to split your items if you'd like.$(br2)Given their powerful state, it's not possible to insert this much power into a single block, which means you'll need to build a structure." }, { "type": "text", "text": "$(l)Required blocks$()$(br2)$(li)$(l)9x$() Frames$(br)$(li)$(l)6x$() Silos$(br)$(li)$(l)3x$() Intakes$(br)$(li)$(l)3x$() Ducts$(br)$(li)$(l)2x$() Cabinets$(br)$(li)$(l)1x$() Warning Strobe$(br)$(li)$(l)1x$() Controller$()$(br2)You'll be able to visualize the block positions by right clicking a factory." }, { "type": "crafting", "recipe": "indrev:shaped/frame", "recipe2": "indrev:shaped/silo" }, { "type": "crafting", "recipe": "indrev:shaped/intake", "recipe2": "indrev:shaped/duct" }, { "type": "crafting", "recipe": "indrev:shaped/cabinet", "recipe2": "indrev:shaped/warning_strobe" }, { "type": "crafting", "recipe": "indrev:shaped/controller" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/farmer.json ================================================ { "name": "Farmer", "category": "indrev:machines", "sortnum": 7, "icon": "indrev:farmer_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/farmer_mk1", "text": "The $(l)Farmer$() is a machine focused in crop farming, and it also supports enhancement upgrades.$(br)Note that it requires some items for it to function: (put them in the 2x2 square)" }, { "type": "text", "text": "$(l)Seeds:$() if present will be planted in the farmer's range. Note that it will try to match a crop and its seeds so you can use multiple crops.$(br2)$(l)Bone meal:$() if present, will fertilize any crops possible within range." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/fisher.json ================================================ { "name": "Fisher", "category": "indrev:machines", "sortnum": 8, "icon": "indrev:fisher_mk2", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/fisher_mk2", "text": "The $(l)Fisher$() is a machine focused in fish farming, and it also supports enhancement upgrades.$(br)It's only requirement is a Fishing Rod. Note that enchantments are supported." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/generators.json ================================================ { "name": "Generators", "category": "indrev:machines", "sortnum": 0, "icon": "indrev:coal_generator_mk1", "pages": [ { "type": "text", "text": "$(l)$(o)Generators $()use other resources to create energy known as Lazuli Flux for your machines" }, { "type": "crafting", "recipe": "indrev:shaped/coal_generator_mk1", "text": "The $(l)$(o)Coal Generator $()can use, not only coal, but anything a furnace can to generate energy." }, { "type": "crafting", "recipe": "indrev:shaped/solar_generator_mk1", "recipe2": "indrev:shaped/solar_generator_mk3" }, { "type": "text", "text": "The $(l)$(o)Solar Generator $()will generate energy as long as the sunlight can reach it." }, { "type": "crafting", "recipe": "indrev:shaped/biomass_generator_mk3", "text": "The $(l)$(o)Biomass Generator $()will burn $(#00ff00)biomass $()to generate energy." }, { "type": "crafting", "recipe": "indrev:shaped/heat_generator", "text": "The $(l)$(o)Heat Generator $()will consume lava inside it to generate energy. Pairs up well with the pump." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/miner.json ================================================ { "name": "Mining Rig Computer", "category": "indrev:machines", "sortnum": 4, "icon": "indrev:mining_rig_mk4", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/mining_rig_mk4", "text": "The Mining Rig Computer has been developed to create virtual dimensions based on ore data cards, allowing you to harvest ores without modifying your world." }, { "type": "crafting", "recipe": "indrev:shaped/drill_bottom", "text": "Mining Rig Drills are required for the Mining Rig Computer to work. They have to be placed next to the Computer and require drill heads to work. Each Drill increases the mining speed.$(br)Screenshot of a functional setup:" }, { "type": "image", "title": "Example", "images": ["indrev:textures/gui/mrc_setup.png"], "text": "Note that the Drills can be placed connected or diagonally to the Mining Rig Computer." }, { "type": "crafting", "recipe": "indrev:shaped/ore_data_card", "text": "Ore Data Cards store the data necessary for the Mining Rig Computer to virtualize a dimension based on the data written on it where it mines the ores." }, { "type": "crafting", "recipe": "indrev:shaped/data_card_writer_mk4", "text": "The Data Card Encoder will encode all the ores and modifiers into your Ore Data Card. You can write multiple times in a card to encode more data." }, { "type": "text", "text": "When looking inside a Data Card Encoder, the ores and modifiers it can encode will be highlighted in your inventory.$(br2)When encoding, some attributes will change in your card, like richness or size.$(br2)$(br)Tip: search \"#indrev:mining_rig_allowed\" on REI to see all allowed ores!" }, { "type": "text", "text": "$(l)Attributes$() can be observed on your data card tooltip.$(br)These attributes can then be improved by modifiers." }, { "type": "text", "text": "$(l)Storage size$() can be seen by the circle UI in the tooltip. The blue circle represents how many cycles the Mining Rig Computer has left to search on the virtual dimension, which will then turn the circle red. At the end of each cycle, the ores found will be added to a connected inventory.$(br2)Each data slot on the Data Card Encoder will increase the total cycles on the card. It can also be increased by adding Stone as a modifier." }, { "type": "text", "text": "$(l)Types$() represents the ores that can be generated from this card. Note that the chance of generating each ore is proportional to the amount of the respective ore that was encoded in the card.$(br2)$(#FF1111)Be careful$(), encoding too many types can cause your data card to overflow and be unusable." }, { "type": "text", "text": "$(l)Richness$() represents the maximum amount of ores that can be generated from each cycle. $(br)This can be increased by adding $(#5300de)Enriched Nikolite Dust$() to the modifier slots." }, { "type": "text", "text": "$(l)Cycles$() information can be seen on the tooltip. After each cycle the ores will be inserted into a nearby inventory.$(br2)You can see the cycle progress on the Mining Rig Computer screen.$(br)Better Drill Heads can complete cycles faster." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/pump.json ================================================ { "name": "Pump", "category": "indrev:machines", "sortnum": 10, "icon": "indrev:pump_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/pump_mk1", "text": "The $(l)Pump$() is used to pump fluids placed on the world into fluid pipes and tanks." }, { "type": "text", "text": "$(l)Usage:$() You must input energy from the top and take the fluid from the side it is facing. After receiving energy, it will drop down a pipe looking for fluids to pump.$(br2)There is no hard limit, except for the fluid's physics. As long as there is fluid touching the pump's pipe, source or not, it will look for a source block to pump. This means it can be used to drain lava lakes from the Nether." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/rancher.json ================================================ { "name": "Rancher", "category": "indrev:machines", "sortnum": 6, "icon": "indrev:rancher_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/rancher_mk1", "text": "The $(l)Rancher$() is a machine focused in automating animal farming, and it also supports enhancement upgrades.$(br)Note that it requires some items for it to function: (put them in the 2x2 square)" }, { "type": "text", "text": "$(l)Sword:$() if present in the inventory, will kill animals if there are more than 7 in its range.$(br2)$(l)Wheat/Seed/Carrot:$() if present in the inventory, will be fed to the respective animals in it's range.$(br2)$(l)Bucket:$() if present in the inventory, will collect milk from cows." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/lead_ore.json ================================================ { "name": "Lead Ore", "category": "indrev:natural_resources", "sortnum": 3, "icon": "indrev:lead_ore", "pages": [ { "type": "spotlight", "item": "indrev:lead_ore", "text": "$(#575d61)$(l)Lead ore$() is uncommon and can be found from the layer 0 to 32 in both stone and deepslate variants.$(br)$(#575d61)$(l)Lead$() is used in the crafting of more advanced machines." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/nikolite_ore.json ================================================ { "name": "Nikolite Ore", "category": "indrev:natural_resources", "sortnum": 5, "icon": "indrev:nikolite_ore", "pages": [ { "type": "spotlight", "item": "indrev:nikolite_ore", "text": "$(#008f8c)$(l)Nikolite ore$() is common and can be found from the layer 0 to 16 in both stone and deepslate variants.$(br)$(#008f8c)$(l)Nikolite$() is the main component used in $(l)INDREV$()'s machines, from basic to advanced." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/silver_ore.json ================================================ { "name": "Silver Ore", "category": "indrev:natural_resources", "sortnum": 2, "icon": "indrev:silver_ore", "pages": [ { "type": "spotlight", "item": "indrev:lead_ore", "text": "$(#c6e9e3)$(l)Silver ore$() is uncommon and can be found from the layer 0 to 32 in both stone and deepslate variants.$(br)$(#c6e9e3)$(l)Silver$() is used in the crafting of more advanced machines." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/sulfur_crystals.json ================================================ { "name": "Sulfur Crystal", "category": "indrev:natural_resources", "sortnum": 6, "icon": "indrev:sulfur_crystal", "pages": [ { "type": "spotlight", "item": "indrev:sulfur_crystal", "text": "$(#f5da68)$(l)Sulfur Crystals$() are common and can be found from the layer 0 to 16 in the overworld or up to layer 100 in the nether.$(br)$(#f5da68)$(l)Sulfur$() is used in the purification process of the advanced ore processing chain." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/tin_ore.json ================================================ { "name": "Tin Ore", "category": "indrev:natural_resources", "sortnum": 1, "icon": "indrev:tin_ore", "pages": [ { "type": "spotlight", "item": "indrev:tin_ore", "text": "$(#999999)$(l)Tin ore$() is common and can be found from the layer 0 to 48 in both stone and deepslate variants.$(br)$(#999999)$(l)Tin$() is heavily used in the crafting of machines." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/tungsten_ore.json ================================================ { "name": "Tungsten Ore", "category": "indrev:natural_resources", "sortnum": 4, "icon": "indrev:deepslate_tungsten_ore", "pages": [ { "type": "spotlight", "item": "indrev:deepslate_tungsten_ore", "text": "$(#686b5b)$(l)Tungsten ore$() is rare and can be found from the layer 0 to 16 just deepslate variant.$(br)$(#686b5b)$(l)Tungsten$() is used in the crafting of resistant items like the Modular Armor." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/temperature/coolers_and_fans.json ================================================ { "name": "Keeping it cool!", "category": "indrev:temperature", "sortnum": 1, "icon": "indrev:cooler_cell", "pages": [ { "type": "text", "text": "Coolers and fans will make the machine's temperature cool faster stay on its most efficient state even while it happens." }, { "type": "crafting", "recipe": "indrev:shaped/fan", "text": "$(o)Sir, can I have your autograph? Please please pleaseeeeeeee$()" }, { "type": "crafting", "recipe": "indrev:shaped/cooler_cell", "text": "$(o)Lame... I'm too cool for this$()" }, { "type": "crafting", "recipe": "indrev:shaped/heatsink", "text": "$(o)What? Were you expecting a pun? Sorry, no puns here Sir.$()" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/temperature/info.json ================================================ { "name": "How does it work?", "category": "indrev:temperature", "sortnum": 0, "icon": "indrev:fan", "pages": [ { "type": "text", "text": "$(l)INDREV's$() scientists have done a good job at making machines smart enough not to explode and to benefit from temperature. Every machine has an optimal temperature which will never exceed." }, { "type": "text", "text": "In order for the machine to stay on its optimal temperature, it needs to cool down often. While it's cooling down, it will $(l)not$() have the efficiency boost.$(br)Check out $(l:temperature/coolers_and_fans)$(t:Go to Keeping it Cool!)Keeping it cool!$() to have 100% efficiency all the time!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/energy_reader.json ================================================ { "name": "Energy Reader", "category": "indrev:tools", "sortnum": 4, "icon": "indrev:energy_reader", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/energy_reader", "text": "Tells you the current stored energy in machines and, in some machines, also tells the energy consumption per tick." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/gamer_axe.json ================================================ { "name": "Gamer Axe", "category": "indrev:tools", "sortnum": 6, "icon": "indrev:gamer_axe", "pages": [ { "type": "text", "text": "The Gamer Axe is the final gaming tool. It mines fast and deals great amount of damage when modules are installed.$(br)It may seem like just a stick, but wait until you activate it...$(br2)Use Right Click to activate it." }, { "type": "crafting", "recipe": "indrev:shaped/gamer_axe" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/hammer.json ================================================ { "name": "Hammer", "category": "indrev:tools", "sortnum": 0, "icon": "indrev:hammer", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/hammer", "text": "Used to make plates out of ingots when you still don't have a compressor!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/mining_drills.json ================================================ { "name": "Mining Drills", "category": "indrev:tools", "sortnum": 3, "icon": "indrev:mining_drill_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/mining_drill_mk1", "recipe2": "indrev:shaped/mining_drill_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/mining_drill_mk3", "recipe2": "indrev:shaped/mining_drill_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/modular_armor.json ================================================ { "name": "Modular Armor", "category": "indrev:tools", "sortnum": 5, "icon": "indrev:modular_armor_helmet", "pages": [ { "type": "text", "text": "The Modular Armor is, on it's own, not much, but when you install modules, it can become very powerful!$(br2)Color modules do not have recipes, you must find them in dungeons." }, { "type": "text", "text": "Module levels can be configured any way you want, go into the $(l)Controls$() menu and add a keybind to \"Modular item configuration.\"" }, { "type": "crafting", "recipe": "indrev:shaped/modular_armor_helmet", "recipe2": "indrev:shaped/modular_armor_chest" }, { "type": "crafting", "recipe": "indrev:shaped/modular_armor_legs", "recipe2": "indrev:shaped/modular_armor_boots" }, { "type": "crafting", "recipe": "indrev:shaped/modular_workbench", "text": "The modular workbench is used to install modules onto Modular Armor parts. Notice that installing modules cost time and some energy!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/modular_core.json ================================================ { "name": "Core of Modularity", "category": "indrev:tools", "sortnum": 7, "icon": "indrev:modular_core", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/modular_core", "text": "The Core of Modularity is the base for every modular tool here. However, it must be activated before being usable. To activate it, you'll need at least one Laser, one Capsule and a lot of stored energy (100M)." }, { "type": "crafting", "recipe": "indrev:shaped/capsule", "text": "The Capsule will hold your Core of Modularity during the process of activation.$(br)It will emit a redstone signal when it's finished." }, { "type": "crafting", "recipe": "indrev:shaped/laser_emitter_mk4", "text": "Laser Emitters should be placed facing the capsule with $(l)AN EXACT 3 BLOCKS DISTANCE$(). They store up to 2,5M LF and require a redstone signal to be toggled on." }, { "type": "text", "text": "This process takes time but it goes faster for each laser emitter active. You must pay attention to these details to avoid problems:$(br)- Pointing the laser emitter to something other than the capsule or and empty capsule will result in explosion.$(br)- If the laser emitters run out of power during the process, all progress will be lost.$(br)- Make sure to turn off your laser emitters before retrieving the activated core." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/screwdriver.json ================================================ { "name": "Screwdriver", "category": "indrev:tools", "sortnum": 2, "icon": "indrev:screwdriver", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/screwdriver", "text": "The Screwdriver is used to configure machines input and outputs for items and fluids.$(br)Can also be used on Lazuli Flux Container to configurate energy sides." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/wrench.json ================================================ { "name": "Wrench", "category": "indrev:tools", "sortnum": 1, "icon": "indrev:wrench", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/wrench", "text": "The Wrench can be used to rotate blocks and break machines keeping their energy when using it while crouching." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/transportation/energy.json ================================================ { "name": "Energy", "category": "indrev:transportation", "sortnum": 0, "icon": "indrev:cable_mk1", "pages": [ { "type": "text", "text": "Cables can be used to transfer power between machines far apart from each other.$(br)Each cable can transfer different amount of powers, which are described on the tooltip when Shift is pressed.$(br)If your machines are running low on power, you might try upgrading your cables or improving your power generation." }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk1", "recipe2": "indrev:shaped/cable_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk3", "recipe2": "indrev:shaped/cable_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/transportation/fluid_pipes.json ================================================ { "name": "Fluids", "category": "indrev:transportation", "sortnum": 2, "icon": "indrev:fluid_pipe_mk1", "pages": [ { "type": "text", "text": "Fluid pipes can be used to transfer fluids between tanks far apart.$(br)Check the tooltips to see the amount of fluid that can be transferred.$(br)Pipes only transfer every second rather than every tick.$(br2)Servos are required, read the $(l:transportation/servos)$(t:Go to Servos)Servos$() chapter." }, { "type": "crafting", "recipe": "indrev:shaped/fluid_pipe_mk1", "recipe2": "indrev:shaped/fluid_pipe_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/fluid_pipe_mk3", "recipe2": "indrev:shaped/fluid_pipe_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/transportation/item_pipes.json ================================================ { "name": "Items", "category": "indrev:transportation", "sortnum": 1, "icon": "indrev:item_pipe_mk1", "pages": [ { "type": "text", "text": "Item pipes can be used to transfer items between inventories far apart.$(br)Check the tooltips to see the amount of items that can be transferred.$(br)Pipes only transfer every second rather than every tick.$(br)Item pipes have $(l)advanced filtering$(), Right Click the pipe to see it.$(br2)Servos are required, read the $(l:transportation/servos)$(t:Go to Servos)Servos$() chapter." }, { "type": "crafting", "recipe": "indrev:shaped/item_pipe_mk1", "recipe2": "indrev:shaped/item_pipe_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/item_pipe_mk3", "recipe2": "indrev:shaped/item_pipe_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/transportation/servos.json ================================================ { "name": "Servos", "category": "indrev:transportation", "sortnum": 3, "icon": "indrev:servo_retriever", "pages": [ { "type": "text", "text": "Item and fluid pipes require servos to function. There are two types of servo: Output and Retriever.$(br)Click on the pipe connected to your container to attach a servo. Use a wrench to remove it.$(br)$(l)Important:$() The Output and Retriever servos ARE INDEPENDENT. You do not need both for your pipes to work. They have different purposes." }, { "type": "text", "text": "For example, if you have a single inventory that needs to output to multiple inventories you just need a single Output Servo on the dispensing end.$(br2)If you have multiple inventories that needs to output to a single inventory you just need a single Retriever Servo on the receiving end.$(br2)$(l)IMPORTANT:$() make sure the machine is configured. Read $(l:tools/screwdriver)$(t:Go to Screwdriver!)the Screwdriver chapter!$()" }, { "type": "text", "title": "Modes", "text": "Servos have multiple modes to define priorities.$(br)Click on the servo to change it.$(br)$(l)Nearest first:$() the servo will look for the closest container.$(br)$(l)Furthest first:$() the servo will look for the furthest container.$(br)$(l)Random:$() the servo will chose a random container.$(br)$(l)Round Robin$() the servo will look for the container with the least amount of the transferred fluid/item." }, { "type": "crafting", "recipe": "indrev:shaped/servo_output", "text": "The Output Servo will push items/fluids to nearby containers UNLESS they are also marked as output." }, { "type": "crafting", "recipe": "indrev:shaped/servo_retriever", "text": "The Retriever Servo will pull items/fluids from nearby UNLESS they are also marked as retriever." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/upgrades/enhancement_upgrades.json ================================================ { "name": "Enhancers", "category": "indrev:upgrades", "sortnum": 0, "icon": "indrev:speed_enhancer", "pages": [ { "type": "text", "text": "These upgrades will improve certain aspects of the machine like speed and energy usage.$(br2)The machine will use upgrades inserted in the 4 right-side slots on its interface." }, { "type": "crafting", "recipe": "indrev:shaped/speed_enhancer", "text": "This upgrade will increase the machine's processing speed at cost of energy." }, { "type": "crafting", "recipe": "indrev:shaped/buffer_enhancer", "text": "This upgrade will increase the machine's internal energy capacity." }, { "type": "crafting", "recipe": "indrev:shaped/blast_furnace_enhancer", "text": "This upgrade will make the Electric Furnace accept Blast Furnace recipes." }, { "type": "crafting", "recipe": "indrev:shaped/smoker_enhancer", "text": "This upgrade will make the Electric Furnace accept Smoker recipes." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/upgrades/tier_upgrades.json ================================================ { "name": "Tier Upgrades", "category": "indrev:upgrades", "sortnum": 1, "icon": "indrev:tier_upgrade_mk2", "pages": [ { "type": "text", "text": "These upgrades will improve the machine's input and output capability as well as other aspects.$(br2)Right clicking on a machine below the upgrade's tier will upgrade it." }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk2", "text": "Upgrades MK1 machines to MK2." }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk3", "text": "Upgrades MK2 machines to MK3." }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk4", "text": "Upgrades MK3 machines to MK4." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/book.json ================================================ { "name": "item.patchouli.industrial_revolution_book.name", "landing_text": "item.patchouli.industrial_revolution_book.landing", "model": "indrev:guide_book", "version": 1, "subtitle": "Всё, что тебе, $(playername), положено знать о $(l)$(o)Industrial Revolution.", "creative_tab": "indrev.indrev_group", "show_progress": false, "pause_game": false } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/enhanced_ore_processing.json ================================================ { "name": "Улучшенная переработка руды", "sortnum": 6, "description": "Одна из многих особенностей, представленных Industrial Revolution — это способность даже учетверять руды.", "icon": "indrev:copper_chunk" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/machines.json ================================================ { "name": "Станки", "sortnum": 1, "description": "Станки являются основным фокусом данного мода. Следующие страницы покажут тебе их рецепты и предоставят чуть больше информации о них.", "icon": "indrev:electric_furnace_mk1" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/natural_resources.json ================================================ { "name": "Полезные ископаемые", "sortnum": 0, "description": "Информация о необходимых полезных ископаемых для изучения Industrial revolution.", "icon": "indrev:tin_ore" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/temperature.json ================================================ { "name": "Температура", "sortnum": 4, "description": "В некоторых машин присутствуют такие характеристики, как температура, которую можно проверить внутри интерфейса. Этот раздел расскажет о ней больше.", "icon": "indrev:fan" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/tools.json ================================================ { "name": "Инструменты", "sortnum": 5, "description": "Инструменты также являются немаловажными в этом моде.", "icon": "indrev:hammer" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/transportation.json ================================================ { "name": "Транспортирование", "sortnum": 2, "description": "Industrial Revolution также предусматривает способы транспортировки энергии, предметов и жидкостей!", "icon": "indrev:cable_mk1" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/upgrades.json ================================================ { "name": "Улучшения", "sortnum": 3, "description": "Улучшения могут повысить характеристики машин.", "icon": "indrev:speed_enhancer" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/enhanced_ore_processing/doubling.json ================================================ { "name": "Удваивание руд", "category": "indrev:enhanced_ore_processing", "sortnum": 0, "icon": "indrev:pulverizer_mk1", "pages": [ { "type": "text", "text": "Удваивание руд — это первый шаг для улучшенной переработки руды.$(br)Чтобы начать, тебе понадобится $(l:machines/basic_machines#pulverizer)$(t:Перейди к Измельчителю)$(l)$(o)Измельчитель$().$(br)Измельчитель переработает одну руду в две пыли, чтобы затем их можно было переплавить в два слитка." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/enhanced_ore_processing/quadrupling.json ================================================ { "name": "Учетверение руд", "category": "indrev:enhanced_ore_processing", "sortnum": 2, "icon": "indrev:fluid_infuser_mk4", "pages": [ { "type": "text", "text": "Учетверение руды — это последний шаг для улучшенной переработки руды. Для этого, тебе понадобится $(l:machines/basic_machines#fluid_infuser)$(t:Перейди к Жидкостному настойнику)$(l)$(o)Жидкостный настойник$(), $(l:machines/basic_machines#smelter)$(t:Перейди к Производственной плавильной печи)$(l)$(o)Производственная плавильная печь$(), $(l:machines/basic_machines#condenser)$(t:Перейди к Конденсатору)$(l)$(o)Конденсатор$(), $(l:machines/basic_machines#pulverizer)$(t:Перейди к Измельчителю)$(l)$(o)Измельчитель$() и много Серной кислоты.$(br)$(#3ead36)Серную кислоту$() можно найти в болотных озера, а также её можно создать из $(#b3b332)Серной пыли$() + $(#88adf2)Воды$() в Жидкостном настойнике." }, { "type": "text", "text": "$(l)1)$(). Помести руду в Жидкостный настойник со Серной кислотой. Он их очистит и выдаст очищенную форму руды.$(br)$(l)2)$(). Помести очищенные руды в Производственную плавильную печь. Она выплавит их в жидкие формы.$(br)$(l)3)$(). Перемести переплавленную руду в Конденсатор.$(br)$(l)4)$(). Измельчи рудный кусок, чтобы с него получить пыль.$(br)$(l)5)$() Переплавь пыль.$(br2)Готово! Теперь ты превращаешь одну руду в четыре слитка!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/enhanced_ore_processing/tripling.json ================================================ { "name": "Утроение руд", "category": "indrev:enhanced_ore_processing", "sortnum": 1, "icon": "indrev:smelter_mk4", "pages": [ { "type": "text", "text": "Утроение руды — это следующий шаг для улучшенной переработки руды. Для этого, тебе понадобится (l:machines/basic_machines#smelter)$(t:Перейди к Производственной плавильной печи) $(l)$(o)Производственная плавильная печь$(), $(l:machines/basic_machines#condenser)$(t:Перейди к Конденсатору)$(l)$(o)Конденсатор$() и $(l:machines/basic_machines#pulverizer)$(t:Перейди к Измельчителю)$(l)$(o)Измельчитель$()." }, { "type": "text", "text": "$(l)1)$(). Помести руды в Производственную плавильную печь. Она выплавит их в жидкие формы.$(br)$(l)2)$(). Перемести переплавленную руду в Конденсатор. Он затвердит их в Рудный кусок.$(br)$(l)3)$(). Измельчи рудный кусок, чтобы с него получить пыль.$(br)$(l)4)$(). Переплавь пыль.$(br2)Готово! Теперь ты превращаешь одну руду в три слитка!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/basic_machines.json ================================================ { "name": "Основные машины", "category": "indrev:machines", "sortnum": 1, "icon": "indrev:electric_furnace_mk1", "pages": [ { "type": "text", "text": "Эти машины используются для превращения ресурсов в другие более сложные ресурсы." }, { "type": "crafting", "anchor": "electric_furnace", "recipe": "indrev:shaped/electric_furnace_mk1", "text": "$(l)$(o)Электропечь$() делает то же, что и обычная печь, но с использованием Лазуритного флакса вместо угля." }, { "type": "crafting", "anchor": "pulverizer", "recipe": "indrev:shaped/pulverizer_mk1", "text": "$(l)$(o)Измельчитель$() по большей части используется для обработки руды в две пыли." }, { "type": "crafting", "anchor": "compressor", "recipe": "indrev:shaped/compressor_mk1", "text": "$(l)$(o)Компрессор$() по большей части используется для сжатия слитков в пластины." }, { "type": "crafting", "anchor": "infuser", "recipe": "indrev:shaped/solid_infuser_mk1", "text": "$(l)$(o)Твердотельный настойник$() умеет смешивать два ингредиента в другой." }, { "type": "crafting", "anchor": "fluid_infuser", "recipe": "indrev:shaped/fluid_infuser_mk1", "text": "$(l)$(o)Жидкостный настойник$() используется для Продвинутого вливания при помощи жидкости. В данный момент, обычно он используется для учетверения руды." }, { "type": "crafting", "anchor": "condenser", "recipe": "indrev:shaped/condenser_mk4", "text": "$(l)$(o)Конденсатор$() используется для превращения переплавленных руд в куски, которых можно измельчить, а затем переплавить в слитки." }, { "type": "crafting", "anchor": "smelter", "recipe": "indrev:shaped/smelter_mk4", "text": "$(l)$(o)Производственная плавильная печь$() в основном используется для переплавке руд в жидкие. Она обычно используется для улучшенной переработки руды." }, { "type": "crafting", "anchor": "recycler", "recipe": "indrev:shaped/recycler_mk2", "text": "$(l)$(o)Переработчик$() может обрабатывать органические ресурсы в биомассу, которую можно использовать для генерации энергии." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/batteries.json ================================================ { "name": "Лазуритовые флаксовые резервуары", "category": "indrev:machines", "sortnum": 3, "icon": "indrev:lazuli_flux_container_mk1", "pages": [ { "type": "text", "text": "Резервуары используются для хранения Лазуритного флакса (ЛФ).$(br)Ты можешь увидеть синие и оранжевые стрелки с обеих сторон. Синие стрелки изображают вход энергии, а оранжевые стрелки изображают выход энергии. Ты можешь настроить эти стрелки используя Гаечный ключ в Режиме конфигурации." }, { "type": "crafting", "recipe": "indrev:shaped/lazuli_flux_container_mk1", "recipe2": "indrev:shaped/lazuli_flux_container_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/lazuli_flux_container_mk3", "recipe2": "indrev:shaped/lazuli_flux_container_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/cables.json ================================================ { "name": "Кабели", "category": "indrev:machines", "sortnum": 2, "icon": "indrev:cable_mk1", "pages": [ { "type": "text", "text": "Кабели используются в машинах для передачи Лазуритного флакса на большие расстояния." }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk1", "recipe2": "indrev:shaped/cable_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk3", "recipe2": "indrev:shaped/cable_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/chopper.json ================================================ { "name": "Лесоруб", "category": "indrev:machines", "sortnum": 5, "icon": "indrev:chopper_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/chopper_mk1", "text": "$(l)Лесоруб$() — это машина предназначена для автоматической ферме дерева, а также она поддерживает улучшения.$(br)Заметь, что для её функционирования требуются определённые предметы: (положи их в квадрат 2х2)." }, { "type": "text", "text": "$(l)Топор:$() если имеется в инвентаре, будет извлекать любую древесину и листья в пределах досягаемости (листья не тратят прочность).$(br2)$(l)Саженцы:$() если имеются в инвентаре, будут посажены в каждый блок внутри его диапазона.$(br2)$(l)Костная мука:$() будет ускорять рост саженцев." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/factories.json ================================================ { "name": "Заводы", "category": "indrev:machines", "sortnum": 9, "icon": "indrev:electric_furnace_factory_mk4", "pages": [ { "type": "text", "text": "$(l)Заводы$() — это улучшенная версия машин и они могут обрабатывать до 5 предметов одновременно. Также они достаточно умны для разделения предметов, если ты этого захочешь.$(br2)Если учитывать их мощное состояние, невозможно поместить столько энергии в 1 блок, значит тебе нужно будет построить конструкцию." }, { "type": "text", "text": "$(l)Требуемые блоки:$()$(br2)$(li)$(l)9x$() Каркасы$(br)$(li)$(l)6x$() Силос$(br)$(li)$(l)3x$() Воздушные каналы$(br)$(li)$(l)3x$() Воздуховоды$(br)$(li)$(l)2x$() Корпуса$(br)$(li)$(l)1x$() Предупреждающие лампы$(br)$(li)$(l)1x$() Регулятор$()$(br2)Ты сможешь визуализировать положения блоков нажатием по заводу." }, { "type": "crafting", "recipe": "indrev:shaped/frame", "recipe2": "indrev:shaped/silo" }, { "type": "crafting", "recipe": "indrev:shaped/intake", "recipe2": "indrev:shaped/duct" }, { "type": "crafting", "recipe": "indrev:shaped/cabinet", "recipe2": "indrev:shaped/warning_strobe" }, { "type": "crafting", "recipe": "indrev:shaped/controller" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/farmer.json ================================================ { "name": "Фермер", "category": "indrev:machines", "sortnum": 7, "icon": "indrev:farmer_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/farmer_mk1", "text": "$(l)Фермер$() — эта машина предназначена для земледелия, а также она поддерживает улучшения.$(br)Заметь, что для её функционирования требуются определённые предметы: (положи их в квадрат 2х2)." }, { "type": "text", "text": "$(l)Семена:$() если имеются они будут высаживаться в пределах фермера. Заметь, что он попытается сопоставить урожай и его семена (урожая), поэтому ты можешь использовать несколько посев.$(br2)$(l)Костная мука:$() если имеется, будет удобрять любые возможные посевы в пределах досягаемости." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/fisher.json ================================================ { "name": "Рыболов", "category": "indrev:machines", "sortnum": 8, "icon": "indrev:fisher_mk2", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/fisher_mk2", "text": "$(l)Рыболов$() — эта машина предназначена для рыбоводства, а также она поддерживает улучшения.$(br)Требуется лишь одно — Удочка. Учти, она поддерживает зачарования." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/generators.json ================================================ { "name": "Генераторы", "category": "indrev:machines", "sortnum": 0, "icon": "indrev:coal_generator_mk1", "pages": [ { "type": "text", "text": "$(l)$(o)Генераторы$() используют прочие ресурсы для создания энергии, так называемой Лазуритовый флакс для твоих машин." }, { "type": "crafting", "recipe": "indrev:shaped/coal_generator_mk1", "text": "$(l)$(o)Угольный генератор$() может использовать не только уголь, но и всё, что может генерировать энергию." }, { "type": "crafting", "recipe": "indrev:shaped/solar_generator_mk1", "recipe2": "indrev:shaped/solar_generator_mk3" }, { "type": "text", "text": "$(l)$(o)Солнечный генератор$() производит энергию до тех пор, пока её охватывает солнечный свет." }, { "type": "crafting", "recipe": "indrev:shaped/biomass_generator_mk3", "text": "$(l)$(o)Генератор на биомассе$() сжигает $(#00ff00)Биомассу$() для производства энергии." }, { "type": "crafting", "recipe": "indrev:shaped/heat_generator", "text": "$(l)$(o)Тепловой генератор$() израсходует лаву для производства энергии. Хорошо сочетается с насосом." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/miner.json ================================================ { "name": "Станочная буровая машина", "category": "indrev:machines", "sortnum": 4, "icon": "indrev:mining_rig_mk4", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/mining_rig_mk4", "text": "Когда внутри $(l)$(o)Станочной буровой машины$() размещён $(l:machines/miner#chunk_scanner)$(t:Сканер чанка) он начнёт добывать $(l)$(o)$(#0000ff)просканированный$() чанк за стоимость энергии. Учти, что он не ломает блоки.$(br)Расходы ЭВМ: $(#1111FF)64ЛФ/тик$(). Также каждое сверло увеличит использование энергии на $(#1111FF)256ЛФ/тик$()." }, { "type": "crafting", "recipe": "indrev:shaped/drill_bottom", "text": "Для работы Станочной буровой машина нужны Свёрла бурового станка. Для работы машины ему нужны головки бура, и их нужно разместить параллельно. Каждое сверло увеличивает скорость добычи.$(br)Снимок экрана функциональной системы:" }, { "type": "image", "title": "Пример", "images": ["indrev:textures/gui/mrc_setup.png"], "text": "Учти, что Свёрла можно разместить связано или по диагонали к Станочной буровой машины." }, { "anchor": "chunk_scanner", "type": "crafting", "recipe": "indrev:shaped/chunk_scanner", "text": "$(l)$(o)Сканер чанка$() — это инструмент используемый для сбора географических данных о типе жильного чанка. После сканирования, Сканер предоставит результаты, требующиеся для работы Станочной буровой машины." }, { "type": "text", "text": "$(l)Примечание:$()$(br)Как только прогресс Станочной буровой машины ударит 100%, буровой станок продолжит искать ещё и камень, землю и гравий вместо руды.$(br2)$(br)В данный момент здесь перечислены не все жилы." }, { "anchor": "vein_types", "type": "text", "text": "$(br)$(l:machines/miner#peat)$(t:Перейти к Торфу)Торф$()$(br)$(l:machines/miner#lignite)$(t:Перейти к Бурому углю)Бурый уголь$()$(br)$(l:machines/miner#bituminous)$(t:Перейти к Каменному углю)Каменный уголь$()$(br)$(l:machines/miner#anthracite)$(t:Перейти к Антрациту)Антрацит$()$(br)$(l:machines/miner#siderite)$(t:Перейти к Сидериту)Сидерит$()$(br)$(l:machines/miner#limonite)$(t:Перейти к Лимониту)Лимонит$()$(br)$(l:machines/miner#hematite)$(t:Перейти к Гематиту)Гематит$()$(br)$(l:machines/miner#magnetite)$(t:Перейти к Магнетиту)Магнетит$()$(br)$(l:machines/miner#chalcopryte)$(t:Перейти к Халькопириту)Халькопирит$()$(br)$(l:machines/miner#cuprite)$(t:Перейти к Куприту)Куприт$()$(br)$(l:machines/miner#cassiterite)$(t:Перейти к Касситериту)Касситерит$()$(br)$(l:machines/miner#stannite)$(t:Перейти к Станниту)Станнит$()$(br)$(l:machines/miner#cavalerite)$(t:Перейти к Калавериту)Калаверит$()$(br)$(l:machines/miner#quartz)$(t:Перейти к Кварцу)Кварц$()$(br)$(l:machines/miner#nikolite)$(t:Перейти к Николиту)Николит$()." }, { "anchor": "peat", "type": "spotlight", "title": "Торф", "item": "minecraft:coal_ore", "text": "Обычно состоит из $(#000000)$(l)Угольной руды$(). Это самая наименьшая и беднейшая угольная жила, следовательно ты будешь получать много камня, земли и немного гравия.$(br)Руду можно найти в любых биомах, но ещё в равнинах, тайге, лесах, болотах и ледяных биомах." }, { "anchor": "lignite", "type": "spotlight", "title": "Бурый уголь", "item": "minecraft:coal_ore", "text": "Обычно состоит из $(#000000)$(l)Угольной руды$(). Это вторая наименьшая и беднейшая угольная жила, следовательно здесь всё ещё много камня, земли и гравия.$(br)Руду можно найти в любых биомах, но ещё в равнинах, тайге, лесах, болотах и (дальше не написано)." }, { "anchor": "bituminous", "type": "spotlight", "title": "Каменный уголь", "item": "minecraft:coal_ore", "text": "Обычно состоит из $(#000000)$(l)Угольной руды$(). Это довольно большая и богатая угольная жила, следовательно ты найдёшь больше угля параллельно с камнем, землёй и гравием, но если тебе повезёт, возможно ты тоже найдёшь несколько $(#00ffff)$(l)Алмазов$().$(br)Руду можно найти во многих местах, но ещё в пустынях, холмах, столовых гор, саванах." }, { "anchor": "anthracite", "type": "spotlight", "title": "Антрацит", "item": "minecraft:coal_ore", "text": "Обычно состоит из $(#000000)$(l)Угольной руды$(). Это самая большая и самая богатая угольная жила, следовательно там тоже много угля, параллельно с камнем, землёй, гравием и несколькими $(#00ffff)$(l)Алмазами$().$(br)Руду можно найти во многих местах, но ещё в пустынях, холмах, столовых гор, саванах." }, { "anchor": "siderite", "type": "spotlight", "title": "Сидерит", "item": "minecraft:iron_ore", "text": "Обычно состоит из $(#666666)$(l)Железной руды$(). Это наименьшая и беднейшая железная жила, с кучей камня, землёй и немного гравия.$(br)Руду можно найти во многих местах, но ещё в холмах, равнинах, саванах и лесах." }, { "anchor": "limonite", "type": "spotlight", "title": "Лимонит", "item": "minecraft:iron_ore", "text": "Обычно состоит из $(#666666)$(l)Железной руды$(). Это вторая наименьшая и беднейшая железная руда, здесь много камня, земли и гравия.$(br)Руду можно найти во многих местах, но ещё в холмах, равнинах и пустынях." }, { "anchor": "hematite", "type": "spotlight", "title": "Гематит", "item": "minecraft:iron_ore", "text": "Обычно состоит из $(#666666)$(l)Железной руды$(). Это довольно большая и богатая железная жила, с кучей железа параллельно с камнем, землёй и немного гравия.$(br)Руду можно найти во многих местах, но ещё в тайге и столовых гор." }, { "anchor": "magnetite", "type": "spotlight", "title": "Магнетит", "item": "minecraft:iron_ore", "text": "Обычно состоит из $(#666666)$(l)Железной руды$(). Это самая большая и самая богатая железная жила, с кучей железа, параллельно с камнем, землёй и немного гравия.$(br)Руду можно найти во многих местах, но ещё в холмах, столовых гор и пустынях." }, { "anchor": "chalcopryte", "type": "spotlight", "title": "Халькопирит", "item": "minecraft:copper_ore", "text": "Обычно состоит из $(#FFA500)$(l)Медной руды$() и $(#666666)$(l)Железной руды$(). Это довольно богатая и очень изменчивая жила, с кучей $(#FFA500)$(l)медной$() и $(#666666)$(l)железной$() руды параллельно с камнем, землёй и гравием.$(br)Руду можно найти во многих местах, но ещё в равнинах, саванах и лесах." }, { "anchor": "cuprite", "type": "spotlight", "title": "Куприт", "item": "minecraft:copper_ore", "text": "Обычно состоит из $(#FFA500)$(l)Медной руды$() Это единственная жила, содержащая только медь, она не очень богатая и очень изменчива по размеру.$(br)Руду можно найти во многих местах, но ещё в саванах и пустынях." }, { "anchor": "cassiterite", "type": "spotlight", "title": "Касситерит", "item": "indrev:tin_ore", "text": "Обычно состоит из $(#999999)$(l)Оловянной руды$(). Это единственная жила, содержащая только медь, она не очень богатая и очень изменчива по размеру.$(br)Руду можно найти во многих местах, но ещё в равнинах, саванах и лесах." }, { "anchor": "stannite", "type": "spotlight", "title": "Станнит", "item": "indrev:tin_ore", "text": "Обычно состоит из $(#999999)$(l)Оловянной руды$(), $(#FFA500)$(l)Медной руды$() и $(#666666)$(l)Железной руды$(), с кучей олова, параллельно с $(#666666)$(l)железом$(), $(#FFA500)$(l)медью$(), камнем, землёй и гравием.$(br)Руду можно найти во многих местах, но ещё в холмах, столовых гор и пустынях." }, { "anchor": "nikolite", "type": "spotlight", "title": "Николит", "item": "indrev:nikolite_ore", "text": "Обычно состоит из $(#5555BB)$(l)Николитовой руды$(), $(#FF0000)$(l)Редстоуновой руды$() и $(#0000FF)$(l)Лазуритовой руды$(), с кучей камня, земли и гравием. Руду можно найти во многих местах, но ещё в тайге и ледяных биомах." }, { "anchor": "cavalerite", "type": "spotlight", "title": "Калаверит", "item": "minecraft:gold_ore", "text": "$(l)Надземный мир$().$(br) Обычно состоит из $(#FFD700)$(l)Золотой руды$(). Это малая и довольно богатая золотая жила.$(br)Руду можно найти во многих местах, но ещё в тайге и столовых гор." }, { "type": "spotlight", "title": "Незерский калаверит", "item": "minecraft:nether_gold_ore", "text": "$(#ff0000)$(l)Незер$().$(br) Это довольно большая и богатая жила $(#FFD700)$(l)Незерской золотой руды$(), с кучей незерака, песка душ, гравием, а если тебе очень повезёт, то немного $(l)$(#000000)Древних обломков$()!" }, { "anchor": "quartz", "type": "spotlight", "title": "Кварц", "item": "minecraft:nether_quartz_ore", "text": "$(#ff0000)$(l)Незер$().$(br) Это довольно большая и богатая $(#dddddd)$(l)Кварцевая$() жила, с кучей незерака, песка душ, гравием." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/pump.json ================================================ { "name": "Насос", "category": "indrev:machines", "sortnum": 10, "icon": "indrev:pump_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/pump_mk1", "text": "$(l)Насос$() используется для выкачивания жидкостей в жидкостные трубы и резервуары." }, { "type": "text", "text": "$(l)Использование:$() Тебе нужно ввести сверху энергию и взять жидкость со стороны, с которой она обращена. После получения энергии, он опустит трубу в поисках жидкости для выкачивания.$(br2)Нет жёстких ограничений, за исключением физики жидкости. Пока есть жидкость, касающаяся трубы насоса, источник или нет, он будет искать источник жидкости для выкачивания. Таким образом, его можно использовать для выкачивания лавовых берегов озера из незера." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/rancher.json ================================================ { "name": "Скотовод", "category": "indrev:machines", "sortnum": 6, "icon": "indrev:rancher_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/rancher_mk1", "text": "$(l)Скотовод$() — это машина предназначена для автоматизации животноводства, а также она поддерживает улучшения.$(br)Заметь, что для её функционирования требуются определённые предметы: (положи их в квадрат 2х2)." }, { "type": "text", "text": "$(l)Меч:$() если имеется в инвентаре, будет убивать животных, если их больше, чем 7 в пределах его досягаемости.$(br2)$(l)Пшеница/Семя/Морковь:$() если имеются в инвентаре, будут кормить соответственных животных в пределах его досягаемости.$(br2)$(l)Ведро:$() если имеется в инвентаре, будет доить коров." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/lead_ore.json ================================================ { "name": "Свинцовая руда", "category": "indrev:natural_resources", "sortnum": 3, "icon": "indrev:lead_ore", "pages": [ { "type": "spotlight", "item": "indrev:lead_ore", "text": "$(#575d61)$(l)Свинцовая руда$() — это редка руда и её можно найти в слое от 0 до 32 как в каменных, так и в глубиносланцевых вариантах.$(br)$(#575d61)$(l)Свинец$() используется в создании более продвинутых машин." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/nikolite_ore.json ================================================ { "name": "Николитовая руда", "category": "indrev:natural_resources", "sortnum": 5, "icon": "indrev:nikolite_ore", "pages": [ { "type": "spotlight", "item": "indrev:nikolite_ore", "text": "$(#008f8c)$(l)Николитовая руда$() — это редкая руда и её можно найти в слое от 0 до 16 как в каменных, так и в глубиносланцевых вариантах.$(br)$(#008f8c)$(l)Николит$() — это главный компонент, использующийся как в основных, так и в продвинутых $(l)INDREV$() машин." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/silver_ore.json ================================================ { "name": "Серебряная руда", "category": "indrev:natural_resources", "sortnum": 2, "icon": "indrev:silver_ore", "pages": [ { "type": "spotlight", "item": "indrev:lead_ore", "text": "$(#c6e9e3)$(l)Серебряная руда$() — это редка руда и её можно найти в слое от 0 до 32 как в каменных, так и в глубиносланцевых вариантах.$(br)$(#c6e9e3)$(l)Серебро$() используется в создании более продвинутых машин." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/sulfur_crystals.json ================================================ { "name": "Серный кристалл", "category": "indrev:natural_resources", "sortnum": 6, "icon": "indrev:sulfur_crystal", "pages": [ { "type": "spotlight", "item": "indrev:sulfur_crystal", "text": "$(#f5da68)$(l)Серные кристаллы$() — это распространённые кристаллы, их можно найти в слое от 0 до 16 в надземном мире или до сотого слоя в незере.$(br)$(#f5da68)$(l)Сера$() используется в процессе очистки цепной продвинутой переработки руды." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/tin_ore.json ================================================ { "name": "Оловянная руда", "category": "indrev:natural_resources", "sortnum": 1, "icon": "indrev:tin_ore", "pages": [ { "type": "spotlight", "item": "indrev:tin_ore", "text": "$(#999999)$(l)Оловянная руда$() — это распространённая руда, её можно найти в слое от 0 до 48 как в каменных, так и в глубиносланцевых вариантах.$(br)$(#999999)$(l)Олово$() интенсивно используется в создании машин." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/tungsten_ore.json ================================================ { "name": "Вольфрамовая руда", "category": "indrev:natural_resources", "sortnum": 4, "icon": "indrev:deepslate_tungsten_ore", "pages": [ { "type": "spotlight", "item": "indrev:deepslate_tungsten_ore", "text": "$(#686b5b)$(l)Вольфрамовая руда$() — это редкая руда, её можно найти в слое от 0 до 16 только в глубинносланцевом варианте.$(br)$(#686b5b)$(l)Вольфрам$() используется в создании прочных предметов, таких как: Модульная броня." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/temperature/coolers_and_fans.json ================================================ { "name": "Охлаждение!", "category": "indrev:temperature", "sortnum": 1, "icon": "indrev:cooler_cell", "pages": [ { "type": "text", "text": "Охладители и вентиляторы заставят быстрее охлаждать температуру машины, держа её в наиболее эффективном состоянии, даже когда это произойдёт они будут сохранять порыв охлаждения." }, { "type": "crafting", "recipe": "indrev:shaped/fan", "text": "$(o)Могу я получить твой автограф? Прошу, прошу, прошу-у-у-у-у-у$()." }, { "type": "crafting", "recipe": "indrev:shaped/cooler_cell", "text": "$(o)Неубедительно... Я слишком крут для этого$()." }, { "type": "crafting", "recipe": "indrev:shaped/heatsink", "text": "$(o)Что? Ты ожидал каламбур? Прости, здесь нет каламбуров.$()" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/temperature/info.json ================================================ { "name": "Как это работает?", "category": "indrev:temperature", "sortnum": 0, "icon": "indrev:fan", "pages": [ { "type": "text", "text": "Учёные $(l)INDREV$() хорошо справились с работой в создании достаточно умных, не взрывающихся машин, извлекающие пользу от температуры. У каждого машины оптимальная, никогда не превышающаяся температура." }, { "type": "text", "text": "Для того, чтобы машина оставалась на своей оптимальной температуре, ей понадобится частое охлаждение. Пока он охлаждается, у неё $(l)не$() будет повышенной эффективности.$(br)Проверь $(l:temperature/coolers_and_fans)$(t:Перейти к Охлаждению!)Охлаждение!$() для достижения постоянной 100% эффективности!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/energy_reader.json ================================================ { "name": "Считыватель энергии", "category": "indrev:tools", "sortnum": 4, "icon": "indrev:energy_reader", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/energy_reader", "text": "Расскажет о текущей запасённой энергии в машинах, а также расскажет о потребления энергии в тик." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/gamer_axe.json ================================================ { "name": "Геймерский топор", "category": "indrev:tools", "sortnum": 6, "icon": "indrev:gamer_axe", "pages": [ { "type": "text", "text": "Геймерский топор является финальным игровым инструментом. Он быстро копает и наносит большое количество урона при установленных модулях.$(br)Это может показаться просто палкой, но подожди, когда ты его активируешь...$(br2)Для активации нажми пкм." }, { "type": "crafting", "recipe": "indrev:shaped/gamer_axe" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/hammer.json ================================================ { "name": "Молот", "category": "indrev:tools", "sortnum": 0, "icon": "indrev:hammer", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/hammer", "text": "Используется для изготовления пластин из слитков, пока у тебя по-прежнему нет Компрессора!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/mining_drills.json ================================================ { "name": "Шахтёрские буры", "category": "indrev:tools", "sortnum": 3, "icon": "indrev:mining_drill_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/mining_drill_mk1", "recipe2": "indrev:shaped/mining_drill_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/mining_drill_mk3", "recipe2": "indrev:shaped/mining_drill_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/modular_armor.json ================================================ { "name": "Модульная броня", "category": "indrev:tools", "sortnum": 5, "icon": "indrev:modular_armor_helmet", "pages": [ { "type": "text", "text": "Модульная броня сама по себе не так уж так себе, но когда ты установишь модули, она может стать очень мощной!$(br2)Цветные модули без рецепта, ты можешь их найти в подземельях." }, { "type": "text", "text": "Уровни модуля можно настроить любым для тебя удобным способом, войди в меню $(l)Управления$() и добавь связанную клавишу к \"Конфигурации модульного предмета.\"" }, { "type": "crafting", "recipe": "indrev:shaped/modular_armor_helmet", "recipe2": "indrev:shaped/modular_armor_chest" }, { "type": "crafting", "recipe": "indrev:shaped/modular_armor_legs", "recipe2": "indrev:shaped/modular_armor_boots" }, { "type": "crafting", "recipe": "indrev:shaped/modular_workbench", "text": "Модульный верстак используется для установки модулей к деталям Модульной брони. Учти, что установка модулей требует время и энергии!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/modular_core.json ================================================ { "name": "Ядро модульности", "category": "indrev:tools", "sortnum": 7, "icon": "indrev:modular_core", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/modular_core", "text": "Ядро модульности — это основа для каждого здесь модульного инструмента. Однако, до практичного использования его нужно активировать. Чтобы его активировать, тебе как минимум нужен один Лазер, одна капсула и много запасённой энергии (100 млн)." }, { "type": "crafting", "recipe": "indrev:shaped/capsule", "text": "Капсула будет сдерживать Ядро модульности во время процесса активации.$(br)После завершения процесса, она излучит редстоун-сигнал." }, { "type": "crafting", "recipe": "indrev:shaped/laser_emitter_mk4", "text": "Лазерных излучателей следует разместить лицом к капсуле на $(l)ДИСТАНЦИИ СТРОГО 3-Х БЛОКОВ$(). Они хранят до 2,5 млн ЛФ, а чтобы их подключить требуется редстоун-сигнал." }, { "type": "text", "text": "Этот процесс требует длительного времени, но оно ускорится за счёт активированного Лазерного излучателя. Ты должен обратить внимание на эти детали для избежания проблем:$(br)- Направив лазерный излучатель на что-то другое, а не на капсулу и/или пустую капсулу приведёт к взрыву.$(br)- Если лазерные излучатели израсходуют энергию во время процесса, весь прогресс будет потерян.$(br)- Убедись, что выключил Лазерные излучатели, прежде чем получить активированное ядро." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/screwdriver.json ================================================ { "name": "Отвёртка", "category": "indrev:tools", "sortnum": 2, "icon": "indrev:screwdriver", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/screwdriver", "text": "Отвёртка используется для настройки входов/выходов машин для предметов и жидкостей.$(br)Также можно использовать в Лазуритовом флаксовом резервуаре для настройки входа/выхода энергии." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/wrench.json ================================================ { "name": "Гаечный ключ", "category": "indrev:tools", "sortnum": 1, "icon": "indrev:wrench", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/wrench", "text": "Гаечный ключ используется для вращения блоков и ломании машин, сохранив свой запас энергии при использовании с Shift по машине." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/transportation/energy.json ================================================ { "name": "Энергия", "category": "indrev:transportation", "sortnum": 0, "icon": "indrev:cable_mk1", "pages": [ { "type": "text", "text": "Кабели можно использовать для передачи энергии между машинами на большом расстоянии друг от друга.$(br)Каждый кабель может передавать определённое количество энергии, которые описываются в подсказке при нажатии Shift.$(br)Если машины работают на малой энергопотреблении, ты можешь попробовать обновить кабели или улучшить свою генерацию энергии." }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk1", "recipe2": "indrev:shaped/cable_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk3", "recipe2": "indrev:shaped/cable_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/transportation/fluid_pipes.json ================================================ { "name": "Жидкости", "category": "indrev:transportation", "sortnum": 2, "icon": "indrev:fluid_pipe_mk1", "pages": [ { "type": "text", "text": "Жидкостные трубы можно использовать для передачи жидкостей между резервуарами на большом расстоянии друг от друга.$(br)Чтобы посмотреть сколько можно передать количество жидкости, проверь подсказки.$(br)Трубы передают жидкость только в секундах, а не в тиках.$(br2)Понадобятся сервоприводы, прочти главу $(l:transportation/servos)$(t:Перейти к Сервоприводы)Сервоприводы$()." }, { "type": "crafting", "recipe": "indrev:shaped/fluid_pipe_mk1", "recipe2": "indrev:shaped/fluid_pipe_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/fluid_pipe_mk3", "recipe2": "indrev:shaped/fluid_pipe_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/transportation/item_pipes.json ================================================ { "name": "Предметы", "category": "indrev:transportation", "sortnum": 1, "icon": "indrev:item_pipe_mk1", "pages": [ { "type": "text", "text": "Предметные трубы можно использовать для передачи предметов между инвентарями на большом расстоянии друг от друга.$(br)Чтобы посмотреть сколько можно передать количество предметов, проверь подсказки.$(br)Трубы передают жидкость только в секундах, а не в тиках.$(br)Предметные трубы имеют $(l)продвинутую фильтрацию$(), чтобы посмотреть нажми пкм трубой.$(br2)Понадобятся сервоприводы, прочти главу $(l:transportation/servos)$(t:Перейти к Сервоприводы)Сервоприводы$()." }, { "type": "crafting", "recipe": "indrev:shaped/item_pipe_mk1", "recipe2": "indrev:shaped/item_pipe_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/item_pipe_mk3", "recipe2": "indrev:shaped/item_pipe_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/transportation/servos.json ================================================ { "name": "Сервоприводы", "category": "indrev:transportation", "sortnum": 3, "icon": "indrev:servo_retriever", "pages": [ { "type": "text", "text": "Для функционирования предметных/жидкостных труб понадобятся сервоприводы. Есть 2 типа сервопривода: Сервопривод-извлекатель/Сервопривод вывода.$(br)Чтобы прикрепить сервопривод к трубе, присоединённой к инвентарю нажми Лкм. Для снятия сервопривода используй Гаечный ключ.$(br)$(l)Важно:$()Сервопривод-извлекатель и вывода — НЕЗАВИСИМЫ. Для работы трубы, тебе не нужны сразу оба сервопривода. У них разные назначения." }, { "type": "text", "text": "Например, если у тебя один инвентарь, который нужно вывести в несколько инвентарей, тебе просто понадобится один Сервопривод вывода на конце выдачи.$(br2)Если у тебя несколько инвентарей, которых нужно вывести в один инвентарь, тебе просто понадобится один Сервопривод-извлекатель в конце приёма.$(br2)$(l)ВАЖНО:$() удостоверься, что машина настроена. Прочти главу $(l:tools/wrench)$(t:Перейти к Гаечному ключу!)Гаечный ключ!$()" }, { "type": "text", "title": "Режимы", "text": "Для обозначения приоритетов у сервоприводов есть несколько режимов.$(br)Чтобы изменить режим нажми пкм держа сервопривод.$(br)$(l)Сначала ближайший:$() сервопривод ищет самый близкий контейнер.$(br)$(l)Сначала дальний:$() сервопривод ищет самый дальний контейнер.$(br)$(l)Случайный:$() сервопривод выбирает случайный контейнер.$(br)$(l)Циклически:$() сервопривод ищет контейнер с наименьшим числом переданной жидкости/предмета." }, { "type": "crafting", "recipe": "indrev:shaped/servo_output", "text": "Сервопривод вывода проталкивает предметы/жидкости в близлежащие контейнеры, ЕСЛИ они не отмечены как вывод." }, { "type": "crafting", "recipe": "indrev:shaped/servo_retriever", "text": "Сервопривод-извлекатель тянет предметы/жидкости из ближайших контейнеров, ЕСЛИ они не отмечены как извлечение." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/upgrades/enhancement_upgrades.json ================================================ { "name": "Усилители", "category": "indrev:upgrades", "sortnum": 0, "icon": "indrev:speed_enhancer", "pages": [ { "type": "text", "text": "Эти улучшение улучшат некоторые аспекты машины, такие как скорость и использование энергии.$(br2)Машина использует внутри-вставленные улучшения в 4 слота, справа в интерфейсе." }, { "type": "crafting", "recipe": "indrev:shaped/speed_enhancer", "text": "Данное улучшение увеличит скорость обработки машины за стоимость энергии." }, { "type": "crafting", "recipe": "indrev:shaped/buffer_enhancer", "text": "Данное улучшение увеличит внутреннюю энергоёмкость машины." }, { "type": "crafting", "recipe": "indrev:shaped/blast_furnace_enhancer", "text": "Данное улучшение заставит Электропечь принимать рецепты от Доменной печи." }, { "type": "crafting", "recipe": "indrev:shaped/smoker_enhancer", "text": "Данное улучшение заставит Электропечь принимать рецепты от Коптильни." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/upgrades/tier_upgrades.json ================================================ { "name": "Повышение уровня", "category": "indrev:upgrades", "sortnum": 1, "icon": "indrev:tier_upgrade_mk2", "pages": [ { "type": "text", "text": "Эти улучшения улучшают возможности ввода/вывода машины, но и другие аспекты.$(br2)Пкм по низкоуровневой машине, чтобы её улучшить." }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk2", "text": "Улучшает машины марк. 1 до марк. 2." }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk3", "text": "Улучшает машины марк. 2 до марк. 3." }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk4", "text": "Улучшает машины марк. 3 до марк. 4." } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/enhanced_ore_processing.json ================================================ { "name": "高级矿物处理", "sortnum": 6, "description": "“工业革命”的一大亮点在于矿产的加倍甚至四倍处理。", "icon": "indrev:copper_chunk" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/machines.json ================================================ { "name": "机器", "sortnum": 1, "description": "机器是本模组的核心内容。本节会介绍机器的合成配方及相关信息。", "icon": "indrev:electric_furnace_mk1" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/natural_resources.json ================================================ { "name": "自然资源", "sortnum": 0, "description": "探索“工业革命”所必要的自然资源信息。", "icon": "indrev:tin_ore" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/temperature.json ================================================ { "name": "温度", "sortnum": 4, "description": "一些机器具有温度属性,可在其界面内查看。本节将详细介绍。", "icon": "indrev:fan" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/tools.json ================================================ { "name": "工具", "sortnum": 5, "description": "工具同样是本模组重要的一节。", "icon": "indrev:hammer" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/transportation.json ================================================ { "name": "运输系统", "sortnum": 2, "description": "“工业革命”同样提供运输物体,液体与能量的解决方案!", "icon": "indrev:cable_mk1" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/upgrades.json ================================================ { "name": "升级", "sortnum": 3, "description": "升级可为机器提供增益效果。", "icon": "indrev:speed_enhancer" } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/enhanced_ore_processing/doubling.json ================================================ { "name": "双倍产矿", "category": "indrev:enhanced_ore_processing", "sortnum": 0, "icon": "indrev:pulverizer_mk1", "pages": [ { "type": "text", "text": "双倍产矿是高级矿物处理的第一步。$(br)你首先需要的是$(l:machines/basic_machines#pulverizer)$(t:跳转到“粉碎机”页面)$(l)$(o)粉碎机$()。$(br)粉碎机可以粉碎一个原矿并产出其对应的两个矿粉,而每个矿粉都可以被烧炼为一个锭。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/enhanced_ore_processing/quadrupling.json ================================================ { "name": "四倍产矿", "category": "indrev:enhanced_ore_processing", "sortnum": 2, "icon": "indrev:fluid_infuser_mk4", "pages": [ { "type": "text", "text": "高级矿物处理的最后一步!为此,你需要$(l:machines/basic_machines#fluid_infuser)$(t:跳转到“液体注入机”)$(l)$(o)液体注入机$(),三倍产矿需要的所有机器,以及充足的硫酸。$(br)$(#3ead36)硫酸$()可以在沼泽地的天然硫酸湖中获取,也可以在液体注入机中通过混合$(#b3b332)硫粉$()和$(#88adf2)水$()得到。" }, { "type": "text", "text": "$(l)1)$()将矿物塞入液体注入机以获取精炼的矿石。$(br)$(l)2)$()接下来按照三倍产矿的操作即可获得四倍的产出啦!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/enhanced_ore_processing/tripling.json ================================================ { "name": "三倍产矿", "category": "indrev:enhanced_ore_processing", "sortnum": 1, "icon": "indrev:smelter_mk4", "pages": [ { "type": "text", "text": "高级矿物处理再进一步则是三倍产矿。为此你需要一台$(l:machines/basic_machines#smelter)$(t:跳转到“工业熔炉”页面)$(l)$(o)工业熔炉$(),一台$(l:machines/basic_machines#condenser)$(t:跳转到“聚合机”页面)$(l)$(o)聚合机$()和一台$(l:machines/basic_machines#pulverizer)$(t:跳转到“粉碎机”页面)$(l)$(o)粉碎机$()。" }, { "type": "text", "text": "$(l)1)$()将矿物放入工业熔炉,矿物会熔融为其对应的液体形式。$(br)$(l)2)$()将熔融的液体转移到至聚合机中,其则会固化为原矿碎块。$(br)$(l)3)$()将原矿碎块放入粉碎机中磨粉,又将会变为一个粉末。$(br)$(l)4)$()烧炼粉末。$(br2)锵锵~!你的一个矿就变成三个锭啦!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/basic_machines.json ================================================ { "name": "基础机器", "category": "indrev:machines", "sortnum": 1, "icon": "indrev:electric_furnace_mk1", "pages": [ { "type": "text", "text": "这些机器可以将资源加工为更复杂的资源。" }, { "type": "crafting", "anchor": "electric_furnace", "recipe": "indrev:shaped/electric_furnace_mk1", "text": "$(l)$(o)电炉$()就是普通的熔炉,不过用青金石通量运作而已。" }, { "type": "crafting", "anchor": "pulverizer", "recipe": "indrev:shaped/pulverizer_mk1", "text": "$(l)$(o)粉碎机$()常用于矿物粉碎。" }, { "type": "crafting", "anchor": "compressor", "recipe": "indrev:shaped/compressor_mk1", "text": "$(l)$(o)压缩机$()常用于将锭压成板。" }, { "type": "crafting", "anchor": "infuser", "recipe": "indrev:shaped/infuser_mk1", "text": "$(l)$(o)固体注入机$()可以将两种材料混合为一种新材料。" }, { "type": "crafting", "anchor": "fluid_infuser", "recipe": "indrev:shaped/fluid_infuser_mk1", "text": "$(l)$(o)液体注入机$()用于液体方面的制作和融合,目前的主要用途是四倍产矿。" }, { "type": "crafting", "anchor": "condenser", "recipe": "indrev:shaped/condenser_mk4", "text": "$(l)$(o)聚合机$()用于将熔融的矿物聚合为矿物碎块。碎块可在经过磨粉工艺后重炼成锭。" }, { "type": "crafting", "anchor": "smelter", "recipe": "indrev:shaped/smelter_mk4", "text": "$(l)$(o)工业熔炉$()主要用于熔化矿物,目前用于高级矿物处理。" }, { "type": "crafting", "anchor": "recycler", "recipe": "indrev:shaped/recycler_mk2", "text": "$(l)$(o)回收机$()将有机物回收为生物质,其可用于发电。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/batteries.json ================================================ { "name": "青金石通量容器", "category": "indrev:machines", "sortnum": 3, "icon": "indrev:lazuli_flux_container_mk1", "pages": [ { "type": "text", "text": "这些容器可用于存储青金石通量。机身上可见的蓝色与橙色箭头标注了输入与输出接口,你可以使用螺丝刀进行接口的修改。" }, { "type": "crafting", "recipe": "indrev:shaped/lazuli_flux_container_mk1", "recipe2": "indrev:shaped/lazuli_flux_container_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/lazuli_flux_container_mk3", "recipe2": "indrev:shaped/lazuli_flux_container_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/cables.json ================================================ { "name": "电缆", "category": "machines", "sortnum": 2, "icon": "indrev:cable_mk1", "pages": [ { "type": "text", "text": "这些线缆可用于远距离运输青金石通量。" }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk1", "recipe2": "indrev:shaped/cable_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk3", "recipe2": "indrev:shaped/cable_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/chopper.json ================================================ { "name": "伐木机", "category": "indrev:machines", "sortnum": 5, "icon": "indrev:chopper_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/chopper_mk1", "text": "$(l)伐木机$(),顾名思义。作为伐木用的机器,同样支持安装各种增益升级。$(br)机器本身需要用于正常工作的用品,请将它们放在所示的2x2方格中。" }, { "type": "text", "text": "$(l)斧:$()放入机器则会自动采伐工作范围内的木头和树叶(树叶不消耗耐久)$(br2)$(l)树苗:$()放入机器则会在工作范围内自动栽种。$(br2)$(l)骨粉:$()自动催熟工作范围内的树苗。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/factories.json ================================================ { "name": "工厂", "category": "indrev:machines", "sortnum": 9, "icon": "indrev:electric_furnace_factory_mk4", "pages": [ { "type": "text", "text": "$(l)工厂$()是机器的升级版,最多可以批处理5个物品。还能智能平分物品到其各个槽位,悉听尊便。$(br2)这般强力自然不能塞进一个方块里,因此你需要组建多方块结构。" }, { "type": "text", "text": "$(l)方块:$()$(br2)$(li)$(l)9x$()机器框架$(br)$(li)$(l)6x$()筒仓$(br)$(li)$(l)3x$()进风口$(br)$(li)$(l)3x$()通风管道$(br)$(li)$(l)2x$()柜子$(br)$(li)$(l)1x$()警示灯$(br)$(li)$(l)1x$()控制器$()$(br2)右击工厂方块本身可以查看多方块结构的构造。" }, { "type": "crafting", "recipe": "indrev:shaped/frame", "recipe2": "indrev:shaped/silo" }, { "type": "crafting", "recipe": "indrev:shaped/intake", "recipe2": "indrev:shaped/duct" }, { "type": "crafting", "recipe": "indrev:shaped/cabinet", "recipe2": "indrev:shaped/warning_strobe" }, { "type": "crafting", "recipe": "indrev:shaped/controller" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/farmer.json ================================================ { "name": "耕作机", "category": "indrev:machines", "sortnum": 7, "icon": "indrev:farmer_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/farmer_mk1", "text": "$(l)耕作机$(),顾名思义。作为耕作用的机器,同样支持安装各种增益升级。$(br)机器本身需要用于正常工作的用品,请将它们放在所示的2x2方格中。" }, { "type": "text", "text": "$(l)种子:$()如果存在则会被放置在耕作机的工作范围内。耕作机本身会尝试匹配田中的作物,所以可以放入多种作物。$(br2)$(l)骨粉:$()如果存在则会试图催熟工作范围内的作物。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/fisher.json ================================================ { "name": "钓鱼机", "category": "indrev:machines", "sortnum": 8, "icon": "indrev:fishing_farm_mk2", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/fishing_farm_mk2", "text": "$(l)钓鱼机$(),顾名思义,钓鱼的机器。支持增益升级。$(br)只需要一个钓竿即可运行,支持附魔哦!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/generators.json ================================================ { "name": "发电机", "category": "indrev:machines", "sortnum": 0, "icon": "indrev:coal_generator_mk1", "pages": [ { "type": "text", "text": "$(l)$(o)发电机$()通过产生名为青金石通量的能量来为你的机器供能。" }, { "type": "crafting", "recipe": "indrev:shaped/coal_generator_mk1", "text": "$(l)$(o)煤炭发电机$()不只烧煤,任何熔炉里的能源都可以。" }, { "type": "crafting", "recipe": "indrev:shaped/solar_generator_mk1", "recipe2": "indrev:shaped/solar_generator_mk3" }, { "type": "text", "text": "$(l)$(o)太阳能发电机$()会在阳光直射下产生能量。" }, { "type": "crafting", "recipe": "indrev:shaped/biomass_generator_mk3", "text": "$(l)$(o)生物质发电机$()通过燃烧$(#00ff00)生物质$()来产能。" }, { "type": "crafting", "recipe": "indrev:shaped/heat_generator", "text": "$(l)$(o)热能发电机$()通过吸收与其接触的$(#ff0000)熔岩$()或$(#ff0000)熔融下界合金$()来产能。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/miner.json ================================================ { "name": "自动挖矿钻机", "category": "indrev:machines", "sortnum": 4, "icon": "indrev:mining_rig_mk4", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/miner_mk4", "text": "将$(l)$(o)自动挖矿钻机$()放置在$(l:machines/miner#chunk_scanner)$(t:区块扫描仪)$(l)$(o)$(#0000ff)扫描过$()的区块中即可自动挖矿。它并不会破坏方块。计算机消耗64 LF/刻,而每一个钻头消耗256 LF/刻" }, { "type": "crafting", "recipe": "indrev:shaped/drill_bottom", "text": "自动挖矿钻机的运行需要钻头。钻头相对钻机本体斜向放置,且需安装钻机头部。每一个钻头都能加快运行速率。$(br)不废话,上图:" }, { "type": "image", "title": "Example", "images": ["indrev:textures/gui/mrc_setup.png"], "text": "注意钻头相对钻机本体斜向放置,且钻机对准中间一格。" }, { "anchor": "chunk_scanner", "type": "crafting", "recipe": "indrev:shaped/chunk_scanner", "text": "$(l)$(o)区块扫描仪$()可用于勘探当前区块的矿脉。扫描后获取的扫描报告即可放入矿机中使用。" }, { "type": "text", "text": "$(l)注意:$()$(br)矿机采掘进度达到100%之后仅会继续产出石头,土,砂砾,而非矿物。$(br2)$(br)列表仅供参考,并未详细列举所有矿脉种类。" }, { "anchor": "vein_types", "type": "text", "text": "$(br)$(l:machines/miner#peat)$(t:查阅泥煤)泥煤$()$(br)$(l:machines/miner#lignite)$(t:查阅褐煤)褐煤$()$(br)$(l:machines/miner#bituminous)$(t:查阅沥青)沥青$()$(br)$(l:machines/miner#anthracite)$(t:查阅白煤)白煤$()$(br)$(l:machines/miner#siderite)$(t:查阅菱铁)菱铁$()$(br)$(l:machines/miner#limonite)$(t:查阅褐铁)褐铁$()$(br)$(l:machines/miner#hematite)$(t:查阅赤铁)赤铁$()$(br)$(l:machines/miner#magnetite)$(t:查阅磁铁)磁铁$()$(br)$(l:machines/miner#chalcopryte)$(t:查阅黄铜)黄铜$()$(br)$(l:machines/miner#cuprite)$(t:查阅赤铜)赤铜$()$(br)$(l:machines/miner#cassiterite)$(t:查阅锡石)锡石$()$(br)$(l:machines/miner#stannite)$(t:查阅黄锡)黄锡$()$(br)$(l:machines/miner#cavalerite)$(t:查阅碲金)碲金$()$(br)$(l:machines/miner#quartz)$(t:查阅石英)石英$()$(br)$(l:machines/miner#nikolite)$(t:查阅蓝石)蓝石$()" }, { "anchor": "peat", "type": "spotlight", "title": "泥煤", "item": "minecraft:coal_ore", "text": "主要为$(#000000)$(l)煤矿石$()最贫的煤矿脉,你会获得大量的石头,土,沙砾等。$(br)这些矿脉随处可见,但在平原、冻原、森林、沼泽和冰冷的生物群系中更常见。" }, { "anchor": "lignite", "type": "spotlight", "title": "褐煤", "item": "minecraft:coal_ore", "text": "主要为$(#000000)$(l)煤矿石$(),次贫的煤矿脉,你还是会获得大量的石头,土,沙砾等。$(br)这些矿脉随处可见,但在平原、冻原、森林、沼泽生物群系中更常见。" }, { "anchor": "bituminous", "type": "spotlight", "title": "沥青", "item": "minecraft:coal_ore", "text": "主要为$(#000000)$(l)煤矿石$()丰富而庞大的煤矿脉,也就是说除了石头,土,沙砾以外你还会找到很多很多煤炭。走运的话找到$(#00ffff)$(l)钻石$()也说不定哦。$(br)这些矿脉随处可见,但在沙漠,丘陵,平顶山和热带草原更常见。" }, { "anchor": "anthracite", "type": "spotlight", "title": "白煤", "item": "minecraft:coal_ore", "text": "主要为$(#000000)$(l)煤矿石$()丰度最高的煤矿脉,大量的煤炭以外也有少数石头,土,沙砾等。$(#00ffff)$(l)钻石$()也有呢。$(br)这些矿脉随处可见,但在沙漠,丘陵,平顶山和热带草原更常见。" }, { "anchor": "siderite", "type": "spotlight", "title": "菱铁", "item": "minecraft:iron_ore", "text": "主要为$(#666666)$(l)铁矿石$(),最贫的铁矿脉,你会获得大量的石头,土,沙砾等。$(br)这些矿脉随处可见,但在丘陵,平原,热带草原和森林更常见。" }, { "anchor": "limonite", "type": "spotlight", "title": "褐铁", "item": "minecraft:iron_ore", "text": "主要为$(#666666)$(l)铁矿石$(),次贫的铁矿脉,你还是会获得大量的石头,土,沙砾等。$(br)这些矿脉随处可见,但在丘陵,平原和沙漠更常见。" }, { "anchor": "hematite", "type": "spotlight", "title": "赤铁", "item": "minecraft:iron_ore", "text": "主要为$(#666666)$(l)铁矿石$(),丰富而庞大的铁矿脉,也就是说除了石头,土,沙砾以外你还会找到很多很多铁矿。$(br)这些矿脉随处可见,但在冻原和平顶山更常见。" }, { "anchor": "magnetite", "type": "spotlight", "title": "磁铁", "item": "minecraft:iron_ore", "text": "主要为$(#666666)$(l)铁矿石$(),丰度最高的铁矿脉,大量的铁矿以外也有少数石头,土,沙砾等。$(br)这些矿脉随处可见,但在丘陵,平顶山和沙漠更常见。" }, { "anchor": "chalcopryte", "type": "spotlight", "title": "黄铜", "item": "minecraft:copper_ore", "text": "主要为$(#FFA500)$(l)铜矿石$()和$(#666666)$(l)铁矿石$(),丰度较高,含量多变,$(#FFA500)$(l)铜$()和$(#666666)$(l)铁$()矿石众多,也有石头,土,沙砾等。$(br)这些矿脉随处可见,但在平原,热带草原和森林更常见。" }, { "anchor": "cuprite", "type": "spotlight", "title": "赤铜", "item": "minecraft:copper_ore", "text": "主要为$(#FFA500)$(l)铜矿石$(),少数只包含铜矿的矿脉,丰度不高,含量多变。$(br)这些矿脉随处可见,但在热带草原和沙漠更常见。" }, { "anchor": "cassiterite", "type": "spotlight", "title": "锡石", "item": "indrev:tin_ore", "text": "主要为$(#999999)$(l)锡矿石$(),少数只包含锡矿的矿脉,丰度不高,含量多变。$(br)这些矿脉随处可见,但在平原,热带草原和森林更常见。" }, { "anchor": "stannite", "type": "spotlight", "title": "黄锡", "item": "indrev:tin_ore", "text": "主要为$(#999999)$(l)锡矿石$(),$(#FFA500)$(l)铜矿石$()和$(#666666)$(l)铁矿石$(),锡矿众多,也含有少数石头,土,沙砾等。$(br)这些矿脉随处可见,但在丘陵,平顶山和沙漠更常见。" }, { "anchor": "nikolite", "type": "spotlight", "title": "蓝石", "item": "indrev:nikolite_ore", "text": "主要为$(#5555BB)$(l)蓝石矿石$(),$(#FF0000)$(l)红石矿石$()和$(#0000FF)$(l)青金石矿石$(),也有少数石头,土,沙砾等,这些矿脉随处可见,但在冻原和冰冷的生物群系更常见。" }, { "anchor": "cavalerite", "type": "spotlight", "title": "碲金", "item": "minecraft:gold_ore", "text": "$(l)主世界$()$(br)主要为$(#FFD700)$(l)金矿石$(),虽小却丰度颇高。$(br)这些矿脉随处可见,但在冻原和平顶山更常见。" }, { "type": "spotlight", "title": "碲金 (下界)", "item": "minecraft:nether_gold_ore", "text": "$(#ff0000)$(l)下界$()$(br)庞大且丰度较高,有较多$(#FFD700)$(l)下界金矿石$(),下界岩,灵魂沙,沙砾等。走运的话也可能有$(l)$(#000000)远古残骸$()出土哦!" }, { "anchor": "quartz", "type": "spotlight", "title": "石英", "item": "minecraft:nether_quartz_ore", "text": "$(#ff0000)$(l)下界$()$(br)丰度极高且庞大的$(#dddddd)$(l)石英$()矿脉,也有下界岩,灵魂沙,沙砾等。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/pump.json ================================================ { "name": "液泵", "category": "indrev:machines", "sortnum": 10, "icon": "indrev:pump_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/pump_mk1", "text": "$(l)液泵$()可以从世界中汲取液体并输入流体管道或储罐之中。" }, { "type": "text", "text": "$(l)使用方法:$()在其上方供能,其会延伸一个管道,伸入液面后即可收集液体。无论液体状态如何,只要管道和液面接触,其会自动搜寻最近的液体源方块,因此适用于下界岩浆湖的清空。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/rancher.json ================================================ { "name": "放牧机", "category": "machines", "sortnum": 6, "icon": "indrev:rancher_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/rancher_mk1", "text": "$(l)放牧机$()可以自动放牧动物,同样支持机器升级。$(br)机器本身需要用于正常工作的用品,请将它们放在所示的2x2方格中。" }, { "type": "text", "text": "$(l)剑:$()放入机器时会在工作范围内动物多于7只时自动杀死多余动物。$(br2)$(l)小麦/种子/胡萝卜:$()放入机器时会喂养工作范围内的动物。$(br2)$(l)桶:$()放入机器时会自动为牛挤奶。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/lead_ore.json ================================================ { "name": "铅矿石", "category": "indrev:natural_resources", "sortnum": 3, "icon": "indrev:lead_ore", "pages": [ { "type": "spotlight", "item": "indrev:lead_ore", "text": "$(#575d61)$(l)铅矿石$()较不常见,在y轴0到32层生成普通或深层矿石。$(br)$(#575d61)$(l)铅$()常用于合成高级机器。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/nikolite_ore.json ================================================ { "name": "蓝石矿石", "category": "indrev:natural_resources", "sortnum": 5, "icon": "indrev:nikolite_ore", "pages": [ { "type": "spotlight", "item": "indrev:nikolite_ore", "text": "$(#008f8c)$(l)蓝石矿石$()较为常见,在y轴0到16层生成普通或深层矿石。$(br)$(#008f8c)$(l)蓝石$()是$(l)“工业革命”$()的主要机械元件。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/silver_ore.json ================================================ { "name": "银矿石", "category": "indrev:natural_resources", "sortnum": 2, "icon": "indrev:silver_ore", "pages": [ { "type": "spotlight", "item": "indrev:lead_ore", "text": "$(#c6e9e3)$(l)银矿石$()较不常见,在y轴0到32层生成普通或深层矿石。$(br)$(#c6e9e3)$(l)银$()常用于合成高级机器。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/sulfur_crystals.json ================================================ { "name": "硫晶体", "category": "indrev:natural_resources", "sortnum": 6, "icon": "indrev:sulfur_crystal", "pages": [ { "type": "spotlight", "item": "indrev:sulfur_crystal", "text": "$(#f5da68)$(l)硫晶体$()较为常见,在主世界y周0到16层或下界100层以下生成。$(br)$(#f5da68)$(l)硫$()常用于高级矿物处理工序中的净化工艺。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/tin_ore.json ================================================ { "name": "锡矿石", "category": "indrev:natural_resources", "sortnum": 1, "icon": "indrev:tin_ore", "pages": [ { "type": "spotlight", "item": "indrev:tin_ore", "text": "$(#999999)$(l)锡矿石$()较为常见,在y轴0到48层生成普通或深层矿石。$(br)$(#999999)$(l)锡$()广泛应用于多种机器合成。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/tungsten_ore.json ================================================ { "name": "钨矿石", "category": "indrev:natural_resources", "sortnum": 4, "icon": "indrev:deepslate_tungsten_ore", "pages": [ { "type": "spotlight", "item": "indrev:deepslate_tungsten_ore", "text": "$(#686b5b)$(l)钨矿石$()较为稀有,在y轴0到16层仅生成深层矿石。$(br)$(#686b5b)$(l)钨$()常用于合成如模块盔甲等强韧的物品。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/temperature/coolers_and_fans.json ================================================ { "name": "时刻保持高“冷”~", "category": "indrev:temperature", "sortnum": 1, "icon": "indrev:cooler_cell", "pages": [ { "type": "text", "text": "制冷液和风扇可降低机器的温度以使机器在特定的最适高效温度范围运行。" }, { "type": "crafting", "recipe": "indrev:shaped/fan", "text": "$(o)先生,我可是你的死忠“饭”!我可以要签名吗?签个名吧球球您了——$()" }, { "type": "crafting", "recipe": "indrev:shaped/cooler_cell", "text": "$(o)冷酷无情就是我——$()" }, { "type": "crafting", "recipe": "indrev:shaped/heatsink", "text": "$(o)还想再来一个双关呢?两个就已经够冷了!$()" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/temperature/info.json ================================================ { "name": "机器如何运作?", "category": "indrev:temperature", "sortnum": 0, "icon": "indrev:fan", "pages": [ { "type": "text", "text": "“工业革命”的科学家™们用最前沿最先进的高精尖理论知识打造出了这些智能高效而,呃,不会爆炸的机器。$(br)并且,机器在其最适温度下运转效率最高。只要机器持续运作,达到这个温度时就会停止升温。" }, { "type": "text", "text": "想要让机器达到最适温度,你需要时刻进行进行冷却,此时的运作速率$(l)并不会$()提升。更多请参阅$(l:temperature/coolers_and_fans)$(t:时刻保持高“冷”~)以时刻保持高效!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/energy_reader.json ================================================ { "name": "能量示数器", "category": "indrev:tools", "sortnum": 4, "icon": "indrev:energy_reader", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/energy_reader", "text": "查看当前机器存储的能量与能耗。对电缆也有用。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/gamer_axe.json ================================================ { "name": "头号玩家斧", "category": "indrev:tools", "sortnum": 6, "icon": "indrev:gamer_axe", "pages": [ { "type": "text", "text": "头号玩家斧头,彰显你头号玩家的最终实力!可以高速砍伐,安装模块后还可以造成巨量伤害,可谓居家不二之选!$(br)跟棍子没两样?先右键启动试试……" }, { "type": "crafting", "recipe": "indrev:shaped/gamer_axe" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/hammer.json ================================================ { "name": "锤子", "category": "indrev:tools", "sortnum": 0, "icon": "indrev:hammer", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/hammer", "text": "没有压缩机时,用这个把锭敲成板吧!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/mining_drills.json ================================================ { "name": "采矿钻头", "category": "indrev:tools", "sortnum": 3, "icon": "indrev:mining_drill_mk1", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/mining_drill_mk1", "recipe2": "indrev:shaped/mining_drill_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/mining_drill_mk3", "recipe2": "indrev:shaped/mining_drill_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/modular_armor.json ================================================ { "name": "模块盔甲", "category": "indrev:tools", "sortnum": 5, "icon": "indrev:modular_armor_helmet", "pages": [ { "type": "text", "text": "模块盔甲本身……没那么出彩,但加装模块以后可以变得很强!$(br2)颜色模块没有合成配方,你只能在地牢里找到颜色模块。" }, { "type": "text", "text": "模块的具体强度可以随意配置。请前往$(l)按键绑定$()菜单中设置“模块化物品设置”" }, { "type": "crafting", "recipe": "indrev:shaped/modular_armor_helmet", "recipe2": "indrev:modular_armor_chest" }, { "type": "crafting", "recipe": "indrev:shaped/modular_armor_legs", "recipe2": "indrev:modular_armor_boots" }, { "type": "crafting", "recipe": "indrev:shaped/modular_workbench", "text": "模块化工作台用于给模块化盔甲安装模块。请注意安装模块会消耗时间和能量!" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/modular_core.json ================================================ { "name": "模块化核心", "category": "indrev:tools", "sortnum": 7, "icon": "indrev:modular_core", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/modular_core", "text": "模块化核心,顾名思义,就是所有模块化物品的核心部件。当然,模块化核心需要激活才能使用。激活它至少需要一个激光发射器,一个微仓,和充足的能量(1亿)。" }, { "type": "crafting", "recipe": "indrev:shaped/capsule", "text": "激活过程中,模块化核心须被放置在微仓内。" }, { "type": "crafting", "recipe": "indrev:shaped/laser_emitter_mk4", "text": "激光发射器需要面向微仓放置在$(l)$(n)3格开外整$()$()。它们可以存储250万LF,且需要红石信号激活。" }, { "type": "text", "text": "充能需要时间,但每一束激光都可以加快进程。$(br)$(n)$(o)必读!激光操作须知:$()$()$(br)- 激光打在$(n)非空微仓$()以外的地方会造成爆炸。$(br)- 充能完成之前掉电的话,所有进程都会被重置。$(br)- 在拿取核心之前记得关掉激光哦。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/screwdriver.json ================================================ { "name": "螺丝刀", "category": "indrev:tools", "sortnum": 2, "icon": "indrev:screwdriver", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/screwdriver", "text": "螺丝刀用于配置机器的流体/物品输入输出。$(br)也用于配置青金石通量容器的能量输入输出。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/wrench.json ================================================ { "name": "扳手", "category": "indrev:tools", "sortnum": 1, "icon": "indrev:wrench", "pages": [ { "type": "crafting", "recipe": "indrev:shaped/wrench", "text": "扳手可用于旋转或拆卸机器。扳手拆除的机器会保存电量。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/transportation/energy.json ================================================ { "name": "能量传输", "category": "indrev:transportation", "sortnum": 0, "icon": "indrev:cable_mk1", "pages": [ { "type": "text", "text": "线缆可用于远距离跨机器传输能量。$(br)按住Shift查阅物品说明以查看每一种线缆的传输上限。$(br)机器没电了?改进产能和升级线缆都是好主意!" }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk1", "recipe2": "indrev:shaped/cable_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/cable_mk3", "recipe2": "indrev:shaped/cable_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/transportation/fluid_pipes.json ================================================ { "name": "流体传输", "category": "indrev:transportation", "sortnum": 2, "icon": "indrev:fluid_pipe_mk1", "pages": [ { "type": "text", "text": "流体管道可用于远距离转运流体。$(br)物品说明附注明传输上限。$(br)管道每秒进行一次传输,而非每游戏刻。$(br2)管道需要伺服机构才能正常运作,请参阅$(l:transportation/servos)$(t:查阅伺服机构)伺服机构$()一章。" }, { "type": "crafting", "recipe": "indrev:shaped/fluid_pipe_mk1", "recipe2": "indrev:shaped/fluid_pipe_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/fluid_pipe_mk3", "recipe2": "indrev:shaped/fluid_pipe_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/transportation/item_pipes.json ================================================ { "name": "物品传输", "category": "indrev:transportation", "sortnum": 1, "icon": "indrev:item_pipe_mk1", "pages": [ { "type": "text", "text": "物品管道可用于远距离转运物品。$(br)物品说明附注明传输上限。$(br)管道每秒进行一次传输,而非每游戏刻。$(br)物品管道附有$(l)高级过滤$()功能,右击配置。$(br2)管道需要伺服机构才能正常运作,请参阅$(l:transportation/servos)$(t:查阅伺服机构)伺服机构$()一章。" }, { "type": "crafting", "recipe": "indrev:shaped/item_pipe_mk1", "recipe2": "indrev:shaped/item_pipe_mk2" }, { "type": "crafting", "recipe": "indrev:shaped/item_pipe_mk3", "recipe2": "indrev:shaped/item_pipe_mk4" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/transportation/servos.json ================================================ { "name": "伺服机构", "category": "indrev:transportation", "sortnum": 3, "icon": "indrev:servo_retriever", "pages": [ { "type": "text", "text": "流体和物品管道需要两种伺服机构运作:推送机构和收取机构。$(br)右击一个已经连接容器的管道以安装伺服机构。使用扳手拆卸。$(br)$(l)注意:$()收取推送机构是相互独立的!二者用途各不相同,无需同时安装。" }, { "type": "text", "text": "例如,假设你想从某一容器中提取物品并输向多个容器,你需要在这一容器的连接的管道上放置推送机构。$(br2)但如果你想从多个容器中抽取物品并抽向某一容器,则在这一容器的管道上放置收取机构。$(br2)$(l)注意:$()请提前配置好机器的输入输出!请查阅$(l:tools/screwdriver)$(t:查阅螺丝刀)“螺丝刀”条目。$()" }, { "type": "text", "title": "Modes", "text": "伺服机构存在检索优先级属性。$(br)右击伺服机构进行改动。$(br)$(l)近处优先:$()伺服机构会优先考虑近端的容器。$(br)$(l)远处优先:$()伺服机构会优先考虑远端的容器。$(br)$(l)随机:$()伺服机构会随机选取容器。$(br)$(l)轮询:$()伺服机构会优先考虑物品/液体存量最少的容器。" }, { "type": "crafting", "recipe": "indrev:shaped/servo_output", "text": "推送机构会向相连的容器(容器输出侧除外)输送物品。" }, { "type": "crafting", "recipe": "indrev:shaped/servo_retriever", "text": "收取机构会向相连的容器(容器输入侧除外)提取物品。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/upgrades/enhancement_upgrades.json ================================================ { "name": "机器增益", "category": "indrev:upgrades", "sortnum": 0, "icon": "indrev:speed_enhancer", "pages": [ { "type": "text", "text": "机器增益可提升机器的运行速度、改善能耗等等。$(br2)在机器界面右侧4个方格放入增益部件。" }, { "type": "crafting", "recipe": "indrev:shaped/speed_enhancer", "text": "这个增益以能耗为代价增加机器的运行速率。" }, { "type": "crafting", "recipe": "indrev:shaped/buffer_enhancer", "text": "这个增益可增加机器的能量容量。" }, { "type": "crafting", "recipe": "indrev:shaped/blast_furnace_enhancer", "text": "这个增益允许电炉处理高炉配方。" }, { "type": "crafting", "recipe": "indrev:shaped/smoker_enhancer", "text": "这个增益允许电炉处理烟熏炉配方。" } ] } ================================================ FILE: src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/upgrades/tier_upgrades.json ================================================ { "name": "等级升级", "category": "indrev:upgrades", "sortnum": 1, "icon": "indrev:tier_upgrade_mk2", "pages": [ { "type": "text", "text": "等级升级可提升机器的输入输出能力等等。$(br2)手持等级升级部件右击低一等的机器即可升级。" }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk2", "text": "将MK1机器升为MK2。" }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk3", "text": "将MK2机器升为MK3。" }, { "type": "crafting", "recipe": "indrev:shaped/tier_upgrade_mk4", "text": "将MK3机器升为MK4。" } ] } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/bronze_ingot_from_smelting.json ================================================ { "type": "minecraft:blasting", "group": "bronze_ingots", "ingredient": { "tag": "c:bronze_dusts" }, "result": "indrev:bronze_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/copper_ingot_from_ore.json ================================================ { "type": "minecraft:blasting", "group": "copper_ingots", "ingredient": { "tag": "minecraft:copper_ores" }, "result": "minecraft:copper_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/copper_ingot_from_smelting.json ================================================ { "type": "minecraft:blasting", "group": "copper_ingots", "ingredient": { "tag": "c:copper_dusts" }, "result": "minecraft:copper_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/electrum_ingot_from_smelting.json ================================================ { "type": "minecraft:blasting", "group": "electrum_ingots", "ingredient": { "tag": "c:electrum_dusts" }, "result": "indrev:electrum_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/gold_ingot.json ================================================ { "type": "minecraft:blasting", "ingredient": { "tag": "c:gold_dusts" }, "result": "minecraft:gold_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/iron_ingot_from_dust.json ================================================ { "type": "minecraft:blasting", "group": "iron_ingots", "ingredient": { "tag": "c:iron_dusts" }, "result": "minecraft:iron_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/lead_ingot_from_ore.json ================================================ { "type": "minecraft:blasting", "group": "lead_ingots", "ingredient": { "tag": "c:lead_ores" }, "result": "indrev:lead_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/lead_ingot_from_raw_ore.json ================================================ { "type": "minecraft:blasting", "group": "lead_ingots", "ingredient": { "tag": "c:raw_lead_ores" }, "result": "indrev:lead_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/lead_ingot_from_smelting.json ================================================ { "type": "minecraft:blasting", "group": "lead_ingots", "ingredient": { "tag": "c:lead_dusts" }, "result": "indrev:lead_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/netherite_scrap.json ================================================ { "type": "minecraft:blasting", "ingredient": { "tag": "c:netherite_scrap_dusts" }, "result": "minecraft:netherite_scrap", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/silver_ingot_from_ore.json ================================================ { "type": "minecraft:blasting", "group": "silver_ingots", "ingredient": { "tag": "c:silver_ores" }, "result": "indrev:silver_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/silver_ingot_from_raw_ores.json ================================================ { "type": "minecraft:blasting", "group": "silver_ingots", "ingredient": { "tag": "c:raw_silver_ores" }, "result": "indrev:silver_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/silver_ingot_from_smelting.json ================================================ { "type": "minecraft:blasting", "group": "silver_ingots", "ingredient": { "tag": "c:silver_dusts" }, "result": "indrev:silver_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/steel_ingot.json ================================================ { "type": "minecraft:blasting", "ingredient": { "tag": "c:steel_dusts" }, "result": "indrev:steel_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/tin_ingot_from_ore.json ================================================ { "type": "minecraft:blasting", "group": "tin_ingots", "ingredient": { "tag": "c:tin_ores" }, "result": "indrev:tin_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/tin_ingot_from_raw_ores.json ================================================ { "type": "minecraft:blasting", "group": "tin_ingots", "ingredient": { "tag": "c:raw_tin_ores" }, "result": "indrev:tin_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/tin_ingot_from_smelting.json ================================================ { "type": "minecraft:blasting", "group": "tin_ingots", "ingredient": { "tag": "c:tin_dusts" }, "result": "indrev:tin_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/tungsten_ingot_from_ore.json ================================================ { "type": "minecraft:blasting", "group": "tungsten_ingots", "ingredient": { "tag": "c:tungsten_ores" }, "result": "indrev:tungsten_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/tungsten_ingot_from_raw_ores.json ================================================ { "type": "minecraft:blasting", "group": "tungsten_ingots", "ingredient": { "tag": "c:raw_tungsten_ores" }, "result": "indrev:tungsten_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/blasting/tungsten_ingot_from_smelting.json ================================================ { "type": "minecraft:blasting", "group": "tungsten_ingots", "ingredient": { "tag": "c:tungsten_dusts" }, "result": "indrev:tungsten_ingot", "cookingTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/bronze_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:bronze_ingots" }, "output": { "item": "indrev:bronze_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/carbon_fiber_plate.json ================================================ { "type": "indrev:compress", "ingredients": { "item": "indrev:soot", "count": 9 }, "output": { "item": "indrev:carbon_fiber_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/copper_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:copper_ingots" }, "output": { "item": "indrev:copper_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/electrum_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:electrum_ingots" }, "output": { "item": "indrev:electrum_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/empty_upgrade.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:tin_plates", "count": 4 }, "output": { "item": "indrev:empty_enhancer", "count": 1 }, "processTime": 300 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/gold_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "item": "minecraft:gold_ingot" }, "output": { "item": "indrev:gold_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/iron_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "item": "minecraft:iron_ingot" }, "output": { "item": "indrev:iron_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/lead_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:lead_ingots" }, "output": { "item": "indrev:lead_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/plank_block.json ================================================ { "type": "indrev:compress", "ingredients": { "item": "indrev:planks", "count": 8 }, "output": { "item": "indrev:plank_block", "count": 1 }, "processTime": 300 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/planks.json ================================================ { "type": "indrev:compress", "ingredients": { "item": "indrev:sawdust", "count": 4 }, "output": { "item": "indrev:planks", "count": 1 }, "processTime": 300 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/silver_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:silver_ingots" }, "output": { "item": "indrev:silver_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/steel_plate.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:steel_ingots" }, "output": { "item": "indrev:steel_plate", "count": 1 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/tin_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:tin_ingots" }, "output": { "item": "indrev:tin_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/compressing/tungsten_plate_from_compressor.json ================================================ { "type": "indrev:compress", "ingredients": { "tag": "c:tungsten_ingots" }, "output": { "item": "indrev:tungsten_plate", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/condensing/copper_chunk_from_molten_copper.json ================================================ { "type": "indrev:condenser", "output": { "item": "indrev:copper_chunk", "count": 1 }, "fluidInput": { "fluid": "indrev:molten_copper_still", "amount": 9000 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/condensing/gold_chunk_from_molten_gold.json ================================================ { "type": "indrev:condenser", "output": { "item": "indrev:gold_chunk", "count": 1 }, "fluidInput": { "fluid": "indrev:molten_gold_still", "amount": 9000 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/condensing/iron_chunk_from_molten_iron.json ================================================ { "type": "indrev:condenser", "output": { "item": "indrev:iron_chunk", "count": 1 }, "fluidInput": { "fluid": "indrev:molten_iron_still", "amount": 9000 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/condensing/lead_chunk_from_molten_lead.json ================================================ { "type": "indrev:condenser", "output": { "item": "indrev:lead_chunk", "count": 1 }, "fluidInput": { "fluid": "indrev:molten_lead_still", "amount": 9000 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/condensing/netherite_chunk_from_molten_netherite.json ================================================ { "type": "indrev:condenser", "output": { "item": "indrev:netherite_scrap_chunk", "count": 1 }, "fluidInput": { "fluid": "indrev:molten_netherite_still", "amount": 250 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/condensing/silver_chunk_from_molten_silver.json ================================================ { "type": "indrev:condenser", "output": { "item": "indrev:silver_chunk", "count": 1 }, "fluidInput": { "fluid": "indrev:molten_silver_still", "amount": 9000 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/condensing/tin_chunk_from_molten_tin.json ================================================ { "type": "indrev:condenser", "output": { "item": "indrev:tin_chunk", "count": 1 }, "fluidInput": { "fluid": "indrev:molten_tin_still", "amount": 9000 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/distiller/salt_from_water.json ================================================ { "type": "indrev:distiller", "output": { "item": "indrev:salt", "count": 1 }, "fluidInput": { "fluid": "minecraft:water", "amount": 27000 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/electrolysis/electrolyze_water.json ================================================ { "type": "indrev:electrolysis", "fluidInput": { "fluid": "minecraft:water", "amount": 81000 }, "fluidOutput": [ { "fluid": "indrev:hydrogen_still", "amount": 40500 }, { "fluid": "indrev:oxygen_still", "amount": 40500 } ], "processTime": 300 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/clay.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "item": "minecraft:sand", "count": 1 } ], "fluidInput": { "fluid": "minecraft:water", "amount": 81000 }, "output": { "item": "minecraft:clay", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/coolant_fluid.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "item": "minecraft:lapis_lazuli" } ], "fluidInput": { "fluid": "minecraft:water", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:coolant_still", "amount": 81000 }, "output": { "item": "empty" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/copper_purified_ore.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "tag": "minecraft:copper_ores" } ], "fluidInput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:toxic_mud_still", "amount": 1000 }, "output": { "item": "indrev:copper_purified_ore", "count": 1 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/gold_purified_ore.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "tag": "minecraft:gold_ores" } ], "fluidInput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:toxic_mud_still", "amount": 1000 }, "output": { "item": "indrev:gold_purified_ore", "count": 1 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_black_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:black_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:black_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_blue_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:blue_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:blue_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_brown_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:brown_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:brown_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_cyan_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:cyan_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:cyan_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_gray_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:gray_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:gray_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_green_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:green_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:green_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_light_blue_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:light_blue_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:light_blue_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_light_gray_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:light_gray_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:light_gray_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_lime_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:lime_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:lime_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_magenta_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:magenta_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:magenta_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_orange_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:orange_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:orange_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_pink_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:pink_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:pink_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_purple_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:purple_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:purple_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_red_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:red_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:red_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_white_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:white_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:white_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/harden_yellow_concrete_powder.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": { "item": "minecraft:yellow_concrete_powder" }, "fluidInput": { "fluid": "minecraft:water", "amount": 8100 }, "output": { "item": "minecraft:yellow_concrete", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/iron_purified_ore.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "tag": "minecraft:iron_ores" } ], "fluidInput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:toxic_mud_still", "amount": 1000 }, "output": { "item": "indrev:iron_purified_ore", "count": 1 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/lead_purified_ore.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "tag": "c:lead_ores" } ], "fluidInput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:toxic_mud_still", "amount": 1000 }, "output": { "item": "indrev:lead_purified_ore", "count": 1 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/netherite_purified_ore.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "item": "minecraft:ancient_debris" } ], "fluidInput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:toxic_mud_still", "amount": 1000 }, "output": { "item": "indrev:netherite_scrap_purified_ore", "count": 1 }, "processTime": 700 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/paper.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "item": "indrev:sawdust", "count": 3 } ], "fluidInput": { "fluid": "minecraft:water", "amount": 27000 }, "output": { "item": "minecraft:paper", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/silver_purified_ore.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "tag": "c:silver_ores" } ], "fluidInput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:toxic_mud_still", "amount": 1000 }, "output": { "item": "indrev:silver_purified_ore", "count": 1 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/sulfuric_acid.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "tag": "c:sulfur_dusts" } ], "fluidInput": { "fluid": "minecraft:water", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "output": { "item": "empty" }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/tin_purified_ore.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "tag": "c:tin_ores" } ], "fluidInput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:toxic_mud_still", "amount": 1000 }, "output": { "item": "indrev:tin_purified_ore", "count": 1 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/tungsten_purified_ore.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "tag": "c:tungsten_ores" } ], "fluidInput": { "fluid": "indrev:sulfuric_acid_still", "amount": 81000 }, "fluidOutput": { "fluid": "indrev:toxic_mud_still", "amount": 1000 }, "output": { "item": "indrev:tungsten_purified_ore", "count": 1 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/fluid_infusing/wither_proof_obsidian.json ================================================ { "type": "indrev:fluid_infuse", "ingredients": [ { "item": "minecraft:obsidian" } ], "fluidInput": { "fluid": "indrev:molten_iron_still", "amount": 81000 }, "output": { "item": "indrev:wither_proof_obsidian", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/allium.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:allium" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:allium", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/azure_bluet.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:azure_bluet" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:azure_bluet", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/big_dripleaf.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:big_dripleaf" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:big_dripleaf", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/blue_orchid.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:blue_orchid" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:blue_orchid", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/bronze_dust.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "tag": "c:tin_dusts" }, { "tag": "c:copper_dusts" } ], "output": { "item": "indrev:bronze_dust", "count": 2 }, "processTime": 300 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/brown_mushroom.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:brown_mushroom" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:brown_mushroom", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/cornflower.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:cornflower" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:cornflower", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/crimson_fungus.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:crimson_fungus" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:crimson_fungus", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/crimson_roots.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:crimson_roots" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:crimson_roots", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/dandelion.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:dandelion" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:dandelion", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/dead_bush.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:dead_bush" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:dead_bush", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/electrum_dust.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "tag": "c:gold_dusts" }, { "tag": "c:silver_dusts" } ], "output": { "item": "indrev:electrum_dust", "count": 2 }, "processTime": 300 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/enriched_nikolite.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "indrev:nikolite_dust" }, { "tag": "c:diamond_dusts" } ], "output": { "item": "indrev:enriched_nikolite_dust", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/enriched_nikolite_ingot.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "indrev:enriched_nikolite_dust" }, { "item": "indrev:nikolite_ingot" } ], "output": { "item": "indrev:enriched_nikolite_ingot", "count": 1 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/fern.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:fern" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:fern", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/glow_lichen.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:glow_lichen" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:glow_lichen", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/grass.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:grass" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:grass", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/kelp.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:kelp" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:kelp", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/large_fern.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:large_fern" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:large_fern", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/lilac.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:lilac" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:lilac", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/lily_of_the_valley.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:lily_of_the_valley" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:lily_of_the_valley", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/lily_pad.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:lily_pad" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:lily_pad", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/nether_sprouts.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:nether_sprouts" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:nether_sprouts", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/nikolite_ingot.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "indrev:nikolite_dust" }, { "item": "minecraft:iron_ingot" } ], "output": { "item": "indrev:nikolite_ingot", "count": 1 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/orange_tulip.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:orange_tulip" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:orange_tulip", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/oxeye_daisy.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:oxeye_daisy" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:oxeye_daisy", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/pink_tulip.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:pink_tulip" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:pink_tulip", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/poppy.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:poppy" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:poppy", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/red_mushroom.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:red_mushroom" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:red_mushroom", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/red_tulip.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:red_tulip" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:red_tulip", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/sea_pickle.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:sea_pickle" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:sea_pickle", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/seagrass.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:seagrass" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:seagrass", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/small_dripleaf.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:small_dripleaf" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:small_dripleaf", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/spore_blossom.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:spore_blossom" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:spore_blossom", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/steel_dust.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "tag": "c:coal_dusts" }, { "tag": "c:iron_dusts" } ], "output": { "item": "indrev:steel_dust", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/tall_grass.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:tall_grass" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:tall_grass", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/twisting_vines.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:twisting_vines" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:twisting_vines", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/untanned_leather.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:rotten_flesh" }, { "item": "minecraft:rotten_flesh" } ], "output": { "item": "indrev:untanned_leather", "count": 1 }, "processTime": 350 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/vine.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:vine" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:vine", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/warped_fungus.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:warped_fungus" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:warped_fungus", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/warped_roots.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:warped_roots" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:warped_roots", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/weeping_vines.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:weeping_vines" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:weeping_vines", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/infusing/white_tulip.json ================================================ { "type": "indrev:infuse", "ingredients": [ { "item": "minecraft:white_tulip" }, { "item": "minecraft:bone_meal" } ], "output": { "item": "minecraft:white_tulip", "count": 2 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/laser.json ================================================ { "type": "indrev:laser", "ingredients": { "item": "indrev:modular_core" }, "output": [ { "item": "indrev:modular_core_activated" } ], "processTime": 100000000 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_auto_feeder.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:tungsten_plates" }, { "tag": "c:tungsten_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_auto_feeder" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_breathing.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:tungsten_plates" }, { "tag": "c:tungsten_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_breathing" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_charger.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "item": "indrev:portable_charger" }, { "tag": "c:tungsten_plates" }, { "item": "indrev:enriched_nikolite_ingot" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_charger" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_efficiency.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:silver_plates" }, { "tag": "c:silver_plates" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_efficiency" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_elytra.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:tungsten_plates" }, { "tag": "c:tungsten_plates" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" }, { "item": "indrev:reinforced_elytra" } ], "output": { "item": "indrev:module_elytra" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_feather_falling.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "item": "minecraft:slime_ball" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_feather_falling" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_fire_aspect.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "item": "minecraft:blaze_rod" }, { "tag": "c:tungsten_plates" }, { "tag": "c:tungsten_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_fire_aspect" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_fire_resistance.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:tungsten_plates" }, { "tag": "c:tungsten_plates" }, { "tag": "c:tungsten_plates" }, { "tag": "c:tungsten_plates" }, { "tag": "c:tungsten_plates" } ], "output": { "item": "indrev:module_fire_resistance" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_fortune.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "item": "minecraft:emerald" }, { "item": "minecraft:diamond" }, { "item": "indrev:enriched_nikolite_ingot" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_fortune" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_jump_boost.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_jump_boost" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_looting.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:silver_plates" }, { "tag": "c:silver_plates" }, { "item": "indrev:enriched_nikolite_ingot" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_looting" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_night_vision.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:silver_plates" }, { "tag": "c:silver_plates" }, { "item": "indrev:enriched_nikolite_ingot" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_night_vision" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_piglin_tricker.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "item": "minecraft:gold_block" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_piglin_tricker" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_protection.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" } ], "output": { "item": "indrev:module_protection" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_range.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:steel_plates" }, { "tag": "c:steel_plates" }, { "tag": "c:electrum_plates" }, { "tag": "c:electrum_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_range" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_sharpness.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "item": "minecraft:quartz_block" }, { "tag": "c:silver_plates" }, { "tag": "c:silver_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_sharpness" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_silk_touch.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:silver_plates" }, { "tag": "c:silver_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_silk_touch" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_solar_panel.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "item": "indrev:solar_generator_mk3" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_solar_panel" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/modules/module_speed.json ================================================ { "type": "indrev:modules", "ingredients": [ { "item": "indrev:circuit_mk4" }, { "tag": "c:silver_plates" }, { "tag": "c:silver_plates" }, { "tag": "c:silver_plates" }, { "tag": "c:silver_plates" }, { "item": "indrev:enriched_nikolite_ingot" } ], "output": { "item": "indrev:module_speed" }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/blaze_power.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:blaze_rod" }, "output": { "item": "minecraft:blaze_powder", "count": 4 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/bone_meal.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:bone" }, "output": { "item": "minecraft:bone_meal", "count": 5 }, "processTime": 200, "extra": { } } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/bronze_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:bronze_ingots" }, "output": { "item": "indrev:bronze_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/coal_dust.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:coal" }, "output": { "item": "indrev:coal_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/coal_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "minecraft:coal_ores" }, "output": { "item": "minecraft:coal", "count": 2 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/cobblestone_pulverizer.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:stone" }, "output": { "item": "minecraft:cobblestone", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_chunk.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:copper_chunk" }, "output": { "item": "indrev:copper_dust", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:copper_ingots" }, "output": { "item": "indrev:copper_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "minecraft:copper_ores" }, "output": { "item": "indrev:copper_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_purified_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:copper_purified_ore" }, "output": { "item": "indrev:copper_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_raw.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:raw_copper_ores", "count": 2 }, "output": { "item": "indrev:copper_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/diamond_dust.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:diamond" }, "output": { "item": "indrev:diamond_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/diamond_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "minecraft:diamond_ores" }, "output": { "item": "minecraft:diamond", "count": 2 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/electrum_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:electrum_ingots" }, "output": { "item": "indrev:electrum_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/gold_dust_from_chunk.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:gold_chunk" }, "output": { "item": "indrev:gold_dust", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/gold_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:gold_ingots" }, "output": { "item": "indrev:gold_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/gold_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "minecraft:gold_ores" }, "output": { "item": "indrev:gold_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/gold_dust_from_raw.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:raw_gold_ores", "count": 2 }, "output": { "item": "indrev:gold_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/gravel_pulverizer.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:cobblestone" }, "output": { "item": "minecraft:gravel", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_chunk.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:iron_chunk" }, "output": { "item": "indrev:iron_dust", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:iron_ingots" }, "output": { "item": "indrev:iron_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "minecraft:iron_ores" }, "output": { "item": "indrev:iron_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_purified_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:iron_purified_ore" }, "output": { "item": "indrev:iron_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_raw.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:raw_iron_ores", "count": 2 }, "output": { "item": "indrev:iron_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_chunk.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:lead_chunk" }, "output": { "item": "indrev:lead_dust", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:lead_ingots" }, "output": { "item": "indrev:lead_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:lead_ores" }, "output": { "item": "indrev:lead_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_purified_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:lead_purified_ore" }, "output": { "item": "indrev:lead_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_raw.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:raw_lead_ores", "count": 2 }, "output": { "item": "indrev:lead_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/netherite_dust.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:ancient_debris" }, "output": { "item": "indrev:netherite_scrap_dust", "count": 2 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/netherite_dust_from_chunk.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:netherite_scrap_chunk" }, "output": { "item": "indrev:netherite_scrap_dust", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/nikolite.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:nikolite_ores" }, "output": { "item": "indrev:nikolite_dust", "count": 7 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/sand_pulverizer.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:gravel" }, "output": { "item": "minecraft:sand", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_chunk.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:silver_chunk" }, "output": { "item": "indrev:silver_dust", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:silver_ingots" }, "output": { "item": "indrev:silver_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:silver_ores" }, "output": { "item": "indrev:silver_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_purified_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:silver_purified_ore" }, "output": { "item": "indrev:silver_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_raw.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:raw_silver_ores", "count": 2 }, "output": { "item": "indrev:silver_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/sulfur_dust.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:sulfurs" }, "output": [ { "item": "indrev:sulfur_dust", "count": 2 }, { "item": "indrev:sulfur_dust", "count": 1, "chance": 0.8 } ], "processTime": 200, "extra": { } } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/sulfur_dust_from_gunpowder.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "minecraft:gunpowder" }, "output": { "item": "indrev:sulfur_dust", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_chunk.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:tin_chunk" }, "output": { "item": "indrev:tin_dust", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:tin_ingots" }, "output": { "item": "indrev:tin_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:tin_ores" }, "output": { "item": "indrev:tin_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_purified_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:tin_purified_ore" }, "output": { "item": "indrev:tin_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_raw.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:raw_tin_ores", "count": 2 }, "output": { "item": "indrev:tin_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/tungsten_dust_from_ingot.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:tungsten_ingots" }, "output": { "item": "indrev:tungsten_dust", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/tungsten_dust_from_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "tag": "c:tungsten_ores" }, "output": { "item": "indrev:tungsten_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/pulverizer/tungsten_dust_from_purified_ore.json ================================================ { "type": "indrev:pulverize", "ingredients": { "item": "indrev:tungsten_purified_ore" }, "output": { "item": "indrev:tungsten_dust", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_baked_potato.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:baked_potato", "count": 3 } ], "output": { "item": "indrev:biomass", "count": 6 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_bamboo.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:bamboo", "count": 4 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 400 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_beetroot.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:beetroot", "count": 2 } ], "output": { "item": "indrev:biomass", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_beetrot_seeds.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:beetroot_seeds", "count": 2 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_bread.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:bread", "count": 1 } ], "output": { "item": "indrev:biomass", "count": 3 }, "processTime": 350 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_brown_mushroom.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:brown_mushroom", "count": 2 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 50 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_cactus.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:cactus", "count": 3 } ], "output": { "item": "indrev:biomass", "count": 2 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_carrot.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:carrot", "count": 2 } ], "output": { "item": "indrev:biomass", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_cookie.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:cookie", "count": 1 } ], "output": { "item": "indrev:biomass", "count": 6 }, "processTime": 450 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_dirt.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:dirt", "count": 8 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 100 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_flowers.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "tag": "minecraft:flowers", "count": 3 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 50 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_grass.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:grass", "count": 4 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 100 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_grass_block.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:grass_block", "count": 6 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 100 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_hay_block.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:hay_block", "count": 1 } ], "output": { "item": "indrev:biomass", "count": 18 }, "processTime": 1000 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_logs.json ================================================ { "type": "indrev:recycle", "ingredients": { "tag": "minecraft:logs" }, "output": { "item": "indrev:biomass", "count": 4 }, "processTime": 500 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_melon.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:melon", "count": 1 } ], "output": { "item": "indrev:biomass", "count": 18 }, "processTime": 1000 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_melon_seeds.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:melon_seeds", "count": 2 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_melon_slice.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:melon_slice", "count": 1 } ], "output": { "item": "indrev:biomass", "count": 2 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_nether_wart.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:nether_wart", "count": 1 } ], "output": { "item": "indrev:biomass", "count": 3 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_planks.json ================================================ { "type": "indrev:recycle", "ingredients": { "tag": "minecraft:planks" }, "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 100 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_potato.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:potato", "count": 2 } ], "output": { "item": "indrev:biomass", "count": 3 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_red_mushroom.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:red_mushroom", "count": 2 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 50 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_saplings.json ================================================ { "type": "indrev:recycle", "ingredients": { "tag": "minecraft:saplings" }, "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_wheat.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:wheat", "count": 1 } ], "output": { "item": "indrev:biomass", "count": 2 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/recycling/recycle_wheat_seeds.json ================================================ { "type": "indrev:recycle", "ingredients": [ { "item": "minecraft:wheat_seeds", "count": 2 } ], "output": { "item": "indrev:biomass", "count": 1 }, "processTime": 150 } ================================================ FILE: src/main/resources/data/indrev/recipes/sawmill/acacia_planks.json ================================================ { "type": "indrev:sawmill", "ingredients": { "tag": "minecraft:acacia_logs" }, "output": [ { "item": "minecraft:acacia_planks", "count": 6 }, { "item": "minecraft:stick", "count": 2, "chance": 0.5 }, { "item": "indrev:sawdust", "count": 3 } ], "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/sawmill/birch_planks.json ================================================ { "type": "indrev:sawmill", "ingredients": { "tag": "minecraft:birch_logs" }, "output": [ { "item": "minecraft:birch_planks", "count": 6 }, { "item": "minecraft:stick", "count": 2, "chance": 0.5 }, { "item": "indrev:sawdust", "count": 3 } ], "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/sawmill/crimson_planks.json ================================================ { "type": "indrev:sawmill", "ingredients": { "tag": "minecraft:crimson_stems" }, "output": [ { "item": "minecraft:crimson_planks", "count": 6 }, { "item": "minecraft:stick", "count": 2, "chance": 0.5 }, { "item": "indrev:sawdust", "count": 3 } ], "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/sawmill/dark_oak_planks.json ================================================ { "type": "indrev:sawmill", "ingredients": { "tag": "minecraft:dark_oak_logs" }, "output": [ { "item": "minecraft:dark_oak_planks", "count": 6 }, { "item": "minecraft:stick", "count": 2, "chance": 0.5 }, { "item": "indrev:sawdust", "count": 3 } ], "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/sawmill/jungle_planks.json ================================================ { "type": "indrev:sawmill", "ingredients": { "tag": "minecraft:jungle_logs" }, "output": [ { "item": "minecraft:jungle_planks", "count": 6 }, { "item": "minecraft:stick", "count": 2, "chance": 0.5 }, { "item": "indrev:sawdust", "count": 3 } ], "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/sawmill/oak_planks.json ================================================ { "type": "indrev:sawmill", "ingredients": { "tag": "minecraft:oak_logs" }, "output": [ { "item": "minecraft:oak_planks", "count": 6 }, { "item": "minecraft:stick", "count": 2, "chance": 0.5 }, { "item": "indrev:sawdust", "count": 3 } ], "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/sawmill/spruce_planks.json ================================================ { "type": "indrev:sawmill", "ingredients": { "tag": "minecraft:spruce_logs" }, "output": [ { "item": "minecraft:spruce_planks", "count": 6 }, { "item": "minecraft:stick", "count": 2, "chance": 0.5 }, { "item": "indrev:sawdust", "count": 3 } ], "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/sawmill/warped_planks.json ================================================ { "type": "indrev:sawmill", "ingredients": { "tag": "minecraft:warped_stems" }, "output": [ { "item": "minecraft:warped_planks", "count": 6 }, { "item": "minecraft:stick", "count": 2, "chance": 0.5 }, { "item": "indrev:sawdust", "count": 3 } ], "processTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/battery.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " I ", "INI", "INI" ], "key": { "N": { "item": "indrev:nikolite_dust" }, "I": { "tag": "c:tin_plates" } }, "result": { "item": "indrev:battery" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/biomass_generator_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ICI", "NMN", "EBE" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk3" }, "I": { "item": "indrev:biomass" }, "E": { "tag": "c:electrum_plates" }, "N": { "item": "indrev:nikolite_dust" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:biomass_generator_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/blast_furnace_enhancer.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " E ", "NCN", " B " ], "key": { "C": { "item": "indrev:circuit_mk1" }, "B": { "item": "minecraft:blast_furnace" }, "E": { "item": "indrev:empty_enhancer" }, "N": { "item": "indrev:enriched_nikolite_dust" } }, "result": { "item": "indrev:blast_furnace_enhancer" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_axe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", "SI", " I" ], "key": { "S": { "tag": "c:bronze_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:bronze_axe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_block.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:bronze_ingots" } }, "result": { "item": "indrev:bronze_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_boots.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "S S" ], "key": { "S": { "tag": "c:bronze_ingots" } }, "result": { "item": "indrev:bronze_boots" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_chestplate.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "SSS", "SSS" ], "key": { "S": { "tag": "c:bronze_ingots" } }, "result": { "item": "indrev:bronze_chestplate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_helmet.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S" ], "key": { "S": { "tag": "c:bronze_ingots" } }, "result": { "item": "indrev:bronze_helmet" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_hoe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", " I", " I" ], "key": { "S": { "tag": "c:bronze_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:bronze_hoe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_ingot_from_nugget.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:bronze_nuggets" } }, "result": { "item": "indrev:bronze_ingot" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_leggings.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S", "S S" ], "key": { "S": { "tag": "c:bronze_ingots" } }, "result": { "item": "indrev:bronze_leggings" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_pickaxe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", " I ", " I " ], "key": { "S": { "tag": "c:bronze_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:bronze_pickaxe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_shovel.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "I", "I" ], "key": { "S": { "tag": "c:bronze_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:bronze_shovel" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/bronze_sword.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "S", "I" ], "key": { "S": { "tag": "c:bronze_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:bronze_sword" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/buffer_enhancer.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NEN", " C ", " B " ], "key": { "C": { "item": "indrev:circuit_mk1" }, "B": { "item": "indrev:battery" }, "E": { "item": "indrev:empty_enhancer" }, "N": { "item": "indrev:enriched_nikolite_dust" } }, "result": { "item": "indrev:buffer_enhancer" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/cabinet.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BDB", "BBB" ], "key": { "B": { "tag": "c:iron_plates" }, "D": { "item": "minecraft:barrel" } }, "result": { "item": "indrev:cabinet" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/cable_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "INI" ], "key": { "N": { "item": "indrev:nikolite_dust" }, "I": { "tag": "c:gold_plates" } }, "result": { "item": "indrev:cable_mk1", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/cable_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BNB", "BBB" ], "key": { "N": { "item": "indrev:nikolite_ingot" }, "B": { "item": "indrev:cable_mk1" } }, "result": { "item": "indrev:cable_mk2", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/cable_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "MMM", "MEM", "MMM" ], "key": { "E": { "item": "indrev:enriched_nikolite_dust" }, "M": { "item": "indrev:cable_mk2" } }, "result": { "item": "indrev:cable_mk3", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/cable_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "HHH", "HEH", "HHH" ], "key": { "E": { "item": "indrev:enriched_nikolite_ingot" }, "H": { "item": "indrev:cable_mk3" } }, "result": { "item": "indrev:cable_mk4", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/capsule.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "PMP", "M M", "LML" ], "key": { "M": { "item": "minecraft:glass" }, "P": { "tag": "c:tungsten_plates" }, "L": { "tag": "c:lead_plates" } }, "result": { "item": "indrev:capsule" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/carbon_fiber_boots_frame.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "S S" ], "key": { "S": { "item": "indrev:carbon_fiber_rod" } }, "result": { "item": "indrev:carbon_fiber_boots_frame" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/carbon_fiber_chest_frame.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "SSS", "SSS" ], "key": { "S": { "item": "indrev:carbon_fiber_rod" } }, "result": { "item": "indrev:carbon_fiber_chest_frame" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/carbon_fiber_helmet_frame.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S" ], "key": { "S": { "item": "indrev:carbon_fiber_rod" } }, "result": { "item": "indrev:carbon_fiber_helmet_frame" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/carbon_fiber_legs_frame.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S", "S S" ], "key": { "S": { "item": "indrev:carbon_fiber_rod" } }, "result": { "item": "indrev:carbon_fiber_legs_frame" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/carbon_fiber_rod.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "H", "H", "H" ], "key": { "H": { "item": "indrev:carbon_fiber_plate" } }, "result": { "item": "indrev:carbon_fiber_rod", "count": 1 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/charge_pad_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ECE", "SLS", "SES" ], "key": { "L": { "item": "indrev:lazuli_flux_container_mk4" }, "C": { "item": "indrev:circuit_mk4" }, "S": { "tag": "c:steel_plates" }, "E": { "item": "indrev:enriched_nikolite_ingot" } }, "result": { "item": "indrev:charge_pad_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/chopper_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "GCG", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "N": { "tag": "c:bronze_plates" }, "G": { "item": "minecraft:golden_axe" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:chopper_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/circuit_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BNB", "NIN", "BNB" ], "key": { "N": { "item": "indrev:nikolite_dust" }, "I": { "tag": "c:gold_plates" }, "B": { "tag": "c:copper_plates" } }, "result": { "item": "indrev:circuit_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/circuit_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SES", "ECE", "SES" ], "key": { "E": { "item": "indrev:nikolite_ingot" }, "C": { "item": "indrev:circuit_mk1" }, "S": { "tag": "c:silver_plates" } }, "result": { "item": "indrev:circuit_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/circuit_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "MEM", "ECE", "MEM" ], "key": { "E": { "item": "indrev:enriched_nikolite_dust" }, "C": { "item": "indrev:circuit_mk2" }, "M": { "tag": "c:electrum_plates" } }, "result": { "item": "indrev:circuit_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/circuit_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "RER", "ECE", "RER" ], "key": { "E": { "item": "indrev:enriched_nikolite_ingot" }, "C": { "item": "indrev:circuit_mk3" }, "R": { "tag": "c:lead_plates" } }, "result": { "item": "indrev:circuit_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/coal_generator_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "IMI", "IBI" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "I": { "tag": "c:copper_plates" }, "N": { "item": "indrev:heat_coil" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:coal_generator_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/compressor_factory_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "IFI", "IBI" ], "key": { "F": { "item": "indrev:compressor_mk4" }, "C": { "item": "indrev:circuit_mk4" }, "I": { "tag": "c:lead_plates" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:compressor_factory_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/compressor_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SCS", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "N": { "tag": "c:copper_plates" }, "S": { "item": "minecraft:stone" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:compressor_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/condenser_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C ", "SMS", "SBS" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk4" }, "S": { "tag": "c:lead_plates" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:condenser_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/controller.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "IBI", "BNB", "IBI" ], "key": { "I": { "tag": "c:steel_plates" }, "B": { "tag": "c:silver_plates" }, "N": { "item": "minecraft:redstone" } }, "result": { "item": "indrev:controller" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/cooler_cell.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " T ", "TCT", " T " ], "key": { "C": { "item": "indrev:coolant_bucket" }, "T": { "tag": "c:tin_plates" } }, "result": { "item": "indrev:cooler_cell" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_axe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", "SI", " I" ], "key": { "S": { "tag": "c:copper_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:copper_axe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_boots.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "S S" ], "key": { "S": { "tag": "c:copper_ingots" } }, "result": { "item": "indrev:copper_boots" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_chestplate.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "SSS", "SSS" ], "key": { "S": { "tag": "c:copper_ingots" } }, "result": { "item": "indrev:copper_chestplate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_helmet.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S" ], "key": { "S": { "tag": "c:copper_ingots" } }, "result": { "item": "indrev:copper_helmet" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_hoe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", " I", " I" ], "key": { "S": { "tag": "c:copper_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:copper_hoe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_ingot_from_nuggets.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:copper_nuggets" } }, "result": { "item": "minecraft:copper_ingot" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_leggings.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S", "S S" ], "key": { "S": { "tag": "c:copper_ingots" } }, "result": { "item": "indrev:copper_leggings" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_pickaxe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", " I ", " I " ], "key": { "S": { "tag": "c:copper_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:copper_pickaxe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_shovel.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "I", "I" ], "key": { "S": { "tag": "c:copper_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:copper_shovel" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/copper_sword.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "S", "I" ], "key": { "S": { "tag": "c:copper_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:copper_sword" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/damage_upgrade.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C ", "SES", "N N" ], "key": { "C": { "item": "indrev:circuit_mk1" }, "S": { "item": "minecraft:diamond_sword" }, "E": { "item": "indrev:empty_enhancer" }, "N": { "item": "indrev:enriched_nikolite_dust" } }, "result": { "item": "indrev:damage_enhancer" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/data_card_writer_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C ", "IFI", "IBI" ], "key": { "F": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk4" }, "I": { "tag": "c:steel_plates" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:data_card_writer_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/diamond_drill_head.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ISI", "III", " I " ], "key": { "I": { "item": "minecraft:diamond" }, "S": { "item": "indrev:iron_drill_head" } }, "result": { "item": "indrev:diamond_drill_head" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/drain_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "GBG", "IMI" ], "key": { "G": { "item": "minecraft:glass" }, "I": { "tag": "c:iron_plates" }, "B": { "item": "minecraft:bucket" }, "M": { "item": "indrev:machine_block" } }, "result": { "item": "indrev:drain_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/drill_bottom.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SLS", "SLS", " S " ], "key": { "S": { "tag": "c:steel_plates" }, "L": { "tag": "c:lead_plates" } }, "result": { "item": "indrev:drill_bottom" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/duct.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BB ", " B ", " B " ], "key": { "B": { "tag": "c:lead_plates" } }, "result": { "item": "indrev:duct" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/electric_furnace_factory_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "IFI", "IBI" ], "key": { "F": { "item": "indrev:electric_furnace_mk4" }, "C": { "item": "indrev:circuit_mk4" }, "I": { "tag": "c:steel_plates" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:electric_furnace_factory_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/electric_furnace_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C ", "IFI", "IBI" ], "key": { "F": { "item": "minecraft:furnace" }, "C": { "item": "indrev:circuit_mk1" }, "I": { "tag": "c:bronze_plates" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:electric_furnace_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/electrum_block.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:electrum_ingots" } }, "result": { "item": "indrev:electrum_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/electrum_block_from_nugget.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:electrum_nuggets" } }, "result": { "item": "indrev:electrum_ingot" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/energy_reader.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C ", "TPT" ], "key": { "C": { "item": "indrev:circuit_mk1" }, "T": { "tag": "c:tin_plates" }, "P": { "tag": "c:copper_plates" } }, "result": { "item": "indrev:energy_reader" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fan.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " T ", "T T", " T " ], "key": { "T": { "tag": "c:tin_plates" } }, "result": { "item": "indrev:fan" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/farmer_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "IFI", "IBI" ], "key": { "F": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "I": { "tag": "c:bronze_plates" }, "N": { "item": "minecraft:golden_hoe" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:farmer_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fisher_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "PCP", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk2" }, "N": { "tag": "c:silver_plates" }, "B": { "item": "indrev:battery" }, "P": { "item": "indrev:planks" } }, "result": { "item": "indrev:fisher_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fisher_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "SMS", "SBS" ], "key": { "M": { "item": "indrev:fisher_mk2" }, "C": { "item": "indrev:circuit_mk3" }, "N": { "item": "indrev:enriched_nikolite_dust" }, "S": { "tag": "c:electrum_plates" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:fisher_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fisher_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "LML", "LBL" ], "key": { "M": { "item": "indrev:fisher_mk3" }, "C": { "item": "indrev:circuit_mk4" }, "L": { "tag": "c:lead_plates" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:fisher_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fluid_infuser_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "GCG", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "N": { "tag": "c:bronze_plates" }, "B": { "item": "indrev:battery" }, "G": { "item": "indrev:tank" } }, "result": { "item": "indrev:fluid_infuser_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fluid_pipe_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "III" ], "key": { "I": { "item": "minecraft:iron_ingot" } }, "result": { "item": "indrev:fluid_pipe_mk1", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fluid_pipe_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BNB", "BBB" ], "key": { "N": { "item": "indrev:nikolite_ingot" }, "B": { "item": "indrev:fluid_pipe_mk1" } }, "result": { "item": "indrev:fluid_pipe_mk2", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fluid_pipe_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BNB", "BBB" ], "key": { "N": { "item": "indrev:enriched_nikolite_dust" }, "B": { "item": "indrev:fluid_pipe_mk2" } }, "result": { "item": "indrev:fluid_pipe_mk3", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/fluid_pipe_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BNB", "BBB" ], "key": { "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:fluid_pipe_mk3" } }, "result": { "item": "indrev:fluid_pipe_mk4", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/frame.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "III", "III", "III" ], "key": { "I": { "tag": "c:steel_plates" } }, "result": { "item": "indrev:frame" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/gamer_axe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "CSC", "ENE", "SBS" ], "key": { "S": { "tag": "c:steel_plates" }, "E": { "item": "indrev:enriched_nikolite_ingot" }, "N": { "item": "minecraft:netherite_axe" }, "B": { "item": "indrev:battery" }, "C": { "item": "indrev:circuit_mk4" } }, "result": { "item": "indrev:gamer_axe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/hammer.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "III", "ISI", " S " ], "key": { "I": { "item": "minecraft:iron_ingot" }, "S": { "item": "minecraft:stick" } }, "result": { "item": "indrev:hammer" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/heat_coil.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " I ", " I ", " I " ], "key": { "I": { "tag": "c:copper_plates" } }, "result": { "item": "indrev:heat_coil" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/heat_generator.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "UCU", "NMN", "LBL" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "U": { "item": "indrev:heat_coil" }, "B": { "item": "indrev:battery" }, "L": { "tag": "c:lead_plates" }, "M": { "item": "indrev:machine_block" }, "N": { "item": "indrev:enriched_nikolite_ingot" } }, "result": { "item": "indrev:heat_generator_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/heatsink.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "TCT", "FCF", "TCT" ], "key": { "C": { "item": "indrev:coolant_bucket" }, "T": { "tag": "c:tin_plates" }, "F": { "item": "indrev:fan" } }, "result": { "item": "indrev:heatsink" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/intake.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "IVI", "IBI", "III" ], "key": { "I": { "tag": "c:steel_plates" }, "B": { "tag": "c:lead_plates" }, "V": { "item": "indrev:fan" } }, "result": { "item": "indrev:intake" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/iron_drill_head.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ISI", "III", " I " ], "key": { "I": { "item": "minecraft:iron_ingot" }, "S": { "item": "indrev:stone_drill_head" } }, "result": { "item": "indrev:iron_drill_head" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/item_pipe_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NIN" ], "key": { "N": { "item": "minecraft:iron_ingot" }, "I": { "item": "minecraft:gold_nugget" } }, "result": { "item": "indrev:item_pipe_mk1", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/item_pipe_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BNB", "BBB" ], "key": { "N": { "item": "indrev:nikolite_ingot" }, "B": { "item": "indrev:item_pipe_mk1" } }, "result": { "item": "indrev:item_pipe_mk2", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/item_pipe_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BNB", "BBB" ], "key": { "N": { "item": "indrev:enriched_nikolite_dust" }, "B": { "item": "indrev:item_pipe_mk2" } }, "result": { "item": "indrev:item_pipe_mk3", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/item_pipe_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BBB", "BNB", "BBB" ], "key": { "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:item_pipe_mk3" } }, "result": { "item": "indrev:item_pipe_mk4", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/laser_emitter_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "PCP", "BMB", "NBN" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "M": { "item": "indrev:machine_block" }, "B": { "item": "indrev:battery" }, "P": { "tag": "c:tungsten_plates" } }, "result": { "item": "indrev:laser_emitter_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lazuli_flux_container_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "PCP", "BMB", "NBN" ], "key": { "C": { "item": "indrev:circuit_mk1" }, "N": { "item": "indrev:nikolite_dust" }, "M": { "item": "indrev:machine_block" }, "B": { "item": "indrev:battery" }, "P": { "tag": "c:bronze_plates" } }, "result": { "item": "indrev:lazuli_flux_container_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lazuli_flux_container_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "PCP", "BLB", "NBN" ], "key": { "C": { "item": "indrev:circuit_mk2" }, "N": { "item": "indrev:nikolite_ingot" }, "L": { "item": "indrev:lazuli_flux_container_mk1" }, "B": { "item": "indrev:battery" }, "P": { "tag": "c:silver_plates" } }, "result": { "item": "indrev:lazuli_flux_container_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lazuli_flux_container_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "PCP", "BLB", "NBN" ], "key": { "C": { "item": "indrev:circuit_mk3" }, "L": { "item": "indrev:lazuli_flux_container_mk2" }, "N": { "item": "indrev:enriched_nikolite_dust" }, "B": { "item": "indrev:battery" }, "P": { "tag": "c:electrum_plates" } }, "result": { "item": "indrev:lazuli_flux_container_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lazuli_flux_container_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "PCP", "BLB", "NBN" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "P": { "tag": "c:lead_plates" }, "L": { "item": "indrev:lazuli_flux_container_mk3" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:lazuli_flux_container_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_axe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", "SI", " I" ], "key": { "S": { "tag": "c:lead_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:lead_axe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_block.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:lead_ingots" } }, "result": { "item": "indrev:lead_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_boots.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "S S" ], "key": { "S": { "tag": "c:lead_ingots" } }, "result": { "item": "indrev:lead_boots" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_chestplate.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "SSS", "SSS" ], "key": { "S": { "tag": "c:lead_ingots" } }, "result": { "item": "indrev:lead_chestplate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_helmet.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S" ], "key": { "S": { "tag": "c:lead_ingots" } }, "result": { "item": "indrev:lead_helmet" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_hoe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", " I", " I" ], "key": { "S": { "tag": "c:lead_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:lead_hoe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_ingot_from_nuggets.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:lead_nuggets" } }, "result": { "item": "indrev:lead_ingot" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_leggings.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S", "S S" ], "key": { "S": { "tag": "c:lead_ingots" } }, "result": { "item": "indrev:lead_leggings" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_pickaxe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", " I ", " I " ], "key": { "S": { "tag": "c:lead_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:lead_pickaxe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_shovel.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "I", "I" ], "key": { "S": { "tag": "c:lead_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:lead_shovel" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/lead_sword.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "S", "I" ], "key": { "S": { "tag": "c:lead_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:lead_sword" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/machine_block.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "INI", "I I", "INI" ], "key": { "N": { "item": "indrev:nikolite_dust" }, "I": { "tag": "c:iron_plates" } }, "result": { "item": "indrev:machine_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/mining_drill_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NPN", "OCO", "OBO" ], "key": { "C": { "item": "indrev:circuit_mk1" }, "N": { "item": "indrev:nikolite_dust" }, "P": { "item": "indrev:stone_drill_head" }, "B": { "item": "indrev:battery" }, "O": { "tag": "c:bronze_plates" } }, "result": { "item": "indrev:mining_drill_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/mining_drill_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NPN", "OCO", "OBO" ], "key": { "C": { "item": "indrev:circuit_mk2" }, "N": { "item": "indrev:nikolite_ingot" }, "P": { "item": "indrev:iron_drill_head" }, "B": { "item": "indrev:battery" }, "O": { "tag": "c:silver_plates" } }, "result": { "item": "indrev:mining_drill_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/mining_drill_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NPN", "OCO", "OBO" ], "key": { "C": { "item": "indrev:circuit_mk3" }, "N": { "item": "indrev:enriched_nikolite_dust" }, "P": { "item": "indrev:diamond_drill_head" }, "B": { "item": "indrev:battery" }, "O": { "tag": "c:electrum_plates" } }, "result": { "item": "indrev:mining_drill_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/mining_drill_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NPN", "OCO", "OBO" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "P": { "item": "indrev:netherite_drill_head" }, "B": { "item": "indrev:battery" }, "O": { "tag": "c:lead_plates" } }, "result": { "item": "indrev:mining_drill_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/mining_rig_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "GCG", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk4" }, "N": { "tag": "c:lead_plates" }, "G": { "item": "indrev:netherite_drill_head" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:mining_rig_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/modular_armor_boots.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ICI", "NSN", "BMB" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "S": { "item": "indrev:steel_boots" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" }, "I": { "item": "minecraft:netherite_ingot" }, "M": { "item": "indrev:modular_core_activated" } }, "result": { "item": "indrev:modular_armor_boots" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/modular_armor_chest.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ICI", "NSN", "BMB" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "S": { "item": "indrev:steel_chestplate" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" }, "I": { "item": "minecraft:netherite_ingot" }, "M": { "item": "indrev:modular_core_activated" } }, "result": { "item": "indrev:modular_armor_chest" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/modular_armor_helmet.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ICI", "NSN", "BMB" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "S": { "item": "indrev:steel_helmet" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" }, "I": { "item": "minecraft:netherite_ingot" }, "M": { "item": "indrev:modular_core_activated" } }, "result": { "item": "indrev:modular_armor_helmet" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/modular_armor_legs.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ICI", "NSN", "BMB" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "S": { "item": "indrev:steel_leggings" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" }, "I": { "item": "minecraft:netherite_ingot" }, "M": { "item": "indrev:modular_core_activated" } }, "result": { "item": "indrev:modular_armor_legs" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/modular_core.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "PNP", "PCP" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "P": { "tag": "c:tungsten_plates" } }, "result": { "item": "indrev:modular_core" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/modular_workbench.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ICI", "GMG", "BNB" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk4" }, "I": { "tag": "c:steel_plates" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" }, "G": { "item": "minecraft:glass" } }, "result": { "item": "indrev:modular_workbench_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/netherite_drill_head.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "ISI", "III", " I " ], "key": { "I": { "item": "minecraft:netherite_ingot" }, "S": { "item": "indrev:diamond_drill_head" } }, "result": { "item": "indrev:netherite_drill_head" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/ore_data_card.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " S ", "IRI", " I " ], "key": { "I": { "tag": "c:iron_plates" }, "S": { "item": "minecraft:glass" }, "R": { "item": "minecraft:redstone" } }, "result": { "item": "indrev:ore_data_card" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/paper.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "item": "indrev:sawdust" }, { "item": "indrev:sawdust" }, { "item": "indrev:sawdust" }, { "item": "minecraft:water_bucket" } ], "result": { "item": "minecraft:paper", "count": 3 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/portable_charger.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "SBS", "BEB" ], "key": { "B": { "item": "indrev:battery" }, "C": { "item": "indrev:circuit_mk4" }, "S": { "tag": "c:steel_plates" }, "E": { "item": "indrev:enriched_nikolite_ingot" }, "N": { "item": "indrev:nikolite_dust" } }, "result": { "item": "indrev:portable_charger" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/pulverizer_factory_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "IFI", "IBI" ], "key": { "F": { "item": "indrev:pulverizer_mk4" }, "C": { "item": "indrev:circuit_mk4" }, "I": { "tag": "c:steel_plates" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:pulverizer_factory_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/pulverizer_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "FCF", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "N": { "tag": "c:copper_plates" }, "F": { "item": "minecraft:flint" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:pulverizer_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/pump_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "FCF", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "N": { "tag": "c:bronze_plates" }, "F": { "item": "minecraft:bucket" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:pump_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/rancher_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "LCF", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "N": { "tag": "c:bronze_plates" }, "F": { "item": "minecraft:feather" }, "L": { "item": "minecraft:milk_bucket" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:rancher_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/raw_lead_block.json ================================================ { "type": "crafting_shaped", "pattern": [ "###", "###", "###" ], "key": { "#": { "tag": "c:raw_lead_ores" } }, "result": { "item": "indrev:raw_lead_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/raw_silver_block.json ================================================ { "type": "crafting_shaped", "pattern": [ "###", "###", "###" ], "key": { "#": { "tag": "c:raw_silver_ores" } }, "result": { "item": "indrev:raw_silver_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/raw_tin_block.json ================================================ { "type": "crafting_shaped", "pattern": [ "###", "###", "###" ], "key": { "#": { "tag": "c:raw_tin_ores" } }, "result": { "item": "indrev:raw_tin_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/raw_tungsten_block.json ================================================ { "type": "crafting_shaped", "pattern": [ "###", "###", "###" ], "key": { "#": { "tag": "c:raw_tungsten_ores" } }, "result": { "item": "indrev:raw_tungsten_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/recycler_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "DCD", "NMN", "SBS" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk2" }, "N": { "item": "indrev:nikolite_dust" }, "D": { "item": "minecraft:composter" }, "S": { "tag": "c:silver_plates" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:recycler_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/reinforced_elytra.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " M ", "MCM", " M " ], "key": { "C": { "item": "minecraft:elytra" }, "M": { "tag": "c:steel_plates" } }, "result": { "item": "indrev:reinforced_elytra" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/sawmill_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SCS", "NMN", "SBS" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "N": { "tag": "c:bronze_plates" }, "B": { "item": "indrev:battery" }, "S": { "tag": "c:silver_plates" } }, "result": { "item": "indrev:sawmill_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/screwdriver.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C", " S ", "S " ], "key": { "C": { "item": "minecraft:iron_nugget" }, "S": { "item": "minecraft:stick" } }, "result": { "item": "indrev:screwdriver" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/servo_output.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "III", "IOI" ], "key": { "I": { "item": "minecraft:iron_nugget" }, "O": { "tag": "c:copper_nuggets" } }, "result": { "item": "indrev:servo_output" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/servo_retriever.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "III", "IOI" ], "key": { "I": { "item": "minecraft:iron_nugget" }, "O": { "item": "indrev:nikolite_dust" } }, "result": { "item": "indrev:servo_retriever" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silo.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "BIB", "I I", "BIB" ], "key": { "I": { "tag": "c:steel_plates" }, "B": { "tag": "c:tungsten_plates" } }, "result": { "item": "indrev:silo" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_axe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", "SI", " I" ], "key": { "S": { "tag": "c:silver_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:silver_axe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_block.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:silver_ingots" } }, "result": { "item": "indrev:silver_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_boots.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "S S" ], "key": { "S": { "tag": "c:silver_ingots" } }, "result": { "item": "indrev:silver_boots" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_chestplate.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "SSS", "SSS" ], "key": { "S": { "tag": "c:silver_ingots" } }, "result": { "item": "indrev:silver_chestplate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_helmet.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S" ], "key": { "S": { "tag": "c:silver_ingots" } }, "result": { "item": "indrev:silver_helmet" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_hoe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", " I", " I" ], "key": { "S": { "tag": "c:silver_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:silver_hoe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_ingot_from_nuggets.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:silver_nuggets" } }, "result": { "item": "indrev:silver_ingot" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_leggings.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S", "S S" ], "key": { "S": { "tag": "c:silver_ingots" } }, "result": { "item": "indrev:silver_leggings" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_pickaxe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", " I ", " I " ], "key": { "S": { "tag": "c:silver_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:silver_pickaxe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_shovel.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "I", "I" ], "key": { "S": { "tag": "c:silver_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:silver_shovel" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/silver_sword.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "S", "I" ], "key": { "S": { "tag": "c:silver_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:silver_sword" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/slaughter_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "IFI", "IBI" ], "key": { "F": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "I": { "tag": "c:bronze_plates" }, "N": { "item": "minecraft:golden_sword" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:slaughter_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/smelter_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C ", "NMN", "SBS" ], "key": { "M": { "item": "minecraft:blast_furnace" }, "C": { "item": "indrev:circuit_mk4" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "S": { "tag": "c:steel_ingots" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:smelter_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/smoker_enhancer.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C ", "BEB", "N N" ], "key": { "C": { "item": "indrev:circuit_mk1" }, "B": { "tag": "minecraft:logs" }, "E": { "item": "indrev:empty_enhancer" }, "N": { "item": "indrev:enriched_nikolite_dust" } }, "result": { "item": "indrev:smoker_enhancer" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/solar_generator_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "GCG", "NMN", "IBI" ], "key": { "G": { "item": "minecraft:glass" }, "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "I": { "tag": "c:bronze_plates" }, "N": { "item": "indrev:nikolite_dust" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:solar_generator_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/solar_generator_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " C ", "NSN", "EBE" ], "key": { "S": { "item": "indrev:solar_generator_mk1" }, "C": { "item": "indrev:circuit_mk3" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "E": { "tag": "c:electrum_plates" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:solar_generator_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/solid_infuser_factory_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NCN", "IFI", "IBI" ], "key": { "F": { "item": "indrev:solid_infuser_mk4" }, "C": { "item": "indrev:circuit_mk4" }, "I": { "tag": "c:steel_plates" }, "N": { "item": "indrev:enriched_nikolite_ingot" }, "B": { "item": "indrev:battery" } }, "result": { "item": "indrev:solid_infuser_factory_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/solid_infuser_mk1.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "GCG", "NMN", "NBN" ], "key": { "M": { "item": "indrev:machine_block" }, "C": { "item": "indrev:circuit_mk1" }, "N": { "tag": "c:copper_plates" }, "B": { "item": "indrev:battery" }, "G": { "item": "minecraft:glass" } }, "result": { "item": "indrev:solid_infuser_mk1" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/speed_enhancer.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " E ", "OCO", " O " ], "key": { "O": { "item": "indrev:cooler_cell" }, "C": { "item": "indrev:circuit_mk1" }, "E": { "item": "indrev:empty_enhancer" } }, "result": { "item": "indrev:speed_enhancer" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_axe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", "SI", " I" ], "key": { "S": { "tag": "c:steel_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:steel_axe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_block.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:steel_ingots" } }, "result": { "item": "indrev:steel_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_boots.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "S S" ], "key": { "S": { "tag": "c:steel_ingots" } }, "result": { "item": "indrev:steel_boots" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_chestplate.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "SSS", "SSS" ], "key": { "S": { "tag": "c:steel_ingots" } }, "result": { "item": "indrev:steel_chestplate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_helmet.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S" ], "key": { "S": { "tag": "c:steel_ingots" } }, "result": { "item": "indrev:steel_helmet" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_hoe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", " I", " I" ], "key": { "S": { "tag": "c:steel_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:steel_hoe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_ingot_from_nuggets.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:steel_nuggets" } }, "result": { "item": "indrev:steel_ingot" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_leggings.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S", "S S" ], "key": { "S": { "tag": "c:steel_ingots" } }, "result": { "item": "indrev:steel_leggings" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_pickaxe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", " I ", " I " ], "key": { "S": { "tag": "c:steel_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:steel_pickaxe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_shovel.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "I", "I" ], "key": { "S": { "tag": "c:steel_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:steel_shovel" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/steel_sword.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "S", "I" ], "key": { "S": { "tag": "c:steel_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:steel_sword" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/stone_drill_head.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "I I", "III", " I " ], "key": { "I": { "item": "minecraft:stone" } }, "result": { "item": "indrev:stone_drill_head" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tank.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "III", "GBG", "III" ], "key": { "G": { "item": "minecraft:glass" }, "I": { "tag": "c:steel_plates" }, "B": { "item": "minecraft:bucket" } }, "result": { "item": "indrev:tank" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tier_upgrade_mk2.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " P ", "PCP", "NPN" ], "key": { "C": { "item": "indrev:circuit_mk2" }, "P": { "tag": "c:silver_plates" }, "N": { "item": "indrev:nikolite_ingot" } }, "result": { "item": "indrev:tier_upgrade_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tier_upgrade_mk3.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " P ", "PCP", "NPN" ], "key": { "C": { "item": "indrev:circuit_mk3" }, "P": { "tag": "c:electrum_plates" }, "N": { "item": "indrev:enriched_nikolite_dust" } }, "result": { "item": "indrev:tier_upgrade_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tier_upgrade_mk4.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " P ", "PCP", "NPN" ], "key": { "C": { "item": "indrev:circuit_mk4" }, "P": { "tag": "c:lead_plates" }, "N": { "item": "indrev:enriched_nikolite_ingot" } }, "result": { "item": "indrev:tier_upgrade_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_axe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", "SI", " I" ], "key": { "S": { "tag": "c:tin_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:tin_axe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_block.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:tin_ingots" } }, "result": { "item": "indrev:tin_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_boots.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "S S" ], "key": { "S": { "tag": "c:tin_ingots" } }, "result": { "item": "indrev:tin_boots" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_chestplate.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S S", "SSS", "SSS" ], "key": { "S": { "tag": "c:tin_ingots" } }, "result": { "item": "indrev:tin_chestplate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_helmet.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S" ], "key": { "S": { "tag": "c:tin_ingots" } }, "result": { "item": "indrev:tin_helmet" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_hoe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SS", " I", " I" ], "key": { "S": { "tag": "c:tin_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:tin_hoe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_ingot_from_nuggets.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:tin_nuggets" } }, "result": { "item": "indrev:tin_ingot" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_leggings.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", "S S", "S S" ], "key": { "S": { "tag": "c:tin_ingots" } }, "result": { "item": "indrev:tin_leggings" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_pickaxe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "SSS", " I ", " I " ], "key": { "S": { "tag": "c:tin_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:tin_pickaxe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_shovel.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "I", "I" ], "key": { "S": { "tag": "c:tin_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:tin_shovel" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tin_sword.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "S", "S", "I" ], "key": { "S": { "tag": "c:tin_ingots" }, "I": { "item": "minecraft:stick" } }, "result": { "item": "indrev:tin_sword" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tungsten_block.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:tungsten_ingots" } }, "result": { "item": "indrev:tungsten_block" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/tungsten_ingot_from_nuggets.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "NNN", "NNN", "NNN" ], "key": { "N": { "tag": "c:tungsten_nuggets" } }, "result": { "item": "indrev:tungsten_ingot" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/warning_strobe.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ "N", "B" ], "key": { "B": { "tag": "c:iron_plates" }, "N": { "item": "minecraft:redstone" } }, "result": { "item": "indrev:warning_strobe" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shaped/wrench.json ================================================ { "type": "minecraft:crafting_shaped", "pattern": [ " CC", " SC", "S " ], "key": { "C": { "tag": "c:copper_plates" }, "S": { "item": "minecraft:stick" } }, "result": { "item": "indrev:wrench" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/bronze_ingot_from_block.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:bronze_blocks" } ], "result": { "item": "indrev:bronze_ingot", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/bronze_nugget.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:bronze_ingots" } ], "result": { "item": "indrev:bronze_nugget", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/copper_ingot_from_block.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:copper_blocks" } ], "result": { "item": "minecraft:copper_ingot", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/copper_nugget.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:copper_ingots" } ], "result": { "item": "indrev:copper_nugget", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/copper_plate_from_hammer.json ================================================ { "type": "indrev:selfremainder", "ingredients": [ { "tag": "c:copper_ingots" }, { "item": "indrev:hammer" } ], "result": { "item": "indrev:copper_plate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/electrum_ingot_from_block.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:electrum_blocks" } ], "result": { "item": "indrev:electrum_ingot", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/electrum_nugget.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:electrum_ingots" } ], "result": { "item": "indrev:electrum_nugget", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/gold_plate_from_hammer.json ================================================ { "type": "indrev:selfremainder", "ingredients": [ { "item": "minecraft:gold_ingot" }, { "item": "indrev:hammer" } ], "result": { "item": "indrev:gold_plate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/guide_book.json ================================================ { "type": "minecraft:crafting_shapeless", "group": "books", "ingredients": [ { "item": "minecraft:book" }, { "item": "indrev:nikolite_dust" } ], "result": { "item": "indrev:guide_book" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/iron_plate_from_hammer.json ================================================ { "type": "indrev:selfremainder", "ingredients": [ { "item": "minecraft:iron_ingot" }, { "item": "indrev:hammer" } ], "result": { "item": "indrev:iron_plate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/lead_ingot_from_block.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:lead_blocks" } ], "result": { "item": "indrev:lead_ingot", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/lead_nugget.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:lead_ingots" } ], "result": { "item": "indrev:lead_nugget", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/planks.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "item": "indrev:plank_block" } ], "result": { "item": "indrev:planks", "count": 8 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/raw_lead.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:raw_lead_block" } ], "result": { "item": "indrev:raw_lead", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/raw_silver.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:raw_silver_block" } ], "result": { "item": "indrev:raw_silver", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/raw_tin.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:raw_tin_block" } ], "result": { "item": "indrev:raw_tin", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/raw_tungsten.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:raw_tungsten_block" } ], "result": { "item": "indrev:raw_tungsten", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/silver_ingot_from_block.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:silver_blocks" } ], "result": { "item": "indrev:silver_ingot", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/silver_nugget.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:silver_ingots" } ], "result": { "item": "indrev:silver_nugget", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/steel_ingot_from_block.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:steel_blocks" } ], "result": { "item": "indrev:steel_ingot", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/steel_nugget.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:steel_ingots" } ], "result": { "item": "indrev:steel_nugget", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/tin_ingot_from_block.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:tin_blocks" } ], "result": { "item": "indrev:tin_ingot", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/tin_nugget.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:tin_ingots" } ], "result": { "item": "indrev:tin_nugget", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/tin_plate_from_hammer.json ================================================ { "type": "indrev:selfremainder", "ingredients": [ { "tag": "c:tin_ingots" }, { "item": "indrev:hammer" } ], "result": { "item": "indrev:tin_plate" } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/tungsten_ingot_from_block.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:tungsten_blocks" } ], "result": { "item": "indrev:tungsten_ingot", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/shapeless/tungsten_nugget.json ================================================ { "type": "minecraft:crafting_shapeless", "ingredients": [ { "tag": "c:tungsten_ingots" } ], "result": { "item": "indrev:tungsten_nugget", "count": 9 } } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_copper_from_block.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:copper_blocks" }, "fluidOutput": { "fluid": "indrev:molten_copper_still", "amount": 81000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_copper_from_dust.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:copper_dusts" }, "fluidOutput": { "fluid": "indrev:molten_copper_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_copper_from_ingot.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:copper_ingots" }, "fluidOutput": { "fluid": "indrev:molten_copper_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_copper_from_nugget.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:copper_nuggets" }, "fluidOutput": { "fluid": "indrev:molten_copper_still", "amount": 1000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_copper_from_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "minecraft:copper_ores" }, "fluidOutput": { "fluid": "indrev:molten_copper_still", "amount": 36000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_copper_from_purified_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "indrev:copper_purified_ore" }, "fluidOutput": { "fluid": "indrev:molten_copper_still", "amount": 45000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_gold_from_block.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "minecraft:gold_block" }, "fluidOutput": { "fluid": "indrev:molten_gold_still", "amount": 81000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_gold_from_dust.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:gold_dusts" }, "fluidOutput": { "fluid": "indrev:molten_gold_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_gold_from_ingot.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "minecraft:gold_ingot" }, "fluidOutput": { "fluid": "indrev:molten_gold_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_gold_from_nugget.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "minecraft:gold_nugget" }, "fluidOutput": { "fluid": "indrev:molten_gold_still", "amount": 1000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_gold_from_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "minecraft:gold_ores" }, "fluidOutput": { "fluid": "indrev:molten_gold_still", "amount": 36000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_gold_from_purified_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "indrev:gold_purified_ore" }, "fluidOutput": { "fluid": "indrev:molten_gold_still", "amount": 45000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_iron_from_block.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "minecraft:iron_block" }, "fluidOutput": { "fluid": "indrev:molten_iron_still", "amount": 81000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_iron_from_dust.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:iron_dusts" }, "fluidOutput": { "fluid": "indrev:molten_iron_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_iron_from_ingot.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "minecraft:iron_ingot" }, "fluidOutput": { "fluid": "indrev:molten_iron_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_iron_from_nugget.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "minecraft:iron_nugget" }, "fluidOutput": { "fluid": "indrev:molten_iron_still", "amount": 1000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_iron_from_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "minecraft:iron_ores" }, "fluidOutput": { "fluid": "indrev:molten_iron_still", "amount": 36000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_iron_from_purified_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "indrev:iron_purified_ore" }, "fluidOutput": { "fluid": "indrev:molten_iron_still", "amount": 45000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_lead_from_block.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:lead_blocks" }, "fluidOutput": { "fluid": "indrev:molten_lead_still", "amount": 81000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_lead_from_dust.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:lead_dusts" }, "fluidOutput": { "fluid": "indrev:molten_lead_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_lead_from_ingot.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:lead_ingots" }, "fluidOutput": { "fluid": "indrev:molten_lead_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_lead_from_nugget.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:lead_nuggets" }, "fluidOutput": { "fluid": "indrev:molten_lead_still", "amount": 1000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_lead_from_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:lead_ores" }, "fluidOutput": { "fluid": "indrev:molten_lead_still", "amount": 36000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_lead_from_purified_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "indrev:lead_purified_ore" }, "fluidOutput": { "fluid": "indrev:molten_lead_still", "amount": 45000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_netherite_from_dust.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:netherite_scrap_dusts" }, "fluidOutput": { "fluid": "indrev:molten_netherite_still", "amount": 250 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_netherite_from_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "minecraft:ancient_debris" }, "fluidOutput": { "fluid": "indrev:molten_netherite_still", "amount": 750 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_netherite_from_purified_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "indrev:netherite_scrap_purified_ore" }, "fluidOutput": { "fluid": "indrev:molten_netherite_still", "amount": 1000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_silver_from_block.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:silver_blocks" }, "fluidOutput": { "fluid": "indrev:molten_silver_still", "amount": 81000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_silver_from_dust.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:silver_dusts" }, "fluidOutput": { "fluid": "indrev:molten_silver_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_silver_from_ingot.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:silver_ingots" }, "fluidOutput": { "fluid": "indrev:molten_silver_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_silver_from_nugget.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:silver_nuggets" }, "fluidOutput": { "fluid": "indrev:molten_silver_still", "amount": 1000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_silver_from_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:silver_ores" }, "fluidOutput": { "fluid": "indrev:molten_silver_still", "amount": 36000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_silver_from_purified_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "indrev:silver_purified_ore" }, "fluidOutput": { "fluid": "indrev:molten_silver_still", "amount": 45000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_tin_from_block.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:tin_blocks" }, "fluidOutput": { "fluid": "indrev:molten_tin_still", "amount": 81000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_tin_from_dust.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:tin_dusts" }, "fluidOutput": { "fluid": "indrev:molten_tin_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_tin_from_ingot.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:tin_ingots" }, "fluidOutput": { "fluid": "indrev:molten_tin_still", "amount": 9000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_tin_from_nugget.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:tin_nuggets" }, "fluidOutput": { "fluid": "indrev:molten_tin_still", "amount": 1000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_tin_from_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "tag": "c:tin_ores" }, "fluidOutput": { "fluid": "indrev:molten_tin_still", "amount": 36000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelter/molten_tin_from_purified_ore.json ================================================ { "type": "indrev:smelter", "ingredients": { "item": "indrev:tin_purified_ore" }, "fluidOutput": { "fluid": "indrev:molten_tin_still", "amount": 45000 }, "processTime": 600 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/bronze_ingot_from_smelting.json ================================================ { "type": "minecraft:smelting", "group": "bronze_ingots", "ingredient": { "tag": "c:bronze_dusts" }, "result": "indrev:bronze_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/copper_ingot_from_smelting.json ================================================ { "type": "minecraft:smelting", "group": "copper_ingots", "ingredient": { "tag": "c:copper_dusts" }, "result": "minecraft:copper_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/electrum_ingot_from_smelting.json ================================================ { "type": "minecraft:smelting", "group": "electrum_ingots", "ingredient": { "tag": "c:electrum_dusts" }, "result": "indrev:electrum_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/gold_ingot.json ================================================ { "type": "minecraft:smelting", "ingredient": { "tag": "c:gold_dusts" }, "result": "minecraft:gold_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/iron_ingot_from_dust.json ================================================ { "type": "minecraft:smelting", "group": "iron_ingots", "ingredient": { "tag": "c:iron_dusts" }, "result": "minecraft:iron_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/lead_ingot_from_ore.json ================================================ { "type": "minecraft:smelting", "group": "lead_ingots", "ingredient": { "tag": "c:lead_ores" }, "result": "indrev:lead_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/lead_ingot_from_raw_ore.json ================================================ { "type": "minecraft:smelting", "group": "lead_ingots", "ingredient": { "tag": "c:raw_lead_ores" }, "result": "indrev:lead_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/lead_ingot_from_smelting.json ================================================ { "type": "minecraft:smelting", "group": "lead_ingots", "ingredient": { "tag": "c:lead_dusts" }, "result": "indrev:lead_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/leather.json ================================================ { "type": "minecraft:smelting", "ingredient": { "item": "indrev:untanned_leather" }, "result": "minecraft:leather", "cookingTime": 250 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/netherite_scrap.json ================================================ { "type": "minecraft:smelting", "ingredient": { "tag": "c:netherite_scrap_dusts" }, "result": "minecraft:netherite_scrap", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/silver_ingot_from_ore.json ================================================ { "type": "minecraft:smelting", "group": "silver_ingots", "ingredient": { "tag": "c:silver_ores" }, "result": "indrev:silver_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/silver_ingot_from_raw_ores.json ================================================ { "type": "minecraft:smelting", "group": "silver_ingots", "ingredient": { "tag": "c:raw_silver_ores" }, "result": "indrev:silver_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/silver_ingot_from_smelting.json ================================================ { "type": "minecraft:smelting", "group": "silver_ingots", "ingredient": { "tag": "c:silver_dusts" }, "result": "indrev:silver_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/steel_ingot.json ================================================ { "type": "minecraft:smelting", "ingredient": { "tag": "c:steel_dusts" }, "result": "indrev:steel_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/tin_ingot_from_ore.json ================================================ { "type": "minecraft:smelting", "group": "tin_ingots", "ingredient": { "tag": "c:tin_ores" }, "result": "indrev:tin_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/tin_ingot_from_raw_ores.json ================================================ { "type": "minecraft:smelting", "group": "tin_ingots", "ingredient": { "tag": "c:raw_tin_ores" }, "result": "indrev:tin_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/tin_ingot_from_smelting.json ================================================ { "type": "minecraft:smelting", "group": "tin_ingots", "ingredient": { "tag": "c:tin_dusts" }, "result": "indrev:tin_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/tungsten_ingot_from_ore.json ================================================ { "type": "minecraft:smelting", "group": "tungsten_ingots", "ingredient": { "tag": "c:tungsten_ores" }, "result": "indrev:tungsten_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/tungsten_ingot_from_raw_ores.json ================================================ { "type": "minecraft:smelting", "group": "tungsten_ingots", "ingredient": { "tag": "c:raw_tungsten_ores" }, "result": "indrev:tungsten_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/smelting/tungsten_ingot_from_smelting.json ================================================ { "type": "minecraft:smelting", "group": "tungsten_ingots", "ingredient": { "tag": "c:tungsten_dusts" }, "result": "indrev:tungsten_ingot", "cookingTime": 200 } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/chopper_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:chopper_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:chopper_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/chopper_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:chopper_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:chopper_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/chopper_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:chopper_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:chopper_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/compressor_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:compressor_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:compressor_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/compressor_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:compressor_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:compressor_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/compressor_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:compressor_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:compressor_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/electric_furnace_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:electric_furnace_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:electric_furnace_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/electric_furnace_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:electric_furnace_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:electric_furnace_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/electric_furnace_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:electric_furnace_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:electric_furnace_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/electrolytic_separator_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:electrolytic_separator_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:electrolytic_separator_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/electrolytic_separator_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:electrolytic_separator_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:electrolytic_separator_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/electrolytic_separator_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:electrolytic_separator_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:electrolytic_separator_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/farmer_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:farmer_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:farmer_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/farmer_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:farmer_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:farmer_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/farmer_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:farmer_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:farmer_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/fluid_infuser_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:fluid_infuser_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:fluid_infuser_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/fluid_infuser_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:fluid_infuser_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:fluid_infuser_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/fluid_infuser_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:fluid_infuser_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:fluid_infuser_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/pulverizer_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:pulverizer_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:pulverizer_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/pulverizer_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:pulverizer_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:pulverizer_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/pulverizer_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:pulverizer_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:pulverizer_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/rancher_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:rancher_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:rancher_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/rancher_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:rancher_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:rancher_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/rancher_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:rancher_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:rancher_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/sawmill_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:sawmill_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:sawmill_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/sawmill_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:sawmill_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:sawmill_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/sawmill_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:sawmill_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:sawmill_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/slaughter_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:slaughter_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:slaughter_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/slaughter_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:slaughter_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:slaughter_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/slaughter_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:slaughter_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:slaughter_mk4" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/solid_infuser_mk1_to_mk2.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:solid_infuser_mk1" }, { "item": "indrev:tier_upgrade_mk2" } ], "result": { "item": "indrev:solid_infuser_mk2" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/solid_infuser_mk2_to_mk3.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:solid_infuser_mk2" }, { "item": "indrev:tier_upgrade_mk3" } ], "result": { "item": "indrev:solid_infuser_mk3" } } ================================================ FILE: src/main/resources/data/indrev/recipes/upgrade/solid_infuser_mk3_to_mk4.json ================================================ { "type": "crafting_shapeless", "ingredients": [ { "item": "indrev:solid_infuser_mk3" }, { "item": "indrev:tier_upgrade_mk4" } ], "result": { "item": "indrev:solid_infuser_mk4" } } ================================================ FILE: src/main/resources/data/indrev/tags/items/coolers.json ================================================ { "replace": false, "values": [ "indrev:fan", "indrev:cooler_cell", "indrev:heatsink", "indrev:heat_coil" ] } ================================================ FILE: src/main/resources/data/minecraft/tags/blocks/mineable/axe.json ================================================ { "replace": false, "values": [ "indrev:biomass_composter", "indrev:plank_block", "indrev:planks" ] } ================================================ FILE: src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json ================================================ { "replace": false, "values": [ "indrev:compressor_factory_mk4", "indrev:coal_generator_mk1", "indrev:drill_middle", "indrev:biomass_generator_mk3", "indrev:duct", "indrev:lazuli_flux_container_mk4", "indrev:modular_workbench_mk4", "indrev:lazuli_flux_container_mk3", "indrev:lazuli_flux_container_mk2", "indrev:raw_tungsten_block", "indrev:lazuli_flux_container_mk1", "indrev:solid_infuser_creative", "indrev:farmer_creative", "indrev:bronze_block", "indrev:electric_furnace_creative", "indrev:machine_block", "indrev:charge_pad_mk4", "indrev:raw_tin_block", "indrev:raw_silver_block", "indrev:lazuli_flux_container_creative", "indrev:recycler_mk2", "indrev:dirt_oxygenator_mk1", "indrev:deepslate_silver_ore", "indrev:compressor_creative", "indrev:lead_block", "indrev:electric_furnace_mk1", "indrev:electric_furnace_mk2", "indrev:electric_furnace_mk3", "indrev:electric_furnace_mk4", "indrev:pulverizer_mk2", "indrev:pulverizer_mk1", "indrev:pulverizer_mk4", "indrev:pulverizer_mk3", "indrev:rancher_creative", "indrev:steam_turbine_energy_output", "indrev:data_card_writer_mk4", "indrev:capsule", "indrev:drill_bottom", "indrev:steel_block", "indrev:drain_mk1", "indrev:raw_lead_block", "indrev:tungsten_ore", "indrev:lead_ore", "indrev:electrolytic_separator_mk4", "indrev:electrolytic_separator_mk3", "indrev:electrolytic_separator_mk2", "indrev:electrolytic_separator_mk1", "indrev:cable_mk1", "indrev:cable_mk3", "indrev:cable_mk2", "indrev:cable_mk4", "indrev:tin_ore", "indrev:pulverizer_factory_mk4", "indrev:deepslate_tungsten_ore", "indrev:heat_generator_mk4", "indrev:steam_turbine_steam_input_valve", "indrev:silver_block", "indrev:solar_power_plant_tower", "indrev:frame", "indrev:solid_infuser_factory_mk4", "indrev:nikolite_ore", "indrev:fluid_pipe_mk4", "indrev:fluid_pipe_mk3", "indrev:fluid_pipe_mk2", "indrev:fluid_pipe_mk1", "indrev:solar_generator_mk3", "indrev:solar_generator_mk1", "indrev:compressor_mk1", "indrev:compressor_mk3", "indrev:compressor_mk2", "indrev:compressor_mk4", "indrev:item_pipe_mk1", "indrev:item_pipe_mk2", "indrev:item_pipe_mk3", "indrev:item_pipe_mk4", "indrev:steam_turbine_mk4", "indrev:condenser_mk4", "indrev:tank", "indrev:chopper_mk1", "indrev:tin_block", "indrev:chopper_mk4", "indrev:chopper_mk3", "indrev:chopper_mk2", "indrev:tungsten_block", "indrev:sulfur_crystal", "indrev:gas_generator_mk4", "indrev:cabinet", "indrev:slaughter_mk1", "indrev:slaughter_mk4", "indrev:slaughter_mk2", "indrev:slaughter_mk3", "indrev:farmer_mk3", "indrev:farmer_mk2", "indrev:farmer_mk4", "indrev:farmer_mk1", "indrev:fluid_valve", "indrev:steam_turbine_casing", "indrev:electrolytic_separator_creative", "indrev:fluid_infuser_creative", "indrev:intake", "indrev:solar_receiver", "indrev:pulverizer_creative", "indrev:silver_ore", "indrev:deepslate_tin_ore", "indrev:drill_top", "indrev:controller", "indrev:slaughter_creative", "indrev:heliostat", "indrev:silo", "indrev:solid_infuser_mk3", "indrev:solid_infuser_mk4", "indrev:solid_infuser_mk1", "indrev:solid_infuser_mk2", "indrev:electrum_block", "indrev:steam_turbine_rotor", "indrev:fluid_infuser_mk4", "indrev:fluid_infuser_mk3", "indrev:fluid_infuser_mk2", "indrev:fluid_infuser_mk1", "indrev:deepslate_lead_ore", "indrev:sawmill_creative", "indrev:rancher_mk4", "indrev:rancher_mk2", "indrev:rancher_mk3", "indrev:rancher_mk1", "indrev:steam_turbine_pressure_valve", "indrev:warning_strobe", "indrev:sawmill_mk2", "indrev:sawmill_mk3", "indrev:sawmill_mk1", "indrev:sawmill_mk4", "indrev:smelter_mk4", "indrev:resistant_glass", "indrev:deepslate_nikolite_ore", "indrev:laser_emitter_mk4", "indrev:mining_rig_mk4", "indrev:fisher_mk4", "indrev:fisher_mk2", "indrev:fisher_mk3", "indrev:wither_proof_obsidian", "indrev:chopper_creative", "indrev:electric_furnace_factory_mk4", "indrev:pump_mk1" ] } ================================================ FILE: src/main/resources/data/minecraft/tags/blocks/wither_immune.json ================================================ { "replace": false, "values": [ "indrev:wither_proof_obsidian" ] } ================================================ FILE: src/main/resources/data/minecraft/tags/fluids/lava.json ================================================ { "replace": false, "values": [ "indrev:molten_netherite_still", "indrev:molten_netherite_flowing", "indrev:molten_iron_still", "indrev:molten_iron_flowing", "indrev:molten_copper_still", "indrev:molten_copper_flowing", "indrev:molten_gold_still", "indrev:molten_gold_flowing", "indrev:molten_tin_still", "indrev:molten_tin_flowing", "indrev:molten_lead_still", "indrev:molten_lead_flowing", "indrev:molten_silver_still", "indrev:molten_silver_flowing" ] } ================================================ FILE: src/main/resources/data/minecraft/tags/fluids/water.json ================================================ { "replace": false, "values": [ "indrev:coolant_still", "indrev:coolant_flowing", "indrev:sulfuric_acid_still", "indrev:sulfuric_acid_flowing", "indrev:toxic_mud_still", "indrev:toxic_mud_flowing" ] } ================================================ FILE: src/main/resources/fabric.mod.json ================================================ { "schemaVersion": 1, "id": "indrev", "version": "${version}", "name": "Industrial Revolution", "description": "Every other industrial mod ever", "authors": [ "Gabriel Henrique de Oliveira" ], "contributors": [ ], "contact": { "homepage": "https://github.com/GabrielOlvH/Industrial-Revolution", "sources": "https://github.com/GabrielOlvH/Industrial-Revolution" }, "license": "Apache-2.0", "icon": "assets/indrev/icon.png", "environment": "*", "entrypoints": { "main": [ { "adapter": "kotlin", "value": "me.steven.indrev.IndustrialRevolution" } ], "client": [ { "adapter": "kotlin", "value": "me.steven.indrev.IndustrialRevolutionClient" } ], "rei": [ { "adapter": "kotlin", "value": "me.steven.indrev.compat.rei.REIPlugin" } ], "fabric-datagen": [ { "adapter": "kotlin", "value": "me.steven.indrev.datagen.IndustrialRevolutionDatagen" } ] }, "custom": { "dashloader:customobject": [ "me.steven.indrev.compat.dashloader.models.DashCableModel", "me.steven.indrev.compat.dashloader.models.DashFluidPipeModel", "me.steven.indrev.compat.dashloader.models.DashItemPipeModel", "me.steven.indrev.compat.dashloader.models.DashLazuliFluxContainerModel", "me.steven.indrev.compat.dashloader.models.DashMachineModel", "me.steven.indrev.compat.dashloader.models.DashMinerModel", "me.steven.indrev.compat.dashloader.models.DashTankModel" ] }, "mixins": [ "indrev.mixins.json" ], "accessWidener": "indrev.accesswidener", "depends": { "fabricloader": ">=0.7.1", "fabric": ">=0.32.0", "fabric-language-kotlin": ">=1.6.0", "minecraft": ">=1.18.2", "libgui": "*" } } ================================================ FILE: src/main/resources/indrev.accesswidener ================================================ accessWidener v1 named # Used for compat between cooking recipes and IR machines accessible field net/minecraft/recipe/AbstractCookingRecipe input Lnet/minecraft/recipe/Ingredient; # Used to simulate enchantments accessible field net/minecraft/predicate/item/EnchantmentPredicate enchantment Lnet/minecraft/enchantment/Enchantment; accessible field net/minecraft/predicate/item/EnchantmentPredicate levels Lnet/minecraft/predicate/NumberRange$IntRange; # Used to get the fluid when draining it accessible field net/minecraft/block/FluidBlock fluid Lnet/minecraft/fluid/FlowableFluid; # Used by the jetpack accessible field net/minecraft/entity/LivingEntity jumping Z # Used by crafting machines accessible field net/minecraft/recipe/RecipeManager recipes Ljava/util/Map; accessible method net/minecraft/recipe/RecipeManager getAllOfType (Lnet/minecraft/recipe/RecipeType;)Ljava/util/Map; # Used to override vanilla's property syncing accessible field net/minecraft/screen/ScreenHandler properties Ljava/util/List; accessible field net/minecraft/screen/ScreenHandler trackedPropertyValues Lit/unimi/dsi/fastutil/ints/IntList; # Used to calculate random results accessible field net/minecraft/util/collection/WeightedList entries Ljava/util/List; # Used by the rancher accessible method net/minecraft/entity/passive/AnimalEntity eat (Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/item/ItemStack;)V ================================================ FILE: src/main/resources/indrev.mixins.json ================================================ { "required": true, "package": "me.steven.indrev.mixin", "compatibilityLevel": "JAVA_8", "plugin": "me.steven.indrev.mixin.aprilfools.AprilFoolsMixinConfigPlugin", "mixins": [ "common.MixinAbstractCookingRecipe", "common.MixinEnchantmentHelper", "common.MixinEntity", "common.MixinItemPredicate", "common.MixinItemStack", "common.MixinLivingEntity", "common.MixinPiglinBrain", "common.MixinPlayerEntity", "common.MixinPlayerInventory", "common.MixinServerPlayerEntity", "common.MixinServerWorld" ], "client": [ "client.MixinBuiltChunk", "client.MixinClientPlayerInteractionManager", "client.MixinGameRenderer", "client.MixinItemRenderer", "client.MixinLivingEntityClient", "client.MixinMinecraftClient", "client.MixinWItemSlot" ], "injectors": { "defaultRequire": 1 } }