mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
Support headless client Battle (#1151)
This commit is contained in:
parent
f09d917110
commit
ee91a72dc6
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -19,3 +19,6 @@ package-lock.json
|
|||
/js/battle-dex-data.js
|
||||
/js/battle-animations-moves.js
|
||||
/js/battle-animations.js
|
||||
/js/battle-scene-stub.js
|
||||
|
||||
.vscode
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@
|
|||
"image-size": "^0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^5.2.5",
|
||||
"@types/node": "^8.0.7",
|
||||
"eslint": "^4.11.0",
|
||||
"mocha": "^5.2.0",
|
||||
"testcafe": "^0.18.4"
|
||||
},
|
||||
"private": true
|
||||
|
|
|
|||
|
|
@ -31,6 +31,80 @@ This license DOES NOT extend to any other files in this repository.
|
|||
*/
|
||||
|
||||
class BattleScene {
|
||||
setFrameHTML(html: any) {
|
||||
this.$frame.html(html);
|
||||
}
|
||||
setControlsHTML(html: any) {
|
||||
let $controls = this.$frame.parent().children('.battle-controls');
|
||||
$controls.html(html);
|
||||
}
|
||||
|
||||
// Methods defined in PokemonSprite but need to be called through BattleScene
|
||||
removeEffect(pokemon: Pokemon, id: ID, instant?: boolean) {
|
||||
return pokemon.sprite.removeEffect(id, instant);
|
||||
}
|
||||
addEffect(pokemon: Pokemon, id: ID, instant?: boolean) {
|
||||
return pokemon.sprite.addEffect(id, instant);
|
||||
}
|
||||
animSummon(pokemon: Pokemon, slot: number, instant?: boolean) {
|
||||
return pokemon.sprite.animSummon(pokemon, slot, instant);
|
||||
}
|
||||
animUnsummon(pokemon: Pokemon, instant?: boolean) {
|
||||
return pokemon.sprite.animUnsummon(pokemon, instant);
|
||||
}
|
||||
animDragIn(pokemon: Pokemon, slot: number) {
|
||||
return pokemon.sprite.animDragIn(pokemon, slot);
|
||||
}
|
||||
animDragOut(pokemon: Pokemon) {
|
||||
return pokemon.sprite.animDragOut(pokemon);
|
||||
}
|
||||
updateStatbar(pokemon: Pokemon, updatePrevhp?: boolean, updateHp?: boolean) {
|
||||
return pokemon.sprite.updateStatbar(pokemon, updatePrevhp, updateHp);
|
||||
}
|
||||
updateStatbarIfExists(pokemon: Pokemon, updatePrevhp?: boolean, updateHp?: boolean) {
|
||||
return pokemon.sprite.updateStatbarIfExists(pokemon, updatePrevhp, updateHp);
|
||||
}
|
||||
animTransform(pokemon: Pokemon, isCustomAnim?: boolean, isPermanent?: boolean) {
|
||||
return pokemon.sprite.animTransform(pokemon, isCustomAnim, isPermanent);
|
||||
}
|
||||
clearEffects(pokemon: Pokemon) {
|
||||
return pokemon.sprite.clearEffects();
|
||||
}
|
||||
removeTransform(pokemon: Pokemon) {
|
||||
return pokemon.sprite.removeTransform();
|
||||
}
|
||||
animFaint(pokemon: Pokemon) {
|
||||
return pokemon.sprite.animFaint(pokemon);
|
||||
}
|
||||
animReset(pokemon: Pokemon) {
|
||||
return pokemon.sprite.animReset();
|
||||
}
|
||||
anim(pokemon: Pokemon, end: ScenePos, transition?: string) {
|
||||
return pokemon.sprite.anim(end, transition);
|
||||
}
|
||||
beforeMove(pokemon: Pokemon) {
|
||||
return pokemon.sprite.beforeMove();
|
||||
}
|
||||
afterMove(pokemon: Pokemon) {
|
||||
return pokemon.sprite.afterMove();
|
||||
}
|
||||
updateSpritesForSide(side: Side) {
|
||||
if (side.missedPokemon && side.missedPokemon.sprite) {
|
||||
side.missedPokemon.sprite.destroy();
|
||||
}
|
||||
|
||||
side.missedPokemon = {
|
||||
sprite: new PokemonSprite(null, {
|
||||
x: side.leftof(-100),
|
||||
y: side.y,
|
||||
z: side.z,
|
||||
opacity: 0,
|
||||
}, this, side.n)
|
||||
} as Pokemon;
|
||||
|
||||
side.missedPokemon.sprite.isMissedPokemon = true;
|
||||
}
|
||||
|
||||
battle: Battle;
|
||||
animating = true;
|
||||
acceleration = 1;
|
||||
|
|
@ -2224,6 +2298,12 @@ class PokemonSprite extends Sprite {
|
|||
}
|
||||
}
|
||||
|
||||
updateStatbarIfExists(pokemon: Pokemon, updatePrevhp?: boolean, updateHp?: boolean) {
|
||||
if (this.$statbar) {
|
||||
this.updateStatbar(pokemon, updatePrevhp, updateHp);
|
||||
}
|
||||
}
|
||||
|
||||
updateStatbar(pokemon: Pokemon, updatePrevhp?: boolean, updateHp?: boolean) {
|
||||
if (!this.scene.animating) return;
|
||||
if (!pokemon.isActive()) {
|
||||
|
|
|
|||
|
|
@ -193,12 +193,12 @@ const Tools = {
|
|||
|
||||
resourcePrefix: (() => {
|
||||
let prefix = '';
|
||||
if (document.location!.protocol !== 'http:') prefix = 'https:';
|
||||
if (!window.document || !document.location || document.location.protocol !== 'http:') prefix = 'https:';
|
||||
return prefix + '//play.pokemonshowdown.com/';
|
||||
})(),
|
||||
|
||||
fxPrefix: (() => {
|
||||
if (document.location!.protocol === 'file:') {
|
||||
if (window.document && document.location && document.location.protocol === 'file:') {
|
||||
if (window.Replays) return 'https://play.pokemonshowdown.com/fx/';
|
||||
return 'fx/';
|
||||
}
|
||||
|
|
|
|||
68
src/battle-scene-stub.ts
Normal file
68
src/battle-scene-stub.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
class BattleSceneStub {
|
||||
animating: boolean = false;
|
||||
acceleration: number = NaN;
|
||||
gen: number = NaN;
|
||||
activeCount: number = NaN;
|
||||
numericId: number = NaN;
|
||||
timeOffset: number = NaN;
|
||||
interruptionCount: number = NaN;
|
||||
messagebarOpen: boolean = false;
|
||||
|
||||
abilityActivateAnim(pokemon: Pokemon, result: string): void { }
|
||||
addPokemonSprite(pokemon: Pokemon) { return null!; }
|
||||
addSideCondition(siden: number, id: ID, instant?: boolean | undefined): void { }
|
||||
animationOff(): void { }
|
||||
animationOn(): void { }
|
||||
closeMessagebar(): void { }
|
||||
damageAnim(pokemon: Pokemon, damage: string | number): void { }
|
||||
destroy(): void { }
|
||||
finishAnimations(): JQuery.Promise<JQuery<HTMLElement>, any, any> | undefined { return void(0); }
|
||||
healAnim(pokemon: Pokemon, damage: string | number): void { }
|
||||
hideJoinButtons(): void { }
|
||||
incrementTurn(): void { }
|
||||
log(html: string, preempt?: boolean | undefined): void { }
|
||||
message(message: string, hiddenMessage?: string | undefined): void { }
|
||||
pause(): void { }
|
||||
preemptCatchup(): void { }
|
||||
removeSideCondition(siden: number, id: ID): void { }
|
||||
reset(): void { }
|
||||
resultAnim(pokemon: Pokemon, result: string, type: "bad" | "good" | "neutral" | "par" | "psn" | "frz" | "slp" | "brn"): void { }
|
||||
resume(): void { }
|
||||
runMoveAnim(moveid: ID, participants: Pokemon[]): void { }
|
||||
runOtherAnim(moveid: ID, participants: Pokemon[]): void { }
|
||||
runPrepareAnim(moveid: ID, attacker: Pokemon, defender: Pokemon): void { }
|
||||
runResidualAnim(moveid: ID, pokemon: Pokemon): void { }
|
||||
runStatusAnim(moveid: ID, participants: Pokemon[]): void { }
|
||||
soundStart(): void { }
|
||||
soundStop(): void { }
|
||||
startAnimations(): void { }
|
||||
teamPreview(): void { }
|
||||
teamPreviewEnd(): void { }
|
||||
updateGen(): void { }
|
||||
updateSidebar(side: Side): void { }
|
||||
updateSidebars(): void { }
|
||||
updateStatbars(): void { }
|
||||
updateWeather(instant?: boolean | undefined): void { }
|
||||
upkeepWeather(): void { }
|
||||
wait(time: number): void { }
|
||||
setFrameHTML(html: any): void { }
|
||||
setControlsHTML(html: any): void { }
|
||||
removeEffect(pokemon: Pokemon, id: ID, instant?: boolean) { }
|
||||
addEffect(pokemon: Pokemon, id: ID, instant?: boolean) { }
|
||||
animSummon(pokemon: Pokemon, slot: number, instant?: boolean) { }
|
||||
animUnsummon(pokemon: Pokemon, instant?: boolean) { }
|
||||
animDragIn(pokemon: Pokemon, slot: number) { }
|
||||
animDragOut(pokemon: Pokemon) { }
|
||||
updateStatbar(pokemon: Pokemon, updatePrevhp?: boolean, updateHp?: boolean) { }
|
||||
updateStatbarIfExists(pokemon: Pokemon, updatePrevhp?: boolean, updateHp?: boolean) { }
|
||||
animTransform(pokemon: Pokemon, isCustomAnim?: boolean, isPermanent?: boolean) { }
|
||||
clearEffects(pokemon: Pokemon) { }
|
||||
removeTransform(pokemon: Pokemon) { }
|
||||
animFaint(pokemon: Pokemon) { }
|
||||
animReset(pokemon: Pokemon) { }
|
||||
anim(pokemon: Pokemon, end: ScenePos, transition?: string) { }
|
||||
beforeMove(pokemon: Pokemon) { }
|
||||
afterMove(pokemon: Pokemon) { }
|
||||
updateSpritesForSide(side: Side) { }
|
||||
unlink(userid: string, showRevealButton = false) { }
|
||||
}
|
||||
118
src/battle.ts
118
src/battle.ts
|
|
@ -245,26 +245,26 @@ class Pokemon {
|
|||
return this.ident.substr(0, 2) + slots[this.slot] + this.ident.substr(2);
|
||||
}
|
||||
removeVolatile(volatile: ID) {
|
||||
this.sprite.removeEffect(volatile);
|
||||
this.side.battle.scene.removeEffect(this, volatile);
|
||||
if (!this.hasVolatile(volatile)) return;
|
||||
delete this.volatiles[volatile];
|
||||
}
|
||||
addVolatile(volatile: ID, ...args: any[]) {
|
||||
if (this.hasVolatile(volatile) && !args.length) return;
|
||||
this.volatiles[volatile] = [volatile, ...args] as EffectState;
|
||||
this.sprite.addEffect(volatile);
|
||||
this.side.battle.scene.addEffect(this, volatile);
|
||||
}
|
||||
hasVolatile(volatile: ID) {
|
||||
return !!this.volatiles[volatile];
|
||||
}
|
||||
removeTurnstatus(volatile: ID) {
|
||||
this.sprite.removeEffect(volatile);
|
||||
this.side.battle.scene.removeEffect(this, volatile);
|
||||
if (!this.hasTurnstatus(volatile)) return;
|
||||
delete this.turnstatuses[volatile];
|
||||
}
|
||||
addTurnstatus(volatile: ID) {
|
||||
volatile = toId(volatile);
|
||||
this.sprite.addEffect(volatile);
|
||||
this.side.battle.scene.addEffect(this, volatile);
|
||||
if (this.hasTurnstatus(volatile)) return;
|
||||
this.turnstatuses[volatile] = [volatile];
|
||||
}
|
||||
|
|
@ -278,7 +278,7 @@ class Pokemon {
|
|||
this.turnstatuses = {};
|
||||
}
|
||||
removeMovestatus(volatile: ID) {
|
||||
this.sprite.removeEffect(volatile);
|
||||
this.side.battle.scene.removeEffect(this, volatile);
|
||||
if (!this.hasMovestatus(volatile)) return;
|
||||
delete this.movestatuses[volatile];
|
||||
}
|
||||
|
|
@ -286,7 +286,7 @@ class Pokemon {
|
|||
volatile = toId(volatile);
|
||||
if (this.hasMovestatus(volatile)) return;
|
||||
this.movestatuses[volatile] = [volatile];
|
||||
this.sprite.addEffect(volatile);
|
||||
this.side.battle.scene.addEffect(this, volatile);
|
||||
}
|
||||
hasMovestatus(volatile: ID) {
|
||||
return !!this.movestatuses[volatile];
|
||||
|
|
@ -301,7 +301,7 @@ class Pokemon {
|
|||
this.volatiles = {};
|
||||
this.clearTurnstatuses();
|
||||
this.clearMovestatuses();
|
||||
this.sprite.clearEffects();
|
||||
this.side.battle.scene.clearEffects(this);
|
||||
}
|
||||
markMove(moveName: string, pp?: number, recursionSource?: string) {
|
||||
if (recursionSource === this.ident) return;
|
||||
|
|
@ -475,7 +475,7 @@ class Pokemon {
|
|||
|
||||
pokemon.boosts = {};
|
||||
pokemon.volatiles = {};
|
||||
pokemon.sprite.removeTransform();
|
||||
pokemon.side.battle.scene.removeTransform(pokemon);
|
||||
pokemon.statusStage = 0;
|
||||
}
|
||||
copyTypesFrom(pokemon: Pokemon) {
|
||||
|
|
@ -613,19 +613,7 @@ class Side {
|
|||
}
|
||||
updateSprites() {
|
||||
this.z = (this.n ? 200 : 0);
|
||||
if (this.missedPokemon) {
|
||||
this.missedPokemon.sprite.destroy();
|
||||
}
|
||||
this.missedPokemon = {
|
||||
sprite: new PokemonSprite(null, {
|
||||
x: this.leftof(-100),
|
||||
y: this.y,
|
||||
z: this.z,
|
||||
opacity: 0,
|
||||
},
|
||||
this.battle.scene, this.n)
|
||||
} as Pokemon;
|
||||
this.missedPokemon.sprite.isMissedPokemon = true;
|
||||
this.battle.scene.updateSpritesForSide(this);
|
||||
}
|
||||
setAvatar(spriteid: string) {
|
||||
this.spriteid = spriteid;
|
||||
|
|
@ -801,7 +789,7 @@ class Side {
|
|||
this.battle.message('' + Tools.escapeHTML(pokemon.side.name) + ' sent out ' + pokemon.getFullName() + '!');
|
||||
}
|
||||
|
||||
pokemon.sprite.animSummon(pokemon, slot);
|
||||
this.battle.scene.animSummon(pokemon, slot);
|
||||
|
||||
if (this.battle.switchCallback) this.battle.switchCallback(this.battle, this);
|
||||
}
|
||||
|
|
@ -811,7 +799,7 @@ class Side {
|
|||
if (oldpokemon === pokemon) return;
|
||||
this.lastPokemon = oldpokemon;
|
||||
if (oldpokemon) {
|
||||
oldpokemon.sprite.animDragOut(oldpokemon);
|
||||
this.battle.scene.animDragOut(oldpokemon);
|
||||
oldpokemon.clearVolatile();
|
||||
}
|
||||
pokemon.clearVolatile();
|
||||
|
|
@ -820,7 +808,7 @@ class Side {
|
|||
this.active[slot] = pokemon;
|
||||
pokemon.slot = slot;
|
||||
|
||||
pokemon.sprite.animDragIn(pokemon, slot);
|
||||
this.battle.scene.animDragIn(pokemon, slot);
|
||||
|
||||
if (this.battle.dragCallback) this.battle.dragCallback(this.battle, this);
|
||||
}
|
||||
|
|
@ -846,9 +834,9 @@ class Side {
|
|||
pokemon.slot = slot;
|
||||
|
||||
if (oldpokemon) {
|
||||
oldpokemon.sprite.animUnsummon(oldpokemon, true);
|
||||
this.battle.scene.animUnsummon(oldpokemon, true);
|
||||
}
|
||||
pokemon.sprite.animSummon(pokemon, slot, true);
|
||||
this.battle.scene.animSummon(pokemon, slot, true);
|
||||
// not sure if we want a different callback
|
||||
if (this.battle.dragCallback) this.battle.dragCallback(this.battle, this);
|
||||
}
|
||||
|
|
@ -873,7 +861,7 @@ class Side {
|
|||
this.lastPokemon = pokemon;
|
||||
this.active[slot] = null;
|
||||
|
||||
pokemon.sprite.animUnsummon(pokemon);
|
||||
this.battle.scene.animUnsummon(pokemon);
|
||||
}
|
||||
swapTo(pokemon: Pokemon, slot: number, kwargs: {[k: string]: string}) {
|
||||
if (pokemon.slot === slot) return;
|
||||
|
|
@ -899,11 +887,11 @@ class Side {
|
|||
this.active[slot] = pokemon;
|
||||
this.active[oslot] = target;
|
||||
|
||||
pokemon.sprite.animUnsummon(pokemon, true);
|
||||
if (target) target.sprite.animUnsummon(target, true);
|
||||
this.battle.scene.animUnsummon(pokemon, true);
|
||||
if (target) this.battle.scene.animUnsummon(target, true);
|
||||
|
||||
pokemon.sprite.animSummon(pokemon, slot, true);
|
||||
if (target) target.sprite.animSummon(target, oslot, true);
|
||||
this.battle.scene.animSummon(pokemon, slot, true);
|
||||
if (target) this.battle.scene.animSummon(target, oslot, true);
|
||||
}
|
||||
swapWith(pokemon: Pokemon, target: Pokemon, kwargs: {[k: string]: string}) {
|
||||
// method provided for backwards compatibility only
|
||||
|
|
@ -926,11 +914,11 @@ class Side {
|
|||
this.active[nslot] = pokemon;
|
||||
this.active[oslot] = target;
|
||||
|
||||
pokemon.sprite.animUnsummon(pokemon, true);
|
||||
target.sprite.animUnsummon(target, true);
|
||||
this.battle.scene.animUnsummon(pokemon, true);
|
||||
this.battle.scene.animUnsummon(target, true);
|
||||
|
||||
pokemon.sprite.animSummon(pokemon, nslot, true);
|
||||
target.sprite.animSummon(target, oslot, true);
|
||||
this.battle.scene.animSummon(pokemon, nslot, true);
|
||||
this.battle.scene.animSummon(target, oslot, true);
|
||||
}
|
||||
faint(pokemon: Pokemon, slot = pokemon.slot) {
|
||||
pokemon.clearVolatile();
|
||||
|
|
@ -946,7 +934,7 @@ class Side {
|
|||
pokemon.fainted = true;
|
||||
pokemon.hp = 0;
|
||||
|
||||
pokemon.sprite.animFaint(pokemon);
|
||||
this.battle.scene.animFaint(pokemon);
|
||||
if (this.battle.faintCallback) this.battle.faintCallback(this.battle, this);
|
||||
}
|
||||
destroy() {
|
||||
|
|
@ -966,7 +954,7 @@ enum Playback {
|
|||
}
|
||||
|
||||
class Battle {
|
||||
scene: BattleScene;
|
||||
scene: BattleScene | BattleSceneStub;
|
||||
|
||||
sidesSwitched = false;
|
||||
|
||||
|
|
@ -1043,7 +1031,12 @@ class Battle {
|
|||
|
||||
constructor($frame: JQuery<HTMLElement>, $logFrame: JQuery<HTMLElement>, id = '') {
|
||||
this.id = id;
|
||||
this.scene = new BattleScene(this, $frame, $logFrame);
|
||||
|
||||
if (!$frame && !$logFrame) {
|
||||
this.scene = new BattleSceneStub();
|
||||
} else {
|
||||
this.scene = new BattleScene(this, $frame, $logFrame);
|
||||
}
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
|
@ -1349,7 +1342,7 @@ class Battle {
|
|||
if (move.id === 'focuspunch') {
|
||||
pokemon.removeTurnstatus('focuspunch' as ID);
|
||||
}
|
||||
pokemon.sprite.updateStatbar(pokemon);
|
||||
this.scene.updateStatbar(pokemon);
|
||||
if (!target) {
|
||||
target = pokemon.side.foe.active[0];
|
||||
}
|
||||
|
|
@ -1522,7 +1515,7 @@ class Battle {
|
|||
}
|
||||
cantUseMove(pokemon: Pokemon, effect: Effect, move: Move, kwargs: {[k: string]: string}) {
|
||||
pokemon.clearMovestatuses();
|
||||
pokemon.sprite.updateStatbar(pokemon);
|
||||
this.scene.updateStatbar(pokemon);
|
||||
if (effect.id in BattleStatusAnims) {
|
||||
this.scene.runStatusAnim(effect.id, [pokemon]);
|
||||
}
|
||||
|
|
@ -1609,7 +1602,7 @@ class Battle {
|
|||
this.message('<small>' + pokemon.getName() + (move.name ? ' can\'t use ' + move.name + '' : ' can\'t move') + '!</small>');
|
||||
break;
|
||||
}
|
||||
pokemon.sprite.animReset();
|
||||
this.scene.animReset(pokemon);
|
||||
}
|
||||
runMinor(args?: string[], kwargs?: {[k: string]: string}, preempt?: boolean, nextArgs?: string[], nextKwargs?: {[k: string]: string}) {
|
||||
let actions = '';
|
||||
|
|
@ -2347,7 +2340,7 @@ class Battle {
|
|||
} case '-mustrecharge': {
|
||||
let poke = this.getPokemon(args[1])!;
|
||||
poke.addMovestatus('mustrecharge' as ID);
|
||||
poke.sprite.updateStatbar(poke);
|
||||
this.scene.updateStatbar(poke);
|
||||
break;
|
||||
|
||||
} case '-status': {
|
||||
|
|
@ -2403,7 +2396,7 @@ class Battle {
|
|||
actions += "" + poke.getName() + " was frozen solid!";
|
||||
break;
|
||||
default:
|
||||
poke.sprite.updateStatbar(poke);
|
||||
this.scene.updateStatbar(poke);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
@ -2510,7 +2503,7 @@ class Battle {
|
|||
let poke = this.getPokemon(args[1])!;
|
||||
for (const target of poke.side.pokemon) {
|
||||
target.status = '';
|
||||
if (target.sprite.$statbar) target.sprite.updateStatbar(target);
|
||||
this.scene.updateStatbarIfExists(target);
|
||||
}
|
||||
|
||||
this.scene.resultAnim(poke, 'Team Cured', 'good');
|
||||
|
|
@ -2855,7 +2848,7 @@ class Battle {
|
|||
for (const trackedMove of tpoke.moveTrack) {
|
||||
poke.markMove(trackedMove[0], 0);
|
||||
}
|
||||
poke.sprite.animTransform(poke);
|
||||
this.scene.animTransform(poke);
|
||||
this.scene.resultAnim(poke, 'Transformed', 'good');
|
||||
break;
|
||||
} case '-formechange': {
|
||||
|
|
@ -2899,7 +2892,7 @@ class Battle {
|
|||
}
|
||||
}
|
||||
poke.addVolatile('formechange' as ID, template.species); // the formechange volatile reminds us to revert the sprite change on switch-out
|
||||
poke.sprite.animTransform(poke, isCustomAnim);
|
||||
this.scene.animTransform(poke, isCustomAnim);
|
||||
break;
|
||||
} case '-mega': {
|
||||
let poke = this.getPokemon(args[1])!;
|
||||
|
|
@ -2942,7 +2935,7 @@ class Battle {
|
|||
poke.removeVolatile('typeadd' as ID);
|
||||
poke.addVolatile('typechange' as ID, types);
|
||||
if (kwargs.silent) {
|
||||
poke.sprite.updateStatbar(poke);
|
||||
this.scene.updateStatbar(poke);
|
||||
break;
|
||||
}
|
||||
if (fromeffect.id) {
|
||||
|
|
@ -3001,7 +2994,7 @@ class Battle {
|
|||
}
|
||||
break;
|
||||
case 'leechseed':
|
||||
poke.sprite.updateStatbar(poke);
|
||||
this.scene.updateStatbar(poke);
|
||||
actions += '' + poke.getName() + ' was seeded!';
|
||||
break;
|
||||
case 'healblock':
|
||||
|
|
@ -3143,7 +3136,7 @@ class Battle {
|
|||
actions += "" + poke.getName() + " fell straight down!";
|
||||
poke.removeVolatile('magnetrise' as ID);
|
||||
poke.removeVolatile('telekinesis' as ID);
|
||||
if (poke.lastMove === 'fly' || poke.lastMove === 'bounce') poke.sprite.animReset();
|
||||
if (poke.lastMove === 'fly' || poke.lastMove === 'bounce') this.scene.animReset(poke);
|
||||
break;
|
||||
case 'substitute':
|
||||
if (kwargs.damage) {
|
||||
|
|
@ -3200,7 +3193,7 @@ class Battle {
|
|||
}
|
||||
}
|
||||
poke.addVolatile(effect.id);
|
||||
poke.sprite.updateStatbar(poke);
|
||||
this.scene.updateStatbar(poke);
|
||||
break;
|
||||
} case '-end': {
|
||||
let poke = this.getPokemon(args[1])!;
|
||||
|
|
@ -3222,7 +3215,7 @@ class Battle {
|
|||
break;
|
||||
case 'skydrop':
|
||||
if (kwargs.interrupt) {
|
||||
poke.sprite.anim({time: 100});
|
||||
this.scene.anim(poke, {time: 100});
|
||||
}
|
||||
actions += "" + poke.getName() + " was freed from the Sky Drop!";
|
||||
break;
|
||||
|
|
@ -3333,7 +3326,7 @@ class Battle {
|
|||
}
|
||||
}
|
||||
}
|
||||
poke.sprite.updateStatbar(poke);
|
||||
this.scene.updateStatbar(poke);
|
||||
break;
|
||||
} case '-singleturn': {
|
||||
let poke = this.getPokemon(args[1])!;
|
||||
|
|
@ -3415,7 +3408,7 @@ class Battle {
|
|||
actions += '' + poke.getName() + ' started heating up its beak!';
|
||||
break;
|
||||
}
|
||||
poke.sprite.updateStatbar(poke);
|
||||
this.scene.updateStatbar(poke);
|
||||
break;
|
||||
} case '-singlemove': {
|
||||
let poke = this.getPokemon(args[1])!;
|
||||
|
|
@ -3562,7 +3555,7 @@ class Battle {
|
|||
target.removeTurnstatus('quickguard' as ID);
|
||||
target.removeTurnstatus('craftyshield' as ID);
|
||||
target.removeTurnstatus('matblock' as ID);
|
||||
target.sprite.updateStatbar(target);
|
||||
this.scene.updateStatbar(target);
|
||||
}
|
||||
break;
|
||||
case 'spite':
|
||||
|
|
@ -3575,7 +3568,7 @@ class Battle {
|
|||
actions += "" + poke.getName() + " couldn't stay airborne because of gravity!";
|
||||
poke.removeVolatile('magnetrise' as ID);
|
||||
poke.removeVolatile('telekinesis' as ID);
|
||||
poke.sprite.anim({time: 100});
|
||||
this.scene.anim(poke, {time: 100});
|
||||
break;
|
||||
case 'magnitude':
|
||||
actions += "Magnitude " + Tools.escapeHTML(args[3]) + "!";
|
||||
|
|
@ -4051,10 +4044,10 @@ class Battle {
|
|||
let move = Tools.getMove(args[2]);
|
||||
if (this.checkActive(poke)) return;
|
||||
let poke2 = this.getPokemon(args[3]);
|
||||
poke.sprite.beforeMove();
|
||||
this.scene.beforeMove(poke);
|
||||
kwargs.silent = '.';
|
||||
this.useMove(poke, move, poke2, kwargs);
|
||||
poke.sprite.afterMove();
|
||||
this.scene.afterMove(poke);
|
||||
break;
|
||||
|
||||
} case '-hint': {
|
||||
|
|
@ -4571,7 +4564,7 @@ class Battle {
|
|||
poke.details = args[2];
|
||||
poke.searchid = args[1].substr(0, 2) + args[1].substr(3) + '|' + args[2];
|
||||
|
||||
poke.sprite.animTransform(poke, true, true);
|
||||
this.scene.animTransform(poke, true, true);
|
||||
if (toId(newSpecies) === 'greninjaash') {
|
||||
this.message('' + poke.getName() + ' became Ash-Greninja!');
|
||||
} else if (toId(newSpecies) === 'mimikyubusted') {
|
||||
|
|
@ -4628,9 +4621,9 @@ class Battle {
|
|||
let move = Tools.getMove(args[2]);
|
||||
if (this.checkActive(poke)) return;
|
||||
let poke2 = this.getPokemon(args[3]);
|
||||
poke.sprite.beforeMove();
|
||||
this.scene.beforeMove(poke);
|
||||
this.useMove(poke, move, poke2, kwargs);
|
||||
poke.sprite.afterMove();
|
||||
this.scene.afterMove(poke);
|
||||
break;
|
||||
} case 'cant': {
|
||||
this.endLastTurn();
|
||||
|
|
@ -4677,11 +4670,10 @@ class Battle {
|
|||
break;
|
||||
} case 'fieldhtml': {
|
||||
this.playbackState = Playback.Seeking; // force seeking to prevent controls etc
|
||||
this.scene.$frame.html(Tools.sanitizeHTML(args[1]));
|
||||
this.scene.setFrameHTML(Tools.sanitizeHTML(args[1]));
|
||||
break;
|
||||
} case 'controlshtml': {
|
||||
let $controls = this.scene.$frame.parent().children('.battle-controls');
|
||||
$controls.html(Tools.sanitizeHTML(args[1]));
|
||||
this.scene.setControlsHTML(Tools.sanitizeHTML(args[1]));
|
||||
break;
|
||||
} default: {
|
||||
this.scene.log('<div class="chat message-error">Unknown command: ' + Tools.escapeHTML(args[0]) + '</div>');
|
||||
|
|
|
|||
94
test/battle-test.mocha.js
Normal file
94
test/battle-test.mocha.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
const fs = require('fs');
|
||||
const assert = require('assert').strict;
|
||||
|
||||
window = global;
|
||||
|
||||
// Without making these modules, the best we can do is directly include them into this workspace.
|
||||
eval('' + fs.readFileSync(`${__dirname}/../js/battle-scene-stub.js`));
|
||||
eval('' + fs.readFileSync(`${__dirname}/../js/battle-dex.js`));
|
||||
eval('' + fs.readFileSync(`${__dirname}/../js/battle-dex-data.js`));
|
||||
eval('' + fs.readFileSync(`${__dirname}/../js/battle.js`));
|
||||
|
||||
describe('Battle', function () {
|
||||
|
||||
it('should instantiate without issue', function () {
|
||||
var battle = new Battle();
|
||||
});
|
||||
|
||||
it('should process a bunch of messages properly', function () {
|
||||
var battle = new Battle();
|
||||
battle.debug = true;
|
||||
|
||||
battle.setQueue([
|
||||
"|init|battle",
|
||||
"|title|FOO vs. BAR",
|
||||
"|j|FOO",
|
||||
"|j|BAR",
|
||||
"|request|",
|
||||
"|player|p1|FOO|169",
|
||||
"|player|p2|BAR|265",
|
||||
"|teamsize|p1|6",
|
||||
"|teamsize|p2|6",
|
||||
"|gametype|singles",
|
||||
"|gen|7",
|
||||
"|tier|[Gen 7] Random Battle",
|
||||
"|rated|",
|
||||
"|seed|",
|
||||
"|rule|Sleep Clause Mod: Limit one foe put to sleep",
|
||||
"|rule|HP Percentage Mod: HP is shown in percentages",
|
||||
"|",
|
||||
"|start",
|
||||
"|switch|p1a: Leafeon|Leafeon, L83, F|100/100",
|
||||
"|switch|p2a: Gliscor|Gliscor, L77, F|242/242",
|
||||
"|turn|1",
|
||||
]);
|
||||
battle.fastForwardTo(-1);
|
||||
|
||||
var p1 = battle.sides[0];
|
||||
var p2 = battle.sides[1];
|
||||
|
||||
assert(p1.name === 'FOO');
|
||||
var p1leafeon = p1.pokemon[0];
|
||||
assert(p1leafeon.ident === 'p1: Leafeon');
|
||||
assert(p1leafeon.details === 'Leafeon, L83, F');
|
||||
assert(p1leafeon.hp === 100);
|
||||
assert(p1leafeon.maxhp === 100);
|
||||
assert(p1leafeon.isActive());
|
||||
assert.deepEqual(p1leafeon.moveTrack, []);
|
||||
|
||||
assert(p2.name === 'BAR');
|
||||
var p2gliscor = p2.pokemon[0];
|
||||
assert(p2gliscor.ident === 'p2: Gliscor');
|
||||
assert(p2gliscor.details === 'Gliscor, L77, F');
|
||||
assert(p2gliscor.hp === 242);
|
||||
assert(p2gliscor.maxhp === 242);
|
||||
assert(p2gliscor.isActive());
|
||||
assert.deepEqual(p2gliscor.moveTrack, []);
|
||||
|
||||
[
|
||||
"|",
|
||||
"|switch|p2a: Kyurem|Kyurem-White, L73|303/303",
|
||||
"|-ability|p2a: Kyurem|Turboblaze",
|
||||
"|move|p1a: Leafeon|Knock Off|p2a: Kyurem",
|
||||
"|-damage|p2a: Kyurem|226/303",
|
||||
"|-enditem|p2a: Kyurem|Leftovers|[from] move: Knock Off|[of] p1a: Leafeon",
|
||||
"|",
|
||||
"|upkeep",
|
||||
"|turn|2",
|
||||
"|inactive|Time left: 150 sec this turn | 740 sec total",
|
||||
].forEach(msg => battle.add(msg));
|
||||
battle.fastForwardTo(-1);
|
||||
|
||||
assert(!p2gliscor.isActive());
|
||||
var p2kyurem = p2.pokemon[1];
|
||||
assert(p2kyurem.ident === 'p2: Kyurem');
|
||||
assert(p2kyurem.details === 'Kyurem-White, L73');
|
||||
assert(p2kyurem.hp === 226);
|
||||
assert(p2kyurem.maxhp === 303);
|
||||
assert(p2kyurem.isActive());
|
||||
assert(p2kyurem.item === '');
|
||||
assert(p2kyurem.prevItem === 'Leftovers');
|
||||
|
||||
assert.deepEqual(p1leafeon.moveTrack, [['Knock Off', 1]]);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user