Merge branch 'main' of github.com:Greenlamp2/pokerogue into test_instance

This commit is contained in:
Greenlamp 2024-05-06 00:43:00 +02:00
commit bea0e216ab
8 changed files with 111 additions and 40 deletions

View File

@ -2356,7 +2356,7 @@ export class IncreasePpAbAttr extends AbAttr { }
export class ForceSwitchOutImmunityAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` can't be switched out!`))
cancelled.value = true;
return true;
}
}

View File

@ -327,11 +327,13 @@ export default class Move implements Localizable {
}
getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
let failedText = null;
for (let attr of this.attrs)
failedText = attr.getFailedText(user, target, move, cancelled);
for (let attr of this.attrs) {
let failedText = attr.getFailedText(user, target, move, cancelled);
if (failedText !== null)
return failedText;
}
return null;
}
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
let score = 0;
@ -445,7 +447,9 @@ export abstract class MoveAttr {
export enum MoveEffectTrigger {
PRE_APPLY,
POST_APPLY,
HIT
HIT,
/** Triggers one time after all target effects have applied */
POST_TARGET,
}
export class MoveEffectAttr extends MoveAttr {
@ -738,19 +742,33 @@ export class SacrificialAttr extends MoveEffectAttr {
}
}
/**
* Attribute used for moves which cut the user's Max HP in half.
* Triggers using POST_TARGET.
*/
export class HalfSacrificialAttr extends MoveEffectAttr {
constructor() {
super(true, MoveEffectTrigger.PRE_APPLY);
super(true, MoveEffectTrigger.POST_TARGET);
}
/**
* Cut's the user's Max HP in half and displays the appropriate recoil message
* @param user Pokemon that used the move
* @param target N/A
* @param move Move with this attribute
* @param args N/A
* @returns true if the function succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args))
return false;
const cancelled = new Utils.BooleanHolder(false);
// Check to see if the Pokemon has an ability that blocks non-direct damage
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
if (!cancelled.value){
user.damageAndUpdate(Math.ceil(user.getMaxHp()/2), HitResult.OTHER, false, true, true);
user.scene.queueMessage(getPokemonMessage(user, ' cut its own HP to power up its move!')); // Queue recoil message
}
return true;
}
@ -2703,26 +2721,19 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
export class CurseAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean {
// Determine the correct target based on the user's type
if (!user.getTypes(true).includes(Type.GHOST)) {
// For non-Ghost types, target the user itself
target = user;
}
if (user.getTypes(true).includes(Type.GHOST)) {
if (target.getTag(BattlerTagType.CURSED)) {
user.scene.queueMessage('But it failed!');
return false;
}
let curseRecoilDamage = Math.floor(user.getMaxHp() / 2);
let curseRecoilDamage = Math.max(1, Math.floor(user.getMaxHp() / 2));
user.damageAndUpdate(curseRecoilDamage, HitResult.OTHER, false, true, true);
user.scene.queueMessage(getPokemonMessage(user, ` cut its own HP\nand laid a curse on the ${target.name}!`));
target.addTag(BattlerTagType.CURSED, 0, move.id, user.id);
return true;
} else {
target = user;
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), this.selfTarget, [BattleStat.ATK, BattleStat.DEF], 1));
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), this.selfTarget, [BattleStat.SPD], -1));
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF], 1));
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), true, [BattleStat.SPD], -1));
return true;
}
}
@ -2977,7 +2988,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
return new Promise(resolve => {
// Check if the move category is not STATUS or if the switch out condition is not met
if (!this.getCondition()(user, target, move)) {
if (!this.getSwitchOutCondition()(user, target, move)) {
//Apply effects before switch out i.e. poison point, flame body, etc
applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, new PokemonMove(move.id), null);
return resolve(false);
@ -3035,8 +3046,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
}
getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, cancelled);
return null;
const blockedByAbility = new Utils.BooleanHolder(false);
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility);
return blockedByAbility.value ? getPokemonMessage(target, ` can't be switched out!`) : null;
}
getSwitchOutCondition(): MoveConditionFunc {
@ -3809,6 +3821,9 @@ const failOnMaxCondition: MoveConditionFunc = (user, target, move) => !target.is
const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
const cancelled = new Utils.BooleanHolder(false);
user.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled));
// Queue a message if an ability prevented usage of the move
if (cancelled.value)
user.scene.queueMessage(getPokemonMessage(user, ` cannot use ${move.name}!`));
return !cancelled.value;
}
@ -4402,10 +4417,8 @@ export function initMoves() {
.condition((user, target, move) => user.status?.effect === StatusEffect.SLEEP)
.soundBased(),
new StatusMove(Moves.CURSE, Type.GHOST, -1, 10, -1, 0, 2)
.attr(StatChangeAttr, BattleStat.SPD, -1, true)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], 1, true)
.target(MoveTarget.USER)
.partial(),
.attr(CurseAttr)
.ignoresProtect(true),
new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2)
.attr(LowHpPowerAttr),
new StatusMove(Moves.CONVERSION_2, Type.NORMAL, -1, 30, -1, 0, 2)
@ -6249,8 +6262,7 @@ export function initMoves() {
.partial(),
new SelfStatusMove(Moves.TAKE_HEART, Type.PSYCHIC, -1, 10, -1, 0, 8)
.attr(StatChangeAttr, [ BattleStat.SPATK, BattleStat.SPDEF ], 1, true)
.attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN)
.condition((user, target, move) => user.status && (user.status.effect === StatusEffect.PARALYSIS || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.BURN)),
.attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN, StatusEffect.SLEEP),
/* Unused
new AttackMove(Moves.G_MAX_WILDFIRE, Type.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8)
.target(MoveTarget.ALL_NEAR_ENEMIES)

View File

@ -54,6 +54,10 @@ export class InputsController {
init(): void {
this.events = new Phaser.Events.EventEmitter();
// Handle the game losing focus
this.scene.game.events.on(Phaser.Core.Events.BLUR, () => {
this.loseFocus()
})
if (typeof this.scene.input.gamepad !== 'undefined') {
this.scene.input.gamepad.on('connected', function (thisGamepad) {
@ -78,10 +82,14 @@ export class InputsController {
this.setupKeyboardControls();
}
loseFocus(): void {
this.deactivatePressedKey();
}
update(): void {
for (const b of Utils.getEnumValues(Button)) {
if (!this.interactions.hasOwnProperty(b)) continue;
if (this.repeatInputDurationJustPassed(b)) {
if (this.repeatInputDurationJustPassed(b) && this.interactions[b].isPressed) {
this.events.emit('input_down', {
controller_type: 'repeated',
button: b,
@ -166,8 +174,8 @@ export class InputsController {
[Button.LEFT]: [keyCodes.LEFT, keyCodes.A],
[Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D],
[Button.SUBMIT]: [keyCodes.ENTER],
[Button.ACTION]: [keyCodes.SPACE, this.scene.abSwapped ? keyCodes.X : keyCodes.Z],
[Button.CANCEL]: [keyCodes.BACKSPACE, this.scene.abSwapped ? keyCodes.Z : keyCodes.X],
[Button.ACTION]: [keyCodes.SPACE, keyCodes.Z],
[Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X],
[Button.MENU]: [keyCodes.ESC, keyCodes.M],
[Button.STATS]: [keyCodes.SHIFT, keyCodes.C],
[Button.CYCLE_SHINY]: [keyCodes.R],
@ -248,11 +256,22 @@ export class InputsController {
if (!this.interactions.hasOwnProperty(button)) return;
this.buttonLock = button;
this.interactions[button].pressTime = this.time.now;
this.interactions[button].isPressed = true;
}
delLastProcessedMovementTime(button: Button): void {
if (!this.interactions.hasOwnProperty(button)) return;
this.buttonLock = null;
this.interactions[button].pressTime = null;
this.interactions[button].isPressed = false;
}
deactivatePressedKey(): void {
this.buttonLock = null;
for (const b of Utils.getEnumValues(Button)) {
if (!this.interactions.hasOwnProperty(b)) return;
this.interactions[b].pressTime = null;
this.interactions[b].isPressed = false;
}
}
}

View File

@ -2278,7 +2278,7 @@ export class MovePhase extends BattlePhase {
// Assume conditions affecting targets only apply to moves with a single target
let success = this.move.getMove().applyConditions(this.pokemon, targets[0], this.move.getMove());
let cancelled = new Utils.BooleanHolder(true);
let cancelled = new Utils.BooleanHolder(false);
let failedText = this.move.getMove().getFailedText(this.pokemon, targets[0], this.move.getMove(), cancelled);
if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove()))
success = false;
@ -2494,6 +2494,9 @@ export class MoveEffectPhase extends PokemonPhase {
});
}));
}
// Trigger effect which should only apply one time after all targeted effects have already applied
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_TARGET,
user, null, this.move.getMove())
Promise.allSettled(applyAttrs).then(() => this.end());
});
});

View File

@ -358,8 +358,11 @@ export class GameData {
this.starterData = systemData.starterData;
}
if (systemData.gameStats)
if (systemData.gameStats) {
if (systemData.gameStats.legendaryPokemonCaught !== undefined && systemData.gameStats.subLegendaryPokemonCaught === undefined)
this.fixLegendaryStats(systemData);
this.gameStats = systemData.gameStats;
}
if (systemData.unlocks) {
for (let key of Object.keys(systemData.unlocks)) {
@ -1005,7 +1008,9 @@ export class GameData {
if (incrementCount) {
dexEntry.seenCount++;
this.gameStats.pokemonSeen++;
if (!trainer && pokemon.species.subLegendary || pokemon.species.legendary)
if (!trainer && pokemon.species.subLegendary)
this.gameStats.subLegendaryPokemonSeen++;
else if (!trainer && pokemon.species.legendary)
this.gameStats.legendaryPokemonSeen++;
else if (!trainer && pokemon.species.mythical)
this.gameStats.mythicalPokemonSeen++;
@ -1043,7 +1048,9 @@ export class GameData {
if (!fromEgg) {
dexEntry.caughtCount++;
this.gameStats.pokemonCaught++;
if (pokemon.species.subLegendary || pokemon.species.legendary)
if (pokemon.species.subLegendary)
this.gameStats.subLegendaryPokemonCaught++;
else if (pokemon.species.legendary)
this.gameStats.legendaryPokemonCaught++;
else if (pokemon.species.mythical)
this.gameStats.mythicalPokemonCaught++;
@ -1052,7 +1059,9 @@ export class GameData {
} else {
dexEntry.hatchedCount++;
this.gameStats.pokemonHatched++;
if (pokemon.species.subLegendary || pokemon.species.legendary)
if (pokemon.species.subLegendary)
this.gameStats.subLegendaryPokemonHatched++;
else if (pokemon.species.legendary)
this.gameStats.legendaryPokemonHatched++;
else if (pokemon.species.mythical)
this.gameStats.mythicalPokemonHatched++;
@ -1311,4 +1320,22 @@ export class GameData {
for (let starterId of defaultStarterSpecies)
systemData.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
}
fixLegendaryStats(systemData: SystemSaveData): void {
systemData.gameStats.subLegendaryPokemonSeen = 0;
systemData.gameStats.subLegendaryPokemonCaught = 0;
systemData.gameStats.subLegendaryPokemonHatched = 0;
allSpecies.filter(s => s.subLegendary).forEach(s => {
const dexEntry = systemData.dexData[s.speciesId];
systemData.gameStats.subLegendaryPokemonSeen += dexEntry.seenCount;
systemData.gameStats.legendaryPokemonSeen = Math.max(systemData.gameStats.legendaryPokemonSeen - dexEntry.seenCount, 0);
systemData.gameStats.subLegendaryPokemonCaught += dexEntry.caughtCount;
systemData.gameStats.legendaryPokemonCaught = Math.max(systemData.gameStats.legendaryPokemonCaught - dexEntry.caughtCount, 0);
systemData.gameStats.subLegendaryPokemonHatched += dexEntry.hatchedCount;
systemData.gameStats.legendaryPokemonHatched = Math.max(systemData.gameStats.legendaryPokemonHatched - dexEntry.hatchedCount, 0);
});
systemData.gameStats.subLegendaryPokemonSeen = Math.max(systemData.gameStats.subLegendaryPokemonSeen, systemData.gameStats.subLegendaryPokemonCaught);
systemData.gameStats.legendaryPokemonSeen = Math.max(systemData.gameStats.legendaryPokemonSeen, systemData.gameStats.legendaryPokemonCaught);
systemData.gameStats.mythicalPokemonSeen = Math.max(systemData.gameStats.mythicalPokemonSeen, systemData.gameStats.mythicalPokemonCaught);
}
}

View File

@ -18,6 +18,9 @@ export class GameStats {
public pokemonDefeated: integer;
public pokemonCaught: integer;
public pokemonHatched: integer;
public subLegendaryPokemonSeen: integer;
public subLegendaryPokemonCaught: integer;
public subLegendaryPokemonHatched: integer;
public legendaryPokemonSeen: integer;
public legendaryPokemonCaught: integer;
public legendaryPokemonHatched: integer;
@ -52,6 +55,10 @@ export class GameStats {
this.pokemonDefeated = source?.pokemonDefeated || 0;
this.pokemonCaught = source?.pokemonCaught || 0;
this.pokemonHatched = source?.pokemonHatched || 0;
// Currently handled by migration
this.subLegendaryPokemonSeen = source?.subLegendaryPokemonSeen;
this.subLegendaryPokemonCaught = source?.subLegendaryPokemonCaught;
this.subLegendaryPokemonHatched = source?.subLegendaryPokemonHatched;
this.legendaryPokemonSeen = source?.legendaryPokemonSeen || 0;
this.legendaryPokemonCaught = source?.legendaryPokemonCaught || 0;
this.legendaryPokemonHatched = source?.legendaryPokemonHatched || 0;

View File

@ -64,13 +64,16 @@ const displayStats: DisplayStats = {
pokemonDefeated: 'Pokémon Defeated',
pokemonCaught: 'Pokémon Caught',
pokemonHatched: 'Eggs Hatched',
legendaryPokemonSeen: 'Legendary Encounters?',
legendaryPokemonCaught: 'Legendaries Caught?',
legendaryPokemonHatched: 'Legendaries Hatched?',
mythicalPokemonSeen: 'Mythical Encounters?',
subLegendaryPokemonSeen: 'Sub-Legends Seen?',
subLegendaryPokemonCaught: 'Sub-Legends Caught?',
subLegendaryPokemonHatched: 'Sub-Legends Hatched?',
legendaryPokemonSeen: 'Legends Seen?',
legendaryPokemonCaught: 'Legends Caught?',
legendaryPokemonHatched: 'Legends Hatched?',
mythicalPokemonSeen: 'Mythicals Seen?',
mythicalPokemonCaught: 'Mythicals Caught?',
mythicalPokemonHatched: 'Mythicals Hatched?',
shinyPokemonSeen: 'Shiny Encounters?',
shinyPokemonSeen: 'Shinies Seen?',
shinyPokemonCaught: 'Shinies Caught?',
shinyPokemonHatched: 'Shinies Hatched?',
pokemonFused: 'Pokémon Fused?',

View File

@ -226,7 +226,7 @@ export const apiUrl = isLocal ? serverUrl : 'api';
export function setCookie(cName: string, cValue: string): void {
const expiration = new Date();
expiration.setTime(new Date().getTime() + 3600000 * 24 * 7);
expiration.setTime(new Date().getTime() + 3600000 * 24 * 30 * 3/*7*/);
document.cookie = `${cName}=${cValue};SameSite=Strict;path=/;expires=${expiration.toUTCString()}`;
}