diff --git a/atlas/root/servers/SurviePote-26.3/fabricmods/required/pala-0.2.1.jar b/atlas/root/servers/SurviePote-26.3/fabricmods/required/pala-0.2.1.jar index 7bc50fc..1fbf3d9 100644 Binary files a/atlas/root/servers/SurviePote-26.3/fabricmods/required/pala-0.2.1.jar and b/atlas/root/servers/SurviePote-26.3/fabricmods/required/pala-0.2.1.jar differ diff --git a/mods/nosingleplayer/.gitignore b/mods/nosingleplayer/.gitignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/mods/nosingleplayer/.gitignore @@ -0,0 +1 @@ +bin diff --git a/mods/pala/build.gradle b/mods/pala/build.gradle index 0aeede8..e24e80f 100644 --- a/mods/pala/build.gradle +++ b/mods/pala/build.gradle @@ -56,6 +56,12 @@ tasks.register('downloadMcLibs') { new File(outDir, 'fastutil-8.5.18.jar')) dl('https://libraries.minecraft.net/com/google/guava/guava/33.6.0-jre/guava-33.6.0-jre.jar', new File(outDir, 'guava-33.6.0-jre.jar')) + // Requis quand le code touche FriendlyByteBuf (codecs de paquets custom) : + // javac charge ByteBuf pour lire les annotations de type. + dl('https://libraries.minecraft.net/io/netty/netty-buffer/4.2.16.Final/netty-buffer-4.2.16.Final.jar', + new File(outDir, 'netty-buffer.jar')) + dl('https://libraries.minecraft.net/io/netty/netty-common/4.2.16.Final/netty-common-4.2.16.Final.jar', + new File(outDir, 'netty-common.jar')) dl('https://libraries.minecraft.net/com/google/guava/failureaccess/1.0.3/failureaccess-1.0.3.jar', new File(outDir, 'failureaccess-1.0.3.jar')) dl('https://libraries.minecraft.net/com/google/code/gson/gson/2.14.0/gson-2.14.0.jar', diff --git a/mods/pala/src/main/java/fr/surviepote/pala/Pala.java b/mods/pala/src/main/java/fr/surviepote/pala/Pala.java index 0facfbe..b0dcbaa 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/Pala.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/Pala.java @@ -12,6 +12,7 @@ public class Pala implements ModInitializer { @Override public void onInitialize() { fr.surviepote.pala.core.PalaCore.register(); + fr.surviepote.pala.network.PalaNetwork.register(); PalaRegistries.registerAll(); fr.surviepote.pala.events.PalaEvents.registerAll(); LOGGER.info("[SurviePote] Pala initialisé."); diff --git a/mods/pala/src/main/java/fr/surviepote/pala/PalaClient.java b/mods/pala/src/main/java/fr/surviepote/pala/PalaClient.java index 83dca28..7ce4651 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/PalaClient.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/PalaClient.java @@ -1,9 +1,19 @@ package fr.surviepote.pala; +import fr.surviepote.pala.client.PalaJobsScreen; +import fr.surviepote.pala.network.JobsDataPayload; +import fr.surviepote.pala.network.JobsRequestPayload; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderingRegistry; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.fabric.api.client.screen.v1.Screens; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.screens.inventory.InventoryScreen; import net.minecraft.client.renderer.block.FluidModel; import net.minecraft.client.resources.model.sprite.Material; +import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; public class PalaClient implements ClientModInitializer { @@ -23,5 +33,25 @@ public class PalaClient implements ClientModInitializer { fluidModel("sulfuricwater_still", "sulfuricwater_flowing")); FluidRenderingRegistry.setBlockTransparency(PalaFluids.ANGELIC_WATER_BLOCK, false); FluidRenderingRegistry.setBlockTransparency(PalaFluids.FAKE_WATER_BLOCK, false); + + ClientPlayNetworking.registerGlobalReceiver(JobsDataPayload.TYPE, + (payload, context) -> context.client().execute( + () -> context.client().gui.setScreen(new PalaJobsScreen(payload.payload())))); + + // Bouton "Metiers" a gauche de l'inventaire (progression des 4 metiers). + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (!(screen instanceof InventoryScreen)) { + return; + } + int left = (screen.width - 176) / 2; + int top = (screen.height - 166) / 2; + Button jobs = Button.builder( + Component.literal("§6M"), + button -> ClientPlayNetworking.send(JobsRequestPayload.INSTANCE)) + .bounds(left - 24, top + 6, 20, 20) + .tooltip(Tooltip.create(Component.literal("§6Métiers §7— progression"))) + .build(); + Screens.getWidgets(screen).add(jobs); + }); } } diff --git a/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java b/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java index 7813c50..a5f5322 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java @@ -17,6 +17,7 @@ import net.minecraft.sounds.SoundSource; import net.minecraft.world.level.Level; import net.minecraft.world.level.portal.TeleportTransition; import net.minecraft.world.phys.Vec3; +import net.minecraft.world.item.ItemStack; import fr.surviepote.pala.core.PalaConfig; import fr.surviepote.pala.core.PalaJobs; @@ -25,8 +26,8 @@ import fr.surviepote.pala.core.PalaStorage; // Commandes Pala : /end /overworld (permission niveau 4, legacy), /nether (gate metier // mineur niv.3, M0), /pala reload|info|rank (admin), /jobs (metiers), /craft (table de -// craft portable, wiki commandes), /spawn (tp spawn overworld, delai 3 s sans grade, wiki). -// Voir CDC §3.8. +// craft portable, wiki commandes), /furnace (cuisson directe du stack en main), /spawn (tp spawn overworld, +// delai 3 s sans grade, wiki). Voir CDC §3.8. // Origine : CommandEnd/Nether/Overworld + procedures *CommandExecuted qui // teleportaient le joueur au spawn de la dimension visee. Ici via // TeleportTransition (API 26.x), sans le Teleporter custom du legacy. @@ -49,6 +50,35 @@ public final class PalaCommands { private record TpaTask(java.util.UUID target, long at) { } + // Zone / home serialises "dimension:x:y:z". Le separateur ':' est aussi celui de + // l'identifiant de dimension (minecraft:overworld) : on decoupe donc a l'envers + // (3 derniers segments = coordonnees) au lieu d'un split(":") naif qui produisait + // 5 morceaux et faisait echouer /home, /mine, /farm et /f home. + private record ZonePos(String dimension, int x, int y, int z) { + } + + private static ZonePos parseZone(String zone) { + if (zone == null || zone.isEmpty()) { + return null; + } + String[] parts = zone.split(":", 3); + if (parts.length != 3) { + return null; + } + String[] coords = parts[2].split(":"); + if (coords.length != 3) { + return null; + } + try { + return new ZonePos(parts[0] + ":" + parts[1], + Integer.parseInt(coords[0]), + Integer.parseInt(coords[1]), + Integer.parseInt(coords[2])); + } catch (NumberFormatException e) { + return null; + } + } + private PalaCommands() { } @@ -90,6 +120,8 @@ public final class PalaCommands { .executes(command -> jobs(command.getSource()))); dispatcher.register(Commands.literal("craft") .executes(command -> craft(command.getSource()))); + dispatcher.register(Commands.literal("furnace") + .executes(command -> furnace(command.getSource()))); dispatcher.register(Commands.literal("spawn") .executes(command -> spawn(command.getSource()))); dispatcher.register(Commands.literal("minage") @@ -827,15 +859,15 @@ public final class PalaCommands { source.sendFailure(Component.literal("§cAucun home defini (/f sethome).")); return 0; } - String[] parts = faction.home.split(":"); - if (parts.length != 4) { + ZonePos home = parseZone(faction.home); + if (home == null) { source.sendFailure(Component.literal("§cHome corrompu.")); return 0; } net.minecraft.server.MinecraftServer server = source.getServer(); net.minecraft.server.level.ServerLevel target = null; for (net.minecraft.server.level.ServerLevel level : server.getAllLevels()) { - if (level.dimension().identifier().toString().equals(parts[0])) { + if (level.dimension().identifier().toString().equals(home.dimension())) { target = level; break; } @@ -844,19 +876,8 @@ public final class PalaCommands { source.sendFailure(Component.literal("§cDimension inconnue.")); return 0; } - int x; - int y; - int z; - try { - x = Integer.parseInt(parts[1]); - y = Integer.parseInt(parts[2]); - z = Integer.parseInt(parts[3]); - } catch (NumberFormatException e) { - source.sendFailure(Component.literal("§cHome corrompu.")); - return 0; - } teleportPlayer(player, target, - new net.minecraft.core.BlockPos(x, y, z)); + new net.minecraft.core.BlockPos(home.x(), home.y(), home.z())); return Command.SINGLE_SUCCESS; } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { return 0; @@ -1114,15 +1135,15 @@ public final class PalaCommands { "§cZone " + label + " non configuree (admin : config pala.json).")); return 0; } - String[] parts = zone.split(":"); - if (parts.length != 4) { + ZonePos parsed = parseZone(zone); + if (parsed == null) { source.sendFailure(Component.literal("§cZone " + label + " corrompue.")); return 0; } net.minecraft.server.MinecraftServer server = source.getServer(); net.minecraft.server.level.ServerLevel target = null; for (net.minecraft.server.level.ServerLevel level : server.getAllLevels()) { - if (level.dimension().identifier().toString().equals(parts[0])) { + if (level.dimension().identifier().toString().equals(parsed.dimension())) { target = level; break; } @@ -1131,19 +1152,8 @@ public final class PalaCommands { source.sendFailure(Component.literal("§cDimension inconnue.")); return 0; } - int x; - int y; - int z; - try { - x = Integer.parseInt(parts[1]); - y = Integer.parseInt(parts[2]); - z = Integer.parseInt(parts[3]); - } catch (NumberFormatException e) { - source.sendFailure(Component.literal("§cZone " + label + " corrompue.")); - return 0; - } teleportPlayer(player, target, - new net.minecraft.core.BlockPos(x, y, z)); + new net.minecraft.core.BlockPos(parsed.x(), parsed.y(), parsed.z())); return Command.SINGLE_SUCCESS; } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { return 0; @@ -1394,6 +1404,60 @@ public final class PalaCommands { } } + // /furnace : cuit instantanement l'item (ou le stack) tenu en main, sans GUI. + // Vanilla XP de la recette de cuisson + XP metier via PalaJobs.onSmeltTake (meme + // comportement qu'une prise de resultat dans un four). + private static int furnace(CommandSourceStack source) { + try { + ServerPlayer player = source.getPlayerOrException(); + net.minecraft.world.InteractionHand hand = net.minecraft.world.InteractionHand.MAIN_HAND; + ItemStack held = player.getMainHandItem(); + if (held.isEmpty()) { + hand = net.minecraft.world.InteractionHand.OFF_HAND; + held = player.getOffhandItem(); + } + if (held.isEmpty()) { + source.sendFailure(Component.literal("§cAucun item en main.")); + return 0; + } + int count = held.getCount(); + net.minecraft.world.item.crafting.SingleRecipeInput input = + new net.minecraft.world.item.crafting.SingleRecipeInput(held.copyWithCount(1)); + java.util.Optional> found = + source.getServer().getRecipeManager().getRecipeFor( + net.minecraft.world.item.crafting.RecipeType.SMELTING, + input, player.level()); + if (found.isEmpty()) { + source.sendFailure(Component.literal( + "§cCet item ne peut pas etre cuit : §e" + held.getHoverName().getString())); + return 0; + } + net.minecraft.world.item.crafting.SmeltingRecipe recipe = found.get().value(); + ItemStack result = recipe.assemble(input); + ItemStack output = result.copyWithCount(result.getCount() * count); + float xp = recipe.experience() * count; + player.setItemInHand(hand, ItemStack.EMPTY); + player.getInventory().placeItemBackInInventory(output, + net.minecraft.util.Prediction.SERVER_ONLY); + int points = net.minecraft.util.Mth.floor(xp); + if (player.getRandom().nextFloat() < xp - points) { + points++; + } + if (points > 0) { + player.giveExperiencePoints(points); + } + fr.surviepote.pala.core.PalaJobs.onSmeltTake(player, output); + final int finalPoints = points; + source.sendSuccess(() -> Component.literal("§6[Pala] §7Cuit : §e" + + output.getCount() + " x " + output.getHoverName().getString() + + (finalPoints > 0 ? " §7(+§a" + finalPoints + " XP§7)" : "")), false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + // /spawn : tp au spawn overworld avec delai wiki 3 s (60 ticks). Pas d'annulation sur // mouvement cote wiki (teleportation simple apres delai). private static int spawn(CommandSourceStack source) { diff --git a/mods/pala/src/main/java/fr/surviepote/pala/PalaTabs.java b/mods/pala/src/main/java/fr/surviepote/pala/PalaTabs.java index 7ea68e7..9b57e79 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/PalaTabs.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/PalaTabs.java @@ -204,10 +204,6 @@ public final class PalaTabs { output.accept(PalaBlocks.CAULDRON_TANK_SUPPORT); output.accept(PalaBlocks.MYSTICAL_BOOKSHELF); output.accept(PalaBlocks.SLEEPING_BAG); - output.accept(PalaBlocks.FIKUTSI_STAGE0); - output.accept(PalaBlocks.FIKUTSI_STAGE1); - output.accept(PalaBlocks.FIKUTSI_STAGE2); - output.accept(PalaBlocks.FIKUTSI_STAGE3); output.accept(PalaBlocks.FINDIUM_ORE); output.accept(PalaBlocks.FINDIUM_ORE_BLOCK); output.accept(PalaBlocks.PALAFORGE); @@ -216,20 +212,6 @@ public final class PalaTabs { output.accept(PalaBlocks.GRINDER_FRAME); output.accept(PalaBlocks.KISAKAY_ORE); output.accept(PalaBlocks.KISAKAY_ORE_BLOCK); - output.accept(PalaBlocks.KIWANO_STAGE0); - output.accept(PalaBlocks.KIWANO_STAGE1); - output.accept(PalaBlocks.KIWANO_STAGE2); - output.accept(PalaBlocks.KIWANO_STAGE3); - output.accept(PalaBlocks.KIWANO_STAGE4); - output.accept(PalaBlocks.OKAMI_STAGE0); - output.accept(PalaBlocks.OKAMI_STAGE1); - output.accept(PalaBlocks.OKAMI_STAGE2); - output.accept(PalaBlocks.OKAMI_STAGE3); - output.accept(PalaBlocks.ORANGE_BLUE_STAGE0); - output.accept(PalaBlocks.ORANGE_BLUE_STAGE1); - output.accept(PalaBlocks.ORANGE_BLUE_STAGE2); - output.accept(PalaBlocks.ORANGE_BLUE_STAGE3); - output.accept(PalaBlocks.ORANGE_BLUE_STAGE4); output.accept(PalaBlocks.PALADIUM_CRUSHER); output.accept(PalaBlocks.PALADIUM_FURNACE); output.accept(PalaBlocks.PALADIUM_ORE); diff --git a/mods/pala/src/main/java/fr/surviepote/pala/client/PalaJobsScreen.java b/mods/pala/src/main/java/fr/surviepote/pala/client/PalaJobsScreen.java new file mode 100644 index 0000000..5087528 --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/client/PalaJobsScreen.java @@ -0,0 +1,117 @@ +package fr.surviepote.pala.client; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; + +// Ecran de progression des 4 metiers, ouvert par le bouton de l'inventaire. +// Donnees recues du serveur via JobsDataPayload. +public class PalaJobsScreen extends Screen { + + private record JobEntry(String key, int level, long xp, long previous, long next) { + } + + private final List jobs = new ArrayList<>(); + private int panelX; + private int panelY; + private int panelWidth = 280; + private int panelHeight; + + public PalaJobsScreen(String payload) { + super(Component.translatable("screen.pala.jobs")); + if (payload != null) { + for (String line : payload.split("\n")) { + String[] parts = line.split("\\|"); + if (parts.length != 5) { + continue; + } + try { + jobs.add(new JobEntry(parts[0], Integer.parseInt(parts[1]), + Long.parseLong(parts[2]), Long.parseLong(parts[3]), + Long.parseLong(parts[4]))); + } catch (NumberFormatException ignored) { + // Ligne invalide : ignoree (payload tolerant). + } + } + } + } + + @Override + protected void init() { + panelHeight = 44 + jobs.size() * 38 + 30; + panelX = (width - panelWidth) / 2; + panelY = (height - panelHeight) / 2; + addRenderableWidget(Button.builder(Component.literal("Fermer"), b -> onClose()) + .bounds(panelX + panelWidth / 2 - 60, panelY + panelHeight - 26, 120, 20) + .build()); + } + + @Override + public void extractBackground(GuiGraphicsExtractor graphics, int mouseX, int mouseY, + float partialTick) { + super.extractBackground(graphics, mouseX, mouseY, partialTick); + graphics.fill(0, 0, width, height, 0x99000000); + graphics.fill(panelX, panelY, panelX + panelWidth, panelY + panelHeight, 0xF0181818); + graphics.fill(panelX, panelY, panelX + panelWidth, panelY + 2, 0xFFFFAA00); + } + + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, + float partialTick) { + super.extractRenderState(graphics, mouseX, mouseY, partialTick); + graphics.centeredText(font, Component.literal("§6Métiers"), panelX + panelWidth / 2, + panelY + 12, 0xFFFFAA00); + int y = panelY + 32; + if (jobs.isEmpty()) { + graphics.text(font, Component.literal("§7Aucune donnée de métier reçue."), + panelX + 16, y, 0xFFAAAAAA); + } + for (JobEntry job : jobs) { + graphics.text(font, Component.literal(displayName(job.key()) + " §7— niveau §a" + + job.level()), panelX + 16, y, 0xFFFFFFFF); + String xpLine; + if (job.next() <= 0) { + xpLine = "§7Niveau maximum"; + } else { + xpLine = "§7" + job.xp() + " / " + job.next() + " XP"; + } + graphics.text(font, Component.literal(xpLine), panelX + 16, y + 12, 0xFFAAAAAA); + drawBar(graphics, panelX + 150, y + 3, 112, 8, job); + y += 38; + } + } + + private void drawBar(GuiGraphicsExtractor graphics, int x, int y, int w, int h, + JobEntry job) { + graphics.fill(x, y, x + w, y + h, 0xFF333333); + long next = job.next(); + if (next <= 0) { + graphics.fill(x, y, x + w, y + h, 0xFF55FF55); + return; + } + long previous = job.previous(); + long span = Math.max(1L, next - previous); + long done = Math.max(0L, job.xp() - previous); + int filled = (int) Math.min(w, (done * w) / span); + graphics.fill(x, y, x + filled, y + h, 0xFF55FF55); + } + + private static String displayName(String key) { + return switch (key) { + case "mineur" -> "Mineur"; + case "fermier" -> "Fermier"; + case "chasseur" -> "Chasseur"; + case "alchimiste" -> "Alchimiste"; + default -> key; + }; + } + + @Override + public boolean isPauseScreen() { + return false; + } +} diff --git a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaConfig.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaConfig.java index 7128bca..e4439b7 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaConfig.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaConfig.java @@ -30,48 +30,54 @@ public final class PalaConfig { // Cles = ids de registres (vanilla ou pala:). Map.ofEntries : Map.of limite a 10 paires. public Map minerXp = new HashMap<>(Map.ofEntries( Map.entry("minecraft:stone", 0.5), + Map.entry("minecraft:cobblestone", 0.5), Map.entry("minecraft:deepslate", 0.5), + Map.entry("minecraft:tuff", 0.5), Map.entry("minecraft:diorite", 3.0), Map.entry("minecraft:andesite", 3.0), Map.entry("minecraft:coal_ore", 4.0), Map.entry("minecraft:deepslate_coal_ore", 4.0), - Map.entry("minecraft:quartz_ore", 6.0), Map.entry("minecraft:obsidian", 6.0), - Map.entry("minecraft:lapis_ore", 15.0), Map.entry("minecraft:redstone_ore", 15.0), + Map.entry("minecraft:nether_quartz_ore", 6.0), Map.entry("minecraft:obsidian", 6.0), + Map.entry("minecraft:lapis_ore", 15.0), Map.entry("minecraft:deepslate_lapis_ore", 15.0), + Map.entry("minecraft:redstone_ore", 15.0), + Map.entry("minecraft:deepslate_redstone_ore", 15.0), Map.entry("minecraft:emerald_ore", 50.0), + Map.entry("minecraft:deepslate_emerald_ore", 50.0), Map.entry("minecraft:iron_ore", 2.0), Map.entry("minecraft:deepslate_iron_ore", 2.0), Map.entry("minecraft:gold_ore", 4.0), Map.entry("minecraft:deepslate_gold_ore", 4.0), + Map.entry("minecraft:nether_gold_ore", 4.0), Map.entry("minecraft:diamond_ore", 8.0), Map.entry("minecraft:deepslate_diamond_ore", 8.0), Map.entry("pala:amethyst_ore", 12.0), Map.entry("pala:titane_ore", 16.0), Map.entry("pala:paladium_ore", 20.0), Map.entry("pala:paladium_green_ore", 20.0), Map.entry("pala:trixium_ore", 10.0), Map.entry("pala:findium_ore", 10.0))); - // M-mineur-xp (wiki Evolution mineur, VERIFIE) : niveaux requis par bloc. - // Niv.1 : roche/diorite/charbon/quartz/obsidienne/lapis/redstone/emeraude ; - // niv.5 : fer/or/diamant/amethyste/titane (+ minerais pala par interpretation - // "Particules paladium 20@5", ECART documente). + // M-mineur-xp : niveaux requis par bloc. Les blocs de base sont accessibles des le + // niveau 0 (sinon un nouveau joueur ne peut jamais gagner son premier XP mineur) ; + // les minerais de fer/or/diamant/pala restent verrouilles au niveau 5 (wiki). public Map minerLevels = new HashMap<>(Map.ofEntries( - Map.entry("minecraft:stone", 1), - Map.entry("minecraft:diorite", 1), Map.entry("minecraft:andesite", 1), - Map.entry("minecraft:coal_ore", 1), Map.entry("minecraft:deepslate_coal_ore", 1), - Map.entry("minecraft:quartz_ore", 1), Map.entry("minecraft:obsidian", 1), - Map.entry("minecraft:lapis_ore", 1), Map.entry("minecraft:redstone_ore", 1), - Map.entry("minecraft:emerald_ore", 1), Map.entry("minecraft:deepslate_emerald_ore", 1), + Map.entry("minecraft:stone", 0), + Map.entry("minecraft:cobblestone", 0), Map.entry("minecraft:deepslate", 0), + Map.entry("minecraft:tuff", 0), + Map.entry("minecraft:diorite", 0), Map.entry("minecraft:andesite", 0), + Map.entry("minecraft:coal_ore", 0), Map.entry("minecraft:deepslate_coal_ore", 0), + Map.entry("minecraft:nether_quartz_ore", 0), Map.entry("minecraft:obsidian", 0), + Map.entry("minecraft:lapis_ore", 0), Map.entry("minecraft:deepslate_lapis_ore", 0), + Map.entry("minecraft:redstone_ore", 0), + Map.entry("minecraft:deepslate_redstone_ore", 0), + Map.entry("minecraft:emerald_ore", 0), Map.entry("minecraft:deepslate_emerald_ore", 0), Map.entry("minecraft:iron_ore", 5), Map.entry("minecraft:deepslate_iron_ore", 5), Map.entry("minecraft:gold_ore", 5), Map.entry("minecraft:deepslate_gold_ore", 5), + Map.entry("minecraft:nether_gold_ore", 5), Map.entry("minecraft:diamond_ore", 5), Map.entry("minecraft:deepslate_diamond_ore", 5), Map.entry("pala:amethyst_ore", 5), Map.entry("pala:titane_ore", 5), Map.entry("pala:paladium_ore", 5), Map.entry("pala:paladium_green_ore", 5), Map.entry("pala:trixium_ore", 5), Map.entry("pala:findium_ore", 5))); - // Courbe XP (M-xpcurve) : seuils wiki VERIFIES 2-20, formule au-dela. La table mineur - // s'applique aux 4 metiers (seule table publiee ; ECART documente). + // Courbe XP : formule par defaut xp(n) = xpBase * n^xpExponent. Les anciens seuils + // wiki (22 123 XP pour le niveau 2) rendaient les premiers niveaux inatteignables en + // jeu normal ; ils sont remplaces par une courbe progressive. Surcharge possible via + // levelThresholds dans pala.json (niveau -> XP total cumule). + public int configVersion = 3; public double xpBase = 100.0; public double xpExponent = 1.5; - public Map levelThresholds = new HashMap<>(Map.ofEntries( - Map.entry(2, 22123L), Map.entry(3, 40390L), Map.entry(4, 73751L), - Map.entry(5, 118886L), Map.entry(6, 176611L), Map.entry(7, 247616L), - Map.entry(8, 332507L), Map.entry(9, 431826L), Map.entry(10, 546062L), - Map.entry(11, 675669L), Map.entry(12, 821062L), Map.entry(13, 982632L), - Map.entry(14, 1160743L), Map.entry(15, 1355741L), Map.entry(16, 1567953L), - Map.entry(17, 1797690L), Map.entry(18, 2045248L), Map.entry(19, 2310912L), - Map.entry(20, 2594953L))); + public Map levelThresholds = new HashMap<>(); // Niveau mineur requis pour /nether (wiki recompenses mineur niv.3, VERIFIE). public int netherMinerLevel = 3; @@ -118,6 +124,8 @@ public final class PalaConfig { // M5a (wiki VERIFIE) : chasseur = entite -> [xp, niveau requis]. // Passifs 14@0, poulpe 10@0 (page : "des le niveau 0/1"), creeper 40, wither 1000. // Moddees (chevre 20@1, escargot 25@2...) : mobs absents du portage => non listees (M8/donjons). + // SurviePote : hostiles vanilla ajoutes (zombie/squelette/araignee/etc.) car la table + // wiki ne couvrait que les passifs + creeper, donc tuer des monstres ne donnait rien. public Map hunterKills = new HashMap<>(Map.ofEntries( Map.entry("minecraft:cow", new double[]{14, 0}), Map.entry("minecraft:sheep", new double[]{14, 0}), @@ -125,10 +133,60 @@ public final class PalaConfig { Map.entry("minecraft:chicken", new double[]{14, 0}), Map.entry("minecraft:rabbit", new double[]{14, 0}), Map.entry("minecraft:horse", new double[]{14, 0}), + Map.entry("minecraft:mooshroom", new double[]{14, 0}), + Map.entry("minecraft:wolf", new double[]{14, 0}), + Map.entry("minecraft:fox", new double[]{14, 0}), + Map.entry("minecraft:cat", new double[]{10, 0}), + Map.entry("minecraft:llama", new double[]{14, 0}), + Map.entry("minecraft:trader_llama", new double[]{14, 0}), + Map.entry("minecraft:camel", new double[]{14, 0}), Map.entry("minecraft:squid", new double[]{10, 0}), Map.entry("minecraft:glow_squid", new double[]{10, 0}), + Map.entry("minecraft:bat", new double[]{5, 0}), + Map.entry("minecraft:bee", new double[]{10, 0}), + Map.entry("minecraft:frog", new double[]{10, 0}), + Map.entry("minecraft:axolotl", new double[]{15, 0}), + Map.entry("minecraft:armadillo", new double[]{15, 0}), + Map.entry("minecraft:sniffer", new double[]{30, 0}), + Map.entry("minecraft:allay", new double[]{20, 0}), + Map.entry("minecraft:polar_bear", new double[]{25, 0}), Map.entry("minecraft:creeper", new double[]{40, 0}), + Map.entry("minecraft:zombie", new double[]{15, 0}), + Map.entry("minecraft:husk", new double[]{15, 0}), + Map.entry("minecraft:drowned", new double[]{15, 0}), + Map.entry("minecraft:skeleton", new double[]{15, 0}), + Map.entry("minecraft:stray", new double[]{15, 0}), + Map.entry("minecraft:bogged", new double[]{15, 0}), + Map.entry("minecraft:spider", new double[]{12, 0}), + Map.entry("minecraft:cave_spider", new double[]{15, 0}), + Map.entry("minecraft:silverfish", new double[]{5, 0}), + Map.entry("minecraft:endermite", new double[]{8, 0}), + Map.entry("minecraft:slime", new double[]{10, 0}), + Map.entry("minecraft:magma_cube", new double[]{20, 0}), + Map.entry("minecraft:enderman", new double[]{30, 0}), + Map.entry("minecraft:witch", new double[]{25, 0}), + Map.entry("minecraft:blaze", new double[]{30, 0}), + Map.entry("minecraft:ghast", new double[]{50, 0}), + Map.entry("minecraft:guardian", new double[]{20, 0}), + Map.entry("minecraft:elder_guardian", new double[]{100, 0}), + Map.entry("minecraft:shulker", new double[]{40, 0}), + Map.entry("minecraft:phantom", new double[]{20, 0}), + Map.entry("minecraft:pillager", new double[]{20, 0}), + Map.entry("minecraft:vindicator", new double[]{25, 0}), + Map.entry("minecraft:evoker", new double[]{50, 0}), + Map.entry("minecraft:illusioner", new double[]{50, 0}), + Map.entry("minecraft:ravager", new double[]{80, 0}), + Map.entry("minecraft:vex", new double[]{20, 0}), + Map.entry("minecraft:hoglin", new double[]{30, 0}), + Map.entry("minecraft:zoglin", new double[]{30, 0}), + Map.entry("minecraft:piglin", new double[]{20, 0}), + Map.entry("minecraft:piglin_brute", new double[]{30, 0}), + Map.entry("minecraft:zombified_piglin", new double[]{15, 0}), + Map.entry("minecraft:wither_skeleton", new double[]{40, 0}), + Map.entry("minecraft:breeze", new double[]{30, 0}), + Map.entry("minecraft:warden", new double[]{200, 0}), Map.entry("minecraft:wither", new double[]{1000, 0}), + Map.entry("minecraft:ender_dragon", new double[]{5000, 0}), // M-hunter-3 (wiki evolution chasseur, VERIFIE) : passifs vanilla // supplementaires (creatures moddees escargot/crabe/serpent/meduse/ // elephant SKIP M8, absentes ; peche "tres variable" + QTE SKIP). @@ -156,10 +214,14 @@ public final class PalaConfig { "pala:egg_plant_fruit", new double[]{3, 8}, "pala:okami_fruit", new double[]{4.5, 12}, "pala:kiwano", new double[]{6, 16})); - // M-alchi-1 (wiki Evolution alchimiste VERIFIE) : XP au craft = item -> [xp, niveau]. - // Fiole 0.2@1. Extracteur/lightning/glueballs/portails : items/crafts en M-alchi-2+. + // M-alchi-1 : XP au craft = item -> [xp, niveau]. La fiole est accessible des le + // niveau 0 (source d'entree) ; valeur remontee de 0.2 (wiki) a 1.0 pour que le + // craft de fioles reste une alternative viable au brassage. public Map alchemistCraft = new HashMap<>(Map.of( - "pala:flask", new double[]{0.2, 1})); + "pala:flask", new double[]{1.0, 0})); + // SurviePote : XP alchimiste par potion reellement brassee et recuperee du + // chaudron (mixin BrewingStand). Source principale pour demarrer le metier. + public double alchemistBrewXp = 10.0; // M4a : recettes machines data-driven (CDC §3.4). Cles = ids de registres. // Defauts = legacy+wiki VERIFIES (M3). Rechargeables via /pala reload. public Map crusherResults = new HashMap<>(Map.of( @@ -224,6 +286,7 @@ public final class PalaConfig { try (Reader reader = Files.newBufferedReader(path)) { PalaConfig loaded = GSON.fromJson(reader, PalaConfig.class); if (loaded != null) { + boolean migrateBalance = loaded.configVersion < 3; if (loaded.minerXp != null && !loaded.minerXp.isEmpty()) { instance.minerXp = loaded.minerXp; } @@ -236,6 +299,12 @@ public final class PalaConfig { if (loaded.xpExponent > 0) { instance.xpExponent = loaded.xpExponent; } + if (loaded.levelThresholds != null) { + instance.levelThresholds = loaded.levelThresholds; + } + if (loaded.alchemistBrewXp > 0) { + instance.alchemistBrewXp = loaded.alchemistBrewXp; + } if (loaded.netherMinerLevel >= 0) { instance.netherMinerLevel = loaded.netherMinerLevel; } @@ -256,6 +325,16 @@ public final class PalaConfig { instance.farmerHarvest = loaded.farmerHarvest; mergedDefaultsApplied |= mergeDefaults(instance.farmerHarvest, __def); } + if (loaded.crusherXp != null && !loaded.crusherXp.isEmpty()) { + java.util.Map __def = new java.util.HashMap<>(instance.crusherXp); + instance.crusherXp = loaded.crusherXp; + mergedDefaultsApplied |= mergeDefaults(instance.crusherXp, __def); + } + if (loaded.alchemistCraft != null && !loaded.alchemistCraft.isEmpty()) { + java.util.Map __def = new java.util.HashMap<>(instance.alchemistCraft); + instance.alchemistCraft = loaded.alchemistCraft; + mergedDefaultsApplied |= mergeDefaults(instance.alchemistCraft, __def); + } if (loaded.crusherResults != null && !loaded.crusherResults.isEmpty()) { java.util.Map __def = new java.util.HashMap<>(instance.crusherResults); instance.crusherResults = loaded.crusherResults; @@ -275,15 +354,38 @@ public final class PalaConfig { if (loaded.furnaceFuels != null && !loaded.furnaceFuels.isEmpty()) { instance.furnaceFuels = loaded.furnaceFuels; } + // Migration v3 : les anciennes tables de balance (seuils wiki 22k+, + // blocs mineur verrouilles niv.1, alchimiste niv.1) rendaient les + // metiers impossibles a demarrer. On remet les defauts code une fois. + if (migrateBalance) { + PalaConfig defaults = new PalaConfig(); + instance.minerXp = defaults.minerXp; + instance.minerLevels = defaults.minerLevels; + instance.hunterKills = defaults.hunterKills; + instance.farmerHarvest = defaults.farmerHarvest; + instance.crusherXp = defaults.crusherXp; + instance.alchemistCraft = defaults.alchemistCraft; + instance.alchemistBrewXp = defaults.alchemistBrewXp; + instance.levelThresholds = defaults.levelThresholds; + instance.xpBase = defaults.xpBase; + instance.xpExponent = defaults.xpExponent; + instance.configVersion = 3; + mergedDefaultsApplied = true; + Pala.LOGGER.info( + "[SurviePote] Config metiers migree v2 -> v3 (courbe XP et tables rebalancees)."); + } else { + instance.configVersion = loaded.configVersion; + } // M5a : un vieux pala.json (M0) n'a pas les nouvelles cles : les defauts // code sont en memoire (Gson utilise le constructeur no-args, les champs // absents gardent leurs valeurs par defaut, jamais null), on re-sauvegarde // pour les materialiser si une cle attendue manque. Une table explicitement // videe (cle presente, tableau vide) est respectee telle quelle. - java.util.Set expected = java.util.Set.of("minerXp", "minerLevels", + java.util.Set expected = java.util.Set.of("configVersion", + "minerXp", "minerLevels", "xpBase", "xpExponent", "levelThresholds", "netherMinerLevel", "stickCooldowns", "rankNodes", "hunterKills", "farmerHarvest", "crusherXp", - "alchemistCraft", + "alchemistCraft", "alchemistBrewXp", "crusherResults", "crusherFuels", "crusherInfiniteFuels", "furnaceSmelts", "furnaceFuels", "serverName", "wikiUrl", "discordUrl", "shopUrl", @@ -326,7 +428,7 @@ public final class PalaConfig { } } - /** Cout XP total pour atteindre le niveau n (n >= 1) : table wiki 2-20, formule au-dela. */ + /** Cout XP total pour atteindre le niveau n (n >= 1), surchargeable par levelThresholds. */ public long xpForLevel(int level) { Long exact = levelThresholds.get(level); if (exact != null) { diff --git a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaJobs.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaJobs.java index 49ba5cd..21b23e6 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaJobs.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaJobs.java @@ -1,6 +1,9 @@ package fr.surviepote.pala.core; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; @@ -17,6 +20,10 @@ public final class PalaJobs { public static final List JOBS = List.of("mineur", "fermier", "chasseur", "alchimiste"); + // Action bar : dernier affichage par joueur (throttle 1 s pour eviter le spam + // pendant le minage). + private static final Map LAST_HUD = new HashMap<>(); + private PalaJobs() { } @@ -46,9 +53,32 @@ public final class PalaJobs { PalaRewards.grant(player, job, data.level); } PalaStorage.get().markDirty(); + sendHud(player, job, data); return data.level; } + // Affiche la progression courante dans l'action bar (1 fois par seconde max). + // Permet de voir que l'XP monte sans rouvrir l'ecran Metiers. + private static void sendHud(ServerPlayer player, String job, PalaStorage.JobData data) { + long now = System.currentTimeMillis(); + Long last = LAST_HUD.get(player.getUUID()); + if (last != null && now - last < 1000L) { + return; + } + LAST_HUD.put(player.getUUID(), now); + PalaConfig config = PalaConfig.get(); + long next = data.level >= 100 ? 0L : config.xpForLevel(data.level + 1); + String text; + if (next <= 0L) { + text = "§6[Metier] §e" + job + " §7niveau maximum"; + } else { + text = "§6[Metier] §e" + job + " §7: §e" + + String.format(java.util.Locale.ROOT, "%.1f", data.xp) + + " §7/ §e" + next + " XP §7(niv. " + data.level + ")"; + } + player.sendOverlayMessage(Component.literal(text)); + } + /** Hook casse-bloc : XP mineur selon la table config + niveau requis (wiki). */ public static void onBlockBreak(ServerPlayer player, ServerLevel level, BlockPos pos, BlockState state) { Identifier id = level.registryAccess() diff --git a/mods/pala/src/main/java/fr/surviepote/pala/events/PalaJobEvents.java b/mods/pala/src/main/java/fr/surviepote/pala/events/PalaJobEvents.java index 06a4a69..0310abe 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/events/PalaJobEvents.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/events/PalaJobEvents.java @@ -160,7 +160,16 @@ public final class PalaJobEvents { } private static void afterDeath(LivingEntity entity, DamageSource source) { - Entity killer = source.getEntity(); + // getKillCredit() gere deja les projectiles et les loups apprivoises (retourne + // le proprietaire). Fallback sur la source directe pour les cas exotiques. + Entity killer = entity.getKillCredit(); + if (killer == null) { + killer = source.getEntity(); + } + if (killer instanceof net.minecraft.world.entity.projectile.Projectile projectile + && projectile.getOwner() instanceof ServerPlayer owner) { + killer = owner; + } if (!(killer instanceof ServerPlayer player)) { return; } diff --git a/mods/pala/src/main/java/fr/surviepote/pala/mixin/PalaBrewTakeXpMixin.java b/mods/pala/src/main/java/fr/surviepote/pala/mixin/PalaBrewTakeXpMixin.java new file mode 100644 index 0000000..3bfda4b --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/mixin/PalaBrewTakeXpMixin.java @@ -0,0 +1,40 @@ +package fr.surviepote.pala.mixin; + +import net.minecraft.core.component.DataComponents; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import fr.surviepote.pala.core.PalaJobs; + +// SurviePote : XP alchimiste quand un joueur recupere une potion fraichement brassee. +// Le marqueur "pala:brewed_xp" est pose par PalaBrewXpMixin a la fin du brassage puis +// retire ici, donc une potion deja prise (ou sortie par un hopper) ne rapporte plus rien. +@Mixin(targets = "net.minecraft.world.inventory.BrewingStandMenu$PotionSlot") +public class PalaBrewTakeXpMixin { + + @Inject(method = "onTake", at = @At("TAIL")) + private void pala$brewedPotionXp(Player player, ItemStack stack, CallbackInfo ci) { + if (!(player instanceof ServerPlayer sp) || stack.isEmpty()) { + return; + } + CustomData data = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + if (!data.copyTag().contains("pala:brewed_xp")) { + return; + } + double xp = data.copyTag().getDoubleOr("pala:brewed_xp", 0.0); + CustomData.update(DataComponents.CUSTOM_DATA, stack, + tag -> tag.remove("pala:brewed_xp")); + if (stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).isEmpty()) { + stack.remove(DataComponents.CUSTOM_DATA); + } + if (xp > 0.0) { + PalaJobs.addXp(sp, "alchimiste", xp); + } + } +} diff --git a/mods/pala/src/main/java/fr/surviepote/pala/mixin/PalaBrewXpMixin.java b/mods/pala/src/main/java/fr/surviepote/pala/mixin/PalaBrewXpMixin.java new file mode 100644 index 0000000..29c8efd --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/mixin/PalaBrewXpMixin.java @@ -0,0 +1,41 @@ +package fr.surviepote.pala.mixin; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.component.DataComponents; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.level.block.entity.BrewingStandBlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import fr.surviepote.pala.core.PalaConfig; + +// SurviePote : marque les potions sorties d'un chaudron avec la valeur d'XP alchimiste +// a attribuer au joueur qui les recupere. Le marqueur est lu puis retire dans +// PalaBrewTakeXpMixin (PotionSlot.onTake), ce qui evite le farm en remettant une potion +// deja recuperee dans le chaudron. +@Mixin(BrewingStandBlockEntity.class) +public class PalaBrewXpMixin { + + @Inject(method = "doBrew", at = @At("TAIL")) + private static void pala$markBrewedPotions(ServerLevel level, BlockPos pos, + BrewingStandBlockEntity stand, CallbackInfo ci) { + double xp = PalaConfig.get().alchemistBrewXp; + if (xp <= 0.0) { + return; + } + // Slots 0-2 = potions de sortie. + for (int i = 0; i < 3; i++) { + ItemStack stack = stand.getItem(i); + if (stack.isEmpty()) { + continue; + } + CustomData.update(DataComponents.CUSTOM_DATA, stack, + tag -> tag.putDouble("pala:brewed_xp", xp)); + } + stand.setChanged(); + } +} diff --git a/mods/pala/src/main/java/fr/surviepote/pala/network/JobsDataPayload.java b/mods/pala/src/main/java/fr/surviepote/pala/network/JobsDataPayload.java new file mode 100644 index 0000000..5bdc606 --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/network/JobsDataPayload.java @@ -0,0 +1,23 @@ +package fr.surviepote.pala.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +// Reponse serveur -> client : une ligne par metier "job|niveau|xp|xp_suivant". +public record JobsDataPayload(String payload) implements CustomPacketPayload { + + public static final Type TYPE = new Type<>( + Identifier.fromNamespaceAndPath("pala", "jobs_data")); + + public static final StreamCodec CODEC = + StreamCodec.of( + (buf, value) -> buf.writeUtf(value.payload, 32767), + buf -> new JobsDataPayload(buf.readUtf(32767))); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/mods/pala/src/main/java/fr/surviepote/pala/network/JobsRequestPayload.java b/mods/pala/src/main/java/fr/surviepote/pala/network/JobsRequestPayload.java new file mode 100644 index 0000000..872d8a5 --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/network/JobsRequestPayload.java @@ -0,0 +1,23 @@ +package fr.surviepote.pala.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +// Requete client -> serveur envoyee par le bouton "Metiers" de l'inventaire. +public record JobsRequestPayload() implements CustomPacketPayload { + + public static final JobsRequestPayload INSTANCE = new JobsRequestPayload(); + + public static final Type TYPE = new Type<>( + Identifier.fromNamespaceAndPath("pala", "jobs_request")); + + public static final StreamCodec CODEC = + StreamCodec.unit(INSTANCE); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/mods/pala/src/main/java/fr/surviepote/pala/network/PalaNetwork.java b/mods/pala/src/main/java/fr/surviepote/pala/network/PalaNetwork.java new file mode 100644 index 0000000..7143f38 --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/network/PalaNetwork.java @@ -0,0 +1,40 @@ +package fr.surviepote.pala.network; + +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.server.level.ServerPlayer; + +import fr.surviepote.pala.core.PalaConfig; +import fr.surviepote.pala.core.PalaJobs; + +// Synchronisation a la demande de la progression des metiers (bouton inventaire). +public final class PalaNetwork { + + private PalaNetwork() { + } + + public static void register() { + PayloadTypeRegistry.serverboundPlay().register(JobsRequestPayload.TYPE, JobsRequestPayload.CODEC); + PayloadTypeRegistry.clientboundPlay().register(JobsDataPayload.TYPE, JobsDataPayload.CODEC); + + ServerPlayNetworking.registerGlobalReceiver(JobsRequestPayload.TYPE, + (payload, context) -> { + ServerPlayer player = context.player(); + ServerPlayNetworking.send(player, new JobsDataPayload(build(player))); + }); + } + + private static String build(ServerPlayer player) { + StringBuilder sb = new StringBuilder(); + for (String job : PalaJobs.JOBS) { + int level = PalaJobs.levelOf(player, job); + long xp = (long) PalaJobs.xpOf(player, job); + long previous = level <= 0 ? 0L : PalaConfig.get().xpForLevel(level); + long next = level >= 100 ? 0L : PalaConfig.get().xpForLevel(level + 1); + sb.append(job).append('|').append(level).append('|') + .append(xp).append('|').append(previous).append('|') + .append(next).append('\n'); + } + return sb.toString(); + } +} diff --git a/mods/pala/src/main/resources/assets/pala/lang/en_us.json b/mods/pala/src/main/resources/assets/pala/lang/en_us.json index 940c4da..b7da0d7 100644 --- a/mods/pala/src/main/resources/assets/pala/lang/en_us.json +++ b/mods/pala/src/main/resources/assets/pala/lang/en_us.json @@ -264,5 +264,6 @@ "item.pala.job_candy": "Job Candy", "block.pala.palaforge": "Palaforge", "item.pala.palaforge": "Palaforge", - "container.pala.palaforge": "Palaforge" + "container.pala.palaforge": "Palaforge", + "screen.pala.jobs": "Jobs" } diff --git a/mods/pala/src/main/resources/data/pala/loot_table/blocks/amethyst_ore.json b/mods/pala/src/main/resources/data/pala/loot_table/blocks/amethyst_ore.json index bf88e0b..580cc63 100644 --- a/mods/pala/src/main/resources/data/pala/loot_table/blocks/amethyst_ore.json +++ b/mods/pala/src/main/resources/data/pala/loot_table/blocks/amethyst_ore.json @@ -2,34 +2,17 @@ "type": "minecraft:block", "pools": [ { + "condition": { + "type": "minecraft:survives_explosion" + }, "entries": [ { - "type": "minecraft:alternatives", - "children": [ - { - "type": "minecraft:item", - "condition": "minecraft:tool/can_silk_touch", - "name": "pala:amethyst_ore" - }, - { - "type": "minecraft:item", - "modifier": [ - { - "type": "minecraft:apply_bonus", - "enchantment": "minecraft:fortune", - "formula": "minecraft:ore_drops" - }, - { - "type": "minecraft:explosion_decay" - } - ], - "name": "pala:amethyst_ingot" - } - ] + "type": "minecraft:item", + "name": "pala:amethyst_ore" } ], "rolls": 1 } ], "random_sequence": "pala:blocks/amethyst_ore" -} \ No newline at end of file +} diff --git a/mods/pala/src/main/resources/data/pala/loot_table/blocks/findium_ore.json b/mods/pala/src/main/resources/data/pala/loot_table/blocks/findium_ore.json index a485675..5973736 100644 --- a/mods/pala/src/main/resources/data/pala/loot_table/blocks/findium_ore.json +++ b/mods/pala/src/main/resources/data/pala/loot_table/blocks/findium_ore.json @@ -2,34 +2,17 @@ "type": "minecraft:block", "pools": [ { + "condition": { + "type": "minecraft:survives_explosion" + }, "entries": [ { - "type": "minecraft:alternatives", - "children": [ - { - "type": "minecraft:item", - "condition": "minecraft:tool/can_silk_touch", - "name": "pala:findium_ore" - }, - { - "type": "minecraft:item", - "modifier": [ - { - "type": "minecraft:apply_bonus", - "enchantment": "minecraft:fortune", - "formula": "minecraft:ore_drops" - }, - { - "type": "minecraft:explosion_decay" - } - ], - "name": "pala:findium_gem" - } - ] + "type": "minecraft:item", + "name": "pala:findium_ore" } ], "rolls": 1 } ], "random_sequence": "pala:blocks/findium_ore" -} \ No newline at end of file +} diff --git a/mods/pala/src/main/resources/data/pala/loot_table/blocks/kisakay_ore.json b/mods/pala/src/main/resources/data/pala/loot_table/blocks/kisakay_ore.json index 6e4a028..fbca2a6 100644 --- a/mods/pala/src/main/resources/data/pala/loot_table/blocks/kisakay_ore.json +++ b/mods/pala/src/main/resources/data/pala/loot_table/blocks/kisakay_ore.json @@ -2,34 +2,17 @@ "type": "minecraft:block", "pools": [ { + "condition": { + "type": "minecraft:survives_explosion" + }, "entries": [ { - "type": "minecraft:alternatives", - "children": [ - { - "type": "minecraft:item", - "condition": "minecraft:tool/can_silk_touch", - "name": "pala:kisakay_ore" - }, - { - "type": "minecraft:item", - "modifier": [ - { - "type": "minecraft:apply_bonus", - "enchantment": "minecraft:fortune", - "formula": "minecraft:ore_drops" - }, - { - "type": "minecraft:explosion_decay" - } - ], - "name": "pala:kisakay_ingot" - } - ] + "type": "minecraft:item", + "name": "pala:kisakay_ore" } ], "rolls": 1 } ], "random_sequence": "pala:blocks/kisakay_ore" -} \ No newline at end of file +} diff --git a/mods/pala/src/main/resources/data/pala/loot_table/blocks/paladium_green_ore.json b/mods/pala/src/main/resources/data/pala/loot_table/blocks/paladium_green_ore.json index 5899f51..0b16107 100644 --- a/mods/pala/src/main/resources/data/pala/loot_table/blocks/paladium_green_ore.json +++ b/mods/pala/src/main/resources/data/pala/loot_table/blocks/paladium_green_ore.json @@ -2,34 +2,17 @@ "type": "minecraft:block", "pools": [ { + "condition": { + "type": "minecraft:survives_explosion" + }, "entries": [ { - "type": "minecraft:alternatives", - "children": [ - { - "type": "minecraft:item", - "condition": "minecraft:tool/can_silk_touch", - "name": "pala:paladium_green_ore" - }, - { - "type": "minecraft:item", - "modifier": [ - { - "type": "minecraft:apply_bonus", - "enchantment": "minecraft:fortune", - "formula": "minecraft:ore_drops" - }, - { - "type": "minecraft:explosion_decay" - } - ], - "name": "pala:paladium_green_ingot" - } - ] + "type": "minecraft:item", + "name": "pala:paladium_green_ore" } ], "rolls": 1 } ], "random_sequence": "pala:blocks/paladium_green_ore" -} \ No newline at end of file +} diff --git a/mods/pala/src/main/resources/data/pala/loot_table/blocks/paladium_ore.json b/mods/pala/src/main/resources/data/pala/loot_table/blocks/paladium_ore.json index 8b98dc0..fc65ad0 100644 --- a/mods/pala/src/main/resources/data/pala/loot_table/blocks/paladium_ore.json +++ b/mods/pala/src/main/resources/data/pala/loot_table/blocks/paladium_ore.json @@ -2,34 +2,17 @@ "type": "minecraft:block", "pools": [ { + "condition": { + "type": "minecraft:survives_explosion" + }, "entries": [ { - "type": "minecraft:alternatives", - "children": [ - { - "type": "minecraft:item", - "condition": "minecraft:tool/can_silk_touch", - "name": "pala:paladium_ore" - }, - { - "type": "minecraft:item", - "modifier": [ - { - "type": "minecraft:apply_bonus", - "enchantment": "minecraft:fortune", - "formula": "minecraft:ore_drops" - }, - { - "type": "minecraft:explosion_decay" - } - ], - "name": "pala:paladium_ingot" - } - ] + "type": "minecraft:item", + "name": "pala:paladium_ore" } ], "rolls": 1 } ], "random_sequence": "pala:blocks/paladium_ore" -} \ No newline at end of file +} diff --git a/mods/pala/src/main/resources/data/pala/loot_table/blocks/titane_ore.json b/mods/pala/src/main/resources/data/pala/loot_table/blocks/titane_ore.json index aacb555..9b59ab7 100644 --- a/mods/pala/src/main/resources/data/pala/loot_table/blocks/titane_ore.json +++ b/mods/pala/src/main/resources/data/pala/loot_table/blocks/titane_ore.json @@ -2,34 +2,17 @@ "type": "minecraft:block", "pools": [ { + "condition": { + "type": "minecraft:survives_explosion" + }, "entries": [ { - "type": "minecraft:alternatives", - "children": [ - { - "type": "minecraft:item", - "condition": "minecraft:tool/can_silk_touch", - "name": "pala:titane_ore" - }, - { - "type": "minecraft:item", - "modifier": [ - { - "type": "minecraft:apply_bonus", - "enchantment": "minecraft:fortune", - "formula": "minecraft:ore_drops" - }, - { - "type": "minecraft:explosion_decay" - } - ], - "name": "pala:titane_ingot" - } - ] + "type": "minecraft:item", + "name": "pala:titane_ore" } ], "rolls": 1 } ], "random_sequence": "pala:blocks/titane_ore" -} \ No newline at end of file +} diff --git a/mods/pala/src/main/resources/data/pala/worldgen/placed_feature/paladium_ore.json b/mods/pala/src/main/resources/data/pala/worldgen/placed_feature/paladium_ore.json index 37dfbaa..267b9fb 100644 --- a/mods/pala/src/main/resources/data/pala/worldgen/placed_feature/paladium_ore.json +++ b/mods/pala/src/main/resources/data/pala/worldgen/placed_feature/paladium_ore.json @@ -3,7 +3,7 @@ "placement": [ { "type": "minecraft:count", - "count": 3 + "count": 4 }, { "type": "minecraft:in_square" diff --git a/mods/pala/src/main/resources/pala.mixins.json b/mods/pala/src/main/resources/pala.mixins.json index 4cdfde0..408af96 100644 --- a/mods/pala/src/main/resources/pala.mixins.json +++ b/mods/pala/src/main/resources/pala.mixins.json @@ -3,7 +3,9 @@ "package": "fr.surviepote.pala.mixin", "mixins": [ "PalaCraftXpMixin", - "PalaSmeltXpMixin" + "PalaSmeltXpMixin", + "PalaBrewXpMixin", + "PalaBrewTakeXpMixin" ], "injectors": { "defaultRequire": 1 diff --git a/server/mods/pala-0.2.1.jar b/server/mods/pala-0.2.1.jar index 7bc50fc..1fbf3d9 100644 Binary files a/server/mods/pala-0.2.1.jar and b/server/mods/pala-0.2.1.jar differ diff --git a/todo_palamod b/todo_palamod new file mode 100644 index 0000000..827124b --- /dev/null +++ b/todo_palamod @@ -0,0 +1,14 @@ +Zone home corrompue + +/furnace manquant + +Les minerais doivent drop leur bloc (sauf trixium) + +Le système de métier, doit avoir un bouton dans l'inventaire pour voir leur progression +Les tree de kiwano, eggplant etc ne doievent pas être dans l'inventaire admin + +augmenter un peu la luck des fillons de minerais de paladium + +----- + +Les boom boots doivent faire tomber bien plus vite, et en fonction de la hauteur/vitesse, faire une explosion +- grosse