Create/src/main/java/com/simibubi/create/content/processing/burner/BlazeBurnerBlockEntity.java

355 lines
10 KiB
Java

package com.simibubi.create.content.processing.burner;
import java.util.List;
import java.util.Random;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllTags;
import com.simibubi.create.AllTags.AllItemTags;
import com.simibubi.create.content.fluids.tank.FluidTankBlock;
import com.simibubi.create.content.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.ForgeHooks;
public class BlazeBurnerBlockEntity extends SmartBlockEntity {
public static final int MAX_HEAT_CAPACITY = 10000;
public static final int INSERTION_THRESHOLD = 500;
protected FuelType activeFuel;
protected int remainingBurnTime;
protected LerpedFloat headAnimation;
protected LerpedFloat headAngle;
protected boolean isCreative;
protected boolean goggles;
protected boolean hat;
public BlazeBurnerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
activeFuel = FuelType.NONE;
remainingBurnTime = 0;
headAnimation = LerpedFloat.linear();
headAngle = LerpedFloat.angular();
isCreative = false;
goggles = false;
headAngle.startWithValue((AngleHelper.horizontalAngle(state.getOptionalValue(BlazeBurnerBlock.FACING)
.orElse(Direction.SOUTH)) + 180) % 360);
}
public FuelType getActiveFuel() {
return activeFuel;
}
public int getRemainingBurnTime() {
return remainingBurnTime;
}
public boolean isCreative() {
return isCreative;
}
@Override
public void tick() {
super.tick();
if (level.isClientSide) {
tickAnimation();
if (!isVirtual())
spawnParticles(getHeatLevelFromBlock(), 1);
return;
}
if (isCreative)
return;
if (remainingBurnTime > 0)
remainingBurnTime--;
if (activeFuel == FuelType.NORMAL)
updateBlockState();
if (remainingBurnTime > 0)
return;
if (activeFuel == FuelType.SPECIAL) {
activeFuel = FuelType.NORMAL;
remainingBurnTime = MAX_HEAT_CAPACITY / 2;
} else
activeFuel = FuelType.NONE;
updateBlockState();
}
@OnlyIn(Dist.CLIENT)
private void tickAnimation() {
boolean active = getHeatLevelFromBlock().isAtLeast(HeatLevel.FADING) && isValidBlockAbove();
if (!active) {
float target = 0;
LocalPlayer player = Minecraft.getInstance().player;
if (player != null && !player.isInvisible()) {
double x;
double z;
if (isVirtual()) {
x = -4;
z = -10;
} else {
x = player.getX();
z = player.getZ();
}
double dx = x - (getBlockPos().getX() + 0.5);
double dz = z - (getBlockPos().getZ() + 0.5);
target = AngleHelper.deg(-Mth.atan2(dz, dx)) - 90;
}
target = headAngle.getValue() + AngleHelper.getShortestAngleDiff(headAngle.getValue(), target);
headAngle.chase(target, .25f, Chaser.exp(5));
headAngle.tickChaser();
} else {
headAngle.chase((AngleHelper.horizontalAngle(getBlockState().getOptionalValue(BlazeBurnerBlock.FACING)
.orElse(Direction.SOUTH)) + 180) % 360, .125f, Chaser.EXP);
headAngle.tickChaser();
}
headAnimation.chase(active ? 1 : 0, .25f, Chaser.exp(.25f));
headAnimation.tickChaser();
}
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {}
@Override
public void write(CompoundTag compound, boolean clientPacket) {
if (!isCreative) {
compound.putInt("fuelLevel", activeFuel.ordinal());
compound.putInt("burnTimeRemaining", remainingBurnTime);
} else
compound.putBoolean("isCreative", true);
if (goggles)
compound.putBoolean("Goggles", true);
if (hat)
compound.putBoolean("TrainHat", true);
super.write(compound, clientPacket);
}
@Override
protected void read(CompoundTag compound, boolean clientPacket) {
activeFuel = FuelType.values()[compound.getInt("fuelLevel")];
remainingBurnTime = compound.getInt("burnTimeRemaining");
isCreative = compound.getBoolean("isCreative");
goggles = compound.contains("Goggles");
hat = compound.contains("TrainHat");
super.read(compound, clientPacket);
}
public BlazeBurnerBlock.HeatLevel getHeatLevelFromBlock() {
return BlazeBurnerBlock.getHeatLevelOf(getBlockState());
}
public void updateBlockState() {
setBlockHeat(getHeatLevel());
}
protected void setBlockHeat(HeatLevel heat) {
HeatLevel inBlockState = getHeatLevelFromBlock();
if (inBlockState == heat)
return;
level.setBlockAndUpdate(worldPosition, getBlockState().setValue(BlazeBurnerBlock.HEAT_LEVEL, heat));
notifyUpdate();
}
/**
* @return true if the heater updated its burn time and an item should be
* consumed
*/
protected boolean tryUpdateFuel(ItemStack itemStack, boolean forceOverflow, boolean simulate) {
if (isCreative)
return false;
FuelType newFuel = FuelType.NONE;
int newBurnTime;
if (AllItemTags.BLAZE_BURNER_FUEL_SPECIAL.matches(itemStack)) {
newBurnTime = 3200;
newFuel = FuelType.SPECIAL;
} else {
newBurnTime = ForgeHooks.getBurnTime(itemStack, null);
if (newBurnTime > 0) {
newFuel = FuelType.NORMAL;
} else if (AllItemTags.BLAZE_BURNER_FUEL_REGULAR.matches(itemStack)) {
newBurnTime = 1600; // Same as coal
newFuel = FuelType.NORMAL;
}
}
if (newFuel == FuelType.NONE)
return false;
if (newFuel.ordinal() < activeFuel.ordinal())
return false;
if (newFuel == activeFuel) {
if (remainingBurnTime <= INSERTION_THRESHOLD) {
newBurnTime += remainingBurnTime;
} else if (forceOverflow && newFuel == FuelType.NORMAL) {
if (remainingBurnTime < MAX_HEAT_CAPACITY) {
newBurnTime = Math.min(remainingBurnTime + newBurnTime, MAX_HEAT_CAPACITY);
} else {
newBurnTime = remainingBurnTime;
}
} else {
return false;
}
}
if (simulate)
return true;
activeFuel = newFuel;
remainingBurnTime = newBurnTime;
if (level.isClientSide) {
spawnParticleBurst(activeFuel == FuelType.SPECIAL);
return true;
}
HeatLevel prev = getHeatLevelFromBlock();
playSound();
updateBlockState();
if (prev != getHeatLevelFromBlock())
level.playSound(null, worldPosition, SoundEvents.BLAZE_AMBIENT, SoundSource.BLOCKS,
.125f + level.random.nextFloat() * .125f, 1.15f - level.random.nextFloat() * .25f);
return true;
}
protected void applyCreativeFuel() {
activeFuel = FuelType.NONE;
remainingBurnTime = 0;
isCreative = true;
HeatLevel next = getHeatLevelFromBlock().nextActiveLevel();
if (level.isClientSide) {
spawnParticleBurst(next.isAtLeast(HeatLevel.SEETHING));
return;
}
playSound();
if (next == HeatLevel.FADING)
next = next.nextActiveLevel();
setBlockHeat(next);
}
public boolean isCreativeFuel(ItemStack stack) {
return AllItems.CREATIVE_BLAZE_CAKE.isIn(stack);
}
public boolean isValidBlockAbove() {
if (isVirtual())
return false;
BlockState blockState = level.getBlockState(worldPosition.above());
return AllTags.AllBlockTags.BASIN.matches(blockState) || blockState.getBlock() instanceof FluidTankBlock;
}
protected void playSound() {
level.playSound(null, worldPosition, SoundEvents.BLAZE_SHOOT, SoundSource.BLOCKS,
.125f + level.random.nextFloat() * .125f, .75f - level.random.nextFloat() * .25f);
}
protected HeatLevel getHeatLevel() {
HeatLevel level = HeatLevel.SMOULDERING;
switch (activeFuel) {
case SPECIAL:
level = HeatLevel.SEETHING;
break;
case NORMAL:
boolean lowPercent = (double) remainingBurnTime / MAX_HEAT_CAPACITY < 0.0125;
level = lowPercent ? HeatLevel.FADING : HeatLevel.KINDLED;
break;
default:
case NONE:
break;
}
return level;
}
protected void spawnParticles(HeatLevel heatLevel, double burstMult) {
if (level == null)
return;
if (heatLevel == BlazeBurnerBlock.HeatLevel.NONE)
return;
Random r = level.getRandom();
Vec3 c = VecHelper.getCenterOf(worldPosition);
Vec3 v = c.add(VecHelper.offsetRandomly(Vec3.ZERO, r, .125f)
.multiply(1, 0, 1));
if (r.nextInt(4) != 0)
return;
boolean empty = level.getBlockState(worldPosition.above())
.getCollisionShape(level, worldPosition.above())
.isEmpty();
if (empty || r.nextInt(8) == 0)
level.addParticle(ParticleTypes.LARGE_SMOKE, v.x, v.y, v.z, 0, 0, 0);
double yMotion = empty ? .0625f : r.nextDouble() * .0125f;
Vec3 v2 = c.add(VecHelper.offsetRandomly(Vec3.ZERO, r, .5f)
.multiply(1, .25f, 1)
.normalize()
.scale((empty ? .25f : .5) + r.nextDouble() * .125f))
.add(0, .5, 0);
if (heatLevel.isAtLeast(HeatLevel.SEETHING)) {
level.addParticle(ParticleTypes.SOUL_FIRE_FLAME, v2.x, v2.y, v2.z, 0, yMotion, 0);
} else if (heatLevel.isAtLeast(HeatLevel.FADING)) {
level.addParticle(ParticleTypes.FLAME, v2.x, v2.y, v2.z, 0, yMotion, 0);
}
return;
}
public void spawnParticleBurst(boolean soulFlame) {
Vec3 c = VecHelper.getCenterOf(worldPosition);
Random r = level.random;
for (int i = 0; i < 20; i++) {
Vec3 offset = VecHelper.offsetRandomly(Vec3.ZERO, r, .5f)
.multiply(1, .25f, 1)
.normalize();
Vec3 v = c.add(offset.scale(.5 + r.nextDouble() * .125f))
.add(0, .125, 0);
Vec3 m = offset.scale(1 / 32f);
level.addParticle(soulFlame ? ParticleTypes.SOUL_FIRE_FLAME : ParticleTypes.FLAME, v.x, v.y, v.z, m.x, m.y,
m.z);
}
}
public enum FuelType {
NONE, NORMAL, SPECIAL
}
}