diff --git a/src/generated/resources/.cache/8202586f691eec5ad0bb88d13a278951d0c130fb b/src/generated/resources/.cache/8202586f691eec5ad0bb88d13a278951d0c130fb index 331858e4..c1938827 100644 --- a/src/generated/resources/.cache/8202586f691eec5ad0bb88d13a278951d0c130fb +++ b/src/generated/resources/.cache/8202586f691eec5ad0bb88d13a278951d0c130fb @@ -1,2 +1,2 @@ -// 1.21.1 2024-09-12T11:15:53.905581 Languages: en_us for mod: justdirethings -80317086157715c924dcacf357e72efab49d8580 assets/justdirethings/lang/en_us.json +// 1.21.1 2024-09-12T13:18:00.5916321 Languages: en_us for mod: justdirethings +370a2cb7525636b760559d6e7176176093558760 assets/justdirethings/lang/en_us.json diff --git a/src/generated/resources/assets/justdirethings/lang/en_us.json b/src/generated/resources/assets/justdirethings/lang/en_us.json index 126f6692..32b32bf5 100644 --- a/src/generated/resources/assets/justdirethings/lang/en_us.json +++ b/src/generated/resources/assets/justdirethings/lang/en_us.json @@ -408,6 +408,7 @@ "justdirethings.screen.renderarea": "Render Area", "justdirethings.screen.renderparadox": "Render Paradox", "justdirethings.screen.requireequipped": "Activate if Equipped", + "justdirethings.screen.retrieveexp": "Retrieve Exp", "justdirethings.screen.rightclicksettings": "Right Click for Settings", "justdirethings.screen.save_close": "Save and Close", "justdirethings.screen.senditems": "Push Items", @@ -419,6 +420,7 @@ "justdirethings.screen.snapshotarea": "Snapshot Area", "justdirethings.screen.sneak-click": "Sneak Click", "justdirethings.screen.stay_open": "Stay Open", + "justdirethings.screen.storeexp": "Store Exp", "justdirethings.screen.swapitems": "Swap Items", "justdirethings.screen.target-adult": "Target Adult", "justdirethings.screen.target-air": "Target Air", diff --git a/src/main/java/com/direwolf20/justdirethings/client/screens/ExperienceHolderScreen.java b/src/main/java/com/direwolf20/justdirethings/client/screens/ExperienceHolderScreen.java index c6cf468e..41fc0623 100644 --- a/src/main/java/com/direwolf20/justdirethings/client/screens/ExperienceHolderScreen.java +++ b/src/main/java/com/direwolf20/justdirethings/client/screens/ExperienceHolderScreen.java @@ -1,17 +1,44 @@ package com.direwolf20.justdirethings.client.screens; import com.direwolf20.justdirethings.client.screens.basescreens.BaseMachineScreen; +import com.direwolf20.justdirethings.client.screens.standardbuttons.ToggleButtonFactory; +import com.direwolf20.justdirethings.common.blockentities.ExperienceHolderBE; import com.direwolf20.justdirethings.common.containers.ExperienceHolderContainer; +import com.direwolf20.justdirethings.common.network.data.ExperienceHolderPayload; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; +import net.neoforged.neoforge.network.PacketDistributor; public class ExperienceHolderScreen extends BaseMachineScreen { + private ExperienceHolderBE experienceHolderBE; + private int exp; public ExperienceHolderScreen(ExperienceHolderContainer container, Inventory inv, Component name) { super(container, inv, name); + if (container.baseMachineBE instanceof ExperienceHolderBE experienceHolderBE) { + this.experienceHolderBE = experienceHolderBE; + this.exp = experienceHolderBE.exp; + } } @Override public void init() { super.init(); + addRenderableWidget(ToggleButtonFactory.STOREEXPBUTTON(getGuiLeft() + 62, topSectionTop + 62, true, b -> { + int amt = 1; + if (Screen.hasControlDown()) + amt = -1; + else if (Screen.hasShiftDown()) + amt = amt * 10; + PacketDistributor.sendToServer(new ExperienceHolderPayload(true, amt)); + })); + addRenderableWidget(ToggleButtonFactory.EXTRACTEXPBUTTON(getGuiLeft() + 102, topSectionTop + 62, true, b -> { + int amt = 1; + if (Screen.hasControlDown()) + amt = -1; + else if (Screen.hasShiftDown()) + amt = amt * 10; + PacketDistributor.sendToServer(new ExperienceHolderPayload(false, amt)); + })); } } diff --git a/src/main/java/com/direwolf20/justdirethings/client/screens/standardbuttons/ToggleButtonFactory.java b/src/main/java/com/direwolf20/justdirethings/client/screens/standardbuttons/ToggleButtonFactory.java index 7d7ceb5a..8f91fc0d 100644 --- a/src/main/java/com/direwolf20/justdirethings/client/screens/standardbuttons/ToggleButtonFactory.java +++ b/src/main/java/com/direwolf20/justdirethings/client/screens/standardbuttons/ToggleButtonFactory.java @@ -49,6 +49,20 @@ public static ToggleButton ALLOWLISTBUTTON(int x, int y, boolean startingValue, return new ToggleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, ALLOW_LIST_TEXTURES, startingValue, onPress); } + private static final ResourceLocation STORE_EXP_BUTTON = ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/add.png"); + private static final Component STORE_EXP_BUTTON_LOCALIZATION = Component.translatable("justdirethings.screen.storeexp"); + + public static GrayscaleButton STOREEXPBUTTON(int x, int y, boolean startingValue, Button.OnPress onPress) { + return new GrayscaleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, STORE_EXP_BUTTON, STORE_EXP_BUTTON_LOCALIZATION, startingValue, onPress); + } + + private static final ResourceLocation EXTRACT_EXP_BUTTON = ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/remove.png"); + private static final Component EXTRACT_EXP_BUTTON_LOCALIZATION = Component.translatable("justdirethings.screen.retrieveexp"); + + public static GrayscaleButton EXTRACTEXPBUTTON(int x, int y, boolean startingValue, Button.OnPress onPress) { + return new GrayscaleButton(x, y, STANDARD_WIDTH, STANDARD_HEIGHT, EXTRACT_EXP_BUTTON, EXTRACT_EXP_BUTTON_LOCALIZATION, startingValue, onPress); + } + private static final ResourceLocation FILTER_ONLY = ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "textures/gui/buttons/allowlisttrue.png"); private static final Component FILTER_ONLY_LOCALIZATION = Component.translatable("justdirethings.screen.filteronlytrue"); diff --git a/src/main/java/com/direwolf20/justdirethings/common/blockentities/ExperienceHolderBE.java b/src/main/java/com/direwolf20/justdirethings/common/blockentities/ExperienceHolderBE.java index 051d4206..61079a36 100644 --- a/src/main/java/com/direwolf20/justdirethings/common/blockentities/ExperienceHolderBE.java +++ b/src/main/java/com/direwolf20/justdirethings/common/blockentities/ExperienceHolderBE.java @@ -5,11 +5,14 @@ import com.direwolf20.justdirethings.common.blockentities.basebe.BaseMachineBE; import com.direwolf20.justdirethings.common.blockentities.basebe.RedstoneControlledBE; import com.direwolf20.justdirethings.setup.Registration; +import com.direwolf20.justdirethings.util.ExperienceUtils; import com.direwolf20.justdirethings.util.interfacehelpers.AreaAffectingData; import com.direwolf20.justdirethings.util.interfacehelpers.FilterData; import com.direwolf20.justdirethings.util.interfacehelpers.RedstoneControlData; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -25,6 +28,7 @@ public class ExperienceHolderBE extends BaseMachineBE implements AreaAffectingBE public FilterData filterData = new FilterData(); public AreaAffectingData areaAffectingData = new AreaAffectingData(); public RedstoneControlData redstoneControlData = new RedstoneControlData(); + public int exp; public ExperienceHolderBE(BlockPos pPos, BlockState pBlockState) { super(Registration.ExperienceHolderBE.get(), pPos, pBlockState); @@ -45,6 +49,70 @@ public AreaAffectingData getAreaAffectingData() { return areaAffectingData; } + public void storeExp(Player player, int levelChange) { + if (levelChange == -1) { + // Move all experience from player + int totalExp = ExperienceUtils.getPlayerTotalExperience(player); + this.exp += totalExp; + player.giveExperiencePoints(-totalExp); // Removes all levels + } else if (levelChange > 0) { + // Handle fractional progress first, if the player is in the middle of a level + int expInCurrentLevel = (int) (player.experienceProgress * player.getXpNeededForNextLevel()); + + // If the player has partial progress within the current level, remove that first + if (expInCurrentLevel > 0) { + int expRemoved = ExperienceUtils.removePoints(player, expInCurrentLevel); + this.exp += expRemoved; + levelChange--; // We've already removed part of a level + } + + if (levelChange > 0) { + // Now remove the specified number of full levels + int expRemoved = ExperienceUtils.removeLevels(player, levelChange); + this.exp += expRemoved; + } + } + + markDirtyClient(); + } + + public void extractExp(Player player, int levelChange) { + if (exp == 0) return; // No experience in the block, exit early + + if (levelChange == -1) { + // Move all experience from block to player + int expToGive = exp; + player.giveExperiencePoints(expToGive); + this.exp = 0; // Remove all experience from the block + } else if (levelChange > 0) { + // Handle fractional progress first, if the player is in the middle of a level + if (roundUpToNextLevel(player)) + levelChange--; + + if (levelChange > 0 && this.exp > 0) { + // Give full levels based on the remaining levels requested + int expForNextLevels = ExperienceUtils.getTotalExperienceForLevel(player.experienceLevel + levelChange) - ExperienceUtils.getPlayerTotalExperience(player); + int expToGive = Math.min(this.exp, expForNextLevels); + player.giveExperiencePoints(expToGive); + this.exp -= expToGive; + roundUpToNextLevel(player); //Thanks Floating point math!! + } + } + + markDirtyClient(); + } + + public boolean roundUpToNextLevel(Player player) { + int expInCurrentLevel = (int) (player.experienceProgress * player.getXpNeededForNextLevel()); + if (expInCurrentLevel > 0) { + int expToGive = Math.min(exp, ExperienceUtils.getExpNeededForNextLevel(player)); + player.giveExperiencePoints(expToGive); + this.exp -= expToGive; + return true; + } + return false; + } + public void tickClient() { } @@ -77,4 +145,16 @@ private void handleExperience() { } } + + @Override + public void saveAdditional(CompoundTag tag, HolderLookup.Provider provider) { + super.saveAdditional(tag, provider); + tag.putInt("exp", exp); + } + + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider provider) { + super.loadAdditional(tag, provider); + exp = tag.getInt("exp"); + } } diff --git a/src/main/java/com/direwolf20/justdirethings/common/network/PacketHandler.java b/src/main/java/com/direwolf20/justdirethings/common/network/PacketHandler.java index 0a8bb021..11333759 100644 --- a/src/main/java/com/direwolf20/justdirethings/common/network/PacketHandler.java +++ b/src/main/java/com/direwolf20/justdirethings/common/network/PacketHandler.java @@ -38,6 +38,7 @@ public static void registerNetworking(final RegisterPayloadHandlersEvent event) registrar.playToServer(InventoryHolderSaveSlotPayload.TYPE, InventoryHolderSaveSlotPayload.STREAM_CODEC, InventoryHolderSaveSlotPacket.get()::handle); registrar.playToServer(InventoryHolderSettingsPayload.TYPE, InventoryHolderSettingsPayload.STREAM_CODEC, InventoryHolderSettingsPacket.get()::handle); registrar.playToServer(InventoryHolderMoveItemsPayload.TYPE, InventoryHolderMoveItemsPayload.STREAM_CODEC, InventoryHolderMoveItemsPacket.get()::handle); + registrar.playToServer(ExperienceHolderPayload.TYPE, ExperienceHolderPayload.STREAM_CODEC, ExperienceHolderPacket.get()::handle); //Going to Client registrar.playToClient(ClientSoundPayload.TYPE, ClientSoundPayload.STREAM_CODEC, ClientSoundPacket.get()::handle); diff --git a/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderPayload.java b/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderPayload.java new file mode 100644 index 00000000..5f9ee887 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/network/data/ExperienceHolderPayload.java @@ -0,0 +1,26 @@ +package com.direwolf20.justdirethings.common.network.data; + +import com.direwolf20.justdirethings.JustDireThings; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record ExperienceHolderPayload( + boolean add, + int levels +) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(JustDireThings.MODID, "experience_holder")); + + @Override + public Type type() { + return TYPE; + } + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, ExperienceHolderPayload::add, + ByteBufCodecs.INT, ExperienceHolderPayload::levels, + ExperienceHolderPayload::new + ); +} diff --git a/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderPacket.java b/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderPacket.java new file mode 100644 index 00000000..54f213c7 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/common/network/handler/ExperienceHolderPacket.java @@ -0,0 +1,30 @@ +package com.direwolf20.justdirethings.common.network.handler; + +import com.direwolf20.justdirethings.common.blockentities.ExperienceHolderBE; +import com.direwolf20.justdirethings.common.containers.ExperienceHolderContainer; +import com.direwolf20.justdirethings.common.network.data.ExperienceHolderPayload; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public class ExperienceHolderPacket { + public static final ExperienceHolderPacket INSTANCE = new ExperienceHolderPacket(); + + public static ExperienceHolderPacket get() { + return INSTANCE; + } + + public void handle(final ExperienceHolderPayload payload, final IPayloadContext context) { + context.enqueueWork(() -> { + Player sender = context.player(); + AbstractContainerMenu container = sender.containerMenu; + + if (container instanceof ExperienceHolderContainer experienceHolderContainer && experienceHolderContainer.baseMachineBE instanceof ExperienceHolderBE experienceHolderBE) { + if (payload.add()) + experienceHolderBE.storeExp(sender, payload.levels()); + else + experienceHolderBE.extractExp(sender, payload.levels()); + } + }); + } +} diff --git a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLanguageProvider.java b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLanguageProvider.java index b12cb6df..94f01083 100644 --- a/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLanguageProvider.java +++ b/src/main/java/com/direwolf20/justdirethings/datagen/JustDireLanguageProvider.java @@ -521,6 +521,8 @@ protected void addTranslations() { add("justdirethings.screen.senditems", "Push Items"); add("justdirethings.screen.pullitems", "Pull Items"); add("justdirethings.screen.swapitems", "Swap Items"); + add("justdirethings.screen.storeexp", "Store Exp"); + add("justdirethings.screen.retrieveexp", "Retrieve Exp"); //Buttons //add("justdirethings.buttons.save", "Save"); diff --git a/src/main/java/com/direwolf20/justdirethings/util/ExperienceUtils.java b/src/main/java/com/direwolf20/justdirethings/util/ExperienceUtils.java new file mode 100644 index 00000000..bb22bce2 --- /dev/null +++ b/src/main/java/com/direwolf20/justdirethings/util/ExperienceUtils.java @@ -0,0 +1,57 @@ +package com.direwolf20.justdirethings.util; + +import net.minecraft.world.entity.player.Player; + +public class ExperienceUtils { + // Calculate experience required to go from level 0 to target level + public static int getTotalExperienceForLevel(int level) { + if (level <= 16) { + return level * level + 6 * level; + } else if (level <= 31) { + return (int) (2.5 * level * level - 40.5 * level + 360); + } else { + return (int) (4.5 * level * level - 162.5 * level + 2220); + } + } + + // Remove levels from a player, ensuring they don't lose more than available + public static int removeLevels(Player player, int levelsToRemove) { + int currentTotalExp = getPlayerTotalExperience(player); + int targetLevel = Math.max(0, player.experienceLevel - levelsToRemove); + + // Calculate how much exp is required to be at the target level + int targetTotalExp = getTotalExperienceForLevel(targetLevel); + int expToRemove = currentTotalExp - targetTotalExp; + + player.giveExperienceLevels(-levelsToRemove); + return expToRemove; // Amount of exp removed + } + + // Calculate experience required to go from one level to the next + public static int getExperienceForNextLevel(int level) { + if (level >= 30) { + return 112 + (level - 30) * 9; + } else { + return level >= 15 ? 37 + (level - 15) * 5 : 7 + level * 2; + } + } + + // Calculate total experience points player currently has (given level and progress) + public static int getPlayerTotalExperience(Player player) { + int exp = getTotalExperienceForLevel(player.experienceLevel); + exp += Math.round(player.experienceProgress * player.getXpNeededForNextLevel()); + return exp; + } + + public static int getExpNeededForNextLevel(Player player) { + return player.getXpNeededForNextLevel() - (int) (player.experienceProgress * player.getXpNeededForNextLevel()); + } + + // Remove points from a player, ensuring they don't lose more than available + public static int removePoints(Player player, int pointsToRemove) { + int currentTotalExp = getPlayerTotalExperience(player); + int expToRemove = Math.min(currentTotalExp, pointsToRemove); + player.giveExperiencePoints(-expToRemove); + return expToRemove; // Amount of exp removed + } +}