Initial commit

This commit is contained in:
Konhaiii 2025-06-01 17:44:30 +02:00
commit 96a5b07652
48 changed files with 1869 additions and 0 deletions

View file

@ -0,0 +1,37 @@
package konhaiii.powered_jetpacks;
import konhaiii.powered_jetpacks.hud.JetpackHUD;
import konhaiii.powered_jetpacks.renderers.JetpackRenderer;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
public class PoweredJetpacksClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
ItemTooltipCallback.EVENT.register(new StackToolTipHandler());
HudRenderCallback.EVENT.register(new JetpackHUD());
LivingEntityFeatureRendererRegistrationCallback.EVENT.register((entityType, entityRenderer, registrationHelper, context) -> {
if (entityRenderer != null) {
FeatureRendererContext<LivingEntity, EntityModel<LivingEntity>> featureRendererContext =
new FeatureRendererContext<>() {
@Override
public EntityModel<LivingEntity> getModel() {
return null;
}
@Override
public Identifier getTexture(LivingEntity entity) {
return null;
}
};
registrationHelper.register(new JetpackRenderer<>(featureRendererContext));
}
});
}
}

View file

@ -0,0 +1,80 @@
package konhaiii.powered_jetpacks;
import konhaiii.powered_jetpacks.energy.EnergySystem;
import konhaiii.powered_jetpacks.item.special.JetpackItem;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.List;
public class StackToolTipHandler implements ItemTooltipCallback {
@Override
public void getTooltip(ItemStack itemStack, TooltipContext tooltipContext, List<Text> tooltipLines) {
Item item = itemStack.getItem();
if (item instanceof JetpackItem jetpackItem) {
if (Screen.hasShiftDown()) {
MutableText line1 = Text.literal(EnergySystem.getEnergy(jetpackItem.getStoredEnergy(itemStack)));
line1.append("/");
line1.append(EnergySystem.getEnergyUnit(jetpackItem.getEnergyCapacity(itemStack)));
line1.formatted(Formatting.GOLD);
tooltipLines.add(1, line1);
int percentage = percentage(jetpackItem.getStoredEnergy(itemStack), jetpackItem.getEnergyCapacity(itemStack));
MutableText line2 = Text.literal(String.valueOf(percentage)).append("%");
line2.append(" ");
line2.formatted(Formatting.GRAY);
line2.append(I18n.translate("tooltip.powered_jetpacks.power_charged"));
tooltipLines.add(2, line2);
double inputRate = jetpackItem.getEnergyMaxInput(itemStack);
double outputRate = jetpackItem.getEnergyMaxOutput(itemStack);
MutableText line3 = Text.literal("");
if (inputRate != 0 && inputRate == outputRate){
line3.append(I18n.translate("tooltip.powered_jetpacks.transfer_rate"));
line3.append(" : ");
line3.formatted(Formatting.GRAY);
line3.append(EnergySystem.getEnergyUnitDiminished(inputRate));
line3.formatted(Formatting.GOLD);
}
else if(inputRate != 0){
line3.append(I18n.translate("tooltip.powered_jetpacks.input_rate"));
line3.append(" : ");
line3.formatted(Formatting.GRAY);
line3.append(EnergySystem.getEnergyUnitDiminished(inputRate));
line3.formatted(Formatting.GOLD);
}
else if (outputRate !=0){
line3.append(I18n.translate("tooltip.powered_jetpacks.output_rate"));
line3.append(" : ");
line3.formatted(Formatting.GRAY);
line3.append(EnergySystem.getEnergyUnitDiminished(outputRate));
line3.formatted(Formatting.GOLD);
}
tooltipLines.add(3, line3);
} else {
MutableText line1 = Text.literal(EnergySystem.getEnergyDiminished(jetpackItem.getStoredEnergy(itemStack)));
line1.append("/");
line1.append(EnergySystem.getEnergyUnitDiminished(jetpackItem.getEnergyCapacity(itemStack)));
line1.formatted(Formatting.GOLD);
tooltipLines.add(1, line1);
}
}
}
private int percentage(double CurrentValue, double MaxValue) {
if (CurrentValue == 0)
return 0;
return (int) ((CurrentValue * 100.0f) / MaxValue);
}
}

View file

@ -0,0 +1,55 @@
package konhaiii.powered_jetpacks.hud;
import dev.emi.trinkets.api.TrinketsApi;
import konhaiii.powered_jetpacks.item.special.JetpackItem;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Pair;
import static net.minecraft.client.resource.language.I18n.translate;
public class JetpackHUD implements HudRenderCallback {
@Override
public void onHudRender(DrawContext drawContext, float tickDelta) {
MinecraftClient client = MinecraftClient.getInstance();
if (client.player == null) return;
ItemStack chestStack = client.player.getEquippedStack(EquipmentSlot.CHEST);
ItemStack backStack = TrinketsApi.getTrinketComponent(client.player).map(component ->
component.getEquipped(stack -> stack.getItem() instanceof JetpackItem)
.stream()
.findFirst()
.map(Pair::getRight)
.orElse(ItemStack.EMPTY)
).orElse(ItemStack.EMPTY);
ItemStack jetpackStack = isValidJetpack(chestStack) ? chestStack : (!backStack.isEmpty() ? backStack : ItemStack.EMPTY);
if (!jetpackStack.isEmpty()) {
JetpackItem jetpack = (JetpackItem) jetpackStack.getItem();
long energy = jetpack.getStoredEnergy(jetpackStack);
long maxEnergy = jetpack.getEnergyCapacity(jetpackStack);
TextRenderer textRenderer = client.textRenderer;
int percentage = percentage(energy, maxEnergy);
String energyText = translate("hud.powered_jetpacks.jetpack_power").concat(String.valueOf(percentage)).concat("%");
drawContext.drawTextWithShadow(textRenderer, energyText, 10, 10, 0xFFFFFF);
}
}
private boolean isValidJetpack(ItemStack stack) {
return stack.getItem() instanceof JetpackItem;
}
private int percentage(double CurrentValue, double MaxValue) {
if (CurrentValue == 0)
return 0;
return (int) ((CurrentValue * 100.0f) / MaxValue);
}
}

View file

@ -0,0 +1,77 @@
package konhaiii.powered_jetpacks.mixin.client;
import dev.emi.trinkets.api.TrinketsApi;
import konhaiii.powered_jetpacks.item.special.JetpackItem;
import konhaiii.powered_jetpacks.packet.JetpackPacket;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.Pair;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayerEntity.class)
public abstract class ClientPlayerEntityMixin {
@Unique
private int soundCounter = 8;
@Inject(method = "tick", at = @At("HEAD"))
private void onTick(CallbackInfo ci) {
ClientPlayerEntity player = (ClientPlayerEntity) (Object) this;
ItemStack chestStack = player.getEquippedStack(EquipmentSlot.CHEST);
ItemStack backStack = TrinketsApi.getTrinketComponent(player).map(component ->
component.getEquipped(stack -> stack.getItem() instanceof JetpackItem)
.stream()
.findFirst()
.map(Pair::getRight)
.orElse(ItemStack.EMPTY)
).orElse(ItemStack.EMPTY);
ItemStack jetpackStack = null;
if (isValidJetpack(chestStack)) {
jetpackStack = chestStack;
} else if (isValidJetpack(backStack)) {
jetpackStack = backStack;
}
if (jetpackStack != null && player.input.jumping) {
PacketByteBuf buf = PacketByteBufs.create();
JetpackItem jetpack = (JetpackItem) jetpackStack.getItem();
Vec3d velocity = player.getVelocity();
float horizontalBoost = jetpack.getFlightSpeed();
player.setVelocity(
velocity.x + (player.getRotationVector().x * horizontalBoost),
jetpack.addToVerticalVelocity(player.getVelocity().y),
velocity.z + (player.getRotationVector().z * horizontalBoost)
);
player.fallDistance = 0;
soundCounter++;
if (soundCounter >= 8) {
JetpackPacket.encode(new JetpackPacket(true), buf);
soundCounter = 0;
} else {
JetpackPacket.encode(new JetpackPacket(false), buf);
}
ClientPlayNetworking.send(JetpackPacket.getPacketId(), buf);
} else if (soundCounter != 8) {
soundCounter = 8;
}
}
@Unique
private boolean isValidJetpack(ItemStack stack) {
if (stack.getItem() instanceof JetpackItem jetpack) {
if (jetpack.getEnergyCost() <= 0) {
return true;
}
return jetpack.getStoredEnergy(stack) > 0;
}
return false;
}
}

View file

@ -0,0 +1,110 @@
package konhaiii.powered_jetpacks.models;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.Direction;
import java.util.*;
public class JetpackModel<T extends LivingEntity> extends EntityModel<T> {
private final ModelPart middle;
private final ModelPart bottomLeft;
private final ModelPart bottomRight;
private final ModelPart mainLeft;
private final ModelPart mainRight;
public JetpackModel() {
// Middle part
Map<Direction, UV> middleUVs = Map.of(
Direction.NORTH, new UV(8, 2, 16, 16),
Direction.EAST, new UV(6, 2, 16, 16),
Direction.SOUTH, new UV(4, 2, 16, 16),
Direction.WEST, new UV(10, 2, 16, 16),
Direction.UP, new UV(4, 4, 16, 16),
Direction.DOWN, new UV(6, 6, 16, 16)
);
this.middle = createCuboid(7, 2, 7, 2, 8, 2, middleUVs);
// Bottom left part
Map<Direction, UV> bottomLeftUVs = Map.of(
Direction.NORTH, new UV(4, 0, 16, 16),
Direction.EAST, new UV(2, 0, 16, 16),
Direction.SOUTH, new UV(0, 0, 16, 16),
Direction.WEST, new UV(6, 0, 16, 16),
Direction.UP, new UV(2, 0, 16, 16),
Direction.DOWN, new UV(4, 0, 16, 16)
);
this.bottomLeft = createCuboid(4, 0, 7, 2, 1, 2, bottomLeftUVs);
// Bottom right part
Map<Direction, UV> bottomRightUVs = Map.of(
Direction.NORTH, new UV(4, 0, 16, 16),
Direction.EAST, new UV(2, 0, 16, 16),
Direction.SOUTH, new UV(0, 0, 16, 16),
Direction.WEST, new UV(6, 0, 16, 16),
Direction.UP, new UV(2, 0, 16, 16),
Direction.DOWN, new UV(4, 0, 16, 16)
);
this.bottomRight = createCuboid(10, 0, 7, 2, 1, 2, bottomRightUVs);
// Main left part
Map<Direction, UV> mainLeftUVs = Map.of(
Direction.NORTH, new UV(8, 0, 16, 16),
Direction.EAST, new UV(4, 0, 16, 16),
Direction.SOUTH, new UV(0, 0, 16, 16),
Direction.WEST, new UV(12, 0, 16, 16),
Direction.UP, new UV(4, 0, 16, 16),
Direction.DOWN, new UV(4, 0, 16, 16)
);
this.mainLeft = createCuboid(3, 1, 6, 4, 10, 4, mainLeftUVs);
// Main right part
Map<Direction, UV> mainRightUVs = Map.of(
Direction.NORTH, new UV(8, 0, 16, 16),
Direction.EAST, new UV(4, 0, 16, 16),
Direction.SOUTH, new UV(0, 0, 16, 16),
Direction.WEST, new UV(12, 0, 16, 16),
Direction.UP, new UV(4, 0, 16, 16),
Direction.DOWN, new UV(4, 0, 16, 16)
);
this.mainRight = createCuboid(9, 1, 6, 4, 10, 4, mainRightUVs);
}
private ModelPart createCuboid(int x, int y, int z, int width, int height, int depth, Map<Direction, UV> faceUVs) {
List<ModelPart.Cuboid> cuboids = new ArrayList<>();
for (Direction face : faceUVs.keySet()) {
UV uv = faceUVs.get(face);
ModelPart.Cuboid cuboid = new ModelPart.Cuboid(
uv.u, uv.v,
x, y, z,
width, height, depth,
0, 0, 0,
false,
uv.uWidth, uv.vHeight,
EnumSet.of(face)
);
cuboids.add(cuboid);
}
return new ModelPart(cuboids, Map.of());
}
public record UV(int u, int v, int uWidth, int vHeight) {
}
@Override
public void setAngles(T entity, float limbAngle, float limbDistance, float animationProgress, float headYaw, float headPitch) {}
@Override
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) {
middle.render(matrices, vertices, light, overlay, red, green, blue, alpha);
bottomLeft.render(matrices, vertices, light, overlay, red, green, blue, alpha);
bottomRight.render(matrices, vertices, light, overlay, red, green, blue, alpha);
mainLeft.render(matrices, vertices, light, overlay, red, green, blue, alpha);
mainRight.render(matrices, vertices, light, overlay, red, green, blue, alpha);
}
}

View file

@ -0,0 +1,71 @@
package konhaiii.powered_jetpacks.renderers;
import dev.emi.trinkets.api.TrinketsApi;
import konhaiii.powered_jetpacks.PoweredJetpacks;
import konhaiii.powered_jetpacks.item.ModItems;
import konhaiii.powered_jetpacks.item.special.JetpackItem;
import konhaiii.powered_jetpacks.models.JetpackModel;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.Pair;
import net.minecraft.util.math.RotationAxis;
public class JetpackRenderer<T extends LivingEntity, M extends EntityModel<T>> extends FeatureRenderer<T, M> {
private final JetpackModel<T> jetpackModel;
private static final Identifier BASIC_JETPACK_TEXTURE = new Identifier(PoweredJetpacks.MOD_ID, "textures/jetpack/basic_jetpack.png");
private static final Identifier ADVANCED_JETPACK_TEXTURE = new Identifier(PoweredJetpacks.MOD_ID, "textures/jetpack/advanced_jetpack.png");
private static final Identifier INDUSTRIAL_JETPACK_TEXTURE = new Identifier(PoweredJetpacks.MOD_ID, "textures/jetpack/industrial_jetpack.png");
public JetpackRenderer(FeatureRendererContext<T, M> context) {
super(context);
this.jetpackModel = new JetpackModel<>();
}
@Override
public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
ItemStack chestStack = entity.getEquippedStack(EquipmentSlot.CHEST);
ItemStack backStack = TrinketsApi.getTrinketComponent(entity).map(component ->
component.getEquipped(stack -> stack.getItem() instanceof JetpackItem)
.stream()
.findFirst()
.map(Pair::getRight)
.orElse(ItemStack.EMPTY)
).orElse(ItemStack.EMPTY);
ItemStack jetpackStack = isValidJetpack(chestStack) ? chestStack : (!backStack.isEmpty() ? backStack : ItemStack.EMPTY);
if (!jetpackStack.isEmpty()) {
matrixStack.push();
matrixStack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180));
matrixStack.translate(-0.5D, -0.7D, -0.8D);
this.jetpackModel.setAngles(entity, limbAngle, limbDistance, animationProgress, headYaw, headPitch);
VertexConsumer vertexConsumer;
if (jetpackStack.getItem() == ModItems.BASIC_JETPACK) {
vertexConsumer = vertexConsumerProvider.getBuffer(RenderLayer.getEntityCutout(BASIC_JETPACK_TEXTURE));
} else if (jetpackStack.getItem() == ModItems.ADVANCED_JETPACK) {
vertexConsumer = vertexConsumerProvider.getBuffer(RenderLayer.getEntityCutout(ADVANCED_JETPACK_TEXTURE));
} else {
vertexConsumer = vertexConsumerProvider.getBuffer(RenderLayer.getEntityCutout(INDUSTRIAL_JETPACK_TEXTURE));
}
this.jetpackModel.render(matrixStack, vertexConsumer, light, OverlayTexture.DEFAULT_UV, 1.0F, 1.0F, 1.0F, 1.0F);
matrixStack.pop();
}
}
private boolean isValidJetpack(ItemStack stack) {
return stack.getItem() instanceof JetpackItem;
}
}

View file

@ -0,0 +1,11 @@
{
"required": true,
"package": "konhaiii.powered_jetpacks.mixin.client",
"compatibilityLevel": "JAVA_17",
"client": [
"ClientPlayerEntityMixin"
],
"injectors": {
"defaultRequire": 1
}
}