summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-03-02 05:30:17 -0700
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-03-02 05:30:17 -0700
commitcbb88091bdf69cc8752ef1cc3662dc0b99e3ead6 (patch)
treeb10e56d8f5281a9e91db8e2a923f91b216129459
parent4b9349b3f8bee21eb086cfd6e7668532a50e6048 (diff)
downloadthe-abstraction-engine-cbb88091bdf69cc8752ef1cc3662dc0b99e3ead6.tar.gz
the-abstraction-engine-cbb88091bdf69cc8752ef1cc3662dc0b99e3ead6.zip
key lock / player curry collisions
-rw-r--r--public/assets/curry.pngbin0 -> 1839 bytes
-rw-r--r--src/engine/Game.ts4
-rw-r--r--src/engine/TheAbstractionEngine.ts21
-rw-r--r--src/engine/config/sprites.ts30
-rw-r--r--src/engine/entities/Curry.ts45
-rw-r--r--src/engine/entities/EntityNames.ts3
-rw-r--r--src/engine/entities/Key.ts45
-rw-r--r--src/engine/entities/LockedDoor.ts45
-rw-r--r--src/engine/entities/index.ts3
-rw-r--r--src/engine/systems/Collision.ts103
-rw-r--r--src/engine/systems/Grid.ts36
-rw-r--r--src/engine/systems/SystemNames.ts1
-rw-r--r--src/engine/systems/index.ts1
13 files changed, 325 insertions, 12 deletions
diff --git a/public/assets/curry.png b/public/assets/curry.png
new file mode 100644
index 0000000..348063c
--- /dev/null
+++ b/public/assets/curry.png
Binary files differ
diff --git a/src/engine/Game.ts b/src/engine/Game.ts
index 2df9f17..9fe9e87 100644
--- a/src/engine/Game.ts
+++ b/src/engine/Game.ts
@@ -25,6 +25,10 @@ export class Game {
this.running = true;
}
+ public stop() {
+ this.running = false;
+ }
+
public addEntity(entity: Entity) {
this.entities.set(entity.id, entity);
}
diff --git a/src/engine/TheAbstractionEngine.ts b/src/engine/TheAbstractionEngine.ts
index 7f71fd5..e84093f 100644
--- a/src/engine/TheAbstractionEngine.ts
+++ b/src/engine/TheAbstractionEngine.ts
@@ -1,12 +1,21 @@
import { Game } from ".";
import { Miscellaneous, loadAssets } from "./config";
-import { Player, FunctionBox, Wall, LambdaFactory } from "./entities";
+import {
+ Player,
+ FunctionBox,
+ Wall,
+ LambdaFactory,
+ Key,
+ LockedDoor,
+ Curry,
+} from "./entities";
import {
Grid,
FacingDirection,
Input,
Render,
LambdaFactory as LambdaFactorySpawnSystem,
+ Collision,
} from "./systems";
export class TheAbstractionEngine {
@@ -39,6 +48,7 @@ export class TheAbstractionEngine {
},
),
new LambdaFactorySpawnSystem(),
+ new Collision(),
new Render(this.ctx),
].forEach((system) => this.game.addSystem(system));
@@ -55,6 +65,15 @@ export class TheAbstractionEngine {
const factory = new LambdaFactory({ x: 6, y: 6 }, "λ x . (x)", 10);
this.game.addEntity(factory);
+
+ const lockedDoor = new LockedDoor({ x: 8, y: 8 });
+ this.game.addEntity(lockedDoor);
+
+ const key = new Key({ x: 7, y: 7 });
+ this.game.addEntity(key);
+
+ const curry = new Curry({ x: 9, y: 8 });
+ this.game.addEntity(curry);
}
public play() {
diff --git a/src/engine/config/sprites.ts b/src/engine/config/sprites.ts
index 39ad260..cca5961 100644
--- a/src/engine/config/sprites.ts
+++ b/src/engine/config/sprites.ts
@@ -5,6 +5,9 @@ export enum Sprites {
FUNCTION_BOX,
WALL,
LAMBDA_FACTORY,
+ KEY,
+ LOCKED_DOOR,
+ CURRY,
}
export interface SpriteSpec {
@@ -66,3 +69,30 @@ const lambdaFactorySpriteSpec = {
sheet: "/assets/function_factory.png",
};
SPRITE_SPECS.set(Sprites.LAMBDA_FACTORY, lambdaFactorySpriteSpec);
+
+const keySpriteSpec = {
+ msPerFrame: 200,
+ width: 64,
+ height: 64,
+ frames: 3,
+ sheet: "/assets/key.png",
+};
+SPRITE_SPECS.set(Sprites.KEY, keySpriteSpec);
+
+const lockedDoorSpriteSpec = {
+ msPerFrame: 200,
+ width: 64,
+ height: 64,
+ frames: 3,
+ sheet: "/assets/locked_door.png",
+};
+SPRITE_SPECS.set(Sprites.LOCKED_DOOR, lockedDoorSpriteSpec);
+
+const currySpriteSpec = {
+ msPerFrame: 200,
+ width: 64,
+ height: 64,
+ frames: 3,
+ sheet: "/assets/curry.png",
+};
+SPRITE_SPECS.set(Sprites.CURRY, currySpriteSpec);
diff --git a/src/engine/entities/Curry.ts b/src/engine/entities/Curry.ts
new file mode 100644
index 0000000..85bc7ef
--- /dev/null
+++ b/src/engine/entities/Curry.ts
@@ -0,0 +1,45 @@
+import { Entity, EntityNames } from ".";
+import { BoundingBox, Colliding, Grid, Sprite } from "../components";
+import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
+import { Coord2D } from "../interfaces";
+
+export class Curry extends Entity {
+ private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(
+ Sprites.CURRY,
+ ) as SpriteSpec;
+
+ constructor(gridPosition: Coord2D) {
+ super(EntityNames.Curry);
+
+ this.addComponent(new Grid(gridPosition));
+
+ this.addComponent(new Colliding());
+
+ this.addComponent(
+ new BoundingBox(
+ {
+ x: 0,
+ y: 0,
+ },
+ {
+ width: Curry.spriteSpec.width,
+ height: Curry.spriteSpec.height,
+ },
+ 0,
+ ),
+ );
+
+ this.addComponent(
+ new Sprite(
+ IMAGES.get(Curry.spriteSpec.sheet)!,
+ { x: 0, y: 0 },
+ {
+ width: Curry.spriteSpec.width,
+ height: Curry.spriteSpec.height,
+ },
+ Curry.spriteSpec.msPerFrame,
+ Curry.spriteSpec.frames,
+ ),
+ );
+ }
+}
diff --git a/src/engine/entities/EntityNames.ts b/src/engine/entities/EntityNames.ts
index 3ad31d0..7fc69c6 100644
--- a/src/engine/entities/EntityNames.ts
+++ b/src/engine/entities/EntityNames.ts
@@ -3,4 +3,7 @@ export namespace EntityNames {
export const FunctionBox = "FunctionBox";
export const Wall = "Wall";
export const LambdaFactory = "LambdaFactory";
+ export const Key = "Key";
+ export const LockedDoor = "LockedDoor";
+ export const Curry = "Curry";
}
diff --git a/src/engine/entities/Key.ts b/src/engine/entities/Key.ts
new file mode 100644
index 0000000..7168ee8
--- /dev/null
+++ b/src/engine/entities/Key.ts
@@ -0,0 +1,45 @@
+import { Entity, EntityNames } from ".";
+import { BoundingBox, Grid, Pushable, Sprite } from "../components";
+import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
+import { Coord2D } from "../interfaces";
+
+export class Key extends Entity {
+ private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(
+ Sprites.KEY,
+ ) as SpriteSpec;
+
+ constructor(gridPosition: Coord2D) {
+ super(EntityNames.Key);
+
+ this.addComponent(new Grid(gridPosition));
+
+ this.addComponent(new Pushable());
+
+ this.addComponent(
+ new BoundingBox(
+ {
+ x: 0,
+ y: 0,
+ },
+ {
+ width: Key.spriteSpec.width,
+ height: Key.spriteSpec.height,
+ },
+ 0,
+ ),
+ );
+
+ this.addComponent(
+ new Sprite(
+ IMAGES.get(Key.spriteSpec.sheet)!,
+ { x: 0, y: 0 },
+ {
+ width: Key.spriteSpec.width,
+ height: Key.spriteSpec.height,
+ },
+ Key.spriteSpec.msPerFrame,
+ Key.spriteSpec.frames,
+ ),
+ );
+ }
+}
diff --git a/src/engine/entities/LockedDoor.ts b/src/engine/entities/LockedDoor.ts
new file mode 100644
index 0000000..5e364b8
--- /dev/null
+++ b/src/engine/entities/LockedDoor.ts
@@ -0,0 +1,45 @@
+import { Entity, EntityNames } from ".";
+import { BoundingBox, Colliding, Grid, Sprite } from "../components";
+import { IMAGES, SPRITE_SPECS, SpriteSpec, Sprites } from "../config";
+import { Coord2D } from "../interfaces";
+
+export class LockedDoor extends Entity {
+ private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(
+ Sprites.LOCKED_DOOR,
+ ) as SpriteSpec;
+
+ constructor(gridPosition: Coord2D) {
+ super(EntityNames.LockedDoor);
+
+ this.addComponent(new Grid(gridPosition));
+
+ this.addComponent(new Colliding());
+
+ this.addComponent(
+ new BoundingBox(
+ {
+ x: 0,
+ y: 0,
+ },
+ {
+ width: LockedDoor.spriteSpec.width,
+ height: LockedDoor.spriteSpec.height,
+ },
+ 0,
+ ),
+ );
+
+ this.addComponent(
+ new Sprite(
+ IMAGES.get(LockedDoor.spriteSpec.sheet)!,
+ { x: 0, y: 0 },
+ {
+ width: LockedDoor.spriteSpec.width,
+ height: LockedDoor.spriteSpec.height,
+ },
+ LockedDoor.spriteSpec.msPerFrame,
+ LockedDoor.spriteSpec.frames,
+ ),
+ );
+ }
+}
diff --git a/src/engine/entities/index.ts b/src/engine/entities/index.ts
index a049350..45c95c0 100644
--- a/src/engine/entities/index.ts
+++ b/src/engine/entities/index.ts
@@ -4,3 +4,6 @@ export * from "./Player";
export * from "./FunctionBox";
export * from "./Wall";
export * from "./LambdaFactory";
+export * from "./Key";
+export * from "./LockedDoor";
+export * from "./Curry";
diff --git a/src/engine/systems/Collision.ts b/src/engine/systems/Collision.ts
new file mode 100644
index 0000000..7b1b963
--- /dev/null
+++ b/src/engine/systems/Collision.ts
@@ -0,0 +1,103 @@
+import { System, SystemNames } from ".";
+import { Game } from "..";
+import { Entity, EntityNames } from "../entities";
+import { BoundingBox, Colliding, ComponentNames, Grid } from "../components";
+
+const collisionMap: Record<string, Set<string>> = {
+ [EntityNames.Key]: new Set([EntityNames.LockedDoor]),
+ [EntityNames.Curry]: new Set([EntityNames.Player]),
+};
+
+export class Collision extends System {
+ static canCollide(entityName: string, otherEntityName: string) {
+ if (collisionMap[entityName]) {
+ return collisionMap[entityName].has(otherEntityName);
+ }
+ return collisionMap[otherEntityName]?.has(entityName) ?? false;
+ }
+
+ constructor() {
+ super(SystemNames.Collision);
+ }
+
+ public update(_dt: number, game: Game) {
+ game.forEachEntityWithComponent(ComponentNames.Colliding, (entity) => {
+ if (!entity.hasComponent(ComponentNames.BoundingBox)) {
+ return;
+ }
+ const collidingBox = entity.getComponent<BoundingBox>(
+ ComponentNames.BoundingBox,
+ );
+ let collidingGrid = entity.hasComponent(ComponentNames.Grid)
+ ? entity.getComponent<Grid>(ComponentNames.Grid)
+ : null;
+
+ const collidingWith: Entity[] = [];
+ game.forEachEntityWithComponent(
+ ComponentNames.BoundingBox,
+ (otherEntity) => {
+ const otherBoundingBox = otherEntity.getComponent<BoundingBox>(
+ ComponentNames.BoundingBox,
+ );
+ let otherGrid = otherEntity.hasComponent(ComponentNames.Grid)
+ ? otherEntity.getComponent<Grid>(ComponentNames.Grid)
+ : null;
+
+ if (collidingGrid && otherGrid) {
+ if (
+ collidingGrid.gridPosition.x === otherGrid.gridPosition.x &&
+ collidingGrid.gridPosition.y === otherGrid.gridPosition.y
+ ) {
+ collidingWith.push(otherEntity);
+ }
+ return;
+ }
+
+ if (collidingBox.isCollidingWith(otherBoundingBox)) {
+ collidingWith.push(otherEntity);
+ }
+ },
+ );
+
+ for (const collision of collidingWith) {
+ this.handleCollision(entity, collision, game);
+ }
+ });
+ }
+
+ private handleCollision(entity: Entity, otherEntity: Entity, game: Game) {
+ if (!Collision.canCollide(entity.name, otherEntity.name)) {
+ return;
+ }
+
+ const keyDoorPair = [EntityNames.Key, EntityNames.LockedDoor].map((x) =>
+ [entity, otherEntity].find((y) => y.name === x),
+ );
+ const [key, door] = keyDoorPair;
+ if (key && door) {
+ this.handleKeyDoorCollision(key, door, game);
+ }
+
+ const curryPlayerPair = [EntityNames.Curry, EntityNames.Player].map((x) =>
+ [entity, otherEntity].find((y) => y.name === x),
+ );
+ const [curry, player] = curryPlayerPair;
+ if (curry && player) {
+ this.handleCurryPlayerCollision(curry, player, game);
+ }
+ }
+
+ private handleKeyDoorCollision(key: Entity, door: Entity, game: Game) {
+ game.removeEntity(key.id);
+ game.removeEntity(door.id);
+ }
+
+ private handleCurryPlayerCollision(
+ curry: Entity,
+ _player: Entity,
+ game: Game,
+ ) {
+ game.removeEntity(curry.id);
+ game.stop();
+ }
+}
diff --git a/src/engine/systems/Grid.ts b/src/engine/systems/Grid.ts
index 8756320..915335b 100644
--- a/src/engine/systems/Grid.ts
+++ b/src/engine/systems/Grid.ts
@@ -1,4 +1,4 @@
-import { System, SystemNames } from ".";
+import { Collision, System, SystemNames } from ".";
import { Game } from "..";
import { Entity } from "../entities";
import { PhysicsConstants } from "../config";
@@ -13,8 +13,8 @@ import { Coord2D, Direction, Dimension2D } from "../interfaces";
import { clamp } from "../utils";
export class Grid extends System {
- private dimension: Dimension2D;
- private grid: Set<string>[][] = [];
+ public dimension: Dimension2D;
+ public grid: Set<string>[][] = [];
constructor(
{ width: columns, height: rows }: Dimension2D,
@@ -108,10 +108,10 @@ export class Grid extends System {
grid.movingDirection = Direction.NONE;
entity.addComponent(grid); // default to not moving
- let nextGridPosition = this.getNewGridPosition(
+ let [currentPosition, nextGridPosition] = [
gridPosition,
- movingDirection,
- );
+ this.getNewGridPosition(gridPosition, movingDirection),
+ ];
const moving = new Set<string>();
moving.add(entity.id);
@@ -122,11 +122,24 @@ export class Grid extends System {
(id) => game.getEntity(id)!,
);
- if (
- entities.some((entity) =>
- entity.hasComponent(ComponentNames.Colliding),
+ const collidingEntities = entities.filter((entity) =>
+ entity.hasComponent(ComponentNames.Colliding),
+ );
+
+ if (collidingEntities.length > 0) {
+ // i.e. key going into a door or function going into an application
+ const allEntitiesInPreviousCellCanCollide = Array.from(
+ this.grid[currentPosition.y][currentPosition.x],
)
- ) {
+ .map((id) => game.getEntity(id)!)
+ .every((entity) =>
+ collidingEntities.every((collidingEntity) =>
+ Collision.canCollide(entity.name, collidingEntity.name),
+ ),
+ );
+ if (allEntitiesInPreviousCellCanCollide) {
+ break;
+ }
moving.clear();
break;
}
@@ -148,6 +161,7 @@ export class Grid extends System {
moving.add(pushableEntity.id);
}
+ currentPosition = nextGridPosition;
nextGridPosition = this.getNewGridPosition(
nextGridPosition,
movingDirection,
@@ -324,7 +338,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) || !game.getEntity(id)) {
cell.delete(id);
}
}
diff --git a/src/engine/systems/SystemNames.ts b/src/engine/systems/SystemNames.ts
index 85d1539..c96dc48 100644
--- a/src/engine/systems/SystemNames.ts
+++ b/src/engine/systems/SystemNames.ts
@@ -4,4 +4,5 @@ export namespace SystemNames {
export const FacingDirection = "FacingDirection";
export const Grid = "Grid";
export const LambdaFactory = "LambdaFactory";
+ export const Collision = "Collision";
}
diff --git a/src/engine/systems/index.ts b/src/engine/systems/index.ts
index 4490ee2..6ee5392 100644
--- a/src/engine/systems/index.ts
+++ b/src/engine/systems/index.ts
@@ -5,3 +5,4 @@ export * from "./Input";
export * from "./FacingDirection";
export * from "./Grid";
export * from "./LambdaFactory";
+export * from "./Collision";