summaryrefslogtreecommitdiff
path: root/src/engine/entities/LambdaFactory.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/entities/LambdaFactory.ts')
-rw-r--r--src/engine/entities/LambdaFactory.ts239
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();
}
}