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:
+129
-7
@@ -51,7 +51,7 @@ function perfNow() {
|
||||
function setPaused(paused, showOverlay = true) {
|
||||
G.paused = paused;
|
||||
document.body.classList.toggle('paused',
|
||||
!!paused && showOverlay && !G.shopOpen &&
|
||||
!!paused && showOverlay && !G.armoryOpen && !G.commandOpen &&
|
||||
!document.body.classList.contains('inventory-open') && !G.gameOver);
|
||||
}
|
||||
|
||||
@@ -161,8 +161,12 @@ function updateHUD() {
|
||||
// ── COMBAT LOG ────────────────────────────────────────────────
|
||||
function addLog(msg, type = '') {
|
||||
if (!G.logLines) G.logLines = [];
|
||||
G.logLines.unshift({ text: msg, type });
|
||||
if (G.logLines.length > 40) G.logLines.length = 40;
|
||||
if (G.logLines.length > 0 && G.logLines[0].text === msg && G.logLines[0].type === type) {
|
||||
G.logLines[0].count = (G.logLines[0].count || 1) + 1;
|
||||
} else {
|
||||
G.logLines.unshift({ text: msg, type, count: 1 });
|
||||
if (G.logLines.length > 40) G.logLines.length = 40;
|
||||
}
|
||||
}
|
||||
|
||||
// ── BANKRUPTCY CHECK ──────────────────────────────────────────
|
||||
@@ -189,7 +193,8 @@ function checkBankruptcy() {
|
||||
|
||||
// ── GAME OVER ─────────────────────────────────────────────────
|
||||
function endGame() {
|
||||
if (G.shopOpen) closeShop();
|
||||
if (G.armoryOpen) closeArmory();
|
||||
if (G.commandOpen) closeCommand();
|
||||
setPaused(false);
|
||||
G.gameOver = true;
|
||||
G.isBankrupt = false;
|
||||
@@ -201,7 +206,8 @@ function endGame() {
|
||||
}
|
||||
|
||||
function endBankrupt() {
|
||||
if (G.shopOpen) closeShop();
|
||||
if (G.armoryOpen) closeArmory();
|
||||
if (G.commandOpen) closeCommand();
|
||||
setPaused(false);
|
||||
G.gameOver = true;
|
||||
G.isBankrupt = true;
|
||||
@@ -214,13 +220,130 @@ function endBankrupt() {
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
const savedBonuses = G.permanentBonuses ? { ...G.permanentBonuses } : {};
|
||||
const savedTiers = G.unlockedTiers ? [...G.unlockedTiers] : [0];
|
||||
const savedPrestige = G.prestigeLevel || 0;
|
||||
G = makeGameState();
|
||||
G.permanentBonuses = savedBonuses;
|
||||
G.unlockedTiers = savedTiers;
|
||||
G.difficultyTier = savedTiers.includes(0) ? 0 : savedTiers[0];
|
||||
G.prestigeLevel = savedPrestige;
|
||||
applyPermanentBonuses();
|
||||
setPaused(false);
|
||||
_sidePanelScrollY = 0;
|
||||
_logScrollY = 0;
|
||||
updateHUD();
|
||||
addLog('System online. Deploy enemies to earn credits.', 'info');
|
||||
addLog('[1–0] deploy [scroll] qty [Space] shop [Esc/P] pause [I] inventory', 'info');
|
||||
if (savedPrestige > 0) addLog(`PRESTIGE ${savedPrestige} active — permanent bonuses applied.`, 'win');
|
||||
}
|
||||
|
||||
// ── THREAT / PRESTIGE PANEL CONTROLS ─────────────────────────
|
||||
function openThreatPanel() {
|
||||
if (G.gameOver) return;
|
||||
G.threatOpen = true;
|
||||
G.prestigeOpen = false;
|
||||
}
|
||||
function closeThreatPanel() {
|
||||
G.threatOpen = false;
|
||||
}
|
||||
function openPrestigeDialog() {
|
||||
if (G.gameOver) return;
|
||||
G.prestigeOpen = true;
|
||||
G.threatOpen = false;
|
||||
}
|
||||
function closePrestigeDialog() {
|
||||
G.prestigeOpen = false;
|
||||
}
|
||||
|
||||
// ── THREAT LEVEL ─────────────────────────────────────────────
|
||||
function unlockThreatTier(tierId) {
|
||||
const tierDef = DIFFICULTY_TIERS[tierId];
|
||||
if (!tierDef) return;
|
||||
if (G.unlockedTiers.includes(tierId)) {
|
||||
// Already unlocked — just switch
|
||||
setThreatTier(tierId);
|
||||
return;
|
||||
}
|
||||
if (spendableCredits() < tierDef.unlockCost) {
|
||||
addLog(`Need ${tierDef.unlockCost}¢ to unlock ${tierDef.name}.`, 'info');
|
||||
return;
|
||||
}
|
||||
G.credits -= tierDef.unlockCost;
|
||||
G.unlockedTiers.push(tierId);
|
||||
setThreatTier(tierId);
|
||||
addLog(`THREAT LEVEL: ${tierDef.name} unlocked and activated.`, 'win');
|
||||
updateHUD();
|
||||
renderShop();
|
||||
}
|
||||
|
||||
function setThreatTier(tierId) {
|
||||
if (!G.unlockedTiers.includes(tierId)) return;
|
||||
G.difficultyTier = tierId;
|
||||
const tierDef = DIFFICULTY_TIERS[tierId];
|
||||
if (tierDef && tierId > 0) {
|
||||
addLog(`THREAT LEVEL: ${tierDef.name} — enemies at ×${tierDef.hpMult} HP, ×${tierDef.rewardMult} rewards.`, 'info');
|
||||
} else {
|
||||
addLog('THREAT LEVEL: NORMAL — standard difficulty.', 'info');
|
||||
}
|
||||
updateHUD();
|
||||
renderShop();
|
||||
}
|
||||
|
||||
// ── PRESTIGE ─────────────────────────────────────────────────
|
||||
function prestigeCost() {
|
||||
const base = [5000, 25000, 60000, 120000, 200000];
|
||||
const lvl = G.prestigeLevel || 0;
|
||||
return base[lvl] ?? (200000 + (lvl - 4) * 100000);
|
||||
}
|
||||
|
||||
function applyPermanentBonuses() {
|
||||
const b = G.permanentBonuses || {};
|
||||
if (b.maxHp) { G.tower.maxHp += Math.floor(b.maxHp); G.tower.hp = Math.min(G.tower.hp, G.tower.maxHp); }
|
||||
if (b.armor) { G.tower.armor += Math.floor(b.armor); }
|
||||
if (b.range) { G.tower.range += Math.floor(b.range); }
|
||||
if (b.aimSpeed) { G.tower.aimSpeed += b.aimSpeed; }
|
||||
}
|
||||
|
||||
function prestige() {
|
||||
const cost = prestigeCost();
|
||||
if (G.credits < cost) {
|
||||
addLog(`Need ${cost}¢ to prestige.`, 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
const FRACTION = 0.25;
|
||||
const newBonuses = { ...(G.permanentBonuses || {}) };
|
||||
|
||||
// Convert all owned tower upgrades into fractional permanent bonuses
|
||||
for (const upg of TOWER_UPGRADE_TREE) {
|
||||
if (!G.towerUpgradesBought.includes(upg.id) || upg.repeatable) continue;
|
||||
const e = upg.effect;
|
||||
if (e.maxHp) newBonuses.maxHp = (newBonuses.maxHp || 0) + e.maxHp * FRACTION;
|
||||
if (e.armor) newBonuses.armor = (newBonuses.armor || 0) + e.armor * FRACTION;
|
||||
if (e.aimSpeed) newBonuses.aimSpeed = (newBonuses.aimSpeed || 0) + e.aimSpeed * FRACTION;
|
||||
if (e.range) newBonuses.range = (newBonuses.range || 0) + e.range * FRACTION;
|
||||
}
|
||||
|
||||
const prevTiers = [...G.unlockedTiers];
|
||||
const newPrestige = (G.prestigeLevel || 0) + 1;
|
||||
|
||||
G = makeGameState();
|
||||
G.permanentBonuses = newBonuses;
|
||||
G.unlockedTiers = prevTiers;
|
||||
G.difficultyTier = prevTiers.includes(0) ? 0 : prevTiers[0];
|
||||
G.prestigeLevel = newPrestige;
|
||||
applyPermanentBonuses();
|
||||
|
||||
setPaused(false);
|
||||
_sidePanelScrollY = 0;
|
||||
_logScrollY = 0;
|
||||
updateHUD();
|
||||
renderShop();
|
||||
|
||||
addLog(`PRESTIGE ${newPrestige} — permanent bonuses banked. New run starts at 150¢.`, 'win');
|
||||
if (newBonuses.maxHp) addLog(` Hull: +${Math.floor(newBonuses.maxHp)} HP permanent`, 'win');
|
||||
if (newBonuses.armor) addLog(` Plating: +${Math.floor(newBonuses.armor)} armor permanent`, 'win');
|
||||
if (newBonuses.range) addLog(` Scanner: +${Math.floor(newBonuses.range)} range permanent`, 'win');
|
||||
}
|
||||
|
||||
// ── INIT ──────────────────────────────────────────────────────
|
||||
@@ -229,7 +352,6 @@ function init() {
|
||||
updateHUD();
|
||||
initInput();
|
||||
addLog('SIEGE PROTOCOL initialized.', 'info');
|
||||
addLog('[1–0] deploy [scroll] qty [Space] shop [Esc/P] pause [I] inventory', 'info');
|
||||
gameLoop();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user