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
}
}

View file

@ -0,0 +1,29 @@
package konhaiii.powered_jetpacks;
import konhaiii.powered_jetpacks.config.ModConfig;
import konhaiii.powered_jetpacks.item.ModItems;
import konhaiii.powered_jetpacks.packet.JetpackPacket;
import konhaiii.powered_jetpacks.sounds.ModSounds;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PoweredJetpacks implements ModInitializer {
public static final String MOD_ID = "powered_jetpacks";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static ModConfig config;
@Override
public void onInitialize() {
LOGGER.info("Initialize");
config = ModConfig.loadConfig();
ModItems.initialize();
ModSounds.initialize();
ServerPlayNetworking.registerGlobalReceiver(JetpackPacket.getPacketId(), (server, player, handler, buf, responseSender) -> {
JetpackPacket packet = JetpackPacket.decode(buf);
server.execute(() -> JetpackPacket.handle(player, packet));
});
}
}

View file

@ -0,0 +1,60 @@
package konhaiii.powered_jetpacks.config;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import konhaiii.powered_jetpacks.PoweredJetpacks;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class ModConfig {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Path CONFIG_PATH = Path.of("config", "powered_jetpacks.json");
public int basicJetpackMaxEnergy = 10000;
public int basicJetpackInputEnergy = 128;
public float basicJetpackVerticalSpeed = 1.0f;
public float basicJetpackHorizontalSpeed = 0.0f;
public int basicJetpackEnergyCost = 2;
public int advancedJetpackMaxEnergy = 100000;
public int advancedJetpackInputEnergy = 2048;
public float advancedJetpackVerticalSpeed = 1.5f;
public float advancedJetpackHorizontalSpeed = 0.05f;
public int advancedJetpackEnergyCost = 16;
public int industrialJetpackMaxEnergy = 1000000;
public int industrialJetpackInputEnergy = 8192;
public float industrialJetpackVerticalSpeed = 2.0f;
public float industrialJetpackHorizontalSpeed = 0.1f;
public int industrialJetpackEnergyCost = 64;
public static ModConfig loadConfig() {
if (!Files.exists(CONFIG_PATH)) {
ModConfig defaultConfig = new ModConfig();
defaultConfig.saveConfig();
return defaultConfig;
}
try (Reader reader = Files.newBufferedReader(CONFIG_PATH)) {
return GSON.fromJson(reader, ModConfig.class);
} catch (IOException | JsonSyntaxException exception) {
PoweredJetpacks.LOGGER.error(exception.getMessage());
return new ModConfig();
}
}
public void saveConfig() {
try {
Files.createDirectories(CONFIG_PATH.getParent());
try (Writer writer = Files.newBufferedWriter(CONFIG_PATH, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
GSON.toJson(this, writer);
}
} catch (IOException exception) {
PoweredJetpacks.LOGGER.error(exception.getMessage());
}
}
}

View file

@ -0,0 +1,90 @@
package konhaiii.powered_jetpacks.energy;
import java.text.DecimalFormat;
import java.util.Locale;
public class EnergySystem {
public static final String EnergyAbbreviation = "E";
public static final char[] magnitude = new char[] { 'k', 'M', 'G', 'T' };
public static String getEnergyUnitDiminished(double energy) {
return getFinalString(energy, true, true);
}
public static String getEnergyDiminished(double energy) {
return getFinalString(energy, false, true);
}
public static String getEnergyUnit(double energy) {
return getFinalString(energy, true, false);
}
public static String getEnergy(double energy) {
return getFinalString(energy, false, false);
}
private static String getFinalString(double energy, boolean showUnit, boolean showDiminutive) {
String returnValue = "";
double finalValue = 0f;
double value = energy;
int i = 0;
boolean doFormat = true;
boolean showMagnitude = true;
if (showDiminutive) {
if (energy < 1000) {
doFormat = false;
showMagnitude = false;
finalValue = value;
} else if (value >= 1000) {
for (i = 0; ; i++) {
if (value < 10000 && value % 1000 >= 100) {
finalValue = Math.floor(value / 1000);
finalValue += ((float) value % 1000) / 1000;
break;
}
value /= 1000;
if (value < 1000) {
finalValue = value;
break;
}
}
}
if (i > 10) {
doFormat = false;
showMagnitude = false;
} else if (i > 3) {
finalValue = energy;
showMagnitude = false;
}
} else {
finalValue = value;
showMagnitude = false;
}
if (doFormat) {
DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH);
returnValue += formatter.format(finalValue);
int idx = returnValue.lastIndexOf(formatter.getDecimalFormatSymbols().getDecimalSeparator());
if (idx > 0){
returnValue = returnValue.substring(0, idx + 2);
}
}
else {
if (i>10){
returnValue += "";
}
else {
returnValue += value;
}
}
if (showMagnitude) {
returnValue += magnitude[i];
}
if (showUnit) {
returnValue += " " + EnergyAbbreviation;
}
return returnValue;
}
}

View file

@ -0,0 +1,52 @@
package konhaiii.powered_jetpacks.item;
import konhaiii.powered_jetpacks.PoweredJetpacks;
import konhaiii.powered_jetpacks.item.special.JetpackItem;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
public class ModItems {
public static Item register(Item item, String id) {
Identifier itemID = new Identifier(PoweredJetpacks.MOD_ID, id);
return Registry.register(Registries.ITEM, itemID, item);
}
public static final Item BASIC_JETPACK = register(
new JetpackItem(PoweredJetpacks.config.basicJetpackMaxEnergy, PoweredJetpacks.config.basicJetpackInputEnergy, PoweredJetpacks.config.basicJetpackVerticalSpeed, PoweredJetpacks.config.basicJetpackHorizontalSpeed, PoweredJetpacks.config.basicJetpackEnergyCost),
"basic_jetpack"
);
public static final Item ADVANCED_JETPACK = register(
new JetpackItem(PoweredJetpacks.config.advancedJetpackMaxEnergy, PoweredJetpacks.config.advancedJetpackInputEnergy, PoweredJetpacks.config.advancedJetpackVerticalSpeed, PoweredJetpacks.config.advancedJetpackHorizontalSpeed, PoweredJetpacks.config.advancedJetpackEnergyCost),
"advanced_jetpack"
);
public static final Item INDUSTRIAL_JETPACK = register(
new JetpackItem(PoweredJetpacks.config.industrialJetpackMaxEnergy, PoweredJetpacks.config.industrialJetpackInputEnergy, PoweredJetpacks.config.industrialJetpackVerticalSpeed, PoweredJetpacks.config.industrialJetpackHorizontalSpeed, PoweredJetpacks.config.industrialJetpackEnergyCost),
"industrial_jetpack"
);
public static void initialize() {
Registry.register(Registries.ITEM_GROUP, ITEM_GROUP_KEY, ITEM_GROUP);
ItemGroupEvents.modifyEntriesEvent(ITEM_GROUP_KEY).register(itemGroup -> {
itemGroup.add(BASIC_JETPACK);
itemGroup.add(ADVANCED_JETPACK);
itemGroup.add(INDUSTRIAL_JETPACK);
});
}
public static final RegistryKey<ItemGroup> ITEM_GROUP_KEY = RegistryKey.of(Registries.ITEM_GROUP.getKey(), new Identifier(PoweredJetpacks.MOD_ID, "item_group"));
public static final ItemGroup ITEM_GROUP = FabricItemGroup.builder()
.icon(() -> new ItemStack(BASIC_JETPACK))
.displayName(Text.translatable("itemGroup.powered_jetpacks"))
.build();
}

View file

@ -0,0 +1,91 @@
package konhaiii.powered_jetpacks.item.special;
import dev.emi.trinkets.api.TrinketItem;
import net.minecraft.block.DispenserBlock;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.Equipment;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;
import team.reborn.energy.api.base.SimpleEnergyItem;
public class JetpackItem extends TrinketItem implements SimpleEnergyItem, Equipment {
private final int maxEnergy;
private final int inputEnergy;
private final float flightPower;
private final float flightSpeed;
private final int energyCost;
public JetpackItem(int maxEnergy, int inputEnergy, float flightPower, float flightSpeed, int energyCost) {
super(new Settings().maxCount(1));
this.maxEnergy = maxEnergy;
this.inputEnergy = inputEnergy;
this.flightPower = flightPower;
this.flightSpeed = flightSpeed;
this.energyCost = energyCost;
DispenserBlock.registerBehavior(this, ArmorItem.DISPENSER_BEHAVIOR);
}
@Override
public long getEnergyCapacity(ItemStack itemStack) {
return maxEnergy;
}
@Override
public long getEnergyMaxInput(ItemStack itemStack) {
return inputEnergy;
}
@Override
public long getEnergyMaxOutput(ItemStack itemStack) {
return 0;
}
@Override
public boolean isItemBarVisible(ItemStack stack) {
return true;
}
@Override
public int getItemBarStep(ItemStack stack) {
return Math.round(getStoredEnergy(stack) * 13.f / getEnergyCapacity(stack));
}
@Override
public int getItemBarColor(ItemStack stack) {
return 0xff8006;
}
@Override
public EquipmentSlot getSlotType() {
return EquipmentSlot.CHEST;
}
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
return this.equipAndSwap(this, world, user, hand);
}
public void extractEnergy(ItemStack stack, boolean simulate) {
long energyStored = getStoredEnergy(stack);
long energyExtracted = Math.min(energyStored, energyCost);
if (!simulate) {
setStoredEnergy(stack, energyStored - energyExtracted);
}
}
public double addToVerticalVelocity(double velocityY) {
return Math.min(velocityY + (0.1*flightPower), (0.25*flightPower));
}
public float getFlightSpeed() {
return flightSpeed;
}
public int getEnergyCost() {
return energyCost;
}
}

View file

@ -0,0 +1,93 @@
package konhaiii.powered_jetpacks.packet;
import dev.emi.trinkets.api.TrinketsApi;
import konhaiii.powered_jetpacks.PoweredJetpacks;
import konhaiii.powered_jetpacks.item.special.JetpackItem;
import konhaiii.powered_jetpacks.sounds.ModSounds;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Identifier;
import net.minecraft.util.Pair;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public record JetpackPacket(boolean shouldPlaySound) {
private static final Identifier PACKET_ID = new Identifier(PoweredJetpacks.MOD_ID, "jetpack_packet");
public static void encode(JetpackPacket packet, PacketByteBuf buf) {
buf.writeBoolean(packet.shouldPlaySound);
}
public static JetpackPacket decode(PacketByteBuf buf) {
return new JetpackPacket(buf.readBoolean());
}
public static void handle(ServerPlayerEntity player, JetpackPacket packet) {
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) {
JetpackItem jetpack = (JetpackItem) jetpackStack.getItem();
player.fallDistance = 0;
World world = player.getWorld();
BlockPos blockPos = player.getBlockPos();
if (packet.shouldPlaySound()) {
if (!world.isClient) {
world.playSound(null, blockPos, ModSounds.JETPACK_SOUND, SoundCategory.PLAYERS, 1f, 1f);
}
}
Vec3d lookVec = player.getRotationVector();
double offsetBack = -0.25;
double offsetSide = 0.2;
double centerX = player.getX() + lookVec.x * offsetBack;
double centerY = player.getY() + 0.75;
double centerZ = player.getZ() + lookVec.z * offsetBack;
double leftX = centerX + lookVec.z * offsetSide;
double leftZ = centerZ - lookVec.x * offsetSide;
double rightX = centerX - lookVec.z * offsetSide;
double rightZ = centerZ + lookVec.x * offsetSide;
((ServerWorld) world).spawnParticles(ParticleTypes.FLAME, leftX, centerY, leftZ,1,0,0,0,0.1);
((ServerWorld) world).spawnParticles(ParticleTypes.SMOKE, leftX, centerY, leftZ,1,0,0,0,0.1);
((ServerWorld) world).spawnParticles(ParticleTypes.FLAME, rightX, centerY, rightZ,1,0,0,0,0.1);
((ServerWorld) world).spawnParticles(ParticleTypes.SMOKE, rightX, centerY, rightZ,1,0,0,0,0.1);
jetpack.extractEnergy(jetpackStack, false);
}
}
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;
}
public static Identifier getPacketId() {
return PACKET_ID;
}
}

View file

@ -0,0 +1,19 @@
package konhaiii.powered_jetpacks.sounds;
import konhaiii.powered_jetpacks.PoweredJetpacks;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
public class ModSounds {
public static final SoundEvent JETPACK_SOUND = registerSoundEvent();
private static SoundEvent registerSoundEvent() {
Identifier id = new Identifier(PoweredJetpacks.MOD_ID, "jetpack_use");
return Registry.register(Registries.SOUND_EVENT, id, SoundEvent.of(id));
}
public static void initialize() {
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,022 B

View file

@ -0,0 +1,12 @@
{
"item.powered_jetpacks.basic_jetpack": "Basic Jetpack",
"item.powered_jetpacks.advanced_jetpack": "Advanced Jetpack",
"item.powered_jetpacks.industrial_jetpack": "Industrial Jetpack",
"itemGroup.powered_jetpacks": "Powered Jetpacks",
"tooltip.powered_jetpacks.energy_meter": "%s/%s",
"tooltip.powered_jetpacks.power_charged": "Charged",
"tooltip.powered_jetpacks.input_rate": "Input Rate",
"tooltip.powered_jetpacks.output_rate": "Output Rate",
"tooltip.powered_jetpacks.transfer_rate": "Transfer Rate",
"hud.powered_jetpacks.jetpack_power": "Jetpack Power: "
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "powered_jetpacks:item/advanced_jetpack"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "powered_jetpacks:item/basic_jetpack"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "powered_jetpacks:item/industrial_jetpack"
}
}

View file

@ -0,0 +1,5 @@
{
"jetpack_use": {
"sounds": ["powered_jetpacks:jetpack_use"]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

View file

@ -0,0 +1,35 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_redstone": {
"conditions": {
"items": [
{
"items": [
"minecraft:redstone"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "powered_jetpacks:advanced_jetpack"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_redstone",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"powered_jetpacks:advanced_jetpack"
]
},
"sends_telemetry_event": false
}

View file

@ -0,0 +1,35 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_redstone": {
"conditions": {
"items": [
{
"items": [
"minecraft:redstone"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "powered_jetpacks:basic_jetpack"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_redstone",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"powered_jetpacks:basic_jetpack"
]
},
"sends_telemetry_event": false
}

View file

@ -0,0 +1,35 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_redstone": {
"conditions": {
"items": [
{
"items": [
"minecraft:redstone"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "powered_jetpacks:industrial_jetpack"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_redstone",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"powered_jetpacks:industrial_jetpack"
]
},
"sends_telemetry_event": false
}

View file

@ -0,0 +1,27 @@
{
"type": "minecraft:crafting_shaped",
"category": "equipment",
"key": {
"I": {
"item": "minecraft:gold_ingot"
},
"L": {
"item": "minecraft:leather"
},
"R": {
"item": "minecraft:redstone"
},
"B": {
"item": "minecraft:gold_block"
}
},
"pattern": [
"RIR",
"BLB",
"I I"
],
"result": {
"item": "powered_jetpacks:advanced_jetpack"
},
"show_notification": true
}

View file

@ -0,0 +1,27 @@
{
"type": "minecraft:crafting_shaped",
"category": "equipment",
"key": {
"I": {
"item": "minecraft:iron_ingot"
},
"L": {
"item": "minecraft:leather"
},
"R": {
"item": "minecraft:redstone"
},
"B": {
"item": "minecraft:iron_block"
}
},
"pattern": [
"RIR",
"BLB",
"I I"
],
"result": {
"item": "powered_jetpacks:basic_jetpack"
},
"show_notification": true
}

View file

@ -0,0 +1,27 @@
{
"type": "minecraft:crafting_shaped",
"category": "equipment",
"key": {
"I": {
"item": "minecraft:diamond"
},
"L": {
"item": "minecraft:leather"
},
"R": {
"item": "minecraft:redstone"
},
"B": {
"item": "minecraft:diamond_block"
}
},
"pattern": [
"RIR",
"BLB",
"I I"
],
"result": {
"item": "powered_jetpacks:industrial_jetpack"
},
"show_notification": true
}

View file

@ -0,0 +1,8 @@
{
"entities": [
"player"
],
"slots": [
"chest/back"
]
}

View file

@ -0,0 +1,8 @@
{
"replace": false,
"values": [
"powered_jetpacks:basic_jetpack",
"powered_jetpacks:advanced_jetpack",
"powered_jetpacks:industrial_jetpack"
]
}

View file

@ -0,0 +1,41 @@
{
"schemaVersion": 1,
"id": "powered_jetpacks",
"version": "${version}",
"name": "Powered Jetpacks",
"description": "Add energy-powered jetpacks to Minecraft.",
"authors": [
"Konhaiii"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"icon": "assets/powered_jetpacks/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"konhaiii.powered_jetpacks.PoweredJetpacks"
],
"client": [
"konhaiii.powered_jetpacks.PoweredJetpacksClient"
]
},
"mixins": [
"powered_jetpacks.mixins.json",
{
"config": "powered_jetpacks.client.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=0.16.14",
"minecraft": "~1.20.1",
"java": ">=17",
"fabric-api": "*"
},
"suggests": {
"another-mod": "*"
}
}

View file

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