generated from nhcarrigan/template
feat: vampire UI infrastructure - mode bar, tab row, and blood-red theme
- Added vampire API client functions (boss challenge, siring, awakening, upgrades, craft, explore) - Fixed goddess API client URL bugs (/goddess/* → /goddess-boss/challenge, /goddess-upgrade/buy, /goddess-craft, /goddess-explore/*) - Added VampireTab type and vampireTabs array (11 tabs) to GameLayout - Fixed mode unlock chain: vampire after apotheosis, goddess after eternalSovereignty - Added body.vampire-mode CSS class toggle alongside existing goddess-mode toggle - Added vampire tab bar nav with blood-red theme - Replaced single vampire placeholder with per-tab placeholders - Added body.vampire-mode CSS variables (blood-red palette) and .vampire-tab-bar styles - Added Blood/Ichor/Soul Shards resource display to ResourceBar (gated on apotheosis)
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
/* eslint-disable max-lines -- Complex layout with many conditional renders */
|
||||
/* eslint-disable max-lines-per-function -- Complex layout with many conditional renders */
|
||||
/* eslint-disable complexity -- Many tab render paths */
|
||||
/* eslint-disable max-statements -- Many state variables for multi-mode tab routing */
|
||||
import { type JSX, useEffect, useState } from "react";
|
||||
import { useGame } from "../../context/gameContext.js";
|
||||
import { ResourceBar } from "../ui/resourceBar.js";
|
||||
@@ -89,6 +90,19 @@ type GoddessTab =
|
||||
| "goddess-exploration"
|
||||
| "goddess-achievements";
|
||||
|
||||
type VampireTab =
|
||||
| "vampire-zones"
|
||||
| "vampire-bosses"
|
||||
| "vampire-quests"
|
||||
| "thralls"
|
||||
| "vampire-equipment"
|
||||
| "vampire-upgrades"
|
||||
| "siring"
|
||||
| "vampire-awakening"
|
||||
| "vampire-crafting"
|
||||
| "vampire-exploration"
|
||||
| "vampire-achievements";
|
||||
|
||||
const baseTabs: Array<{ id: Tab; label: string }> = [
|
||||
{ id: "adventurers", label: "⚔️ Adventurers" },
|
||||
{ id: "upgrades", label: "🔧 Upgrades" },
|
||||
@@ -111,6 +125,20 @@ const baseTabs: Array<{ id: Tab; label: string }> = [
|
||||
{ id: "debug", label: "🔧 Debug" },
|
||||
];
|
||||
|
||||
const vampireTabs: Array<{ id: VampireTab; label: string }> = [
|
||||
{ id: "vampire-zones", label: "🗺️ Zones" },
|
||||
{ id: "vampire-bosses", label: "🩸 Bosses" },
|
||||
{ id: "vampire-quests", label: "📜 Quests" },
|
||||
{ id: "thralls", label: "🧟 Thralls" },
|
||||
{ id: "vampire-equipment", label: "🦇 Equipment" },
|
||||
{ id: "vampire-upgrades", label: "⚔️ Upgrades" },
|
||||
{ id: "siring", label: "🩸 Siring" },
|
||||
{ id: "vampire-awakening", label: "💀 Awakening" },
|
||||
{ id: "vampire-crafting", label: "⚗️ Crafting" },
|
||||
{ id: "vampire-exploration", label: "🌑 Exploration" },
|
||||
{ id: "vampire-achievements", label: "🏆 Achievements" },
|
||||
];
|
||||
|
||||
const goddessTabs: Array<{ id: GoddessTab; label: string }> = [
|
||||
{ id: "goddess-zones", label: "🌟 Zones" },
|
||||
{ id: "goddess-bosses", label: "👁️ Bosses" },
|
||||
@@ -169,12 +197,15 @@ const GameLayout = (): JSX.Element => {
|
||||
const [ activeTab, setActiveTab ] = useState<Tab>("adventurers");
|
||||
const [ activeGoddessTab, setActiveGoddessTab ]
|
||||
= useState<GoddessTab>("goddess-zones");
|
||||
const [ activeVampireTab, setActiveVampireTab ]
|
||||
= useState<VampireTab>("vampire-zones");
|
||||
const [ editingProfile, setEditingProfile ] = useState(false);
|
||||
const [ dismissedOutdatedWarning, setDismissedOutdatedWarning ]
|
||||
= useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.toggle("goddess-mode", activeMode === "goddess");
|
||||
document.body.classList.toggle("vampire-mode", activeMode === "vampire");
|
||||
}, [ activeMode ]);
|
||||
|
||||
if (isLoading) {
|
||||
@@ -273,8 +304,13 @@ const GameLayout = (): JSX.Element => {
|
||||
<nav className="mode-bar">
|
||||
{modes.map((mode) => {
|
||||
const apotheosisCount = state.apotheosis?.count ?? 0;
|
||||
const goddessLocked = mode === "goddess" && apotheosisCount === 0;
|
||||
const isLocked = goddessLocked || mode === "vampire";
|
||||
const eternalSovereigntyCount
|
||||
= state.vampire?.eternalSovereignty.count ?? 0;
|
||||
const vampireLocked
|
||||
= mode === "vampire" && apotheosisCount === 0;
|
||||
const goddessLocked
|
||||
= mode === "goddess" && eternalSovereigntyCount === 0;
|
||||
const isLocked = vampireLocked || goddessLocked;
|
||||
function handleModeClick(): void {
|
||||
if (!isLocked) {
|
||||
handleSetMode(mode);
|
||||
@@ -304,6 +340,7 @@ const GameLayout = (): JSX.Element => {
|
||||
})}
|
||||
</nav>
|
||||
|
||||
{/* eslint-disable-next-line no-nested-ternary -- Three-way mode switch for tab bar */}
|
||||
{activeMode === "mortal"
|
||||
? <nav className="tab-bar">
|
||||
{baseTabs.map((tab) => {
|
||||
@@ -331,26 +368,47 @@ const GameLayout = (): JSX.Element => {
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
: <nav className="tab-bar goddess-tab-bar">
|
||||
{goddessTabs.map((tab) => {
|
||||
const { id: tabId, label } = tab;
|
||||
function handleGoddessTabClick(): void {
|
||||
setActiveGoddessTab(tabId);
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={`tab-button${activeGoddessTab === tabId
|
||||
? " active"
|
||||
: ""}`}
|
||||
key={tabId}
|
||||
onClick={handleGoddessTabClick}
|
||||
type="button"
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
: activeMode === "goddess"
|
||||
? <nav className="tab-bar goddess-tab-bar">
|
||||
{goddessTabs.map((tab) => {
|
||||
const { id: tabId, label } = tab;
|
||||
function handleGoddessTabClick(): void {
|
||||
setActiveGoddessTab(tabId);
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={`tab-button${activeGoddessTab === tabId
|
||||
? " active"
|
||||
: ""}`}
|
||||
key={tabId}
|
||||
onClick={handleGoddessTabClick}
|
||||
type="button"
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
: <nav className="tab-bar vampire-tab-bar">
|
||||
{vampireTabs.map((tab) => {
|
||||
const { id: tabId, label } = tab;
|
||||
function handleVampireTabClick(): void {
|
||||
setActiveVampireTab(tabId);
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={`tab-button${activeVampireTab === tabId
|
||||
? " active"
|
||||
: ""}`}
|
||||
key={tabId}
|
||||
onClick={handleVampireTabClick}
|
||||
type="button"
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
}
|
||||
|
||||
<div className="tab-content">
|
||||
@@ -420,8 +478,69 @@ const GameLayout = (): JSX.Element => {
|
||||
&& activeGoddessTab === "goddess-achievements"
|
||||
&& <GoddessAchievementsPanel />}
|
||||
{activeMode === "vampire"
|
||||
&& <div className="goddess-placeholder">
|
||||
<p>{"🧛 Vampire panels coming soon..."}</p>
|
||||
&& activeVampireTab === "vampire-zones"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"🗺️ Vampire Zones coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "vampire-bosses"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"🩸 Vampire Bosses coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "vampire-quests"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"📜 Vampire Quests coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "thralls"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"🧟 Thralls coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "vampire-equipment"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"🦇 Vampire Equipment coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "vampire-upgrades"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"⚔️ Vampire Upgrades coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "siring"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"🩸 Siring coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "vampire-awakening"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"💀 Vampire Awakening coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "vampire-crafting"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"⚗️ Vampire Crafting coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "vampire-exploration"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"🌑 Vampire Exploration coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
{activeMode === "vampire"
|
||||
&& activeVampireTab === "vampire-achievements"
|
||||
&& <div className="vampire-placeholder">
|
||||
<p>{"🏆 Vampire Achievements coming soon..."}</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user