From a35a62cfce755a58c29caca5e8a53c2ef7423453 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Wed, 14 May 2025 14:28:36 +0000 Subject: [PATCH] Preact minor updates batch 20 Battles - Fix render issues on mobile layout - Fix join/leave batching - Support disabled buttons Teambuilder - Redesign upload UI to be clearer - Support scrolling tab bar for Boxes in focus editor - Don't overwrite local team with remote team - Improve Defensive Coverage ability support - Support importing from pokepaste (fixes #2422) --- play.pokemonshowdown.com/src/battle-log.ts | 2 +- .../src/battle-team-editor.tsx | 56 ++++- play.pokemonshowdown.com/src/client-main.ts | 24 +-- play.pokemonshowdown.com/src/panel-battle.tsx | 192 ++++++++++-------- play.pokemonshowdown.com/src/panel-chat.tsx | 6 +- .../src/panel-teambuilder-team.tsx | 70 ++++--- .../src/panel-teambuilder.tsx | 4 +- play.pokemonshowdown.com/style/client2.css | 4 +- .../style/teambuilder.css | 8 +- 9 files changed, 223 insertions(+), 143 deletions(-) diff --git a/play.pokemonshowdown.com/src/battle-log.ts b/play.pokemonshowdown.com/src/battle-log.ts index 0f9787665..b8c9899f3 100644 --- a/play.pokemonshowdown.com/src/battle-log.ts +++ b/play.pokemonshowdown.com/src/battle-log.ts @@ -141,7 +141,7 @@ export class BattleLog { let divClass = 'chat'; let divHTML = ''; let noNotify: boolean | undefined; - if (!['join', 'j', 'leave', 'l'].includes(args[0])) this.joinLeave = null; + if (!['join', 'j', 'leave', 'l', 'turn'].includes(args[0])) this.joinLeave = null; if (!['name', 'n'].includes(args[0])) this.lastRename = null; switch (args[0]) { case 'chat': case 'c': case 'c:': diff --git a/play.pokemonshowdown.com/src/battle-team-editor.tsx b/play.pokemonshowdown.com/src/battle-team-editor.tsx index 6d851624b..2a5e76cd1 100644 --- a/play.pokemonshowdown.com/src/battle-team-editor.tsx +++ b/play.pokemonshowdown.com/src/battle-team-editor.tsx @@ -15,6 +15,7 @@ import { PSSearchResults } from "./battle-searchresults"; import { BattleNatures, BattleStatNames, type StatName } from "./battle-dex-data"; import { BattleStatGuesser, BattleStatOptimizer } from "./battle-tooltips"; import { PSModel } from "./client-core"; +import { Net } from "./client-connection"; type SelectionType = 'pokemon' | 'ability' | 'item' | 'move' | 'stats' | 'details'; @@ -49,6 +50,10 @@ class TeamEditorState extends PSModel { this.setFormat(team.format); window.search = this.search; } + setReadonly(readonly: boolean) { + if (!readonly && this.readonly) this.sets = PSTeambuilder.unpackTeam(this.team.packedTeam); + this.readonly = readonly; + } setFormat(format: string) { const team = this.team; const formatid = toID(format); @@ -499,6 +504,9 @@ class TeamEditorState extends PSModel { if (attackType === 'Ground' && abilityid === 'eartheater') return 0; if (attackType === 'Fire' && abilityid === 'wellbakedbody') return 0; + if (attackType === 'Fire' && abilityid === 'primordialsea') return 0; + if (attackType === 'Water' && abilityid === 'desolateland') return 0; + if (abilityid === 'wonderguard') { for (const type of types) { if (this.getTypeWeakness(type, attackType) <= 1) return 0; @@ -506,6 +514,14 @@ class TeamEditorState extends PSModel { } let factor = 1; + if ((attackType === 'Fire' || attackType === 'Ice') && abilityid === 'thickfat') factor *= 0.5; + if (attackType === 'Fire' && abilityid === 'waterbubble') factor *= 0.5; + if (attackType === 'Fire' && abilityid === 'heatproof') factor *= 0.5; + if (attackType === 'Ghost' && abilityid === 'purifyingsalt') factor *= 0.5; + if (attackType === 'Fire' && abilityid === 'fluffy') factor *= 2; + if ((attackType === 'Electric' || attackType === 'Rock' || attackType === 'Ice') && abilityid === 'deltastream') { + factor *= 0.5; + } for (const type of types) { factor *= this.getTypeWeakness(type, attackType); } @@ -650,7 +666,7 @@ export class TeamEditor extends preact.Component<{ } override render() { this.editor ||= new TeamEditorState(this.props.team); - this.editor.readonly = !!this.props.readonly; + this.editor.setReadonly(!!this.props.readonly); this.editor.narrow = this.props.narrow ?? document.body.offsetWidth < 500; if (this.props.team.format !== this.editor.format) { this.editor.setFormat(this.props.team.format); @@ -714,7 +730,10 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?: this.heightTester.value = fullLine && !newValue.endsWith('\n') ? newValue + '\n' : newValue; return this.heightTester.scrollHeight; } - input = () => this.updateText(); + input = () => { + this.updateText(); + this.save(); + }; keyUp = () => this.updateText(true); contextMenu = (ev: MouseEvent) => { if (!ev.shiftKey) { @@ -786,13 +805,15 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?: if (ev.keyCode === 13 && ev.shiftKey) return; if (ev.altKey || ev.metaKey) return; if (!this.innerFocus) { - if ( + if (this.maybeReplaceLine()) { + // do nothing else + } else if ( this.textbox.selectionStart === this.textbox.value.length && (this.textbox.value.endsWith('\n\n') || !this.textbox.value) ) { this.addPokemon(); - } else { - this.openInnerFocus(); + } else if (!this.openInnerFocus()) { + break; } ev.stopImmediatePropagation(); ev.preventDefault(); @@ -820,6 +841,24 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?: } } }; + maybeReplaceLine = () => { + if (this.textbox.selectionStart !== this.textbox.selectionEnd) return; + const current = this.textbox.selectionEnd; + const lineStart = this.textbox.value.lastIndexOf('\n', current) + 1; + const value = this.textbox.value.slice(lineStart, current); + + const pokepaste = /^https?:\/\/pokepast.es\/([a-z0-9]+)(?:\/.*)?$/.exec(value)?.[1]; + if (pokepaste) { + Net(`https://pokepast.es/${pokepaste}/json`).get().then(json => { + const paste = JSON.parse(json); + // make sure it's still there: + const valueIndex = this.textbox.value.indexOf(value); + this.replace(paste.paste.replace(/\r\n/g, '\n'), valueIndex, valueIndex + value.length); + }); + return true; + } + return false; + }; getInnerFocusValue() { if (!this.innerFocus) return ''; return this.textbox.value.slice(this.innerFocus.range[0], this.innerFocus.range[1]); @@ -840,6 +879,7 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?: this.clearInnerFocus(); if (this.setDirty) { this.updateText(); + this.save(); } else { this.forceUpdate(); } @@ -986,7 +1026,6 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?: } textbox.style.height = `${bottomY + 100}px`; - this.save(); } this.forceUpdate(); }; @@ -1180,6 +1219,7 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?: // for future updates if (!this.setInfo[index]) { this.updateText(); + this.save(); } else { if (this.setInfo[index + 1]) { this.setInfo[index + 1].index = start + newText.length; @@ -1352,7 +1392,7 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?: