diff --git a/Dockerfile b/Dockerfile
index 8dc72fe..884312e 100755
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,39 +1,22 @@
-# Mana Loop - Next.js Game Docker Image (Development Build)
-
FROM node:20-alpine AS base
WORKDIR /app
-
-# Install dependencies
RUN apk add --no-cache libc6-compat openssl
-
-# Install bun
RUN npm install -g bun
-
-# Copy package files first for better caching
-COPY package.json bun.lockb* ./
-COPY prisma ./prisma/
-
# Install dependencies
+COPY package.json bun.lock* bun.lockb* ./
+COPY prisma ./prisma/
RUN bun install --frozen-lockfile
-
-# Copy the rest of the application
+# Copy source
COPY . .
-
-# Development environment variables (no production optimizations)
-ENV NODE_ENV=development
-ENV NEXT_TELEMETRY_DISABLED=1
-ENV DATABASE_URL="file:./dev.db"
-ENV NEXT_DEV_MODE=true
-
# Generate Prisma client
RUN bunx prisma generate --schema=./prisma/schema.prisma
-
-# Expose port
+# Build the application
+ENV NODE_ENV=production
+ENV NEXT_TELEMETRY_DISABLED=1
+RUN bun run build
EXPOSE 3000
-
-# Health check for development
-HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+ENV PORT=3000
+ENV HOSTNAME=0.0.0.0
+HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000 || exit 1
-
-# Use development server (next dev) for better error messages and HMR
-CMD ["bun", "run", "dev"]
+CMD ["bun", "run", "start"]
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 3b8f02d..6a4c214 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -205,14 +205,15 @@ export default function ManaLoopGame() {
initGame();
}, [initGame]);
+ const [mounted, setMounted] = useState(false);
+ useEffect(() => { setMounted(true); }, []);
+
// Conditional returns AFTER all hooks
if (gameOver) {
return ;
}
- if (typeof window === 'undefined') {
- return
Loading...
;
- }
+ if (!mounted) return Loading...
;
return (
diff --git a/src/components/game/GameContext/Provider.tsx b/src/components/game/GameContext/Provider.tsx
index cf8623e..03975e6 100644
--- a/src/components/game/GameContext/Provider.tsx
+++ b/src/components/game/GameContext/Provider.tsx
@@ -7,7 +7,8 @@ import { usePrestigeStore } from '@/lib/game/stores/prestigeStore';
import { useUIStore } from '@/lib/game/stores/uiStore';
import { useCombatStore } from '@/lib/game/stores/combatStore';
import { useGameStore } from '@/lib/game/stores/gameStore';
-import { computeEffects, hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/upgrade-effects';
+import { computeEffects } from '@/lib/game/upgrade-effects';
+import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/special-effects';
import { getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants';
import {
computeMaxMana,
diff --git a/src/components/game/GameContext/types.ts b/src/components/game/GameContext/types.ts
index 63d0bb2..e80550c 100644
--- a/src/components/game/GameContext/types.ts
+++ b/src/components/game/GameContext/types.ts
@@ -4,7 +4,8 @@ import { useManaStore } from '@/lib/game/stores/manaStore';
import { usePrestigeStore } from '@/lib/game/stores/prestigeStore';
import { useUIStore } from '@/lib/game/stores/uiStore';
import { useCombatStore } from '@/lib/game/stores/combatStore';
-import { computeEffects, hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/upgrade-effects';
+import { computeEffects } from '@/lib/game/upgrade-effects';
+import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/special-effects';
import { getBoonBonuses } from '@/lib/game/utils';
// Define a unified store type that combines all stores
diff --git a/src/components/game/SkillsTab.tsx b/src/components/game/SkillsTab.tsx
index 5d59b46..55b3026 100755
--- a/src/components/game/SkillsTab.tsx
+++ b/src/components/game/SkillsTab.tsx
@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
-import { useGameStore } from '@/lib/game/stores';
+import { useSkillStore } from '@/lib/game/stores';
import { SKILL_CATEGORIES } from '@/lib/game/constants';
import { Card, CardContent } from '@/components/ui/card';
import { SkillUpgradeDialog } from './SkillsTab/SkillUpgradeDialog';
@@ -9,7 +9,7 @@ import { SkillStudyProgress } from './SkillsTab/SkillStudyProgress';
import { SkillCategory } from './SkillsTab/SkillCategory';
export function SkillsTab() {
- const store = useGameStore();
+ const currentStudyTarget = useSkillStore((s) => s.currentStudyTarget);
const [upgradeDialogSkill, setUpgradeDialogSkill] = useState(null);
const [upgradeDialogMilestone, setUpgradeDialogMilestone] = useState<5 | 10>(5);
@@ -32,7 +32,7 @@ export function SkillsTab() {
/>
{/* Current Study Progress */}
- {store.currentStudyTarget && store.currentStudyTarget.type === 'skill' && (
+ {currentStudyTarget && currentStudyTarget.type === 'skill' && (
diff --git a/src/components/game/SkillsTab/SkillRow.tsx b/src/components/game/SkillsTab/SkillRow.tsx
index 50eff24..9536ea8 100644
--- a/src/components/game/SkillsTab/SkillRow.tsx
+++ b/src/components/game/SkillsTab/SkillRow.tsx
@@ -20,9 +20,9 @@ export function SkillRow({ skillId, onUpgradeClick }: SkillRowProps) {
const rawMana = useManaStore((s) => s.rawMana);
const startStudyingSkill = useSkillStore((s) => s.startStudyingSkill);
const tierUpSkill = useSkillStore((s) => s.tierUpSkill);
- const startParallelStudySkill = useSkillStore((s) => s.startParallelStudySkill);
+
- const { studySpeedMult, studyCostMult, hasParallelStudy } = useStudyStats();
+ const { studySpeedMult, studyCostMult } = useStudyStats();
const skillInfo = getSkillDisplayInfo(skillState, skillId);
const {
@@ -172,30 +172,7 @@ export function SkillRow({ skillId, onUpgradeClick }: SkillRowProps) {
)}
- {/* Parallel Study button */}
- {hasParallelStudy &&
- skillState.currentStudyTarget &&
- !skillState.parallelStudyTarget &&
- skillState.currentStudyTarget.id !== tieredSkillId &&
- canStudy && (
-
-
-
-
-
-
- Study in parallel (50% speed)
-
-
-
- )}
+
)}
diff --git a/src/components/game/SkillsTab/SkillUpgradeDialog.tsx b/src/components/game/SkillsTab/SkillUpgradeDialog.tsx
index a020c67..ec4d9b6 100644
--- a/src/components/game/SkillsTab/SkillUpgradeDialog.tsx
+++ b/src/components/game/SkillsTab/SkillUpgradeDialog.tsx
@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
-import { useGameStore, fmt } from '@/lib/game/stores';
+import { useSkillStore, fmt } from '@/lib/game/stores';
import { SKILLS_DEF } from '@/lib/game/constants';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
@@ -14,13 +14,14 @@ interface SkillUpgradeDialogProps {
}
export function SkillUpgradeDialog({ skillId, milestone, onClose }: SkillUpgradeDialogProps) {
- const store = useGameStore();
+ const getSkillUpgradeChoices = useSkillStore((s) => s.getSkillUpgradeChoices);
+ const commitSkillUpgrades = useSkillStore((s) => s.commitSkillUpgrades);
const [pendingUpgradeSelections, setPendingUpgradeSelections] = useState([]);
if (!skillId) return null;
const skillDef = SKILLS_DEF[skillId];
- const { available, selected: alreadySelected } = store.getSkillUpgradeChoices(skillId, milestone);
+ const { available, selected: alreadySelected } = getSkillUpgradeChoices(skillId, milestone);
const currentSelections = pendingUpgradeSelections.length > 0 ? pendingUpgradeSelections : alreadySelected;
@@ -34,7 +35,7 @@ export function SkillUpgradeDialog({ skillId, milestone, onClose }: SkillUpgrade
const handleDone = () => {
if (currentSelections.length === 2 && skillId) {
- store.commitSkillUpgrades(skillId, currentSelections, milestone);
+ commitSkillUpgrades(skillId, currentSelections, milestone);
}
setPendingUpgradeSelections([]);
onClose();
diff --git a/src/components/game/crafting/EnchantmentApplier.tsx b/src/components/game/crafting/EnchantmentApplier.tsx
index 552e852..32bd836 100644
--- a/src/components/game/crafting/EnchantmentApplier.tsx
+++ b/src/components/game/crafting/EnchantmentApplier.tsx
@@ -9,7 +9,8 @@ import { Badge } from '@/components/ui/badge';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Separator } from '@/components/ui/separator';
import { ENCHANTMENT_EFFECTS } from '@/lib/game/data/enchantment-effects';
-import type { EquipmentInstance, EnchantmentDesign, AppliedEnchantment, LootInventory, EquipmentCraftingProgress, EquipmentSlot } from '@/lib/game/types';
+import type { EquipmentInstance, EnchantmentDesign, AppliedEnchantment, LootInventory, EquipmentCraftingProgress } from '@/lib/game/types';
+import type { EquipmentSlot } from '@/lib/game/data/equipment';
import { fmt } from '@/lib/game/stores';
import { CheckCircle, Sparkles } from 'lucide-react';
import { useGameStore, useCraftingStore, useManaStore } from '@/lib/game/stores';
@@ -253,7 +254,7 @@ export function EnchantmentApplier({
{design.effects.map(eff => (
-
- {ENCHANTMENT_EFFECT_S[eff.effectId]?.name} x{eff.stacks}
+ {ENCHANTMENT_EFFECTS[eff.effectId]?.name} x{eff.stacks}
))}
diff --git a/src/lib/game/stores/craftingStore.ts b/src/lib/game/stores/craftingStore.ts
index 689411d..a2aff4f 100644
--- a/src/lib/game/stores/craftingStore.ts
+++ b/src/lib/game/stores/craftingStore.ts
@@ -21,6 +21,7 @@ import { useCombatStore } from './combatStore';
import * as ApplicationActions from '../crafting-actions/application-actions';
import * as CraftingApply from '../crafting-apply';
import * as PreparationActions from '../crafting-actions/preparation-actions';
+import * as EquipmentCraftingActions from '../crafting-actions/crafting-equipment-actions';
export interface CraftingState {
// Crafting progress
@@ -78,6 +79,10 @@ export interface CraftingActions {
// Loot inventory actions
deleteMaterial: (materialId: string, amount: number) => void;
deleteEquipmentInstance: (instanceId: string) => void;
+
+ // Equipment crafting actions
+ startCraftingEquipment: (blueprintId: string) => boolean;
+ cancelEquipmentCrafting: () => void;
}
export type CraftingStore = CraftingState & CraftingActions;
@@ -236,6 +241,34 @@ export const useCraftingStore = create()(
useCombatStore.setState({ currentAction: 'meditate' });
},
+ // Equipment crafting actions
+ startCraftingEquipment: (blueprintId: string) => {
+ // Get state needed for equipment crafting
+ const rawMana = useManaStore.getState().rawMana;
+ const currentAction = useCombatStore.getState().currentAction;
+ const lootInventory = get().lootInventory;
+ // Create a temporary state object with all required fields
+ const tempState = {
+ ...get(),
+ rawMana,
+ currentAction,
+ lootInventory,
+ } as any;
+ return EquipmentCraftingActions.startCraftingEquipment(
+ blueprintId,
+ () => tempState,
+ set
+ );
+ },
+
+ cancelEquipmentCrafting: () => {
+ EquipmentCraftingActions.cancelEquipmentCrafting(
+ get,
+ set
+ );
+ useCombatStore.setState({ currentAction: 'meditate' });
+ },
+
// Loot inventory actions
deleteMaterial: (materialId: string, amount: number) => {
set((state) => {
diff --git a/src/lib/game/stores/gameActions.ts b/src/lib/game/stores/gameActions.ts
index a29141a..6aa01d8 100644
--- a/src/lib/game/stores/gameActions.ts
+++ b/src/lib/game/stores/gameActions.ts
@@ -15,6 +15,8 @@ export const createResetGame = (set: (state: any) => void, initialState: any) =>
localStorage.removeItem('mana-loop-skill-storage');
localStorage.removeItem('mana-loop-combat-storage');
localStorage.removeItem('mana-loop-game-storage');
+ localStorage.removeItem('mana-loop-crafting-storage');
+ localStorage.removeItem('mana-loop-attunement-storage');
}
const startFloor = 1;