Merge branch 'beta' into evil-team-monogen

This commit is contained in:
Madmadness65 2024-10-28 20:59:49 -05:00 committed by GitHub
commit 12ffa17ea2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 454 additions and 462 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "pokemon-rogue-battle",
"version": "1.1.0",
"version": "1.1.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pokemon-rogue-battle",
"version": "1.1.0",
"version": "1.1.6",
"hasInstallScript": true,
"dependencies": {
"@material/material-color-utilities": "^0.2.7",

View File

@ -1,7 +1,7 @@
{
"name": "pokemon-rogue-battle",
"private": true,
"version": "1.1.0",
"version": "1.1.6",
"type": "module",
"scripts": {
"start": "vite",

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "658-ash.png",
"format": "RGBA8888",
"size": {
"w": 79,
"h": 79
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 79,
"h": 74
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 79,
"h": 74
},
"frame": {
"x": 0,
"y": 0,
"w": 79,
"h": 74
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:936f62fa49ba4d6e402bb2e2eaf2afd0:ed00ba047a44b4bf1309bc147dd000e3:bfbf521a5c7bd80bcd95a96d9789c0dd$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 79, "h": 74 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 79, "h": 74 },
"sourceSize": { "w": 79, "h": 74 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"format": "I8",
"size": { "w": 79, "h": 74 },
"scale": "1"
}
}

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "658.png",
"format": "RGBA8888",
"size": {
"w": 75,
"h": 75
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 75,
"h": 65
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 75,
"h": 65
},
"frame": {
"x": 0,
"y": 0,
"w": 75,
"h": 65
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:e0b10df331bd4ce6760edab61dee144b:061561c45beff89a92bf0158d065204f:5affcab976148657d36bf4ff3410f92d$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 85, "h": 67 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 85, "h": 67 },
"sourceSize": { "w": 85, "h": 67 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"format": "I8",
"size": { "w": 85, "h": 67 },
"scale": "1"
}
}

View File

@ -5,8 +5,7 @@
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 63 },
"sourceSize": { "w": 64, "h": 63 },
"duration": 100
"sourceSize": { "w": 64, "h": 63 }
}
],
"meta": {

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "658-ash.png",
"format": "RGBA8888",
"size": {
"w": 73,
"h": 73
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 73,
"h": 69
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 73,
"h": 69
},
"frame": {
"x": 0,
"y": 0,
"w": 73,
"h": 69
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:4f38801bb3afeda5faff04bdcf6a666f:0c78ce2715e7510bf55da0a92b42661c:bfbf521a5c7bd80bcd95a96d9789c0dd$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 73, "h": 73 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 73, "h": 73 },
"sourceSize": { "w": 73, "h": 73 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"format": "I8",
"size": { "w": 73, "h": 73 },
"scale": "1"
}
}

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "658.png",
"format": "RGBA8888",
"size": {
"w": 77,
"h": 77
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 77,
"h": 65
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 77,
"h": 65
},
"frame": {
"x": 0,
"y": 0,
"w": 77,
"h": 65
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:acdb9925f3f23b947504eec7cc28c92d:1a13d9d418f6c107bb9e5d621d9154bb:5affcab976148657d36bf4ff3410f92d$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 77, "h": 77 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 77, "h": 77 },
"sourceSize": { "w": 77, "h": 77 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"format": "I8",
"size": { "w": 77, "h": 77 },
"scale": "1"
}
}

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "688.png",
"format": "RGBA8888",
"size": {
"w": 52,
"h": 52
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 41,
"h": 52
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 41,
"h": 52
},
"frame": {
"x": 0,
"y": 0,
"w": 41,
"h": 52
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:ea462f2b1b46327e3b8fcb7ec5e44f08:2d2598cc03dec73182dbea237ad83b34:176060351d0044923af938ba7932a6ef$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 51, "h": 65 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 51, "h": 65 },
"sourceSize": { "w": 51, "h": 65 }
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-dev",
"image": "688.png",
"format": "I8",
"size": { "w": 51, "h": 65 },
"scale": "1"
}
}

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "658-ash.png",
"format": "RGBA8888",
"size": {
"w": 73,
"h": 73
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 73,
"h": 69
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 73,
"h": 69
},
"frame": {
"x": 0,
"y": 0,
"w": 73,
"h": 69
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:d474b821316a87dfe09b397bdc2db5ef:497de0c2ec59ceba163e870b3226c76c:bfbf521a5c7bd80bcd95a96d9789c0dd$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 73, "h": 73 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 73, "h": 73 },
"sourceSize": { "w": 73, "h": 73 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"format": "I8",
"size": { "w": 73, "h": 73 },
"scale": "1"
}
}

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "658.png",
"format": "RGBA8888",
"size": {
"w": 77,
"h": 77
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 77,
"h": 65
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 77,
"h": 65
},
"frame": {
"x": 0,
"y": 0,
"w": 77,
"h": 65
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:5891f87a78022cde3402e7d9714cc7bf:756360084290e39c139e3fef91c81759:5affcab976148657d36bf4ff3410f92d$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 77, "h": 77 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 77, "h": 77 },
"sourceSize": { "w": 77, "h": 77 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"format": "I8",
"size": { "w": 77, "h": 77 },
"scale": "1"
}
}

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "688.png",
"format": "RGBA8888",
"size": {
"w": 52,
"h": 52
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 41,
"h": 52
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 41,
"h": 52
},
"frame": {
"x": 0,
"y": 0,
"w": 41,
"h": 52
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:0261b6c9242bba728fcfbfc515875b27:de0d9ddceed9311b33ae50ba86e969d1:176060351d0044923af938ba7932a6ef$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 51, "h": 65 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 51, "h": 65 },
"sourceSize": { "w": 51, "h": 65 }
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-dev",
"image": "688.png",
"format": "I8",
"size": { "w": 51, "h": 65 },
"scale": "1"
}
}

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "658-ash.png",
"format": "RGBA8888",
"size": {
"w": 79,
"h": 79
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 79,
"h": 74
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 79,
"h": 74
},
"frame": {
"x": 0,
"y": 0,
"w": 79,
"h": 74
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:3dd081ba5490f090a73de8423aac2f6b:f088fafaea755476f2abf488e7340cab:bfbf521a5c7bd80bcd95a96d9789c0dd$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 79, "h": 74 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 79, "h": 74 },
"sourceSize": { "w": 79, "h": 74 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"format": "I8",
"size": { "w": 79, "h": 74 },
"scale": "1"
}
}

View File

@ -1,41 +1,19 @@
{
"textures": [
{
"image": "658.png",
"format": "RGBA8888",
"size": {
"w": 75,
"h": 75
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 75,
"h": 65
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 75,
"h": 65
},
"frame": {
"x": 0,
"y": 0,
"w": 75,
"h": 65
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:be07c062265a19e890f1e2d2d1b5527d:ad4583a5a0498c496e9a93574c55ee03:5affcab976148657d36bf4ff3410f92d$"
}
{ "frames": [
{
"filename": "0001.png",
"frame": { "x": 0, "y": 0, "w": 85, "h": 67 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 85, "h": 67 },
"sourceSize": { "w": 85, "h": 67 },
"duration": 100
}
],
"meta": {
"app": "https://www.aseprite.org/",
"version": "1.3.7-x64",
"format": "I8",
"size": { "w": 85, "h": 67 },
"scale": "1"
}
}

View File

@ -5,8 +5,7 @@
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 63 },
"sourceSize": { "w": 64, "h": 63 },
"duration": 100
"sourceSize": { "w": 64, "h": 63 }
}
],
"meta": {

View File

@ -330,6 +330,30 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr {
}
}
/**
* Reduces the damage dealt to an allied Pokemon. Used by Friend Guard.
* @see {@linkcode applyPreDefend}
*/
export class AlliedFieldDamageReductionAbAttr extends PreDefendAbAttr {
private damageMultiplier: number;
constructor(damageMultiplier: number) {
super();
this.damageMultiplier = damageMultiplier;
}
/**
* Handles the damage reduction
* @param args
* - `[0]` {@linkcode Utils.NumberHolder} - The damage being dealt
*/
override applyPreDefend(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _attacker: Pokemon, _move: Move, _cancelled: Utils.BooleanHolder, args: any[]): boolean {
const damage = args[0] as Utils.NumberHolder;
damage.value = Utils.toDmgValue(damage.value * this.damageMultiplier);
return true;
}
}
export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
constructor(moveType: Type, damageMultiplier: number) {
super((target, user, move) => user.getMoveType(move) === moveType, damageMultiplier);
@ -5310,8 +5334,8 @@ export function initAbilities() {
new Ability(Abilities.HEALER, 5)
.conditionalAttr(pokemon => pokemon.getAlly() && Utils.randSeedInt(10) < 3, PostTurnResetStatusAbAttr, true),
new Ability(Abilities.FRIEND_GUARD, 5)
.ignorable()
.unimplemented(),
.attr(AlliedFieldDamageReductionAbAttr, 0.75)
.ignorable(),
new Ability(Abilities.WEAK_ARMOR, 5)
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, Stat.DEF, -1)
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, Stat.SPD, 2),

View File

@ -126,6 +126,7 @@ export class MistTag extends ArenaTag {
* Cancels the lowering of stats
* @param arena the {@linkcode Arena} containing this effect
* @param simulated `true` if the effect should be applied quietly
* @param attacker the {@linkcode Pokemon} using a move into this effect.
* @param cancelled a {@linkcode BooleanHolder} whose value is set to `true`
* to flag the stat reduction as cancelled
* @returns `true` if a stat reduction was cancelled; `false` otherwise

View File

@ -497,7 +497,7 @@ export const speciesEggTiers = {
[Species.DREEPY]: EggTier.RARE,
[Species.ZACIAN]: EggTier.LEGENDARY,
[Species.ZAMAZENTA]: EggTier.LEGENDARY,
[Species.ETERNATUS]: EggTier.COMMON,
[Species.ETERNATUS]: EggTier.LEGENDARY,
[Species.KUBFU]: EggTier.EPIC,
[Species.ZARUDE]: EggTier.EPIC,
[Species.REGIELEKI]: EggTier.EPIC,

View File

@ -544,11 +544,15 @@ export class Egg {
////
}
export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species {
const legendarySpecies = Object.entries(speciesEggTiers)
export function getValidLegendaryGachaSpecies() : Species[] {
return Object.entries(speciesEggTiers)
.filter(s => s[1] === EggTier.LEGENDARY)
.map(s => parseInt(s[0]))
.filter(s => getPokemonSpecies(s).isObtainable());
.filter(s => getPokemonSpecies(s).isObtainable() && s !== Species.ETERNATUS);
}
export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species {
const legendarySpecies = getValidLegendaryGachaSpecies();
let ret: Species;

View File

@ -22,7 +22,7 @@ import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag, PowerTrickTag } from "../data/battler-tags";
import { WeatherType } from "#app/data/weather";
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr } from "#app/data/ability";
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr } from "#app/data/ability";
import PokemonData from "#app/system/pokemon-data";
import { BattlerIndex } from "#app/battle";
import { Mode } from "#app/ui/ui";
@ -2667,9 +2667,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
}
/** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */
if (!ignoreAbility) {
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, simulated, damage);
/** Additionally apply friend guard damage reduction if ally has it. */
if (this.scene.currentBattle.double && this.getAlly()?.isActive(true)) {
applyPreDefendAbAttrs(AlliedFieldDamageReductionAbAttr, this.getAlly(), source, move, cancelled, simulated, damage);
}
}
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.

View File

@ -44,7 +44,7 @@ document.fonts.load("16px emerald").then(() => document.fonts.load("10px pkmnems
let game;
const startGame = async () => {
const startGame = async (manifest?: any) => {
await initI18n();
const LoadingScene = (await import("./loading-scene")).LoadingScene;
const BattleScene = (await import("./battle-scene")).default;
@ -94,13 +94,15 @@ const startGame = async () => {
version: version
});
game.sound.pauseOnBlur = false;
if (manifest) {
game["manifest"] = manifest;
}
};
fetch("/manifest.json")
.then(res => res.json())
.then(jsonResponse => {
startGame();
game["manifest"] = jsonResponse.manifest;
startGame(jsonResponse.manifest);
}).catch(() => {
// Manifest not found (likely local build)
startGame();

View File

@ -502,45 +502,25 @@ export class BerryModifierType extends PokemonHeldItemModifierType implements Ge
}
}
function getAttackTypeBoosterItemName(type: Type) {
switch (type) {
case Type.NORMAL:
return "Silk Scarf";
case Type.FIGHTING:
return "Black Belt";
case Type.FLYING:
return "Sharp Beak";
case Type.POISON:
return "Poison Barb";
case Type.GROUND:
return "Soft Sand";
case Type.ROCK:
return "Hard Stone";
case Type.BUG:
return "Silver Powder";
case Type.GHOST:
return "Spell Tag";
case Type.STEEL:
return "Metal Coat";
case Type.FIRE:
return "Charcoal";
case Type.WATER:
return "Mystic Water";
case Type.GRASS:
return "Miracle Seed";
case Type.ELECTRIC:
return "Magnet";
case Type.PSYCHIC:
return "Twisted Spoon";
case Type.ICE:
return "Never-Melt Ice";
case Type.DRAGON:
return "Dragon Fang";
case Type.DARK:
return "Black Glasses";
case Type.FAIRY:
return "Fairy Feather";
}
enum AttackTypeBoosterItem {
SILK_SCARF,
BLACK_BELT,
SHARP_BEAK,
POISON_BARB,
SOFT_SAND,
HARD_STONE,
SILVER_POWDER,
SPELL_TAG,
METAL_COAT,
CHARCOAL,
MYSTIC_WATER,
MIRACLE_SEED,
MAGNET,
TWISTED_SPOON,
NEVER_MELT_ICE,
DRAGON_FANG,
BLACK_GLASSES,
FAIRY_FEATHER
}
export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType {
@ -548,7 +528,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i
public boostPercent: integer;
constructor(moveType: Type, boostPercent: integer) {
super("", `${getAttackTypeBoosterItemName(moveType)?.replace(/[ \-]/g, "_").toLowerCase()}`,
super("", `${AttackTypeBoosterItem[moveType]?.toLowerCase()}`,
(_type, args) => new AttackTypeBoosterModifier(this, (args[0] as Pokemon).id, moveType, boostPercent));
this.moveType = moveType;
@ -556,7 +536,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i
}
get name(): string {
return i18next.t(`modifierType:AttackTypeBoosterItem.${getAttackTypeBoosterItemName(this.moveType)?.replace(/[ \-]/g, "_").toLowerCase()}`);
return i18next.t(`modifierType:AttackTypeBoosterItem.${AttackTypeBoosterItem[this.moveType]?.toLowerCase()}`);
}
getDescription(scene: BattleScene): string {

View File

@ -65,7 +65,7 @@ export class StatStageChangePhase extends PokemonPhase {
if (!this.selfTarget && stages.value < 0) {
// TODO: add a reference to the source of the stat change to fix Infiltrator interaction
this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, false, null, false, cancelled);
this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, false, null, cancelled);
}
if (!cancelled.value && !this.selfTarget && stages.value < 0) {

View File

@ -205,11 +205,11 @@ export class TurnStartPhase extends FieldPhase {
}
this.scene.pushPhase(new WeatherEffectPhase(this.scene));
this.scene.pushPhase(new BerryPhase(this.scene));
/** Add a new phase to check who should be taking status damage */
this.scene.pushPhase(new CheckStatusEffectPhase(this.scene, moveOrder));
this.scene.pushPhase(new BerryPhase(this.scene));
this.scene.pushPhase(new TurnEndPhase(this.scene));
/**

View File

@ -164,7 +164,7 @@ export async function initI18n(): Promise<void> {
} else {
fileName = camelCaseToKebabCase(ns);
}
return `/locales/${lng}/${fileName}.json?v=${pkg.version}`;
return `./locales/${lng}/${fileName}.json?v=${pkg.version}`;
},
},
defaultNS: "menu",

View File

@ -0,0 +1,120 @@
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { Abilities } from "#enums/abilities";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { BattlerIndex } from "#app/battle";
import { allAbilities } from "#app/data/ability";
import { allMoves, MoveCategory } from "#app/data/move";
describe("Moves - Friend Guard", () => {
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
.battleType("double")
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset([ Moves.TACKLE, Moves.SPLASH, Moves.DRAGON_RAGE ])
.enemySpecies(Species.SHUCKLE)
.moveset([ Moves.SPLASH ])
.startingLevel(100);
});
it("should reduce damage that other allied Pokémon receive from attacks (from any Pokémon) by 25%", async () => {
await game.classicMode.startBattle([ Species.BULBASAUR, Species.CHARMANDER ]);
const [ player1, player2 ] = game.scene.getPlayerField();
const spy = vi.spyOn(player1, "getAttackDamage");
const enemy1 = game.scene.getEnemyField()[0];
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.toNextTurn();
// Get the last return value from `getAttackDamage`
const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
// Making sure the test is controlled; turn 1 damage is equal to base damage (after rounding)
expect(turn1Damage).toBe(Math.floor(player1.getBaseDamage(enemy1, allMoves[Moves.TACKLE], MoveCategory.PHYSICAL)));
vi.spyOn(player2, "getAbility").mockReturnValue(allAbilities[Abilities.FRIEND_GUARD]);
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.toNextTurn();
// Get the last return value from `getAttackDamage`
const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
// With the ally's Friend Guard, damage should have been reduced from base damage by 25%
expect(turn2Damage).toBe(Math.floor(player1.getBaseDamage(enemy1, allMoves[Moves.TACKLE], MoveCategory.PHYSICAL) * 0.75));
});
it("should NOT reduce damage to pokemon with friend guard", async () => {
await game.classicMode.startBattle([ Species.BULBASAUR, Species.CHARMANDER ]);
const player2 = game.scene.getPlayerField()[1];
const spy = vi.spyOn(player2, "getAttackDamage");
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
vi.spyOn(player2, "getAbility").mockReturnValue(allAbilities[Abilities.FRIEND_GUARD]);
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
expect(turn2Damage).toBe(turn1Damage);
});
it("should NOT reduce damage from fixed damage attacks", async () => {
await game.classicMode.startBattle([ Species.BULBASAUR, Species.CHARMANDER ]);
const [ player1, player2 ] = game.scene.getPlayerField();
const spy = vi.spyOn(player1, "getAttackDamage");
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.DRAGON_RAGE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
expect(turn1Damage).toBe(40);
vi.spyOn(player2, "getAbility").mockReturnValue(allAbilities[Abilities.FRIEND_GUARD]);
game.move.select(Moves.SPLASH);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.DRAGON_RAGE, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.toNextTurn();
const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage;
expect(turn2Damage).toBe(40);
});
});

View File

@ -1,4 +1,7 @@
import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg";
import { speciesEggTiers } from "#app/data/balance/species-egg-tiers";
import { speciesStarterCosts } from "#app/data/balance/starters";
import { Egg, getLegendaryGachaSpeciesForTimestamp, getValidLegendaryGachaSpecies } from "#app/data/egg";
import { allSpecies } from "#app/data/pokemon-species";
import { EggSourceType } from "#app/enums/egg-source-types";
import { EggTier } from "#app/enums/egg-type";
import { VariantTier } from "#app/enums/variant-tier";
@ -64,6 +67,12 @@ describe("Egg Generation Tests", () => {
expect(gachaSpeciesCount).toBeGreaterThan(0.4 * EGG_HATCH_COUNT);
expect(gachaSpeciesCount).toBeLessThan(0.6 * EGG_HATCH_COUNT);
});
it("should never be allowed to generate Eternatus via the legendary gacha", () => {
const validLegendaryGachaSpecies = getValidLegendaryGachaSpecies();
expect(validLegendaryGachaSpecies.every(s => speciesEggTiers[s] === EggTier.LEGENDARY)).toBe(true);
expect(validLegendaryGachaSpecies.every(s => allSpecies[s].isObtainable())).toBe(true);
expect(validLegendaryGachaSpecies.includes(Species.ETERNATUS)).toBe(false);
});
it("should hatch an Arceus. Set from species", () => {
const scene = game.scene;
const expectedSpecies = Species.ARCEUS;
@ -376,4 +385,23 @@ describe("Egg Generation Tests", () => {
expect(diffShiny).toBe(true);
expect(diffAbility).toBe(true);
});
// For now, we are using this test to detect oversights in egg tiers.
// Delete this test if the balance team rebalances species costs independently of egg tiers.
it("should have correct egg tiers based on species costs", () => {
const getExpectedEggTier = (starterCost) =>
starterCost <= 3 ? EggTier.COMMON
: starterCost <= 5 ? EggTier.RARE
: starterCost <= 7 ? EggTier.EPIC
: EggTier.LEGENDARY;
allSpecies.forEach(pokemonSpecies => {
const rootSpecies = pokemonSpecies.getRootSpeciesId();
const speciesCost = speciesStarterCosts[rootSpecies];
const expectedEggTier = getExpectedEggTier(speciesCost);
const actualEggTier = speciesEggTiers[rootSpecies];
expect(actualEggTier).toBe(expectedEggTier);
});
});
});

View File

@ -0,0 +1,49 @@
import { Stat } from "#enums/stat";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Moves - Mist", () => {
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
.moveset([ Moves.MIST, Moves.SPLASH ])
.ability(Abilities.BALL_FETCH)
.battleType("double")
.disableCrits()
.enemySpecies(Species.SNORLAX)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.GROWL);
});
it("should prevent the user's side from having stats lowered", async () => {
await game.classicMode.startBattle([ Species.MAGIKARP, Species.FEEBAS ]);
const playerPokemon = game.scene.getPlayerField();
game.move.select(Moves.MIST, 0);
game.move.select(Moves.SPLASH, 1);
await game.phaseInterceptor.to("BerryPhase");
playerPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0));
});
it.todo("should be ignored by opponents with Infiltrator");
});