§
    ÊV¿jÌ4  ã                   ó&  — d Z ddlZddlmc mZ ddlZddlZddl	Z	ddl
mZ ddlZ ee¦  «                             ¦   «         j        j        j        Zedz  dz  dz  dz  dz  Zedz  dz  dz  dz  d	z  Zedz  dz  dz  dz  d
z  Zedz  dz  dz  Zedz  dz  dz  dz  dz  Zedz  dz  dz  dz  dz  Zedz  dz  dz  dz  dz  Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        ZdS )z¯
Unit Test Suite for Fog of War Multi-State Engine & Minimap HUD (Milestone M4).
Genuine Node.js runtime tests: 3-state lifecycle, persistence, 30Hz throttling, anti-maphack.
é    N)ÚPathÚclientÚwebappÚjsÚuiz
war_fog.jszwar_fog_renderer.jszminimap_hud.jsz
index.htmlÚenginezworld_renderer.jszentity_renderer.jszmonster_system.jsc                   ó   — e Zd Zd„ Zd„ ZdS )ÚTestCodeHygieneAndExportsc                 ó†  — t           j        }d} ||¬¦  «        }|j        } |¦   «         }t          |¦  «        }d}||k    }|�s[t	          j        d|fd||f¦  «        dt          j        ¦   «         v st	          j        t          ¦  «        rt	          j	        t          ¦  «        nddt          j        ¦   «         v st	          j        t           ¦  «        rt	          j	        t           ¦  «        ndt	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        dœ	z  }	d	d
|	iz  }
t          t	          j        |
¦  «        ¦  «        ‚dx}x}x}x}x}x}x}}t          j        }d} ||¬¦  «        }|j        } |¦   «         }t          |¦  «        }d}||k    }|�s[t	          j        d|fd||f¦  «        dt          j        ¦   «         v st	          j        t          ¦  «        rt	          j	        t          ¦  «        nddt          j        ¦   «         v st	          j        t          ¦  «        rt	          j	        t          ¦  «        ndt	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        dœ	z  }	d	d
|	iz  }
t          t	          j        |
¦  «        ¦  «        ‚dx}x}x}x}x}x}x}}t          j        }d} ||¬¦  «        }|j        } |¦   «         }t          |¦  «        }d}||k    }|�s[t	          j        d|fd||f¦  «        dt          j        ¦   «         v st	          j        t          ¦  «        rt	          j	        t          ¦  «        nddt          j        ¦   «         v st	          j        t          ¦  «        rt	          j	        t          ¦  «        ndt	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        dœ	z  }	d	d
|	iz  }
t          t	          j        |
¦  «        ¦  «        ‚dx}x}x}x}x}x}x}}t          j        }d} ||¬¦  «        }|j        } |¦   «         }t          |¦  «        }d}||k    }|�s[t	          j        d|fd||f¦  «        dt          j        ¦   «         v st	          j        t          ¦  «        rt	          j	        t          ¦  «        nddt          j        ¦   «         v st	          j        t          ¦  «        rt	          j	        t          ¦  «        ndt	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        t	          j	        |¦  «        dœ	z  }	d	d
|	iz  }
t          t	          j        |
¦  «        ¦  «        ‚dx}x}x}x}x}x}x}}dS )z*Verifies strict line budgets for M4 files.úutf-8©Úencodingi,  )ú<=)z²%(py13)s
{%(py13)s = %(py0)s(%(py11)s
{%(py11)s = %(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.read_text
}(encoding=%(py5)s)
}.splitlines
}()
})
} <= %(py16)sÚlenÚ
WAR_FOG_JS)	Úpy0Úpy1Úpy3Úpy5Úpy7Úpy9Úpy11Úpy13Úpy16zassert %(py18)sÚpy18Néú   ÚWAR_FOG_RENDERER_JSi  ÚMINIMAP_HUD_JSÚ
INDEX_HTML)r   Ú	read_textÚ
splitlinesr   Ú
@pytest_arÚ_call_reprcompareÚ@py_builtinsÚlocalsÚ_should_repr_global_nameÚ	_safereprÚAssertionErrorÚ_format_explanationr   r   r   )ÚselfÚ@py_assert2Ú@py_assert4Ú@py_assert6Ú@py_assert8Ú@py_assert10Ú@py_assert12Ú@py_assert15Ú@py_assert14Ú@py_format17Ú@py_format19s              ú8C:\Projects\FreeExile\tests\unit\test_fog_and_minimap.pyÚtest_line_count_capsz.TestCodeHygieneAndExports.test_line_count_caps   s  € åÔ'ÐN°ÐNÐ'Ð'°Ð9Ñ9Ô9ÐNÐ9ÔDÐNÐDÐDÑFÔFÐN�sÐFÑGÔGÐNÈ3ÐNÐGÈ3ÒNÐNÐNÑNÕNÔNÐNÐNÐNÐNÐGÈ3ÐNÑNÔNÐNÕNÔNÑNÔNÐNÐNÕNÔN�sÑNÔNÐNÕNÔN�sÑNÔNÐNÐNÐNÕNÔNÑNÔNÐNÐNÕNÔN•:ÑNÔNÐNÕNÔN•:ÑNÔNÐNÐNÕNÔNÐ'ÑNÔNÕNÔN°ÑNÔNÕNÔNÐ9ÑNÔNÕNÔNÐDÑNÔNÕNÔNÐFÑNÔNÕNÔNÐGÑNÔNÕNÔNÈ3ÑNÔNÐNÐNÑNÐNÐNÐNÐNÐNÑNÐNÕNÕNÔNÐNÑNÔNÑNÔNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÝ&Ô0ÐW¸'ÐWÐ0Ð0¸'ÐBÑBÔBÐWÐBÔMÐWÐMÐMÑOÔOÐW�sÐOÑPÔPÐWÐTWÐWÐPÐTWÒWÐWÐWÑWÕWÔWÐWÐWÐWÐWÐPÐTWÐWÑWÔWÐWÕWÔWÑWÔWÐWÐWÕWÔW�sÑWÔWÐWÕWÔW�sÑWÔWÐWÐWÐWÕWÔWÑWÔWÐWÐWÕWÔWÕ&ÑWÔWÐWÕWÔWÕ&ÑWÔWÐWÐWÕWÔWÐ0ÑWÔWÕWÔW¸'ÑWÔWÕWÔWÐBÑWÔWÕWÔWÐMÑWÔWÕWÔWÐOÑWÔWÕWÔWÐPÑWÔWÕWÔWÐTWÑWÔWÐWÐWÑWÐWÐWÐWÐWÐWÑWÐWÕWÕWÔWÐWÑWÔWÑWÔWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÝ!Ô+ÐR°WÐRÐ+Ð+°WÐ=Ñ=Ô=ÐRÐ=ÔHÐRÐHÐHÑJÔJÐR�sÐJÑKÔKÐRÈsÐRÐKÈsÒRÐRÐRÑRÕRÔRÐRÐRÐRÐRÐKÈsÐRÑRÔRÐRÕRÔRÑRÔRÐRÐRÕRÔR�sÑRÔRÐRÕRÔR�sÑRÔRÐRÐRÐRÕRÔRÑRÔRÐRÐRÕRÔR•>ÑRÔRÐRÕRÔR•>ÑRÔRÐRÐRÕRÔRÐ+ÑRÔRÕRÔR°WÑRÔRÕRÔRÐ=ÑRÔRÕRÔRÐHÑRÔRÕRÔRÐJÑRÔRÕRÔRÐKÑRÔRÕRÔRÈsÑRÔRÐRÐRÑRÐRÐRÐRÐRÐRÑRÐRÕRÕRÔRÐRÑRÔRÑRÔRÐRÐRÐRÐRÐRÐRÐRÐRÐRÐRÐRÐRÐRÐRÐRÐRÐRÝÔ'ÐN°ÐNÐ'Ð'°Ð9Ñ9Ô9ÐNÐ9ÔDÐNÐDÐDÑFÔFÐN�sÐFÑGÔGÐNÈ3ÐNÐGÈ3ÒNÐNÐNÑNÕNÔNÐNÐNÐNÐNÐGÈ3ÐNÑNÔNÐNÕNÔNÑNÔNÐNÐNÕNÔN�sÑNÔNÐNÕNÔN�sÑNÔNÐNÐNÐNÕNÔNÑNÔNÐNÐNÕNÔN•:ÑNÔNÐNÕNÔN•:ÑNÔNÐNÐNÕNÔNÐ'ÑNÔNÕNÔN°ÑNÔNÕNÔNÐ9ÑNÔNÕNÔNÐDÑNÔNÕNÔNÐFÑNÔNÕNÔNÐGÑNÔNÕNÔNÈ3ÑNÔNÐNÐNÑNÐNÐNÐNÐNÐNÑNÐNÕNÕNÔNÐNÑNÔNÑNÔNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNÐNó    c                 ó„  — t                                d¬¦  «        }dD ]å}||v }|sÛt          j        d|fd||f¦  «        dt	          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddt	          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }t          j        d	|› d
�¦  «        dz   d|iz  }t          t          j
        |¦  «        ¦  «        ‚d}Œæd}||v }|s¬t          j        d|fd||f¦  «        t          j        |¦  «        dt	          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }t          j        d¦  «        dz   d|iz  }	t          t          j
        |	¦  «        ¦  «        ‚dx}}t                               d¬¦  «        }
d}||
v }|s—t          j        d|fd||
f¦  «        t          j        |¦  «        dt	          j        ¦   «         v st          j        |
¦  «        rt          j        |
¦  «        nddœz  }dd|iz  }	t          t          j
        |	¦  «        ¦  «        ‚dx}}d}||
v }|s—t          j        d|fd||
f¦  «        t          j        |¦  «        dt	          j        ¦   «         v st          j        |
¦  «        rt          j        |
¦  «        nddœz  }dd|iz  }	t          t          j
        |	¦  «        ¦  «        ‚dx}}d}||
v }|s—t          j        d|fd||
f¦  «        t          j        |¦  «        dt	          j        ¦   «         v st          j        |
¦  «        rt          j        |
¦  «        nddœz  }dd|iz  }	t          t          j
        |	¦  «        ¦  «        ‚dx}}dS )z1Verifies required symbols and DOM bindings exist.r   r   )
Ú	FOG_STATEÚinitFogÚgetFogStateÚsetFogStateÚupdatePlayerVisionÚpackFogBitsÚunpackFogBitsÚsaveFogÚloadFogÚgetStorageKey©Úin)z%(py0)s in %(py2)sÚsymÚwf)r   Úpy2zMissing symbol z in war_fog.jsz
>assert %(py4)sÚpy4Nu   Bá»Š CHáº¶N Bá»žI RÃ€O Cáº¢N©z%(py1)s in %(py3)s©r   r   z5Notice string must exist for modal test compatibilityz
>assert %(py5)sr   zid="minimap-container"Úhtmlúassert %(py5)szid="minimap-canvas"zsrc="js/ui/minimap_hud.js")r   r    r"   r#   r$   r%   r&   r'   Ú_format_assertmsgr(   r)   r   )r*   rF   rE   Ú@py_assert1Ú@py_format3Ú@py_format5Ú@py_assert0r+   Ú@py_format4Ú@py_format6rK   s              r5   Ú$test_es_exports_and_elements_presentz>TestCodeHygieneAndExports.test_es_exports_and_elements_present   sp  € å×!Ò!¨7Ð!Ñ3Ô3ˆð[ð 	Dð 	DˆCà˜"�9ÐCÐCÐCÕCÔCÐCÐCÐCÐC�3˜"ÐCÑCÔCÐCÕCÔCÑCÔCÐCÐCÕCÔC�3ÑCÔCÐCÕCÔC�3ÑCÔCÐCÐCÐCÕCÔCÑCÔCÐCÐCÕCÔC˜"ÑCÔCÐCÕCÔC˜"ÑCÔCÐCÐCÐCÐCÑCÐCÕCÔCÐC°ÐCÐCÐCÑCÔCÐCÑCÐCÐCÐCÑCÐCÕCÕCÔCÐCÑCÔCÑCÔCÐCÐCÐCÐCØ-ÐlÐ-°Ð3ÐlÐlÐlÕlÔlÐlÐlÐlÐlÐ-°ÐlÑlÔlÕlÔlÐ-ÑlÔlÐlÕlÔlÑlÔlÐlÐlÕlÔl°ÑlÔlÐlÕlÔl°ÑlÔlÐlÐlÐlÐlÑlÐlÕlÔlÐ5lÑlÔlÐlÑlÐlÐlÐlÑlÐlÕlÕlÔlÐlÑlÔlÑlÔlÐlÐlÐlÐlÐlå×#Ò#¨WÐ#Ñ5Ô5ˆØ'Ð/Ð'¨4Ð/Ð/Ð/Ð/Õ/Ô/Ð/Ð/Ð/Ð/Ð'¨4Ð/Ñ/Ô/Õ/Ô/Ð'Ñ/Ô/Ð/Õ/Ô/Ñ/Ô/Ð/Ð/Õ/Ô/¨4Ñ/Ô/Ð/Õ/Ô/¨4Ñ/Ô/Ð/Ð/Ð/Ð/Ñ/Ð/Ð/Ð/Ð/Ð/Ñ/Ð/Õ/Õ/Ô/Ð/Ñ/Ô/Ñ/Ô/Ð/Ð/Ð/Ð/Ð/Ø$Ð,Ð$¨Ð,Ð,Ð,Ð,Õ,Ô,Ð,Ð,Ð,Ð,Ð$¨Ð,Ñ,Ô,Õ,Ô,Ð$Ñ,Ô,Ð,Õ,Ô,Ñ,Ô,Ð,Ð,Õ,Ô,¨Ñ,Ô,Ð,Õ,Ô,¨Ñ,Ô,Ð,Ð,Ð,Ð,Ñ,Ð,Ð,Ð,Ð,Ð,Ñ,Ð,Õ,Õ,Ô,Ð,Ñ,Ô,Ñ,Ô,Ð,Ð,Ð,Ð,Ð,Ø+Ð3Ð+¨tÐ3Ð3Ð3Ð3Õ3Ô3Ð3Ð3Ð3Ð3Ð+¨tÐ3Ñ3Ô3Õ3Ô3Ð+Ñ3Ô3Ð3Õ3Ô3Ñ3Ô3Ð3Ð3Õ3Ô3¨tÑ3Ô3Ð3Õ3Ô3¨tÑ3Ô3Ð3Ð3Ð3Ð3Ñ3Ð3Ð3Ð3Ð3Ð3Ñ3Ð3Õ3Õ3Ô3Ð3Ñ3Ô3Ñ3Ô3Ð3Ð3Ð3Ð3Ð3Ð3Ð3r7   N)Ú__name__Ú
__module__Ú__qualname__r6   rT   © r7   r5   r
   r
      s5   € € € € € ðOð Oð Oð4ð 4ð 4ð 4ð 4r7   r
   c                   ó6   — e Zd Zed„ ¦   «         Zd„ Zd„ Zd„ ZdS )ÚTestWarFogNodeRuntimec                 ó¢   — d}t          j        dd|gddt          t          ¦  «        d¬¦  «        }t	          j        |j        ¦  «        | _        d S )Na²	  
        const fs = require('fs');
        let code = fs.readFileSync('client/webapp/js/ui/war_fog.js', 'utf8')
          .replace(/import\s+[^;]+;\s*/g, '').replace(/export\s+(const|let)\s+/g, 'var ')
          .replace(/export\s+function\s+/g, 'function ').replace(/export\s+\{[^}]+\};?\s*/g, '')
          .replace(/export\s+default\s+[^;]+;?\s*/g, '');

        const mockStorage = {};
        global.localStorage = {
          getItem: (k) => mockStorage[k] || null,
          setItem: (k, v) => { mockStorage[k] = v; },
          removeItem: (k) => { delete mockStorage[k]; }
        };
        global.document = { getElementById: () => null, addEventListener: () => {} };
        global.window = {};
        const GRID_SIZE = 32, TILE_PX = 15, BIOMES = [{ id: 'TEST', name: 'Test' }];
        function generateMazeData() { return { biomeIdx: 0, mapTiles: [], fogMatrix: [], barricades: [], bossGate: null, bossRoom: null, playerPos: {x:0, y:0}, bossPos: {x:10, y:10}, simulatedPath: [] }; }
        function renderProceduralMap() {}

        eval(code);

        const results = {};
        initFog(60, 45, 'zone_tang_kiem_nhai', 101);
        results.init = { w: mapW, h: mapH, at00: getFogState(0, 0), atNeg: getFogState(-1, 0), atOob: getFogState(60, 45) };

        updatePlayerVision(10, 10, 8);
        const r10_10 = getFogState(10, 10), rEdge = getFogState(10 + 8, 10), rOut = getFogState(10 + 9, 10);
        updatePlayerVision(25, 25, 8);
        results.vision = { cBefore: r10_10, edgeBefore: rEdge, outBefore: rOut, cDecayed: getFogState(10, 10), cNew: getFogState(25, 25) };

        initFog(120, 90, 'zone_boundless_celestial_palace', 777);
        for (let i = 0; i < 120 * 90; i++) {
          if ((i * 37 + 13) % 7 === 0) fogGrid[i] = (i % 2 === 0) ? 1 : 2;
        }
        const storageKey = getStorageKey('zone_boundless_celestial_palace', 777);
        const packed = packFogBits(fogGrid, 120, 90);
        saveFog('zone_boundless_celestial_palace', 777);
        const savedRaw = localStorage.getItem(storageKey);
        const fresh = new Uint8Array(120 * 90);
        const unpackOk = unpackFogBits(savedRaw, fresh, 120, 90);
        let bitMatches = 0;
        for (let i = 0; i < 120 * 90; i++) {
          if ((fogGrid[i] > 0) === (fresh[i] > 0)) bitMatches++;
        }
        results.persist = { storageKey, packedBytesKb: (packed.length / 1024), unpackOk, total: 120 * 90, bitMatches };

        console.log(JSON.stringify(results));
        Únodeú-eT©Úcapture_outputÚtextÚcwdÚcheck)Ú
subprocessÚrunÚstrÚROOTÚjsonÚloadsÚstdoutÚnode_res©ÚclsÚjs_codeÚprocs      r5   Úsetup_classz!TestWarFogNodeRuntime.setup_class-   sP   € ð/ˆõ` Œ~˜v t¨WÐ5ÀdÐQUÕ[^Õ_cÑ[dÔ[dÐlpÐqÑqÔqˆÝ”z $¤+Ñ.Ô.ˆŒˆˆr7   c                 ó  — | j         d         }g }|d         }d}||k    }|}|r|d         }d}||k    }	|	}|�st          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }
d	d
|
iz  }|                     |¦  «         |r`t          j        d|	fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }|                     |¦  «         t          j        |d¦  «        i z  }dd|iz  }t          t          j        |¦  «        ¦  «        ‚d x}x}x}x}x}x}x}	}g }|d         }d}||k    }|}|r&|d         }d}||k    }	|	}|	r|d         }d}||k    }|}|�sdt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }
d	d
|
iz  }|                     |¦  «         |rÂt          j        d|	fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }|                     |¦  «         |	r`t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }|                     |¦  «         t          j        |d¦  «        i z  }dd|iz  }t          t          j        |¦  «        ¦  «        ‚d x}x}x}x}x}x}x}	x}x}x}}d S )NÚinitÚwé<   Úhé-   ©ú==©z%(py3)s == %(py6)s©r   Úpy6ú%(py8)sÚpy8©z%(py11)s == %(py14)s©r   Úpy14ú%(py16)sr   r   zassert %(py19)sÚpy19Úat00ÚatNegÚatOob©z%(py19)s == %(py22)s©r�   Úpy22ú%(py24)sÚpy24úassert %(py27)sÚpy27)rj   r"   r#   r'   ÚappendÚ_format_boolopr(   r)   )r*   ÚstrN   r+   Ú@py_assert5r,   rQ   r/   Ú@py_assert13r0   Ú@py_format7Ú@py_format9Ú@py_format15r3   Ú@py_format18Ú@py_format20Ú@py_assert18Ú@py_assert21Ú@py_assert20Ú@py_format23Ú@py_format25Ú@py_format26Ú@py_format28s                          r5   Útest_node_init_and_boundsz/TestWarFogNodeRuntime.test_node_init_and_boundsb   s  € ØŒ]˜6Ô"ˆØ.Ð.ˆr�#ŒwÐ.˜"Ð.ˆw˜"Š}Ð.Ð.Ð.Ð.Ð.  C¤Ð.¨BÐ. ¨B¢Ð.Ð.Ð.Ð.Ñ.Õ.Ô.Ð.Ð.Ð.Ð.ˆw˜"Ð.Ñ.Ô.Õ.Ô.ˆwÑ.Ô.Õ.Ô.˜"Ñ.Ô.Ð.Ð.Ñ.Ð.Ð.Ð.Ð.Ð.Ñ.Ð.Ð.×.Ò.Ð.Ñ.Ô.Ð.Ð.Ð.Õ.Ô.Ð.Ð.Ð.Ð. ¨BÐ.Ñ.Ô.Õ.Ô. Ñ.Ô.Õ.Ô.¨BÑ.Ô.Ð.Ð.Ñ.Ð.Ð.Ð.Ð.Ð.Ñ.Ð.Ð.×.Ò.Ð.Ñ.Ô.Ð.Õ.Ô.Ð.Ð.Ñ.Ô.Ð.Ñ.Ð.Ð.Ð.Ð.Ð.Ñ.Ð.Õ.Õ.Ô.Ð.Ñ.Ô.Ñ.Ô.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.ØHÐHˆr�&ŒzÐH˜QÐHˆz˜QŠÐHÐHÐHÐHÐH 2 g¤;ÐH°!ÐH ;°!Ò#3ÐHÐHÐHÐHÐH¸¸7¼ÐHÀqÐH¸ÀqÒ8HÐHÐHÐHÐHÑHÕHÔHÐHÐHÐHÐHˆz˜QÐHÑHÔHÕHÔHˆzÑHÔHÕHÔH˜QÑHÔHÐHÐHÑHÐHÐHÐHÐHÐHÑHÐHÐH×HÒHÐHÑHÔHÐHÐHÐHÕHÔHÐHÐHÐHÐH ;°!ÐHÑHÔHÕHÔH ;ÑHÔHÕHÔH°!ÑHÔHÐHÐHÑHÐHÐHÐHÐHÐHÑHÐHÐH×HÒHÐHÑHÔHÐHÐHÐHÕHÔHÐHÐHÐHÐH¸ÀqÐHÑHÔHÕHÔH¸ÑHÔHÕHÔHÀqÑHÔHÐHÐHÑHÐHÐHÐHÐHÐHÑHÐHÐH×HÒHÐHÑHÔHÐHÕHÔHÐHÐHÑHÔHÐHÑHÐHÐHÐHÐHÐHÑHÐHÕHÕHÔHÐHÑHÔHÑHÔHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHÐHr7   c                 óÌ  — | j         d         }g }|d         }d}||k    }|}|r&|d         }d}||k    }	|	}|	r|d         }
d}|
|k    }|}|�sdt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        d	œz  }d
d|iz  }|                     |¦  «         |rÂt          j        d|	fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }|                     |¦  «         |	r`t          j        d|fd|
|f¦  «        t          j        |
¦  «        t          j        |¦  «        dœz  }dd|iz  }|                     |¦  «         t          j        |d¦  «        i z  }dd|iz  }t          t          j        |¦  «        ¦  «        ‚d x}x}x}x}x}x}x}	x}x}
x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d|iz  }t          t          j        |¦  «        ¦  «        ‚d x}x}}|d         }d}||k    }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t          t          j        |¦  «        ¦  «        ‚d x}x}}d S )NÚvisionÚcBeforeé   Ú
edgeBeforeÚ	outBeforer   rv   rx   ry   r{   r|   r}   r~   r€   r   r…   r†   rˆ   r‰   rŠ   r‹   ÚcDecayedé   ©z%(py1)s == %(py4)s©r   rH   z>Tiles outside current radius must decay to EXPLORED_FOGGED (1)ú
>assert %(py6)srz   ÚcNewúassert %(py6)s)	rj   r"   r#   r'   rŒ   r�   r(   r)   rM   )r*   ÚvisrN   r+   r�   r,   rQ   r/   r�   r0   r–   r—   r˜   r‘   r’   r“   r3   r™   rš   r›   rœ   Ú@py_assert3rP   s                          r5   Ú(test_node_3state_vision_reveal_and_decayz>TestWarFogNodeRuntime.test_node_3state_vision_reveal_and_decayg   sÎ  € ØŒm˜HÔ%ˆØWÐWˆs�9Œ~ÐW ÐWˆ~ Ò"ÐWÐWÐWÐWÐW s¨<Ô'8ÐW¸AÐWÐ'8¸AÒ'=ÐWÐWÐWÐWÐWÀ#ÀkÔBRÐWÐVWÐWÐBRÐVWÒBWÐWÐWÐWÐWÑWÕWÔWÐWÐWÐWÐWˆ~ ÐWÑWÔWÕWÔWˆ~ÑWÔWÕWÔW ÑWÔWÐWÐWÑWÐWÐWÐWÐWÐWÑWÐWÐW×WÒWÐWÑWÔWÐWÐWÐWÕWÔWÐWÐWÐWÐWÐ'8¸AÐWÑWÔWÕWÔWÐ'8ÑWÔWÕWÔW¸AÑWÔWÐWÐWÑWÐWÐWÐWÐWÐWÑWÐWÐW×WÒWÐWÑWÔWÐWÐWÐWÕWÔWÐWÐWÐWÐWÐBRÐVWÐWÑWÔWÕWÔWÐBRÑWÔWÕWÔWÐVWÑWÔWÐWÐWÑWÐWÐWÐWÐWÐWÑWÐWÐW×WÒWÐWÑWÔWÐWÕWÔWÐWÐWÑWÔWÐWÑWÐWÐWÐWÐWÐWÑWÐWÕWÕWÔWÐWÑWÔWÑWÔWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWÐWØ�:ŒÐe !Ðeˆ !Ò#ÐeÐeÐeÕeÔeÐeÐeÐeÐeˆ !ÐeÑeÔeÕeÔeˆÑeÔeÕeÔe !ÑeÔeÐeÐeÑeÐeÕeÔeÐ%eÑeÔeÐeÑeÐeÐeÐeÑeÐeÕeÕeÔeÐeÑeÔeÑeÔeÐeÐeÐeÐeÐeÐeÐeØ�6Œ{Ð˜aÐˆ{˜aÒÐÐÐÕÔÐÐÐÐˆ{˜aÐÑÔÕÔˆ{ÑÔÕÔ˜aÑÔÐÐÑÐÐÐÐÐÑÐÕÕÔÐÑÔÑÔÐÐÐÐÐÐÐÐÐr7   c                 ó°  — | j         d         }|d         }d}||k    }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚d x}x}}|d	         }d
}||u }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚d x}x}}|d         }d}||k     }|s‹t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d|d         › d�¦  «        dz   d|iz  }t	          t          j        |¦  «        ¦  «        ‚d x}x}}|d         }|d         }||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d|iz  }t	          t          j        |¦  «        ¦  «        ‚d x}x}}d S )NÚpersistÚ
storageKeyÚ*fe_fog_zone_boundless_celestial_palace_777rv   r¦   r§   rª   rz   ÚunpackOkT©Úis©z%(py1)s is %(py4)sÚpackedBytesKbç       @)ú<)z%(py1)s < %(py4)szPacked size z KB exceeds 2.0 KBr¨   Ú
bitMatchesÚtotalz100% bit preservation required)rj   r"   r#   r'   r(   r)   rM   )r*   ÚprQ   r¬   r+   rP   r‘   s          r5   Útest_node_compact_bitpackingz2TestWarFogNodeRuntime.test_node_compact_bitpackingm   sè  € ØŒM˜)Ô$ˆØ�ŒÐNÐ"NÐNˆÐ"NÒNÐNÐNÐNÕNÔNÐNÐNÐNÐNˆÐ"NÐNÑNÔNÕNÔNˆÑNÔNÕNÔNÐ"NÑNÔNÐNÐNÑNÐNÐNÐNÐNÐNÑNÐNÕNÕNÔNÐNÑNÔNÑNÔNÐNÐNÐNÐNÐNÐNÐNØ�Œ}Ð$ Ð$ˆ} Ð$Ð$Ð$Ð$Õ$Ô$Ð$Ð$Ð$Ð$ˆ} Ð$Ñ$Ô$Õ$Ô$ˆ}Ñ$Ô$Õ$Ô$ Ñ$Ô$Ð$Ð$Ñ$Ð$Ð$Ð$Ð$Ð$Ñ$Ð$Õ$Õ$Ô$Ð$Ñ$Ô$Ñ$Ô$Ð$Ð$Ð$Ð$Ð$Ð$Ð$Ø�Ô!Ð^ CÐ^Ð! CÒ'Ð^Ð^Ð^Õ^Ô^Ð^Ð^Ð^Ð^Ð! CÐ^Ñ^Ô^Õ^Ô^Ð!Ñ^Ô^Õ^Ô^ CÑ^Ô^Ð^Ð^Ñ^Ð^Õ^Ô^Ð)^¸¸/Ô8JÐ)^Ð)^Ð)^Ñ^Ô^Ð^Ñ^Ð^Ð^Ð^Ñ^Ð^Õ^Õ^Ô^Ð^Ñ^Ô^Ñ^Ô^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ø�ŒÐN ! G¤*ÐNˆ *Ò,ÐNÐNÐNÕNÔNÐNÐNÐNÐNˆ *ÐNÑNÔNÕNÔNˆÑNÔNÕNÔN *ÑNÔNÐNÐNÑNÐNÕNÔNÐ.NÑNÔNÐNÑNÐNÐNÐNÑNÐNÕNÕNÔNÐNÑNÔNÑNÔNÐNÐNÐNÐNÐNÐNÐNÐNÐNr7   N)rU   rV   rW   Úclassmethodro   r�   r­   r¼   rX   r7   r5   rZ   rZ   ,   sc   € € € € € Øð2/ð 2/ñ „[ð2/ðhIð Ið Ið
 ð  ð  ðOð Oð Oð Oð Or7   rZ   c                   ó   — e Zd Zd„ ZdS )Ú TestMinimapProjectionAndGeometryc           
      ó^
  — t          dd¦  «        }t          j        }d}d}||z  }d} ||||¬¦  «        }|�sddt          j        ¦   «         v st          j        t          ¦  «        rt          j        t          ¦  «        ndt          j        |¦  «        d	t          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nd	t          j        |¦  «        t          j        |¦  «        t          j        |¦  «        t          j        |¦  «        d
œz  }t          t          j	        |¦  «        ¦  «        ‚dx}x}x}x}x}}ddd|z  z
  dz  z   }	d}
|	|
k    }|s—t          j
        d|fd|	|
f¦  «        dt          j        ¦   «         v st          j        |	¦  «        rt          j        |	¦  «        ndt          j        |
¦  «        dœz  }dd|iz  }t          t          j	        |¦  «        ¦  «        ‚dx}}
t          dd¦  «        }t          j        }d}d}||z  }d} ||||¬¦  «        }|�sddt          j        ¦   «         v st          j        t          ¦  «        rt          j        t          ¦  «        ndt          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        ndt          j        |¦  «        t          j        |¦  «        t          j        |¦  «        t          j        |¦  «        d
œz  }t          t          j	        |¦  «        ¦  «        ‚dx}x}x}x}x}}dd|z  z   }dd|z  z   }g }d}
|
|k    }d}||k     }|o|}|r|rd}||k    }d}||k     }|o|}|�s„t          j
        d||fd|
||f¦  «        t          j        |
¦  «        d t          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nd t          j        |¦  «        d!œz  }d"d#|iz  }|                     |¦  «         |r¢|r t          j
        d||fd$|||f¦  «        t          j        |¦  «        d%t          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nd%t          j        |¦  «        d&œz  }d'd(|iz  }|                     |¦  «         t          j        |d¦  «        i z  }d)d*|iz  }t          t          j	        |¦  «        ¦  «        ‚dx}x}x}
x}x}x}x}x}x}}dS )+z:Aspect ratio preservation with padding 2 on 120x80 canvas.gïîîîîîþ?g[°[°û?g      S@g     €F@g-Cëâ6?)Úrel_tolzrassert %(py12)s
{%(py12)s = %(py2)s
{%(py2)s = %(py0)s.isclose
}(%(py3)s, (%(py5)s / %(py7)s), rel_tol=%(py10)s)
}ÚmathÚscale)r   rG   r   r   r   Úpy10Úpy12Nr·   g      ]@g      N@)ú>)z%(py0)s > %(py3)sÚpad_x)r   r   rL   r   gïîîîîîî?g[°[°ë?g     €V@Úscale_largeg     À]@g     @V@r   éx   éP   )r   r¸   )z%(py3)s <= %(py6)sz%(py6)s < %(py8)sÚcorner_x)r   rz   r|   z%(py10)srÄ   )z%(py13)s <= %(py16)sz%(py16)s < %(py18)sÚcorner_y)r   r   r   z%(py20)sÚpy20zassert %(py23)sÚpy23)ÚminrÂ   Úiscloser$   r%   r"   r&   r'   r(   r)   r#   rŒ   r�   )r*   rÃ   rN   r,   r-   r.   Ú@py_assert9Ú@py_assert11Ú@py_format13rÇ   r+   rR   rS   rÈ   rË   rÌ   Ú@py_assert7r�   rQ   r0   r2   Ú@py_assert17r1   r’   Ú@py_format11r4   Ú@py_format21Ú@py_format22Ú@py_format24s                                r5   Ú,test_aspect_ratio_preservation_and_centeringzMTestMinimapProjectionAndGeometry.test_aspect_ratio_preservation_and_centeringv   s  € õ �L +Ñ.Ô.ˆÝŒ|Ð= 4Ð=¨$Ð= 4¨$¡;Ð=¸Ð=ˆ|ˆ|˜E ;¸Ð=Ñ=Ô=Ð=Ð=Ñ=Ð=Ð=Õ=Ô=Ñ=Ô=Ð=Ð=Õ=Ô=�tÑ=Ô=Ð=Õ=Ô=�tÑ=Ô=Ð=Ð=Õ=Ô=ˆ|Ñ=Ô=Ð=Õ=Ô=Ñ=Ô=Ð=Ð=Õ=Ô=˜EÑ=Ô=Ð=Õ=Ô=˜EÑ=Ô=Ð=Ð=Õ=Ô= 4Ñ=Ô=Õ=Ô=¨$Ñ=Ô=Õ=Ô=¸Ñ=Ô=Õ=Ô=Ð=Ñ=Ô=Ð=Ð=Ñ=Ð=Õ=Õ=Ô=Ð=Ñ=Ô=Ñ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ø�u˜t e™|Ñ+¨sÑ2Ñ2ˆØÐˆu�sŠ{ÐÐÐÕÔÐÐÐÐˆu�sÐÑÔÐÕÔÑÔÐÐÕÔˆuÑÔÐÕÔˆuÑÔÐÐÕÔ�sÑÔÐÐÑÐÐÐÐÐÑÐÕÕÔÐÑÔÑÔÐÐÐÐÐõ ˜-¨Ñ5Ô5ˆÝŒ|ÐC¨ÐC°ÐC¨°©ÐC¸dÐCˆ|ˆ|˜K¨¸dÐCÑCÔCÐCÐCÑCÐCÐCÕCÔCÑCÔCÐCÐCÕCÔC�tÑCÔCÐCÕCÔC�tÑCÔCÐCÐCÕCÔCˆ|ÑCÔCÐCÕCÔCÑCÔCÐCÐCÕCÔC˜KÑCÔCÐCÕCÔC˜KÑCÔCÐCÐCÕCÔC¨ÑCÔCÕCÔC°ÑCÔCÕCÔC¸dÑCÔCÕCÔCÐCÑCÔCÐCÐCÑCÐCÕCÕCÔCÐCÑCÔCÑCÔCÐCÐCÐCÐCÐCÐCÐCÐCÐCÐCÐCÐCÐCØ˜ Ñ,Ñ,ˆØ˜ Ñ+Ñ+ˆØ9Ð9ˆqÐ9ˆq�HÒ"Ð9˜sÐ9�H˜sÒ"Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9 qÐ9 q¨HÒ'9Ð9°rÐ9¨H°rÒ'9Ð9Ð9Ð9Ð9Ð9Ð9Ñ9Õ9Ô9Ð9Ð9Ð9Ð9Ð9ˆq�H˜sÐ9Ñ9Ô9Õ9Ô9ˆqÑ9Ô9Ð9Õ9Ô9Ñ9Ô9Ð9Ð9Õ9Ô9�HÑ9Ô9Ð9Õ9Ô9�HÑ9Ô9Ð9Ð9Õ9Ô9˜sÑ9Ô9Ð9Ð9Ñ9Ð9Ð9Ð9Ð9Ð9Ñ9Ð9Ð9×9Ò9Ð9Ñ9Ô9Ð9Ð9Ð9Ð9Ð9Õ9Ô9Ð9Ð9Ð9Ð9Ð9 q¨H°rÐ9Ñ9Ô9Õ9Ô9 qÑ9Ô9Ð9Õ9Ô9Ñ9Ô9Ð9Ð9Õ9Ô9¨HÑ9Ô9Ð9Õ9Ô9¨HÑ9Ô9Ð9Ð9Õ9Ô9°rÑ9Ô9Ð9Ð9Ñ9Ð9Ð9Ð9Ð9Ð9Ñ9Ð9Ð9×9Ò9Ð9Ñ9Ô9Ð9Õ9Ô9Ð9Ð9Ñ9Ô9Ð9Ñ9Ð9Ð9Ð9Ð9Ð9Ñ9Ð9Õ9Õ9Ô9Ð9Ñ9Ô9Ñ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9r7   N)rU   rV   rW   rÚ   rX   r7   r5   r¿   r¿   u   s#   € € € € € ð:ð :ð :ð :ð :r7   r¿   c                   ó0   — e Zd Zed„ ¦   «         Zd„ Zd„ ZdS )Ú)TestMinimapRuntimeThrottlingAndIndicatorsc                 ó¢   — d}t          j        dd|gddt          t          ¦  «        d¬¦  «        }t	          j        |j        ¦  «        | _        d S )Naã	  
        const fs = require('fs');
        let code = fs.readFileSync('client/webapp/js/ui/minimap_hud.js', 'utf8')
          .replace(/import\s+[^;]+;\s*/g, '').replace(/export\s+(const|let)\s+/g, 'var ')
          .replace(/export\s+function\s+/g, 'function ').replace(/export\s+class\s+MinimapHUD/g, 'MinimapHUD = class MinimapHUD')
          .replace(/export\s+\{[^}]+\};?\s*/g, '').replace(/export\s+default\s+[^;]+;?\s*/g, '');

        global.window = { isGamePaused: false };
        global.document = { getElementById: () => null };
        var MinimapHUD;
        eval(code);

        const hud = new MinimapHUD('minimap-canvas');
        hud.init();
        hud.setMap(new Uint8Array(60 * 45).fill(1), 60, 45, 1, null);

        let throttledRenders = 0;
        hud.render = () => { throttledRenders++; };
        for (let frame = 0; frame < 120; frame++) {
          hud.update(frame * 8.3333, 10, 10, 0);
        }

        global.window.isGamePaused = true;
        let pauseRenders = 0;
        hud.render = () => { pauseRenders++; };
        for (let i = 0; i < 50; i++) {
          hud.update(1000 + i * 100, 10, 10, 0);
        }
        global.window.isGamePaused = false;

        const fog = new Uint8Array(60 * 45);
        fog[10 * 60 + 10] = 1;
        global.fogGrid = fog;
        global.window.fogGrid = fog;
        const metadata = {
          pois: [{ x: 5, y: 5 }, { x: 10, y: 10 }],
          bossGate: { x: 20, y: 20 },
          spawn: { x: 0, y: 0 }
        };
        hud.setMap(new Uint8Array(60 * 45).fill(1), 60, 45, 1, metadata);

        let drawnArcs = [];
        const mockCtx = {
          fillStyle: '', strokeStyle: '', lineWidth: 1,
          beginPath: () => {},
          arc: (x, y, r) => { drawnArcs.push({ x, y, r, fill: mockCtx.fillStyle }); },
          fill: () => {}, stroke: () => {}, moveTo: () => {}, lineTo: () => {}
        };

        global.window.bossGateBreached = false;
        hud.renderIndicators(mockCtx, 0, 0, 0, 1000);
        const lockedGateRed = drawnArcs.some(a => a.fill === '#ef4444');
        const poiArcs = drawnArcs.filter(a => a.fill === '#fbbf24');

        drawnArcs = [];
        global.window.bossGateBreached = true;
        hud.renderIndicators(mockCtx, 0, 0, 0, 1000);
        const unlockedGateGreen = drawnArcs.some(a => a.fill === '#10b981' && a.r === 2.5);

        console.log(JSON.stringify({
          throttledRenders, pauseRenders, lockedGateRed, unlockedGateGreen,
          exploredPoiCount: poiArcs.length
        }));
        r\   r]   Tr^   ©rc   rd   re   rf   rg   rh   ri   Úresrk   s      r5   ro   z5TestMinimapRuntimeThrottlingAndIndicators.setup_class‡   sP   € ð?ˆõ@ Œ~˜v t¨WÐ5ÀdÐQUÕ[^Õ_cÑ[dÔ[dÐlpÐqÑqÔqˆÝ”*˜Tœ[Ñ)Ô)ˆŒˆˆr7   c                 óp  — d}| j         d         }||k    }d}||k    }|r|s�t          j        d||fd|||f¦  «        t          j        |¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚d	x}x}x}x}}| j         d
         }d}||k    }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }	t	          t          j        |	¦  «        ¦  «        ‚d	x}x}}d	S )z>Verifies 30Hz throttling and pause freeze via Node.js runtime.é   ÚthrottledRendersé   )r   r   )z%(py1)s <= %(py5)sz%(py5)s <= %(py7)s)r   r   r   zassert %(py9)sr   NÚpauseRendersr   rv   r¦   r§   rª   rz   ©rß   r"   r#   r'   r(   r)   )
r*   rQ   r,   r+   r-   r¬   Ú@py_format8Ú@py_format10rP   r‘   s
             r5   Ú!test_battery_throttling_and_pausezKTestMinimapRuntimeThrottlingAndIndicators.test_battery_throttling_and_pauseÌ   s”  € àÐ7�T”XÐ0Ô1Ð7ˆrÐ1Ò7Ð7°RÐ7Ð1°RÒ7Ð7Ð7Ð7Ð7Ð7Õ7Ô7Ð7Ð7Ð7Ð7Ð7ˆrÐ1°RÐ7Ñ7Ô7Õ7Ô7ˆrÑ7Ô7Õ7Ô7Ð1Ñ7Ô7Õ7Ô7°RÑ7Ô7Ð7Ð7Ñ7Ð7Ð7Ð7Ð7Ð7Ñ7Ð7Õ7Õ7Ô7Ð7Ñ7Ô7Ñ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7ØŒx˜Ô'Ð,¨1Ð,Ð'¨1Ò,Ð,Ð,Ð,Õ,Ô,Ð,Ð,Ð,Ð,Ð'¨1Ð,Ñ,Ô,Õ,Ô,Ð'Ñ,Ô,Õ,Ô,¨1Ñ,Ô,Ð,Ð,Ñ,Ð,Ð,Ð,Ð,Ð,Ñ,Ð,Õ,Õ,Ô,Ð,Ñ,Ô,Ñ,Ô,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,r7   c                 ó4  — | j         d         }d}||u }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}| j         d	         }d}||u }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}| j         d
         }d}||k    }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}dS )zDVerifies Boss Gate colors and POI fog filtering via Node.js runtime.ÚlockedGateRedTr³   rµ   r§   rª   rz   NÚunlockedGateGreenÚexploredPoiCountr¥   rv   r¦   rå   ©r*   rQ   r¬   r+   rP   r‘   s         r5   Útest_indicator_state_logiczDTestMinimapRuntimeThrottlingAndIndicators.test_indicator_state_logicÑ   s  € àŒx˜Ô(Ð0¨DÐ0Ð(¨DÐ0Ð0Ð0Ð0Õ0Ô0Ð0Ð0Ð0Ð0Ð(¨DÐ0Ñ0Ô0Õ0Ô0Ð(Ñ0Ô0Õ0Ô0¨DÑ0Ô0Ð0Ð0Ñ0Ð0Ð0Ð0Ð0Ð0Ñ0Ð0Õ0Õ0Ô0Ð0Ñ0Ô0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0Ð0ØŒxÐ+Ô,Ð4°Ð4Ð,°Ð4Ð4Ð4Ð4Õ4Ô4Ð4Ð4Ð4Ð4Ð,°Ð4Ñ4Ô4Õ4Ô4Ð,Ñ4Ô4Õ4Ô4°Ñ4Ô4Ð4Ð4Ñ4Ð4Ð4Ð4Ð4Ð4Ñ4Ð4Õ4Õ4Ô4Ð4Ñ4Ô4Ñ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4Ð4ØŒxÐ*Ô+Ð0¨qÐ0Ð+¨qÒ0Ð0Ð0Ð0Õ0Ô0Ð0Ð0Ð0Ð0Ð+¨qÐ0Ñ0Ô0Õ0Ô0Ð+Ñ0Ô0Õ0Ô0¨qÑ0Ô0Ð0Ð0Ñ0Ð0Ð0Ð0Ð0Ð0Ñ0Ð0Õ0Õ0Ô0Ð0Ñ0Ô0Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0r7   N)rU   rV   rW   r½   ro   rè   rî   rX   r7   r5   rÜ   rÜ   †   sO   € € € € € ØðB*ð B*ñ „[ðB*ðH-ð -ð -ð
1ð 1ð 1ð 1ð 1r7   rÜ   c                   ó   — e Zd Zd„ Zd„ ZdS )ÚTestEntitySuppressionWiringc                 ó¾  — t                                d¬¦  «        }g }d}||v }|}|rd}||v }|}|�sXt          j        d|fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }d	d
|iz  }	|                     |	¦  «         |r‹t          j        d|fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }
dd|
iz  }|                     |¦  «         t          j	        |d¦  «        i z  }dd|iz  }t          t          j        |¦  «        ¦  «        ‚dx}x}x}x}x}}d}||v }|s—t          j        d|fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }dd|iz  }t          t          j        |¦  «        ¦  «        ‚dx}}d}||v }|s—t          j        d|fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }dd|iz  }t          t          j        |¦  «        ¦  «        ‚dx}}dS )zOVerifies world_renderer.js has drop, waypoint, boss gate and fog overlay hooks.r   r   zdrop.wxúWarFog.getFogStaterC   ©z%(py3)s in %(py5)sÚcontent©r   r   ú%(py7)sr   ©z%(py10)s in %(py12)s©rÄ   rÅ   ú%(py14)sr   r   úassert %(py17)sÚpy17NÚwpFogrI   rJ   rL   r   ÚWarFogRenderer)ÚWORLD_RENDERER_JSr    r"   r#   r'   r$   r%   r&   rŒ   r�   r(   r)   )r*   rô   rN   r+   r,   rQ   rÑ   rÒ   rS   ræ   rÓ   r“   Ú@py_format16r”   rR   s                  r5   Ú%test_world_renderer_suppression_wiredzATestEntitySuppressionWiring.test_world_renderer_suppression_wiredÙ   s‹  € å#×-Ò-°wÐ-Ñ?Ô?ˆØGÐGˆyÐGˆy˜GÐ#ÐGÐGÐGÐGÐGÐ(<ÐGÐ(<ÀÐ(GÐGÐGÐGÐGÑGÕGÔGÐGÐGÐGÐGˆy˜GÐGÑGÔGÕGÔGˆyÑGÔGÐGÕGÔGÑGÔGÐGÐGÕGÔG˜GÑGÔGÐGÕGÔG˜GÑGÔGÐGÐGÐGÐGÑGÐGÐGÐGÐGÐGÑGÐGÐG×GÒGÐGÑGÔGÐGÐGÐGÕGÔGÐGÐGÐGÐGÐ(<ÀÐGÑGÔGÕGÔGÐ(<ÑGÔGÐGÕGÔGÑGÔGÐGÐGÕGÔGÀÑGÔGÐGÕGÔGÀÑGÔGÐGÐGÐGÐGÑGÐGÐGÐGÐGÐGÑGÐGÐG×GÒGÐGÑGÔGÐGÕGÔGÐGÐGÑGÔGÐGÑGÐGÐGÐGÐGÐGÑGÐGÕGÕGÔGÐGÑGÔGÑGÔGÐGÐGÐGÐGÐGÐGÐGÐGÐGÐGÐGÐGÐGØÐ!ˆw˜'Ð!Ð!Ð!Ð!Õ!Ô!Ð!Ð!Ð!Ð!ˆw˜'Ð!Ñ!Ô!Õ!Ô!ˆwÑ!Ô!Ð!Õ!Ô!Ñ!Ô!Ð!Ð!Õ!Ô!˜'Ñ!Ô!Ð!Õ!Ô!˜'Ñ!Ô!Ð!Ð!Ð!Ð!Ñ!Ð!Ð!Ð!Ð!Ð!Ñ!Ð!Õ!Õ!Ô!Ð!Ñ!Ô!Ñ!Ô!Ð!Ð!Ð!Ð!Ð!ØÐ*Ð 7Ð*Ð*Ð*Ð*Õ*Ô*Ð*Ð*Ð*Ð*Ð 7Ð*Ñ*Ô*Õ*Ô*ÐÑ*Ô*Ð*Õ*Ô*Ñ*Ô*Ð*Ð*Õ*Ô* 7Ñ*Ô*Ð*Õ*Ô* 7Ñ*Ô*Ð*Ð*Ð*Ð*Ñ*Ð*Ð*Ð*Ð*Ð*Ñ*Ð*Õ*Õ*Ô*Ð*Ñ*Ô*Ñ*Ô*Ð*Ð*Ð*Ð*Ð*Ð*Ð*r7   c                 ón  — t                                d¬¦  «        }d}||v }|s—t          j        d|fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }dd	|iz  }t          t          j	        |¦  «        ¦  «        ‚d
x}}g }d}||v }|}|rd}||v }	|	}|�sXt          j        d|fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }dd|iz  }
| 
                    |
¦  «         |r‹t          j        d|	fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }dd|iz  }| 
                    |¦  «         t          j        |d¦  «        i z  }dd|iz  }t          t          j	        |¦  «        ¦  «        ‚d
x}x}x}x}x}}	g }d}||v }|}|rd}||v }	|	}|�sXt          j        d|fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }dd|iz  }
| 
                    |
¦  «         |r‹t          j        d|	fd||f¦  «        t          j        |¦  «        dt          j        ¦   «         v st          j        |¦  «        rt          j        |¦  «        nddœz  }dd|iz  }| 
                    |¦  «         t          j        |d¦  «        i z  }dd|iz  }t          t          j	        |¦  «        ¦  «        ‚d
x}x}x}x}x}}	d
S )zMVerifies entity_renderer.js suppresses non-visible monsters, NPCs, and props.r   r   ÚpropFogrC   rI   rô   rJ   rL   r   Nzmob.wxrò   ró   rõ   rö   r   r÷   rø   rù   r   r   rú   rû   znpc.wx)ÚENTITY_RENDERER_JSr    r"   r#   r'   r$   r%   r&   r(   r)   rŒ   r�   )r*   rô   rQ   r+   rR   rS   rN   r,   rÑ   rÒ   ræ   rÓ   r“   rÿ   r”   s                  r5   Ú&test_entity_renderer_suppression_wiredzBTestEntitySuppressionWiring.test_entity_renderer_suppression_wiredà   s¬  € å$×.Ò.¸Ð.Ñ@Ô@ˆØÐ#ˆy˜GÐ#Ð#Ð#Ð#Õ#Ô#Ð#Ð#Ð#Ð#ˆy˜GÐ#Ñ#Ô#Õ#Ô#ˆyÑ#Ô#Ð#Õ#Ô#Ñ#Ô#Ð#Ð#Õ#Ô#˜GÑ#Ô#Ð#Õ#Ô#˜GÑ#Ô#Ð#Ð#Ð#Ð#Ñ#Ð#Ð#Ð#Ð#Ð#Ñ#Ð#Õ#Õ#Ô#Ð#Ñ#Ô#Ñ#Ô#Ð#Ð#Ð#Ð#Ð#ØFÐFˆxÐFˆx˜7Ð"ÐFÐFÐFÐFÐFÐ';ÐFÐ';¸wÐ'FÐFÐFÐFÐFÑFÕFÔFÐFÐFÐFÐFˆx˜7ÐFÑFÔFÕFÔFˆxÑFÔFÐFÕFÔFÑFÔFÐFÐFÕFÔF˜7ÑFÔFÐFÕFÔF˜7ÑFÔFÐFÐFÐFÐFÑFÐFÐFÐFÐFÐFÑFÐFÐF×FÒFÐFÑFÔFÐFÐFÐFÕFÔFÐFÐFÐFÐFÐ';¸wÐFÑFÔFÕFÔFÐ';ÑFÔFÐFÕFÔFÑFÔFÐFÐFÕFÔF¸wÑFÔFÐFÕFÔF¸wÑFÔFÐFÐFÐFÐFÑFÐFÐFÐFÐFÐFÑFÐFÐF×FÒFÐFÑFÔFÐFÕFÔFÐFÐFÑFÔFÐFÑFÐFÐFÐFÐFÐFÑFÐFÕFÕFÔFÐFÑFÔFÑFÔFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFØFÐFˆxÐFˆx˜7Ð"ÐFÐFÐFÐFÐFÐ';ÐFÐ';¸wÐ'FÐFÐFÐFÐFÑFÕFÔFÐFÐFÐFÐFˆx˜7ÐFÑFÔFÕFÔFˆxÑFÔFÐFÕFÔFÑFÔFÐFÐFÕFÔF˜7ÑFÔFÐFÕFÔF˜7ÑFÔFÐFÐFÐFÐFÑFÐFÐFÐFÐFÐFÑFÐFÐF×FÒFÐFÑFÔFÐFÐFÐFÕFÔFÐFÐFÐFÐFÐ';¸wÐFÑFÔFÕFÔFÐ';ÑFÔFÐFÕFÔFÑFÔFÐFÐFÕFÔF¸wÑFÔFÐFÕFÔF¸wÑFÔFÐFÐFÐFÐFÑFÐFÐFÐFÐFÐFÑFÐFÐF×FÒFÐFÑFÔFÐFÕFÔFÐFÐFÑFÔFÐFÑFÐFÐFÐFÐFÐFÑFÐFÕFÕFÔFÐFÑFÔFÑFÔFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFÐFr7   N)rU   rV   rW   r   r  rX   r7   r5   rð   rð   Ø   s7   € € € € € ð+ð +ð +ðGð Gð Gð Gð Gr7   rð   c                   ó<   — e Zd Zed„ ¦   «         Zd„ Zd„ Zd„ Zd„ ZdS )Ú"TestCombatTargetAntiMaphackRuntimec                 ó¢   — d}t          j        dd|gddt          t          ¦  «        d¬¦  «        }t	          j        |j        ¦  «        | _        d S )Na6  
        const fs = require('fs');
        let code = fs.readFileSync('client/webapp/js/engine/monster_system.js', 'utf8');
        const match = code.match(/function getBestCombatTarget[\s\S]*?\n}/);
        if (!match) throw new Error('getBestCombatTarget not found');
        eval(match[0]);

        const results = {};
        global.window = { WarFog: { getFogState: () => 0 } };
        global.activeMonsters = [{ id: 'm_unexp', wx: 3, wy: 3, hp: 100 }];
        global.window.monster = global.activeMonsters[0];
        results.unexploredTarget = getBestCombatTarget(0, 0, 7.5);
        global.window.WarFog.getFogState = () => 1;
        results.foggedTarget = getBestCombatTarget(0, 0, 7.5);
        global.activeMonsters = [];
        global.window.monster = { id: 'boss_fogged', wx: 3, wy: 3, hp: 100 };
        results.fallbackFoggedTarget = getBestCombatTarget(0, 0, 7.5);
        global.window.WarFog.getFogState = () => 2;
        global.activeMonsters = [{ id: 'm_visible', wx: 3, wy: 3, hp: 100 }];
        global.window.monster = global.activeMonsters[0];
        const resVis = getBestCombatTarget(0, 0, 7.5);
        results.visibleTargetId = resVis ? resVis.id : null;
        global.activeMonsters = [];
        global.window.monster = { id: 'boss_visible', wx: 4, wy: 4, hp: 500 };
        const resFallback = getBestCombatTarget(0, 0, 7.5);
        results.fallbackVisibleId = resFallback ? resFallback.id : null;
        global.window.monster = { id: 'boss_far', wx: 10, wy: 10, hp: 500 };
        results.beyondRangeTarget = getBestCombatTarget(0, 0, 7.5);
        global.activeMonsters = [{ id: 'm_dead', wx: 3, wy: 3, hp: 0 }];
        global.window.monster = { id: 'boss_dead', wx: 3, wy: 3, hp: 0 };
        results.deadTarget = getBestCombatTarget(0, 0, 7.5);
        console.log(JSON.stringify(results));
        r\   r]   Tr^   rÞ   rk   s      r5   ro   z.TestCombatTargetAntiMaphackRuntime.setup_classé   sP   € ð ˆõB Œ~˜v t¨WÐ5ÀdÐQUÕ[^Õ_cÑ[dÔ[dÐlpÐqÑqÔqˆÝ”*˜Tœ[Ñ)Ô)ˆŒˆˆr7   c                 ó0  — | j         d         }d}||u }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}| j         d         }d}||u }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}| j         d	         }d}||u }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}dS )
zBMonsters in unexplored (0) or fogged (1) tiles cannot be targeted.ÚunexploredTargetNr³   rµ   r§   rª   rz   ÚfoggedTargetÚfallbackFoggedTargetrå   rí   s         r5   Ú.test_unexplored_and_fogged_target_returns_nullzQTestCombatTargetAntiMaphackRuntime.test_unexplored_and_fogged_target_returns_null  s  € àŒxÐ*Ô+Ð3¨tÐ3Ð+¨tÐ3Ð3Ð3Ð3Õ3Ô3Ð3Ð3Ð3Ð3Ð+¨tÐ3Ñ3Ô3Õ3Ô3Ð+Ñ3Ô3Õ3Ô3¨tÑ3Ô3Ð3Ð3Ñ3Ð3Ð3Ð3Ð3Ð3Ñ3Ð3Õ3Õ3Ô3Ð3Ñ3Ô3Ñ3Ô3Ð3Ð3Ð3Ð3Ð3Ð3Ð3ØŒx˜Ô'Ð/¨4Ð/Ð'¨4Ð/Ð/Ð/Ð/Õ/Ô/Ð/Ð/Ð/Ð/Ð'¨4Ð/Ñ/Ô/Õ/Ô/Ð'Ñ/Ô/Õ/Ô/¨4Ñ/Ô/Ð/Ð/Ñ/Ð/Ð/Ð/Ð/Ð/Ñ/Ð/Õ/Õ/Ô/Ð/Ñ/Ô/Ñ/Ô/Ð/Ð/Ð/Ð/Ð/Ð/Ð/ØŒxÐ.Ô/Ð7°4Ð7Ð/°4Ð7Ð7Ð7Ð7Õ7Ô7Ð7Ð7Ð7Ð7Ð/°4Ð7Ñ7Ô7Õ7Ô7Ð/Ñ7Ô7Õ7Ô7°4Ñ7Ô7Ð7Ð7Ñ7Ð7Ð7Ð7Ð7Ð7Ñ7Ð7Õ7Õ7Ô7Ð7Ñ7Ô7Ñ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7r7   c                 ó*  — | j         d         }d}||k    }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}| j         d	         }d
}||k    }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}dS )z.Visible monsters within maxRange are acquired.ÚvisibleTargetIdÚ	m_visiblerv   r¦   r§   rª   rz   NÚfallbackVisibleIdÚboss_visiblerå   rí   s         r5   Ú0test_visible_target_within_range_returns_monsterzSTestCombatTargetAntiMaphackRuntime.test_visible_target_within_range_returns_monster  s`  € àŒxÐ)Ô*Ð9¨kÐ9Ð*¨kÒ9Ð9Ð9Ð9Õ9Ô9Ð9Ð9Ð9Ð9Ð*¨kÐ9Ñ9Ô9Õ9Ô9Ð*Ñ9Ô9Õ9Ô9¨kÑ9Ô9Ð9Ð9Ñ9Ð9Ð9Ð9Ð9Ð9Ñ9Ð9Õ9Õ9Ô9Ð9Ñ9Ô9Ñ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9Ð9ØŒxÐ+Ô,Ð>°Ð>Ð,°Ò>Ð>Ð>Ð>Õ>Ô>Ð>Ð>Ð>Ð>Ð,°Ð>Ñ>Ô>Õ>Ô>Ð,Ñ>Ô>Õ>Ô>°Ñ>Ô>Ð>Ð>Ñ>Ð>Ð>Ð>Ð>Ð>Ñ>Ð>Õ>Õ>Ô>Ð>Ñ>Ô>Ñ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>r7   c                 ó  — | j         d         }d}||u }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}dS )z-Visible monsters beyond maxRange return null.ÚbeyondRangeTargetNr³   rµ   r§   rª   rz   rå   rí   s         r5   Ú-test_visible_target_beyond_range_returns_nullzPTestCombatTargetAntiMaphackRuntime.test_visible_target_beyond_range_returns_null  s´   € àŒxÐ+Ô,Ð4°Ð4Ð,°Ð4Ð4Ð4Ð4Õ4Ô4Ð4Ð4Ð4Ð4Ð,°Ð4Ñ4Ô4Õ4Ô4Ð,Ñ4Ô4Õ4Ô4°Ñ4Ô4Ð4Ð4Ñ4Ð4Ð4Ð4Ð4Ð4Ñ4Ð4Õ4Õ4Ô4Ð4Ñ4Ô4Ñ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4Ð4Ð4Ð4r7   c                 ó  — | j         d         }d}||u }|slt          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}dS )z+Dead monsters (hp <= 0) cannot be targeted.Ú
deadTargetNr³   rµ   r§   rª   rz   rå   rí   s         r5   Útest_dead_target_ignoredz;TestCombatTargetAntiMaphackRuntime.test_dead_target_ignored  s³   € àŒx˜Ô%Ð-¨Ð-Ð%¨Ð-Ð-Ð-Ð-Õ-Ô-Ð-Ð-Ð-Ð-Ð%¨Ð-Ñ-Ô-Õ-Ô-Ð%Ñ-Ô-Õ-Ô-¨Ñ-Ô-Ð-Ð-Ñ-Ð-Ð-Ð-Ð-Ð-Ñ-Ð-Õ-Õ-Ô-Ð-Ñ-Ô-Ñ-Ô-Ð-Ð-Ð-Ð-Ð-Ð-Ð-Ð-Ð-r7   N)	rU   rV   rW   r½   ro   r  r  r  r  rX   r7   r5   r  r  è   sj   € € € € € Øð#*ð #*ñ „[ð#*ðJ8ð 8ð 8ð?ð ?ð ?ð
5ð 5ð 5ð.ð .ð .ð .ð .r7   r  )Ú__doc__Úbuiltinsr$   Ú_pytest.assertion.rewriteÚ	assertionÚrewriter"   rg   rÂ   rc   Úpathlibr   ÚpytestÚ__file__ÚresolveÚparentrf   r   r   r   r   rþ   r  ÚMONSTER_SYSTEM_JSr
   rZ   r¿   rÜ   rð   r  rX   r7   r5   ú<module>r$     sl  ððð ð
 € € € € € € € € € € € € €€€€Ø €€€Ø Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø €€€à€tˆH�~„~×ÒÑÔÔ&Ô-Ô4€Ø�H‰_˜xÑ'¨$Ñ.°Ñ5¸ÑD€
Ø˜X‘o¨Ñ0°4Ñ7¸$Ñ>ÐAVÑVÐ Ø˜‘ 8Ñ+¨dÑ2°TÑ9Ð<LÑL€Ø�H‰_˜xÑ'¨,Ñ6€
Ø˜8‘O hÑ.°Ñ5¸Ñ@ÐCVÑVÐ Ø˜H‘_ xÑ/°$Ñ6¸ÑAÐDXÑXÐ Ø˜8‘O hÑ.°Ñ5¸Ñ@ÐCVÑVÐ ð4ð 4ð 4ð 4ð 4ñ 4ô 4ð 4ð,FOð FOð FOð FOð FOñ FOô FOð FOðR:ð :ð :ð :ð :ñ :ô :ð :ð"O1ð O1ð O1ð O1ð O1ñ O1ô O1ð O1ðdGð Gð Gð Gð Gñ Gô Gð Gð 8.ð 8.ð 8.ð 8.ð 8.ñ 8.ô 8.ð 8.ð 8.ð 8.r7   