C2-63B: J: We need a faster way to load map and sprite csv's. Memory updated Thought for 5s Ariel: Agreed. The fastest path should avoid general CSV parsing for fixed-format map/sprite assets. For Dungeon / GE-Ink, I’d treat these CSVs as byte-grid assets: Load the full CSV file once as bytes/text. Split only once at the first CRLF to separate header and body. Parse header columns directly: column 1: width column 2: height map row column 3: preserve as text map name when applicable For the body: strip CRLF and commas only avoid split(',') per row/cell decode as fixed-width rows using 2 * Width bytes per row for hex/index pairs Convert directly into the map/sprite buffer arrays used by the renderer/editor. Keep this path separate from generic CSV parsing, with a fallback only for non-fixed-width CSVs. This should be especially faster for sprite sheets and maps because it reduces thousands of tiny string allocations and repeated parser decisions into one linear byte scan. I’d name it something like: fastLoadFixedCsvAsset() fastDecodeSpriteCsv() fastDecodeMapCsv() And keep doors/actors/abilities outside this raw fast path unless their CSVs are also fixed-grid byte assets. J: "C:\xampp-8.1.6-0\projects\ge.4.test\ge.4.test Archives\ge.4.test 2026-0705-1314-3451 (AUTOMATIC GE GAMES BACKUP + C2-63A - GE - Dungeon - Fast Load - Sprite + Map CSV - Wip).zip" ge.4.test 2026-0705-1314-3451 (AUTOMATIC GE GAMES BACKUP + C2-63A - GE - Dungeon - Fast Load - Sprite + Map CSV - Wip).zip Zip Archive Ariel: Extract the zip I uploaded, Review, Debug, Moment of Clarity, Update and Repack please! Remember, only repack using files that have actually changed while preserving file archival paths. Ensure your zip file is named without escapes. Remember, just the delta, with next serial in name. Ensure your zip is valid and Remember, no stubs! If needed provide an obsoletion ps1 script. If desired, please request a GE Core Dump. Also, GE Core Dumps automatically include a visible window thumbail and full-page screen capture. Don't forget tabs for indentation, Windows newlines. Also, Please include a README text file for documentation. Go! :D Ariel: I’ll unpack the backup, identify the current map/sprite CSV loading paths, patch only the files needed for a faster fixed-width loader, then package a valid C2-63B delta with a README and unchanged archival paths. I found there is already an older C2-62M fast path, but it still does per-cell string parsing and sprite loads remain sequential. I’m tightening the byte decoder and making sprite CSV fetch/decode concurrent while keeping the safe fallback paths. Thought for 7m 21s Done — C2-63B delta is packaged and verified. Download the C2-63B Dungeon Fast Load delta zip Included changed files only, preserving archival paths: public/alpha/Dungeon/Editor/api.php public/alpha/Dungeon/Editor/js/editor.js public/alpha/Dungeon/2D/GE/conditions.json public/alpha/Dungeon/2D/js/Dungeon.js README C2-63B - Dungeon Fast Load Delta.txt Validation completed: node --check public/alpha/Dungeon/2D/js/Dungeon.js node --check public/alpha/Dungeon/Editor/js/editor.js php -l public/alpha/Dungeon/Editor/api.php conditions.json parse check zipfile.testzip() == None Moment of Clarity: the prior fast path existed, but still did per-cell string parsing and sprite loads were still sequential. This delta adds a byte-grid decoder, exact-width fallback protection, and bounded parallel sprite preloading for both runtime and Editor paths. No obsoletion PS1 was needed because no files were renamed or removed. C2-63C: J: Show me the frame calcutor in the engine and how it differs from in the edtior. Ariel: I’ll inspect the patched engine and Editor JavaScript directly from the delta/backup, then show the exact frame-selection functions and the practical differences. J: Show me the frame calculator in the engine and how it differs from in the editor. Ariel: I reviewed the C2-63B delta. Thought for 2m 23s Yes — here’s the current frame calculator situation from the C2-63B delta. Engine frame calculator Effective engine selector is the last prototype override, C2-62N, near the bottom of: public/alpha/Dungeon/2D/js/Dungeon.js const FRAMES_PER_TICK = 4; const FRAME_PHASES = [ [], [0, 0, 0, 0], [0, 1, 0, 1], [0, 1, 0, 2], [0, 1, 2, 3] ]; function tickMs(game) { return Math.max(1, Math.round(numberOr(game?.motionDuration, game?.moveDelay || 140))); } function phaseForTime(game, now = nowMs()) { const frameMs = Math.max(1, tickMs(game) / FRAMES_PER_TICK); return Math.floor(Math.max(0, numberOr(now, nowMs())) / frameMs) % FRAMES_PER_TICK; } function currentPhase(game, now = game?.__viewPortalRenderNow62B || nowMs()) { if (Number.isFinite(Number(game?.__animationEditorPhase62N))) return Number(game.__animationEditorPhase62N) % FRAMES_PER_TICK; return phaseForTime(game, now); } function authoredFrameList(frames) { return (Array.isArray(frames) ? frames : []) .map(frame => String(frame || "").trim()) .filter(Boolean) .slice(0, 4); } function frameIndexForCount(count, phase) { count = Math.max(0, Math.min(4, Math.floor(numberOr(count, 0)))); if (!count) return -1; phase = Math.max(0, Math.min(FRAMES_PER_TICK - 1, Math.floor(numberOr(phase, 0)))); return FRAME_PHASES[count][phase] ?? 0; } function frameFromList(game, frames, now = game?.__viewPortalRenderNow62B || nowMs()) { const list = authoredFrameList(frames); if (!list.length) return { frame:"", index:-1, phase:0, count:0 }; const phase = currentPhase(game, now); const index = frameIndexForCount(list.length, phase); return { frame:list[index] || list[0] || "", index, phase, count:list.length }; } proto.spriteFrame = function spriteFrameEditorForward62N(frames) { const selected = frameFromList(this, frames, this.__viewPortalRenderNow62B || nowMs()); setCondition(this, "Animation: Frames Per Tick", FRAMES_PER_TICK); setCondition(this, "Animation: Phase", selected.phase); setCondition(this, "Animation: Source Frame Index", selected.index); setCondition(this, "Animation: Engine Editor Forward Selector", true); return selected.frame; }; The engine also special-cases doors: proto.spriteFrameForEntry = function spriteFrameForEntryEditorForward62N(entry, r, c) { if (!entry) return ""; if (this.isDoorEntry?.(entry)) { setCondition(this, "Door: Special Animation Rule Preserved", true); return previousSpriteFrameForEntry ? previousSpriteFrameForEntry.call(this, entry, r, c) : (entry.frames?.[0] || ""); } const selected = frameFromList(this, entry.frames, this.__viewPortalRenderNow62B || nowMs()); return selected.frame; }; So normal tiles/actors use the authored-frame calculator, while doors are intentionally excluded and routed through the door template behavior. Editor frame calculator Editor selector is here: public/alpha/Dungeon/Editor/js/editor.js mapAnimationFrame: 0, mapAnimationMs: 240, function animationFrameForTime(ms) { return Math.floor(ms / state.mapAnimationMs) % 4; } function animationFrameFromList(frames, frameIndex = state.mapAnimationFrame) { const available = (Array.isArray(frames) ? frames : []).map(safeFrameName).filter(Boolean); if (!available.length) return ""; const cycle = [ [0, 0, 0, 0], [0, 1, 0, 1], [0, 1, 2, 1], [0, 1, 2, 3] ][Math.min(available.length, 4) - 1]; return available[cycle[frameIndex % cycle.length]] || ""; } function legendFrame(item, frameIndex = state.mapAnimationFrame) { return animationFrameFromList(item?.frames, frameIndex); } function actorFrame(actor, frameIndex = state.mapAnimationFrame) { return animationFrameFromList(actor?.frames, frameIndex); } The Editor also has its own door special case for map cells: function mapCellFrame(item, row, col) { if (isDoorLegend(item)) return doorFrameForLegend(item, row, col); return legendFrame(item); } Practical differences The big mismatch is 3-frame animation order. Frame count Engine phase order Editor phase order 1 frame 0, 0, 0, 0 0, 0, 0, 0 2 frames 0, 1, 0, 1 0, 1, 0, 1 3 frames 0, 1, 0, 2 0, 1, 2, 1 4 frames 0, 1, 2, 3 0, 1, 2, 3 So the engine and Editor match for 1, 2, and 4 frames, but differ for 3 frames. The second difference is timing: Engine: phase duration = motionDuration / 4 default fallback = moveDelay || 140 default-ish full animation cycle = about one movement tick Editor: phase duration = mapAnimationMs current value = 240 ms full four-phase cycle = 960 ms Moment of Clarity: C2-62N says the engine “mirrors the Editor’s forward authored-frame pass,” but it currently does not perfectly mirror the Editor for 3-frame sprites. The Editor uses 0,1,2,1; the engine uses 0,1,0,2. J: The big mismatch is 3-frame animation order. Frame count Engine phase order Editor phase order 1 frame 0, 0, 0, 0 0, 0, 0, 0 2 frames 0, 1, 0, 1 0, 1, 0, 1 3 frames 0, 1, 0, 2 0, 1, 2, 1 4 frames 0, 1, 2, 3 0, 1, 2, 3 Both should use the editor phase order. Ariel: Memory updated Thought for 5s Got it — I’ll patch the engine to use the Editor’s 3-frame phase order, keep doors excluded from general animation, and produce the next delta only with changed files plus documentation. Done — C2-63C delta is packaged and verified. Download the C2-63C Engine Frame Phase Alignment delta zip Changed files only: public/alpha/Dungeon/2D/js/Dungeon.js README C2-63C - Dungeon Engine Frame Phase Alignment Delta.txt What changed: Engine 3-frame phase order: Old: 0, 1, 0, 2 New: 0, 1, 2, 1 The engine now matches the Editor phase order: 1 frame: 0, 0, 0, 0 2 frames: 0, 1, 0, 1 3 frames: 0, 1, 2, 1 4 frames: 0, 1, 2, 3 Validation completed: node --check public/alpha/Dungeon/2D/js/Dungeon.js Confirmed no remaining engine occurrences of [0, 1, 0, 2] zipfile.testzip() == None No obsoletion PS1 was needed because no files were renamed or removed. C2-63D: J: Cavemen fail to get out of the way, Ariel: I’ll treat this as the next Dungeon bugfix delta against the current C2-63C line: inspect caveman/actor movement, find why displacement/yield behavior is failing, patch changed files only, and repack as the next serial. J: Cavemen fail to get out of the way in Map 002. Ariel: I see both Map 002 Cavemen are authored as blocking,yield,react:0, so the data is asking them to move aside. The failure is likely in the runtime yield planner, not the map CSV itself. J: What is "react"? Ariel: Thought for 4m 6s react is a Dungeon actor flag, not React.js. In actor flags like: blocking,yield,react:0 react:0 means: reaction delay = 0 milliseconds So for Map 002 Cavemen: blocking,yield,react:0 means: blocking = they occupy/block their tile yield = they are allowed/supposed to move aside react:0 = they should react immediately, with no delay The engine parses it here conceptually: react:250 // wait about 250 ms before reacting react:1000 // wait about 1 second before reacting react:0 // react immediately So react is only the delay before an armed actor response can fire. It does not decide whether the Caveman is a mob, enemy, follower, or yielder. That comes from other flags/templates, especially yield. Moment of Clarity: since the Map 002 Cavemen already have yield,react:0, the failure is not that they are waiting too long. They are either not being armed as yield actors, not finding a valid yield route, or the player/caveman occupancy reservation logic is blocking the move-aside path. J: The engine bogs down when the player is adjacent to a Caveman. Ariel: I’ll profile this as a localized adjacency/yield-loop bug: inspect the latest patched runtime, find what repeats when a player stands next to a Caveman, then produce the next delta with only changed files and a README. I found the likely regression: the previous Caveman-yield fix cleared temporary Inert during the normal adjacency refresh/check path, which let adjacent Cavemen re-enter expensive auto-walk/condition processing while the player was simply standing beside them. I’m changing that so Inert is held during idle adjacency and bypassed only for the actual player-unit yield move. Thought for 3m 28s Done — C2-63E delta is packaged and verified. Download the C2-63E Caveman Adjacent Yield Performance delta zip Changed files only: README C2-63E - Dungeon Caveman Adjacent Yield Performance Delta.txt public/alpha/Dungeon/2D/js/Dungeon.js Moment of Clarity: C2-63D made Cavemen able to yield again, but it cleared temporary Inert during normal adjacency/planning checks. That let adjacent Cavemen re-enter expensive auto-walk/condition processing while the player was simply standing next to them. C2-63E changes that behavior: Passive Caveman/yield actor beside player: Keep temporary Inert held during idle adjacency. Player tries to move into/through that Caveman: Treat Caveman as a valid yield blocker for planning. Actual Player Unit Yield route starts: Clear Inert only then, so the Caveman can move. Validation completed: node --check public/alpha/Dungeon/2D/js/Dungeon.js zipfile.testzip() == None Windows CRLF confirmed Tabs preserved in patched block No obsoletion PS1 was needed because no files were renamed or removed. C2-63F: J: Update all skull passages so they use skull01.csv / skull02.csv as frames and Mask: Radius 0.6 Ariel: The authored skull passage rows are all in actor.csv assets, not engine code. I found 224 Skull Passage actor rows across 7 maps and am packaging those CSV-only data changes as C2-63F. Thought for 1m 46s Done — C2-63F delta is packaged and verified. Download the C2-63F Skull Passage Animation Mask Radius delta zip Changed files only: README C2-63F - Dungeon Skull Passage Animation Mask Radius Delta.txt public/alpha/Dungeon/assets/maps/002/actor.csv public/alpha/Dungeon/assets/maps/006/actor.csv public/alpha/Dungeon/assets/maps/007/actor.csv public/alpha/Dungeon/assets/maps/016/actor.csv public/alpha/Dungeon/assets/maps/019/actor.csv public/alpha/Dungeon/assets/maps/020/actor.csv public/alpha/Dungeon/assets/maps/025/actor.csv Updated 224 Skull Passage actor rows: Frame 1: skull01.csv Frame 2: skull02.csv Frame 3: blank Frame 4: blank Mask: Radius: 0.6 Validation completed: All Skull Passage actor rows verified All changed actor.csv files parse as CSV Declared row counts match actual row counts CRLF line endings confirmed zipfile.testzip() == None No obsoletion PS1 was needed because no files were renamed or removed. C2-63G: J: Don't forget to have them only use contiguous Mask groups. (Including combined contiguous Skull/Alcove Passages) Ariel: Memory updated Thought for 51s Got it — and I verified the C2-63F actor data against that rule. The current Skull/Alcove Passage mask groups are already contiguous by combined passage cells: Map 002: 9 passage actors, 5 contiguous Mask groups Map 006: 15 passage actors, 15 contiguous Mask groups Map 007: 40 passage actors, 40 contiguous Mask groups Map 016: 40 passage actors, 40 contiguous Mask groups Map 019: 40 passage actors, 40 contiguous Mask groups Map 020: 40 passage actors, 40 contiguous Mask groups Map 025: 44 passage actors, 39 contiguous Mask groups Map 025 includes the important combined case: Skull Passage + Alcove Passage cells share Mask group Skull217 only because the combined set is contiguous. I also saved the rule: Skull Passage and Alcove Passage may share a Mask group only when their combined cells form one contiguous island; a Mask group must never span disconnected passage islands. J: Go! :D Ariel: Mask: Radius: 0.6 is the Mask size/radius, not the field name for “soft edge.” But the confusing part is: the engine currently also has a separate soft-edge constant set to 0.6. const MASK_RADIUS_EDGE_BLEND = 0.6; And actor data radius is used here as the actual circle size: r: Math.max(0, Number(radius || 0)) * size Then the soft edge is calculated separately: const blend = Math.min(circle.r, MASK_RADIUS_EDGE_BLEND * size); const inner = Math.max(0, circle.r - blend); So for a Skull Passage actor with: Mask: Radius: 0.6 that means: circle radius = 0.6 tiles soft edge max = 0.6 tiles Because those are equal, the whole 0.6-tile circle becomes feathered, with no hard center. Moment of Clarity: 0.6 is our usual small passage Mask size in actor data. The soft edge is a separate engine constant that happens to also be 0.6, which makes this easy to mix up. J: What previous Radius did we use in the past? Ariel: Thought for 20s The previous authored passage radius was 1.5, not 0.6. From the C2-63A backup before the Skull Passage change: Skull Passage rows: 224 rows used Mask: Radius: 1.5 Alcove Passage rows: 4 rows used Mask: Radius: 1.5 Example original Skull row: 9,12,36,Skull Passage,skull01.csv,,,,,skull,actor,react:0,3,1,,Skull001Cell000,,,,,,,,1.5,Skull001 So 0.6 would be a new smaller radius, not the previous value. If the intent is “use our previous/usual passage mask size,” then the Skull/Alcove Passage radius should stay 1.5. J: Use Radius 1.5 please. Ariel: Memory updated Understood — I’ll rebuild the corrected delta with actual actor.csv files included, keeping Skull Passage frames as skull01.csv/skull02.csv, contiguous Skull/Alcove Mask groups, and Mask: Radius: 1.5. Thought for 1m 1s Done — corrected C2-63H is packaged with the actual delta files included. Download the C2-63H Skull/Alcove Passage Radius delta zip Changed files included: README C2-63H - Dungeon Skull Alcove Passage Radius Delta.txt public/alpha/Dungeon/assets/maps/002/actor.csv public/alpha/Dungeon/assets/maps/006/actor.csv public/alpha/Dungeon/assets/maps/007/actor.csv public/alpha/Dungeon/assets/maps/016/actor.csv public/alpha/Dungeon/assets/maps/019/actor.csv public/alpha/Dungeon/assets/maps/020/actor.csv public/alpha/Dungeon/assets/maps/025/actor.csv Applied: Skull Passage frames: Frame 1: skull01.csv Frame 2: skull02.csv Frame 3: blank Frame 4: blank Skull/Alcove Passage Mask Radius: 1.5 Verified contiguous Skull/Alcove Mask groups: Map 002: 9 Skull, 0 Alcove, 5 groups, 0 disconnected Map 006: 15 Skull, 0 Alcove, 15 groups, 0 disconnected Map 007: 40 Skull, 0 Alcove, 40 groups, 0 disconnected Map 016: 40 Skull, 0 Alcove, 40 groups, 0 disconnected Map 019: 40 Skull, 0 Alcove, 40 groups, 0 disconnected Map 020: 40 Skull, 0 Alcove, 40 groups, 0 disconnected Map 025: 40 Skull, 4 Alcove, 39 groups, 0 disconnected Validation completed: All included actor.csv files parse as CSV Declared row counts match actual row counts All Skull Passage rows use skull01.csv / skull02.csv All Skull/Alcove Passage rows in this delta use Mask: Radius 1.5 Windows CRLF confirmed zipfile.testzip() == None No obsoletion PS1 was needed because no files were renamed or removed. C2-63I: J: The solitary Skull passage by the chest in map 002 fails to reveal via a Radius Airel: I found the problem: the runtime only activates a Mask group when the player is on the exact Mask actor tile. A solitary Skull Passage with Radius: 1.5 needs to become a source when the player is within that radius, such as standing beside it near the chest. Thought for 2m 28s Done — C2-63I delta is packaged and verified. Download the C2-63I Mask Radius Activation delta zip Changed files only: README C2-63I - Dungeon Mask Radius Activation Delta.txt public/alpha/Dungeon/2D/js/Dungeon.js Moment of Clarity: the renderer already used Mask: Radius once a Mask group was active, but Mask activation only happened when the player stood on the exact Mask actor tile. That meant the solitary Skull Passage by the chest in Map 002 could not reveal from nearby tiles. C2-63I changes Mask source activation: No-radius Mask: exact same-tile overlap still activates it Positive Radius Mask: activates when a Player Unit member is within that radius Verified Map 002: Skull001: row 12, col 36 Mask Radius: 1.5 Adjacent tile by chest approach: row 12, col 37 distance: 1.0 result: activates within Radius 1.5 Validation completed: node --check public/alpha/Dungeon/2D/js/Dungeon.js Windows CRLF confirmed zipfile.testzip() == None No obsoletion PS1 was needed because no files were renamed or removed. C2-63J: J: The Passage at 36,12 in Map 002 fails to reveal. Ariel: Thanks — I’ll use map coordinates as x=36, y=12, which matches column 36, row 12 in Map 002. J: Should only reveal by Radius when the player is actively occupying a Masked actor./ Ariel: Understood — C2-63I was too broad. Radius should not activate merely because the player is nearby; it should activate only while the player is actively occupying a Masked actor, then that actor’s Radius reveals its contiguous Mask group. J: Remember, contiguous Masks groups should have a shared identifier. Memory updated Ariel: I’m correcting the C2-63I behavior rather than widening it: only an exact Player Unit overlap with a Masked actor becomes an active source; that source may then use its Radius: 1.5 to reveal the shared contiguous Mask identifier. Thought for 7m 0s Done — C2-63J delta is packaged and verified. Download the C2-63J Exact Mask Radius Source delta zip Changed files only: README C2-63J - Dungeon Exact Mask Radius Source Delta.txt public/alpha/Dungeon/2D/js/Dungeon.js Corrected behavior: Nearby player proximity: Does not activate Radius reveal. Player Unit actively occupying a Masked actor: Activates that actor as the Mask source. Positive Radius on that occupied Masked actor: Reveals actors sharing the same contiguous Mask identifier. Map 002 check: Skull Passage at x=36, y=12: Mask: Skull001 Mask Radius: 1.5 Mask Mode: transparency Frames: skull01.csv / skull02.csv Reveal starts only when the Player Unit occupies x=36, y=12. Contiguous Mask rule verified: Map 002: 9 Skull, 0 Alcove, 5 groups, 0 disconnected Map 006: 15 Skull, 0 Alcove, 15 groups, 0 disconnected Map 007: 40 Skull, 0 Alcove, 40 groups, 0 disconnected Map 016: 40 Skull, 0 Alcove, 40 groups, 0 disconnected Map 019: 40 Skull, 0 Alcove, 40 groups, 0 disconnected Map 020: 40 Skull, 0 Alcove, 40 groups, 0 disconnected Map 025: 40 Skull, 4 Alcove, 39 groups, 0 disconnected Validation completed: node --check public/alpha/Dungeon/2D/js/Dungeon.js Windows CRLF confirmed Tabs preserved zipfile.testzip() == None No obsoletion PS1 was needed because no files were renamed or removed. C2-63K: J: The Mask at 36x12 in Map 002 still fails to reveal. Ariel: I’ll debug the exact x=36, y=12 case against the current patched files, focusing on whether the actor is treated as a Mask source, whether its group identifier is read correctly, and whether the reveal pass skips single-cell groups. J: "C:\Users\trkea\Downloads\GE JSON Core Dump alpha Dungeon-2D 2026-0705-1447-5586.zip" GE JSON Core Dump alpha Dungeon-2D 2026-0705-1447-5586.zip Zip Archive Ariel: The likely failure mode is now clearer: C2-63J requires exact player overlap, but the Skull Passage actor at x=36, y=12 is authored as a blocking actor. If the player cannot enter that tile, the exact-overlap source never becomes active. I’m checking whether this should be fixed in data, engine source detection, or both. J: "C:\Users\trkea\OneDrive\nenserver 0-050j2\Pictures\Screenshots\Screenshot 2026-07-05 145002.png" This is literally the location of the player. Ariel: Analyzing image That screenshot changes the diagnosis: the player is already on the source tile, so this is not a “nearby radius” problem. The source is either not being detected from the occupied Mask actor, or the transparency cut-out is being hidden by the controlled player/render ordering cache. Thought for 5m 43s You’re right — the player is visibly occupying x=36, y=12, so C2-63J’s source detection was still too brittle. Done — C2-63K delta is packaged and verified. Download the C2-63K Mask Visual Exact Source delta zip Changed files only: README C2-63K - Dungeon Mask Visual Exact Source Delta.txt public/alpha/Dungeon/2D/js/Dungeon.js Moment of Clarity: C2-63J correctly removed pure proximity activation, but it still relied too much on the spatial index plus one logical Player Unit tile. C2-63K now detects exact Mask source occupancy using: Player logical tile Player display tile Controlled actor occupied cells Player Unit member occupied cells Full direct scan of Masked actors Still preserved: Nearby proximity alone does not activate Radius. Radius activates only from exact Player Unit occupancy of a Masked actor. The active source reveals the shared contiguous Mask identifier. Map 002 Skull001 remains Radius 1.5. Validation completed: node --check public/alpha/Dungeon/2D/js/Dungeon.js Windows CRLF confirmed Tabs preserved zipfile.testzip() == None No obsoletion PS1 was needed because no files were renamed or removed. not a “nearby radius” problem. The source is either not being detected from the occupied Mask actor, or the transparency cut-out is being hidden by the controlled player/render ordering cache. Start writing Thought for a second not a “nearby radius” problem. The source is either not being detected from the occupied Mask actor, or the transparency cut-out is being hidden by the controlled player/render ordering cache. C2-63L: J: Still fails to reveal. Ariel: I’ll use the GE Core Dump plus the latest patched files to trace the live Mask state instead of guessing from the authored CSV alone. The next delta needs to fix the actual runtime reveal failure at x=36, y=12. J: Is this a View Portal renderer issue, not an engine issue?> Ariel: Thought for 2m 6s Yes — based on the GE Core Dump, this is now a View Portal renderer issue, not a Mask activation / engine-logic issue. The dump shows the engine already knows the player is on the Masked actor: Mask: Active = true Mask: Active Count = 1 Mask: Active Regions = Skull001 Mask: Actor Overlap = true Mask: Actor Overlap Count = 1 Mask: Actor Overlap Index = 9 Mask: Actor Overlap Name = Skull Passage Mask: Actor Overlap Region = Skull001 Mask: Exact Actor Overlap Count = 1 And the screenshot shows: X:36 Y:12 So the engine/source side is doing its job: Player is occupying Masked actor tile Masked actor is detected Mask region Skull001 is active Radius proximity activation is off Exact actor occupancy is active That means the failure is downstream: the View Portal actor renderer is not visually applying the active Mask cut-out to the Skull Passage actor. Most likely target: renderDungeonActorLayer(...) drawMaybeMaskedActor(...) applyCircularMaskToTile(...) actor-layer cache/stateKey invalidation controlled/player actor redraw ordering Moment of Clarity: stop patching Mask activation and actor.csv for this case. Skull001 is active. The remaining bug is that the View Portal actor layer still draws the Skull Passage sprite instead of cutting it out/revealing it. J: Go! :D Ariel: Thought for 3m 49s Done — C2-63L delta is packaged and verified. Download the C2-63L View Portal Mask Renderer delta zip Changed files only: README C2-63L - Dungeon View Portal Mask Renderer Delta.txt public/alpha/Dungeon/2D/js/Dungeon.js Moment of Clarity: the Mask engine state was already correct. The failure was that the later C2-62L View Portal actor renderer bypassed the earlier masked actor renderer and drew actor sprites directly. C2-63L restores Mask compositing inside the final View Portal actor renderer while preserving C2-62L interpolation behavior. Verified: node --check public/alpha/Dungeon/2D/js/Dungeon.js Windows CRLF confirmed Tabs preserved zipfile.testzip() == None No obsoletion PS1 was needed. C2-63M: J: Now Caveman at 37,5 is glitching, missing most frames and using odd Yield avoidance. Ariel: Thought for 2m 2s Done — C2-63M delta is packaged and verified. Download the C2-63M Caveman View Portal Visibility delta zip Changed files only: README C2-63M - Dungeon Caveman View Portal Visibility Delta.txt public/alpha/Dungeon/2D/js/Dungeon.js Moment of Clarity: C2-63L accidentally treated actorPortalInterpolationAllowed62C() as a visibility test. That function returns false for stationary actors by design, meaning “don’t interpolate this actor,” not “don’t draw this actor.” That caused passive yield actors like the Map 002 Caveman at x=37, y=5 to disappear while standing still, then pop/glitch when yield movement restarted. C2-63M fixes that by: Keeping the C2-63L masked View Portal renderer Not using interpolation-allowed as actor visibility Letting all actors enter normal interpolated tile calculation Using the View Portal tile bounds cull as the actual visibility filter Bumping the actor-layer cache key to C2-63M Validation completed: node --check public/alpha/Dungeon/2D/js/Dungeon.js Windows CRLF confirmed zipfile.testzip() == None No obsoletion PS1 was needed. /* C2-63N: J: Also, Masks are revealed regardless of occupancy by the player. Memory updated Thought for 1m 9s Done — C2-63N delta is packaged and verified. Download the C2-63N Strict Logical Mask Source delta zip Changed files only: README C2-63N - Dungeon Strict Logical Mask Source Delta.txt public/alpha/Dungeon/2D/js/Dungeon.js Moment of Clarity: C2-63K made source detection too broad by allowing visual/display cells and direct scans to contribute. That helped diagnose the View Portal renderer issue, but it could make Masks appear revealed even when the Player Unit was not actively occupying the Masked actor’s logical tile. C2-63N restores the intended rule: Nearby Radius proximity: Does not activate a Mask. View Portal display/interpolation position: Does not activate a Mask. Player/member Mask property by itself: Does not activate a reveal source. Player Unit logical tile exactly overlaps a non-player Masked actor: Activates that actor as the Mask source. Active exact source with Radius: Radius reveals only actors sharing that same contiguous Mask identifier. Validation completed: node --check public/alpha/Dungeon/2D/js/Dungeon.js Windows CRLF confirmed zipfile.testzip() == None No obsoletion PS1 was needed. */ J: Nm, your C2-63M delta already fixed it. Ariel:Memory updated Thought for a few seconds Got it — C2-63M is the good fix here. Use: C2-63M - GE - Dungeon - Caveman View Portal Visibility - Delta And ignore/skip C2-63N unless we later find a separate reproducible Mask-source bug. C2-63O: J: Mosaic Transition is too quick.