Add freshness bar, enhance overlays and renderers

- Add enemy freshness tracking (novelty bonus for repeated deploys)
- Add freshness bar to sidepanel enemy cards with penalty indicator
- Major overhaul of renderer-overlays.js (790+ lines for UI polish)
- Enhanced combat log, shop overlays, and inventory UI
- Improved weapon/upgrade display with partial ownership colors
- Added element icons and weakness/resistance indicators to cards
- Enhanced radial menu and tooltip system
- Add "stale/%" penalty text when freshness depleted
- Update play link to ffazeshift.net in index.html
This commit is contained in:
2026-06-17 11:58:17 -04:00
parent 6a710c3f03
commit 626879ed0c
21 changed files with 1884 additions and 312 deletions
+45
View File
@@ -73,6 +73,7 @@ function applyFreeze(enemy, duration) {
// Diminishing returns: each successive freeze is shorter
// 60-frame immunity window after thawing
if (enemy._freezeImmune > 0) return;
if (enemy.immuneToFreeze) return;
const diminish = enemy._freezeCount ? Math.pow(0.75, enemy._freezeCount) : 1.0;
const actual = Math.max(30, Math.round(duration * diminish));
enemy.frozen = actual;
@@ -133,6 +134,35 @@ function dealDamage(enemy, rawDamage, elements = ['physical'], canCrit = false,
}
dmg = Math.round(dmg);
// ── Special enemy modifiers ───────────────────────────────
// Commander aura: -30% incoming damage
if (enemy.commanderAuraDR > 0) {
dmg = Math.max(1, Math.round(dmg * (1 - enemy.commanderAuraDR)));
}
// Juggernaut damage cap (12 per hit; lightning doubles cap)
if (enemy.defId === 'juggernaut') {
const baseCap = ENEMY_DEFS.find(d => d.id === 'juggernaut')?.damageCapPerHit ?? 12;
const cap = elements.includes('lightning') ? baseCap * 2 : baseCap;
dmg = Math.min(dmg, cap);
}
// Void Herald shield: absorbs 60% damage when 3+ weapons hit in same frame
if (enemy.defId === 'voidherald') {
enemy._voidHitsThisFrame = (enemy._voidHitsThisFrame || 0) + 1;
if (enemy._voidHitsThisFrame >= 3) {
dmg = Math.max(1, Math.round(dmg * 0.4));
}
}
// Echo split: on burst hit >30 raw damage, queue a split (once per echo)
if (enemy.defId === 'echo' && !enemy._echoSplit && rawDamage > 30) {
enemy._echoSplit = true;
G._pendingEchoSpawns = G._pendingEchoSpawns || [];
G._pendingEchoSpawns.push({ x: enemy.x, y: enemy.y, copyHp: Math.round(enemy.maxHp * 0.4) });
}
enemy.hp -= dmg;
enemy.hitFlash = 6;
@@ -166,6 +196,9 @@ function applyElementalEffect(enemy, el) {
break;
case 'void':
enemy.armor = Math.max(0, (enemy.armor || 0) - 1);
if (enemy.defId === 'siegebreaker') {
enemy.regenPausedUntil = G.frame + 180; // pause regen 3 seconds
}
break;
case 'arcane':
enemy.amplified = Math.min(1.5, (enemy.amplified || 0) + 0.1);
@@ -227,4 +260,16 @@ function tickEnemyStatus(enemy) {
enemy.amplified *= 0.995;
if (enemy.amplified < 0.01) enemy.amplified = 0;
}
// Siege Breaker HP regen: +5 HP/sec; void damage pauses for 3s
if (enemy.defId === 'siegebreaker' && enemy.hp > 0) {
if (!enemy.regenPausedUntil || G.frame >= enemy.regenPausedUntil) {
enemy._regenAcc = (enemy._regenAcc || 0) + 5 / 60;
if (enemy._regenAcc >= 1) {
const regen = Math.floor(enemy._regenAcc);
enemy.hp = Math.min(enemy.maxHp, enemy.hp + regen);
enemy._regenAcc -= regen;
}
}
}
}