Merge branch 'beta' into tera-summary-screen

This commit is contained in:
NightKev 2025-02-26 08:59:39 -08:00 committed by GitHub
commit 84ae4d9660
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 65 additions and 37 deletions

View File

@ -5152,6 +5152,10 @@ function applySingleAbAttrs<TAttr extends AbAttr>(
showAbilityInstant: boolean = false, showAbilityInstant: boolean = false,
messages: string[] = [] messages: string[] = []
) { ) {
if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) {
return;
}
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
if (gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain())) { if (gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain())) {
return; return;
@ -5445,14 +5449,12 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(
gainedMidTurn: boolean = false gainedMidTurn: boolean = false
) { ) {
for (const passive of [ false, true ]) { for (const passive of [ false, true ]) {
if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) { if (pokemon) {
continue;
}
applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, showAbilityInstant, messages); applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, showAbilityInstant, messages);
globalScene.clearPhaseQueueSplice(); globalScene.clearPhaseQueueSplice();
} }
} }
}
export function applyAbAttrs( export function applyAbAttrs(
attrType: Constructor<AbAttr>, attrType: Constructor<AbAttr>,

View File

@ -1230,10 +1230,12 @@ export class FairyLockTag extends ArenaTag {
*/ */
export class SuppressAbilitiesTag extends ArenaTag { export class SuppressAbilitiesTag extends ArenaTag {
private sourceCount: number; private sourceCount: number;
private beingRemoved: boolean;
constructor(sourceId: number) { constructor(sourceId: number) {
super(ArenaTagType.NEUTRALIZING_GAS, 0, undefined, sourceId); super(ArenaTagType.NEUTRALIZING_GAS, 0, undefined, sourceId);
this.sourceCount = 1; this.sourceCount = 1;
this.beingRemoved = false;
} }
public override onAdd(arena: Arena): void { public override onAdd(arena: Arena): void {
@ -1267,6 +1269,7 @@ export class SuppressAbilitiesTag extends ArenaTag {
} }
public override onRemove(arena: Arena, quiet: boolean = false) { public override onRemove(arena: Arena, quiet: boolean = false) {
this.beingRemoved = true;
if (!quiet) { if (!quiet) {
globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove")); globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
} }
@ -1282,6 +1285,10 @@ export class SuppressAbilitiesTag extends ArenaTag {
public shouldApplyToSelf(): boolean { public shouldApplyToSelf(): boolean {
return this.sourceCount > 1; return this.sourceCount > 1;
} }
public isBeingRemoved() {
return this.beingRemoved;
}
} }
// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter // TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter

View File

@ -1866,11 +1866,14 @@ export class HalfSacrificialAttr extends MoveEffectAttr {
export class AddSubstituteAttr extends MoveEffectAttr { export class AddSubstituteAttr extends MoveEffectAttr {
/** The ratio of the user's max HP that is required to apply this effect */ /** The ratio of the user's max HP that is required to apply this effect */
private hpCost: number; private hpCost: number;
/** Whether the damage taken should be rounded up (Shed Tail rounds up) */
private roundUp: boolean;
constructor(hpCost: number = 0.25) { constructor(hpCost: number, roundUp: boolean) {
super(true); super(true);
this.hpCost = hpCost; this.hpCost = hpCost;
this.roundUp = roundUp;
} }
/** /**
@ -1886,7 +1889,8 @@ export class AddSubstituteAttr extends MoveEffectAttr {
return false; return false;
} }
user.damageAndUpdate(Math.floor(user.getMaxHp() * this.hpCost), HitResult.OTHER, false, true, true); const damageTaken = this.roundUp ? Math.ceil(user.getMaxHp() * this.hpCost) : Math.floor(user.getMaxHp() * this.hpCost);
user.damageAndUpdate(damageTaken, HitResult.OTHER, false, true, true);
user.addTag(BattlerTagType.SUBSTITUTE, 0, move.id, user.id); user.addTag(BattlerTagType.SUBSTITUTE, 0, move.id, user.id);
return true; return true;
} }
@ -1899,7 +1903,7 @@ export class AddSubstituteAttr extends MoveEffectAttr {
} }
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user, target, move) => !user.getTag(SubstituteTag) && user.hp > Math.floor(user.getMaxHp() * this.hpCost) && user.getMaxHp() > 1; return (user, target, move) => !user.getTag(SubstituteTag) && user.hp > (this.roundUp ? Math.ceil(user.getMaxHp() * this.hpCost) : Math.floor(user.getMaxHp() * this.hpCost)) && user.getMaxHp() > 1;
} }
/** /**
@ -9036,7 +9040,7 @@ export function initMoves() {
.attr(HighCritAttr) .attr(HighCritAttr)
.slicingMove(), .slicingMove(),
new SelfStatusMove(Moves.SUBSTITUTE, Type.NORMAL, -1, 10, -1, 0, 1) new SelfStatusMove(Moves.SUBSTITUTE, Type.NORMAL, -1, 10, -1, 0, 1)
.attr(AddSubstituteAttr), .attr(AddSubstituteAttr, 0.25, false),
new AttackMove(Moves.STRUGGLE, Type.NORMAL, MoveCategory.PHYSICAL, 50, -1, 1, -1, 0, 1) new AttackMove(Moves.STRUGGLE, Type.NORMAL, MoveCategory.PHYSICAL, 50, -1, 1, -1, 0, 1)
.attr(RecoilAttr, true, 0.25, true) .attr(RecoilAttr, true, 0.25, true)
.attr(TypelessAttr) .attr(TypelessAttr)
@ -11382,7 +11386,7 @@ export function initMoves() {
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 5461 / 4096 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 5461 / 4096 : 1)
.makesContact(), .makesContact(),
new SelfStatusMove(Moves.SHED_TAIL, Type.NORMAL, -1, 10, -1, 0, 9) new SelfStatusMove(Moves.SHED_TAIL, Type.NORMAL, -1, 10, -1, 0, 9)
.attr(AddSubstituteAttr, 0.5) .attr(AddSubstituteAttr, 0.5, true)
.attr(ForceSwitchOutAttr, true, SwitchType.SHED_TAIL) .attr(ForceSwitchOutAttr, true, SwitchType.SHED_TAIL)
.condition(failIfLastInPartyCondition), .condition(failIfLastInPartyCondition),
new SelfStatusMove(Moves.CHILLY_RECEPTION, Type.ICE, -1, 10, -1, 0, 9) new SelfStatusMove(Moves.CHILLY_RECEPTION, Type.ICE, -1, 10, -1, 0, 9)

View File

@ -1501,8 +1501,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* Suppresses an ability and calls its onlose attributes * Suppresses an ability and calls its onlose attributes
*/ */
public suppressAbility() { public suppressAbility() {
this.summonData.abilitySuppressed = true;
[ true, false ].forEach((passive) => applyOnLoseAbAttrs(this, passive)); [ true, false ].forEach((passive) => applyOnLoseAbAttrs(this, passive));
this.summonData.abilitySuppressed = true;
} }
/** /**
@ -1563,7 +1563,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return false; return false;
} }
const suppressAbilitiesTag = arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag; const suppressAbilitiesTag = arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag;
if (this.isOnField() && suppressAbilitiesTag) { if (this.isOnField() && suppressAbilitiesTag && !suppressAbilitiesTag.isBeingRemoved()) {
const thisAbilitySuppressing = ability.hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr); const thisAbilitySuppressing = ability.hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr);
const hasSuppressingAbility = this.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false); const hasSuppressingAbility = this.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false);
// Neutralizing gas is up - suppress abilities unless they are unsuppressable or this pokemon is responsible for the gas // Neutralizing gas is up - suppress abilities unless they are unsuppressable or this pokemon is responsible for the gas

View File

@ -629,6 +629,8 @@ export class DropDown extends Phaser.GameObjects.Container {
} }
} }
} }
this.onChange();
} }
} }

View File

@ -86,6 +86,15 @@ export class FilterBar extends Phaser.GameObjects.Container {
return this.dropDowns[this.columns.indexOf(col)]; return this.dropDowns[this.columns.indexOf(col)];
} }
/**
* Get the DropDownColumn associated to a given index
* @param index the index of the column to retrieve
* @returns the associated DropDownColumn if it exists, undefined otherwise
*/
public getColumn(index: number): DropDownColumn {
return this.columns[index];
}
/** /**
* Highlight the labels of the FilterBar if the filters are different from their default values * Highlight the labels of the FilterBar if the filters are different from their default values
*/ */
@ -185,6 +194,11 @@ export class FilterBar extends Phaser.GameObjects.Container {
return this.getFilter(col).getVals(); return this.getFilter(col).getVals();
} }
public resetSelection(col: DropDownColumn): void {
this.dropDowns[col].resetToDefault();
this.updateFilterLabels();
}
setValsToDefault(): void { setValsToDefault(): void {
for (const dropDown of this.dropDowns) { for (const dropDown of this.dropDowns) {
dropDown.resetToDefault(); dropDown.resetToDefault();

View File

@ -898,16 +898,11 @@ export default class PokedexUiHandler extends MessageUiHandler {
if (this.filterMode && this.filterBar.openDropDown) { if (this.filterMode && this.filterBar.openDropDown) {
// CANCEL with a filter menu open > close it // CANCEL with a filter menu open > close it
this.filterBar.toggleDropDown(this.filterBarCursor); this.filterBar.toggleDropDown(this.filterBarCursor);
// if there are possible pokemon go the first one of the list
if (numberOfStarters > 0) {
this.setFilterMode(false);
this.scrollCursor = 0;
this.updateScroll();
this.setCursor(0);
}
success = true; success = true;
} else if (this.filterMode && !this.filterBar.getFilter(this.filterBarCursor).hasDefaultValues()) {
this.filterBar.resetSelection(this.filterBarCursor);
this.updateStarters();
success = true;
} else if (this.filterTextMode && !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)) { } else if (this.filterTextMode && !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)) {
this.filterText.resetSelection(this.filterTextCursor); this.filterText.resetSelection(this.filterTextCursor);
success = true; success = true;

View File

@ -1080,10 +1080,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
/** /**
* Set the selections for all filters to their default starting value * Set the selections for all filters to their default starting value
*/ */
resetFilters() : void { public resetFilters(): void {
this.filterBar.setValsToDefault();
this.resetCaughtDropdown();
}
/**
* Set default value for the caught dropdown, which only shows caught mons
*/
public resetCaughtDropdown(): void {
const caughtDropDown: DropDown = this.filterBar.getFilter(DropDownColumn.CAUGHT); const caughtDropDown: DropDown = this.filterBar.getFilter(DropDownColumn.CAUGHT);
this.filterBar.setValsToDefault(); caughtDropDown.resetToDefault();
// initial setting, in caught filter, select the options excluding the uncaught option // initial setting, in caught filter, select the options excluding the uncaught option
for (let i = 0; i < caughtDropDown.options.length; i++) { for (let i = 0; i < caughtDropDown.options.length; i++) {
@ -1323,16 +1331,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
if (this.filterMode && this.filterBar.openDropDown) { if (this.filterMode && this.filterBar.openDropDown) {
// CANCEL with a filter menu open > close it // CANCEL with a filter menu open > close it
this.filterBar.toggleDropDown(this.filterBarCursor); this.filterBar.toggleDropDown(this.filterBarCursor);
// if there are possible starters go the first one of the list
if (numberOfStarters > 0) {
this.setFilterMode(false);
this.scrollCursor = 0;
this.updateScroll();
this.setCursor(0);
}
success = true; success = true;
} else if (this.filterMode && !this.filterBar.getFilter(this.filterBar.getColumn(this.filterBarCursor)).hasDefaultValues()) {
if (this.filterBar.getColumn(this.filterBarCursor) === DropDownColumn.CAUGHT) {
this.resetCaughtDropdown();
} else {
this.filterBar.resetSelection(this.filterBarCursor);
}
this.updateStarters();
success = true;
} else if (this.statsMode) { } else if (this.statsMode) {
this.toggleStatsMode(false); this.toggleStatsMode(false);
success = true; success = true;

View File

@ -46,11 +46,8 @@ describe("Moves - Shed Tail", () => {
expect(feebas).not.toBe(magikarp); expect(feebas).not.toBe(magikarp);
expect(feebas.hp).toBe(feebas.getMaxHp()); expect(feebas.hp).toBe(feebas.getMaxHp());
// Note: Shed Tail's HP cost is currently not accurate to mainline, as it // Note: Altered the test to be consistent with the correct HP cost :yipeevee_static:
// should cost ceil(maxHP / 2) instead of max(floor(maxHp / 2), 1). The current expect(magikarp.hp).toBe(Math.floor(magikarp.getMaxHp() / 2));
// implementation is consistent with Substitute's HP cost logic, but that's not
// the case in mainline for some reason :regiDespair:.
expect(magikarp.hp).toBe(Math.ceil(magikarp.getMaxHp() / 2));
expect(substituteTag).toBeDefined(); expect(substituteTag).toBeDefined();
expect(substituteTag?.hp).toBe(Math.floor(magikarp.getMaxHp() / 4)); expect(substituteTag?.hp).toBe(Math.floor(magikarp.getMaxHp() / 4));
}); });