]> git.lizzy.rs Git - dragonblocks.git/blob - engine/map.js
Map abstraction and World class
[dragonblocks.git] / engine / map.js
1 /*
2  * map.js
3  *
4  * Copyright 2020 Elias Fleckenstein <eliasfleckenstein@web.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  *
21  *
22  */
23
24 dragonblocks.Map = class
25 {
26         constructor(data)
27         {
28                 if (data)
29                         this.deserialize(data);
30                 else
31                         this.clear();
32
33                 this.initGraphics();
34                 this.updateGraphics();
35         }
36
37         serialize()
38         {
39                 return {
40                         data: this.data,
41                         width: this.width,
42                         height: this.height,
43                         displayLeft: this.displayLeft,
44                         displayTop: this.displayTop,
45                         structures: this.structures,
46                         entities: dblib.removeTmp(this.entities),
47                 };
48         }
49
50         deserialize(data)
51         {
52                 this.data = [];
53                 this.width = data.width;
54                 this.height = data.height;
55                 this.displayLeft = data.displayLeft;
56                 this.displayTop = data.displayTop;
57                 this.entities = [];
58                 this.structures = data.structures;
59
60                 for (let x = 0; x < this.width; x++) {
61                         this.data[x] = [];
62                         for (let y = 0; y < this.height; y++)
63                                 this.setNode(x, y, new dragonblocks.MapNode().createFromMapNode(data.data[x][y]));
64                 }
65
66                 for (let entity of data.entities)
67                         new dragonblocks.SpawnedEntity(entity);
68         }
69
70         clear()
71         {
72                 this.data = [];
73                 this.width = dragonblocks.settings.map.width;
74                 this.height = dragonblocks.settings.map.height;
75                 this.displayTop = dragonblocks.settings.map.height / 2;
76                 this.displayLeft = dragonblocks.settings.map.width / 2 - 5;
77                 this.entities = [];
78                 this.structures = {};
79
80                 for (let x = 0; x < this.width; x++) {
81                         this.data[x] = [];
82                         for (let y = 0; y < this.height; y++)
83                                 this.setNode(x, y, new dragonblocks.MapNode("air"));
84                 }
85         }
86
87         withinBounds(x, y)
88         {
89                 return x < this.width && y < this.height && x >= 0 && y >= 0;
90         }
91
92         getNode(x, y)
93         {
94                 if (this.withinBounds(x, y))
95                         return this.data[x][y];
96         }
97
98         setNode(x, y, node)
99         {
100                 node = new dragonblocks.MapNode(node);
101
102                 if (this.withinBounds(x, y)) {
103                         let oldNode = this.data[x][y];
104                         let oldNodeDef = oldNode instanceof dragonblocks.MapNode && oldNode.toNode();
105                         oldNodeDef && oldNodeDef.onremove && oldNodeDef.onremove(this, x, y);
106
107                         this.data[x][y] = node;
108
109                         let nodeDef = node.toNode();
110                         nodeDef.onset && nodeDef.onset(this, x, y);
111
112                         this.updateNodeGraphics(x, y);
113                 }
114         }
115
116         activate(x, y)
117         {
118                 for (let ix = x - 1; ix <= x + 1; ix++) {
119                         for (let iy = y - 1; iy <= y + 1; iy++) {
120                                 let node = this.getNode(ix, iy);
121
122                                 if (! node)
123                                         continue;
124
125                                 let nodeDef = node.toNode();
126                                 nodeDef.onactivate && nodeDef.onactivate(this, ix, iy);
127
128                                 for (let func of dragonblocks.onActivateCallbacks)
129                                         func(this, ix, iy);
130                         }
131                 }
132         }
133
134         getNodeDisplay(x, y)
135         {
136                 return document.getElementById("dragonblocks.map.node[" + (x - this.displayLeft) + "][" + (y - this.displayTop) + "]");
137         }
138
139         getScreenCoordinates(x, y)
140         {
141                 return [Math.floor(x / dragonblocks.settings.map.scale) + this.displayLeft, Math.floor(y / dragonblocks.settings.map.scale) + this.displayTop];
142         }
143
144         updateNodeGraphics(x, y)
145         {
146                 let nodeDisplay = this.getNodeDisplay(x, y);
147
148                 if (! nodeDisplay)
149                         return;
150
151                 let nodeDef = this.getNode(x, y).toNode();
152
153                 if (! nodeDef)
154                         return;
155
156                 nodeDisplay.style.background = dragonblocks.getTexture(nodeDef.texture);
157                 nodeDisplay.style.backgroundSize = "cover";
158                 nodeDisplay.style.zIndex = nodeDef.zIndex || "1";
159         }
160
161         updateGraphics()
162         {
163                 if (this.displayLeft < 0)
164                         this.displayLeft = 0;
165                 else if (this.displayLeft + this.displayWidth > this.width)
166                         this.displayLeft = this.width - this.displayWidth;
167
168                 if (this.displayTop < 0)
169                         this.displayTop = 0;
170                 else if (this.displayTop + this.displayHeight > this.height)
171                         this.displayTop = this.height - this.displayHeight;
172
173                 for (let x = 0; x < this.displayWidth; x++) {
174                         for(let y = 0; y < this.displayHeight; y++) {
175                                 this.updateNodeGraphics(x + this.displayLeft, y + this.displayTop);
176                         }
177                 }
178         }
179
180         initGraphics()
181         {
182                 this.displayWidth = Math.min(Math.ceil(innerWidth / dragonblocks.settings.map.scale), this.width);
183                 this.displayHeight = Math.min(Math.ceil(innerHeight / dragonblocks.settings.map.scale), this.height);
184
185                 let display = document.body.insertBefore(document.createElement("div"), document.body.firstChild);
186                 display.id = "dragonblocks.map";
187                 display.style.width = this.displayWidth * dragonblocks.settings.map.scale + "px";
188                 display.style.height = this.displayHeight * dragonblocks.settings.map.scale + "px";
189                 display.style.position = "fixed";
190                 display.style.top = "0px";
191                 display.style.left = "0px";
192                 display.style.backgroundColor = "skyblue";
193                 display.style.visibility = "hidden";
194
195                 for (let x = 0; x < this.displayWidth; x++){
196                         for (let y = 0; y < this.displayHeight; y++){
197                                 let nodeDisplay = display.appendChild(document.createElement("div"));
198                                 nodeDisplay.id = "dragonblocks.map.node[" + x + "][" + y + "]";
199                                 nodeDisplay.style.position = "absolute";
200                                 nodeDisplay.style.top = y * dragonblocks.settings.map.scale + "px";
201                                 nodeDisplay.style.left = x * dragonblocks.settings.map.scale + "px";
202                                 nodeDisplay.style.width = dragonblocks.settings.map.scale + "px";
203                                 nodeDisplay.style.height = dragonblocks.settings.map.scale + "px";
204                         }
205                 }
206         }
207
208         addStructure(name, msg, pos)
209         {
210                 this.structures[name] = this.structures[name] || [];
211                 this.structures[name].push({msg, pos});
212         }
213
214         spawnEntity(name, x, y)
215         {
216                 let def = dragonblocks.entities[name];
217
218                 if (def)
219                         return new dragonblocks.SpawnedEntity(def, this, x, y);
220         }
221
222 };
223
224 dragonblocks.onActivateCallbacks = [];
225 dragonblocks.registerOnActivate = func => {
226         dragonblocks.onActivateCallbacks.push(func);
227 };
228
229 dragonblocks.registerOnStarted(_ => {
230         document.getElementById("dragonblocks.map").style.visibility = "visible";
231 });
232
233 dragonblocks.registerOnQuit(_ => {
234         document.getElementById("dragonblocks.map").style.visibility = "hidden";
235 });