diff --git a/play.pokemonshowdown.com/src/battle-animations.ts b/play.pokemonshowdown.com/src/battle-animations.ts index c7c416e47..9ed8a19a2 100644 --- a/play.pokemonshowdown.com/src/battle-animations.ts +++ b/play.pokemonshowdown.com/src/battle-animations.ts @@ -740,9 +740,10 @@ export class BattleScene implements BattleSceneStub { } badgehtml += ''; } + const avatar = Dex.resolveAvatar(side.avatar); return ( `
${BattleLog.escapeHTML(side.name)}` + - `
` + + `
` + `
${badgehtml}${pokemonhtml}
` ); } diff --git a/play.pokemonshowdown.com/src/battle-choices.ts b/play.pokemonshowdown.com/src/battle-choices.ts index 713c27e15..bfd3b251c 100644 --- a/play.pokemonshowdown.com/src/battle-choices.ts +++ b/play.pokemonshowdown.com/src/battle-choices.ts @@ -224,14 +224,15 @@ export class BattleChoiceBuilder { /** only the last choice can be uncancelable */ const isLastChoice = this.choices.length + 1 >= this.requestLength(); if (choice.choiceType === 'move') { - if (!choice.targetLoc && (this.request as BattleMoveRequest).targetable) { + const targetable = (this.request as BattleMoveRequest).targetable; + if (!choice.targetLoc && targetable) { const choosableTargets: unknown[] = ['normal', 'any', 'adjacentAlly', 'adjacentAllyOrSelf', 'adjacentFoe']; if (choosableTargets.includes(this.currentMove(choice)?.target)) { this.current = choice; return null; } } - if (this.currentMoveRequest()?.maybeDisabled && isLastChoice && this.requestLength() === 1) { + if (this.currentMoveRequest()?.maybeDisabled && isLastChoice && !targetable) { this.noCancel = true; } if (choice.mega || choice.megax || choice.megay) this.alreadyMega = true; diff --git a/play.pokemonshowdown.com/src/battle-dex.ts b/play.pokemonshowdown.com/src/battle-dex.ts index 75c9da463..560002986 100644 --- a/play.pokemonshowdown.com/src/battle-dex.ts +++ b/play.pokemonshowdown.com/src/battle-dex.ts @@ -296,6 +296,9 @@ export const Dex = new class implements ModdedDex { return Dex.resourcePrefix + 'sprites/trainers-custom/' + toID(avatar.substr(1)) + '.png'; } if (avatar.includes('.')) { + if (!window.Config?.server) { + return Dex.resourcePrefix + 'sprites/trainers/unknown.png'; + } // previously checked `&& window.Config?.server?.registered` // currently doesn't, bc server registration isn't a thing anymore // custom avatar served by the server diff --git a/play.pokemonshowdown.com/src/battle-team-editor.tsx b/play.pokemonshowdown.com/src/battle-team-editor.tsx index 7c22c5c39..d138bd344 100644 --- a/play.pokemonshowdown.com/src/battle-team-editor.tsx +++ b/play.pokemonshowdown.com/src/battle-team-editor.tsx @@ -2094,7 +2094,7 @@ class TeamEditorForm extends preact.Component<{ const cur = (i: number) => setIndex === i ? ' cur' : ''; const isSearchMode = type !== 'stats' && type !== 'details' && type !== 'import'; const SEARCH_PLACEHOLDERS = { - 'pokemon': 'Search species or filter by type, learnable moves, ability, or egg group', + 'pokemon': 'Search species or filter by type, learnable moves, ability, tier, or egg group', 'ability': 'Search abilities', 'item': 'Search items', 'move': 'Search moves or filter by type or category', @@ -2708,6 +2708,7 @@ class TeamEditorForm extends preact.Component<{ this.resetScroll(); this.forceUpdate(); } else if (!type) { + this.focusFocusedSetField(); const searchBox = this.base!.querySelector('input[name=value]'); if (searchBox) { searchBox.value = ''; diff --git a/play.pokemonshowdown.com/src/battle-tooltips.ts b/play.pokemonshowdown.com/src/battle-tooltips.ts index 89552c350..2ec8ddb89 100644 --- a/play.pokemonshowdown.com/src/battle-tooltips.ts +++ b/play.pokemonshowdown.com/src/battle-tooltips.ts @@ -163,6 +163,7 @@ export class BattleTooltips { static parentElem: HTMLElement | null = null; static isLocked = false; static isPressed = false; + static outsideClickListenerAdded = false; static allowsTouchScroll(elem: HTMLElement | null) { return elem && (elem.tagName === 'DIV' || elem.tagName === 'SPAN'); } @@ -185,6 +186,14 @@ export class BattleTooltips { $('#tooltipwrapper').removeClass('tooltip-locking-click tooltip-locking-tap'); } + static dismissOnOutsideClick(e: MouseEvent) { + if (!BattleTooltips.elem) return; + const target = e.target; + if (target && BattleTooltips.elem.contains(target as Node)) return; + if (target && (target as Element).closest?.('.has-tooltip')) return; + BattleTooltips.hideTooltip(); + } + lockTooltip() { if (BattleTooltips.elem && !BattleTooltips.isLocked) { BattleTooltips.isLocked = true; @@ -204,6 +213,10 @@ export class BattleTooltips { } listen(elem: HTMLElement | JQuery) { + if (!BattleTooltips.outsideClickListenerAdded) { + window.addEventListener('click', BattleTooltips.dismissOnOutsideClick, true); + BattleTooltips.outsideClickListenerAdded = true; + } const $elem = $(elem); $elem.on('mouseover.battleTooltips', '.has-tooltip', this.mouseOverEvent); $elem.on('click.battleTooltips', '.has-tooltip', this.clickTooltipEvent); diff --git a/play.pokemonshowdown.com/src/miniedit.ts b/play.pokemonshowdown.com/src/miniedit.ts index 12288554a..84e6e4f20 100644 --- a/play.pokemonshowdown.com/src/miniedit.ts +++ b/play.pokemonshowdown.com/src/miniedit.ts @@ -203,6 +203,7 @@ export class MiniEditPastePlugin { .replace(/<(script|style)\b[\s\S]*?<\/\1>/gi, '') // handle newlines // .replace(/\n/g, '
') // in case they're in
?
+			.replace(/\n/g, '') // Firefox bug: just adds random newlines for fun apparently???
 			.replace(new RegExp(`]*>`, 'gi'), '\n')
 			.replace(/\n{2,}/g, '\n')
 			.replace(/]*>\n?/gi, '\n')
diff --git a/play.pokemonshowdown.com/src/panel-battle.tsx b/play.pokemonshowdown.com/src/panel-battle.tsx
index bcf3eac56..86aeb6cae 100644
--- a/play.pokemonshowdown.com/src/panel-battle.tsx
+++ b/play.pokemonshowdown.com/src/panel-battle.tsx
@@ -157,14 +157,15 @@ export class BattleRoom extends ChatRoom {
 	overlayActive: 'move' | 'switch' | null = null;
 
 	override interruptClose(explicit?: boolean, elem?: HTMLElement | null) {
-		const battle = this.battle;
-		const activeBattle = battle && !battle.ended && this.request && this.connectMode !== 'deleted';
-		if (activeBattle || this.requireForfeit) {
+		if (this.isPlaying() || this.requireForfeit) {
 			PS.join('forfeitbattle' as RoomID, { parentElem: elem, parentRoomid: this.id });
 			return `You are still in ${this.title}`;
 		}
 		return super.interruptClose(explicit, elem);
 	}
+	isPlaying() {
+		return this.battle && !this.battle.ended && this.request && this.connectMode !== 'deleted';
+	}
 
 	override handleReconnect(): boolean | void {
 		if (this.battle) {
@@ -1172,9 +1173,7 @@ class BattlePanel extends PSRoomPanel {
 			return 
{this.renderOldChoices(request, choices)} -
-
- {choices.noCancel || room.battle.hardcoreMode ? + Waiting for opponent... {choices.noCancel || room.battle.hardcoreMode ? null : }
{this.renderTeamList()} diff --git a/play.pokemonshowdown.com/src/panel-chat.tsx b/play.pokemonshowdown.com/src/panel-chat.tsx index 348257810..e9a736a14 100644 --- a/play.pokemonshowdown.com/src/panel-chat.tsx +++ b/play.pokemonshowdown.com/src/panel-chat.tsx @@ -281,12 +281,15 @@ export class ChatRoom extends PSRoom { this.highlightRegExp[i] = new RegExp('(?:\\b|(?!\\w))(?:' + highlights[i].join('|') + ')(?:\\b|(?!\\w))', 'i'); } } - static isHighlightableChatMessage(message: string) { + static isHighlightableChatMessage(message: string, isDM = false) { if (!message.startsWith('/')) return true; const [cmd] = PSUtils.splitFirst(message.slice(1), ' '); if (['raw', 'nonotify', 'text', 'error'].includes(cmd)) { return false; } + if (['uhtml', 'uhtmlchange'].includes(cmd)) { + return isDM; + } if (cmd === 'subtlenotify') { return 'subtle'; } @@ -332,7 +335,8 @@ export class ChatRoom extends PSRoom { if (!message) return false; if (userid === PS.user.userid) return false; - const highlightType = ChatRoom.isHighlightableChatMessage(message); + const isDM = this.id.startsWith("dm-"); + const highlightType = ChatRoom.isHighlightableChatMessage(message, isDM); const isIgnored = PS.prefs.ignore?.[userid]; if (isIgnored || !highlightType) return false; if (highlightType === 'subtle') { @@ -340,7 +344,7 @@ export class ChatRoom extends PSRoom { return false; } - if (this.id.startsWith("dm-")) { + if (isDM) { this.notify({ title: `${this.title}`, body: this.getChatNotificationBody(message), @@ -1630,8 +1634,8 @@ export class PSTextarea extends preact.Component<{ const textboxTest = this.base!.querySelector('textarea.heighttester')!; textboxTest.style.width = `${textbox.offsetWidth}px`; textboxTest.value = textbox.value; - const newHeight = Math.max(textboxTest.scrollHeight + 40, 50); - textbox.style.height = `${newHeight}px`; + // +2 for the borders + textbox.style.height = `${textboxTest.scrollHeight + 2}px`; }; handleInput = (e: Event) => { if (this.props.singleLine) { @@ -1673,7 +1677,7 @@ export class PSTextarea extends preact.Component<{ /> {!this.cssAutosize &&