]> git.lizzy.rs Git - dragonblocks.git/blob - engine/spawned_entity.js
Add Map Manager
[dragonblocks.git] / engine / spawned_entity.js
1 /*
2  * spawned_entity.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.SpawnedEntity = class
25 {
26         constructor(def, map, x, y)
27         {
28                 dblib.copy(this, def);
29                 this.tmp = {map};
30
31                 if (def instanceof dragonblocks.Entity) {
32                         this.jumping = this.movingRight = this.movingLeft = this.movingUp = this.movingDown = false;
33                         this.x = x;
34                         this.y = y;
35                         this.ax = 0;
36                         this.ay = 0;
37                         this.physicsResetX();
38                         this.physicsResetY();
39                         this.meta = this.meta || {};
40                         this.toEntity().oninit && this.toEntity().oninit(this);
41                 }
42
43                 this.restorePhysics();
44                 this.addGraphics();
45
46                 let self = this;
47
48                 this.tickInterval = setInterval(_ => {
49                         self.tick();
50                 }, 100);
51
52                 this.physicInterval = setInterval(_=>{
53                         self.physicsTick();
54                 });
55
56                 let entityDef = this.toEntity();
57                 entityDef.onspawn && entityDef.onspawn(this);
58
59                 addEventListener("focus", _ => {
60                         self.restorePhysics();
61                 });
62
63                 addEventListener("blur", _ => {
64                         self.restorePhysics();
65                 });
66
67                 map.entities.push(this);
68         }
69
70         toEntity()
71         {
72                 return dragonblocks.entities[this.name];
73         }
74
75         removeFromMap()
76         {
77                 let self = this;
78                 this.map.entities = this.map.entities.filter(entity => {
79                         return entity != self;
80                 });
81         }
82
83         setMap(map, x, y)
84         {
85                 this.removeFromMap();
86                 map.entities.push(this);
87
88                 this.tmp.map = map;
89                 this.tmp.display = map.entityContainer.appendChild(this.tmp.display);
90
91                 this.teleport(x, y);
92         }
93
94         despawn()
95         {
96                 let entityDef = this.toEntity();
97                 entityDef.ondespawn && entityDef.ondespawn(this);
98
99                 this.removeFromMap();
100
101                 clearInterval(this.physicInterval);
102                 clearInterval(this.tickInterval);
103
104                 this.tmp.display && this.tmp.display.remove();
105         }
106
107         restorePhysics()
108         {
109                 this.tx0 = new Date().getTime() / 1000;
110                 this.ty0 = new Date().getTime() / 1000;
111                 this.x0 = this.x;
112                 this.y0 = this.y;
113         }
114
115         collisionX()
116         {
117                 if (this.x < 0)
118                         return false;
119
120                 if (this.x + this.width > this.map.width)
121                         return false;
122
123                 return this.collision();
124         }
125
126         collisionY()
127         {
128                 if (this.y < 0)
129                         return false;
130
131                 if (this.y + this.height > this.map.height)
132                         return false;
133
134                 return this.collision();
135         }
136
137         collision()
138         {
139                 for (let ix = Math.floor(this.x); ix <= Math.ceil(this.x + this.width - 0.01) - 1; ix++)
140                         for (let iy = Math.floor(this.y); iy <= Math.ceil(this.y + this.height - 0.01) - 1; iy++)
141                                 if (this.map.getNode(ix, iy).mobstable)
142                                         return false;
143                 return true;
144         }
145
146         physicsResetX()
147         {
148                 this.tx0 = new Date().getTime() / 1000;
149                 this.vx = 0;
150                 this.x0 = this.x;
151                 this.x = Math.round(this.x * 10) / 10;
152         }
153
154         physicsResetY()
155         {
156                 this.ty0 = new Date().getTime() / 1000;
157                 this.vy = 0;
158                 this.y0 = this.y;
159                 this.y = Math.round(this.y * 10) / 10;
160         }
161
162         physicsTick()
163         {
164                 let t = new Date().getTime() / 1000;
165
166                 let oldX = this.x;
167                 let dtx = t - this.tx0;
168
169                 if (this.ax)
170                         this.x = this.ax * dtx * dtx + this.vx * dtx + this.x0;
171                 else if (this.vx)
172                         this.x = this.vx * dtx + this.x0;
173
174                 if (! this.collisionX()) {
175                         this.x = oldX;
176                         this.physicsResetX();
177                         this.toEntity().oncollide && this.toEntity().oncollide(this);
178                 }
179
180                 let oldY = this.y;
181                 let dty = t - this.ty0;
182
183                 if (this.ay)
184                         this.y = this.ay * dty * dty + this.vy * dty + this.y0;
185                 else if (this.vy)
186                         this.y = this.vy * dty + this.y0;
187
188                 if (! this.collisionY()) {
189                         this.y = oldY;
190                         this.physicsResetY();
191                         this.toEntity().oncollide && this.toEntity().oncollide(this);
192                 }
193
194                 this.y = Math.round(this.y * 50) / 50;
195
196                 this.updateGraphics();
197         }
198
199         touch(map, x, y)
200         {
201                 if (map != this.map)
202                         return false;
203                 for (let ix = Math.floor(this.x); ix <= Math.ceil(this.x + this.width - 0.01) - 1; ix++)
204                         for (let iy = Math.floor(this.y); iy <= Math.ceil(this.y + this.height - 0.01) - 1; iy++)
205                                 if (iy == y && ix == x)
206                                         return true;
207         }
208
209         addGraphics(obj)
210         {
211                 this.tmp.display = this.map.entityContainer.appendChild(document.createElement("div"));
212                 this.tmp.display.style.position = "absolute";
213                 this.tmp.display.style.width = this.width * dragonblocks.settings.mapDisplay.scale + "px";
214                 this.tmp.display.style.height = this.height * dragonblocks.settings.mapDisplay.scale + "px";
215                 this.tmp.display.style.zIndex = "0";
216
217                 this.tmp.display.addEventListener("mouseover", event => {
218                         event.srcElement.style.boxShadow = "0 0 0 1px black inset";
219                 });
220
221                 this.tmp.display.addEventListener("mouseleave", event => {
222                         event.srcElement.style.boxShadow = "none";
223                 });
224
225                 let self = this;
226
227                 this.tmp.display.addEventListener("mousedown", event => {
228                         let entityDef = self.toEntity();
229
230                         switch (event.which) {
231                                 case 1:
232                                         entityDef.onpunch && entityDef.onpunch(self);
233                                         break;
234
235                                 case 3:
236                                         entityDef.onclick && entityDef.onclick(self);
237                                         break;
238                         }
239                 });
240
241                 this.updateTexture();
242                 this.updateGraphics();
243         }
244
245         async updateGraphics()
246         {
247                 if (! this.tmp.display)
248                         return;
249
250                 this.tmp.display.style.left = this.x * dragonblocks.settings.mapDisplay.scale + "px";
251                 this.tmp.display.style.top = this.y * dragonblocks.settings.mapDisplay.scale + "px";
252         }
253
254         updateTexture()
255         {
256                 this.tmp.display.style.background = dragonblocks.getTexture(this.texture);
257         }
258
259         teleport(x, y)
260         {
261                 this.physicsResetX();
262                 this.physicsResetY();
263                 this.x = x;
264                 this.y = y;
265         }
266
267         moveLeft()
268         {
269                 if (this.vx == -this.horizontalSpeed)
270                         return;
271
272                 if (this.movingRight)
273                         this.movingRight = false;
274
275                 this.movingLeft = true;
276                 this.physicsResetX();
277                 this.vx = -this.horizontalSpeed;
278         }
279
280         moveRight()
281         {
282                 if (this.vx == this.horizontalSpeed)
283                         return;
284
285                 if (this.movingLeft)
286                         this.movingLeft = false;
287
288                 this.movingRight = true;
289                 this.physicsResetX();
290                 this.vx = this.horizontalSpeed;
291         }
292
293         stop()
294         {
295                 this.movingLeft = false;
296                 this.movingRight = false;
297                 this.physicsResetX();
298         }
299
300         moveDown()
301         {
302                 if (this.vy == this.verticalSpeed)
303                         return;
304
305                 if (this.movingDown)
306                         this.movingDown = false;
307
308                 this.movingDown = true;
309                 this.physicsResetY();
310                 this.vy = this.verticalSpeed;
311         }
312
313         moveUp()
314         {
315                 if (this.vy == -this.verticalSpeed)
316                         return;
317
318                 if (this.movingUp)
319                         this.movingUp = false;
320
321                 this.movingUp = true;
322                 this.physicsResetY();
323                 this.vy = -this.verticalSpeed;
324         }
325
326         stopFly()
327         {
328                 this.movingUp = false;
329                 this.movingDown = false;
330                 this.physicsResetY();
331         }
332
333         canJump()
334         {
335                 for (let x = Math.floor(this.x); x <= Math.ceil(this.x + this.width - 0.01) - 1; x++) {
336                         let entityY = this.y + this.height;
337                         let y = Math.ceil(entityY);
338
339                         if (y - entityY <= 0.01) {
340                                 let node = this.map.getNode(x, y);
341
342                                 if (! node || node.mobstable)
343                                         return true;
344                         }
345                 }
346         }
347
348         jump()
349         {
350                 if (! this.canJump())
351                         return;
352
353                 this.jumping = true;
354                 this.vy = -this.verticalSpeed;
355         }
356
357         stopJump()
358         {
359                 this.jumping = false;
360         }
361
362         jumpOnce()
363         {
364                 this.vy = -this.verticalSpeed;
365         }
366
367         set gravity(value)
368         {
369                 this._gravity = value;
370
371                 if (this._gravity)
372                         this.ay = dragonblocks.settings.physics.gravity;
373                 else
374                         this.ay = 0;
375         }
376
377         get gravity()
378         {
379                 return this._gravity;
380         }
381
382         tick()
383         {
384                 if (this.movingLeft)
385                         this.moveLeft();
386                 if (this.movingRight)
387                         this.moveRight();
388                 if (this.movingUp)
389                         this.moveUp();
390                 if (this.movingDown)
391                         this.moveDown();
392
393                 if (this.jumping)
394                         this.jump();
395
396                 if (this.gravity)
397                         this.gravity = true;
398         }
399
400         get map()
401         {
402                 return this.tmp.map;
403         }
404 };