Fantastic

- Encased fans are now directional and only emit air flows on one side
- Reworked particles emitted by fans and in-world item processing
- Air flows can now morph into different particle types along the way
- Air flows are more precise about being blocked or not
- Nerfed sand washing
This commit is contained in:
simibubi 2019-12-12 07:38:44 +01:00
parent c56fd66012
commit a1c37c5af6
35 changed files with 1322 additions and 717 deletions

View file

@ -15,7 +15,6 @@ import com.simibubi.create.modules.contraptions.receivers.CrushingWheelBlock;
import com.simibubi.create.modules.contraptions.receivers.CrushingWheelControllerBlock;
import com.simibubi.create.modules.contraptions.receivers.DrillBlock;
import com.simibubi.create.modules.contraptions.receivers.DrillBlock.DrillHeadBlock;
import com.simibubi.create.modules.contraptions.receivers.EncasedFanBlock;
import com.simibubi.create.modules.contraptions.receivers.HarvesterBlock;
import com.simibubi.create.modules.contraptions.receivers.HarvesterBlock.HarvesterBladeBlock;
import com.simibubi.create.modules.contraptions.receivers.MechanicalMixerBlock;
@ -32,6 +31,7 @@ import com.simibubi.create.modules.contraptions.receivers.constructs.piston.Mech
import com.simibubi.create.modules.contraptions.receivers.constructs.piston.MechanicalPistonHeadBlock;
import com.simibubi.create.modules.contraptions.receivers.constructs.piston.PistonPoleBlock;
import com.simibubi.create.modules.contraptions.receivers.crafter.MechanicalCrafterBlock;
import com.simibubi.create.modules.contraptions.receivers.fan.EncasedFanBlock;
import com.simibubi.create.modules.contraptions.redstone.ContactBlock;
import com.simibubi.create.modules.contraptions.relays.ClutchBlock;
import com.simibubi.create.modules.contraptions.relays.CogWheelBlock;
@ -116,7 +116,7 @@ public enum AllBlocks {
MOTOR(new MotorBlock()),
WATER_WHEEL(new WaterWheelBlock()),
ENCASED_FAN(new EncasedFanBlock()),
ENCASED_FAN_INNER(new RenderUtilityAxisBlock()),
ENCASED_FAN_INNER(new RenderUtilityDirectionalBlock()),
TURNTABLE(new TurntableBlock()),
SHAFT_HALF(new ShaftHalfBlock()),

View file

@ -3,6 +3,7 @@ package com.simibubi.create;
import java.util.function.Supplier;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.modules.contraptions.particle.AirFlowParticle;
import com.simibubi.create.modules.contraptions.particle.RotationIndicatorParticle;
import net.minecraft.client.Minecraft;
@ -18,6 +19,7 @@ import net.minecraftforge.registries.IForgeRegistry;
public enum AllParticles {
ROTATION_INDICATOR(RotationIndicatorParticle.Type::new, RotationIndicatorParticle.Factory::new),
AIR_FLOW(AirFlowParticle.Type::new, AirFlowParticle.Factory::new),
;

View file

@ -8,7 +8,7 @@ import com.simibubi.create.modules.contraptions.receivers.CrushingRecipe;
import com.simibubi.create.modules.contraptions.receivers.CuttingRecipe;
import com.simibubi.create.modules.contraptions.receivers.MixingRecipe;
import com.simibubi.create.modules.contraptions.receivers.PressingRecipe;
import com.simibubi.create.modules.contraptions.receivers.SplashingRecipe;
import com.simibubi.create.modules.contraptions.receivers.fan.SplashingRecipe;
import com.simibubi.create.modules.curiosities.placementHandgun.BuilderGunUpgradeRecipe;
import net.minecraft.inventory.IInventory;

View file

@ -13,8 +13,6 @@ import com.simibubi.create.modules.contraptions.receivers.CrushingWheelControlle
import com.simibubi.create.modules.contraptions.receivers.CrushingWheelTileEntity;
import com.simibubi.create.modules.contraptions.receivers.DrillTileEntity;
import com.simibubi.create.modules.contraptions.receivers.DrillTileEntityRenderer;
import com.simibubi.create.modules.contraptions.receivers.EncasedFanTileEntity;
import com.simibubi.create.modules.contraptions.receivers.EncasedFanTileEntityRenderer;
import com.simibubi.create.modules.contraptions.receivers.HarvesterTileEntity;
import com.simibubi.create.modules.contraptions.receivers.HarvesterTileEntityRenderer;
import com.simibubi.create.modules.contraptions.receivers.MechanicalMixerTileEntity;
@ -31,6 +29,8 @@ import com.simibubi.create.modules.contraptions.receivers.constructs.piston.Mech
import com.simibubi.create.modules.contraptions.receivers.constructs.piston.MechanicalPistonTileEntityRenderer;
import com.simibubi.create.modules.contraptions.receivers.crafter.MechanicalCrafterTileEntity;
import com.simibubi.create.modules.contraptions.receivers.crafter.MechanicalCrafterTileEntityRenderer;
import com.simibubi.create.modules.contraptions.receivers.fan.EncasedFanTileEntity;
import com.simibubi.create.modules.contraptions.receivers.fan.EncasedFanTileEntityRenderer;
import com.simibubi.create.modules.contraptions.relays.ClutchTileEntity;
import com.simibubi.create.modules.contraptions.relays.EncasedShaftTileEntity;
import com.simibubi.create.modules.contraptions.relays.EncasedShaftTileEntityRenderer;

View file

@ -13,7 +13,6 @@ import com.simibubi.create.foundation.block.SpriteShifter.SpriteShiftEntry;
import com.simibubi.create.foundation.utility.SuperByteBufferCache;
import com.simibubi.create.modules.contraptions.WrenchModel;
import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer;
import com.simibubi.create.modules.contraptions.receivers.EncasedFanParticleHandler;
import com.simibubi.create.modules.contraptions.receivers.constructs.ContraptionRenderer;
import com.simibubi.create.modules.curiosities.deforester.DeforesterModel;
import com.simibubi.create.modules.curiosities.partialWindows.WindowInABlockModel;
@ -50,7 +49,6 @@ public class CreateClient {
public static SchematicHandler schematicHandler;
public static SchematicHologram schematicHologram;
public static SchematicAndQuillHandler schematicAndQuillHandler;
public static EncasedFanParticleHandler fanParticles;
public static SuperByteBufferCache bufferCache;
public static int renderTicks;
@ -72,7 +70,6 @@ public class CreateClient {
schematicHandler = new SchematicHandler();
schematicHologram = new SchematicHologram();
schematicAndQuillHandler = new SchematicAndQuillHandler();
fanParticles = new EncasedFanParticleHandler();
bufferCache = new SuperByteBufferCache();
bufferCache.registerCompartment(KineticTileEntityRenderer.KINETIC_TILE);

View file

@ -4,6 +4,7 @@ import org.apache.commons.lang3.tuple.Pair;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.common.ForgeConfigSpec.BooleanValue;
import net.minecraftforge.common.ForgeConfigSpec.DoubleValue;
public class CreateClientConfig {
@ -19,15 +20,22 @@ public class CreateClientConfig {
}
public BooleanValue enableTooltips;
public DoubleValue fanParticleDensity;
CreateClientConfig(final ForgeConfigSpec.Builder builder) {
builder.comment("Client-only settings - If you're looking for server/common settings, look inside your worlds serverconfig folder!").push("client");
builder.comment(
"Client-only settings - If you're looking for server/common settings, look inside your worlds serverconfig folder!")
.push("client");
String basePath = "create.config.client.";
String name = "enableTooltips";
enableTooltips = builder.comment("", "Show item descriptions on Shift and controls on Ctrl.")
.translation(basePath + name).define(name, true);
name = "fanParticleDensity";
fanParticleDensity = builder.comment("", "Controls the average amount of fan particles spawned per tick.")
.translation(basePath + name).defineInRange(name, .5D, 0D, 1D);
builder.pop();
}

View file

@ -200,7 +200,7 @@ public class CreateConfig {
name = "fanBlockCheckRate";
fanBlockCheckRate = builder
.comment("", "Game ticks between Fans checking for anything blocking their air flow.")
.translation(basePath + name).defineInRange(name, 100, 20, Integer.MAX_VALUE);
.translation(basePath + name).defineInRange(name, 30, 10, Integer.MAX_VALUE);
name = "fanMaxPushDistance";
fanMaxPushDistance = builder.comment("", "Maximum distance in blocks Fans can push entities.")
@ -208,11 +208,11 @@ public class CreateConfig {
name = "fanMaxPullDistance";
fanMaxPullDistance = builder.comment("", "Maximum distance in blocks from where Fans can pull entities.")
.translation(basePath + name).defineInRange(name, 5, 1, Integer.MAX_VALUE);
.translation(basePath + name).defineInRange(name, 10, 1, Integer.MAX_VALUE);
name = "fanRotationArgmax";
fanRotationArgmax = builder.comment("", "Rotation speed at which the maximum stats of fans are reached.")
.translation(basePath + name).defineInRange(name, 8192, 64, Integer.MAX_VALUE);
.translation(basePath + name).defineInRange(name, 1024, 64, Integer.MAX_VALUE);
name = "generatingFanSpeed";
generatingFanSpeed = builder.comment("", "Rotation speed generated by a vertical fan above fire.")

View file

@ -16,7 +16,7 @@ import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.block.BlockState;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction;
public abstract class ProcessingViaFanCategory<T extends IRecipe<?>> implements IRecipeCategory<T> {
@ -72,12 +72,12 @@ public abstract class ProcessingViaFanCategory<T extends IRecipe<?>> implements
protected BlockState renderFanCasing() {
return AllBlocks.ENCASED_FAN.get().getDefaultState().with(BlockStateProperties.AXIS, Axis.X);
return AllBlocks.ENCASED_FAN.get().getDefaultState().with(BlockStateProperties.FACING, Direction.WEST);
}
protected BlockState renderFanInner() {
return AllBlocks.ENCASED_FAN_INNER.get().getDefaultState().with(BlockStateProperties.AXIS, Axis.X);
return AllBlocks.ENCASED_FAN_INNER.get().getDefaultState().with(BlockStateProperties.FACING, Direction.WEST);
}
public abstract void renderAttachedBlock();

View file

@ -10,7 +10,7 @@ import com.simibubi.create.ScreenResources;
import com.simibubi.create.foundation.gui.ScreenElementRenderer;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.modules.contraptions.base.StochasticOutput;
import com.simibubi.create.modules.contraptions.receivers.SplashingRecipe;
import com.simibubi.create.modules.contraptions.receivers.fan.SplashingRecipe;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.IRecipeLayout;

View file

@ -38,6 +38,11 @@ public class VecHelper {
vec.z + (r.nextFloat() - .5f) * 2 * radius);
}
public static Vec3d planeByNormal(Vec3d vec) {
vec = vec.normalize();
return new Vec3d(1, 1, 1).subtract(Math.abs(vec.x), Math.abs(vec.y), Math.abs(vec.z));
}
public static ListNBT writeNBT(Vec3d vec) {
ListNBT listnbt = new ListNBT();
listnbt.add(new DoubleNBT(vec.x));

View file

@ -0,0 +1,37 @@
package com.simibubi.create.foundation.utility.recipe;
import java.util.function.Predicate;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
/**
* Commonly used Predicates for searching through recipe collections.
*
* @author simibubi
*
*/
public class RecipeConditions {
public static Predicate<IRecipe<?>> isOfType(IRecipeType<?> type, IRecipeType<?>... otherTypes) {
return recipe -> {
IRecipeType<?> recipeType = recipe.getType();
if (recipeType == type)
return true;
for (IRecipeType<?> other : otherTypes)
if (recipeType == other)
return true;
return false;
};
}
public static Predicate<IRecipe<?>> firstIngredientMatches(ItemStack stack) {
return r -> !r.getIngredients().isEmpty() && r.getIngredients().get(0).test(stack);
}
public static Predicate<IRecipe<?>> outputMatchesFilter(ItemStack filter) {
return r -> filter.isEmpty() || ItemStack.areItemsEqual(filter, r.getRecipeOutput());
}
}

View file

@ -0,0 +1,96 @@
package com.simibubi.create.foundation.utility.recipe;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.world.World;
/**
* Utility for searching through a world's recipe collection. Non-dynamic
* conditions can be split off into an initial search for caching intermediate
* results.
*
* @author simibubi
*
*/
public class RecipeFinder {
private static Cache<Object, StartedSearch> cachedSearches = CacheBuilder.newBuilder().build();
public static class StartedSearch {
List<IRecipe<?>> findings;
public StartedSearch(List<IRecipe<?>> findings) {
this.findings = findings;
}
public RecipeStream<IRecipe<?>> search() {
return new RecipeStream<>(findings.stream());
}
public static class RecipeStream<R extends IRecipe<?>> {
Stream<R> stream;
public RecipeStream(Stream<R> stream) {
this.stream = stream;
}
@SuppressWarnings("unchecked")
public <X extends IRecipe<?>> RecipeStream<X> assumeType(IRecipeType<X> type) {
return (RecipeStream<X>) this;
}
public RecipeStream<R> filter(Predicate<R> condition) {
stream.filter(condition);
return this;
}
public List<R> asList() {
return stream.collect(Collectors.toList());
}
}
}
/**
* Find all IRecipes matching the condition predicate. If this search is made
* more than once, using the same object instance as the cacheKey will retrieve
* the cached result from the first time.
*
* @param cacheKey (can be null to prevent the caching)
* @param world
* @param conditions
* @return A started search to continue with more specific conditions.
*/
public static StartedSearch get(@Nullable Object cacheKey, World world, Predicate<IRecipe<?>> conditions) {
if (cacheKey == null)
return startSearch(world, conditions);
try {
return cachedSearches.get(cacheKey, () -> startSearch(world, conditions));
} catch (ExecutionException e) {
e.printStackTrace();
}
return new StartedSearch(Collections.emptyList());
}
private static StartedSearch startSearch(World world, Predicate<? super IRecipe<?>> conditions) {
List<IRecipe<?>> list = world.getRecipeManager().getRecipes().stream().filter(conditions)
.collect(Collectors.toList());
return new StartedSearch(list);
}
}

View file

@ -6,6 +6,7 @@ import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.DirectionProperty;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
@ -23,6 +24,24 @@ public abstract class DirectionalKineticBlock extends KineticBlock {
super.fillStateContainer(builder);
}
public Direction getPreferredFacing(BlockItemUseContext context) {
Direction prefferedSide = null;
for (Direction side : Direction.values()) {
BlockState blockState = context.getWorld().getBlockState(context.getPos().offset(side));
if (blockState.getBlock() instanceof IRotate) {
if (((IRotate) blockState.getBlock()).hasShaftTowards(context.getWorld(), context.getPos().offset(side),
blockState, side.getOpposite()))
if (prefferedSide != null && prefferedSide.getAxis() != side.getAxis()) {
prefferedSide = null;
break;
} else {
prefferedSide = side;
}
}
}
return prefferedSide;
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext context) {
return getDefaultState().with(FACING, context.getFace());

View file

@ -0,0 +1,176 @@
package com.simibubi.create.modules.contraptions.particle;
import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.receivers.fan.EncasedFanTileEntity;
import com.simibubi.create.modules.logistics.InWorldProcessing;
import net.minecraft.block.Blocks;
import net.minecraft.client.particle.IAnimatedSprite;
import net.minecraft.client.particle.IParticleFactory;
import net.minecraft.client.particle.IParticleRenderType;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.SimpleAnimatedParticle;
import net.minecraft.particles.BlockParticleData;
import net.minecraft.particles.ParticleType;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class AirFlowParticle extends SimpleAnimatedParticle {
private EncasedFanTileEntity source;
protected AirFlowParticle(World world, EncasedFanTileEntity source, double x, double y, double z,
IAnimatedSprite sprite) {
super(world, x, y, z, sprite, world.rand.nextFloat() * .5f);
this.source = source;
this.particleScale *= 0.75F;
this.maxAge = 40;
canCollide = false;
selectSprite(7);
Vec3d offset = VecHelper.offsetRandomly(Vec3d.ZERO, world.rand, .25f);
this.setPosition(posX + offset.x, posY + offset.y, posZ + offset.z);
this.prevPosX = posX;
this.prevPosY = posY;
this.prevPosZ = posZ;
setAlphaF(.25f);
}
public IParticleRenderType getRenderType() {
return IParticleRenderType.PARTICLE_SHEET_TRANSLUCENT;
}
@Override
public void tick() {
if (source == null || source.isRemoved()) {
dissipate();
return;
}
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
if (this.age++ >= this.maxAge) {
this.setExpired();
} else {
if (source.airCurrent == null || !source.airCurrent.bounds.grow(.25f).contains(posX, posY, posZ)) {
dissipate();
return;
}
Vec3d directionVec = new Vec3d(source.airCurrent.direction.getDirectionVec());
Vec3d motion = directionVec.scale(1 / 8f);
if (!source.airCurrent.pushing)
motion = motion.scale(-1);
double distance = new Vec3d(posX, posY, posZ).subtract(VecHelper.getCenterOf(source.getPos()))
.mul(directionVec).length() - .5f;
if (distance > source.airCurrent.maxDistance + 1 || distance < -.25f) {
dissipate();
return;
}
motion = motion.scale(source.airCurrent.maxDistance - (distance - 1f)).scale(.5f);
selectSprite((int) MathHelper.clamp((distance / source.airCurrent.maxDistance) * 8 + world.rand.nextInt(4),
0, 7));
morphType(distance);
motionX = motion.x;
motionY = motion.y;
motionZ = motion.z;
if (this.onGround) {
this.motionX *= (double) 0.7F;
this.motionZ *= (double) 0.7F;
}
this.move(this.motionX, this.motionY, this.motionZ);
}
}
public void morphType(double distance) {
InWorldProcessing.Type type = source.airCurrent.getSegmentAt((float) distance);
if (type == InWorldProcessing.Type.SPLASHING) {
setColor(ColorHelper.mixColors(0x4499FF, 0x2277FF, world.rand.nextFloat()));
setAlphaF(1f);
selectSprite(world.rand.nextInt(3));
if (world.rand.nextFloat() < 1 / 32f)
world.addParticle(ParticleTypes.BUBBLE, posX, posY, posZ, motionX * .125f, motionY * .125f,
motionZ * .125f);
if (world.rand.nextFloat() < 1 / 32f)
world.addParticle(ParticleTypes.BUBBLE_POP, posX, posY, posZ, motionX * .125f, motionY * .125f,
motionZ * .125f);
}
if (type == InWorldProcessing.Type.SMOKING) {
setColor(ColorHelper.mixColors(0x0, 0x555555, world.rand.nextFloat()));
setAlphaF(1f);
selectSprite(world.rand.nextInt(3));
if (world.rand.nextFloat() < 1 / 32f)
world.addParticle(ParticleTypes.SMOKE, posX, posY, posZ, motionX * .125f, motionY * .125f,
motionZ * .125f);
if (world.rand.nextFloat() < 1 / 32f)
world.addParticle(ParticleTypes.LARGE_SMOKE, posX, posY, posZ, motionX * .125f, motionY * .125f,
motionZ * .125f);
}
if (type == InWorldProcessing.Type.BLASTING) {
setColor(ColorHelper.mixColors(0xFF4400, 0xFF8855, world.rand.nextFloat()));
setAlphaF(.5f);
selectSprite(world.rand.nextInt(3));
if (world.rand.nextFloat() < 1 / 32f)
world.addParticle(ParticleTypes.FLAME, posX, posY, posZ, motionX * .25f, motionY * .25f,
motionZ * .25f);
if (world.rand.nextFloat() < 1 / 16f)
world.addParticle(new BlockParticleData(ParticleTypes.BLOCK, Blocks.LAVA.getDefaultState()), posX, posY,
posZ, motionX * .25f, motionY * .25f, motionZ * .25f);
}
if (type == null) {
setColor(0xEEEEEE);
setAlphaF(.25f);
setSize(.2f, .2f);
}
}
private void dissipate() {
setExpired();
}
public int getBrightnessForRender(float partialTick) {
BlockPos blockpos = new BlockPos(this.posX, this.posY, this.posZ);
return this.world.isBlockPresent(blockpos) ? this.world.getCombinedLight(blockpos, 0) : 0;
}
private void selectSprite(int index) {
setSprite(field_217584_C.get(index, 8));
}
public static class Type extends ParticleType<AirFlowParticleData> {
public Type() {
super(false, AirFlowParticleData.DESERIALIZER);
}
}
public static class Factory implements IParticleFactory<AirFlowParticleData> {
private final IAnimatedSprite spriteSet;
public Factory(IAnimatedSprite animatedSprite) {
this.spriteSet = animatedSprite;
}
public Particle makeParticle(AirFlowParticleData data, World worldIn, double x, double y, double z,
double xSpeed, double ySpeed, double zSpeed) {
TileEntity te = worldIn.getTileEntity(new BlockPos(data.posX, data.posY, data.posZ));
if (!(te instanceof EncasedFanTileEntity))
te = null;
return new AirFlowParticle(worldIn, (EncasedFanTileEntity) te, x, y, z, this.spriteSet);
}
}
}

View file

@ -0,0 +1,64 @@
package com.simibubi.create.modules.contraptions.particle;
import java.util.Locale;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.simibubi.create.AllParticles;
import net.minecraft.network.PacketBuffer;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleType;
import net.minecraft.util.math.Vec3i;
public class AirFlowParticleData implements IParticleData {
public static final IParticleData.IDeserializer<AirFlowParticleData> DESERIALIZER = new IParticleData.IDeserializer<AirFlowParticleData>() {
public AirFlowParticleData deserialize(ParticleType<AirFlowParticleData> particleTypeIn, StringReader reader)
throws CommandSyntaxException {
reader.expect(' ');
int x = reader.readInt();
reader.expect(' ');
int y = reader.readInt();
reader.expect(' ');
int z = reader.readInt();
return new AirFlowParticleData(x, y, z);
}
public AirFlowParticleData read(ParticleType<AirFlowParticleData> particleTypeIn, PacketBuffer buffer) {
return new AirFlowParticleData(buffer.readInt(), buffer.readInt(), buffer.readInt());
}
};
final int posX;
final int posY;
final int posZ;
public AirFlowParticleData(Vec3i pos) {
this(pos.getX(), pos.getY(), pos.getZ());
}
public AirFlowParticleData(int posX, int posY, int posZ) {
this.posX = posX;
this.posY = posY;
this.posZ = posZ;
}
@Override
public ParticleType<?> getType() {
return AllParticles.AIR_FLOW.get();
}
@Override
public void write(PacketBuffer buffer) {
buffer.writeInt(posX);
buffer.writeInt(posY);
buffer.writeInt(posZ);
}
@Override
public String getParameters() {
return String.format(Locale.ROOT, "%s %d %d %d", AllParticles.ROTATION_INDICATOR.parameter(), posX, posY, posZ);
}
}

View file

@ -1,71 +0,0 @@
package com.simibubi.create.modules.contraptions.receivers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.modules.logistics.InWorldProcessing;
import com.simibubi.create.modules.logistics.InWorldProcessing.Type;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class EncasedFanBeltHandler {
// Fans need to be aware of belt TEs within their range
// all belts are handled equally
// requires ref to controller and index
static List<BeltTileEntity> findBelts(EncasedFanTileEntity fan) {
if (fan.getSpeed() == 0)
return Collections.emptyList();
List<BeltTileEntity> belts = new ArrayList<>();
AxisAlignedBB searchBB = fan.frontBB.shrink(.25).contract(0, 0, 0).expand(0, -.25f, 0);
BlockPos.getAllInBox((int) searchBB.minX, (int) searchBB.minY, (int) searchBB.minZ, (int) searchBB.maxX - 1,
(int) searchBB.maxY - 1, (int) searchBB.maxZ - 1)
.filter(p -> AllBlocks.BELT.typeOf(fan.getWorld().getBlockState(p))).forEach(p -> {
TileEntity te = fan.getWorld().getTileEntity(p);
if (te == null || !(te instanceof BeltTileEntity))
return;
belts.add((BeltTileEntity) te);
});
return belts;
}
static void tickBelts(EncasedFanTileEntity fan, List<BeltTileEntity> belts) {
Type processingType = fan.getProcessingType();
if (processingType == null)
return;
for (BeltTileEntity belt : belts) {
BeltTileEntity controller = belt.getControllerTE();
if (controller == null)
continue;
World world = belt.getWorld();
controller.getInventory().forEachWithin(belt.index + .5f, .5f, (transported) -> {
if (world.rand.nextInt(4) == 0 && world.isRemote) {
Vec3d vec = controller.getInventory().getVectorForOffset(transported.beltPosition);
if (processingType == Type.BLASTING)
world.addParticle(ParticleTypes.LARGE_SMOKE, vec.x, vec.y + .25f, vec.z, 0, 1 / 16f, 0);
if (processingType == Type.SMOKING)
world.addParticle(ParticleTypes.CLOUD, vec.x, vec.y + .25f, vec.z, 0, 1 / 16f, 0);
if (processingType == Type.SPLASHING)
world.addParticle(ParticleTypes.BUBBLE_POP, vec.x + (world.rand.nextFloat() - .5f) * .5f,
vec.y + .25f, vec.z + (world.rand.nextFloat() - .5f) * .5f, 0, 1 / 16f, 0);
}
if (world.isRemote)
return null;
return InWorldProcessing.applyProcessing(transported, belt, processingType);
});
}
}
}

View file

@ -1,121 +0,0 @@
package com.simibubi.create.modules.contraptions.receivers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.particles.BlockParticleData;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.particles.RedstoneParticleData;
import net.minecraft.util.Direction;
import net.minecraft.util.math.Vec3i;
public class EncasedFanParticleHandler {
public final Map<Block, List<FanEffect>> effects = new HashMap<>();
public EncasedFanParticleHandler() {
initEffects();
}
private void initEffects() {
List<FanEffect> standardFX = new ArrayList<>(2);
standardFX.add(new FanEffect(ParticleTypes.BUBBLE_POP, 1 / 4f, 1 / 4f, 1 / 8f, 1));
standardFX.add(new FanEffect(new RedstoneParticleData(1, 1, 1, 1), 1 / 2f, 1 / 32f, 1/16f, 512f));
effects.put(Blocks.AIR, standardFX);
List<FanEffect> waterFX = new ArrayList<>(2);
waterFX.add(new FanEffect(new BlockParticleData(ParticleTypes.BLOCK, Blocks.WATER.getDefaultState()), 1 / 4f,
1 / 2f, 1 / 4f, 1));
waterFX.add(new FanEffect(ParticleTypes.SPLASH, 1 / 4f, 1 / 2f, 0.5f, 1));
effects.put(Blocks.WATER, waterFX);
List<FanEffect> fireFX = new ArrayList<>(2);
fireFX.add(new FanEffect(ParticleTypes.LARGE_SMOKE, 1 / 4f, 1 / 8f, 0.125f, .5f));
fireFX.add(new FanEffect(ParticleTypes.FLAME, 1 / 4f, 1 / 8f, 1 / 32f, 1 / 256f));
effects.put(Blocks.FIRE, fireFX);
List<FanEffect> lavaFX = new ArrayList<>(3);
lavaFX.add(new FanEffect(new BlockParticleData(ParticleTypes.BLOCK, Blocks.LAVA.getDefaultState()), 1 / 4f,
1 / 2f, 1 / 4f, 1));
lavaFX.add(new FanEffect(ParticleTypes.LAVA, 1 / 2f, 1 / 32f, 0, .25f));
lavaFX.add(new FanEffect(ParticleTypes.FLAME, 1 / 4f, 1 / 32f, 1 / 32f, 1 / 256f));
effects.put(Blocks.LAVA, lavaFX);
}
public void makeParticles(EncasedFanTileEntity te) {
Direction direction = te.getAirFlow();
Vec3i directionVec = direction.getDirectionVec();
boolean hasFx = false;
BlockState frontBlock = te.frontBlock;
if (frontBlock != null) {
if (effects.containsKey(frontBlock.getBlock())) {
hasFx = true;
for (FanEffect fx : effects.get(frontBlock.getBlock()))
fx.render(directionVec, true, te);
}
}
if (!hasFx)
for (FanEffect fx : effects.get(Blocks.AIR))
fx.render(directionVec, true, te);
for (FanEffect fx : effects.get(Blocks.AIR))
fx.render(directionVec, false, te);
}
protected static class FanEffect {
private IParticleData particle;
private float density;
private float chance;
private float spread;
private float speed;
private Random r;
public FanEffect(IParticleData particle, float density, float chance, float spread, float speed) {
r = new Random();
this.particle = particle;
this.density = density;
this.chance = chance;
this.spread = spread;
this.speed = speed;
}
public void render(Vec3i directionVec, boolean front, EncasedFanTileEntity te) {
render(directionVec, front ? .5f : -te.pullDistance, front ? te.pushDistance : -.5f, te);
}
private void render(Vec3i directionVec, float start, float end, EncasedFanTileEntity te) {
float x = directionVec.getX();
float y = directionVec.getY();
float z = directionVec.getZ();
float speed = this.speed * Math.abs(te.getSpeed()) / 512f;
for (float offset = start; offset < end; offset += density) {
if (r.nextFloat() > chance)
continue;
float xs = rollOffset() * spread;
float ys = rollOffset() * spread;
float zs = rollOffset() * spread;
float xs2 = rollOffset() * spread;
float ys2 = rollOffset() * spread;
float zs2 = rollOffset() * spread;
te.getWorld().addParticle(particle, te.getPos().getX() + .5f + x * offset + xs2,
te.getPos().getY() + .5f + y * offset + ys2, te.getPos().getZ() + .5f + z * offset + zs2,
x * speed + xs, y * speed + ys, z * speed + zs);
}
}
private float rollOffset() {
return (r.nextFloat() - .5f) * 2;
}
}
}

View file

@ -1,318 +0,0 @@
package com.simibubi.create.modules.contraptions.receivers;
import static com.simibubi.create.CreateConfig.parameters;
import static net.minecraft.state.properties.BlockStateProperties.AXIS;
import static net.minecraft.util.Direction.AxisDirection.NEGATIVE;
import static net.minecraft.util.Direction.AxisDirection.POSITIVE;
import java.util.Collections;
import java.util.List;
import com.simibubi.create.AllBlockTags;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.CreateClient;
import com.simibubi.create.CreateConfig;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.base.GeneratingKineticTileEntity;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.modules.logistics.InWorldProcessing;
import com.simibubi.create.modules.logistics.InWorldProcessing.Type;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
public class EncasedFanTileEntity extends GeneratingKineticTileEntity {
private static DamageSource damageSourceFire = new DamageSource("create.fan_fire").setDifficultyScaled()
.setFireDamage();
private static DamageSource damageSourceLava = new DamageSource("create.fan_lava").setDifficultyScaled()
.setFireDamage();
private static EncasedFanParticleHandler particleHandler;
protected float pushDistance;
protected float pullDistance;
protected AxisAlignedBB frontBB;
protected AxisAlignedBB backBB;
protected int blockCheckCooldown;
protected boolean findFrontBlock;
protected BlockState frontBlock;
protected boolean isGenerator;
protected List<BeltTileEntity> affectedBelts = Collections.emptyList();
public EncasedFanTileEntity() {
super(AllTileEntities.ENCASED_FAN.type);
blockCheckCooldown = -1;
findFrontBlock = true;
frontBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
backBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
particleHandler = CreateClient.fanParticles;
isGenerator = false;
}
@Override
public void readClientUpdate(CompoundNBT tag) {
super.readClientUpdate(tag);
updateFrontBlock();
updateBBs();
affectedBelts = EncasedFanBeltHandler.findBelts(this);
}
@Override
public void read(CompoundNBT compound) {
super.read(compound);
isGenerator = compound.getBoolean("Generating");
pushDistance = compound.getFloat("PushDistance");
pullDistance = compound.getFloat("PullDistance");
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.putBoolean("Generating", isGenerator);
compound.putFloat("PushDistance", pushDistance);
compound.putFloat("PullDistance", pullDistance);
return super.write(compound);
}
@Override
public float getAddedStressCapacity() {
return isGenerator ? super.getAddedStressCapacity() : 0;
}
@Override
public float getGeneratedSpeed() {
return isGenerator ? CreateConfig.parameters.generatingFanSpeed.get() : 0;
}
public void updateGenerator() {
boolean shouldGenerate = world.isBlockPowered(pos) && world.isBlockPresent(pos.down()) && blockBelowIsHot();
if (shouldGenerate == isGenerator)
return;
isGenerator = shouldGenerate;
updateGeneratedRotation();
}
public boolean blockBelowIsHot() {
return world.getBlockState(pos.down()).getBlock().isIn(AllBlockTags.FAN_HEATERS.tag);
}
protected void updateReachAndForce() {
if (getWorld() == null)
return;
if (world.isRemote)
return;
float speed = Math.abs(this.getSpeed());
float distanceFactor = Math.min(speed / parameters.fanRotationArgmax.get(), 1);
pushDistance = MathHelper.lerp(distanceFactor, 3, parameters.fanMaxPushDistance.get());
pullDistance = MathHelper.lerp(distanceFactor, 1.5f, parameters.fanMaxPullDistance.get());
Direction direction = getAirFlow();
if (speed != 0) {
for (int distance = 1; distance <= pushDistance; distance++) {
if (!EncasedFanBlock.canAirPassThrough(world, getPos().offset(direction, distance), direction)) {
pushDistance = distance - 1;
break;
}
}
for (int distance = 1; distance <= pullDistance; distance++) {
if (!EncasedFanBlock.canAirPassThrough(world, getPos().offset(direction, -distance), direction)) {
pullDistance = distance - 1;
break;
}
}
updateBBs();
} else {
frontBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
backBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
}
affectedBelts = EncasedFanBeltHandler.findBelts(this);
sendData();
}
protected void updateBBs() {
Direction flow = getAirFlow();
if (flow == null)
return;
Vec3i flowVec = flow.getDirectionVec();
float remainder = pushDistance - (int) pushDistance;
frontBB = new AxisAlignedBB(pos.offset(flow), pos.offset(flow, (int) pushDistance))
.expand(flowVec.getX() * remainder + 1, flowVec.getY() * remainder + 1, flowVec.getZ() * remainder + 1)
.grow(.25f);
remainder = pullDistance - (int) pullDistance;
backBB = new AxisAlignedBB(pos.offset(flow, -(int) pullDistance), pos.offset(flow, -1))
.expand(-flowVec.getX() * remainder + 1, -flowVec.getY() * remainder + 1,
-flowVec.getZ() * remainder + 1)
.grow(.25f);
}
public void updateFrontBlock() {
Direction facing = getAirFlow();
if (facing == null) {
frontBlock = Blocks.AIR.getDefaultState();
return;
}
BlockPos front = pos.offset(facing);
if (world.isBlockPresent(front))
frontBlock = world.getBlockState(front);
updateReachAndForce();
}
public Direction getAirFlow() {
if (getSpeed() == 0)
return null;
return Direction.getFacingFromAxisDirection(getBlockState().get(AXIS), getSpeed() > 0 ? POSITIVE : NEGATIVE);
}
@Override
public void onSpeedChanged() {
updateReachAndForce();
updateFrontBlock();
}
@Override
public void tick() {
super.tick();
if (getSpeed() == 0 || isGenerator)
return;
EncasedFanBeltHandler.tickBelts(this, affectedBelts);
List<Entity> frontEntities = world.getEntitiesWithinAABBExcludingEntity(null, frontBB);
for (Entity entity : frontEntities) {
moveEntity(entity, true);
processEntity(entity);
}
for (Entity entity : world.getEntitiesWithinAABBExcludingEntity(null, backBB)) {
moveEntity(entity, false);
}
if (findFrontBlock) {
findFrontBlock = false;
updateFrontBlock();
}
if (!world.isRemote && blockCheckCooldown-- <= 0) {
blockCheckCooldown = parameters.fanBlockCheckRate.get();
updateReachAndForce();
}
if (world.isRemote) {
particleHandler.makeParticles(this);
return;
}
}
public void processEntity(Entity entity) {
if (InWorldProcessing.isFrozen())
return;
if (entity instanceof ItemEntity) {
if (world.rand.nextInt(4) == 0) {
Type processingType = getProcessingType();
if (processingType == Type.BLASTING)
world.addParticle(ParticleTypes.LARGE_SMOKE, entity.posX, entity.posY + .25f, entity.posZ, 0,
1 / 16f, 0);
if (processingType == Type.SMOKING)
world.addParticle(ParticleTypes.CLOUD, entity.posX, entity.posY + .25f, entity.posZ, 0, 1 / 16f, 0);
if (processingType == Type.SPLASHING)
world.addParticle(ParticleTypes.BUBBLE_POP, entity.posX + (world.rand.nextFloat() - .5f) * .5f,
entity.posY + .25f, entity.posZ + (world.rand.nextFloat() - .5f) * .5f, 0, 1 / 16f, 0);
}
if (world.isRemote)
return;
if (canProcess((ItemEntity) entity))
InWorldProcessing.applyProcessing((ItemEntity) entity, getProcessingType());
} else {
if (getProcessingType() == Type.SMOKING) {
entity.setFire(2);
entity.attackEntityFrom(damageSourceFire, 4);
}
if (getProcessingType() == Type.BLASTING) {
entity.setFire(10);
entity.attackEntityFrom(damageSourceLava, 8);
}
if (getProcessingType() == Type.SPLASHING) {
if (entity.isBurning()) {
entity.extinguish();
world.playSound(null, entity.getPosition(), SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE,
SoundCategory.NEUTRAL, 0.7F,
1.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.4F);
}
}
}
}
protected boolean canProcess() {
return getProcessingType() != null;
}
protected boolean canProcess(ItemEntity entity) {
return canProcess() && InWorldProcessing.canProcess(entity, getProcessingType());
}
protected InWorldProcessing.Type getProcessingType() {
if (frontBlock == null)
return null;
Block block = frontBlock.getBlock();
if (block == Blocks.FIRE)
return Type.SMOKING;
if (block == Blocks.WATER)
return Type.SPLASHING;
if (block == Blocks.LAVA)
return Type.BLASTING;
return null;
}
protected void moveEntity(Entity entity, boolean push) {
if ((entity instanceof ItemEntity) && AllBlocks.BELT.typeOf(world.getBlockState(entity.getPosition()))
&& getAirFlow() != Direction.UP) {
return;
}
Vec3d center = VecHelper.getCenterOf(pos);
Vec3i flow = getAirFlow().getDirectionVec();
float sneakModifier = entity.isSneaking() ? 4096f : 512f;
float acceleration = (float) (getSpeed() * 1 / sneakModifier
/ (entity.getPositionVec().distanceTo(center) / (push ? pushDistance : pullDistance)));
Vec3d previousMotion = entity.getMotion();
float maxAcceleration = 5;
double xIn = MathHelper.clamp(flow.getX() * acceleration - previousMotion.x, -maxAcceleration, maxAcceleration);
double yIn = MathHelper.clamp(flow.getY() * acceleration - previousMotion.y, -maxAcceleration, maxAcceleration);
double zIn = MathHelper.clamp(flow.getZ() * acceleration - previousMotion.z, -maxAcceleration, maxAcceleration);
entity.setMotion(
previousMotion.add(new Vec3d(xIn, yIn, zIn).mul(flow.getX(), flow.getY(), flow.getZ()).scale(1 / 8f)));
entity.fallDistance = 0;
}
}

View file

@ -1,18 +0,0 @@
package com.simibubi.create.modules.contraptions.receivers;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer;
import net.minecraft.block.BlockState;
import net.minecraft.state.properties.BlockStateProperties;
public class EncasedFanTileEntityRenderer extends KineticTileEntityRenderer {
@Override
protected BlockState getRenderedBlockState(KineticTileEntity te) {
return AllBlocks.ENCASED_FAN_INNER.get().getDefaultState().with(BlockStateProperties.AXIS,
te.getBlockState().get(BlockStateProperties.AXIS));
}
}

View file

@ -5,12 +5,13 @@ import static com.simibubi.create.modules.contraptions.receivers.SawBlock.RUNNIN
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllRecipes;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.recipe.RecipeConditions;
import com.simibubi.create.foundation.utility.recipe.RecipeFinder;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.modules.logistics.block.IHaveFilter;
@ -39,6 +40,7 @@ import net.minecraftforge.items.IItemHandler;
public class SawTileEntity extends KineticTileEntity implements IHaveFilter {
private static final Object cuttingRecipesKey = new Object();
public ProcessingInventory inventory;
private int recipeIndex;
private ItemStack filter;
@ -235,7 +237,7 @@ public class SawTileEntity extends KineticTileEntity implements IHaveFilter {
}
private void applyRecipe() {
List<IRecipe<?>> recipes = getRecipes();
List<? extends IRecipe<?>> recipes = getRecipes();
if (recipes.isEmpty())
return;
if (recipeIndex >= recipes.size())
@ -267,14 +269,12 @@ public class SawTileEntity extends KineticTileEntity implements IHaveFilter {
}
private List<IRecipe<?>> getRecipes() {
List<IRecipe<?>> recipes = world.getRecipeManager().getRecipes().parallelStream()
.filter(r -> r.getType() == IRecipeType.STONECUTTING || r.getType() == AllRecipes.Types.CUTTING)
.filter(r -> filter.isEmpty() || ItemStack.areItemsEqual(filter, r.getRecipeOutput()))
.filter(r -> !r.getIngredients().isEmpty()
&& r.getIngredients().get(0).test(inventory.getStackInSlot(0)))
.collect(Collectors.toList());
return recipes;
private List<? extends IRecipe<?>> getRecipes() {
return RecipeFinder
.get(cuttingRecipesKey, world,
RecipeConditions.isOfType(IRecipeType.STONECUTTING, AllRecipes.Types.CUTTING))
.search().filter(RecipeConditions.outputMatchesFilter(filter))
.filter(RecipeConditions.firstIngredientMatches(inventory.getStackInSlot(0))).asList();
}
public void insertItem(ItemEntity entity) {
@ -299,7 +299,7 @@ public class SawTileEntity extends KineticTileEntity implements IHaveFilter {
if (world.isRemote)
return;
List<IRecipe<?>> recipes = getRecipes();
List<? extends IRecipe<?>> recipes = getRecipes();
boolean valid = !recipes.isEmpty();
int time = 100;

View file

@ -0,0 +1,294 @@
package com.simibubi.create.modules.contraptions.receivers.fan;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.CreateClientConfig;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.particle.AirFlowParticleData;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.modules.logistics.InWorldProcessing;
import com.simibubi.create.modules.logistics.InWorldProcessing.Type;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.World;
public class AirCurrent {
private static DamageSource damageSourceFire = new DamageSource("create.fan_fire").setDifficultyScaled()
.setFireDamage();
private static DamageSource damageSourceLava = new DamageSource("create.fan_lava").setDifficultyScaled()
.setFireDamage();
public final EncasedFanTileEntity source;
public AxisAlignedBB bounds = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
public List<AirCurrentSegment> segments = new ArrayList<>();
public Direction direction;
public boolean pushing;
public float maxDistance;
protected List<Pair<BeltTileEntity, InWorldProcessing.Type>> affectedBelts = new ArrayList<>();
protected List<Entity> caughtEntities = new ArrayList<>();
public AirCurrent(EncasedFanTileEntity source) {
this.source = source;
}
public void tick() {
World world = source.getWorld();
Direction facing = direction;
if (world.isRemote) {
float offset = pushing ? 0.5f : maxDistance + .5f;
Vec3d pos = VecHelper.getCenterOf(source.getPos()).add(new Vec3d(facing.getDirectionVec()).scale(offset));
if (world.rand.nextFloat() < CreateClientConfig.instance.fanParticleDensity.get())
world.addParticle(new AirFlowParticleData(source.getPos()), pos.x, pos.y, pos.z, 0, 0, 0);
}
for (Iterator<Entity> iterator = caughtEntities.iterator(); iterator.hasNext();) {
Entity entity = iterator.next();
if (!entity.getBoundingBox().intersects(bounds)) {
iterator.remove();
continue;
}
Vec3d center = VecHelper.getCenterOf(source.getPos());
Vec3i flow = (pushing ? facing : facing.getOpposite()).getDirectionVec();
float sneakModifier = entity.isSneaking() ? 4096f : 512f;
float speed = Math.abs(source.getSpeed());
double entityDistance = entity.getPositionVec().distanceTo(center);
float acceleration = (float) (speed / sneakModifier / (entityDistance / maxDistance));
Vec3d previousMotion = entity.getMotion();
float maxAcceleration = 5;
double xIn = MathHelper.clamp(flow.getX() * acceleration - previousMotion.x, -maxAcceleration,
maxAcceleration);
double yIn = MathHelper.clamp(flow.getY() * acceleration - previousMotion.y, -maxAcceleration,
maxAcceleration);
double zIn = MathHelper.clamp(flow.getZ() * acceleration - previousMotion.z, -maxAcceleration,
maxAcceleration);
entity.setMotion(previousMotion.add(new Vec3d(xIn, yIn, zIn).scale(1 / 8f)));
entity.fallDistance = 0;
if (InWorldProcessing.isFrozen())
return;
entityDistance -= .5f;
InWorldProcessing.Type processingType = getSegmentAt((float) entityDistance);
if (entity instanceof ItemEntity) {
InWorldProcessing.spawnParticlesForProcessing(world, entity.getPositionVec(), processingType);
if (InWorldProcessing.canProcess(((ItemEntity) entity), processingType))
InWorldProcessing.applyProcessing((ItemEntity) entity, processingType);
} else {
if (processingType == InWorldProcessing.Type.SMOKING) {
entity.setFire(2);
entity.attackEntityFrom(damageSourceFire, 2);
}
if (processingType == InWorldProcessing.Type.BLASTING) {
entity.setFire(10);
entity.attackEntityFrom(damageSourceLava, 4);
}
if (processingType == InWorldProcessing.Type.SPLASHING) {
if (entity.isBurning()) {
entity.extinguish();
world.playSound(null, entity.getPosition(), SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE,
SoundCategory.NEUTRAL, 0.7F,
1.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.4F);
}
}
}
}
tickBelts();
}
public void rebuild() {
if (source.getSpeed() == 0) {
maxDistance = 0;
segments.clear();
bounds = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
return;
}
World world = source.getWorld();
BlockPos start = source.getPos();
direction = source.getBlockState().get(BlockStateProperties.FACING);
pushing = source.getAirFlowDirection() == direction;
Vec3d directionVec = new Vec3d(direction.getDirectionVec());
Vec3d planeVec = VecHelper.planeByNormal(directionVec);
// 4 Rays test for holes in the shapes blocking the flow
float offsetDistance = .25f;
Vec3d[] offsets = new Vec3d[] { planeVec.mul(offsetDistance, offsetDistance, offsetDistance),
planeVec.mul(-offsetDistance, -offsetDistance, offsetDistance),
planeVec.mul(offsetDistance, -offsetDistance, -offsetDistance),
planeVec.mul(-offsetDistance, offsetDistance, -offsetDistance), };
maxDistance = source.getMaxDistance();
float limitedDistance = 0;
// Determine the distance of the air flow
Outer: for (int i = 1; i < maxDistance; i++) {
BlockPos currentPos = start.offset(direction, i);
if (!world.isBlockPresent(currentPos))
break;
BlockState state = world.getBlockState(currentPos);
VoxelShape voxelshape = state.getCollisionShape(world, currentPos, ISelectionContext.dummy());
for (Vec3d offset : offsets) {
Vec3d rayStart = VecHelper.getCenterOf(currentPos).subtract(directionVec.scale(.5f + 1 / 32f))
.add(offset);
Vec3d rayEnd = rayStart.add(directionVec.scale(1 + 1 / 32f));
BlockRayTraceResult blockraytraceresult = world.rayTraceBlocks(rayStart, rayEnd, currentPos, voxelshape,
state);
if (blockraytraceresult == null)
continue Outer;
double distance = i - 1 + blockraytraceresult.getHitVec().distanceTo(rayStart);
if (limitedDistance < distance)
limitedDistance = (float) distance;
}
maxDistance = limitedDistance;
break;
}
// Determine segments with transported fluids/gases
AirCurrentSegment currentSegment = new AirCurrentSegment();
segments.clear();
currentSegment.startOffset = 0;
InWorldProcessing.Type type = null;
int limit = (int) (maxDistance + .5f);
int searchStart = pushing ? 0 : limit;
int searchEnd = pushing ? limit : 0;
int searchStep = pushing ? 1 : -1;
for (int i = searchStart; i * searchStep <= searchEnd * searchStep; i += searchStep) {
BlockPos currentPos = start.offset(direction, i);
InWorldProcessing.Type newType = InWorldProcessing.Type.byBlock(world, currentPos);
if (newType != null)
type = newType;
if (currentSegment.type != type || currentSegment.startOffset == 0) {
currentSegment.endOffset = i;
if (currentSegment.startOffset != 0)
segments.add(currentSegment);
currentSegment = new AirCurrentSegment();
currentSegment.startOffset = i;
currentSegment.type = type;
}
}
currentSegment.endOffset = searchEnd + searchStep;
segments.add(currentSegment);
// Build Bounding Box
if (maxDistance < 0.25f)
bounds = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
else {
float factor = maxDistance - 1;
Vec3d scale = directionVec.scale(factor);
if (factor > 0)
bounds = new AxisAlignedBB(start.offset(direction)).expand(scale);
else {
bounds = new AxisAlignedBB(start.offset(direction)).contract(scale.x, scale.y, scale.z).offset(scale);
}
}
findAffectedBelts();
}
public void findEntities() {
caughtEntities.clear();
caughtEntities = source.getWorld().getEntitiesWithinAABBExcludingEntity(null, bounds);
}
public void findAffectedBelts() {
World world = source.getWorld();
BlockPos start = source.getPos();
affectedBelts.clear();
for (int i = 0; i < maxDistance + 1; i++) {
Type type = getSegmentAt(i);
if (type != null) {
BlockPos pos = start.offset(direction, i);
TileEntity te = world.getTileEntity(pos);
if (te != null && (te instanceof BeltTileEntity))
affectedBelts.add(Pair.of((BeltTileEntity) te, type));
if (direction.getAxis().isVertical())
continue;
pos = pos.down();
te = world.getTileEntity(pos);
if (te == null || !(te instanceof BeltTileEntity))
continue;
affectedBelts.add(Pair.of((BeltTileEntity) te, type));
}
}
}
public void tickBelts() {
for (Pair<BeltTileEntity, Type> pair : affectedBelts) {
BeltTileEntity belt = pair.getKey();
InWorldProcessing.Type processingType = pair.getRight();
BeltTileEntity controller = belt.getControllerTE();
if (controller == null)
continue;
World world = belt.getWorld();
controller.getInventory().forEachWithin(belt.index + .5f, .5f, (transported) -> {
InWorldProcessing.spawnParticlesForProcessing(world,
controller.getInventory().getVectorForOffset(transported.beltPosition), processingType);
if (world.isRemote)
return null;
return InWorldProcessing.applyProcessing(transported, belt, processingType);
});
}
}
public void writeToNBT(CompoundNBT nbt) {
}
public void readFromNBT(CompoundNBT nbt) {
}
public InWorldProcessing.Type getSegmentAt(float offset) {
for (AirCurrentSegment airCurrentSegment : segments) {
if (offset > airCurrentSegment.endOffset && pushing)
continue;
if (offset < airCurrentSegment.endOffset && !pushing)
continue;
return airCurrentSegment.type;
}
return null;
}
public static class AirCurrentSegment {
InWorldProcessing.Type type;
int startOffset;
int endOffset;
}
}

View file

@ -1,11 +1,11 @@
package com.simibubi.create.modules.contraptions.receivers;
package com.simibubi.create.modules.contraptions.receivers.fan;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.block.IWithTileEntity;
import com.simibubi.create.modules.contraptions.relays.EncasedShaftBlock;
import com.simibubi.create.modules.contraptions.base.DirectionalKineticBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
@ -16,7 +16,11 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
public class EncasedFanBlock extends EncasedShaftBlock implements IWithTileEntity<EncasedFanTileEntity> {
public class EncasedFanBlock extends DirectionalKineticBlock implements IWithTileEntity<EncasedFanTileEntity> {
public EncasedFanBlock() {
super(Properties.from(Blocks.ANDESITE));
}
@Override
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
@ -33,7 +37,7 @@ public class EncasedFanBlock extends EncasedShaftBlock implements IWithTileEntit
boolean isMoving) {
notifyFanTile(worldIn, pos);
if (worldIn.isRemote || state.get(AXIS).isHorizontal())
if (worldIn.isRemote || getRotationAxis(state).isHorizontal())
return;
withTileEntityDo(worldIn, pos, EncasedFanTileEntity::updateGenerator);
@ -41,20 +45,15 @@ public class EncasedFanBlock extends EncasedShaftBlock implements IWithTileEntit
@Override
public BlockState getStateForPlacement(BlockItemUseContext context) {
if (context.isPlacerSneaking())
return super.getStateForPlacement(context);
BlockState blockState = context.getWorld()
.getBlockState(context.getPos().offset(context.getFace().getOpposite()));
if (AllBlocks.ENCASED_FAN.typeOf(blockState))
return getDefaultState().with(AXIS, blockState.get(AXIS));
Axis preferred = getPreferredAxis(context);
if (preferred != null)
return getDefaultState().with(AXIS, preferred);
return super.getStateForPlacement(context);
Direction preferredFacing = getPreferredFacing(context);
if (preferredFacing == null)
preferredFacing = context.getNearestLookingDirection();
return getDefaultState().with(FACING,
context.isPlacerSneaking() ? preferredFacing : preferredFacing.getOpposite());
}
protected void notifyFanTile(IWorld world, BlockPos pos) {
withTileEntityDo(world, pos, EncasedFanTileEntity::updateFrontBlock);
withTileEntityDo(world, pos, EncasedFanTileEntity::blockInFrontChanged);
}
@Override
@ -62,11 +61,19 @@ public class EncasedFanBlock extends EncasedShaftBlock implements IWithTileEntit
return BlockRenderLayer.CUTOUT;
}
public static boolean canAirPassThrough(World world, BlockPos pos, Direction direction) {
if (!world.isBlockPresent(pos))
return true;
BlockState state = world.getBlockState(pos);
return !Block.hasSolidSide(state, world, pos, direction.getOpposite());
@Override
public Axis getRotationAxis(BlockState state) {
return state.get(FACING).getAxis();
}
@Override
protected boolean hasStaticPart() {
return true;
}
@Override
public boolean hasShaftTowards(World world, BlockPos pos, BlockState state, Direction face) {
return face == state.get(FACING).getOpposite();
}
}

View file

@ -0,0 +1,196 @@
package com.simibubi.create.modules.contraptions.receivers.fan;
import static com.simibubi.create.CreateConfig.parameters;
import com.simibubi.create.AllBlockTags;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.CreateConfig;
import com.simibubi.create.modules.contraptions.base.GeneratingKineticTileEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
public class EncasedFanTileEntity extends GeneratingKineticTileEntity {
public AirCurrent airCurrent;
protected int airCurrentUpdateCooldown;
protected int entitySearchCooldown;
protected boolean isGenerator;
protected boolean updateAirFlow;
public EncasedFanTileEntity() {
super(AllTileEntities.ENCASED_FAN.type);
isGenerator = false;
airCurrent = new AirCurrent(this);
updateAirFlow = true;
}
@Override
public void readClientUpdate(CompoundNBT tag) {
super.readClientUpdate(tag);
airCurrent.rebuild();
}
@Override
public void read(CompoundNBT compound) {
super.read(compound);
isGenerator = compound.getBoolean("Generating");
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.putBoolean("Generating", isGenerator);
return super.write(compound);
}
@Override
public float getAddedStressCapacity() {
return isGenerator ? super.getAddedStressCapacity() : 0;
}
@Override
public float getGeneratedSpeed() {
return isGenerator ? CreateConfig.parameters.generatingFanSpeed.get() : 0;
}
public void updateGenerator() {
boolean shouldGenerate = world.isBlockPowered(pos) && world.isBlockPresent(pos.down()) && blockBelowIsHot();
if (shouldGenerate == isGenerator)
return;
isGenerator = shouldGenerate;
updateGeneratedRotation();
}
public boolean blockBelowIsHot() {
return world.getBlockState(pos.down()).getBlock().isIn(AllBlockTags.FAN_HEATERS.tag);
}
public float getMaxDistance() {
float speed = Math.abs(this.getSpeed());
float distanceFactor = Math.min(speed / parameters.fanRotationArgmax.get(), 1);
float pushDistance = MathHelper.lerp(distanceFactor, 3, parameters.fanMaxPushDistance.get());
float pullDistance = MathHelper.lerp(distanceFactor, 1.5f, parameters.fanMaxPullDistance.get());
return this.getSpeed() > 0 ? pushDistance : pullDistance;
}
public Direction getAirFlowDirection() {
if (getSpeed() == 0)
return null;
Direction facing = getBlockState().get(BlockStateProperties.FACING);
return getSpeed() > 0 ? facing : facing.getOpposite();
}
@Override
public void onSpeedChanged() {
updateAirFlow = true;
}
public void blockInFrontChanged() {
updateAirFlow = true;
}
@Override
public void tick() {
super.tick();
if (!world.isRemote && airCurrentUpdateCooldown-- <= 0) {
airCurrentUpdateCooldown = parameters.fanBlockCheckRate.get();
updateAirFlow = true;
}
if (updateAirFlow) {
updateAirFlow = false;
airCurrent.rebuild();
sendData();
}
if (getSpeed() == 0 || isGenerator)
return;
if (entitySearchCooldown-- <= 0) {
entitySearchCooldown = 5;
airCurrent.findEntities();
}
airCurrent.tick();
}
// public void processEntity(Entity entity) {
// if (InWorldProcessing.isFrozen())
// return;
//
// if (entity instanceof ItemEntity) {
// if (world.rand.nextInt(4) == 0) {
// Type processingType = getProcessingType();
// if (processingType == Type.BLASTING)
// world.addParticle(ParticleTypes.LARGE_SMOKE, entity.posX, entity.posY + .25f, entity.posZ, 0,
// 1 / 16f, 0);
// if (processingType == Type.SMOKING)
// world.addParticle(ParticleTypes.CLOUD, entity.posX, entity.posY + .25f, entity.posZ, 0, 1 / 16f, 0);
// if (processingType == Type.SPLASHING)
// world.addParticle(ParticleTypes.BUBBLE_POP, entity.posX + (world.rand.nextFloat() - .5f) * .5f,
// entity.posY + .25f, entity.posZ + (world.rand.nextFloat() - .5f) * .5f, 0, 1 / 16f, 0);
// }
//
// if (world.isRemote)
// return;
//
// if (canProcess((ItemEntity) entity))
// InWorldProcessing.applyProcessing((ItemEntity) entity, getProcessingType());
//
// } else {
// if (getProcessingType() == Type.SMOKING) {
// entity.setFire(2);
// entity.attackEntityFrom(damageSourceFire, 4);
// }
// if (getProcessingType() == Type.BLASTING) {
// entity.setFire(10);
// entity.attackEntityFrom(damageSourceLava, 8);
// }
// if (getProcessingType() == Type.SPLASHING) {
// if (entity.isBurning()) {
// entity.extinguish();
// world.playSound(null, entity.getPosition(), SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE,
// SoundCategory.NEUTRAL, 0.7F,
// 1.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.4F);
// }
// }
// }
// }
//
// protected boolean canProcess() {
// return getProcessingType() != null;
// }
//
// protected boolean canProcess(ItemEntity entity) {
// return canProcess() && InWorldProcessing.canProcess(entity, getProcessingType());
// }
//
// protected void moveEntity(Entity entity, boolean push) {
// if ((entity instanceof ItemEntity) && AllBlocks.BELT.typeOf(world.getBlockState(entity.getPosition()))
// && getAirFlowDirection() != Direction.UP) {
// return;
// }
//
// Vec3d center = VecHelper.getCenterOf(pos);
// Vec3i flow = getAirFlowDirection().getDirectionVec();
//
// float sneakModifier = entity.isSneaking() ? 4096f : 512f;
// float acceleration = (float) (getSpeed() * 1 / sneakModifier
// / (entity.getPositionVec().distanceTo(center) / (push ? pushDistance : pullDistance)));
// Vec3d previousMotion = entity.getMotion();
// float maxAcceleration = 5;
//
// double xIn = MathHelper.clamp(flow.getX() * acceleration - previousMotion.x, -maxAcceleration, maxAcceleration);
// double yIn = MathHelper.clamp(flow.getY() * acceleration - previousMotion.y, -maxAcceleration, maxAcceleration);
// double zIn = MathHelper.clamp(flow.getZ() * acceleration - previousMotion.z, -maxAcceleration, maxAcceleration);
//
// entity.setMotion(
// previousMotion.add(new Vec3d(xIn, yIn, zIn).mul(flow.getX(), flow.getY(), flow.getZ()).scale(1 / 8f)));
// entity.fallDistance = 0;
// }
}

View file

@ -0,0 +1,48 @@
package com.simibubi.create.modules.contraptions.receivers.fan;
import static net.minecraft.state.properties.BlockStateProperties.FACING;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.SuperByteBuffer;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.MathHelper;
public class EncasedFanTileEntityRenderer extends KineticTileEntityRenderer {
@Override
public void renderTileEntityFast(KineticTileEntity te, double x, double y, double z, float partialTicks,
int destroyStage, BufferBuilder buffer) {
super.renderTileEntityFast(te, x, y, z, partialTicks, destroyStage, buffer);
float time = AnimationTickHolder.getRenderTick();
float speed = te.getSpeed() * 20;
if (speed > 0)
speed = MathHelper.clamp(speed, 80, 128 * 20);
if (speed < 0)
speed = MathHelper.clamp(speed, -128 * 20, -80);
float angle = (time * speed) % 360;
angle = angle / 180f * (float) Math.PI;
SuperByteBuffer superByteBuffer = CreateClient.bufferCache.renderBlockState(KINETIC_TILE,
getRenderedPropellerState(te));
kineticRotationTransform(superByteBuffer, te, te.getBlockState().get(FACING).getAxis(), angle, getWorld());
superByteBuffer.translate(x, y, z).renderInto(buffer);
}
@Override
protected BlockState getRenderedBlockState(KineticTileEntity te) {
return AllBlocks.SHAFT_HALF.get().getDefaultState().with(FACING, te.getBlockState().get(FACING).getOpposite());
}
protected BlockState getRenderedPropellerState(KineticTileEntity te) {
return AllBlocks.ENCASED_FAN_INNER.get().getDefaultState().with(FACING, te.getBlockState().get(FACING));
}
}

View file

@ -1,4 +1,4 @@
package com.simibubi.create.modules.contraptions.receivers;
package com.simibubi.create.modules.contraptions.receivers.fan;
import java.util.List;

View file

@ -8,12 +8,18 @@ import java.util.Optional;
import com.simibubi.create.AllRecipes;
import com.simibubi.create.CreateConfig;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.modules.contraptions.base.ProcessingRecipe;
import com.simibubi.create.modules.contraptions.receivers.SplashingRecipe;
import com.simibubi.create.modules.contraptions.receivers.fan.SplashingRecipe;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.modules.contraptions.relays.belt.TransportedItemStack;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.CampfireBlock;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.fluid.Fluids;
import net.minecraft.fluid.IFluidState;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.BlastingRecipe;
import net.minecraft.item.crafting.FurnaceRecipe;
@ -21,9 +27,14 @@ import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.SmokingRecipe;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.particles.RedstoneParticleData;
import net.minecraft.tileentity.BlastFurnaceTileEntity;
import net.minecraft.tileentity.FurnaceTileEntity;
import net.minecraft.tileentity.SmokerTileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
@ -41,6 +52,21 @@ public class InWorldProcessing {
public enum Type {
SMOKING, BLASTING, SPLASHING
;
public static Type byBlock(IBlockReader reader, BlockPos pos) {
BlockState blockState = reader.getBlockState(pos);
IFluidState fluidState = reader.getFluidState(pos);
if (fluidState.getFluid() == Fluids.WATER)
return Type.SPLASHING;
if (blockState.getBlock() == Blocks.FIRE
|| (blockState.getBlock() == Blocks.CAMPFIRE && blockState.get(CampfireBlock.LIT)))
return Type.SMOKING;
if (blockState.getBlock() == Blocks.LAVA)
return Type.BLASTING;
return null;
}
}
public static boolean canProcess(ItemEntity entity, Type type) {
@ -105,7 +131,8 @@ public class InWorldProcessing {
}
}
public static List<TransportedItemStack> applyProcessing(TransportedItemStack transported, BeltTileEntity belt, Type type) {
public static List<TransportedItemStack> applyProcessing(TransportedItemStack transported, BeltTileEntity belt,
Type type) {
if (transported.processedBy != type) {
transported.processedBy = type;
transported.processingTime = CreateConfig.parameters.inWorldProcessingTime.get() + 1;
@ -243,6 +270,23 @@ public class InWorldProcessing {
return stacks;
}
public static void spawnParticlesForProcessing(World world, Vec3d vec, Type type) {
if (world.rand.nextInt(4) == 0 && world.isRemote) {
if (type == Type.BLASTING)
world.addParticle(ParticleTypes.LARGE_SMOKE, vec.x, vec.y + .25f, vec.z, 0, 1 / 16f, 0);
if (type == Type.SMOKING)
world.addParticle(ParticleTypes.POOF, vec.x, vec.y + .25f, vec.z, 0, 1 / 16f, 0);
if (type == Type.SPLASHING) {
Vec3d color = ColorHelper.getRGB(0x0055FF);
world.addParticle(new RedstoneParticleData((float) color.x, (float) color.y, (float) color.z, 1),
vec.x + (world.rand.nextFloat() - .5f) * .5f, vec.y + .5f,
vec.z + (world.rand.nextFloat() - .5f) * .5f, 0, 1 / 8f, 0);
world.addParticle(ParticleTypes.SPIT, vec.x + (world.rand.nextFloat() - .5f) * .5f, vec.y + .5f,
vec.z + (world.rand.nextFloat() - .5f) * .5f, 0, 1 / 8f, 0);
}
}
}
public static boolean isFrozen() {
return CreateConfig.parameters.freezeInWorldProcessing.get();
}

View file

@ -1,13 +1,16 @@
{
"forge_marker": 1,
"defaults": {
"model": "create:block/encased_fan"
},
"variants": {
"axis" : {
"x": { "y": 90 },
"y": { "x": 90 },
"z": { }
}
}
"forge_marker": 1,
"defaults": {
"model": "create:block/encased_fan/casing"
},
"variants": {
"facing": {
"north": { "y": 0 },
"south": { "y": 180 },
"west": { "y": 270 },
"up": { "x": 270 },
"down": { "x": 90 },
"east": { "y": 90 }
}
}
}

View file

@ -1,13 +1,16 @@
{
"forge_marker": 1,
"defaults": {
"model": "create:block/encased_fan_inner"
},
"variants": {
"axis" : {
"x": { "y": 90 },
"y": { "x": 90 },
"z": { }
}
}
"forge_marker": 1,
"defaults": {
"model": "create:block/encased_fan/propeller"
},
"variants": {
"facing": {
"north": { "y": 0 },
"south": { "y": 180 },
"west": { "y": 270 },
"up": { "x": 270 },
"down": { "x": 90 },
"east": { "y": 90 }
}
}
}

View file

@ -546,11 +546,11 @@
"block.create.encased_fan.tooltip": "ENCASED FAN",
"block.create.encased_fan.tooltip.summary": "Converts _Rotational_ _Force_ to _Air_ _Currents_ and back. Has a variety of uses.",
"block.create.encased_fan.tooltip.condition1": "When Powered by Redstone",
"block.create.encased_fan.tooltip.behaviour1": "Provides _Rotational_ _Force_ from any _heat_ _sources_ immediately below itself (fan has to be vertical)",
"block.create.encased_fan.tooltip.behaviour1": "Provides _rotational_ _force_ from any _heat_ _sources_ immediately below itself. The fan has to be facing down.",
"block.create.encased_fan.tooltip.condition2": "When Rotated",
"block.create.encased_fan.tooltip.behaviour2": "_Pushes_ Entities on one side, _Pulls_ on the other. Force and Speed depend on incoming Rotations.",
"block.create.encased_fan.tooltip.condition3": "When air flows through special blocks",
"block.create.encased_fan.tooltip.behaviour3": "Processes items in front of the Block: _Water_ washes, _Fire_ smokes and _Lava_ smelts the ingredient.",
"block.create.encased_fan.tooltip.behaviour2": "_Pushes_ or _Pulls_ Entities, depending on the incoming Rotation speed.",
"block.create.encased_fan.tooltip.condition3": "When blowing through special blocks",
"block.create.encased_fan.tooltip.behaviour3": "_Liquids_ and _Fire_ emit particles into the air flow. This can be used to _process_ _items._",
"block.create.turntable.tooltip": "TURNTABLE",
"block.create.turntable.tooltip.summary": "Turns _Rotational_ _Force_ into refined Motion Sickness.",

View file

@ -0,0 +1,78 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"back": "create:block/gearbox",
"fan_casing": "create:block/fan_casing",
"fan_side": "create:block/fan_side",
"particle": "create:block/fan_side"
},
"elements": [
{
"name": "Bottom",
"from": [0, 0, 0],
"to": [16, 2, 16],
"faces": {
"north": {"uv": [0, 14, 16, 16], "texture": "#fan_casing"},
"east": {"uv": [14, 0, 16, 16], "rotation": 90, "texture": "#fan_side"},
"south": {"uv": [0, 14, 16, 16], "texture": "#fan_casing"},
"west": {"uv": [14, 0, 16, 16], "rotation": 90, "texture": "#fan_side"},
"up": {"uv": [0, 0, 16, 16], "texture": "#fan_side"},
"down": {"uv": [0, 0, 16, 16], "texture": "#fan_side"}
}
},
{
"name": "Top",
"from": [0, 14, 0],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 16, 2], "texture": "#fan_casing"},
"east": {"uv": [0, 0, 2, 16], "rotation": 90, "texture": "#fan_side"},
"south": {"uv": [0, 0, 16, 2], "texture": "#fan_casing"},
"west": {"uv": [14, 0, 16, 16], "rotation": 270, "texture": "#fan_side"},
"up": {"uv": [0, 0, 16, 16], "texture": "#fan_side"},
"down": {"uv": [0, 0, 16, 16], "texture": "#fan_side"}
}
},
{
"name": "Side",
"from": [0, 2, 0],
"to": [2, 14, 16],
"faces": {
"north": {"uv": [14, 2, 16, 14], "texture": "#fan_casing"},
"east": {"uv": [2, 0, 14, 16], "rotation": 90, "texture": "#fan_side"},
"south": {"uv": [0, 2, 2, 14], "texture": "#fan_casing"},
"west": {"uv": [14, 0, 2, 16], "rotation": 270, "texture": "#fan_side"}
}
},
{
"name": "Side",
"from": [14, 2, 0],
"to": [16, 14, 16],
"faces": {
"north": {"uv": [0, 2, 2, 14], "texture": "#fan_casing"},
"east": {"uv": [2, 0, 14, 16], "rotation": 270, "texture": "#fan_side"},
"south": {"uv": [14, 2, 16, 14], "texture": "#fan_casing"},
"west": {"uv": [14, 0, 2, 16], "rotation": 270, "texture": "#fan_side"}
}
},
{
"name": "Lattice",
"from": [2, 2, 1],
"to": [14, 14, 1],
"faces": {
"north": {"uv": [2, 2, 14, 14], "texture": "#fan_casing"},
"south": {"uv": [2, 2, 14, 14], "texture": "#fan_casing"}
}
},
{
"name": "Back",
"from": [2, 2, 9],
"to": [14, 14, 15],
"faces": {
"north": {"uv": [2, 2, 14, 14], "texture": "#back"},
"south": {"uv": [2, 2, 14, 14], "texture": "#back"}
}
}
]
}

View file

@ -0,0 +1,111 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"2": "create:block/gearbox",
"fan_casing": "create:block/fan_casing",
"fan_side": "create:block/fan_side",
"particle": "create:block/fan_side",
"axis_top": "create:block/axis_top",
"fan_blades": "create:block/fan_blades",
"axis": "create:block/axis"
},
"elements": [
{
"name": "Bottom",
"from": [0, 0, 0],
"to": [16, 2, 16],
"faces": {
"north": {"uv": [0, 14, 16, 16], "texture": "#fan_casing"},
"east": {"uv": [14, 0, 16, 16], "rotation": 90, "texture": "#fan_side"},
"south": {"uv": [0, 14, 16, 16], "texture": "#fan_casing"},
"west": {"uv": [14, 0, 16, 16], "rotation": 90, "texture": "#fan_side"},
"up": {"uv": [0, 0, 16, 16], "texture": "#fan_side"},
"down": {"uv": [0, 0, 16, 16], "texture": "#fan_side"}
}
},
{
"name": "Top",
"from": [0, 14, 0],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 16, 2], "texture": "#fan_casing"},
"east": {"uv": [0, 0, 2, 16], "rotation": 90, "texture": "#fan_side"},
"south": {"uv": [0, 0, 16, 2], "texture": "#fan_casing"},
"west": {"uv": [14, 0, 16, 16], "rotation": 270, "texture": "#fan_side"},
"up": {"uv": [0, 0, 16, 16], "texture": "#fan_side"},
"down": {"uv": [0, 0, 16, 16], "texture": "#fan_side"}
}
},
{
"name": "Side",
"from": [0, 2, 0],
"to": [2, 14, 16],
"faces": {
"north": {"uv": [14, 2, 16, 14], "texture": "#fan_casing"},
"east": {"uv": [2, 0, 14, 16], "rotation": 90, "texture": "#fan_side"},
"south": {"uv": [0, 2, 2, 14], "texture": "#fan_casing"},
"west": {"uv": [14, 0, 2, 16], "rotation": 270, "texture": "#fan_side"}
}
},
{
"name": "Side",
"from": [14, 2, 0],
"to": [16, 14, 16],
"faces": {
"north": {"uv": [0, 2, 2, 14], "texture": "#fan_casing"},
"east": {"uv": [2, 0, 14, 16], "rotation": 270, "texture": "#fan_side"},
"south": {"uv": [14, 2, 16, 14], "texture": "#fan_casing"},
"west": {"uv": [14, 0, 2, 16], "rotation": 270, "texture": "#fan_side"}
}
},
{
"name": "Lattice",
"from": [2, 2, 1],
"to": [14, 14, 1],
"faces": {
"north": {"uv": [2, 2, 14, 14], "texture": "#fan_casing"},
"south": {"uv": [2, 2, 14, 14], "texture": "#fan_casing"}
}
},
{
"name": "Back",
"from": [2, 2, 9],
"to": [14, 14, 15],
"faces": {
"north": {"uv": [2, 2, 14, 14], "texture": "#2"},
"south": {"uv": [2, 2, 14, 14], "texture": "#2"}
}
},
{
"name": "Shaft",
"from": [6, 6, 1.2],
"to": [10, 10, 16],
"faces": {
"north": {"uv": [6, 6, 10, 10], "texture": "#axis_top"},
"east": {"uv": [6, 0, 10, 16], "rotation": 90, "texture": "#axis"},
"south": {"uv": [6, 6, 10, 10], "texture": "#axis_top"},
"west": {"uv": [6, 0, 10, 16], "rotation": 270, "texture": "#axis"},
"up": {"uv": [6, 0, 10, 16], "texture": "#axis"},
"down": {"uv": [6, 0, 10, 16], "texture": "#axis"}
}
},
{
"name": "Fan",
"from": [1, 1, 4],
"to": [15, 15, 12],
"rotation": {"angle": 22.5, "axis": "z", "origin": [8, 8, 8]},
"faces": {
"north": {"uv": [1, 1, 15, 15], "texture": "#fan_blades"},
"south": {"uv": [1, 1, 15, 15], "texture": "#fan_blades"}
}
}
],
"groups": [0, 1, 2, 3, 4, 5,
{
"name": "encased_fan_inner",
"origin": [8, 8, 8],
"children": [6, 7]
}
]
}

View file

@ -0,0 +1,32 @@
{
"textures": {
"axis_top": "create:block/axis_top",
"fan_blades": "create:block/fan_blades",
"axis": "create:block/axis"
},
"elements": [
{
"name": "Shaft",
"from": [6, 6, 1.2],
"to": [10, 10, 8],
"faces": {
"north": {"uv": [6, 6, 10, 10], "texture": "#axis_top"},
"east": {"uv": [6, 0, 10, 6.8], "rotation": 90, "texture": "#axis"},
"south": {"uv": [6, 6, 10, 10], "texture": "#axis_top"},
"west": {"uv": [6, 0, 10, 6.8], "rotation": 90, "texture": "#axis"},
"up": {"uv": [6, 0, 10, 6.8], "rotation": 90, "texture": "#axis"},
"down": {"uv": [6, 0, 10, 6.8], "rotation": 90, "texture": "#axis"}
}
},
{
"name": "Fan",
"from": [1, 1, 4],
"to": [15, 15, 12],
"rotation": {"angle": 22.5, "axis": "z", "origin": [8, 8, 8]},
"faces": {
"north": {"uv": [1, 1, 15, 15], "texture": "#fan_blades"},
"south": {"uv": [1, 1, 15, 15], "texture": "#fan_blades"}
}
}
]
}

View file

@ -1,103 +1,3 @@
{
"__comment": "Model generated using MrCrayfish's Model Creator (https://mrcrayfish.com/tools?id=mc)",
"parent": "block/block",
"textures": {
"particle": "create:block/fan_side",
"fan_casing": "create:block/fan_casing",
"fan_side": "create:block/fan_side",
"axis_top": "create:block/axis_top",
"fan_blades": "create:block/fan_blades",
"axis": "create:block/axis"
},
"elements": [
{
"name": "Bottom",
"from": [ 0, 0, 0 ],
"to": [ 16, 2, 16 ],
"faces": {
"north": { "texture": "#fan_casing", "uv": [ 0, 14, 16, 16 ] },
"east": { "texture": "#fan_side", "uv": [ 14, 0, 16, 16 ], "rotation": 90 },
"south": { "texture": "#fan_casing", "uv": [ 0, 14, 16, 16 ] },
"west": { "texture": "#fan_side", "uv": [ 14, 0, 16, 16 ], "rotation": 90 },
"up": { "texture": "#fan_side", "uv": [ 0, 0, 16, 16 ] },
"down": { "texture": "#fan_side", "uv": [ 0, 0, 16, 16 ] }
}
},
{
"name": "Top",
"from": [ 0, 14, 0 ],
"to": [ 16, 16, 16 ],
"faces": {
"north": { "texture": "#fan_casing", "uv": [ 0, 0, 16, 2 ] },
"east": { "texture": "#fan_side", "uv": [ 0, 0, 2, 16 ], "rotation": 90 },
"south": { "texture": "#fan_casing", "uv": [ 0, 0, 16, 2 ] },
"west": { "texture": "#fan_side", "uv": [ 14, 0, 16, 16 ], "rotation": 270 },
"up": { "texture": "#fan_side", "uv": [ 0, 0, 16, 16 ] },
"down": { "texture": "#fan_side", "uv": [ 0, 0, 16, 16 ] }
}
},
{
"name": "Side",
"from": [ 0, 2, 0 ],
"to": [ 2, 14, 16 ],
"faces": {
"north": { "texture": "#fan_casing", "uv": [ 14, 2, 16, 14 ] },
"east": { "texture": "#fan_side", "uv": [ 2, 0, 14, 16 ], "rotation": 90 },
"south": { "texture": "#fan_casing", "uv": [ 0, 2, 2, 14 ] },
"west": { "texture": "#fan_side", "uv": [ 14, 0, 2, 16 ], "rotation": 270 }
}
},
{
"name": "Side",
"from": [ 14, 2, 0 ],
"to": [ 16, 14, 16 ],
"faces": {
"north": { "texture": "#fan_casing", "uv": [ 0, 2, 2, 14 ] },
"east": { "texture": "#fan_side", "uv": [ 2, 0, 14, 16 ], "rotation": 270 },
"south": { "texture": "#fan_casing", "uv": [ 14, 2, 16, 14 ] },
"west": { "texture": "#fan_side", "uv": [ 14, 0, 2, 16 ], "rotation": 270 }
}
},
{
"name": "Lattice",
"from": [ 2, 2, 1 ],
"to": [ 14, 14, 1 ],
"faces": {
"north": { "texture": "#fan_casing", "uv": [ 2, 2, 14, 14 ] },
"south": { "texture": "#fan_casing", "uv": [ 2, 2, 14, 14 ] }
}
},
{
"name": "Lattice",
"from": [ 2, 2, 15 ],
"to": [ 14, 14, 15 ],
"faces": {
"north": { "texture": "#fan_casing", "uv": [ 2, 2, 14, 14 ] },
"south": { "texture": "#fan_casing", "uv": [ 2, 2, 14, 14 ] }
}
},
{
"name": "Shaft",
"from": [ 6, 6, 0 ],
"to": [ 10, 10, 16 ],
"faces": {
"north": { "texture": "#axis_top", "uv": [ 6, 6, 10, 10 ] },
"east": { "texture": "#axis", "uv": [ 6, 0, 10, 16 ], "rotation": 90 },
"south": { "texture": "#axis_top", "uv": [ 6, 6, 10, 10 ] },
"west": { "texture": "#axis", "uv": [ 6, 0, 10, 16 ], "rotation": 270 },
"up": { "texture": "#axis", "uv": [ 6, 0, 10, 16 ] },
"down": { "texture": "#axis", "uv": [ 6, 0, 10, 16 ] }
}
},
{
"name": "Fan",
"from": [ 1, 1, 4 ],
"to": [ 15, 15, 12 ],
"rotation": { "origin": [ 8, 8, 8 ], "axis": "z", "angle": 22.5 },
"faces": {
"north": { "texture": "#fan_blades", "uv": [ 1, 1, 15, 15 ] },
"south": { "texture": "#fan_blades", "uv": [ 1, 1, 15, 15 ] }
}
}
]
"parent": "create:block/encased_fan/item"
}

View file

@ -0,0 +1,12 @@
{
"textures": [
"minecraft:generic_7",
"minecraft:generic_6",
"minecraft:generic_5",
"minecraft:generic_4",
"minecraft:generic_3",
"minecraft:generic_2",
"minecraft:generic_1",
"minecraft:generic_0"
]
}

View file

@ -9,7 +9,8 @@
"results": [
{
"item": "minecraft:clay_ball",
"count": 2
"count": 1,
"chance": 0.25
}
],
"processingTime": 100