Skip to content
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

fix: ensure that we never have negative values for soul fragment counts #1034

Merged
merged 2 commits into from
Sep 29, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 44 additions & 31 deletions src/states/DemonHunterSoulFragments.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import aceEvent, { AceEvent } from "@wowts/ace_event-3.0";
import { LuaArray, pairs } from "@wowts/lua";
import { max, min } from "@wowts/math";
import { AceModule } from "@wowts/tsaddon";
import { GetTime, SpellId } from "@wowts/wow-mock";
import { OvaleClass } from "../Ovale";
Expand Down Expand Up @@ -32,6 +33,12 @@ class SoulFragmentsData {
count = 0; // total soul fragments, including pending spawns
}

/* There is an in-game lag between when a spell that generates Lesser Soul
* Fragments is cast and when those Lesser Soul Fragments appear in the
* game world before the GCD has finished. Keep track of pending Lesser
* Soul Fragments that have not yet appeared so that we know how many
* Lesser Soul Fragments will be available when the next spell can be cast.
*/
export class OvaleDemonHunterSoulFragmentsClass
extends States<SoulFragmentsData>
implements StateModule
Expand All @@ -43,7 +50,6 @@ export class OvaleDemonHunterSoulFragmentsClass
private hasMetamorphosis = false;
private count = 0; // stack count of Soul Fragment buff
private pending = 0; // pending soul fragment spawns
// invariant: count + pending <= 5

constructor(
private ovale: OvaleClass,
Expand Down Expand Up @@ -185,23 +191,16 @@ export class OvaleDemonHunterSoulFragmentsClass
if (aura && this.aura.isActiveAura(aura, atTime)) {
const gained = aura.stacks - this.count;
if (gained > 0) {
const pending = this.pending - gained;
this.pending = (pending > 0 && pending) || 0;
// invariant: this.pending >= 0
this.pending = max(this.pending - gained, 0);
}
this.count = aura.stacks;
const count = this.count + this.pending;
this.current.count = (count < 5 && count) || 5;
this.pending = this.current.count - this.count;
this.tracer.debug(
`${this.current.count} = ${this.count} + ${this.pending}`
);
// invariant: this.count <= 5
this.count = min(aura.stacks, 5);
this.updateCurrentSoulFragments();
}
} else if (event == "Ovale_AuraRemoved") {
this.count = 0;
this.current.count = this.pending;
this.tracer.debug(
`${this.current.count} = ${this.count} + ${this.pending}`
);
this.updateCurrentSoulFragments();
}
}
}
Expand All @@ -212,23 +211,28 @@ export class OvaleDemonHunterSoulFragmentsClass
if (cleu.sourceGUID == this.ovale.playerGUID) {
const header = cleu.header as SpellPayloadHeader;
const spellId = header.spellId;
if (generator[spellId]) {
let fragments = generator[spellId];
if (fragments > 0 && this.hasMetamorphosis) {
const fragments = generator[spellId];
if (fragments && fragments > 0) {
this.pending += fragments;
if (this.hasMetamorphosis) {
// Metamorphosis triggers an extra Lesser Soul Fragment
fragments = fragments + 1;
this.pending += 1;
}
this.pending += fragments;
const count = this.count + this.pending;
this.current.count = (count < 5 && count) || 5;
this.pending = this.current.count - this.count;
this.tracer.debug(
`${this.current.count} = ${this.count} + ${this.pending}`
);
this.updateCurrentSoulFragments();
}
}
};

private updateCurrentSoulFragments = () => {
// invariant: this.current.count <= 5
this.current.count = min(this.count + this.pending, 5);
// invariant: this.pending >= 0
this.pending = max(this.current.count - this.count, 0);
this.tracer.debug(
`${this.current.count} = ${this.count} + ${this.pending}`
);
};

initializeState(): void {}

resetState() {
Expand All @@ -248,14 +252,23 @@ export class OvaleDemonHunterSoulFragmentsClass
spellcast
) => {
if (this.hasSoulFragmentsHandlers) {
// Spend Soul Fragments first.
if (spender[spellId]) {
const fragments = spender[spellId];
if (fragments < 0) {
const count = this.next.count + fragments;
// invariant: this.next.count >= 0
this.next.count = max(count, 0);
}
}
// Gain Soul Fragments second, to possibly handle refunds.
if (generator[spellId]) {
const fragments = generator[spellId];
const count = this.next.count + fragments;
this.next.count = (count < 5 && count) || 5;
} else if (spender[spellId]) {
const fragments = spender[spellId];
const count = this.next.count + fragments;
this.next.count = (count > 0 && count) || 0;
if (fragments > 0) {
const count = this.next.count + fragments;
// invariant: this.next.count <= 5
this.next.count = min(count, 5);
}
}
}
};
Expand Down