summaryrefslogtreecommitdiff
path: root/src/engine/entities
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-03-07 20:45:47 -0700
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-03-07 20:45:47 -0700
commite6e29440563e33bb67e0ad51f9fb6c5c2c3fe809 (patch)
tree5deaee322ff1a039dc44a3cb52ecc48a671fda2a /src/engine/entities
parent823620b2a6ebb7ece619991e47a37ad46542b69f (diff)
downloadthe-abstraction-engine-e6e29440563e33bb67e0ad51f9fb6c5c2c3fe809.tar.gz
the-abstraction-engine-e6e29440563e33bb67e0ad51f9fb6c5c2c3fe809.zip
level one (applications prototype finished!)
Diffstat (limited to 'src/engine/entities')
-rw-r--r--src/engine/entities/FunctionApplication.ts180
-rw-r--r--src/engine/entities/FunctionBox.ts15
-rw-r--r--src/engine/entities/LambdaFactory.ts11
-rw-r--r--src/engine/entities/LockedDoor.ts16
-rw-r--r--src/engine/entities/Particles.ts8
-rw-r--r--src/engine/entities/Player.ts3
6 files changed, 225 insertions, 8 deletions
diff --git a/src/engine/entities/FunctionApplication.ts b/src/engine/entities/FunctionApplication.ts
index 31e3490..24e4eec 100644
--- a/src/engine/entities/FunctionApplication.ts
+++ b/src/engine/entities/FunctionApplication.ts
@@ -1,7 +1,183 @@
-import { Entity, EntityNames } from ".";
+import {
+ Entity,
+ EntityNames,
+ FunctionBox,
+ Key,
+ Particles,
+ makeLambdaTermHighlightComponent,
+} from ".";
+import {
+ BoundingBox,
+ Colliding,
+ ComponentNames,
+ Grid,
+ LambdaTerm,
+ Sprite,
+} from "../components";
+import {
+ Failure,
+ IMAGES,
+ LambdaTransformSound,
+ SOUNDS,
+ SPRITE_SPECS,
+ SpriteSpec,
+ Sprites,
+} from "../config";
+import { Coord2D, Direction } from "../interfaces";
+import { Game } from "..";
+import { Grid as GridSystem, SystemNames } from "../systems";
+import { colors } from "../utils";
+import {
+ DebrujinifiedLambdaTerm,
+ SymbolTable,
+ emitNamed,
+ interpret,
+} from "../../interpreter";
export class FunctionApplication extends Entity {
- constructor() {
+ private static spriteSpec = SPRITE_SPECS.get(Sprites.BUBBLE) as SpriteSpec;
+
+ private symbolTable: SymbolTable;
+
+ constructor(gridPosition: Coord2D, lambdaTerm: string) {
super(EntityNames.FunctionApplication);
+
+ this.symbolTable = new SymbolTable();
+ this.symbolTable.add("key");
+
+ const dimension = {
+ width: FunctionApplication.spriteSpec.width,
+ height: FunctionApplication.spriteSpec.height,
+ };
+ this.addComponent(
+ new BoundingBox(
+ {
+ x: 0,
+ y: 0,
+ },
+ dimension,
+ 0,
+ ),
+ );
+
+ this.addComponent(new Grid(gridPosition));
+
+ this.addComponent(new LambdaTerm(lambdaTerm));
+
+ this.addComponent(
+ new Sprite(
+ IMAGES.get(FunctionApplication.spriteSpec.sheet)!,
+ { x: 0, y: 0 },
+ dimension,
+ FunctionApplication.spriteSpec.msPerFrame,
+ FunctionApplication.spriteSpec.frames,
+ ),
+ );
+
+ this.addComponent(new Colliding(this.handleCollision.bind(this)));
+
+ this.addComponent(makeLambdaTermHighlightComponent(this));
+ }
+
+ public handleCollision(game: Game, entity: Entity) {
+ if (entity.name !== EntityNames.FunctionBox) {
+ return;
+ }
+
+ const entityGrid = entity.getComponent<Grid>(ComponentNames.Grid);
+ if (entityGrid.movingDirection !== Direction.NONE) {
+ // prevent recursive functionBox -> application creation
+ return;
+ }
+
+ const grid = this.getComponent<Grid>(ComponentNames.Grid);
+ const gridSystem = game.getSystem<GridSystem>(SystemNames.Grid);
+ const fail = () => {
+ entityGrid.movingDirection = gridSystem.oppositeDirection(
+ entityGrid.previousDirection,
+ );
+ entity.addComponent(entityGrid);
+
+ const failureSound = SOUNDS.get(Failure.name)!;
+ failureSound.play();
+ };
+
+ const applicationTerm = this.getComponent<LambdaTerm>(
+ ComponentNames.LambdaTerm,
+ );
+ const functionTerm = entity.getComponent<LambdaTerm>(
+ ComponentNames.LambdaTerm,
+ );
+ const newCode = applicationTerm.code.replace("_INPUT", functionTerm.code);
+ let result: DebrujinifiedLambdaTerm | null = null;
+ try {
+ result = interpret(newCode, this.symbolTable);
+ } catch (e) {
+ console.error(e);
+ fail();
+ return;
+ }
+
+ const { dimension } = gridSystem;
+ const nextPosition = gridSystem.getNewGridPosition(
+ grid.gridPosition,
+ entityGrid.previousDirection,
+ );
+
+ let applicationResultingEntity: Entity | null = null; // this should be its own function
+ if ("abstraction" in result) {
+ const code = emitNamed(result);
+
+ applicationResultingEntity = new FunctionBox(grid.gridPosition, code);
+ } else if ("name" in result) {
+ const { name } = result;
+ if (name === "key") {
+ applicationResultingEntity = new Key(grid.gridPosition);
+ }
+ } else {
+ fail();
+ return;
+ }
+
+ game.removeEntity(entity.id);
+ if (applicationResultingEntity) {
+ const grid = applicationResultingEntity.getComponent<Grid>(
+ ComponentNames.Grid,
+ );
+ grid.movingDirection = entityGrid.previousDirection;
+ applicationResultingEntity.addComponent(grid);
+
+ game.addEntity(applicationResultingEntity);
+ }
+
+ this.playTransformSound();
+ const particles = new Particles({
+ center: gridSystem.gridToScreenPosition(nextPosition),
+ spawnerDimensions: {
+ width: dimension.width / 2,
+ height: dimension.height / 2,
+ },
+ particleCount: 10,
+ spawnerShape: "circle",
+ particleShape: "circle",
+ particleMeanSpeed: 0.25,
+ particleSpeedVariance: 0.15,
+ particleMeanLife: 150,
+ particleMeanSize: 2,
+ particleSizeVariance: 1,
+ particleLifeVariance: 20,
+ particleColors: [
+ colors.lightAqua,
+ colors.blue,
+ colors.green,
+ colors.lightGreen,
+ ],
+ });
+ game.addEntity(particles);
+ }
+
+ private playTransformSound() {
+ const audio = SOUNDS.get(LambdaTransformSound.name)!;
+ audio.play();
}
}
diff --git a/src/engine/entities/FunctionBox.ts b/src/engine/entities/FunctionBox.ts
index 92f1908..0c9123e 100644
--- a/src/engine/entities/FunctionBox.ts
+++ b/src/engine/entities/FunctionBox.ts
@@ -1,6 +1,9 @@
import {
IMAGES,
Miscellaneous,
+ ModalClose,
+ ModalOpen,
+ SOUNDS,
SPRITE_SPECS,
SpriteSpec,
Sprites,
@@ -72,10 +75,15 @@ export const makeLambdaTermHighlightComponent = (entity: Entity) => {
const onHighlight = () => {
let modalOpen = false;
+ const doModalClose = () => {
+ SOUNDS.get(ModalClose.name)!.play();
+ modalOpen = false;
+ closeModal();
+ };
+
const interaction = () => {
if (modalOpen) {
- modalOpen = false;
- closeModal();
+ doModalClose();
return;
}
@@ -86,9 +94,10 @@ export const makeLambdaTermHighlightComponent = (entity: Entity) => {
`<div style="text-align:center"><p>${code}</p> <br> <button id="close">Close</button></div>`,
);
modalOpen = true;
+ SOUNDS.get(ModalOpen.name)!.play();
document.getElementById("close")!.addEventListener("click", () => {
- closeModal();
+ doModalClose();
document.getElementById(Miscellaneous.CANVAS_ID)!.focus();
});
};
diff --git a/src/engine/entities/LambdaFactory.ts b/src/engine/entities/LambdaFactory.ts
index a0f5749..9ad1398 100644
--- a/src/engine/entities/LambdaFactory.ts
+++ b/src/engine/entities/LambdaFactory.ts
@@ -1,6 +1,11 @@
import {
+ Failure,
IMAGES,
+ LambdaSave,
+ LambdaTransformSound,
Miscellaneous,
+ ModalOpen,
+ SOUNDS,
SPRITE_SPECS,
SpriteSpec,
Sprites,
@@ -144,6 +149,8 @@ export class LambdaFactory extends Entity {
const text = this.getComponent<Text>(ComponentNames.Text);
text.text = spawner.spawnsLeft.toString();
this.addComponent(text);
+
+ SOUNDS.get(LambdaTransformSound.name)!.play();
}
private openCodeEditor() {
@@ -185,6 +192,8 @@ export class LambdaFactory extends Entity {
canvas,
closeButton,
};
+
+ SOUNDS.get(ModalOpen.name)!.play();
}
private refreshCodeEditorText(text: string) {
@@ -239,6 +248,7 @@ export class LambdaFactory extends Entity {
});
syntaxError.innerText = e.message;
+ SOUNDS.get(Failure.name)!.play();
return;
}
@@ -250,6 +260,7 @@ export class LambdaFactory extends Entity {
closeModal();
canvas.focus();
+ SOUNDS.get(LambdaSave.name)!.play();
}
private onHighlight(direction: Direction) {
diff --git a/src/engine/entities/LockedDoor.ts b/src/engine/entities/LockedDoor.ts
index b4887d6..aa1f328 100644
--- a/src/engine/entities/LockedDoor.ts
+++ b/src/engine/entities/LockedDoor.ts
@@ -7,7 +7,14 @@ import {
Sprite,
ComponentNames,
} from "../components";
-import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
+import {
+ IMAGES,
+ KeyOpen,
+ SOUNDS,
+ SPRITE_SPECS,
+ SpriteSpec,
+ Sprites,
+} from "../config";
import { Coord2D } from "../interfaces";
import { Grid as GridSystem, SystemNames } from "../systems";
import { colors } from "../utils";
@@ -82,5 +89,12 @@ export class LockedDoor extends Entity {
});
game.addEntity(particles);
+
+ this.playKeySound();
+ }
+
+ private playKeySound() {
+ const audio = SOUNDS.get(KeyOpen.name)!;
+ audio.play();
}
}
diff --git a/src/engine/entities/Particles.ts b/src/engine/entities/Particles.ts
index 34b475c..5381b23 100644
--- a/src/engine/entities/Particles.ts
+++ b/src/engine/entities/Particles.ts
@@ -156,8 +156,12 @@ export class Particles extends Entity {
Math.floor(Math.random() * options.particleColors.length)
];
const position = {
- x: options.center.x + Math.cos(angle) * options.spawnerDimensions.width,
- y: options.center.y + Math.sin(angle) * options.spawnerDimensions.height,
+ x:
+ options.center.x +
+ (Math.cos(angle) * options.spawnerDimensions.width) / 2,
+ y:
+ options.center.y +
+ (Math.sin(angle) * options.spawnerDimensions.height) / 2,
};
if (options.spawnerShape === "rectangle") {
// determine a random position on the edge of the spawner based on the angle
diff --git a/src/engine/entities/Player.ts b/src/engine/entities/Player.ts
index cb9161b..1b98383 100644
--- a/src/engine/entities/Player.ts
+++ b/src/engine/entities/Player.ts
@@ -6,6 +6,7 @@ import {
Grid,
BoundingBox,
Control,
+ Pushable,
} from "../components";
import { Direction } from "../interfaces/";
@@ -28,6 +29,8 @@ export class Player extends Entity {
),
);
+ this.addComponent(new Pushable());
+
this.addComponent(new Control());
this.addComponent(new Grid());