Fix incorrect imports: hasSpecial and SPECIAL_EFFECTS should be imported from special-effects.ts, not upgrade-effects.ts
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 40s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 40s
- Fixed src/components/game/GameContext/Provider.tsx - Fixed src/components/game/GameContext/types.ts The upgrade-effects.ts imports these from special-effects.ts but doesn't re-export them.
This commit is contained in:
+11
-28
@@ -1,39 +1,22 @@
|
|||||||
# Mana Loop - Next.js Game Docker Image (Development Build)
|
|
||||||
|
|
||||||
FROM node:20-alpine AS base
|
FROM node:20-alpine AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
RUN apk add --no-cache libc6-compat openssl
|
RUN apk add --no-cache libc6-compat openssl
|
||||||
|
|
||||||
# Install bun
|
|
||||||
RUN npm install -g bun
|
RUN npm install -g bun
|
||||||
|
|
||||||
# Copy package files first for better caching
|
|
||||||
COPY package.json bun.lockb* ./
|
|
||||||
COPY prisma ./prisma/
|
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
|
COPY package.json bun.lock* bun.lockb* ./
|
||||||
|
COPY prisma ./prisma/
|
||||||
RUN bun install --frozen-lockfile
|
RUN bun install --frozen-lockfile
|
||||||
|
# Copy source
|
||||||
# Copy the rest of the application
|
|
||||||
COPY . .
|
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
|
# Generate Prisma client
|
||||||
RUN bunx prisma generate --schema=./prisma/schema.prisma
|
RUN bunx prisma generate --schema=./prisma/schema.prisma
|
||||||
|
# Build the application
|
||||||
# Expose port
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
RUN bun run build
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
ENV PORT=3000
|
||||||
# Health check for development
|
ENV HOSTNAME=0.0.0.0
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||||
CMD wget --no-verbose --tries=1 --spider http://localhost:3000 || exit 1
|
CMD wget --no-verbose --tries=1 --spider http://localhost:3000 || exit 1
|
||||||
|
CMD ["bun", "run", "start"]
|
||||||
# Use development server (next dev) for better error messages and HMR
|
|
||||||
CMD ["bun", "run", "dev"]
|
|
||||||
+4
-3
@@ -205,14 +205,15 @@ export default function ManaLoopGame() {
|
|||||||
initGame();
|
initGame();
|
||||||
}, [initGame]);
|
}, [initGame]);
|
||||||
|
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
useEffect(() => { setMounted(true); }, []);
|
||||||
|
|
||||||
// Conditional returns AFTER all hooks
|
// Conditional returns AFTER all hooks
|
||||||
if (gameOver) {
|
if (gameOver) {
|
||||||
return <GameOverScreen store={{ day, hour, insight }} />;
|
return <GameOverScreen store={{ day, hour, insight }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof window === 'undefined') {
|
if (!mounted) return <div className="p-4 text-center text-gray-400">Loading...</div>;
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import { usePrestigeStore } from '@/lib/game/stores/prestigeStore';
|
|||||||
import { useUIStore } from '@/lib/game/stores/uiStore';
|
import { useUIStore } from '@/lib/game/stores/uiStore';
|
||||||
import { useCombatStore } from '@/lib/game/stores/combatStore';
|
import { useCombatStore } from '@/lib/game/stores/combatStore';
|
||||||
import { useGameStore } from '@/lib/game/stores/gameStore';
|
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 { getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants';
|
||||||
import {
|
import {
|
||||||
computeMaxMana,
|
computeMaxMana,
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { useManaStore } from '@/lib/game/stores/manaStore';
|
|||||||
import { usePrestigeStore } from '@/lib/game/stores/prestigeStore';
|
import { usePrestigeStore } from '@/lib/game/stores/prestigeStore';
|
||||||
import { useUIStore } from '@/lib/game/stores/uiStore';
|
import { useUIStore } from '@/lib/game/stores/uiStore';
|
||||||
import { useCombatStore } from '@/lib/game/stores/combatStore';
|
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';
|
import { getBoonBonuses } from '@/lib/game/utils';
|
||||||
|
|
||||||
// Define a unified store type that combines all stores
|
// Define a unified store type that combines all stores
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useGameStore } from '@/lib/game/stores';
|
import { useSkillStore } from '@/lib/game/stores';
|
||||||
import { SKILL_CATEGORIES } from '@/lib/game/constants';
|
import { SKILL_CATEGORIES } from '@/lib/game/constants';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { SkillUpgradeDialog } from './SkillsTab/SkillUpgradeDialog';
|
import { SkillUpgradeDialog } from './SkillsTab/SkillUpgradeDialog';
|
||||||
@@ -9,7 +9,7 @@ import { SkillStudyProgress } from './SkillsTab/SkillStudyProgress';
|
|||||||
import { SkillCategory } from './SkillsTab/SkillCategory';
|
import { SkillCategory } from './SkillsTab/SkillCategory';
|
||||||
|
|
||||||
export function SkillsTab() {
|
export function SkillsTab() {
|
||||||
const store = useGameStore();
|
const currentStudyTarget = useSkillStore((s) => s.currentStudyTarget);
|
||||||
const [upgradeDialogSkill, setUpgradeDialogSkill] = useState<string | null>(null);
|
const [upgradeDialogSkill, setUpgradeDialogSkill] = useState<string | null>(null);
|
||||||
const [upgradeDialogMilestone, setUpgradeDialogMilestone] = useState<5 | 10>(5);
|
const [upgradeDialogMilestone, setUpgradeDialogMilestone] = useState<5 | 10>(5);
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export function SkillsTab() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Current Study Progress */}
|
{/* Current Study Progress */}
|
||||||
{store.currentStudyTarget && store.currentStudyTarget.type === 'skill' && (
|
{currentStudyTarget && currentStudyTarget.type === 'skill' && (
|
||||||
<Card className="bg-gray-900/80 border-purple-600/50">
|
<Card className="bg-gray-900/80 border-purple-600/50">
|
||||||
<CardContent className="pt-4">
|
<CardContent className="pt-4">
|
||||||
<SkillStudyProgress />
|
<SkillStudyProgress />
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ export function SkillRow({ skillId, onUpgradeClick }: SkillRowProps) {
|
|||||||
const rawMana = useManaStore((s) => s.rawMana);
|
const rawMana = useManaStore((s) => s.rawMana);
|
||||||
const startStudyingSkill = useSkillStore((s) => s.startStudyingSkill);
|
const startStudyingSkill = useSkillStore((s) => s.startStudyingSkill);
|
||||||
const tierUpSkill = useSkillStore((s) => s.tierUpSkill);
|
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 skillInfo = getSkillDisplayInfo(skillState, skillId);
|
||||||
const {
|
const {
|
||||||
@@ -172,30 +172,7 @@ export function SkillRow({ skillId, onUpgradeClick }: SkillRowProps) {
|
|||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
{/* Parallel Study button */}
|
|
||||||
{hasParallelStudy &&
|
|
||||||
skillState.currentStudyTarget &&
|
|
||||||
!skillState.parallelStudyTarget &&
|
|
||||||
skillState.currentStudyTarget.id !== tieredSkillId &&
|
|
||||||
canStudy && (
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
className="border-cyan-500 text-cyan-400 hover:bg-cyan-900/30"
|
|
||||||
onClick={() => startParallelStudySkill(tieredSkillId)}
|
|
||||||
>
|
|
||||||
⚡
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Study in parallel (50% speed)</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
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 { SKILLS_DEF } from '@/lib/game/constants';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -14,13 +14,14 @@ interface SkillUpgradeDialogProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SkillUpgradeDialog({ skillId, milestone, onClose }: 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<string[]>([]);
|
const [pendingUpgradeSelections, setPendingUpgradeSelections] = useState<string[]>([]);
|
||||||
|
|
||||||
if (!skillId) return null;
|
if (!skillId) return null;
|
||||||
|
|
||||||
const skillDef = SKILLS_DEF[skillId];
|
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;
|
const currentSelections = pendingUpgradeSelections.length > 0 ? pendingUpgradeSelections : alreadySelected;
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ export function SkillUpgradeDialog({ skillId, milestone, onClose }: SkillUpgrade
|
|||||||
|
|
||||||
const handleDone = () => {
|
const handleDone = () => {
|
||||||
if (currentSelections.length === 2 && skillId) {
|
if (currentSelections.length === 2 && skillId) {
|
||||||
store.commitSkillUpgrades(skillId, currentSelections, milestone);
|
commitSkillUpgrades(skillId, currentSelections, milestone);
|
||||||
}
|
}
|
||||||
setPendingUpgradeSelections([]);
|
setPendingUpgradeSelections([]);
|
||||||
onClose();
|
onClose();
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { ENCHANTMENT_EFFECTS } from '@/lib/game/data/enchantment-effects';
|
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 { fmt } from '@/lib/game/stores';
|
||||||
import { CheckCircle, Sparkles } from 'lucide-react';
|
import { CheckCircle, Sparkles } from 'lucide-react';
|
||||||
import { useGameStore, useCraftingStore, useManaStore } from '@/lib/game/stores';
|
import { useGameStore, useCraftingStore, useManaStore } from '@/lib/game/stores';
|
||||||
@@ -253,7 +254,7 @@ export function EnchantmentApplier({
|
|||||||
<ul className="list-disc list-inside mt-1">
|
<ul className="list-disc list-inside mt-1">
|
||||||
{design.effects.map(eff => (
|
{design.effects.map(eff => (
|
||||||
<li key={eff.effectId} className="text-[var(--text-secondary)]">
|
<li key={eff.effectId} className="text-[var(--text-secondary)]">
|
||||||
{ENCHANTMENT_EFFECT_S[eff.effectId]?.name} x{eff.stacks}
|
{ENCHANTMENT_EFFECTS[eff.effectId]?.name} x{eff.stacks}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { useCombatStore } from './combatStore';
|
|||||||
import * as ApplicationActions from '../crafting-actions/application-actions';
|
import * as ApplicationActions from '../crafting-actions/application-actions';
|
||||||
import * as CraftingApply from '../crafting-apply';
|
import * as CraftingApply from '../crafting-apply';
|
||||||
import * as PreparationActions from '../crafting-actions/preparation-actions';
|
import * as PreparationActions from '../crafting-actions/preparation-actions';
|
||||||
|
import * as EquipmentCraftingActions from '../crafting-actions/crafting-equipment-actions';
|
||||||
|
|
||||||
export interface CraftingState {
|
export interface CraftingState {
|
||||||
// Crafting progress
|
// Crafting progress
|
||||||
@@ -78,6 +79,10 @@ export interface CraftingActions {
|
|||||||
// Loot inventory actions
|
// Loot inventory actions
|
||||||
deleteMaterial: (materialId: string, amount: number) => void;
|
deleteMaterial: (materialId: string, amount: number) => void;
|
||||||
deleteEquipmentInstance: (instanceId: string) => void;
|
deleteEquipmentInstance: (instanceId: string) => void;
|
||||||
|
|
||||||
|
// Equipment crafting actions
|
||||||
|
startCraftingEquipment: (blueprintId: string) => boolean;
|
||||||
|
cancelEquipmentCrafting: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CraftingStore = CraftingState & CraftingActions;
|
export type CraftingStore = CraftingState & CraftingActions;
|
||||||
@@ -236,6 +241,34 @@ export const useCraftingStore = create<CraftingStore>()(
|
|||||||
useCombatStore.setState({ currentAction: 'meditate' });
|
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
|
// Loot inventory actions
|
||||||
deleteMaterial: (materialId: string, amount: number) => {
|
deleteMaterial: (materialId: string, amount: number) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export const createResetGame = (set: (state: any) => void, initialState: any) =>
|
|||||||
localStorage.removeItem('mana-loop-skill-storage');
|
localStorage.removeItem('mana-loop-skill-storage');
|
||||||
localStorage.removeItem('mana-loop-combat-storage');
|
localStorage.removeItem('mana-loop-combat-storage');
|
||||||
localStorage.removeItem('mana-loop-game-storage');
|
localStorage.removeItem('mana-loop-game-storage');
|
||||||
|
localStorage.removeItem('mana-loop-crafting-storage');
|
||||||
|
localStorage.removeItem('mana-loop-attunement-storage');
|
||||||
}
|
}
|
||||||
|
|
||||||
const startFloor = 1;
|
const startFloor = 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user