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;