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