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