1 | "use strict";
|
2 |
|
3 | var ImageLoader = require("./image-loader");
|
4 | var Input = require("./input");
|
5 | var Scene = require("./scene");
|
6 | var SoundLoader = require("./sound-loader");
|
7 |
|
8 | function clone(obj) {
|
9 | if (obj === undefined) {
|
10 | return undefined;
|
11 | }
|
12 | return JSON.parse(JSON.stringify(obj));
|
13 | }
|
14 | function splitFilmStripAnimations(animations) {
|
15 | Object.keys(animations).forEach(function(key) {
|
16 | var firstFrame = animations[key][0];
|
17 | if (firstFrame.filmstripFrames) {
|
18 | splitFilmStripAnimation(animations, key);
|
19 | }
|
20 | });
|
21 | }
|
22 | function splitFilmStripAnimation(animations, key) {
|
23 | var firstFrame = animations[key][0];
|
24 | if (firstFrame.properties.image.sourceWidth % firstFrame.filmstripFrames != 0) {
|
25 | console.warn("The \"" + key + "\" animation is " + firstFrame.properties.image.sourceWidth + " pixels wide and that is is not evenly divisible by " + firstFrame.filmstripFrames + " frames.");
|
26 | }
|
27 | for (var i = 0; i < firstFrame.filmstripFrames; i++) {
|
28 | var frameWidth = firstFrame.properties.image.sourceWidth / firstFrame.filmstripFrames;
|
29 | var newFrame = clone(firstFrame);
|
30 | newFrame.properties.image.sourceX = frameWidth * i;
|
31 | newFrame.properties.image.sourceWidth = frameWidth;
|
32 | animations[key].push(newFrame);
|
33 | }
|
34 | animations[key].splice(0,1);
|
35 | }
|
36 |
|
37 | function Game(canvas, customRequire) {
|
38 | this.animations = customRequire("./data/animations");
|
39 | splitFilmStripAnimations(this.animations);
|
40 | this.canvas = canvas;
|
41 | this.context = canvas.getContext("2d");
|
42 | this.entities = customRequire("./data/entities");
|
43 | this.images = new ImageLoader();
|
44 | this.images.loadFromManifest(customRequire("./data/images"));
|
45 | this.input = new Input(customRequire("./data/inputs"), canvas);
|
46 | this.require = customRequire;
|
47 | this.scenes = customRequire("./data/scenes");
|
48 | this.sounds = new SoundLoader();
|
49 | this.sounds.loadFromManifest(customRequire("./data/sounds"));
|
50 | this.systems = customRequire("./data/systems");
|
51 | this.prefabs = customRequire("./data/prefabs");
|
52 |
|
53 | this.scaleCanvasToCssSize();
|
54 | window.addEventListener("resize", this.onCanvasResize.bind(this));
|
55 |
|
56 | this.makeScenes(this.scenes);
|
57 | }
|
58 | Game.prototype.makeScenes = function(sceneList) {
|
59 | Object.keys(sceneList).forEach(function(scene) {
|
60 | if (sceneList[scene].first) {
|
61 | this.scene = this.makeScene(scene, sceneList[scene], {});
|
62 | }
|
63 | }.bind(this));
|
64 | };
|
65 | Game.prototype.makeScene = function(name, sceneData, sceneArgs) {
|
66 | var scene = new Scene();
|
67 |
|
68 | var data = this.makeSceneData(scene.entities, sceneArgs);
|
69 | scene.simulation.add(function() {
|
70 | data.input.processUpdates();
|
71 | });
|
72 | this.installSystems(name, this.systems.simulation, scene.simulation, data);
|
73 | this.installSystems(name, this.systems.renderer, scene.renderer, data);
|
74 | scene.entities.load(clone(this.entities[name]));
|
75 |
|
76 | if (typeof sceneData.onEnter === "string") {
|
77 | var enterScript = this.require(sceneData.onEnter);
|
78 | if (typeof enterScript === "function") {
|
79 | enterScript = enterScript.bind(scene, data);
|
80 | }
|
81 | scene.onEnter = enterScript;
|
82 | }
|
83 | if (typeof sceneData.onExit === "string") {
|
84 | var exitScript = this.require(sceneData.onExit);
|
85 | if (typeof exitScript === "function") {
|
86 | exitScript = exitScript.bind(scene, data);
|
87 | }
|
88 | scene.onExit = exitScript;
|
89 | }
|
90 |
|
91 | return scene;
|
92 | };
|
93 | Game.prototype.makeSceneData = function(entities, sceneArgs) {
|
94 | return {
|
95 | animations: this.animations,
|
96 | arguments: sceneArgs || {},
|
97 | canvas: this.canvas,
|
98 | context: this.context,
|
99 | entities: entities,
|
100 | images: this.images,
|
101 | input: this.input,
|
102 | require: this.require,
|
103 | scaleCanvasToCssSize: this.scaleCanvasToCssSize.bind(this),
|
104 | scaleCanvasToFitRectangle: this.scaleCanvasToFitRectangle.bind(this),
|
105 | sounds: this.sounds,
|
106 | switchScene: this.switchScene.bind(this),
|
107 | instantiatePrefab: this.instantiatePrefab.bind(this)
|
108 | };
|
109 | };
|
110 | Game.prototype.installSystems = function(scene, systems, ecs, data) {
|
111 | systems.forEach(function(system) {
|
112 | if (system.scenes.indexOf(scene) === -1) {
|
113 | return;
|
114 | }
|
115 | var script = this.require(system.name);
|
116 | if (script === undefined) {
|
117 | console.error("failed to load script", system.name);
|
118 | }
|
119 | script(ecs, data);
|
120 | }.bind(this));
|
121 | };
|
122 | Game.prototype.switchScene = function(name, sceneArgs) {
|
123 | if (this.scene !== undefined) {
|
124 | this.scene.stop();
|
125 | }
|
126 | this.scene = this.makeScene(name, this.scenes[name], sceneArgs);
|
127 | this.scene.start(this.context);
|
128 | };
|
129 | Game.prototype.onCanvasResize = function() {
|
130 | this.resizer();
|
131 | };
|
132 | Game.prototype.scaleCanvasToCssSize = function() {
|
133 | this.resizer = function() {
|
134 | var canvasStyle = window.getComputedStyle(this.canvas);
|
135 | var width = parseInt(canvasStyle.width);
|
136 | var height = parseInt(canvasStyle.height);
|
137 | this.canvas.width = width;
|
138 | this.canvas.height = height;
|
139 | }.bind(this);
|
140 | this.resizer();
|
141 | };
|
142 | Game.prototype.scaleCanvasToFitRectangle = function(width, height) {
|
143 | this.resizer = function() {
|
144 | var canvasStyle = window.getComputedStyle(this.canvas);
|
145 | var cssWidth = parseInt(canvasStyle.width);
|
146 | var cssHeight = parseInt(canvasStyle.height);
|
147 | var cssAspectRatio = cssWidth / cssHeight;
|
148 |
|
149 | var desiredWidth = width;
|
150 | var desiredHeight = height;
|
151 | var desiredAspectRatio = width / height;
|
152 | if (desiredAspectRatio > cssAspectRatio) {
|
153 | desiredHeight = Math.floor(width / cssAspectRatio);
|
154 | } else if (desiredAspectRatio < cssAspectRatio) {
|
155 | desiredWidth = Math.floor(height * cssAspectRatio);
|
156 | }
|
157 |
|
158 | this.canvas.width = desiredWidth;
|
159 | this.canvas.height = desiredHeight;
|
160 | }.bind(this);
|
161 | this.resizer();
|
162 | };
|
163 | Game.prototype.instantiatePrefab = function(name) {
|
164 | var id = this.scene.entities.create();
|
165 | var prefab = this.prefabs[name];
|
166 | Object.keys(prefab).forEach(function(key) {
|
167 | if (key === "id") {
|
168 | return;
|
169 | }
|
170 | this.scene.entities.set(id, key, clone(prefab[key]));
|
171 | }.bind(this));
|
172 | return id;
|
173 | };
|
174 |
|
175 | module.exports = Game;
|