|
21 | 21 | public abstract class WarpStone { |
22 | 22 |
|
23 | 23 | protected static final String STONE_CHARGES = "stoneCharges"; |
| 24 | + protected static final int MAX_CHARGES = 64; |
| 25 | + // p(c) = ALPHA/(c+ALPHA) — harmonic decay, so cost-per-charge grows linearly with c (log-scale). |
| 26 | + // ALPHA chosen so Σ(c=0..63) (c+ALPHA)/ALPHA = 864: ALPHA = 2016/(864-64) = 2.52 |
| 27 | + private static final double CHARGE_ALPHA = 2.52; |
24 | 28 |
|
25 | 29 | static final ExecutorService executor = Executors.newThreadPerTaskExecutor(runnable -> new Thread(runnable, "HearthStoneEvent-Thread")); |
26 | 30 | static final Map<String, TeleportTask> playerTeleportTasks = new ConcurrentHashMap<>(); |
@@ -63,11 +67,26 @@ protected static void adjustCharges(ItemStack stack, int amount) { |
63 | 67 | stack.set(DataComponentTypes.LORE, new LoreComponent(lines)); |
64 | 68 | } |
65 | 69 |
|
| 70 | + protected static int getCharges(ItemStack stack) { |
| 71 | + return stack.get(DataComponentTypes.CUSTOM_DATA).copyNbt().getInt(STONE_CHARGES, 0); |
| 72 | + } |
| 73 | + |
66 | 74 | protected static boolean isCharged(ItemStack stack) { |
67 | | - NbtComponent customData = stack.get(DataComponentTypes.CUSTOM_DATA); |
68 | | - NbtCompound nbt = customData.copyNbt(); |
69 | | - int charges = nbt.getInt(STONE_CHARGES, 0); |
70 | | - return charges > 0; |
| 75 | + return getCharges(stack) > 0; |
| 76 | + } |
| 77 | + |
| 78 | + protected static boolean isMaxCharged(ItemStack stack) { |
| 79 | + return getCharges(stack) >= MAX_CHARGES; |
| 80 | + } |
| 81 | + |
| 82 | + /** Consumes one charge attempt. Returns true if the pearl succeeded, false if it fizzled. */ |
| 83 | + protected static boolean rollCharge(ItemStack stack) { |
| 84 | + int c = getCharges(stack); |
| 85 | + if (Math.random() < CHARGE_ALPHA / (c + CHARGE_ALPHA)) { |
| 86 | + adjustCharges(stack, +1); |
| 87 | + return true; |
| 88 | + } |
| 89 | + return false; |
71 | 90 | } |
72 | 91 |
|
73 | 92 | static double jitter(double d) { |
|
0 commit comments