summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-03-02 01:07:55 -0700
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-03-02 01:07:55 -0700
commit1ec5a8d088f599d094f387abc6014f228607b605 (patch)
tree0d0e0271b6d5db354a56337c5de9df1a933e767b
parenta333ce8845de4269d6a6f5523d2273da11fe271c (diff)
downloadthe-abstraction-engine-1ec5a8d088f599d094f387abc6014f228607b605.tar.gz
the-abstraction-engine-1ec5a8d088f599d094f387abc6014f228607b605.zip
add interactable component
-rw-r--r--public/assets/function_block.pngbin0 -> 1731 bytes
-rw-r--r--public/assets/function_factory.pngbin0 -> 1302 bytes
-rw-r--r--public/assets/key.pngbin0 -> 1086 bytes
-rw-r--r--public/assets/locked_door.pngbin0 -> 1845 bytes
-rw-r--r--public/assets/wall.pngbin0 -> 2247 bytes
-rw-r--r--src/components/GameCanvas.tsx4
-rw-r--r--src/engine/TheAbstractionEngine.ts4
-rw-r--r--src/engine/components/ComponentNames.ts2
-rw-r--r--src/engine/components/Highlight.ts7
-rw-r--r--src/engine/components/Interactable.ts15
-rw-r--r--src/engine/components/index.ts2
-rw-r--r--src/engine/config/constants.ts2
-rw-r--r--src/engine/config/sprites.ts2
-rw-r--r--src/engine/entities/Entity.ts12
-rw-r--r--src/engine/entities/FunctionBox.ts27
-rw-r--r--src/engine/systems/FacingDirection.ts1
-rw-r--r--src/engine/systems/Grid.ts149
-rw-r--r--src/engine/systems/Input.ts85
-rw-r--r--src/engine/systems/Render.ts4
-rw-r--r--src/main.tsx7
20 files changed, 258 insertions, 65 deletions
diff --git a/public/assets/function_block.png b/public/assets/function_block.png
new file mode 100644
index 0000000..a54a4a0
--- /dev/null
+++ b/public/assets/function_block.png
Binary files differ
diff --git a/public/assets/function_factory.png b/public/assets/function_factory.png
new file mode 100644
index 0000000..2c46758
--- /dev/null
+++ b/public/assets/function_factory.png
Binary files differ
diff --git a/public/assets/key.png b/public/assets/key.png
new file mode 100644
index 0000000..c6d07a4
--- /dev/null
+++ b/public/assets/key.png
Binary files differ
diff --git a/public/assets/locked_door.png b/public/assets/locked_door.png
new file mode 100644
index 0000000..4a90d84
--- /dev/null
+++ b/public/assets/locked_door.png
Binary files differ
diff --git a/public/assets/wall.png b/public/assets/wall.png
new file mode 100644
index 0000000..fe9390d
--- /dev/null
+++ b/public/assets/wall.png
Binary files differ
diff --git a/src/components/GameCanvas.tsx b/src/components/GameCanvas.tsx
index 5cb40a6..0ea7180 100644
--- a/src/components/GameCanvas.tsx
+++ b/src/components/GameCanvas.tsx
@@ -8,10 +8,10 @@ export interface GameCanvasProps {
export const GameCanvas = ({ width, height }: GameCanvasProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
- const [_game, setGame] = useState<TheAbstractionEngine>();
+ const [game, setGame] = useState<TheAbstractionEngine>();
useEffect(() => {
- if (canvasRef.current) {
+ if (canvasRef.current && !game) {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
if (ctx) {
diff --git a/src/engine/TheAbstractionEngine.ts b/src/engine/TheAbstractionEngine.ts
index 3859447..78d4f88 100644
--- a/src/engine/TheAbstractionEngine.ts
+++ b/src/engine/TheAbstractionEngine.ts
@@ -39,8 +39,10 @@ export class TheAbstractionEngine {
const player = new Player();
this.game.addEntity(player);
- const box = new FunctionBox({ x: 5, y: 5 });
+ const box = new FunctionBox({ x: 3, y: 1 });
this.game.addEntity(box);
+ const box2 = new FunctionBox({ x: 4, y: 1 });
+ this.game.addEntity(box2);
}
public play() {
diff --git a/src/engine/components/ComponentNames.ts b/src/engine/components/ComponentNames.ts
index 032d08a..da6c37a 100644
--- a/src/engine/components/ComponentNames.ts
+++ b/src/engine/components/ComponentNames.ts
@@ -4,4 +4,6 @@ export namespace ComponentNames {
export const Grid = "Grid";
export const BoundingBox = "BoundingBox";
export const Control = "Control";
+ export const Highlight = "Highlight";
+ export const Interactable = "Interactable";
}
diff --git a/src/engine/components/Highlight.ts b/src/engine/components/Highlight.ts
new file mode 100644
index 0000000..49d9f96
--- /dev/null
+++ b/src/engine/components/Highlight.ts
@@ -0,0 +1,7 @@
+import { Component, ComponentNames } from ".";
+
+export class Highlight extends Component {
+ constructor() {
+ super(ComponentNames.Highlight);
+ }
+}
diff --git a/src/engine/components/Interactable.ts b/src/engine/components/Interactable.ts
new file mode 100644
index 0000000..2937596
--- /dev/null
+++ b/src/engine/components/Interactable.ts
@@ -0,0 +1,15 @@
+import { Component, ComponentNames } from ".";
+
+export class Interactable extends Component {
+ private interaction: Function;
+
+ constructor(interaction: Function) {
+ super(ComponentNames.Interactable);
+
+ this.interaction = interaction;
+ }
+
+ public interact() {
+ this.interaction();
+ }
+}
diff --git a/src/engine/components/index.ts b/src/engine/components/index.ts
index e9f8de9..c470eff 100644
--- a/src/engine/components/index.ts
+++ b/src/engine/components/index.ts
@@ -5,3 +5,5 @@ export * from "./FacingDirection";
export * from "./Grid";
export * from "./BoundingBox";
export * from "./Control";
+export * from "./Highlight";
+export * from "./Interactable";
diff --git a/src/engine/config/constants.ts b/src/engine/config/constants.ts
index 0b07108..5dcd60c 100644
--- a/src/engine/config/constants.ts
+++ b/src/engine/config/constants.ts
@@ -42,7 +42,7 @@ export namespace KeyConstants {
}
export namespace PhysicsConstants {
- export const GRID_MOVEMENT_VELOCITY = 2;
+ export const GRID_MOVEMENT_VELOCITY = 1;
}
export namespace Miscellaneous {
diff --git a/src/engine/config/sprites.ts b/src/engine/config/sprites.ts
index e62d714..eab65fd 100644
--- a/src/engine/config/sprites.ts
+++ b/src/engine/config/sprites.ts
@@ -43,6 +43,6 @@ const functionBoxSpriteSpec = {
width: 64,
height: 64,
frames: 3,
- sheet: "/assets/border.png",
+ sheet: "/assets/function_block.png",
};
SPRITE_SPECS.set(Sprites.FUNCTION_BOX, functionBoxSpriteSpec);
diff --git a/src/engine/entities/Entity.ts b/src/engine/entities/Entity.ts
index 2cc2ac3..d5a8e6e 100644
--- a/src/engine/entities/Entity.ts
+++ b/src/engine/entities/Entity.ts
@@ -7,14 +7,26 @@ export abstract class Entity {
public components: Map<string, Component>;
public name: string;
+ protected hooks: Map<string, { add: Function; remove: Function }>;
+
constructor(name: string, id: string = (Entity.Id++).toString()) {
this.name = name;
this.id = id;
this.components = new Map();
+ this.hooks = new Map();
}
public addComponent(component: Component) {
+ const hadBeforeSet = this.components.has(component.name);
this.components.set(component.name, component);
+ if (!hadBeforeSet) {
+ this.hooks.get(component.name)?.add();
+ }
+ }
+
+ public removeComponent(name: string) {
+ this.components.delete(name);
+ this.hooks.get(name)?.remove();
}
public getComponent<T extends Component>(name: string): T {
diff --git a/src/engine/entities/FunctionBox.ts b/src/engine/entities/FunctionBox.ts
index e6c41c2..393514e 100644
--- a/src/engine/entities/FunctionBox.ts
+++ b/src/engine/entities/FunctionBox.ts
@@ -1,6 +1,12 @@
import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
import { Entity, EntityNames } from ".";
-import { BoundingBox, Grid, Sprite } from "../components";
+import {
+ BoundingBox,
+ ComponentNames,
+ Grid,
+ Interactable,
+ Sprite,
+} from "../components";
import { Coord2D } from "../interfaces";
export class FunctionBox extends Entity {
@@ -8,9 +14,13 @@ export class FunctionBox extends Entity {
Sprites.FUNCTION_BOX,
) as SpriteSpec;
- constructor(gridPosition: Coord2D) {
+ private code: string;
+
+ constructor(gridPosition: Coord2D, code: string) {
super(EntityNames.FunctionBox);
+ this.code = code;
+
this.addComponent(
new BoundingBox(
{
@@ -39,5 +49,18 @@ export class FunctionBox extends Entity {
FunctionBox.spriteSpec.frames,
),
);
+
+ this.hooks.set(ComponentNames.Highlight, {
+ add: () => {
+ this.addComponent(new Interactable(() => this.viewInsides()));
+ },
+ remove: () => {
+ this.removeComponent(ComponentNames.Interactable);
+ },
+ });
+ }
+
+ public viewInsides() {
+ console.log("I am a function box!");
}
}
diff --git a/src/engine/systems/FacingDirection.ts b/src/engine/systems/FacingDirection.ts
index f831bf6..042484a 100644
--- a/src/engine/systems/FacingDirection.ts
+++ b/src/engine/systems/FacingDirection.ts
@@ -51,6 +51,7 @@ export class FacingDirection extends System {
: angleToDirection(angle);
facingDirection.setDirection(direction);
+ entity.addComponent(facingDirection);
const oldSprite = entity.getComponent<Sprite>(ComponentNames.Sprite);
const sprite = facingDirection.directionSprites.get(direction)!;
diff --git a/src/engine/systems/Grid.ts b/src/engine/systems/Grid.ts
index 0869fd6..c9cab6b 100644
--- a/src/engine/systems/Grid.ts
+++ b/src/engine/systems/Grid.ts
@@ -1,9 +1,12 @@
import { System, SystemNames } from ".";
import { Game } from "..";
+import { Entity } from "../entities";
import { PhysicsConstants } from "../config";
import {
BoundingBox,
ComponentNames,
+ FacingDirection,
+ Highlight,
Grid as GridComponent,
} from "../components";
import { Coord2D, Direction, Dimension2D } from "../interfaces";
@@ -28,9 +31,109 @@ export class Grid extends System {
public update(dt: number, game: Game) {
this.putUninitializedEntitiesInGrid(game);
this.rebuildGrid(game);
+
+ this.highlightEntitiesLookedAt(game);
+ this.propogateEntityMovements(game);
+
this.updateMovingEntities(dt, game);
}
+ private highlightEntitiesLookedAt(game: Game) {
+ const highlightableEntities = new Set<string>();
+
+ game.forEachEntityWithComponent(
+ ComponentNames.FacingDirection,
+ (entity) => {
+ if (!entity.hasComponent(ComponentNames.Grid)) {
+ return;
+ }
+
+ const grid = entity.getComponent<GridComponent>(ComponentNames.Grid)!;
+ const facingDirection = entity.getComponent<FacingDirection>(
+ ComponentNames.FacingDirection,
+ )!;
+ const lookingAt = this.getNewGridPosition(
+ grid.gridPosition,
+ facingDirection.currentDirection,
+ );
+ if (
+ facingDirection.currentDirection === Direction.NONE ||
+ this.isOutOfBounds(lookingAt)
+ ) {
+ return;
+ }
+
+ this.grid[lookingAt.y][lookingAt.x].forEach((id) => {
+ highlightableEntities.add(id);
+ });
+ },
+ );
+
+ highlightableEntities.forEach((id) => {
+ const entity = game.getEntity(id)!;
+ if (!entity.hasComponent(ComponentNames.Highlight)) {
+ entity.addComponent(new Highlight());
+ }
+ });
+
+ game.forEachEntityWithComponent(ComponentNames.Highlight, (entity) => {
+ if (!highlightableEntities.has(entity.id)) {
+ entity.removeComponent(ComponentNames.Highlight);
+ }
+ });
+ }
+
+ private propogateEntityMovements(game: Game) {
+ const movingEntities: Entity[] = [];
+ game.forEachEntityWithComponent(ComponentNames.Grid, (entity) => {
+ const grid = entity.getComponent<GridComponent>(ComponentNames.Grid)!;
+ if (grid.movingDirection !== Direction.NONE) {
+ movingEntities.push(entity);
+ }
+ });
+
+ // for each moving entity, check the entities in the grid cell it's moving to
+ // if they are pushable, move them in the same direction
+ // continue until no more pushable entities are found
+ for (const entity of movingEntities) {
+ const grid = entity.getComponent<GridComponent>(ComponentNames.Grid)!;
+ let nextGridPosition = this.getNewGridPosition(
+ grid.gridPosition,
+ grid.movingDirection,
+ );
+ while (!this.isOutOfBounds(nextGridPosition)) {
+ const { x, y } = nextGridPosition;
+ const entities = Array.from(this.grid[y][x]).map(
+ (id) => game.getEntity(id)!,
+ );
+
+ const pushableEntities = entities.filter((entity) => {
+ if (!entity.hasComponent(ComponentNames.Grid)) return false;
+
+ const { pushable, movingDirection } =
+ entity.getComponent<GridComponent>(ComponentNames.Grid)!;
+ return movingDirection === Direction.NONE && pushable;
+ });
+ if (pushableEntities.length === 0) {
+ break;
+ }
+
+ for (const pushableEntity of pushableEntities) {
+ const pushableGrid = pushableEntity.getComponent<GridComponent>(
+ ComponentNames.Grid,
+ )!;
+ pushableGrid.movingDirection = grid.movingDirection;
+ pushableEntity.addComponent(pushableEntity);
+ }
+
+ nextGridPosition = this.getNewGridPosition(
+ nextGridPosition,
+ grid.movingDirection,
+ );
+ }
+ }
+ }
+
private putUninitializedEntitiesInGrid(game: Game) {
game.forEachEntityWithComponent(ComponentNames.Grid, (entity) => {
const grid = entity.getComponent<GridComponent>(ComponentNames.Grid)!;
@@ -71,23 +174,10 @@ export class Grid extends System {
ComponentNames.BoundingBox,
)!;
- let { x: newX, y: newY } = grid.gridPosition;
- switch (grid.movingDirection) {
- case Direction.LEFT:
- newX -= 1;
- break;
- case Direction.UP:
- newY -= 1;
- break;
- case Direction.DOWN:
- newY += 1;
- break;
- case Direction.RIGHT:
- newX += 1;
- break;
- }
-
- const newGridPosition = { x: newX, y: newY };
+ const newGridPosition = this.getNewGridPosition(
+ grid.gridPosition,
+ grid.movingDirection,
+ );
if (this.isOutOfBounds(newGridPosition)) {
grid.movingDirection = Direction.NONE;
entity.addComponent(grid);
@@ -137,6 +227,26 @@ export class Grid extends System {
});
}
+ private getNewGridPosition(prev: Coord2D, direction: Direction) {
+ let { x: newX, y: newY } = prev;
+ switch (direction) {
+ case Direction.LEFT:
+ newX -= 1;
+ break;
+ case Direction.UP:
+ newY -= 1;
+ break;
+ case Direction.DOWN:
+ newY += 1;
+ break;
+ case Direction.RIGHT:
+ newX += 1;
+ break;
+ }
+
+ return { x: newX, y: newY };
+ }
+
private isEntityPastCenterWhenMoving(
direction: Direction,
gridPosition: Coord2D,
@@ -185,7 +295,7 @@ export class Grid extends System {
this.grid.forEach((row) =>
row.forEach((cell) => {
for (const id of cell) {
- if (!movedEntities.has(id)) {
+ if (movedEntities.has(id)) {
cell.delete(id);
}
}
@@ -194,7 +304,8 @@ export class Grid extends System {
movedEntities.forEach((id) => {
const entity = game.getEntity(id)!;
const grid = entity.getComponent<GridComponent>(ComponentNames.Grid)!;
- this.grid[grid.gridPosition.y][grid.gridPosition.x].add(id);
+ const { x, y } = grid.gridPosition;
+ this.grid[y][x].add(id);
});
}
}
diff --git a/src/engine/systems/Input.ts b/src/engine/systems/Input.ts
index e9691e0..df4d651 100644
--- a/src/engine/systems/Input.ts
+++ b/src/engine/systems/Input.ts
@@ -1,6 +1,6 @@
import { SystemNames, System } from ".";
import { Game } from "..";
-import { ComponentNames } from "../components";
+import { ComponentNames, Grid, Interactable } from "../components";
import { Control } from "../components/Control";
import { Action, KeyConstants } from "../config";
import { Entity } from "../entities";
@@ -31,11 +31,30 @@ export class Input extends System {
public update(_dt: number, game: Game) {
game.forEachEntityWithComponent(ComponentNames.Control, (entity) =>
- this.handleInput(entity),
+ this.handleMovement(entity),
);
+ game.forEachEntityWithComponent(ComponentNames.Interactable, (entity) =>
+ this.handleInteraction(entity),
+ );
+ }
+
+ private handleInteraction(entity: Entity) {
+ const interactable = entity.getComponent<Interactable>(
+ ComponentNames.Interactable,
+ );
+
+ const interact = this.hasSomeKey(
+ KeyConstants.ActionKeys.get(Action.INTERACT),
+ );
+
+ if (!interact) {
+ return;
+ }
+
+ interactable.interact();
}
- public handleInput(entity: Entity) {
+ public handleMovement(entity: Entity) {
const controlComponent = entity.getComponent<Control>(
ComponentNames.Control,
);
@@ -50,36 +69,38 @@ export class Input extends System {
Action.MOVE_RIGHT,
Action.MOVE_DOWN,
].map((action) => this.hasSomeKey(KeyConstants.ActionKeys.get(action)));
- if (hasGrid) {
- const gridComponent = entity.getComponent(ComponentNames.Grid);
- if (gridComponent.movingDirection !== Direction.NONE) {
- return;
- }
-
- if (moveUp) {
- gridComponent.movingDirection = Direction.UP;
- KeyConstants.ActionKeys.get(Action.MOVE_UP)!.forEach((key) =>
- this.keyReleased(key),
- );
- } else if (moveLeft) {
- gridComponent.movingDirection = Direction.LEFT;
- KeyConstants.ActionKeys.get(Action.MOVE_LEFT)!.forEach((key) =>
- this.keyReleased(key),
- );
- } else if (moveRight) {
- gridComponent.movingDirection = Direction.RIGHT;
- KeyConstants.ActionKeys.get(Action.MOVE_RIGHT)!.forEach((key) =>
- this.keyReleased(key),
- );
- } else if (moveDown) {
- gridComponent.movingDirection = Direction.DOWN;
- KeyConstants.ActionKeys.get(Action.MOVE_DOWN)!.forEach((key) =>
- this.keyReleased(key),
- );
- }
-
- entity.addComponent(gridComponent);
+ if (!hasGrid) {
+ return;
+ }
+
+ const gridComponent = entity.getComponent<Grid>(ComponentNames.Grid)!;
+ if (gridComponent.movingDirection !== Direction.NONE) {
+ return;
}
+
+ if (moveUp) {
+ gridComponent.movingDirection = Direction.UP;
+ KeyConstants.ActionKeys.get(Action.MOVE_UP)!.forEach((key) =>
+ this.keyReleased(key),
+ );
+ } else if (moveLeft) {
+ gridComponent.movingDirection = Direction.LEFT;
+ KeyConstants.ActionKeys.get(Action.MOVE_LEFT)!.forEach((key) =>
+ this.keyReleased(key),
+ );
+ } else if (moveRight) {
+ gridComponent.movingDirection = Direction.RIGHT;
+ KeyConstants.ActionKeys.get(Action.MOVE_RIGHT)!.forEach((key) =>
+ this.keyReleased(key),
+ );
+ } else if (moveDown) {
+ gridComponent.movingDirection = Direction.DOWN;
+ KeyConstants.ActionKeys.get(Action.MOVE_DOWN)!.forEach((key) =>
+ this.keyReleased(key),
+ );
+ }
+
+ entity.addComponent(gridComponent);
}
private hasSomeKey(keys?: string[]): boolean {
diff --git a/src/engine/systems/Render.ts b/src/engine/systems/Render.ts
index 6f539c0..7cb5d81 100644
--- a/src/engine/systems/Render.ts
+++ b/src/engine/systems/Render.ts
@@ -2,6 +2,7 @@ import { System, SystemNames } from ".";
import { BoundingBox, ComponentNames, Sprite } from "../components";
import { Game } from "..";
import { clamp } from "../utils";
+import { DrawArgs } from "../interfaces";
export class Render extends System {
private ctx: CanvasRenderingContext2D;
@@ -38,10 +39,11 @@ export class Render extends System {
return;
}
- const drawArgs = {
+ const drawArgs: DrawArgs = {
center: boundingBox.center,
dimension: boundingBox.dimension,
rotation: boundingBox.rotation,
+ tint: entity.hasComponent(ComponentNames.Highlight) ? "red" : undefined,
};
sprite.draw(this.ctx, drawArgs);
diff --git a/src/main.tsx b/src/main.tsx
index 94b1039..7404467 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,9 +1,4 @@
-import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "./App.tsx";
import "./css/style.css";
-ReactDOM.createRoot(document.getElementById("root")!).render(
- <React.StrictMode>
- <App />
- </React.StrictMode>,
-);
+ReactDOM.createRoot(document.getElementById("root")!).render(<App />);