diff options
Diffstat (limited to 'src/engine/entities/LambdaFactory.ts')
-rw-r--r-- | src/engine/entities/LambdaFactory.ts | 239 |
1 files changed, 45 insertions, 194 deletions
diff --git a/src/engine/entities/LambdaFactory.ts b/src/engine/entities/LambdaFactory.ts index 770c096..61a3b0a 100644 --- a/src/engine/entities/LambdaFactory.ts +++ b/src/engine/entities/LambdaFactory.ts @@ -1,10 +1,7 @@ import { Failure, IMAGES, - LambdaSave, LambdaTransformSound, - Miscellaneous, - ModalOpen, SOUNDS, SPRITE_SPECS, SpriteSpec, @@ -19,66 +16,19 @@ import { GridSpawn, Highlight, Interactable, + Modal, Sprite, Text, } from "../components"; import { Coord2D, Direction } from "../interfaces"; -import { openModal, closeModal } from "../utils"; -import { - EditorState, - StateField, - StateEffect, - Range, - Extension, -} from "@codemirror/state"; -import { Decoration, EditorView, keymap } from "@codemirror/view"; -import { defaultKeymap } from "@codemirror/commands"; -import rainbowBrackets from "rainbowbrackets"; -import { basicSetup } from "codemirror"; +import { tryWrap } from "../utils"; import { parse } from "../../interpreter"; -interface CodeEditorState { - view: EditorView; - editorElement: HTMLElement; - syntaxError: HTMLElement; - canvas: HTMLCanvasElement; - closeButton: HTMLButtonElement; -} - -const highlightEffect = StateEffect.define<Range<Decoration>[]>(); -const highlightExtension = StateField.define({ - create() { - return Decoration.none; - }, - update(value, transaction) { - value = value.map(transaction.changes); - - for (let effect of transaction.effects) { - if (effect.is(highlightEffect)) - value = value.update({ add: effect.value, sort: true }); - } - - return value; - }, - provide: (f) => EditorView.decorations.from(f), -}); - -const FontSizeTheme = EditorView.theme({ - $: { - fontSize: "16pt", - }, -}); -const FontSizeThemeExtension: Extension = [FontSizeTheme]; -const syntaxErrorDecoration = Decoration.mark({ - class: "syntax-error", -}); - export class LambdaFactory extends Entity { private static spriteSpec: SpriteSpec = SPRITE_SPECS.get( - Sprites.LAMBDA_FACTORY, + Sprites.LAMBDA_FACTORY ) as SpriteSpec; - private codeEditorState: CodeEditorState | null; private spawns: number; private code: string; @@ -87,7 +37,6 @@ export class LambdaFactory extends Entity { this.spawns = spawns; this.code = code; - this.codeEditorState = null; this.addComponent( new BoundingBox( @@ -99,8 +48,8 @@ export class LambdaFactory extends Entity { width: LambdaFactory.spriteSpec.width, height: LambdaFactory.spriteSpec.height, }, - 0, - ), + 0 + ) ); this.addComponent(new Text(spawns.toString())); @@ -110,8 +59,8 @@ export class LambdaFactory extends Entity { this.addComponent( new GridSpawn( this.spawns, - () => new FunctionBox({ x: 0, y: 0 }, this.code), - ), + () => new FunctionBox({ x: 0, y: 0 }, this.code) + ) ); this.addComponent(new Grid(gridPosition)); @@ -125,166 +74,68 @@ export class LambdaFactory extends Entity { height: LambdaFactory.spriteSpec.height, }, LambdaFactory.spriteSpec.msPerFrame, - LambdaFactory.spriteSpec.frames, - ), + LambdaFactory.spriteSpec.frames + ) ); this.addComponent( - new Highlight( - (direction) => this.onHighlight(direction), - () => this.onUnhighlight(), - ), + new Highlight(this.onHighlight.bind(this), this.onUnhighlight.bind(this)) ); } - private onUnhighlight() { - closeModal(); - this.removeComponent(ComponentNames.Interactable); - } - - private spawnNewLambda(direction: Direction) { - try { - parse(this.code); - } catch (e: any) { - SOUNDS.get(Failure.name)!.play(); - return; + private codeConsumer(code: string) { + const parsed = tryWrap(() => parse(code)); + if (parsed.error) { + return { error: parsed.error }; } - - const spawner = this.getComponent<GridSpawn>(ComponentNames.GridSpawn); - spawner.spawnEntity(direction); - - const textComponent = this.getComponent<Text>(ComponentNames.Text); - textComponent.text = spawner.spawnsLeft.toString(); - this.addComponent(textComponent); - - SOUNDS.get(LambdaTransformSound.name)!.play(); - } - - private openCodeEditor() { - const modalContent = - "<div class='code'><div id='code'></div><br><p id='syntax-error' class='error'></p><button id='close-modal'>Save</button></div>"; - openModal(modalContent); - - const startState = EditorState.create({ - doc: this.code, - extensions: [ - basicSetup, - keymap.of(defaultKeymap), - rainbowBrackets(), - highlightExtension, - FontSizeThemeExtension, - ], - }); - - const codeBox = document.getElementById("code")!; - const syntaxError = document.getElementById("syntax-error")!; - const canvas = document.getElementById( - Miscellaneous.CANVAS_ID, - ) as HTMLCanvasElement; - const closeButton = document.getElementById( - "close-modal", - ) as HTMLButtonElement; - closeButton.addEventListener("click", () => this.saveAndCloseCodeEditor()); - - const editorView = new EditorView({ - state: startState, - parent: codeBox, - }); - editorView.focus(); - - this.codeEditorState = { - view: editorView, - editorElement: codeBox, - syntaxError, - canvas, - closeButton, - }; - - SOUNDS.get(ModalOpen.name)!.play(); + this.code = code; + this.removeComponent(ComponentNames.Modal); + return { consumed: true }; } - private refreshCodeEditorText(text: string) { - if (!this.codeEditorState) { + private onHighlight(direction: Direction) { + if (direction === Direction.LEFT || direction === Direction.RIGHT) { + this.addComponent(new Interactable(() => this.spawnNewLambda(direction))); return; } - const { view } = this.codeEditorState; - view.dispatch({ - changes: { - from: 0, - to: text.length, - insert: "", - }, - }); - view.dispatch({ - changes: { - from: 0, - to: 0, - insert: text, - }, - }); + this.addComponent(new Interactable(this.interaction.bind(this))); } - private saveAndCloseCodeEditor() { - if (!this.codeEditorState) { + private interaction() { + if (this.hasComponent(ComponentNames.Modal)) { return; } - - const { canvas, view, editorElement, syntaxError } = this.codeEditorState; - const text = view.state.doc.toString(); - this.refreshCodeEditorText(text); - syntaxError.innerText = ""; - - try { - parse(text); - } catch (e: any) { - if (!e.location) { - return; - } - const { - location: { - start: { offset: start }, - end: { offset: end }, + this.addComponent( + new Modal({ + type: "CODE_EDITOR", + codeInit: { + code: this.code, + codeConsumer: this.codeConsumer.bind(this), }, - } = e; - - view.dispatch({ - effects: highlightEffect.of([ - syntaxErrorDecoration.range(start === end ? start - 1 : start, end), - ]), - }); - - syntaxError.innerText = e.message; - SOUNDS.get(Failure.name)!.play(); - return; - } - - this.code = text; - - view.destroy(); - editorElement.innerHTML = ""; - this.codeEditorState = null; - closeModal(); + }) + ); + } - canvas.focus(); - SOUNDS.get(LambdaSave.name)!.play(); + private onUnhighlight() { + this.removeComponent(ComponentNames.Modal); + this.removeComponent(ComponentNames.Interactable); } - private onHighlight(direction: Direction) { - if (direction === Direction.LEFT || direction === Direction.RIGHT) { - this.addComponent(new Interactable(() => this.spawnNewLambda(direction))); + private spawnNewLambda(direction: Direction) { + const parsed = tryWrap(() => parse(this.code)); + if (parsed.error) { + SOUNDS.get(Failure.name)!.play(); return; } - const interaction = () => { - if (this.codeEditorState) { - this.saveAndCloseCodeEditor(); - return; - } + const spawner = this.getComponent<GridSpawn>(ComponentNames.GridSpawn); + spawner.spawnEntity(direction); - this.openCodeEditor(); - }; + const textComponent = this.getComponent<Text>(ComponentNames.Text); + textComponent.text = spawner.spawnsLeft.toString(); + this.addComponent(textComponent); - this.addComponent(new Interactable(interaction)); + SOUNDS.get(LambdaTransformSound.name)!.play(); } } |