@@ -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 ) ;
0 commit comments