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