Compare commits

...

8 Commits

Author SHA1 Message Date
Xavion3
9013921523
[Bug] Fix speed tie code (#1895)
* Fix speed tie code

* Fix off by one error

* Shuffle before sorting to make code cleaner
2024-06-06 23:01:13 -04:00
DustinLin
a815b73d96
[Documentation] documentation of move.ts (#1828)
* starting documentation of move.ts

* more docs

* fixing comments

* updating comments
2024-06-06 20:11:14 -05:00
Benjamin Odom
4cce8a1bfa
Update index.css (#1898) 2024-06-06 19:57:12 -05:00
prime
5568def1a4
Show C-Button on mobile during modifier selection (#1893) 2024-06-06 19:20:13 -05:00
Raidette
209a69d098
[Move] Rototiller implementation (#1885)
* rototiller implementation

* attack now fails if no eligible grass type pokémon is fielded
2024-06-06 17:22:04 -04:00
Greenlamp2
9c1a13eb54
Fix - double inputs (#1842)
* fix an issue where the input repeated itself too fast

* remove remnant code
2024-06-06 22:55:01 +02:00
returntoice
a18d796e2e
[Localization] Fix Korean multi lens description (#1891) 2024-06-06 16:43:25 -04:00
Cycrum
4fcc3ef9ff
[Feature] Update Gardevoir, Glalie, and Meloetta genders (#1864)
Updated Pokemon genders to canonical rates
2024-06-06 16:19:41 -04:00
7 changed files with 240 additions and 17 deletions

View File

@ -146,13 +146,13 @@ body {
margin-left: 10%;
}
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadRectBtnContainer > .apadSqBtn,
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadSqBtnContainer
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadRectBtnContainer > .apadSqBtn:not(.apadBattle),
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadSqBtnContainer > .apadSqBtn:not(.apadBattle)
{
display: none;
}
#touchControls:not([data-ui-mode='COMMAND']):not([data-ui-mode='FIGHT']):not([data-ui-mode='BALL']):not([data-ui-mode='TARGET_SELECT']) #apad .apadBattle {
#touchControls:not([data-ui-mode='COMMAND']):not([data-ui-mode='FIGHT']):not([data-ui-mode='BALL']):not([data-ui-mode='TARGET_SELECT']):not([data-ui-mode='MODIFIER_SELECT']) #apad .apadBattle {
display: none;
}

View File

@ -42,7 +42,7 @@ export enum MoveTarget {
/** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_target_all_adjacent_Pok%C3%A9mon Moves that target all adjacent Pokemon} */
ALL_NEAR_OTHERS,
NEAR_ENEMY,
/** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_target_all_adjacent_foes Moves that taret all adjacent foes} */
/** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_target_all_adjacent_foes Moves that target all adjacent foes} */
ALL_NEAR_ENEMIES,
RANDOM_NEAR_ENEMY,
ALL_ENEMIES,
@ -166,10 +166,22 @@ export default class Move implements Localizable {
return this.attrs.some((attr) => attr instanceof attrType);
}
/**
* Takes as input a boolean function and returns the first MoveAttr in attrs that matches true
* @param attrPredicate
* @returns the first {@linkcode MoveAttr} element in attrs that makes the input function return true
*/
findAttr(attrPredicate: (attr: MoveAttr) => boolean): MoveAttr {
return this.attrs.find(attrPredicate);
}
/**
* Adds a new MoveAttr to the move (appends to the attr array)
* if the MoveAttr also comes with a condition, also adds that to the conditions array: {@linkcode MoveCondition}
* @param AttrType {@linkcode MoveAttr} the constructor of a MoveAttr class
* @param args the args needed to instantiate a the given class
* @returns the called object {@linkcode Move}
*/
attr<T extends new (...args: any[]) => MoveAttr>(AttrType: T, ...args: ConstructorParameters<T>): this {
const attr = new AttrType(...args);
this.attrs.push(attr);
@ -184,9 +196,16 @@ export default class Move implements Localizable {
return this;
}
addAttr(attr: MoveAttr): this {
this.attrs.push(attr);
let attrCondition = attr.getCondition();
/**
* Adds a new MoveAttr to the move (appends to the attr array)
* if the MoveAttr also comes with a condition, also adds that to the conditions array: {@linkcode MoveCondition}
* Almost identical to {@link attr}, except you are passing in a MoveAttr object, instead of a constructor and it's arguments
* @param attrAdd {@linkcode MoveAttr} the attribute to add
* @returns the called object {@linkcode Move}
*/
addAttr(attrAdd: MoveAttr): this {
this.attrs.push(attrAdd);
let attrCondition = attrAdd.getCondition();
if (attrCondition) {
if (typeof attrCondition === "function") {
attrCondition = new MoveCondition(attrCondition);
@ -197,15 +216,30 @@ export default class Move implements Localizable {
return this;
}
/**
* Sets the move target of this move
* @param moveTarget {@linkcode MoveTarget} the move target to set
* @returns the called object {@linkcode Move}
*/
target(moveTarget: MoveTarget): this {
this.moveTarget = moveTarget;
return this;
}
/**
* Getter function that returns if this Move has a MoveFlag
* @param flag {@linkcode MoveFlags} to check
* @returns boolean
*/
hasFlag(flag: MoveFlags): boolean {
// internally it is taking the bitwise AND (MoveFlags are represented as bit-shifts) and returning False if result is 0 and true otherwise
return !!(this.flags & flag);
}
/**
* Getter function that returns if the move hits multiple targets
* @returns boolean
*/
isMultiTarget(): boolean {
switch (this.moveTarget) {
case MoveTarget.ALL_OTHERS:
@ -222,6 +256,11 @@ export default class Move implements Localizable {
return false;
}
/**
* Getter function that returns if the move targets itself or an ally
* @returns boolean
*/
isAllyTarget(): boolean {
switch (this.moveTarget) {
case MoveTarget.USER:
@ -235,6 +274,12 @@ export default class Move implements Localizable {
return false;
}
/**
* Checks if the move is immune to certain types
* currently only look at case of Grass types and powder moves
* @param type {@linkcode Type} enum
* @returns boolean
*/
isTypeImmune(type: Type): boolean {
switch (type) {
case Type.GRASS:
@ -246,6 +291,11 @@ export default class Move implements Localizable {
return false;
}
/**
* Adds a move condition to the move
* @param condition {@linkcode MoveCondition} or {@linkcode MoveConditionFunc}, appends to conditions array a new MoveCondition object
* @returns the called object {@linkcode Move}
*/
condition(condition: MoveCondition | MoveConditionFunc): this {
if (typeof condition === "function") {
condition = new MoveCondition(condition as MoveConditionFunc);
@ -255,17 +305,31 @@ export default class Move implements Localizable {
return this;
}
/**
* Marks the move as "partial": appends texts to the move name
* @returns the called object {@linkcode Move}
*/
partial(): this {
this.nameAppend += " (P)";
return this;
}
/**
* Marks the move as "unimplemented": appends texts to the move name
* @returns the called object {@linkcode Move}
*/
unimplemented(): this {
this.nameAppend += " (N)";
return this;
}
/**
* Sets the flags of the move
* @param flag {@linkcode MoveFlags}
* @param on a boolean, if True, then "ORs" the flag onto existing ones, if False then "XORs" the flag onto existing ones
*/
private setFlag(flag: MoveFlags, on: boolean): void {
// bitwise OR and bitwise XOR respectively
if (on) {
this.flags |= flag;
} else {
@ -273,51 +337,110 @@ export default class Move implements Localizable {
}
}
/**
* Sets the {@linkcode MoveFlags.MAKES_CONTACT} flag for the calling Move
* @param makesContact The value (boolean) to set the flag to
* @returns The {@linkcode Move} that called this function
*/
makesContact(makesContact?: boolean): this {
this.setFlag(MoveFlags.MAKES_CONTACT, makesContact);
return this;
}
/**
* Sets the {@linkcode MoveFlags.IGNORE_PROTECT} flag for the calling Move
* @param ignoresProtect The value (boolean) to set the flag to
* example: @see {@linkcode Moves.CURSE}
* @returns The {@linkcode Move} that called this function
*/
ignoresProtect(ignoresProtect?: boolean): this {
this.setFlag(MoveFlags.IGNORE_PROTECT, ignoresProtect);
return this;
}
/**
* Sets the {@linkcode MoveFlags.IGNORE_VIRTUAL} flag for the calling Move
* @param ignoresVirtual The value (boolean) to set the flag to
* example: @see {@linkcode Moves.NATURE_POWER}
* @returns The {@linkcode Move} that called this function
*/
ignoresVirtual(ignoresVirtual?: boolean): this {
this.setFlag(MoveFlags.IGNORE_VIRTUAL, ignoresVirtual);
return this;
}
/**
* Sets the {@linkcode MoveFlags.SOUND_BASED} flag for the calling Move
* @param soundBased The value (boolean) to set the flag to
* example: @see {@linkcode Moves.UPROAR}
* @returns The {@linkcode Move} that called this function
*/
soundBased(soundBased?: boolean): this {
this.setFlag(MoveFlags.SOUND_BASED, soundBased);
return this;
}
/**
* Sets the {@linkcode MoveFlags.HIDE_USER} flag for the calling Move
* @param hidesUser The value (boolean) to set the flag to
* example: @see {@linkcode Moves.TELEPORT}
* @returns The {@linkcode Move} that called this function
*/
hidesUser(hidesUser?: boolean): this {
this.setFlag(MoveFlags.HIDE_USER, hidesUser);
return this;
}
/**
* Sets the {@linkcode MoveFlags.HIDE_TARGET} flag for the calling Move
* @param hidesTarget The value (boolean) to set the flag to
* example: @see {@linkcode Moves.WHIRLWIND}
* @returns The {@linkcode Move} that called this function
*/
hidesTarget(hidesTarget?: boolean): this {
this.setFlag(MoveFlags.HIDE_TARGET, hidesTarget);
return this;
}
/**
* Sets the {@linkcode MoveFlags.BITING_MOVE} flag for the calling Move
* @param bitingMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.BITE}
* @returns The {@linkcode Move} that called this function
*/
bitingMove(bitingMove?: boolean): this {
this.setFlag(MoveFlags.BITING_MOVE, bitingMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.PULSE_MOVE} flag for the calling Move
* @param pulseMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.WATER_PULSE}
* @returns The {@linkcode Move} that called this function
*/
pulseMove(pulseMove?: boolean): this {
this.setFlag(MoveFlags.PULSE_MOVE, pulseMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.PUNCHING_MOVE} flag for the calling Move
* @param punchingMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.DRAIN_PUNCH}
* @returns The {@linkcode Move} that called this function
*/
punchingMove(punchingMove?: boolean): this {
this.setFlag(MoveFlags.PUNCHING_MOVE, punchingMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.SLICING_MOVE} flag for the calling Move
* @param slicingMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.X_SCISSOR}
* @returns The {@linkcode Move} that called this function
*/
slicingMove(slicingMove?: boolean): this {
this.setFlag(MoveFlags.SLICING_MOVE, slicingMove);
return this;
@ -334,42 +457,92 @@ export default class Move implements Localizable {
return this;
}
/**
* Sets the {@linkcode MoveFlags.BALLBOMB_MOVE} flag for the calling Move
* @param ballBombMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.ELECTRO_BALL}
* @returns The {@linkcode Move} that called this function
*/
ballBombMove(ballBombMove?: boolean): this {
this.setFlag(MoveFlags.BALLBOMB_MOVE, ballBombMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.POWDER_MOVE} flag for the calling Move
* @param powderMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.STUN_SPORE}
* @returns The {@linkcode Move} that called this function
*/
powderMove(powderMove?: boolean): this {
this.setFlag(MoveFlags.POWDER_MOVE, powderMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.DANCE_MOVE} flag for the calling Move
* @param danceMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.PETAL_DANCE}
* @returns The {@linkcode Move} that called this function
*/
danceMove(danceMove?: boolean): this {
this.setFlag(MoveFlags.DANCE_MOVE, danceMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.WIND_MOVE} flag for the calling Move
* @param windMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.HURRICANE}
* @returns The {@linkcode Move} that called this function
*/
windMove(windMove?: boolean): this {
this.setFlag(MoveFlags.WIND_MOVE, windMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.TRIAGE_MOVE} flag for the calling Move
* @param triageMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.ABSORB}
* @returns The {@linkcode Move} that called this function
*/
triageMove(triageMove?: boolean): this {
this.setFlag(MoveFlags.TRIAGE_MOVE, triageMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.IGNORE_ABILITIES} flag for the calling Move
* @param ignoresAbilities sThe value (boolean) to set the flag to
* example: @see {@linkcode Moves.SUNSTEEL_STRIKE}
* @returns The {@linkcode Move} that called this function
*/
ignoresAbilities(ignoresAbilities?: boolean): this {
this.setFlag(MoveFlags.IGNORE_ABILITIES, ignoresAbilities);
return this;
}
/**
* Sets the {@linkcode MoveFlags.CHECK_ALL_HITS} flag for the calling Move
* @param checkAllHits The value (boolean) to set the flag to
* example: @see {@linkcode Moves.TRIPLE_AXEL}
* @returns The {@linkcode Move} that called this function
*/
checkAllHits(checkAllHits?: boolean): this {
this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits);
return this;
}
/**
* Checks if the move flag applies to the pokemon(s) using/receiving the move
* @param flag {@linkcode MoveFlags} MoveFlag to check on user and/or target
* @param user {@linkcode Pokemon} the Pokemon using the move
* @param target {@linkcode Pokemon} the Pokemon receiving the move
* @returns boolean
*/
checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean {
// special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact
switch (flag) {
case MoveFlags.MAKES_CONTACT:
if (user.hasAbilityWithAttr(IgnoreContactAbAttr)) {
@ -389,6 +562,13 @@ export default class Move implements Localizable {
return !!(this.flags & flag);
}
/**
* Applies each {@linkcode MoveCondition} of this move to the params
* @param user {@linkcode Pokemon} to apply conditions to
* @param target {@linkcode Pokemon} to apply conditions to
* @param move {@linkcode Move} to apply conditions to
* @returns boolean: false if any of the apply()'s return false, else true
*/
applyConditions(user: Pokemon, target: Pokemon, move: Move): boolean {
for (const condition of this.conditions) {
if (!condition.apply(user, target, move)) {
@ -399,6 +579,14 @@ export default class Move implements Localizable {
return true;
}
/**
* Sees if, given the target pokemon, a move fails on it (by looking at each {@linkcode MoveAttr} of this move
* @param user {@linkcode Pokemon} using the move
* @param target {@linkcode Pokemon} receiving the move
* @param move {@linkcode Move} using the move
* @param cancelled {@linkcode Utils.BooleanHolder} to hold boolean value
* @returns string of the failed text, or null
*/
getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
for (const attr of this.attrs) {
const failedText = attr.getFailedText(user, target, move, cancelled);
@ -409,6 +597,13 @@ export default class Move implements Localizable {
return null;
}
/**
* Calculates the userBenefitScore across all the attributes and conditions
* @param user {@linkcode Pokemon} using the move
* @param target {@linkcode Pokemon} receiving the move
* @param move {@linkcode Move} using the move
* @returns integer representing the total benefitScore
*/
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
let score = 0;
@ -423,10 +618,18 @@ export default class Move implements Localizable {
return score;
}
/**
* Calculates the targetBenefitScore across all the attributes
* @param user {@linkcode Pokemon} using the move
* @param target {@linkcode Pokemon} receiving the move
* @param move {@linkcode Move} using the move
* @returns integer representing the total benefitScore
*/
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
let score = 0;
for (const attr of this.attrs) {
// conditionals to check if the move is self targeting (if so then you are applying the move to yourself, not the target)
score += attr.getTargetBenefitScore(user, !attr.selfTarget ? target : user, move) * (target !== user && attr.selfTarget ? -1 : 1);
}
@ -6683,7 +6886,11 @@ export function initMoves() {
.condition((user, target, move) => user.battleData.berriesEaten.length > 0),
new StatusMove(Moves.ROTOTILLER, Type.GROUND, -1, 10, 100, 0, 6)
.target(MoveTarget.ALL)
.unimplemented(),
.condition((user,target,move) => {
// If any fielded pokémon is grass-type and grounded.
return [...user.scene.getEnemyParty(),...user.scene.getParty()].some((poke) => poke.isOfType(Type.GRASS) && poke.isGrounded());
})
.attr(StatChangeAttr, [BattleStat.ATK, BattleStat.SPATK], 1, false, (user, target, move) => target.isOfType(Type.GRASS) && target.isGrounded()),
new StatusMove(Moves.STICKY_WEB, Type.BUG, -1, 20, -1, 0, 6)
.attr(AddArenaTrapTagAttr, ArenaTagType.STICKY_WEB)
.target(MoveTarget.ENEMY_SIDE),

View File

@ -1286,7 +1286,7 @@ export function initSpecies() {
new PokemonSpecies(Species.PELIPPER, 3, false, false, false, "Water Bird Pokémon", Type.WATER, Type.FLYING, 1.2, 28, Abilities.KEEN_EYE, Abilities.DRIZZLE, Abilities.RAIN_DISH, 440, 60, 50, 100, 95, 70, 65, 45, 50, 154, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.RALTS, 3, false, false, false, "Feeling Pokémon", Type.PSYCHIC, Type.FAIRY, 0.4, 6.6, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 198, 28, 25, 25, 45, 35, 40, 235, 35, 40, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.KIRLIA, 3, false, false, false, "Emotion Pokémon", Type.PSYCHIC, Type.FAIRY, 0.8, 20.2, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 278, 38, 35, 35, 65, 55, 50, 120, 35, 97, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.GARDEVOIR, 3, false, false, false, "Embrace Pokémon", Type.PSYCHIC, Type.FAIRY, 1.6, 48.4, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 518, 68, 65, 65, 125, 115, 80, 45, 35, 259, GrowthRate.SLOW, 0, false, true,
new PokemonSpecies(Species.GARDEVOIR, 3, false, false, false, "Embrace Pokémon", Type.PSYCHIC, Type.FAIRY, 1.6, 48.4, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 518, 68, 65, 65, 125, 115, 80, 45, 35, 259, GrowthRate.SLOW, 50, false, true,
new PokemonForm("Normal", "", Type.PSYCHIC, Type.FAIRY, 1.6, 48.4, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 518, 68, 65, 65, 125, 115, 80, 45, 35, 259, false, null, true),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.PSYCHIC, Type.FAIRY, 1.6, 48.4, Abilities.PIXILATE, Abilities.PIXILATE, Abilities.PIXILATE, 618, 68, 85, 65, 165, 135, 100, 45, 35, 259),
),
@ -1404,7 +1404,7 @@ export function initSpecies() {
),
new PokemonSpecies(Species.WYNAUT, 3, false, false, false, "Bright Pokémon", Type.PSYCHIC, null, 0.6, 14, Abilities.SHADOW_TAG, Abilities.NONE, Abilities.TELEPATHY, 260, 95, 23, 48, 23, 48, 23, 125, 50, 52, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.SNORUNT, 3, false, false, false, "Snow Hat Pokémon", Type.ICE, null, 0.7, 16.8, Abilities.INNER_FOCUS, Abilities.ICE_BODY, Abilities.MOODY, 300, 50, 50, 50, 50, 50, 50, 190, 50, 60, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.GLALIE, 3, false, false, false, "Face Pokémon", Type.ICE, null, 1.5, 256.5, Abilities.INNER_FOCUS, Abilities.ICE_BODY, Abilities.MOODY, 480, 80, 80, 80, 80, 80, 80, 75, 50, 168, GrowthRate.MEDIUM_FAST, 100, false, true,
new PokemonSpecies(Species.GLALIE, 3, false, false, false, "Face Pokémon", Type.ICE, null, 1.5, 256.5, Abilities.INNER_FOCUS, Abilities.ICE_BODY, Abilities.MOODY, 480, 80, 80, 80, 80, 80, 80, 75, 50, 168, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.ICE, null, 1.5, 256.5, Abilities.INNER_FOCUS, Abilities.ICE_BODY, Abilities.MOODY, 480, 80, 80, 80, 80, 80, 80, 75, 50, 168, false, null, true),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.ICE, null, 2.1, 350.2, Abilities.REFRIGERATE, Abilities.REFRIGERATE, Abilities.REFRIGERATE, 580, 80, 120, 80, 120, 80, 100, 75, 50, 168),
),
@ -1829,7 +1829,7 @@ export function initSpecies() {
new PokemonForm("Ordinary Form", "ordinary", Type.WATER, Type.FIGHTING, 1.4, 48.5, Abilities.JUSTIFIED, Abilities.NONE, Abilities.NONE, 580, 91, 72, 90, 129, 90, 108, 3, 35, 290, false, null, true),
new PokemonForm("Resolute", "resolute", Type.WATER, Type.FIGHTING, 1.4, 48.5, Abilities.JUSTIFIED, Abilities.NONE, Abilities.NONE, 580, 91, 72, 90, 129, 90, 108, 3, 35, 290),
),
new PokemonSpecies(Species.MELOETTA, 5, false, false, true, "Melody Pokémon", Type.NORMAL, Type.PSYCHIC, 0.6, 6.5, Abilities.SERENE_GRACE, Abilities.NONE, Abilities.NONE, 600, 100, 77, 77, 128, 128, 90, 3, 100, 270, GrowthRate.SLOW, 0, false, true,
new PokemonSpecies(Species.MELOETTA, 5, false, false, true, "Melody Pokémon", Type.NORMAL, Type.PSYCHIC, 0.6, 6.5, Abilities.SERENE_GRACE, Abilities.NONE, Abilities.NONE, 600, 100, 77, 77, 128, 128, 90, 3, 100, 270, GrowthRate.SLOW, null, false, true,
new PokemonForm("Aria Forme", "aria", Type.NORMAL, Type.PSYCHIC, 0.6, 6.5, Abilities.SERENE_GRACE, Abilities.NONE, Abilities.NONE, 600, 100, 77, 77, 128, 128, 90, 3, 100, 270, false, null, true),
new PokemonForm("Pirouette Forme", "pirouette", Type.NORMAL, Type.FIGHTING, 0.6, 6.5, Abilities.SERENE_GRACE, Abilities.NONE, Abilities.NONE, 600, 100, 128, 90, 77, 77, 128, 3, 100, 270),
),

View File

@ -48,7 +48,7 @@ export interface InterfaceConfig {
custom?: MappingLayout;
}
const repeatInputDelayMillis = 250;
const repeatInputDelayMillis = 500;
// Phaser.Input.Gamepad.GamepadPlugin#refreshPads
declare module "phaser" {
@ -88,7 +88,6 @@ declare module "phaser" {
* providing a unified interface for all input-related interactions.
*/
export class InputsController {
private buttonKeys: Phaser.Input.Keyboard.Key[][];
private gamepads: Array<Phaser.Input.Gamepad.Gamepad> = new Array();
private scene: BattleScene;
public events: Phaser.Events.EventEmitter;
@ -123,7 +122,6 @@ export class InputsController {
constructor(scene: BattleScene) {
this.scene = scene;
this.time = this.scene.time;
this.buttonKeys = [];
this.selectedDevice = {
[Device.GAMEPAD]: null,
[Device.KEYBOARD]: "default"

View File

@ -93,7 +93,7 @@ export const modifierType: ModifierTypeTranslationEntries = {
description: "기술의 명중률이 {{accuracyAmount}} 증가 (최대 100)",
},
"PokemonMultiHitModifierType": {
description: "공격이 가진 갯수에 따라 60/75/82.5%의 위력으로 한번 더 명중",
description: "지닌 개수(최대 3개)마다 추가 공격을 하는 대신, 공격력이 60%(1개)/75%(2개)/82.5%(3개)만큼 감소합니다.",
},
"TmModifierType": {
name: "No.{{moveId}} {{moveName}}",

View File

@ -633,11 +633,12 @@ export abstract class FieldPhase extends BattlePhase {
const playerField = this.scene.getPlayerField().filter(p => p.isActive()) as Pokemon[];
const enemyField = this.scene.getEnemyField().filter(p => p.isActive()) as Pokemon[];
let orderedTargets: Pokemon[] = playerField.concat(enemyField).sort((a: Pokemon, b: Pokemon) => {
// We shuffle the list before sorting so speed ties produce random results
let orderedTargets: Pokemon[] = Utils.randSeedShuffle(playerField.concat(enemyField)).sort((a: Pokemon, b: Pokemon) => {
const aSpeed = a?.getBattleStat(Stat.SPD) || 0;
const bSpeed = b?.getBattleStat(Stat.SPD) || 0;
return aSpeed < bSpeed ? 1 : aSpeed > bSpeed ? -1 : !this.scene.randBattleSeedInt(2) ? -1 : 1;
return bSpeed - aSpeed;
});
const speedReversed = new Utils.BooleanHolder(false);

View File

@ -127,6 +127,23 @@ export function randSeedEasedWeightedItem<T>(items: T[], easingFunction: string
return items[Math.floor(easedValue * items.length)];
}
/**
* Shuffle a list using the seeded rng. Utilises the Fisher-Yates algorithm.
* @param {Array} items An array of items.
* @returns {Array} A new shuffled array of items.
*/
export function randSeedShuffle<T>(items: T[]): T[] {
if (items.length <= 1) {
return items;
}
const newArray = items.slice(0);
for (let i = items.length - 1; i > 0; i--) {
const j = Phaser.Math.RND.integerInRange(0, i);
[newArray[i], newArray[j]] = [newArray[j], newArray[i]];
}
return newArray;
}
export function getFrameMs(frameCount: integer): integer {
return Math.floor((1 / 60) * 1000 * frameCount);
}