diff --git a/## Changes.md b/## Changes.md deleted file mode 100644 index b6e11f765..000000000 --- a/## Changes.md +++ /dev/null @@ -1,138 +0,0 @@ -## Additions - -- Netherite Backtank -- Netherite Diving Helmet and Boots -- Contraption Controls -- Elevator Pulley -- Copycat Panels and Copycat Steps -- Block of Andesite Alloy -- Block of Industrial Iron -- Large Water Wheel -- Mechanical Roller -- Andesite, Brass and Copper Doors -- Andesite, Brass and Copper Bars -- Andesite, Brass and Copper Scaffolding -- Clipboard -- Mangrove Windows (1.19) - -## Changes - -- Overhauled models and textures of Andesite & Brass components (Kryppers) -- Reworked textures of colored blocks such as seats and sails (dani) -- New filter sprites (vectorwing) -- Valve handles can now be used to precisely turn mechanical bearings by a set angle -- Pulley ropes are now climbable -- Lowered hitbox of seats for improved traversability inside contraptions -- Improved safety for players standing on vertically moving contraptions -- Pulley contraptions will now make an effort to place remote players at y values sensible to the client -- Fixed seated entities on rotating contraptions not rendering at the correct location -- Deployers no longer fail to activate in chunks claimed or protected by the player that placed them -- Fixed couplings, schematics and in-world overlays not rendering correctly at coordinates far from the origin -- Fixed Bearings, Pistons, Pulleys and Gantries powered by a Sequenced Gearshift not moving precisely to its instructions at high speeds -- Minecart contraptions no longer visually jump to a location when stalled -- Mechanical bearings now snap to a rounded angle when stopped -- Contraption storage now accepts more chests and barrels from other mods -- Players can now open chests and barrels on assembled contraptions -- Mechanical Pumps no longer reverse direction based on kinetic input -- Fixed pipe connections pulling fluids with half the speed compared to a directly attached pump -- Substantially increased speed of visual flow propagation inside pipe networks -- Portable storage interfaces now stall for longer after an exchange has happened, and shorter otherwise -- Single train track blocks with slopes connected on either side will angle themselves to create a smoother ascend across both -- Multiple pulleys can now attach to contraptions in a synchronised group -- Display Boards now update text instantaneously at high input rpm -- Diving helmets now always grant aqua affinity -- Diving helmets can no longer be enchanted with aqua affinity -- Water wheel fins are no longer directional -- Water wheels now only have one speed level -- Water wheels can now take the appearance of any reasonably implemented wood type -- Added sided door control options to elevator contact and train station UI -- Liquid can no longer spread perpendicularly on top of water wheels -- Overhauled UX of scroll values and item filtering -- Filtered item extraction can now be configured to pull "up to x items" per operation -- Connected textures now use and apply the getAppearance() standard by Forge, allowing them to connect across other mods' facades, etc. (1.19) -- Boiler status now highlights information about water flow when insufficient -- The majority of in-world options no longer require a wrench -- Chutes can now be encased in Industrial Iron Blocks -- Chutes are now less prone to resetting shape when moved or rotated -- Moved metal block variants to Building Blocks tab -- Changed stonecutting ingredient of metal block variants from sheet to ingot -- Base stone blocks can now be stonecut back from their cut variants -- Fixed track placement allowing an s-bend between two sloped track pieces in specific arrangements -- Fixed funnels losing filters when changing between types -- New randomised textures for natural palette stone types (Kryppers) -- Readjusted palette stone generation to use taller layers -- World generation now places fewer stone type veins by default -- Fixed lava fans voiding items that have smoking & smelting recipes with different outputs -- Filter items now filter for their own item type if left empty -- Valve handles no longer create stress config entries for each dyed variant -- Place near initial angle mode on bearings now has a smaller interval considered 'near' -- Players can now take items from saws via right-click -- Item Drains now accept dropped items as input -- Train track placement overlay now explicitly mentions the ctrl key -- Fixed Mechanical Saws not rendering as animated when using rubidium -- Fixed a ui element of the Station Screen rendering behind the background -- Belts printed instantly or via cannon now retain the correct type of casing -- Scheduled trains no longer slow down for slight ascends/descents on a straight track -- Fixed saplings and other non-collidables sticking to chassis or super glue -- Encased Fluid Pipes no longer z-fight on open pipe faces -- Valve handles now turn twice as quickly -- Bearings no longer have the angle-indicating nook on their block -- Depot hitbox is now a simple cuboid -- Fixed belts encased with andesite briefly showing brass textures -- Fixed Filters and Attribute Filters not stacking with unmodified, equivalent stacks -- Fixed Attribute Filters saving the name tag preview item in their data -- Filters and Schedules can now be reset via crafting -- Renamed Sails to Windmill Sails -- Crushing gold ore now yields more experience nuggets -- Fixed valve pipes sometimes not rotating their indicator fully -- Horizontal, encased belts now render a support structure when solid blocks are above them -- Added placement assist for mechanical drills, saws and deployers -- Mechanical Belts can now be waterlogged -- Depots and Ejectors can now be Waterlogged -- Chutes and Funnels can now be Waterlogged -- Fixed upright mechanical saws only able to be oriented in two directions -- Deployers now have their filter slot on the side of the block -- Deployers can now be rotated by wrenching them near the edge of the front face -- Deployers now set filters on blocks only by targeting any location on a correct side -- Fixed Schematics loaded for deployer printing not rotating block entity contents -- Added tripwire to #movable_empty_collider -- Renamed Stockpile Switch to Threshold Switch -- Renamed Content Observer to Smart Observer -- Smart observer and threshold switch can now be oriented to face blocks above or below them -- Smart observer will now also emit redstone when the block in front of it matches its filter -- Fixed non-vanilla signs not accepted as valid display targets -- Brass tunnels with no distribution behaviour no longer show the mode switcher -- Used more contrasting colours for diode and tunnel value inputs -- Fixed crash when hose pulley cannot find reference fluid for infinite draining -- Clipboards can now be used to transfer settings between blocks -- Clipboards can now be used to manually write to Display Boards and Nixie Tubes -- Clipboards can now be used as Material Checklists in the Schematicannon -- Fixed and edited existing tooltips and ponder scenes to include behavioural changes in 0.5.1 -- New ponder scenes for Smart Observer, Threshold Switch, Elevator Pulley, Contraption Controls and Mechanical Rollers -- Fixed ponder overlay text rendering with wonky pixels -- Added a ponder category for recently added/changed blocks -- Renamed Filter to List Filter -- Deployers can now apply filters to a Redstone link with less required precision -- Bezier track segments now render with a slight angle to reduce z-fighting -- Fixed offset shaft rotation on encased large cogwheels -- Fixed Smart Fluid Pipe not dropping filter when broken -- Placards and Creative Crates will no longer hold on to special nbt content (except potion data, damage, enchants) of the contained item when imported via Schematicannon -- Schematicannons can no longer print mobs -- Fixed item frames not requiring an exact nbt match for printed contents -- Players can now sneak while using exp nuggets to only consume one item at a time -- Minecart contraption items can no longer be placed in container items like toolboxes or shulkers (configurable) -- Implemented ComputerCraft interaction for Speed Controllers, Display Links, Speedometers, Stressometers, Sequenced Gearshifts and Train Stations (caelwarner) -- Hand crank no longer drains hunger when using the extendo grip (Xstoudi) -- Fixed Encased Chain Drives not reacting to block rotation and mirroring correctly -- Open Ended Pipes now correctly handle Builder's Tea (NerdsOfAFeather) -- Added Config entry for brass tunnel distribution cooldown (Walle123) -- API for custom bogey & track types (Rabbitminers, techno-sam) -- Fixed server crash caused by Gantry Contraptions assembling (Lucasmellof) -- Fix "Lighter than air" fluids displayed incorrectly in spouts (cakeGit) -- Added rotate and mirror methods to Fluid Pipes (xieve) -- Chocolate & Honey fluid fog distance is now configurable (radimous) -- Added a TrackGraph merge event (DaComputerNerd717) -- Fixed players dismounting when trains get assembled (Equinoxxe) -- Added GameTests (TropheusJ) -- Added armor tags (NerdsOfAFeather) -- Major updates now release as patch A \ No newline at end of file diff --git a/build.gradle b/build.gradle index defe2a5ce..fa92f2fc7 100644 --- a/build.gradle +++ b/build.gradle @@ -214,7 +214,7 @@ dependencies { jarJar.ranged(it, '[MC1.19-1.1.5,)') } jarJar("com.jozufozu.flywheel:flywheel-forge-${flywheel_minecraft_version}:${flywheel_version}") { - jarJar.ranged(it, '[0.6.9,0.6.10)') + jarJar.ranged(it, '[0.6.10,0.6.11)') } implementation fg.deobf("com.tterrag.registrate:Registrate:${registrate_version}") diff --git a/gradle.properties b/gradle.properties index 1a00be0f6..3ded1fc61 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.jvmargs = -Xmx3G org.gradle.daemon = false # mod version info -mod_version = 0.5.1.c +mod_version = 0.5.1.e artifact_minecraft_version = 1.19.2 minecraft_version = 1.19.2 @@ -23,7 +23,7 @@ use_parchment = true # dependency versions registrate_version = MC1.19-1.1.5 flywheel_minecraft_version = 1.19.2 -flywheel_version = 0.6.9-18 +flywheel_version = 0.6.10-20 jei_minecraft_version = 1.19.2 jei_version = 11.2.0.254 curios_minecraft_version = 1.19.2 diff --git a/src/generated/resources/.cache/e0a39a97205b7149114f15de91b614248d05fd95 b/src/generated/resources/.cache/e0a39a97205b7149114f15de91b614248d05fd95 new file mode 100644 index 000000000..eaca7738a --- /dev/null +++ b/src/generated/resources/.cache/e0a39a97205b7149114f15de91b614248d05fd95 @@ -0,0 +1,2 @@ +// 1.19.2 2023-08-24T18:35:23.8733985 Create's Recipe Serializer Tags +0d8718f7383761bc5d7bc45306ed266ebf25dc1d data/create/tags/recipe_serializer/automation_ignore.json diff --git a/src/generated/resources/data/create/tags/recipe_serializer/automation_ignore.json b/src/generated/resources/data/create/tags/recipe_serializer/automation_ignore.json new file mode 100644 index 000000000..b5876896e --- /dev/null +++ b/src/generated/resources/data/create/tags/recipe_serializer/automation_ignore.json @@ -0,0 +1,12 @@ +{ + "values": [ + { + "id": "occultism:spirit_trade", + "required": false + }, + { + "id": "occultism:ritual", + "required": false + } + ] +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/AllRecipeTypes.java b/src/main/java/com/simibubi/create/AllRecipeTypes.java index 83b70187b..6a87ee81a 100644 --- a/src/main/java/com/simibubi/create/AllRecipeTypes.java +++ b/src/main/java/com/simibubi/create/AllRecipeTypes.java @@ -1,12 +1,10 @@ package com.simibubi.create; import java.util.Optional; -import java.util.Set; import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; -import com.google.common.collect.ImmutableSet; import com.simibubi.create.compat.jei.ConversionRecipe; import com.simibubi.create.content.equipment.sandPaper.SandPaperPolishingRecipe; import com.simibubi.create.content.equipment.toolbox.ToolboxDyeingRecipe; @@ -16,8 +14,8 @@ import com.simibubi.create.content.kinetics.crafter.MechanicalCraftingRecipe; import com.simibubi.create.content.kinetics.crusher.CrushingRecipe; import com.simibubi.create.content.kinetics.deployer.DeployerApplicationRecipe; import com.simibubi.create.content.kinetics.deployer.ManualApplicationRecipe; -import com.simibubi.create.content.kinetics.fan.HauntingRecipe; -import com.simibubi.create.content.kinetics.fan.SplashingRecipe; +import com.simibubi.create.content.kinetics.fan.processing.HauntingRecipe; +import com.simibubi.create.content.kinetics.fan.processing.SplashingRecipe; import com.simibubi.create.content.kinetics.millstone.MillingRecipe; import com.simibubi.create.content.kinetics.mixer.CompactingRecipe; import com.simibubi.create.content.kinetics.mixer.MixingRecipe; @@ -29,7 +27,6 @@ import com.simibubi.create.content.processing.recipe.ProcessingRecipeSerializer; import com.simibubi.create.content.processing.sequenced.SequencedAssemblyRecipeSerializer; import com.simibubi.create.foundation.recipe.IRecipeTypeInfo; -import net.createmod.catnip.platform.CatnipServices; import net.createmod.catnip.utility.lang.Lang; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; @@ -127,12 +124,9 @@ public enum AllRecipeTypes implements IRecipeTypeInfo { .getRecipeFor(getType(), inv, world); } - public static final Set RECIPE_DENY_SET = - ImmutableSet.of(new ResourceLocation("occultism", "spirit_trade"), new ResourceLocation("occultism", "ritual")); - public static boolean shouldIgnoreInAutomation(Recipe recipe) { RecipeSerializer serializer = recipe.getSerializer(); - if (serializer != null && RECIPE_DENY_SET.contains(CatnipServices.REGISTRIES.getKeyOrThrow(serializer))) + if (serializer != null && AllTags.AllRecipeSerializerTags.AUTOMATION_IGNORE.matches(serializer)) return true; return recipe.getId() .getPath() diff --git a/src/main/java/com/simibubi/create/AllTags.java b/src/main/java/com/simibubi/create/AllTags.java index ff998e825..a28a79f72 100644 --- a/src/main/java/com/simibubi/create/AllTags.java +++ b/src/main/java/com/simibubi/create/AllTags.java @@ -19,6 +19,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Fluid; @@ -315,11 +316,54 @@ public class AllTags { private static void init() {} } + + public enum AllRecipeSerializerTags { + + AUTOMATION_IGNORE, + + ; + + public final TagKey> tag; + public final boolean alwaysDatagen; + + AllRecipeSerializerTags() { + this(MOD); + } + + AllRecipeSerializerTags(NameSpace namespace) { + this(namespace, namespace.optionalDefault, namespace.alwaysDatagenDefault); + } + + AllRecipeSerializerTags(NameSpace namespace, String path) { + this(namespace, path, namespace.optionalDefault, namespace.alwaysDatagenDefault); + } + + AllRecipeSerializerTags(NameSpace namespace, boolean optional, boolean alwaysDatagen) { + this(namespace, null, optional, alwaysDatagen); + } + + AllRecipeSerializerTags(NameSpace namespace, String path, boolean optional, boolean alwaysDatagen) { + ResourceLocation id = new ResourceLocation(namespace.id, path == null ? Lang.asId(name()) : path); + if (optional) { + tag = optionalTag(ForgeRegistries.RECIPE_SERIALIZERS, id); + } else { + tag = TagKey.create(Registry.RECIPE_SERIALIZER_REGISTRY, id); + } + this.alwaysDatagen = alwaysDatagen; + } + + public boolean matches(RecipeSerializer recipeSerializer) { + return ForgeRegistries.RECIPE_SERIALIZERS.getHolder(recipeSerializer).orElseThrow().is(tag); + } + + private static void init() {} + } public static void init() { AllBlockTags.init(); AllItemTags.init(); AllFluidTags.init(); AllEntityTags.init(); + AllRecipeSerializerTags.init(); } } diff --git a/src/main/java/com/simibubi/create/Create.java b/src/main/java/com/simibubi/create/Create.java index 38ff3c646..7317e42af 100644 --- a/src/main/java/com/simibubi/create/Create.java +++ b/src/main/java/com/simibubi/create/Create.java @@ -16,10 +16,10 @@ import com.simibubi.create.content.decoration.palettes.AllPaletteBlocks; import com.simibubi.create.content.equipment.potatoCannon.BuiltinPotatoProjectileTypes; import com.simibubi.create.content.fluids.tank.BoilerHeaters; import com.simibubi.create.content.kinetics.TorquePropagator; +import com.simibubi.create.content.kinetics.fan.processing.AllFanProcessingTypes; import com.simibubi.create.content.kinetics.mechanicalArm.AllArmInteractionPointTypes; import com.simibubi.create.content.redstone.displayLink.AllDisplayBehaviours; import com.simibubi.create.content.redstone.link.RedstoneLinkNetworkHandler; -import com.simibubi.create.content.schematics.SchematicInstances; import com.simibubi.create.content.schematics.ServerSchematicLoader; import com.simibubi.create.content.trains.GlobalRailwayManager; import com.simibubi.create.content.trains.bogey.BogeySizes; @@ -29,6 +29,7 @@ import com.simibubi.create.foundation.block.CopperRegistries; import com.simibubi.create.foundation.data.AllLangPartials; import com.simibubi.create.foundation.data.CreateRegistrate; import com.simibubi.create.foundation.data.LangMerger; +import com.simibubi.create.foundation.data.RecipeSerializerTagGen; import com.simibubi.create.foundation.data.TagGen; import com.simibubi.create.foundation.data.recipe.MechanicalCraftingRecipeGen; import com.simibubi.create.foundation.data.recipe.ProcessingRecipeGen; @@ -71,7 +72,7 @@ public class Create { public static final String ID = "create"; public static final String NAME = "Create"; - public static final String VERSION = "0.5.1c"; + public static final String VERSION = "0.5.1e"; public static final Logger LOGGER = LogUtils.getLogger(); @@ -126,21 +127,26 @@ public class Create { AllParticleTypes.register(modEventBus); AllStructureProcessorTypes.register(modEventBus); AllEntityDataSerializers.register(modEventBus); + AllPackets.registerPackets(); AllOreFeatureConfigEntries.init(); AllFeatures.register(modEventBus); AllPlacementModifiers.register(modEventBus); BuiltinRegistration.register(modEventBus); - BogeySizes.init(); - AllBogeyStyles.register(); AllConfigs.register(modLoadingContext); + // FIXME: some of these registrations are not thread-safe AllMovementBehaviours.registerDefaults(); AllInteractionBehaviours.registerDefaults(); AllDisplayBehaviours.registerDefaults(); ContraptionMovementSetting.registerDefaults(); AllArmInteractionPointTypes.register(); + AllFanProcessingTypes.register(); BlockSpoutingBehaviour.registerDefaults(); + BogeySizes.init(); + AllBogeyStyles.register(); + // ---- + ComputerCraftProxy.register(); ForgeMod.enableMilkFluid(); @@ -152,21 +158,25 @@ public class Create { DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> CreateClient.onCtorClient(modEventBus, forgeEventBus)); + // FIXME: this is not thread-safe Mods.CURIOS.executeIfInstalled(() -> () -> Curios.init(modEventBus, forgeEventBus)); } public static void init(final FMLCommonSetupEvent event) { - AllPackets.registerPackets(); - SchematicInstances.register(); - BuiltinPotatoProjectileTypes.register(); + AllFluids.registerFluidInteractions(); CreateNBTProcessors.register(); event.enqueueWork(() -> { + // TODO: custom registration should all happen in one place + // Most registration happens in the constructor. + // These registrations use Create's registered objects directly so they must run after registration has finished. + BuiltinPotatoProjectileTypes.register(); + BoilerHeaters.registerDefaults(); + // -- + AttachedRegistry.unwrapAll(); AllAdvancements.register(); AllTriggers.register(); - BoilerHeaters.registerDefaults(); - AllFluids.registerFluidInteractions(); }); } @@ -178,11 +188,13 @@ public class Create { gen.addProvider(event.includeClient(), new LangMerger(gen, ID, NAME, AllLangPartials.values())); gen.addProvider(event.includeClient(), AllSoundEvents.provider(gen)); + gen.addProvider(event.includeServer(), new RecipeSerializerTagGen(gen, event.getExistingFileHelper())); gen.addProvider(event.includeServer(), new AllAdvancements(gen)); gen.addProvider(event.includeServer(), new StandardRecipeGen(gen)); gen.addProvider(event.includeServer(), new MechanicalCraftingRecipeGen(gen)); gen.addProvider(event.includeServer(), new SequencedAssemblyRecipeGen(gen)); + if (event.includeServer()) { ProcessingRecipeGen.registerAll(gen); //AllOreFeatureConfigEntries.gatherData(event); diff --git a/src/main/java/com/simibubi/create/compat/Mods.java b/src/main/java/com/simibubi/create/compat/Mods.java index 9d71f076b..bde93078b 100644 --- a/src/main/java/com/simibubi/create/compat/Mods.java +++ b/src/main/java/com/simibubi/create/compat/Mods.java @@ -13,26 +13,42 @@ import net.minecraftforge.registries.ForgeRegistries; * For compatibility with and without another mod present, we have to define load conditions of the specific code */ public enum Mods { - DYNAMICTREES, - TCONSTRUCT, - CURIOS, - COMPUTERCRAFT, + CONNECTIVITY, + CURIOS, + DYNAMICTREES, + OCCULTISM, + PACKETFIXER, STORAGEDRAWERS, + TCONSTRUCT, XLPACKETS; - /** - * @return a boolean of whether the mod is loaded or not based on mod id - */ - public boolean isLoaded() { - return ModList.get().isLoaded(asId()); + private final String id; + + Mods() { + id = Lang.asId(name()); } /** * @return the mod id */ - public String asId() { - return Lang.asId(name()); + public String id() { + return id; + } + + public ResourceLocation rl(String path) { + return new ResourceLocation(id, path); + } + + public Block getBlock(String id) { + return ForgeRegistries.BLOCKS.getValue(rl(id)); + } + + /** + * @return a boolean of whether the mod is loaded or not based on mod id + */ + public boolean isLoaded() { + return ModList.get().isLoaded(id); } /** @@ -55,8 +71,4 @@ public enum Mods { toExecute.get().run(); } } - - public Block getBlock(String id) { - return ForgeRegistries.BLOCKS.getValue(new ResourceLocation(asId(), id)); - } } diff --git a/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java b/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java index 71ccc80b7..6095f8937 100644 --- a/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java +++ b/src/main/java/com/simibubi/create/compat/jei/CreateJEI.java @@ -51,8 +51,8 @@ import com.simibubi.create.content.kinetics.crusher.AbstractCrushingRecipe; import com.simibubi.create.content.kinetics.deployer.DeployerApplicationRecipe; import com.simibubi.create.content.kinetics.deployer.ItemApplicationRecipe; import com.simibubi.create.content.kinetics.deployer.ManualApplicationRecipe; -import com.simibubi.create.content.kinetics.fan.HauntingRecipe; -import com.simibubi.create.content.kinetics.fan.SplashingRecipe; +import com.simibubi.create.content.kinetics.fan.processing.HauntingRecipe; +import com.simibubi.create.content.kinetics.fan.processing.SplashingRecipe; import com.simibubi.create.content.kinetics.press.MechanicalPressBlockEntity; import com.simibubi.create.content.kinetics.press.PressingRecipe; import com.simibubi.create.content.kinetics.saw.CuttingRecipe; diff --git a/src/main/java/com/simibubi/create/compat/jei/category/FanHauntingCategory.java b/src/main/java/com/simibubi/create/compat/jei/category/FanHauntingCategory.java index 683a59d7c..b3ebf5d98 100644 --- a/src/main/java/com/simibubi/create/compat/jei/category/FanHauntingCategory.java +++ b/src/main/java/com/simibubi/create/compat/jei/category/FanHauntingCategory.java @@ -4,7 +4,7 @@ import org.jetbrains.annotations.NotNull; import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.compat.jei.category.animations.AnimatedKinetics; -import com.simibubi.create.content.kinetics.fan.HauntingRecipe; +import com.simibubi.create.content.kinetics.fan.processing.HauntingRecipe; import com.simibubi.create.foundation.gui.AllGuiTextures; import net.createmod.catnip.gui.element.GuiGameElement; diff --git a/src/main/java/com/simibubi/create/compat/jei/category/FanWashingCategory.java b/src/main/java/com/simibubi/create/compat/jei/category/FanWashingCategory.java index 625559229..72eaaa0b2 100644 --- a/src/main/java/com/simibubi/create/compat/jei/category/FanWashingCategory.java +++ b/src/main/java/com/simibubi/create/compat/jei/category/FanWashingCategory.java @@ -4,7 +4,7 @@ import org.jetbrains.annotations.NotNull; import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.compat.jei.category.animations.AnimatedKinetics; -import com.simibubi.create.content.kinetics.fan.SplashingRecipe; +import com.simibubi.create.content.kinetics.fan.processing.SplashingRecipe; import net.createmod.catnip.gui.element.GuiGameElement; import net.minecraft.world.level.material.Fluids; diff --git a/src/main/java/com/simibubi/create/compat/storageDrawers/StorageDrawers.java b/src/main/java/com/simibubi/create/compat/storageDrawers/StorageDrawers.java index 5f4731aee..b35aaa247 100644 --- a/src/main/java/com/simibubi/create/compat/storageDrawers/StorageDrawers.java +++ b/src/main/java/com/simibubi/create/compat/storageDrawers/StorageDrawers.java @@ -3,39 +3,35 @@ package com.simibubi.create.compat.storageDrawers; import com.simibubi.create.compat.Mods; import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour; +import net.createmod.catnip.platform.CatnipServices; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraftforge.items.IItemHandler; public class StorageDrawers { public static boolean isDrawer(BlockEntity be) { - return be != null && Mods.STORAGEDRAWERS.asId() - .equals(BlockEntityType.getKey(be.getType()) - .getNamespace()); + return be != null && Mods.STORAGEDRAWERS.id().equals( + CatnipServices.REGISTRIES.getKeyOrThrow(be.getType()).getNamespace()); } public static float getTrueFillLevel(IItemHandler inv, FilteringBehaviour filtering) { float occupied = 0; float totalSpace = 0; - + for (int slot = 1; slot < inv.getSlots(); slot++) { ItemStack stackInSlot = inv.getStackInSlot(slot); int space = inv.getSlotLimit(slot); int count = stackInSlot.getCount(); - if (space == 0) - continue; + if (space == 0) continue; totalSpace += 1; - if (filtering.test(stackInSlot)) - occupied += count * (1f / space); + if (filtering.test(stackInSlot)) occupied += count * (1f / space); } - - if (totalSpace == 0) - return 0; + + if (totalSpace == 0) return 0; return occupied / totalSpace; } - + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/BlockMovementChecks.java b/src/main/java/com/simibubi/create/content/contraptions/BlockMovementChecks.java index 0a223944d..63d2a5cf9 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/BlockMovementChecks.java +++ b/src/main/java/com/simibubi/create/content/contraptions/BlockMovementChecks.java @@ -337,7 +337,7 @@ public class BlockMovementChecks { return direction == state.getValue(StickerBlock.FACING) && !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite()); } - if (block instanceof AbstractBogeyBlock bogey) + if (block instanceof AbstractBogeyBlock bogey) return bogey.getStickySurfaces(world, pos, state) .contains(direction); if (block instanceof WhistleBlock) diff --git a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java index f8bd07a31..236ed5d9f 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java @@ -1376,7 +1376,7 @@ public abstract class Contraption { return blocks.values(); } - public Collection getSpecialRenderedTEs() { + public Collection getSpecialRenderedBEs() { return specialRenderedBlockEntities; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/ContraptionData.java b/src/main/java/com/simibubi/create/content/contraptions/ContraptionData.java index c204d4edf..158d306a7 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/ContraptionData.java +++ b/src/main/java/com/simibubi/create/content/contraptions/ContraptionData.java @@ -14,17 +14,46 @@ public class ContraptionData { /** * A sane, default maximum for contraption data size. */ - public static final int DEFAULT_MAX = 2_000_000; + public static final int DEFAULT_LIMIT = 2_000_000; + /** + * Connectivity expands the NBT packet limit to 2 GB. + */ + public static final int CONNECTIVITY_LIMIT = Integer.MAX_VALUE; + /** + * Packet Fixer expands the NBT packet limit to 200 MB. + */ + public static final int PACKET_FIXER_LIMIT = 209_715_200; /** * XL Packets expands the NBT packet limit to 2 GB. */ - public static final int EXPANDED_MAX = 2_000_000_000; + public static final int XL_PACKETS_LIMIT = 2_000_000_000; /** * Minecart item sizes are limited by the vanilla slot change packet ({@link ClientboundContainerSetSlotPacket}). - * {@link ContraptionData#DEFAULT_MAX} is used as the default. - * XL Packets expands the size limit to ~2 GB. If the mod is loaded, we take advantage of it and use the higher limit. + * {@link #DEFAULT_LIMIT} is used as the default. + * Connectivity, PacketFixer, and XL Packets expand the size limit. + * If one of these mods is loaded, we take advantage of it and use the higher limit. */ - public static final int PICKUP_MAX = Mods.XLPACKETS.isLoaded() ? EXPANDED_MAX : DEFAULT_MAX; + public static final int PICKUP_LIMIT; + + static { + int limit = DEFAULT_LIMIT; + + // Check from largest to smallest to use the smallest limit if multiple mods are loaded. + // It is necessary to use the smallest limit because even if multiple mods are loaded, + // not all of their mixins may be applied. Therefore, it is safest to only assume that + // the mod with the smallest limit is actually active. + if (Mods.CONNECTIVITY.isLoaded()) { + limit = CONNECTIVITY_LIMIT; + } + if (Mods.XLPACKETS.isLoaded()) { + limit = XL_PACKETS_LIMIT; + } + if (Mods.PACKETFIXER.isLoaded()) { + limit = PACKET_FIXER_LIMIT; + } + + PICKUP_LIMIT = limit; + } /** * @return true if the given NBT is too large for a contraption to be synced to clients. @@ -38,7 +67,7 @@ public class ContraptionData { * @return true if the given NBT is too large for a contraption to be picked up with a wrench. */ public static boolean isTooLargeForPickup(CompoundTag data) { - return packetSize(data) > PICKUP_MAX; + return packetSize(data) > PICKUP_LIMIT; } /** diff --git a/src/main/java/com/simibubi/create/content/contraptions/render/ContraptionRenderDispatcher.java b/src/main/java/com/simibubi/create/content/contraptions/render/ContraptionRenderDispatcher.java index 13a464a96..5dd889849 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/render/ContraptionRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/content/contraptions/render/ContraptionRenderDispatcher.java @@ -119,9 +119,9 @@ public class ContraptionRenderDispatcher { ContraptionWorld contraptionWorld = c.getContraptionWorld(); BlockPos origin = c.anchor; - int height = contraptionWorld.getHeight(); int minBuildHeight = contraptionWorld.getMinBuildHeight(); - VirtualRenderWorld renderWorld = new VirtualRenderWorld(world, origin, height, minBuildHeight) { + int height = contraptionWorld.getHeight(); + VirtualRenderWorld renderWorld = new VirtualRenderWorld(world, minBuildHeight, height, origin) { @Override public boolean supportsFlywheel() { return canInstance(); @@ -134,13 +134,13 @@ public class ContraptionRenderDispatcher { // Skip individual lighting updates to prevent lag with large contraptions renderWorld.setBlock(info.pos, info.state, Block.UPDATE_SUPPRESS_LIGHT); - renderWorld.runLightingEngine(); + renderWorld.runLightEngine(); return renderWorld; } public static void renderBlockEntities(Level world, VirtualRenderWorld renderWorld, Contraption c, ContraptionMatrices matrices, MultiBufferSource buffer) { - BlockEntityRenderHelper.renderBlockEntities(world, renderWorld, c.getSpecialRenderedTEs(), + BlockEntityRenderHelper.renderBlockEntities(world, renderWorld, c.getSpecialRenderedBEs(), matrices.getModelViewProjection(), matrices.getLight(), buffer); } diff --git a/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatBarsModel.java b/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatBarsModel.java index 7a62d1f6a..263bce99e 100644 --- a/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatBarsModel.java +++ b/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatBarsModel.java @@ -31,15 +31,14 @@ public class CopycatBarsModel extends CopycatModel { protected List getCroppedQuads(BlockState state, Direction side, RandomSource rand, BlockState material, ModelData wrappedData, RenderType renderType) { BakedModel model = getModelOf(material); - List templateQuads = model.getQuads(material, null, rand, wrappedData, renderType); List superQuads = originalModel.getQuads(state, side, rand, wrappedData, renderType); - List quads = new ArrayList<>(); TextureAtlasSprite targetSprite = model.getParticleIcon(wrappedData); boolean vertical = state.getValue(CopycatPanelBlock.FACING) .getAxis() == Axis.Y; - if (side != null && (vertical || side.getAxis() == Axis.Y)) + if (side != null && (vertical || side.getAxis() == Axis.Y)) { + List templateQuads = model.getQuads(material, null, rand, wrappedData, renderType); for (int i = 0; i < templateQuads.size(); i++) { BakedQuad quad = templateQuads.get(i); if (quad.getDirection() != Direction.UP) @@ -47,10 +46,13 @@ public class CopycatBarsModel extends CopycatModel { targetSprite = quad.getSprite(); break; } + } if (targetSprite == null) return superQuads; + List quads = new ArrayList<>(); + for (int i = 0; i < superQuads.size(); i++) { BakedQuad quad = superQuads.get(i); TextureAtlasSprite original = quad.getSprite(); diff --git a/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatModel.java b/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatModel.java index bc4ab7fee..fc022af54 100644 --- a/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatModel.java +++ b/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatModel.java @@ -1,5 +1,6 @@ package com.simibubi.create.content.decoration.copycat; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -110,14 +111,24 @@ public abstract class CopycatModel extends BakedModelWrapperWithData { // Rubidium: render side!=null versions of the base material during side==null, // to avoid getting culled away - if (side == null && state.getBlock() instanceof CopycatBlock ccb) + if (side == null && state.getBlock() instanceof CopycatBlock ccb) { + boolean immutable = true; for (Direction nonOcclusionSide : Iterate.directions) - if (ccb.shouldFaceAlwaysRender(state, nonOcclusionSide)) + if (ccb.shouldFaceAlwaysRender(state, nonOcclusionSide)) { + if (immutable) { + croppedQuads = new ArrayList<>(croppedQuads); + immutable = false; + } croppedQuads.addAll(getCroppedQuads(state, nonOcclusionSide, rand, material, wrappedData, renderType)); + } + } return croppedQuads; } + /** + * The returned list must not be mutated. + */ protected abstract List getCroppedQuads(BlockState state, Direction side, RandomSource rand, BlockState material, ModelData wrappedData, RenderType renderType); diff --git a/src/main/java/com/simibubi/create/content/kinetics/belt/transport/TransportedItemStack.java b/src/main/java/com/simibubi/create/content/kinetics/belt/transport/TransportedItemStack.java index aec7c61f8..aa5500260 100644 --- a/src/main/java/com/simibubi/create/content/kinetics/belt/transport/TransportedItemStack.java +++ b/src/main/java/com/simibubi/create/content/kinetics/belt/transport/TransportedItemStack.java @@ -3,7 +3,7 @@ package com.simibubi.create.content.kinetics.belt.transport; import java.util.Random; import com.simibubi.create.content.kinetics.belt.BeltHelper; -import com.simibubi.create.content.kinetics.fan.FanProcessing; +import com.simibubi.create.content.kinetics.fan.processing.FanProcessingType; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -25,7 +25,7 @@ public class TransportedItemStack implements Comparable { public float prevBeltPosition; public float prevSideOffset; - public FanProcessing.Type processedBy; + public FanProcessingType processedBy; public int processingTime; public TransportedItemStack(ItemStack stack) { diff --git a/src/main/java/com/simibubi/create/content/kinetics/crusher/AbstractCrushingRecipe.java b/src/main/java/com/simibubi/create/content/kinetics/crusher/AbstractCrushingRecipe.java index effcf39bc..b4647c196 100644 --- a/src/main/java/com/simibubi/create/content/kinetics/crusher/AbstractCrushingRecipe.java +++ b/src/main/java/com/simibubi/create/content/kinetics/crusher/AbstractCrushingRecipe.java @@ -16,4 +16,9 @@ public abstract class AbstractCrushingRecipe extends ProcessingRecipe> affectedItemHandlers = + protected List> affectedItemHandlers = new ArrayList<>(); protected List caughtEntities = new ArrayList<>(); @@ -110,14 +111,14 @@ public class AirCurrent { ((ServerPlayer) entity).connection.aboveGroundTickCount = 0; entityDistance -= .5f; - FanProcessing.Type processingType = getSegmentAt((float) entityDistance); + FanProcessingType processingType = getSegmentAt((float) entityDistance); - if (processingType == null || processingType == Type.NONE) + if (processingType == AllFanProcessingTypes.NONE) continue; if (entity instanceof ItemEntity itemEntity) { - if (world.isClientSide) { - processingType.spawnParticlesForProcessing(world, entity.position()); + if (world != null && world.isClientSide) { + processingType.spawnProcessingParticles(world, entity.position()); continue; } if (FanProcessing.canProcess(itemEntity, processingType)) @@ -127,7 +128,8 @@ public class AirCurrent { continue; } - processingType.affectEntity(entity, world); + if (world != null) + processingType.affectEntity(entity, world); } } @@ -155,7 +157,7 @@ public class AirCurrent { AirCurrentSegment currentSegment = new AirCurrentSegment(); segments.clear(); currentSegment.startOffset = 0; - FanProcessing.Type type = Type.NONE; + FanProcessingType type = AllFanProcessingTypes.NONE; int limit = (int) (maxDistance + .5f); int searchStart = pushing ? 0 : limit; @@ -164,8 +166,8 @@ public class AirCurrent { for (int i = searchStart; i * searchStep <= searchEnd * searchStep; i += searchStep) { BlockPos currentPos = start.relative(direction, i); - FanProcessing.Type newType = FanProcessing.Type.byBlock(world, currentPos); - if (newType != Type.NONE) + FanProcessingType newType = FanProcessingType.getAt(world, currentPos); + if (newType != AllFanProcessingTypes.NONE) type = newType; if (currentSegment.type != type || currentSegment.startOffset == 0) { currentSegment.endOffset = i; @@ -258,21 +260,18 @@ public class AirCurrent { BlockPos start = source.getAirCurrentPos(); affectedItemHandlers.clear(); for (int i = 0; i < maxDistance + 1; i++) { - Type type = getSegmentAt(i); - if (type == null) - continue; - + FanProcessingType segmentType = getSegmentAt(i); for (int offset : Iterate.zeroAndOne) { BlockPos pos = start.relative(direction, i) .below(offset); TransportedItemStackHandlerBehaviour behaviour = BlockEntityBehaviour.get(world, pos, TransportedItemStackHandlerBehaviour.TYPE); - FanProcessing.Type typeAtHandler = type; - if (world.getFluidState(pos) - .is(Fluids.WATER)) - typeAtHandler = Type.SPLASHING; - if (behaviour != null) - affectedItemHandlers.add(Pair.of(behaviour, typeAtHandler)); + if (behaviour == null) + continue; + FanProcessingType type = FanProcessingType.getAt(world, pos); + if (type == AllFanProcessingTypes.NONE) + type = segmentType; + affectedItemHandlers.add(Pair.of(behaviour, type)); if (direction.getAxis() .isVertical()) break; @@ -281,15 +280,14 @@ public class AirCurrent { } public void tickAffectedHandlers() { - for (Pair pair : affectedItemHandlers) { + for (Pair pair : affectedItemHandlers) { TransportedItemStackHandlerBehaviour handler = pair.getKey(); Level world = handler.getWorld(); - FanProcessing.Type processingType = pair.getRight(); + FanProcessingType processingType = pair.getRight(); - handler.handleProcessingOnAllItems((transported) -> { + handler.handleProcessingOnAllItems(transported -> { if (world.isClientSide) { - if (world != null) - processingType.spawnParticlesForProcessing(world, handler.getWorldPositionOf(transported)); + processingType.spawnProcessingParticles(world, handler.getWorldPositionOf(transported)); return TransportedResult.doNothing(); } TransportedResult applyProcessing = FanProcessing.applyProcessing(transported, world, processingType); @@ -304,7 +302,7 @@ public class AirCurrent { return AllTags.AllBlockTags.FAN_TRANSPARENT.matches(state); } - public FanProcessing.Type getSegmentAt(float offset) { + public FanProcessingType getSegmentAt(float offset) { for (AirCurrentSegment airCurrentSegment : segments) { if (offset > airCurrentSegment.endOffset && pushing) continue; @@ -312,11 +310,11 @@ public class AirCurrent { continue; return airCurrentSegment.type; } - return FanProcessing.Type.NONE; + return AllFanProcessingTypes.NONE; } public static class AirCurrentSegment { - FanProcessing.Type type; + FanProcessingType type; int startOffset; int endOffset; } diff --git a/src/main/java/com/simibubi/create/content/kinetics/fan/AirFlowParticle.java b/src/main/java/com/simibubi/create/content/kinetics/fan/AirFlowParticle.java index 11fe3f108..8e886964f 100644 --- a/src/main/java/com/simibubi/create/content/kinetics/fan/AirFlowParticle.java +++ b/src/main/java/com/simibubi/create/content/kinetics/fan/AirFlowParticle.java @@ -2,8 +2,10 @@ package com.simibubi.create.content.kinetics.fan; import javax.annotation.Nonnull; +import com.simibubi.create.content.kinetics.fan.processing.AllFanProcessingTypes; +import com.simibubi.create.content.kinetics.fan.processing.FanProcessingType; + import net.createmod.catnip.utility.VecHelper; -import net.createmod.catnip.utility.theme.Color; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleProvider; @@ -12,16 +14,15 @@ import net.minecraft.client.particle.SimpleAnimatedParticle; import net.minecraft.client.particle.SpriteSet; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.core.BlockPos; -import net.minecraft.core.particles.BlockParticleOption; -import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.core.particles.ParticleOptions; import net.minecraft.util.Mth; -import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.Vec3; public class AirFlowParticle extends SimpleAnimatedParticle { private final IAirCurrentSource source; + private final Access access = new Access(); protected AirFlowParticle(ClientLevel world, IAirCurrentSource source, double x, double y, double z, SpriteSet sprite) { @@ -31,11 +32,12 @@ public class AirFlowParticle extends SimpleAnimatedParticle { this.lifetime = 40; hasPhysics = false; selectSprite(7); - Vec3 offset = VecHelper.offsetRandomly(Vec3.ZERO, world.random, .25f); + Vec3 offset = VecHelper.offsetRandomly(Vec3.ZERO, random, .25f); this.setPos(x + offset.x, y + offset.y, z + offset.z); this.xo = x; this.yo = y; this.zo = z; + setColor(0xEEEEEE); setAlpha(.25f); } @@ -47,36 +49,44 @@ public class AirFlowParticle extends SimpleAnimatedParticle { @Override public void tick() { if (source == null || source.isSourceRemoved()) { - dissipate(); + remove(); return; } this.xo = this.x; this.yo = this.y; this.zo = this.z; if (this.age++ >= this.lifetime) { - this.remove(); + remove(); } else { - if (source.getAirCurrent() == null || !source.getAirCurrent().bounds.inflate(.25f).contains(x, y, z)) { - dissipate(); + AirCurrent airCurrent = source.getAirCurrent(); + if (airCurrent == null || !airCurrent.bounds.inflate(.25f).contains(x, y, z)) { + remove(); return; } - Vec3 directionVec = Vec3.atLowerCornerOf(source.getAirCurrent().direction.getNormal()); + Vec3 directionVec = Vec3.atLowerCornerOf(airCurrent.direction.getNormal()); Vec3 motion = directionVec.scale(1 / 8f); if (!source.getAirCurrent().pushing) motion = motion.scale(-1); double distance = new Vec3(x, y, z).subtract(VecHelper.getCenterOf(source.getAirCurrentPos())) .multiply(directionVec).length() - .5f; - if (distance > source.getAirCurrent().maxDistance + 1 || distance < -.25f) { - dissipate(); + if (distance > airCurrent.maxDistance + 1 || distance < -.25f) { + remove(); return; } - motion = motion.scale(source.getAirCurrent().maxDistance - (distance - 1f)).scale(.5f); - selectSprite((int) Mth.clamp((distance / source.getAirCurrent().maxDistance) * 8 + level.random.nextInt(4), - 0, 7)); + motion = motion.scale(airCurrent.maxDistance - (distance - 1f)).scale(.5f); - morphType(distance); + FanProcessingType type = getType(distance); + if (type == AllFanProcessingTypes.NONE) { + setColor(0xEEEEEE); + setAlpha(.25f); + selectSprite((int) Mth.clamp((distance / airCurrent.maxDistance) * 8 + random.nextInt(4), + 0, 7)); + } else { + type.morphAirFlow(access, random); + selectSprite(random.nextInt(3)); + } xd = motion.x; yd = motion.y; @@ -92,68 +102,10 @@ public class AirFlowParticle extends SimpleAnimatedParticle { } - public void morphType(double distance) { + private FanProcessingType getType(double distance) { if (source.getAirCurrent() == null) - return; - FanProcessing.Type type = source.getAirCurrent().getSegmentAt((float) distance); - - if (type == FanProcessing.Type.SPLASHING) { - setColor(Color.mixColors(0x4499FF, 0x2277FF, level.random.nextFloat())); - setAlpha(1f); - selectSprite(level.random.nextInt(3)); - if (level.random.nextFloat() < 1 / 32f) - level.addParticle(ParticleTypes.BUBBLE, x, y, z, xd * .125f, yd * .125f, - zd * .125f); - if (level.random.nextFloat() < 1 / 32f) - level.addParticle(ParticleTypes.BUBBLE_POP, x, y, z, xd * .125f, yd * .125f, - zd * .125f); - } - - if (type == FanProcessing.Type.SMOKING) { - setColor(Color.mixColors(0x0, 0x555555, level.random.nextFloat())); - setAlpha(1f); - selectSprite(level.random.nextInt(3)); - if (level.random.nextFloat() < 1 / 32f) - level.addParticle(ParticleTypes.SMOKE, x, y, z, xd * .125f, yd * .125f, - zd * .125f); - if (level.random.nextFloat() < 1 / 32f) - level.addParticle(ParticleTypes.LARGE_SMOKE, x, y, z, xd * .125f, yd * .125f, - zd * .125f); - } - - if (type == FanProcessing.Type.HAUNTING) { - setColor(Color.mixColors(0x0, 0x126568, level.random.nextFloat())); - setAlpha(1f); - selectSprite(level.random.nextInt(3)); - if (level.random.nextFloat() < 1 / 128f) - level.addParticle(ParticleTypes.SOUL_FIRE_FLAME, x, y, z, xd * .125f, yd * .125f, - zd * .125f); - if (level.random.nextFloat() < 1 / 32f) - level.addParticle(ParticleTypes.SMOKE, x, y, z, xd * .125f, yd * .125f, - zd * .125f); - } - - if (type == FanProcessing.Type.BLASTING) { - setColor(Color.mixColors(0xFF4400, 0xFF8855, level.random.nextFloat())); - setAlpha(.5f); - selectSprite(level.random.nextInt(3)); - if (level.random.nextFloat() < 1 / 32f) - level.addParticle(ParticleTypes.FLAME, x, y, z, xd * .25f, yd * .25f, - zd * .25f); - if (level.random.nextFloat() < 1 / 16f) - level.addParticle(new BlockParticleOption(ParticleTypes.BLOCK, Blocks.LAVA.defaultBlockState()), x, y, - z, xd * .25f, yd * .25f, zd * .25f); - } - - if (type == null) { - setColor(0xEEEEEE); - setAlpha(.25f); - setSize(.2f, .2f); - } - } - - private void dissipate() { - remove(); + return AllFanProcessingTypes.NONE; + return source.getAirCurrent().getSegmentAt((float) distance); } public int getLightColor(float partialTick) { @@ -181,4 +133,21 @@ public class AirFlowParticle extends SimpleAnimatedParticle { } } + private class Access implements FanProcessingType.AirFlowParticleAccess { + @Override + public void setColor(int color) { + AirFlowParticle.this.setColor(color); + } + + @Override + public void setAlpha(float alpha) { + AirFlowParticle.this.setAlpha(alpha); + } + + @Override + public void spawnExtraParticle(ParticleOptions options, float speedMultiplier) { + level.addParticle(options, x, y, z, xd * speedMultiplier, yd * speedMultiplier, zd * speedMultiplier); + } + } + } diff --git a/src/main/java/com/simibubi/create/content/kinetics/fan/FanProcessing.java b/src/main/java/com/simibubi/create/content/kinetics/fan/FanProcessing.java deleted file mode 100644 index cac780076..000000000 --- a/src/main/java/com/simibubi/create/content/kinetics/fan/FanProcessing.java +++ /dev/null @@ -1,452 +0,0 @@ -package com.simibubi.create.content.kinetics.fan; - -import static com.simibubi.create.content.processing.burner.BlazeBurnerBlock.getHeatLevelOf; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import com.mojang.math.Vector3f; -import com.simibubi.create.AllBlocks; -import com.simibubi.create.AllRecipeTypes; -import com.simibubi.create.content.kinetics.belt.behaviour.TransportedItemStackHandlerBehaviour.TransportedResult; -import com.simibubi.create.content.kinetics.belt.transport.TransportedItemStack; -import com.simibubi.create.content.processing.burner.BlazeBurnerBlock; -import com.simibubi.create.content.processing.burner.LitBlazeBurnerBlock; -import com.simibubi.create.foundation.recipe.RecipeApplier; -import com.simibubi.create.infrastructure.config.AllConfigs; - -import net.createmod.catnip.utility.VecHelper; -import net.createmod.catnip.utility.theme.Color; -import net.minecraft.core.BlockPos; -import net.minecraft.core.particles.DustParticleOptions; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.effect.MobEffects; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.animal.horse.Horse; -import net.minecraft.world.entity.animal.horse.SkeletonHorse; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.monster.EnderMan; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.AbstractCookingRecipe; -import net.minecraft.world.item.crafting.BlastingRecipe; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.item.crafting.SmeltingRecipe; -import net.minecraft.world.item.crafting.SmokingRecipe; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.CampfireBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.material.Fluids; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.items.ItemStackHandler; -import net.minecraftforge.items.wrapper.RecipeWrapper; - -public class FanProcessing { - - private static final DamageSource FIRE_DAMAGE_SOURCE = new DamageSource("create.fan_fire").setScalesWithDifficulty() - .setIsFire(); - private static final DamageSource LAVA_DAMAGE_SOURCE = new DamageSource("create.fan_lava").setScalesWithDifficulty() - .setIsFire(); - - private static final RecipeWrapper RECIPE_WRAPPER = new RecipeWrapper(new ItemStackHandler(1)); - private static final SplashingWrapper SPLASHING_WRAPPER = new SplashingWrapper(); - private static final HauntingWrapper HAUNTING_WRAPPER = new HauntingWrapper(); - - public static boolean canProcess(ItemEntity entity, Type type) { - if (entity.getPersistentData() - .contains("CreateData")) { - CompoundTag compound = entity.getPersistentData() - .getCompound("CreateData"); - if (compound.contains("Processing")) { - CompoundTag processing = compound.getCompound("Processing"); - - if (Type.valueOf(processing.getString("Type")) != type) - return type.canProcess(entity.getItem(), entity.level); - else if (processing.getInt("Time") >= 0) - return true; - else if (processing.getInt("Time") == -1) - return false; - } - } - return type.canProcess(entity.getItem(), entity.level); - } - - public static boolean isWashable(ItemStack stack, Level world) { - SPLASHING_WRAPPER.setItem(0, stack); - Optional recipe = AllRecipeTypes.SPLASHING.find(SPLASHING_WRAPPER, world); - return recipe.isPresent(); - } - - public static boolean isHauntable(ItemStack stack, Level world) { - HAUNTING_WRAPPER.setItem(0, stack); - Optional recipe = AllRecipeTypes.HAUNTING.find(HAUNTING_WRAPPER, world); - return recipe.isPresent(); - } - - public static boolean applyProcessing(ItemEntity entity, Type type) { - if (decrementProcessingTime(entity, type) != 0) - return false; - List stacks = process(entity.getItem(), type, entity.level); - if (stacks == null) - return false; - if (stacks.isEmpty()) { - entity.discard(); - return false; - } - entity.setItem(stacks.remove(0)); - for (ItemStack additional : stacks) { - ItemEntity entityIn = new ItemEntity(entity.level, entity.getX(), entity.getY(), entity.getZ(), additional); - entityIn.setDeltaMovement(entity.getDeltaMovement()); - entity.level.addFreshEntity(entityIn); - } - return true; - } - - public static TransportedResult applyProcessing(TransportedItemStack transported, Level world, Type type) { - TransportedResult ignore = TransportedResult.doNothing(); - if (transported.processedBy != type) { - transported.processedBy = type; - int timeModifierForStackSize = ((transported.stack.getCount() - 1) / 16) + 1; - int processingTime = - (int) (AllConfigs.server().kinetics.fanProcessingTime.get() * timeModifierForStackSize) + 1; - transported.processingTime = processingTime; - if (!type.canProcess(transported.stack, world)) - transported.processingTime = -1; - return ignore; - } - if (transported.processingTime == -1) - return ignore; - if (transported.processingTime-- > 0) - return ignore; - - List stacks = process(transported.stack, type, world); - if (stacks == null) - return ignore; - - List transportedStacks = new ArrayList<>(); - for (ItemStack additional : stacks) { - TransportedItemStack newTransported = transported.getSimilar(); - newTransported.stack = additional.copy(); - transportedStacks.add(newTransported); - } - return TransportedResult.convertTo(transportedStacks); - } - - private static List process(ItemStack stack, Type type, Level world) { - if (type == Type.SPLASHING) { - SPLASHING_WRAPPER.setItem(0, stack); - Optional recipe = AllRecipeTypes.SPLASHING.find(SPLASHING_WRAPPER, world); - if (recipe.isPresent()) - return RecipeApplier.applyRecipeOn(stack, recipe.get()); - return null; - } - if (type == Type.HAUNTING) { - HAUNTING_WRAPPER.setItem(0, stack); - Optional recipe = AllRecipeTypes.HAUNTING.find(HAUNTING_WRAPPER, world); - if (recipe.isPresent()) - return RecipeApplier.applyRecipeOn(stack, recipe.get()); - return null; - } - - RECIPE_WRAPPER.setItem(0, stack); - Optional smokingRecipe = world.getRecipeManager() - .getRecipeFor(RecipeType.SMOKING, RECIPE_WRAPPER, world); - - if (type == Type.BLASTING) { - RECIPE_WRAPPER.setItem(0, stack); - Optional smeltingRecipe = world.getRecipeManager() - .getRecipeFor(RecipeType.SMELTING, RECIPE_WRAPPER, world); - if (!smeltingRecipe.isPresent()) { - RECIPE_WRAPPER.setItem(0, stack); - smeltingRecipe = world.getRecipeManager() - .getRecipeFor(RecipeType.BLASTING, RECIPE_WRAPPER, world); - } - - if (smeltingRecipe.isPresent()) { - if (!smokingRecipe.isPresent() || !ItemStack.isSame(smokingRecipe.get() - .getResultItem(), - smeltingRecipe.get() - .getResultItem())) { - return RecipeApplier.applyRecipeOn(stack, smeltingRecipe.get()); - } - } - - return Collections.emptyList(); - } - - if (type == Type.SMOKING && smokingRecipe.isPresent()) - return RecipeApplier.applyRecipeOn(stack, smokingRecipe.get()); - - return null; - } - - private static int decrementProcessingTime(ItemEntity entity, Type type) { - CompoundTag nbt = entity.getPersistentData(); - - if (!nbt.contains("CreateData")) - nbt.put("CreateData", new CompoundTag()); - CompoundTag createData = nbt.getCompound("CreateData"); - - if (!createData.contains("Processing")) - createData.put("Processing", new CompoundTag()); - CompoundTag processing = createData.getCompound("Processing"); - - if (!processing.contains("Type") || Type.valueOf(processing.getString("Type")) != type) { - processing.putString("Type", type.name()); - int timeModifierForStackSize = ((entity.getItem() - .getCount() - 1) / 16) + 1; - int processingTime = - (int) (AllConfigs.server().kinetics.fanProcessingTime.get() * timeModifierForStackSize) + 1; - processing.putInt("Time", processingTime); - } - - int value = processing.getInt("Time") - 1; - processing.putInt("Time", value); - return value; - } - - public enum Type { - SPLASHING { - @Override - public void spawnParticlesForProcessing(Level level, Vec3 pos) { - if (level.random.nextInt(8) != 0) - return; - Vector3f color = new Color(0x0055FF).asVectorF(); - level.addParticle(new DustParticleOptions(color, 1), pos.x + (level.random.nextFloat() - .5f) * .5f, - pos.y + .5f, pos.z + (level.random.nextFloat() - .5f) * .5f, 0, 1 / 8f, 0); - level.addParticle(ParticleTypes.SPIT, pos.x + (level.random.nextFloat() - .5f) * .5f, pos.y + .5f, - pos.z + (level.random.nextFloat() - .5f) * .5f, 0, 1 / 8f, 0); - } - - @Override - public void affectEntity(Entity entity, Level level) { - if (level.isClientSide) - return; - - if (entity instanceof EnderMan || entity.getType() == EntityType.SNOW_GOLEM - || entity.getType() == EntityType.BLAZE) { - entity.hurt(DamageSource.DROWN, 2); - } - if (entity.isOnFire()) { - entity.clearFire(); - level.playSound(null, entity.blockPosition(), SoundEvents.GENERIC_EXTINGUISH_FIRE, - SoundSource.NEUTRAL, 0.7F, 1.6F + (level.random.nextFloat() - level.random.nextFloat()) * 0.4F); - } - } - - @Override - public boolean canProcess(ItemStack stack, Level level) { - return isWashable(stack, level); - } - }, - SMOKING { - @Override - public void spawnParticlesForProcessing(Level level, Vec3 pos) { - if (level.random.nextInt(8) != 0) - return; - level.addParticle(ParticleTypes.POOF, pos.x, pos.y + .25f, pos.z, 0, 1 / 16f, 0); - } - - @Override - public void affectEntity(Entity entity, Level level) { - if (level.isClientSide) - return; - - if (!entity.fireImmune()) { - entity.setSecondsOnFire(2); - entity.hurt(FIRE_DAMAGE_SOURCE, 2); - } - } - - @Override - public boolean canProcess(ItemStack stack, Level level) { - RECIPE_WRAPPER.setItem(0, stack); - Optional recipe = level.getRecipeManager() - .getRecipeFor(RecipeType.SMOKING, RECIPE_WRAPPER, level); - return recipe.isPresent(); - } - }, - HAUNTING { - @Override - public void spawnParticlesForProcessing(Level level, Vec3 pos) { - if (level.random.nextInt(8) != 0) - return; - pos = pos.add(VecHelper.offsetRandomly(Vec3.ZERO, level.random, 1) - .multiply(1, 0.05f, 1) - .normalize() - .scale(0.15f)); - level.addParticle(ParticleTypes.SOUL_FIRE_FLAME, pos.x, pos.y + .45f, pos.z, 0, 0, 0); - if (level.random.nextInt(2) == 0) - level.addParticle(ParticleTypes.SMOKE, pos.x, pos.y + .25f, pos.z, 0, 0, 0); - } - - @Override - public void affectEntity(Entity entity, Level level) { - if (level.isClientSide) { - if (entity instanceof Horse) { - Vec3 p = entity.getPosition(0); - Vec3 v = p.add(0, 0.5f, 0) - .add(VecHelper.offsetRandomly(Vec3.ZERO, level.random, 1) - .multiply(1, 0.2f, 1) - .normalize() - .scale(1f)); - level.addParticle(ParticleTypes.SOUL_FIRE_FLAME, v.x, v.y, v.z, 0, 0.1f, 0); - if (level.random.nextInt(3) == 0) - level.addParticle(ParticleTypes.LARGE_SMOKE, p.x, p.y + .5f, p.z, - (level.random.nextFloat() - .5f) * .5f, 0.1f, (level.random.nextFloat() - .5f) * .5f); - } - return; - } - - if (entity instanceof LivingEntity livingEntity) { - livingEntity.addEffect(new MobEffectInstance(MobEffects.BLINDNESS, 30, 0, false, false)); - livingEntity.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 20, 1, false, false)); - } - if (entity instanceof Horse horse) { - int progress = horse.getPersistentData() - .getInt("CreateHaunting"); - if (progress < 100) { - if (progress % 10 == 0) { - level.playSound(null, entity.blockPosition(), SoundEvents.SOUL_ESCAPE, SoundSource.NEUTRAL, - 1f, 1.5f * progress / 100f); - } - horse.getPersistentData() - .putInt("CreateHaunting", progress + 1); - return; - } - - level.playSound(null, entity.blockPosition(), SoundEvents.GENERIC_EXTINGUISH_FIRE, - SoundSource.NEUTRAL, 1.25f, 0.65f); - - SkeletonHorse skeletonHorse = EntityType.SKELETON_HORSE.create(level); - CompoundTag serializeNBT = horse.saveWithoutId(new CompoundTag()); - serializeNBT.remove("UUID"); - if (!horse.getArmor() - .isEmpty()) - horse.spawnAtLocation(horse.getArmor()); - - skeletonHorse.deserializeNBT(serializeNBT); - skeletonHorse.setPos(horse.getPosition(0)); - level.addFreshEntity(skeletonHorse); - horse.discard(); - } - } - - @Override - public boolean canProcess(ItemStack stack, Level level) { - return isHauntable(stack, level); - } - }, - BLASTING { - @Override - public void spawnParticlesForProcessing(Level level, Vec3 pos) { - if (level.random.nextInt(8) != 0) - return; - level.addParticle(ParticleTypes.LARGE_SMOKE, pos.x, pos.y + .25f, pos.z, 0, 1 / 16f, 0); - } - - @Override - public void affectEntity(Entity entity, Level level) { - if (level.isClientSide) - return; - - if (!entity.fireImmune()) { - entity.setSecondsOnFire(10); - entity.hurt(LAVA_DAMAGE_SOURCE, 4); - } - } - - @Override - public boolean canProcess(ItemStack stack, Level level) { - RECIPE_WRAPPER.setItem(0, stack); - Optional smeltingRecipe = level.getRecipeManager() - .getRecipeFor(RecipeType.SMELTING, RECIPE_WRAPPER, level); - - if (smeltingRecipe.isPresent()) - return true; - - RECIPE_WRAPPER.setItem(0, stack); - Optional blastingRecipe = level.getRecipeManager() - .getRecipeFor(RecipeType.BLASTING, RECIPE_WRAPPER, level); - - if (blastingRecipe.isPresent()) - return true; - - return !stack.getItem() - .isFireResistant(); - } - }, - NONE { - @Override - public void spawnParticlesForProcessing(Level level, Vec3 pos) {} - - @Override - public void affectEntity(Entity entity, Level level) {} - - @Override - public boolean canProcess(ItemStack stack, Level level) { - return false; - } - }; - - public abstract boolean canProcess(ItemStack stack, Level level); - - public abstract void spawnParticlesForProcessing(Level level, Vec3 pos); - - public abstract void affectEntity(Entity entity, Level level); - - public static Type byBlock(BlockGetter reader, BlockPos pos) { - FluidState fluidState = reader.getFluidState(pos); - if (fluidState.getType() == Fluids.WATER || fluidState.getType() == Fluids.FLOWING_WATER) - return Type.SPLASHING; - BlockState blockState = reader.getBlockState(pos); - Block block = blockState.getBlock(); - if (block == Blocks.SOUL_FIRE - || block == Blocks.SOUL_CAMPFIRE && blockState.getOptionalValue(CampfireBlock.LIT) - .orElse(false) - || AllBlocks.LIT_BLAZE_BURNER.has(blockState) - && blockState.getOptionalValue(LitBlazeBurnerBlock.FLAME_TYPE) - .map(flame -> flame == LitBlazeBurnerBlock.FlameType.SOUL) - .orElse(false)) - return Type.HAUNTING; - if (block == Blocks.FIRE - || blockState.is(BlockTags.CAMPFIRES) && blockState.getOptionalValue(CampfireBlock.LIT) - .orElse(false) - || AllBlocks.LIT_BLAZE_BURNER.has(blockState) - && blockState.getOptionalValue(LitBlazeBurnerBlock.FLAME_TYPE) - .map(flame -> flame == LitBlazeBurnerBlock.FlameType.REGULAR) - .orElse(false) - || getHeatLevelOf(blockState) == BlazeBurnerBlock.HeatLevel.SMOULDERING) - return Type.SMOKING; - if (block == Blocks.LAVA || getHeatLevelOf(blockState).isAtLeast(BlazeBurnerBlock.HeatLevel.FADING)) - return Type.BLASTING; - return Type.NONE; - } - } - - public static class SplashingWrapper extends RecipeWrapper { - public SplashingWrapper() { - super(new ItemStackHandler(1)); - } - } - - public static class HauntingWrapper extends RecipeWrapper { - public HauntingWrapper() { - super(new ItemStackHandler(1)); - } - } - -} diff --git a/src/main/java/com/simibubi/create/content/kinetics/fan/processing/AllFanProcessingTypes.java b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/AllFanProcessingTypes.java new file mode 100644 index 000000000..e286db492 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/AllFanProcessingTypes.java @@ -0,0 +1,488 @@ +package com.simibubi.create.content.kinetics.fan.processing; + +import static com.simibubi.create.content.processing.burner.BlazeBurnerBlock.getHeatLevelOf; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.mojang.math.Vector3f; +import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllRecipeTypes; +import com.simibubi.create.Create; +import com.simibubi.create.content.kinetics.fan.processing.HauntingRecipe.HauntingWrapper; +import com.simibubi.create.content.kinetics.fan.processing.SplashingRecipe.SplashingWrapper; +import com.simibubi.create.content.processing.burner.BlazeBurnerBlock; +import com.simibubi.create.content.processing.burner.LitBlazeBurnerBlock; +import com.simibubi.create.foundation.recipe.RecipeApplier; + +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import net.createmod.catnip.utility.VecHelper; +import net.createmod.catnip.utility.theme.Color; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.BlockParticleOption; +import net.minecraft.core.particles.DustParticleOptions; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.BlockTags; +import net.minecraft.util.RandomSource; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.animal.horse.SkeletonHorse; +import net.minecraft.world.entity.monster.EnderMan; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.AbstractCookingRecipe; +import net.minecraft.world.item.crafting.BlastingRecipe; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.item.crafting.SmeltingRecipe; +import net.minecraft.world.item.crafting.SmokingRecipe; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.CampfireBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.wrapper.RecipeWrapper; + +public class AllFanProcessingTypes { + public static final NoneType NONE = register("none", new NoneType()); + public static final BlastingType BLASTING = register("blasting", new BlastingType()); + public static final HauntingType HAUNTING = register("haunting", new HauntingType()); + public static final SmokingType SMOKING = register("smoking", new SmokingType()); + public static final SplashingType SPLASHING = register("splashing", new SplashingType()); + + private static final Map LEGACY_NAME_MAP; + + static { + Object2ReferenceOpenHashMap map = new Object2ReferenceOpenHashMap<>(); + map.put("NONE", NONE); + map.put("BLASTING", BLASTING); + map.put("HAUNTING", HAUNTING); + map.put("SMOKING", SMOKING); + map.put("SPLASHING", SPLASHING); + map.trim(); + LEGACY_NAME_MAP = map; + } + + private static T register(String id, T type) { + FanProcessingTypeRegistry.register(Create.asResource(id), type); + return type; + } + + @Nullable + public static FanProcessingType ofLegacyName(String name) { + return LEGACY_NAME_MAP.get(name); + } + + public static void register() { + } + + public static FanProcessingType parseLegacy(String str) { + FanProcessingType type = ofLegacyName(str); + if (type != null) { + return type; + } + return FanProcessingType.parse(str); + } + + public static class NoneType implements FanProcessingType { + @Override + public boolean isValidAt(Level level, BlockPos pos) { + return true; + } + + @Override + public int getPriority() { + return -1000000; + } + + @Override + public boolean canProcess(ItemStack stack, Level level) { + return false; + } + + @Override + @Nullable + public List process(ItemStack stack, Level level) { + return null; + } + + @Override + public void spawnProcessingParticles(Level level, Vec3 pos) { + } + + @Override + public void morphAirFlow(AirFlowParticleAccess particleAccess, RandomSource random) { + } + + @Override + public void affectEntity(Entity entity, Level level) { + } + } + + public static class BlastingType implements FanProcessingType { + private static final RecipeWrapper RECIPE_WRAPPER = new RecipeWrapper(new ItemStackHandler(1)); + private static final DamageSource LAVA_DAMAGE_SOURCE = new DamageSource("create.fan_lava").setScalesWithDifficulty() + .setIsFire(); + + @Override + public boolean isValidAt(Level level, BlockPos pos) { + BlockState blockState = level.getBlockState(pos); + Block block = blockState.getBlock(); + return block == Blocks.LAVA || getHeatLevelOf(blockState).isAtLeast(BlazeBurnerBlock.HeatLevel.FADING); + } + + @Override + public int getPriority() { + return 100; + } + + @Override + public boolean canProcess(ItemStack stack, Level level) { + RECIPE_WRAPPER.setItem(0, stack); + Optional smeltingRecipe = level.getRecipeManager() + .getRecipeFor(RecipeType.SMELTING, RECIPE_WRAPPER, level); + + if (smeltingRecipe.isPresent()) + return true; + + RECIPE_WRAPPER.setItem(0, stack); + Optional blastingRecipe = level.getRecipeManager() + .getRecipeFor(RecipeType.BLASTING, RECIPE_WRAPPER, level); + + if (blastingRecipe.isPresent()) + return true; + + return !stack.getItem() + .isFireResistant(); + } + + @Override + @Nullable + public List process(ItemStack stack, Level level) { + RECIPE_WRAPPER.setItem(0, stack); + Optional smokingRecipe = level.getRecipeManager() + .getRecipeFor(RecipeType.SMOKING, RECIPE_WRAPPER, level); + + RECIPE_WRAPPER.setItem(0, stack); + Optional smeltingRecipe = level.getRecipeManager() + .getRecipeFor(RecipeType.SMELTING, RECIPE_WRAPPER, level); + if (!smeltingRecipe.isPresent()) { + RECIPE_WRAPPER.setItem(0, stack); + smeltingRecipe = level.getRecipeManager() + .getRecipeFor(RecipeType.BLASTING, RECIPE_WRAPPER, level); + } + + if (smeltingRecipe.isPresent()) { + if (!smokingRecipe.isPresent() || !ItemStack.isSame(smokingRecipe.get() + .getResultItem(), + smeltingRecipe.get() + .getResultItem())) { + return RecipeApplier.applyRecipeOn(stack, smeltingRecipe.get()); + } + } + + return Collections.emptyList(); + } + + @Override + public void spawnProcessingParticles(Level level, Vec3 pos) { + if (level.random.nextInt(8) != 0) + return; + level.addParticle(ParticleTypes.LARGE_SMOKE, pos.x, pos.y + .25f, pos.z, 0, 1 / 16f, 0); + } + + @Override + public void morphAirFlow(AirFlowParticleAccess particleAccess, RandomSource random) { + particleAccess.setColor(Color.mixColors(0xFF4400, 0xFF8855, random.nextFloat())); + particleAccess.setAlpha(.5f); + if (random.nextFloat() < 1 / 32f) + particleAccess.spawnExtraParticle(ParticleTypes.FLAME, .25f); + if (random.nextFloat() < 1 / 16f) + particleAccess.spawnExtraParticle(new BlockParticleOption(ParticleTypes.BLOCK, Blocks.LAVA.defaultBlockState()), .25f); + } + + @Override + public void affectEntity(Entity entity, Level level) { + if (level.isClientSide) + return; + + if (!entity.fireImmune()) { + entity.setSecondsOnFire(10); + entity.hurt(LAVA_DAMAGE_SOURCE, 4); + } + } + } + + public static class HauntingType implements FanProcessingType { + private static final HauntingWrapper HAUNTING_WRAPPER = new HauntingWrapper(); + + @Override + public boolean isValidAt(Level level, BlockPos pos) { + BlockState blockState = level.getBlockState(pos); + Block block = blockState.getBlock(); + return block == Blocks.SOUL_FIRE + || block == Blocks.SOUL_CAMPFIRE && blockState.getOptionalValue(CampfireBlock.LIT) + .orElse(false) + || AllBlocks.LIT_BLAZE_BURNER.has(blockState) + && blockState.getOptionalValue(LitBlazeBurnerBlock.FLAME_TYPE) + .map(flame -> flame == LitBlazeBurnerBlock.FlameType.SOUL) + .orElse(false); + } + + @Override + public int getPriority() { + return 300; + } + + @Override + public boolean canProcess(ItemStack stack, Level level) { + HAUNTING_WRAPPER.setItem(0, stack); + Optional recipe = AllRecipeTypes.HAUNTING.find(HAUNTING_WRAPPER, level); + return recipe.isPresent(); + } + + @Override + @Nullable + public List process(ItemStack stack, Level level) { + HAUNTING_WRAPPER.setItem(0, stack); + Optional recipe = AllRecipeTypes.HAUNTING.find(HAUNTING_WRAPPER, level); + if (recipe.isPresent()) + return RecipeApplier.applyRecipeOn(stack, recipe.get()); + return null; + } + + @Override + public void spawnProcessingParticles(Level level, Vec3 pos) { + if (level.random.nextInt(8) != 0) + return; + pos = pos.add(VecHelper.offsetRandomly(Vec3.ZERO, level.random, 1) + .multiply(1, 0.05f, 1) + .normalize() + .scale(0.15f)); + level.addParticle(ParticleTypes.SOUL_FIRE_FLAME, pos.x, pos.y + .45f, pos.z, 0, 0, 0); + if (level.random.nextInt(2) == 0) + level.addParticle(ParticleTypes.SMOKE, pos.x, pos.y + .25f, pos.z, 0, 0, 0); + } + + @Override + public void morphAirFlow(AirFlowParticleAccess particleAccess, RandomSource random) { + particleAccess.setColor(Color.mixColors(0x0, 0x126568, random.nextFloat())); + particleAccess.setAlpha(1f); + if (random.nextFloat() < 1 / 128f) + particleAccess.spawnExtraParticle(ParticleTypes.SOUL_FIRE_FLAME, .125f); + if (random.nextFloat() < 1 / 32f) + particleAccess.spawnExtraParticle(ParticleTypes.SMOKE, .125f); + } + + @Override + public void affectEntity(Entity entity, Level level) { + if (level.isClientSide) { + if (entity instanceof Horse) { + Vec3 p = entity.getPosition(0); + Vec3 v = p.add(0, 0.5f, 0) + .add(VecHelper.offsetRandomly(Vec3.ZERO, level.random, 1) + .multiply(1, 0.2f, 1) + .normalize() + .scale(1f)); + level.addParticle(ParticleTypes.SOUL_FIRE_FLAME, v.x, v.y, v.z, 0, 0.1f, 0); + if (level.random.nextInt(3) == 0) + level.addParticle(ParticleTypes.LARGE_SMOKE, p.x, p.y + .5f, p.z, + (level.random.nextFloat() - .5f) * .5f, 0.1f, (level.random.nextFloat() - .5f) * .5f); + } + return; + } + + if (entity instanceof LivingEntity livingEntity) { + livingEntity.addEffect(new MobEffectInstance(MobEffects.BLINDNESS, 30, 0, false, false)); + livingEntity.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 20, 1, false, false)); + } + if (entity instanceof Horse horse) { + int progress = horse.getPersistentData() + .getInt("CreateHaunting"); + if (progress < 100) { + if (progress % 10 == 0) { + level.playSound(null, entity.blockPosition(), SoundEvents.SOUL_ESCAPE, SoundSource.NEUTRAL, + 1f, 1.5f * progress / 100f); + } + horse.getPersistentData() + .putInt("CreateHaunting", progress + 1); + return; + } + + level.playSound(null, entity.blockPosition(), SoundEvents.GENERIC_EXTINGUISH_FIRE, + SoundSource.NEUTRAL, 1.25f, 0.65f); + + SkeletonHorse skeletonHorse = EntityType.SKELETON_HORSE.create(level); + CompoundTag serializeNBT = horse.saveWithoutId(new CompoundTag()); + serializeNBT.remove("UUID"); + if (!horse.getArmor() + .isEmpty()) + horse.spawnAtLocation(horse.getArmor()); + + skeletonHorse.deserializeNBT(serializeNBT); + skeletonHorse.setPos(horse.getPosition(0)); + level.addFreshEntity(skeletonHorse); + horse.discard(); + } + } + } + + public static class SmokingType implements FanProcessingType { + private static final RecipeWrapper RECIPE_WRAPPER = new RecipeWrapper(new ItemStackHandler(1)); + private static final DamageSource FIRE_DAMAGE_SOURCE = new DamageSource("create.fan_fire").setScalesWithDifficulty() + .setIsFire(); + + @Override + public boolean isValidAt(Level level, BlockPos pos) { + BlockState blockState = level.getBlockState(pos); + Block block = blockState.getBlock(); + return block == Blocks.FIRE + || blockState.is(BlockTags.CAMPFIRES) && blockState.getOptionalValue(CampfireBlock.LIT) + .orElse(false) + || AllBlocks.LIT_BLAZE_BURNER.has(blockState) + && blockState.getOptionalValue(LitBlazeBurnerBlock.FLAME_TYPE) + .map(flame -> flame == LitBlazeBurnerBlock.FlameType.REGULAR) + .orElse(false) + || getHeatLevelOf(blockState) == BlazeBurnerBlock.HeatLevel.SMOULDERING; + } + + @Override + public int getPriority() { + return 200; + } + + @Override + public boolean canProcess(ItemStack stack, Level level) { + RECIPE_WRAPPER.setItem(0, stack); + Optional recipe = level.getRecipeManager() + .getRecipeFor(RecipeType.SMOKING, RECIPE_WRAPPER, level); + return recipe.isPresent(); + } + + @Override + @Nullable + public List process(ItemStack stack, Level level) { + RECIPE_WRAPPER.setItem(0, stack); + Optional smokingRecipe = level.getRecipeManager() + .getRecipeFor(RecipeType.SMOKING, RECIPE_WRAPPER, level); + + if (smokingRecipe.isPresent()) + return RecipeApplier.applyRecipeOn(stack, smokingRecipe.get()); + + return null; + } + + @Override + public void spawnProcessingParticles(Level level, Vec3 pos) { + if (level.random.nextInt(8) != 0) + return; + level.addParticle(ParticleTypes.POOF, pos.x, pos.y + .25f, pos.z, 0, 1 / 16f, 0); + } + + @Override + public void morphAirFlow(AirFlowParticleAccess particleAccess, RandomSource random) { + particleAccess.setColor(Color.mixColors(0x0, 0x555555, random.nextFloat())); + particleAccess.setAlpha(1f); + if (random.nextFloat() < 1 / 32f) + particleAccess.spawnExtraParticle(ParticleTypes.SMOKE, .125f); + if (random.nextFloat() < 1 / 32f) + particleAccess.spawnExtraParticle(ParticleTypes.LARGE_SMOKE, .125f); + } + + @Override + public void affectEntity(Entity entity, Level level) { + if (level.isClientSide) + return; + + if (!entity.fireImmune()) { + entity.setSecondsOnFire(2); + entity.hurt(FIRE_DAMAGE_SOURCE, 2); + } + } + } + + public static class SplashingType implements FanProcessingType { + private static final SplashingWrapper SPLASHING_WRAPPER = new SplashingWrapper(); + + @Override + public boolean isValidAt(Level level, BlockPos pos) { + FluidState fluidState = level.getFluidState(pos); + Fluid fluid = fluidState.getType(); + return fluid == Fluids.WATER || fluid == Fluids.FLOWING_WATER; + } + + @Override + public int getPriority() { + return 400; + } + + @Override + public boolean canProcess(ItemStack stack, Level level) { + SPLASHING_WRAPPER.setItem(0, stack); + Optional recipe = AllRecipeTypes.SPLASHING.find(SPLASHING_WRAPPER, level); + return recipe.isPresent(); + } + + @Override + @Nullable + public List process(ItemStack stack, Level level) { + SPLASHING_WRAPPER.setItem(0, stack); + Optional recipe = AllRecipeTypes.SPLASHING.find(SPLASHING_WRAPPER, level); + if (recipe.isPresent()) + return RecipeApplier.applyRecipeOn(stack, recipe.get()); + return null; + } + + @Override + public void spawnProcessingParticles(Level level, Vec3 pos) { + if (level.random.nextInt(8) != 0) + return; + Vector3f color = new Color(0x0055FF).asVectorF(); + level.addParticle(new DustParticleOptions(color, 1), pos.x + (level.random.nextFloat() - .5f) * .5f, + pos.y + .5f, pos.z + (level.random.nextFloat() - .5f) * .5f, 0, 1 / 8f, 0); + level.addParticle(ParticleTypes.SPIT, pos.x + (level.random.nextFloat() - .5f) * .5f, pos.y + .5f, + pos.z + (level.random.nextFloat() - .5f) * .5f, 0, 1 / 8f, 0); + } + + @Override + public void morphAirFlow(AirFlowParticleAccess particleAccess, RandomSource random) { + particleAccess.setColor(Color.mixColors(0x4499FF, 0x2277FF, random.nextFloat())); + particleAccess.setAlpha(1f); + if (random.nextFloat() < 1 / 32f) + particleAccess.spawnExtraParticle(ParticleTypes.BUBBLE, .125f); + if (random.nextFloat() < 1 / 32f) + particleAccess.spawnExtraParticle(ParticleTypes.BUBBLE_POP, .125f); + } + + @Override + public void affectEntity(Entity entity, Level level) { + if (level.isClientSide) + return; + + if (entity instanceof EnderMan || entity.getType() == EntityType.SNOW_GOLEM + || entity.getType() == EntityType.BLAZE) { + entity.hurt(DamageSource.DROWN, 2); + } + if (entity.isOnFire()) { + entity.clearFire(); + level.playSound(null, entity.blockPosition(), SoundEvents.GENERIC_EXTINGUISH_FIRE, + SoundSource.NEUTRAL, 0.7F, 1.6F + (level.random.nextFloat() - level.random.nextFloat()) * 0.4F); + } + } + } +} diff --git a/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessing.java b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessing.java new file mode 100644 index 000000000..8ea07366c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessing.java @@ -0,0 +1,108 @@ +package com.simibubi.create.content.kinetics.fan.processing; + +import java.util.ArrayList; +import java.util.List; + +import com.simibubi.create.content.kinetics.belt.behaviour.TransportedItemStackHandlerBehaviour.TransportedResult; +import com.simibubi.create.content.kinetics.belt.transport.TransportedItemStack; +import com.simibubi.create.infrastructure.config.AllConfigs; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +public class FanProcessing { + public static boolean canProcess(ItemEntity entity, FanProcessingType type) { + if (entity.getPersistentData() + .contains("CreateData")) { + CompoundTag compound = entity.getPersistentData() + .getCompound("CreateData"); + if (compound.contains("Processing")) { + CompoundTag processing = compound.getCompound("Processing"); + + if (AllFanProcessingTypes.parseLegacy(processing.getString("Type")) != type) + return type.canProcess(entity.getItem(), entity.level); + else if (processing.getInt("Time") >= 0) + return true; + else if (processing.getInt("Time") == -1) + return false; + } + } + return type.canProcess(entity.getItem(), entity.level); + } + + public static boolean applyProcessing(ItemEntity entity, FanProcessingType type) { + if (decrementProcessingTime(entity, type) != 0) + return false; + List stacks = type.process(entity.getItem(), entity.level); + if (stacks == null) + return false; + if (stacks.isEmpty()) { + entity.discard(); + return false; + } + entity.setItem(stacks.remove(0)); + for (ItemStack additional : stacks) { + ItemEntity entityIn = new ItemEntity(entity.level, entity.getX(), entity.getY(), entity.getZ(), additional); + entityIn.setDeltaMovement(entity.getDeltaMovement()); + entity.level.addFreshEntity(entityIn); + } + return true; + } + + public static TransportedResult applyProcessing(TransportedItemStack transported, Level world, FanProcessingType type) { + TransportedResult ignore = TransportedResult.doNothing(); + if (transported.processedBy != type) { + transported.processedBy = type; + int timeModifierForStackSize = ((transported.stack.getCount() - 1) / 16) + 1; + int processingTime = + (int) (AllConfigs.server().kinetics.fanProcessingTime.get() * timeModifierForStackSize) + 1; + transported.processingTime = processingTime; + if (!type.canProcess(transported.stack, world)) + transported.processingTime = -1; + return ignore; + } + if (transported.processingTime == -1) + return ignore; + if (transported.processingTime-- > 0) + return ignore; + + List stacks = type.process(transported.stack, world); + if (stacks == null) + return ignore; + + List transportedStacks = new ArrayList<>(); + for (ItemStack additional : stacks) { + TransportedItemStack newTransported = transported.getSimilar(); + newTransported.stack = additional.copy(); + transportedStacks.add(newTransported); + } + return TransportedResult.convertTo(transportedStacks); + } + + private static int decrementProcessingTime(ItemEntity entity, FanProcessingType type) { + CompoundTag nbt = entity.getPersistentData(); + + if (!nbt.contains("CreateData")) + nbt.put("CreateData", new CompoundTag()); + CompoundTag createData = nbt.getCompound("CreateData"); + + if (!createData.contains("Processing")) + createData.put("Processing", new CompoundTag()); + CompoundTag processing = createData.getCompound("Processing"); + + if (!processing.contains("Type") || AllFanProcessingTypes.parseLegacy(processing.getString("Type")) != type) { + processing.putString("Type", FanProcessingTypeRegistry.getIdOrThrow(type).toString()); + int timeModifierForStackSize = ((entity.getItem() + .getCount() - 1) / 16) + 1; + int processingTime = + (int) (AllConfigs.server().kinetics.fanProcessingTime.get() * timeModifierForStackSize) + 1; + processing.putInt("Time", processingTime); + } + + int value = processing.getInt("Time") - 1; + processing.putInt("Time", value); + return value; + } +} diff --git a/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessingType.java b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessingType.java new file mode 100644 index 000000000..e0a47d4c5 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessingType.java @@ -0,0 +1,60 @@ +package com.simibubi.create.content.kinetics.fan.processing; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; + +public interface FanProcessingType { + boolean isValidAt(Level level, BlockPos pos); + + int getPriority(); + + boolean canProcess(ItemStack stack, Level level); + + @Nullable + List process(ItemStack stack, Level level); + + void spawnProcessingParticles(Level level, Vec3 pos); + + void morphAirFlow(AirFlowParticleAccess particleAccess, RandomSource random); + + void affectEntity(Entity entity, Level level); + + static FanProcessingType parse(String str) { + ResourceLocation id = ResourceLocation.tryParse(str); + if (id == null) { + return AllFanProcessingTypes.NONE; + } + FanProcessingType type = FanProcessingTypeRegistry.getType(id); + if (type == null) { + return AllFanProcessingTypes.NONE; + } + return type; + } + + static FanProcessingType getAt(Level level, BlockPos pos) { + for (FanProcessingType type : FanProcessingTypeRegistry.getSortedTypesView()) { + if (type.isValidAt(level, pos)) { + return type; + } + } + return AllFanProcessingTypes.NONE; + } + + interface AirFlowParticleAccess { + void setColor(int color); + + void setAlpha(float alpha); + + void spawnExtraParticle(ParticleOptions options, float speedMultiplier); + } +} diff --git a/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessingTypeRegistry.java b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessingTypeRegistry.java new file mode 100644 index 000000000..0e177a012 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/FanProcessingTypeRegistry.java @@ -0,0 +1,68 @@ +package com.simibubi.create.content.kinetics.fan.processing; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.Nullable; + +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import net.minecraft.resources.ResourceLocation; + +public class FanProcessingTypeRegistry { + private static final Map TYPES = new Object2ReferenceOpenHashMap<>(); + private static final Map IDS = new Reference2ObjectOpenHashMap<>(); + private static final List SORTED_TYPES = new ReferenceArrayList<>(); + private static final List SORTED_TYPES_VIEW = Collections.unmodifiableList(SORTED_TYPES); + + public static void register(ResourceLocation id, FanProcessingType type) { + if (TYPES.put(id, type) != null) { + throw new IllegalArgumentException("Tried to override FanProcessingType registration for id '" + id + "'. This is not supported!"); + } + ResourceLocation prevId = IDS.put(type, id); + if (prevId != null) { + throw new IllegalArgumentException("Tried to register same FanProcessingType instance for multiple ids '" + prevId + "' and '" + id + "'. This is not supported!"); + } + insertSortedType(type, id); + } + + private static void insertSortedType(FanProcessingType type, ResourceLocation id) { + int index = Collections.binarySearch(SORTED_TYPES, type, (type1, type2) -> type2.getPriority() - type1.getPriority()); + if (index >= 0) { + throw new IllegalStateException(); + } + SORTED_TYPES.add(-index - 1, type); + } + + @Nullable + public static FanProcessingType getType(ResourceLocation id) { + return TYPES.get(id); + } + + public static FanProcessingType getTypeOrThrow(ResourceLocation id) { + FanProcessingType type = getType(id); + if (type == null) { + throw new IllegalArgumentException("Could not get FanProcessingType for id '" + id + "'!"); + } + return type; + } + + @Nullable + public static ResourceLocation getId(FanProcessingType type) { + return IDS.get(type); + } + + public static ResourceLocation getIdOrThrow(FanProcessingType type) { + ResourceLocation id = getId(type); + if (id == null) { + throw new IllegalArgumentException("Could not get id for FanProcessingType " + type + "!"); + } + return id; + } + + public static List getSortedTypesView() { + return SORTED_TYPES_VIEW; + } +} diff --git a/src/main/java/com/simibubi/create/content/kinetics/fan/HauntingRecipe.java b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/HauntingRecipe.java similarity index 56% rename from src/main/java/com/simibubi/create/content/kinetics/fan/HauntingRecipe.java rename to src/main/java/com/simibubi/create/content/kinetics/fan/processing/HauntingRecipe.java index 43dbc81d0..b936db96f 100644 --- a/src/main/java/com/simibubi/create/content/kinetics/fan/HauntingRecipe.java +++ b/src/main/java/com/simibubi/create/content/kinetics/fan/processing/HauntingRecipe.java @@ -1,22 +1,25 @@ -package com.simibubi.create.content.kinetics.fan; +package com.simibubi.create.content.kinetics.fan.processing; import javax.annotation.ParametersAreNonnullByDefault; import com.simibubi.create.AllRecipeTypes; +import com.simibubi.create.content.kinetics.fan.processing.HauntingRecipe.HauntingWrapper; import com.simibubi.create.content.processing.recipe.ProcessingRecipe; import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder.ProcessingRecipeParams; import net.minecraft.world.level.Level; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.items.wrapper.RecipeWrapper; @ParametersAreNonnullByDefault -public class HauntingRecipe extends ProcessingRecipe { +public class HauntingRecipe extends ProcessingRecipe { public HauntingRecipe(ProcessingRecipeParams params) { super(AllRecipeTypes.HAUNTING, params); } @Override - public boolean matches(FanProcessing.HauntingWrapper inv, Level worldIn) { + public boolean matches(HauntingWrapper inv, Level worldIn) { if (inv.isEmpty()) return false; return ingredients.get(0) @@ -33,4 +36,10 @@ public class HauntingRecipe extends ProcessingRecipe { +public class SplashingRecipe extends ProcessingRecipe { public SplashingRecipe(ProcessingRecipeParams params) { super(AllRecipeTypes.SPLASHING, params); @@ -34,4 +36,10 @@ public class SplashingRecipe extends ProcessingRecipe implements IA return 4; } + @Override + protected boolean canSpecifyDuration() { + return true; + } + @Override public void addAssemblyIngredients(List list) {} diff --git a/src/main/java/com/simibubi/create/content/logistics/filter/ItemAttribute.java b/src/main/java/com/simibubi/create/content/logistics/filter/ItemAttribute.java index a3c5aa50e..c12ef4932 100644 --- a/src/main/java/com/simibubi/create/content/logistics/filter/ItemAttribute.java +++ b/src/main/java/com/simibubi/create/content/logistics/filter/ItemAttribute.java @@ -13,7 +13,7 @@ import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import com.simibubi.create.AllRecipeTypes; -import com.simibubi.create.content.kinetics.fan.FanProcessing; +import com.simibubi.create.content.kinetics.fan.processing.AllFanProcessingTypes; import com.simibubi.create.content.logistics.filter.attribute.BookAuthorAttribute; import com.simibubi.create.content.logistics.filter.attribute.BookCopyAttribute; import com.simibubi.create.content.logistics.filter.attribute.ColorAttribute; @@ -146,8 +146,8 @@ public interface ItemAttribute { EQUIPABLE(s -> LivingEntity.getEquipmentSlotForItem(s) .getType() != EquipmentSlot.Type.HAND), FURNACE_FUEL(AbstractFurnaceBlockEntity::isFuel), - WASHABLE(FanProcessing::isWashable), - HAUNTABLE(FanProcessing::isHauntable), + WASHABLE(AllFanProcessingTypes.SPLASHING::canProcess), + HAUNTABLE(AllFanProcessingTypes.HAUNTING::canProcess), CRUSHABLE((s, w) -> testRecipe(s, w, AllRecipeTypes.CRUSHING.getType()) || testRecipe(s, w, AllRecipeTypes.MILLING.getType())), SMELTABLE((s, w) -> testRecipe(s, w, RecipeType.SMELTING)), diff --git a/src/main/java/com/simibubi/create/content/processing/basin/BasinRecipe.java b/src/main/java/com/simibubi/create/content/processing/basin/BasinRecipe.java index dfb4142e5..a2e728d00 100644 --- a/src/main/java/com/simibubi/create/content/processing/basin/BasinRecipe.java +++ b/src/main/java/com/simibubi/create/content/processing/basin/BasinRecipe.java @@ -208,6 +208,11 @@ public class BasinRecipe extends ProcessingRecipe { return true; } + @Override + protected boolean canSpecifyDuration() { + return true; + } + @Override public boolean matches(SmartInventory inv, @Nonnull Level worldIn) { return false; diff --git a/src/main/java/com/simibubi/create/content/processing/recipe/ProcessingRecipe.java b/src/main/java/com/simibubi/create/content/processing/recipe/ProcessingRecipe.java index e21ff458e..d14890ad5 100644 --- a/src/main/java/com/simibubi/create/content/processing/recipe/ProcessingRecipe.java +++ b/src/main/java/com/simibubi/create/content/processing/recipe/ProcessingRecipe.java @@ -71,7 +71,7 @@ public abstract class ProcessingRecipe implements Recipe } protected boolean canSpecifyDuration() { - return true; + return false; } protected int getMaxFluidInputCount() { diff --git a/src/main/java/com/simibubi/create/content/redstone/displayLink/AllDisplayBehaviours.java b/src/main/java/com/simibubi/create/content/redstone/displayLink/AllDisplayBehaviours.java index be20d7376..24a0a0d19 100644 --- a/src/main/java/com/simibubi/create/content/redstone/displayLink/AllDisplayBehaviours.java +++ b/src/main/java/com/simibubi/create/content/redstone/displayLink/AllDisplayBehaviours.java @@ -237,10 +237,10 @@ public class AllDisplayBehaviours { Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> { DisplayBehaviour computerDisplaySource = register(Create.asResource("computer_display_source"), new ComputerDisplaySource()); - assignBlockEntity(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "wired_modem_full")); - assignBlockEntity(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_normal")); - assignBlockEntity(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_advanced")); - assignBlockEntity(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_command")); + assignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("wired_modem_full")); + assignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_normal")); + assignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_advanced")); + assignBlockEntity(computerDisplaySource, Mods.COMPUTERCRAFT.rl("computer_command")); }); } } diff --git a/src/main/java/com/simibubi/create/content/schematics/SchematicInstances.java b/src/main/java/com/simibubi/create/content/schematics/SchematicInstances.java index 27f407db1..91488ca63 100644 --- a/src/main/java/com/simibubi/create/content/schematics/SchematicInstances.java +++ b/src/main/java/com/simibubi/create/content/schematics/SchematicInstances.java @@ -24,19 +24,13 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp public class SchematicInstances { - public static final WorldAttached> loadedSchematics; - - static { - loadedSchematics = new WorldAttached<>($ -> CacheBuilder.newBuilder() + private static final WorldAttached> LOADED_SCHEMATICS = new WorldAttached<>($ -> CacheBuilder.newBuilder() .expireAfterAccess(5, TimeUnit.MINUTES) .build()); - } - - public static void register() {} @Nullable public static SchematicLevel get(Level world, ItemStack schematic) { - Cache map = loadedSchematics.get(world); + Cache map = LOADED_SCHEMATICS.get(world); int hash = getHash(schematic); SchematicLevel ifPresent = map.getIfPresent(hash); if (ifPresent != null) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraption.java b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraption.java index 52965bd3c..44c40d702 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraption.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraption.java @@ -289,9 +289,9 @@ public class CarriageContraption extends Contraption { } @Override - public Collection getSpecialRenderedTEs() { + public Collection getSpecialRenderedBEs() { if (notInPortal()) - return super.getSpecialRenderedTEs(); + return super.getSpecialRenderedBEs(); return specialRenderedBEsOutsidePortal; } diff --git a/src/main/java/com/simibubi/create/content/trains/track/TrackPlacement.java b/src/main/java/com/simibubi/create/content/trains/track/TrackPlacement.java index b61d08d0b..c8a3b15cd 100644 --- a/src/main/java/com/simibubi/create/content/trains/track/TrackPlacement.java +++ b/src/main/java/com/simibubi/create/content/trains/track/TrackPlacement.java @@ -11,6 +11,7 @@ import com.simibubi.create.AllSpecialTextures; import com.simibubi.create.AllTags; import com.simibubi.create.content.equipment.blueprint.BlueprintOverlayRenderer; import com.simibubi.create.foundation.block.ProperWaterloggedBlock; +import com.simibubi.create.foundation.utility.BlockHelper; import com.simibubi.create.foundation.utility.CreateLang; import com.simibubi.create.infrastructure.config.AllConfigs; @@ -44,7 +45,6 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult.Type; @@ -478,18 +478,6 @@ public class TrackPlacement { info.requiredPavement += TrackPaver.paveCurve(level, info.curve, block, simulate, visited); } - private static BlockState copyProperties(BlockState from, BlockState onto) { - for (Property property : onto.getProperties()) { - if (from.hasProperty(property)) - onto = onto.setValue(property, from.getValue(property)); - } - return onto; - } - - private static BlockState copyProperties(BlockState from, BlockState onto, boolean keepFrom) { - return keepFrom ? from : copyProperties(from, onto); - } - private static PlacementInfo placeTracks(Level level, PlacementInfo info, BlockState state1, BlockState state2, BlockPos targetPos1, BlockPos targetPos2, boolean simulate) { info.requiredTracks = 0; @@ -518,7 +506,7 @@ public class TrackPlacement { BlockPos offsetPos = pos.offset(offset.x, offset.y, offset.z); BlockState stateAtPos = level.getBlockState(offsetPos); // copy over all shared properties from the shaped state to the correct track material block - BlockState toPlace = copyProperties(state, info.trackMaterial.getBlock().defaultBlockState()); + BlockState toPlace = BlockHelper.copyProperties(state, info.trackMaterial.getBlock().defaultBlockState()); boolean canPlace = stateAtPos.getMaterial() .isReplaceable(); @@ -544,12 +532,12 @@ public class TrackPlacement { BlockState onto = info.trackMaterial.getBlock().defaultBlockState(); BlockState stateAtPos = level.getBlockState(targetPos1); level.setBlock(targetPos1, ProperWaterloggedBlock.withWater(level, - (AllTags.AllBlockTags.TRACKS.matches(stateAtPos) ? stateAtPos : copyProperties(state1, onto)) + (AllTags.AllBlockTags.TRACKS.matches(stateAtPos) ? stateAtPos : BlockHelper.copyProperties(state1, onto)) .setValue(TrackBlock.HAS_BE, true), targetPos1), 3); stateAtPos = level.getBlockState(targetPos2); level.setBlock(targetPos2, ProperWaterloggedBlock.withWater(level, - (AllTags.AllBlockTags.TRACKS.matches(stateAtPos) ? stateAtPos : copyProperties(state2, onto)) + (AllTags.AllBlockTags.TRACKS.matches(stateAtPos) ? stateAtPos : BlockHelper.copyProperties(state2, onto)) .setValue(TrackBlock.HAS_BE, true), targetPos2), 3); } diff --git a/src/main/java/com/simibubi/create/foundation/data/RecipeSerializerTagGen.java b/src/main/java/com/simibubi/create/foundation/data/RecipeSerializerTagGen.java new file mode 100644 index 000000000..15e8328eb --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/data/RecipeSerializerTagGen.java @@ -0,0 +1,31 @@ +package com.simibubi.create.foundation.data; + +import org.jetbrains.annotations.Nullable; + +import com.simibubi.create.AllTags.AllRecipeSerializerTags; +import com.simibubi.create.Create; +import com.simibubi.create.compat.Mods; + +import net.minecraft.core.Registry; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.tags.TagsProvider; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraftforge.common.data.ExistingFileHelper; + +public class RecipeSerializerTagGen extends TagsProvider> { + public RecipeSerializerTagGen(DataGenerator generator, @Nullable ExistingFileHelper existingFileHelper) { + super(generator, Registry.RECIPE_SERIALIZER, Create.ID, existingFileHelper); + } + + @Override + public String getName() { + return "Create's Recipe Serializer Tags"; + } + + @Override + protected void addTags() { + this.tag(AllRecipeSerializerTags.AUTOMATION_IGNORE.tag) + .addOptional(Mods.OCCULTISM.rl("spirit_trade")) + .addOptional(Mods.OCCULTISM.rl("ritual")); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/mixin/client/FixNormalScalingMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/client/FixNormalScalingMixin.java deleted file mode 100644 index 4eb81c31c..000000000 --- a/src/main/java/com/simibubi/create/foundation/mixin/client/FixNormalScalingMixin.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.simibubi.create.foundation.mixin.client; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.At.Shift; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.mojang.blaze3d.vertex.PoseStack; - -@Mixin(PoseStack.class) -public class FixNormalScalingMixin { - /** - * Minecraft negates the normal matrix if all scales are equal and negative, but - * does not return afterward. This allows the rest of the method's logic to be - * applied, which negates the matrix again, resulting in the matrix being the - * same as in the beginning. - */ - @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/math/Matrix3f;mul(F)V", shift = Shift.AFTER), method = "scale(FFF)V", cancellable = true) - private void create$returnAfterNegate(float x, float y, float z, CallbackInfo ci) { - ci.cancel(); - } - - /** - * Minecraft takes the inverse cube root of the product of all scales to provide a - * rough estimate for normalization so that it does not need to be done later. It - * does not make sense for this "normalization factor" to be negative though, as - * that would invert all normals. Additionally, Minecraft's fastInverseCbrt method - * does not work for negative numbers. - */ - @ModifyArg(at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;fastInvCubeRoot(F)F"), method = "scale(FFF)V") - private float create$absInvCbrtInput(float input) { - return Math.abs(input); - } -} diff --git a/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java b/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java index 46c46ba21..e7e1f246c 100644 --- a/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java +++ b/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java @@ -31,7 +31,7 @@ public class CKinetics extends ConfigBase { public final ConfigGroup contraptions = group(1, "contraptions", "Moving Contraptions"); public final ConfigInt maxBlocksMoved = i(2048, 1, "maxBlocksMoved", Comments.maxBlocksMoved); public final ConfigInt maxDataSize = - i(ContraptionData.DEFAULT_MAX, 0, "maxDataSize", Comments.bytes, Comments.maxDataDisable, Comments.maxDataSize, Comments.maxDataSize2); + i(ContraptionData.DEFAULT_LIMIT, 0, "maxDataSize", Comments.bytes, Comments.maxDataDisable, Comments.maxDataSize, Comments.maxDataSize2); public final ConfigInt maxChassisRange = i(16, 1, "maxChassisRange", Comments.maxChassisRange); public final ConfigInt maxPistonPoles = i(64, 1, "maxPistonPoles", Comments.maxPistonPoles); public final ConfigInt maxRopeLength = i(256, 1, "maxRopeLength", Comments.maxRopeLength); diff --git a/src/main/java/com/simibubi/create/infrastructure/ponder/AllPonderTags.java b/src/main/java/com/simibubi/create/infrastructure/ponder/AllPonderTags.java index 3666a4149..d03a8a0ec 100644 --- a/src/main/java/com/simibubi/create/infrastructure/ponder/AllPonderTags.java +++ b/src/main/java/com/simibubi/create/infrastructure/ponder/AllPonderTags.java @@ -9,7 +9,6 @@ import com.tterrag.registrate.util.entry.RegistryEntry; import net.createmod.ponder.foundation.CustomPonderRegistrationHelper; import net.createmod.ponder.foundation.PonderTag; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Block; @@ -318,7 +317,7 @@ public class AllPonderTags { .add(Blocks.TARGET); Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> { - Block computer = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_advanced")); + Block computer = ForgeRegistries.BLOCKS.getValue(Mods.COMPUTERCRAFT.rl("computer_advanced")); if (computer != null) HELPER.addToTag(DISPLAY_SOURCES).add(computer); }); diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index a632d9897..c665e0cca 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -32,7 +32,7 @@ ${mod_description} [[dependencies.create]] modId="flywheel" mandatory=true - versionRange="[0.6.9,0.6.10)" + versionRange="[0.6.10,0.6.11)" ordering="AFTER" side="CLIENT" diff --git a/src/main/resources/create.mixins.json b/src/main/resources/create.mixins.json index 5c48d5761..89473d7d2 100644 --- a/src/main/resources/create.mixins.json +++ b/src/main/resources/create.mixins.json @@ -28,7 +28,6 @@ "client.BlockDestructionProgressMixin", "client.CameraMixin", "client.EntityContraptionInteractionMixin", - "client.FixNormalScalingMixin", "client.GameRendererMixin", "client.HeavyBootsOnPlayerMixin", "client.HumanoidArmorLayerMixin", diff --git a/wiki/Lua-Display-Link.md b/wiki/Lua-Display-Link.md deleted file mode 100644 index d75969da1..000000000 --- a/wiki/Lua-Display-Link.md +++ /dev/null @@ -1,88 +0,0 @@ -| Method | Description | -|---------------------------------------|------------------------------------------------------| -| [`setCursorPos(x, y)`](#setCursorPos) | Sets the cursor position | -| [`getCursorPos()`](#getCursorPos) | Gets the current cursor position | -| [`getSize()`](#getSize) | Gets the display size of the connected target | -| [`isColor()`](#isColor) | Whether the connected display target supports color | -| [`isColour()`](#isColour) | Whether the connected display target supports colour | -| [`write(text)`](#writetext) | Writes text at the current cursor position | -| [`clearLine()`](#clearLine) | Clears the line at the current cursor position | -| [`clear()`](#clear) | Clears the whole display | -| [`update()`](#update) | Pushes an update to the display target | - ---- -### `setCursorPos(x, y)` -Sets the cursor position. Can be outside the bounds of the connected display. - -**Parameters** -- _x:_ `number` The cursor x position. -- _y:_ `number` The cursor y position. - ---- -### `getCursorPos()` -Gets the current cursor position. - -**Returns** -- `number` The cursor x position. -- `number` The cursor y position. - ---- -### `getSize()` -Gets the size of the connected display target. - -**Returns** -- `number` The width of the display. -- `number` The height of the display. - ---- -### `isColor()` -Checks whether the connected display target supports color. - -**Returns** -- `boolean` Whether the display supports color. - ---- -### `isColour()` -Checks whether the connected display target supports colour. - -**Returns** -- `boolean` Whether the display supports colour. - ---- -### `write(text)` -Writes text at the current cursor position, moving the cursor to the end of the text. -This only writes to an internal buffer. For the changes to show up on the display [`update()`](#update) must be used. -If the cursor is outside the bounds of the connected display, the text will not show up on the display. - -This will overwrite any text currently at the cursor position. - -**Parameters** -- _text:_ `string` The text to write. - -**See also** -- [`update()`](#update) To push the changes to the display target. - ---- -### `clearLine()` -Clears the line at the current cursor position. - -**See also** -- [`update()`](#update) To push the changes to the display target. - ---- -### `clear()` -Clears the whole display. - -**See also** -- [`update()`](#update) To push the changes to the display target. - ---- -### `update()` -Pushes any changes to the connected display target. -Any changes made are only made to an internal buffer. -For them to show up on the display they must be pushed to the display using this function. -This allows for this peripheral to be better multithreaded and for users to be able to change multiple lines at once by -using multiple [`write(text)`](#writetext) calls and then one [`update()`](#update) call. - -**See also** -- [`write(text)`](#writetext) To write text to the display target. diff --git a/wiki/Lua-Rotation-Speed-Controller.md b/wiki/Lua-Rotation-Speed-Controller.md deleted file mode 100644 index ffd8ca785..000000000 --- a/wiki/Lua-Rotation-Speed-Controller.md +++ /dev/null @@ -1,18 +0,0 @@ -| Method | Description | -|-------------------------------------------------|----------------------------------------| -| [`setTargetSpeed(speed)`](#setTargetSpeedspeed) | Sets the target rotation speed | -| [`getTargetSpeed()`](#getTargetSpeed) | Gets the current target rotation speed | - ---- -### `setTargetSpeed(speed)` -Sets the rotation speed controller's target speed. - -**Parameters** -- _speed:_ `number` The target speed in RPM. Must be an integer within the range of [-256..256]. Values outside of this range will be clamped. - ---- -### `getTargetSpeed()` -Gets the rotation speed controller's current target speed. - -**Returns** -- `number` The current target rotation speed in RPM. diff --git a/wiki/Lua-Sequenced-Gearshift.md b/wiki/Lua-Sequenced-Gearshift.md deleted file mode 100644 index 9e7813322..000000000 --- a/wiki/Lua-Sequenced-Gearshift.md +++ /dev/null @@ -1,28 +0,0 @@ -| Method | Description | -|--------------------------------------------------------|--------------------------------------------------------------| -| [`rotate(angle, [modifier])`](#rotateangle-modifier) | Rotates shaft by a set angle | -| [`move(distance, [modifier])`](#movedistance-modifier) | Rotates shaft to move Piston/Pulley/Gantry by a set distance | -| [`isRunning()`](#isRunning) | Whether the gearshift is currently spinning | - ---- -### `rotate(angle, [modifier])` -Rotates connected components by a set angle. - -**Parameters** -- _angle:_ `number` Angle to rotate the shaft by in degrees. Must be a positive integer. To do backwards rotation, set _modifier_ to a negative value. -- _modifier?:_ `number = 1` Speed modifier which can be used to reverse rotation. Must be an integer within the range of [-2..2]. Values out of this range are ignored and the default of 1 is used. - ---- -### `move(distance, [modifier])` -Rotates connected components to move connected piston, pulley or gantry contractions by a set distance. - -**Parameters** -- _distance:_ `number` Distance to move connected piston, pulley or gantry contraptions by. Must be a positive integer. To do backwards movement, set _modifier_ to a negative value. -- _modifier?:_ `number = 1` Speed modifier which can be used to reverse direction. Must be an integer within the range of [-2..2]. Values out of this range are ignored and the default of 1 is used. - ---- -### `isRunning()` -Checks if the sequenced gearshift is currently spinning. - -**Returns** -- `boolean` Whether the sequenced gearshift is currently spinning. diff --git a/wiki/Lua-Speedometer.md b/wiki/Lua-Speedometer.md deleted file mode 100644 index 868714e0d..000000000 --- a/wiki/Lua-Speedometer.md +++ /dev/null @@ -1,10 +0,0 @@ -| Method | Description | -|---------------------------|---------------------------------| -| [`getSpeed()`](#getSpeed) | Gets the current rotation speed | - ---- -### `getSpeed()` -Gets the current rotation speed of the attached components. - -**Returns** -- `number` The current rotation speed in RPM. diff --git a/wiki/Lua-Stressometer.md b/wiki/Lua-Stressometer.md deleted file mode 100644 index 0e609f5d0..000000000 --- a/wiki/Lua-Stressometer.md +++ /dev/null @@ -1,18 +0,0 @@ -| Method | Description | -|---------------------------------------------|--------------------------------| -| [`getStress()`](#getStress) | Gets the current stress level | -| [`getStressCapacity()`](#getStressCapacity) | Gets the total stress capacity | - ---- -### `getStress()` -Gets the connected network's current stress level. - -**Returns** -- `number` The current stress level in SU. - ---- -### `getStressCapacity()` -Gets the connected network's total stress capacity. - -**Returns** -- `number` The total stress capacity in SU. diff --git a/wiki/Lua-Train-Schedule.md b/wiki/Lua-Train-Schedule.md deleted file mode 100644 index e221985ae..000000000 --- a/wiki/Lua-Train-Schedule.md +++ /dev/null @@ -1,195 +0,0 @@ -Train schedules are represented by a table in Lua. The table contains a list of entries where each entry has a single instruction and multiple conditions. -Each instruction and condition has a `data` table that stores specific data about the instruction or condition. - -```lua -schedule = { - cyclic = true, -- Does the schedule repeat itself after the end has been reached? - entries = { -- List of entries, each entry contains a single instruction and multiple conditions. - { - instruction = { - id = "create:destination", -- The different instructions are described below. - data = { -- Data that is stored about the instruction. Different for each instruction type. - text = "Station 1", - }, - }, - conditions = { -- List of lists of conditions. The outer list is the "OR" list - { -- and the inner lists are "AND" lists. - { - id = "create:delay", -- The different conditions are described below. - data = { -- Data that is stored about the condition. Different for each condition type. - value = 5, - time_unit = 1, - }, - }, - { - id = "create:powered", - data = {}, - }, - }, - { - { - id = "create:time_of_day", - data = { - rotation = 0, - hour = 14, - minute = 0, - }, - }, - }, - }, - }, - }, -} -``` ---- -## Instructions -| ID | Description | -|----------------------------------------------|---------------------------------| -| [`"create:destination"`](#createdestination) | Move to a certain train station | -| [`"create:rename"`](#createrename) | Change the schedule title | -| [`"create:throttle"`](#createthrottle) | Change the train's throttle | - ---- -### `"create:destination"` -Moves the train to the chosen train station. This instruction must have at least one condition. - -**Data** -- _text:_ `string` The name of the station to travel to. Can include * as a wildcard. - ---- -### `"create:rename"` -Renames the schedule. This name shows up on display link targets. This instruction cannot have conditions. - -**Data** -- _text:_ `string` The name to rename the schedule to. - ---- -### `"create:throttle"` -Changes the throttle of the train. This instruction cannot have conditions. - -**Data** -- _value:_ `number` The throttle to set the train to. Must be an integer within the range of [5..100]. - ---- -## Conditions -Conditions are stored in a list of lists of conditions. The inner lists contain conditions that get `AND`'ed together. They must all be met for that group to be true. -The outer list contains the `AND`'ed groups of conditions that get `OR`'ed together. Only one of the groups needs to be true for the schedule to move onto the next instruction. - -| ID | Description | -|-----------------------------------------------------|-----------------------------------------------------| -| [`"create:delay"`](#createdelay) | Wait for a certain delay | -| [`"create:time_of_day"`](#createtimeofday) | Wait for a specific time of day | -| [`"create:fluid_threshold"`](#createfluidthreshold) | Wait for a certain amount of fluid to be on board | -| [`"create:item_threshold"`](#createitemthreshold) | Wait for a certain amount of items to be on board | -| [`"create:redstone_link"`](#createredstonelink) | Wait for a redstone link to be powered | -| [`"create:player_count"`](#createplayercount) | Wait for a certain amount of players to be on board | -| [`"create:idle"`](#createidle) | Wait for cargo loading inactivity | -| [`"create:unloaded"`](#createunloaded) | Wait for the current chunk to be unloaded | -| [`"create:powered"`](#createpowered) | Wait for the station to be powered | - ---- -### `"create:delay"` -Wait for a set delay. Can be measured in ticks, seconds or minutes. - -**Data** -- _value:_ `number` The amount of time to wait for. -- _time_unit:_ `number` The unit of time. 0 for ticks, 1 for seconds and 2 for minutes. - ---- -### `"create:time_of_day"` -Wait for a time of day, then repeat at a specified interval. - -**Data** -- _hour:_ `number` The hour of the day to wait for in a 24-hour format. Must be an integer within the range of [0..23]. -- _minute:_ `number` The minute of the hour to wait for. Must be an integer within the range of [0..59]. -- _rotation:_ `number` The interval to repeat at after the time of day has been met. Check the rotation table below for valid values. Must be an integer within the range of [0..9]. - -**Rotation** - -| Rotation | Time Interval | -|----------|------------------| -| 0 | Every Day | -| 1 | Every 12 Hours | -| 2 | Every 6 Hours | -| 3 | Every 4 Hours | -| 4 | Every 3 Hours | -| 5 | Every 2 Hours | -| 6 | Every Hour | -| 7 | Every 45 Minutes | -| 8 | Every 30 Minutes | -| 9 | Every 15 Minutes | - ---- -### `"create:fluid_threshold"` -Wait for a certain amount of a specific fluid to be loaded onto the train. - -**Data** -- _bucket:_ `table` The bucket item of the fluid. -- _threshold:_ `number` The threshold in number of buckets of fluid. Must be a positive integer. -- _operator:_ `number` Whether the condition should wait for the train to be loaded above the threshold, below the threshold or exactly at the threshold. 0 for greater than, 1 for less than, 2 for equal to. -- _measure:_ `number` The unit to measure the fluid in. This condition supports buckets as the only unit. Set to 0. - -**See also** -- [Items](#items) How items are represented in Lua. - ---- -### `"create:item_threshold"` -Wait for a certain amount of a specific item to be loaded onto the train. - -**Data** -- _item:_ `table` The item. -- _threshold:_ `number` The threshold of items. Must be a positive integer. -- _operator:_ `number` Whether the condition should wait for the train to be loaded above the threshold, below the threshold or exactly at the threshold. 0 for greater than, 1 for less than, 2 for equal to. -- _measure:_ `number` The unit to measure the items in. 0 for items. 1 for stacks of items. - -**See also** -- [Items](#items) How items are represented in Lua. - ---- -### `"create:redstone_link"` -Wait for a redstone link to be powered. - -**Data** -- _frequency:_ `{ table... }` A list of the two items making up the redstone link frequency. -- _inverted:_ `number` Whether the redstone link should be powered or not to meet the condition. 0 for powered. 1 for not powered. - -**See also** -- [Items](#items) How items are represented in Lua. - ---- -### `"create:player_count"` -Wait for a certain amount of players to be seated on the train. - -**Data** -- _count:_ `number` The number of players to be seated on the train. Must be a positive integer. -- _exact:_ `number` Whether the seated player count has to be exact to meet the condition. 0 for the exact amount of players seated, 1 for a greater than or equal amount of seated players. - ---- -### `"create:idle"` -Wait for a period of inactivity in loading or unloading the train. Can be measured in ticks, seconds or minutes. - -**Data** -- _value:_ `number` The amount of idle time to meet the condition. Must be a positive integer. -- _time_unit:_ `number` The unit of time. 0 for ticks, 1 for seconds and 2 for minutes. - ---- -### `"create:unloaded"` -Wait for the chunk the train is in to be unloaded. - ---- -### `"create:powered"` -Wait for the station to be powered with a redstone signal. - ---- -## Items -In Lua, items are represented with an ID and a count. - -```lua -item = { - id = "minecraft:stone", - count = 1, -} -``` - -- _id:_ `string` The ID of the item. -- _count:_ `number` The amount of items in the stack. For the purposes of working with train schedules the count should always be 1. Must be an integer. diff --git a/wiki/Lua-Train-Station.md b/wiki/Lua-Train-Station.md deleted file mode 100644 index c8a39ae45..000000000 --- a/wiki/Lua-Train-Station.md +++ /dev/null @@ -1,179 +0,0 @@ -| Method | Description | -|-----------------------------------------------------------------|----------------------------------------------------| -| [`assemble()`](#assemble) | Assembles a new train at the station | -| [`disassemble()`](#disassemble) | Disassembles the currently present train | -| [`setAssemblyMode(assemblyMode)`](#setAssemblyModeassemblyMode) | Sets the station's assembly mode | -| [`isInAssemblyMode()`](#isInAssemblyMode) | Whether the station is in assembly mode | -| [`getStationName()`](#getStationName) | Gets the station's current name | -| [`setStationName(name)`](#setStationNamename) | Sets the station's name | -| [`isTrainPresent()`](#isTrainPresent) | Whether a train is present at the station | -| [`isTrainImminent()`](#isTrainImminent) | Whether a train is imminent to the station | -| [`isTrainEnroute()`](#isTrainEnroute) | Whether a train is enroute to the station | -| [`getTrainName()`](#getTrainName) | Gets the currently present train's name | -| [`setTrainName(name)`](#setTrainNamename) | Sets the currently present train's name | -| [`hasSchedule()`](#hasSchedule) | Whether the currently present train has a schedule | -| [`getSchedule()`](#getSchedule) | Gets the currently present train's schedule | -| [`setSchedule(schedule)`](#setScheduleschedule) | Sets the currently present train's schedule | - ---- -### `assemble()` -Assembles a new train at the station. The station must be in assembly mode prior to calling this function. -This function also causes the station to exit assembly mode after the train is done assembing. - -**Throws** -- If the station is not in assembly mode. -- If the station is not connected to a track. -- If the train failed to assemble. -- If the station failed to exit assembly mode. - -**See also** -- [`setAssemblyMode(assemblyMode)`](#setAssemblyModeassemblyMode) To set the assembly mode of the station. - ---- -### `disassemble()` -Disassembles the station's currently present train. The station must not be in assembly mode. - -**Throws** -- If the station is in assembly mode. -- If the station is not connected to a track. -- If there is currently no train present at the station. -- If the train failed to disassemble. - -**See also** -- [`setAssemblyMode(assemblyMode)`](#setAssemblyModeassemblyMode) To set the assembly mode of the station. - ---- -### `setAssemblyMode(assemblyMode)` -Sets the station's assembly mode. - -**Parameters** -- _assemblyMode:_ `boolean` Whether the station should be in assembly mode. - -**Throws** -- If the station fails to enter or exit assembly mode. -- If the station is not connected to a track. - ---- -### `isInAssemblyMode()` -Checks whether the station is in assembly mode. - -**Returns** -- `boolean` Whether the station is in assembly mode. - ---- -### `getStationName()` -Gets the station's current name. - -**Returns** -- `string` The station's current name. - -**Throws** -- If the station is not connected to a track. - ---- -### `setStationName(name)` -Sets the station's name. - -**Parameters** -- _name:_ `string` What to set the station's name to. - -**Throws** -- If the station name fails to be set. -- If the station is not connected to a track. - ---- -### `isTrainPresent()` -Checks whether a train is currently present at the station. - -**Returns** -- `boolean` Whether a train is present at the station. - -**Throws** -- If the station is not connected to a track. - ---- -### `isTrainImminent()` -Checks whether a train is imminently arriving at the station. -Imminent is defined as being within 30 blocks of the station. -This will not be true if the train has arrived and stopped at the station. - -**Returns** -- `boolean` Whether a train is imminent to the station. - -**Throws** -- If the station is not connected to a track. - -**See also** -- [`isTrainPresent()`](#isTrainPresent) To check if a train is present at the station. - ---- -### `isTrainEnroute()` -Checks whether a train is enroute and navigating to the station. - -**Returns** -- `boolean` Whether a train is enroute to the station. - -**Throws** -- If the station is not connected to a track. - ---- -### `getTrainName()` -Gets the currently present train's name. - -**Returns** -- `string` The currently present train's name. - -**Throws** -- If the station is not connected to a track. -- If there is currently no train present at the station. - ---- -### `setTrainName(name)` -Sets the currently present train's name. - -**Parameters** -- _name:_ `string` What to set the currently present train's name to. - -**Throws** -- If the station is not connected to a track. -- If there is currently no train present at the station. - ---- -### `hasSchedule()` -Checks whether the currently present train has a schedule. - -**Returns** -- `boolean` Whether the currently present train has a schedule. - -**Throws** -- If the station is not connected to a track. -- If there is currently no train present at the station. - ---- -### `getSchedule()` -Gets the currently present train's schedule. - -**Returns** -- `table` The train's schedule - -**Throws** -- If the station is not connected to a track. -- If there is currently no train present at the station. -- If the present train doesn't have a schedule. - -**See also** -- [Lua Train Schedules](#Lua-Train-Schedules) How train schedules are represented in Lua. - ---- -### `setSchedule(schedule)` -Sets the currently present train's schedule. This will overwrite the currently set schedule. - -**Parameters** -- _schedule:_ `table` The schedule to set the present train to. - -**Throws** -- If the station is not connected to a track. -- If there is currently no train present at the station. - -**See also** -- [Lua Train Schedules](#Lua-Train-Schedules) How train schedules are represented in Lua. diff --git a/wiki/README.md b/wiki/README.md deleted file mode 100644 index cf91b97ae..000000000 --- a/wiki/README.md +++ /dev/null @@ -1,2 +0,0 @@ -Just before this PR is about to be merged this /wiki folder will be removed from the PR and the pages will be added to -the wiki section of the Create GitHub under API Reference