summaryrefslogtreecommitdiff
path: root/src/engine/systems/Collision.ts
blob: 0bc6f5c85f118cbdefa8390a84db9df560c34f19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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]),
  [EntityNames.FunctionApplication]: new Set([EntityNames.FunctionBox]),
  [EntityNames.Portal]: 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;
    }

    [entity, otherEntity].forEach((e) => {
      if (!e.hasComponent(ComponentNames.Colliding)) {
        return;
      }
      const colliding = e.getComponent<Colliding>(ComponentNames.Colliding);
      if (colliding?.onCollision) {
        colliding.onCollision(game, e === entity ? otherEntity : entity);
      }
    });
  }
}