-
Notifications
You must be signed in to change notification settings - Fork 6
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?
Conversation
Contains (NEW) API + Event Hooks. Contains Refactored/Factored Code. Desc: Made for new "WHILE_LEECHING" system. Also houses new HasExileEffectCondition, e.g. allowing WHILE_(...) tags.
Implemented brand new Resource Threshold fetcher, tracking ALL resources for conditional stats/effects. Desc: Completely Datapack driven.
src/main/java/com/robertx22/mine_and_slash/capability/entity/ResourceTracker.java
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/capability/entity/EntityData.java
Outdated
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/capability/entity/ResourceTracker.java
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/datapack/SpendThresholdDef.java
Outdated
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/datapack/SpendThresholdDef.java
Outdated
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/SpendThresholdRuntime.java
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/SpendThresholdSpec.java
Outdated
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/SpendThresholdSpec.java
Outdated
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/SpendThresholdSpec.java
Outdated
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/ThresholdsInit.java
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/datapack/SpendThresholdDef.java
Outdated
Show resolved
Hide resolved
| } | ||
|
|
||
| // Pretty names for resources (UI text) | ||
| private static final Map<ResourceType, String> RES_NAME = Map.of( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In next PR after you move thresholds generation into database it should be possible to use ResourceType to handle translations instead of raw text
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had figured that leeching was hardcoded and wouldn't need datapack functionality, unless you want me to use datadriven resource types.
| ); | ||
|
|
||
| // ---------- Generic flags ---------- | ||
| public static final EffectCtx LEECHING_STATE = state( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In next PR after you move thresholds generation into database it should be possible to replace raw text with translatable placeholders similar to locNameForLangFile function
...ain/java/com/robertx22/mine_and_slash/aoe_data/database/exile_effects/adders/ModEffects.java
Outdated
Show resolved
Hide resolved
...ain/java/com/robertx22/mine_and_slash/aoe_data/database/exile_effects/adders/ModEffects.java
Outdated
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/datapack/SpendThresholdDef.java
Outdated
Show resolved
Hide resolved
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/SpendThresholdSpec.java
Outdated
Show resolved
Hide resolved
|
I see a lot of conversations resolved, but where any changes actually made? I'd like some of the conventions to be standardized. If it helps I can comment which conversations you've resolved I want actioned to make it more clear. |
Yeah, please do. Although for many if not all resolved commits, I simply implemented the suggested changes. |
It's still an ongoing process on making it datapack-driven, I hope too that Dice will push everything after it's done |
Oh yeah, thanks for working with duble on this. Yeah spoke with duble earlier, I will wait patiently |
|
Added Suggested Fixes: Datapack Threshold to the PR. Contains all requested changes. Contains potentially some placeholder values like EDIT: Some of the PR is missing, I will add it named "Suggested Fixes: Extension". Apologies for the mistake. EDIT 2: Suggested Fixes: Extension has been added. |
Suggested Fixes: Extension
| public static int ESSENCE_OF_FROST_MAX_STACKS = 5; | ||
|
|
||
| // ---------- Helper ---------- | ||
| private static EffectCtx state(String id, String name, Elements elem) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has to be done as method overload
| private static EffectCtx state(String id, String name, Elements elem) { | |
| private static EffectCtx state(String id, String name, Elements elem) { | |
| return new EffectCtx(id, name, elem, EffectType.beneficial); | |
| } | |
| private static EffectCtx statePhysical(String id, String name) { | |
| return state(id, name, Elements.Physical); | |
| } |
|
|
||
| LEECHING_STATE_BY_RES.put( | ||
| rt, state("leeching_" + rt.id + "_state", "Leeching " + nice, elem) | ||
| rt, state("leeching_" + rt.id + "_state", "Leeching " + nice, null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's always a good behavior to get rid of arguments that can be null
| rt, state("leeching_" + rt.id + "_state", "Leeching " + nice, null) | |
| rt, statePhysical("leeching_" + rt.id + "_state", "Leeching " + nice) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, same thing here. I can't remove null. It causes errors because of how EffectCtx is setup.
| ); | ||
| REGEN_STATE_BY_RES.put( | ||
| rt, state("regen_" + rt.id + "_state", "Regenerating " + nice, elem) | ||
| rt, state("regen_" + rt.id + "_state", "Regenerating " + nice, null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| rt, state("regen_" + rt.id + "_state", "Regenerating " + nice, null) | |
| rt, statePhysical("regen_" + rt.id + "_state", "Regenerating " + nice) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why statePhysical?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also changing it to:
REGEN_STATE_BY_RES.put(
rt, statePhysical("regen_" + rt.id + "_state", "Regenerating " + nice)
);
causes errors. Only rt, state("regen_" + rt.id + "_state", "Regenerating " + nice, null) is accepted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because that null you had before meant Elements.Physical in your state method little higher here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because that
nullyou had before meantElements.Physicalin yourstatemethod little higher here
Sure, but I can not change it to anything else other than using physical or another element. If you want me to change it to rt, state("regen_" + rt.id + "_state", "Regenerating " + nice, Elements.Physical) I can do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but I can not change it to anything else other than using physical. If you want me to change it to
rt, state("regen_" + rt.id + "_state", "Regenerating " + nice, Elements.Physical)I can do that.
Yea, either this or what I suggested before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but I can not change it to anything else other than using physical. If you want me to change it to
rt, state("regen_" + rt.id + "_state", "Regenerating " + nice, Elements.Physical)I can do that.Yea, either this or what I suggested before
I can't do what you suggested before because of how EffectCtx is defined. I will have to use a specific element from now on. Which I assume I can just default everything to phys if I have to.
|
|
||
| public boolean showUi() { return showUi; } | ||
|
|
||
| // Perk lock is handled by callers (anonymous subclass) when needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this will be implemented later. It is placeholder for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, just make sure next time no placeholders are added.
They just add extra lines and some complexity to the PR while doing literally nothing, I think everyone would appreciate such approach
src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/SpendThresholdSpec.java
Outdated
Show resolved
Hide resolved
| @SerializedName("effect_id") public String effectId; | ||
| @SerializedName("duration_ticks") public int durationTicks = 200; | ||
| @SerializedName("exile_potion_id") public String effectId; | ||
| @SerializedName("duration_seconds") public int durationSeconds = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you return back to ticks instead of seconds everywhere?
It seems like your code needs ticks and MnS also usually uses ticks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can revert this, but, the code still uses ticks. I just did some math to convert seconds to ticks because I am a neanderthal and cant count ticks. If it's an issue I can just deal with it and eventually I will remember the conversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please, let's use ticks instead of seconds
| for (ProcAction a : onProc) { | ||
| if (!"apply_effect".equalsIgnoreCase(a.action) || a.effectId == null) continue; | ||
| if (!"exile_effect".equalsIgnoreCase(a.action) || a.effectId == null) continue; | ||
| var effect = com.robertx22.mine_and_slash.database.registry.ExileDB.ExileEffects().get(a.effectId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add an import and
| var effect = com.robertx22.mine_and_slash.database.registry.ExileDB.ExileEffects().get(a.effectId); | |
| var effect = ExileDB.ExileEffects().get(a.effectId); |
|
|
||
| // Attach on-expire duration overrides (convert seconds -> ticks) | ||
| if (a.onExpire != null && !a.onExpire.isEmpty()) { | ||
| if (inst.onExpireEffectDurationTicks == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR doesn't have ExileEffectInstanceData.onExpireEffectDurationTicks, looks like this belongs to an another PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is apart of another PR. If it's causing errors just comment it out as it will be used later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you try to run your branch now, it won't even compile due to this error, so yea, just comment it out until next PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it specifically lines 108-118 that is causing issues?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, just try to compile the mod
Mine-And-Slash-Rework-new/src/main/java/com/robertx22/mine_and_slash/mechanics/thresholds/datapack/SpendThresholdDef.java:109: error: cannot find symbol
if (inst.onExpireEffectDurationTicks == null) {
^
symbol: variable onExpireEffectDurationTicks
location: variable inst of type ExileEffectInstanceData
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, got it. Thank you!
| var unit = com.robertx22.mine_and_slash.uncommon.datasaving.Load.Unit(sp); | ||
| var store = unit.getStatusEffectsData(); | ||
|
|
||
| for (ProcAction a : onProc) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace this loop with
for (ProcAction a : onProc) {
if (!"exile_effect".equalsIgnoreCase(a.action) || a.effectId == null) continue;
var effect = ExileDB.ExileEffects().get(a.effectId);
if (effect == null) continue;
int durTicks = SpendThresholdSpec.secondsToTicks(a.durationSeconds);
int stacks = Math.max(1, a.stacks);
var inst = EffectUtils.applyEffect(sp, effect, durTicks, stacks);
// Attach on-expire duration overrides (convert seconds -> ticks)
if (a.onExpire != null && !a.onExpire.isEmpty()) {
if (inst.onExpireEffectDurationTicks == null) {
inst.onExpireEffectDurationTicks = new java.util.HashMap<>();
}
for (var e : a.onExpire.entrySet()) {
int ticks = SpendThresholdSpec.secondsToTicks(Math.max(0, e.getValue()));
if (ticks > 0) {
inst.onExpireEffectDurationTicks.put(e.getKey(), ticks);
}
}
}
effect.onApply(sp);
unit.sync.setDirty();
}
To make this work, replace EffectUtils with this:
public final class EffectUtils {
private EffectUtils() {}
/**
* Apply/refresh a state effect on the player.
*
* @param sp target player
* @param ctx effect context (ids defined in ModEffects)
* @param durationTicks desired remaining lifetime (ticks); merged via MAX
* @param stacks desired stacks; clamped to effect.max_stacks and merged via MAX
* @return ExileEffectInstanceData for the applied effect, or null if resolve failed.
*/
public static ExileEffectInstanceData applyState(ServerPlayer sp, EffectCtx ctx, int durationTicks, int stacks) {
final ExileEffect effect = resolveEffect(ctx);
if (effect == null) return null;
return applyEffect(sp, effect, durationTicks, stacks);
}
public static ExileEffectInstanceData applyEffect(ServerPlayer sp, ExileEffect effect, int durationTicks, int stacks) {
if (effect == null) return null;
var unit = Load.Unit(sp);
var store = unit.getStatusEffectsData();
var inst = store.getOrCreate(effect); // persist if missing
// Merge stacks/ticks: refresh semantics (never decrease on re-apply)
final int wanted = Math.max(1, stacks);
final int capped = (effect.max_stacks > 0) ? Math.min(wanted, effect.max_stacks) : wanted;
inst.stacks = Math.max(inst.stacks, capped);
inst.ticks_left = Math.max(inst.ticks_left, durationTicks);
// Keep vanilla stats / one-of-a-kind cleanup in sync
effect.onApply(sp);
unit.sync.setDirty(); // network/state sync
return inst;
}
/** Try both resourcePath (preferred) and id; some data uses either. */
private static ExileEffect resolveEffect(EffectCtx ctx) {
ExileEffect eff = ExileDB.ExileEffects().get(ctx.resourcePath);
if (eff == null) eff = ExileDB.ExileEffects().get(ctx.id);
return eff;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is wrong with the current version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a good behavior to have no duplicated code and your for loop was almost a clean copy of EffectUtils.applyEffect so let's keep code clean
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace this loop with
for (ProcAction a : onProc) { if (!"exile_effect".equalsIgnoreCase(a.action) || a.effectId == null) continue; var effect = ExileDB.ExileEffects().get(a.effectId); if (effect == null) continue; int durTicks = SpendThresholdSpec.secondsToTicks(a.durationSeconds); int stacks = Math.max(1, a.stacks); var inst = EffectUtils.applyEffect(sp, effect, durTicks, stacks); // Attach on-expire duration overrides (convert seconds -> ticks) if (a.onExpire != null && !a.onExpire.isEmpty()) { if (inst.onExpireEffectDurationTicks == null) { inst.onExpireEffectDurationTicks = new java.util.HashMap<>(); } for (var e : a.onExpire.entrySet()) { int ticks = SpendThresholdSpec.secondsToTicks(Math.max(0, e.getValue())); if (ticks > 0) { inst.onExpireEffectDurationTicks.put(e.getKey(), ticks); } } } effect.onApply(sp); unit.sync.setDirty(); }To make this work, replace EffectUtils with this:
public final class EffectUtils { private EffectUtils() {} /** * Apply/refresh a state effect on the player. * * @param sp target player * @param ctx effect context (ids defined in ModEffects) * @param durationTicks desired remaining lifetime (ticks); merged via MAX * @param stacks desired stacks; clamped to effect.max_stacks and merged via MAX * @return ExileEffectInstanceData for the applied effect, or null if resolve failed. */ public static ExileEffectInstanceData applyState(ServerPlayer sp, EffectCtx ctx, int durationTicks, int stacks) { final ExileEffect effect = resolveEffect(ctx); if (effect == null) return null; return applyEffect(sp, effect, durationTicks, stacks); } public static ExileEffectInstanceData applyEffect(ServerPlayer sp, ExileEffect effect, int durationTicks, int stacks) { if (effect == null) return null; var unit = Load.Unit(sp); var store = unit.getStatusEffectsData(); var inst = store.getOrCreate(effect); // persist if missing // Merge stacks/ticks: refresh semantics (never decrease on re-apply) final int wanted = Math.max(1, stacks); final int capped = (effect.max_stacks > 0) ? Math.min(wanted, effect.max_stacks) : wanted; inst.stacks = Math.max(inst.stacks, capped); inst.ticks_left = Math.max(inst.ticks_left, durationTicks); // Keep vanilla stats / one-of-a-kind cleanup in sync effect.onApply(sp); unit.sync.setDirty(); // network/state sync return inst; } /** Try both resourcePath (preferred) and id; some data uses either. */ private static ExileEffect resolveEffect(EffectCtx ctx) { ExileEffect eff = ExileDB.ExileEffects().get(ctx.resourcePath); if (eff == null) eff = ExileDB.ExileEffects().get(ctx.id); return eff; } }
This ended up breaking my code in EffectUtils.java, but I will fix it since I see the vision.
Suggested Fixes: Cleanup
|
Added changes to new PR: Suggested Fixes: Cleanup. Awaiting review. |
| public int priority() { return priority; } | ||
| public boolean showUi() { return showUi; } | ||
|
|
||
| // fluent config (for code-defined specs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Methods from 73 to 111 still needed to be removed excluding withPriority method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dubledice this is still pending
| public final class SpendThresholdManager { | ||
| private SpendThresholdManager() {} | ||
|
|
||
| public static void registerDefaults() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still needs to be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't delete SpendThresholdManager.java as it is used for onExpire logic later. It also handles debugging.
Suggested Fixes: SpendThresholdManager.java
|
Suggested Fixes: SpendThresholdManager.java added to PR |
|
Added Suggested Fixes: SpendThresholdSpec.java to the PR. |
SaloEater
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fingers crossed
PR to Parent branch.
Commits summarized:
General:
IMPORTANT: The commits on Aug 19, 2025 were mandatory changes for me to access submodules, please review those changes carefully as I don't want them to cause issues for the rest of the team.