filename:
common/src/main/java/rearth/oritech/item/tools/armor/BaseJetpackItem.java
branch:
1.21
back to repo
package rearth.oritech.item.tools.armor;
import dev.architectury.fluid.FluidStack;
import dev.architectury.hooks.fluid.FluidStackHooks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import rearth.oritech.api.fluid.FluidApi;
import rearth.oritech.api.fluid.containers.SimpleItemFluidStorage;
import rearth.oritech.client.init.ParticleContent;
import rearth.oritech.client.renderers.LaserArmRenderer;
import rearth.oritech.init.ComponentContent;
import rearth.oritech.init.FluidContent;
import rearth.oritech.item.tools.util.OritechEnergyItem;
import rearth.oritech.network.NetworkContent;
import rearth.oritech.util.TooltipHelper;
import java.util.List;
import static rearth.oritech.item.tools.harvesting.ChainsawItem.BAR_STEP_COUNT;
public interface BaseJetpackItem extends OritechEnergyItem, FluidApi.ItemProvider {
boolean requireUpward();
int getRfUsage();
int getFuelUsage();
long getFuelCapacity();
float getSpeed();
default boolean requireTakeoff() {return true;}
default void tickJetpack(ItemStack stack, Entity entity, World world) {
if (!(entity instanceof PlayerEntity player)) return;
var isEquipped = player.getEquippedStack(EquipmentSlot.CHEST).equals(stack);
if (!isEquipped) return;
var client = MinecraftClient.getInstance();
var up = client.options.jumpKey.isPressed();
var forward = client.options.forwardKey.isPressed();
var backward = client.options.backKey.isPressed();
var left = client.options.leftKey.isPressed();
var right = client.options.rightKey.isPressed();
var horizontal = forward || backward || left || right;
var upOnly = up && !horizontal;
var isActive = up;
if (!requireUpward()) isActive = up || horizontal;
if (requireTakeoff() && !isJetpackStarted(player, world, up)) return;
if (!isActive || player.isOnGround() || player.isSubmergedInWater()) return;
var powerMultiplier = getSpeed();
// try using energy/fuel
if (tryUseFluid(stack)) {
powerMultiplier *= 2.5f;
} else if (!tryUseEnergy(stack, getRfUsage(), player)) {
return;
}
if (up) {
processUpwardsMotion(player, powerMultiplier, upOnly);
} else {
powerMultiplier *= 0.7f; // slower forward while not going up
}
if (forward || backward)
processForwardMotion(player, forward, powerMultiplier);
if (left || right)
processSideMotion(player, right, powerMultiplier);
var fluidStack = getStoredFluid(stack);
var fluid = Registries.FLUID.getId(fluidStack.getFluid());
// this will currently only for instances of this class
NetworkContent.UI_CHANNEL.clientHandle().send(new NetworkContent.JetpackUsageUpdatePacket(getStoredEnergy(stack), fluid.toString(), fluidStack.getAmount()));
var playerForward = player.getRotationVecClient();
var playerRight = playerForward.normalize().rotateY(-90);
var particleCenter = player.getEyePos().add(0, -1.1, 0).subtract(playerForward.multiply(0.2f));
var particlePosA = particleCenter.add(playerRight.multiply(0.4f));
var particlePosB = particleCenter.add(playerRight.multiply(-0.4f));
var direction = new Vec3d(0, -1, 0);
if (forward) direction = playerForward.normalize().multiply(-1).add(0, -1, 0);
ParticleContent.JETPACK_EXHAUST.spawn(world, particlePosA, direction);
ParticleContent.JETPACK_EXHAUST.spawn(world, particlePosB, direction);
}
private static boolean isJetpackStarted(PlayerEntity player, World world, boolean up) {
var grounded = player.isOnGround() || player.isSubmergedInWater();
if (grounded) {
JetpackItem.LAST_GROUND_CONTACT = world.getTime();
JetpackItem.PRESSED_SPACE = false;
return false;
} else {
var flightTime = world.getTime() - JetpackItem.LAST_GROUND_CONTACT;
if (flightTime < 5) return false;
if (up) JetpackItem.PRESSED_SPACE = true;
return JetpackItem.PRESSED_SPACE;
}
}
private static void processUpwardsMotion(PlayerEntity player, float powerMultiplier, boolean upOnly) {
var velocity = player.getMovement();
var verticalMultiplier = LaserArmRenderer.lerp(powerMultiplier, 1, 0.6f);
var power = 0.13f * verticalMultiplier;
var dampeningFactor = 1.7f;
if (!upOnly) power *= 0.7f;
var speed = Math.max(velocity.y, 0.8);
var addedVelocity = power / Math.pow(speed, dampeningFactor);
player.setVelocity(velocity.add(0, addedVelocity, 0));
}
private static void processSideMotion(PlayerEntity player, boolean right, float powerMultiplier) {
var modifier = right ? 1 : -1; // either go full speed ahead, or slowly backwards
var power = 0.04f * powerMultiplier;
// get existing movement
var movement = player.getMovement();
var horizontalMovement = new Vec3d(movement.x, 0, movement.z);
// get player facing
var playerForward = player.getRotationVecClient();
playerForward = new Vec3d(playerForward.x, 0, playerForward.z).normalize();
var playerRight = playerForward.rotateY(-90);
// apply forward / back
horizontalMovement = horizontalMovement.add(playerRight.multiply(modifier * power));
player.setVelocity(horizontalMovement.x, movement.y, horizontalMovement.z);
}
private static void processForwardMotion(PlayerEntity player, boolean forward, float powerMultiplier) {
var modifier = forward ? 1f : -0.4; // either go full speed ahead, or slowly backwards
var power = 0.06f * powerMultiplier;
// get existing movement
var movement = player.getMovement();
var horizontalMovement = new Vec3d(movement.x, 0, movement.z);
// get player facing
var playerForward = player.getRotationVecClient();
playerForward = new Vec3d(playerForward.x, 0, playerForward.z).normalize();
// apply forward / back
horizontalMovement = horizontalMovement.add(playerForward.multiply(modifier * power));
player.setVelocity(horizontalMovement.x, movement.y, horizontalMovement.z);
}
default boolean tryUseFluid(ItemStack stack) {
var fluidStack = getStoredFluid(stack);
if (fluidStack.getAmount() < getFuelUsage() || !isValidFuel(fluidStack.getFluid()))
return false;
var res = FluidStack.create(fluidStack.getFluid(), fluidStack.getAmount() - getFuelUsage());
stack.set(ComponentContent.STORED_FLUID.get(), res);
return true;
}
default FluidStack getStoredFluid(ItemStack stack) {
return stack.getOrDefault(ComponentContent.STORED_FLUID.get(), FluidStack.empty());
}
default void addJetpackTooltip(ItemStack stack, List<Text> tooltip, boolean includeEnergy) {
var text = Text.translatable("tooltip.oritech.energy_indicator", TooltipHelper.getEnergyText(this.getStoredEnergy(stack)), TooltipHelper.getEnergyText(this.getEnergyCapacity(stack)));
if (includeEnergy) tooltip.add(text.formatted(Formatting.GOLD));
var container = getStoredFluid(stack);
var fluidText = Text.translatable("tooltip.oritech.jetpack_fuel", container.getAmount() * 1000 / FluidStackHooks.bucketAmount(), getFuelCapacity() * 1000 / FluidStackHooks.bucketAmount(), FluidStackHooks.getName(container).getString());
tooltip.add(fluidText);
}
default int getJetpackBarColor(ItemStack stack) {
var fluidStack = getStoredFluid(stack);
if (fluidStack.getAmount() > getFuelUsage() && isValidFuel(fluidStack.getFluid())) {
return 0xbafc03;
}
return 0xff7007;
}
default int getJetpackBarStep(ItemStack stack) {
var fluidStack = getStoredFluid(stack);
if (fluidStack.getAmount() > getFuelUsage() && isValidFuel(fluidStack.getFluid())) {
var fillPercent = fluidStack.getAmount() * 100 / getFuelCapacity();
return Math.round(fillPercent * BAR_STEP_COUNT) / 100;
}
return Math.round((getStoredEnergy(stack) * 100f / this.getEnergyCapacity(stack)) * BAR_STEP_COUNT) / 100;
}
default boolean isValidFuel(Fluid variant) {
return variant.matchesType(FluidContent.STILL_FUEL.get());
}
@Override
default FluidApi.SingleSlotStorage getFluidStorage(ItemStack stack) {
return new SimpleItemFluidStorage(getFuelCapacity(), stack) {
@Override
public long insert(FluidStack toInsert, boolean simulate) {
var valid = isValidFuel(toInsert.getFluid());
if (!valid) return 0L;
return super.insert(toInsert, simulate);
}
};
}
}