diff --git a/play.pokemonshowdown.com/src/battle.ts b/play.pokemonshowdown.com/src/battle.ts index 2b5ac4af0..9625c251a 100644 --- a/play.pokemonshowdown.com/src/battle.ts +++ b/play.pokemonshowdown.com/src/battle.ts @@ -1175,6 +1175,9 @@ export class Battle { * paused, not just waiting for the opponent to make a move. */ paused: boolean; + realtime = false; + timeWait: number | null = null; + timerCount = 0; constructor(options: { $frame?: JQuery, @@ -3481,6 +3484,32 @@ export class Battle { } runMajor(args: Args, kwArgs: KWArgs, preempt?: boolean) { switch (args[0]) { + case 't:': { + if (this.realtime && this.timerCount && !this.seeking) { + if (this.timeWait) { + setTimeout(() => { + this.timeWait = null; + this.nextStep(); + }, this.timeWait); + return true; + } + const curStep = this.stepQueue.indexOf(`|t:|${args[1]}`); + const curTime = Number(args[1]); + + const extractTime = (line: string) => Number(line.split('|')[2]); + const nextStep = this.stepQueue.findIndex((line, index) => { + if (index < curStep) return false; + const parts = line.split('|'); + return parts[1] === 't:' && extractTime(line) > curTime; + }); + if (nextStep > -1) { + const nextTime = extractTime(this.stepQueue[nextStep]); + this.timeWait = (nextTime - curTime) * 1000; + } + } + this.timerCount++; + break; + } case 'start': { this.nearSide.active[0] = null; this.farSide.active[0] = null; @@ -3885,14 +3914,18 @@ export class Battle { if (args[0].startsWith('-') || args[0] === 'detailschange') { this.runMinor(args, kwArgs, nextArgs, nextKwargs); } else { - this.runMajor(args, kwArgs, preempt); + if (this.runMajor(args, kwArgs, preempt)) { + return true; + } } } else { try { if (args[0].startsWith('-') || args[0] === 'detailschange') { this.runMinor(args, kwArgs, nextArgs, nextKwargs); } else { - this.runMajor(args, kwArgs, preempt); + if (this.runMajor(args, kwArgs, preempt)) { + return true; + } } } catch (err: any) { this.log(['majorerror', 'Error parsing: ' + str + ' (' + err + ')']); @@ -4015,7 +4048,9 @@ export class Battle { return; } - this.run(this.stepQueue[this.currentStep]); + if (this.run(this.stepQueue[this.currentStep])) { + break; + } this.currentStep++; if (this.waitForAnimations === true) { animations = this.scene.finishAnimations(); diff --git a/replay.pokemonshowdown.com/src/replays-battle.tsx b/replay.pokemonshowdown.com/src/replays-battle.tsx index 7a020bef6..5160d2fc7 100644 --- a/replay.pokemonshowdown.com/src/replays-battle.tsx +++ b/replay.pokemonshowdown.com/src/replays-battle.tsx @@ -297,6 +297,8 @@ export class BattlePanel extends preact.Component<{ id: string }> { return 'slow'; } else if (this.battle.messageFadeTime >= 1000) { return 'reallyslow'; + } else if (this.battle.realtime) { + return 'realtime'; } return 'normal'; } @@ -317,6 +319,12 @@ export class BattlePanel extends preact.Component<{ id: string }> { reallyslow: 3000, }; if (!this.battle) return; + if (speed === 'realtime') { + this.battle.realtime = true; + this.battle.resetToCurrentTurn(); + return; + } + this.battle.realtime = false; this.battle.messageShownTime = delayTable[speed as 'fast']; this.battle.messageFadeTime = fadeTable[speed as 'fast']; this.battle.scene.updateAcceleration(); @@ -324,7 +332,7 @@ export class BattlePanel extends preact.Component<{ id: string }> { stepSpeed(delta: number) { const target = this.base?.querySelector('select[name=speed]'); if (!target) return; // should never happen - const values = ['reallyslow', 'slow', 'normal', 'fast', 'hyperfast']; + const values = ['realtime', 'reallyslow', 'slow', 'normal', 'fast', 'hyperfast']; const newValue = values[values.indexOf(target.value) + delta]; if (newValue) { target.value = newValue; @@ -472,6 +480,7 @@ export class BattlePanel extends preact.Component<{ id: string }> { + {}