@makefully/platypus 4.1.8 → 4.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/platypus",
3
- "version": "4.1.8",
3
+ "version": "4.1.9",
4
4
  "description": "2D tile-based HTML5 game engine with a component-based entity model",
5
5
  "license": "MIT",
6
6
  "contributors": [
@@ -437,14 +437,7 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
437
437
 
438
438
  if (map) {
439
439
  this.updateTile(tile, map[z || 0], x, y);
440
- // Only flag a re-render if the changed tile is within the currently
441
- // cached region. Changes outside the cache will be drawn correctly
442
- // the next time the camera pans to include them.
443
- if (!this.cache.empty &&
444
- x >= this.cache.left && x <= this.cache.right &&
445
- y >= this.cache.top && y <= this.cache.bottom) {
446
- this.updateCache = true;
447
- }
440
+ this.updateCache = true;
448
441
  }
449
442
  },
450
443
 
@@ -458,70 +451,13 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
458
451
  },
459
452
 
460
453
  /**
461
- * Forces an immediate full re-render centered on the current camera
462
- * position. Call this after bulk tile changes to ensure the display
463
- * is up to date without requiring a camera pan.
464
- *
465
- * For the panning cache, we invalidate cachePixels and re-run the full
466
- * camera-update recache logic using the last known camera, which correctly
467
- * handles texture swapping, tile population, and sprite positioning.
454
+ * Forces an immediate full re-render of all cached tile textures.
455
+ * Call this after bulk tile changes to ensure the display is up to date
456
+ * without requiring a camera pan.
468
457
  */
469
458
  "force-render": function () {
470
- if (this.cacheAll) {
471
- // Single/full cache — re-render entire layer.
472
- this.populateTiles(this.cache, null);
473
- this.renderCache(this.cache, this.cacheTexture, this.mapContainer, this.mapContainerWrapper);
474
- } else if (this.cacheTexture) {
475
- // Panning cache — invalidate cachePixels so the recache branch
476
- // runs, then execute the full camera-update recache inline.
477
- const
478
- {viewport: vp} = this.owner.parent.worldCamera,
479
- {cache, cachePixels} = this,
480
- tempC = tempCache,
481
- ctw = this.cacheTilesWidth - 1,
482
- cth = this.cacheTilesHeight - 1,
483
- ctw2 = ctw / 2,
484
- cth2 = cth / 2;
485
-
486
- this.convertCamera(vp);
487
-
488
- // Force the recache branch to run regardless of camera position.
489
- cachePixels.empty = true;
490
-
491
- tempC.setAll(
492
- Math.round((this.laxCam.x - this.left) / this.tileWidth - ctw2) + ctw2,
493
- Math.round((this.laxCam.y - this.top) / this.tileHeight - cth2) + cth2,
494
- ctw, cth
495
- );
496
- if (tempC.left < 0) {
497
- tempC.moveX(tempC.halfWidth);
498
- } else if (tempC.right > this.tilesWidth - 1) {
499
- tempC.moveX(this.tilesWidth - 1 - tempC.halfWidth);
500
- }
501
- if (tempC.top < 0) {
502
- tempC.moveY(tempC.halfHeight);
503
- } else if (tempC.bottom > this.tilesHeight - 1) {
504
- tempC.moveY(this.tilesHeight - 1 - tempC.halfHeight);
505
- }
506
-
507
- this.tilesSpriteCache.texture = this.cacheTexture;
508
- this.cacheTexture = this.cacheTexture.alternate;
509
- this.tilesSprite.texture = this.cacheTexture;
510
-
511
- this.populateTiles(tempC, cache);
512
- this.renderCache(tempC, this.cacheTexture, this.mapContainer, this.mapContainerWrapper, this.tilesSpriteCache);
513
- cache.set(tempC);
514
-
515
- cachePixels.setAll(
516
- (cache.x + 0.5) * this.tileWidth + this.left,
517
- (cache.y + 0.5) * this.tileHeight + this.top,
518
- (cache.width + 1) * this.tileWidth,
519
- (cache.height + 1) * this.tileHeight
520
- );
521
-
522
- this.tilesSprite.x = vp.left - this.laxCam.left + cache.left * this.tileWidth + this.left;
523
- this.tilesSprite.y = vp.top - this.laxCam.top + cache.top * this.tileHeight + this.top;
524
- }
459
+ this.refreshAllCaches();
460
+ this.updateCache = false;
525
461
  }
526
462
  },
527
463
 
@@ -571,8 +507,7 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
571
507
  'handle-render': function () {
572
508
  if (this.updateCache) {
573
509
  this.updateCache = false;
574
- this.populateTiles(this.cache, null);
575
- this.renderCache(this.cache, this.cacheTexture, this.mapContainer, this.mapContainerWrapper);
510
+ this.refreshAllCaches();
576
511
  }
577
512
  }
578
513
  });
@@ -632,21 +567,8 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
632
567
  },
633
568
  'handle-render': function () {
634
569
  if (this.updateCache) {
635
- const
636
- {cache, cacheGrid, cacheTilesHeight, cacheTilesWidth, tilesHeight, tilesWidth} = this,
637
- tsh = tilesHeight - 1,
638
- tsw = tilesWidth - 1;
639
-
640
570
  this.updateCache = false;
641
-
642
- for (let x = 0; x < cacheGrid.length; x++) {
643
- for (let y = 0; y < cacheGrid[x].length; y++) {
644
- cache.setBounds(x * cacheTilesWidth, y * cacheTilesHeight, Math.min((x + 1) * cacheTilesWidth, tsw), Math.min((y + 1) * cacheTilesHeight, tsh));
645
-
646
- this.populateTiles(cache, null);
647
- this.renderCacheWithExtrusion(cache, cacheGrid[x][y].texture, this.mapContainer, this.mapContainerWrapper);
648
- }
649
- }
571
+ this.refreshAllCaches();
650
572
  }
651
573
  }
652
574
  });
@@ -699,24 +621,7 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
699
621
  this.updateBufferRegion(laxCam);
700
622
  }
701
623
 
702
- const
703
- ctw = this.cacheTilesWidth - 1,
704
- cth = this.cacheTilesHeight - 1,
705
- ctw2 = ctw / 2,
706
- cth2 = cth / 2;
707
-
708
- //only attempt to draw children that are relevant
709
- tempC.setAll(Math.round((laxCam.x - this.left) / this.tileWidth - ctw2) + ctw2, Math.round((laxCam.y - this.top) / this.tileHeight - cth2) + cth2, ctw, cth);
710
- if (tempC.left < 0) {
711
- tempC.moveX(tempC.halfWidth);
712
- } else if (tempC.right > this.tilesWidth - 1) {
713
- tempC.moveX(this.tilesWidth - 1 - tempC.halfWidth);
714
- }
715
- if (tempC.top < 0) {
716
- tempC.moveY(tempC.halfHeight);
717
- } else if (tempC.bottom > this.tilesHeight - 1) {
718
- tempC.moveY(this.tilesHeight - 1 - tempC.halfHeight);
719
- }
624
+ this.alignCacheToCamera(tempC, vp);
720
625
 
721
626
  this.tilesSpriteCache.texture = this.cacheTexture;
722
627
  this.cacheTexture = this.cacheTexture.alternate;
@@ -734,17 +639,9 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
734
639
  this.tilesSprite.y = vp.top - laxCam.top + cache.top * this.tileHeight + this.top;
735
640
  },
736
641
  'handle-render': function () {
737
- if (this.updateCache && !this.cache.empty) {
642
+ if (this.updateCache) {
738
643
  this.updateCache = false;
739
- this.populateTiles(this.cache, null);
740
- this.renderCacheClear(this.cache, this.cacheTexture, this.mapContainer, this.mapContainerWrapper);
741
- this.tilesSpriteCache.texture = this.cacheTexture;
742
- this.cachePixels.setAll(
743
- (this.cache.x + 0.5) * this.tileWidth + this.left,
744
- (this.cache.y + 0.5) * this.tileHeight + this.top,
745
- (this.cache.width + 1) * this.tileWidth,
746
- (this.cache.height + 1) * this.tileHeight
747
- );
644
+ this.refreshAllCaches();
748
645
  }
749
646
  }
750
647
  });
@@ -782,24 +679,7 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
782
679
  this.updateBufferRegion(laxCam);
783
680
  }
784
681
 
785
- const
786
- ctw = this.cacheTilesWidth - 1,
787
- cth = this.cacheTilesHeight - 1,
788
- ctw2 = ctw / 2,
789
- cth2 = cth / 2;
790
-
791
- //only attempt to draw children that are relevant
792
- tempC.setAll(Math.round((laxCam.x - this.left) / this.tileWidth - ctw2) + ctw2, Math.round((laxCam.y - this.top) / this.tileHeight - cth2) + cth2, ctw, cth);
793
- if (tempC.left < 0) {
794
- tempC.moveX(tempC.halfWidth);
795
- } else if (tempC.right > this.tilesWidth - 1) {
796
- tempC.moveX(this.tilesWidth - 1 - tempC.halfWidth);
797
- }
798
- if (tempC.top < 0) {
799
- tempC.moveY(tempC.halfHeight);
800
- } else if (tempC.bottom > this.tilesHeight - 1) {
801
- tempC.moveY(this.tilesHeight - 1 - tempC.halfHeight);
802
- }
682
+ this.alignCacheToCamera(tempC, vp);
803
683
 
804
684
  this.populateTiles(tempC, null);
805
685
  cache.set(tempC);
@@ -1079,7 +959,151 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
1079
959
  this.mapContainer.mask = new Graphics().rect(0, 0, this.cacheClipWidth, this.cacheClipHeight).fill(0x000000);
1080
960
  },
1081
961
 
962
+ /**
963
+ * Computes tile bounds for the panning cache centered on the current camera.
964
+ *
965
+ * @param {platypus.AABB} bounds - Receives the computed tile bounds.
966
+ * @param {platypus.AABB} viewport - The camera viewport.
967
+ */
968
+ alignCacheToCamera: function (bounds, viewport) {
969
+ const
970
+ ctw = this.cacheTilesWidth - 1,
971
+ cth = this.cacheTilesHeight - 1,
972
+ ctw2 = ctw / 2,
973
+ cth2 = cth / 2;
974
+
975
+ this.convertCamera(viewport);
976
+
977
+ bounds.setAll(
978
+ Math.round((this.laxCam.x - this.left) / this.tileWidth - ctw2) + ctw2,
979
+ Math.round((this.laxCam.y - this.top) / this.tileHeight - cth2) + cth2,
980
+ ctw, cth
981
+ );
982
+
983
+ if (bounds.left < 0) {
984
+ bounds.moveX(bounds.halfWidth);
985
+ } else if (bounds.right > this.tilesWidth - 1) {
986
+ bounds.moveX(this.tilesWidth - 1 - bounds.halfWidth);
987
+ }
988
+
989
+ if (bounds.top < 0) {
990
+ bounds.moveY(bounds.halfHeight);
991
+ } else if (bounds.bottom > this.tilesHeight - 1) {
992
+ bounds.moveY(this.tilesHeight - 1 - bounds.halfHeight);
993
+ }
994
+ },
995
+
996
+ /**
997
+ * Re-renders every texture in a multi-texture full-map cache grid.
998
+ */
999
+ refreshAllCacheGrid: function () {
1000
+ const
1001
+ {cache, cacheGrid, cacheTilesHeight, cacheTilesWidth, tilesHeight, tilesWidth} = this,
1002
+ tsh = tilesHeight - 1,
1003
+ tsw = tilesWidth - 1;
1004
+
1005
+ for (let x = 0; x < cacheGrid.length; x++) {
1006
+ for (let y = 0; y < cacheGrid[x].length; y++) {
1007
+ cache.setBounds(
1008
+ x * cacheTilesWidth,
1009
+ y * cacheTilesHeight,
1010
+ Math.min((x + 1) * cacheTilesWidth, tsw),
1011
+ Math.min((y + 1) * cacheTilesHeight, tsh)
1012
+ );
1013
+
1014
+ this.populate(cache, null);
1015
+ this.renderCacheWithExtrusion(cache, cacheGrid[x][y].texture, this.mapContainer, this.mapContainerWrapper);
1016
+ }
1017
+ }
1018
+ },
1019
+
1020
+ /**
1021
+ * Re-renders the current panning-cache window in place.
1022
+ * Uses the alternate render texture while drawing so we never render
1023
+ * into the texture that is currently displayed on screen.
1024
+ *
1025
+ * @param {platypus.AABB} [viewport] - Optional camera viewport.
1026
+ */
1027
+ refreshPanningCache: function (viewport) {
1028
+ const
1029
+ {cache, cachePixels, laxCam, left, top, tilesSprite} = this,
1030
+ vp = viewport ?? this.owner.parent.worldCamera?.viewport;
1031
+
1032
+ if (!vp) {
1033
+ return;
1034
+ }
1035
+
1036
+ if (cache.empty) {
1037
+ this.alignCacheToCamera(cache, vp);
1038
+ }
1039
+
1040
+ if (cache.empty) {
1041
+ return;
1042
+ }
1043
+
1044
+ this.detachCacheCopy();
1045
+
1046
+ this.tilesSpriteCache.texture = this.cacheTexture;
1047
+ this.cacheTexture = this.cacheTexture.alternate;
1048
+ tilesSprite.texture = this.cacheTexture;
1049
+
1050
+ this.populate(cache, null);
1051
+ this.renderCacheClear(cache, this.cacheTexture, this.mapContainer, this.mapContainerWrapper);
1052
+
1053
+ this.tilesSpriteCache.texture = this.cacheTexture;
1054
+ cachePixels.setAll(
1055
+ (cache.x + 0.5) * this.tileWidth + left,
1056
+ (cache.y + 0.5) * this.tileHeight + top,
1057
+ (cache.width + 1) * this.tileWidth,
1058
+ (cache.height + 1) * this.tileHeight
1059
+ );
1060
+
1061
+ this.convertCamera(vp);
1062
+ tilesSprite.x = vp.left - laxCam.left + cache.left * this.tileWidth + left;
1063
+ tilesSprite.y = vp.top - laxCam.top + cache.top * this.tileHeight + top;
1064
+ },
1065
+
1066
+ detachCacheCopy: function () {
1067
+ const
1068
+ {mapContainer, tilesSpriteCache} = this;
1069
+
1070
+ if (tilesSpriteCache?.parent === mapContainer) {
1071
+ mapContainer.removeChild(tilesSpriteCache);
1072
+ }
1073
+ },
1074
+
1075
+ /**
1076
+ * Re-renders all cached tile textures for the active caching strategy.
1077
+ *
1078
+ * @param {platypus.AABB} [viewport] - Optional camera viewport for panning caches.
1079
+ */
1080
+ refreshAllCaches: function (viewport) {
1081
+ if (!this.tileCache) {
1082
+ this.cachePixels.empty = true;
1083
+ return;
1084
+ }
1085
+
1086
+ if (this.cacheGrid) {
1087
+ this.refreshAllCacheGrid();
1088
+ return;
1089
+ }
1090
+
1091
+ if (this.cacheAll && this.cacheTexture) {
1092
+ this.populate(this.cache, null);
1093
+ this.render(this.cache, this.cacheTexture, this.mapContainer, this.mapContainerWrapper);
1094
+ return;
1095
+ }
1096
+
1097
+ if (this.cacheTexture) {
1098
+ this.refreshPanningCache(viewport);
1099
+ }
1100
+ },
1101
+
1082
1102
  populateTiles: function (bounds, oldBounds) {
1103
+ // When oldBounds is provided, only tiles outside the previous window are
1104
+ // drawn. That is correct for camera scrolling (reuse pixels from the old
1105
+ // cache texture) but must not be used for in-place tile edits — pass null
1106
+ // to redraw the entire bounds.
1083
1107
  const
1084
1108
  tiles = arrayCache.setUp();
1085
1109
 
@@ -1194,6 +1218,11 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
1194
1218
  container: wrapper,
1195
1219
  target: renderTexture
1196
1220
  });
1221
+
1222
+ if (oldCache?.parent === src) {
1223
+ src.removeChild(oldCache);
1224
+ }
1225
+
1197
1226
  renderTexture.requiresUpdate = true;
1198
1227
  },
1199
1228
 
@@ -1201,6 +1230,8 @@ export default createComponentClass(/** @lends platypus.components.RenderTiles.p
1201
1230
  const
1202
1231
  {renderer} = this;
1203
1232
 
1233
+ this.detachCacheCopy();
1234
+
1204
1235
  src.x = -bounds.left * this.tileWidth;
1205
1236
  src.y = -bounds.top * this.tileHeight;
1206
1237
  renderer.render({