mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-20 14:29:28 +02:00
Add critical captures, update shake probability to gen 6
This commit is contained in:
parent
fc42937cc8
commit
4e1d99d0a0
@ -1,3 +1,4 @@
|
|||||||
|
import { IntegerHolder } from "#app/utils";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
@ -82,11 +83,35 @@ export function getPokeballTintColor(type: PokeballType): number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite, y1: number, y2: number, baseBounceDuration: integer, callback: Function) {
|
/**
|
||||||
|
* Gets the critical capture chance based on number of mons registered in Dex and modified {@link https://bulbapedia.bulbagarden.net/wiki/Catch_rate Catch rate}
|
||||||
|
* Multipliers and formula from {@link https://bulbapedia.bulbagarden.net/wiki/Catch_rate#Critical_capture Bulbapedia "Catch Rate" page}
|
||||||
|
* @param scene {@linkcode BattleScene} current BattleScene
|
||||||
|
* @param modifiedCatchRate the modified catch rate as calculated in {@linkcode AttemptCapturePhase}
|
||||||
|
* @returns the chance of getting a critical capture, out of 256
|
||||||
|
*/
|
||||||
|
export function getCriticalCaptureChance(scene: BattleScene, modifiedCatchRate: number): number {
|
||||||
|
const dexCount = scene.gameData.getSpeciesCount(d => !!d.caughtAttr);
|
||||||
|
const catchingCharmMultiplier = new IntegerHolder(1);
|
||||||
|
//scene.findModifier(m => m instanceof CriticalCaptureBoostModifier)?.apply(catchingCharmMultiplier);
|
||||||
|
const dexMultiplier = dexCount > 600 ? 2.5
|
||||||
|
: dexCount > 450 ? 2
|
||||||
|
: dexCount > 300 ? 1.5
|
||||||
|
: dexCount > 150 ? 1
|
||||||
|
: dexCount > 30 ? 0.5
|
||||||
|
: 0;
|
||||||
|
return Math.floor(catchingCharmMultiplier.value * dexMultiplier * modifiedCatchRate / 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite, y1: number, y2: number, baseBounceDuration: integer, callback: Function, isCritical: boolean = false) {
|
||||||
let bouncePower = 1;
|
let bouncePower = 1;
|
||||||
let bounceYOffset = y1;
|
let bounceYOffset = y1;
|
||||||
let bounceY = y2;
|
let bounceY = y2;
|
||||||
const yd = y2 - y1;
|
const yd = y2 - y1;
|
||||||
|
const x0 = pokeball.x;
|
||||||
|
const x1 = x0 + 4;
|
||||||
|
const x2 = x0 - 4;
|
||||||
|
let critShakes = 4;
|
||||||
|
|
||||||
const doBounce = () => {
|
const doBounce = () => {
|
||||||
scene.tweens.add({
|
scene.tweens.add({
|
||||||
@ -117,5 +142,40 @@ export function doPokeballBounceAnim(scene: BattleScene, pokeball: Phaser.GameOb
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
doBounce();
|
const doCritShake = () => {
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: pokeball,
|
||||||
|
x: x2,
|
||||||
|
duration: 125,
|
||||||
|
ease: "Linear",
|
||||||
|
onComplete: () => {
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: pokeball,
|
||||||
|
x: x1,
|
||||||
|
duration: 125,
|
||||||
|
ease: "Linear",
|
||||||
|
onComplete: () => {
|
||||||
|
critShakes--;
|
||||||
|
if (critShakes > 0) {
|
||||||
|
doCritShake();
|
||||||
|
} else {
|
||||||
|
scene.tweens.add({
|
||||||
|
targets: pokeball,
|
||||||
|
x: x0,
|
||||||
|
duration: 60,
|
||||||
|
ease: "Linear",
|
||||||
|
onComplete: () => doBounce()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isCritical) {
|
||||||
|
scene.time.delayedCall(500, doCritShake);
|
||||||
|
} else {
|
||||||
|
doBounce();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { getPokeballCatchMultiplier, getPokeballAtlasKey, getPokeballTintColor, doPokeballBounceAnim } from "#app/data/pokeball";
|
import { getPokeballCatchMultiplier, getPokeballAtlasKey, getPokeballTintColor, getCriticalCaptureChance, doPokeballBounceAnim } from "#app/data/pokeball";
|
||||||
import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
|
import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
|
||||||
import { PokeballType } from "#app/enums/pokeball";
|
import { PokeballType } from "#app/enums/pokeball";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
@ -52,7 +52,9 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType);
|
const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType);
|
||||||
const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1;
|
const statusMultiplier = pokemon.status ? getStatusEffectCatchRateMultiplier(pokemon.status.effect) : 1;
|
||||||
const modifiedCatchRate = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier);
|
const modifiedCatchRate = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier);
|
||||||
const shakeProbability = Math.round(65536 * Math.pow((modifiedCatchRate / 1044480), 0.1875));
|
const shakeProbability = Math.round(65536 * Math.pow((modifiedCatchRate / 1044480), 0.1875)); // Formula taken from gen 6
|
||||||
|
const criticalCaptureChance = getCriticalCaptureChance(this.scene, modifiedCatchRate);
|
||||||
|
const isCritical = pokemon.randSeedInt(256) < criticalCaptureChance;
|
||||||
const fpOffset = pokemon.getFieldPositionOffset();
|
const fpOffset = pokemon.getFieldPositionOffset();
|
||||||
|
|
||||||
const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType);
|
const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType);
|
||||||
@ -60,17 +62,19 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
this.pokeball.setOrigin(0.5, 0.625);
|
this.pokeball.setOrigin(0.5, 0.625);
|
||||||
this.scene.field.add(this.pokeball);
|
this.scene.field.add(this.pokeball);
|
||||||
|
|
||||||
this.scene.playSound("se/pb_throw");
|
this.scene.playSound("se/pb_throw", isCritical ? { rate: 0.2 } : undefined); // Crit catch throws are higher pitched
|
||||||
this.scene.time.delayedCall(300, () => {
|
this.scene.time.delayedCall(300, () => {
|
||||||
this.scene.field.moveBelow(this.pokeball as Phaser.GameObjects.GameObject, pokemon);
|
this.scene.field.moveBelow(this.pokeball as Phaser.GameObjects.GameObject, pokemon);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
|
// Throw animation
|
||||||
targets: this.pokeball,
|
targets: this.pokeball,
|
||||||
x: { value: 236 + fpOffset[0], ease: "Linear" },
|
x: { value: 236 + fpOffset[0], ease: "Linear" },
|
||||||
y: { value: 16 + fpOffset[1], ease: "Cubic.easeOut" },
|
y: { value: 16 + fpOffset[1], ease: "Cubic.easeOut" },
|
||||||
duration: 500,
|
duration: 500,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
|
// Ball opens
|
||||||
this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
|
this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
|
||||||
this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`));
|
this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}_open`));
|
||||||
this.scene.playSound("se/pb_rel");
|
this.scene.playSound("se/pb_rel");
|
||||||
@ -79,30 +83,33 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
addPokeballOpenParticles(this.scene, this.pokeball.x, this.pokeball.y, this.pokeballType);
|
addPokeballOpenParticles(this.scene, this.pokeball.x, this.pokeball.y, this.pokeballType);
|
||||||
|
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
|
// Mon enters ball
|
||||||
targets: pokemon,
|
targets: pokemon,
|
||||||
duration: 500,
|
duration: 500,
|
||||||
ease: "Sine.easeIn",
|
ease: "Sine.easeIn",
|
||||||
scale: 0.25,
|
scale: 0.25,
|
||||||
y: 20,
|
y: 20,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
|
// Ball closes
|
||||||
this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
|
this.pokeball.setTexture("pb", `${pokeballAtlasKey}_opening`);
|
||||||
pokemon.setVisible(false);
|
pokemon.setVisible(false);
|
||||||
this.scene.playSound("se/pb_catch");
|
this.scene.playSound("se/pb_catch");
|
||||||
this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}`));
|
this.scene.time.delayedCall(17, () => this.pokeball.setTexture("pb", `${pokeballAtlasKey}`));
|
||||||
|
|
||||||
const doShake = () => {
|
const doShake = () => {
|
||||||
|
// After the overall catch rate check, the game does 4 shake checks before confirming the catch.
|
||||||
let shakeCount = 0;
|
let shakeCount = 0;
|
||||||
const pbX = this.pokeball.x;
|
const pbX = this.pokeball.x;
|
||||||
const shakeCounter = this.scene.tweens.addCounter({
|
const shakeCounter = this.scene.tweens.addCounter({
|
||||||
from: 0,
|
from: 0,
|
||||||
to: 1,
|
to: 1,
|
||||||
repeat: 4,
|
repeat: isCritical ? 2 : 4, // Critical captures only perform 2 shake checks
|
||||||
yoyo: true,
|
yoyo: true,
|
||||||
ease: "Cubic.easeOut",
|
ease: "Cubic.easeOut",
|
||||||
duration: 250,
|
duration: 250,
|
||||||
repeatDelay: 500,
|
repeatDelay: 500,
|
||||||
onUpdate: t => {
|
onUpdate: t => {
|
||||||
if (shakeCount && shakeCount < 4) {
|
if (shakeCount && shakeCount < (isCritical ? 2 : 4)) {
|
||||||
const value = t.getValue();
|
const value = t.getValue();
|
||||||
const directionMultiplier = shakeCount % 2 === 1 ? 1 : -1;
|
const directionMultiplier = shakeCount % 2 === 1 ? 1 : -1;
|
||||||
this.pokeball.setX(pbX + value * 4 * directionMultiplier);
|
this.pokeball.setX(pbX + value * 4 * directionMultiplier);
|
||||||
@ -113,7 +120,8 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
if (!pokemon.species.isObtainable()) {
|
if (!pokemon.species.isObtainable()) {
|
||||||
shakeCounter.stop();
|
shakeCounter.stop();
|
||||||
this.failCatch(shakeCount);
|
this.failCatch(shakeCount);
|
||||||
} else if (shakeCount++ < 3) {
|
} else if (shakeCount++ < (isCritical ? 1 : 3)) {
|
||||||
|
// Shake check
|
||||||
if (pokeballMultiplier === -1 || pokemon.randSeedInt(65536) < shakeProbability) {
|
if (pokeballMultiplier === -1 || pokemon.randSeedInt(65536) < shakeProbability) {
|
||||||
this.scene.playSound("se/pb_move");
|
this.scene.playSound("se/pb_move");
|
||||||
} else {
|
} else {
|
||||||
@ -152,7 +160,8 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.scene.time.delayedCall(250, () => doPokeballBounceAnim(this.scene, this.pokeball, 16, 72, 350, doShake));
|
// Ball bounces (handled in pokemon.ts)
|
||||||
|
this.scene.time.delayedCall(250, () => doPokeballBounceAnim(this.scene, this.pokeball, 16, 72, 350, doShake, isCritical));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user