@@ -1360,12 +1360,21 @@ class ReusableMapdataCellUpdate implements MapdataCellUpdate {
13601360 clear_space ( ) : void {
13611361 const cell = this . requireWorkingCell ( ) ;
13621362 const { px, py } = this . absoluteCoords ( ) ;
1363+ const original = this . originalCell ;
1364+ const originalHasData =
1365+ ( original ?. darkness ?? 0 ) !== 0 ||
1366+ ( original ?. labels . length ?? 0 ) > 0 ||
1367+ ( original ?. heads . some ( ( head ) => head . face !== 0 ) ?? false ) ||
1368+ ( original ?. tails . some ( ( tail ) => tail . face !== 0 ) ?? false ) ||
1369+ ( original ?. smooth . some ( ( value ) => value !== 0 ) ?? false ) ;
13631370 notifyWatchedCell ( px , py , "space cleared (transitioning to fog)" ) ;
13641371 if ( mapdata_contains ( px , py ) ) {
13651372 const idxL = ci ( px , py ) * L ;
13661373 layerUpdatedAfterClear . fill ( 0 , idxL , idxL + L ) ;
13671374 }
1368- if ( this . originalState === MapCellState . Empty ) {
1375+ // Some clear_space updates arrive with state=Empty while legacy tail/head
1376+ // data is still populated; only hard-clear when the cell is truly blank.
1377+ if ( this . originalState === MapCellState . Empty && ! originalHasData ) {
13691378 for ( let l = 0 ; l < L ; l ++ ) {
13701379 this . clearHeadLayer ( cell . heads [ l ] ! ) ;
13711380 this . clearTailLayer ( cell . tails [ l ] ! ) ;
@@ -1376,7 +1385,6 @@ class ReusableMapdataCellUpdate implements MapdataCellUpdate {
13761385 // Preserve remembered content when transitioning from Visible→Fog.
13771386 // In particular, keep big-face heads so objects remain visible in fog
13781387 // until a later explicit layer update replaces or clears them.
1379- const original = this . originalCell ;
13801388 if ( original ) {
13811389 for ( let l = 0 ; l < L ; l ++ ) {
13821390 const prevHead = original . heads [ l ] ! ;
@@ -1401,7 +1409,10 @@ class ReusableMapdataCellUpdate implements MapdataCellUpdate {
14011409 ) {
14021410 cell . needUpdate = true ;
14031411 }
1404- cell . state = MapCellState . Fog ;
1412+ cell . state =
1413+ this . originalState === MapCellState . Empty
1414+ ? MapCellState . Empty
1415+ : MapCellState . Fog ;
14051416 this . clearSpaceCalled = true ;
14061417 }
14071418
@@ -1599,6 +1610,25 @@ class ReusableMapdataCellUpdate implements MapdataCellUpdate {
15991610 const idx = ci ( px , py ) ;
16001611 const idxL = idx * L ;
16011612 const original = this . originalCell ;
1613+ if (
1614+ this . clearSpaceCalled &&
1615+ original &&
1616+ this . originalState !== MapCellState . Empty
1617+ ) {
1618+ // A clear_space can arrive without per-layer updates; keep remembered
1619+ // tail coverage on untouched layers so fog transitions don't drop it.
1620+ for ( let l = 0 ; l < L ; l ++ ) {
1621+ if ( this . dirtyLayers [ l ] ) {
1622+ continue ;
1623+ }
1624+ if (
1625+ this . workingCell . tails [ l ] ! . face === 0 &&
1626+ original . tails [ l ] ! . face !== 0
1627+ ) {
1628+ this . workingCell . tails [ l ] = { ...original . tails [ l ] ! } ;
1629+ }
1630+ }
1631+ }
16021632 const originalHasBigFaceHead =
16031633 original ?. heads . some (
16041634 ( head ) => head . face !== 0 && ( head . sizeX > 1 || head . sizeY > 1 ) ,
@@ -1616,6 +1646,34 @@ class ReusableMapdataCellUpdate implements MapdataCellUpdate {
16161646 if ( this . dirtyLayers [ l ] || layerUpdatedAfterClear [ idxL + l ] ) {
16171647 continue ;
16181648 }
1649+ const staleTail = original ?. tails [ l ] ;
1650+ if (
1651+ staleTail &&
1652+ staleTail . face !== 0 &&
1653+ ( staleTail . sizeX > 0 || staleTail . sizeY > 0 )
1654+ ) {
1655+ const staleHeadX = px + staleTail . sizeX ;
1656+ const staleHeadY = py + staleTail . sizeY ;
1657+ let danglingTail = ! mapdata_contains ( staleHeadX , staleHeadY ) ;
1658+ if ( ! danglingTail ) {
1659+ const staleHeadIdxL = ci ( staleHeadX , staleHeadY ) * L + l ;
1660+ const staleHeadFace = headFace [ staleHeadIdxL ] ! ;
1661+ const staleHeadW = headSizeX [ staleHeadIdxL ] ! ;
1662+ const staleHeadH = headSizeY [ staleHeadIdxL ] ! ;
1663+ danglingTail =
1664+ staleHeadFace === 0 ||
1665+ staleHeadFace !== staleTail . face ||
1666+ ( staleHeadW <= 1 && staleHeadH <= 1 ) ;
1667+ }
1668+ if ( danglingTail && mapdata_contains ( staleHeadX , staleHeadY ) ) {
1669+ // Clear stale fog-era big-face coverage that points to a missing
1670+ // or mismatched head so it no longer lingers in fog cells.
1671+ expandClearFaceFromLayer ( staleHeadX , staleHeadY , l ) ;
1672+ }
1673+ if ( danglingTail ) {
1674+ this . clearTailLayer ( this . workingCell . tails [ l ] ! ) ;
1675+ }
1676+ }
16191677 this . clearHeadLayer ( this . workingCell . heads [ l ] ! ) ;
16201678 }
16211679 }
@@ -1745,10 +1803,12 @@ class ReusableMapdataCellUpdate implements MapdataCellUpdate {
17451803 }
17461804 }
17471805
1748- // Clear tails of the previous big face from neighboring cells.
1749- // For Fog cells mapdata_clear_old already did this; for Visible cells
1750- // we must do it here so stale neighbor tails don't linger.
1751- expandClearFaceFromLayer ( px , py , l ) ;
1806+ // Clear tails of the previous big face from neighboring cells only
1807+ // when this update leaves the cell visible. For Visible->Fog
1808+ // transitions, keep remembered tail coverage in fog.
1809+ if ( this . workingCell . state !== MapCellState . Fog ) {
1810+ expandClearFaceFromLayer ( px , py , l ) ;
1811+ }
17521812
17531813 headFace [ lOff ] = head . face ;
17541814 headSizeX [ lOff ] = head . sizeX ;
0 commit comments