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 &&