From 48172bbc954f5d632caadfc0c0e18c686c0a1b0b Mon Sep 17 00:00:00 2001 From: Sterling Archer Date: Wed, 27 May 2026 15:16:09 -0700 Subject: [PATCH] Add party select screen before battles with fainted Pokemon tracking, zZz overlay, and starter protection --- game.js | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++- index.html | 6 +++ style.css | 92 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 1 deletion(-) diff --git a/game.js b/game.js index a6430c9..68fff3a 100644 --- a/game.js +++ b/game.js @@ -375,6 +375,7 @@ class Game { if (s === 'map') { this._talkToNPC(); return; } if (s === 'battle' && !this.state.answering) { this._confirmCursor(); return; } if (s === 'intro') { this.advanceIntro(); return; } + if (s === 'party-select') { this._partySelectConfirm(); return; } if (s === 'starter') { this.pickStarter(this._starterCursor||0); return; } if (s === 'starter-confirm') { document.getElementById('btn-starter-continue')?.click(); return; } if (s === 'catch') { const btns = document.querySelectorAll('.catch-action-btn'); if (btns[0]) btns[0].click(); return; } @@ -398,6 +399,13 @@ class Game { _dpadStart(dir) { const s = this.state.screen; + if (s === 'party-select' && (dir === 'up' || dir === 'down')) { + const max = (this._partySelectOrdered || []).length - 1; + this._partySelectCursor = dir === 'up' ? Math.max(0, this._partySelectCursor - 1) : Math.min(max, this._partySelectCursor + 1); + this._renderPartySelectCursor(); + SFX.select(); + return; + } if (s === 'continue' && (dir === 'up' || dir === 'down')) { this._contCursor = this._contCursor === 0 ? 1 : 0; this._renderContCursor(); @@ -660,7 +668,83 @@ class Game { const bub = document.getElementById('player-bubble'); if (bub) bub.classList.remove('visible'); SFX.encounter(); - this.startQuestion(); + const conscious = this.state.party.filter(p => !p.fainted); + if (conscious.length > 1) { + this.showPartySelect(); + } else { + this.state.activePokemon = conscious[0] || this.state.party[0]; + this.startQuestion(); + } + } + + showPartySelect() { + this.show('party-select'); + this._showController(true); + this._partySelectCursor = 0; + + const list = document.getElementById('party-select-list'); + list.innerHTML = ''; + + const ordered = [...this.state.party].reverse(); + + ordered.forEach((poke, i) => { + const entry = document.createElement('div'); + entry.className = 'party-select-entry' + (poke.fainted ? ' fainted' : ''); + if (i === 0 && !poke.fainted) entry.classList.add('party-selected'); + + const emojiWrap = document.createElement('div'); + emojiWrap.className = 'party-select-emoji'; + emojiWrap.textContent = poke.emoji; + if (poke.fainted) { + const zzz = document.createElement('span'); + zzz.className = 'party-select-zzz'; + zzz.textContent = 'zZz'; + emojiWrap.appendChild(zzz); + } + + const info = document.createElement('div'); + info.className = 'party-select-info'; + info.innerHTML = `
${poke.name.toUpperCase()}
${poke.type} · Lv ${poke.level}
`; + + const badge = document.createElement('div'); + badge.className = 'party-select-badge' + (poke.fainted ? ' badge-fainted' : ''); + badge.textContent = poke.fainted ? 'FAINTED' : 'READY'; + + entry.appendChild(emojiWrap); + entry.appendChild(info); + entry.appendChild(badge); + + if (!poke.fainted) { + entry.addEventListener('click', () => { + this.state.activePokemon = poke; + SFX.confirm(); + this.startQuestion(); + }); + } + + list.appendChild(entry); + }); + + this._partySelectOrdered = ordered; + this._renderPartySelectCursor(); + } + + _renderPartySelectCursor() { + const entries = document.querySelectorAll('.party-select-entry'); + entries.forEach((e, i) => { + e.classList.toggle('party-selected', i === this._partySelectCursor && !e.classList.contains('fainted')); + }); + } + + _partySelectConfirm() { + const ordered = this._partySelectOrdered; + if (!ordered) return; + const poke = ordered[this._partySelectCursor]; + if (poke && !poke.fainted) { + this.state.activePokemon = poke; + SFX.confirm(); + this.startQuestion(); + } } _placeMapSprites() { @@ -729,6 +813,26 @@ class Game { } if (s === 'title' && (e.key==='Enter'||e.key===' ')) { document.getElementById('screen-title').click(); return; } + if (s === 'party-select') { + if (e.key==='ArrowUp'||e.key==='w'||e.key==='W') { + this._partySelectCursor = Math.max(0, this._partySelectCursor - 1); + this._renderPartySelectCursor(); + SFX.select(); + return; + } + if (e.key==='ArrowDown'||e.key==='s'||e.key==='S') { + const max = (this._partySelectOrdered || []).length - 1; + this._partySelectCursor = Math.min(max, this._partySelectCursor + 1); + this._renderPartySelectCursor(); + SFX.select(); + return; + } + if (e.key==='Enter'||e.key===' '||e.key==='e'||e.key==='E') { + this._partySelectConfirm(); + return; + } + return; + } if (s === 'continue') { if (e.key==='ArrowUp'||e.key==='w'||e.key==='W'||e.key==='ArrowDown'||e.key==='s'||e.key==='S') { this._contCursor = this._contCursor === 0 ? 1 : 0; @@ -953,6 +1057,12 @@ class Game { if (!this._walkLoop) this._startWalkLoop(); } if (s === 'battle') this._moveCursor(dir); + if (s === 'party-select' && (dir === 'up' || dir === 'down')) { + const max = (this._partySelectOrdered || []).length - 1; + this._partySelectCursor = dir === 'up' ? Math.max(0, this._partySelectCursor - 1) : Math.min(max, this._partySelectCursor + 1); + this._renderPartySelectCursor(); + SFX.select(); + } if (s === 'starter' && (dir === 'left' || dir === 'right')) { this._starterCursor = dir === 'left' ? Math.max(0, (this._starterCursor||0) - 1) : Math.min(2, (this._starterCursor||0) + 1); this._renderStarterCursor(); @@ -980,6 +1090,7 @@ class Game { else if (s === 'levelup') { document.getElementById('btn-lu-cont')?.click(); } else if (s === 'complete') { document.getElementById('btn-play-again')?.click(); } else if (s === 'starter-confirm') { document.getElementById('btn-starter-continue')?.click(); } + else if (s === 'party-select') { this._partySelectConfirm(); } else if (s === 'starter') { this.pickStarter(this._starterCursor||0); } else if (s === 'catch') { const cbtns = document.querySelectorAll('.catch-action-btn'); if (cbtns[this._catchCursor||0]) cbtns[this._catchCursor||0].click(); } else if (s === 'catch-result') { document.getElementById('btn-catch-continue')?.click(); } @@ -1601,6 +1712,9 @@ class Game { } _getActivePokemon() { + if (this.state.activePokemon && !this.state.activePokemon.fainted) return this.state.activePokemon; + const conscious = this.state.party.filter(p => !p.fainted); + if (conscious.length > 0) return conscious[0]; if (this.state.party.length === 0) return { name:'MissingNo', emoji:'❓', type:'???', level:1 }; return this.state.party[0]; } @@ -1848,6 +1962,11 @@ class Game { playerSprite.style.opacity = '0.3'; playerSprite.style.transform = 'translateY(20px) scale(0.5)'; SFX.battleLose(); + playerPoke.fainted = true; + const conscious = this.state.party.filter(p => !p.fainted); + if (conscious.length === 0) { + this.state.party[0].fainted = false; + } }, 1400); setTimeout(() => { playerSprite.style.opacity = '1'; diff --git a/index.html b/index.html index 829f430..0b8a708 100644 --- a/index.html +++ b/index.html @@ -132,6 +132,12 @@ + +
+
CHOOSE YOUR POKÉMON!
+
+
+
diff --git a/style.css b/style.css index 10d9189..ce4b9a3 100644 --- a/style.css +++ b/style.css @@ -1333,3 +1333,95 @@ html, body { background-position:-48px 0; image-rendering:pixelated; } + +/* ─── PARTY SELECT SCREEN ────────────────────────────────── */ +#screen-party-select { + background:linear-gradient(180deg,#1a1a2e 0%,#16213e 100%); + flex-direction:column; + align-items:center; + padding:12px 10px; + overflow-y:auto; + -webkit-overflow-scrolling:touch; +} +.party-select-header { + font-family:var(--font); + font-size:clamp(10px,3vw,14px); + color:var(--yellow); + text-shadow:2px 2px 0 #604000; + text-align:center; + padding:8px 0 12px; + flex-shrink:0; +} +.party-select-list { + display:flex; + flex-direction:column; + gap:6px; + width:100%; + max-width:380px; + padding-bottom:max(16px, calc(var(--safe-bottom) + 160px)); +} +.party-select-entry { + display:flex; + align-items:center; + gap:10px; + padding:10px 12px; + background:var(--dialog-bg); + border:3px solid var(--dialog-bd); + cursor:pointer; + font-family:var(--font); + transition:transform .1s, box-shadow .1s; + box-shadow:3px 3px 0 rgba(0,0,0,.4); + position:relative; +} +.party-select-entry:hover, +.party-select-entry.party-selected { + transform:translateX(6px); + box-shadow:3px 3px 0 rgba(0,0,0,.4); + border-color:var(--yellow); + background:#fff8e0; +} +.party-select-entry.fainted { + opacity:.5; + cursor:not-allowed; +} +.party-select-entry.fainted:hover { + transform:none; + border-color:var(--dialog-bd); + background:var(--dialog-bg); +} +.party-select-emoji { + font-size:clamp(28px,8vw,40px); + position:relative; + flex-shrink:0; +} +.party-select-zzz { + position:absolute; + top:-8px; + right:-8px; + font-family:var(--font); + font-size:clamp(6px,1.8vw,8px); + color:var(--lightgray); + animation:bounce 1.5s ease-in-out infinite; +} +.party-select-info { + display:flex; + flex-direction:column; + gap:2px; +} +.party-select-name { + font-size:clamp(8px,2.5vw,11px); + color:var(--black); +} +.party-select-detail { + font-size:clamp(5.5px,1.5vw,7px); + color:var(--gray); +} +.party-select-badge { + margin-left:auto; + font-size:clamp(5px,1.3vw,6.5px); + color:var(--green); + font-family:var(--font); +} +.party-select-badge.badge-fainted { + color:var(--red); +}