Everything is a bit better
This commit is contained in:
114
src/App.tsx
114
src/App.tsx
@ -3,20 +3,22 @@ import { useStore } from '@nanostores/react'
|
|||||||
import { Square, Archive, Dices } from 'lucide-react'
|
import { Square, Archive, Dices } from 'lucide-react'
|
||||||
import { PlaybackManager } from './services/PlaybackManager'
|
import { PlaybackManager } from './services/PlaybackManager'
|
||||||
import { DownloadService } from './services/DownloadService'
|
import { DownloadService } from './services/DownloadService'
|
||||||
import { generateFormulaGrid, generateRandomFormula } from './utils/bytebeatFormulas'
|
import { generateTileGrid, generateRandomFormula } from './utils/bytebeatFormulas'
|
||||||
import { BytebeatTile } from './components/BytebeatTile'
|
import { BytebeatTile } from './components/BytebeatTile'
|
||||||
import { EffectsBar } from './components/EffectsBar'
|
import { EffectsBar } from './components/EffectsBar'
|
||||||
import { EngineControls } from './components/EngineControls'
|
import { EngineControls } from './components/EngineControls'
|
||||||
import { getSampleRateFromIndex } from './config/effects'
|
import { getSampleRateFromIndex } from './config/effects'
|
||||||
import { engineSettings, effectSettings } from './stores/settings'
|
import { engineSettings, effectSettings } from './stores/settings'
|
||||||
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
|
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
|
||||||
|
import type { TileState } from './types/tiles'
|
||||||
|
import { createTileStateFromCurrent, loadTileParams, saveTileParams } from './utils/tileState'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const engineValues = useStore(engineSettings)
|
const engineValues = useStore(engineSettings)
|
||||||
const effectValues = useStore(effectSettings)
|
const effectValues = useStore(effectSettings)
|
||||||
|
|
||||||
const [formulas, setFormulas] = useState<string[][]>(() =>
|
const [tiles, setTiles] = useState<TileState[][]>(() =>
|
||||||
generateFormulaGrid(100, 2, engineValues.complexity)
|
generateTileGrid(100, 2, engineValues.complexity)
|
||||||
)
|
)
|
||||||
const [playing, setPlaying] = useState<string | null>(null)
|
const [playing, setPlaying] = useState<string | null>(null)
|
||||||
const [queued, setQueued] = useState<string | null>(null)
|
const [queued, setQueued] = useState<string | null>(null)
|
||||||
@ -27,18 +29,18 @@ function App() {
|
|||||||
const playbackManagerRef = useRef<PlaybackManager | null>(null)
|
const playbackManagerRef = useRef<PlaybackManager | null>(null)
|
||||||
const downloadServiceRef = useRef<DownloadService>(new DownloadService())
|
const downloadServiceRef = useRef<DownloadService>(new DownloadService())
|
||||||
const animationFrameRef = useRef<number | null>(null)
|
const animationFrameRef = useRef<number | null>(null)
|
||||||
const formulasRef = useRef<string[][]>(formulas)
|
const tilesRef = useRef<TileState[][]>(tiles)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
formulasRef.current = formulas
|
tilesRef.current = tiles
|
||||||
}, [formulas])
|
}, [tiles])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
effectSettings.setKey('masterVolume', engineValues.masterVolume)
|
effectSettings.setKey('masterVolume', engineValues.masterVolume)
|
||||||
}, [engineValues.masterVolume])
|
}, [engineValues.masterVolume])
|
||||||
|
|
||||||
const handleRandom = () => {
|
const handleRandom = () => {
|
||||||
setFormulas(generateFormulaGrid(100, 2, engineValues.complexity))
|
setTiles(generateTileGrid(100, 2, engineValues.complexity))
|
||||||
setQueued(null)
|
setQueued(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,8 +84,20 @@ function App() {
|
|||||||
updatePosition()
|
updatePosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTileClick = (formula: string, row: number, col: number, isDoubleClick: boolean = false) => {
|
const handleTileClick = (_formula: string, row: number, col: number, isDoubleClick: boolean = false) => {
|
||||||
const id = `${row}-${col}`
|
const id = `${row}-${col}`
|
||||||
|
const tile = tiles[row]?.[col]
|
||||||
|
|
||||||
|
if (!tile) return
|
||||||
|
|
||||||
|
if (focusedTile.row !== row || focusedTile.col !== col) {
|
||||||
|
const currentTile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||||
|
if (currentTile) {
|
||||||
|
saveTileParams(currentTile)
|
||||||
|
}
|
||||||
|
loadTileParams(tile)
|
||||||
|
}
|
||||||
|
|
||||||
setFocusedTile({ row, col })
|
setFocusedTile({ row, col })
|
||||||
|
|
||||||
if (playing === id) {
|
if (playing === id) {
|
||||||
@ -98,14 +112,15 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isDoubleClick || playing === null) {
|
if (isDoubleClick || playing === null) {
|
||||||
playFormula(formula, id)
|
playFormula(tile.formula, id)
|
||||||
} else {
|
} else {
|
||||||
setQueued(id)
|
setQueued(id)
|
||||||
if (playbackManagerRef.current) {
|
if (playbackManagerRef.current) {
|
||||||
playbackManagerRef.current.scheduleNextTrack(() => {
|
playbackManagerRef.current.scheduleNextTrack(() => {
|
||||||
const queuedFormula = formulasRef.current.flat()[parseInt(id.split('-')[0]) * 2 + parseInt(id.split('-')[1])]
|
const queuedTile = tilesRef.current[row]?.[col]
|
||||||
if (queuedFormula) {
|
if (queuedTile) {
|
||||||
playFormula(queuedFormula, id)
|
loadTileParams(queuedTile)
|
||||||
|
playFormula(queuedTile.formula, id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -120,6 +135,11 @@ function App() {
|
|||||||
const handleEngineChange = (parameterId: string, value: number) => {
|
const handleEngineChange = (parameterId: string, value: number) => {
|
||||||
engineSettings.setKey(parameterId as keyof typeof engineValues, value)
|
engineSettings.setKey(parameterId as keyof typeof engineValues, value)
|
||||||
|
|
||||||
|
const currentTile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||||
|
if (currentTile) {
|
||||||
|
saveTileParams(currentTile)
|
||||||
|
}
|
||||||
|
|
||||||
if (parameterId === 'masterVolume' && playbackManagerRef.current) {
|
if (parameterId === 'masterVolume' && playbackManagerRef.current) {
|
||||||
playbackManagerRef.current.setEffects({ ...effectValues, masterVolume: value })
|
playbackManagerRef.current.setEffects({ ...effectValues, masterVolume: value })
|
||||||
}
|
}
|
||||||
@ -131,6 +151,12 @@ function App() {
|
|||||||
|
|
||||||
const handleEffectChange = (parameterId: string, value: number | boolean | string) => {
|
const handleEffectChange = (parameterId: string, value: number | boolean | string) => {
|
||||||
effectSettings.setKey(parameterId as any, value as any)
|
effectSettings.setKey(parameterId as any, value as any)
|
||||||
|
|
||||||
|
const currentTile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||||
|
if (currentTile) {
|
||||||
|
saveTileParams(currentTile)
|
||||||
|
}
|
||||||
|
|
||||||
if (playbackManagerRef.current) {
|
if (playbackManagerRef.current) {
|
||||||
playbackManagerRef.current.setEffects(effectValues)
|
playbackManagerRef.current.setEffects(effectValues)
|
||||||
}
|
}
|
||||||
@ -138,6 +164,7 @@ function App() {
|
|||||||
|
|
||||||
const handleDownloadAll = async () => {
|
const handleDownloadAll = async () => {
|
||||||
setDownloading(true)
|
setDownloading(true)
|
||||||
|
const formulas = tiles.map(row => row.map(tile => tile.formula))
|
||||||
await downloadServiceRef.current.downloadAll(formulas, { duration: 10, bitDepth: 8 })
|
await downloadServiceRef.current.downloadAll(formulas, { duration: 10, bitDepth: 8 })
|
||||||
setDownloading(false)
|
setDownloading(false)
|
||||||
}
|
}
|
||||||
@ -149,25 +176,27 @@ function App() {
|
|||||||
const handleRegenerate = (row: number, col: number) => {
|
const handleRegenerate = (row: number, col: number) => {
|
||||||
const id = `${row}-${col}`
|
const id = `${row}-${col}`
|
||||||
const newFormula = generateRandomFormula(engineValues.complexity)
|
const newFormula = generateRandomFormula(engineValues.complexity)
|
||||||
|
const newTile = createTileStateFromCurrent(newFormula)
|
||||||
|
|
||||||
if (playing === id && playbackManagerRef.current) {
|
if (playing === id && playbackManagerRef.current) {
|
||||||
setRegenerating(id)
|
setRegenerating(id)
|
||||||
playbackManagerRef.current.scheduleNextTrack(() => {
|
playbackManagerRef.current.scheduleNextTrack(() => {
|
||||||
setFormulas(prevFormulas => {
|
setTiles(prevTiles => {
|
||||||
const newFormulas = [...prevFormulas]
|
const newTiles = [...prevTiles]
|
||||||
newFormulas[row] = [...newFormulas[row]]
|
newTiles[row] = [...newTiles[row]]
|
||||||
newFormulas[row][col] = newFormula
|
newTiles[row][col] = newTile
|
||||||
return newFormulas
|
return newTiles
|
||||||
})
|
})
|
||||||
playFormula(newFormula, id)
|
loadTileParams(newTile)
|
||||||
|
playFormula(newTile.formula, id)
|
||||||
setRegenerating(null)
|
setRegenerating(null)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
setFormulas(prevFormulas => {
|
setTiles(prevTiles => {
|
||||||
const newFormulas = [...prevFormulas]
|
const newTiles = [...prevTiles]
|
||||||
newFormulas[row] = [...newFormulas[row]]
|
newTiles[row] = [...newTiles[row]]
|
||||||
newFormulas[row][col] = newFormula
|
newTiles[row][col] = newTile
|
||||||
return newFormulas
|
return newTiles
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,10 +212,15 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const moveFocus = (direction: 'up' | 'down' | 'left' | 'right', step: number = 1) => {
|
const moveFocus = (direction: 'up' | 'down' | 'left' | 'right', step: number = 1) => {
|
||||||
|
const currentTile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||||
|
if (currentTile) {
|
||||||
|
saveTileParams(currentTile)
|
||||||
|
}
|
||||||
|
|
||||||
setFocusedTile(prev => {
|
setFocusedTile(prev => {
|
||||||
let { row, col } = prev
|
let { row, col } = prev
|
||||||
const maxRow = formulas.length - 1
|
const maxRow = tiles.length - 1
|
||||||
const maxCol = (formulas[row]?.length || 1) - 1
|
const maxCol = (tiles[row]?.length || 1) - 1
|
||||||
|
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 'up':
|
case 'up':
|
||||||
@ -202,6 +236,12 @@ function App() {
|
|||||||
col = Math.min(maxCol, col + step)
|
col = Math.min(maxCol, col + step)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newTile = tiles[row]?.[col]
|
||||||
|
if (newTile) {
|
||||||
|
loadTileParams(newTile)
|
||||||
|
}
|
||||||
|
|
||||||
return { row, col }
|
return { row, col }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -210,24 +250,24 @@ function App() {
|
|||||||
if (playing) {
|
if (playing) {
|
||||||
handleStop()
|
handleStop()
|
||||||
} else {
|
} else {
|
||||||
const formula = formulas[focusedTile.row]?.[focusedTile.col]
|
const tile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||||
if (formula) {
|
if (tile) {
|
||||||
handleTileClick(formula, focusedTile.row, focusedTile.col, true)
|
handleTileClick(tile.formula, focusedTile.row, focusedTile.col, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyboardEnter = () => {
|
const handleKeyboardEnter = () => {
|
||||||
const formula = formulas[focusedTile.row]?.[focusedTile.col]
|
const tile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||||
if (formula) {
|
if (tile) {
|
||||||
handleTileClick(formula, focusedTile.row, focusedTile.col, false)
|
handleTileClick(tile.formula, focusedTile.row, focusedTile.col, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyboardDoubleEnter = () => {
|
const handleKeyboardDoubleEnter = () => {
|
||||||
const formula = formulas[focusedTile.row]?.[focusedTile.col]
|
const tile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||||
if (formula) {
|
if (tile) {
|
||||||
handleTileClick(formula, focusedTile.row, focusedTile.col, true)
|
handleTileClick(tile.formula, focusedTile.row, focusedTile.col, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,13 +333,13 @@ function App() {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="flex-1 grid grid-cols-2 auto-rows-min gap-[1px] bg-white p-[1px] overflow-auto">
|
<div className="flex-1 grid grid-cols-2 auto-rows-min gap-[1px] bg-white p-[1px] overflow-auto">
|
||||||
{formulas.map((row, i) =>
|
{tiles.map((row, i) =>
|
||||||
row.map((formula, j) => {
|
row.map((tile, j) => {
|
||||||
const id = `${i}-${j}`
|
const id = `${i}-${j}`
|
||||||
return (
|
return (
|
||||||
<BytebeatTile
|
<BytebeatTile
|
||||||
key={id}
|
key={id}
|
||||||
formula={formula}
|
formula={tile.formula}
|
||||||
row={i}
|
row={i}
|
||||||
col={j}
|
col={j}
|
||||||
isPlaying={playing === id}
|
isPlaying={playing === id}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { Dices } from 'lucide-react'
|
||||||
import { Slider } from './Slider'
|
import { Slider } from './Slider'
|
||||||
import { Switch } from './Switch'
|
import { Switch } from './Switch'
|
||||||
import { Dropdown } from './Dropdown'
|
import { Dropdown } from './Dropdown'
|
||||||
@ -10,6 +11,23 @@ interface EffectsBarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function EffectsBar({ values, onChange }: EffectsBarProps) {
|
export function EffectsBar({ values, onChange }: EffectsBarProps) {
|
||||||
|
const randomizeEffect = (effect: typeof EFFECTS[number]) => {
|
||||||
|
effect.parameters.forEach(param => {
|
||||||
|
if (param.id.endsWith('Enable')) return
|
||||||
|
|
||||||
|
if (param.options) {
|
||||||
|
const randomOption = param.options[Math.floor(Math.random() * param.options.length)]
|
||||||
|
onChange(param.id, randomOption.value)
|
||||||
|
} else {
|
||||||
|
const range = param.max - param.min
|
||||||
|
const steps = Math.floor(range / param.step)
|
||||||
|
const randomStep = Math.floor(Math.random() * (steps + 1))
|
||||||
|
const randomValue = param.min + (randomStep * param.step)
|
||||||
|
onChange(param.id, randomValue)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const renderFilterEffect = (effect: typeof EFFECTS[number]) => {
|
const renderFilterEffect = (effect: typeof EFFECTS[number]) => {
|
||||||
const filterGroups = [
|
const filterGroups = [
|
||||||
{ prefix: 'hp', label: 'HP' },
|
{ prefix: 'hp', label: 'HP' },
|
||||||
@ -19,9 +37,17 @@ export function EffectsBar({ values, onChange }: EffectsBarProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={effect.id} className="border-2 border-white p-3">
|
<div key={effect.id} className="border-2 border-white p-3">
|
||||||
<h3 className="font-mono text-[10px] tracking-[0.2em] text-white mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
{effect.name.toUpperCase()}
|
<h3 className="font-mono text-[10px] tracking-[0.2em] text-white">
|
||||||
</h3>
|
{effect.name.toUpperCase()}
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
onClick={() => randomizeEffect(effect)}
|
||||||
|
className="p-1 text-white hover:bg-white hover:text-black transition-colors"
|
||||||
|
>
|
||||||
|
<Dices size={12} strokeWidth={2} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
{filterGroups.map(group => {
|
{filterGroups.map(group => {
|
||||||
const enableParam = effect.parameters.find(p => p.id === `${group.prefix}Enable`)
|
const enableParam = effect.parameters.find(p => p.id === `${group.prefix}Enable`)
|
||||||
@ -32,11 +58,14 @@ export function EffectsBar({ values, onChange }: EffectsBarProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={group.prefix} className="flex gap-2 items-center">
|
<div key={group.prefix} className="flex gap-2 items-center">
|
||||||
<Switch
|
<button
|
||||||
checked={Boolean(values[enableParam.id])}
|
onClick={() => onChange(enableParam.id, values[enableParam.id] ? 0 : 1)}
|
||||||
onChange={(checked) => onChange(enableParam.id, checked ? 1 : 0)}
|
className="w-4 h-4 border-2 border-white bg-black flex items-center justify-center cursor-pointer hover:bg-white transition-colors group"
|
||||||
vertical
|
>
|
||||||
/>
|
{Boolean(values[enableParam.id]) && (
|
||||||
|
<div className="w-2 h-2 bg-white group-hover:bg-black" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
<div className="flex-1 flex flex-col gap-2">
|
<div className="flex-1 flex flex-col gap-2">
|
||||||
<Slider
|
<Slider
|
||||||
label={freqParam.label}
|
label={freqParam.label}
|
||||||
@ -78,9 +107,17 @@ export function EffectsBar({ values, onChange }: EffectsBarProps) {
|
|||||||
return (
|
return (
|
||||||
<div key={effect.id} className="border-2 border-white p-3">
|
<div key={effect.id} className="border-2 border-white p-3">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<h3 className="font-mono text-[10px] tracking-[0.2em] text-white">
|
<div className="flex items-center gap-2">
|
||||||
{effect.name.toUpperCase()}
|
<h3 className="font-mono text-[10px] tracking-[0.2em] text-white">
|
||||||
</h3>
|
{effect.name.toUpperCase()}
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
onClick={() => randomizeEffect(effect)}
|
||||||
|
className="p-1 text-white hover:bg-white hover:text-black transition-colors"
|
||||||
|
>
|
||||||
|
<Dices size={12} strokeWidth={2} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{effect.bypassable && (
|
{effect.bypassable && (
|
||||||
<Switch
|
<Switch
|
||||||
checked={!Boolean(values[`${effect.id}Bypass`])}
|
checked={!Boolean(values[`${effect.id}Bypass`])}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const ENGINE_CONTROLS: EffectConfig[] = [
|
|||||||
label: 'Sample Rate',
|
label: 'Sample Rate',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 3,
|
max: 3,
|
||||||
default: 1,
|
default: 3,
|
||||||
step: 1,
|
step: 1,
|
||||||
unit: ''
|
unit: ''
|
||||||
},
|
},
|
||||||
@ -37,7 +37,7 @@ export const ENGINE_CONTROLS: EffectConfig[] = [
|
|||||||
label: 'Bit Depth',
|
label: 'Bit Depth',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 2,
|
max: 2,
|
||||||
default: 0,
|
default: 2,
|
||||||
step: 1,
|
step: 1,
|
||||||
unit: ''
|
unit: ''
|
||||||
},
|
},
|
||||||
|
|||||||
5
src/types/tiles.ts
Normal file
5
src/types/tiles.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export interface TileState {
|
||||||
|
formula: string
|
||||||
|
engineParams: Record<string, number>
|
||||||
|
effectParams: Record<string, number | boolean | string>
|
||||||
|
}
|
||||||
@ -1,3 +1,6 @@
|
|||||||
|
import type { TileState } from '../types/tiles'
|
||||||
|
import { createTileState } from './tileState'
|
||||||
|
|
||||||
interface Template {
|
interface Template {
|
||||||
pattern: string
|
pattern: string
|
||||||
weight: number
|
weight: number
|
||||||
@ -216,4 +219,17 @@ export function generateFormulaGrid(rows: number, cols: number, complexity: numb
|
|||||||
grid.push(row)
|
grid.push(row)
|
||||||
}
|
}
|
||||||
return grid
|
return grid
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateTileGrid(rows: number, cols: number, complexity: number = 1): TileState[][] {
|
||||||
|
const grid: TileState[][] = []
|
||||||
|
for (let i = 0; i < rows; i++) {
|
||||||
|
const row: TileState[] = []
|
||||||
|
for (let j = 0; j < cols; j++) {
|
||||||
|
const formula = generateRandomFormula(complexity)
|
||||||
|
row.push(createTileState(formula))
|
||||||
|
}
|
||||||
|
grid.push(row)
|
||||||
|
}
|
||||||
|
return grid
|
||||||
}
|
}
|
||||||
46
src/utils/tileState.ts
Normal file
46
src/utils/tileState.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import type { TileState } from '../types/tiles'
|
||||||
|
import { engineSettings, effectSettings } from '../stores/settings'
|
||||||
|
import { getDefaultEngineValues, getDefaultEffectValues } from '../config/effects'
|
||||||
|
|
||||||
|
export function createTileState(
|
||||||
|
formula: string,
|
||||||
|
engineParams?: Record<string, number>,
|
||||||
|
effectParams?: Record<string, number | boolean | string>
|
||||||
|
): TileState {
|
||||||
|
return {
|
||||||
|
formula,
|
||||||
|
engineParams: engineParams ?? { ...getDefaultEngineValues() },
|
||||||
|
effectParams: effectParams ?? { ...getDefaultEffectValues(), masterVolume: 75 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTileStateFromCurrent(formula: string): TileState {
|
||||||
|
return {
|
||||||
|
formula,
|
||||||
|
engineParams: { ...engineSettings.get() },
|
||||||
|
effectParams: { ...effectSettings.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadTileParams(tile: TileState): void {
|
||||||
|
Object.entries(tile.engineParams).forEach(([key, value]) => {
|
||||||
|
engineSettings.setKey(key as any, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.entries(tile.effectParams).forEach(([key, value]) => {
|
||||||
|
effectSettings.setKey(key as any, value as any)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveTileParams(tile: TileState): void {
|
||||||
|
tile.engineParams = { ...engineSettings.get() }
|
||||||
|
tile.effectParams = { ...effectSettings.get() }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cloneTileState(tile: TileState): TileState {
|
||||||
|
return {
|
||||||
|
formula: tile.formula,
|
||||||
|
engineParams: { ...tile.engineParams },
|
||||||
|
effectParams: { ...tile.effectParams }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user