[Bug] Fix return in applyChosenModifier; prevent race condition in modifier-ui-handler (#6099)

* Fix return in applyChosenModifier

* Stop race condition in modifier-select-ui

* Fix null errors inside the tween's onUpdate method

* Minor cleanup of tween chains

* Minor cleanup of tween chains

* Minor fixup
This commit is contained in:
Sirz Benjie 2025-07-16 11:25:58 -06:00 committed by GitHub
parent 3d42e976b7
commit b3f4005a16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 101 additions and 81 deletions

View File

@ -179,7 +179,7 @@ export class SelectModifierPhase extends BattlePhase {
} else { } else {
this.applyModifier(modifierType.newModifier()!); this.applyModifier(modifierType.newModifier()!);
} }
return !cost; return cost === -1;
} }
// Reroll rewards // Reroll rewards

View File

@ -269,13 +269,22 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
globalScene.updateBiomeWaveText(); globalScene.updateBiomeWaveText();
globalScene.updateMoneyText(); globalScene.updateMoneyText();
// DO NOT REMOVE: Fixes bug which allows action input to be processed before the UI is shown,
// causing errors if reroll is selected
this.awaitingActionInput = false;
// TODO: Replace with `Promise.withResolvers` when possible.
let tweenResolve: () => void;
const tweenPromise = new Promise<void>(resolve => (tweenResolve = resolve));
let i = 0; let i = 0;
// TODO: Rework this bespoke logic for animating the modifier options.
globalScene.tweens.addCounter({ globalScene.tweens.addCounter({
ease: "Sine.easeIn", ease: "Sine.easeIn",
duration: 1250, duration: 1250,
onUpdate: t => { onUpdate: t => {
const value = t.getValue(); // The bang here is safe, as `getValue()` only returns null if the tween has been destroyed (which obviously isn't the case inside onUpdate)
const value = t.getValue()!;
const index = Math.floor(value * typeOptions.length); const index = Math.floor(value * typeOptions.length);
if (index > i && index <= typeOptions.length) { if (index > i && index <= typeOptions.length) {
const option = this.options[i]; const option = this.options[i];
@ -286,67 +295,77 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
i++; i++;
} }
}, },
onComplete: () => {
tweenResolve();
},
}); });
globalScene.time.delayedCall(1000 + maxUpgradeCount * 2000, () => { let shopResolve: () => void;
for (const shopOption of this.shopOptionsRows.flat()) { const shopPromise = new Promise<void>(resolve => (shopResolve = resolve));
shopOption.show(0, 0); tweenPromise.then(() => {
} globalScene.time.delayedCall(1000, () => {
for (const shopOption of this.shopOptionsRows.flat()) {
shopOption.show(0, 0);
}
shopResolve();
});
}); });
globalScene.time.delayedCall(4000 + maxUpgradeCount * 2000, () => { shopPromise.then(() => {
if (partyHasHeldItem) { globalScene.time.delayedCall(500, () => {
this.transferButtonContainer.setAlpha(0); if (partyHasHeldItem) {
this.transferButtonContainer.setVisible(true); this.transferButtonContainer.setAlpha(0);
this.transferButtonContainer.setVisible(true);
globalScene.tweens.add({
targets: this.transferButtonContainer,
alpha: 1,
duration: 250,
});
}
this.rerollButtonContainer.setAlpha(0);
this.checkButtonContainer.setAlpha(0);
this.lockRarityButtonContainer.setAlpha(0);
this.continueButtonContainer.setAlpha(0);
this.rerollButtonContainer.setVisible(true);
this.checkButtonContainer.setVisible(true);
this.continueButtonContainer.setVisible(this.rerollCost < 0);
this.lockRarityButtonContainer.setVisible(canLockRarities);
globalScene.tweens.add({ globalScene.tweens.add({
targets: this.transferButtonContainer, targets: [this.checkButtonContainer, this.continueButtonContainer],
alpha: 1, alpha: 1,
duration: 250, duration: 250,
}); });
}
this.rerollButtonContainer.setAlpha(0); globalScene.tweens.add({
this.checkButtonContainer.setAlpha(0); targets: [this.rerollButtonContainer, this.lockRarityButtonContainer],
this.lockRarityButtonContainer.setAlpha(0); alpha: this.rerollCost < 0 ? 0.5 : 1,
this.continueButtonContainer.setAlpha(0); duration: 250,
this.rerollButtonContainer.setVisible(true); });
this.checkButtonContainer.setVisible(true);
this.continueButtonContainer.setVisible(this.rerollCost < 0);
this.lockRarityButtonContainer.setVisible(canLockRarities);
globalScene.tweens.add({ const updateCursorTarget = () => {
targets: [this.checkButtonContainer, this.continueButtonContainer], if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) {
alpha: 1, this.setRowCursor(0);
duration: 250, this.setCursor(2);
}); } else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) {
this.setRowCursor(ShopCursorTarget.REWARDS);
this.setCursor(0);
} else {
this.setRowCursor(globalScene.shopCursorTarget);
this.setCursor(0);
}
};
globalScene.tweens.add({ updateCursorTarget();
targets: [this.rerollButtonContainer, this.lockRarityButtonContainer],
alpha: this.rerollCost < 0 ? 0.5 : 1,
duration: 250,
});
const updateCursorTarget = () => { handleTutorial(Tutorial.Select_Item).then(res => {
if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) { if (res) {
this.setRowCursor(0); updateCursorTarget();
this.setCursor(2); }
} else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) { this.awaitingActionInput = true;
this.setRowCursor(ShopCursorTarget.REWARDS); this.onActionInput = args[2];
this.setCursor(0); });
} else {
this.setRowCursor(globalScene.shopCursorTarget);
this.setCursor(0);
}
};
updateCursorTarget();
handleTutorial(Tutorial.Select_Item).then(res => {
if (res) {
updateCursorTarget();
}
this.awaitingActionInput = true;
this.onActionInput = args[2];
}); });
}); });
@ -687,7 +706,11 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
scale: 0.01, scale: 0.01,
duration: 250, duration: 250,
ease: "Cubic.easeIn", ease: "Cubic.easeIn",
onComplete: () => options.forEach(o => o.destroy()), onComplete: () => {
options.forEach(o => {
o.destroy();
});
},
}); });
[ [
@ -819,7 +842,7 @@ class ModifierOption extends Phaser.GameObjects.Container {
if (!globalScene) { if (!globalScene) {
return; return;
} }
const value = t.getValue(); const value = t.getValue()!;
if (!bounce && value > lastValue) { if (!bounce && value > lastValue) {
globalScene.playSound("se/pb_bounce_1", { globalScene.playSound("se/pb_bounce_1", {
volume: 1 / ++bounceCount, volume: 1 / ++bounceCount,
@ -832,41 +855,38 @@ class ModifierOption extends Phaser.GameObjects.Container {
}, },
}); });
// TODO: Figure out proper delay between chains and then convert this into a single tween chain
// rather than starting multiple tween chains.
for (let u = 0; u < this.modifierTypeOption.upgradeCount; u++) { for (let u = 0; u < this.modifierTypeOption.upgradeCount; u++) {
const upgradeIndex = u; globalScene.tweens.chain({
globalScene.time.delayedCall( tweens: [
remainingDuration - 2000 * (this.modifierTypeOption.upgradeCount - (upgradeIndex + 1 + upgradeCountOffset)), {
() => { delay: remainingDuration - 2000 * (this.modifierTypeOption.upgradeCount - (u + 1 + upgradeCountOffset)),
globalScene.playSound("se/upgrade", { onStart: () => {
rate: 1 + 0.25 * upgradeIndex, globalScene.playSound("se/upgrade", {
}); rate: 1 + 0.25 * u,
this.pbTint.setPosition(this.pb.x, this.pb.y); });
this.pbTint.setTintFill(0xffffff); this.pbTint.setPosition(this.pb.x, this.pb.y).setTintFill(0xffffff).setVisible(true).setAlpha(0);
this.pbTint.setAlpha(0); },
this.pbTint.setVisible(true);
globalScene.tweens.add({
targets: this.pbTint, targets: this.pbTint,
alpha: 1, alpha: 1,
duration: 1000, duration: 1000,
ease: "Sine.easeIn", ease: "Sine.easeIn",
onComplete: () => { onComplete: () => {
this.pb.setTexture( this.pb.setTexture("pb", this.getPbAtlasKey(-this.modifierTypeOption.upgradeCount + (u + 1)));
"pb",
this.getPbAtlasKey(-this.modifierTypeOption.upgradeCount + (upgradeIndex + 1)),
);
globalScene.tweens.add({
targets: this.pbTint,
alpha: 0,
duration: 750,
ease: "Sine.easeOut",
onComplete: () => {
this.pbTint.setVisible(false);
},
});
}, },
}); },
}, {
); targets: this.pbTint,
alpha: 0,
duration: 750,
ease: "Sine.easeOut",
onComplete: () => {
this.pbTint.setVisible(false);
},
},
],
});
} }
} }