Skip to content

Commit 6e4bdec

Browse files
committed
feat: probabilistic stone charging with harmonic decay via ender pearls
1 parent c71aa74 commit 6e4bdec

4 files changed

Lines changed: 43 additions & 11 deletions

File tree

src/main/java/bor/samsara/questing/SamsaraFabricQuesting.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,10 @@
2323
import net.fabricmc.fabric.api.event.player.UseEntityCallback;
2424
import net.fabricmc.fabric.api.event.player.UseItemCallback;
2525
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
26-
import net.minecraft.entity.LivingEntity;
27-
import net.minecraft.entity.damage.DamageSource;
2826
import net.minecraft.server.command.CommandManager;
2927
import net.minecraft.server.command.ServerCommandSource;
3028
import net.minecraft.server.network.ServerPlayerEntity;
3129
import org.apache.commons.lang3.StringUtils;
32-
import org.jetbrains.annotations.NotNull;
3330
import org.slf4j.Logger;
3431
import org.slf4j.LoggerFactory;
3532

src/main/java/bor/samsara/questing/hearth/HearthStoneEventRegisters.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,17 @@ public static CommandRegistrationCallback createHearthstone() {
114114
if (isHearthstone(stack)) {
115115

116116
if (player.getOffHandStack().isOf(Items.ENDER_PEARL)) {
117-
Sounds.toOnlyPlayer((ServerPlayerEntity) player, SoundEvents.BLOCK_BEACON_POWER_SELECT);
117+
if (isMaxCharged(stack)) {
118+
player.sendMessage(Text.literal("The stone cannot hold more charges.").styled(s -> s.withColor(Formatting.YELLOW)), true);
119+
return ActionResult.CONSUME;
120+
}
118121
player.getOffHandStack().decrement(1);
119-
adjustCharges(stack, +1);
122+
if (rollCharge(stack)) {
123+
Sounds.toOnlyPlayer((ServerPlayerEntity) player, SoundEvents.BLOCK_BEACON_POWER_SELECT);
124+
} else {
125+
Sounds.toOnlyPlayer((ServerPlayerEntity) player, SoundEvents.ITEM_WOLF_ARMOR_REPAIR);
126+
player.sendMessage(Text.literal("The pearl dissolved... no charge gained.").styled(s -> s.withColor(Formatting.GRAY)), true);
127+
}
120128
return ActionResult.CONSUME;
121129
}
122130

src/main/java/bor/samsara/questing/hearth/SoulStoneEventRegisters.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,17 @@ public static UseItemCallback useSoulstone() {
103103

104104
if (isSoulStone(stack)) {
105105
if (player.getOffHandStack().isOf(Items.ENDER_PEARL)) {
106-
Sounds.toOnlyPlayer((ServerPlayerEntity) player, SoundEvents.BLOCK_TRIAL_SPAWNER_OMINOUS_ACTIVATE);
106+
if (isMaxCharged(stack)) {
107+
player.sendMessage(Text.literal("The stone cannot hold more charges.").styled(s -> s.withColor(Formatting.YELLOW)), true);
108+
return ActionResult.CONSUME;
109+
}
107110
player.getOffHandStack().decrement(1);
108-
adjustCharges(stack, +1);
111+
if (rollCharge(stack)) {
112+
Sounds.toOnlyPlayer((ServerPlayerEntity) player, SoundEvents.BLOCK_TRIAL_SPAWNER_OMINOUS_ACTIVATE);
113+
} else {
114+
Sounds.toOnlyPlayer((ServerPlayerEntity) player, SoundEvents.ITEM_OMINOUS_BOTTLE_DISPOSE.value());
115+
player.sendMessage(Text.literal("The pearl dissolved... no charge gained.").styled(s -> s.withColor(Formatting.GRAY)), true);
116+
}
109117
return ActionResult.CONSUME;
110118
}
111119
if (player.getMovement().equals(Vec3d.ZERO)) {

src/main/java/bor/samsara/questing/hearth/WarpStone.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
public abstract class WarpStone {
2222

2323
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;
2428

2529
static final ExecutorService executor = Executors.newThreadPerTaskExecutor(runnable -> new Thread(runnable, "HearthStoneEvent-Thread"));
2630
static final Map<String, TeleportTask> playerTeleportTasks = new ConcurrentHashMap<>();
@@ -63,11 +67,26 @@ protected static void adjustCharges(ItemStack stack, int amount) {
6367
stack.set(DataComponentTypes.LORE, new LoreComponent(lines));
6468
}
6569

70+
protected static int getCharges(ItemStack stack) {
71+
return stack.get(DataComponentTypes.CUSTOM_DATA).copyNbt().getInt(STONE_CHARGES, 0);
72+
}
73+
6674
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;
7190
}
7291

7392
static double jitter(double d) {

0 commit comments

Comments
 (0)