Compare commits
11 Commits
99e17c94eb
...
d052d444b6
Author | SHA1 | Date | |
---|---|---|---|
|
d052d444b6 | ||
|
13c9888902 | ||
|
19885e0558 | ||
|
f2a6fe5c6a | ||
|
218cf025a3 | ||
|
7f1e94e8ca | ||
|
cf9992f9b8 | ||
|
2dff35c407 | ||
|
ff5b227a6b | ||
|
e0bc1d8781 | ||
|
63a416a65b |
5
.github/workflows/github-pages.yml
vendored
@ -4,6 +4,9 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pages:
|
pages:
|
||||||
@ -33,6 +36,7 @@ jobs:
|
|||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
- name: Checkout repository for Github Pages
|
- name: Checkout repository for Github Pages
|
||||||
|
if: github.event_name == 'push'
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
path: pokerogue_gh
|
path: pokerogue_gh
|
||||||
@ -52,6 +56,7 @@ jobs:
|
|||||||
npx typedoc --out /tmp/docs --githubPages false --entryPoints ./src/
|
npx typedoc --out /tmp/docs --githubPages false --entryPoints ./src/
|
||||||
|
|
||||||
- name: Commit & Push docs
|
- name: Commit & Push docs
|
||||||
|
if: github.event_name == 'push'
|
||||||
run: |
|
run: |
|
||||||
cd pokerogue_gh
|
cd pokerogue_gh
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
@ -7400,7 +7400,49 @@
|
|||||||
"w": 16,
|
"w": 16,
|
||||||
"h": 16
|
"h": 16
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"filename": "toxic_orb",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 32,
|
||||||
|
"h": 32
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 7,
|
||||||
|
"y": 7,
|
||||||
|
"w": 18,
|
||||||
|
"h": 18
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 379,
|
||||||
|
"y": 274,
|
||||||
|
"w": 18,
|
||||||
|
"h": 18
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "flame_orb",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 32,
|
||||||
|
"h": 32
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 7,
|
||||||
|
"y": 7,
|
||||||
|
"w": 18,
|
||||||
|
"h": 18
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 379,
|
||||||
|
"y": 292,
|
||||||
|
"w": 18,
|
||||||
|
"h": 18
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 125 KiB |
BIN
public/images/items/flame_orb.png
Normal file
After Width: | Height: | Size: 367 B |
BIN
public/images/items/toxic_orb.png
Normal file
After Width: | Height: | Size: 408 B |
@ -1332,6 +1332,27 @@
|
|||||||
"h": 24
|
"h": 24
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"filename": "173_2",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 40,
|
||||||
|
"h": 30
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 11,
|
||||||
|
"y": 11,
|
||||||
|
"w": 18,
|
||||||
|
"h": 18
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 174,
|
||||||
|
"y": 253,
|
||||||
|
"w": 18,
|
||||||
|
"h": 18
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filename": "173_3",
|
"filename": "173_3",
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 60 KiB |
41
public/images/trainer/firebreather.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "firebreather.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 80,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 80,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:f692676a166fc1915532cd94d5799af4:fb833f76fb6797474657726bb59a7eee:aeb55e30992938f494b6cd2420158dda$"
|
||||||
|
}
|
||||||
|
}
|
BIN
public/images/trainer/firebreather.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
@ -55,8 +55,22 @@ export class Ability implements Localizable {
|
|||||||
this.description = this.id ? i18next.t(`ability:${i18nKey}.description`) as string : "";
|
this.description = this.id ? i18next.t(`ability:${i18nKey}.description`) as string : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttrs(attrType: { new(...args: any[]): AbAttr }): AbAttr[] {
|
/**
|
||||||
return this.attrs.filter(a => a instanceof attrType);
|
* Get all ability attributes that match `attrType`
|
||||||
|
* @param attrType any attribute that extends {@linkcode AbAttr}
|
||||||
|
* @returns Array of attributes that match `attrType`, Empty Array if none match.
|
||||||
|
*/
|
||||||
|
getAttrs<T extends AbAttr>(attrType: new(...args: any[]) => T ): T[] {
|
||||||
|
return this.attrs.filter((a): a is T => a instanceof attrType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an ability has an attribute that matches `attrType`
|
||||||
|
* @param attrType any attribute that extends {@linkcode AbAttr}
|
||||||
|
* @returns true if the ability has attribute `attrType`
|
||||||
|
*/
|
||||||
|
hasAttr<T extends AbAttr>(attrType: new(...args: any[]) => T): boolean {
|
||||||
|
return this.attrs.some((attr) => attr instanceof attrType);
|
||||||
}
|
}
|
||||||
|
|
||||||
attr<T extends new (...args: any[]) => AbAttr>(AttrType: T, ...args: ConstructorParameters<T>): Ability {
|
attr<T extends new (...args: any[]) => AbAttr>(AttrType: T, ...args: ConstructorParameters<T>): Ability {
|
||||||
@ -74,10 +88,6 @@ export class Ability implements Localizable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAttr(attrType: { new(...args: any[]): AbAttr }): boolean {
|
|
||||||
return !!this.getAttrs(attrType).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
bypassFaint(): Ability {
|
bypassFaint(): Ability {
|
||||||
this.isBypassFaint = true;
|
this.isBypassFaint = true;
|
||||||
return this;
|
return this;
|
||||||
@ -341,7 +351,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
if ((move.getMove() instanceof AttackMove || move.getMove().getAttrs(StatusMoveTypeImmunityAttr).find(attr => (attr as StatusMoveTypeImmunityAttr).immuneType === this.immuneType)) && move.getMove().type === this.immuneType) {
|
if ((move.getMove() instanceof AttackMove || move.getMove().getAttrs(StatusMoveTypeImmunityAttr).find(attr => attr.immuneType === this.immuneType)) && move.getMove().type === this.immuneType) {
|
||||||
(args[0] as Utils.NumberHolder).value = 0;
|
(args[0] as Utils.NumberHolder).value = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -568,7 +578,7 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr {
|
|||||||
|
|
||||||
export class ReverseDrainAbAttr extends PostDefendAbAttr {
|
export class ReverseDrainAbAttr extends PostDefendAbAttr {
|
||||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||||
if (!!move.getMove().getAttrs(HitHealAttr).length || !!move.getMove().getAttrs(StrengthSapHealAttr).length ) {
|
if (move.getMove().hasAttr(HitHealAttr) || move.getMove().hasAttr(StrengthSapHealAttr) ) {
|
||||||
pokemon.scene.queueMessage(getPokemonMessage(attacker, " sucked up the liquid ooze!"));
|
pokemon.scene.queueMessage(getPokemonMessage(attacker, " sucked up the liquid ooze!"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2194,7 +2204,7 @@ function getAnticipationCondition(): AbAttrCondition {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// move is a OHKO
|
// move is a OHKO
|
||||||
if (move.getMove().findAttr(attr => attr instanceof OneHitKOAttr)) {
|
if (move.getMove().hasAttr(OneHitKOAttr)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// edge case for hidden power, type is computed
|
// edge case for hidden power, type is computed
|
||||||
@ -2248,7 +2258,7 @@ export class ForewarnAbAttr extends PostSummonAbAttr {
|
|||||||
for (const move of opponent.moveset) {
|
for (const move of opponent.moveset) {
|
||||||
if (move.getMove() instanceof StatusMove) {
|
if (move.getMove() instanceof StatusMove) {
|
||||||
movePower = 1;
|
movePower = 1;
|
||||||
} else if (move.getMove().findAttr(attr => attr instanceof OneHitKOAttr)) {
|
} else if (move.getMove().hasAttr(OneHitKOAttr)) {
|
||||||
movePower = 150;
|
movePower = 150;
|
||||||
} else if (move.getMove().id === Moves.COUNTER || move.getMove().id === Moves.MIRROR_COAT || move.getMove().id === Moves.METAL_BURST) {
|
} else if (move.getMove().id === Moves.COUNTER || move.getMove().id === Moves.MIRROR_COAT || move.getMove().id === Moves.METAL_BURST) {
|
||||||
movePower = 120;
|
movePower = 120;
|
||||||
@ -3325,7 +3335,7 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ability = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility());
|
const ability = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility());
|
||||||
const attrs = ability.getAttrs(attrType) as TAttr[];
|
const attrs = ability.getAttrs(attrType);
|
||||||
|
|
||||||
const clearSpliceQueueAndResolve = () => {
|
const clearSpliceQueueAndResolve = () => {
|
||||||
pokemon.scene.clearPhaseQueueSplice();
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
@ -3524,7 +3534,7 @@ export const allAbilities = [ new Ability(Abilities.NONE, 3) ];
|
|||||||
export function initAbilities() {
|
export function initAbilities() {
|
||||||
allAbilities.push(
|
allAbilities.push(
|
||||||
new Ability(Abilities.STENCH, 3)
|
new Ability(Abilities.STENCH, 3)
|
||||||
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => (move.getMove().category !== MoveCategory.STATUS && !move.getMove().findAttr(attr => attr instanceof FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED),
|
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => (move.getMove().category !== MoveCategory.STATUS && !move.getMove().hasAttr(FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED),
|
||||||
new Ability(Abilities.DRIZZLE, 3)
|
new Ability(Abilities.DRIZZLE, 3)
|
||||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN)
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN)
|
||||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN),
|
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN),
|
||||||
@ -3716,9 +3726,9 @@ export function initAbilities() {
|
|||||||
.conditionalAttr(pokemon => !Utils.randSeedInt(3), PostTurnResetStatusAbAttr),
|
.conditionalAttr(pokemon => !Utils.randSeedInt(3), PostTurnResetStatusAbAttr),
|
||||||
new Ability(Abilities.GUTS, 3)
|
new Ability(Abilities.GUTS, 3)
|
||||||
.attr(BypassBurnDamageReductionAbAttr)
|
.attr(BypassBurnDamageReductionAbAttr)
|
||||||
.conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5),
|
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.ATK, 1.5),
|
||||||
new Ability(Abilities.MARVEL_SCALE, 3)
|
new Ability(Abilities.MARVEL_SCALE, 3)
|
||||||
.conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5)
|
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.LIQUID_OOZE, 3)
|
new Ability(Abilities.LIQUID_OOZE, 3)
|
||||||
.attr(ReverseDrainAbAttr),
|
.attr(ReverseDrainAbAttr),
|
||||||
@ -3813,7 +3823,7 @@ export function initAbilities() {
|
|||||||
.condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)),
|
.condition(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)),
|
||||||
new Ability(Abilities.QUICK_FEET, 4)
|
new Ability(Abilities.QUICK_FEET, 4)
|
||||||
.conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, BattleStatMultiplierAbAttr, BattleStat.SPD, 2)
|
.conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, BattleStatMultiplierAbAttr, BattleStat.SPD, 2)
|
||||||
.conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.SPD, 1.5),
|
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), BattleStatMultiplierAbAttr, BattleStat.SPD, 1.5),
|
||||||
new Ability(Abilities.NORMALIZE, 4)
|
new Ability(Abilities.NORMALIZE, 4)
|
||||||
.attr(MoveTypeChangeAttr, Type.NORMAL, 1.2, (user, target, move) => move.id !== Moves.HIDDEN_POWER && move.id !== Moves.WEATHER_BALL &&
|
.attr(MoveTypeChangeAttr, Type.NORMAL, 1.2, (user, target, move) => move.id !== Moves.HIDDEN_POWER && move.id !== Moves.WEATHER_BALL &&
|
||||||
move.id !== Moves.NATURAL_GIFT && move.id !== Moves.JUDGMENT && move.id !== Moves.TECHNO_BLAST),
|
move.id !== Moves.NATURAL_GIFT && move.id !== Moves.JUDGMENT && move.id !== Moves.TECHNO_BLAST),
|
||||||
|
@ -468,7 +468,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
|||||||
} else {
|
} else {
|
||||||
const loadedCheckTimer = setInterval(() => {
|
const loadedCheckTimer = setInterval(() => {
|
||||||
if (moveAnims.get(move) !== null) {
|
if (moveAnims.get(move) !== null) {
|
||||||
const chargeAttr = allMoves[move].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[move].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr;
|
const chargeAttr = allMoves[move].getAttrs(ChargeAttr)[0] || allMoves[move].getAttrs(DelayedAttackAttr)[0];
|
||||||
if (chargeAttr && chargeAnims.get(chargeAttr.chargeAnim) === null) {
|
if (chargeAttr && chargeAnims.get(chargeAttr.chargeAnim) === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -498,7 +498,7 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
|||||||
} else {
|
} else {
|
||||||
populateMoveAnim(move, ba);
|
populateMoveAnim(move, ba);
|
||||||
}
|
}
|
||||||
const chargeAttr = allMoves[move].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[move].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr;
|
const chargeAttr = allMoves[move].getAttrs(ChargeAttr)[0] || allMoves[move].getAttrs(DelayedAttackAttr)[0];
|
||||||
if (chargeAttr) {
|
if (chargeAttr) {
|
||||||
initMoveChargeAnim(scene, chargeAttr.chargeAnim).then(() => resolve());
|
initMoveChargeAnim(scene, chargeAttr.chargeAnim).then(() => resolve());
|
||||||
} else {
|
} else {
|
||||||
@ -569,7 +569,7 @@ export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLo
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
|
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
|
||||||
for (const moveId of moveIds) {
|
for (const moveId of moveIds) {
|
||||||
const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[moveId].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr;
|
const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr)[0] || allMoves[moveId].getAttrs(DelayedAttackAttr)[0];
|
||||||
if (chargeAttr) {
|
if (chargeAttr) {
|
||||||
const moveChargeAnims = chargeAnims.get(chargeAttr.chargeAnim);
|
const moveChargeAnims = chargeAnims.get(chargeAttr.chargeAnim);
|
||||||
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims[0]);
|
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims[0]);
|
||||||
|
@ -509,7 +509,7 @@ export class EncoreTag extends BattlerTag {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allMoves[repeatableMove.move].getAttrs(ChargeAttr).length && repeatableMove.result === MoveResult.OTHER) {
|
if (allMoves[repeatableMove.move].hasAttr(ChargeAttr) && repeatableMove.result === MoveResult.OTHER) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||||||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
||||||
},
|
},
|
||||||
[Biome.VOLCANO]: {
|
[Biome.VOLCANO]: {
|
||||||
[BiomePoolTier.COMMON]: [],
|
[BiomePoolTier.COMMON]: [ TrainerType.FIREBREATHER ],
|
||||||
[BiomePoolTier.UNCOMMON]: [],
|
[BiomePoolTier.UNCOMMON]: [],
|
||||||
[BiomePoolTier.RARE]: [],
|
[BiomePoolTier.RARE]: [],
|
||||||
[BiomePoolTier.SUPER_RARE]: [],
|
[BiomePoolTier.SUPER_RARE]: [],
|
||||||
@ -7304,6 +7304,10 @@ export function initBiomes() {
|
|||||||
[ Biome.GRAVEYARD, BiomePoolTier.UNCOMMON ]
|
[ Biome.GRAVEYARD, BiomePoolTier.UNCOMMON ]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
[ TrainerType.FIREBREATHER, [
|
||||||
|
[ Biome.VOLCANO, BiomePoolTier.COMMON ]
|
||||||
|
]
|
||||||
|
],
|
||||||
[ TrainerType.BROCK, [
|
[ TrainerType.BROCK, [
|
||||||
[ Biome.CAVE, BiomePoolTier.BOSS ]
|
[ Biome.CAVE, BiomePoolTier.BOSS ]
|
||||||
]
|
]
|
||||||
|
@ -421,6 +421,20 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
[TrainerType.FIREBREATHER]: [
|
||||||
|
{
|
||||||
|
encounter: [
|
||||||
|
"dialogue:firebreather.encounter.1",
|
||||||
|
"dialogue:firebreather.encounter.2",
|
||||||
|
"dialogue:firebreather.encounter.3",
|
||||||
|
],
|
||||||
|
victory: [
|
||||||
|
"dialogue:firebreather.victory.1",
|
||||||
|
"dialogue:firebreather.victory.2",
|
||||||
|
"dialogue:firebreather.victory.3",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
[TrainerType.BROCK]: {
|
[TrainerType.BROCK]: {
|
||||||
encounter: [
|
encounter: [
|
||||||
"dialogue:brock.encounter.1",
|
"dialogue:brock.encounter.1",
|
||||||
|
@ -16,6 +16,7 @@ export enum TrainerType {
|
|||||||
DANCER,
|
DANCER,
|
||||||
DEPOT_AGENT,
|
DEPOT_AGENT,
|
||||||
DOCTOR,
|
DOCTOR,
|
||||||
|
FIREBREATHER,
|
||||||
FISHERMAN,
|
FISHERMAN,
|
||||||
GUITARIST,
|
GUITARIST,
|
||||||
HARLEQUIN,
|
HARLEQUIN,
|
||||||
|
@ -148,8 +148,22 @@ export default class Move implements Localizable {
|
|||||||
this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : "";
|
this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttrs(attrType: { new(...args: any[]): MoveAttr }): MoveAttr[] {
|
/**
|
||||||
return this.attrs.filter(a => a instanceof attrType);
|
* Get all move attributes that match `attrType`
|
||||||
|
* @param attrType any attribute that extends {@linkcode MoveAttr}
|
||||||
|
* @returns Array of attributes that match `attrType`, Empty Array if none match.
|
||||||
|
*/
|
||||||
|
getAttrs<T extends MoveAttr>(attrType: new(...args: any[]) => T): T[] {
|
||||||
|
return this.attrs.filter((a): a is T => a instanceof attrType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a move has an attribute that matches `attrType`
|
||||||
|
* @param attrType any attribute that extends {@linkcode MoveAttr}
|
||||||
|
* @returns true if the move has attribute `attrType`
|
||||||
|
*/
|
||||||
|
hasAttr<T extends MoveAttr>(attrType: new(...args: any[]) => T): boolean {
|
||||||
|
return this.attrs.some((attr) => attr instanceof attrType);
|
||||||
}
|
}
|
||||||
|
|
||||||
findAttr(attrPredicate: (attr: MoveAttr) => boolean): MoveAttr {
|
findAttr(attrPredicate: (attr: MoveAttr) => boolean): MoveAttr {
|
||||||
@ -3698,7 +3712,7 @@ export class ProtectAttr extends AddBattlerTagAttr {
|
|||||||
|
|
||||||
while (moveHistory.length) {
|
while (moveHistory.length) {
|
||||||
turnMove = moveHistory.shift();
|
turnMove = moveHistory.shift();
|
||||||
if (!allMoves[turnMove.move].getAttrs(ProtectAttr).length || turnMove.result !== MoveResult.SUCCESS) {
|
if (!allMoves[turnMove.move].hasAttr(ProtectAttr) || turnMove.result !== MoveResult.SUCCESS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
timesUsed++;
|
timesUsed++;
|
||||||
@ -4480,7 +4494,7 @@ const lastMoveCopiableCondition: MoveConditionFunc = (user, target, move) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allMoves[copiableMove].getAttrs(ChargeAttr).length) {
|
if (allMoves[copiableMove].hasAttr(ChargeAttr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4570,7 +4584,7 @@ const targetMoveCopiableCondition: MoveConditionFunc = (user, target, move) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allMoves[copiableMove.move].getAttrs(ChargeAttr).length && copiableMove.result === MoveResult.OTHER) {
|
if (allMoves[copiableMove.move].hasAttr(ChargeAttr) && copiableMove.result === MoveResult.OTHER) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4987,7 +5001,7 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet {
|
|||||||
const variableTarget = new Utils.NumberHolder(0);
|
const variableTarget = new Utils.NumberHolder(0);
|
||||||
user.getOpponents().forEach(p => applyMoveAttrs(VariableTargetAttr, user, p, allMoves[move], variableTarget));
|
user.getOpponents().forEach(p => applyMoveAttrs(VariableTargetAttr, user, p, allMoves[move], variableTarget));
|
||||||
|
|
||||||
const moveTarget = allMoves[move].getAttrs(VariableTargetAttr).length ? variableTarget.value : move ? allMoves[move].moveTarget : move === undefined ? MoveTarget.NEAR_ENEMY : [];
|
const moveTarget = allMoves[move].hasAttr(VariableTargetAttr) ? variableTarget.value : move ? allMoves[move].moveTarget : move === undefined ? MoveTarget.NEAR_ENEMY : [];
|
||||||
const opponents = user.getOpponents();
|
const opponents = user.getOpponents();
|
||||||
|
|
||||||
let set: Pokemon[] = [];
|
let set: Pokemon[] = [];
|
||||||
|
@ -56,7 +56,7 @@ export class Terrain {
|
|||||||
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
|
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean {
|
||||||
switch (this.terrainType) {
|
switch (this.terrainType) {
|
||||||
case TerrainType.PSYCHIC:
|
case TerrainType.PSYCHIC:
|
||||||
if (!move.getAttrs(ProtectAttr).length) {
|
if (!move.hasAttr(ProtectAttr)) {
|
||||||
const priority = new Utils.IntegerHolder(move.priority);
|
const priority = new Utils.IntegerHolder(move.priority);
|
||||||
applyAbAttrs(IncrementMovePriorityAbAttr, user, null, move, priority);
|
applyAbAttrs(IncrementMovePriorityAbAttr, user, null, move, priority);
|
||||||
return priority.value > 0 && user.getOpponents().filter(o => targets.includes(o.getBattlerIndex())).length > 0;
|
return priority.value > 0 && user.getOpponents().filter(o => targets.includes(o.getBattlerIndex())).length > 0;
|
||||||
|
@ -989,6 +989,8 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
[TrainerType.DEPOT_AGENT]: new TrainerConfig(++t).setMoneyMultiplier(1.45).setEncounterBgm(TrainerType.CLERK),
|
[TrainerType.DEPOT_AGENT]: new TrainerConfig(++t).setMoneyMultiplier(1.45).setEncounterBgm(TrainerType.CLERK),
|
||||||
[TrainerType.DOCTOR]: new TrainerConfig(++t).setHasGenders("Nurse", "lass").setHasDouble("Medical Team").setMoneyMultiplier(3).setEncounterBgm(TrainerType.CLERK)
|
[TrainerType.DOCTOR]: new TrainerConfig(++t).setHasGenders("Nurse", "lass").setHasDouble("Medical Team").setMoneyMultiplier(3).setEncounterBgm(TrainerType.CLERK)
|
||||||
.setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.HEAL_PULSE)),
|
.setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.HEAL_PULSE)),
|
||||||
|
[TrainerType.FIREBREATHER]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.ROUGHNECK)
|
||||||
|
.setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.SMOG) || s.isOfType(Type.FIRE)),
|
||||||
[TrainerType.FISHERMAN]: new TrainerConfig(++t).setMoneyMultiplier(1.25).setEncounterBgm(TrainerType.BACKPACKER).setSpecialtyTypes(Type.WATER)
|
[TrainerType.FISHERMAN]: new TrainerConfig(++t).setMoneyMultiplier(1.25).setEncounterBgm(TrainerType.BACKPACKER).setSpecialtyTypes(Type.WATER)
|
||||||
.setPartyTemplates(trainerPartyTemplates.TWO_WEAK_SAME_ONE_AVG, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.THREE_WEAK_SAME, trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.SIX_WEAKER)
|
.setPartyTemplates(trainerPartyTemplates.TWO_WEAK_SAME_ONE_AVG, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.THREE_WEAK_SAME, trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.SIX_WEAKER)
|
||||||
.setSpeciesPools({
|
.setSpeciesPools({
|
||||||
|
@ -36,6 +36,7 @@ const trainerNameConfigs: TrainerNameConfigs = {
|
|||||||
[TrainerType.DANCER]: new TrainerNameConfig(TrainerType.DANCER),
|
[TrainerType.DANCER]: new TrainerNameConfig(TrainerType.DANCER),
|
||||||
[TrainerType.DEPOT_AGENT]: new TrainerNameConfig(TrainerType.DEPOT_AGENT),
|
[TrainerType.DEPOT_AGENT]: new TrainerNameConfig(TrainerType.DEPOT_AGENT),
|
||||||
[TrainerType.DOCTOR]: new TrainerNameConfig(TrainerType.DOCTOR).hasGenderVariant("Nurse"),
|
[TrainerType.DOCTOR]: new TrainerNameConfig(TrainerType.DOCTOR).hasGenderVariant("Nurse"),
|
||||||
|
[TrainerType.FIREBREATHER]: new TrainerNameConfig(TrainerType.FIREBREATHER),
|
||||||
[TrainerType.FISHERMAN]: new TrainerNameConfig(TrainerType.FISHERMAN),
|
[TrainerType.FISHERMAN]: new TrainerNameConfig(TrainerType.FISHERMAN),
|
||||||
[TrainerType.GUITARIST]: new TrainerNameConfig(TrainerType.GUITARIST),
|
[TrainerType.GUITARIST]: new TrainerNameConfig(TrainerType.GUITARIST),
|
||||||
[TrainerType.HARLEQUIN]: new TrainerNameConfig(TrainerType.HARLEQUIN),
|
[TrainerType.HARLEQUIN]: new TrainerNameConfig(TrainerType.HARLEQUIN),
|
||||||
@ -87,6 +88,7 @@ export const trainerNamePools = {
|
|||||||
[TrainerType.DANCER]: ["Brian","Davey","Dirk","Edmond","Mickey","Raymond","Cara","Julia","Maika","Mireille","Ronda","Zoe"],
|
[TrainerType.DANCER]: ["Brian","Davey","Dirk","Edmond","Mickey","Raymond","Cara","Julia","Maika","Mireille","Ronda","Zoe"],
|
||||||
[TrainerType.DEPOT_AGENT]: ["Josh","Hank","Vincent"],
|
[TrainerType.DEPOT_AGENT]: ["Josh","Hank","Vincent"],
|
||||||
[TrainerType.DOCTOR]: [["Hank","Jerry","Jules","Logan","Wayne","Braid","Derek","Heath","Julius","Kit","Graham"],["Kirsten","Sachiko","Shery","Carol","Dixie","Mariah"]],
|
[TrainerType.DOCTOR]: [["Hank","Jerry","Jules","Logan","Wayne","Braid","Derek","Heath","Julius","Kit","Graham"],["Kirsten","Sachiko","Shery","Carol","Dixie","Mariah"]],
|
||||||
|
[TrainerType.FIREBREATHER]: ["Bill","Burt","Cliff","Dick","Lyle","Ned","Otis","Ray","Richard","Walt"],
|
||||||
[TrainerType.FISHERMAN]: ["Andre","Arnold","Barney","Chris","Edgar","Henry","Jonah","Justin","Kyle","Martin","Marvin","Ralph","Raymond","Scott","Stephen","Wilton","Tully","Andrew","Barny","Carter","Claude","Dale","Elliot","Eugene","Ivan","Ned","Nolan","Roger","Ronald","Wade","Wayne","Darian","Kai","Chip","Hank","Kaden","Tommy","Tylor","Alec","Brett","Cameron","Cody","Cole","Cory","Erick","George","Joseph","Juan","Kenneth","Luc","Miguel","Travis","Walter","Zachary","Josh","Gideon","Kyler","Liam","Murphy","Bruce","Damon","Devon","Hubert","Jones","Lydon","Mick","Pete","Sean","Sid","Vince","Bucky","Dean","Eustace","Kenzo","Leroy","Mack","Ryder","Ewan","Finn","Murray","Seward","Shad","Wharton","Finley","Fisher","Fisk","River","Sheaffer","Timin","Carl","Ernest","Hal","Herbert","Hisato","Mike","Vernon","Harriet","Marina","Chase"],
|
[TrainerType.FISHERMAN]: ["Andre","Arnold","Barney","Chris","Edgar","Henry","Jonah","Justin","Kyle","Martin","Marvin","Ralph","Raymond","Scott","Stephen","Wilton","Tully","Andrew","Barny","Carter","Claude","Dale","Elliot","Eugene","Ivan","Ned","Nolan","Roger","Ronald","Wade","Wayne","Darian","Kai","Chip","Hank","Kaden","Tommy","Tylor","Alec","Brett","Cameron","Cody","Cole","Cory","Erick","George","Joseph","Juan","Kenneth","Luc","Miguel","Travis","Walter","Zachary","Josh","Gideon","Kyler","Liam","Murphy","Bruce","Damon","Devon","Hubert","Jones","Lydon","Mick","Pete","Sean","Sid","Vince","Bucky","Dean","Eustace","Kenzo","Leroy","Mack","Ryder","Ewan","Finn","Murray","Seward","Shad","Wharton","Finley","Fisher","Fisk","River","Sheaffer","Timin","Carl","Ernest","Hal","Herbert","Hisato","Mike","Vernon","Harriet","Marina","Chase"],
|
||||||
[TrainerType.GUITARIST]: ["Anna","Beverly","January","Tina","Alicia","Claudia","Julia","Lidia","Mireia","Noelia","Sara","Sheila","Tatiana"],
|
[TrainerType.GUITARIST]: ["Anna","Beverly","January","Tina","Alicia","Claudia","Julia","Lidia","Mireia","Noelia","Sara","Sheila","Tatiana"],
|
||||||
[TrainerType.HARLEQUIN]: ["Charley","Ian","Jack","Kerry","Louis","Pat","Paul","Rick","Anders","Clarence","Gary"],
|
[TrainerType.HARLEQUIN]: ["Charley","Ian","Jack","Kerry","Louis","Pat","Paul","Rick","Anders","Clarence","Gary"],
|
||||||
|
@ -114,9 +114,9 @@ export class Weather {
|
|||||||
const field = scene.getField(true);
|
const field = scene.getField(true);
|
||||||
|
|
||||||
for (const pokemon of field) {
|
for (const pokemon of field) {
|
||||||
let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr;
|
let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0];
|
||||||
if (!suppressWeatherEffectAbAttr) {
|
if (!suppressWeatherEffectAbAttr) {
|
||||||
suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr : null;
|
suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr)[0] : null;
|
||||||
}
|
}
|
||||||
if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) {
|
if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -14,30 +14,51 @@ import { EggTier } from "./data/enums/egg-type";
|
|||||||
import PokemonInfoContainer from "./ui/pokemon-info-container";
|
import PokemonInfoContainer from "./ui/pokemon-info-container";
|
||||||
import EggsToHatchCountContainer from "./ui/eggs-to-hatch-count-container";
|
import EggsToHatchCountContainer from "./ui/eggs-to-hatch-count-container";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents egg hatching
|
||||||
|
*/
|
||||||
export class EggHatchPhase extends Phase {
|
export class EggHatchPhase extends Phase {
|
||||||
|
/** The egg that is hatching */
|
||||||
private egg: Egg;
|
private egg: Egg;
|
||||||
|
|
||||||
|
/** The number of eggs that are hatching */
|
||||||
private eggsToHatchCount: integer;
|
private eggsToHatchCount: integer;
|
||||||
|
/** The container that lists how many eggs are hatching */
|
||||||
private eggsToHatchCountContainer: EggsToHatchCountContainer;
|
private eggsToHatchCountContainer: EggsToHatchCountContainer;
|
||||||
|
|
||||||
|
/** The scene handler for egg hatching */
|
||||||
private eggHatchHandler: EggHatchSceneHandler;
|
private eggHatchHandler: EggHatchSceneHandler;
|
||||||
|
/** The phaser gameobject container that holds everything */
|
||||||
private eggHatchContainer: Phaser.GameObjects.Container;
|
private eggHatchContainer: Phaser.GameObjects.Container;
|
||||||
|
/** The phaser image that is the background */
|
||||||
private eggHatchBg: Phaser.GameObjects.Image;
|
private eggHatchBg: Phaser.GameObjects.Image;
|
||||||
|
/** The phaser rectangle that overlays during the scene */
|
||||||
private eggHatchOverlay: Phaser.GameObjects.Rectangle;
|
private eggHatchOverlay: Phaser.GameObjects.Rectangle;
|
||||||
|
/** The phaser container that holds the egg */
|
||||||
private eggContainer: Phaser.GameObjects.Container;
|
private eggContainer: Phaser.GameObjects.Container;
|
||||||
|
/** The phaser sprite of the egg */
|
||||||
private eggSprite: Phaser.GameObjects.Sprite;
|
private eggSprite: Phaser.GameObjects.Sprite;
|
||||||
|
/** The phaser sprite of the cracks in an egg */
|
||||||
private eggCrackSprite: Phaser.GameObjects.Sprite;
|
private eggCrackSprite: Phaser.GameObjects.Sprite;
|
||||||
|
/** The phaser sprite that represents the overlaid light rays */
|
||||||
private eggLightraysOverlay: Phaser.GameObjects.Sprite;
|
private eggLightraysOverlay: Phaser.GameObjects.Sprite;
|
||||||
|
/** The phaser sprite of the hatched Pokemon */
|
||||||
private pokemonSprite: Phaser.GameObjects.Sprite;
|
private pokemonSprite: Phaser.GameObjects.Sprite;
|
||||||
|
/** The phaser sprite for shiny sparkles */
|
||||||
private pokemonShinySparkle: Phaser.GameObjects.Sprite;
|
private pokemonShinySparkle: Phaser.GameObjects.Sprite;
|
||||||
|
|
||||||
|
/** The {@link PokemonInfoContainer} of the newly hatched Pokemon */
|
||||||
private infoContainer: PokemonInfoContainer;
|
private infoContainer: PokemonInfoContainer;
|
||||||
|
|
||||||
|
/** The newly hatched {@link PlayerPokemon} */
|
||||||
private pokemon: PlayerPokemon;
|
private pokemon: PlayerPokemon;
|
||||||
|
/** The index of which egg move is unlocked. 0-2 is common, 3 is rare */
|
||||||
private eggMoveIndex: integer;
|
private eggMoveIndex: integer;
|
||||||
|
/** Internal booleans representing if the egg is hatched, able to be skipped, or skipped */
|
||||||
private hatched: boolean;
|
private hatched: boolean;
|
||||||
private canSkip: boolean;
|
private canSkip: boolean;
|
||||||
private skipped: boolean;
|
private skipped: boolean;
|
||||||
|
/** The sound effect being played when the egg is hatched */
|
||||||
private evolutionBgm: AnySound;
|
private evolutionBgm: AnySound;
|
||||||
|
|
||||||
constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) {
|
constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) {
|
||||||
@ -117,6 +138,7 @@ export class EggHatchPhase extends Phase {
|
|||||||
|
|
||||||
this.eggHatchContainer.add(this.infoContainer);
|
this.eggHatchContainer.add(this.infoContainer);
|
||||||
|
|
||||||
|
// The game will try to unfuse any Pokemon even though eggs should not generate fused Pokemon in the first place
|
||||||
const pokemon = this.generatePokemon();
|
const pokemon = this.generatePokemon();
|
||||||
if (pokemon.fusionSpecies) {
|
if (pokemon.fusionSpecies) {
|
||||||
pokemon.clearFusionSpecies();
|
pokemon.clearFusionSpecies();
|
||||||
@ -187,6 +209,13 @@ export class EggHatchPhase extends Phase {
|
|||||||
super.end();
|
super.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that animates egg shaking
|
||||||
|
* @param intensity of horizontal shaking. Doubled on the first call (where count is 0)
|
||||||
|
* @param repeatCount the number of times this function should be called (asynchronous recursion?!?)
|
||||||
|
* @param count the current number of times this function has been called.
|
||||||
|
* @returns nothing since it's a Promise<void>
|
||||||
|
*/
|
||||||
doEggShake(intensity: number, repeatCount?: integer, count?: integer): Promise<void> {
|
doEggShake(intensity: number, repeatCount?: integer, count?: integer): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (repeatCount === undefined) {
|
if (repeatCount === undefined) {
|
||||||
@ -226,6 +255,10 @@ export class EggHatchPhase extends Phase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to skip the hatching animation
|
||||||
|
* @returns false if cannot be skipped or already skipped. True otherwise
|
||||||
|
*/
|
||||||
trySkip(): boolean {
|
trySkip(): boolean {
|
||||||
if (!this.canSkip || this.skipped) {
|
if (!this.canSkip || this.skipped) {
|
||||||
return false;
|
return false;
|
||||||
@ -239,6 +272,9 @@ export class EggHatchPhase extends Phase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays the animation of an egg hatch
|
||||||
|
*/
|
||||||
doHatch(): void {
|
doHatch(): void {
|
||||||
this.canSkip = false;
|
this.canSkip = false;
|
||||||
this.hatched = true;
|
this.hatched = true;
|
||||||
@ -268,6 +304,9 @@ export class EggHatchPhase extends Phase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to do the logic and animation of completing a hatch and revealing the Pokemon
|
||||||
|
*/
|
||||||
doReveal(): void {
|
doReveal(): void {
|
||||||
// Update/reduce count of hatching eggs when revealed if count is at least 1
|
// Update/reduce count of hatching eggs when revealed if count is at least 1
|
||||||
// If count is 0, hide eggsToHatchCountContainer instead
|
// If count is 0, hide eggsToHatchCountContainer instead
|
||||||
@ -333,10 +372,21 @@ export class EggHatchPhase extends Phase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to generate sine. (Why is this not a Utils?!?)
|
||||||
|
* @param index random number from 0-7 being passed in to scale pi/128
|
||||||
|
* @param amplitude Scaling
|
||||||
|
* @returns a number
|
||||||
|
*/
|
||||||
sin(index: integer, amplitude: integer): number {
|
sin(index: integer, amplitude: integer): number {
|
||||||
return amplitude * Math.sin(index * (Math.PI / 128));
|
return amplitude * Math.sin(index * (Math.PI / 128));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates spraying
|
||||||
|
* @param intensity number of times this is repeated (this is a badly named variable)
|
||||||
|
* @param offsetY how much to offset the Y coordinates
|
||||||
|
*/
|
||||||
doSpray(intensity: integer, offsetY?: number) {
|
doSpray(intensity: integer, offsetY?: number) {
|
||||||
this.scene.tweens.addCounter({
|
this.scene.tweens.addCounter({
|
||||||
repeat: intensity,
|
repeat: intensity,
|
||||||
@ -347,6 +397,11 @@ export class EggHatchPhase extends Phase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates a particle used in the spray animation
|
||||||
|
* @param trigIndex Used to modify the particle's vertical speed, is a random number from 0-7
|
||||||
|
* @param offsetY how much to offset the Y coordinate
|
||||||
|
*/
|
||||||
doSprayParticle(trigIndex: integer, offsetY: number) {
|
doSprayParticle(trigIndex: integer, offsetY: number) {
|
||||||
const initialX = this.eggHatchBg.displayWidth / 2;
|
const initialX = this.eggHatchBg.displayWidth / 2;
|
||||||
const initialY = this.eggHatchBg.displayHeight / 2 + offsetY;
|
const initialY = this.eggHatchBg.displayHeight / 2 + offsetY;
|
||||||
@ -387,12 +442,22 @@ export class EggHatchPhase extends Phase {
|
|||||||
updateParticle();
|
updateParticle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a Pokemon to be hatched by the egg
|
||||||
|
* @returns the hatched PlayerPokemon
|
||||||
|
*/
|
||||||
generatePokemon(): PlayerPokemon {
|
generatePokemon(): PlayerPokemon {
|
||||||
let ret: PlayerPokemon;
|
let ret: PlayerPokemon;
|
||||||
let speciesOverride: Species;
|
let speciesOverride: Species; // SpeciesOverride should probably be a passed in parameter for future species-eggs
|
||||||
|
|
||||||
this.scene.executeWithSeedOffset(() => {
|
this.scene.executeWithSeedOffset(() => {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manaphy eggs have a 1/8 chance of being Manaphy and 7/8 chance of being Phione
|
||||||
|
* Legendary eggs pulled from the legendary gacha have a 50% of being converted into
|
||||||
|
* the species that was the legendary focus at the time
|
||||||
|
*/
|
||||||
if (this.egg.isManaphyEgg()) {
|
if (this.egg.isManaphyEgg()) {
|
||||||
const rand = Utils.randSeedInt(8);
|
const rand = Utils.randSeedInt(8);
|
||||||
|
|
||||||
@ -437,6 +502,18 @@ export class EggHatchPhase extends Phase {
|
|||||||
.map(s => parseInt(s) as Species)
|
.map(s => parseInt(s) as Species)
|
||||||
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
|
.filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokemon that are cheaper in their tier get a weight boost. Regionals get a weight penalty
|
||||||
|
* 1 cost mons get 2x
|
||||||
|
* 2 cost mons get 1.5x
|
||||||
|
* 4, 6, 8 cost mons get 1.75x
|
||||||
|
* 3, 5, 7, 9 cost mons get 1x
|
||||||
|
* Alolan, Galarian, and Paldean mons get 0.5x
|
||||||
|
* Hisui mons get 0.125x
|
||||||
|
*
|
||||||
|
* The total weight is also being calculated EACH time there is an egg hatch instead of being generated once
|
||||||
|
* and being the same each time
|
||||||
|
*/
|
||||||
let totalWeight = 0;
|
let totalWeight = 0;
|
||||||
const speciesWeights = [];
|
const speciesWeights = [];
|
||||||
for (const speciesId of speciesPool) {
|
for (const speciesId of speciesPool) {
|
||||||
@ -464,6 +541,16 @@ export class EggHatchPhase extends Phase {
|
|||||||
ret = this.scene.addPlayerPokemon(pokemonSpecies, 1, undefined, undefined, undefined, false);
|
ret = this.scene.addPlayerPokemon(pokemonSpecies, 1, undefined, undefined, undefined, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non Shiny gacha Pokemon have a 1/128 chance of being shiny
|
||||||
|
* Shiny gacha Pokemon have a 1/64 chance of being shiny
|
||||||
|
* IVs are rolled twice and the higher of each stat's IV is taken
|
||||||
|
* The egg move gacha doubles the rate of rare egg moves but the base rates are
|
||||||
|
* Common: 1/48
|
||||||
|
* Rare: 1/24
|
||||||
|
* Epic: 1/12
|
||||||
|
* Legendary: 1/6
|
||||||
|
*/
|
||||||
ret.trySetShiny(this.egg.gachaType === GachaType.SHINY ? 1024 : 512);
|
ret.trySetShiny(this.egg.gachaType === GachaType.SHINY ? 1024 : 512);
|
||||||
ret.variant = ret.shiny ? ret.generateVariant() : 0;
|
ret.variant = ret.shiny ? ret.generateVariant() : 0;
|
||||||
|
|
||||||
|
77
src/field/arena-events.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { ArenaTagSide } from "#app/data/arena-tag.js";
|
||||||
|
import { ArenaTagType } from "#app/data/enums/arena-tag-type.js";
|
||||||
|
import { TerrainType } from "#app/data/terrain.js";
|
||||||
|
import { WeatherType } from "#app/data/weather.js";
|
||||||
|
|
||||||
|
/** Alias for all {@linkcode ArenaEvent} type strings */
|
||||||
|
export enum ArenaEventType {
|
||||||
|
/** Triggers when a {@linkcode WeatherType} is added, overlapped, or removed */
|
||||||
|
WEATHER_CHANGED = "onWeatherChanged",
|
||||||
|
/** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
|
||||||
|
TERRAIN_CHANGED = "onTerrainChanged",
|
||||||
|
|
||||||
|
/** Triggers when a {@linkcode ArenaTagType} is added or removed */
|
||||||
|
TAG_CHANGED = "onTagChanged",
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base container class for all {@linkcode ArenaEventType} events
|
||||||
|
* @extends Event
|
||||||
|
*/
|
||||||
|
export class ArenaEvent extends Event {
|
||||||
|
/** The total duration of the {@linkcode ArenaEventType} */
|
||||||
|
public duration: number;
|
||||||
|
constructor(eventType: ArenaEventType, duration: number) {
|
||||||
|
super(eventType);
|
||||||
|
|
||||||
|
this.duration = duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Container class for {@linkcode ArenaEventType.WEATHER_CHANGED} events
|
||||||
|
* @extends ArenaEvent
|
||||||
|
*/
|
||||||
|
export class WeatherChangedEvent extends ArenaEvent {
|
||||||
|
/** The {@linkcode WeatherType} being overridden */
|
||||||
|
public oldWeatherType: WeatherType;
|
||||||
|
/** The {@linkcode WeatherType} being set */
|
||||||
|
public newWeatherType: WeatherType;
|
||||||
|
constructor(oldWeatherType: WeatherType, newWeatherType: WeatherType, duration: number) {
|
||||||
|
super(ArenaEventType.WEATHER_CHANGED, duration);
|
||||||
|
|
||||||
|
this.oldWeatherType = oldWeatherType;
|
||||||
|
this.newWeatherType = newWeatherType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Container class for {@linkcode ArenaEventType.TERRAIN_CHANGED} events
|
||||||
|
* @extends ArenaEvent
|
||||||
|
*/
|
||||||
|
export class TerrainChangedEvent extends ArenaEvent {
|
||||||
|
/** The {@linkcode TerrainType} being overridden */
|
||||||
|
public oldTerrainType: TerrainType;
|
||||||
|
/** The {@linkcode TerrainType} being set */
|
||||||
|
public newTerrainType: TerrainType;
|
||||||
|
constructor(oldTerrainType: TerrainType, newTerrainType: TerrainType, duration: number) {
|
||||||
|
super(ArenaEventType.TERRAIN_CHANGED, duration);
|
||||||
|
|
||||||
|
this.oldTerrainType = oldTerrainType;
|
||||||
|
this.newTerrainType = newTerrainType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Container class for {@linkcode ArenaEventType.TAG_CHANGED} events
|
||||||
|
* @extends ArenaEvent
|
||||||
|
*/
|
||||||
|
export class TagChangedEvent extends ArenaEvent {
|
||||||
|
/** The {@linkcode ArenaTagType} being set */
|
||||||
|
public arenaTagType: ArenaTagType;
|
||||||
|
/** The {@linkcode ArenaTagSide} the tag is being placed on */
|
||||||
|
public arenaTagSide: ArenaTagSide;
|
||||||
|
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
|
||||||
|
super(ArenaEventType.TAG_CHANGED, duration);
|
||||||
|
|
||||||
|
this.arenaTagType = arenaTagType;
|
||||||
|
this.arenaTagSide = arenaTagSide;
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ import { Terrain, TerrainType } from "../data/terrain";
|
|||||||
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
||||||
import Pokemon from "./pokemon";
|
import Pokemon from "./pokemon";
|
||||||
import * as Overrides from "../overrides";
|
import * as Overrides from "../overrides";
|
||||||
|
import { WeatherChangedEvent, TerrainChangedEvent, TagChangedEvent } from "./arena-events";
|
||||||
|
|
||||||
export class Arena {
|
export class Arena {
|
||||||
public scene: BattleScene;
|
public scene: BattleScene;
|
||||||
@ -34,6 +35,8 @@ export class Arena {
|
|||||||
private pokemonPool: PokemonPools;
|
private pokemonPool: PokemonPools;
|
||||||
private trainerPool: BiomeTierTrainerPools;
|
private trainerPool: BiomeTierTrainerPools;
|
||||||
|
|
||||||
|
public readonly eventTarget: EventTarget = new EventTarget();
|
||||||
|
|
||||||
constructor(scene: BattleScene, biome: Biome, bgm: string) {
|
constructor(scene: BattleScene, biome: Biome, bgm: string) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.biomeType = biome;
|
this.biomeType = biome;
|
||||||
@ -300,6 +303,7 @@ export class Arena {
|
|||||||
const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
|
const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
|
||||||
|
|
||||||
this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null;
|
this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null;
|
||||||
|
this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft));
|
||||||
|
|
||||||
if (this.weather) {
|
if (this.weather) {
|
||||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
|
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
|
||||||
@ -324,6 +328,7 @@ export class Arena {
|
|||||||
const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE;
|
const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE;
|
||||||
|
|
||||||
this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null;
|
this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null;
|
||||||
|
this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType,this.terrain?.terrainType, this.terrain?.turnsLeft));
|
||||||
|
|
||||||
if (this.terrain) {
|
if (this.terrain) {
|
||||||
if (!ignoreAnim) {
|
if (!ignoreAnim) {
|
||||||
@ -545,6 +550,8 @@ export class Arena {
|
|||||||
this.tags.push(newTag);
|
this.tags.push(newTag);
|
||||||
newTag.onAdd(this);
|
newTag.onAdd(this);
|
||||||
|
|
||||||
|
this.eventTarget.dispatchEvent(new TagChangedEvent(newTag.tagType, newTag.side, newTag.turnCount));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -978,7 +978,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
if (this.isOnField() && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) {
|
if (this.isOnField() && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) {
|
||||||
const suppressed = new Utils.BooleanHolder(false);
|
const suppressed = new Utils.BooleanHolder(false);
|
||||||
this.scene.getField(true).map(p => {
|
this.scene.getField(true).filter(p => p !== this).map(p => {
|
||||||
if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) {
|
if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) {
|
||||||
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, suppressed, [ability]));
|
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, suppressed, [ability]));
|
||||||
}
|
}
|
||||||
@ -1058,7 +1058,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAttackMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier {
|
getAttackMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier {
|
||||||
const typeless = !!move.getMove().getAttrs(TypelessAttr).length;
|
const typeless = move.getMove().hasAttr(TypelessAttr);
|
||||||
const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.getMove().type, source));
|
const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.getMove().type, source));
|
||||||
const cancelled = new Utils.BooleanHolder(false);
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
if (!typeless) {
|
if (!typeless) {
|
||||||
@ -1424,19 +1424,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.isBoss()) { // Bosses never get self ko moves
|
if (this.isBoss()) { // Bosses never get self ko moves
|
||||||
movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(SacrificialAttr).length);
|
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr));
|
||||||
}
|
}
|
||||||
movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(SacrificialAttrOnHit).length);
|
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit));
|
||||||
if (this.hasTrainer()) {
|
if (this.hasTrainer()) {
|
||||||
// Trainers never get OHKO moves
|
// Trainers never get OHKO moves
|
||||||
movePool = movePool.filter(m => !allMoves[m[0]].getAttrs(OneHitKOAttr).length);
|
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(OneHitKOAttr));
|
||||||
// Half the weight of self KO moves
|
// Half the weight of self KO moves
|
||||||
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(SacrificialAttr).length ? 0.5 : 1)]);
|
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(SacrificialAttr) ? 0.5 : 1)]);
|
||||||
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(SacrificialAttrOnHit).length ? 0.5 : 1)]);
|
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(SacrificialAttrOnHit) ? 0.5 : 1)]);
|
||||||
// Trainers get a weight bump to stat buffing moves
|
// Trainers get a weight bump to stat buffing moves
|
||||||
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatChangeAttr).some(a => (a as StatChangeAttr).levels > 1 && (a as StatChangeAttr).selfTarget) ? 1.25 : 1)]);
|
movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].getAttrs(StatChangeAttr).some(a => a.levels > 1 && a.selfTarget) ? 1.25 : 1)]);
|
||||||
// Trainers get a weight decrease to multiturn moves
|
// Trainers get a weight decrease to multiturn moves
|
||||||
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].getAttrs(ChargeAttr).length || !!allMoves[m[0]].getAttrs(RechargeAttr).length ? 0.7 : 1)]);
|
movePool = movePool.map(m => [m[0], m[1] * (!!allMoves[m[0]].hasAttr(ChargeAttr) || !!allMoves[m[0]].hasAttr(RechargeAttr) ? 0.7 : 1)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weight towards higher power moves, by reducing the power of moves below the highest power.
|
// Weight towards higher power moves, by reducing the power of moves below the highest power.
|
||||||
@ -1627,8 +1627,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const types = this.getTypes(true, true);
|
const types = this.getTypes(true, true);
|
||||||
|
|
||||||
const cancelled = new Utils.BooleanHolder(false);
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
const typeless = !!move.getAttrs(TypelessAttr).length;
|
const typeless = move.hasAttr(TypelessAttr);
|
||||||
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes((attr as StatusMoveTypeImmunityAttr).immuneType)))
|
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType)))
|
||||||
? this.getAttackTypeEffectiveness(type, source)
|
? this.getAttackTypeEffectiveness(type, source)
|
||||||
: 1);
|
: 1);
|
||||||
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
|
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
|
||||||
@ -1654,7 +1654,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||||
const power = new Utils.NumberHolder(move.power);
|
const power = new Utils.NumberHolder(move.power);
|
||||||
const sourceTeraType = source.getTeraType();
|
const sourceTeraType = source.getTeraType();
|
||||||
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === type && power.value < 60 && move.priority <= 0 && !move.getAttrs(MultiHitAttr).length && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === type && power.value < 60 && move.priority <= 0 && !move.hasAttr(MultiHitAttr) && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
||||||
power.value = 60;
|
power.value = 60;
|
||||||
}
|
}
|
||||||
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power);
|
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power);
|
||||||
@ -1756,7 +1756,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (!isTypeImmune) {
|
if (!isTypeImmune) {
|
||||||
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier.value);
|
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier.value);
|
||||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
|
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
|
||||||
if (!move.getAttrs(BypassBurnDamageReductionAttr).length) {
|
if (!move.hasAttr(BypassBurnDamageReductionAttr)) {
|
||||||
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
|
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
|
||||||
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled);
|
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled);
|
||||||
if (!burnDamageReductionCancelled.value) {
|
if (!burnDamageReductionCancelled.value) {
|
||||||
@ -1768,12 +1768,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, battlerMove, damage);
|
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, battlerMove, damage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
|
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
|
||||||
* The target has a {@link BattlerTagType} that this move interacts with
|
* The target has a {@link BattlerTagType} that this move interacts with
|
||||||
* AND
|
* AND
|
||||||
* The move doubles damage when used against that tag
|
* The move doubles damage when used against that tag
|
||||||
* */
|
*/
|
||||||
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
move.getAttrs(HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
||||||
if (this.getTag(hta.tagType)) {
|
if (this.getTag(hta.tagType)) {
|
||||||
damage.value *= 2;
|
damage.value *= 2;
|
||||||
}
|
}
|
||||||
@ -3454,7 +3454,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
if (!sortedBenefitScores.length) {
|
if (!sortedBenefitScores.length) {
|
||||||
// Set target to BattlerIndex.ATTACKER when using a counter move
|
// Set target to BattlerIndex.ATTACKER when using a counter move
|
||||||
// This is the same as when the player does so
|
// This is the same as when the player does so
|
||||||
if (!!move.findAttr(attr => attr instanceof CounterDamageAttr)) {
|
if (move.hasAttr(CounterDamageAttr)) {
|
||||||
return [BattlerIndex.ATTACKER];
|
return [BattlerIndex.ATTACKER];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,6 +367,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "Ich werde für das nächste Rennen tunen."
|
1: "Ich werde für das nächste Rennen tunen."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "Meine Expertise in Bezug auf Gesteins-Pokémon wird dich besiegen! Komm schon!",
|
1: "Meine Expertise in Bezug auf Gesteins-Pokémon wird dich besiegen! Komm schon!",
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "Bahnangestellter",
|
"depot_agent": "Bahnangestellter",
|
||||||
"doctor": "Arzt",
|
"doctor": "Arzt",
|
||||||
"doctor_female": "Ärztin",
|
"doctor_female": "Ärztin",
|
||||||
|
"firebreather": "Feuerspucker",
|
||||||
"fisherman": "Angler",
|
"fisherman": "Angler",
|
||||||
"fisherman_female": "Angler", // Seems to be the same in german but exists in other languages like italian
|
"fisherman_female": "Angler", // Seems to be the same in german but exists in other languages like italian
|
||||||
"gentleman": "Gentleman",
|
"gentleman": "Gentleman",
|
||||||
|
@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "I'll tune up for the next race."
|
1: "I'll tune up for the next race."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
||||||
|
@ -209,6 +209,9 @@ export const modifierType: ModifierTypeTranslationEntries = {
|
|||||||
"LEFTOVERS": { name: "Leftovers", description: "Heals 1/16 of a Pokémon's maximum HP every turn" },
|
"LEFTOVERS": { name: "Leftovers", description: "Heals 1/16 of a Pokémon's maximum HP every turn" },
|
||||||
"SHELL_BELL": { name: "Shell Bell", description: "Heals 1/8 of a Pokémon's dealt damage" },
|
"SHELL_BELL": { name: "Shell Bell", description: "Heals 1/8 of a Pokémon's dealt damage" },
|
||||||
|
|
||||||
|
"TOXIC_ORB": { name: "Toxic Orb", description: "Badly poisons its holder at the end of the turn if they do not have a status condition already" },
|
||||||
|
"FLAME_ORB": { name: "Flame Orb", description: "Burns its holder at the end of the turn if they do not have a status condition already" },
|
||||||
|
|
||||||
"BATON": { name: "Baton", description: "Allows passing along effects when switching Pokémon, which also bypasses traps" },
|
"BATON": { name: "Baton", description: "Allows passing along effects when switching Pokémon, which also bypasses traps" },
|
||||||
|
|
||||||
"SHINY_CHARM": { name: "Shiny Charm", description: "Dramatically increases the chance of a wild Pokémon being Shiny" },
|
"SHINY_CHARM": { name: "Shiny Charm", description: "Dramatically increases the chance of a wild Pokémon being Shiny" },
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "Depot Agent",
|
"depot_agent": "Depot Agent",
|
||||||
"doctor": "Doctor",
|
"doctor": "Doctor",
|
||||||
"doctor_female": "Doctor",
|
"doctor_female": "Doctor",
|
||||||
|
"firebreather": "Firebreather",
|
||||||
"fisherman": "Fisherman",
|
"fisherman": "Fisherman",
|
||||||
"fisherman_female": "Fisherman",
|
"fisherman_female": "Fisherman",
|
||||||
"gentleman": "Gentleman",
|
"gentleman": "Gentleman",
|
||||||
|
@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "I'll tune up for the next race."
|
1: "I'll tune up for the next race."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "Ferroviario",
|
"depot_agent": "Ferroviario",
|
||||||
"doctor": "Enfermero",
|
"doctor": "Enfermero",
|
||||||
"doctor_female": "Enfermera",
|
"doctor_female": "Enfermera",
|
||||||
|
"firebreather": "Comefuegos",
|
||||||
"fisherman": "Pescador",
|
"fisherman": "Pescador",
|
||||||
"fisherman_female": "Pescadora",
|
"fisherman_female": "Pescadora",
|
||||||
"gentleman": "Aristócrata",
|
"gentleman": "Aristócrata",
|
||||||
|
@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "I'll tune up for the next race."
|
1: "I'll tune up for the next race."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "Cheminot",
|
"depot_agent": "Cheminot",
|
||||||
"doctor": "Docteur",
|
"doctor": "Docteur",
|
||||||
"doctor_female": "Docteure",
|
"doctor_female": "Docteure",
|
||||||
|
"firebreather": "Firebreather",
|
||||||
"fisherman": "Pêcheur",
|
"fisherman": "Pêcheur",
|
||||||
"fisherman_female": "Pêcheuse",
|
"fisherman_female": "Pêcheuse",
|
||||||
"gentleman": "Gentleman",
|
"gentleman": "Gentleman",
|
||||||
|
@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "I'll tune up for the next race."
|
1: "I'll tune up for the next race."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "Depot Agent",
|
"depot_agent": "Depot Agent",
|
||||||
"doctor": "Doctor",
|
"doctor": "Doctor",
|
||||||
"doctor_female": "Doctor",
|
"doctor_female": "Doctor",
|
||||||
|
"firebreather": "Firebreather",
|
||||||
"fisherman": "Fisherman",
|
"fisherman": "Fisherman",
|
||||||
"fisherman_female": "Fisherman",
|
"fisherman_female": "Fisherman",
|
||||||
"gentleman": "Gentleman",
|
"gentleman": "Gentleman",
|
||||||
|
@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "I'll tune up for the next race."
|
1: "I'll tune up for the next race."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "역무원",
|
"depot_agent": "역무원",
|
||||||
"doctor": "의사",
|
"doctor": "의사",
|
||||||
"doctor_female": "간호사", // doctor_f.png 파일이 간호사
|
"doctor_female": "간호사", // doctor_f.png 파일이 간호사
|
||||||
|
"firebreather": "Firebreather",
|
||||||
"fisherman": "낚시꾼",
|
"fisherman": "낚시꾼",
|
||||||
"fisherman_female": "낚시꾼",
|
"fisherman_female": "낚시꾼",
|
||||||
"gentleman": "신사",
|
"gentleman": "신사",
|
||||||
|
@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "I'll tune up for the next race."
|
1: "I'll tune up for the next race."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "Ferroviário",
|
"depot_agent": "Ferroviário",
|
||||||
"doctor": "Doutor",
|
"doctor": "Doutor",
|
||||||
"doctor_female": "Doutora",
|
"doctor_female": "Doutora",
|
||||||
|
"firebreather": "Firebreather",
|
||||||
"fishermen": "Pescador",
|
"fishermen": "Pescador",
|
||||||
"fishermen_female": "Pescadora",
|
"fishermen_female": "Pescadora",
|
||||||
"gentleman": "Cavalheiro",
|
"gentleman": "Cavalheiro",
|
||||||
|
@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "I'll tune up for the next race."
|
1: "I'll tune up for the next race."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "铁路员工",
|
"depot_agent": "铁路员工",
|
||||||
"doctor": "医生",
|
"doctor": "医生",
|
||||||
"doctor_female": "医生",
|
"doctor_female": "医生",
|
||||||
|
"firebreather": "Firebreather",
|
||||||
"fisherman": "垂钓者",
|
"fisherman": "垂钓者",
|
||||||
"fisherman_female": "垂钓者",
|
"fisherman_female": "垂钓者",
|
||||||
"gentleman": "绅士",
|
"gentleman": "绅士",
|
||||||
|
@ -359,6 +359,18 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
|||||||
1: "I'll tune up for the next race."
|
1: "I'll tune up for the next race."
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"firebreather": {
|
||||||
|
"encounter": {
|
||||||
|
1: "My flames shall devour you!",
|
||||||
|
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||||
|
3: "Step right up and take a look!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
1: "I burned down to ashes...",
|
||||||
|
2: "Yow! That's hot!",
|
||||||
|
3: "Ow! I scorched the tip of my nose!"
|
||||||
|
},
|
||||||
|
},
|
||||||
"brock": {
|
"brock": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
1: "My expertise on Rock-type Pokémon will take you down! Come on!",
|
||||||
|
@ -48,6 +48,7 @@ export const trainerClasses: SimpleTranslationEntries = {
|
|||||||
"depot_agent": "鐵路員工",
|
"depot_agent": "鐵路員工",
|
||||||
"doctor": "醫生",
|
"doctor": "醫生",
|
||||||
"doctor_female": "醫生",
|
"doctor_female": "醫生",
|
||||||
|
"firebreather": "Firebreather",
|
||||||
"fisherman": "垂釣者",
|
"fisherman": "垂釣者",
|
||||||
"fisherman_female": "垂釣者",
|
"fisherman_female": "垂釣者",
|
||||||
"gentleman": "紳士",
|
"gentleman": "紳士",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import * as Modifiers from "./modifier";
|
import * as Modifiers from "./modifier";
|
||||||
import { AttackMove, allMoves } from "../data/move";
|
import { AttackMove, allMoves } from "../data/move";
|
||||||
import { Moves } from "../data/enums/moves";
|
import { Moves } from "../data/enums/moves";
|
||||||
|
import { Abilities } from "../data/enums/abilities";
|
||||||
import { PokeballType, getPokeballCatchMultiplier, getPokeballName } from "../data/pokeball";
|
import { PokeballType, getPokeballCatchMultiplier, getPokeballName } from "../data/pokeball";
|
||||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "../field/pokemon";
|
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "../field/pokemon";
|
||||||
import { EvolutionItem, pokemonEvolutions } from "../data/pokemon-evolutions";
|
import { EvolutionItem, pokemonEvolutions } from "../data/pokemon-evolutions";
|
||||||
@ -756,14 +757,23 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents form changing items
|
||||||
|
*/
|
||||||
export class FormChangeItemModifierType extends PokemonModifierType implements GeneratedPersistentModifierType {
|
export class FormChangeItemModifierType extends PokemonModifierType implements GeneratedPersistentModifierType {
|
||||||
public formChangeItem: FormChangeItem;
|
public formChangeItem: FormChangeItem;
|
||||||
|
|
||||||
constructor(formChangeItem: FormChangeItem) {
|
constructor(formChangeItem: FormChangeItem) {
|
||||||
super("", FormChangeItem[formChangeItem].toLowerCase(), (_type, args) => new Modifiers.PokemonFormChangeItemModifier(this, (args[0] as PlayerPokemon).id, formChangeItem, true),
|
super("", FormChangeItem[formChangeItem].toLowerCase(), (_type, args) => new Modifiers.PokemonFormChangeItemModifier(this, (args[0] as PlayerPokemon).id, formChangeItem, true),
|
||||||
(pokemon: PlayerPokemon) => {
|
(pokemon: PlayerPokemon) => {
|
||||||
if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) && !!pokemonFormChanges[pokemon.species.speciesId].find(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger)
|
// Make sure the Pokemon has alternate forms
|
||||||
&& (fc.trigger as SpeciesFormChangeItemTrigger).item === this.formChangeItem)) {
|
if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId)
|
||||||
|
// Get all form changes for this species with an item trigger, including any compound triggers
|
||||||
|
&& pokemonFormChanges[pokemon.species.speciesId].filter(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger))
|
||||||
|
// Returns true if any form changes match this item
|
||||||
|
.map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
|
||||||
|
.flat().flatMap(fc => fc.item).includes(this.formChangeItem)
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1182,6 +1192,9 @@ export const modifierTypes = {
|
|||||||
LEFTOVERS: () => new PokemonHeldItemModifierType("modifierType:ModifierType.LEFTOVERS", "leftovers", (type, args) => new Modifiers.TurnHealModifier(type, (args[0] as Pokemon).id)),
|
LEFTOVERS: () => new PokemonHeldItemModifierType("modifierType:ModifierType.LEFTOVERS", "leftovers", (type, args) => new Modifiers.TurnHealModifier(type, (args[0] as Pokemon).id)),
|
||||||
SHELL_BELL: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SHELL_BELL", "shell_bell", (type, args) => new Modifiers.HitHealModifier(type, (args[0] as Pokemon).id)),
|
SHELL_BELL: () => new PokemonHeldItemModifierType("modifierType:ModifierType.SHELL_BELL", "shell_bell", (type, args) => new Modifiers.HitHealModifier(type, (args[0] as Pokemon).id)),
|
||||||
|
|
||||||
|
TOXIC_ORB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.TOXIC_ORB", "toxic_orb", (type, args) => new Modifiers.TurnStatusEffectModifier(type, (args[0] as Pokemon).id)),
|
||||||
|
FLAME_ORB: () => new PokemonHeldItemModifierType("modifierType:ModifierType.FLAME_ORB", "flame_orb", (type, args) => new Modifiers.TurnStatusEffectModifier(type, (args[0] as Pokemon).id)),
|
||||||
|
|
||||||
BATON: () => new PokemonHeldItemModifierType("modifierType:ModifierType.BATON", "stick", (type, args) => new Modifiers.SwitchEffectTransferModifier(type, (args[0] as Pokemon).id)),
|
BATON: () => new PokemonHeldItemModifierType("modifierType:ModifierType.BATON", "stick", (type, args) => new Modifiers.SwitchEffectTransferModifier(type, (args[0] as Pokemon).id)),
|
||||||
|
|
||||||
SHINY_CHARM: () => new ModifierType("modifierType:ModifierType.SHINY_CHARM", "shiny_charm", (type, _args) => new Modifiers.ShinyRateBoosterModifier(type)),
|
SHINY_CHARM: () => new ModifierType("modifierType:ModifierType.SHINY_CHARM", "shiny_charm", (type, _args) => new Modifiers.ShinyRateBoosterModifier(type)),
|
||||||
@ -1247,7 +1260,12 @@ const modifierPool: ModifierPool = {
|
|||||||
[ModifierTier.GREAT]: [
|
[ModifierTier.GREAT]: [
|
||||||
new WeightedModifierType(modifierTypes.GREAT_BALL, 6),
|
new WeightedModifierType(modifierTypes.GREAT_BALL, 6),
|
||||||
new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => {
|
new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => {
|
||||||
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status).length, 3);
|
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => {
|
||||||
|
if (i instanceof Modifiers.TurnStatusEffectModifier) {
|
||||||
|
return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status.effect;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})).length, 3);
|
||||||
return statusEffectPartyMemberCount * 6;
|
return statusEffectPartyMemberCount * 6;
|
||||||
}, 18),
|
}, 18),
|
||||||
new WeightedModifierType(modifierTypes.REVIVE, (party: Pokemon[]) => {
|
new WeightedModifierType(modifierTypes.REVIVE, (party: Pokemon[]) => {
|
||||||
@ -1270,7 +1288,12 @@ const modifierPool: ModifierPool = {
|
|||||||
return thresholdPartyMemberCount;
|
return thresholdPartyMemberCount;
|
||||||
}, 3),
|
}, 3),
|
||||||
new WeightedModifierType(modifierTypes.FULL_RESTORE, (party: Pokemon[]) => {
|
new WeightedModifierType(modifierTypes.FULL_RESTORE, (party: Pokemon[]) => {
|
||||||
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status).length, 3);
|
const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => {
|
||||||
|
if (i instanceof Modifiers.TurnStatusEffectModifier) {
|
||||||
|
return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status.effect;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})).length, 3);
|
||||||
const thresholdPartyMemberCount = Math.floor((Math.min(party.filter(p => (p.getInverseHp() >= 150 || p.getHpRatio() <= 0.5) && !p.isFainted()).length, 3) + statusEffectPartyMemberCount) / 2);
|
const thresholdPartyMemberCount = Math.floor((Math.min(party.filter(p => (p.getInverseHp() >= 150 || p.getHpRatio() <= 0.5) && !p.isFainted()).length, 3) + statusEffectPartyMemberCount) / 2);
|
||||||
return thresholdPartyMemberCount;
|
return thresholdPartyMemberCount;
|
||||||
}, 3),
|
}, 3),
|
||||||
@ -1312,6 +1335,40 @@ const modifierPool: ModifierPool = {
|
|||||||
new WeightedModifierType(modifierTypes.MINT, 4),
|
new WeightedModifierType(modifierTypes.MINT, 4),
|
||||||
new WeightedModifierType(modifierTypes.RARE_EVOLUTION_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * 4, 32), 32),
|
new WeightedModifierType(modifierTypes.RARE_EVOLUTION_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * 4, 32), 32),
|
||||||
new WeightedModifierType(modifierTypes.AMULET_COIN, 3),
|
new WeightedModifierType(modifierTypes.AMULET_COIN, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.TOXIC_ORB, (party: Pokemon[]) => {
|
||||||
|
let weight = 0;
|
||||||
|
const filteredParty = party.filter(p => (p.status?.effect === StatusEffect.TOXIC || p.canSetStatus(StatusEffect.TOXIC, true, true))
|
||||||
|
&& !p.hasAbility(Abilities.FLARE_BOOST)
|
||||||
|
&& !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier));
|
||||||
|
if (filteredParty.some(p => p.hasAbility(Abilities.TOXIC_BOOST) || p.hasAbility(Abilities.POISON_HEAL))) {
|
||||||
|
weight = 4;
|
||||||
|
} else if (filteredParty.some(p => p.hasAbility(Abilities.GUTS) || p.hasAbility(Abilities.QUICK_FEET) || p.hasAbility(Abilities.MARVEL_SCALE))) {
|
||||||
|
weight = 2;
|
||||||
|
} else {
|
||||||
|
const moveList = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT];
|
||||||
|
if (filteredParty.some(p => p.getMoveset().some(m => moveList.includes(m.moveId)))) {
|
||||||
|
weight = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * weight, 8 * weight);
|
||||||
|
}, 32),
|
||||||
|
new WeightedModifierType(modifierTypes.FLAME_ORB, (party: Pokemon[]) => {
|
||||||
|
let weight = 0;
|
||||||
|
const filteredParty = party.filter(p => (p.status?.effect === StatusEffect.BURN || p.canSetStatus(StatusEffect.BURN, true, true))
|
||||||
|
&& !p.hasAbility(Abilities.TOXIC_BOOST) && !p.hasAbility(Abilities.POISON_HEAL)
|
||||||
|
&& !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier));
|
||||||
|
if (filteredParty.some(p => p.hasAbility(Abilities.FLARE_BOOST))) {
|
||||||
|
weight = 4;
|
||||||
|
} else if (filteredParty.some(p => p.hasAbility(Abilities.GUTS) || p.hasAbility(Abilities.QUICK_FEET) || p.hasAbility(Abilities.MARVEL_SCALE))) {
|
||||||
|
weight = 2;
|
||||||
|
} else {
|
||||||
|
const moveList = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT];
|
||||||
|
if (filteredParty.some(p => p.getMoveset().some(m => moveList.includes(m.moveId)))) {
|
||||||
|
weight = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * weight, 8 * weight);
|
||||||
|
}, 32),
|
||||||
new WeightedModifierType(modifierTypes.REVIVER_SEED, 4),
|
new WeightedModifierType(modifierTypes.REVIVER_SEED, 4),
|
||||||
new WeightedModifierType(modifierTypes.CANDY_JAR, 5),
|
new WeightedModifierType(modifierTypes.CANDY_JAR, 5),
|
||||||
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10),
|
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10),
|
||||||
|
@ -851,6 +851,66 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifier used for held items, namely Toxic Orb and Flame Orb, that apply a
|
||||||
|
* set {@linkcode StatusEffect} at the end of a turn.
|
||||||
|
* @extends PokemonHeldItemModifier
|
||||||
|
* @see {@linkcode apply}
|
||||||
|
*/
|
||||||
|
export class TurnStatusEffectModifier extends PokemonHeldItemModifier {
|
||||||
|
/** The status effect to be applied by the held item */
|
||||||
|
private effect: StatusEffect;
|
||||||
|
|
||||||
|
constructor (type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
||||||
|
super(type, pokemonId, stackCount);
|
||||||
|
|
||||||
|
switch (type.id) {
|
||||||
|
case "TOXIC_ORB":
|
||||||
|
this.effect = StatusEffect.TOXIC;
|
||||||
|
break;
|
||||||
|
case "FLAME_ORB":
|
||||||
|
this.effect = StatusEffect.BURN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if {@linkcode modifier} is an instance of this class,
|
||||||
|
* intentionally ignoring potentially different {@linkcode effect}s
|
||||||
|
* to prevent held item stockpiling since the item obtained first
|
||||||
|
* would be the only item able to {@linkcode apply} successfully.
|
||||||
|
* @override
|
||||||
|
* @param modifier {@linkcode Modifier} being type tested
|
||||||
|
* @return true if {@linkcode modifier} is an instance of
|
||||||
|
* TurnStatusEffectModifier, false otherwise
|
||||||
|
*/
|
||||||
|
matchType(modifier: Modifier): boolean {
|
||||||
|
return modifier instanceof TurnStatusEffectModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new TurnStatusEffectModifier(this.type, this.pokemonId, this.stackCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to inflicts the holder with the associated {@linkcode StatusEffect}.
|
||||||
|
* @param args [0] {@linkcode Pokemon} that holds the held item
|
||||||
|
* @returns true if the status effect was applied successfully, false if
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
apply(args: any[]): boolean {
|
||||||
|
return (args[0] as Pokemon).trySetStatus(this.effect, true, undefined, undefined, this.type.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMaxHeldItemCount(pokemon: Pokemon): integer {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusEffect(): StatusEffect {
|
||||||
|
return this.effect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class HitHealModifier extends PokemonHeldItemModifier {
|
export class HitHealModifier extends PokemonHeldItemModifier {
|
||||||
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
@ -14,6 +14,7 @@ import { PokeballType } from "./data/pokeball";
|
|||||||
import {TimeOfDay} from "#app/data/enums/time-of-day";
|
import {TimeOfDay} from "#app/data/enums/time-of-day";
|
||||||
import { Gender } from "./data/gender";
|
import { Gender } from "./data/gender";
|
||||||
import { StatusEffect } from "./data/status-effect";
|
import { StatusEffect } from "./data/status-effect";
|
||||||
|
import { modifierTypes } from "./modifier/modifier-type";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides for testing different in game situations
|
* Overrides for testing different in game situations
|
||||||
@ -100,7 +101,7 @@ export const OPP_VARIANT_OVERRIDE: Variant = 0;
|
|||||||
* - BerryType is for BERRY
|
* - BerryType is for BERRY
|
||||||
*/
|
*/
|
||||||
interface ModifierOverride {
|
interface ModifierOverride {
|
||||||
name: string,
|
name: keyof typeof modifierTypes & string,
|
||||||
count?: integer
|
count?: integer
|
||||||
type?: TempBattleStat|Stat|Nature|Type|BerryType
|
type?: TempBattleStat|Stat|Nature|Type|BerryType
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMov
|
|||||||
import { Mode } from "./ui/ui";
|
import { Mode } from "./ui/ui";
|
||||||
import { Command } from "./ui/command-ui-handler";
|
import { Command } from "./ui/command-ui-handler";
|
||||||
import { Stat } from "./data/pokemon-stat";
|
import { Stat } from "./data/pokemon-stat";
|
||||||
import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, PokemonMoveAccuracyBoosterModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier } from "./modifier/modifier";
|
import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, MoneyMultiplierModifier, MoneyInterestModifier, IvScannerModifier, LapsingPokemonHeldItemModifier, PokemonMultiHitModifier, PokemonMoveAccuracyBoosterModifier, overrideModifiers, overrideHeldItems, BypassSpeedChanceModifier, TurnStatusEffectModifier } from "./modifier/modifier";
|
||||||
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
||||||
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
|
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
|
||||||
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims";
|
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims";
|
||||||
@ -1553,7 +1553,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
const lastUsedMove = moveId ? allMoves[moveId] : undefined;
|
const lastUsedMove = moveId ? allMoves[moveId] : undefined;
|
||||||
|
|
||||||
const currentCommand = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command;
|
const currentCommand = pokemon.scene.currentBattle.turnCommands[this.fieldIndex]?.command;
|
||||||
const lastPokemonIsForceSwitchedAndNotFainted = !!lastUsedMove?.findAttr(attr => attr instanceof ForceSwitchOutAttr) && !this.lastPokemon.isFainted();
|
const lastPokemonIsForceSwitchedAndNotFainted = lastUsedMove?.hasAttr(ForceSwitchOutAttr) && !this.lastPokemon.isFainted();
|
||||||
|
|
||||||
// Compensate for turn spent summoning
|
// Compensate for turn spent summoning
|
||||||
// Or compensate for force switch move if switched out pokemon is not fainted
|
// Or compensate for force switch move if switched out pokemon is not fainted
|
||||||
@ -2289,6 +2289,8 @@ export class TurnEndPhase extends FieldPhase {
|
|||||||
|
|
||||||
applyPostTurnAbAttrs(PostTurnAbAttr, pokemon);
|
applyPostTurnAbAttrs(PostTurnAbAttr, pokemon);
|
||||||
|
|
||||||
|
this.scene.applyModifiers(TurnStatusEffectModifier, pokemon.isPlayer(), pokemon);
|
||||||
|
|
||||||
this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon);
|
this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon);
|
||||||
|
|
||||||
pokemon.battleSummonData.turnCount++;
|
pokemon.battleSummonData.turnCount++;
|
||||||
@ -2457,9 +2459,9 @@ export class MovePhase extends BattlePhase {
|
|||||||
const oldTarget = moveTarget.value;
|
const oldTarget = moveTarget.value;
|
||||||
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget));
|
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget));
|
||||||
//Check if this move is immune to being redirected, and restore its target to the intended target if it is.
|
//Check if this move is immune to being redirected, and restore its target to the intended target if it is.
|
||||||
if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().getAttrs(BypassRedirectAttr).length)) {
|
if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().hasAttr(BypassRedirectAttr))) {
|
||||||
//If an ability prevented this move from being redirected, display its ability pop up.
|
//If an ability prevented this move from being redirected, display its ability pop up.
|
||||||
if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().getAttrs(BypassRedirectAttr).length) && oldTarget !== moveTarget.value) {
|
if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().hasAttr(BypassRedirectAttr)) && oldTarget !== moveTarget.value) {
|
||||||
this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr)));
|
this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr)));
|
||||||
}
|
}
|
||||||
moveTarget.value = oldTarget;
|
moveTarget.value = oldTarget;
|
||||||
@ -2549,7 +2551,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed));
|
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allMoves[this.move.moveId].getAttrs(CopyMoveAttr).length) {
|
if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) {
|
||||||
this.scene.currentBattle.lastMove = this.move.moveId;
|
this.scene.currentBattle.lastMove = this.move.moveId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2633,7 +2635,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showMoveText(): void {
|
showMoveText(): void {
|
||||||
if (this.move.getMove().getAttrs(ChargeAttr).length) {
|
if (this.move.getMove().hasAttr(ChargeAttr)) {
|
||||||
const lastMove = this.pokemon.getLastXMoves() as TurnMove[];
|
const lastMove = this.pokemon.getLastXMoves() as TurnMove[];
|
||||||
if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER) {
|
if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER) {
|
||||||
this.scene.queueMessage(getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500);
|
this.scene.queueMessage(getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500);
|
||||||
@ -2704,7 +2706,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
const hitCount = new Utils.IntegerHolder(1);
|
const hitCount = new Utils.IntegerHolder(1);
|
||||||
// Assume single target for multi hit
|
// Assume single target for multi hit
|
||||||
applyMoveAttrs(MultiHitAttr, user, this.getTarget(), this.move.getMove(), hitCount);
|
applyMoveAttrs(MultiHitAttr, user, this.getTarget(), this.move.getMove(), hitCount);
|
||||||
if (this.move.getMove() instanceof AttackMove && !this.move.getMove().getAttrs(FixedDamageAttr).length) {
|
if (this.move.getMove() instanceof AttackMove && !this.move.getMove().hasAttr(FixedDamageAttr)) {
|
||||||
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
||||||
}
|
}
|
||||||
user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value;
|
user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value;
|
||||||
@ -2715,7 +2717,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ]));
|
const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ]));
|
||||||
const activeTargets = targets.map(t => t.isActive(true));
|
const activeTargets = targets.map(t => t.isActive(true));
|
||||||
if (!activeTargets.length || (!this.move.getMove().getAttrs(VariableTargetAttr).length && !this.move.getMove().isMultiTarget() && !targetHitChecks[this.targets[0]])) {
|
if (!activeTargets.length || (!this.move.getMove().hasAttr(VariableTargetAttr) && !this.move.getMove().isMultiTarget() && !targetHitChecks[this.targets[0]])) {
|
||||||
user.turnData.hitCount = 1;
|
user.turnData.hitCount = 1;
|
||||||
user.turnData.hitsLeft = 1;
|
user.turnData.hitsLeft = 1;
|
||||||
if (activeTargets.length) {
|
if (activeTargets.length) {
|
||||||
@ -2759,7 +2761,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit),
|
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit),
|
||||||
user, target, this.move.getMove()).then(() => {
|
user, target, this.move.getMove()).then(() => {
|
||||||
if (hitResult !== HitResult.FAIL) {
|
if (hitResult !== HitResult.FAIL) {
|
||||||
const chargeEffect = !!this.move.getMove().getAttrs(ChargeAttr).find(ca => (ca as ChargeAttr).usedChargeEffect(user, this.getTarget(), this.move.getMove()));
|
const chargeEffect = !!this.move.getMove().getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget(), this.move.getMove()));
|
||||||
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
||||||
Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
|
Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
|
||||||
&& (attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove())).then(() => {
|
&& (attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove())).then(() => {
|
||||||
@ -2859,7 +2861,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hiddenTag = target.getTag(HiddenTag);
|
const hiddenTag = target.getTag(HiddenTag);
|
||||||
if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length) {
|
if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === hiddenTag.tagType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2871,7 +2873,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOhko = !!this.move.getMove().getAttrs(OneHitKOAccuracyAttr).length;
|
const isOhko = this.move.getMove().hasAttr(OneHitKOAccuracyAttr);
|
||||||
|
|
||||||
if (!isOhko) {
|
if (!isOhko) {
|
||||||
user.scene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
|
user.scene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
|
||||||
@ -3532,7 +3534,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
if (defeatSource?.isOnField()) {
|
if (defeatSource?.isOnField()) {
|
||||||
applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource);
|
applyPostVictoryAbAttrs(PostVictoryAbAttr, defeatSource);
|
||||||
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
|
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
|
||||||
const pvattrs = pvmove.getAttrs(PostVictoryStatChangeAttr) as PostVictoryStatChangeAttr[];
|
const pvattrs = pvmove.getAttrs(PostVictoryStatChangeAttr);
|
||||||
if (pvattrs.length) {
|
if (pvattrs.length) {
|
||||||
for (const pvattr of pvattrs) {
|
for (const pvattr of pvattrs) {
|
||||||
pvattr.applyPostVictory(defeatSource, defeatSource, pvmove);
|
pvattr.applyPostVictory(defeatSource, defeatSource, pvmove);
|
||||||
|
@ -30,6 +30,7 @@ import { allMoves } from "../data/move";
|
|||||||
import { TrainerVariant } from "../field/trainer";
|
import { TrainerVariant } from "../field/trainer";
|
||||||
import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
|
import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
|
||||||
import { Variant, variantData } from "#app/data/variant";
|
import { Variant, variantData } from "#app/data/variant";
|
||||||
|
import { TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js";
|
||||||
|
|
||||||
const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
|
||||||
|
|
||||||
@ -740,6 +741,10 @@ export class GameData {
|
|||||||
});
|
});
|
||||||
|
|
||||||
scene.arena.weather = sessionData.arena.weather;
|
scene.arena.weather = sessionData.arena.weather;
|
||||||
|
scene.arena.eventTarget.dispatchEvent(new WeatherChangedEvent(null, scene.arena.weather?.weatherType, scene.arena.weather?.turnsLeft));
|
||||||
|
|
||||||
|
scene.arena.terrain = sessionData.arena.terrain;
|
||||||
|
scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(null, scene.arena.terrain?.terrainType, scene.arena.terrain?.turnsLeft));
|
||||||
// TODO
|
// TODO
|
||||||
//scene.arena.tags = sessionData.arena.tags;
|
//scene.arena.tags = sessionData.arena.tags;
|
||||||
|
|
||||||
|