[BUG] fixing multi-hit and move messages on faint (#2981)
* fixing order of messages, scences, to render messages before fainting * updated fix for effectiveness text rendering order for multi hit moves * fixing messages not appearing for multi-hit moves on faint * updated multi-hit condition) * fixing PR conflicts * adding comments and FaintPhase setPhaseQueueSplice bug, fixing overrides merge conflict * writing better comments * removing space diff in overrides * adding fainting check for self damage moves * emergency fixing broken last commit * additional comments for multi-hit problem * updating comments, jsdoc style * fixing linter, destiny bond errors * splitting up varaible comments to be in JSDoc format * fixing tests and merge mistakes * adding rendering of multihit moves that only hit once * fixing comment formatting_tabs and spaces --------- Co-authored-by: Benjamin Odom <bennybroseph@gmail.com>
This commit is contained in:
parent
d0cbe19bd8
commit
ccb38ca12b
|
@ -180,11 +180,16 @@ export default class BattleScene extends SceneBase {
|
||||||
public gameData: GameData;
|
public gameData: GameData;
|
||||||
public sessionSlotId: integer;
|
public sessionSlotId: integer;
|
||||||
|
|
||||||
|
/** PhaseQueue: dequeue/remove the first element to get the next phase */
|
||||||
public phaseQueue: Phase[];
|
public phaseQueue: Phase[];
|
||||||
public conditionalQueue: Array<[() => boolean, Phase]>;
|
public conditionalQueue: Array<[() => boolean, Phase]>;
|
||||||
|
/** PhaseQueuePrepend: is a temp storage of what will be added to PhaseQueue */
|
||||||
private phaseQueuePrepend: Phase[];
|
private phaseQueuePrepend: Phase[];
|
||||||
|
|
||||||
|
/** overrides default of inserting phases to end of phaseQueuePrepend array, useful or inserting Phases "out of order" */
|
||||||
private phaseQueuePrependSpliceIndex: integer;
|
private phaseQueuePrependSpliceIndex: integer;
|
||||||
private nextCommandPhaseQueue: Phase[];
|
private nextCommandPhaseQueue: Phase[];
|
||||||
|
|
||||||
private currentPhase: Phase;
|
private currentPhase: Phase;
|
||||||
private standbyPhase: Phase;
|
private standbyPhase: Phase;
|
||||||
public field: Phaser.GameObjects.Container;
|
public field: Phaser.GameObjects.Container;
|
||||||
|
@ -1961,6 +1966,7 @@ export default class BattleScene extends SceneBase {
|
||||||
return this.standbyPhase;
|
return this.standbyPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a phase to the conditional queue and ensures it is executed only when the specified condition is met.
|
* Adds a phase to the conditional queue and ensures it is executed only when the specified condition is met.
|
||||||
*
|
*
|
||||||
|
@ -1975,11 +1981,19 @@ export default class BattleScene extends SceneBase {
|
||||||
this.conditionalQueue.push([condition, phase]);
|
this.conditionalQueue.push([condition, phase]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a phase to nextCommandPhaseQueue, as long as boolean passed in is false
|
||||||
|
* @param phase {@linkcode Phase} the phase to add
|
||||||
|
* @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue
|
||||||
|
*/
|
||||||
pushPhase(phase: Phase, defer: boolean = false): void {
|
pushPhase(phase: Phase, defer: boolean = false): void {
|
||||||
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
|
(!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds Phase to the end of phaseQueuePrepend, or at phaseQueuePrependSpliceIndex
|
||||||
|
* @param phase {@linkcode Phase} the phase to add
|
||||||
|
*/
|
||||||
unshiftPhase(phase: Phase): void {
|
unshiftPhase(phase: Phase): void {
|
||||||
if (this.phaseQueuePrependSpliceIndex === -1) {
|
if (this.phaseQueuePrependSpliceIndex === -1) {
|
||||||
this.phaseQueuePrepend.push(phase);
|
this.phaseQueuePrepend.push(phase);
|
||||||
|
@ -1988,18 +2002,32 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the phaseQueue
|
||||||
|
*/
|
||||||
clearPhaseQueue(): void {
|
clearPhaseQueue(): void {
|
||||||
this.phaseQueue.splice(0, this.phaseQueue.length);
|
this.phaseQueue.splice(0, this.phaseQueue.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by function unshiftPhase(), sets index to start inserting at current length instead of the end of the array, useful if phaseQueuePrepend gets longer with Phases
|
||||||
|
*/
|
||||||
setPhaseQueueSplice(): void {
|
setPhaseQueueSplice(): void {
|
||||||
this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length;
|
this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets phaseQueuePrependSpliceIndex to -1, implies that calls to unshiftPhase will insert at end of phaseQueuePrepend
|
||||||
|
*/
|
||||||
clearPhaseQueueSplice(): void {
|
clearPhaseQueueSplice(): void {
|
||||||
this.phaseQueuePrependSpliceIndex = -1;
|
this.phaseQueuePrependSpliceIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is called by each Phase implementations "end()" by default
|
||||||
|
* We dump everything from phaseQueuePrepend to the start of of phaseQueue
|
||||||
|
* then removes first Phase and starts it
|
||||||
|
*/
|
||||||
shiftPhase(): void {
|
shiftPhase(): void {
|
||||||
if (this.standbyPhase) {
|
if (this.standbyPhase) {
|
||||||
this.currentPhase = this.standbyPhase;
|
this.currentPhase = this.standbyPhase;
|
||||||
|
@ -2017,7 +2045,7 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
if (!this.phaseQueue.length) {
|
if (!this.phaseQueue.length) {
|
||||||
this.populatePhaseQueue();
|
this.populatePhaseQueue();
|
||||||
// clear the conditionalQueue if there are no phases left in the phaseQueue
|
// Clear the conditionalQueue if there are no phases left in the phaseQueue
|
||||||
this.conditionalQueue = [];
|
this.conditionalQueue = [];
|
||||||
}
|
}
|
||||||
this.currentPhase = this.phaseQueue.shift();
|
this.currentPhase = this.phaseQueue.shift();
|
||||||
|
@ -2102,15 +2130,28 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue
|
||||||
|
* @param message string for MessagePhase
|
||||||
|
* @param callbackDelay optional param for MessagePhase constructor
|
||||||
|
* @param prompt optional param for MessagePhase constructor
|
||||||
|
* @param promptDelay optional param for MessagePhase constructor
|
||||||
|
* @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue
|
||||||
|
*/
|
||||||
queueMessage(message: string, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer, defer?: boolean) {
|
queueMessage(message: string, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer, defer?: boolean) {
|
||||||
const phase = new MessagePhase(this, message, callbackDelay, prompt, promptDelay);
|
const phase = new MessagePhase(this, message, callbackDelay, prompt, promptDelay);
|
||||||
if (!defer) {
|
if (!defer) {
|
||||||
|
// adds to the end of PhaseQueuePrepend
|
||||||
this.unshiftPhase(phase);
|
this.unshiftPhase(phase);
|
||||||
} else {
|
} else {
|
||||||
|
//remember that pushPhase adds it to nextCommandPhaseQueue
|
||||||
this.pushPhase(phase);
|
this.pushPhase(phase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order)
|
||||||
|
*/
|
||||||
populatePhaseQueue(): void {
|
populatePhaseQueue(): void {
|
||||||
if (this.nextCommandPhaseQueue.length) {
|
if (this.nextCommandPhaseQueue.length) {
|
||||||
this.phaseQueue.push(...this.nextCommandPhaseQueue);
|
this.phaseQueue.push(...this.nextCommandPhaseQueue);
|
||||||
|
|
|
@ -2035,6 +2035,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
*/
|
*/
|
||||||
damage.value = this.damageAndUpdate(damage.value, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true);
|
damage.value = this.damageAndUpdate(damage.value, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true);
|
||||||
this.turnData.damageTaken += damage.value;
|
this.turnData.damageTaken += damage.value;
|
||||||
|
|
||||||
if (isCritical) {
|
if (isCritical) {
|
||||||
this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit"));
|
this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit"));
|
||||||
}
|
}
|
||||||
|
@ -2054,7 +2055,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.turnData.hitsLeft === 1) {
|
// want to include is.Fainted() in case multi hit move ends early, still want to render message
|
||||||
|
if (source.turnData.hitsLeft === 1 || this.isFainted()) {
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case HitResult.SUPER_EFFECTIVE:
|
case HitResult.SUPER_EFFECTIVE:
|
||||||
this.scene.queueMessage(i18next.t("battle:hitResultSuperEffective"));
|
this.scene.queueMessage(i18next.t("battle:hitResultSuperEffective"));
|
||||||
|
@ -2075,13 +2077,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isFainted()) {
|
if (this.isFainted()) {
|
||||||
|
// set splice index here, so future scene queues happen before FaintedPhase
|
||||||
|
this.scene.setPhaseQueueSplice();
|
||||||
this.scene.unshiftPhase(new FaintPhase(this.scene, this.getBattlerIndex(), isOneHitKo));
|
this.scene.unshiftPhase(new FaintPhase(this.scene, this.getBattlerIndex(), isOneHitKo));
|
||||||
this.resetSummonData();
|
this.resetSummonData();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (damage) {
|
if (damage) {
|
||||||
this.scene.clearPhaseQueueSplice();
|
|
||||||
|
|
||||||
const attacker = this.scene.getPokemonById(source.id);
|
const attacker = this.scene.getPokemonById(source.id);
|
||||||
destinyTag?.lapse(attacker, BattlerTagLapseType.CUSTOM);
|
destinyTag?.lapse(attacker, BattlerTagLapseType.CUSTOM);
|
||||||
}
|
}
|
||||||
|
@ -2105,6 +2107,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by damageAndUpdate()
|
||||||
|
* @param damage integer
|
||||||
|
* @param ignoreSegments boolean, not currently used
|
||||||
|
* @param preventEndure used to update damage if endure or sturdy
|
||||||
|
* @param ignoreFaintPhase flag on wheter to add FaintPhase if pokemon after applying damage faints
|
||||||
|
* @returns integer representing damage
|
||||||
|
*/
|
||||||
damage(damage: integer, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false): integer {
|
damage(damage: integer, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false): integer {
|
||||||
if (this.isFainted()) {
|
if (this.isFainted()) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2126,9 +2136,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
damage = Math.min(damage, this.hp);
|
damage = Math.min(damage, this.hp);
|
||||||
|
|
||||||
this.hp = this.hp - damage;
|
this.hp = this.hp - damage;
|
||||||
if (this.isFainted() && !ignoreFaintPhase) {
|
if (this.isFainted() && !ignoreFaintPhase) {
|
||||||
|
/**
|
||||||
|
* When adding the FaintPhase, want to toggle future unshiftPhase() and queueMessage() calls
|
||||||
|
* to appear before the FaintPhase (as FaintPhase will potentially end the encounter and add Phases such as
|
||||||
|
* GameOverPhase, VictoryPhase, etc.. that will interfere with anything else that happens during this MoveEffectPhase)
|
||||||
|
*
|
||||||
|
* Once the MoveEffectPhase is over (and calls it's .end() function, shiftPhase() will reset the PhaseQueueSplice via clearPhaseQueueSplice() )
|
||||||
|
*/
|
||||||
|
this.scene.setPhaseQueueSplice();
|
||||||
this.scene.unshiftPhase(new FaintPhase(this.scene, this.getBattlerIndex(), preventEndure));
|
this.scene.unshiftPhase(new FaintPhase(this.scene, this.getBattlerIndex(), preventEndure));
|
||||||
this.resetSummonData();
|
this.resetSummonData();
|
||||||
}
|
}
|
||||||
|
@ -2136,6 +2153,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by apply(), given the damage, adds a new DamagePhase and actually updates HP values, etc.
|
||||||
|
* @param damage integer - passed to damage()
|
||||||
|
* @param result an enum if it's super effective, not very, etc.
|
||||||
|
* @param critical boolean if move is a critical hit
|
||||||
|
* @param ignoreSegments boolean, passed to damage() and not used currently
|
||||||
|
* @param preventEndure boolean, ignore endure properties of pokemon, passed to damage()
|
||||||
|
* @param ignoreFaintPhase boolean to ignore adding a FaintPhase, passsed to damage()
|
||||||
|
* @returns integer of damage done
|
||||||
|
*/
|
||||||
damageAndUpdate(damage: integer, result?: DamageResult, critical: boolean = false, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false): integer {
|
damageAndUpdate(damage: integer, result?: DamageResult, critical: boolean = false, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false): integer {
|
||||||
const damagePhase = new DamagePhase(this.scene, this.getBattlerIndex(), damage, result as DamageResult, critical);
|
const damagePhase = new DamagePhase(this.scene, this.getBattlerIndex(), damage, result as DamageResult, critical);
|
||||||
this.scene.unshiftPhase(damagePhase);
|
this.scene.unshiftPhase(damagePhase);
|
||||||
|
|
|
@ -2380,6 +2380,11 @@ export class TurnStartPhase extends FieldPhase {
|
||||||
this.scene.pushPhase(new BerryPhase(this.scene));
|
this.scene.pushPhase(new BerryPhase(this.scene));
|
||||||
this.scene.pushPhase(new TurnEndPhase(this.scene));
|
this.scene.pushPhase(new TurnEndPhase(this.scene));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this.end() will call shiftPhase(), which dumps everything from PrependQueue (aka everything that is unshifted()) to the front
|
||||||
|
* of the queue and dequeues to start the next phase
|
||||||
|
* this is important since stuff like SwitchSummon, AttemptRun, AttemptCapture Phases break the "flow" and should take precedence
|
||||||
|
*/
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3036,8 +3041,11 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||||
if (--user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) {
|
if (--user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) {
|
||||||
this.scene.unshiftPhase(this.getNewHitPhase());
|
this.scene.unshiftPhase(this.getNewHitPhase());
|
||||||
} else {
|
} else {
|
||||||
|
// Queue message for number of hits made by multi-move
|
||||||
|
// If multi-hit attack only hits once, still want to render a message
|
||||||
const hitsTotal = user.turnData.hitCount - Math.max(user.turnData.hitsLeft, 0);
|
const hitsTotal = user.turnData.hitCount - Math.max(user.turnData.hitsLeft, 0);
|
||||||
if (hitsTotal > 1) {
|
if (hitsTotal > 1 || user.turnData.hitsLeft > 0) {
|
||||||
|
// If there are multiple hits, or if there are hits of the multi-hit move left
|
||||||
this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal }));
|
this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal }));
|
||||||
}
|
}
|
||||||
this.scene.applyModifiers(HitHealModifier, this.player, user);
|
this.scene.applyModifiers(HitHealModifier, this.player, user);
|
||||||
|
|
Loading…
Reference in New Issue