Skip to content

Commit b4a9424

Browse files
Copilotjbampton
andauthored
fix(code-breaker, dev-duel): fix canvas visibility, texture collision, and safe tile iteration
Agent-Logs-Url: https://github.com/NextCommunity/NextCommunity.github.io/sessions/ad42dcb0-9f21-4451-bc76-4eae16616da5 Co-authored-by: jbampton <418747+jbampton@users.noreply.github.com>
1 parent ec1a558 commit b4a9424

2 files changed

Lines changed: 46 additions & 17 deletions

File tree

src/assets/js/games/code-breaker.js

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ const CodeBreaker = (function () {
3939
const W = Math.min(window.innerWidth, 900);
4040
const H = Math.min(window.innerHeight, 600);
4141

42+
// Destroy any previous game instance BEFORE building the new overlay so
43+
// that GameManager.destroy() (which removes "#game-canvas-{id}") doesn't
44+
// rip out the canvas wrapper we are about to create.
45+
GameManager.destroy(GAME_ID);
46+
GameManager.destroyOverlay(GAME_ID);
47+
4248
const overlay = GameManager.createOverlay(GAME_ID);
4349

4450
// Title bar above the canvas
@@ -50,9 +56,11 @@ const CodeBreaker = (function () {
5056
"⌨️ CODE BREAKER" + (devName ? " — " + devName : "");
5157
overlay.appendChild(titleBar);
5258

53-
// Canvas container
59+
// Canvas container — intentionally has NO id matching "game-canvas-{GAME_ID}"
60+
// so that future GameManager.destroy() calls don't remove it before Phaser
61+
// has a chance to mount its canvas inside it. Cleanup is handled by
62+
// destroyOverlay() which removes the entire overlay (including this wrapper).
5463
const canvasWrap = document.createElement("div");
55-
canvasWrap.id = "game-canvas-" + GAME_ID;
5664
canvasWrap.style.cssText = "position:relative;";
5765
overlay.appendChild(canvasWrap);
5866

@@ -76,7 +84,10 @@ const CodeBreaker = (function () {
7684
},
7785
};
7886

79-
GameManager.create(GAME_ID, config);
87+
// Register the instance directly so destroy() can still clean it up,
88+
// but skip the internal destroy() pre-call (already done above).
89+
var instance = new Phaser.Game(config);
90+
GameManager.instances[GAME_ID] = instance;
8091
}
8192

8293
// ─── Scene ───────────────────────────────────────────────────────────────
@@ -179,8 +190,10 @@ const CodeBreaker = (function () {
179190
_spawnTile(scene, getGameTheme(), scene.scale.width);
180191
}
181192

182-
// Check if any tile fell off the bottom
183-
scene.cb_tiles.getChildren().forEach(function (tile) {
193+
// Check if any tile fell off the bottom.
194+
// Use .slice() to iterate over a copy — destroying tiles inside forEach
195+
// mutates the live getChildren() array and causes tiles to be skipped.
196+
scene.cb_tiles.getChildren().slice().forEach(function (tile) {
184197
if (tile.y > scene.scale.height + 40) {
185198
tile.destroy();
186199
_loseLife(scene);
@@ -197,20 +210,25 @@ const CodeBreaker = (function () {
197210
scene.cb_skills[Phaser.Math.Between(0, scene.cb_skills.length - 1)];
198211
const rarity = skillRarity(skill);
199212
const color = RARITY_COLORS[rarity] || "#94a3b8";
200-
const hexColor = parseInt(color.replace("#", "0x"));
213+
const hexColor = parseInt(color.replace("#", ""), 16);
214+
const textureKey = "cb_tile_" + skill;
201215

202216
const x = Phaser.Math.Between(60, W - 60);
203217

204-
// Background pill
205-
const bg = scene.add.graphics();
206-
bg.fillStyle(hexColor, 0.25);
207-
bg.fillRoundedRect(-50, -14, 100, 28, 6);
208-
bg.lineStyle(1, hexColor, 0.8);
209-
bg.strokeRoundedRect(-50, -14, 100, 28, 6);
210-
bg.generateTexture("tile_" + skill, 100, 28);
211-
bg.destroy();
218+
// Generate the pill background texture only once per unique skill name.
219+
// Generating again with the same key would be rejected by Phaser's
220+
// TextureManager and break subsequent tile rendering.
221+
if (!scene.textures.exists(textureKey)) {
222+
const bg = scene.add.graphics();
223+
bg.fillStyle(hexColor, 0.25);
224+
bg.fillRoundedRect(-50, -14, 100, 28, 6);
225+
bg.lineStyle(1, hexColor, 0.8);
226+
bg.strokeRoundedRect(-50, -14, 100, 28, 6);
227+
bg.generateTexture(textureKey, 100, 28);
228+
bg.destroy();
229+
}
212230

213-
const tile = scene.physics.add.image(x, -20, "tile_" + skill);
231+
const tile = scene.physics.add.image(x, -20, textureKey);
214232
tile.body.setAllowGravity(false);
215233
tile.body.setVelocityY(scene.cb_tileSpeed);
216234
tile.setData("skill", skill);

src/assets/js/games/dev-duel.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ const DevDuel = (function () {
3434
const W = Math.min(window.innerWidth, 900);
3535
const H = Math.min(window.innerHeight, 520);
3636

37+
// Destroy any previous game instance BEFORE building the new overlay so
38+
// that GameManager.destroy() (which removes "#game-canvas-{id}") doesn't
39+
// rip out the canvas wrapper we are about to create.
40+
GameManager.destroy(GAME_ID);
41+
GameManager.destroyOverlay(GAME_ID);
42+
3743
const overlay = GameManager.createOverlay(GAME_ID);
3844

3945
const titleBar = document.createElement("div");
@@ -43,8 +49,10 @@ const DevDuel = (function () {
4349
titleBar.textContent = "⚔️ DEVELOPER DUEL";
4450
overlay.appendChild(titleBar);
4551

52+
// Canvas container — intentionally has NO id matching "game-canvas-{GAME_ID}"
53+
// to avoid being removed by a subsequent GameManager.destroy() call before
54+
// Phaser has a chance to mount inside it.
4655
const canvasWrap = document.createElement("div");
47-
canvasWrap.id = "game-canvas-" + GAME_ID;
4856
overlay.appendChild(canvasWrap);
4957

5058
const config = {
@@ -60,7 +68,10 @@ const DevDuel = (function () {
6068
},
6169
};
6270

63-
GameManager.create(GAME_ID, config);
71+
// Register the instance directly so destroy() can still clean it up,
72+
// but skip the internal destroy() pre-call (already done above).
73+
var instance = new Phaser.Game(config);
74+
GameManager.instances[GAME_ID] = instance;
6475
}
6576

6677
// ─── Scene ───────────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)