@dryanovski/gamefoo 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # gamefoo
2
+
3
+ Most basic ever game engine in TypeScript using Bun.
4
+
5
+ To install dependencies:
6
+
7
+ ```bash
8
+ bun install
9
+ ```
10
+
11
+ To run:
12
+
13
+ ```bash
14
+ bun run index.ts
15
+ ```
16
+
17
+ This project was created using `bun init` in bun v1.3.6. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
18
+
19
+ ### Demo
20
+
21
+ ```bash
22
+ bun run dev
23
+ ```
@@ -0,0 +1,14 @@
1
+ import type Entity from "../entities/entity";
2
+ export declare abstract class Behaviour<T extends Entity = Entity> {
3
+ protected owner: T;
4
+ abstract readonly type: string;
5
+ priority: number;
6
+ enabled: boolean;
7
+ get key(): string;
8
+ constructor(owner: T);
9
+ abstract update(deltaTime: number): void;
10
+ render?(ctx: CanvasRenderingContext2D): void;
11
+ onAttach?(): void;
12
+ onDetach?(): void;
13
+ }
14
+ //# sourceMappingURL=behaviour.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behaviour.d.ts","sourceRoot":"","sources":["../../core/behaviour.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAE7C,8BAAsB,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACvD,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;IAEnB,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAExB,QAAQ,EAAE,MAAM,CAAK;IAErB,OAAO,EAAE,OAAO,CAAQ;IAE/B,IAAI,GAAG,IAAI,MAAM,CAEhB;gBAEW,KAAK,EAAE,CAAC;IAIpB,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAExC,MAAM,CAAC,CAAC,GAAG,EAAE,wBAAwB,GAAG,IAAI;IAC5C,QAAQ,CAAC,IAAI,IAAI;IACjB,QAAQ,CAAC,IAAI,IAAI;CAClB"}
@@ -0,0 +1,33 @@
1
+ import type DynamicEntity from "../../entities/dynamic_entity";
2
+ import type Entity from "../../entities/entity";
3
+ import type { ColliderShape, CollisionInfo, WorldBounds } from "../../types";
4
+ import { Behaviour } from "../behaviour";
5
+ import type World from "../world";
6
+ type CollidableOptions = {
7
+ shape: ColliderShape;
8
+ layer?: number;
9
+ tags?: Set<string>;
10
+ collidesWith?: Set<string>;
11
+ solid?: boolean;
12
+ fixed?: boolean;
13
+ onCollision?: (info: CollisionInfo) => void;
14
+ };
15
+ export declare class Collidable extends Behaviour<DynamicEntity> {
16
+ readonly type = "collidable";
17
+ shape: ColliderShape;
18
+ layer: number;
19
+ tags: Set<string>;
20
+ collidesWith: Set<string>;
21
+ solid: boolean;
22
+ fixed: boolean;
23
+ onCollision: (info: CollisionInfo) => void;
24
+ private world;
25
+ constructor(owner: DynamicEntity, world: World, options: CollidableOptions);
26
+ update(_deltaTime: number): void;
27
+ onAttach(): void;
28
+ onDetach(): void;
29
+ getOwner(): Entity;
30
+ getWorldBounds(): WorldBounds;
31
+ }
32
+ export {};
33
+ //# sourceMappingURL=collidable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collidable.d.ts","sourceRoot":"","sources":["../../../core/behaviours/collidable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,KAAK,MAAM,UAAU,CAAC;AAElC,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CAC7C,CAAC;AAEF,qBAAa,UAAW,SAAQ,SAAS,CAAC,aAAa,CAAC;IACtD,QAAQ,CAAC,IAAI,gBAAgB;IAEtB,KAAK,EAAE,aAAa,CAAC;IAErB,KAAK,EAAE,MAAM,CAAK;IAElB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IAE9B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IAEtC,KAAK,EAAE,OAAO,CAAS;IAEvB,KAAK,EAAE,OAAO,CAAS;IAEvB,WAAW,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAElD,OAAO,CAAC,KAAK,CAAQ;gBAET,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB;IAqB1E,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEvB,QAAQ,IAAI,IAAI;IAIhB,QAAQ,IAAI,IAAI;IAIzB,QAAQ,IAAI,MAAM;IAIlB,cAAc,IAAI,WAAW;CAqB9B"}
@@ -0,0 +1,11 @@
1
+ import type DynamicEntity from "../../entities/dynamic_entity";
2
+ import { Behaviour } from "../behaviour";
3
+ import type Input from "../input";
4
+ export declare class Control extends Behaviour<DynamicEntity> {
5
+ readonly type = "control";
6
+ private input;
7
+ private speed;
8
+ constructor(owner: DynamicEntity, input: Input);
9
+ update(deltaTime: number): void;
10
+ }
11
+ //# sourceMappingURL=control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../../../core/behaviours/control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,KAAK,MAAM,UAAU,CAAC;AAElC,qBAAa,OAAQ,SAAQ,SAAS,CAAC,aAAa,CAAC;IACnD,QAAQ,CAAC,IAAI,aAAa;IAE1B,OAAO,CAAC,KAAK,CAAQ;IAErB,OAAO,CAAC,KAAK,CAAe;gBAEhB,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK;IAK9C,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAgBhC"}
@@ -0,0 +1,17 @@
1
+ import type DynamicEntity from "../../entities/dynamic_entity";
2
+ import { Behaviour } from "../behaviour";
3
+ export declare class HealthKit extends Behaviour<DynamicEntity> {
4
+ readonly type = "healthkit";
5
+ private health;
6
+ private maxHP;
7
+ constructor(owner: DynamicEntity, health: number, maxHP?: number);
8
+ update(_deltaTime: number): void;
9
+ takeDamage(amount: number): void;
10
+ heal(amount: number): void;
11
+ getHealth(): number;
12
+ getMaxHealth(): number;
13
+ setMaxHealth(value: number): void;
14
+ isDead(): boolean;
15
+ getHealthPercent(): number;
16
+ }
17
+ //# sourceMappingURL=healtkit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healtkit.d.ts","sourceRoot":"","sources":["../../../core/behaviours/healtkit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,qBAAa,SAAU,SAAQ,SAAS,CAAC,aAAa,CAAC;IACrD,QAAQ,CAAC,IAAI,eAAe;IAE5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAMhE,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEhC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIhC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI1B,SAAS,IAAI,MAAM;IAInB,YAAY,IAAI,MAAM;IAItB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOjC,MAAM,IAAI,OAAO;IAIjB,gBAAgB,IAAI,MAAM;CAG3B"}
@@ -0,0 +1,19 @@
1
+ import type { Vector2 } from "../types";
2
+ export default class Camera {
3
+ private x;
4
+ private y;
5
+ private width;
6
+ private height;
7
+ constructor(width: number, height: number);
8
+ follow(target: Vector2): void;
9
+ moveTo(target: Vector2): void;
10
+ getPosition(): Vector2;
11
+ getViewRect(): {
12
+ x: number;
13
+ y: number;
14
+ width: number;
15
+ height: number;
16
+ };
17
+ resize(width: number, height: number): void;
18
+ }
19
+ //# sourceMappingURL=camera.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../../core/camera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,OAAO,CAAC,CAAC,CAAa;IACtB,OAAO,CAAC,CAAC,CAAa;IACtB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;gBAEX,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKzC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAK7B,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAK7B,WAAW,IAAI,OAAO;IAItB,WAAW,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAStE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAI5C"}
@@ -0,0 +1,40 @@
1
+ import type Player from "../entities/player";
2
+ import type { GameObject } from "../types";
3
+ import World from "./world";
4
+ interface EngineConfig {
5
+ backgroundColor?: string;
6
+ }
7
+ /**
8
+ * Game Engine - the main class that manages the game loop, rendering, and overall game state.
9
+ */
10
+ export default class Engine {
11
+ private canvas;
12
+ private ctx;
13
+ private lastTime;
14
+ private width;
15
+ private height;
16
+ private _initialized;
17
+ private running;
18
+ private cnf;
19
+ private _player?;
20
+ private engine;
21
+ constructor(canvasId: string, width: number, height: number, config: EngineConfig);
22
+ set player(player: Player);
23
+ get player(): Player | undefined;
24
+ get collisions(): World;
25
+ attachObjects(objects: GameObject): void;
26
+ handleResize(): void;
27
+ resize(width: number, height: number): void;
28
+ private loop;
29
+ /**
30
+ * Public - cause I'm old school
31
+ */
32
+ setup(setupFn: () => void): Promise<void>;
33
+ update(deltaTime: number): void;
34
+ render(): void;
35
+ pause(): void;
36
+ clear(): void;
37
+ destroy(): void;
38
+ }
39
+ export {};
40
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../core/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C,OAAO,KAAK,MAAM,SAAS,CAAC;AAE5B,UAAU,YAAY;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,GAAG,CAA2B;IAEtC,OAAO,CAAC,QAAQ,CAAa;IAE7B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,OAAO,CAAkB;IAEjC,OAAO,CAAC,GAAG,CAET;IAEF,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,OAAO,CAAC,MAAM,CAIZ;gBAEU,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY;IA6BjF,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAExB;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED,IAAI,UAAU,IAAI,KAAK,CAEtB;IAEM,aAAa,CAAC,OAAO,EAAE,UAAU;IAIxC,YAAY;IAkBZ,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAK3C,OAAO,CAAC,IAAI;IAiBZ;;OAEG;IAEU,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI;IAiB/B,MAAM,CAAC,SAAS,EAAE,MAAM;IAoBxB,MAAM;IAcN,KAAK;IAIL,KAAK;IAKL,OAAO;CAGf"}
@@ -0,0 +1,11 @@
1
+ import type { GameObject } from "../types";
2
+ export default class GameObjectRegister {
3
+ private objects;
4
+ register(object: GameObject): void;
5
+ get(id: string): GameObject | undefined;
6
+ has(id: string): boolean;
7
+ getAll(_filter: () => true): GameObject[];
8
+ updateAll(deltaTime: number): void;
9
+ renderAll(ctx: CanvasRenderingContext2D): void;
10
+ }
11
+ //# sourceMappingURL=game_object_register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game_object_register.d.ts","sourceRoot":"","sources":["../../core/game_object_register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,CAAC,OAAO,OAAO,kBAAkB;IACrC,OAAO,CAAC,OAAO,CAAsC;IAErD,QAAQ,CAAC,MAAM,EAAE,UAAU;IAI3B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIvC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,UAAU,EAAE;IAIzC,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMlC,SAAS,CAAC,GAAG,EAAE,wBAAwB,GAAG,IAAI;CAK/C"}
@@ -0,0 +1,15 @@
1
+ export default class Input {
2
+ private keys;
3
+ private mouseButtons;
4
+ private mousePosition;
5
+ constructor();
6
+ isKeyDown(key: string): boolean;
7
+ getPressedKeys(): Set<string>;
8
+ isMouseButtonDown(button: number): boolean;
9
+ getMousePosition(): {
10
+ x: number;
11
+ y: number;
12
+ };
13
+ reset(): void;
14
+ }
15
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../core/input.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,aAAa,CAA4C;;IAyBjE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI/B,cAAc,IAAI,GAAG,CAAC,MAAM,CAAC;IAI7B,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAI1C,gBAAgB,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAI5C,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,14 @@
1
+ import type { Collidable } from "./behaviours/collidable";
2
+ export default class World {
3
+ private colliders;
4
+ register(collider: Collidable): void;
5
+ unregister(collider: Collidable): void;
6
+ detect(): void;
7
+ private tagsOverlap;
8
+ private intersects;
9
+ private aabbVSAabb;
10
+ private circleVSCircle;
11
+ private circleVSAAabb;
12
+ private resolveOverlap;
13
+ }
14
+ //# sourceMappingURL=world.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.d.ts","sourceRoot":"","sources":["../../core/world.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,OAAO,CAAC,SAAS,CAA8B;IAE/C,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;IAIpC,UAAU,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;IAItC,MAAM,IAAI,IAAI;IA+Cd,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,UAAU;IAsBlB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,cAAc;CA4CvB"}
@@ -0,0 +1,11 @@
1
+ import type { Vector2 } from "../types";
2
+ import Entity from "./entity";
3
+ export default abstract class DynamicEntity extends Entity {
4
+ protected velocity: Vector2;
5
+ protected speed: number;
6
+ setVelocity(velocity: Vector2): void;
7
+ getVelocity(): Vector2;
8
+ setSpeed(speed: number): void;
9
+ getSpeed(): number;
10
+ }
11
+ //# sourceMappingURL=dynamic_entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic_entity.d.ts","sourceRoot":"","sources":["../../entities/dynamic_entity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,MAAM,MAAM,UAAU,CAAC;AAE9B,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,aAAc,SAAQ,MAAM;IACxD,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAkB;IAC7C,SAAS,CAAC,KAAK,EAAE,MAAM,CAAK;IAE5B,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAIpC,WAAW,IAAI,OAAO;IAItB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI7B,QAAQ,IAAI,MAAM;CAGnB"}
@@ -0,0 +1,33 @@
1
+ import type { Behaviour } from "../core/behaviour";
2
+ import type { Vector2 } from "../types";
3
+ export default abstract class Entity {
4
+ id: string;
5
+ protected position: Vector2;
6
+ protected size: {
7
+ width: number;
8
+ height: number;
9
+ };
10
+ private behaviorMap;
11
+ private _sortedBehaviors;
12
+ get x(): number;
13
+ set x(value: number);
14
+ get y(): number;
15
+ set y(value: number);
16
+ constructor(id: string, x: number, y: number, width: number, height: number);
17
+ abstract update(deltaTime: number): void;
18
+ abstract render(ctx: CanvasRenderingContext2D): void;
19
+ getPosition(): Vector2;
20
+ getSize(): {
21
+ width: number;
22
+ height: number;
23
+ };
24
+ getBehaviour<T extends Behaviour>(key: string): T | undefined;
25
+ getBehavioursByType<T extends Behaviour>(type: new (...args: any[]) => T): T[];
26
+ hasBehaviour(key: string): boolean;
27
+ attachBehaviour<T extends Behaviour>(behavior: T): T;
28
+ detachBehaviour(key: string): void;
29
+ private get behaviors();
30
+ protected updateBehaviours(deltaTime: number): void;
31
+ protected renderBehaviours(ctx: CanvasRenderingContext2D): void;
32
+ }
33
+ //# sourceMappingURL=entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../entities/entity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,MAAM;IAC3B,EAAE,EAAE,MAAM,CAAM;IACvB,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAkB;IAC7C,SAAS,CAAC,IAAI;;;MAA2B;IAEzC,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,gBAAgB,CAA4B;IAEpD,IAAI,CAAC,IAAI,MAAM,CAEd;IAED,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,EAElB;IAED,IAAI,CAAC,IAAI,MAAM,CAEd;IAED,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,EAElB;gBAEW,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAM3E,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IACxC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,wBAAwB,GAAG,IAAI;IAEpD,WAAW,IAAI,OAAO;IAItB,OAAO,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAI5C,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI7D,mBAAmB,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE;IAI9E,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIlC,eAAe,CAAC,CAAC,SAAS,SAAS,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC;IAUpD,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAWlC,OAAO,KAAK,SAAS,GAOpB;IAED,SAAS,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAQnD,SAAS,CAAC,gBAAgB,CAAC,GAAG,EAAE,wBAAwB,GAAG,IAAI;CAOhE"}
@@ -0,0 +1,10 @@
1
+ import type { Control } from "../core/behaviours/control";
2
+ import type { HealthKit } from "../core/behaviours/healtkit";
3
+ import DynamicEntity from "./dynamic_entity";
4
+ export default class Player extends DynamicEntity {
5
+ get control(): Control | undefined;
6
+ get healthkit(): HealthKit | undefined;
7
+ update(deltaTime: number): void;
8
+ render(ctx: CanvasRenderingContext2D): void;
9
+ }
10
+ //# sourceMappingURL=player.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../entities/player.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,aAAa;IAC/C,IAAI,OAAO,IAAI,OAAO,GAAG,SAAS,CAEjC;IAED,IAAI,SAAS,IAAI,SAAS,GAAG,SAAS,CAErC;IAED,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI/B,MAAM,CAAC,GAAG,EAAE,wBAAwB,GAAG,IAAI;CAK5C"}
@@ -0,0 +1,14 @@
1
+ export { Behaviour } from "./core/behaviour";
2
+ export { Collidable } from "./core/behaviours/collidable";
3
+ export { Control } from "./core/behaviours/control";
4
+ export { HealthKit } from "./core/behaviours/healtkit";
5
+ export { default as Camera } from "./core/camera";
6
+ export { default as Engine } from "./core/engine";
7
+ export { default as GameObjectRegister } from "./core/game_object_register";
8
+ export { default as Input } from "./core/input";
9
+ export { default as World } from "./core/world";
10
+ export { default as DynamicEntity } from "./entities/dynamic_entity";
11
+ export { default as Entity } from "./entities/entity";
12
+ export { default as Player } from "./entities/player";
13
+ export type { ColliderShape, CollisionInfo, GameObject, Vector2, } from "./types";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EACV,aAAa,EACb,aAAa,EACb,UAAU,EACV,OAAO,GACR,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,610 @@
1
+ // core/behaviour.ts
2
+ class Behaviour {
3
+ owner;
4
+ priority = 1;
5
+ enabled = true;
6
+ get key() {
7
+ return this.type.toLowerCase();
8
+ }
9
+ constructor(owner) {
10
+ this.owner = owner;
11
+ }
12
+ }
13
+ // core/behaviours/collidable.ts
14
+ class Collidable extends Behaviour {
15
+ type = "collidable";
16
+ shape;
17
+ layer = 0;
18
+ tags = new Set;
19
+ collidesWith = new Set;
20
+ solid = false;
21
+ fixed = false;
22
+ onCollision;
23
+ world;
24
+ constructor(owner, world, options) {
25
+ super(owner);
26
+ this.world = world;
27
+ const size = owner.getSize();
28
+ this.shape = options.shape;
29
+ this.shape = options.shape ?? {
30
+ type: "aabb",
31
+ width: size.width,
32
+ height: size.height
33
+ };
34
+ this.layer = options.layer ?? 0;
35
+ this.tags = options.tags ?? new Set;
36
+ this.solid = options.solid ?? false;
37
+ this.fixed = options.fixed ?? false;
38
+ this.collidesWith = options.collidesWith ?? new Set;
39
+ this.onCollision = options.onCollision || (() => {});
40
+ }
41
+ update(_deltaTime) {}
42
+ onAttach() {
43
+ this.world.register(this);
44
+ }
45
+ onDetach() {
46
+ this.world.unregister(this);
47
+ }
48
+ getOwner() {
49
+ return this.owner;
50
+ }
51
+ getWorldBounds() {
52
+ const pos = this.owner.getPosition();
53
+ const offset = "offset" in this.shape && this.shape.offset ? this.shape.offset : { x: 0, y: 0 };
54
+ if (this.shape.type === "aabb") {
55
+ return {
56
+ x: pos.x + offset.x,
57
+ y: pos.y + offset.y,
58
+ width: this.shape.width,
59
+ height: this.shape.height
60
+ };
61
+ }
62
+ const r = this.shape.radius;
63
+ return {
64
+ x: pos.x + offset.x - r,
65
+ y: pos.y + offset.y - r,
66
+ width: r * 2,
67
+ height: r * 2
68
+ };
69
+ }
70
+ }
71
+ // core/behaviours/control.ts
72
+ class Control extends Behaviour {
73
+ type = "control";
74
+ input;
75
+ speed = 500;
76
+ constructor(owner, input) {
77
+ super(owner);
78
+ this.input = input;
79
+ }
80
+ update(deltaTime) {
81
+ let dx = 0;
82
+ let dy = 0;
83
+ if (this.input.isKeyDown("a") || this.input.isKeyDown("arrowleft"))
84
+ dx -= 1;
85
+ if (this.input.isKeyDown("d") || this.input.isKeyDown("arrowright"))
86
+ dx += 1;
87
+ if (this.input.isKeyDown("w") || this.input.isKeyDown("arrowup"))
88
+ dy -= 1;
89
+ if (this.input.isKeyDown("s") || this.input.isKeyDown("arrowdown"))
90
+ dy += 1;
91
+ const len = Math.sqrt(dx * dx + dy * dy);
92
+ if (len > 0) {
93
+ this.owner.x += dx / len * this.speed * deltaTime;
94
+ this.owner.y += dy / len * this.speed * deltaTime;
95
+ }
96
+ }
97
+ }
98
+ // core/behaviours/healtkit.ts
99
+ class HealthKit extends Behaviour {
100
+ type = "healthkit";
101
+ health;
102
+ maxHP;
103
+ constructor(owner, health, maxHP) {
104
+ super(owner);
105
+ this.health = health;
106
+ this.maxHP = maxHP || health;
107
+ }
108
+ update(_deltaTime) {}
109
+ takeDamage(amount) {
110
+ this.health = Math.max(0, this.health - amount);
111
+ }
112
+ heal(amount) {
113
+ this.health = Math.min(this.maxHP, this.health + amount);
114
+ }
115
+ getHealth() {
116
+ return this.health;
117
+ }
118
+ getMaxHealth() {
119
+ return this.maxHP;
120
+ }
121
+ setMaxHealth(value) {
122
+ this.maxHP = value;
123
+ if (this.health > this.maxHP) {
124
+ this.health = this.maxHP;
125
+ }
126
+ }
127
+ isDead() {
128
+ return this.health <= 0;
129
+ }
130
+ getHealthPercent() {
131
+ return this.maxHP > 0 ? this.health / this.maxHP : 0;
132
+ }
133
+ }
134
+ // core/camera.ts
135
+ class Camera {
136
+ x = 0;
137
+ y = 0;
138
+ width;
139
+ height;
140
+ constructor(width, height) {
141
+ this.width = width;
142
+ this.height = height;
143
+ }
144
+ follow(target) {
145
+ this.x = target.x;
146
+ this.y = target.y;
147
+ }
148
+ moveTo(target) {
149
+ this.x = target.x;
150
+ this.y = target.y;
151
+ }
152
+ getPosition() {
153
+ return { x: this.x, y: this.y };
154
+ }
155
+ getViewRect() {
156
+ return {
157
+ x: this.x - this.width / 2,
158
+ y: this.y - this.height / 2,
159
+ width: this.width,
160
+ height: this.height
161
+ };
162
+ }
163
+ resize(width, height) {
164
+ this.width = width;
165
+ this.height = height;
166
+ }
167
+ }
168
+ // core/game_object_register.ts
169
+ class GameObjectRegister {
170
+ objects = new Map;
171
+ register(object) {
172
+ this.objects.set(object.id, object);
173
+ }
174
+ get(id) {
175
+ return this.objects.get(id);
176
+ }
177
+ has(id) {
178
+ return this.objects.has(id);
179
+ }
180
+ getAll(_filter) {
181
+ return Array.from(this.objects.values()).filter(_filter);
182
+ }
183
+ updateAll(deltaTime) {
184
+ this.getAll(() => true).forEach((obj) => {
185
+ obj.update(deltaTime);
186
+ });
187
+ }
188
+ renderAll(ctx) {
189
+ this.getAll(() => true).forEach((obj) => {
190
+ obj.render(ctx);
191
+ });
192
+ }
193
+ }
194
+
195
+ // core/world.ts
196
+ class World {
197
+ colliders = new Set;
198
+ register(collider) {
199
+ this.colliders.add(collider);
200
+ }
201
+ unregister(collider) {
202
+ this.colliders.delete(collider);
203
+ }
204
+ detect() {
205
+ const list = Array.from(this.colliders);
206
+ const len = list.length;
207
+ for (let i = 0;i < len; i++) {
208
+ const obj = list[i];
209
+ if (!obj?.enabled)
210
+ continue;
211
+ for (let j = i + 1;j < len; j++) {
212
+ const other = list[j];
213
+ if (!other?.enabled)
214
+ continue;
215
+ if (obj.layer !== other.layer)
216
+ continue;
217
+ const objWantOther = this.tagsOverlap(obj.collidesWith, other.tags);
218
+ const otherWantObj = this.tagsOverlap(other.collidesWith, obj.tags);
219
+ const boundsObj = obj.getWorldBounds();
220
+ const boundsOther = other.getWorldBounds();
221
+ if (!this.intersects(obj, boundsObj, other, boundsOther))
222
+ continue;
223
+ if (obj.solid && other.solid) {
224
+ this.resolveOverlap(obj, boundsObj, other, boundsOther);
225
+ }
226
+ if (objWantOther && obj.onCollision) {
227
+ obj.onCollision({
228
+ self: obj.getOwner(),
229
+ other: other.getOwner(),
230
+ selfTags: obj.tags,
231
+ otherTags: other.tags
232
+ });
233
+ }
234
+ if (otherWantObj && other.onCollision) {
235
+ other.onCollision({
236
+ self: other.getOwner(),
237
+ other: obj.getOwner(),
238
+ selfTags: other.tags,
239
+ otherTags: obj.tags
240
+ });
241
+ }
242
+ }
243
+ }
244
+ }
245
+ tagsOverlap(wants, has) {
246
+ for (const tag of wants) {
247
+ if (has.has(tag))
248
+ return true;
249
+ }
250
+ return false;
251
+ }
252
+ intersects(a, boundsA, b, boundsB) {
253
+ const shapeA = a.shape;
254
+ const shapeB = b.shape;
255
+ if (shapeA.type === "aabb" && shapeB.type === "aabb") {
256
+ return this.aabbVSAabb(boundsA, boundsB);
257
+ }
258
+ if (shapeA.type === "circle" && shapeB.type === "circle") {
259
+ return this.circleVSCircle(a, boundsA, b, boundsB);
260
+ }
261
+ const [circle, circleBounds, rect] = shapeA.type === "circle" ? [a, boundsA, boundsB] : [b, boundsB, boundsA];
262
+ return this.circleVSAAabb(circle, circleBounds, rect);
263
+ }
264
+ aabbVSAabb(a, b) {
265
+ return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
266
+ }
267
+ circleVSCircle(a, boundsA, b, boundsB) {
268
+ if (a.shape.type !== "circle" || b.shape.type !== "circle")
269
+ return false;
270
+ const cx1 = boundsA.x + a.shape.radius;
271
+ const cy1 = boundsA.y + a.shape.radius;
272
+ const cx2 = boundsB.x + b.shape.radius;
273
+ const cy2 = boundsB.y + b.shape.radius;
274
+ const dx = cx2 - cx1;
275
+ const dy = cy2 - cy1;
276
+ const distSq = dx * dx + dy * dy;
277
+ const radSum = a.shape.radius + b.shape.radius;
278
+ return distSq <= radSum * radSum;
279
+ }
280
+ circleVSAAabb(circle, circleBounds, rect) {
281
+ if (circle.shape.type !== "circle")
282
+ return false;
283
+ const cx = circleBounds.x + circle.shape.radius;
284
+ const cy = circleBounds.y + circle.shape.radius;
285
+ const closestX = Math.max(rect.x, Math.min(cx, rect.x + rect.width));
286
+ const closestY = Math.max(rect.y, Math.min(cy, rect.y + rect.height));
287
+ const dx = cx - closestX;
288
+ const dy = cy - closestY;
289
+ return dx * dx + dy * dy <= circle.shape.radius * circle.shape.radius;
290
+ }
291
+ resolveOverlap(a, boundsA, b, boundsB) {
292
+ const overlapX = Math.min(boundsA.x + boundsA.width - boundsB.x, boundsB.x + boundsB.width - boundsA.x);
293
+ const overlapY = Math.min(boundsA.y + boundsA.height - boundsB.y, boundsB.y + boundsB.height - boundsA.y);
294
+ let pushX = 0;
295
+ let pushY = 0;
296
+ if (overlapX < overlapY) {
297
+ pushX = boundsA.x < boundsB.x ? -overlapX : overlapX;
298
+ } else {
299
+ pushY = boundsA.y < boundsB.y ? -overlapY : overlapY;
300
+ }
301
+ const ownerA = a.getOwner();
302
+ const ownerB = b.getOwner();
303
+ if (a.fixed && b.fixed) {
304
+ return;
305
+ }
306
+ if (a.fixed) {
307
+ ownerB.x -= pushX;
308
+ ownerB.y -= pushY;
309
+ } else if (b.fixed) {
310
+ ownerA.x += pushX;
311
+ ownerA.y += pushY;
312
+ } else {
313
+ ownerA.x += pushX / 2;
314
+ ownerA.y += pushY / 2;
315
+ ownerB.x -= pushX / 2;
316
+ ownerB.y -= pushY / 2;
317
+ }
318
+ }
319
+ }
320
+
321
+ // core/engine.ts
322
+ class Engine {
323
+ canvas;
324
+ ctx;
325
+ lastTime = 0;
326
+ width;
327
+ height;
328
+ _initialized = false;
329
+ running = false;
330
+ cnf = {
331
+ backgroundColor: "#000000"
332
+ };
333
+ _player;
334
+ engine;
335
+ constructor(canvasId, width, height, config) {
336
+ this.canvas = document.getElementById(canvasId);
337
+ this.height = height;
338
+ this.width = width;
339
+ this.canvas.width = width;
340
+ this.canvas.height = height;
341
+ const context = this.canvas.getContext("2d");
342
+ if (!context) {
343
+ throw new Error("Failed to get 2D context");
344
+ }
345
+ this.ctx = context;
346
+ this.cnf = { ...this.cnf, ...config };
347
+ this.engine = {
348
+ camera: new Camera(this.width, this.height),
349
+ objects: new GameObjectRegister,
350
+ collisions: new World
351
+ };
352
+ }
353
+ set player(player) {
354
+ this._player = player;
355
+ }
356
+ get player() {
357
+ return this._player;
358
+ }
359
+ get collisions() {
360
+ return this.engine.collisions;
361
+ }
362
+ attachObjects(objects) {
363
+ this.engine.objects.register(objects);
364
+ }
365
+ handleResize() {
366
+ if (!this.canvas)
367
+ return;
368
+ const container = this.canvas.parentElement;
369
+ if (!container)
370
+ return;
371
+ const containerWidth = container.clientWidth;
372
+ const containerHeight = container.clientHeight;
373
+ const scaleX = containerWidth / this.width;
374
+ const scaleY = containerHeight / this.height;
375
+ const scale = Math.min(scaleX, scaleY);
376
+ const offsetX = (containerWidth - this.width * scale) / 2;
377
+ const offsetY = (containerHeight - this.height * scale) / 2;
378
+ this.canvas.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`;
379
+ }
380
+ resize(width, height) {
381
+ this.canvas.width = width;
382
+ this.canvas.height = height;
383
+ }
384
+ loop(timestamp) {
385
+ if (!this.running) {
386
+ return;
387
+ }
388
+ if (this.lastTime === 0) {
389
+ this.lastTime = timestamp;
390
+ }
391
+ const deltaTime = (timestamp - this.lastTime) / 1000;
392
+ this.lastTime = timestamp;
393
+ this.update(deltaTime);
394
+ this.render();
395
+ requestAnimationFrame((timestamp2) => this.loop(timestamp2));
396
+ }
397
+ async setup(setupFn) {
398
+ if (this._initialized) {
399
+ console.warn("Engine is already initialized.");
400
+ return;
401
+ }
402
+ if (typeof setupFn !== "function") {
403
+ throw new Error("Setup function must be provided and must be a function.");
404
+ }
405
+ this.lastTime = 0;
406
+ setupFn();
407
+ this._initialized = true;
408
+ this.running = true;
409
+ requestAnimationFrame((timestamp) => this.loop(timestamp));
410
+ }
411
+ update(deltaTime) {
412
+ if (this.player) {
413
+ this.player.update(deltaTime);
414
+ }
415
+ if (this.engine.objects) {
416
+ this.engine.objects.updateAll(deltaTime);
417
+ }
418
+ this.engine.collisions.detect();
419
+ if (this.engine.camera && this.player) {
420
+ this.engine.camera.follow(this.player.getPosition());
421
+ }
422
+ }
423
+ render() {
424
+ this.ctx.clearRect(0, 0, this.width, this.height);
425
+ this.ctx.fillStyle = this.cnf.backgroundColor || "#000000";
426
+ this.ctx.fillRect(0, 0, this.width, this.height);
427
+ if (this.player) {
428
+ this.player.render(this.ctx);
429
+ }
430
+ if (this.engine.objects) {
431
+ this.engine.objects.renderAll(this.ctx);
432
+ }
433
+ }
434
+ pause() {
435
+ this.running = false;
436
+ }
437
+ clear() {
438
+ this.running = false;
439
+ this.ctx.clearRect(0, 0, this.width, this.height);
440
+ }
441
+ destroy() {}
442
+ }
443
+ // core/input.ts
444
+ class Input {
445
+ keys = new Set;
446
+ mouseButtons = new Set;
447
+ mousePosition = { x: 0, y: 0 };
448
+ constructor() {
449
+ window.addEventListener("keydown", (e) => {
450
+ this.keys.add(e.key.toLowerCase());
451
+ });
452
+ window.addEventListener("keyup", (e) => {
453
+ this.keys.delete(e.key.toLowerCase());
454
+ });
455
+ window.addEventListener("mousedown", (e) => {
456
+ this.mouseButtons.add(e.button);
457
+ });
458
+ window.addEventListener("mouseup", (e) => {
459
+ this.mouseButtons.delete(e.button);
460
+ });
461
+ window.addEventListener("mousemove", (e) => {
462
+ this.mousePosition = { x: e.clientX, y: e.clientY };
463
+ });
464
+ }
465
+ isKeyDown(key) {
466
+ return this.keys.has(key.toLowerCase());
467
+ }
468
+ getPressedKeys() {
469
+ return new Set(this.keys);
470
+ }
471
+ isMouseButtonDown(button) {
472
+ return this.mouseButtons.has(button);
473
+ }
474
+ getMousePosition() {
475
+ return { ...this.mousePosition };
476
+ }
477
+ reset() {
478
+ this.keys.clear();
479
+ this.mouseButtons.clear();
480
+ }
481
+ }
482
+ // entities/entity.ts
483
+ class Entity {
484
+ id = "";
485
+ position = { x: 0, y: 0 };
486
+ size = { width: 0, height: 0 };
487
+ behaviorMap = new Map;
488
+ _sortedBehaviors = null;
489
+ get x() {
490
+ return this.position.x;
491
+ }
492
+ set x(value) {
493
+ this.position.x = value;
494
+ }
495
+ get y() {
496
+ return this.position.y;
497
+ }
498
+ set y(value) {
499
+ this.position.y = value;
500
+ }
501
+ constructor(id, x, y, width, height) {
502
+ this.id = id;
503
+ this.position = { x, y };
504
+ this.size = { width, height };
505
+ }
506
+ getPosition() {
507
+ return { ...this.position };
508
+ }
509
+ getSize() {
510
+ return { ...this.size };
511
+ }
512
+ getBehaviour(key) {
513
+ return this.behaviorMap.get(key.toLowerCase());
514
+ }
515
+ getBehavioursByType(type) {
516
+ return this.behaviors.filter((b) => b instanceof type);
517
+ }
518
+ hasBehaviour(key) {
519
+ return this.behaviorMap.has(key.toLowerCase());
520
+ }
521
+ attachBehaviour(behavior) {
522
+ this.behaviorMap.set(behavior.key, behavior);
523
+ this._sortedBehaviors = null;
524
+ if (behavior.onAttach) {
525
+ behavior.onAttach();
526
+ }
527
+ return behavior;
528
+ }
529
+ detachBehaviour(key) {
530
+ const behavior = this.behaviorMap.get(key.toLowerCase());
531
+ if (!behavior)
532
+ return;
533
+ if (behavior.onDetach) {
534
+ behavior.onDetach();
535
+ }
536
+ this.behaviorMap.delete(key.toLowerCase());
537
+ this._sortedBehaviors = null;
538
+ }
539
+ get behaviors() {
540
+ if (!this._sortedBehaviors) {
541
+ this._sortedBehaviors = Array.from(this.behaviorMap.values()).sort((a, b) => a.priority - b.priority);
542
+ }
543
+ return this._sortedBehaviors;
544
+ }
545
+ updateBehaviours(deltaTime) {
546
+ for (const behavior of this.behaviors) {
547
+ if (behavior.enabled) {
548
+ behavior.update(deltaTime);
549
+ }
550
+ }
551
+ }
552
+ renderBehaviours(ctx) {
553
+ for (const behavior of this.behaviors) {
554
+ if (behavior.enabled && behavior.render) {
555
+ behavior.render(ctx);
556
+ }
557
+ }
558
+ }
559
+ }
560
+
561
+ // entities/dynamic_entity.ts
562
+ class DynamicEntity extends Entity {
563
+ velocity = { x: 0, y: 0 };
564
+ speed = 0;
565
+ setVelocity(velocity) {
566
+ this.velocity = velocity;
567
+ }
568
+ getVelocity() {
569
+ return { ...this.velocity };
570
+ }
571
+ setSpeed(speed) {
572
+ this.speed = speed;
573
+ }
574
+ getSpeed() {
575
+ return this.speed;
576
+ }
577
+ }
578
+ // entities/player.ts
579
+ class Player extends DynamicEntity {
580
+ get control() {
581
+ return this.getBehaviour("control");
582
+ }
583
+ get healthkit() {
584
+ return this.getBehaviour("healthkit");
585
+ }
586
+ update(deltaTime) {
587
+ this.updateBehaviours(deltaTime);
588
+ }
589
+ render(ctx) {
590
+ ctx.fillStyle = "blue";
591
+ ctx.fillRect(this.x, this.y, this.size.width, this.size.height);
592
+ this.renderBehaviours(ctx);
593
+ }
594
+ }
595
+ export {
596
+ World,
597
+ Player,
598
+ Input,
599
+ HealthKit,
600
+ GameObjectRegister,
601
+ Entity,
602
+ Engine,
603
+ DynamicEntity,
604
+ Control,
605
+ Collidable,
606
+ Camera,
607
+ Behaviour
608
+ };
609
+
610
+ //# debugId=AC6FB9916B04E31F64756E2164756E21
@@ -0,0 +1,21 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../core/behaviour.ts", "../core/behaviours/collidable.ts", "../core/behaviours/control.ts", "../core/behaviours/healtkit.ts", "../core/camera.ts", "../core/game_object_register.ts", "../core/world.ts", "../core/engine.ts", "../core/input.ts", "../entities/entity.ts", "../entities/dynamic_entity.ts", "../entities/player.ts"],
4
+ "sourcesContent": [
5
+ "import type Entity from \"../entities/entity\";\n\nexport abstract class Behaviour<T extends Entity = Entity> {\n protected owner: T;\n\n abstract readonly type: string;\n\n public priority: number = 1;\n\n public enabled: boolean = true;\n\n get key(): string {\n return this.type.toLowerCase();\n }\n\n constructor(owner: T) {\n this.owner = owner;\n }\n\n abstract update(deltaTime: number): void;\n\n render?(ctx: CanvasRenderingContext2D): void;\n onAttach?(): void;\n onDetach?(): void;\n}\n",
6
+ "import type DynamicEntity from \"../../entities/dynamic_entity\";\nimport type Entity from \"../../entities/entity\";\nimport type { ColliderShape, CollisionInfo, WorldBounds } from \"../../types\";\nimport { Behaviour } from \"../behaviour\";\nimport type World from \"../world\";\n\ntype CollidableOptions = {\n shape: ColliderShape;\n layer?: number;\n tags?: Set<string>;\n collidesWith?: Set<string>;\n solid?: boolean;\n fixed?: boolean;\n onCollision?: (info: CollisionInfo) => void;\n};\n\nexport class Collidable extends Behaviour<DynamicEntity> {\n readonly type = \"collidable\";\n\n public shape: ColliderShape;\n\n public layer: number = 0;\n\n public tags: Set<string> = new Set();\n\n public collidesWith: Set<string> = new Set();\n\n public solid: boolean = false;\n\n public fixed: boolean = false;\n\n public onCollision: (info: CollisionInfo) => void;\n\n private world: World;\n\n constructor(owner: DynamicEntity, world: World, options: CollidableOptions) {\n super(owner);\n\n this.world = world;\n\n const size = owner.getSize();\n\n this.shape = options.shape;\n this.shape = options.shape ?? {\n type: \"aabb\",\n width: size.width,\n height: size.height,\n };\n this.layer = options.layer ?? 0;\n this.tags = options.tags ?? new Set();\n this.solid = options.solid ?? false;\n this.fixed = options.fixed ?? false;\n this.collidesWith = options.collidesWith ?? new Set();\n this.onCollision = options.onCollision || (() => {});\n }\n\n update(_deltaTime: number): void {}\n\n override onAttach(): void {\n this.world.register(this);\n }\n\n override onDetach(): void {\n this.world.unregister(this);\n }\n\n getOwner(): Entity {\n return this.owner;\n }\n\n getWorldBounds(): WorldBounds {\n const pos = this.owner.getPosition();\n const offset = \"offset\" in this.shape && this.shape.offset ? this.shape.offset : { x: 0, y: 0 };\n\n if (this.shape.type === \"aabb\") {\n return {\n x: pos.x + offset.x,\n y: pos.y + offset.y,\n width: this.shape.width,\n height: this.shape.height,\n };\n }\n\n const r = this.shape.radius;\n return {\n x: pos.x + offset.x - r,\n y: pos.y + offset.y - r,\n width: r * 2,\n height: r * 2,\n };\n }\n}\n",
7
+ "import type DynamicEntity from \"../../entities/dynamic_entity\";\nimport { Behaviour } from \"../behaviour\";\nimport type Input from \"../input\";\n\nexport class Control extends Behaviour<DynamicEntity> {\n readonly type = \"control\";\n\n private input: Input;\n\n private speed: number = 500; // pixels per second\n\n constructor(owner: DynamicEntity, input: Input) {\n super(owner);\n this.input = input;\n }\n\n update(deltaTime: number): void {\n let dx = 0;\n let dy = 0;\n\n if (this.input.isKeyDown(\"a\") || this.input.isKeyDown(\"arrowleft\")) dx -= 1;\n if (this.input.isKeyDown(\"d\") || this.input.isKeyDown(\"arrowright\")) dx += 1;\n if (this.input.isKeyDown(\"w\") || this.input.isKeyDown(\"arrowup\")) dy -= 1;\n if (this.input.isKeyDown(\"s\") || this.input.isKeyDown(\"arrowdown\")) dy += 1;\n\n // Normalize diagonal movement\n const len = Math.sqrt(dx * dx + dy * dy);\n if (len > 0) {\n this.owner.x += (dx / len) * this.speed * deltaTime;\n this.owner.y += (dy / len) * this.speed * deltaTime;\n }\n }\n}\n",
8
+ "import type DynamicEntity from \"../../entities/dynamic_entity\";\nimport { Behaviour } from \"../behaviour\";\n\nexport class HealthKit extends Behaviour<DynamicEntity> {\n readonly type = \"healthkit\";\n\n private health: number;\n private maxHP: number;\n\n constructor(owner: DynamicEntity, health: number, maxHP?: number) {\n super(owner);\n this.health = health;\n this.maxHP = maxHP || health;\n }\n\n update(_deltaTime: number): void {}\n\n takeDamage(amount: number): void {\n this.health = Math.max(0, this.health - amount);\n }\n\n heal(amount: number): void {\n this.health = Math.min(this.maxHP, this.health + amount);\n }\n\n getHealth(): number {\n return this.health;\n }\n\n getMaxHealth(): number {\n return this.maxHP;\n }\n\n setMaxHealth(value: number): void {\n this.maxHP = value;\n if (this.health > this.maxHP) {\n this.health = this.maxHP;\n }\n }\n\n isDead(): boolean {\n return this.health <= 0;\n }\n\n getHealthPercent(): number {\n return this.maxHP > 0 ? this.health / this.maxHP : 0;\n }\n}\n",
9
+ "import type { Vector2 } from \"../types\";\n\nexport default class Camera {\n private x: number = 0;\n private y: number = 0;\n private width: number;\n private height: number;\n\n constructor(width: number, height: number) {\n this.width = width;\n this.height = height;\n }\n\n follow(target: Vector2): void {\n this.x = target.x;\n this.y = target.y;\n }\n\n moveTo(target: Vector2): void {\n this.x = target.x;\n this.y = target.y;\n }\n\n getPosition(): Vector2 {\n return { x: this.x, y: this.y };\n }\n\n getViewRect(): { x: number; y: number; width: number; height: number } {\n return {\n x: this.x - this.width / 2,\n y: this.y - this.height / 2,\n width: this.width,\n height: this.height,\n };\n }\n\n resize(width: number, height: number): void {\n this.width = width;\n this.height = height;\n }\n}\n",
10
+ "import type { GameObject } from \"../types\";\n\nexport default class GameObjectRegister {\n private objects: Map<string, GameObject> = new Map();\n\n register(object: GameObject) {\n this.objects.set(object.id, object);\n }\n\n get(id: string): GameObject | undefined {\n return this.objects.get(id);\n }\n\n has(id: string): boolean {\n return this.objects.has(id);\n }\n\n getAll(_filter: () => true): GameObject[] {\n return Array.from(this.objects.values()).filter(_filter);\n }\n\n updateAll(deltaTime: number): void {\n this.getAll(() => true).forEach((obj) => {\n obj.update(deltaTime);\n });\n }\n\n renderAll(ctx: CanvasRenderingContext2D): void {\n this.getAll(() => true).forEach((obj) => {\n obj.render(ctx);\n });\n }\n}\n",
11
+ "import type { WorldBounds } from \"../types\";\nimport type { Collidable } from \"./behaviours/collidable\";\n\nexport default class World {\n private colliders: Set<Collidable> = new Set();\n\n register(collider: Collidable): void {\n this.colliders.add(collider);\n }\n\n unregister(collider: Collidable): void {\n this.colliders.delete(collider);\n }\n\n detect(): void {\n const list = Array.from(this.colliders);\n const len = list.length;\n\n for (let i = 0; i < len; i++) {\n const obj = list[i];\n if (!obj?.enabled) continue;\n\n for (let j = i + 1; j < len; j++) {\n const other = list[j];\n if (!other?.enabled) continue;\n\n if (obj.layer !== other.layer) continue;\n\n const objWantOther = this.tagsOverlap(obj.collidesWith, other.tags);\n const otherWantObj = this.tagsOverlap(other.collidesWith, obj.tags);\n\n const boundsObj = obj.getWorldBounds();\n const boundsOther = other.getWorldBounds();\n\n if (!this.intersects(obj, boundsObj, other, boundsOther)) continue;\n\n if (obj.solid && other.solid) {\n this.resolveOverlap(obj, boundsObj, other, boundsOther);\n }\n\n if (objWantOther && obj.onCollision) {\n obj.onCollision({\n self: obj.getOwner(),\n other: other.getOwner(),\n selfTags: obj.tags,\n otherTags: other.tags,\n });\n }\n\n if (otherWantObj && other.onCollision) {\n other.onCollision({\n self: other.getOwner(),\n other: obj.getOwner(),\n selfTags: other.tags,\n otherTags: obj.tags,\n });\n }\n }\n }\n }\n\n private tagsOverlap(wants: Set<string>, has: Set<string>): boolean {\n for (const tag of wants) {\n if (has.has(tag)) return true;\n }\n return false;\n }\n\n private intersects(\n a: Collidable,\n boundsA: WorldBounds,\n b: Collidable,\n boundsB: WorldBounds,\n ): boolean {\n const shapeA = a.shape;\n const shapeB = b.shape;\n\n if (shapeA.type === \"aabb\" && shapeB.type === \"aabb\") {\n return this.aabbVSAabb(boundsA, boundsB);\n }\n\n if (shapeA.type === \"circle\" && shapeB.type === \"circle\") {\n return this.circleVSCircle(a, boundsA, b, boundsB);\n }\n\n const [circle, circleBounds, rect] =\n shapeA.type === \"circle\" ? [a, boundsA, boundsB] : [b, boundsB, boundsA];\n\n return this.circleVSAAabb(circle, circleBounds, rect);\n }\n private aabbVSAabb(a: WorldBounds, b: WorldBounds): boolean {\n return (\n a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y\n );\n }\n\n private circleVSCircle(\n a: Collidable,\n boundsA: WorldBounds,\n b: Collidable,\n boundsB: WorldBounds,\n ): boolean {\n if (a.shape.type !== \"circle\" || b.shape.type !== \"circle\") return false;\n\n const cx1 = boundsA.x + a.shape.radius;\n const cy1 = boundsA.y + a.shape.radius;\n const cx2 = boundsB.x + b.shape.radius;\n const cy2 = boundsB.y + b.shape.radius;\n\n const dx = cx2 - cx1;\n const dy = cy2 - cy1;\n const distSq = dx * dx + dy * dy;\n const radSum = a.shape.radius + b.shape.radius;\n\n return distSq <= radSum * radSum;\n }\n\n private circleVSAAabb(circle: Collidable, circleBounds: WorldBounds, rect: WorldBounds): boolean {\n if (circle.shape.type !== \"circle\") return false;\n\n const cx = circleBounds.x + circle.shape.radius;\n const cy = circleBounds.y + circle.shape.radius;\n\n const closestX = Math.max(rect.x, Math.min(cx, rect.x + rect.width));\n const closestY = Math.max(rect.y, Math.min(cy, rect.y + rect.height));\n\n const dx = cx - closestX;\n const dy = cy - closestY;\n\n return dx * dx + dy * dy <= circle.shape.radius * circle.shape.radius;\n }\n\n private resolveOverlap(\n a: Collidable,\n boundsA: WorldBounds,\n b: Collidable,\n boundsB: WorldBounds,\n ): void {\n const overlapX = Math.min(\n boundsA.x + boundsA.width - boundsB.x,\n boundsB.x + boundsB.width - boundsA.x,\n );\n const overlapY = Math.min(\n boundsA.y + boundsA.height - boundsB.y,\n boundsB.y + boundsB.height - boundsA.y,\n );\n\n let pushX = 0;\n let pushY = 0;\n\n if (overlapX < overlapY) {\n pushX = boundsA.x < boundsB.x ? -overlapX : overlapX;\n } else {\n pushY = boundsA.y < boundsB.y ? -overlapY : overlapY;\n }\n\n const ownerA = a.getOwner();\n const ownerB = b.getOwner();\n\n if (a.fixed && b.fixed) {\n return;\n }\n\n if (a.fixed) {\n ownerB.x -= pushX;\n ownerB.y -= pushY;\n } else if (b.fixed) {\n ownerA.x += pushX;\n ownerA.y += pushY;\n } else {\n ownerA.x += pushX / 2;\n ownerA.y += pushY / 2;\n ownerB.x -= pushX / 2;\n ownerB.y -= pushY / 2;\n }\n }\n}\n",
12
+ "import type Player from \"../entities/player\";\nimport type { GameObject } from \"../types\";\nimport Camera from \"./camera\";\nimport GameObjectRegister from \"./game_object_register\";\nimport World from \"./world\";\n\ninterface EngineConfig {\n backgroundColor?: string;\n}\n\n/**\n * Game Engine - the main class that manages the game loop, rendering, and overall game state.\n */\nexport default class Engine {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n\n private lastTime: number = 0;\n\n private width: number;\n private height: number;\n\n private _initialized: boolean = false;\n private running: boolean = false;\n\n private cnf: EngineConfig = {\n backgroundColor: \"#000000\",\n };\n\n private _player?: Player;\n\n private engine: {\n camera: Camera | null;\n objects: GameObjectRegister; // Placeholder for game objects, can be expanded later\n collisions: World;\n };\n\n constructor(canvasId: string, width: number, height: number, config: EngineConfig) {\n this.canvas = document.getElementById(canvasId) as HTMLCanvasElement;\n this.height = height;\n this.width = width;\n this.canvas.width = width;\n this.canvas.height = height;\n const context = this.canvas.getContext(\"2d\");\n if (!context) {\n throw new Error(\"Failed to get 2D context\");\n }\n this.ctx = context;\n\n this.cnf = { ...this.cnf, ...config };\n\n this.engine = {\n /**\n * Maybe later will find a beter way to initialize this\n */\n camera: new Camera(this.width, this.height),\n\n /**\n * This holds all the game objects\n */\n objects: new GameObjectRegister(),\n\n collisions: new World(),\n };\n }\n\n set player(player: Player) {\n this._player = player;\n }\n\n get player(): Player | undefined {\n return this._player;\n }\n\n get collisions(): World {\n return this.engine.collisions;\n }\n\n public attachObjects(objects: GameObject) {\n this.engine.objects.register(objects);\n }\n\n handleResize() {\n if (!this.canvas) return;\n\n const container = this.canvas.parentElement;\n if (!container) return;\n\n const containerWidth = container.clientWidth;\n const containerHeight = container.clientHeight;\n\n const scaleX = containerWidth / this.width;\n const scaleY = containerHeight / this.height;\n const scale = Math.min(scaleX, scaleY);\n const offsetX = (containerWidth - this.width * scale) / 2;\n const offsetY = (containerHeight - this.height * scale) / 2;\n\n this.canvas.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`;\n }\n\n resize(width: number, height: number): void {\n this.canvas.width = width;\n this.canvas.height = height;\n }\n\n private loop(timestamp: number) {\n if (!this.running) {\n return;\n }\n\n if (this.lastTime === 0) {\n this.lastTime = timestamp;\n }\n\n const deltaTime = (timestamp - this.lastTime) / 1000;\n this.lastTime = timestamp;\n\n this.update(deltaTime);\n this.render();\n requestAnimationFrame((timestamp) => this.loop(timestamp));\n }\n\n /**\n * Public - cause I'm old school\n */\n\n public async setup(setupFn: () => void) {\n if (this._initialized) {\n console.warn(\"Engine is already initialized.\");\n return;\n }\n\n if (typeof setupFn !== \"function\") {\n throw new Error(\"Setup function must be provided and must be a function.\");\n }\n\n this.lastTime = 0;\n setupFn();\n this._initialized = true;\n this.running = true;\n requestAnimationFrame((timestamp) => this.loop(timestamp));\n }\n\n public update(deltaTime: number) {\n if (this.player) {\n this.player.update(deltaTime);\n }\n\n if (this.engine.objects) {\n this.engine.objects.updateAll(deltaTime);\n }\n\n this.engine.collisions.detect();\n\n /**\n * There is oportunity to optimize this with much better follow mechanic\n * but I'm to lazy today so won't do it\n */\n if (this.engine.camera && this.player) {\n this.engine.camera.follow(this.player.getPosition());\n }\n }\n\n public render() {\n this.ctx.clearRect(0, 0, this.width, this.height);\n this.ctx.fillStyle = this.cnf.backgroundColor || \"#000000\";\n this.ctx.fillRect(0, 0, this.width, this.height);\n\n if (this.player) {\n this.player.render(this.ctx);\n }\n\n if (this.engine.objects) {\n this.engine.objects.renderAll(this.ctx);\n }\n }\n\n public pause() {\n this.running = false;\n }\n\n public clear() {\n this.running = false;\n this.ctx.clearRect(0, 0, this.width, this.height);\n }\n\n public destroy() {\n // Clean up resources, event listeners, etc. if needed.\n }\n}\n",
13
+ "export default class Input {\n private keys: Set<string> = new Set();\n private mouseButtons: Set<number> = new Set();\n private mousePosition: { x: number; y: number } = { x: 0, y: 0 };\n\n constructor() {\n window.addEventListener(\"keydown\", (e) => {\n this.keys.add(e.key.toLowerCase());\n });\n\n window.addEventListener(\"keyup\", (e) => {\n this.keys.delete(e.key.toLowerCase());\n });\n\n // Mouse events\n window.addEventListener(\"mousedown\", (e) => {\n this.mouseButtons.add(e.button);\n });\n\n window.addEventListener(\"mouseup\", (e) => {\n this.mouseButtons.delete(e.button);\n });\n\n window.addEventListener(\"mousemove\", (e) => {\n this.mousePosition = { x: e.clientX, y: e.clientY };\n });\n }\n\n isKeyDown(key: string): boolean {\n return this.keys.has(key.toLowerCase());\n }\n\n getPressedKeys(): Set<string> {\n return new Set(this.keys);\n }\n\n isMouseButtonDown(button: number): boolean {\n return this.mouseButtons.has(button);\n }\n\n getMousePosition(): { x: number; y: number } {\n return { ...this.mousePosition };\n }\n\n reset(): void {\n this.keys.clear();\n this.mouseButtons.clear();\n }\n}\n",
14
+ "import type { Behaviour } from \"../core/behaviour\";\nimport type { Vector2 } from \"../types\";\n\nexport default abstract class Entity {\n public id: string = \"\";\n protected position: Vector2 = { x: 0, y: 0 };\n protected size = { width: 0, height: 0 };\n\n private behaviorMap: Map<string, Behaviour> = new Map();\n private _sortedBehaviors: Behaviour[] | null = null;\n\n get x(): number {\n return this.position.x;\n }\n\n set x(value: number) {\n this.position.x = value;\n }\n\n get y(): number {\n return this.position.y;\n }\n\n set y(value: number) {\n this.position.y = value;\n }\n\n constructor(id: string, x: number, y: number, width: number, height: number) {\n this.id = id;\n this.position = { x, y };\n this.size = { width, height };\n }\n\n abstract update(deltaTime: number): void;\n abstract render(ctx: CanvasRenderingContext2D): void;\n\n getPosition(): Vector2 {\n return { ...this.position };\n }\n\n getSize(): { width: number; height: number } {\n return { ...this.size };\n }\n\n getBehaviour<T extends Behaviour>(key: string): T | undefined {\n return this.behaviorMap.get(key.toLowerCase()) as T | undefined;\n }\n\n getBehavioursByType<T extends Behaviour>(type: new (...args: any[]) => T): T[] {\n return this.behaviors.filter((b) => b instanceof type) as T[];\n }\n\n hasBehaviour(key: string): boolean {\n return this.behaviorMap.has(key.toLowerCase());\n }\n\n attachBehaviour<T extends Behaviour>(behavior: T): T {\n this.behaviorMap.set(behavior.key, behavior);\n this._sortedBehaviors = null; // Invalidate sorted cache\n\n if (behavior.onAttach) {\n behavior.onAttach();\n }\n return behavior;\n }\n\n detachBehaviour(key: string): void {\n const behavior = this.behaviorMap.get(key.toLowerCase());\n if (!behavior) return;\n\n if (behavior.onDetach) {\n behavior.onDetach();\n }\n this.behaviorMap.delete(key.toLowerCase());\n this._sortedBehaviors = null; // Invalidate sorted cache\n }\n\n private get behaviors(): Behaviour[] {\n if (!this._sortedBehaviors) {\n this._sortedBehaviors = Array.from(this.behaviorMap.values()).sort(\n (a, b) => a.priority - b.priority,\n );\n }\n return this._sortedBehaviors;\n }\n\n protected updateBehaviours(deltaTime: number): void {\n for (const behavior of this.behaviors) {\n if (behavior.enabled) {\n behavior.update(deltaTime);\n }\n }\n }\n\n protected renderBehaviours(ctx: CanvasRenderingContext2D): void {\n for (const behavior of this.behaviors) {\n if (behavior.enabled && behavior.render) {\n behavior.render(ctx);\n }\n }\n }\n}\n",
15
+ "import type { Vector2 } from \"../types\";\nimport Entity from \"./entity\";\n\nexport default abstract class DynamicEntity extends Entity {\n protected velocity: Vector2 = { x: 0, y: 0 };\n protected speed: number = 0;\n\n setVelocity(velocity: Vector2): void {\n this.velocity = velocity;\n }\n\n getVelocity(): Vector2 {\n return { ...this.velocity };\n }\n\n setSpeed(speed: number): void {\n this.speed = speed;\n }\n\n getSpeed(): number {\n return this.speed;\n }\n}\n",
16
+ "import type { Control } from \"../core/behaviours/control\";\nimport type { HealthKit } from \"../core/behaviours/healtkit\";\nimport DynamicEntity from \"./dynamic_entity\";\n\nexport default class Player extends DynamicEntity {\n get control(): Control | undefined {\n return this.getBehaviour<Control>(\"control\");\n }\n\n get healthkit(): HealthKit | undefined {\n return this.getBehaviour<HealthKit>(\"healthkit\");\n }\n\n update(deltaTime: number): void {\n this.updateBehaviours(deltaTime);\n }\n\n render(ctx: CanvasRenderingContext2D): void {\n ctx.fillStyle = \"blue\";\n ctx.fillRect(this.x, this.y, this.size.width, this.size.height);\n this.renderBehaviours(ctx);\n }\n}\n"
17
+ ],
18
+ "mappings": "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",
19
+ "debugId": "AC6FB9916B04E31F64756E2164756E21",
20
+ "names": []
21
+ }
@@ -0,0 +1,30 @@
1
+ import type DynamicEntity from "./entities/dynamic_entity";
2
+ import type Entity from "./entities/entity";
3
+ export interface Vector2 {
4
+ x: number;
5
+ y: number;
6
+ }
7
+ export type GameObject = Entity | DynamicEntity;
8
+ export type ColliderShape = {
9
+ type: "aabb";
10
+ width: number;
11
+ height: number;
12
+ offset?: Vector2;
13
+ } | {
14
+ type: "circle";
15
+ radius: number;
16
+ offset?: Vector2;
17
+ };
18
+ export interface CollisionInfo {
19
+ self: Entity;
20
+ other: Entity;
21
+ selfTags: Set<string>;
22
+ otherTags: Set<string>;
23
+ }
24
+ export interface WorldBounds {
25
+ x: number;
26
+ y: number;
27
+ width: number;
28
+ height: number;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAE5C,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;AAEhD,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEzD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@dryanovski/gamefoo",
3
+ "version": "0.0.1",
4
+ "description": "A lightweight 2D game engine with behavior-based component system",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "sideEffects": false,
23
+ "scripts": {
24
+ "dev": "bun --hot demo/server.ts",
25
+ "build": "rm -rf dist && bun build ./index.ts --outdir dist --format esm --target browser --sourcemap=external && tsc -p tsconfig.build.json",
26
+ "typecheck": "tsc --noEmit",
27
+ "typecheck:watch": "tsc --noEmit --watch",
28
+ "lint": "bunx biome check .",
29
+ "lint:fix": "bunx biome check --write --unsafe .",
30
+ "format": "bunx biome format --write .",
31
+ "check": "bun run typecheck && bun run lint",
32
+ "prepublishOnly": "bun run check && bun run build",
33
+ "prepare": "husky"
34
+ },
35
+ "devDependencies": {
36
+ "@biomejs/biome": "^2.4.4",
37
+ "@types/bun": "latest",
38
+ "husky": "^9.1.7"
39
+ }
40
+ }