|
|
@@ -1,5 +1,4 @@
|
|
|
-import { useState } from "react";
|
|
|
-import InputConfigurator from "./InputConfigurator";
|
|
|
+import { useMemo, useState } from "react";
|
|
|
import ModuleSlots from "./ModuleSlots";
|
|
|
import styles from "./ClockWizard.module.css"; // (Create a simple flex-col layout for this)
|
|
|
import { useClockStore } from "../../store/useClockStore";
|
|
|
@@ -10,92 +9,130 @@ import type {
|
|
|
} from "../../../scripts/factorio-dump/process-data.models";
|
|
|
import {
|
|
|
calculateOptimalBatch,
|
|
|
+ computeMachineStats,
|
|
|
generateAdvancedClock,
|
|
|
} from "../../engine/factorioEngine";
|
|
|
import MachineSelector from "../MachineSelector";
|
|
|
+import BeaconConfigurator, { type BeaconGroup } from "./BeaconConfigurator";
|
|
|
|
|
|
export default function ClockWizard() {
|
|
|
const loadState = useClockStore((s) => s.loadState);
|
|
|
|
|
|
const [recipe, setRecipe] = useState<Recipe | null>(null);
|
|
|
const [machine, setMachine] = useState<Machine | null>(null);
|
|
|
-
|
|
|
- // Track inputs and modules
|
|
|
+ const [machineQuality, setMachineQuality] = useState<number>(0);
|
|
|
+
|
|
|
+ const [machineModules, setMachineModules] = useState<{ module: Module, qualityLevel: number }[]>([]);
|
|
|
+ const [beaconGroups, setBeaconGroups] = useState<BeaconGroup[]>([]);
|
|
|
const [inputConfigs, setInputConfigs] = useState<Record<string, any>>({});
|
|
|
- const [machineModules, setMachineModules] = useState<
|
|
|
- { module: Module; qualityLevel: number }[]
|
|
|
- >([]);
|
|
|
|
|
|
- // Called when the user clicks a recipe/machine in your existing MachineSelector
|
|
|
- const handleMachineSelect = (
|
|
|
- selectedMachine: Machine,
|
|
|
- selectedRecipe: Recipe,
|
|
|
- ) => {
|
|
|
- setMachine(selectedMachine);
|
|
|
- setRecipe(selectedRecipe);
|
|
|
- };
|
|
|
+ // --- LIVE STATS CALCULATION ---
|
|
|
+ const stats = useMemo(() => {
|
|
|
+ if (!machine || !recipe) return null;
|
|
|
+ return computeMachineStats({
|
|
|
+ machine,
|
|
|
+ machineQualityLevel: machineQuality,
|
|
|
+ machineModules: machineModules,
|
|
|
+ beacons: beaconGroups.map(bg => ({
|
|
|
+ beacon: bg.beacon,
|
|
|
+ beaconQualityLevel: bg.qualityLevel,
|
|
|
+ count: bg.count,
|
|
|
+ modules: bg.modules
|
|
|
+ }))
|
|
|
+ }, recipe);
|
|
|
+ }, [machine, recipe, machineQuality, machineModules, beaconGroups]);
|
|
|
+
|
|
|
|
|
|
const handleGenerate = () => {
|
|
|
- if (!recipe || !machine) return;
|
|
|
+ if (!recipe || !machine || !stats) return;
|
|
|
|
|
|
- // Run the math engine (assuming basic timings for now, you can hook up the exact module math here)
|
|
|
- const batch = calculateOptimalBatch(
|
|
|
- recipe,
|
|
|
- 0, // productivity bonus (calculate from machineModules)
|
|
|
- machine.crafting_speed, // actual speed (calculate from machineModules)
|
|
|
- 16,
|
|
|
- );
|
|
|
+ // 1. Math
|
|
|
+ const batch = calculateOptimalBatch(recipe, stats, 16);
|
|
|
|
|
|
- // Generate the timeline blocks
|
|
|
+ // 2. Blueprint / Timeline Blocks
|
|
|
const clockData = generateAdvancedClock(batch, {
|
|
|
stackSize: 16,
|
|
|
- //inputConfigs // Pass the user's mixed-belt config to the generator
|
|
|
+ //inputConfigs // (Assuming inputConfigs logic is mapped inside generator)
|
|
|
});
|
|
|
|
|
|
- // 3. Inject it straight into the Zustand store to update the UI
|
|
|
+ // 3. Update Store
|
|
|
loadState({
|
|
|
duration: clockData.duration,
|
|
|
- rows: Object.fromEntries(clockData.rows.map((r) => [r.id, r])),
|
|
|
- rowOrder: clockData.rows.map((r) => r.id),
|
|
|
- blocks: Object.fromEntries(clockData.blocks.map((b) => [b.id, b])),
|
|
|
- selectedBlockIds: new Set(),
|
|
|
+ rows: Object.fromEntries(clockData.rows.map(r => [r.id, r])),
|
|
|
+ rowOrder: clockData.rows.map(r => r.id),
|
|
|
+ blocks: Object.fromEntries(clockData.blocks.map(b => [b.id, b])),
|
|
|
+ selectedBlockIds: new Set()
|
|
|
});
|
|
|
};
|
|
|
|
|
|
return (
|
|
|
- <div className={styles.wrap}>
|
|
|
- <h2>1. Choose Recipe & Machine</h2>
|
|
|
- <MachineSelector
|
|
|
- onChange={(m) => {
|
|
|
- handleMachineSelect(m.machine,m.recipe);
|
|
|
- }}
|
|
|
- // You'll need to expose the selected recipe from MachineSelector or lift its state up
|
|
|
- />
|
|
|
+ <div className={styles.wrap} style={{ display: "flex", flexDirection: "column", gap: "24px", padding: "20px", background: "#313031", color: "#ffe6c0", border: "1px solid #646464", borderRadius: "8px" }}>
|
|
|
+
|
|
|
+ {/* 1. Recipe & Machine */}
|
|
|
+ <div style={{ display: "flex", gap: "20px", alignItems: "flex-start" }}>
|
|
|
+ <div style={{ flex: 1 }}>
|
|
|
+ <h2 style={{ fontSize: "16px", color: "#f1be64", margin: "0 0 12px 0" }}>1. Setup Machine</h2>
|
|
|
+ <MachineSelector
|
|
|
+ onChange={(res) => {
|
|
|
+ setMachine(res.machine);
|
|
|
+ setMachineQuality(res.qualityLevel);
|
|
|
+ setRecipe(res.recipe);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
|
|
|
- {machine && recipe && (
|
|
|
- <>
|
|
|
- <div className={styles.section}>
|
|
|
- <h2>2. Machine Modules</h2>
|
|
|
- <ModuleSlots
|
|
|
- maxSlots={machine.module_slots || 0}
|
|
|
- allowedEffects={machine.allowed_effects as string[]}
|
|
|
- onChange={setMachineModules}
|
|
|
- />
|
|
|
+ {/* --- STATS PREVIEW DASHBOARD --- */}
|
|
|
+ {stats && (
|
|
|
+ <div style={{ flex: 1, background: "#1a1a1a", border: "1px dashed #f1be64", borderRadius: "6px", padding: "12px", display: "grid", gridTemplateColumns: "1fr 1fr", gap: "10px" }}>
|
|
|
+ <div style={{ gridColumn: "span 2" }}>
|
|
|
+ <h3 style={{ margin: 0, fontSize: "12px", color: "#999", textTransform: "uppercase" }}>Live Capabilities</h3>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div style={{ fontSize: "11px", color: "#999" }}>Crafting Speed</div>
|
|
|
+ <div style={{ fontSize: "18px", color: "#7fc98a", fontWeight: "bold" }}>{stats.actualCraftingSpeed.toFixed(2)}</div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div style={{ fontSize: "11px", color: "#999" }}>Productivity</div>
|
|
|
+ <div style={{ fontSize: "18px", color: "#7fc98a", fontWeight: "bold" }}>+{Math.round(stats.productivityBonus * 100)}%</div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div style={{ fontSize: "11px", color: "#999" }}>Craft Time (Ticks)</div>
|
|
|
+ <div style={{ fontSize: "14px", color: "#ffe6c0" }}>{stats.singleCraftTicks.toFixed(1)}t</div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div style={{ fontSize: "11px", color: "#999" }}>Overload Limit (Multiplier)</div>
|
|
|
+ <div style={{ fontSize: "14px", color: "#ffe6c0" }}>{stats.overloadMultiplier}x</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
|
|
|
- <div className={styles.section}>
|
|
|
- <h2>3. Configure Inputs (Belts vs Chests)</h2>
|
|
|
- <InputConfigurator
|
|
|
- recipe={recipe}
|
|
|
- onConfigChange={setInputConfigs} // Assuming you pass the config up
|
|
|
- />
|
|
|
+ {machine && recipe && (
|
|
|
+ <>
|
|
|
+ {/* 2. Modules & Beacons */}
|
|
|
+ <div style={{ display: "flex", gap: "40px" }}>
|
|
|
+ <div style={{ flex: 1 }}>
|
|
|
+ <h2 style={{ fontSize: "14px", color: "#f1be64", margin: "0 0 12px 0" }}>2. Machine Modules</h2>
|
|
|
+ <ModuleSlots
|
|
|
+ maxSlots={machine.module_slots || 0}
|
|
|
+ allowedEffects={machine.allowed_effects as string[]}
|
|
|
+ onChange={setMachineModules}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div style={{ flex: 2 }}>
|
|
|
+ <h2 style={{ fontSize: "14px", color: "#f1be64", margin: "0 0 12px 0" }}>3. Beacons</h2>
|
|
|
+ <BeaconConfigurator groups={beaconGroups} onChange={setBeaconGroups} />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
- <button className={styles.generateBtn} onClick={handleGenerate}>
|
|
|
- Generate Optimal Timeline
|
|
|
+ <button
|
|
|
+ onClick={handleGenerate}
|
|
|
+ style={{ padding: "10px 16px", background: "#f1be64", color: "#1a1300", fontWeight: "bold", border: "none", borderRadius: "4px", cursor: "pointer", alignSelf: "flex-start" }}
|
|
|
+ >
|
|
|
+ Generate Optimized Timeline
|
|
|
</button>
|
|
|
</>
|
|
|
)}
|
|
|
</div>
|
|
|
);
|
|
|
-}
|
|
|
+}
|