2025-03-25 12:04:20 +00:00
|
|
|
import { useMemo, useState } from 'react';
|
|
|
|
import { useSelectedActionSphere, useToggleView, useSimulationPaths, useSelectedPath, useStartSimulation } from '../../store/store';
|
|
|
|
import * as THREE from 'three';
|
|
|
|
import useModuleStore from '../../store/useModuleStore';
|
|
|
|
|
|
|
|
function SimulationUI() {
|
|
|
|
const { ToggleView } = useToggleView();
|
|
|
|
const { activeModule } = useModuleStore();
|
|
|
|
const { startSimulation, setStartSimulation } = useStartSimulation();
|
|
|
|
const { selectedActionSphere } = useSelectedActionSphere();
|
|
|
|
const { selectedPath, setSelectedPath } = useSelectedPath();
|
|
|
|
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
|
|
|
|
|
|
|
|
const handleAddAction = () => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
2025-03-26 13:03:51 +00:00
|
|
|
points: path.points.map((point) => {
|
|
|
|
if (point.uuid === selectedActionSphere.point.uuid) {
|
|
|
|
const actionIndex = point.actions.length;
|
|
|
|
const newAction = {
|
|
|
|
uuid: THREE.MathUtils.generateUUID(),
|
|
|
|
name: `Action ${actionIndex + 1}`, // Assign action name based on index
|
|
|
|
type: 'Inherit',
|
|
|
|
material: 'Inherit',
|
|
|
|
delay: 'Inherit',
|
|
|
|
spawnInterval: 'Inherit',
|
|
|
|
isUsed: false
|
|
|
|
};
|
|
|
|
|
|
|
|
return { ...point, actions: [...point.actions, newAction] };
|
|
|
|
}
|
|
|
|
return point;
|
|
|
|
}),
|
2025-03-25 12:04:20 +00:00
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleDeleteAction = (uuid: string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleActionSelect = (uuid: string, actionType: string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? {
|
|
|
|
...point,
|
|
|
|
actions: point.actions.map((action) =>
|
|
|
|
action.uuid === uuid ? { ...action, type: actionType } : action
|
|
|
|
),
|
|
|
|
}
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleMaterialSelect = (uuid: string, material: string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? {
|
|
|
|
...point,
|
|
|
|
actions: point.actions.map((action) =>
|
|
|
|
action.uuid === uuid ? { ...action, material } : action
|
|
|
|
),
|
|
|
|
}
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleDelayChange = (uuid: string, delay: number | string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? {
|
|
|
|
...point,
|
|
|
|
actions: point.actions.map((action) =>
|
|
|
|
action.uuid === uuid ? { ...action, delay } : action
|
|
|
|
),
|
|
|
|
}
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? {
|
|
|
|
...point,
|
|
|
|
actions: point.actions.map((action) =>
|
|
|
|
action.uuid === uuid ? { ...action, spawnInterval } : action
|
|
|
|
),
|
|
|
|
}
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleSpeedChange = (speed: number) => {
|
|
|
|
if (!selectedPath) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) =>
|
|
|
|
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
|
|
|
|
);
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleAddTrigger = () => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
2025-03-26 13:03:51 +00:00
|
|
|
points: path.points.map((point) => {
|
|
|
|
if (point.uuid === selectedActionSphere.point.uuid) {
|
|
|
|
const triggerIndex = point.triggers.length;
|
|
|
|
const newTrigger = {
|
|
|
|
uuid: THREE.MathUtils.generateUUID(),
|
|
|
|
name: `Trigger ${triggerIndex + 1}`, // Assign name based on index
|
|
|
|
type: '',
|
|
|
|
isUsed: false
|
|
|
|
};
|
|
|
|
|
|
|
|
return { ...point, triggers: [...point.triggers, newTrigger] };
|
|
|
|
}
|
|
|
|
return point;
|
|
|
|
}),
|
2025-03-25 12:04:20 +00:00
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleDeleteTrigger = (uuid: string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleTriggerSelect = (uuid: string, triggerType: string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? {
|
|
|
|
...point,
|
|
|
|
triggers: point.triggers.map((trigger) =>
|
|
|
|
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
|
|
|
|
),
|
|
|
|
}
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleResetPath = () => {
|
|
|
|
if (!selectedPath) return;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleActionToggle = (uuid: string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? {
|
|
|
|
...point,
|
|
|
|
actions: point.actions.map((action) => ({
|
|
|
|
...action,
|
|
|
|
isUsed: action.uuid === uuid ? !action.isUsed : false,
|
|
|
|
})),
|
|
|
|
}
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleTriggerToggle = (uuid: string) => {
|
|
|
|
if (!selectedActionSphere) return;
|
|
|
|
|
|
|
|
const updatedPaths = simulationPaths.map((path) => ({
|
|
|
|
...path,
|
|
|
|
points: path.points.map((point) =>
|
|
|
|
point.uuid === selectedActionSphere.point.uuid
|
|
|
|
? {
|
|
|
|
...point,
|
|
|
|
triggers: point.triggers.map((trigger) => ({
|
|
|
|
...trigger,
|
|
|
|
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
|
|
|
|
})),
|
|
|
|
}
|
|
|
|
: point
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setSimulationPaths(updatedPaths);
|
|
|
|
};
|
|
|
|
|
|
|
|
const selectedPoint = useMemo(() => {
|
|
|
|
if (!selectedActionSphere) return null;
|
|
|
|
return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid);
|
|
|
|
}, [selectedActionSphere, simulationPaths]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
{activeModule === "simulation" && (
|
|
|
|
<div style={{ zIndex: 10, position: "relative", width: '260px' }}>
|
|
|
|
{!ToggleView && (
|
|
|
|
<>
|
|
|
|
<button
|
|
|
|
onClick={() => setStartSimulation(!startSimulation)}
|
|
|
|
style={{
|
|
|
|
marginTop: "10px",
|
|
|
|
background: startSimulation ? '#ff320e' : '',
|
|
|
|
padding: "10px",
|
|
|
|
borderRadius: "5px"
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{startSimulation ? 'Stop Simulation' : 'Start Simulation'}
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{selectedPath && (
|
|
|
|
<div style={{ marginTop: "10px" }}>
|
|
|
|
<label>Path Speed:</label>
|
|
|
|
<input
|
|
|
|
style={{ width: '50px' }}
|
|
|
|
type="number"
|
|
|
|
value={selectedPath.path.speed}
|
|
|
|
min="0.1"
|
|
|
|
step="0.1"
|
|
|
|
onChange={(e) => handleSpeedChange(parseFloat(e.target.value))}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{selectedActionSphere && (
|
|
|
|
<div style={{ marginTop: "10px" }}>
|
|
|
|
<button onClick={handleAddAction}>Add Action</button>
|
|
|
|
<button onClick={handleAddTrigger}>Add Trigger</button>
|
|
|
|
|
|
|
|
{selectedPoint?.actions.map((action) => (
|
|
|
|
<div key={action.uuid} style={{ marginTop: "10px" }}>
|
|
|
|
<select value={action.type} onChange={(e) => handleActionSelect(action.uuid, e.target.value)}>
|
|
|
|
<option value="Inherit">Inherit</option>
|
|
|
|
<option value="Spawn">Spawn Point</option>
|
|
|
|
<option value="Swap">Swap Material</option>
|
|
|
|
<option value="Despawn">Despawn Point</option>
|
|
|
|
<option value="Delay">Delay</option>
|
|
|
|
</select>
|
|
|
|
<button onClick={() => handleDeleteAction(action.uuid)}>Delete Action</button>
|
|
|
|
<label>
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={action.isUsed}
|
|
|
|
onChange={() => handleActionToggle(action.uuid)}
|
|
|
|
/>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
{(action.type === 'Spawn' || action.type === 'Swap') && (
|
|
|
|
<div style={{ marginTop: "10px" }}>
|
|
|
|
<select value={action.material} onChange={(e) => handleMaterialSelect(action.uuid, e.target.value)}>
|
|
|
|
<option value="Inherit">Inherit</option>
|
|
|
|
<option value="Crate">Crate</option>
|
|
|
|
<option value="Box">Box</option>
|
|
|
|
</select>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{action.type === 'Delay' && (
|
|
|
|
<div style={{ marginTop: "10px" }}>
|
|
|
|
<label>Delay Time:</label>
|
|
|
|
<input
|
|
|
|
style={{ width: '50px' }}
|
|
|
|
type="text"
|
|
|
|
value={isNaN(Number(action.delay)) || action.delay === "Inherit" ? "Inherit" : action.delay}
|
|
|
|
min="1"
|
|
|
|
onChange={(e) => handleDelayChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{action.type === 'Spawn' && (
|
|
|
|
<div style={{ marginTop: "10px" }}>
|
|
|
|
<label>Spawn Interval:</label>
|
|
|
|
<input
|
|
|
|
style={{ width: '50px' }}
|
|
|
|
type="text"
|
|
|
|
value={isNaN(Number(action.spawnInterval)) || action.spawnInterval === "Inherit" ? "Inherit" : action.spawnInterval}
|
|
|
|
min="1"
|
|
|
|
onChange={(e) => handleSpawnIntervalChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
<hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
|
|
|
|
<hr style={{ margin: "10px 0", border: "1px solid black" }} />
|
|
|
|
|
|
|
|
{selectedPoint?.triggers.map((trigger) => (
|
|
|
|
<div key={trigger.uuid} style={{ marginTop: "10px" }}>
|
|
|
|
<select value={trigger.type} onChange={(e) => handleTriggerSelect(trigger.uuid, e.target.value)}>
|
|
|
|
<option value="">Select Trigger Type</option>
|
|
|
|
<option value="On-Hit">On Hit</option>
|
|
|
|
<option value="Buffer">Buffer</option>
|
|
|
|
</select>
|
|
|
|
<button onClick={() => handleDeleteTrigger(trigger.uuid)}>Delete Trigger</button>
|
|
|
|
<label>
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={trigger.isUsed}
|
|
|
|
onChange={() => handleTriggerToggle(trigger.uuid)}
|
|
|
|
/>
|
|
|
|
</label>
|
|
|
|
<hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{selectedPath && (
|
|
|
|
<div style={{ marginTop: "10px" }}>
|
|
|
|
<button
|
|
|
|
onClick={handleResetPath}
|
|
|
|
style={{ padding: "10px", borderRadius: "5px", background: "#ff0000", color: "#fff" }}
|
|
|
|
>
|
|
|
|
Reset Path
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-03-25 11:05:54 +00:00
|
|
|
export default SimulationUI;
|