From 4b9349b3f8bee21eb086cfd6e7668532a50e6048 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sat, 2 Mar 2024 04:02:20 -0700 Subject: [PATCH] add text on lambda factory --- public/assets/function_factory.png | Bin 1302 -> 1242 bytes src/App.tsx | 2 +- src/css/modal.css | 8 +- src/engine/TheAbstractionEngine.ts | 14 ++- src/engine/components/ComponentNames.ts | 2 + src/engine/components/Highlight.ts | 9 +- src/engine/components/LambdaSpawn.ts | 29 +++++++ src/engine/components/Sprite.ts | 13 ++- src/engine/components/Text.ts | 22 +++++ src/engine/components/index.ts | 2 + src/engine/config/assets.ts | 6 ++ src/engine/config/sprites.ts | 10 +++ src/engine/entities/EntityNames.ts | 1 + src/engine/entities/FunctionBox.ts | 2 +- src/engine/entities/LambdaFactory.ts | 109 ++++++++++++++++++++++++ src/engine/entities/index.ts | 1 + src/engine/interfaces/Draw.ts | 6 ++ src/engine/systems/Grid.ts | 10 +-- src/engine/systems/LambdaFactory.ts | 34 ++++++++ src/engine/systems/Render.ts | 17 +++- src/engine/systems/SystemNames.ts | 1 + src/engine/systems/index.ts | 1 + 22 files changed, 281 insertions(+), 18 deletions(-) create mode 100644 src/engine/components/LambdaSpawn.ts create mode 100644 src/engine/components/Text.ts create mode 100644 src/engine/entities/LambdaFactory.ts create mode 100644 src/engine/systems/LambdaFactory.ts diff --git a/public/assets/function_factory.png b/public/assets/function_factory.png index 2c4675803197cf921b40be3b99188e72c383cbd8..e070be6f1af37f7fa7b77b4144708849f020c345 100644 GIT binary patch delta 1222 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 { simponic {" "} | inspired by{" "} - + baba is you diff --git a/src/css/modal.css b/src/css/modal.css index a609ef9..c10070e 100644 --- a/src/css/modal.css +++ b/src/css/modal.css @@ -1,3 +1,5 @@ +@import url("./colors.css"); + .modal { display: none; position: fixed; @@ -15,11 +17,11 @@ .modal-content { display: flex; - background-color: #282828; - color: #ebdbb2; + background-color: var(--bg); + color: var(--text); margin: auto; padding: 20px; - border: 1px solid #928374; + border: 1px solid var(--yellow); width: 40%; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); animation: scaleUp 0.25s; diff --git a/src/engine/TheAbstractionEngine.ts b/src/engine/TheAbstractionEngine.ts index 2db599b..7f71fd5 100644 --- a/src/engine/TheAbstractionEngine.ts +++ b/src/engine/TheAbstractionEngine.ts @@ -1,7 +1,13 @@ import { Game } from "."; import { Miscellaneous, loadAssets } from "./config"; -import { Player, FunctionBox, Wall } from "./entities"; -import { Grid, FacingDirection, Input, Render } from "./systems"; +import { Player, FunctionBox, Wall, LambdaFactory } from "./entities"; +import { + Grid, + FacingDirection, + Input, + Render, + LambdaFactory as LambdaFactorySpawnSystem, +} from "./systems"; export class TheAbstractionEngine { private game: Game; @@ -32,6 +38,7 @@ export class TheAbstractionEngine { height: Miscellaneous.GRID_CELL_HEIGHT, }, ), + new LambdaFactorySpawnSystem(), new Render(this.ctx), ].forEach((system) => this.game.addSystem(system)); @@ -45,6 +52,9 @@ export class TheAbstractionEngine { const wall = new Wall({ x: 5, y: 3 }); this.game.addEntity(wall); + + const factory = new LambdaFactory({ x: 6, y: 6 }, "λ x . (x)", 10); + this.game.addEntity(factory); } public play() { diff --git a/src/engine/components/ComponentNames.ts b/src/engine/components/ComponentNames.ts index 5e9b589..1f41d18 100644 --- a/src/engine/components/ComponentNames.ts +++ b/src/engine/components/ComponentNames.ts @@ -8,4 +8,6 @@ export namespace ComponentNames { export const Interactable = "Interactable"; export const Pushable = "Pushable"; export const Colliding = "Colliding"; + export const LambdaSpawn = "LambdaSpawn"; + export const Text = "Text"; } diff --git a/src/engine/components/Highlight.ts b/src/engine/components/Highlight.ts index 5875057..66ec74b 100644 --- a/src/engine/components/Highlight.ts +++ b/src/engine/components/Highlight.ts @@ -1,4 +1,5 @@ import { Component, ComponentNames } from "."; +import { Direction } from "../interfaces"; export class Highlight extends Component { public isHighlighted: boolean; @@ -6,8 +7,8 @@ export class Highlight extends Component { private onUnhighlight: Function; constructor( - onHighlight: Function, - onUnhighlight: Function, + onHighlight: (direction: Direction) => void, + onUnhighlight: () => void, isHighlighted: boolean = false, ) { super(ComponentNames.Highlight); @@ -17,10 +18,10 @@ export class Highlight extends Component { this.onUnhighlight = onUnhighlight; } - public highlight() { + public highlight(direction: Direction) { if (!this.isHighlighted) { this.isHighlighted = true; - this.onHighlight(); + this.onHighlight(direction); } } diff --git a/src/engine/components/LambdaSpawn.ts b/src/engine/components/LambdaSpawn.ts new file mode 100644 index 0000000..c45092a --- /dev/null +++ b/src/engine/components/LambdaSpawn.ts @@ -0,0 +1,29 @@ +import { Component, ComponentNames } from "."; +import { Direction } from "../interfaces"; + +export class LambdaSpawn extends Component { + public direction: Direction | null; + public spawnsLeft: number; + public code: string = ""; + + constructor( + spawnsLeft: number, + code: string, + direction: Direction | null = null, + ) { + super(ComponentNames.LambdaSpawn); + + this.spawnsLeft = spawnsLeft; + this.direction = direction; + this.code = code; + } + + public spawn(direction: Direction) { + if (this.spawnsLeft <= 0) { + return; + } + + this.direction = direction; + this.spawnsLeft -= 1; + } +} diff --git a/src/engine/components/Sprite.ts b/src/engine/components/Sprite.ts index 82d7011..c623bac 100644 --- a/src/engine/components/Sprite.ts +++ b/src/engine/components/Sprite.ts @@ -46,7 +46,7 @@ export class Sprite extends Component { } public draw(ctx: CanvasRenderingContext2D, drawArgs: DrawArgs) { - const { center, rotation, tint, opacity } = drawArgs; + const { center, rotation, tint, opacity, backgroundText } = drawArgs; ctx.save(); ctx.translate(center.x, center.y); @@ -59,6 +59,17 @@ export class Sprite extends Component { ctx.globalAlpha = opacity; } + if (backgroundText) { + // draw text + const { fillStyle, font, textAlign, text } = backgroundText; + ctx.fillStyle = fillStyle; + ctx.font = font; + ctx.textAlign = textAlign; + + const height = ctx.measureText("M").width; + ctx.fillText(text, center.x, center.y + height / 2); + } + ctx.drawImage( this.sheet, ...this.getSpriteArgs(), diff --git a/src/engine/components/Text.ts b/src/engine/components/Text.ts new file mode 100644 index 0000000..94dc7a7 --- /dev/null +++ b/src/engine/components/Text.ts @@ -0,0 +1,22 @@ +import { Component, ComponentNames } from "."; + +export class Text extends Component { + public text: string = ""; + public fillStyle: string; + public font: string; + public textAlign: CanvasTextAlign; + + constructor( + text: string, + fillStyle = "white", + font = "25px scientifica", + textAlign: CanvasTextAlign = "center", + ) { + super(ComponentNames.Text); + + this.text = text; + this.fillStyle = fillStyle; + this.font = font; + this.textAlign = textAlign; + } +} diff --git a/src/engine/components/index.ts b/src/engine/components/index.ts index 4ae886a..104ba2d 100644 --- a/src/engine/components/index.ts +++ b/src/engine/components/index.ts @@ -9,3 +9,5 @@ export * from "./Highlight"; export * from "./Interactable"; export * from "./Pushable"; export * from "./Colliding"; +export * from "./LambdaSpawn"; +export * from "./Text"; diff --git a/src/engine/config/assets.ts b/src/engine/config/assets.ts index bf41461..5ce13e8 100644 --- a/src/engine/config/assets.ts +++ b/src/engine/config/assets.ts @@ -1,5 +1,10 @@ import { type SpriteSpec, SPRITE_SPECS } from "."; +export const FONT = new FontFace("scientifica", "url(/fonts/scientifica.ttf)"); +FONT.load().then((font) => { + document.fonts.add(font); +}); + export const IMAGES = new Map(); export const loadSpritesIntoImageElements = ( @@ -37,5 +42,6 @@ export const loadAssets = () => (key) => SPRITE_SPECS.get(key) as SpriteSpec, ), ), + FONT.load(), // TODO: Sound ]); diff --git a/src/engine/config/sprites.ts b/src/engine/config/sprites.ts index 7cb8adf..39ad260 100644 --- a/src/engine/config/sprites.ts +++ b/src/engine/config/sprites.ts @@ -4,6 +4,7 @@ export enum Sprites { PLAYER, FUNCTION_BOX, WALL, + LAMBDA_FACTORY, } export interface SpriteSpec { @@ -56,3 +57,12 @@ const wallSpriteSpec = { sheet: "/assets/wall.png", }; SPRITE_SPECS.set(Sprites.WALL, wallSpriteSpec); + +const lambdaFactorySpriteSpec = { + msPerFrame: 200, + width: 64, + height: 64, + frames: 3, + sheet: "/assets/function_factory.png", +}; +SPRITE_SPECS.set(Sprites.LAMBDA_FACTORY, lambdaFactorySpriteSpec); diff --git a/src/engine/entities/EntityNames.ts b/src/engine/entities/EntityNames.ts index ffcc937..3ad31d0 100644 --- a/src/engine/entities/EntityNames.ts +++ b/src/engine/entities/EntityNames.ts @@ -2,4 +2,5 @@ export namespace EntityNames { export const Player = "Player"; export const FunctionBox = "FunctionBox"; export const Wall = "Wall"; + export const LambdaFactory = "LambdaFactory"; } diff --git a/src/engine/entities/FunctionBox.ts b/src/engine/entities/FunctionBox.ts index e5d031a..b7015f2 100644 --- a/src/engine/entities/FunctionBox.ts +++ b/src/engine/entities/FunctionBox.ts @@ -57,7 +57,7 @@ export class FunctionBox extends Entity { this.addComponent( new Highlight( - () => this.onHighlight(), + (_direction) => this.onHighlight(), () => this.onUnhighlight(), ), ); diff --git a/src/engine/entities/LambdaFactory.ts b/src/engine/entities/LambdaFactory.ts new file mode 100644 index 0000000..1483b9d --- /dev/null +++ b/src/engine/entities/LambdaFactory.ts @@ -0,0 +1,109 @@ +import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config"; +import { Entity, EntityNames } from "."; +import { + BoundingBox, + Colliding, + ComponentNames, + Grid, + Highlight, + Interactable, + LambdaSpawn, + Sprite, + Text, +} from "../components"; +import { Coord2D, Direction } from "../interfaces"; +import { openModal, closeModal } from "../utils"; + +export class LambdaFactory extends Entity { + private static spriteSpec: SpriteSpec = SPRITE_SPECS.get( + Sprites.LAMBDA_FACTORY, + ) as SpriteSpec; + + private code: string; + private spawns: number; + + constructor(gridPosition: Coord2D, code: string, spawns: number) { + super(EntityNames.LambdaFactory); + + this.code = code; + this.spawns = spawns; + + this.addComponent( + new BoundingBox( + { + x: 0, + y: 0, + }, + { + width: LambdaFactory.spriteSpec.width, + height: LambdaFactory.spriteSpec.height, + }, + 0, + ), + ); + + this.addComponent(new Text(spawns.toString())); + + this.addComponent(new Colliding()); + + this.addComponent(new LambdaSpawn(this.spawns, this.code)); + + this.addComponent(new Grid(gridPosition)); + + this.addComponent( + new Sprite( + IMAGES.get(LambdaFactory.spriteSpec.sheet)!, + { x: 0, y: 0 }, + { + width: LambdaFactory.spriteSpec.width, + height: LambdaFactory.spriteSpec.height, + }, + LambdaFactory.spriteSpec.msPerFrame, + LambdaFactory.spriteSpec.frames, + ), + ); + + this.addComponent( + new Highlight( + (direction) => this.onHighlight(direction), + () => this.onUnhighlight(), + ), + ); + } + + private onUnhighlight() { + closeModal(); + this.removeComponent(ComponentNames.Interactable); + } + + private onHighlight(direction: Direction) { + if (direction === Direction.LEFT || direction === Direction.RIGHT) { + const interaction = () => { + const spawner = this.getComponent( + ComponentNames.LambdaSpawn, + ); + spawner.spawn(direction); + + const text = this.getComponent(ComponentNames.Text); + text.text = spawner.spawnsLeft.toString(); + this.addComponent(text); + }; + + this.addComponent(new Interactable(interaction)); + return; + } + + let modalOpen = false; + const interaction = () => { + if (modalOpen) { + modalOpen = false; + closeModal(); + return; + } + modalOpen = true; + openModal(this.code); + }; + + this.addComponent(new Interactable(interaction)); + } +} diff --git a/src/engine/entities/index.ts b/src/engine/entities/index.ts index e63b272..a049350 100644 --- a/src/engine/entities/index.ts +++ b/src/engine/entities/index.ts @@ -3,3 +3,4 @@ export * from "./EntityNames"; export * from "./Player"; export * from "./FunctionBox"; export * from "./Wall"; +export * from "./LambdaFactory"; diff --git a/src/engine/interfaces/Draw.ts b/src/engine/interfaces/Draw.ts index 6561a01..78de7ed 100644 --- a/src/engine/interfaces/Draw.ts +++ b/src/engine/interfaces/Draw.ts @@ -6,4 +6,10 @@ export interface DrawArgs { tint?: string; opacity?: number; rotation?: number; + backgroundText?: { + text: string; + font: string; + fillStyle: string; + textAlign: CanvasTextAlign; + }; } diff --git a/src/engine/systems/Grid.ts b/src/engine/systems/Grid.ts index 28ca5ea..8756320 100644 --- a/src/engine/systems/Grid.ts +++ b/src/engine/systems/Grid.ts @@ -39,7 +39,7 @@ export class Grid extends System { } private highlightEntitiesLookedAt(game: Game) { - const highlightableEntities = new Set(); + const highlightableEntities: [string, Direction][] = []; game.forEachEntityWithComponent( ComponentNames.FacingDirection, @@ -64,23 +64,23 @@ export class Grid extends System { } this.grid[lookingAt.y][lookingAt.x].forEach((id) => { - highlightableEntities.add(id); + highlightableEntities.push([id, facingDirection.currentDirection]); }); }, ); - highlightableEntities.forEach((id) => { + highlightableEntities.forEach(([id, direction]) => { const entity = game.getEntity(id)!; if (entity.hasComponent(ComponentNames.Highlight)) { const highlight = entity.getComponent( ComponentNames.Highlight, )!; - highlight.highlight(); + highlight.highlight(direction); } }); game.forEachEntityWithComponent(ComponentNames.Highlight, (entity) => { - if (!highlightableEntities.has(entity.id)) { + if (!highlightableEntities.find(([id]) => id === entity.id)) { const highlight = entity.getComponent( ComponentNames.Highlight, )!; diff --git a/src/engine/systems/LambdaFactory.ts b/src/engine/systems/LambdaFactory.ts new file mode 100644 index 0000000..1263eae --- /dev/null +++ b/src/engine/systems/LambdaFactory.ts @@ -0,0 +1,34 @@ +import { System, SystemNames } from "."; +import { Game } from ".."; +import { ComponentNames, Grid, LambdaSpawn } from "../components"; +import { FunctionBox } from "../entities"; + +export class LambdaFactory extends System { + constructor() { + super(SystemNames.LambdaFactory); + } + + public update(_dt: number, game: Game) { + game.forEachEntityWithComponent(ComponentNames.LambdaSpawn, (entity) => { + const lambdaSpawn = entity.getComponent( + ComponentNames.LambdaSpawn, + )!; + const hasGrid = entity.hasComponent(SystemNames.Grid); + + if (!lambdaSpawn.direction || !hasGrid) { + return; + } + + const grid = entity.getComponent(SystemNames.Grid)!; + + const lambda = new FunctionBox(grid.gridPosition, lambdaSpawn.code); + const lambdaGrid = lambda.getComponent(SystemNames.Grid)!; + lambdaGrid.movingDirection = lambdaSpawn.direction; + lambda.addComponent(lambdaGrid); + game.addEntity(lambda); + + lambdaSpawn.direction = null; + entity.addComponent(lambdaSpawn); + }); + } +} diff --git a/src/engine/systems/Render.ts b/src/engine/systems/Render.ts index 83daa52..f273deb 100644 --- a/src/engine/systems/Render.ts +++ b/src/engine/systems/Render.ts @@ -1,5 +1,11 @@ import { System, SystemNames } from "."; -import { BoundingBox, ComponentNames, Highlight, Sprite } from "../components"; +import { + BoundingBox, + Text, + ComponentNames, + Highlight, + Sprite, +} from "../components"; import { Game } from ".."; import { clamp } from "../utils"; import { DrawArgs } from "../interfaces"; @@ -50,6 +56,15 @@ export class Render extends System { ); drawArgs.tint = highlight.isHighlighted ? "red" : undefined; } + if (entity.hasComponent(ComponentNames.Text)) { + const text = entity.getComponent(ComponentNames.Text); + drawArgs.backgroundText = { + text: text.text, + font: text.font, + fillStyle: text.fillStyle, + textAlign: text.textAlign, + }; + } sprite.draw(this.ctx, drawArgs); }); diff --git a/src/engine/systems/SystemNames.ts b/src/engine/systems/SystemNames.ts index 0de5857..85d1539 100644 --- a/src/engine/systems/SystemNames.ts +++ b/src/engine/systems/SystemNames.ts @@ -3,4 +3,5 @@ export namespace SystemNames { export const Input = "Input"; export const FacingDirection = "FacingDirection"; export const Grid = "Grid"; + export const LambdaFactory = "LambdaFactory"; } diff --git a/src/engine/systems/index.ts b/src/engine/systems/index.ts index 18ffa2e..4490ee2 100644 --- a/src/engine/systems/index.ts +++ b/src/engine/systems/index.ts @@ -4,3 +4,4 @@ export * from "./Render"; export * from "./Input"; export * from "./FacingDirection"; export * from "./Grid"; +export * from "./LambdaFactory";