polats Claude Opus 4.8 (1M context) commited on
Commit
261c0fe
·
1 Parent(s): f3c5314

Trickle spawns + remove dead code (attribute-point spend UI, level-up toast)

Browse files

Rebuild comboBattler for the 1-2/wave trickle. Remove the now-unused attribute-point
spend flow (openSpend, spendPoint, the spend button) and the old level-up toast —
superseded by the roguelike perk draft.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files changed (3) hide show
  1. web/comboBattler.js +8 -9
  2. web/progression.js +0 -11
  3. web/tiny.js +1 -34
web/comboBattler.js CHANGED
@@ -6036,9 +6036,7 @@ var STEP = 0.05;
6036
  var SPAWN_TILES = 22;
6037
  var SPAWN_JITTER = 6;
6038
  var DESPAWN_TILES = 52;
6039
- var TARGET_FOES = 8;
6040
- var MAX_FOES = 26;
6041
- var SPAWN_INTERVAL = 0.3;
6042
  var DEATH_LINGER = 1.6;
6043
  async function contentHeight(url) {
6044
  try {
@@ -6782,7 +6780,7 @@ function mountComboBattler(pixi, host, opts = {}) {
6782
  }
6783
  let foeN = 0;
6784
  const deadAt = /* @__PURE__ */ new Map();
6785
- const spawnAcc = { t: SPAWN_INTERVAL };
6786
  let nextEliteAt = 8, nextBossAt = 24;
6787
  runState = { time: 0, kills: 0, diff: 1 };
6788
  const TIER = {
@@ -6842,12 +6840,13 @@ function mountComboBattler(pixi, host, opts = {}) {
6842
  }
6843
  }
6844
  spawnAcc.t += dt;
6845
- if (spawnAcc.t < SPAWN_INTERVAL) return;
6846
- spawnAcc.t = 0;
6847
  const diff = runState.diff;
6848
- const target = Math.min(MAX_FOES, Math.round(TARGET_FOES + diff * 4));
6849
- const need = target - ea().filter((a) => a.alive).length;
6850
- for (let i = 0; i < need; i++) {
 
 
 
6851
  const ang = Math.random() * Math.PI * 2;
6852
  const rr = (SPAWN_TILES + (Math.random() * 2 - 1) * SPAWN_JITTER) * FSTEP;
6853
  const pos = snapField(p.x + Math.cos(ang) * rr, p.y + Math.sin(ang) * rr);
 
6036
  var SPAWN_TILES = 22;
6037
  var SPAWN_JITTER = 6;
6038
  var DESPAWN_TILES = 52;
6039
+ var MAX_FOES = 12;
 
 
6040
  var DEATH_LINGER = 1.6;
6041
  async function contentHeight(url) {
6042
  try {
 
6780
  }
6781
  let foeN = 0;
6782
  const deadAt = /* @__PURE__ */ new Map();
6783
+ const spawnAcc = { t: 99 };
6784
  let nextEliteAt = 8, nextBossAt = 24;
6785
  runState = { time: 0, kills: 0, diff: 1 };
6786
  const TIER = {
 
6840
  }
6841
  }
6842
  spawnAcc.t += dt;
 
 
6843
  const diff = runState.diff;
6844
+ const waveInterval = Math.max(0.8, 3 / diff);
6845
+ if (spawnAcc.t < waveInterval) return;
6846
+ spawnAcc.t = 0;
6847
+ if (ea().filter((a) => a.alive).length >= MAX_FOES) return;
6848
+ const count = 1 + (Math.random() < Math.min(0.7, (diff - 1) / 3) ? 1 : 0);
6849
+ for (let i = 0; i < count; i++) {
6850
  const ang = Math.random() * Math.PI * 2;
6851
  const rr = (SPAWN_TILES + (Math.random() * 2 - 1) * SPAWN_JITTER) * FSTEP;
6852
  const pos = snapField(p.x + Math.cos(ang) * rr, p.y + Math.sin(ang) * rr);
web/progression.js CHANGED
@@ -76,17 +76,6 @@ export function statBonuses(progression) {
76
  }
77
  }
78
 
79
- // Spend one attribute point into a stat ('hp' | 'dmg'). Returns a NEW progression object;
80
- // no-op if no points or an unknown stat.
81
- export function spendPoint(progression, stat) {
82
- const p = { level: 1, xp: 0, kills: 0, attributePoints: 0, ...(progression || {}) }
83
- p.spent = { hp: 0, dmg: 0, ...(p.spent || {}) }
84
- if ((p.attributePoints || 0) <= 0 || !['hp', 'dmg'].includes(stat)) return p
85
- p.attributePoints -= 1
86
- p.spent[stat] = (p.spent[stat] || 0) + 1
87
- return p
88
- }
89
-
90
  // Append a capped, structured event to a persona's log (newest last). Used for the chat
91
  // "what's happening to you" context (woid-style perception log) and the level-up feed.
92
  const MAX_EVENTS = 60
 
76
  }
77
  }
78
 
 
 
 
 
 
 
 
 
 
 
 
79
  // Append a capped, structured event to a persona's log (newest last). Used for the chat
80
  // "what's happening to you" context (woid-style perception log) and the level-up feed.
81
  const MAX_EVENTS = 60
web/tiny.js CHANGED
@@ -16,7 +16,7 @@ import { mountComboBattler } from '/web/comboBattler.js'
16
  import { mountPersonaPanel, CLASS_SLUG } from '/web/personaPanel.js'
17
  import { mountHeroCreator, animateIdleIcon } from '/web/heroCreator.js'
18
  import { listPersonas, onRosterChange, getPortrait, getPersona, patchPersona, setActiveHeroId } from '/web/personaStore.js'
19
- import { applyXp, enemyXpValue, levelProgress, xpToNext, appendEvent, statBonuses, spendPoint } from '/web/progression.js'
20
  import { getSkillIcon, getSkillArt, putSkillArt } from '/web/personaStore.js'
21
  import { effectSummary, resolveEquipped } from '/web/skillSchema.js'
22
  import { generatePortrait } from '/web/imagen.js'
@@ -433,16 +433,6 @@ whenEl('battle-stage', async (el) => {
433
  }
434
  showNext()
435
  }
436
- // A brief level-up banner over the canvas (mobile-friendly: centered, auto-dismiss).
437
- let levelToast = null
438
- const showLevelUp = (prog) => {
439
- levelToast?.remove()
440
- const t = document.createElement('div')
441
- t.textContent = `⚔️ ${currentHero?.name || 'Hero'} reached level ${prog.level}!`
442
- t.style.cssText = 'position:absolute;left:50%;top:18%;transform:translateX(-50%);z-index:9;padding:12px 18px;border-radius:12px;border:1px solid #c9a227;background:rgba(20,24,33,.94);color:#ffe082;font:700 15px var(--tac-font,system-ui);box-shadow:0 6px 24px rgba(0,0,0,.5);pointer-events:none;text-align:center;max-width:86vw'
443
- el.append(t); levelToast = t
444
- setTimeout(() => { t.style.transition = 'opacity .5s'; t.style.opacity = '0'; setTimeout(() => t.remove(), 520) }, 2200)
445
- }
446
  // Picking a hero opens its detail page (portrait/about/quote + Select); Select confirms → spawnWithFly.
447
  const onPick = (p) => openHeroDetail(el, p, chars, () => spawnWithFly(p))
448
  const buildPicker = () => {
@@ -603,28 +593,6 @@ whenEl('battle-stage', async (el) => {
603
  } catch (e) { status.textContent = 'Forge failed: ' + (e && e.message ? e.message : e); busy = false; go.disabled = false; ta.disabled = false }
604
  })
605
  }
606
- // Spend attribute points (earned on level-up) into +HP or +Damage. Applies on next spawn.
607
- const openSpend = () => {
608
- const { sh } = openToolSheet('Spend attribute points')
609
- const render = () => {
610
- sh.querySelectorAll('.spend-dyn').forEach((n) => n.remove())
611
- const pr = getPersona(currentHero.id).progression
612
- const sp = pr.spent || { hp: 0, dmg: 0 }
613
- const info = document.createElement('div'); info.className = 'spend-dyn'; info.style.cssText = 'color:#9aa4b2;font-size:12px;margin-bottom:10px'
614
- info.textContent = `${pr.attributePoints} point${pr.attributePoints !== 1 ? 's' : ''} to spend · current: +${Math.round((sp.hp || 0) * 5)}% HP, +${Math.round((sp.dmg || 0) * 5)}% DMG`
615
- sh.append(info)
616
- const mk = (label, stat) => {
617
- const b = document.createElement('button'); b.className = 'spend-dyn'; b.type = 'button'; b.textContent = label
618
- const none = pr.attributePoints <= 0; b.disabled = none
619
- b.style.cssText = `width:100%;padding:12px;border-radius:10px;border:1px solid #2a3340;background:#1a2230;color:#e8e8e8;font:600 13px var(--tac-font,system-ui);cursor:${none ? 'default' : 'pointer'};margin-bottom:8px;opacity:${none ? 0.5 : 1}`
620
- b.addEventListener('click', () => { const np = spendPoint(getPersona(currentHero.id).progression, stat); patchPersona(currentHero.id, { progression: np }); currentHero = getPersona(currentHero.id); render(); renderSheet() })
621
- sh.append(b)
622
- }
623
- mk('+5% Max HP', 'hp'); mk('+5% Damage', 'dmg')
624
- const note = document.createElement('div'); note.className = 'spend-dyn'; note.style.cssText = 'color:#6b7686;font-size:11px;margin-top:4px'; note.textContent = 'Takes full effect on your next spawn.'; sh.append(note)
625
- }
626
- render()
627
- }
628
  const renderSheet = () => {
629
  sheet.innerHTML = ''
630
  const close = document.createElement('button'); close.type = 'button'; close.textContent = '✕'
@@ -649,7 +617,6 @@ whenEl('battle-stage', async (el) => {
649
  const meta = document.createElement('div'); meta.style.cssText = 'display:flex;justify-content:space-between;font-size:11px;color:#9aa4b2;margin-bottom:10px'
650
  const kills = document.createElement('span'); kills.textContent = `⚔ ${prog.kills} kills`
651
  meta.append(kills)
652
- if (prog.attributePoints > 0) { const pts = document.createElement('button'); pts.type = 'button'; pts.textContent = `★ ${prog.attributePoints} point${prog.attributePoints > 1 ? 's' : ''} — spend`; pts.style.cssText = 'background:none;border:none;color:#ffe082;font:inherit;cursor:pointer;text-decoration:underline'; pts.addEventListener('click', openSpend); meta.append(pts) }
653
  sheet.append(meta)
654
  if (prog.best && prog.best.kills) { const bst = document.createElement('div'); bst.textContent = `🏆 Best run: ${prog.best.kills} foes · threat ${prog.best.threat || 1}×`; bst.style.cssText = 'font-size:11px;color:#c9a227;margin:-4px 0 10px'; sheet.append(bst) }
655
  }
 
16
  import { mountPersonaPanel, CLASS_SLUG } from '/web/personaPanel.js'
17
  import { mountHeroCreator, animateIdleIcon } from '/web/heroCreator.js'
18
  import { listPersonas, onRosterChange, getPortrait, getPersona, patchPersona, setActiveHeroId } from '/web/personaStore.js'
19
+ import { applyXp, enemyXpValue, levelProgress, xpToNext, appendEvent, statBonuses } from '/web/progression.js'
20
  import { getSkillIcon, getSkillArt, putSkillArt } from '/web/personaStore.js'
21
  import { effectSummary, resolveEquipped } from '/web/skillSchema.js'
22
  import { generatePortrait } from '/web/imagen.js'
 
433
  }
434
  showNext()
435
  }
 
 
 
 
 
 
 
 
 
 
436
  // Picking a hero opens its detail page (portrait/about/quote + Select); Select confirms → spawnWithFly.
437
  const onPick = (p) => openHeroDetail(el, p, chars, () => spawnWithFly(p))
438
  const buildPicker = () => {
 
593
  } catch (e) { status.textContent = 'Forge failed: ' + (e && e.message ? e.message : e); busy = false; go.disabled = false; ta.disabled = false }
594
  })
595
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
  const renderSheet = () => {
597
  sheet.innerHTML = ''
598
  const close = document.createElement('button'); close.type = 'button'; close.textContent = '✕'
 
617
  const meta = document.createElement('div'); meta.style.cssText = 'display:flex;justify-content:space-between;font-size:11px;color:#9aa4b2;margin-bottom:10px'
618
  const kills = document.createElement('span'); kills.textContent = `⚔ ${prog.kills} kills`
619
  meta.append(kills)
 
620
  sheet.append(meta)
621
  if (prog.best && prog.best.kills) { const bst = document.createElement('div'); bst.textContent = `🏆 Best run: ${prog.best.kills} foes · threat ${prog.best.threat || 1}×`; bst.style.cssText = 'font-size:11px;color:#c9a227;margin:-4px 0 10px'; sheet.append(bst) }
622
  }