key lock / player curry collisions
This commit is contained in:
parent
4b9349b3f8
commit
cbb88091bd
BIN
public/assets/curry.png
Normal file
BIN
public/assets/curry.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
@ -25,6 +25,10 @@ export class Game {
|
|||||||
this.running = true;
|
this.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public stop() {
|
||||||
|
this.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
public addEntity(entity: Entity) {
|
public addEntity(entity: Entity) {
|
||||||
this.entities.set(entity.id, entity);
|
this.entities.set(entity.id, entity);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,21 @@
|
|||||||
import { Game } from ".";
|
import { Game } from ".";
|
||||||
import { Miscellaneous, loadAssets } from "./config";
|
import { Miscellaneous, loadAssets } from "./config";
|
||||||
import { Player, FunctionBox, Wall, LambdaFactory } from "./entities";
|
import {
|
||||||
|
Player,
|
||||||
|
FunctionBox,
|
||||||
|
Wall,
|
||||||
|
LambdaFactory,
|
||||||
|
Key,
|
||||||
|
LockedDoor,
|
||||||
|
Curry,
|
||||||
|
} from "./entities";
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
FacingDirection,
|
FacingDirection,
|
||||||
Input,
|
Input,
|
||||||
Render,
|
Render,
|
||||||
LambdaFactory as LambdaFactorySpawnSystem,
|
LambdaFactory as LambdaFactorySpawnSystem,
|
||||||
|
Collision,
|
||||||
} from "./systems";
|
} from "./systems";
|
||||||
|
|
||||||
export class TheAbstractionEngine {
|
export class TheAbstractionEngine {
|
||||||
@ -39,6 +48,7 @@ export class TheAbstractionEngine {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
new LambdaFactorySpawnSystem(),
|
new LambdaFactorySpawnSystem(),
|
||||||
|
new Collision(),
|
||||||
new Render(this.ctx),
|
new Render(this.ctx),
|
||||||
].forEach((system) => this.game.addSystem(system));
|
].forEach((system) => this.game.addSystem(system));
|
||||||
|
|
||||||
@ -55,6 +65,15 @@ export class TheAbstractionEngine {
|
|||||||
|
|
||||||
const factory = new LambdaFactory({ x: 6, y: 6 }, "λ x . (x)", 10);
|
const factory = new LambdaFactory({ x: 6, y: 6 }, "λ x . (x)", 10);
|
||||||
this.game.addEntity(factory);
|
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() {
|
public play() {
|
||||||
|
@ -5,6 +5,9 @@ export enum Sprites {
|
|||||||
FUNCTION_BOX,
|
FUNCTION_BOX,
|
||||||
WALL,
|
WALL,
|
||||||
LAMBDA_FACTORY,
|
LAMBDA_FACTORY,
|
||||||
|
KEY,
|
||||||
|
LOCKED_DOOR,
|
||||||
|
CURRY,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SpriteSpec {
|
export interface SpriteSpec {
|
||||||
@ -66,3 +69,30 @@ const lambdaFactorySpriteSpec = {
|
|||||||
sheet: "/assets/function_factory.png",
|
sheet: "/assets/function_factory.png",
|
||||||
};
|
};
|
||||||
SPRITE_SPECS.set(Sprites.LAMBDA_FACTORY, lambdaFactorySpriteSpec);
|
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);
|
||||||
|
45
src/engine/entities/Curry.ts
Normal file
45
src/engine/entities/Curry.ts
Normal file
@ -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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,4 +3,7 @@ export namespace EntityNames {
|
|||||||
export const FunctionBox = "FunctionBox";
|
export const FunctionBox = "FunctionBox";
|
||||||
export const Wall = "Wall";
|
export const Wall = "Wall";
|
||||||
export const LambdaFactory = "LambdaFactory";
|
export const LambdaFactory = "LambdaFactory";
|
||||||
|
export const Key = "Key";
|
||||||
|
export const LockedDoor = "LockedDoor";
|
||||||
|
export const Curry = "Curry";
|
||||||
}
|
}
|
||||||
|
45
src/engine/entities/Key.ts
Normal file
45
src/engine/entities/Key.ts
Normal file
@ -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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
45
src/engine/entities/LockedDoor.ts
Normal file
45
src/engine/entities/LockedDoor.ts
Normal file
@ -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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,3 +4,6 @@ export * from "./Player";
|
|||||||
export * from "./FunctionBox";
|
export * from "./FunctionBox";
|
||||||
export * from "./Wall";
|
export * from "./Wall";
|
||||||
export * from "./LambdaFactory";
|
export * from "./LambdaFactory";
|
||||||
|
export * from "./Key";
|
||||||
|
export * from "./LockedDoor";
|
||||||
|
export * from "./Curry";
|
||||||
|
103
src/engine/systems/Collision.ts
Normal file
103
src/engine/systems/Collision.ts
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { System, SystemNames } from ".";
|
import { Collision, System, SystemNames } from ".";
|
||||||
import { Game } from "..";
|
import { Game } from "..";
|
||||||
import { Entity } from "../entities";
|
import { Entity } from "../entities";
|
||||||
import { PhysicsConstants } from "../config";
|
import { PhysicsConstants } from "../config";
|
||||||
@ -13,8 +13,8 @@ import { Coord2D, Direction, Dimension2D } from "../interfaces";
|
|||||||
import { clamp } from "../utils";
|
import { clamp } from "../utils";
|
||||||
|
|
||||||
export class Grid extends System {
|
export class Grid extends System {
|
||||||
private dimension: Dimension2D;
|
public dimension: Dimension2D;
|
||||||
private grid: Set<string>[][] = [];
|
public grid: Set<string>[][] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{ width: columns, height: rows }: Dimension2D,
|
{ width: columns, height: rows }: Dimension2D,
|
||||||
@ -108,10 +108,10 @@ export class Grid extends System {
|
|||||||
grid.movingDirection = Direction.NONE;
|
grid.movingDirection = Direction.NONE;
|
||||||
entity.addComponent(grid); // default to not moving
|
entity.addComponent(grid); // default to not moving
|
||||||
|
|
||||||
let nextGridPosition = this.getNewGridPosition(
|
let [currentPosition, nextGridPosition] = [
|
||||||
gridPosition,
|
gridPosition,
|
||||||
movingDirection,
|
this.getNewGridPosition(gridPosition, movingDirection),
|
||||||
);
|
];
|
||||||
|
|
||||||
const moving = new Set<string>();
|
const moving = new Set<string>();
|
||||||
moving.add(entity.id);
|
moving.add(entity.id);
|
||||||
@ -122,11 +122,24 @@ export class Grid extends System {
|
|||||||
(id) => game.getEntity(id)!,
|
(id) => game.getEntity(id)!,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
const collidingEntities = entities.filter((entity) =>
|
||||||
entities.some((entity) =>
|
entity.hasComponent(ComponentNames.Colliding),
|
||||||
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();
|
moving.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -148,6 +161,7 @@ export class Grid extends System {
|
|||||||
moving.add(pushableEntity.id);
|
moving.add(pushableEntity.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentPosition = nextGridPosition;
|
||||||
nextGridPosition = this.getNewGridPosition(
|
nextGridPosition = this.getNewGridPosition(
|
||||||
nextGridPosition,
|
nextGridPosition,
|
||||||
movingDirection,
|
movingDirection,
|
||||||
@ -324,7 +338,7 @@ export class Grid extends System {
|
|||||||
this.grid.forEach((row) =>
|
this.grid.forEach((row) =>
|
||||||
row.forEach((cell) => {
|
row.forEach((cell) => {
|
||||||
for (const id of cell) {
|
for (const id of cell) {
|
||||||
if (movedEntities.has(id)) {
|
if (movedEntities.has(id) || !game.getEntity(id)) {
|
||||||
cell.delete(id);
|
cell.delete(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,5 @@ export namespace SystemNames {
|
|||||||
export const FacingDirection = "FacingDirection";
|
export const FacingDirection = "FacingDirection";
|
||||||
export const Grid = "Grid";
|
export const Grid = "Grid";
|
||||||
export const LambdaFactory = "LambdaFactory";
|
export const LambdaFactory = "LambdaFactory";
|
||||||
|
export const Collision = "Collision";
|
||||||
}
|
}
|
||||||
|
@ -5,3 +5,4 @@ export * from "./Input";
|
|||||||
export * from "./FacingDirection";
|
export * from "./FacingDirection";
|
||||||
export * from "./Grid";
|
export * from "./Grid";
|
||||||
export * from "./LambdaFactory";
|
export * from "./LambdaFactory";
|
||||||
|
export * from "./Collision";
|
||||||
|
Loading…
Reference in New Issue
Block a user