From bc319a8eda6807501befcd2c756dbb4579024d82 Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Sat, 4 May 2024 00:27:20 +1000 Subject: [PATCH 01/11] Fix eggs during dailies --- src/system/game-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system/game-data.ts b/src/system/game-data.ts index b7495a1eac9..6410c52a2b8 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1060,7 +1060,7 @@ export class GameData { this.gameStats.shinyPokemonHatched++; } - if (!hasPrevolution && (!pokemon.scene.gameMode.isDaily || hasNewAttr)) + if (!hasPrevolution && (!pokemon.scene.gameMode.isDaily || hasNewAttr || fromEgg)) this.addStarterCandy(species, (1 * (pokemon.isShiny() ? 5 * Math.pow(2, pokemon.variant || 0) : 1)) * (fromEgg || pokemon.isBoss() ? 2 : 1)); } From e082dd90892342669670b9d6c2b6a843b9a624d3 Mon Sep 17 00:00:00 2001 From: Lugiad Date: Fri, 3 May 2024 16:51:00 +0200 Subject: [PATCH 02/11] Minor correction to apostrophes in French menu-ui-handler.ts (#415) --- src/locales/fr/menu-ui-handler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/fr/menu-ui-handler.ts b/src/locales/fr/menu-ui-handler.ts index 684a8cc07ba..54328a05eb1 100644 --- a/src/locales/fr/menu-ui-handler.ts +++ b/src/locales/fr/menu-ui-handler.ts @@ -13,9 +13,9 @@ export const menuUiHandler: SimpleTranslationEntries = { "LOG_OUT": "Déconnexion", "slot": "Emplacement {{slotNumber}}", "importSession": "Importer session", - "importSlotSelect": "Sélectionnez l'emplacement vers lequel importer les données.", + "importSlotSelect": "Sélectionnez l’emplacement vers lequel importer les données.", "exportSession": "Exporter session", - "exportSlotSelect": "Sélectionnez l'emplacement depuis lequel exporter les données.", + "exportSlotSelect": "Sélectionnez l’emplacement depuis lequel exporter les données.", "importData": "Importer données", "exportData": "Exporter données", "cancel": "Retour", From c7c4deb416413e0077c5830a78dd47001d5f4bad Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Sat, 4 May 2024 01:56:13 +1000 Subject: [PATCH 03/11] Implement Sniper --- src/data/ability.ts | 23 ++++++++++++++++++++++- src/field/pokemon.ts | 7 ++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 7c350f025be..a0f85e2fd5e 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1609,6 +1609,27 @@ export class BonusCritAbAttr extends AbAttr { } } +export class MultCritAbAttr extends AbAttr { + public multAmount: number; + + constructor(multAmount: number) { + super(true); + + this.multAmount = multAmount; + } + + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const critMult = args[0] as Utils.NumberHolder; + if (critMult.value > 1){ + critMult.value *= this.multAmount; + return true; + } + + return false; + } +} + + export class BlockNonDirectDamageAbAttr extends AbAttr { apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; @@ -2776,7 +2797,7 @@ export function initAbilities() { .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), new Ability(Abilities.SNIPER, 4) - .unimplemented(), + .attr(MultCritAbAttr, 1.5), new Ability(Abilities.MAGIC_GUARD, 4) .attr(BlockNonDirectDamageAbAttr), new Ability(Abilities.NO_GUARD, 4) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index bc51323075b..909d255eef8 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr } from '../data/ability'; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import Battle, { BattlerIndex } from '../battle'; @@ -1332,7 +1332,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, null, isCritical)); const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); - const criticalMultiplier = isCritical ? 1.5 : 1; + const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); + applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier); const screenMultiplier = new Utils.NumberHolder(1); if (!isCritical) { this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, this.scene.currentBattle.double, screenMultiplier); @@ -1355,7 +1356,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(VariableDefAttr, source, this, move, targetDef); if (!isTypeImmune) { - damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier); + damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier.value); if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { const burnDamageReductionCancelled = new Utils.BooleanHolder(false); applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled); From c44ec421b08b7c193c67e3d57c8e30fb9f9a4c95 Mon Sep 17 00:00:00 2001 From: Dario Krause Date: Fri, 3 May 2024 16:49:26 +0200 Subject: [PATCH 04/11] Update pokemon.ts --- src/locales/de/pokemon.ts | 354 +++++++++++++++++++------------------- 1 file changed, 177 insertions(+), 177 deletions(-) diff --git a/src/locales/de/pokemon.ts b/src/locales/de/pokemon.ts index db92ccc5c18..9efecb819cd 100644 --- a/src/locales/de/pokemon.ts +++ b/src/locales/de/pokemon.ts @@ -605,208 +605,208 @@ export const pokemon: SimpleTranslationEntries = { "tynamo": "Zapplardin", "eelektrik": "Zapplalek", "eelektross": "Zapplarang", - "elgyem": "Elgyem", - "beheeyem": "Beheeyem", - "litwick": "Litwick", - "lampent": "Lampent", - "chandelure": "Chandelure", - "axew": "Axew", - "fraxure": "Fraxure", - "haxorus": "Haxorus", - "cubchoo": "Cubchoo", - "beartic": "Beartic", - "cryogonal": "Cryogonal", - "shelmet": "Shelmet", - "accelgor": "Accelgor", - "stunfisk": "Stunfisk", - "mienfoo": "Mienfoo", - "mienshao": "Mienshao", - "druddigon": "Druddigon", - "golett": "Golett", - "golurk": "Golurk", - "pawniard": "Pawniard", - "bisharp": "Bisharp", - "bouffalant": "Bouffalant", - "rufflet": "Rufflet", - "braviary": "Braviary", - "vullaby": "Vullaby", - "mandibuzz": "Mandibuzz", - "heatmor": "Heatmor", - "durant": "Durant", - "deino": "Deino", - "zweilous": "Zweilous", - "hydreigon": "Hydreigon", - "larvesta": "Larvesta", - "volcarona": "Volcarona", - "cobalion": "Cobalion", - "terrakion": "Terrakion", - "virizion": "Virizion", - "tornadus": "Tornadus", - "thundurus": "Thundurus", + "elgyem": "Pygraulon", + "beheeyem": "Megalon", + "litwick": "Lichtel", + "lampent": "Laternecto", + "chandelure": "Skelabra", + "axew": "Milza", + "fraxure": "Scharfax", + "haxorus": "Maxax", + "cubchoo": "Petznief", + "beartic": "Siberio", + "cryogonal": "Frigometri", + "shelmet": "Schnuthelm", + "accelgor": "Hydragil", + "stunfisk": "Flunschlik", + "mienfoo": "Lin-Fu", + "mienshao": "Wie-Shu", + "druddigon": "Shardrago", + "golett": "Golbit", + "golurk": "Golgantes", + "pawniard": "Gladiantri", + "bisharp": "Ceasurio", + "bouffalant": "Bisofank", + "rufflet": "Geronimatz", + "braviary": "Washakwil", + "vullaby": "Skallyk", + "mandibuzz": "Grypheldis", + "heatmor": "Furnifraß", + "durant": "Fermicula", + "deino": "Kapuno", + "zweilous": "Duodino", + "hydreigon": "Trikephalo", + "larvesta": "Ignivor", + "volcarona": "Ramoth", + "cobalion": "Kobalium", + "terrakion": "Terrakium", + "virizion": "Viridium", + "tornadus": "Boreos", + "thundurus": "Voltolos", "reshiram": "Reshiram", "zekrom": "Zekrom", - "landorus": "Landorus", + "landorus": "Dementeros", "kyurem": "Kyurem", "keldeo": "Keldeo", "meloetta": "Meloetta", "genesect": "Genesect", - "chespin": "Chespin", - "quilladin": "Quilladin", - "chesnaught": "Chesnaught", - "fennekin": "Fennekin", - "braixen": "Braixen", - "delphox": "Delphox", - "froakie": "Froakie", - "frogadier": "Frogadier", - "greninja": "Greninja", - "bunnelby": "Bunnelby", - "diggersby": "Diggersby", - "fletchling": "Fletchling", - "fletchinder": "Fletchinder", - "talonflame": "Talonflame", - "scatterbug": "Scatterbug", - "spewpa": "Spewpa", + "chespin": "Igamaro", + "quilladin": "Igastarnish", + "chesnaught": "Brigaron", + "fennekin": "Fynx", + "braixen": "Rutena", + "delphox": "Fennexis", + "froakie": "Froxy", + "frogadier": "Amphizel", + "greninja": "Quajutsu", + "bunnelby": "Scoppel", + "diggersby": "Grebbit", + "fletchling": "Dartiri", + "fletchinder": "Dartignis", + "talonflame": "Fiaro", + "scatterbug": "Purmel", + "spewpa": "Puponcho", "vivillon": "Vivillon", - "litleo": "Litleo", - "pyroar": "Pyroar", + "litleo": "Leufeo", + "pyroar": "Pyroleo", "flabebe": "Flabébé", "floette": "Floette", "florges": "Florges", - "skiddo": "Skiddo", - "gogoat": "Gogoat", - "pancham": "Pancham", - "pangoro": "Pangoro", - "furfrou": "Furfrou", - "espurr": "Espurr", - "meowstic": "Meowstic", - "honedge": "Honedge", - "doublade": "Doublade", - "aegislash": "Aegislash", - "spritzee": "Spritzee", - "aromatisse": "Aromatisse", - "swirlix": "Swirlix", - "slurpuff": "Slurpuff", - "inkay": "Inkay", - "malamar": "Malamar", - "binacle": "Binacle", - "barbaracle": "Barbaracle", - "skrelp": "Skrelp", - "dragalge": "Dragalge", - "clauncher": "Clauncher", - "clawitzer": "Clawitzer", - "helioptile": "Helioptile", - "heliolisk": "Heliolisk", - "tyrunt": "Tyrunt", - "tyrantrum": "Tyrantrum", - "amaura": "Amaura", - "aurorus": "Aurorus", - "sylveon": "Sylveon", - "hawlucha": "Hawlucha", + "skiddo": "Mähikel", + "gogoat": "Chevrumm", + "pancham": "Pam-Pam", + "pangoro": "Pandrago", + "furfrou": "Coiffwaff", + "espurr": "Psiau", + "meowstic": "Psiaugon", + "honedge": "Gramokles", + "doublade": "Duokles", + "aegislash": "Durengard", + "spritzee": "Parfi", + "aromatisse": "Parfinesse", + "swirlix": "Flauschling", + "slurpuff": "Sabbaione", + "inkay": "Iscalar", + "malamar": "Calamanero", + "binacle": "Bithora", + "barbaracle": "Thanathora", + "skrelp": "Algitt", + "dragalge": "Tandrak", + "clauncher": "Scampisto", + "clawitzer": "Wummer", + "helioptile": "Eguana", + "heliolisk": "Elezard", + "tyrunt": "Balgoras", + "tyrantrum": "Monargoras", + "amaura": "Amarino", + "aurorus": "Amagarga", + "sylveon": "Feelinara", + "hawlucha": "Resladero", "dedenne": "Dedenne", - "carbink": "Carbink", - "goomy": "Goomy", - "sliggoo": "Sliggoo", - "goodra": "Goodra", - "klefki": "Klefki", - "phantump": "Phantump", - "trevenant": "Trevenant", - "pumpkaboo": "Pumpkaboo", - "gourgeist": "Gourgeist", - "bergmite": "Bergmite", - "avalugg": "Avalugg", - "noibat": "Noibat", - "noivern": "Noivern", + "carbink": "Rocara", + "goomy": "Viscora", + "sliggoo": "Viscargot", + "goodra": "Viscogon", + "klefki": "Clavion", + "phantump": "Paragoni", + "trevenant": "Trombork", + "pumpkaboo": "Irrbis", + "gourgeist": "Pumpdjinn", + "bergmite": "Arktip", + "avalugg": "Arktilas", + "noibat": "eF-eM", + "noivern": "UHaFnir", "xerneas": "Xerneas", "yveltal": "Yveltal", "zygarde": "Zygarde", "diancie": "Diancie", "hoopa": "Hoopa", "volcanion": "Volcanion", - "rowlet": "Rowlet", - "dartrix": "Dartrix", - "decidueye": "Decidueye", - "litten": "Litten", - "torracat": "Torracat", - "incineroar": "Incineroar", - "popplio": "Popplio", - "brionne": "Brionne", - "primarina": "Primarina", - "pikipek": "Pikipek", - "trumbeak": "Trumbeak", - "toucannon": "Toucannon", - "yungoos": "Yungoos", - "gumshoos": "Gumshoos", - "grubbin": "Grubbin", - "charjabug": "Charjabug", - "vikavolt": "Vikavolt", - "crabrawler": "Crabrawler", - "crabominable": "Crabominable", - "oricorio": "Oricorio", - "cutiefly": "Cutiefly", - "ribombee": "Ribombee", - "rockruff": "Rockruff", - "lycanroc": "Lycanroc", - "wishiwashi": "Wishiwashi", - "mareanie": "Mareanie", - "toxapex": "Toxapex", - "mudbray": "Mudbray", - "mudsdale": "Mudsdale", - "dewpider": "Dewpider", - "araquanid": "Araquanid", - "fomantis": "Fomantis", - "lurantis": "Lurantis", - "morelull": "Morelull", - "shiinotic": "Shiinotic", - "salandit": "Salandit", - "salazzle": "Salazzle", - "stufful": "Stufful", - "bewear": "Bewear", - "bounsweet": "Bounsweet", - "steenee": "Steenee", - "tsareena": "Tsareena", - "comfey": "Comfey", - "oranguru": "Oranguru", - "passimian": "Passimian", - "wimpod": "Wimpod", - "golisopod": "Golisopod", - "sandygast": "Sandygast", - "palossand": "Palossand", - "pyukumuku": "Pyukumuku", - "type_null": "Type: Null", - "silvally": "Silvally", - "minior": "Minior", - "komala": "Komala", + "rowlet": "Bauz", + "dartrix": "Arboretoss", + "decidueye": "Silvarro", + "litten": "Flamiau", + "torracat": "Miezunder", + "incineroar": "Fuegro", + "popplio": "Robball", + "brionne": "Marikeck", + "primarina": "Primarene", + "pikipek": "Peppeck", + "trumbeak": "Trompeck", + "toucannon": "Tukanon", + "yungoos": "Mangunior", + "gumshoos": "Manguspektor", + "grubbin": "Mabula", + "charjabug": "Akkup", + "vikavolt": "Donarion", + "crabrawler": "Krabbox", + "crabominable": "Krawell", + "oricorio": "Choreogel", + "cutiefly": "Wommel", + "ribombee": "Bandelby", + "rockruff": "Wuffels", + "lycanroc": "Wolwerock", + "wishiwashi": "Lusardin", + "mareanie": "Garstella", + "toxapex": "Aggrostella", + "mudbray": "Pampuli", + "mudsdale": "Pampross", + "dewpider": "Araqua", + "araquanid": "Aranestro", + "fomantis": "Imantis", + "lurantis": "Mantidea", + "morelull": "Bubungus", + "shiinotic": "Lamellus", + "salandit": "Molunk", + "salazzle": "Amfira", + "stufful": "Velursi", + "bewear": "Kosturso", + "bounsweet": "Frubberl", + "steenee": "Frubaila", + "tsareena": "Fruyal", + "comfey": "Curelei", + "oranguru": "Kommandutan", + "passimian": "Quartermak", + "wimpod": "Reißlaus", + "golisopod": "Tectass", + "sandygast": "Sankabuh", + "palossand": "Colossand", + "pyukumuku": "Gufa", + "type_null": "Typ:Null", + "silvally": "Amigento", + "minior": "Meteno", + "komala": "Koalelu", "turtonator": "Turtonator", "togedemaru": "Togedemaru", - "mimikyu": "Mimikyu", - "bruxish": "Bruxish", - "drampa": "Drampa", - "dhelmise": "Dhelmise", - "jangmo_o": "Jangmo-o", - "hakamo_o": "Hakamo-o", - "kommo_o": "Kommo-o", - "tapu_koko": "Tapu Koko", - "tapu_lele": "Tapu Lele", - "tapu_bulu": "Tapu Bulu", - "tapu_fini": "Tapu Fini", + "mimikyu": "Mimigma", + "bruxish": "Knirfish", + "drampa": "Sen-Long", + "dhelmise": "Moruda", + "jangmo_o": "Miniras", + "hakamo_o": "Mediras", + "kommo_o": "Grandiras", + "tapu_koko": "Kapu-Riki", + "tapu_lele": "Kapu-Fala", + "tapu_bulu": "Kapu-Toro", + "tapu_fini": "Kapu-Kime", "cosmog": "Cosmog", - "cosmoem": "Cosmoem", + "cosmoem": "Cosmovum", "solgaleo": "Solgaleo", "lunala": "Lunala", - "nihilego": "Nihilego", - "buzzwole": "Buzzwole", - "pheromosa": "Pheromosa", - "xurkitree": "Xurkitree", - "celesteela": "Celesteela", - "kartana": "Kartana", - "guzzlord": "Guzzlord", + "nihilego": "Anego", + "buzzwole": "Masskito", + "pheromosa": "Schabelle", + "xurkitree": "Voltriant", + "celesteela": "Kaguron", + "kartana": "Katagami", + "guzzlord": "Schlingking", "necrozma": "Necrozma", "magearna": "Magearna", "marshadow": "Marshadow", - "poipole": "Poipole", - "naganadel": "Naganadel", - "stakataka": "Stakataka", - "blacephalon": "Blacephalon", + "poipole": "Venicro", + "naganadel": "Agoyon", + "stakataka": "Muramura", + "blacephalon": "Kopplosio", "zeraora": "Zeraora", "meltan": "Meltan", "melmetal": "Melmetal", From 70324c415955477a3f569a5c342df4561537da56 Mon Sep 17 00:00:00 2001 From: Greenlamp2 <44787002+Greenlamp2@users.noreply.github.com> Date: Fri, 3 May 2024 18:59:10 +0200 Subject: [PATCH 05/11] Rework - Inputs management to include all gamepad mapping (#390) * rework of the input handling, including different gamepad and keyboard * rework of the input handling, including different gamepad and keyboard * first version of a too complex inputHandler based on phaser3-merged-input * removed useless control management and kept it simple for our use case, investigating to put out button_XX() * renamed inputHandler to inputController * aggregate directions and some action into a same method + fix menu return value * added back repeated input feature on keeping down a key * cleanup + return type * fix submit/action doing two things simultaneously, still same behaviour as before * extracted UI inputs out of battle-scene * tab -> spaces * tab -> spaces what about now github ? --- src/battle-scene.ts | 272 +-------------------- src/configs/pad_dualshock.ts | 29 +++ src/configs/pad_generic.ts | 27 ++ src/configs/pad_unlicensedSNES.ts | 23 ++ src/configs/pad_xbox360.ts | 28 +++ src/inputs-controller.ts | 266 ++++++++++++++++++++ src/ui-inputs.ts | 159 ++++++++++++ src/ui/abstact-option-select-ui-handler.ts | 3 +- src/ui/achvs-ui-handler.ts | 3 +- src/ui/awaitable-ui-handler.ts | 3 +- src/ui/ball-ui-handler.ts | 3 +- src/ui/battle-message-ui-handler.ts | 3 +- src/ui/command-ui-handler.ts | 3 +- src/ui/confirm-ui-handler.ts | 3 +- src/ui/egg-gacha-ui-handler.ts | 3 +- src/ui/egg-hatch-scene-handler.ts | 3 +- src/ui/egg-list-ui-handler.ts | 3 +- src/ui/evolution-scene-handler.ts | 3 +- src/ui/fight-ui-handler.ts | 3 +- src/ui/form-modal-ui-handler.ts | 3 +- src/ui/game-stats-ui-handler.ts | 3 +- src/ui/menu-ui-handler.ts | 3 +- src/ui/modal-ui-handler.ts | 3 +- src/ui/modifier-select-ui-handler.ts | 3 +- src/ui/party-ui-handler.ts | 3 +- src/ui/save-slot-select-ui-handler.ts | 3 +- src/ui/settings-ui-handler.ts | 3 +- src/ui/starter-select-ui-handler.ts | 3 +- src/ui/summary-ui-handler.ts | 3 +- src/ui/target-select-ui-handler.ts | 3 +- src/ui/ui-handler.ts | 3 +- src/ui/ui.ts | 3 +- src/ui/vouchers-ui-handler.ts | 3 +- 33 files changed, 593 insertions(+), 289 deletions(-) create mode 100644 src/configs/pad_dualshock.ts create mode 100644 src/configs/pad_generic.ts create mode 100644 src/configs/pad_unlicensedSNES.ts create mode 100644 src/configs/pad_xbox360.ts create mode 100644 src/inputs-controller.ts create mode 100644 src/ui-inputs.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index cbf363f689a..f6e5a9c5948 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,4 +1,4 @@ -import Phaser, { Time } from 'phaser'; +import Phaser from 'phaser'; import UI, { Mode } from './ui/ui'; import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from './phases'; import Pokemon, { PlayerPokemon, EnemyPokemon } from './field/pokemon'; @@ -54,13 +54,14 @@ import CharSprite from './ui/char-sprite'; import DamageNumberHandler from './field/damage-number-handler'; import PokemonInfoContainer from './ui/pokemon-info-container'; import { biomeDepths } from './data/biomes'; -import { initTouchControls } from './touch-controls'; import { UiTheme } from './enums/ui-theme'; import { SceneBase } from './scene-base'; import CandyBar from './ui/candy-bar'; import { Variant, variantData } from './data/variant'; import { Localizable } from './plugins/i18n'; import { STARTING_WAVE_OVERRIDE, OPP_SPECIES_OVERRIDE, SEED_OVERRIDE, STARTING_BIOME_OVERRIDE } from './overrides'; +import {InputsController} from "./inputs-controller"; +import {UiInputs} from "./ui-inputs"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -69,33 +70,12 @@ const DEBUG_RNG = false; export const startingWave = STARTING_WAVE_OVERRIDE || 1; const expSpriteKeys: string[] = []; -const repeatInputDelayMillis = 250; export let starterColors: StarterColors; interface StarterColors { [key: string]: [string, string] } -export enum Button { - UP, - DOWN, - LEFT, - RIGHT, - SUBMIT, - ACTION, - CANCEL, - MENU, - STATS, - CYCLE_SHINY, - CYCLE_FORM, - CYCLE_GENDER, - CYCLE_ABILITY, - CYCLE_NATURE, - CYCLE_VARIANT, - SPEED_UP, - SLOW_DOWN -} - export interface PokeballCounts { [pb: string]: integer; } @@ -104,6 +84,8 @@ export type AnySound = Phaser.Sound.WebAudioSound | Phaser.Sound.HTML5AudioSound export default class BattleScene extends SceneBase { public rexUI: UIPlugin; + public inputController: InputsController; + public uiInputs: UiInputs; public sessionPlayTime: integer = null; public masterVolume: number = 0.5; @@ -189,34 +171,6 @@ export default class BattleScene extends SceneBase { private bgmResumeTimer: Phaser.Time.TimerEvent; private bgmCache: Set = new Set(); private playTimeTimer: Phaser.Time.TimerEvent; - - private buttonKeys: Phaser.Input.Keyboard.Key[][]; - private lastProcessedButtonPressTimes: Map = new Map(); - // movementButtonLock ensures only a single movement key is firing repeated inputs - // (i.e. by holding down a button) at a time - private movementButtonLock: Button; - - // using a dualshock controller as a map - private gamepadKeyConfig = { - [Button.UP]: 12, // up - [Button.DOWN]: 13, // down - [Button.LEFT]: 14, // left - [Button.RIGHT]: 15, // right - [Button.SUBMIT]: 17, // touchpad - [Button.ACTION]: 0, // X - [Button.CANCEL]: 1, // O - [Button.MENU]: 9, // options - [Button.STATS]: 8, // share - [Button.CYCLE_SHINY]: 5, // RB - [Button.CYCLE_FORM]: 4, // LB - [Button.CYCLE_GENDER]: 6, // LT - [Button.CYCLE_ABILITY]: 7, // RT - [Button.CYCLE_NATURE]: 2, // square - [Button.CYCLE_VARIANT]: 3, // triangle - [Button.SPEED_UP]: 10, // L3 - [Button.SLOW_DOWN]: 11 // R3 - }; - public gamepadButtonStates: boolean[] = new Array(17).fill(false); public rngCounter: integer = 0; public rngSeedOverride: string = ''; @@ -246,7 +200,9 @@ export default class BattleScene extends SceneBase { this.load.atlas(key, `images/pokemon/${variant ? 'variant/' : ''}${experimental ? 'exp/' : ''}${atlasPath}.png`, `images/pokemon/${variant ? 'variant/' : ''}${experimental ? 'exp/' : ''}${atlasPath}.json`); } - async preload() { + async preload() { + this.load.scenePlugin('inputController', InputsController); + this.load.scenePlugin('uiInputs', UiInputs); if (DEBUG_RNG) { const scene = this; const originalRealInRange = Phaser.Math.RND.realInRange; @@ -260,7 +216,7 @@ export default class BattleScene extends SceneBase { return ret; }; } - + populateAnims(); await this.initVariantData(); @@ -273,8 +229,6 @@ export default class BattleScene extends SceneBase { addUiThemeOverrides(this); - this.setupControls(); - this.load.setBaseURL(); this.spritePipeline = new SpritePipeline(this.game); @@ -287,7 +241,6 @@ export default class BattleScene extends SceneBase { } update() { - this.checkInput(); this.ui?.update(); } @@ -606,42 +559,6 @@ export default class BattleScene extends SceneBase { return true; } - setupControls() { - const keyCodes = Phaser.Input.Keyboard.KeyCodes; - const keyConfig = { - [Button.UP]: [keyCodes.UP, keyCodes.W], - [Button.DOWN]: [keyCodes.DOWN, keyCodes.S], - [Button.LEFT]: [keyCodes.LEFT, keyCodes.A], - [Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D], - [Button.SUBMIT]: [keyCodes.ENTER], - [Button.ACTION]: [keyCodes.SPACE, keyCodes.ENTER, keyCodes.Z], - [Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X], - [Button.MENU]: [keyCodes.ESC, keyCodes.M], - [Button.STATS]: [keyCodes.SHIFT, keyCodes.C], - [Button.CYCLE_SHINY]: [keyCodes.R], - [Button.CYCLE_FORM]: [keyCodes.F], - [Button.CYCLE_GENDER]: [keyCodes.G], - [Button.CYCLE_ABILITY]: [keyCodes.E], - [Button.CYCLE_NATURE]: [keyCodes.N], - [Button.CYCLE_VARIANT]: [keyCodes.V], - [Button.SPEED_UP]: [keyCodes.PLUS], - [Button.SLOW_DOWN]: [keyCodes.MINUS] - }; - const mobileKeyConfig = {}; - this.buttonKeys = []; - for (let b of Utils.getEnumValues(Button)) { - const keys: Phaser.Input.Keyboard.Key[] = []; - if (keyConfig.hasOwnProperty(b)) { - for (let k of keyConfig[b]) - keys.push(this.input.keyboard.addKey(k, false)); - mobileKeyConfig[Button[b]] = keys[0]; - } - this.buttonKeys[b] = keys; - } - - initTouchControls(mobileKeyConfig); - } - getParty(): PlayerPokemon[] { return this.party; } @@ -1343,177 +1260,6 @@ export default class BattleScene extends SceneBase { return biomes[Utils.randSeedInt(biomes.length)]; } - checkInput(): boolean { - let inputSuccess = false; - let vibrationLength = 0; - if (this.buttonJustPressed(Button.UP) || this.repeatInputDurationJustPassed(Button.UP)) { - inputSuccess = this.ui.processInput(Button.UP); - vibrationLength = 5; - this.setLastProcessedMovementTime(Button.UP) - } else if (this.buttonJustPressed(Button.DOWN) || this.repeatInputDurationJustPassed(Button.DOWN)) { - inputSuccess = this.ui.processInput(Button.DOWN); - vibrationLength = 5; - this.setLastProcessedMovementTime(Button.DOWN) - } else if (this.buttonJustPressed(Button.LEFT) || this.repeatInputDurationJustPassed(Button.LEFT)) { - inputSuccess = this.ui.processInput(Button.LEFT); - vibrationLength = 5; - this.setLastProcessedMovementTime(Button.LEFT) - } else if (this.buttonJustPressed(Button.RIGHT) || this.repeatInputDurationJustPassed(Button.RIGHT)) { - inputSuccess = this.ui.processInput(Button.RIGHT); - vibrationLength = 5; - this.setLastProcessedMovementTime(Button.RIGHT) - } else if (this.buttonJustPressed(Button.SUBMIT) || this.repeatInputDurationJustPassed(Button.SUBMIT)) { - inputSuccess = this.ui.processInput(Button.SUBMIT) || this.ui.processInput(Button.ACTION); - this.setLastProcessedMovementTime(Button.SUBMIT); - } else if (this.buttonJustPressed(Button.ACTION) || this.repeatInputDurationJustPassed(Button.ACTION)) { - inputSuccess = this.ui.processInput(Button.ACTION); - this.setLastProcessedMovementTime(Button.ACTION); - } else if (this.buttonJustPressed(Button.CANCEL)|| this.repeatInputDurationJustPassed(Button.CANCEL)) { - inputSuccess = this.ui.processInput(Button.CANCEL); - this.setLastProcessedMovementTime(Button.CANCEL); - } else if (this.buttonJustPressed(Button.MENU)) { - if (this.disableMenu) - return; - switch (this.ui?.getMode()) { - case Mode.MESSAGE: - if (!(this.ui.getHandler() as MessageUiHandler).pendingPrompt) - return; - case Mode.TITLE: - case Mode.COMMAND: - case Mode.FIGHT: - case Mode.BALL: - case Mode.TARGET_SELECT: - case Mode.SAVE_SLOT: - case Mode.PARTY: - case Mode.SUMMARY: - case Mode.STARTER_SELECT: - case Mode.CONFIRM: - case Mode.OPTION_SELECT: - this.ui.setOverlayMode(Mode.MENU); - inputSuccess = true; - break; - case Mode.MENU: - case Mode.SETTINGS: - case Mode.ACHIEVEMENTS: - this.ui.revertMode(); - this.playSound('select'); - inputSuccess = true; - break; - default: - return; - } - } else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) { - if (this.buttonJustPressed(Button.CYCLE_SHINY)) { - inputSuccess = this.ui.processInput(Button.CYCLE_SHINY); - this.setLastProcessedMovementTime(Button.CYCLE_SHINY); - } else if (this.buttonJustPressed(Button.CYCLE_FORM)) { - inputSuccess = this.ui.processInput(Button.CYCLE_FORM); - this.setLastProcessedMovementTime(Button.CYCLE_FORM); - } else if (this.buttonJustPressed(Button.CYCLE_GENDER)) { - inputSuccess = this.ui.processInput(Button.CYCLE_GENDER); - this.setLastProcessedMovementTime(Button.CYCLE_GENDER); - } else if (this.buttonJustPressed(Button.CYCLE_ABILITY)) { - inputSuccess = this.ui.processInput(Button.CYCLE_ABILITY); - this.setLastProcessedMovementTime(Button.CYCLE_ABILITY); - } else if (this.buttonJustPressed(Button.CYCLE_NATURE)) { - inputSuccess = this.ui.processInput(Button.CYCLE_NATURE); - this.setLastProcessedMovementTime(Button.CYCLE_NATURE); - } else if (this.buttonJustPressed(Button.CYCLE_VARIANT)) { - inputSuccess = this.ui.processInput(Button.CYCLE_VARIANT); - this.setLastProcessedMovementTime(Button.CYCLE_VARIANT); - } else - return; - } else if (this.buttonJustPressed(Button.SPEED_UP)) { - if (this.gameSpeed < 5) { - this.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) + 1); - if (this.ui?.getMode() === Mode.SETTINGS) - (this.ui.getHandler() as SettingsUiHandler).show([]); - } - } else if (this.buttonJustPressed(Button.SLOW_DOWN)) { - if (this.gameSpeed > 1) { - this.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) - 1, 0)); - if (this.ui?.getMode() === Mode.SETTINGS) - (this.ui.getHandler() as SettingsUiHandler).show([]); - } - } else { - let pressed = false; - if (this.ui && (this.buttonJustReleased(Button.STATS) || (pressed = this.buttonJustPressed(Button.STATS)))) { - for (let p of this.getField().filter(p => p?.isActive(true))) - p.toggleStats(pressed); - if (pressed) - this.setLastProcessedMovementTime(Button.STATS); - } else - return; - } - if (inputSuccess && this.enableVibration && typeof navigator.vibrate !== 'undefined') - navigator.vibrate(vibrationLength || 10); - } - - /** - * gamepadButtonJustDown returns true if @param button has just been pressed down - * or not. It will only return true once, until the key is released and pressed down - * again. - */ - gamepadButtonJustDown(button: Phaser.Input.Gamepad.Button): boolean { - if (!button || !this.gamepadSupport) - return false; - - let ret = false; - if (button.pressed) { - if (!this.gamepadButtonStates[button.index]) - ret = true; - this.gamepadButtonStates[button.index] = true; - } else - this.gamepadButtonStates[button.index] = false; - - return ret; - } - - buttonJustPressed(button: Button): boolean { - const gamepad = this.input.gamepad?.gamepads[0]; - return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustDown(k)) || this.gamepadButtonJustDown(gamepad?.buttons[this.gamepadKeyConfig[button]]); - } - - /** - * gamepadButtonJustUp returns true if @param button has just been released - * or not. It will only return true once, until the key is released and pressed down - * again. - */ - gamepadButtonJustUp(button: Phaser.Input.Gamepad.Button): boolean { - if (!button || !this.gamepadSupport) - return false; - - return !this.gamepadButtonStates[button.index]; - } - - buttonJustReleased(button: Button): boolean { - const gamepad = this.input.gamepad?.gamepads[0]; - return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustUp(k)) || this.gamepadButtonJustUp(gamepad?.buttons[this.gamepadKeyConfig[button]]); - } - - /** - * repeatInputDurationJustPassed returns true if @param button has been held down long - * enough to fire a repeated input. A button must claim the movementButtonLock before - * firing a repeated input - this is to prevent multiple buttons from firing repeatedly. - */ - repeatInputDurationJustPassed(button: Button): boolean { - if (this.movementButtonLock !== null && this.movementButtonLock !== button) { - return false; - } - if (this.buttonKeys[button].every(k => k.isUp) && this.gamepadButtonStates.every(b => b == false)) { - this.movementButtonLock = null; - return false; - } - if (this.time.now - this.lastProcessedButtonPressTimes.get(button) >= repeatInputDelayMillis) { - return true; - } - } - - setLastProcessedMovementTime(button: Button) { - this.lastProcessedButtonPressTimes.set(button, this.time.now); - this.movementButtonLock = button; - } - isBgmPlaying(): boolean { return this.bgm && this.bgm.isPlaying; } diff --git a/src/configs/pad_dualshock.ts b/src/configs/pad_dualshock.ts new file mode 100644 index 00000000000..4f66ff8c00a --- /dev/null +++ b/src/configs/pad_dualshock.ts @@ -0,0 +1,29 @@ +/** + * Dualshock mapping + */ +const pad_dualshock = { + padID: 'Dualshock', + padType: 'Sony', + gamepadMapping: { + RC_S: 0, + RC_E: 1, + RC_W: 2, + RC_N: 3, + START: 9, // Options + SELECT: 8, // Share + LB: 4, + RB: 5, + LT: 6, + RT: 7, + LS: 10, + RS: 11, + LC_N: 12, + LC_S: 13, + LC_W: 14, + LC_E: 15, + MENU: 16, + TOUCH: 17 + }, +}; + +export default pad_dualshock; diff --git a/src/configs/pad_generic.ts b/src/configs/pad_generic.ts new file mode 100644 index 00000000000..19b5d3df16e --- /dev/null +++ b/src/configs/pad_generic.ts @@ -0,0 +1,27 @@ +/** + * Generic pad mapping + */ +const pad_generic = { + padID: 'Generic', + padType: 'generic', + gamepadMapping: { + RC_S: 0, + RC_E: 1, + RC_W: 2, + RC_N: 3, + START: 9, + SELECT: 8, + LB: 4, + RB: 5, + LT: 6, + RT: 7, + LS: 10, + RS: 11, + LC_N: 12, + LC_S: 13, + LC_W: 14, + LC_E: 15 + }, +}; + +export default pad_generic; diff --git a/src/configs/pad_unlicensedSNES.ts b/src/configs/pad_unlicensedSNES.ts new file mode 100644 index 00000000000..ba8ee538d30 --- /dev/null +++ b/src/configs/pad_unlicensedSNES.ts @@ -0,0 +1,23 @@ +/** + * 081f-e401 - UnlicensedSNES + */ +const pad_unlicensedSNES = { + padID: '081f-e401', + padType: 'snes', + gamepadMapping : { + RC_S: 2, + RC_E: 1, + RC_W: 3, + RC_N: 0, + START: 9, + SELECT: 8, + LB: 4, + RB: 5, + LC_N: 12, + LC_S: 13, + LC_W: 14, + LC_E: 15 + } +}; + +export default pad_unlicensedSNES; diff --git a/src/configs/pad_xbox360.ts b/src/configs/pad_xbox360.ts new file mode 100644 index 00000000000..e44ebb54b64 --- /dev/null +++ b/src/configs/pad_xbox360.ts @@ -0,0 +1,28 @@ +/** + * Generic pad mapping + */ +const pad_xbox360 = { + padID: 'Xbox 360 controller (XInput STANDARD GAMEPAD)', + padType: 'xbox', + gamepadMapping: { + RC_S: 0, + RC_E: 1, + RC_W: 2, + RC_N: 3, + START: 9, + SELECT: 8, + LB: 4, + RB: 5, + LT: 6, + RT: 7, + LS: 10, + RS: 11, + LC_N: 12, + LC_S: 13, + LC_W: 14, + LC_E: 15, + MENU: 16 + }, +}; + +export default pad_xbox360; diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts new file mode 100644 index 00000000000..441649a5199 --- /dev/null +++ b/src/inputs-controller.ts @@ -0,0 +1,266 @@ +import Phaser, {Time} from "phaser"; +import * as Utils from "./utils"; +import {initTouchControls} from './touch-controls'; +import pad_generic from "#app/configs/pad_generic"; +import pad_unlicensedSNES from "#app/configs/pad_unlicensedSNES"; +import pad_xbox360 from "#app/configs/pad_xbox360"; +import pad_dualshock from "#app/configs/pad_dualshock"; + + +export enum Button { + UP, + DOWN, + LEFT, + RIGHT, + SUBMIT, + ACTION, + CANCEL, + MENU, + STATS, + CYCLE_SHINY, + CYCLE_FORM, + CYCLE_GENDER, + CYCLE_ABILITY, + CYCLE_NATURE, + CYCLE_VARIANT, + SPEED_UP, + SLOW_DOWN +} + +const repeatInputDelayMillis = 250; + +export class InputsController extends Phaser.Plugins.ScenePlugin { + private game: Phaser.Game; + private buttonKeys: Phaser.Input.Keyboard.Key[][]; + private gamepads: Array = new Array(); + private scene: Phaser.Scene; + + // buttonLock ensures only a single movement key is firing repeated inputs + // (i.e. by holding down a button) at a time + private buttonLock: Button; + private interactions: Map> = new Map(); + private time: Time; + + constructor(scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) { + super(scene, pluginManager, pluginKey); + this.game = pluginManager.game; + this.scene = scene; + this.time = this.scene.time; + this.buttonKeys = []; + + for (const b of Utils.getEnumValues(Button)) { + this.interactions[b] = { + pressTime: false, + isPressed: false, + } + } + // We don't want the menu key to be repeated + delete this.interactions[Button.MENU]; + } + + boot() { + this.eventEmitter = this.systems.events; + this.events = new Phaser.Events.EventEmitter(); + this.game.events.on(Phaser.Core.Events.STEP, this.update, this); + + if (typeof this.systems.input.gamepad !== 'undefined') { + this.systems.input.gamepad.on('connected', function (thisGamepad) { + this.refreshGamepads(); + this.setupGamepad(thisGamepad); + }, this); + + // Check to see if the gamepad has already been setup by the browser + this.systems.input.gamepad.refreshPads(); + if (this.systems.input.gamepad.total) { + this.refreshGamepads(); + for (const thisGamepad of this.gamepads) { + this.systems.input.gamepad.emit('connected', thisGamepad); + } + } + + this.systems.input.gamepad.on('down', this.gamepadButtonDown, this); + this.systems.input.gamepad.on('up', this.gamepadButtonUp, this); + } + + // Keyboard + this.setupKeyboardControls(); + } + + update() { + for (const b of Utils.getEnumValues(Button)) { + if (!this.interactions.hasOwnProperty(b)) continue; + if (this.repeatInputDurationJustPassed(b)) { + this.events.emit('input_down', { + controller_type: 'keyboard', + button: b, + }); + this.setLastProcessedMovementTime(b); + } + } + } + + setupGamepad(thisGamepad): void { + let gamepadID = thisGamepad.id.toLowerCase(); + const mappedPad = this.mapGamepad(gamepadID); + this.player = { + 'mapping': mappedPad.gamepadMapping, + } + } + + refreshGamepads(): void { + // Sometimes, gamepads are undefined. For some reason. + this.gamepads = this.systems.input.gamepad.gamepads.filter(function (el) { + return el != null; + }); + + for (const [index, thisGamepad] of this.gamepads.entries()) { + thisGamepad.index = index; // Overwrite the gamepad index, in case we had undefined gamepads earlier + } + } + + getActionGamepadMapping() { + const gamepadMapping = {}; + gamepadMapping[this.player.mapping.LC_N] = Button.UP; + gamepadMapping[this.player.mapping.LC_S] = Button.DOWN; + gamepadMapping[this.player.mapping.LC_W] = Button.LEFT; + gamepadMapping[this.player.mapping.LC_E] = Button.RIGHT; + gamepadMapping[this.player.mapping.TOUCH] = Button.SUBMIT; + gamepadMapping[this.player.mapping.RC_S] = Button.ACTION; + gamepadMapping[this.player.mapping.RC_E] = Button.CANCEL; + gamepadMapping[this.player.mapping.SELECT] = Button.STATS; + gamepadMapping[this.player.mapping.START] = Button.MENU; + gamepadMapping[this.player.mapping.RB] = Button.CYCLE_SHINY; + gamepadMapping[this.player.mapping.LB] = Button.CYCLE_FORM; + gamepadMapping[this.player.mapping.LT] = Button.CYCLE_GENDER; + gamepadMapping[this.player.mapping.RT] = Button.CYCLE_ABILITY; + gamepadMapping[this.player.mapping.RC_W] = Button.CYCLE_NATURE; + gamepadMapping[this.player.mapping.RC_N] = Button.CYCLE_VARIANT; + gamepadMapping[this.player.mapping.LS] = Button.SPEED_UP; + gamepadMapping[this.player.mapping.RS] = Button.SLOW_DOWN; + + return gamepadMapping; + } + + gamepadButtonDown(pad, button, value): void { + const actionMapping = this.getActionGamepadMapping(); + const buttonDown = actionMapping.hasOwnProperty(button.index) && actionMapping[button.index]; + if (buttonDown !== undefined) { + this.events.emit('input_down', { + controller_type: 'gamepad', + button: buttonDown, + }); + this.setLastProcessedMovementTime(buttonDown); + } + } + + gamepadButtonUp(pad, button, value): void { + const actionMapping = this.getActionGamepadMapping(); + const buttonUp = actionMapping.hasOwnProperty(button.index) && actionMapping[button.index]; + if (buttonUp !== undefined) { + this.events.emit('input_up', { + controller_type: 'gamepad', + button: buttonUp, + }); + this.delLastProcessedMovementTime(buttonUp); + } + } + + setupKeyboardControls(): void { + const keyCodes = Phaser.Input.Keyboard.KeyCodes; + const keyConfig = { + [Button.UP]: [keyCodes.UP, keyCodes.W], + [Button.DOWN]: [keyCodes.DOWN, keyCodes.S], + [Button.LEFT]: [keyCodes.LEFT, keyCodes.A], + [Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D], + [Button.SUBMIT]: [keyCodes.ENTER], + [Button.ACTION]: [keyCodes.SPACE, keyCodes.Z], + [Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X], + [Button.MENU]: [keyCodes.ESC, keyCodes.M], + [Button.STATS]: [keyCodes.SHIFT, keyCodes.C], + [Button.CYCLE_SHINY]: [keyCodes.R], + [Button.CYCLE_FORM]: [keyCodes.F], + [Button.CYCLE_GENDER]: [keyCodes.G], + [Button.CYCLE_ABILITY]: [keyCodes.E], + [Button.CYCLE_NATURE]: [keyCodes.N], + [Button.CYCLE_VARIANT]: [keyCodes.V], + [Button.SPEED_UP]: [keyCodes.PLUS], + [Button.SLOW_DOWN]: [keyCodes.MINUS] + }; + const mobileKeyConfig = {}; + for (const b of Utils.getEnumValues(Button)) { + const keys: Phaser.Input.Keyboard.Key[] = []; + if (keyConfig.hasOwnProperty(b)) { + for (let k of keyConfig[b]) + keys.push(this.systems.input.keyboard.addKey(k, false)); + mobileKeyConfig[Button[b]] = keys[0]; + } + this.buttonKeys[b] = keys; + } + + initTouchControls(mobileKeyConfig); + this.listenInputKeyboard(); + } + + listenInputKeyboard(): void { + this.buttonKeys.forEach((row, index) => { + for (const key of row) { + key.on('down', () => { + this.events.emit('input_down', { + controller_type: 'keyboard', + button: index, + }); + this.setLastProcessedMovementTime(index); + }); + key.on('up', () => { + this.events.emit('input_up', { + controller_type: 'keyboard', + button: index, + }); + this.delLastProcessedMovementTime(index); + }); + } + }); + } + + mapGamepad(id) { + id = id.toLowerCase(); + let padConfig = pad_generic; + + if (id.includes('081f') && id.includes('e401')) { + padConfig = pad_unlicensedSNES; + } else if (id.includes('xbox') && id.includes('360')) { + padConfig = pad_xbox360; + } else if (id.includes('054c')) { + padConfig = pad_dualshock; + } + + return padConfig; + } + + /** + * repeatInputDurationJustPassed returns true if @param button has been held down long + * enough to fire a repeated input. A button must claim the buttonLock before + * firing a repeated input - this is to prevent multiple buttons from firing repeatedly. + */ + repeatInputDurationJustPassed(button: Button): boolean { + if (this.buttonLock === null || this.buttonLock !== button) { + return false; + } + if (this.time.now - this.interactions[button].pressTime >= repeatInputDelayMillis) { + this.buttonLock = null; + return true; + } + } + + setLastProcessedMovementTime(button: Button): void { + if (!this.interactions.hasOwnProperty(button)) return; + this.buttonLock = button; + this.interactions[button].pressTime = this.time.now; + } + + delLastProcessedMovementTime(button: Button): void { + if (!this.interactions.hasOwnProperty(button)) return; + this.buttonLock = null; + this.interactions[button].pressTime = null; + } +} \ No newline at end of file diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts new file mode 100644 index 00000000000..bcdcc34b1fa --- /dev/null +++ b/src/ui-inputs.ts @@ -0,0 +1,159 @@ +import Phaser from "phaser"; +import UI, {Mode} from "./ui/ui"; +import {Button} from "#app/inputs-controller"; +import MessageUiHandler from "#app/ui/message-ui-handler"; +import StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; +import {Setting, settingOptions} from "#app/system/settings"; +import SettingsUiHandler from "#app/ui/settings-ui-handler"; + + +export class UiInputs extends Phaser.Plugins.ScenePlugin { + private game: Phaser.Game; + private scene: Phaser.Scene; + private events; + + constructor(scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) { + super(scene, pluginManager, pluginKey); + this.game = pluginManager.game; + this.scene = scene; + this.events = this.scene.inputController.events + } + + boot() { + this.listenInputs(); + } + + listenInputs(): void { + this.events.on('input_down', (event) => { + const actions = this.getActionsKeyDown(); + if (!actions.hasOwnProperty(event.button)) return; + const [inputSuccess, vibrationLength] = actions[event.button](); + if (inputSuccess && this.scene.enableVibration && typeof navigator.vibrate !== 'undefined') + navigator.vibrate(vibrationLength); + }, this); + + this.events.on('input_up', (event) => { + const actions = this.getActionsKeyUp(); + if (!actions.hasOwnProperty(event.button)) return; + const [inputSuccess, vibrationLength] = actions[event.button](); + if (inputSuccess && this.scene.enableVibration && typeof navigator.vibrate !== 'undefined') + navigator.vibrate(vibrationLength); + }, this); + } + + getActionsKeyDown() { + const actions = {}; + actions[Button.UP] = () => this.buttonDirection(Button.UP); + actions[Button.DOWN] = () => this.buttonDirection(Button.DOWN); + actions[Button.LEFT] = () => this.buttonDirection(Button.LEFT); + actions[Button.RIGHT] = () => this.buttonDirection(Button.RIGHT); + actions[Button.SUBMIT] = () => this.buttonTouch(); + actions[Button.ACTION] = () => this.buttonAb(Button.ACTION); + actions[Button.CANCEL] = () => this.buttonAb(Button.CANCEL); + actions[Button.MENU] = () => this.buttonMenu(); + actions[Button.STATS] = () => this.buttonStats(true); + actions[Button.CYCLE_SHINY] = () => this.buttonCycleOption(Button.CYCLE_SHINY); + actions[Button.CYCLE_FORM] = () => this.buttonCycleOption(Button.CYCLE_FORM); + actions[Button.CYCLE_GENDER] = () => this.buttonCycleOption(Button.CYCLE_GENDER); + actions[Button.CYCLE_ABILITY] = () => this.buttonCycleOption(Button.CYCLE_ABILITY); + actions[Button.CYCLE_NATURE] = () => this.buttonCycleOption(Button.CYCLE_NATURE); + actions[Button.CYCLE_VARIANT] = () => this.buttonCycleOption(Button.CYCLE_VARIANT); + actions[Button.SPEED_UP] = () => this.buttonSpeedChange(); + actions[Button.SLOW_DOWN] = () => this.buttonSpeedChange(false); + return actions; + } + + getActionsKeyUp() { + const actions = {}; + actions[Button.STATS] = () => this.buttonStats(false); + return actions; + } + + buttonDirection(direction): Array { + const inputSuccess = this.scene.ui.processInput(direction); + const vibrationLength = 5; + return [inputSuccess, vibrationLength]; + } + + buttonAb(button): Array { + const inputSuccess = this.scene.ui.processInput(button); + return [inputSuccess, 0]; + } + + buttonTouch(): Array { + const inputSuccess = this.scene.ui.processInput(Button.SUBMIT) || this.scene.ui.processInput(Button.ACTION); + return [inputSuccess, 0]; + } + + buttonStats(pressed = true): Array { + if (pressed) { + for (let p of this.scene.getField().filter(p => p?.isActive(true))) + p.toggleStats(true); + } else { + for (let p of this.scene.getField().filter(p => p?.isActive(true))) + p.toggleStats(false); + } + return [true, 0]; + } + + buttonMenu(): Array { + let inputSuccess; + if (this.scene.disableMenu) + return [true, 0]; + switch (this.scene.ui?.getMode()) { + case Mode.MESSAGE: + if (!(this.scene.ui.getHandler() as MessageUiHandler).pendingPrompt) + return [true, 0]; + case Mode.TITLE: + case Mode.COMMAND: + case Mode.FIGHT: + case Mode.BALL: + case Mode.TARGET_SELECT: + case Mode.SAVE_SLOT: + case Mode.PARTY: + case Mode.SUMMARY: + case Mode.STARTER_SELECT: + case Mode.CONFIRM: + case Mode.OPTION_SELECT: + this.scene.ui.setOverlayMode(Mode.MENU); + inputSuccess = true; + break; + case Mode.MENU: + case Mode.SETTINGS: + case Mode.ACHIEVEMENTS: + this.scene.ui.revertMode(); + this.scene.playSound('select'); + inputSuccess = true; + break; + default: + return [true, 0]; + } + return [inputSuccess, 0]; + } + + buttonCycleOption(button): Array { + let inputSuccess; + if (this.scene.ui?.getHandler() instanceof StarterSelectUiHandler) { + inputSuccess = this.scene.ui.processInput(button); + } + return [inputSuccess, 0]; + } + + buttonSpeedChange(up = true): Array { + if (up) { + if (this.scene.gameSpeed < 5) { + this.scene.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) + 1); + if (this.scene.ui?.getMode() === Mode.SETTINGS) + (this.scene.ui.getHandler() as SettingsUiHandler).show([]); + } + return [0, 0]; + } + if (this.scene.gameSpeed > 1) { + this.scene.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) - 1, 0)); + if (this.scene.ui?.getMode() === Mode.SETTINGS) + (this.scene.ui.getHandler() as SettingsUiHandler).show([]); + } + return [0, 0]; + } + +} \ No newline at end of file diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index 2f2c4face89..ac2dca03ed3 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -1,10 +1,11 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import * as Utils from "../utils"; import { argbFromRgba } from "@material/material-color-utilities"; +import {Button} from "#app/inputs-controller"; export interface OptionSelectConfig { xOffset?: number; diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index cadda64e032..778a5d5f131 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -1,9 +1,10 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { Achv, achvs } from "../system/achv"; import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; +import {Button} from "#app/inputs-controller"; export default class AchvsUiHandler extends MessageUiHandler { private achvsContainer: Phaser.GameObjects.Container; diff --git a/src/ui/awaitable-ui-handler.ts b/src/ui/awaitable-ui-handler.ts index e8cc979e423..5d5f0b7a094 100644 --- a/src/ui/awaitable-ui-handler.ts +++ b/src/ui/awaitable-ui-handler.ts @@ -1,6 +1,7 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; +import {Button} from "#app/inputs-controller"; export default abstract class AwaitableUiHandler extends UiHandler { protected awaitingActionInput: boolean; diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index f2ebdc342a4..a4ee6c99d46 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -1,11 +1,12 @@ import { CommandPhase } from "../phases"; -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { getPokeballName } from "../data/pokeball"; import { addTextObject, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; +import {Button} from "#app/inputs-controller"; export default class BallUiHandler extends UiHandler { private pokeballSelectContainer: Phaser.GameObjects.Container; diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index 5e2cb56518f..7bba9657cd9 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; import { Mode } from "./ui"; import * as Utils from "../utils"; @@ -6,6 +6,7 @@ import MessageUiHandler from "./message-ui-handler"; import { getStatName, Stat } from "../data/pokemon-stat"; import { addWindow } from "./ui-theme"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; +import {Button} from "#app/inputs-controller"; export default class BattleMessageUiHandler extends MessageUiHandler { private levelUpStatsContainer: Phaser.GameObjects.Container; diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index b8223694b4c..4c49246ebb6 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -1,10 +1,11 @@ import { CommandPhase } from "../phases"; -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { addTextObject, TextStyle } from "./text"; import PartyUiHandler, { PartyUiMode } from "./party-ui-handler"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import i18next from '../plugins/i18n'; +import {Button} from "#app/inputs-controller"; export enum Command { FIGHT = 0, diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/confirm-ui-handler.ts index d9e7726d826..600a84e614d 100644 --- a/src/ui/confirm-ui-handler.ts +++ b/src/ui/confirm-ui-handler.ts @@ -1,6 +1,7 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import AbstractOptionSelectUiHandler, { OptionSelectConfig } from "./abstact-option-select-ui-handler"; import { Mode } from "./ui"; +import {Button} from "#app/inputs-controller"; export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { private switchCheck: boolean; diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index f0e32dbc2e9..0f8b999ba59 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { Mode } from "./ui"; import { TextStyle, addTextObject, getEggTierTextTint } from "./text"; import MessageUiHandler from "./message-ui-handler"; @@ -9,6 +9,7 @@ import { getPokemonSpecies } from "../data/pokemon-species"; import { addWindow } from "./ui-theme"; import { Tutorial, handleTutorial } from "../tutorial"; import { EggTier } from "../data/enums/egg-type"; +import {Button} from "#app/inputs-controller"; const defaultText = 'Select a machine.'; diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index f841bafc268..fefd5197be2 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -1,7 +1,8 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { EggHatchPhase } from "../egg-hatch-phase"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; +import {Button} from "#app/inputs-controller"; export default class EggHatchSceneHandler extends UiHandler { public eggHatchContainer: Phaser.GameObjects.Container; diff --git a/src/ui/egg-list-ui-handler.ts b/src/ui/egg-list-ui-handler.ts index 7537b8deeb0..8271ee00788 100644 --- a/src/ui/egg-list-ui-handler.ts +++ b/src/ui/egg-list-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { Mode } from "./ui"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; import { TextStyle, addTextObject } from "./text"; @@ -6,6 +6,7 @@ import MessageUiHandler from "./message-ui-handler"; import { EGG_SEED, Egg, GachaType, getEggGachaTypeDescriptor, getEggHatchWavesMessage, getEggDescriptor } from "../data/egg"; import * as Utils from "../utils"; import { addWindow } from "./ui-theme"; +import {Button} from "#app/inputs-controller"; export default class EggListUiHandler extends MessageUiHandler { private eggListContainer: Phaser.GameObjects.Container; diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/evolution-scene-handler.ts index 7e0ef063ea0..21b39b593cf 100644 --- a/src/ui/evolution-scene-handler.ts +++ b/src/ui/evolution-scene-handler.ts @@ -1,7 +1,8 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; +import {Button} from "#app/inputs-controller"; export default class EvolutionSceneHandler extends MessageUiHandler { public evolutionContainer: Phaser.GameObjects.Container; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 1a7a8bef597..b35292ebd45 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { addTextObject, TextStyle } from "./text"; import { Type } from "../data/type"; import { Command } from "./command-ui-handler"; @@ -8,6 +8,7 @@ import * as Utils from "../utils"; import { CommandPhase } from "../phases"; import { MoveCategory } from "#app/data/move.js"; import i18next from '../plugins/i18n'; +import {Button} from "#app/inputs-controller"; export default class FightUiHandler extends UiHandler { private movesContainer: Phaser.GameObjects.Container; diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index ec5f4147c58..4830bd191f9 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { ModalConfig, ModalUiHandler } from "./modal-ui-handler"; import { Mode } from "./ui"; import { TextStyle, addTextInputObject, addTextObject } from "./text"; @@ -6,6 +6,7 @@ import { WindowVariant, addWindow } from "./ui-theme"; import InputText from "phaser3-rex-plugins/plugins/inputtext"; import * as Utils from "../utils"; import i18next from '../plugins/i18n'; +import {Button} from "#app/inputs-controller"; export interface FormModalConfig extends ModalConfig { errorMessage?: string; diff --git a/src/ui/game-stats-ui-handler.ts b/src/ui/game-stats-ui-handler.ts index 00e358ff8e4..5b271159f4a 100644 --- a/src/ui/game-stats-ui-handler.ts +++ b/src/ui/game-stats-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { TextStyle, addTextObject, getTextColor } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; @@ -6,6 +6,7 @@ import { addWindow } from "./ui-theme"; import * as Utils from "../utils"; import { DexAttr, GameData } from "../system/game-data"; import { speciesStarters } from "../data/pokemon-species"; +import {Button} from "#app/inputs-controller"; interface DisplayStat { label?: string; diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 03d93699e75..7ce5302aa0e 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button, bypassLogin } from "../battle-scene"; +import BattleScene, { bypassLogin } from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import * as Utils from "../utils"; @@ -9,6 +9,7 @@ import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui import { Tutorial, handleTutorial } from "../tutorial"; import { updateUserInfo } from "../account"; import i18next from '../plugins/i18n'; +import {Button} from "#app/inputs-controller"; export enum MenuOptions { GAME_SETTINGS, diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index f193a3db54f..507e3292fb9 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -1,8 +1,9 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { WindowVariant, addWindow } from "./ui-theme"; +import {Button} from "#app/inputs-controller"; export interface ModalConfig { buttonActions: Function[]; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index e5252e02a8f..7597503d645 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { getPlayerShopModifierTypeOptionsForWave, ModifierTypeOption } from "../modifier/modifier-type"; import { getPokeballAtlasKey, PokeballType } from "../data/pokeball"; import { addTextObject, getModifierTierTextTint, getTextColor, TextStyle } from "./text"; @@ -6,6 +6,7 @@ import AwaitableUiHandler from "./awaitable-ui-handler"; import { Mode } from "./ui"; import { LockModifierTiersModifier, PokemonHeldItemModifier } from "../modifier/modifier"; import { handleTutorial, Tutorial } from "../tutorial"; +import {Button} from "#app/inputs-controller"; export const SHOP_OPTIONS_ROW_LIMIT = 6; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 35014fa7027..29b0bb85011 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,5 +1,5 @@ import { CommandPhase } from "../phases"; -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { PlayerPokemon, PokemonMove } from "../field/pokemon"; import { addTextObject, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; @@ -16,6 +16,7 @@ import { pokemonEvolutions } from "../data/pokemon-evolutions"; import { addWindow } from "./ui-theme"; import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms"; import { getVariantTint } from "#app/data/variant"; +import {Button} from "#app/inputs-controller"; const defaultMessage = 'Choose a Pokémon.'; diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 181b0643cb9..07a9283f7e8 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { gameModes } from "../game-mode"; import { SessionSaveData } from "../system/game-data"; import { TextStyle, addTextObject } from "./text"; @@ -8,6 +8,7 @@ import * as Utils from "../utils"; import PokemonData from "../system/pokemon-data"; import { PokemonHeldItemModifier } from "../modifier/modifier"; import MessageUiHandler from "./message-ui-handler"; +import {Button} from "#app/inputs-controller"; const sessionSlotCount = 5; diff --git a/src/ui/settings-ui-handler.ts b/src/ui/settings-ui-handler.ts index 8f43b377d21..6b1b7467d67 100644 --- a/src/ui/settings-ui-handler.ts +++ b/src/ui/settings-ui-handler.ts @@ -1,10 +1,11 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { Setting, reloadSettings, settingDefaults, settingOptions } from "../system/settings"; import { hasTouchscreen, isMobile } from "../touch-controls"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; +import {Button} from "#app/inputs-controller"; export default class SettingsUiHandler extends UiHandler { private settingsContainer: Phaser.GameObjects.Container; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index f1e058b12cf..ad3e51d24a2 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button, starterColors } from "../battle-scene"; +import BattleScene, { starterColors } from "../battle-scene"; import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; import { Species } from "../data/enums/species"; import { TextStyle, addBBCodeTextObject, addTextObject } from "./text"; @@ -27,6 +27,7 @@ import { argbFromRgba } from "@material/material-color-utilities"; import { OptionSelectItem } from "./abstact-option-select-ui-handler"; import { pokemonPrevolutions } from "#app/data/pokemon-evolutions"; import { Variant, getVariantTint } from "#app/data/variant"; +import {Button} from "#app/inputs-controller"; export type StarterSelectCallback = (starters: Starter[]) => void; diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index cf5eb3639fd..4b014b83778 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import * as Utils from "../utils"; @@ -17,6 +17,7 @@ import { Nature, getNatureStatMultiplier } from "../data/nature"; import { loggedInUser } from "../account"; import { PlayerGender } from "../system/game-data"; import { Variant, getVariantTint } from "#app/data/variant"; +import {Button} from "#app/inputs-controller"; enum Page { PROFILE, diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index f8a7c9d28a3..b5242e713d7 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -1,10 +1,11 @@ import { BattlerIndex } from "../battle"; -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { Moves } from "../data/enums/moves"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import * as Utils from "../utils"; import { getMoveTargets } from "../data/move"; +import {Button} from "#app/inputs-controller"; export type TargetSelectCallback = (cursor: integer) => void; diff --git a/src/ui/ui-handler.ts b/src/ui/ui-handler.ts index e3c94b76467..20f34a33388 100644 --- a/src/ui/ui-handler.ts +++ b/src/ui/ui-handler.ts @@ -1,6 +1,7 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { TextStyle, getTextColor } from "./text"; import UI, { Mode } from "./ui"; +import {Button} from "#app/inputs-controller"; export default abstract class UiHandler { protected scene: BattleScene; diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 6e20b2cb8b8..0555c54193d 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -1,4 +1,4 @@ -import { Button, default as BattleScene } from '../battle-scene'; +import { default as BattleScene } from '../battle-scene'; import UiHandler from './ui-handler'; import BattleMessageUiHandler from './battle-message-ui-handler'; import CommandUiHandler from './command-ui-handler'; @@ -35,6 +35,7 @@ import SavingIconHandler from './saving-icon-handler'; import UnavailableModalUiHandler from './unavailable-modal-ui-handler'; import OutdatedModalUiHandler from './outdated-modal-ui-handler'; import SessionReloadModalUiHandler from './session-reload-modal-ui-handler'; +import {Button} from "#app/inputs-controller"; export enum Mode { MESSAGE, diff --git a/src/ui/vouchers-ui-handler.ts b/src/ui/vouchers-ui-handler.ts index 3f41cf9ae74..36926c6fa71 100644 --- a/src/ui/vouchers-ui-handler.ts +++ b/src/ui/vouchers-ui-handler.ts @@ -1,9 +1,10 @@ -import BattleScene, { Button } from "../battle-scene"; +import BattleScene from "../battle-scene"; import { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "../system/voucher"; import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; +import {Button} from "#app/inputs-controller"; const itemRows = 4; const itemCols = 17; From 9fc6bdde21dd36e297ece74c1e471f22ba5364c4 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Fri, 3 May 2024 13:04:32 -0400 Subject: [PATCH 06/11] Revert "Rework - Inputs management to include all gamepad mapping (#390)" This reverts commit 70324c415955477a3f569a5c342df4561537da56. --- src/battle-scene.ts | 272 ++++++++++++++++++++- src/configs/pad_dualshock.ts | 29 --- src/configs/pad_generic.ts | 27 -- src/configs/pad_unlicensedSNES.ts | 23 -- src/configs/pad_xbox360.ts | 28 --- src/inputs-controller.ts | 266 -------------------- src/ui-inputs.ts | 159 ------------ src/ui/abstact-option-select-ui-handler.ts | 3 +- src/ui/achvs-ui-handler.ts | 3 +- src/ui/awaitable-ui-handler.ts | 3 +- src/ui/ball-ui-handler.ts | 3 +- src/ui/battle-message-ui-handler.ts | 3 +- src/ui/command-ui-handler.ts | 3 +- src/ui/confirm-ui-handler.ts | 3 +- src/ui/egg-gacha-ui-handler.ts | 3 +- src/ui/egg-hatch-scene-handler.ts | 3 +- src/ui/egg-list-ui-handler.ts | 3 +- src/ui/evolution-scene-handler.ts | 3 +- src/ui/fight-ui-handler.ts | 3 +- src/ui/form-modal-ui-handler.ts | 3 +- src/ui/game-stats-ui-handler.ts | 3 +- src/ui/menu-ui-handler.ts | 3 +- src/ui/modal-ui-handler.ts | 3 +- src/ui/modifier-select-ui-handler.ts | 3 +- src/ui/party-ui-handler.ts | 3 +- src/ui/save-slot-select-ui-handler.ts | 3 +- src/ui/settings-ui-handler.ts | 3 +- src/ui/starter-select-ui-handler.ts | 3 +- src/ui/summary-ui-handler.ts | 3 +- src/ui/target-select-ui-handler.ts | 3 +- src/ui/ui-handler.ts | 3 +- src/ui/ui.ts | 3 +- src/ui/vouchers-ui-handler.ts | 3 +- 33 files changed, 289 insertions(+), 593 deletions(-) delete mode 100644 src/configs/pad_dualshock.ts delete mode 100644 src/configs/pad_generic.ts delete mode 100644 src/configs/pad_unlicensedSNES.ts delete mode 100644 src/configs/pad_xbox360.ts delete mode 100644 src/inputs-controller.ts delete mode 100644 src/ui-inputs.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index f6e5a9c5948..cbf363f689a 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,4 +1,4 @@ -import Phaser from 'phaser'; +import Phaser, { Time } from 'phaser'; import UI, { Mode } from './ui/ui'; import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from './phases'; import Pokemon, { PlayerPokemon, EnemyPokemon } from './field/pokemon'; @@ -54,14 +54,13 @@ import CharSprite from './ui/char-sprite'; import DamageNumberHandler from './field/damage-number-handler'; import PokemonInfoContainer from './ui/pokemon-info-container'; import { biomeDepths } from './data/biomes'; +import { initTouchControls } from './touch-controls'; import { UiTheme } from './enums/ui-theme'; import { SceneBase } from './scene-base'; import CandyBar from './ui/candy-bar'; import { Variant, variantData } from './data/variant'; import { Localizable } from './plugins/i18n'; import { STARTING_WAVE_OVERRIDE, OPP_SPECIES_OVERRIDE, SEED_OVERRIDE, STARTING_BIOME_OVERRIDE } from './overrides'; -import {InputsController} from "./inputs-controller"; -import {UiInputs} from "./ui-inputs"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -70,12 +69,33 @@ const DEBUG_RNG = false; export const startingWave = STARTING_WAVE_OVERRIDE || 1; const expSpriteKeys: string[] = []; +const repeatInputDelayMillis = 250; export let starterColors: StarterColors; interface StarterColors { [key: string]: [string, string] } +export enum Button { + UP, + DOWN, + LEFT, + RIGHT, + SUBMIT, + ACTION, + CANCEL, + MENU, + STATS, + CYCLE_SHINY, + CYCLE_FORM, + CYCLE_GENDER, + CYCLE_ABILITY, + CYCLE_NATURE, + CYCLE_VARIANT, + SPEED_UP, + SLOW_DOWN +} + export interface PokeballCounts { [pb: string]: integer; } @@ -84,8 +104,6 @@ export type AnySound = Phaser.Sound.WebAudioSound | Phaser.Sound.HTML5AudioSound export default class BattleScene extends SceneBase { public rexUI: UIPlugin; - public inputController: InputsController; - public uiInputs: UiInputs; public sessionPlayTime: integer = null; public masterVolume: number = 0.5; @@ -171,6 +189,34 @@ export default class BattleScene extends SceneBase { private bgmResumeTimer: Phaser.Time.TimerEvent; private bgmCache: Set = new Set(); private playTimeTimer: Phaser.Time.TimerEvent; + + private buttonKeys: Phaser.Input.Keyboard.Key[][]; + private lastProcessedButtonPressTimes: Map = new Map(); + // movementButtonLock ensures only a single movement key is firing repeated inputs + // (i.e. by holding down a button) at a time + private movementButtonLock: Button; + + // using a dualshock controller as a map + private gamepadKeyConfig = { + [Button.UP]: 12, // up + [Button.DOWN]: 13, // down + [Button.LEFT]: 14, // left + [Button.RIGHT]: 15, // right + [Button.SUBMIT]: 17, // touchpad + [Button.ACTION]: 0, // X + [Button.CANCEL]: 1, // O + [Button.MENU]: 9, // options + [Button.STATS]: 8, // share + [Button.CYCLE_SHINY]: 5, // RB + [Button.CYCLE_FORM]: 4, // LB + [Button.CYCLE_GENDER]: 6, // LT + [Button.CYCLE_ABILITY]: 7, // RT + [Button.CYCLE_NATURE]: 2, // square + [Button.CYCLE_VARIANT]: 3, // triangle + [Button.SPEED_UP]: 10, // L3 + [Button.SLOW_DOWN]: 11 // R3 + }; + public gamepadButtonStates: boolean[] = new Array(17).fill(false); public rngCounter: integer = 0; public rngSeedOverride: string = ''; @@ -200,9 +246,7 @@ export default class BattleScene extends SceneBase { this.load.atlas(key, `images/pokemon/${variant ? 'variant/' : ''}${experimental ? 'exp/' : ''}${atlasPath}.png`, `images/pokemon/${variant ? 'variant/' : ''}${experimental ? 'exp/' : ''}${atlasPath}.json`); } - async preload() { - this.load.scenePlugin('inputController', InputsController); - this.load.scenePlugin('uiInputs', UiInputs); + async preload() { if (DEBUG_RNG) { const scene = this; const originalRealInRange = Phaser.Math.RND.realInRange; @@ -216,7 +260,7 @@ export default class BattleScene extends SceneBase { return ret; }; } - + populateAnims(); await this.initVariantData(); @@ -229,6 +273,8 @@ export default class BattleScene extends SceneBase { addUiThemeOverrides(this); + this.setupControls(); + this.load.setBaseURL(); this.spritePipeline = new SpritePipeline(this.game); @@ -241,6 +287,7 @@ export default class BattleScene extends SceneBase { } update() { + this.checkInput(); this.ui?.update(); } @@ -559,6 +606,42 @@ export default class BattleScene extends SceneBase { return true; } + setupControls() { + const keyCodes = Phaser.Input.Keyboard.KeyCodes; + const keyConfig = { + [Button.UP]: [keyCodes.UP, keyCodes.W], + [Button.DOWN]: [keyCodes.DOWN, keyCodes.S], + [Button.LEFT]: [keyCodes.LEFT, keyCodes.A], + [Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D], + [Button.SUBMIT]: [keyCodes.ENTER], + [Button.ACTION]: [keyCodes.SPACE, keyCodes.ENTER, keyCodes.Z], + [Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X], + [Button.MENU]: [keyCodes.ESC, keyCodes.M], + [Button.STATS]: [keyCodes.SHIFT, keyCodes.C], + [Button.CYCLE_SHINY]: [keyCodes.R], + [Button.CYCLE_FORM]: [keyCodes.F], + [Button.CYCLE_GENDER]: [keyCodes.G], + [Button.CYCLE_ABILITY]: [keyCodes.E], + [Button.CYCLE_NATURE]: [keyCodes.N], + [Button.CYCLE_VARIANT]: [keyCodes.V], + [Button.SPEED_UP]: [keyCodes.PLUS], + [Button.SLOW_DOWN]: [keyCodes.MINUS] + }; + const mobileKeyConfig = {}; + this.buttonKeys = []; + for (let b of Utils.getEnumValues(Button)) { + const keys: Phaser.Input.Keyboard.Key[] = []; + if (keyConfig.hasOwnProperty(b)) { + for (let k of keyConfig[b]) + keys.push(this.input.keyboard.addKey(k, false)); + mobileKeyConfig[Button[b]] = keys[0]; + } + this.buttonKeys[b] = keys; + } + + initTouchControls(mobileKeyConfig); + } + getParty(): PlayerPokemon[] { return this.party; } @@ -1260,6 +1343,177 @@ export default class BattleScene extends SceneBase { return biomes[Utils.randSeedInt(biomes.length)]; } + checkInput(): boolean { + let inputSuccess = false; + let vibrationLength = 0; + if (this.buttonJustPressed(Button.UP) || this.repeatInputDurationJustPassed(Button.UP)) { + inputSuccess = this.ui.processInput(Button.UP); + vibrationLength = 5; + this.setLastProcessedMovementTime(Button.UP) + } else if (this.buttonJustPressed(Button.DOWN) || this.repeatInputDurationJustPassed(Button.DOWN)) { + inputSuccess = this.ui.processInput(Button.DOWN); + vibrationLength = 5; + this.setLastProcessedMovementTime(Button.DOWN) + } else if (this.buttonJustPressed(Button.LEFT) || this.repeatInputDurationJustPassed(Button.LEFT)) { + inputSuccess = this.ui.processInput(Button.LEFT); + vibrationLength = 5; + this.setLastProcessedMovementTime(Button.LEFT) + } else if (this.buttonJustPressed(Button.RIGHT) || this.repeatInputDurationJustPassed(Button.RIGHT)) { + inputSuccess = this.ui.processInput(Button.RIGHT); + vibrationLength = 5; + this.setLastProcessedMovementTime(Button.RIGHT) + } else if (this.buttonJustPressed(Button.SUBMIT) || this.repeatInputDurationJustPassed(Button.SUBMIT)) { + inputSuccess = this.ui.processInput(Button.SUBMIT) || this.ui.processInput(Button.ACTION); + this.setLastProcessedMovementTime(Button.SUBMIT); + } else if (this.buttonJustPressed(Button.ACTION) || this.repeatInputDurationJustPassed(Button.ACTION)) { + inputSuccess = this.ui.processInput(Button.ACTION); + this.setLastProcessedMovementTime(Button.ACTION); + } else if (this.buttonJustPressed(Button.CANCEL)|| this.repeatInputDurationJustPassed(Button.CANCEL)) { + inputSuccess = this.ui.processInput(Button.CANCEL); + this.setLastProcessedMovementTime(Button.CANCEL); + } else if (this.buttonJustPressed(Button.MENU)) { + if (this.disableMenu) + return; + switch (this.ui?.getMode()) { + case Mode.MESSAGE: + if (!(this.ui.getHandler() as MessageUiHandler).pendingPrompt) + return; + case Mode.TITLE: + case Mode.COMMAND: + case Mode.FIGHT: + case Mode.BALL: + case Mode.TARGET_SELECT: + case Mode.SAVE_SLOT: + case Mode.PARTY: + case Mode.SUMMARY: + case Mode.STARTER_SELECT: + case Mode.CONFIRM: + case Mode.OPTION_SELECT: + this.ui.setOverlayMode(Mode.MENU); + inputSuccess = true; + break; + case Mode.MENU: + case Mode.SETTINGS: + case Mode.ACHIEVEMENTS: + this.ui.revertMode(); + this.playSound('select'); + inputSuccess = true; + break; + default: + return; + } + } else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) { + if (this.buttonJustPressed(Button.CYCLE_SHINY)) { + inputSuccess = this.ui.processInput(Button.CYCLE_SHINY); + this.setLastProcessedMovementTime(Button.CYCLE_SHINY); + } else if (this.buttonJustPressed(Button.CYCLE_FORM)) { + inputSuccess = this.ui.processInput(Button.CYCLE_FORM); + this.setLastProcessedMovementTime(Button.CYCLE_FORM); + } else if (this.buttonJustPressed(Button.CYCLE_GENDER)) { + inputSuccess = this.ui.processInput(Button.CYCLE_GENDER); + this.setLastProcessedMovementTime(Button.CYCLE_GENDER); + } else if (this.buttonJustPressed(Button.CYCLE_ABILITY)) { + inputSuccess = this.ui.processInput(Button.CYCLE_ABILITY); + this.setLastProcessedMovementTime(Button.CYCLE_ABILITY); + } else if (this.buttonJustPressed(Button.CYCLE_NATURE)) { + inputSuccess = this.ui.processInput(Button.CYCLE_NATURE); + this.setLastProcessedMovementTime(Button.CYCLE_NATURE); + } else if (this.buttonJustPressed(Button.CYCLE_VARIANT)) { + inputSuccess = this.ui.processInput(Button.CYCLE_VARIANT); + this.setLastProcessedMovementTime(Button.CYCLE_VARIANT); + } else + return; + } else if (this.buttonJustPressed(Button.SPEED_UP)) { + if (this.gameSpeed < 5) { + this.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) + 1); + if (this.ui?.getMode() === Mode.SETTINGS) + (this.ui.getHandler() as SettingsUiHandler).show([]); + } + } else if (this.buttonJustPressed(Button.SLOW_DOWN)) { + if (this.gameSpeed > 1) { + this.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) - 1, 0)); + if (this.ui?.getMode() === Mode.SETTINGS) + (this.ui.getHandler() as SettingsUiHandler).show([]); + } + } else { + let pressed = false; + if (this.ui && (this.buttonJustReleased(Button.STATS) || (pressed = this.buttonJustPressed(Button.STATS)))) { + for (let p of this.getField().filter(p => p?.isActive(true))) + p.toggleStats(pressed); + if (pressed) + this.setLastProcessedMovementTime(Button.STATS); + } else + return; + } + if (inputSuccess && this.enableVibration && typeof navigator.vibrate !== 'undefined') + navigator.vibrate(vibrationLength || 10); + } + + /** + * gamepadButtonJustDown returns true if @param button has just been pressed down + * or not. It will only return true once, until the key is released and pressed down + * again. + */ + gamepadButtonJustDown(button: Phaser.Input.Gamepad.Button): boolean { + if (!button || !this.gamepadSupport) + return false; + + let ret = false; + if (button.pressed) { + if (!this.gamepadButtonStates[button.index]) + ret = true; + this.gamepadButtonStates[button.index] = true; + } else + this.gamepadButtonStates[button.index] = false; + + return ret; + } + + buttonJustPressed(button: Button): boolean { + const gamepad = this.input.gamepad?.gamepads[0]; + return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustDown(k)) || this.gamepadButtonJustDown(gamepad?.buttons[this.gamepadKeyConfig[button]]); + } + + /** + * gamepadButtonJustUp returns true if @param button has just been released + * or not. It will only return true once, until the key is released and pressed down + * again. + */ + gamepadButtonJustUp(button: Phaser.Input.Gamepad.Button): boolean { + if (!button || !this.gamepadSupport) + return false; + + return !this.gamepadButtonStates[button.index]; + } + + buttonJustReleased(button: Button): boolean { + const gamepad = this.input.gamepad?.gamepads[0]; + return this.buttonKeys[button].some(k => Phaser.Input.Keyboard.JustUp(k)) || this.gamepadButtonJustUp(gamepad?.buttons[this.gamepadKeyConfig[button]]); + } + + /** + * repeatInputDurationJustPassed returns true if @param button has been held down long + * enough to fire a repeated input. A button must claim the movementButtonLock before + * firing a repeated input - this is to prevent multiple buttons from firing repeatedly. + */ + repeatInputDurationJustPassed(button: Button): boolean { + if (this.movementButtonLock !== null && this.movementButtonLock !== button) { + return false; + } + if (this.buttonKeys[button].every(k => k.isUp) && this.gamepadButtonStates.every(b => b == false)) { + this.movementButtonLock = null; + return false; + } + if (this.time.now - this.lastProcessedButtonPressTimes.get(button) >= repeatInputDelayMillis) { + return true; + } + } + + setLastProcessedMovementTime(button: Button) { + this.lastProcessedButtonPressTimes.set(button, this.time.now); + this.movementButtonLock = button; + } + isBgmPlaying(): boolean { return this.bgm && this.bgm.isPlaying; } diff --git a/src/configs/pad_dualshock.ts b/src/configs/pad_dualshock.ts deleted file mode 100644 index 4f66ff8c00a..00000000000 --- a/src/configs/pad_dualshock.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Dualshock mapping - */ -const pad_dualshock = { - padID: 'Dualshock', - padType: 'Sony', - gamepadMapping: { - RC_S: 0, - RC_E: 1, - RC_W: 2, - RC_N: 3, - START: 9, // Options - SELECT: 8, // Share - LB: 4, - RB: 5, - LT: 6, - RT: 7, - LS: 10, - RS: 11, - LC_N: 12, - LC_S: 13, - LC_W: 14, - LC_E: 15, - MENU: 16, - TOUCH: 17 - }, -}; - -export default pad_dualshock; diff --git a/src/configs/pad_generic.ts b/src/configs/pad_generic.ts deleted file mode 100644 index 19b5d3df16e..00000000000 --- a/src/configs/pad_generic.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Generic pad mapping - */ -const pad_generic = { - padID: 'Generic', - padType: 'generic', - gamepadMapping: { - RC_S: 0, - RC_E: 1, - RC_W: 2, - RC_N: 3, - START: 9, - SELECT: 8, - LB: 4, - RB: 5, - LT: 6, - RT: 7, - LS: 10, - RS: 11, - LC_N: 12, - LC_S: 13, - LC_W: 14, - LC_E: 15 - }, -}; - -export default pad_generic; diff --git a/src/configs/pad_unlicensedSNES.ts b/src/configs/pad_unlicensedSNES.ts deleted file mode 100644 index ba8ee538d30..00000000000 --- a/src/configs/pad_unlicensedSNES.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 081f-e401 - UnlicensedSNES - */ -const pad_unlicensedSNES = { - padID: '081f-e401', - padType: 'snes', - gamepadMapping : { - RC_S: 2, - RC_E: 1, - RC_W: 3, - RC_N: 0, - START: 9, - SELECT: 8, - LB: 4, - RB: 5, - LC_N: 12, - LC_S: 13, - LC_W: 14, - LC_E: 15 - } -}; - -export default pad_unlicensedSNES; diff --git a/src/configs/pad_xbox360.ts b/src/configs/pad_xbox360.ts deleted file mode 100644 index e44ebb54b64..00000000000 --- a/src/configs/pad_xbox360.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Generic pad mapping - */ -const pad_xbox360 = { - padID: 'Xbox 360 controller (XInput STANDARD GAMEPAD)', - padType: 'xbox', - gamepadMapping: { - RC_S: 0, - RC_E: 1, - RC_W: 2, - RC_N: 3, - START: 9, - SELECT: 8, - LB: 4, - RB: 5, - LT: 6, - RT: 7, - LS: 10, - RS: 11, - LC_N: 12, - LC_S: 13, - LC_W: 14, - LC_E: 15, - MENU: 16 - }, -}; - -export default pad_xbox360; diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts deleted file mode 100644 index 441649a5199..00000000000 --- a/src/inputs-controller.ts +++ /dev/null @@ -1,266 +0,0 @@ -import Phaser, {Time} from "phaser"; -import * as Utils from "./utils"; -import {initTouchControls} from './touch-controls'; -import pad_generic from "#app/configs/pad_generic"; -import pad_unlicensedSNES from "#app/configs/pad_unlicensedSNES"; -import pad_xbox360 from "#app/configs/pad_xbox360"; -import pad_dualshock from "#app/configs/pad_dualshock"; - - -export enum Button { - UP, - DOWN, - LEFT, - RIGHT, - SUBMIT, - ACTION, - CANCEL, - MENU, - STATS, - CYCLE_SHINY, - CYCLE_FORM, - CYCLE_GENDER, - CYCLE_ABILITY, - CYCLE_NATURE, - CYCLE_VARIANT, - SPEED_UP, - SLOW_DOWN -} - -const repeatInputDelayMillis = 250; - -export class InputsController extends Phaser.Plugins.ScenePlugin { - private game: Phaser.Game; - private buttonKeys: Phaser.Input.Keyboard.Key[][]; - private gamepads: Array = new Array(); - private scene: Phaser.Scene; - - // buttonLock ensures only a single movement key is firing repeated inputs - // (i.e. by holding down a button) at a time - private buttonLock: Button; - private interactions: Map> = new Map(); - private time: Time; - - constructor(scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) { - super(scene, pluginManager, pluginKey); - this.game = pluginManager.game; - this.scene = scene; - this.time = this.scene.time; - this.buttonKeys = []; - - for (const b of Utils.getEnumValues(Button)) { - this.interactions[b] = { - pressTime: false, - isPressed: false, - } - } - // We don't want the menu key to be repeated - delete this.interactions[Button.MENU]; - } - - boot() { - this.eventEmitter = this.systems.events; - this.events = new Phaser.Events.EventEmitter(); - this.game.events.on(Phaser.Core.Events.STEP, this.update, this); - - if (typeof this.systems.input.gamepad !== 'undefined') { - this.systems.input.gamepad.on('connected', function (thisGamepad) { - this.refreshGamepads(); - this.setupGamepad(thisGamepad); - }, this); - - // Check to see if the gamepad has already been setup by the browser - this.systems.input.gamepad.refreshPads(); - if (this.systems.input.gamepad.total) { - this.refreshGamepads(); - for (const thisGamepad of this.gamepads) { - this.systems.input.gamepad.emit('connected', thisGamepad); - } - } - - this.systems.input.gamepad.on('down', this.gamepadButtonDown, this); - this.systems.input.gamepad.on('up', this.gamepadButtonUp, this); - } - - // Keyboard - this.setupKeyboardControls(); - } - - update() { - for (const b of Utils.getEnumValues(Button)) { - if (!this.interactions.hasOwnProperty(b)) continue; - if (this.repeatInputDurationJustPassed(b)) { - this.events.emit('input_down', { - controller_type: 'keyboard', - button: b, - }); - this.setLastProcessedMovementTime(b); - } - } - } - - setupGamepad(thisGamepad): void { - let gamepadID = thisGamepad.id.toLowerCase(); - const mappedPad = this.mapGamepad(gamepadID); - this.player = { - 'mapping': mappedPad.gamepadMapping, - } - } - - refreshGamepads(): void { - // Sometimes, gamepads are undefined. For some reason. - this.gamepads = this.systems.input.gamepad.gamepads.filter(function (el) { - return el != null; - }); - - for (const [index, thisGamepad] of this.gamepads.entries()) { - thisGamepad.index = index; // Overwrite the gamepad index, in case we had undefined gamepads earlier - } - } - - getActionGamepadMapping() { - const gamepadMapping = {}; - gamepadMapping[this.player.mapping.LC_N] = Button.UP; - gamepadMapping[this.player.mapping.LC_S] = Button.DOWN; - gamepadMapping[this.player.mapping.LC_W] = Button.LEFT; - gamepadMapping[this.player.mapping.LC_E] = Button.RIGHT; - gamepadMapping[this.player.mapping.TOUCH] = Button.SUBMIT; - gamepadMapping[this.player.mapping.RC_S] = Button.ACTION; - gamepadMapping[this.player.mapping.RC_E] = Button.CANCEL; - gamepadMapping[this.player.mapping.SELECT] = Button.STATS; - gamepadMapping[this.player.mapping.START] = Button.MENU; - gamepadMapping[this.player.mapping.RB] = Button.CYCLE_SHINY; - gamepadMapping[this.player.mapping.LB] = Button.CYCLE_FORM; - gamepadMapping[this.player.mapping.LT] = Button.CYCLE_GENDER; - gamepadMapping[this.player.mapping.RT] = Button.CYCLE_ABILITY; - gamepadMapping[this.player.mapping.RC_W] = Button.CYCLE_NATURE; - gamepadMapping[this.player.mapping.RC_N] = Button.CYCLE_VARIANT; - gamepadMapping[this.player.mapping.LS] = Button.SPEED_UP; - gamepadMapping[this.player.mapping.RS] = Button.SLOW_DOWN; - - return gamepadMapping; - } - - gamepadButtonDown(pad, button, value): void { - const actionMapping = this.getActionGamepadMapping(); - const buttonDown = actionMapping.hasOwnProperty(button.index) && actionMapping[button.index]; - if (buttonDown !== undefined) { - this.events.emit('input_down', { - controller_type: 'gamepad', - button: buttonDown, - }); - this.setLastProcessedMovementTime(buttonDown); - } - } - - gamepadButtonUp(pad, button, value): void { - const actionMapping = this.getActionGamepadMapping(); - const buttonUp = actionMapping.hasOwnProperty(button.index) && actionMapping[button.index]; - if (buttonUp !== undefined) { - this.events.emit('input_up', { - controller_type: 'gamepad', - button: buttonUp, - }); - this.delLastProcessedMovementTime(buttonUp); - } - } - - setupKeyboardControls(): void { - const keyCodes = Phaser.Input.Keyboard.KeyCodes; - const keyConfig = { - [Button.UP]: [keyCodes.UP, keyCodes.W], - [Button.DOWN]: [keyCodes.DOWN, keyCodes.S], - [Button.LEFT]: [keyCodes.LEFT, keyCodes.A], - [Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D], - [Button.SUBMIT]: [keyCodes.ENTER], - [Button.ACTION]: [keyCodes.SPACE, keyCodes.Z], - [Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X], - [Button.MENU]: [keyCodes.ESC, keyCodes.M], - [Button.STATS]: [keyCodes.SHIFT, keyCodes.C], - [Button.CYCLE_SHINY]: [keyCodes.R], - [Button.CYCLE_FORM]: [keyCodes.F], - [Button.CYCLE_GENDER]: [keyCodes.G], - [Button.CYCLE_ABILITY]: [keyCodes.E], - [Button.CYCLE_NATURE]: [keyCodes.N], - [Button.CYCLE_VARIANT]: [keyCodes.V], - [Button.SPEED_UP]: [keyCodes.PLUS], - [Button.SLOW_DOWN]: [keyCodes.MINUS] - }; - const mobileKeyConfig = {}; - for (const b of Utils.getEnumValues(Button)) { - const keys: Phaser.Input.Keyboard.Key[] = []; - if (keyConfig.hasOwnProperty(b)) { - for (let k of keyConfig[b]) - keys.push(this.systems.input.keyboard.addKey(k, false)); - mobileKeyConfig[Button[b]] = keys[0]; - } - this.buttonKeys[b] = keys; - } - - initTouchControls(mobileKeyConfig); - this.listenInputKeyboard(); - } - - listenInputKeyboard(): void { - this.buttonKeys.forEach((row, index) => { - for (const key of row) { - key.on('down', () => { - this.events.emit('input_down', { - controller_type: 'keyboard', - button: index, - }); - this.setLastProcessedMovementTime(index); - }); - key.on('up', () => { - this.events.emit('input_up', { - controller_type: 'keyboard', - button: index, - }); - this.delLastProcessedMovementTime(index); - }); - } - }); - } - - mapGamepad(id) { - id = id.toLowerCase(); - let padConfig = pad_generic; - - if (id.includes('081f') && id.includes('e401')) { - padConfig = pad_unlicensedSNES; - } else if (id.includes('xbox') && id.includes('360')) { - padConfig = pad_xbox360; - } else if (id.includes('054c')) { - padConfig = pad_dualshock; - } - - return padConfig; - } - - /** - * repeatInputDurationJustPassed returns true if @param button has been held down long - * enough to fire a repeated input. A button must claim the buttonLock before - * firing a repeated input - this is to prevent multiple buttons from firing repeatedly. - */ - repeatInputDurationJustPassed(button: Button): boolean { - if (this.buttonLock === null || this.buttonLock !== button) { - return false; - } - if (this.time.now - this.interactions[button].pressTime >= repeatInputDelayMillis) { - this.buttonLock = null; - return true; - } - } - - setLastProcessedMovementTime(button: Button): void { - if (!this.interactions.hasOwnProperty(button)) return; - this.buttonLock = button; - this.interactions[button].pressTime = this.time.now; - } - - delLastProcessedMovementTime(button: Button): void { - if (!this.interactions.hasOwnProperty(button)) return; - this.buttonLock = null; - this.interactions[button].pressTime = null; - } -} \ No newline at end of file diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts deleted file mode 100644 index bcdcc34b1fa..00000000000 --- a/src/ui-inputs.ts +++ /dev/null @@ -1,159 +0,0 @@ -import Phaser from "phaser"; -import UI, {Mode} from "./ui/ui"; -import {Button} from "#app/inputs-controller"; -import MessageUiHandler from "#app/ui/message-ui-handler"; -import StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; -import {Setting, settingOptions} from "#app/system/settings"; -import SettingsUiHandler from "#app/ui/settings-ui-handler"; - - -export class UiInputs extends Phaser.Plugins.ScenePlugin { - private game: Phaser.Game; - private scene: Phaser.Scene; - private events; - - constructor(scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) { - super(scene, pluginManager, pluginKey); - this.game = pluginManager.game; - this.scene = scene; - this.events = this.scene.inputController.events - } - - boot() { - this.listenInputs(); - } - - listenInputs(): void { - this.events.on('input_down', (event) => { - const actions = this.getActionsKeyDown(); - if (!actions.hasOwnProperty(event.button)) return; - const [inputSuccess, vibrationLength] = actions[event.button](); - if (inputSuccess && this.scene.enableVibration && typeof navigator.vibrate !== 'undefined') - navigator.vibrate(vibrationLength); - }, this); - - this.events.on('input_up', (event) => { - const actions = this.getActionsKeyUp(); - if (!actions.hasOwnProperty(event.button)) return; - const [inputSuccess, vibrationLength] = actions[event.button](); - if (inputSuccess && this.scene.enableVibration && typeof navigator.vibrate !== 'undefined') - navigator.vibrate(vibrationLength); - }, this); - } - - getActionsKeyDown() { - const actions = {}; - actions[Button.UP] = () => this.buttonDirection(Button.UP); - actions[Button.DOWN] = () => this.buttonDirection(Button.DOWN); - actions[Button.LEFT] = () => this.buttonDirection(Button.LEFT); - actions[Button.RIGHT] = () => this.buttonDirection(Button.RIGHT); - actions[Button.SUBMIT] = () => this.buttonTouch(); - actions[Button.ACTION] = () => this.buttonAb(Button.ACTION); - actions[Button.CANCEL] = () => this.buttonAb(Button.CANCEL); - actions[Button.MENU] = () => this.buttonMenu(); - actions[Button.STATS] = () => this.buttonStats(true); - actions[Button.CYCLE_SHINY] = () => this.buttonCycleOption(Button.CYCLE_SHINY); - actions[Button.CYCLE_FORM] = () => this.buttonCycleOption(Button.CYCLE_FORM); - actions[Button.CYCLE_GENDER] = () => this.buttonCycleOption(Button.CYCLE_GENDER); - actions[Button.CYCLE_ABILITY] = () => this.buttonCycleOption(Button.CYCLE_ABILITY); - actions[Button.CYCLE_NATURE] = () => this.buttonCycleOption(Button.CYCLE_NATURE); - actions[Button.CYCLE_VARIANT] = () => this.buttonCycleOption(Button.CYCLE_VARIANT); - actions[Button.SPEED_UP] = () => this.buttonSpeedChange(); - actions[Button.SLOW_DOWN] = () => this.buttonSpeedChange(false); - return actions; - } - - getActionsKeyUp() { - const actions = {}; - actions[Button.STATS] = () => this.buttonStats(false); - return actions; - } - - buttonDirection(direction): Array { - const inputSuccess = this.scene.ui.processInput(direction); - const vibrationLength = 5; - return [inputSuccess, vibrationLength]; - } - - buttonAb(button): Array { - const inputSuccess = this.scene.ui.processInput(button); - return [inputSuccess, 0]; - } - - buttonTouch(): Array { - const inputSuccess = this.scene.ui.processInput(Button.SUBMIT) || this.scene.ui.processInput(Button.ACTION); - return [inputSuccess, 0]; - } - - buttonStats(pressed = true): Array { - if (pressed) { - for (let p of this.scene.getField().filter(p => p?.isActive(true))) - p.toggleStats(true); - } else { - for (let p of this.scene.getField().filter(p => p?.isActive(true))) - p.toggleStats(false); - } - return [true, 0]; - } - - buttonMenu(): Array { - let inputSuccess; - if (this.scene.disableMenu) - return [true, 0]; - switch (this.scene.ui?.getMode()) { - case Mode.MESSAGE: - if (!(this.scene.ui.getHandler() as MessageUiHandler).pendingPrompt) - return [true, 0]; - case Mode.TITLE: - case Mode.COMMAND: - case Mode.FIGHT: - case Mode.BALL: - case Mode.TARGET_SELECT: - case Mode.SAVE_SLOT: - case Mode.PARTY: - case Mode.SUMMARY: - case Mode.STARTER_SELECT: - case Mode.CONFIRM: - case Mode.OPTION_SELECT: - this.scene.ui.setOverlayMode(Mode.MENU); - inputSuccess = true; - break; - case Mode.MENU: - case Mode.SETTINGS: - case Mode.ACHIEVEMENTS: - this.scene.ui.revertMode(); - this.scene.playSound('select'); - inputSuccess = true; - break; - default: - return [true, 0]; - } - return [inputSuccess, 0]; - } - - buttonCycleOption(button): Array { - let inputSuccess; - if (this.scene.ui?.getHandler() instanceof StarterSelectUiHandler) { - inputSuccess = this.scene.ui.processInput(button); - } - return [inputSuccess, 0]; - } - - buttonSpeedChange(up = true): Array { - if (up) { - if (this.scene.gameSpeed < 5) { - this.scene.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) + 1); - if (this.scene.ui?.getMode() === Mode.SETTINGS) - (this.scene.ui.getHandler() as SettingsUiHandler).show([]); - } - return [0, 0]; - } - if (this.scene.gameSpeed > 1) { - this.scene.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.scene.gameSpeed}x`) - 1, 0)); - if (this.scene.ui?.getMode() === Mode.SETTINGS) - (this.scene.ui.getHandler() as SettingsUiHandler).show([]); - } - return [0, 0]; - } - -} \ No newline at end of file diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index ac2dca03ed3..2f2c4face89 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -1,11 +1,10 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import * as Utils from "../utils"; import { argbFromRgba } from "@material/material-color-utilities"; -import {Button} from "#app/inputs-controller"; export interface OptionSelectConfig { xOffset?: number; diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index 778a5d5f131..cadda64e032 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -1,10 +1,9 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { Achv, achvs } from "../system/achv"; import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; -import {Button} from "#app/inputs-controller"; export default class AchvsUiHandler extends MessageUiHandler { private achvsContainer: Phaser.GameObjects.Container; diff --git a/src/ui/awaitable-ui-handler.ts b/src/ui/awaitable-ui-handler.ts index 5d5f0b7a094..e8cc979e423 100644 --- a/src/ui/awaitable-ui-handler.ts +++ b/src/ui/awaitable-ui-handler.ts @@ -1,7 +1,6 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; -import {Button} from "#app/inputs-controller"; export default abstract class AwaitableUiHandler extends UiHandler { protected awaitingActionInput: boolean; diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index a4ee6c99d46..f2ebdc342a4 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -1,12 +1,11 @@ import { CommandPhase } from "../phases"; -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { getPokeballName } from "../data/pokeball"; import { addTextObject, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; -import {Button} from "#app/inputs-controller"; export default class BallUiHandler extends UiHandler { private pokeballSelectContainer: Phaser.GameObjects.Container; diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index 7bba9657cd9..5e2cb56518f 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; import { Mode } from "./ui"; import * as Utils from "../utils"; @@ -6,7 +6,6 @@ import MessageUiHandler from "./message-ui-handler"; import { getStatName, Stat } from "../data/pokemon-stat"; import { addWindow } from "./ui-theme"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import {Button} from "#app/inputs-controller"; export default class BattleMessageUiHandler extends MessageUiHandler { private levelUpStatsContainer: Phaser.GameObjects.Container; diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index 4c49246ebb6..b8223694b4c 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -1,11 +1,10 @@ import { CommandPhase } from "../phases"; -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { addTextObject, TextStyle } from "./text"; import PartyUiHandler, { PartyUiMode } from "./party-ui-handler"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import i18next from '../plugins/i18n'; -import {Button} from "#app/inputs-controller"; export enum Command { FIGHT = 0, diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/confirm-ui-handler.ts index 600a84e614d..d9e7726d826 100644 --- a/src/ui/confirm-ui-handler.ts +++ b/src/ui/confirm-ui-handler.ts @@ -1,7 +1,6 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import AbstractOptionSelectUiHandler, { OptionSelectConfig } from "./abstact-option-select-ui-handler"; import { Mode } from "./ui"; -import {Button} from "#app/inputs-controller"; export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { private switchCheck: boolean; diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 0f8b999ba59..f0e32dbc2e9 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { Mode } from "./ui"; import { TextStyle, addTextObject, getEggTierTextTint } from "./text"; import MessageUiHandler from "./message-ui-handler"; @@ -9,7 +9,6 @@ import { getPokemonSpecies } from "../data/pokemon-species"; import { addWindow } from "./ui-theme"; import { Tutorial, handleTutorial } from "../tutorial"; import { EggTier } from "../data/enums/egg-type"; -import {Button} from "#app/inputs-controller"; const defaultText = 'Select a machine.'; diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index fefd5197be2..f841bafc268 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -1,8 +1,7 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { EggHatchPhase } from "../egg-hatch-phase"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; -import {Button} from "#app/inputs-controller"; export default class EggHatchSceneHandler extends UiHandler { public eggHatchContainer: Phaser.GameObjects.Container; diff --git a/src/ui/egg-list-ui-handler.ts b/src/ui/egg-list-ui-handler.ts index 8271ee00788..7537b8deeb0 100644 --- a/src/ui/egg-list-ui-handler.ts +++ b/src/ui/egg-list-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { Mode } from "./ui"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; import { TextStyle, addTextObject } from "./text"; @@ -6,7 +6,6 @@ import MessageUiHandler from "./message-ui-handler"; import { EGG_SEED, Egg, GachaType, getEggGachaTypeDescriptor, getEggHatchWavesMessage, getEggDescriptor } from "../data/egg"; import * as Utils from "../utils"; import { addWindow } from "./ui-theme"; -import {Button} from "#app/inputs-controller"; export default class EggListUiHandler extends MessageUiHandler { private eggListContainer: Phaser.GameObjects.Container; diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/evolution-scene-handler.ts index 21b39b593cf..7e0ef063ea0 100644 --- a/src/ui/evolution-scene-handler.ts +++ b/src/ui/evolution-scene-handler.ts @@ -1,8 +1,7 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; -import {Button} from "#app/inputs-controller"; export default class EvolutionSceneHandler extends MessageUiHandler { public evolutionContainer: Phaser.GameObjects.Container; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index b35292ebd45..1a7a8bef597 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { addTextObject, TextStyle } from "./text"; import { Type } from "../data/type"; import { Command } from "./command-ui-handler"; @@ -8,7 +8,6 @@ import * as Utils from "../utils"; import { CommandPhase } from "../phases"; import { MoveCategory } from "#app/data/move.js"; import i18next from '../plugins/i18n'; -import {Button} from "#app/inputs-controller"; export default class FightUiHandler extends UiHandler { private movesContainer: Phaser.GameObjects.Container; diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index 4830bd191f9..ec5f4147c58 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { ModalConfig, ModalUiHandler } from "./modal-ui-handler"; import { Mode } from "./ui"; import { TextStyle, addTextInputObject, addTextObject } from "./text"; @@ -6,7 +6,6 @@ import { WindowVariant, addWindow } from "./ui-theme"; import InputText from "phaser3-rex-plugins/plugins/inputtext"; import * as Utils from "../utils"; import i18next from '../plugins/i18n'; -import {Button} from "#app/inputs-controller"; export interface FormModalConfig extends ModalConfig { errorMessage?: string; diff --git a/src/ui/game-stats-ui-handler.ts b/src/ui/game-stats-ui-handler.ts index 5b271159f4a..00e358ff8e4 100644 --- a/src/ui/game-stats-ui-handler.ts +++ b/src/ui/game-stats-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { TextStyle, addTextObject, getTextColor } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; @@ -6,7 +6,6 @@ import { addWindow } from "./ui-theme"; import * as Utils from "../utils"; import { DexAttr, GameData } from "../system/game-data"; import { speciesStarters } from "../data/pokemon-species"; -import {Button} from "#app/inputs-controller"; interface DisplayStat { label?: string; diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 7ce5302aa0e..03d93699e75 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { bypassLogin } from "../battle-scene"; +import BattleScene, { Button, bypassLogin } from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import * as Utils from "../utils"; @@ -9,7 +9,6 @@ import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui import { Tutorial, handleTutorial } from "../tutorial"; import { updateUserInfo } from "../account"; import i18next from '../plugins/i18n'; -import {Button} from "#app/inputs-controller"; export enum MenuOptions { GAME_SETTINGS, diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index 507e3292fb9..f193a3db54f 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -1,9 +1,8 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { WindowVariant, addWindow } from "./ui-theme"; -import {Button} from "#app/inputs-controller"; export interface ModalConfig { buttonActions: Function[]; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 7597503d645..e5252e02a8f 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { getPlayerShopModifierTypeOptionsForWave, ModifierTypeOption } from "../modifier/modifier-type"; import { getPokeballAtlasKey, PokeballType } from "../data/pokeball"; import { addTextObject, getModifierTierTextTint, getTextColor, TextStyle } from "./text"; @@ -6,7 +6,6 @@ import AwaitableUiHandler from "./awaitable-ui-handler"; import { Mode } from "./ui"; import { LockModifierTiersModifier, PokemonHeldItemModifier } from "../modifier/modifier"; import { handleTutorial, Tutorial } from "../tutorial"; -import {Button} from "#app/inputs-controller"; export const SHOP_OPTIONS_ROW_LIMIT = 6; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 29b0bb85011..35014fa7027 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,5 +1,5 @@ import { CommandPhase } from "../phases"; -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { PlayerPokemon, PokemonMove } from "../field/pokemon"; import { addTextObject, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; @@ -16,7 +16,6 @@ import { pokemonEvolutions } from "../data/pokemon-evolutions"; import { addWindow } from "./ui-theme"; import { SpeciesFormChangeItemTrigger } from "../data/pokemon-forms"; import { getVariantTint } from "#app/data/variant"; -import {Button} from "#app/inputs-controller"; const defaultMessage = 'Choose a Pokémon.'; diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 07a9283f7e8..181b0643cb9 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { gameModes } from "../game-mode"; import { SessionSaveData } from "../system/game-data"; import { TextStyle, addTextObject } from "./text"; @@ -8,7 +8,6 @@ import * as Utils from "../utils"; import PokemonData from "../system/pokemon-data"; import { PokemonHeldItemModifier } from "../modifier/modifier"; import MessageUiHandler from "./message-ui-handler"; -import {Button} from "#app/inputs-controller"; const sessionSlotCount = 5; diff --git a/src/ui/settings-ui-handler.ts b/src/ui/settings-ui-handler.ts index 6b1b7467d67..8f43b377d21 100644 --- a/src/ui/settings-ui-handler.ts +++ b/src/ui/settings-ui-handler.ts @@ -1,11 +1,10 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { Setting, reloadSettings, settingDefaults, settingOptions } from "../system/settings"; import { hasTouchscreen, isMobile } from "../touch-controls"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; -import {Button} from "#app/inputs-controller"; export default class SettingsUiHandler extends UiHandler { private settingsContainer: Phaser.GameObjects.Container; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index ad3e51d24a2..f1e058b12cf 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene, { starterColors } from "../battle-scene"; +import BattleScene, { Button, starterColors } from "../battle-scene"; import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; import { Species } from "../data/enums/species"; import { TextStyle, addBBCodeTextObject, addTextObject } from "./text"; @@ -27,7 +27,6 @@ import { argbFromRgba } from "@material/material-color-utilities"; import { OptionSelectItem } from "./abstact-option-select-ui-handler"; import { pokemonPrevolutions } from "#app/data/pokemon-evolutions"; import { Variant, getVariantTint } from "#app/data/variant"; -import {Button} from "#app/inputs-controller"; export type StarterSelectCallback = (starters: Starter[]) => void; diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 4b014b83778..cf5eb3639fd 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -1,4 +1,4 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import * as Utils from "../utils"; @@ -17,7 +17,6 @@ import { Nature, getNatureStatMultiplier } from "../data/nature"; import { loggedInUser } from "../account"; import { PlayerGender } from "../system/game-data"; import { Variant, getVariantTint } from "#app/data/variant"; -import {Button} from "#app/inputs-controller"; enum Page { PROFILE, diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index b5242e713d7..f8a7c9d28a3 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -1,11 +1,10 @@ import { BattlerIndex } from "../battle"; -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { Moves } from "../data/enums/moves"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import * as Utils from "../utils"; import { getMoveTargets } from "../data/move"; -import {Button} from "#app/inputs-controller"; export type TargetSelectCallback = (cursor: integer) => void; diff --git a/src/ui/ui-handler.ts b/src/ui/ui-handler.ts index 20f34a33388..e3c94b76467 100644 --- a/src/ui/ui-handler.ts +++ b/src/ui/ui-handler.ts @@ -1,7 +1,6 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { TextStyle, getTextColor } from "./text"; import UI, { Mode } from "./ui"; -import {Button} from "#app/inputs-controller"; export default abstract class UiHandler { protected scene: BattleScene; diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 0555c54193d..6e20b2cb8b8 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -1,4 +1,4 @@ -import { default as BattleScene } from '../battle-scene'; +import { Button, default as BattleScene } from '../battle-scene'; import UiHandler from './ui-handler'; import BattleMessageUiHandler from './battle-message-ui-handler'; import CommandUiHandler from './command-ui-handler'; @@ -35,7 +35,6 @@ import SavingIconHandler from './saving-icon-handler'; import UnavailableModalUiHandler from './unavailable-modal-ui-handler'; import OutdatedModalUiHandler from './outdated-modal-ui-handler'; import SessionReloadModalUiHandler from './session-reload-modal-ui-handler'; -import {Button} from "#app/inputs-controller"; export enum Mode { MESSAGE, diff --git a/src/ui/vouchers-ui-handler.ts b/src/ui/vouchers-ui-handler.ts index 36926c6fa71..3f41cf9ae74 100644 --- a/src/ui/vouchers-ui-handler.ts +++ b/src/ui/vouchers-ui-handler.ts @@ -1,10 +1,9 @@ -import BattleScene from "../battle-scene"; +import BattleScene, { Button } from "../battle-scene"; import { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "../system/voucher"; import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; -import {Button} from "#app/inputs-controller"; const itemRows = 4; const itemCols = 17; From 71705dd6d40c67f6df64b80e68f0e144b4438888 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Fri, 3 May 2024 15:03:11 -0400 Subject: [PATCH 07/11] Add candy popup sound --- public/audio/se/shing.wav | Bin 0 -> 124500 bytes src/loading-scene.ts | 1 + src/ui/candy-bar.ts | 2 ++ src/ui/save-slot-select-ui-handler.ts | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 public/audio/se/shing.wav diff --git a/public/audio/se/shing.wav b/public/audio/se/shing.wav new file mode 100644 index 0000000000000000000000000000000000000000..e693cb207fca07ca996378bc42c1492516988a25 GIT binary patch literal 124500 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 literal 0 HcmV?d00001 diff --git a/src/loading-scene.ts b/src/loading-scene.ts index d0751e9a2da..05b6c9f50b1 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -223,6 +223,7 @@ export class LoadingScene extends SceneBase { this.loadSe('sparkle'); this.loadSe('restore'); this.loadSe('shine'); + this.loadSe('shing'); this.loadSe('charge'); this.loadSe('beam'); this.loadSe('upgrade'); diff --git a/src/ui/candy-bar.ts b/src/ui/candy-bar.ts index 2219e07479d..a4cc1295028 100644 --- a/src/ui/candy-bar.ts +++ b/src/ui/candy-bar.ts @@ -69,6 +69,8 @@ export default class CandyBar extends Phaser.GameObjects.Container { if (this.tween) this.tween.stop(); + (this.scene as BattleScene).playSound('shing'); + this.tween = this.scene.tweens.add({ targets: this, x: (this.scene.game.canvas.width / 6) - (this.bg.width - 5), diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 181b0643cb9..11680cd3952 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -258,7 +258,7 @@ class SessionSlot extends Phaser.GameObjects.Container { async setupWithData(data: SessionSaveData) { this.remove(this.loadingLabel, true); - const gameModeLabel = addTextObject(this.scene, 8, 5, `${gameModes[data.gameMode].getName()} - Wave ${data.waveIndex}`, TextStyle.WINDOW); + const gameModeLabel = addTextObject(this.scene, 8, 5, `${gameModes[data.gameMode]?.getName() || 'Unknown'} - Wave ${data.waveIndex}`, TextStyle.WINDOW); this.add(gameModeLabel); const timestampLabel = addTextObject(this.scene, 8, 19, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW); From 9a559c8d6ac8e24009a0df5e0a7c1d6fb9a7e28a Mon Sep 17 00:00:00 2001 From: Madmadness65 Date: Fri, 3 May 2024 14:04:43 -0500 Subject: [PATCH 08/11] Update passive abilities for legendaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the first batch of passive ability revisions, this one directed at legendary and mythical Pokémon. --- src/data/pokemon-species.ts | 112 ++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index c67f0c14b47..57bb9230c5e 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -3300,7 +3300,7 @@ export const starterPassiveAbilities = { [Species.ENTEI]: Abilities.MOXIE, [Species.SUICUNE]: Abilities.UNAWARE, [Species.LARVITAR]: Abilities.SAND_FORCE, - [Species.LUGIA]: Abilities.STORM_DRAIN, + [Species.LUGIA]: Abilities.DELTA_STREAM, [Species.HO_OH]: Abilities.MAGIC_GUARD, [Species.CELEBI]: Abilities.GRASSY_SURGE, [Species.TREECKO]: Abilities.GRASSY_SURGE, @@ -3366,13 +3366,13 @@ export const starterPassiveAbilities = { [Species.LUVDISC]: Abilities.PICKUP, [Species.BAGON]: Abilities.GALE_WINGS, [Species.BELDUM]: Abilities.IRON_FIST, - [Species.REGIROCK]: Abilities.REGENERATOR, - [Species.REGICE]: Abilities.ICE_SCALES, - [Species.REGISTEEL]: Abilities.STEELY_SPIRIT, - [Species.LATIAS]: Abilities.SERENE_GRACE, - [Species.LATIOS]: Abilities.SERENE_GRACE, + [Species.REGIROCK]: Abilities.SAND_STREAM, + [Species.REGICE]: Abilities.SNOW_WARNING, + [Species.REGISTEEL]: Abilities.FILTER, + [Species.LATIAS]: Abilities.SOUL_HEART, + [Species.LATIOS]: Abilities.TINTED_LENS, [Species.KYOGRE]: Abilities.HYDRATION, - [Species.GROUDON]: Abilities.EARTH_EATER, + [Species.GROUDON]: Abilities.FLAME_BODY, [Species.RAYQUAZA]: Abilities.UNNERVE, [Species.JIRACHI]: Abilities.COMATOSE, [Species.DEOXYS]: Abilities.PROTEAN, @@ -3414,16 +3414,16 @@ export const starterPassiveAbilities = { [Species.MANTYKE]: Abilities.STORM_DRAIN, [Species.SNOVER]: Abilities.SNOW_CLOAK, [Species.ROTOM]: Abilities.HADRON_ENGINE, - [Species.UXIE]: Abilities.ILLUSION, + [Species.UXIE]: Abilities.UNAWARE, [Species.MESPRIT]: Abilities.MOODY, [Species.AZELF]: Abilities.NEUROFORCE, [Species.DIALGA]: Abilities.SPEED_BOOST, - [Species.PALKIA]: Abilities.MAGIC_BOUNCE, - [Species.HEATRAN]: Abilities.ROUGH_SKIN, + [Species.PALKIA]: Abilities.MULTISCALE, + [Species.HEATRAN]: Abilities.FILTER, [Species.REGIGIGAS]: Abilities.MINDS_EYE, - [Species.GIRATINA]: Abilities.SHADOW_TAG, + [Species.GIRATINA]: Abilities.SHADOW_SHIELD, [Species.CRESSELIA]: Abilities.MAGIC_BOUNCE, - [Species.PHIONE]: Abilities.SWIFT_SWIM, + [Species.PHIONE]: Abilities.SIMPLE, [Species.MANAPHY]: Abilities.SIMPLE, [Species.DARKRAI]: Abilities.UNNERVE, [Species.SHAYMIN]: Abilities.FLOWER_VEIL, @@ -3500,16 +3500,16 @@ export const starterPassiveAbilities = { [Species.LARVESTA]: Abilities.DROUGHT, [Species.COBALION]: Abilities.INTREPID_SWORD, [Species.TERRAKION]: Abilities.ROCKY_PAYLOAD, - [Species.VIRIZION]: Abilities.SYMBIOSIS, - [Species.TORNADUS]: Abilities.DELTA_STREAM, + [Species.VIRIZION]: Abilities.SHARPNESS, + [Species.TORNADUS]: Abilities.DRIZZLE, [Species.THUNDURUS]: Abilities.DRIZZLE, [Species.RESHIRAM]: Abilities.ORICHALCUM_PULSE, [Species.ZEKROM]: Abilities.HADRON_ENGINE, - [Species.LANDORUS]: Abilities.PRANKSTER, + [Species.LANDORUS]: Abilities.STORM_DRAIN, [Species.KYUREM]: Abilities.SNOW_WARNING, - [Species.KELDEO]: Abilities.SHARPNESS, - [Species.MELOETTA]: Abilities.PUNK_ROCK, - [Species.GENESECT]: Abilities.MEGA_LAUNCHER, + [Species.KELDEO]: Abilities.GRIM_NEIGH, + [Species.MELOETTA]: Abilities.MINDS_EYE, + [Species.GENESECT]: Abilities.REGENERATOR, [Species.CHESPIN]: Abilities.IRON_BARBS, [Species.FENNEKIN]: Abilities.MAGIC_BOUNCE, [Species.FROAKIE]: Abilities.ADAPTABILITY, @@ -3541,11 +3541,11 @@ export const starterPassiveAbilities = { [Species.PUMPKABOO]: Abilities.FLASH_FIRE, [Species.BERGMITE]: Abilities.MIRROR_ARMOR, [Species.NOIBAT]: Abilities.PUNK_ROCK, - [Species.XERNEAS]: Abilities.COMPETITIVE, - [Species.YVELTAL]: Abilities.DEFIANT, - [Species.ZYGARDE]: Abilities.REGENERATOR, - [Species.DIANCIE]: Abilities.QUEENLY_MAJESTY, - [Species.HOOPA]: Abilities.TRACE, + [Species.XERNEAS]: Abilities.MISTY_SURGE, + [Species.YVELTAL]: Abilities.SOUL_HEART, + [Species.ZYGARDE]: Abilities.HUGE_POWER, + [Species.DIANCIE]: Abilities.LEVITATE, + [Species.HOOPA]: Abilities.OPPORTUNIST, [Species.VOLCANION]: Abilities.FILTER, [Species.ROWLET]: Abilities.SNIPER, [Species.LITTEN]: Abilities.PRANKSTER, @@ -3582,26 +3582,26 @@ export const starterPassiveAbilities = { [Species.DRAMPA]: Abilities.FLASH_FIRE, [Species.DHELMISE]: Abilities.INFILTRATOR, [Species.JANGMO_O]: Abilities.DANCER, - [Species.TAPU_KOKO]: Abilities.GALVANIZE, - [Species.TAPU_LELE]: Abilities.BERSERK, - [Species.TAPU_BULU]: Abilities.FLOWER_VEIL, + [Species.TAPU_KOKO]: Abilities.TRANSISTOR, + [Species.TAPU_LELE]: Abilities.SHEER_FORCE, + [Species.TAPU_BULU]: Abilities.GRASS_PELT, [Species.TAPU_FINI]: Abilities.FAIRY_AURA, [Species.COSMOG]: Abilities.BEAST_BOOST, - [Species.NIHILEGO]: Abilities.POISON_PUPPETEER, + [Species.NIHILEGO]: Abilities.LEVITATE, [Species.BUZZWOLE]: Abilities.MOXIE, - [Species.PHEROMOSA]: Abilities.MOXIE, - [Species.XURKITREE]: Abilities.LIGHTNING_ROD, - [Species.CELESTEELA]: Abilities.CHLOROPHYLL, + [Species.PHEROMOSA]: Abilities.TINTED_LENS, + [Species.XURKITREE]: Abilities.TRANSISTOR, + [Species.CELESTEELA]: Abilities.HEATPROOF, [Species.KARTANA]: Abilities.SHARPNESS, - [Species.GUZZLORD]: Abilities.GLUTTONY, + [Species.GUZZLORD]: Abilities.INNARDS_OUT, [Species.NECROZMA]: Abilities.BEAST_BOOST, [Species.MAGEARNA]: Abilities.STEELY_SPIRIT, [Species.MARSHADOW]: Abilities.IRON_FIST, - [Species.POIPOLE]: Abilities.MERCILESS, - [Species.STAKATAKA]: Abilities.DAUNTLESS_SHIELD, + [Species.POIPOLE]: Abilities.SHEER_FORCE, + [Species.STAKATAKA]: Abilities.SOLID_ROCK, [Species.BLACEPHALON]: Abilities.MAGIC_GUARD, - [Species.ZERAORA]: Abilities.MOTOR_DRIVE, - [Species.MELTAN]: Abilities.FULL_METAL_BODY, + [Species.ZERAORA]: Abilities.TOUGH_CLAWS, + [Species.MELTAN]: Abilities.STEELY_SPIRIT, [Species.GROOKEY]: Abilities.GRASS_PELT, [Species.SCORBUNNY]: Abilities.RECKLESS, [Species.SOBBLE]: Abilities.SUPER_LUCK, @@ -3639,17 +3639,17 @@ export const starterPassiveAbilities = { [Species.ARCTOVISH]: Abilities.STRONG_JAW, [Species.DURALUDON]: Abilities.MEGA_LAUNCHER, [Species.DREEPY]: Abilities.PARENTAL_BOND, - [Species.ZACIAN]: Abilities.GUARD_DOG, - [Species.ZAMAZENTA]: Abilities.GUARD_DOG, + [Species.ZACIAN]: Abilities.UNNERVE, + [Species.ZAMAZENTA]: Abilities.STAMINA, [Species.ETERNATUS]: Abilities.SUPREME_OVERLORD, [Species.KUBFU]: Abilities.IRON_FIST, - [Species.ZARUDE]: Abilities.PRANKSTER, - [Species.REGIELEKI]: Abilities.LEVITATE, - [Species.REGIDRAGO]: Abilities.INTIMIDATE, + [Species.ZARUDE]: Abilities.GRASSY_SURGE, + [Species.REGIELEKI]: Abilities.ELECTRIC_SURGE, + [Species.REGIDRAGO]: Abilities.MULTISCALE, [Species.GLASTRIER]: Abilities.FILTER, - [Species.SPECTRIER]: Abilities.PERISH_BODY, + [Species.SPECTRIER]: Abilities.SHADOW_SHIELD, [Species.CALYREX]: Abilities.HARVEST, - [Species.ENAMORUS]: Abilities.PRANKSTER, + [Species.ENAMORUS]: Abilities.FAIRY_AURA, [Species.SPRIGATITO]: Abilities.MAGICIAN, [Species.FUECOCO]: Abilities.EARTH_EATER, [Species.QUAXLY]: Abilities.DANCER, @@ -3688,40 +3688,40 @@ export const starterPassiveAbilities = { [Species.DONDOZO]: Abilities.GLUTTONY, [Species.TATSUGIRI]: Abilities.WATER_BUBBLE, [Species.GREAT_TUSK]: Abilities.INTIMIDATE, - [Species.SCREAM_TAIL]: Abilities.PIXILATE, + [Species.SCREAM_TAIL]: Abilities.UNAWARE, [Species.BRUTE_BONNET]: Abilities.BEAST_BOOST, [Species.FLUTTER_MANE]: Abilities.DAZZLING, - [Species.SLITHER_WING]: Abilities.MOXIE, + [Species.SLITHER_WING]: Abilities.SCRAPPY, [Species.SANDY_SHOCKS]: Abilities.EARTH_EATER, - [Species.IRON_TREADS]: Abilities.BULLETPROOF, + [Species.IRON_TREADS]: Abilities.STEELY_SPIRIT, [Species.IRON_BUNDLE]: Abilities.SNOW_WARNING, [Species.IRON_HANDS]: Abilities.IRON_FIST, - [Species.IRON_JUGULIS]: Abilities.NO_GUARD, + [Species.IRON_JUGULIS]: Abilities.AERILATE, [Species.IRON_MOTH]: Abilities.LEVITATE, [Species.IRON_THORNS]: Abilities.SAND_STREAM, [Species.FRIGIBAX]: Abilities.THICK_FAT, [Species.GIMMIGHOUL]: Abilities.SUPER_LUCK, - [Species.WO_CHIEN]: Abilities.TRIAGE, - [Species.CHIEN_PAO]: Abilities.REFRIGERATE, + [Species.WO_CHIEN]: Abilities.GRASSY_SURGE, + [Species.CHIEN_PAO]: Abilities.INTREPID_SWORD, [Species.TING_LU]: Abilities.STAMINA, - [Species.CHI_YU]: Abilities.BLAZE, - [Species.ROARING_MOON]: Abilities.AERILATE, + [Species.CHI_YU]: Abilities.DROUGHT, + [Species.ROARING_MOON]: Abilities.TOUGH_CLAWS, [Species.IRON_VALIANT]: Abilities.DOWNLOAD, [Species.KORAIDON]: Abilities.PROTOSYNTHESIS, [Species.MIRAIDON]: Abilities.QUARK_DRIVE, [Species.WALKING_WAKE]: Abilities.BEAST_BOOST, [Species.IRON_LEAVES]: Abilities.SHARPNESS, [Species.POLTCHAGEIST]: Abilities.FLAME_BODY, - [Species.OKIDOGI]: Abilities.INTIMIDATE, - [Species.MUNKIDORI]: Abilities.PRANKSTER, - [Species.FEZANDIPITI]: Abilities.DAZZLING, + [Species.OKIDOGI]: Abilities.FUR_COAT, + [Species.MUNKIDORI]: Abilities.NEUROFORCE, + [Species.FEZANDIPITI]: Abilities.LEVITATE, [Species.OGERPON]: Abilities.OPPORTUNIST, [Species.GOUGING_FIRE]: Abilities.BEAST_BOOST, [Species.RAGING_BOLT]: Abilities.BEAST_BOOST, [Species.IRON_BOULDER]: Abilities.SHARPNESS, [Species.IRON_CROWN]: Abilities.SHARPNESS, - [Species.TERAPAGOS]: Abilities.PROTEAN, - [Species.PECHARUNT]: Abilities.CORROSION, + [Species.TERAPAGOS]: Abilities.REGENERATOR, + [Species.PECHARUNT]: Abilities.TOXIC_CHAIN, [Species.ALOLA_RATTATA]: Abilities.CHEEK_POUCH, [Species.ALOLA_SANDSHREW]: Abilities.ICE_BODY, [Species.ALOLA_VULPIX]: Abilities.ICE_BODY, @@ -3736,7 +3736,7 @@ export const starterPassiveAbilities = { [Species.GALAR_FARFETCHD]: Abilities.SUPER_LUCK, [Species.GALAR_ARTICUNO]: Abilities.SERENE_GRACE, [Species.GALAR_ZAPDOS]: Abilities.TOUGH_CLAWS, - [Species.GALAR_MOLTRES]: Abilities.REGENERATOR, + [Species.GALAR_MOLTRES]: Abilities.DARK_AURA, [Species.GALAR_CORSOLA]: Abilities.SHADOW_TAG, [Species.GALAR_ZIGZAGOON]: Abilities.PICKPOCKET, [Species.GALAR_DARUMAKA]: Abilities.FLASH_FIRE, From cbf06ffa2ca647dc9ee8a89028cc044431c8b89d Mon Sep 17 00:00:00 2001 From: Madi Simpson Date: Fri, 3 May 2024 12:06:43 -0700 Subject: [PATCH 09/11] bugfix: ensure direct stat modifying moves update both pokemon's stat info --- src/data/move.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 21f376254f7..264f5576ea5 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1573,7 +1573,7 @@ export class CopyStatsAttr extends MoveEffectAttr { user.addTag(BattlerTagType.CRIT_BOOST, 0, move.id); else user.removeTag(BattlerTagType.CRIT_BOOST); - + target.updateInfo(); user.updateInfo(); target.scene.queueMessage(getPokemonMessage(user, 'copied\n') + getPokemonMessage(target, `'s stat changes!`)); @@ -1589,7 +1589,7 @@ export class InvertStatsAttr extends MoveEffectAttr { for (let s = 0; s < target.summonData.battleStats.length; s++) target.summonData.battleStats[s] *= -1; - + target.updateInfo(); user.updateInfo(); target.scene.queueMessage(getPokemonMessage(target, `'s stat changes\nwere all reversed!`)); @@ -1605,7 +1605,7 @@ export class ResetStatsAttr extends MoveEffectAttr { for (let s = 0; s < target.summonData.battleStats.length; s++) target.summonData.battleStats[s] = 0; - + target.updateInfo(); user.updateInfo(); target.scene.queueMessage(getPokemonMessage(target, `'s stat changes\nwere eliminated!`)); From 1484a52fd70adbce0e8ac5f7ce07fadae998465c Mon Sep 17 00:00:00 2001 From: Matt Ross <13306707+mattrossdev@users.noreply.github.com> Date: Fri, 3 May 2024 12:54:21 -0700 Subject: [PATCH 10/11] Add variable power attribute for knock off (#426) * feature/add-variable-power-knock-off * Remove console logs --- src/data/move.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/data/move.ts b/src/data/move.ts index 264f5576ea5..5f86911aebb 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2021,6 +2021,17 @@ export class PresentPowerAttr extends VariablePowerAttr { } } +export class KnockOffPowerAttr extends VariablePowerAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if(target.getHeldItems().length > 0){ + (args[0] as Utils.NumberHolder).value *= 1.5; + return true; + } + + return false; + } +} + export class VariableAtkAttr extends MoveAttr { constructor() { super(); @@ -4583,6 +4594,7 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true) .condition((user, target, move) => !target.status), new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3) + .attr(KnockOffPowerAttr) .partial(), new AttackMove(Moves.ENDEAVOR, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 3) .attr(MatchHpAttr) From 3b61662bba9927115b385abb2893804f5cc5e564 Mon Sep 17 00:00:00 2001 From: Benjamin Odom Date: Fri, 3 May 2024 15:17:37 -0500 Subject: [PATCH 11/11] Fixed Mighty Cleave not being a Slicing Move --- src/data/move.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/move.ts b/src/data/move.ts index 5f86911aebb..5c29b6a5758 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -6428,6 +6428,7 @@ export function initMoves() { new AttackMove(Moves.THUNDERCLAP, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9) .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), new AttackMove(Moves.MIGHTY_CLEAVE, Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9) + .slicingMove() .ignoresProtect(), new AttackMove(Moves.TACHYON_CUTTER, Type.STEEL, MoveCategory.SPECIAL, 50, -1, 10, -1, 0, 9) .attr(MultiHitAttr, MultiHitType._2)