diff options
author | Elizabeth Hunt <elizabeth.hunt@simponic.xyz> | 2024-03-07 20:45:47 -0700 |
---|---|---|
committer | Elizabeth Hunt <elizabeth.hunt@simponic.xyz> | 2024-03-07 20:45:47 -0700 |
commit | e6e29440563e33bb67e0ad51f9fb6c5c2c3fe809 (patch) | |
tree | 5deaee322ff1a039dc44a3cb52ecc48a671fda2a /src/engine/entities | |
parent | 823620b2a6ebb7ece619991e47a37ad46542b69f (diff) | |
download | the-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.ts | 180 | ||||
-rw-r--r-- | src/engine/entities/FunctionBox.ts | 15 | ||||
-rw-r--r-- | src/engine/entities/LambdaFactory.ts | 11 | ||||
-rw-r--r-- | src/engine/entities/LockedDoor.ts | 16 | ||||
-rw-r--r-- | src/engine/entities/Particles.ts | 8 | ||||
-rw-r--r-- | src/engine/entities/Player.ts | 3 |
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()); |