the-abstraction-engine/src/engine/entities/FunctionApplication.ts
Elizabeth Hunt de4f3fd2fe
All checks were successful
continuous-integration/drone/push Build is passing
prettier, fix assets and css
2024-03-11 16:35:51 -06:00

192 lines
4.9 KiB
TypeScript

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";
const APPLICATION_RESULTS: Record<string, (gridPosition: Coord2D) => Entity> = {
_KEY: (gridPosition: Coord2D) => new Key(gridPosition),
};
export class FunctionApplication extends Entity {
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();
Object.keys(APPLICATION_RESULTS).forEach((key) => {
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, true);
} 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;
const entityFactory = APPLICATION_RESULTS[name];
if (entityFactory) {
game.addEntity(entityFactory(nextPosition));
}
} 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();
}
}