All checks were successful
continuous-integration/drone/push Build is passing
192 lines
4.9 KiB
TypeScript
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();
|
|
}
|
|
}
|