diff --git a/play.pokemonshowdown.com/src/battle-animations.ts b/play.pokemonshowdown.com/src/battle-animations.ts index 7d5b959317..289a50c254 100644 --- a/play.pokemonshowdown.com/src/battle-animations.ts +++ b/play.pokemonshowdown.com/src/battle-animations.ts @@ -107,8 +107,8 @@ export class BattleScene implements BattleSceneStub { if (pokemon) return pokemon.speciesForme; } if (!pokemonId.startsWith('p')) return '???pokemon:' + pokemonId + '???'; - if (pokemonId.charAt(3) === ':') return pokemonId.slice(4).trim(); - else if (pokemonId.charAt(2) === ':') return pokemonId.slice(3).trim(); + if (pokemonId.charAt(3) === ':') return BattleTextParser.escapeReplace(pokemonId.slice(4).trim()); + else if (pokemonId.charAt(2) === ':') return BattleTextParser.escapeReplace(pokemonId.slice(3).trim()); return '???pokemon:' + pokemonId + '???'; }; diff --git a/play.pokemonshowdown.com/src/battle-text-parser.ts b/play.pokemonshowdown.com/src/battle-text-parser.ts index af84ecb006..0ae6ad93e0 100644 --- a/play.pokemonshowdown.com/src/battle-text-parser.ts +++ b/play.pokemonshowdown.com/src/battle-text-parser.ts @@ -15,9 +15,13 @@ type KWArgs = {[kw: string]: string}; type SideID = 'p1' | 'p2' | 'p3' | 'p4'; class BattleTextParser { + /** escaped for string.replace */ p1 = "Player 1"; + /** escaped for string.replace */ p2 = "Player 2"; + /** escaped for string.replace */ p3 = "Player 3"; + /** escaped for string.replace */ p4 = "Player 4"; perspective: SideID; gen = 9; @@ -243,15 +247,20 @@ class BattleTextParser { static escapeRegExp(input: string) { return input.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'); } + static escapeReplace(input: string) { + return input.replace(/\$/g, '$$$$'); + } + /** Returns a pokemon name escaped for passing into the second argument of string.replace */ pokemonName = (pokemon: string) => { if (!pokemon) return ''; if (!pokemon.startsWith('p')) return `???pokemon:${pokemon}???`; - if (pokemon.charAt(3) === ':') return pokemon.slice(4).trim(); - else if (pokemon.charAt(2) === ':') return pokemon.slice(3).trim(); + if (pokemon.charAt(3) === ':') return BattleTextParser.escapeReplace(pokemon.slice(4).trim()); + else if (pokemon.charAt(2) === ':') return BattleTextParser.escapeReplace(pokemon.slice(3).trim()); return `???pokemon:${pokemon}???`; }; + /** Returns a string escaped for passing into the second argument of string.replace */ pokemon(pokemon: string) { if (!pokemon) return ''; let side = pokemon.slice(0, 2); @@ -259,9 +268,10 @@ class BattleTextParser { const name = this.pokemonName(pokemon); const isNear = side === this.perspective || side === BattleTextParser.allyID(side as SideID); const template = BattleText.default[isNear ? 'pokemon' : 'opposingPokemon']; - return template.replace('[NICKNAME]', name); + return template.replace('[NICKNAME]', name).replace(/\$/g, '$$$$'); } + /** Returns a string escaped for passing into the second argument of string.replace */ pokemonFull(pokemon: string, details: string): [string, string] { const nickname = this.pokemonName(pokemon); @@ -434,13 +444,13 @@ class BattleTextParser { case 'player': { const [, side, name] = args; if (side === 'p1' && name) { - this.p1 = name; + this.p1 = BattleTextParser.escapeReplace(name); } else if (side === 'p2' && name) { - this.p2 = name; + this.p2 = BattleTextParser.escapeReplace(name); } else if (side === 'p3' && name) { - this.p3 = name; + this.p3 = BattleTextParser.escapeReplace(name); } else if (side === 'p4' && name) { - this.p4 = name; + this.p4 = BattleTextParser.escapeReplace(name); } return ''; }