diff --git a/play.pokemonshowdown.com/src/client-main.ts b/play.pokemonshowdown.com/src/client-main.ts index ced0867a9..633a390e8 100644 --- a/play.pokemonshowdown.com/src/client-main.ts +++ b/play.pokemonshowdown.com/src/client-main.ts @@ -120,6 +120,7 @@ class PSPrefs extends PSStreamModel { ignoreopp: boolean | null = null; autotimer: boolean | null = null; autohardcore: boolean | null = null; + spectatefromstart: boolean | null = null; rightpanelbattles: boolean | null = null; disallowspectators: boolean | null = null; starredformats: { [formatid: string]: true | undefined } | null = null; diff --git a/play.pokemonshowdown.com/src/panel-battle.tsx b/play.pokemonshowdown.com/src/panel-battle.tsx index 3f0f3a2ac..3e72c0676 100644 --- a/play.pokemonshowdown.com/src/panel-battle.tsx +++ b/play.pokemonshowdown.com/src/panel-battle.tsx @@ -147,6 +147,8 @@ export class BattleRoom extends ChatRoom { request: BattleRequest | null = null; choices: BattleChoiceBuilder | null = null; autoTimerActivated: boolean | null = null; + /** null = initializing */ + rejoining: boolean | null = null; loadReplay() { const replayid = this.id.slice(7); @@ -360,7 +362,7 @@ class BattlePanel extends PSRoomPanel { scene.tooltips.listen($elem.find('.battle-controls-container')); scene.tooltips.listen(scene.log.elem); super.componentDidMount(); - battle.seekTurn(Infinity); + this.fastForwardIfRejoining(); if (PS.prefs.autohardcore) { battle.setHardcoreMode(true); } @@ -380,11 +382,18 @@ class BattlePanel extends PSRoomPanel { this.battleHeight = 360; } } + fastForwardIfRejoining() { + const room = this.props.room; + if (!room.rejoining || !room.side) return; + room.rejoining = false; + if (!PS.prefs.spectatefromstart) room.battle.seekTurn(Infinity); + } override receiveLine(args: Args) { const room = this.props.room; switch (args[0]) { case 'initdone': - room.battle.seekTurn(Infinity); + room.rejoining ||= false; + this.fastForwardIfRejoining(); return; case 'request': this.receiveRequest(args[1] ? JSON.parse(args[1]) : null); @@ -395,6 +404,9 @@ class BattlePanel extends PSRoomPanel { case 'c': case 'c:': case 'chat': case 'chatmsg': case 'inactive': room.battle.instantAdd('|' + args.join('|')); return; + case 'start': + room.rejoining ??= true; + break; case 'error': if (args[1].startsWith('[Invalid choice]') && room.request) { room.choices = new BattleChoiceBuilder(room.request); @@ -421,9 +433,11 @@ class BattlePanel extends PSRoomPanel { BattleChoiceBuilder.fixRequest(request, room.battle); if (request.side) { + const wasPlayer = !!room.side; room.battle.myPokemon = request.side.pokemon; room.battle.setViewpoint(request.side.id); room.side = request.side; + if (!wasPlayer) this.fastForwardIfRejoining(); } if (request.ally) { room.battle.myAllyPokemon = request.ally.pokemon; diff --git a/play.pokemonshowdown.com/src/panel-popups.tsx b/play.pokemonshowdown.com/src/panel-popups.tsx index 67eab3d8a..74e14b836 100644 --- a/play.pokemonshowdown.com/src/panel-popups.tsx +++ b/play.pokemonshowdown.com/src/panel-popups.tsx @@ -1571,6 +1571,10 @@ class BattleOptionsPanel extends PSRoomPanel { } break; } + case 'spectatefromstart': { + PS.prefs.set('spectatefromstart', value); + break; + } case 'ignoreopp': { PS.prefs.set('ignoreopp', value); this.handleIgnoreOpponent(value); @@ -1692,6 +1696,14 @@ class BattleOptionsPanel extends PSRoomPanel { /> Automatically enable hardcore mode

+

+ +

{!PS.prefs.onepanel && document.body.offsetWidth >= 800 &&