Compare commits

..

14 Commits

Author SHA1 Message Date
NightKev
50c8e1a38c
Merge branch 'beta' into summary-effect-ui-adjust 2025-08-09 08:09:32 -07:00
NightKev
79576ad117
[GitHub] Update .github/CODEOWNERS file (#6240)
* [GitHub] Update `.github/CODEOWNERS` file

`@pagefaultgames/senior-dev-team` added to
`package.json` and `pnpm-lock.yaml`

`@pagefaultgames/balance-team` added to `/src/data/trainers`

* Move senior dev team entries to the bottom of the file
2025-08-09 03:18:40 +00:00
Madmadness65
f0a56a3049
[Sprite] Add unique trainer sprite for Rocket Boss Giovanni (#6235) 2025-08-09 02:15:33 +00:00
Madmadness65
7316628448 [Misc] Fix comment formatting nitpick in ability-id
Adds a space between every `/**` and `{@link`.
2025-08-08 20:31:51 -05:00
Blitzy
8a66cfe40b
[Bug] Fix Poké Fan in Trainer Config (#6238)
Fix Poke Fan in Trainer Config
2025-08-08 15:28:33 -04:00
damocleas
2dfe7232aa
Add 1/16 chance for Trainers in Laboratory 2025-08-08 00:27:59 -04:00
Madmadness65
32f5421b32 [Misc] Replace Abilities.ABILITY_NAME with AbilityId.ABILITY_NAME in comments
This commit mirrors the comment changes made in 375587213e.
2025-08-07 23:11:03 -05:00
Desroi
7873181726
[Bug] Fix Recoil & Shell Bell not applying when hitting Substitute
https://github.com/pagefaultgames/pokerogue/pull/5712

* Created Double Edge testing for recoil damage on substitutes

* Changed the logic for substitute damage

* Made testing for the updated substitute logic

* Update test formatting

* Fixed error in damage logic

* Apply Biome

* Fix test file

* Apply suggestion and update test

* Remove obsolete workaround and console logs

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-07 22:46:19 -05:00
damocleas
a289206a96
[Balance] More Passive/Egg Move/Cost changes for 1.10 (#6234)
* Update passives.ts

* Update egg-moves.ts

* Update starters.ts
2025-08-07 23:33:16 -04:00
Blitzy
98d957de75
[Balance] Champion Team Adjustments (#6132)
* Update Morty

* Update trainer-config.ts

---------

Co-authored-by: damocleas <damocleas25@gmail.com>
2025-08-08 02:58:40 +00:00
Blitzy
ae9f03e060
[Balance] Evil Leader Adjustments (#6133)
* Update trainer-config.ts

* Update Starmobiles to use their mainline movesets

* Update Starmobile forms and Trainer Config

---------

Co-authored-by: damocleas <damocleas25@gmail.com>
2025-08-07 21:45:37 -05:00
Bertie690
4bb3e11c74
[Misc] Added chance for alt title logo (#6182)
* [Misc] Added 0.1% chance for fake login screen
* Actually added logo file
* Update title-ui-handler.ts

Co-authored-by: damocleas <damocleas25@gmail.com>
Co-authored-by: Wlowscha
<54003515+Wlowscha@users.noreply.github.com>
2025-08-07 21:24:33 -04:00
NightKev
89871b10c6 [i18n] Update locales submodule 2025-08-07 18:11:47 -07:00
Bertie690
8e261512fc
[Test] Improved tests for arena trap (#6209)
* Added arena trap tests and such

* hopefullt fixed tests

* Marked test as todo since me smol brain

---------

Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com>
2025-08-08 01:00:34 +00:00
22 changed files with 892 additions and 692 deletions

10
.github/CODEOWNERS vendored
View File

@ -3,9 +3,6 @@
# everything (whole code-base) - Junior Devs # everything (whole code-base) - Junior Devs
* @pagefaultgames/junior-dev-team * @pagefaultgames/junior-dev-team
# github actions/templates etc. - Dev Leads
/.github @pagefaultgames/senior-dev-team
# Art Team # Art Team
/public/**/*.png @pagefaultgames/art-team /public/**/*.png @pagefaultgames/art-team
/public/**/*.json @pagefaultgames/art-team /public/**/*.json @pagefaultgames/art-team
@ -20,3 +17,10 @@
# Balance Files; contain actual code logic and must also be owned by dev team # Balance Files; contain actual code logic and must also be owned by dev team
/src/data/balance @pagefaultgames/balance-team @pagefaultgames/junior-dev-team /src/data/balance @pagefaultgames/balance-team @pagefaultgames/junior-dev-team
/src/data/trainers @pagefaultgames/balance-team @pagefaultgames/junior-dev-team
# GitHub actions/templates etc. - Senior Devs
# Should be defined last in the file to make sure these always override all other definitions
/.github @pagefaultgames/senior-dev-team
package.json @pagefaultgames/senior-dev-team
pnpm-lock.yaml @pagefaultgames/senior-dev-team

BIN
public/images/logo_fake.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "rocket_boss_giovanni_1.png",
"format": "RGBA8888",
"size": {
"w": 79,
"h": 79
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 39,
"h": 79
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 39,
"h": 79
},
"frame": {
"x": 0,
"y": 0,
"w": 39,
"h": 79
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:d6c5e1804414106d43a7c46f83468d39:1f3f7898a58950988acac6ee7167e012:5f742cbdaafcd5ae864f18ec2af7512a$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

@ -1 +1 @@
Subproject commit fa35780fed762017c89d1e9ece8a2779dff56c4d Subproject commit ab2716d5440c25f73986664aa3f3131821c3c392

View File

@ -94,3 +94,10 @@ export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 12;
* So anti-variance adds -15/256 to the spawn weight check for ME spawn. * So anti-variance adds -15/256 to the spawn weight check for ME spawn.
*/ */
export const ANTI_VARIANCE_WEIGHT_MODIFIER = 15; export const ANTI_VARIANCE_WEIGHT_MODIFIER = 15;
/**
* The chance (out of 1) that a different title logo will show when the title screen is drawn.
* Inverted during April Fools (such that this becomes the chance for the _normal_ title logo is displayed).
* Default: `10000` (0.01%)
*/
export const FAKE_TITLE_LOGO_CHANCE = 10000;

View File

@ -5491,7 +5491,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
* Attribute used for abilities that damage opponents causing the user to faint * Attribute used for abilities that damage opponents causing the user to faint
* equal to the amount of damage the last attack inflicted. * equal to the amount of damage the last attack inflicted.
* *
* Used for {@linkcode Abilities.INNARDS_OUT}. * Used for {@linkcode AbilityId.INNARDS_OUT | Innards Out}.
* @sealed * @sealed
*/ */
export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { export class PostFaintHPDamageAbAttr extends PostFaintAbAttr {
@ -7307,7 +7307,7 @@ export function initAbilities() {
.attr(HealFromBerryUseAbAttr, 1 / 3), .attr(HealFromBerryUseAbAttr, 1 / 3),
new Ability(AbilityId.PROTEAN, 6) new Ability(AbilityId.PROTEAN, 6)
.attr(PokemonTypeChangeAbAttr) .attr(PokemonTypeChangeAbAttr)
// .condition((p) => !p.summonData.abilitiesApplied.includes(Abilities.PROTEAN)) //Gen 9 Implementation // .condition((p) => !p.summonData.abilitiesApplied.includes(AbilityId.PROTEAN)) //Gen 9 Implementation
// TODO: needs testing on interaction with weather blockage // TODO: needs testing on interaction with weather blockage
.edgeCase(), .edgeCase(),
new Ability(AbilityId.FUR_COAT, 6) new Ability(AbilityId.FUR_COAT, 6)
@ -7566,7 +7566,7 @@ export function initAbilities() {
.attr(PostSummonStatStageChangeAbAttr, [ Stat.DEF ], 1, true), .attr(PostSummonStatStageChangeAbAttr, [ Stat.DEF ], 1, true),
new Ability(AbilityId.LIBERO, 8) new Ability(AbilityId.LIBERO, 8)
.attr(PokemonTypeChangeAbAttr) .attr(PokemonTypeChangeAbAttr)
//.condition((p) => !p.summonData.abilitiesApplied.includes(Abilities.LIBERO)), //Gen 9 Implementation //.condition((p) => !p.summonData.abilitiesApplied.includes(AbilityId.LIBERO)), //Gen 9 Implementation
// TODO: needs testing on interaction with weather blockage // TODO: needs testing on interaction with weather blockage
.edgeCase(), .edgeCase(),
new Ability(AbilityId.BALL_FETCH, 8) new Ability(AbilityId.BALL_FETCH, 8)

View File

@ -15,7 +15,7 @@ export const speciesEggMoves = {
[SpeciesId.SPEAROW]: [ MoveId.FLOATY_FALL, MoveId.EXTREME_SPEED, MoveId.KNOCK_OFF, MoveId.TRIPLE_ARROWS ], [SpeciesId.SPEAROW]: [ MoveId.FLOATY_FALL, MoveId.EXTREME_SPEED, MoveId.KNOCK_OFF, MoveId.TRIPLE_ARROWS ],
[SpeciesId.EKANS]: [ MoveId.NOXIOUS_TORQUE, MoveId.DRAGON_DANCE, MoveId.SLACK_OFF, MoveId.SHED_TAIL ], [SpeciesId.EKANS]: [ MoveId.NOXIOUS_TORQUE, MoveId.DRAGON_DANCE, MoveId.SLACK_OFF, MoveId.SHED_TAIL ],
[SpeciesId.SANDSHREW]: [ MoveId.HIGH_HORSEPOWER, MoveId.DIRE_CLAW, MoveId.SHORE_UP, MoveId.MIGHTY_CLEAVE ], [SpeciesId.SANDSHREW]: [ MoveId.HIGH_HORSEPOWER, MoveId.DIRE_CLAW, MoveId.SHORE_UP, MoveId.MIGHTY_CLEAVE ],
[SpeciesId.NIDORAN_F]: [ MoveId.CALM_MIND, MoveId.MOONLIGHT, MoveId.MALIGNANT_CHAIN, MoveId.SANDSEAR_STORM ], [SpeciesId.NIDORAN_F]: [ MoveId.BANEFUL_BUNKER, MoveId.MOONLIGHT, MoveId.BARB_BARRAGE, MoveId.THOUSAND_WAVES ],
[SpeciesId.NIDORAN_M]: [ MoveId.DRAGON_DANCE, MoveId.MOUNTAIN_GALE, MoveId.NOXIOUS_TORQUE, MoveId.PRECIPICE_BLADES ], [SpeciesId.NIDORAN_M]: [ MoveId.DRAGON_DANCE, MoveId.MOUNTAIN_GALE, MoveId.NOXIOUS_TORQUE, MoveId.PRECIPICE_BLADES ],
[SpeciesId.VULPIX]: [ MoveId.MOONBLAST, MoveId.INFERNAL_PARADE, MoveId.MORNING_SUN, MoveId.TAIL_GLOW ], [SpeciesId.VULPIX]: [ MoveId.MOONBLAST, MoveId.INFERNAL_PARADE, MoveId.MORNING_SUN, MoveId.TAIL_GLOW ],
[SpeciesId.ZUBAT]: [ MoveId.FLOATY_FALL, MoveId.DIRE_CLAW, MoveId.SWORDS_DANCE, MoveId.COLLISION_COURSE ], [SpeciesId.ZUBAT]: [ MoveId.FLOATY_FALL, MoveId.DIRE_CLAW, MoveId.SWORDS_DANCE, MoveId.COLLISION_COURSE ],
@ -293,7 +293,7 @@ export const speciesEggMoves = {
[SpeciesId.ARCHEN]: [ MoveId.ROOST, MoveId.EARTHQUAKE, MoveId.FLOATY_FALL, MoveId.MIGHTY_CLEAVE ], [SpeciesId.ARCHEN]: [ MoveId.ROOST, MoveId.EARTHQUAKE, MoveId.FLOATY_FALL, MoveId.MIGHTY_CLEAVE ],
[SpeciesId.TRUBBISH]: [ MoveId.COIL, MoveId.RECOVER, MoveId.DIRE_CLAW, MoveId.GIGATON_HAMMER ], [SpeciesId.TRUBBISH]: [ MoveId.COIL, MoveId.RECOVER, MoveId.DIRE_CLAW, MoveId.GIGATON_HAMMER ],
[SpeciesId.ZORUA]: [ MoveId.MALIGNANT_CHAIN, MoveId.MOONBLAST, MoveId.SECRET_SWORD, MoveId.FIERY_WRATH ], [SpeciesId.ZORUA]: [ MoveId.MALIGNANT_CHAIN, MoveId.MOONBLAST, MoveId.SECRET_SWORD, MoveId.FIERY_WRATH ],
[SpeciesId.MINCCINO]: [ MoveId.ICICLE_SPEAR, MoveId.TIDY_UP, MoveId.KNOCK_OFF, MoveId.POPULATION_BOMB ], [SpeciesId.MINCCINO]: [ MoveId.ICICLE_SPEAR, MoveId.TIDY_UP, MoveId.LOW_KICK, MoveId.POPULATION_BOMB ],
[SpeciesId.GOTHITA]: [ MoveId.RECOVER, MoveId.MOONBLAST, MoveId.AURA_SPHERE, MoveId.LUMINA_CRASH ], [SpeciesId.GOTHITA]: [ MoveId.RECOVER, MoveId.MOONBLAST, MoveId.AURA_SPHERE, MoveId.LUMINA_CRASH ],
[SpeciesId.SOLOSIS]: [ MoveId.MIST_BALL, MoveId.SPEED_SWAP, MoveId.FLAMETHROWER, MoveId.LIGHT_OF_RUIN ], [SpeciesId.SOLOSIS]: [ MoveId.MIST_BALL, MoveId.SPEED_SWAP, MoveId.FLAMETHROWER, MoveId.LIGHT_OF_RUIN ],
[SpeciesId.DUCKLETT]: [ MoveId.SPLISHY_SPLASH, MoveId.SANDSEAR_STORM, MoveId.WILDBOLT_STORM, MoveId.QUIVER_DANCE ], [SpeciesId.DUCKLETT]: [ MoveId.SPLISHY_SPLASH, MoveId.SANDSEAR_STORM, MoveId.WILDBOLT_STORM, MoveId.QUIVER_DANCE ],
@ -310,7 +310,7 @@ export const speciesEggMoves = {
[SpeciesId.TYNAMO]: [ MoveId.SCALD, MoveId.STRENGTH_SAP, MoveId.FIRE_LASH, MoveId.AURA_WHEEL ], [SpeciesId.TYNAMO]: [ MoveId.SCALD, MoveId.STRENGTH_SAP, MoveId.FIRE_LASH, MoveId.AURA_WHEEL ],
[SpeciesId.ELGYEM]: [ MoveId.THUNDERCLAP, MoveId.BADDY_BAD, MoveId.AURA_SPHERE, MoveId.PHOTON_GEYSER ], [SpeciesId.ELGYEM]: [ MoveId.THUNDERCLAP, MoveId.BADDY_BAD, MoveId.AURA_SPHERE, MoveId.PHOTON_GEYSER ],
[SpeciesId.LITWICK]: [ MoveId.GIGA_DRAIN, MoveId.EARTH_POWER, MoveId.MOONBLAST, MoveId.TORCH_SONG ], [SpeciesId.LITWICK]: [ MoveId.GIGA_DRAIN, MoveId.EARTH_POWER, MoveId.MOONBLAST, MoveId.TORCH_SONG ],
[SpeciesId.AXEW]: [ MoveId.STONE_AXE, MoveId.DIRE_CLAW, MoveId.BITTER_BLADE, MoveId.GLAIVE_RUSH ], [SpeciesId.AXEW]: [ MoveId.STONE_AXE, MoveId.DIRE_CLAW, MoveId.RAGING_FURY, MoveId.BITTER_BLADE ],
[SpeciesId.CUBCHOO]: [ MoveId.MOUNTAIN_GALE, MoveId.AQUA_STEP, MoveId.ICE_SHARD, MoveId.COLLISION_COURSE ], [SpeciesId.CUBCHOO]: [ MoveId.MOUNTAIN_GALE, MoveId.AQUA_STEP, MoveId.ICE_SHARD, MoveId.COLLISION_COURSE ],
[SpeciesId.CRYOGONAL]: [ MoveId.FREEZING_GLARE, MoveId.AURORA_VEIL, MoveId.NASTY_PLOT, MoveId.ORIGIN_PULSE ], [SpeciesId.CRYOGONAL]: [ MoveId.FREEZING_GLARE, MoveId.AURORA_VEIL, MoveId.NASTY_PLOT, MoveId.ORIGIN_PULSE ],
[SpeciesId.SHELMET]: [ MoveId.POWER_GEM, MoveId.NASTY_PLOT, MoveId.EARTH_POWER, MoveId.STEAM_ERUPTION ], [SpeciesId.SHELMET]: [ MoveId.POWER_GEM, MoveId.NASTY_PLOT, MoveId.EARTH_POWER, MoveId.STEAM_ERUPTION ],
@ -448,7 +448,7 @@ export const speciesEggMoves = {
[SpeciesId.ROOKIDEE]: [ MoveId.ROOST, MoveId.BODY_PRESS, MoveId.KINGS_SHIELD, MoveId.BEHEMOTH_BASH ], [SpeciesId.ROOKIDEE]: [ MoveId.ROOST, MoveId.BODY_PRESS, MoveId.KINGS_SHIELD, MoveId.BEHEMOTH_BASH ],
[SpeciesId.BLIPBUG]: [ MoveId.HEAL_ORDER, MoveId.LUSTER_PURGE, MoveId.SLEEP_POWDER, MoveId.TAIL_GLOW ], [SpeciesId.BLIPBUG]: [ MoveId.HEAL_ORDER, MoveId.LUSTER_PURGE, MoveId.SLEEP_POWDER, MoveId.TAIL_GLOW ],
[SpeciesId.NICKIT]: [ MoveId.BADDY_BAD, MoveId.MYSTICAL_FIRE, MoveId.SPARKLY_SWIRL, MoveId.MAKE_IT_RAIN ], [SpeciesId.NICKIT]: [ MoveId.BADDY_BAD, MoveId.MYSTICAL_FIRE, MoveId.SPARKLY_SWIRL, MoveId.MAKE_IT_RAIN ],
[SpeciesId.GOSSIFLEUR]: [ MoveId.PARTING_SHOT, MoveId.STRENGTH_SAP, MoveId.SAPPY_SEED, MoveId.SEED_FLARE ], [SpeciesId.GOSSIFLEUR]: [ MoveId.BATON_PASS, MoveId.TAILWIND, MoveId.SAPPY_SEED, MoveId.SPORE ],
[SpeciesId.WOOLOO]: [ MoveId.NUZZLE, MoveId.MILK_DRINK, MoveId.BODY_PRESS, MoveId.MULTI_ATTACK ], [SpeciesId.WOOLOO]: [ MoveId.NUZZLE, MoveId.MILK_DRINK, MoveId.BODY_PRESS, MoveId.MULTI_ATTACK ],
[SpeciesId.CHEWTLE]: [ MoveId.ICE_FANG, MoveId.PSYCHIC_FANGS, MoveId.SHELL_SMASH, MoveId.MIGHTY_CLEAVE ], [SpeciesId.CHEWTLE]: [ MoveId.ICE_FANG, MoveId.PSYCHIC_FANGS, MoveId.SHELL_SMASH, MoveId.MIGHTY_CLEAVE ],
[SpeciesId.YAMPER]: [ MoveId.ICE_FANG, MoveId.SWORDS_DANCE, MoveId.THUNDER_FANG, MoveId.BOLT_STRIKE ], [SpeciesId.YAMPER]: [ MoveId.ICE_FANG, MoveId.SWORDS_DANCE, MoveId.THUNDER_FANG, MoveId.BOLT_STRIKE ],
@ -514,7 +514,7 @@ export const speciesEggMoves = {
[SpeciesId.TAROUNTULA]: [ MoveId.STONE_AXE, MoveId.LEECH_LIFE, MoveId.FAKE_OUT, MoveId.SPORE ], [SpeciesId.TAROUNTULA]: [ MoveId.STONE_AXE, MoveId.LEECH_LIFE, MoveId.FAKE_OUT, MoveId.SPORE ],
[SpeciesId.NYMBLE]: [ MoveId.KNOCK_OFF, MoveId.FELL_STINGER, MoveId.ATTACK_ORDER, MoveId.WICKED_BLOW ], [SpeciesId.NYMBLE]: [ MoveId.KNOCK_OFF, MoveId.FELL_STINGER, MoveId.ATTACK_ORDER, MoveId.WICKED_BLOW ],
[SpeciesId.PAWMI]: [ MoveId.DRAIN_PUNCH, MoveId.METEOR_MASH, MoveId.JET_PUNCH, MoveId.PLASMA_FISTS ], [SpeciesId.PAWMI]: [ MoveId.DRAIN_PUNCH, MoveId.METEOR_MASH, MoveId.JET_PUNCH, MoveId.PLASMA_FISTS ],
[SpeciesId.TANDEMAUS]: [ MoveId.BATON_PASS, MoveId.COVET, MoveId.SIZZLY_SLIDE, MoveId.REVIVAL_BLESSING ], [SpeciesId.TANDEMAUS]: [ MoveId.BATON_PASS, MoveId.FAKE_OUT, MoveId.POWER_UP_PUNCH, MoveId.REVIVAL_BLESSING ],
[SpeciesId.FIDOUGH]: [ MoveId.SOFT_BOILED, MoveId.HIGH_HORSEPOWER, MoveId.SIZZLY_SLIDE, MoveId.TIDY_UP ], [SpeciesId.FIDOUGH]: [ MoveId.SOFT_BOILED, MoveId.HIGH_HORSEPOWER, MoveId.SIZZLY_SLIDE, MoveId.TIDY_UP ],
[SpeciesId.SMOLIV]: [ MoveId.STRENGTH_SAP, MoveId.EARTH_POWER, MoveId.CALM_MIND, MoveId.BOOMBURST ], [SpeciesId.SMOLIV]: [ MoveId.STRENGTH_SAP, MoveId.EARTH_POWER, MoveId.CALM_MIND, MoveId.BOOMBURST ],
[SpeciesId.SQUAWKABILLY]: [ MoveId.PARTING_SHOT, MoveId.EARTHQUAKE, MoveId.FLARE_BLITZ, MoveId.EXTREME_SPEED ], [SpeciesId.SQUAWKABILLY]: [ MoveId.PARTING_SHOT, MoveId.EARTHQUAKE, MoveId.FLARE_BLITZ, MoveId.EXTREME_SPEED ],

View File

@ -36,9 +36,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.ARBOK]: { 0: AbilityId.REGENERATOR }, [SpeciesId.ARBOK]: { 0: AbilityId.REGENERATOR },
[SpeciesId.SANDSHREW]: { 0: AbilityId.TOUGH_CLAWS }, [SpeciesId.SANDSHREW]: { 0: AbilityId.TOUGH_CLAWS },
[SpeciesId.SANDSLASH]: { 0: AbilityId.TOUGH_CLAWS }, [SpeciesId.SANDSLASH]: { 0: AbilityId.TOUGH_CLAWS },
[SpeciesId.NIDORAN_F]: { 0: AbilityId.FLARE_BOOST }, [SpeciesId.NIDORAN_F]: { 0: AbilityId.TOXIC_DEBRIS },
[SpeciesId.NIDORINA]: { 0: AbilityId.FLARE_BOOST }, [SpeciesId.NIDORINA]: { 0: AbilityId.TOXIC_DEBRIS },
[SpeciesId.NIDOQUEEN]: { 0: AbilityId.FLARE_BOOST }, [SpeciesId.NIDOQUEEN]: { 0: AbilityId.TOXIC_DEBRIS },
[SpeciesId.NIDORAN_M]: { 0: AbilityId.GUTS }, [SpeciesId.NIDORAN_M]: { 0: AbilityId.GUTS },
[SpeciesId.NIDORINO]: { 0: AbilityId.GUTS }, [SpeciesId.NIDORINO]: { 0: AbilityId.GUTS },
[SpeciesId.NIDOKING]: { 0: AbilityId.GUTS }, [SpeciesId.NIDOKING]: { 0: AbilityId.GUTS },
@ -220,8 +220,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.YANMEGA]: { 0: AbilityId.SHEER_FORCE }, [SpeciesId.YANMEGA]: { 0: AbilityId.SHEER_FORCE },
[SpeciesId.WOOPER]: { 0: AbilityId.WATER_VEIL }, [SpeciesId.WOOPER]: { 0: AbilityId.WATER_VEIL },
[SpeciesId.QUAGSIRE]: { 0: AbilityId.COMATOSE }, [SpeciesId.QUAGSIRE]: { 0: AbilityId.COMATOSE },
[SpeciesId.MURKROW]: { 0: AbilityId.DARK_AURA }, [SpeciesId.MURKROW]: { 0: AbilityId.UNNERVE },
[SpeciesId.HONCHKROW]: { 0: AbilityId.DARK_AURA }, [SpeciesId.HONCHKROW]: { 0: AbilityId.INTIMIDATE },
[SpeciesId.MISDREAVUS]: { 0: AbilityId.BEADS_OF_RUIN }, [SpeciesId.MISDREAVUS]: { 0: AbilityId.BEADS_OF_RUIN },
[SpeciesId.MISMAGIUS]: { 0: AbilityId.BEADS_OF_RUIN }, [SpeciesId.MISMAGIUS]: { 0: AbilityId.BEADS_OF_RUIN },
[SpeciesId.UNOWN]: { 0: AbilityId.ADAPTABILITY, 1: AbilityId.BEAST_BOOST, 2: AbilityId.CONTRARY, 3: AbilityId.DAZZLING, 4: AbilityId.EMERGENCY_EXIT, 5: AbilityId.FRIEND_GUARD, 6: AbilityId.GOOD_AS_GOLD, 7: AbilityId.HONEY_GATHER, 8: AbilityId.IMPOSTER, 9: AbilityId.JUSTIFIED, 10: AbilityId.KLUTZ, 11: AbilityId.LIBERO, 12: AbilityId.MOODY, 13: AbilityId.NEUTRALIZING_GAS, 14: AbilityId.OPPORTUNIST, 15: AbilityId.PICKUP, 16: AbilityId.QUICK_DRAW, 17: AbilityId.RUN_AWAY, 18: AbilityId.SIMPLE, 19: AbilityId.TRACE, 20: AbilityId.UNNERVE, 21: AbilityId.VICTORY_STAR, 22: AbilityId.WANDERING_SPIRIT, 23: AbilityId.FAIRY_AURA, 24: AbilityId.DARK_AURA, 25: AbilityId.AURA_BREAK, 26: AbilityId.PURE_POWER, 27: AbilityId.UNAWARE }, [SpeciesId.UNOWN]: { 0: AbilityId.ADAPTABILITY, 1: AbilityId.BEAST_BOOST, 2: AbilityId.CONTRARY, 3: AbilityId.DAZZLING, 4: AbilityId.EMERGENCY_EXIT, 5: AbilityId.FRIEND_GUARD, 6: AbilityId.GOOD_AS_GOLD, 7: AbilityId.HONEY_GATHER, 8: AbilityId.IMPOSTER, 9: AbilityId.JUSTIFIED, 10: AbilityId.KLUTZ, 11: AbilityId.LIBERO, 12: AbilityId.MOODY, 13: AbilityId.NEUTRALIZING_GAS, 14: AbilityId.OPPORTUNIST, 15: AbilityId.PICKUP, 16: AbilityId.QUICK_DRAW, 17: AbilityId.RUN_AWAY, 18: AbilityId.SIMPLE, 19: AbilityId.TRACE, 20: AbilityId.UNNERVE, 21: AbilityId.VICTORY_STAR, 22: AbilityId.WANDERING_SPIRIT, 23: AbilityId.FAIRY_AURA, 24: AbilityId.DARK_AURA, 25: AbilityId.AURA_BREAK, 26: AbilityId.PURE_POWER, 27: AbilityId.UNAWARE },
@ -391,7 +391,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.BANETTE]: { 0: AbilityId.SHADOW_SHIELD, 1: AbilityId.SHADOW_SHIELD }, [SpeciesId.BANETTE]: { 0: AbilityId.SHADOW_SHIELD, 1: AbilityId.SHADOW_SHIELD },
[SpeciesId.DUSKULL]: { 0: AbilityId.UNNERVE }, [SpeciesId.DUSKULL]: { 0: AbilityId.UNNERVE },
[SpeciesId.DUSCLOPS]: { 0: AbilityId.UNNERVE }, [SpeciesId.DUSCLOPS]: { 0: AbilityId.UNNERVE },
[SpeciesId.DUSKNOIR]: { 0: AbilityId.UNNERVE }, [SpeciesId.DUSKNOIR]: { 0: AbilityId.LEVITATE },
[SpeciesId.TROPIUS]: { 0: AbilityId.RIPEN }, [SpeciesId.TROPIUS]: { 0: AbilityId.RIPEN },
[SpeciesId.ABSOL]: { 0: AbilityId.SHARPNESS, 1: AbilityId.SHARPNESS }, [SpeciesId.ABSOL]: { 0: AbilityId.SHARPNESS, 1: AbilityId.SHARPNESS },
[SpeciesId.WYNAUT]: { 0: AbilityId.STURDY }, [SpeciesId.WYNAUT]: { 0: AbilityId.STURDY },
@ -640,9 +640,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.LITWICK]: { 0: AbilityId.SHADOW_TAG }, [SpeciesId.LITWICK]: { 0: AbilityId.SHADOW_TAG },
[SpeciesId.LAMPENT]: { 0: AbilityId.SHADOW_TAG }, [SpeciesId.LAMPENT]: { 0: AbilityId.SHADOW_TAG },
[SpeciesId.CHANDELURE]: { 0: AbilityId.SHADOW_TAG }, [SpeciesId.CHANDELURE]: { 0: AbilityId.SHADOW_TAG },
[SpeciesId.AXEW]: { 0: AbilityId.DRAGONS_MAW }, [SpeciesId.AXEW]: { 0: AbilityId.OWN_TEMPO },
[SpeciesId.FRAXURE]: { 0: AbilityId.DRAGONS_MAW }, [SpeciesId.FRAXURE]: { 0: AbilityId.OWN_TEMPO },
[SpeciesId.HAXORUS]: { 0: AbilityId.DRAGONS_MAW }, [SpeciesId.HAXORUS]: { 0: AbilityId.OWN_TEMPO },
[SpeciesId.CUBCHOO]: { 0: AbilityId.FUR_COAT }, [SpeciesId.CUBCHOO]: { 0: AbilityId.FUR_COAT },
[SpeciesId.BEARTIC]: { 0: AbilityId.FUR_COAT }, [SpeciesId.BEARTIC]: { 0: AbilityId.FUR_COAT },
[SpeciesId.CRYOGONAL]: { 0: AbilityId.SNOW_WARNING }, [SpeciesId.CRYOGONAL]: { 0: AbilityId.SNOW_WARNING },
@ -827,7 +827,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.TAPU_LELE]: { 0: AbilityId.BERSERK }, [SpeciesId.TAPU_LELE]: { 0: AbilityId.BERSERK },
[SpeciesId.TAPU_BULU]: { 0: AbilityId.FLOWER_VEIL }, [SpeciesId.TAPU_BULU]: { 0: AbilityId.FLOWER_VEIL },
[SpeciesId.TAPU_FINI]: { 0: AbilityId.FAIRY_AURA }, [SpeciesId.TAPU_FINI]: { 0: AbilityId.FAIRY_AURA },
[SpeciesId.COSMOG]: { 0: AbilityId.PICKUP }, [SpeciesId.COSMOG]: { 0: AbilityId.POWER_SPOT },
[SpeciesId.COSMOEM]: { 0: AbilityId.POWER_SPOT }, [SpeciesId.COSMOEM]: { 0: AbilityId.POWER_SPOT },
[SpeciesId.SOLGALEO]: { 0: AbilityId.BEAST_BOOST }, [SpeciesId.SOLGALEO]: { 0: AbilityId.BEAST_BOOST },
[SpeciesId.LUNALA]: { 0: AbilityId.BEAST_BOOST }, [SpeciesId.LUNALA]: { 0: AbilityId.BEAST_BOOST },
@ -1044,7 +1044,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.FINIZEN]: { 0: AbilityId.FRIEND_GUARD }, [SpeciesId.FINIZEN]: { 0: AbilityId.FRIEND_GUARD },
[SpeciesId.PALAFIN]: { 0: AbilityId.EMERGENCY_EXIT, 1: AbilityId.IRON_FIST }, [SpeciesId.PALAFIN]: { 0: AbilityId.EMERGENCY_EXIT, 1: AbilityId.IRON_FIST },
[SpeciesId.VAROOM]: { 0: AbilityId.LEVITATE }, [SpeciesId.VAROOM]: { 0: AbilityId.LEVITATE },
[SpeciesId.REVAVROOM]: { 0: AbilityId.LEVITATE, 1: AbilityId.DARK_AURA, 2: AbilityId.FLASH_FIRE, 3: AbilityId.MERCILESS, 4: AbilityId.FILTER, 5: AbilityId.SCRAPPY }, [SpeciesId.REVAVROOM]: { 0: AbilityId.LEVITATE, 1: AbilityId.INTIMIDATE, 2: AbilityId.SPEED_BOOST, 3: AbilityId.TOXIC_DEBRIS, 4: AbilityId.MISTY_SURGE, 5: AbilityId.STAMINA },
[SpeciesId.CYCLIZAR]: { 0: AbilityId.PROTEAN }, [SpeciesId.CYCLIZAR]: { 0: AbilityId.PROTEAN },
[SpeciesId.ORTHWORM]: { 0: AbilityId.REGENERATOR }, [SpeciesId.ORTHWORM]: { 0: AbilityId.REGENERATOR },
[SpeciesId.GLIMMET]: { 0: AbilityId.STURDY }, [SpeciesId.GLIMMET]: { 0: AbilityId.STURDY },

View File

@ -1618,11 +1618,11 @@ export function initSpecies() {
new PokemonSpecies(SpeciesId.VAROOM, 9, false, false, false, "Single-Cyl Pokémon", PokemonType.STEEL, PokemonType.POISON, 1, 35, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.SLOW_START, 300, 45, 70, 63, 30, 45, 47, 190, 50, 60, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(SpeciesId.VAROOM, 9, false, false, false, "Single-Cyl Pokémon", PokemonType.STEEL, PokemonType.POISON, 1, 35, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.SLOW_START, 300, 45, 70, 63, 30, 45, 47, 190, 50, 60, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(SpeciesId.REVAVROOM, 9, false, false, false, "Multi-Cyl Pokémon", PokemonType.STEEL, PokemonType.POISON, 1.8, 120, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.FILTER, 500, 80, 119, 90, 54, 67, 90, 75, 50, 175, GrowthRate.MEDIUM_FAST, 50, false, false, new PokemonSpecies(SpeciesId.REVAVROOM, 9, false, false, false, "Multi-Cyl Pokémon", PokemonType.STEEL, PokemonType.POISON, 1.8, 120, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.FILTER, 500, 80, 119, 90, 54, 67, 90, 75, 50, 175, GrowthRate.MEDIUM_FAST, 50, false, false,
new PokemonForm("Normal", "", PokemonType.STEEL, PokemonType.POISON, 1.8, 120, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.FILTER, 500, 80, 119, 90, 54, 67, 90, 75, 50, 175, false, null, true), new PokemonForm("Normal", "", PokemonType.STEEL, PokemonType.POISON, 1.8, 120, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.FILTER, 500, 80, 119, 90, 54, 67, 90, 75, 50, 175, false, null, true),
new PokemonForm("Segin Starmobile", "segin-starmobile", PokemonType.STEEL, PokemonType.DARK, 1.8, 240, AbilityId.INTIMIDATE, AbilityId.NONE, AbilityId.INTIMIDATE, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true), new PokemonForm("Segin Starmobile", "segin-starmobile", PokemonType.STEEL, PokemonType.DARK, 1.8, 240, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true),
new PokemonForm("Schedar Starmobile", "schedar-starmobile", PokemonType.STEEL, PokemonType.FIRE, 1.8, 240, AbilityId.SPEED_BOOST, AbilityId.NONE, AbilityId.SPEED_BOOST, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true), new PokemonForm("Schedar Starmobile", "schedar-starmobile", PokemonType.STEEL, PokemonType.FIRE, 1.8, 240, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true),
new PokemonForm("Navi Starmobile", "navi-starmobile", PokemonType.STEEL, PokemonType.POISON, 1.8, 240, AbilityId.TOXIC_DEBRIS, AbilityId.NONE, AbilityId.TOXIC_DEBRIS, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true), new PokemonForm("Navi Starmobile", "navi-starmobile", PokemonType.STEEL, PokemonType.POISON, 1.8, 240, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true),
new PokemonForm("Ruchbah Starmobile", "ruchbah-starmobile", PokemonType.STEEL, PokemonType.FAIRY, 1.8, 240, AbilityId.MISTY_SURGE, AbilityId.NONE, AbilityId.MISTY_SURGE, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true), new PokemonForm("Ruchbah Starmobile", "ruchbah-starmobile", PokemonType.STEEL, PokemonType.FAIRY, 1.8, 240, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true),
new PokemonForm("Caph Starmobile", "caph-starmobile", PokemonType.STEEL, PokemonType.FIGHTING, 1.8, 240, AbilityId.STAMINA, AbilityId.NONE, AbilityId.STAMINA, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true) new PokemonForm("Caph Starmobile", "caph-starmobile", PokemonType.STEEL, PokemonType.FIGHTING, 1.8, 240, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 600, 110, 129, 100, 77, 79, 105, 75, 50, 175, false, null, false, true)
), ),
new PokemonSpecies(SpeciesId.CYCLIZAR, 9, false, false, false, "Mount Pokémon", PokemonType.DRAGON, PokemonType.NORMAL, 1.6, 63, AbilityId.SHED_SKIN, AbilityId.NONE, AbilityId.REGENERATOR, 501, 70, 95, 65, 85, 65, 121, 190, 50, 175, GrowthRate.MEDIUM_SLOW, 50, false), new PokemonSpecies(SpeciesId.CYCLIZAR, 9, false, false, false, "Mount Pokémon", PokemonType.DRAGON, PokemonType.NORMAL, 1.6, 63, AbilityId.SHED_SKIN, AbilityId.NONE, AbilityId.REGENERATOR, 501, 70, 95, 65, 85, 65, 121, 190, 50, 175, GrowthRate.MEDIUM_SLOW, 50, false),
new PokemonSpecies(SpeciesId.ORTHWORM, 9, false, false, false, "Earthworm Pokémon", PokemonType.STEEL, null, 2.5, 310, AbilityId.EARTH_EATER, AbilityId.NONE, AbilityId.SAND_VEIL, 480, 70, 85, 145, 60, 55, 65, 25, 50, 240, GrowthRate.SLOW, 50, false), new PokemonSpecies(SpeciesId.ORTHWORM, 9, false, false, false, "Earthworm Pokémon", PokemonType.STEEL, null, 2.5, 310, AbilityId.EARTH_EATER, AbilityId.NONE, AbilityId.SAND_VEIL, 480, 70, 85, 145, 60, 55, 65, 25, 50, 240, GrowthRate.SLOW, 50, false),

View File

@ -30,7 +30,7 @@ export const signatureSpecies: SignatureSpecies = new Proxy({
FALKNER: [SpeciesId.PIDGEY, SpeciesId.HOOTHOOT, SpeciesId.NATU, SpeciesId.MURKROW], FALKNER: [SpeciesId.PIDGEY, SpeciesId.HOOTHOOT, SpeciesId.NATU, SpeciesId.MURKROW],
BUGSY: [SpeciesId.SCYTHER, SpeciesId.SHUCKLE, SpeciesId.YANMA, [SpeciesId.PINSIR, SpeciesId.HERACROSS]], BUGSY: [SpeciesId.SCYTHER, SpeciesId.SHUCKLE, SpeciesId.YANMA, [SpeciesId.PINSIR, SpeciesId.HERACROSS]],
WHITNEY: [SpeciesId.MILTANK, SpeciesId.AIPOM, SpeciesId.IGGLYBUFF, [SpeciesId.GIRAFARIG, SpeciesId.STANTLER]], WHITNEY: [SpeciesId.MILTANK, SpeciesId.AIPOM, SpeciesId.IGGLYBUFF, [SpeciesId.GIRAFARIG, SpeciesId.STANTLER]],
MORTY: [SpeciesId.GASTLY, SpeciesId.MISDREAVUS, SpeciesId.DUSKULL, SpeciesId.SABLEYE], MORTY: [SpeciesId.GASTLY, SpeciesId.MISDREAVUS, SpeciesId.DUSKULL, SpeciesId.HISUI_TYPHLOSION],
CHUCK: [SpeciesId.POLIWRATH, SpeciesId.MANKEY, SpeciesId.TYROGUE, SpeciesId.MACHOP], CHUCK: [SpeciesId.POLIWRATH, SpeciesId.MANKEY, SpeciesId.TYROGUE, SpeciesId.MACHOP],
JASMINE: [SpeciesId.STEELIX, SpeciesId.MAGNEMITE, SpeciesId.PINECO, SpeciesId.SKARMORY], JASMINE: [SpeciesId.STEELIX, SpeciesId.MAGNEMITE, SpeciesId.PINECO, SpeciesId.SKARMORY],
PRYCE: [SpeciesId.SWINUB, SpeciesId.SEEL, SpeciesId.SHELLDER, SpeciesId.SNEASEL], PRYCE: [SpeciesId.SWINUB, SpeciesId.SEEL, SpeciesId.SHELLDER, SpeciesId.SNEASEL],

View File

@ -566,7 +566,7 @@ export const speciesStarterCosts = {
[SpeciesId.FLITTLE]: 3, [SpeciesId.FLITTLE]: 3,
[SpeciesId.TINKATINK]: 4, [SpeciesId.TINKATINK]: 4,
[SpeciesId.WIGLETT]: 2, [SpeciesId.WIGLETT]: 2,
[SpeciesId.BOMBIRDIER]: 3, [SpeciesId.BOMBIRDIER]: 4,
[SpeciesId.FINIZEN]: 3, [SpeciesId.FINIZEN]: 3,
[SpeciesId.VAROOM]: 4, [SpeciesId.VAROOM]: 4,
[SpeciesId.CYCLIZAR]: 4, [SpeciesId.CYCLIZAR]: 4,

File diff suppressed because it is too large Load Diff

View File

@ -536,6 +536,7 @@ export class Arena {
case BiomeId.ABYSS: case BiomeId.ABYSS:
case BiomeId.SPACE: case BiomeId.SPACE:
case BiomeId.TEMPLE: case BiomeId.TEMPLE:
case BiomeId.LABORATORY:
return 16; return 16;
default: default:
return 0; return 0;

View File

@ -29,6 +29,7 @@ export class LoadingScene extends SceneBase {
this.loadImage("loading_bg", "arenas"); this.loadImage("loading_bg", "arenas");
this.loadImage("logo", ""); this.loadImage("logo", "");
this.loadImage("logo_fake", "");
// Load menu images // Load menu images
this.loadAtlas("bg", "ui"); this.loadAtlas("bg", "ui");

View File

@ -829,6 +829,7 @@ export class MoveEffectPhase extends PokemonPhase {
const substitute = target.getTag(SubstituteTag); const substitute = target.getTag(SubstituteTag);
const isBlockedBySubstitute = substitute && this.move.hitsSubstitute(user, target); const isBlockedBySubstitute = substitute && this.move.hitsSubstitute(user, target);
if (isBlockedBySubstitute) { if (isBlockedBySubstitute) {
user.turnData.totalDamageDealt += Math.min(dmg, substitute.hp);
substitute.hp -= dmg; substitute.hp -= dmg;
} else if (!target.isPlayer() && dmg >= target.hp) { } else if (!target.isPlayer() && dmg >= target.hp) {
globalScene.applyModifiers(EnemyEndureChanceModifier, false, target); globalScene.applyModifiers(EnemyEndureChanceModifier, false, target);

View File

@ -397,6 +397,16 @@ export class TimedEventManager {
return timedEvents.some((te: TimedEvent) => this.isActive(te)); return timedEvents.some((te: TimedEvent) => this.isActive(te));
} }
/**
* Check whether the current event is active and for April Fools.
* @returns Whether the April Fools event is currently active.
*/
isAprilFoolsActive(): boolean {
return timedEvents.some(
te => this.isActive(te) && te.hasOwnProperty("bannerKey") && te.bannerKey!.startsWith("aprf"),
);
}
activeEventHasBanner(): boolean { activeEventHasBanner(): boolean {
const activeEvents = timedEvents.filter(te => this.isActive(te) && te.hasOwnProperty("bannerKey")); const activeEvents = timedEvents.filter(te => this.isActive(te) && te.hasOwnProperty("bannerKey"));
return activeEvents.length > 0; return activeEvents.length > 0;

View File

@ -1,4 +1,5 @@
import { pokerogueApi } from "#api/pokerogue-api"; import { pokerogueApi } from "#api/pokerogue-api";
import { FAKE_TITLE_LOGO_CHANCE } from "#app/constants";
import { timedEventManager } from "#app/global-event-manager"; import { timedEventManager } from "#app/global-event-manager";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { TimedEventDisplay } from "#app/timed-event-manager"; import { TimedEventDisplay } from "#app/timed-event-manager";
@ -41,7 +42,7 @@ export class TitleUiHandler extends OptionSelectUiHandler {
this.titleContainer.setAlpha(0); this.titleContainer.setAlpha(0);
ui.add(this.titleContainer); ui.add(this.titleContainer);
const logo = globalScene.add.image(globalScene.scaledCanvas.width / 2, 8, "logo"); const logo = globalScene.add.image(globalScene.scaledCanvas.width / 2, 8, this.getLogo());
logo.setOrigin(0.5, 0); logo.setOrigin(0.5, 0);
this.titleContainer.add(logo); this.titleContainer.add(logo);
@ -186,4 +187,14 @@ export class TitleUiHandler extends OptionSelectUiHandler {
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
}); });
} }
/**
* Get the logo file path to load, with a 0.1% chance to use the fake logo instead.
* @returns The path to the image.
*/
private getLogo(): string {
// Invert spawn chances on april fools
const aprilFools = timedEventManager.isAprilFoolsActive();
return aprilFools === !!randInt(FAKE_TITLE_LOGO_CHANCE) ? "logo_fake" : "logo";
}
} }

View File

@ -70,12 +70,16 @@ export function padInt(value: number, length: number, padWith?: string): string
} }
/** /**
* Returns a random integer between min and min + range * Returns a **completely unseeded** random integer between `min` and `min + range`.
* @param range The amount of possible numbers * @param range - The amount of possible numbers to pick
* @param min The starting number * @param min - The minimum number to pick; default `0`
* @returns A psuedo-random, unseeded integer within the interval [min, min+range].
* @remarks
* This should not be used for battles or other outwards-facing randomness;
* battles are intended to be seeded and deterministic.
*/ */
export function randInt(range: number, min = 0): number { export function randInt(range: number, min = 0): number {
if (range === 1) { if (range <= 1) {
return min; return min;
} }
return Math.floor(Math.random() * range) + min; return Math.floor(Math.random() * range) + min;

View File

@ -1,10 +1,15 @@
import { getPokemonNameWithAffix } from "#app/messages";
import { allAbilities } from "#data/data-lists"; import { allAbilities } from "#data/data-lists";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { Button } from "#enums/buttons";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import type { PartyUiHandler } from "#ui/party-ui-handler";
import i18next from "i18next";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Abilities - Arena Trap", () => { describe("Abilities - Arena Trap", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -23,68 +28,64 @@ describe("Abilities - Arena Trap", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override game.override
.moveset(MoveId.SPLASH)
.ability(AbilityId.ARENA_TRAP) .ability(AbilityId.ARENA_TRAP)
.enemyAbility(AbilityId.ARENA_TRAP)
.enemySpecies(SpeciesId.RALTS) .enemySpecies(SpeciesId.RALTS)
.enemyAbility(AbilityId.BALL_FETCH) .enemyMoveset(MoveId.SPLASH);
.enemyMoveset(MoveId.TELEPORT);
}); });
// TODO: Enable test when Issue #935 is addressed // NB: Since switching moves bypass trapping, the only way fleeing can occur in PKR is from the player
it.todo("should not allow grounded Pokémon to flee", async () => { // TODO: Implement once forced flee helper exists
it.todo("should interrupt player flee attempt and display message, unless user has Run Away");
// TODO: Figure out how to wrangle the UI into not timing out
it.todo("should interrupt player switch attempt and display message", async () => {
game.override.battleStyle("single"); game.override.battleStyle("single");
await game.classicMode.startBattle([SpeciesId.DUGTRIO, SpeciesId.GOTHITELLE]);
await game.classicMode.startBattle(); const enemy = game.field.getEnemyPokemon();
const enemy = game.scene.getEnemyPokemon(); game.doSwitchPokemon(1);
game.onNextPrompt("CommandPhase", UiMode.PARTY, () => {
// no switch out command should be queued due to arena trap
expect(game.scene.currentBattle.turnCommands[0]).toBeNull();
game.move.select(MoveId.SPLASH); // back out and end the phase to avoid timeout
console.log(game.scene.ui.getHandler().constructor.name);
(game.scene.ui.getHandler() as PartyUiHandler).processInput(Button.CANCEL);
});
await game.toNextTurn(); await game.phaseInterceptor.to("CommandPhase");
expect(enemy).toBe(game.scene.getEnemyPokemon()); expect(game.textInterceptor.logs).toContain(
i18next.t("abilityTriggers:arenaTrap", {
pokemonNameWithAffix: getPokemonNameWithAffix(enemy),
abilityName: allAbilities[AbilityId.ARENA_TRAP].name,
}),
);
}); });
it("should guarantee double battle with any one LURE", async () => { it("should guarantee double battle with any one LURE", async () => {
game.override.startingModifier([{ name: "LURE" }]).startingWave(2); game.override.startingModifier([{ name: "LURE" }]).startingWave(2);
await game.classicMode.startBattle([SpeciesId.DUGTRIO]);
await game.classicMode.startBattle(); expect(game.scene.getEnemyField()).toHaveLength(2);
expect(game.scene.getEnemyField().length).toBe(2);
}); });
/**
* This checks if the Player Pokemon is able to switch out/run away after the Enemy Pokemon with {@linkcode AbilityId.ARENA_TRAP}
* is forcefully moved out of the field from moves such as Roar {@linkcode MoveId.ROAR}
*
* Note: It should be able to switch out/run away
*/
it("should lift if pokemon with this ability leaves the field", async () => { it("should lift if pokemon with this ability leaves the field", async () => {
game.override game.override.battleStyle("single");
.battleStyle("double") await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
.enemyMoveset(MoveId.SPLASH)
.moveset([MoveId.ROAR, MoveId.SPLASH])
.ability(AbilityId.BALL_FETCH);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.SUDOWOODO, SpeciesId.LUNATONE]);
const [enemy1, enemy2] = game.scene.getEnemyField(); const player = game.field.getPlayerPokemon();
const [player1, player2] = game.scene.getPlayerField(); const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy1, "getAbility").mockReturnValue(allAbilities[AbilityId.ARENA_TRAP]); expect(player.isTrapped()).toBe(true);
expect(enemy.isOnField()).toBe(true);
game.move.select(MoveId.ROAR); game.move.use(MoveId.ROAR);
game.move.select(MoveId.SPLASH, 1); await game.toEndOfTurn();
// This runs the fist command phase where the moves are selected expect(player.isTrapped()).toBe(false);
await game.toNextTurn(); expect(enemy.isOnField()).toBe(false);
// During the next command phase the player pokemons should not be trapped anymore
game.move.select(MoveId.SPLASH);
game.move.select(MoveId.SPLASH, 1);
await game.toNextTurn();
expect(player1.isTrapped()).toBe(false);
expect(player2.isTrapped()).toBe(false);
expect(enemy1.isOnField()).toBe(false);
expect(enemy2.isOnField()).toBe(true);
}); });
}); });

View File

@ -0,0 +1,84 @@
import { AbilityId } from "#enums/ability-id";
import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Moves - Recoil Moves", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.battleStyle("single")
.enemySpecies(SpeciesId.PIDOVE)
.startingLevel(1)
.enemyLevel(100)
.enemyMoveset(MoveId.SUBSTITUTE)
.criticalHits(false)
.ability(AbilityId.NO_GUARD)
.enemyAbility(AbilityId.BALL_FETCH);
});
it.each([
{ moveName: "Double Edge", moveId: MoveId.DOUBLE_EDGE },
{ moveName: "Brave Bird", moveId: MoveId.BRAVE_BIRD },
{ moveName: "Flare Blitz", moveId: MoveId.FLARE_BLITZ },
{ moveName: "Head Charge", moveId: MoveId.HEAD_CHARGE },
{ moveName: "Head Smash", moveId: MoveId.HEAD_SMASH },
{ moveName: "Light of Ruin", moveId: MoveId.LIGHT_OF_RUIN },
{ moveName: "Struggle", moveId: MoveId.STRUGGLE },
{ moveName: "Submission", moveId: MoveId.SUBMISSION },
{ moveName: "Take Down", moveId: MoveId.TAKE_DOWN },
{ moveName: "Volt Tackle", moveId: MoveId.VOLT_TACKLE },
{ moveName: "Wave Crash", moveId: MoveId.WAVE_CRASH },
{ moveName: "Wild Charge", moveId: MoveId.WILD_CHARGE },
{ moveName: "Wood Hammer", moveId: MoveId.WOOD_HAMMER },
])("$moveName causes recoil damage when hitting a substitute", async ({ moveId }) => {
await game.classicMode.startBattle([SpeciesId.TOGEPI]);
game.move.use(moveId);
await game.phaseInterceptor.to("MoveEndPhase"); // Pidove substitute
const pidove = game.field.getEnemyPokemon();
const subTag = pidove.getTag(BattlerTagType.SUBSTITUTE)!;
expect(subTag).toBeDefined();
const subInitialHp = subTag.hp;
await game.phaseInterceptor.to("MoveEndPhase"); // player attack
expect(subTag.hp).toBeLessThan(subInitialHp);
const playerPokemon = game.field.getPlayerPokemon();
expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp());
});
it("causes recoil damage when hitting a substitute in a double battle", async () => {
game.override.battleStyle("double");
await game.classicMode.startBattle([SpeciesId.TOGEPI, SpeciesId.TOGEPI]);
const [playerPokemon1, playerPokemon2] = game.scene.getPlayerField();
game.move.use(MoveId.DOUBLE_EDGE, 0);
game.move.use(MoveId.DOUBLE_EDGE, 1);
await game.toNextTurn();
expect(playerPokemon1.hp).toBeLessThan(playerPokemon1.getMaxHp());
expect(playerPokemon2.hp).toBeLessThan(playerPokemon2.getMaxHp());
});
});