-
Notifications
You must be signed in to change notification settings - Fork 7
NEW API + EVENT HOOKS (While_Leeching + Resource Tracker/Thresholds) #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 1.20-Forge
Are you sure you want to change the base?
Changes from 1 commit
ff6fb59
bf52165
7336969
62bcd68
2559965
211c0ff
6ec6525
c0a0476
5aece79
1fdcb7f
f06bf30
e83314d
0ee8eb6
c364377
26310f1
5976286
308fd77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| package com.robertx22.mine_and_slash.capability.entity; | ||
|
|
||
| import com.robertx22.mine_and_slash.saveclasses.unit.ResourceType; | ||
|
|
||
| /** | ||
| * Accumulates resource LOSS per type (spend, drains, damage, etc). | ||
| * Call addLoss(...) whenever a resource actually decreases. | ||
| * Use consumeThresholds(...) / addAndConsumeForKey(...) to fire effects and keep remainder. | ||
| */ | ||
| public class ResourceTracker { | ||
Dubledice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| private static final float EPS = 1e-4f; | ||
|
|
||
| // Global per-resource accumulators (used for simple thresholds or debug) | ||
| private final java.util.EnumMap<ResourceType, Float> lost = new java.util.EnumMap<>(ResourceType.class); | ||
|
|
||
| /** Record an actual decrease in a resource. */ | ||
| public void addLoss(ResourceType rt, float amount) { | ||
| if (amount <= 0f) return; | ||
| lost.merge(rt, amount, Float::sum); | ||
| } | ||
|
|
||
| /** Current accumulated loss for a resource. */ | ||
| public float getLoss(ResourceType rt) { | ||
| return lost.getOrDefault(rt, 0f); | ||
| } | ||
|
|
||
| /** Consume thresholds for a single resource; keep remainder. */ | ||
| public int consumeThresholds(ResourceType type, float threshold) { | ||
| if (threshold <= 0f) return 0; | ||
| float have = lost.getOrDefault(type, 0f); | ||
| if (have + EPS < threshold) return 0; | ||
|
|
||
| int procs = (int) Math.floor((have + EPS) / threshold); | ||
| float remainder = have - procs * threshold; | ||
|
|
||
| if (remainder <= EPS) lost.remove(type); | ||
| else lost.put(type, remainder); | ||
| return procs; | ||
| } | ||
|
|
||
| /** | ||
| * Consume as many full thresholds as available across a set of resources (combined bucket). | ||
| * Drain is deterministic: the iteration order of the set decides which resource is consumed first. | ||
| * <p><b>Note:</b> Pass an {@link java.util.EnumSet} to guarantee stable drain order.</p> | ||
| */ | ||
| public int consumeThresholdsAcross(java.util.Set<ResourceType> types, float threshold) { | ||
| if (threshold <= 0f || types == null || types.isEmpty()) return 0; | ||
|
|
||
| int procs = 0; | ||
| // Loop while the combined total can pay for at least one threshold | ||
| while (total(types) + EPS >= threshold) { | ||
| float need = threshold; | ||
|
|
||
| for (ResourceType rt : types) { | ||
| float have = lost.getOrDefault(rt, 0f); | ||
| if (have <= 0f) continue; | ||
|
|
||
| float take = Math.min(have, need); | ||
| if (take > 0f) { | ||
| float remaining = have - take; | ||
| if (remaining <= EPS) lost.remove(rt); | ||
| else lost.put(rt, remaining); | ||
| need -= take; | ||
| } | ||
| if (need <= EPS) break; // satisfied this proc | ||
| } | ||
| procs++; | ||
| } | ||
|
|
||
| // Prune tiny leftovers just in case | ||
| for (ResourceType rt : types) { | ||
| if (lost.getOrDefault(rt, 0f) <= EPS) lost.remove(rt); | ||
| } | ||
| return procs; | ||
| } | ||
|
|
||
| /** Sum of accumulated losses for the given set. */ | ||
| private float total(java.util.Set<ResourceType> types) { | ||
| float sum = 0f; | ||
| for (ResourceType rt : types) sum += lost.getOrDefault(rt, 0f); | ||
| return sum; | ||
| } | ||
|
|
||
| // Per-key cursors so multiple specs on the same resource don't interfere | ||
| private final java.util.EnumMap<ResourceType, java.util.Map<String, Float>> keyProgress = | ||
| new java.util.EnumMap<>(ResourceType.class); | ||
|
|
||
| public void clearKey(ResourceType rt, String key) { | ||
Dubledice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (key == null || key.isEmpty()) return; | ||
| var byKey = keyProgress.get(rt); | ||
| if (byKey == null) return; | ||
| byKey.remove(key); | ||
| if (byKey.isEmpty()) { | ||
| keyProgress.remove(rt); | ||
| } | ||
| } | ||
|
|
||
| /** Add loss to a specific key’s cursor for this resource and consume thresholds. */ | ||
| public int addAndConsumeForKey(String key, ResourceType rt, float add, float threshold) { | ||
| if (key == null || key.isEmpty() || add <= 0f || threshold <= 0f) return 0; | ||
|
|
||
| var byKey = keyProgress.computeIfAbsent(rt, __ -> new java.util.HashMap<>()); | ||
| float cur = byKey.getOrDefault(key, 0f) + add; | ||
|
|
||
| int procs = 0; | ||
| while (cur + EPS >= threshold) { | ||
| cur -= threshold; | ||
| procs++; | ||
| } | ||
| if (cur <= EPS) byKey.remove(key); | ||
| else byKey.put(key, cur); | ||
|
|
||
| return procs; | ||
| } | ||
|
|
||
| /** Read current cursor for debug/UI. */ | ||
| public float getKeyProgress(String key, ResourceType rt) { | ||
| var byKey = keyProgress.get(rt); | ||
| return byKey == null ? 0f : byKey.getOrDefault(key, 0f); | ||
| } | ||
|
|
||
| /** Optional utility if you want to wipe a resource’s accumulator. */ | ||
| public void clear(ResourceType rt) { | ||
| lost.remove(rt); | ||
| } | ||
|
|
||
| /** Optional: wipe all. */ | ||
| public void clearAll() { | ||
| lost.clear(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package com.robertx22.mine_and_slash.event_hooks.my_events; | ||
|
|
||
| import com.robertx22.mine_and_slash.mechanics.thresholds.SpendThresholdManager; | ||
| import com.robertx22.mine_and_slash.saveclasses.unit.ResourceType; | ||
| import com.robertx22.mine_and_slash.uncommon.datasaving.Load; | ||
| import net.minecraft.server.level.ServerPlayer; | ||
| import net.minecraft.world.entity.LivingEntity; | ||
|
|
||
| /** | ||
| * Unified entrypoint for resource LOSS (spend, drains, damage). | ||
| * Health damage integration calls this via the LivingDamageEvent handler below. | ||
| * | ||
| * Debug printing is handled inside SpendThresholdManager and is toggled by | ||
| * OnResourceLost.DEBUG_ENABLED. | ||
| */ | ||
| public final class OnResourceLost { | ||
| private OnResourceLost() {} | ||
|
|
||
| public enum LossSource { SpendOrDrain, Damage, Other } | ||
|
|
||
| /** Toggle SpendThresholdManager debug logs per player. */ | ||
| public static boolean DEBUG_ENABLED = false; | ||
|
|
||
| /** Call this whenever a resource actually goes down. */ | ||
| public static void trigger(LivingEntity entity, ResourceType type, float loss, LossSource source) { | ||
| if (loss <= 0f) return; | ||
| if (!(entity instanceof ServerPlayer sp)) return; | ||
|
|
||
| var unit = Load.Unit(sp); | ||
| long now = sp.level().getGameTime(); // ticks | ||
| SpendThresholdManager.processSpend(sp, unit, type, loss, now); | ||
| } | ||
|
|
||
| /** Wire health damage into the unified loss path. */ | ||
| @net.minecraftforge.eventbus.api.SubscribeEvent | ||
| public static void onLivingDamage(net.minecraftforge.event.entity.living.LivingDamageEvent evt) { | ||
|
||
| if (!(evt.getEntity() instanceof ServerPlayer sp)) return; | ||
| float applied = evt.getAmount(); | ||
| if (applied > 0f) { | ||
| trigger(sp, ResourceType.health, applied, LossSource.Damage); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package com.robertx22.mine_and_slash.mechanics.thresholds; | ||
|
|
||
| import com.robertx22.mine_and_slash.capability.entity.EntityData; | ||
| import com.robertx22.mine_and_slash.saveclasses.unit.ResourceType; | ||
| import net.minecraft.server.level.ServerPlayer; | ||
|
|
||
| import javax.annotation.Nullable; | ||
| import java.util.Set; | ||
|
|
||
| public class DataDrivenSpendThresholdSpec extends SpendThresholdSpec { | ||
|
|
||
| public enum ThresholdMode { X_PER_LEVEL, FLAT, PCT_OF_MAX } | ||
|
|
||
| private final ThresholdMode mode; | ||
| private final float value; | ||
| private final boolean multiplyByLevel; | ||
| @Nullable private final ResourceType pctMaxOf; | ||
|
|
||
| public DataDrivenSpendThresholdSpec( | ||
| String key, | ||
| ResourceType resource, | ||
| ThresholdMode mode, | ||
| float value, | ||
| boolean multiplyByLevel, | ||
| @Nullable ResourceType pctMaxOf, | ||
| Set<String> lockWhileEffectIds, | ||
| int cooldownTicks, | ||
| boolean lockWhileCooldown, | ||
| boolean dropProgressWhileLocked, | ||
| boolean resetProgressOnProc | ||
| ) { | ||
| super(resource, /*perLevelFactor (unused)*/ 0f, key, | ||
Dubledice marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| lockWhileEffectIds, cooldownTicks, lockWhileCooldown, dropProgressWhileLocked, resetProgressOnProc); | ||
| this.mode = mode; | ||
| this.value = value; | ||
| this.multiplyByLevel = multiplyByLevel; | ||
| this.pctMaxOf = pctMaxOf; | ||
| } | ||
|
|
||
| @Override | ||
| public float thresholdFor(EntityData unit) { | ||
| float base; | ||
| switch (mode) { | ||
| case X_PER_LEVEL: | ||
| base = value * Math.max(1, unit.getLevel()); | ||
| break; | ||
| case FLAT: | ||
| base = value * (multiplyByLevel ? Math.max(1, unit.getLevel()) : 1f); | ||
| break; | ||
| case PCT_OF_MAX: | ||
| ResourceType rt = (pctMaxOf != null) ? pctMaxOf : resource(); | ||
| float max = unit.getResources().getMax(unit.getEntity(), rt); | ||
| base = (value / 100f) * max; | ||
| if (multiplyByLevel) base *= Math.max(1, unit.getLevel()); | ||
| break; | ||
| default: | ||
| base = 0f; | ||
| } | ||
| return Math.max(0f, base); | ||
| } | ||
|
|
||
| @Override | ||
| public void onProc(ServerPlayer sp, int procs) { | ||
| // No default action here; datapack loader wires actions. | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package com.robertx22.mine_and_slash.mechanics.thresholds; | ||
|
|
||
| import com.robertx22.mine_and_slash.saveclasses.unit.ResourceType; | ||
| import com.robertx22.mine_and_slash.uncommon.datasaving.Load; | ||
| import net.minecraft.server.level.ServerPlayer; | ||
|
|
||
| public final class SpendKeys { | ||
Dubledice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| private SpendKeys() {} | ||
| public static String key(String nodeId, ResourceType rt) { return "spend." + rt.id + "." + nodeId; } | ||
| public static float threshold(ServerPlayer sp, float perLevelFactor) { | ||
| return perLevelFactor * Load.Unit(sp).getLevel(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.robertx22.mine_and_slash.mechanics.thresholds; | ||
|
|
||
| import com.robertx22.mine_and_slash.capability.entity.EntityData; | ||
| import java.util.List; | ||
|
|
||
| public interface SpendThresholdContributor { | ||
| /** Return zero or more specs active for this unit (e.g., from allocated talents). */ | ||
| List<SpendThresholdSpec> getSpendThresholds(EntityData unit); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.