diff --git a/PALAMOD_CDC.md b/PALAMOD_CDC.md index 5c4c231..347172c 100644 --- a/PALAMOD_CDC.md +++ b/PALAMOD_CDC.md @@ -581,6 +581,12 @@ avant M4 (TODO:crafts-machines). existants (findium, amethyste, graines, pepite endium). ECART : lots speciaux inexistants (voidstone, excavatrices...) => TODO:recompenses-items. + furnaceSmelts completee (6 minerais). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu (level-up joueur). +- M-xpcurve LIVRE 2026-10-19 ~03:40 : seuils XP wiki VERIFIES (table mineur niv.2-20 : + 22 123 a 2 594 953, points = milliers) en config data-driven + formule au-dela ; XP + "broyer" fermier au crusher (3/4.5/6, attribution dernier ouvreur, ECART documente) ; + pas d'XP au toucher de soie (wiki) ; hammerArea confirme sans XP (destroyBlock direct). + TODO:crusher-xp CLOS. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur, 19 cles config. + UNVERIFIED en jeu. - Client/mod : boucle visuelle OPERATIONNELLE 2026-10-19 (Xvfb :99 + runClient SDL x11 + captures Java Robot + pilotage xdotool + vision_analyze) : menu, Select World, monde New World (creatif, cheats), /give, pose de bloc, inventaire, F3+T. Screens : scratch vis*.png. 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 15fd7c2..25d56a1 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 @@ -43,9 +43,20 @@ public final class PalaConfig { Map.entry("pala:paladium_green_ore", 20.0), Map.entry("pala:trixium_ore", 10.0), Map.entry("pala:findium_ore", 10.0))); - // Courbe XP : cout du niveau n. UNVERIFIED (formule placeholder, a caler M5). + // Courbe XP : seuils wiki VERIFIES (page Evolution mineur, "XP necessaire" : les points + // sont des separateurs de milliers : 22.123 = 22 123). Niveaux 2-20 en table exacte ; + // au-dela : formule de prolongation (xpBase * n^xpExponent, UNVERIFIED). La table mineur + // s'applique aux 4 metiers (seule table publiee ; ECART documente). 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))); // Niveau mineur requis pour /nether (wiki recompenses mineur niv.3, VERIFIE). public int netherMinerLevel = 3; @@ -92,6 +103,13 @@ public final class PalaConfig { Map.entry("minecraft:melon", new double[]{4, 3}), Map.entry("minecraft:pumpkin", new double[]{5, 6}))); + // M-xpcurve (wiki fermier "Broyer", VERIFIE) : fruit -> [xp, niveau requis]. + // Eggplant 3@8, chervil==okami 4.5@12, kiwano 6@16. Attribution : dernier ouvreur + // de la GUI (BE, ECART documente : pas de tracking du deposant). + public Map crusherXp = new HashMap<>(Map.of( + "pala:egg_plant_fruit", new double[]{3, 8}, + "pala:okami_fruit", new double[]{4.5, 12}, + "pala:kiwano", new double[]{6, 16})); // 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( @@ -182,9 +200,10 @@ public final class PalaConfig { // 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", "xpBase", - "xpExponent", "netherMinerLevel", "stickCooldowns", "rankNodes", - "hunterKills", "farmerHarvest", "crusherResults", "crusherFuels", - "crusherInfiniteFuels", "furnaceSmelts", "furnaceFuels", + "xpExponent", "levelThresholds", "netherMinerLevel", "stickCooldowns", + "rankNodes", "hunterKills", "farmerHarvest", "crusherXp", + "crusherResults", "crusherFuels", "crusherInfiniteFuels", + "furnaceSmelts", "furnaceFuels", "serverName", "wikiUrl", "discordUrl", "shopUrl"); boolean complete = true; try (Reader keysReader = Files.newBufferedReader(path)) { @@ -222,8 +241,12 @@ public final class PalaConfig { } } - /** Cout XP total pour atteindre le niveau n (n >= 1). */ + /** Cout XP total pour atteindre le niveau n (n >= 1) : table wiki 2-20, formule au-dela. */ public long xpForLevel(int level) { + Long exact = levelThresholds.get(level); + if (exact != null) { + return exact; + } return (long) (xpBase * Math.pow(level, xpExponent)); } } diff --git a/mods/pala/src/main/java/fr/surviepote/pala/events/PalaToolEvents.java b/mods/pala/src/main/java/fr/surviepote/pala/events/PalaToolEvents.java index 1346429..0bdf792 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/events/PalaToolEvents.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/events/PalaToolEvents.java @@ -137,6 +137,19 @@ public final class PalaToolEvents { return true; } + // M-xpcurve (wiki mineur) : le toucher de soie ne donne pas d'XP metier. + private static boolean hasSilkTouch(ServerLevel level, ItemStack hand) { + if (hand.isEmpty()) { + return false; + } + net.minecraft.core.Holder silk = + level.registryAccess() + .lookupOrThrow(net.minecraft.core.registries.Registries.ENCHANTMENT) + .getOrThrow(net.minecraft.world.item.enchantment.Enchantments.SILK_TOUCH); + return net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(silk, + hand) > 0; + } + private static void afterBreak(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity) { if (level.isClientSide() || !(level instanceof ServerLevel sl) @@ -147,8 +160,10 @@ public final class PalaToolEvents { ItemStack hand = sp.getMainHandItem(); Item held = hand.isEmpty() ? null : hand.getItem(); - // --- Metiers : XP mineur (M0) --- - PalaJobs.onBlockBreak(sp, sl, pos, state); + // --- Metiers : XP mineur (M0) : pas de XP au toucher de soie (wiki) --- + if (!hasSilkTouch(sl, hand)) { + PalaJobs.onBlockBreak(sp, sl, pos, state); + } // --- Metiers : XP fermier recoltes vanilla (M5a) --- PalaJobEvents.onHarvest(sp, state); diff --git a/mods/pala/src/main/java/fr/surviepote/pala/machine/PalaMachineBlockEntity.java b/mods/pala/src/main/java/fr/surviepote/pala/machine/PalaMachineBlockEntity.java index 4955b58..f714d74 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/machine/PalaMachineBlockEntity.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/machine/PalaMachineBlockEntity.java @@ -47,6 +47,8 @@ public class PalaMachineBlockEntity extends BlockEntity implements MenuProvider private final SimpleContainer inventory = new SimpleContainer(9); private int progress; private double luck; + // M-xpcurve : dernier ouvreur de la GUI (attribution XP "broyer" fermier). + private String lastOpener = ""; public PalaMachineBlockEntity(BlockPos pos, BlockState state) { super(PalaBlockEntities.MACHINE, pos, state); @@ -88,6 +90,7 @@ public class PalaMachineBlockEntity extends BlockEntity implements MenuProvider output.grow(1); } luck = 0.0; + grantCrusherXp(fruit); } setChanged(); } @@ -149,12 +152,35 @@ public class PalaMachineBlockEntity extends BlockEntity implements MenuProvider return PalaConfig.get().furnaceFuels.contains(id) || PalaConfig.get().crusherFuels.contains(id); } + // M-xpcurve (wiki fermier "Broyer") : XP au dernier ouvreur a chaque production. + private void grantCrusherXp(ItemStack fruit) { + if (lastOpener.isEmpty() || level == null || level.isClientSide()) { + return; + } + double[] table = PalaConfig.get().crusherXp.get(itemId(fruit)); + if (table == null) { + return; + } + net.minecraft.server.level.ServerPlayer opener; + try { + opener = ((net.minecraft.server.level.ServerLevel) level).getServer().getPlayerList() + .getPlayer(java.util.UUID.fromString(lastOpener)); + } catch (IllegalArgumentException e) { + return; + } + if (opener != null + && fr.surviepote.pala.core.PalaJobs.levelOf(opener, "fermier") >= (int) table[1]) { + fr.surviepote.pala.core.PalaJobs.addXp(opener, "fermier", table[0]); + } + } + @Override protected void saveAdditional(ValueOutput output) { super.saveAdditional(output); inventory.storeAsItemList(output.list("Items", ItemStack.CODEC)); output.putInt("Progress", progress); output.putDouble("Luck", luck); + output.putString("LastOpener", lastOpener); } @Override @@ -163,6 +189,7 @@ public class PalaMachineBlockEntity extends BlockEntity implements MenuProvider inventory.fromItemList(input.listOrEmpty("Items", ItemStack.CODEC)); progress = input.getIntOr("Progress", 0); luck = input.getDoubleOr("Luck", 0.0); + lastOpener = input.getStringOr("LastOpener", ""); } @Override @@ -172,6 +199,10 @@ public class PalaMachineBlockEntity extends BlockEntity implements MenuProvider @Override public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { + if (player instanceof net.minecraft.server.level.ServerPlayer sp) { + lastOpener = sp.getUUID().toString(); + setChanged(); + } return new DispenserMenu(containerId, playerInventory, inventory); } } diff --git a/server/mods/pala-0.2.1.jar b/server/mods/pala-0.2.1.jar index 076c611..8b35909 100644 Binary files a/server/mods/pala-0.2.1.jar and b/server/mods/pala-0.2.1.jar differ