mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-25 16:26:25 +00:00
[Move] Implement Safeguard (#3447)
* Implemented safeguard and tests * Update tests * Add i18n placeholders * Implement Safeguard for non-volatile statuses * Implement protection from confusion and Yawn * Replace `target instanceof EnemyPokemon` with `target.isPlayer()` * Minor capitalization change * First batch of i18n Adds fr, pt_BR, zh_CN, zh_TW Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> * Add more translations + de, es, ko Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Asdar <asdargmng@gmail.com> Co-authored-by: sodam <66295123+sodaMelon@users.noreply.github.com> * Fix broken character in es translation * Update test with new function definition * Add Italian translation Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> * Add move category check for message display Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * Update phase imports in Safeguard test * Fix test imports * Update tests --------- Co-authored-by: Joshua Keegan <keeganjosh@vuw.ac.nz> Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Asdar <asdargmng@gmail.com> Co-authored-by: sodam <66295123+sodaMelon@users.noreply.github.com> Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com>
This commit is contained in:
parent
2d5bd57c44
commit
f54846f735
@ -850,7 +850,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
|
export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
|
||||||
private chance: integer;
|
public chance: integer;
|
||||||
private effects: StatusEffect[];
|
private effects: StatusEffect[];
|
||||||
|
|
||||||
constructor(chance: integer, ...effects: StatusEffect[]) {
|
constructor(chance: integer, ...effects: StatusEffect[]) {
|
||||||
|
@ -905,6 +905,21 @@ class HappyHourTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SafeguardTag extends ArenaTag {
|
||||||
|
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
|
||||||
|
super(ArenaTagType.SAFEGUARD, turnCount, Moves.SAFEGUARD, sourceId, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAdd(arena: Arena): void {
|
||||||
|
arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove(arena: Arena): void {
|
||||||
|
arena.scene.queueMessage(i18next.t(`arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
|
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
|
||||||
switch (tagType) {
|
switch (tagType) {
|
||||||
case ArenaTagType.MIST:
|
case ArenaTagType.MIST:
|
||||||
@ -950,6 +965,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
|
|||||||
return new TailwindTag(turnCount, sourceId, side);
|
return new TailwindTag(turnCount, sourceId, side);
|
||||||
case ArenaTagType.HAPPY_HOUR:
|
case ArenaTagType.HAPPY_HOUR:
|
||||||
return new HappyHourTag(turnCount, sourceId, side);
|
return new HappyHourTag(turnCount, sourceId, side);
|
||||||
|
case ArenaTagType.SAFEGUARD:
|
||||||
|
return new SafeguardTag(turnCount, sourceId, side);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1953,6 +1953,13 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user !== target && target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)) {
|
||||||
|
if (move.category === MoveCategory.STATUS) {
|
||||||
|
user.scene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target)}));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0))
|
if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0))
|
||||||
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
|
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
|
||||||
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect);
|
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect);
|
||||||
@ -4659,6 +4666,17 @@ export class ConfuseAttr extends AddBattlerTagAttr {
|
|||||||
constructor(selfTarget?: boolean) {
|
constructor(selfTarget?: boolean) {
|
||||||
super(BattlerTagType.CONFUSED, selfTarget, false, 2, 5);
|
super(BattlerTagType.CONFUSED, selfTarget, false, 2, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
if (!this.selfTarget && target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)) {
|
||||||
|
if (move.category === MoveCategory.STATUS) {
|
||||||
|
user.scene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target)}));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.apply(user, target, move, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RechargeAttr extends AddBattlerTagAttr {
|
export class RechargeAttr extends AddBattlerTagAttr {
|
||||||
@ -7014,7 +7032,7 @@ export function initMoves() {
|
|||||||
.attr(FriendshipPowerAttr, true),
|
.attr(FriendshipPowerAttr, true),
|
||||||
new StatusMove(Moves.SAFEGUARD, Type.NORMAL, -1, 25, -1, 0, 2)
|
new StatusMove(Moves.SAFEGUARD, Type.NORMAL, -1, 25, -1, 0, 2)
|
||||||
.target(MoveTarget.USER_SIDE)
|
.target(MoveTarget.USER_SIDE)
|
||||||
.unimplemented(),
|
.attr(AddArenaTagAttr, ArenaTagType.SAFEGUARD, 5, true, true),
|
||||||
new StatusMove(Moves.PAIN_SPLIT, Type.NORMAL, -1, 20, -1, 0, 2)
|
new StatusMove(Moves.PAIN_SPLIT, Type.NORMAL, -1, 20, -1, 0, 2)
|
||||||
.attr(HpSplitAttr)
|
.attr(HpSplitAttr)
|
||||||
.condition(failOnBossCondition),
|
.condition(failOnBossCondition),
|
||||||
@ -7203,7 +7221,7 @@ export function initMoves() {
|
|||||||
.attr(RemoveScreensAttr),
|
.attr(RemoveScreensAttr),
|
||||||
new StatusMove(Moves.YAWN, Type.NORMAL, -1, 10, -1, 0, 3)
|
new StatusMove(Moves.YAWN, Type.NORMAL, -1, 10, -1, 0, 3)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true)
|
.attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true)
|
||||||
.condition((user, target, move) => !target.status),
|
.condition((user, target, move) => !target.status && !target.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY)),
|
||||||
new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3)
|
new AttackMove(Moves.KNOCK_OFF, Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3)
|
||||||
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferrable).length > 0 ? 1.5 : 1)
|
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferrable).length > 0 ? 1.5 : 1)
|
||||||
.attr(RemoveHeldItemAttr, false),
|
.attr(RemoveHeldItemAttr, false),
|
||||||
|
@ -22,5 +22,6 @@ export enum ArenaTagType {
|
|||||||
CRAFTY_SHIELD = "CRAFTY_SHIELD",
|
CRAFTY_SHIELD = "CRAFTY_SHIELD",
|
||||||
TAILWIND = "TAILWIND",
|
TAILWIND = "TAILWIND",
|
||||||
HAPPY_HOUR = "HAPPY_HOUR",
|
HAPPY_HOUR = "HAPPY_HOUR",
|
||||||
|
SAFEGUARD = "SAFEGUARD",
|
||||||
NO_CRIT = "NO_CRIT"
|
NO_CRIT = "NO_CRIT"
|
||||||
}
|
}
|
||||||
|
@ -2787,6 +2787,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
const types = this.getTypes(true, true);
|
const types = this.getTypes(true, true);
|
||||||
|
|
||||||
|
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
|
if (sourcePokemon && sourcePokemon !== this && this.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch (effect) {
|
switch (effect) {
|
||||||
case StatusEffect.POISON:
|
case StatusEffect.POISON:
|
||||||
case StatusEffect.TOXIC:
|
case StatusEffect.TOXIC:
|
||||||
|
@ -47,5 +47,11 @@
|
|||||||
"tailwindOnRemovePlayer": "Der Rückenwind auf deiner Seite hat sich gelegt!",
|
"tailwindOnRemovePlayer": "Der Rückenwind auf deiner Seite hat sich gelegt!",
|
||||||
"tailwindOnRemoveEnemy": "Der Rückenwind auf gegnerischer Seite hat sich gelegt!",
|
"tailwindOnRemoveEnemy": "Der Rückenwind auf gegnerischer Seite hat sich gelegt!",
|
||||||
"happyHourOnAdd": "Goldene Zeiten sind angebrochen!",
|
"happyHourOnAdd": "Goldene Zeiten sind angebrochen!",
|
||||||
"happyHourOnRemove": "Die goldenen Zeiten sind vorbei!"
|
"happyHourOnRemove": "Die goldenen Zeiten sind vorbei!",
|
||||||
|
"safeguardOnAdd": "Das ganze Feld wird von einem Schleier umhüllt!",
|
||||||
|
"safeguardOnAddPlayer": "Das Team des Anwenders wird von einem Schleier umhüllt!",
|
||||||
|
"safeguardOnAddEnemy": "Das gegnerische Team wird von einem Schleier umhüllt!",
|
||||||
|
"safeguardOnRemove": "Der mystische Schleier, der das ganze Feld umgab, hat sich gelüftet!",
|
||||||
|
"safeguardOnRemovePlayer": "Der mystische Schleier, der dein Team umgab, hat sich gelüftet!",
|
||||||
|
"safeguardOnRemoveEnemy": "Der mystische Schleier, der das gegnerische Team umgab, hat sich gelüftet!"
|
||||||
}
|
}
|
@ -61,5 +61,6 @@
|
|||||||
"suppressAbilities": "Die Fähigkeit von {{pokemonName}} wirkt nicht mehr!",
|
"suppressAbilities": "Die Fähigkeit von {{pokemonName}} wirkt nicht mehr!",
|
||||||
"revivalBlessing": "{{pokemonName}} ist wieder fit und kampfbereit!",
|
"revivalBlessing": "{{pokemonName}} ist wieder fit und kampfbereit!",
|
||||||
"swapArenaTags": "{{pokemonName}} hat die Effekte, die auf den beiden Seiten des Kampffeldes wirken, miteinander getauscht!",
|
"swapArenaTags": "{{pokemonName}} hat die Effekte, die auf den beiden Seiten des Kampffeldes wirken, miteinander getauscht!",
|
||||||
"exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!"
|
"exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!",
|
||||||
|
"safeguard": "{{targetName}} wird durch Bodyguard geschützt!"
|
||||||
}
|
}
|
@ -1 +1,8 @@
|
|||||||
{}
|
{
|
||||||
|
"safeguardOnAdd": "¡Todos los Pokémon están protegidos por Velo Sagrado!",
|
||||||
|
"safeguardOnAddPlayer": "¡Tu equipo se ha protegido con Velo Sagrado!",
|
||||||
|
"safeguardOnAddEnemy": "¡El equipo enemigo se ha protegido con Velo Sagrado!",
|
||||||
|
"safeguardOnRemove": "¡Velo Sagrado dejó de hacer efecto!",
|
||||||
|
"safeguardOnRemovePlayer": "El efecto de Velo Sagrado en tu equipo se ha disipado.",
|
||||||
|
"safeguardOnRemoveEnemy": "El efecto de Velo Sagrado en el equipo enemigo se ha disipado."
|
||||||
|
}
|
@ -7,5 +7,6 @@
|
|||||||
"usedUpAllElectricity": "¡{{pokemonName}} ha descargado toda su electricidad!",
|
"usedUpAllElectricity": "¡{{pokemonName}} ha descargado toda su electricidad!",
|
||||||
"stoleItem": "¡{{pokemonName}} robó el objeto\n{{itemName}} de {{targetName}}!",
|
"stoleItem": "¡{{pokemonName}} robó el objeto\n{{itemName}} de {{targetName}}!",
|
||||||
"statEliminated": "¡Los cambios en estadísticas fueron eliminados!",
|
"statEliminated": "¡Los cambios en estadísticas fueron eliminados!",
|
||||||
"revivalBlessing": "¡{{pokemonName}} ha revivido!"
|
"revivalBlessing": "¡{{pokemonName}} ha revivido!",
|
||||||
|
"safeguard": "¡{{targetName}} está protegido por Velo Sagrado!"
|
||||||
}
|
}
|
@ -47,5 +47,11 @@
|
|||||||
"tailwindOnRemovePlayer": "Le vent arrière soufflant\nsur votre équipe s’arrête !",
|
"tailwindOnRemovePlayer": "Le vent arrière soufflant\nsur votre équipe s’arrête !",
|
||||||
"tailwindOnRemoveEnemy": "Le vent arrière soufflant\nsur l’équipe ennemie s’arrête !",
|
"tailwindOnRemoveEnemy": "Le vent arrière soufflant\nsur l’équipe ennemie s’arrête !",
|
||||||
"happyHourOnAdd": "L’ambiance est euphorique !",
|
"happyHourOnAdd": "L’ambiance est euphorique !",
|
||||||
"happyHourOnRemove": "L’ambiance se calme !"
|
"happyHourOnRemove": "L’ambiance se calme !",
|
||||||
|
"safeguardOnAdd": "Un voile mystérieux recouvre\ntout le terrain !",
|
||||||
|
"safeguardOnAddPlayer": "Un voile mystérieux recouvre\nvotre équipe !",
|
||||||
|
"safeguardOnAddEnemy": "Un voile mystérieux recouvre\nl’équipe ennemie !",
|
||||||
|
"safeguardOnRemove": "Le terrain n’est plus protégé\npar le voile mystérieux !",
|
||||||
|
"safeguardOnRemovePlayer": "Votre équipe n’est plus protégée\npar le voile mystérieux !",
|
||||||
|
"safeguardOnRemoveEnemy": "L’équipe ennemie n’est plus protégée\npar le voile mystérieux !"
|
||||||
}
|
}
|
@ -61,5 +61,6 @@
|
|||||||
"suppressAbilities": "Le talent de {{pokemonName}}\na été rendu inactif !",
|
"suppressAbilities": "Le talent de {{pokemonName}}\na été rendu inactif !",
|
||||||
"revivalBlessing": "{{pokemonName}} a repris connaissance\net est prêt à se battre de nouveau !",
|
"revivalBlessing": "{{pokemonName}} a repris connaissance\net est prêt à se battre de nouveau !",
|
||||||
"swapArenaTags": "Les effets affectant chaque côté du terrain\nont été échangés par {{pokemonName}} !",
|
"swapArenaTags": "Les effets affectant chaque côté du terrain\nont été échangés par {{pokemonName}} !",
|
||||||
"exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !"
|
"exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !",
|
||||||
|
"safeguard": "{{targetName}} est protégé\npar la capacité Rune Protect !"
|
||||||
}
|
}
|
@ -1 +1,8 @@
|
|||||||
{}
|
{
|
||||||
|
"safeguardOnAdd": "Un velo mistico ricopre il campo!",
|
||||||
|
"safeguardOnAddPlayer": "Un velo mistico ricopre la tua squadra!",
|
||||||
|
"safeguardOnAddEnemy": "Un velo mistico ricopre la squadra avversaria!",
|
||||||
|
"safeguardOnRemove": "Il campo non è più protetto da Salvaguardia!",
|
||||||
|
"safeguardOnRemovePlayer": "La tua squadra non è più protetta da Salvaguardia!",
|
||||||
|
"safeguardOnRemoveEnemy": "La squadra avversaria non è più protetta da Salvaguardia!"
|
||||||
|
}
|
@ -61,5 +61,6 @@
|
|||||||
"suppressAbilities": "L’abilità di {{pokemonName}}\nperde ogni efficacia!",
|
"suppressAbilities": "L’abilità di {{pokemonName}}\nperde ogni efficacia!",
|
||||||
"revivalBlessing": "{{pokemonName}} torna in forze!",
|
"revivalBlessing": "{{pokemonName}} torna in forze!",
|
||||||
"swapArenaTags": "{{pokemonName}} ha invertito gli effetti attivi\nnelle due metà del campo!",
|
"swapArenaTags": "{{pokemonName}} ha invertito gli effetti attivi\nnelle due metà del campo!",
|
||||||
"exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!"
|
"exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!",
|
||||||
|
"safeguard": "Salvaguardia protegge {{targetName}}!"
|
||||||
}
|
}
|
@ -47,5 +47,11 @@
|
|||||||
"tailwindOnRemovePlayer": "우리 편의\n순풍이 멈췄다!",
|
"tailwindOnRemovePlayer": "우리 편의\n순풍이 멈췄다!",
|
||||||
"tailwindOnRemoveEnemy": "상대의\n순풍이 멈췄다!",
|
"tailwindOnRemoveEnemy": "상대의\n순풍이 멈췄다!",
|
||||||
"happyHourOnAdd": "모두 행복한 기분에\n휩싸였다!",
|
"happyHourOnAdd": "모두 행복한 기분에\n휩싸였다!",
|
||||||
"happyHourOnRemove": "기분이 원래대로 돌아왔다."
|
"happyHourOnRemove": "기분이 원래대로 돌아왔다.",
|
||||||
|
"safeguardOnAdd": "필드 전체가 신비의 베일에 둘러싸였다!",
|
||||||
|
"safeguardOnAddPlayer": "우리 편은 신비의 베일에 둘러싸였다!",
|
||||||
|
"safeguardOnAddEnemy": "상대 편은 신비의 베일에 둘러싸였다!",
|
||||||
|
"safeguardOnRemove": "필드를 감싸던 신비의 베일이 없어졌다!",
|
||||||
|
"safeguardOnRemovePlayer": "우리 편을 감싸던 신비의 베일이 없어졌다!",
|
||||||
|
"safeguardOnRemoveEnemy": "상대 편을 감싸던 신비의 베일이 없어졌다!"
|
||||||
}
|
}
|
@ -61,5 +61,6 @@
|
|||||||
"suppressAbilities": "{{pokemonName}}의\n특성이 효과를 발휘하지 못하게 되었다!",
|
"suppressAbilities": "{{pokemonName}}의\n특성이 효과를 발휘하지 못하게 되었다!",
|
||||||
"revivalBlessing": "{{pokemonName}}[[는]]\n정신을 차려 싸울 수 있게 되었다!",
|
"revivalBlessing": "{{pokemonName}}[[는]]\n정신을 차려 싸울 수 있게 되었다!",
|
||||||
"swapArenaTags": "{{pokemonName}}[[는]]\n서로의 필드 효과를 교체했다!",
|
"swapArenaTags": "{{pokemonName}}[[는]]\n서로의 필드 효과를 교체했다!",
|
||||||
"exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!"
|
"exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!",
|
||||||
|
"safeguard": "{{targetName}}[[는]] 신비의 베일이 지켜 주고 있다!"
|
||||||
}
|
}
|
@ -47,5 +47,11 @@
|
|||||||
"tailwindOnRemovePlayer": "O Tailwind de sua equipe acabou!",
|
"tailwindOnRemovePlayer": "O Tailwind de sua equipe acabou!",
|
||||||
"tailwindOnRemoveEnemy": "O Tailwind da equipe adversária acabou!",
|
"tailwindOnRemoveEnemy": "O Tailwind da equipe adversária acabou!",
|
||||||
"happyHourOnAdd": "Todos foram envolvidos por uma atmosfera alegre!",
|
"happyHourOnAdd": "Todos foram envolvidos por uma atmosfera alegre!",
|
||||||
"happyHourOnRemove": "A atmosfera retornou ao normal."
|
"happyHourOnRemove": "A atmosfera retornou ao normal.",
|
||||||
|
"safeguardOnAdd": "O campo de batalha está envolto num véu místico!",
|
||||||
|
"safeguardOnAddPlayer": "Sua equipe se envolveu num véu místico!",
|
||||||
|
"safeguardOnAddEnemy": "A equipe adversária se envolveu num véu místico!",
|
||||||
|
"safeguardOnRemove": "O campo não está mais protegido por Safeguard!",
|
||||||
|
"safeguardOnRemovePlayer": "Sua equipe não está mais protegido por Safeguard!",
|
||||||
|
"safeguardOnRemoveEnemy": "A equipe adversária não está mais protegido por Safeguard!"
|
||||||
}
|
}
|
@ -61,5 +61,6 @@
|
|||||||
"suppressAbilities": "A habilidade de {{pokemonName}}\nfoi suprimida!",
|
"suppressAbilities": "A habilidade de {{pokemonName}}\nfoi suprimida!",
|
||||||
"revivalBlessing": "{{pokemonName}} foi reanimado!",
|
"revivalBlessing": "{{pokemonName}} foi reanimado!",
|
||||||
"swapArenaTags": "{{pokemonName}} trocou os efeitos de batalha que afetam cada lado do campo!",
|
"swapArenaTags": "{{pokemonName}} trocou os efeitos de batalha que afetam cada lado do campo!",
|
||||||
"exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!"
|
"exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!",
|
||||||
|
"safeguard": "{{targetName}} está protegido por Safeguard!"
|
||||||
}
|
}
|
@ -47,5 +47,11 @@
|
|||||||
"tailwindOnRemovePlayer": "我方的顺风停止了!",
|
"tailwindOnRemovePlayer": "我方的顺风停止了!",
|
||||||
"tailwindOnRemoveEnemy": "敌方的顺风停止了!",
|
"tailwindOnRemoveEnemy": "敌方的顺风停止了!",
|
||||||
"happyHourOnAdd": "大家被欢乐的\n气氛包围了!",
|
"happyHourOnAdd": "大家被欢乐的\n气氛包围了!",
|
||||||
"happyHourOnRemove": "气氛回复到平常了。"
|
"happyHourOnRemove": "气氛回复到平常了。",
|
||||||
|
"safeguardOnAdd": "整个场地被\n神秘之幕包围了!",
|
||||||
|
"safeguardOnAddPlayer": "我方被\n神秘之幕包围了!",
|
||||||
|
"safeguardOnAddEnemy": "对手被\n神秘之幕包围了!",
|
||||||
|
"safeguardOnRemove": "包围整个场地的\n神秘之幕消失了!",
|
||||||
|
"safeguardOnRemovePlayer": "包围我方的\n神秘之幕消失了!",
|
||||||
|
"safeguardOnRemoveEnemy": "包围对手的\n神秘之幕消失了!"
|
||||||
}
|
}
|
@ -61,5 +61,6 @@
|
|||||||
"suppressAbilities": "{{pokemonName}}的特性\n变得无效了!",
|
"suppressAbilities": "{{pokemonName}}的特性\n变得无效了!",
|
||||||
"revivalBlessing": "{{pokemonName}}复活了!",
|
"revivalBlessing": "{{pokemonName}}复活了!",
|
||||||
"swapArenaTags": "{{pokemonName}}\n交换了双方的场地效果!",
|
"swapArenaTags": "{{pokemonName}}\n交换了双方的场地效果!",
|
||||||
"exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!"
|
"exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!",
|
||||||
|
"safeguard": "{{targetName}}\n正受到神秘之幕的保护!"
|
||||||
}
|
}
|
@ -1,5 +1,11 @@
|
|||||||
{
|
{
|
||||||
"noCritOnAddPlayer": "{{moveName}}保護了你的\n隊伍不被擊中要害!",
|
"noCritOnAddPlayer": "{{moveName}}保護了你的\n隊伍不被擊中要害!",
|
||||||
"noCritOnAddEnemy": "{{moveName}}保護了對方的\n隊伍不被擊中要害!",
|
"noCritOnAddEnemy": "{{moveName}}保護了對方的\n隊伍不被擊中要害!",
|
||||||
"noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!"
|
"noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!",
|
||||||
|
"safeguardOnAdd": "整個場地被\n神秘之幕包圍了!",
|
||||||
|
"safeguardOnAddPlayer": "我方被\n神秘之幕包圍了!",
|
||||||
|
"safeguardOnAddEnemy": "對手被\n神秘之幕包圍了!",
|
||||||
|
"safeguardOnRemove": "包圍整個場地的\n神秘之幕消失了!",
|
||||||
|
"safeguardOnRemovePlayer": "包圍我方的\n神秘之幕消失了!",
|
||||||
|
"safeguardOnRemoveEnemy": "包圍對手的\n神秘之幕消失了!"
|
||||||
}
|
}
|
@ -61,5 +61,6 @@
|
|||||||
"suppressAbilities": "{{pokemonName}}的特性\n變得無效了!",
|
"suppressAbilities": "{{pokemonName}}的特性\n變得無效了!",
|
||||||
"revivalBlessing": "{{pokemonName}}復活了!",
|
"revivalBlessing": "{{pokemonName}}復活了!",
|
||||||
"swapArenaTags": "{{pokemonName}}\n交換了雙方的場地效果!",
|
"swapArenaTags": "{{pokemonName}}\n交換了雙方的場地效果!",
|
||||||
"exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!"
|
"exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!",
|
||||||
|
"safeguard": "{{targetName}}\n正受到神秘之幕的保護!"
|
||||||
}
|
}
|
150
src/test/moves/safeguard.test.ts
Normal file
150
src/test/moves/safeguard.test.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { allAbilities, PostDefendContactApplyStatusEffectAbAttr } from "#app/data/ability";
|
||||||
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
describe("Moves - Safeguard", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.enemySpecies(Species.DRATINI)
|
||||||
|
.enemyMoveset(Array(4).fill(Moves.SAFEGUARD))
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyLevel(5)
|
||||||
|
.starterSpecies(Species.DRATINI)
|
||||||
|
.moveset([Moves.NUZZLE, Moves.SPORE, Moves.YAWN, Moves.SPLASH])
|
||||||
|
.ability(Abilities.BALL_FETCH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("protects from damaging moves with additional effects", async () => {
|
||||||
|
await game.startBattle();
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.NUZZLE);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemy.status).toBeUndefined();
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("protects from status moves", async () => {
|
||||||
|
await game.startBattle();
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPORE);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.status).toBeUndefined();
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("protects from confusion", async () => {
|
||||||
|
game.override.moveset([Moves.CONFUSE_RAY]);
|
||||||
|
await game.startBattle();
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.CONFUSE_RAY);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.summonData.tags).toEqual([]);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("protects ally from status", async () => {
|
||||||
|
game.override.battleType("double");
|
||||||
|
|
||||||
|
await game.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.SPORE, 0, BattlerIndex.ENEMY_2);
|
||||||
|
game.move.select(Moves.NUZZLE, 1, BattlerIndex.ENEMY_2);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
|
expect(enemyPokemon[0].status).toBeUndefined();
|
||||||
|
expect(enemyPokemon[1].status).toBeUndefined();
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("protects from Yawn", async () => {
|
||||||
|
await game.startBattle();
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.YAWN);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.summonData.tags).toEqual([]);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("doesn't protect from already existing Yawn", async () => {
|
||||||
|
await game.startBattle();
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.YAWN);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.status?.effect).toEqual(StatusEffect.SLEEP);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("doesn't protect from self-inflicted via Rest or Flame Orb", async () => {
|
||||||
|
game.override.enemyHeldItems([{name: "FLAME_ORB"}]);
|
||||||
|
await game.startBattle();
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.status?.effect).toEqual(StatusEffect.BURN);
|
||||||
|
|
||||||
|
game.override.enemyMoveset(Array(4).fill(Moves.REST));
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.status?.effect).toEqual(StatusEffect.SLEEP);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("protects from ability-inflicted status", async () => {
|
||||||
|
game.override.ability(Abilities.STATIC);
|
||||||
|
vi.spyOn(allAbilities[Abilities.STATIC].getAttrs(PostDefendContactApplyStatusEffectAbAttr)[0], "chance", "get").mockReturnValue(100);
|
||||||
|
await game.startBattle();
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.status).toBeUndefined();
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user