mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-01-27 05:18:08 +01:00
merge mc1.19/dev
This commit is contained in:
commit
c847680b46
52 changed files with 1071 additions and 1387 deletions
138
## Changes.md
138
## Changes.md
|
@ -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
|
|
@ -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}")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"values": [
|
||||
{
|
||||
"id": "occultism:spirit_trade",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"id": "occultism:ritual",
|
||||
"required": false
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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<ResourceLocation> 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()
|
||||
|
|
|
@ -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<RecipeSerializer<?>> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1376,7 +1376,7 @@ public abstract class Contraption {
|
|||
return blocks.values();
|
||||
}
|
||||
|
||||
public Collection<BlockEntity> getSpecialRenderedTEs() {
|
||||
public Collection<BlockEntity> getSpecialRenderedBEs() {
|
||||
return specialRenderedBlockEntities;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,15 +31,14 @@ public class CopycatBarsModel extends CopycatModel {
|
|||
protected List<BakedQuad> getCroppedQuads(BlockState state, Direction side, RandomSource rand, BlockState material,
|
||||
ModelData wrappedData, RenderType renderType) {
|
||||
BakedModel model = getModelOf(material);
|
||||
List<BakedQuad> templateQuads = model.getQuads(material, null, rand, wrappedData, renderType);
|
||||
List<BakedQuad> superQuads = originalModel.getQuads(state, side, rand, wrappedData, renderType);
|
||||
List<BakedQuad> 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<BakedQuad> 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<BakedQuad> quads = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < superQuads.size(); i++) {
|
||||
BakedQuad quad = superQuads.get(i);
|
||||
TextureAtlasSprite original = quad.getSprite();
|
||||
|
|
|
@ -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<BakedQuad> getCroppedQuads(BlockState state, Direction side, RandomSource rand,
|
||||
BlockState material, ModelData wrappedData, RenderType renderType);
|
||||
|
||||
|
|
|
@ -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<TransportedItemStack> {
|
|||
public float prevBeltPosition;
|
||||
public float prevSideOffset;
|
||||
|
||||
public FanProcessing.Type processedBy;
|
||||
public FanProcessingType processedBy;
|
||||
public int processingTime;
|
||||
|
||||
public TransportedItemStack(ItemStack stack) {
|
||||
|
|
|
@ -16,4 +16,9 @@ public abstract class AbstractCrushingRecipe extends ProcessingRecipe<RecipeWrap
|
|||
protected int getMaxInputCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSpecifyDuration() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ import com.simibubi.create.AllTags;
|
|||
import com.simibubi.create.content.decoration.copycat.CopycatBlock;
|
||||
import com.simibubi.create.content.kinetics.belt.behaviour.TransportedItemStackHandlerBehaviour;
|
||||
import com.simibubi.create.content.kinetics.belt.behaviour.TransportedItemStackHandlerBehaviour.TransportedResult;
|
||||
import com.simibubi.create.content.kinetics.fan.FanProcessing.Type;
|
||||
import com.simibubi.create.content.kinetics.fan.processing.AllFanProcessingTypes;
|
||||
import com.simibubi.create.content.kinetics.fan.processing.FanProcessing;
|
||||
import com.simibubi.create.content.kinetics.fan.processing.FanProcessingType;
|
||||
import com.simibubi.create.foundation.advancement.AllAdvancements;
|
||||
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
|
||||
import com.simibubi.create.infrastructure.config.AllConfigs;
|
||||
|
@ -29,7 +31,6 @@ import net.minecraft.world.entity.item.ItemEntity;
|
|||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
@ -49,7 +50,7 @@ public class AirCurrent {
|
|||
public boolean pushing;
|
||||
public float maxDistance;
|
||||
|
||||
protected List<Pair<TransportedItemStackHandlerBehaviour, FanProcessing.Type>> affectedItemHandlers =
|
||||
protected List<Pair<TransportedItemStackHandlerBehaviour, FanProcessingType>> affectedItemHandlers =
|
||||
new ArrayList<>();
|
||||
protected List<Entity> 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<TransportedItemStackHandlerBehaviour, Type> pair : affectedItemHandlers) {
|
||||
for (Pair<TransportedItemStackHandlerBehaviour, FanProcessingType> 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<SplashingRecipe> recipe = AllRecipeTypes.SPLASHING.find(SPLASHING_WRAPPER, world);
|
||||
return recipe.isPresent();
|
||||
}
|
||||
|
||||
public static boolean isHauntable(ItemStack stack, Level world) {
|
||||
HAUNTING_WRAPPER.setItem(0, stack);
|
||||
Optional<HauntingRecipe> 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<ItemStack> 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<ItemStack> stacks = process(transported.stack, type, world);
|
||||
if (stacks == null)
|
||||
return ignore;
|
||||
|
||||
List<TransportedItemStack> 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<ItemStack> process(ItemStack stack, Type type, Level world) {
|
||||
if (type == Type.SPLASHING) {
|
||||
SPLASHING_WRAPPER.setItem(0, stack);
|
||||
Optional<SplashingRecipe> 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<HauntingRecipe> 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> smokingRecipe = world.getRecipeManager()
|
||||
.getRecipeFor(RecipeType.SMOKING, RECIPE_WRAPPER, world);
|
||||
|
||||
if (type == Type.BLASTING) {
|
||||
RECIPE_WRAPPER.setItem(0, stack);
|
||||
Optional<? extends AbstractCookingRecipe> 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<SmokingRecipe> 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> smeltingRecipe = level.getRecipeManager()
|
||||
.getRecipeFor(RecipeType.SMELTING, RECIPE_WRAPPER, level);
|
||||
|
||||
if (smeltingRecipe.isPresent())
|
||||
return true;
|
||||
|
||||
RECIPE_WRAPPER.setItem(0, stack);
|
||||
Optional<BlastingRecipe> 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, FanProcessingType> LEGACY_NAME_MAP;
|
||||
|
||||
static {
|
||||
Object2ReferenceOpenHashMap<String, FanProcessingType> 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 extends FanProcessingType> 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<ItemStack> 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> smeltingRecipe = level.getRecipeManager()
|
||||
.getRecipeFor(RecipeType.SMELTING, RECIPE_WRAPPER, level);
|
||||
|
||||
if (smeltingRecipe.isPresent())
|
||||
return true;
|
||||
|
||||
RECIPE_WRAPPER.setItem(0, stack);
|
||||
Optional<BlastingRecipe> blastingRecipe = level.getRecipeManager()
|
||||
.getRecipeFor(RecipeType.BLASTING, RECIPE_WRAPPER, level);
|
||||
|
||||
if (blastingRecipe.isPresent())
|
||||
return true;
|
||||
|
||||
return !stack.getItem()
|
||||
.isFireResistant();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<ItemStack> process(ItemStack stack, Level level) {
|
||||
RECIPE_WRAPPER.setItem(0, stack);
|
||||
Optional<SmokingRecipe> smokingRecipe = level.getRecipeManager()
|
||||
.getRecipeFor(RecipeType.SMOKING, RECIPE_WRAPPER, level);
|
||||
|
||||
RECIPE_WRAPPER.setItem(0, stack);
|
||||
Optional<? extends AbstractCookingRecipe> 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<HauntingRecipe> recipe = AllRecipeTypes.HAUNTING.find(HAUNTING_WRAPPER, level);
|
||||
return recipe.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<ItemStack> process(ItemStack stack, Level level) {
|
||||
HAUNTING_WRAPPER.setItem(0, stack);
|
||||
Optional<HauntingRecipe> 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<SmokingRecipe> recipe = level.getRecipeManager()
|
||||
.getRecipeFor(RecipeType.SMOKING, RECIPE_WRAPPER, level);
|
||||
return recipe.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<ItemStack> process(ItemStack stack, Level level) {
|
||||
RECIPE_WRAPPER.setItem(0, stack);
|
||||
Optional<SmokingRecipe> 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<SplashingRecipe> recipe = AllRecipeTypes.SPLASHING.find(SPLASHING_WRAPPER, level);
|
||||
return recipe.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<ItemStack> process(ItemStack stack, Level level) {
|
||||
SPLASHING_WRAPPER.setItem(0, stack);
|
||||
Optional<SplashingRecipe> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<ItemStack> 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<ItemStack> stacks = type.process(transported.stack, world);
|
||||
if (stacks == null)
|
||||
return ignore;
|
||||
|
||||
List<TransportedItemStack> 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;
|
||||
}
|
||||
}
|
|
@ -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<ItemStack> 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);
|
||||
}
|
||||
}
|
|
@ -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<ResourceLocation, FanProcessingType> TYPES = new Object2ReferenceOpenHashMap<>();
|
||||
private static final Map<FanProcessingType, ResourceLocation> IDS = new Reference2ObjectOpenHashMap<>();
|
||||
private static final List<FanProcessingType> SORTED_TYPES = new ReferenceArrayList<>();
|
||||
private static final List<FanProcessingType> 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<FanProcessingType> getSortedTypesView() {
|
||||
return SORTED_TYPES_VIEW;
|
||||
}
|
||||
}
|
|
@ -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<FanProcessing.HauntingWrapper> {
|
||||
public class HauntingRecipe extends ProcessingRecipe<HauntingWrapper> {
|
||||
|
||||
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<FanProcessing.HauntingWrapp
|
|||
return 12;
|
||||
}
|
||||
|
||||
public static class HauntingWrapper extends RecipeWrapper {
|
||||
public HauntingWrapper() {
|
||||
super(new ItemStackHandler(1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +1,18 @@
|
|||
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.FanProcessing.SplashingWrapper;
|
||||
import com.simibubi.create.content.kinetics.fan.processing.SplashingRecipe.SplashingWrapper;
|
||||
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 SplashingRecipe extends ProcessingRecipe<FanProcessing.SplashingWrapper> {
|
||||
public class SplashingRecipe extends ProcessingRecipe<SplashingWrapper> {
|
||||
|
||||
public SplashingRecipe(ProcessingRecipeParams params) {
|
||||
super(AllRecipeTypes.SPLASHING, params);
|
||||
|
@ -34,4 +36,10 @@ public class SplashingRecipe extends ProcessingRecipe<FanProcessing.SplashingWra
|
|||
return 12;
|
||||
}
|
||||
|
||||
public static class SplashingWrapper extends RecipeWrapper {
|
||||
public SplashingWrapper() {
|
||||
super(new ItemStackHandler(1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -47,6 +47,11 @@ public class CuttingRecipe extends ProcessingRecipe<RecipeWrapper> implements IA
|
|||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSpecifyDuration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAssemblyIngredients(List<Ingredient> list) {}
|
||||
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -208,6 +208,11 @@ public class BasinRecipe extends ProcessingRecipe<SmartInventory> {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSpecifyDuration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(SmartInventory inv, @Nonnull Level worldIn) {
|
||||
return false;
|
||||
|
|
|
@ -71,7 +71,7 @@ public abstract class ProcessingRecipe<T extends Container> implements Recipe<T>
|
|||
}
|
||||
|
||||
protected boolean canSpecifyDuration() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int getMaxFluidInputCount() {
|
||||
|
|
|
@ -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"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,19 +24,13 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
|
|||
|
||||
public class SchematicInstances {
|
||||
|
||||
public static final WorldAttached<Cache<Integer, SchematicLevel>> loadedSchematics;
|
||||
|
||||
static {
|
||||
loadedSchematics = new WorldAttached<>($ -> CacheBuilder.newBuilder()
|
||||
private static final WorldAttached<Cache<Integer, SchematicLevel>> 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<Integer, SchematicLevel> map = loadedSchematics.get(world);
|
||||
Cache<Integer, SchematicLevel> map = LOADED_SCHEMATICS.get(world);
|
||||
int hash = getHash(schematic);
|
||||
SchematicLevel ifPresent = map.getIfPresent(hash);
|
||||
if (ifPresent != null)
|
||||
|
|
|
@ -289,9 +289,9 @@ public class CarriageContraption extends Contraption {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<BlockEntity> getSpecialRenderedTEs() {
|
||||
public Collection<BlockEntity> getSpecialRenderedBEs() {
|
||||
if (notInPortal())
|
||||
return super.getSpecialRenderedTEs();
|
||||
return super.getSpecialRenderedBEs();
|
||||
return specialRenderedBEsOutsidePortal;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<RecipeSerializer<?>> {
|
||||
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"));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
"client.BlockDestructionProgressMixin",
|
||||
"client.CameraMixin",
|
||||
"client.EntityContraptionInteractionMixin",
|
||||
"client.FixNormalScalingMixin",
|
||||
"client.GameRendererMixin",
|
||||
"client.HeavyBootsOnPlayerMixin",
|
||||
"client.HumanoidArmorLayerMixin",
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
Loading…
Reference in a new issue