summaryrefslogtreecommitdiffstats
path: root/src/controller/gamecontext.ts
blob: 295872da0dc26202e59fc4931f000169c38fb948 (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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { MapData } from '../model/data/map';

import { loadSimpleEntity } from '../view/entity';
import { DirectionHandler } from '../view/input/directionhandler';
import { loadMap } from '../view/map';
import { Renderer } from '../view/renderer/renderer';
import { SpriteView } from '../view/sprite';

import { LineSegment, Movement } from '../math/line';
import { getJSON } from '../util';

import { vec2 } from 'gl-matrix';

export class GameContext {
	public static async load(renderer: Renderer): Promise<GameContext> {
		const mapView = this.loadMap(renderer);
		const entity = loadSimpleEntity(renderer, 'simple_circle');

		return new GameContext(
			renderer,
			await mapView,
			await entity,
		);
	}

	private static async loadMap(renderer: Renderer): Promise<SpriteView> {
		const map = new MapData(await getJSON('resources/map/test.json'));
		return loadMap(renderer, map);
	}

	private time: number|null = null;

	private readonly tick = 10; // ms per tick
	private readonly speed = 0.05; // movement per tick
	private readonly maxSkip = 20; // maximum ticks to process in a single render step

	private readonly input: DirectionHandler;

	private readonly entityPos: vec2 = vec2.clone([6, 6]);
	private readonly entityMovement: vec2 = vec2.create();

	private readonly walls: LineSegment[] = [
		LineSegment.fromPoints(vec2.fromValues(1, 1), vec2.fromValues(11, 1)),

		LineSegment.fromPoints(vec2.fromValues(11, 1), vec2.fromValues(11, 5)),
		LineSegment.fromPoints(vec2.fromValues(11, 5), vec2.fromValues(12, 5)),
		LineSegment.fromPoints(vec2.fromValues(12, 5), vec2.fromValues(12, 7)),
		LineSegment.fromPoints(vec2.fromValues(12, 7), vec2.fromValues(11, 7)),
		LineSegment.fromPoints(vec2.fromValues(11, 7), vec2.fromValues(11, 11)),

		LineSegment.fromPoints(vec2.fromValues(11, 11), vec2.fromValues(7, 11)),
		LineSegment.fromPoints(vec2.fromValues(7, 11), vec2.fromValues(7, 12)),
		LineSegment.fromPoints(vec2.fromValues(7, 12), vec2.fromValues(5, 12)),
		LineSegment.fromPoints(vec2.fromValues(5, 12), vec2.fromValues(5, 11)),
		LineSegment.fromPoints(vec2.fromValues(5, 11), vec2.fromValues(1, 11)),

		LineSegment.fromPoints(vec2.fromValues(1, 11), vec2.fromValues(1, 7)),
		LineSegment.fromPoints(vec2.fromValues(1, 7), vec2.fromValues(0, 7)),
		LineSegment.fromPoints(vec2.fromValues(0, 7), vec2.fromValues(0, 5)),
		LineSegment.fromPoints(vec2.fromValues(0, 5), vec2.fromValues(1, 5)),
		LineSegment.fromPoints(vec2.fromValues(1, 5), vec2.fromValues(1, 1)),
	];

	private constructor(
		private readonly renderer: Renderer,
		private readonly mapView: SpriteView,
		private readonly entity: SpriteView,
	) {
		this.input = new DirectionHandler();
		this.input.addListener((v) => {
			if (vec2.sqrLen(v) > 0)
				vec2.normalize(this.entityMovement, v);
			else
				vec2.copy(this.entityMovement, [0, 0]);
		});

		window.requestAnimationFrame(this.render);
	}

	private updateTime(time: number): number {
		const diff = this.time !== null ? time - this.time : 0;
		this.time = time;

		return diff;
	}

	private updateStep(): void {
		const dest = vec2.scaleAndAdd(vec2.create(), this.entityPos, this.entityMovement, this.speed);
		const dest2 = vec2.create();

		let rescan = true;

		while (rescan) {
			rescan = false;

			if (vec2.equals(dest, this.entityPos))
				return;

			const move = new Movement(this.entityPos, dest);

			for (const w of this.walls) {
				if (!w.collidesMove(dest2, move))
					continue;

				if (!vec2.exactEquals(dest, dest2)) {
					vec2.copy(dest, dest2);
					rescan = true;
					break;
				}
			}
		}

		vec2.copy(this.entityPos, dest);
	}

	private update(time: number): void {
		const diff = Math.min(this.maxSkip, this.updateTime(time));

		if (vec2.sqrLen(this.entityMovement) === 0) {
			this.renderer.snapToGrid(this.entityPos, this.entityPos);
			return;
		}

		for (let i = 0; i < diff; i++)
			this.updateStep();
	}

	private render = (time: number) => {
		this.update(Math.round(time / this.tick));

		this.renderer.setCenter(this.entityPos);
		this.renderer.clear();

		this.mapView.render();

		this.renderer.setTranslation(this.entityPos);
		this.entity.render();

		window.requestAnimationFrame(this.render);
	}
}