Merge branch 'beta' into summary-effect-ui-adjust

This commit is contained in:
NightKev 2025-08-09 08:09:32 -07:00 committed by GitHub
commit 50c8e1a38c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
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
* @pagefaultgames/junior-dev-team
# github actions/templates etc. - Dev Leads
/.github @pagefaultgames/senior-dev-team
# Art Team
/public/**/*.png @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
/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.
*/
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
* 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
*/
export class PostFaintHPDamageAbAttr extends PostFaintAbAttr {
@ -7307,7 +7307,7 @@ export function initAbilities() {
.attr(HealFromBerryUseAbAttr, 1 / 3),
new Ability(AbilityId.PROTEAN, 6)
.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
.edgeCase(),
new Ability(AbilityId.FUR_COAT, 6)
@ -7566,7 +7566,7 @@ export function initAbilities() {
.attr(PostSummonStatStageChangeAbAttr, [ Stat.DEF ], 1, true),
new Ability(AbilityId.LIBERO, 8)
.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
.edgeCase(),
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.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.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.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 ],
@ -293,7 +293,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -310,7 +310,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -448,7 +448,7 @@ export const speciesEggMoves = {
[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.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.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 ],
@ -514,7 +514,7 @@ export const speciesEggMoves = {
[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.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.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 ],

View File

@ -36,9 +36,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.ARBOK]: { 0: AbilityId.REGENERATOR },
[SpeciesId.SANDSHREW]: { 0: AbilityId.TOUGH_CLAWS },
[SpeciesId.SANDSLASH]: { 0: AbilityId.TOUGH_CLAWS },
[SpeciesId.NIDORAN_F]: { 0: AbilityId.FLARE_BOOST },
[SpeciesId.NIDORINA]: { 0: AbilityId.FLARE_BOOST },
[SpeciesId.NIDOQUEEN]: { 0: AbilityId.FLARE_BOOST },
[SpeciesId.NIDORAN_F]: { 0: AbilityId.TOXIC_DEBRIS },
[SpeciesId.NIDORINA]: { 0: AbilityId.TOXIC_DEBRIS },
[SpeciesId.NIDOQUEEN]: { 0: AbilityId.TOXIC_DEBRIS },
[SpeciesId.NIDORAN_M]: { 0: AbilityId.GUTS },
[SpeciesId.NIDORINO]: { 0: AbilityId.GUTS },
[SpeciesId.NIDOKING]: { 0: AbilityId.GUTS },
@ -220,8 +220,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.YANMEGA]: { 0: AbilityId.SHEER_FORCE },
[SpeciesId.WOOPER]: { 0: AbilityId.WATER_VEIL },
[SpeciesId.QUAGSIRE]: { 0: AbilityId.COMATOSE },
[SpeciesId.MURKROW]: { 0: AbilityId.DARK_AURA },
[SpeciesId.HONCHKROW]: { 0: AbilityId.DARK_AURA },
[SpeciesId.MURKROW]: { 0: AbilityId.UNNERVE },
[SpeciesId.HONCHKROW]: { 0: AbilityId.INTIMIDATE },
[SpeciesId.MISDREAVUS]: { 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 },
@ -391,7 +391,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.BANETTE]: { 0: AbilityId.SHADOW_SHIELD, 1: AbilityId.SHADOW_SHIELD },
[SpeciesId.DUSKULL]: { 0: AbilityId.UNNERVE },
[SpeciesId.DUSCLOPS]: { 0: AbilityId.UNNERVE },
[SpeciesId.DUSKNOIR]: { 0: AbilityId.UNNERVE },
[SpeciesId.DUSKNOIR]: { 0: AbilityId.LEVITATE },
[SpeciesId.TROPIUS]: { 0: AbilityId.RIPEN },
[SpeciesId.ABSOL]: { 0: AbilityId.SHARPNESS, 1: AbilityId.SHARPNESS },
[SpeciesId.WYNAUT]: { 0: AbilityId.STURDY },
@ -640,9 +640,9 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.LITWICK]: { 0: AbilityId.SHADOW_TAG },
[SpeciesId.LAMPENT]: { 0: AbilityId.SHADOW_TAG },
[SpeciesId.CHANDELURE]: { 0: AbilityId.SHADOW_TAG },
[SpeciesId.AXEW]: { 0: AbilityId.DRAGONS_MAW },
[SpeciesId.FRAXURE]: { 0: AbilityId.DRAGONS_MAW },
[SpeciesId.HAXORUS]: { 0: AbilityId.DRAGONS_MAW },
[SpeciesId.AXEW]: { 0: AbilityId.OWN_TEMPO },
[SpeciesId.FRAXURE]: { 0: AbilityId.OWN_TEMPO },
[SpeciesId.HAXORUS]: { 0: AbilityId.OWN_TEMPO },
[SpeciesId.CUBCHOO]: { 0: AbilityId.FUR_COAT },
[SpeciesId.BEARTIC]: { 0: AbilityId.FUR_COAT },
[SpeciesId.CRYOGONAL]: { 0: AbilityId.SNOW_WARNING },
@ -827,7 +827,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[SpeciesId.TAPU_LELE]: { 0: AbilityId.BERSERK },
[SpeciesId.TAPU_BULU]: { 0: AbilityId.FLOWER_VEIL },
[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.SOLGALEO]: { 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.PALAFIN]: { 0: AbilityId.EMERGENCY_EXIT, 1: AbilityId.IRON_FIST },
[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.ORTHWORM]: { 0: AbilityId.REGENERATOR },
[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.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("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("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("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("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("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("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.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.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.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.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.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],
BUGSY: [SpeciesId.SCYTHER, SpeciesId.SHUCKLE, SpeciesId.YANMA, [SpeciesId.PINSIR, SpeciesId.HERACROSS]],
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],
JASMINE: [SpeciesId.STEELIX, SpeciesId.MAGNEMITE, SpeciesId.PINECO, SpeciesId.SKARMORY],
PRYCE: [SpeciesId.SWINUB, SpeciesId.SEEL, SpeciesId.SHELLDER, SpeciesId.SNEASEL],

View File

@ -566,7 +566,7 @@ export const speciesStarterCosts = {
[SpeciesId.FLITTLE]: 3,
[SpeciesId.TINKATINK]: 4,
[SpeciesId.WIGLETT]: 2,
[SpeciesId.BOMBIRDIER]: 3,
[SpeciesId.BOMBIRDIER]: 4,
[SpeciesId.FINIZEN]: 3,
[SpeciesId.VAROOM]: 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.SPACE:
case BiomeId.TEMPLE:
case BiomeId.LABORATORY:
return 16;
default:
return 0;

View File

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

View File

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

View File

@ -397,6 +397,16 @@ export class TimedEventManager {
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 {
const activeEvents = timedEvents.filter(te => this.isActive(te) && te.hasOwnProperty("bannerKey"));
return activeEvents.length > 0;

View File

@ -1,4 +1,5 @@
import { pokerogueApi } from "#api/pokerogue-api";
import { FAKE_TITLE_LOGO_CHANCE } from "#app/constants";
import { timedEventManager } from "#app/global-event-manager";
import { globalScene } from "#app/global-scene";
import { TimedEventDisplay } from "#app/timed-event-manager";
@ -41,7 +42,7 @@ export class TitleUiHandler extends OptionSelectUiHandler {
this.titleContainer.setAlpha(0);
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);
this.titleContainer.add(logo);
@ -186,4 +187,14 @@ export class TitleUiHandler extends OptionSelectUiHandler {
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
* @param range The amount of possible numbers
* @param min The starting number
* Returns a **completely unseeded** random integer between `min` and `min + range`.
* @param range - The amount of possible numbers to pick
* @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 {
if (range === 1) {
if (range <= 1) {
return 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 { AbilityId } from "#enums/ability-id";
import { Button } from "#enums/buttons";
import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode";
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 { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Abilities - Arena Trap", () => {
let phaserGame: Phaser.Game;
@ -23,68 +28,64 @@ describe("Abilities - Arena Trap", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset(MoveId.SPLASH)
.ability(AbilityId.ARENA_TRAP)
.enemyAbility(AbilityId.ARENA_TRAP)
.enemySpecies(SpeciesId.RALTS)
.enemyAbility(AbilityId.BALL_FETCH)
.enemyMoveset(MoveId.TELEPORT);
.enemyMoveset(MoveId.SPLASH);
});
// TODO: Enable test when Issue #935 is addressed
it.todo("should not allow grounded Pokémon to flee", async () => {
// NB: Since switching moves bypass trapping, the only way fleeing can occur in PKR is from the player
// 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");
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 () => {
game.override.startingModifier([{ name: "LURE" }]).startingWave(2);
await game.classicMode.startBattle([SpeciesId.DUGTRIO]);
await game.classicMode.startBattle();
expect(game.scene.getEnemyField().length).toBe(2);
expect(game.scene.getEnemyField()).toHaveLength(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 () => {
game.override
.battleStyle("double")
.enemyMoveset(MoveId.SPLASH)
.moveset([MoveId.ROAR, MoveId.SPLASH])
.ability(AbilityId.BALL_FETCH);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.SUDOWOODO, SpeciesId.LUNATONE]);
game.override.battleStyle("single");
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const [enemy1, enemy2] = game.scene.getEnemyField();
const [player1, player2] = game.scene.getPlayerField();
const player = game.field.getPlayerPokemon();
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.select(MoveId.SPLASH, 1);
game.move.use(MoveId.ROAR);
await game.toEndOfTurn();
// This runs the fist command phase where the moves are selected
await game.toNextTurn();
// 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);
expect(player.isTrapped()).toBe(false);
expect(enemy.isOnField()).toBe(false);
});
});

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());
});
});