fix: address multiple bugs (1,2,3,5,6,9,10,11,12,13) - partial fix for 4, remaining 7,8
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m9s

This commit is contained in:
2026-05-07 12:28:16 +02:00
parent 54d5e576ab
commit 7851d8c7cb
10 changed files with 136 additions and 22 deletions
+1
View File
@@ -49,3 +49,4 @@ prompt
server.log server.log
# Skills directory # Skills directory
/.zscripts/ /.zscripts/
.gitnexus
+44
View File
@@ -600,3 +600,47 @@ Base Elements (7) → Compound (3) → Exotic (3)
Utility (1) ← Special attunement-based Utility (1) ← Special attunement-based
``` ```
<!-- gitnexus:start -->
# GitNexus — Code Intelligence
This project is indexed by GitNexus as **Mana-Loop** (3795 symbols, 6409 relationships, 146 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
## Always Do
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
## Never Do
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
## Resources
| Resource | Use for |
|----------|---------|
| `gitnexus://repo/Mana-Loop/context` | Codebase overview, check index freshness |
| `gitnexus://repo/Mana-Loop/clusters` | All functional areas |
| `gitnexus://repo/Mana-Loop/processes` | All execution flows |
| `gitnexus://repo/Mana-Loop/process/{name}` | Step-by-step execution trace |
## CLI
| Task | Read this skill file |
|------|---------------------|
| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` |
| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` |
| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` |
| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` |
| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` |
| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |
<!-- gitnexus:end -->
+43
View File
@@ -0,0 +1,43 @@
<!-- gitnexus:start -->
# GitNexus — Code Intelligence
This project is indexed by GitNexus as **Mana-Loop** (3795 symbols, 6409 relationships, 146 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
## Always Do
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
## Never Do
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
## Resources
| Resource | Use for |
|----------|---------|
| `gitnexus://repo/Mana-Loop/context` | Codebase overview, check index freshness |
| `gitnexus://repo/Mana-Loop/clusters` | All functional areas |
| `gitnexus://repo/Mana-Loop/processes` | All execution flows |
| `gitnexus://repo/Mana-Loop/process/{name}` | Step-by-step execution trace |
## CLI
| Task | Read this skill file |
|------|---------------------|
| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` |
| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` |
| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` |
| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` |
| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` |
| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |
<!-- gitnexus:end -->
+1
View File
@@ -455,6 +455,7 @@ Mana-Loop/
├── .gitignore ├── .gitignore
├── 3001 ├── 3001
├── AGENTS.md ├── AGENTS.md
├── CLAUDE.md
├── Caddyfile ├── Caddyfile
├── Dockerfile ├── Dockerfile
├── README.md ├── README.md
+5 -4
View File
@@ -8,10 +8,11 @@ import { useGameStore } from '@/lib/game/stores';
interface GameOverScreenProps { interface GameOverScreenProps {
day: number; day: number;
hour: number; hour: number;
insight: number; insightGained: number;
totalInsight: number;
} }
export function GameOverScreen({ day, hour, insight }: GameOverScreenProps) { export function GameOverScreen({ day, hour, insightGained, totalInsight }: GameOverScreenProps) {
const startNewLoop = () => { const startNewLoop = () => {
useGameStore.getState().startNewLoop(); useGameStore.getState().startNewLoop();
}; };
@@ -31,7 +32,7 @@ export function GameOverScreen({ day, hour, insight }: GameOverScreenProps) {
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="p-3 bg-gray-800 rounded"> <div className="p-3 bg-gray-800 rounded">
<div className="text-xl font-bold text-amber-400 game-mono">{fmt(insight)}</div> <div className="text-xl font-bold text-amber-400 game-mono">{fmt(insightGained)}</div>
<div className="text-xs text-gray-400">Insight Gained</div> <div className="text-xs text-gray-400">Insight Gained</div>
</div> </div>
<div className="p-3 bg-gray-800 rounded"> <div className="p-3 bg-gray-800 rounded">
@@ -43,7 +44,7 @@ export function GameOverScreen({ day, hour, insight }: GameOverScreenProps) {
<div className="text-xs text-gray-400">Hour</div> <div className="text-xs text-gray-400">Hour</div>
</div> </div>
<div className="p-3 bg-gray-800 rounded"> <div className="p-3 bg-gray-800 rounded">
<div className="text-xl font-bold text-green-400 game-mono">{insight}</div> <div className="text-xl font-bold text-green-400 game-mono">{fmt(totalInsight)}</div>
<div className="text-xs text-gray-400">Total Insight</div> <div className="text-xs text-gray-400">Total Insight</div>
</div> </div>
</div> </div>
+7 -12
View File
@@ -49,7 +49,7 @@ import { LeftPanel } from './components/LeftPanel';
const SpireTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SpireTab }))); const SpireTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SpireTab })));
const SkillsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SkillsTab }))); const SkillsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SkillsTab })));
const SpellsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SpellsTab }))); const SpellsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SpellsTab })));
const LabTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.LabTab })));
const StatsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.StatsTab }))); const StatsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.StatsTab })));
const EquipmentTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.EquipmentTab }))); const EquipmentTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.EquipmentTab })));
const AttunementsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.AttunementsTab }))); const AttunementsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.AttunementsTab })));
@@ -72,7 +72,7 @@ function GrimoireTab() {
} }
return []; return [];
}); });
const loaded = grimoireSpells.length > 0 || (typeof window !== 'undefined' && !SPELLS_DEF); const loaded = typeof window !== 'undefined';
if (!loaded) { if (!loaded) {
return <div className="p-4 text-center text-gray-400">Loading grimoire...</div>; return <div className="p-4 text-center text-gray-400">Loading grimoire...</div>;
@@ -142,6 +142,7 @@ export default function ManaLoopGame() {
const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
const insight = usePrestigeStore((s) => s.insight); const insight = usePrestigeStore((s) => s.insight);
const loopInsight = usePrestigeStore((s) => s.loopInsight);
const rawMana = useManaStore((s) => s.rawMana); const rawMana = useManaStore((s) => s.rawMana);
const meditateTicks = useManaStore((s) => s.meditateTicks); const meditateTicks = useManaStore((s) => s.meditateTicks);
@@ -221,7 +222,7 @@ export default function ManaLoopGame() {
// Conditional returns AFTER all hooks // Conditional returns AFTER all hooks
if (gameOver) { if (gameOver) {
return <GameOverScreen store={{ day, hour, insight }} />; return <GameOverScreen day={day} hour={hour} insightGained={loopInsight} totalInsight={insight} />;
} }
if (!mounted) return <div className="p-4 text-center text-gray-400">Loading...</div>; if (!mounted) return <div className="p-4 text-center text-gray-400">Loading...</div>;
@@ -256,7 +257,7 @@ export default function ManaLoopGame() {
<TabsTrigger value="crafting" className="text-xs px-2 py-1">🔧 Craft</TabsTrigger> <TabsTrigger value="crafting" className="text-xs px-2 py-1">🔧 Craft</TabsTrigger>
<TabsTrigger value="loot" className="text-xs px-2 py-1">💎 Loot</TabsTrigger> <TabsTrigger value="loot" className="text-xs px-2 py-1">💎 Loot</TabsTrigger>
<TabsTrigger value="achievements" className="text-xs px-2 py-1">🏆 Achieve</TabsTrigger> <TabsTrigger value="achievements" className="text-xs px-2 py-1">🏆 Achieve</TabsTrigger>
<TabsTrigger value="lab" className="text-xs px-2 py-1">🔬 Lab</TabsTrigger>
<TabsTrigger value="stats" className="text-xs px-2 py-1">📊 Stats</TabsTrigger> <TabsTrigger value="stats" className="text-xs px-2 py-1">📊 Stats</TabsTrigger>
<TabsTrigger value="debug" className="text-xs px-2 py-1">🐛 Debug</TabsTrigger> <TabsTrigger value="debug" className="text-xs px-2 py-1">🐛 Debug</TabsTrigger>
<TabsTrigger value="grimoire" className="text-xs px-2 py-1">📖 Grimoire</TabsTrigger> <TabsTrigger value="grimoire" className="text-xs px-2 py-1">📖 Grimoire</TabsTrigger>
@@ -265,7 +266,7 @@ export default function ManaLoopGame() {
<TabsContent value="spire"> <TabsContent value="spire">
<ErrorBoundary fallback={<div className="p-4 text-red-400">spire tab failed to load.</div>}> <ErrorBoundary fallback={<div className="p-4 text-red-400">spire tab failed to load.</div>}>
<Suspense fallback={<TabLoadingFallback />}> <Suspense fallback={<TabLoadingFallback />}>
<SpireTab /> <SpireTab simpleMode={spireMode} />
</Suspense> </Suspense>
</ErrorBoundary> </ErrorBoundary>
</TabsContent> </TabsContent>
@@ -334,13 +335,7 @@ export default function ManaLoopGame() {
</ErrorBoundary> </ErrorBoundary>
</TabsContent> </TabsContent>
<TabsContent value="lab">
<ErrorBoundary fallback={<div className="p-4 text-red-400">lab tab failed to load.</div>}>
<Suspense fallback={<TabLoadingFallback />}>
<LabTab />
</Suspense>
</ErrorBoundary>
</TabsContent>
<TabsContent value="stats"> <TabsContent value="stats">
<ErrorBoundary fallback={<div className="p-4 text-red-400">stats tab failed to load.</div>}> <ErrorBoundary fallback={<div className="p-4 text-red-400">stats tab failed to load.</div>}>
+1 -2
View File
@@ -29,7 +29,6 @@ export function GameStateDebug() {
// Get actions from stores // Get actions from stores
const resetGame = useGameStore((s) => s.resetGame); const resetGame = useGameStore((s) => s.resetGame);
const setTime = useCombatStore((s) => s.debugSetTime);
const setFloor = useCombatStore((s) => s.debugSetFloor); const setFloor = useCombatStore((s) => s.debugSetFloor);
const resetHP = useCombatStore((s) => s.resetFloorHP); const resetHP = useCombatStore((s) => s.resetFloorHP);
@@ -184,7 +183,7 @@ export function GameStateDebug() {
Current: Day {day}, Hour {hour} Current: Day {day}, Hour {hour}
</div> </div>
<div className="flex gap-2 flex-wrap"> <div className="flex gap-2 flex-wrap">
<Button size="sm" variant="outline" onClick={() => setTime?.(1, 0)}> <Button size="sm" variant="outline" onClick={() => useGameStore.setState({ day: 1, hour: 0 })}>
Day 1 Day 1
</Button> </Button>
<Button size="sm" variant="outline" onClick={() => setTime?.(10, 0)}> <Button size="sm" variant="outline" onClick={() => setTime?.(10, 0)}>
@@ -13,7 +13,8 @@ export const RAW_SPELLS: Record<string, SpellDef> = {
castSpeed: 3, castSpeed: 3,
unlock: 0, unlock: 0,
studyTime: 0, studyTime: 0,
desc: "A weak bolt of pure mana. Costs raw mana instead of elemental." desc: "A weak bolt of pure mana. Costs raw mana instead of elemental.",
grimoire: true
}, },
manaStrike: { manaStrike: {
name: "Mana Strike", name: "Mana Strike",
@@ -24,6 +25,7 @@ export const RAW_SPELLS: Record<string, SpellDef> = {
castSpeed: 2.5, castSpeed: 2.5,
unlock: 50, unlock: 50,
studyTime: 1, studyTime: 1,
desc: "A concentrated strike of raw mana. Slightly stronger than Mana Bolt." desc: "A concentrated strike of raw mana. Slightly stronger than Mana Bolt.",
grimoire: true
}, },
}; };
+1
View File
@@ -17,6 +17,7 @@ import { useSkillStore } from './skillStore';
import { useGameStore } from './gameStore'; import { useGameStore } from './gameStore';
import { useManaStore } from './manaStore'; import { useManaStore } from './manaStore';
import { useCombatStore } from './combatStore'; import { useCombatStore } from './combatStore';
import { createStartingEquipment } from '../store/crafting-modules/starting-equipment';
import { useUIStore } from './uiStore'; import { useUIStore } from './uiStore';
// Import action modules // Import action modules
+28 -1
View File
@@ -24,6 +24,8 @@ import { usePrestigeStore } from './prestigeStore';
import { useManaStore } from './manaStore'; import { useManaStore } from './manaStore';
import { useSkillStore } from './skillStore'; import { useSkillStore } from './skillStore';
import { useCombatStore, makeInitialSpells } from './combatStore'; import { useCombatStore, makeInitialSpells } from './combatStore';
import { useAttunementStore } from './attunementStore';
import { ATTUNEMENTS_DEF, getAttunementConversionRate } from '../data/attunements';
import { createResetGame, createGatherMana } from './gameActions'; import { createResetGame, createGatherMana } from './gameActions';
import { createStartNewLoop } from './gameLoopActions'; import { createStartNewLoop } from './gameLoopActions';
@@ -146,9 +148,34 @@ export const useGameStore = create<GameCoordinatorStore>()(
// Mana regeneration // Mana regeneration
let rawMana = Math.min(manaState.rawMana + effectiveRegen * HOURS_PER_TICK, maxMana); let rawMana = Math.min(manaState.rawMana + effectiveRegen * HOURS_PER_TICK, maxMana);
let totalManaGathered = manaState.totalManaGathered;
let elements = { ...manaState.elements }; let elements = { ...manaState.elements };
// Apply attunement conversion (raw mana to primary mana types)
const attunementState = useAttunementStore.getState();
Object.entries(attunementState.attunements).forEach(([id, state]) => {
if (!state.active) return;
const def = ATTUNEMENTS_DEF[id];
if (!def || def.conversionRate <= 0 || !def.primaryManaType) return;
const scaledRate = getAttunementConversionRate(id, state.level || 1);
const conversionThisTick = scaledRate * HOURS_PER_TICK; // per tick
// Cap conversion to available raw mana
const actualConversion = Math.min(conversionThisTick, rawMana);
// Subtract from raw mana
rawMana = Math.max(0, rawMana - actualConversion);
// Add to primary mana type
if (elements[def.primaryManaType]) {
elements[def.primaryManaType].current = Math.min(
elements[def.primaryManaType].max,
elements[def.primaryManaType].current + actualConversion
);
}
});
let totalManaGathered = manaState.totalManaGathered;
// Study progress - handled by skillStore // Study progress - handled by skillStore
if (combatState.currentAction === 'study' && skillState.currentStudyTarget) { if (combatState.currentAction === 'study' && skillState.currentStudyTarget) {
const studySpeedMult = getStudySpeedMultiplier(skillState.skills); const studySpeedMult = getStudySpeedMultiplier(skillState.skills);