add hover flying and creative fly
Some checks are pending
build / build (push) Waiting to run

This commit is contained in:
Soph :3 2025-10-07 13:04:51 +03:00
parent cc89a22c53
commit f3d9ec1ef6
8 changed files with 240 additions and 75 deletions

View file

@ -4,6 +4,16 @@ which was originally for 1.20.1. I've ported the mod to 1.21.8, but
there's still a major bug, which is if you're using the powered jetback in quirk armor, there's still a major bug, which is if you're using the powered jetback in quirk armor,
it won't show it on your player model. it won't show it on your player model.
This version also adds hover flying and creative flying.
Hover is by default bound to H, and creative fly is bound by default to J.
| Jetpack type | Hover | Creative |
|-------------- |------- |---------- |
| Basic | ❌ | ❌ |
| Advanced | ✅ | ❌ |
| Industrial | ✅ | ✅ |
# Powered Jetpacks # Powered Jetpacks
A simple fabric mod about jetpacks powered with E energy. A simple fabric mod about jetpacks powered with E energy.

View file

@ -15,7 +15,7 @@ maven_group=konhaiii.powered_jetpacks
archives_base_name=powered_jetpacks archives_base_name=powered_jetpacks
# Dependencies # Dependencies
energy_version=4.2.0 energy_version=4.2.1
trinkets_version=3.11.0-1.21.6 trinkets_version=3.11.0-1.21.6
fabric_version=0.134.0+1.21.8 fabric_version=0.134.0+1.21.8
cca_version=7.0.0-beta.1 cca_version=7.0.0-beta.1

View file

@ -1,27 +1,134 @@
package konhaiii.powered_jetpacks; package konhaiii.powered_jetpacks;
import konhaiii.powered_jetpacks.hud.JetpackHUD; import konhaiii.powered_jetpacks.hud.JetpackHUD;
import konhaiii.powered_jetpacks.item.special.JetpackItem;
import konhaiii.powered_jetpacks.models.JetpackModel; import konhaiii.powered_jetpacks.models.JetpackModel;
import konhaiii.powered_jetpacks.renderers.JetpackRenderer; import konhaiii.powered_jetpacks.renderers.JetpackRenderer;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback; import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback;
import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry; import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.hud.VanillaHudElements; import net.fabricmc.fabric.api.client.rendering.v1.hud.VanillaHudElements;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.InputUtil;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.tag.EntityTypeTags; import net.minecraft.registry.tag.EntityTypeTags;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Unique;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class PoweredJetpacksClient implements ClientModInitializer { public class PoweredJetpacksClient implements ClientModInitializer {
public static KeyBinding hoverKeybinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.powered_jetpack.hover",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_H,
"category.powered_jetpack"
));
public static KeyBinding creativeKeybinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.powered_jetpack.creative",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_J,
"category.powered_jetpack"
));
public static boolean toggledHover = false;
public static boolean toggledCreative = false;
public static ItemStack getJetpack(ClientPlayerEntity player) {
ItemStack chestStack = player.getEquippedStack(EquipmentSlot.CHEST);
ItemStack backStack = ItemStack.EMPTY;
if (PoweredJetpacks.isTrinketsLoaded) {
try {
Class<?> optionalClass = Class.forName("konhaiii.powered_jetpacks.compat.TrinketsServer");
Method getBackStackMethod = optionalClass.getMethod("getBackStack", LivingEntity.class);
backStack = (ItemStack) getBackStackMethod.invoke(null, player);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
IllegalAccessException e) {
PoweredJetpacks.LOGGER.error("ClientPlayerEntityMixin: Could not load Trinkets compat class.");
}
}
ItemStack jetpackStack = ItemStack.EMPTY;
if (isValidJetpack(chestStack)) {
jetpackStack = chestStack;
} else if (isValidJetpack(backStack)) {
jetpackStack = backStack;
}
return jetpackStack;
}
@Unique
private static boolean isValidJetpack(ItemStack stack) {
if (stack.getItem() instanceof JetpackItem jetpack) {
if (jetpack.getEnergyCost() <= 0) {
return true;
}
return jetpack.getStoredEnergy(stack) > 0;
}
return false;
}
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
ItemTooltipCallback.EVENT.register(new StackToolTipHandler()); ItemTooltipCallback.EVENT.register(new StackToolTipHandler());
ClientTickEvents.END_CLIENT_TICK.register(client -> {
while (creativeKeybinding.wasPressed()) {
if (client.player != null) {
ItemStack stack = getJetpack(client.player);
if (!stack.isEmpty() && ((JetpackItem)stack.getItem()).allowCreative) {
if (toggledHover) {
toggledHover = false;
client.player.setNoGravity(toggledHover);
}
toggledCreative = !toggledCreative;
if (!toggledCreative) {
client.player.getAbilities().allowFlying = false;
client.player.getAbilities().flying = false;
} else {
client.player.getAbilities().allowFlying = true;
client.player.getAbilities().flying = true;
client.player.sendAbilitiesUpdate();
}
}
}
}
});
ClientTickEvents.END_CLIENT_TICK.register(client -> {
while (hoverKeybinding.wasPressed()) {
if (client.player != null) {
ItemStack stack = getJetpack(client.player);
if (!stack.isEmpty() && ((JetpackItem)stack.getItem()).allowHover) {
if (toggledCreative) {
toggledCreative = false;
client.player.getAbilities().allowFlying = false;
client.player.getAbilities().flying = false;
}
toggledHover = !toggledHover;
client.player.setNoGravity(toggledHover);
}
}
}
});
LivingEntityFeatureRendererRegistrationCallback.EVENT.register( LivingEntityFeatureRendererRegistrationCallback.EVENT.register(
(entityType, entityRenderer, registrationHelper, context) -> { (entityType, entityRenderer, registrationHelper, context) -> {
if (entityRenderer != null && entityType == EntityType.PLAYER) { if (entityRenderer != null && entityType == EntityType.PLAYER) {

View file

@ -1,6 +1,7 @@
package konhaiii.powered_jetpacks.hud; package konhaiii.powered_jetpacks.hud;
import konhaiii.powered_jetpacks.PoweredJetpacks; import konhaiii.powered_jetpacks.PoweredJetpacks;
import konhaiii.powered_jetpacks.PoweredJetpacksClient;
import konhaiii.powered_jetpacks.item.special.JetpackItem; import konhaiii.powered_jetpacks.item.special.JetpackItem;
import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElement; import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElement;
import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry; import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry;
@ -59,8 +60,22 @@ public class JetpackHUD implements HudElement {
TextRenderer textRenderer = client.textRenderer; TextRenderer textRenderer = client.textRenderer;
int percentage = percentage(energy, maxEnergy); int percentage = percentage(energy, maxEnergy);
String energyText = translate("hud.powered_jetpacks.jetpack_power").concat(String.valueOf(percentage)).concat("%"); String energyText = translate("hud.powered_jetpacks.jetpack_power").concat(String.valueOf(percentage)).concat("%");
drawContext.drawTextWithShadow(textRenderer, energyText, 10, 10, 0xFFFFFFFF); drawContext.drawTextWithShadow(textRenderer, energyText, 10, 10, 0xFFFFFFFF);
String hoverStatus = PoweredJetpacksClient.toggledHover ? "ON" : "OFF";
String creativeStatus = PoweredJetpacksClient.toggledCreative ? "ON" : "OFF";
if(!jetpack.allowCreative) {
creativeStatus = "DISABLED";
}
if(!jetpack.allowHover) {
hoverStatus = "DISABLED";
}
if(jetpack.allowHover || jetpack.allowCreative) {
String statusText = translate("hud.powered_jetpacks.status", hoverStatus, creativeStatus);
drawContext.drawTextWithShadow(textRenderer, statusText, 10, 20, 0xFFFFFFFF);
}
} }
} }
} }

View file

@ -1,15 +1,11 @@
package konhaiii.powered_jetpacks.mixin.client; package konhaiii.powered_jetpacks.mixin.client;
import konhaiii.powered_jetpacks.PoweredJetpacks; import konhaiii.powered_jetpacks.PoweredJetpacksClient;
import konhaiii.powered_jetpacks.item.special.JetpackItem; import konhaiii.powered_jetpacks.item.special.JetpackItem;
import konhaiii.powered_jetpacks.packet.JetpackPacket; import konhaiii.powered_jetpacks.packet.JetpackPacket;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 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.client.network.ClientPlayerEntity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@ -17,75 +13,103 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Mixin(ClientPlayerEntity.class) @Mixin(ClientPlayerEntity.class)
public abstract class ClientPlayerEntityMixin { public abstract class ClientPlayerEntityMixin {
@Unique @Unique
private int soundCounter = 8; private int soundCounter = 8;
@Inject(method = "tick", at = @At("HEAD")) @Inject(method = "tick", at = @At("HEAD"))
private void onTick(CallbackInfo ci) { private void onTick(CallbackInfo ci) {
ClientPlayerEntity player = (ClientPlayerEntity) (Object) this; ClientPlayerEntity player = (ClientPlayerEntity) (Object) this;
if (player.input.playerInput.jump()) {
ItemStack chestStack = player.getEquippedStack(EquipmentSlot.CHEST);
ItemStack backStack = ItemStack.EMPTY;
if (PoweredJetpacks.isTrinketsLoaded) { ItemStack jetpackStack = PoweredJetpacksClient.getJetpack(player);
try {
Class<?> optionalClass = Class.forName("konhaiii.powered_jetpacks.compat.TrinketsServer");
Method getBackStackMethod = optionalClass.getMethod("getBackStack", LivingEntity.class);
backStack = (ItemStack) getBackStackMethod.invoke(null, player);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
IllegalAccessException e) {
PoweredJetpacks.LOGGER.error("ClientPlayerEntityMixin: Could not load Trinkets compat class.");
}
}
ItemStack jetpackStack = null; if (!jetpackStack.isEmpty()) {
if (isValidJetpack(chestStack)) { JetpackPacket packet;
jetpackStack = chestStack;
} else if (isValidJetpack(backStack)) {
jetpackStack = backStack;
}
if (jetpackStack != null) { JetpackItem jetpack = (JetpackItem) jetpackStack.getItem();
JetpackPacket packet; if (PoweredJetpacksClient.toggledCreative && !jetpack.allowCreative) {
PoweredJetpacksClient.toggledCreative = false;
player.getAbilities().allowFlying = false;
player.getAbilities().flying = false;
}
JetpackItem jetpack = (JetpackItem) jetpackStack.getItem(); if (PoweredJetpacksClient.toggledHover && !jetpack.allowHover) {
Vec3d velocity = player.getVelocity(); PoweredJetpacksClient.toggledHover = false;
float horizontalBoost = jetpack.getFlightSpeed(); player.setNoGravity(false);
player.setVelocity( }
velocity.x + (player.getRotationVector().x * horizontalBoost),
jetpack.addToVerticalVelocity(player.getVelocity().y),
velocity.z + (player.getRotationVector().z * horizontalBoost)
);
player.fallDistance = 0;
soundCounter++; Vec3d velocity = player.getVelocity();
if (soundCounter >= 8) {
packet = new JetpackPacket(true); boolean sendPacket = PoweredJetpacksClient.toggledCreative && !player.isOnGround();
soundCounter = 0;
} else { if (PoweredJetpacksClient.toggledHover && jetpack.allowHover && !player.isOnGround()) {
packet = new JetpackPacket(false); double vertical = 0.0;
} double hoverUpSpeed = 0.5;
ClientPlayNetworking.send(packet); double hoverDownSpeed = 0.4;
} else if (soundCounter != 8) { double horizontalSpeed = 0.35;
soundCounter = 8; double inertia = 0.25;
}
} else if (soundCounter != 8) { if (player.input.playerInput.jump()) {
soundCounter = 8; vertical += hoverUpSpeed;
} } else if (player.input.playerInput.sneak()) {
} vertical -= hoverDownSpeed;
@Unique } else {
private boolean isValidJetpack(ItemStack stack) { vertical = 0.0;
if (stack.getItem() instanceof JetpackItem jetpack) { }
if (jetpack.getEnergyCost() <= 0) {
return true; Vec3d lookDir = player.getRotationVector().normalize();
} Vec3d strafeDir = new Vec3d(-lookDir.z, 0, lookDir.x).normalize();
return jetpack.getStoredEnergy(stack) > 0; Vec3d move = Vec3d.ZERO;
}
return false; if (player.input.playerInput.forward()) move = move.add(lookDir);
} if (player.input.playerInput.backward()) move = move.subtract(lookDir);
if (player.input.playerInput.right()) move = move.add(strafeDir);
if (player.input.playerInput.left()) move = move.subtract(strafeDir);
if (move.lengthSquared() > 0) {
move = move.normalize().multiply(horizontalSpeed);
}
Vec3d targetVel = new Vec3d(move.x, vertical, move.z);
Vec3d currentVel = player.getVelocity();
Vec3d blended = currentVel.multiply(inertia).add(targetVel.multiply(1 - inertia));
player.setVelocity(blended);
sendPacket = true;
} else if (player.input.playerInput.jump()) {
float horizontalBoost = jetpack.getFlightSpeed();
player.setVelocity(velocity.x + (player.getRotationVector().x * horizontalBoost), jetpack.addToVerticalVelocity(player.getVelocity().y), velocity.z + (player.getRotationVector().z * horizontalBoost));
sendPacket = true;
}
player.fallDistance = 0;
if (sendPacket) {
soundCounter++;
if (soundCounter >= 8) {
packet = new JetpackPacket(true);
soundCounter = 0;
} else {
packet = new JetpackPacket(false);
}
ClientPlayNetworking.send(packet);
}
} else {
if (PoweredJetpacksClient.toggledCreative) {
PoweredJetpacksClient.toggledCreative = false;
player.getAbilities().allowFlying = false;
player.getAbilities().flying = false;
}
if (PoweredJetpacksClient.toggledHover) {
PoweredJetpacksClient.toggledHover = false;
player.setNoGravity(false);
}
}
if (soundCounter != 8) {
soundCounter = 8;
}
}
} }

View file

@ -35,19 +35,19 @@ public class ModItems {
public static final Item BASIC_JETPACK = register( public static final Item BASIC_JETPACK = register(
"basic_jetpack", "basic_jetpack",
settings -> new JetpackItem(settings, PoweredJetpacks.config.basicJetpackMaxEnergy, PoweredJetpacks.config.basicJetpackInputEnergy, PoweredJetpacks.config.basicJetpackVerticalSpeed, PoweredJetpacks.config.basicJetpackHorizontalSpeed, PoweredJetpacks.config.basicJetpackEnergyCost), settings -> new JetpackItem(settings, PoweredJetpacks.config.basicJetpackMaxEnergy, PoweredJetpacks.config.basicJetpackInputEnergy, PoweredJetpacks.config.basicJetpackVerticalSpeed, PoweredJetpacks.config.basicJetpackHorizontalSpeed, PoweredJetpacks.config.basicJetpackEnergyCost, false, false),
new Item.Settings().armor(ArmorMaterials.IRON, EquipmentType.CHESTPLATE) new Item.Settings().armor(ArmorMaterials.IRON, EquipmentType.CHESTPLATE)
); );
public static final Item ADVANCED_JETPACK = register( public static final Item ADVANCED_JETPACK = register(
"advanced_jetpack", "advanced_jetpack",
settings -> new JetpackItem(settings, PoweredJetpacks.config.advancedJetpackMaxEnergy, PoweredJetpacks.config.advancedJetpackInputEnergy, PoweredJetpacks.config.advancedJetpackVerticalSpeed, PoweredJetpacks.config.advancedJetpackHorizontalSpeed, PoweredJetpacks.config.advancedJetpackEnergyCost), settings -> new JetpackItem(settings, PoweredJetpacks.config.advancedJetpackMaxEnergy, PoweredJetpacks.config.advancedJetpackInputEnergy, PoweredJetpacks.config.advancedJetpackVerticalSpeed, PoweredJetpacks.config.advancedJetpackHorizontalSpeed, PoweredJetpacks.config.advancedJetpackEnergyCost, true, false),
new Item.Settings().armor(ArmorMaterials.DIAMOND, EquipmentType.CHESTPLATE) new Item.Settings().armor(ArmorMaterials.DIAMOND, EquipmentType.CHESTPLATE)
); );
public static final Item INDUSTRIAL_JETPACK = register( public static final Item INDUSTRIAL_JETPACK = register(
"industrial_jetpack", "industrial_jetpack",
settings -> new JetpackItem(settings, PoweredJetpacks.config.industrialJetpackMaxEnergy, PoweredJetpacks.config.industrialJetpackInputEnergy, PoweredJetpacks.config.industrialJetpackVerticalSpeed, PoweredJetpacks.config.industrialJetpackHorizontalSpeed, PoweredJetpacks.config.industrialJetpackEnergyCost), settings -> new JetpackItem(settings, PoweredJetpacks.config.industrialJetpackMaxEnergy, PoweredJetpacks.config.industrialJetpackInputEnergy, PoweredJetpacks.config.industrialJetpackVerticalSpeed, PoweredJetpacks.config.industrialJetpackHorizontalSpeed, PoweredJetpacks.config.industrialJetpackEnergyCost, true, true),
new Item.Settings().armor(ArmorMaterials.NETHERITE, EquipmentType.CHESTPLATE) new Item.Settings().armor(ArmorMaterials.NETHERITE, EquipmentType.CHESTPLATE)
); );

View file

@ -21,14 +21,19 @@ public class JetpackItem extends Item implements SimpleEnergyItem {
private final float flightPower; private final float flightPower;
private final float flightSpeed; private final float flightSpeed;
private final int energyCost; private final int energyCost;
public final boolean allowHover;
public final boolean allowCreative;
public JetpackItem(Settings settings, int maxEnergy, int inputEnergy, float flightPower, float flightSpeed, int energyCost) { public JetpackItem(Settings settings, int maxEnergy, int inputEnergy, float flightPower, float flightSpeed, int energyCost, boolean allowHover, boolean allowCreative) {
super(settings.maxCount(1)); super(settings.maxCount(1));
this.maxEnergy = maxEnergy; this.maxEnergy = maxEnergy;
this.inputEnergy = inputEnergy; this.inputEnergy = inputEnergy;
this.flightPower = flightPower; this.flightPower = flightPower;
this.flightSpeed = flightSpeed; this.flightSpeed = flightSpeed;
this.energyCost = energyCost; this.energyCost = energyCost;
this.allowHover = allowHover;
this.allowCreative = allowCreative;
DispenserBlock.registerBehavior(this, DispenserBehavior.NOOP); DispenserBlock.registerBehavior(this, DispenserBehavior.NOOP);
} }

View file

@ -8,5 +8,9 @@
"tooltip.powered_jetpacks.input_rate": "Input Rate", "tooltip.powered_jetpacks.input_rate": "Input Rate",
"tooltip.powered_jetpacks.output_rate": "Output Rate", "tooltip.powered_jetpacks.output_rate": "Output Rate",
"tooltip.powered_jetpacks.transfer_rate": "Transfer Rate", "tooltip.powered_jetpacks.transfer_rate": "Transfer Rate",
"hud.powered_jetpacks.jetpack_power": "Jetpack Power: " "hud.powered_jetpacks.jetpack_power": "Jetpack Power: ",
"key.powered_jetpack.hover": "Hover",
"key.powered_jetpack.creative": "Creative Fly",
"category.powered_jetpack": "Powered Jetpacks",
"hud.powered_jetpacks.status": "HOVER: %s, CREATIVE: %s"
} }