cartridge.cafe · open source world
ESPER
HOW TO PLAY
A stealth quest on the hex-kite lattice — three rooms in one world. CLICK a node (centers, edge midpoints, corners): your esper pathfinds and walks. Enemy vision cones radiate live. Forest blocks sight — and hides you inside it. CARDS (bottom, or keys 1–4): click FIRE or PSYCHIC to arm — the range zone paints on the board; click an enemy inside it to cast. Click the SHADOW card (or SPACE) to blink forward, unseen for 1.2s. Click an adjacent enemy from BEHIND: silent kill. Three alerts = permanent hunt. Caught = the room resets. The quest: the key in the Hollow → the sealed north door opens → the chest in the Keep.
1 visual shader · 0 shader modules · 1 step hook · runs on WebGPU in the browser
This source is part of the cartridge.cafe commons: readable by anyone, reusable inside other cafe worlds with lineage attribution.
— VISUAL SHADERS (WGSL) —
visual · esper
const ES: f32 = 30.00;
const EX0: f32 = 70.00;
const EY0: f32 = 66.00;
const ISY: f32 = 0.56;
const IYOFF: f32 = 112.00;
const ZOOM: f32 = 1.7; // LOCKSTEP: every hook divisor below must equal this
fn hex_px(h: vec2i) -> vec2f {
return vec2f(EX0 + ES * 1.5 * f32(h.x), EY0 + ES * (0.8660254 * f32(h.x) + 1.7320508 * f32(h.y)));
}
fn px_hex(p: vec2f) -> vec2i {
let qf = (0.6666667 * (p.x - EX0)) / ES;
let rf = (-0.3333333 * (p.x - EX0) + 0.5773503 * (p.y - EY0)) / ES;
let sf = -qf - rf;
var q = round(qf); var r = round(rf); var s = round(sf);
let dq = abs(q - qf); let dr = abs(r - rf); let ds = abs(s - sf);
if (dq > dr && dq > ds) { q = -r - s; } else if (dr > ds) { r = -q - s; }
return vec2i(i32(q), i32(r));
}
fn hex_dist(a: vec2i, b: vec2i) -> i32 {
let dq = b.x - a.x; let dr = b.y - a.y;
return (abs(dq) + abs(dr) + abs(dq + dr)) / 2;
}
fn esp_land(room: i32, q: i32, r: i32) -> i32 {
if (room == 1) {
if (q == 0 && r == 0) { return 2; }
if (q == 1 && r == 0) { return 2; }
if (q == 2 && r == -1) { return 2; }
if (q == 3 && r == -1) { return 2; }
if (q == 4 && r == -2) { return 2; }
if (q == 5 && r == -2) { return 2; }
if (q == 6 && r == -3) { return 2; }
if (q == 7 && r == -3) { return 2; }
if (q == 8 && r == -4) { return 2; }
if (q == 0 && r == 1) { return 2; }
if (q == 6 && r == -2) { return 1; }
if (q == 8 && r == -3) { return 2; }
if (q == 0 && r == 2) { return 2; }
if (q == 2 && r == 1) { return 1; }
if (q == 6 && r == -1) { return 1; }
if (q == 8 && r == -2) { return 2; }
if (q == 0 && r == 3) { return 2; }
if (q == 4 && r == 1) { return 1; }
if (q == 0 && r == 4) { return 2; }
if (q == 2 && r == 3) { return 3; }
if (q == 3 && r == 3) { return 3; }
if (q == 5 && r == 2) { return 1; }
if (q == 8 && r == 0) { return 2; }
if (q == 0 && r == 5) { return 2; }
if (q == 1 && r == 5) { return 3; }
if (q == 2 && r == 4) { return 3; }
if (q == 3 && r == 4) { return 3; }
if (q == 8 && r == 1) { return 2; }
if (q == 0 && r == 6) { return 2; }
if (q == 8 && r == 2) { return 2; }
if (q == 0 && r == 7) { return 2; }
if (q == 1 && r == 7) { return 2; }
if (q == 2 && r == 6) { return 2; }
if (q == 3 && r == 6) { return 2; }
if (q == 4 && r == 5) { return 2; }
if (q == 5 && r == 5) { return 2; }
if (q == 6 && r == 4) { return 2; }
if (q == 7 && r == 4) { return 2; }
if (q == 8 && r == 3) { return 2; }
return 0;
}
if (room == 2) {
if (q == 0 && r == 0) { return 2; }
if (q == 1 && r == 0) { return 2; }
if (q == 2 && r == -1) { return 2; }
if (q == 3 && r == -1) { return 2; }
if (q == 4 && r == -2) { return 2; }
if (q == 5 && r == -2) { return 2; }
if (q == 6 && r == -3) { return 2; }
if (q == 7 && r == -3) { return 2; }
if (q == 8 && r == -4) { return 2; }
if (q == 0 && r == 1) { return 2; }
if (q == 3 && r == 0) { return 2; }
if (q == 4 && r == -1) { return 2; }
if (q == 8 && r == -3) { return 2; }
if (q == 0 && r == 2) { return 2; }
if (q == 2 && r == 1) { return 1; }
if (q == 5 && r == 0) { return 2; }
if (q == 8 && r == -2) { return 2; }
if (q == 4 && r == 1) { return 1; }
if (q == 8 && r == -1) { return 2; }
if (q == 0 && r == 4) { return 2; }
if (q == 2 && r == 3) { return 2; }
if (q == 3 && r == 3) { return 2; }
if (q == 6 && r == 1) { return 1; }
if (q == 8 && r == 0) { return 2; }
if (q == 0 && r == 5) { return 2; }
if (q == 4 && r == 3) { return 2; }
if (q == 8 && r == 1) { return 2; }
if (q == 0 && r == 6) { return 2; }
if (q == 2 && r == 5) { return 1; }
if (q == 8 && r == 2) { return 2; }
if (q == 0 && r == 7) { return 2; }
if (q == 1 && r == 7) { return 2; }
if (q == 2 && r == 6) { return 2; }
if (q == 3 && r == 6) { return 2; }
if (q == 4 && r == 5) { return 2; }
if (q == 5 && r == 5) { return 2; }
if (q == 6 && r == 4) { return 2; }
if (q == 7 && r == 4) { return 2; }
if (q == 8 && r == 3) { return 2; }
return 0;
}
if (room == 3) {
if (q == 0 && r == 0) { return 2; }
if (q == 1 && r == 0) { return 2; }
if (q == 2 && r == -1) { return 2; }
if (q == 3 && r == -1) { return 2; }
if (q == 4 && r == -2) { return 2; }
if (q == 5 && r == -2) { return 2; }
if (q == 6 && r == -3) { return 2; }
if (q == 7 && r == -3) { return 2; }
if (q == 8 && r == -4) { return 2; }
if (q == 0 && r == 1) { return 2; }
if (q == 8 && r == -3) { return 2; }
if (q == 0 && r == 2) { return 2; }
if (q == 2 && r == 1) { return 2; }
if (q == 6 && r == -1) { return 2; }
if (q == 8 && r == -2) { return 2; }
if (q == 0 && r == 3) { return 2; }
if (q == 3 && r == 2) { return 1; }
if (q == 5 && r == 1) { return 1; }
if (q == 8 && r == -1) { return 2; }
if (q == 0 && r == 4) { return 2; }
if (q == 2 && r == 3) { return 2; }
if (q == 6 && r == 1) { return 2; }
if (q == 8 && r == 0) { return 2; }
if (q == 0 && r == 5) { return 2; }
if (q == 8 && r == 1) { return 2; }
if (q == 0 && r == 6) { return 2; }
if (q == 1 && r == 6) { return 2; }
if (q == 2 && r == 5) { return 2; }
if (q == 3 && r == 5) { return 2; }
if (q == 5 && r == 4) { return 2; }
if (q == 6 && r == 3) { return 2; }
if (q == 7 && r == 3) { return 2; }
if (q == 8 && r == 2) { return 2; }
if (q == 0 && r == 7) { return 2; }
if (q == 1 && r == 7) { return 2; }
if (q == 2 && r == 6) { return 2; }
if (q == 3 && r == 6) { return 2; }
if (q == 4 && r == 5) { return 2; }
if (q == 5 && r == 5) { return 2; }
if (q == 6 && r == 4) { return 2; }
if (q == 7 && r == 4) { return 2; }
if (q == 8 && r == 3) { return 2; }
return 0;
}
return 9;
}
fn hex_los(a: vec2i, b: vec2i, room: i32) -> f32 {
let pa = hex_px(a); let pb = hex_px(b);
let n = hex_dist(a, b);
if (n <= 1) { return 1.0; }
let steps = n * 2;
for (var i = 1; i < 15; i++) {
if (i >= steps) { break; }
let h = px_hex(mix(pa, pb, f32(i) / f32(steps)));
if (all(h == a) || all(h == b)) { continue; }
let l = esp_land(room, h.x, h.y);
if (l == 1 || l == 2 || l == 9) { return 0.0; }
}
return 1.0;
}
// screen → board plan at height h (the iso inverse)
fn iso_plan(p: vec2f, h: f32) -> vec2f { return vec2f(p.x, (p.y - IYOFF + h) / ISY); }
// board plan → screen (for placing characters/props)
fn iso_scr(g: vec2f, h: f32) -> vec2f { return vec2f(g.x, g.y * ISY + IYOFF - h); }
fn visual_esper(uv: vec2f, sdf: f32, color: vec4f, time: f32, params: vec4f, behind: vec4f) -> vec4f {
// camera: zoomed in, eased onto the esper (hook publishes cam at uni 26,27)
let cam = vec2f(uni(26), uni(27));
// FULL-LENGTH FILL: the playable rect is 288x512 (origin-anchored), so the
// visible window is field x 0..288 - design center (144, 256). uv is FIELD-
// local (-1..1 over the 512 field): (uv+1)*256 = field px = grid coords.
let p = cam + ((uv + vec2f(1.0)) * 256.0 - vec2f(144.0, 256.0)) / ZOOM;
let t = time;
let room = i32(uni(0) + 0.5);
// ── ground plane ──
let g = iso_plan(p, 0.0);
let gh = px_hex(g);
let gc = hex_px(gh);
let glp = g - gc;
let gland = esp_land(room, gh.x, gh.y);
let grad = length(glp);
var ga = vec3f(0.035, 0.060, 0.045); var gb = vec3f(0.062, 0.102, 0.066);
if (room == 2) { ga = vec3f(0.024, 0.028, 0.040); gb = vec3f(0.046, 0.052, 0.068); }
if (room == 3) { ga = vec3f(0.052, 0.044, 0.038); gb = vec3f(0.088, 0.075, 0.060); }
var col = mix(ga, gb, fbm3(g * 0.05) * 0.7 + vnoise(g * 0.2) * 0.3);
// beyond the level lies nothing — the board ends, the dark begins
if (gland == 9) {
col = vec3f(0.010, 0.014, 0.022) * (0.6 + 0.4 * fbm3(g * 0.012 + vec2f(t * 0.01, 0.0)));
} else {
// lattice: hex borders + kite spokes, faint
let spoke = abs(fract(atan2(glp.y, glp.x) / 1.0471976 + 0.5) - 0.5);
col += vec3f(0.010, 0.016, 0.018) * smoothstep(0.05, 0.0, spoke) * step(grad, ES * 0.82);
var second = 1.0e9;
for (var d = 0; d < 6; d++) {
var nb = vec2i(0, 0);
if (d == 0) { nb = vec2i(1, 0); } else if (d == 1) { nb = vec2i(1, -1); }
else if (d == 2) { nb = vec2i(0, -1); } else if (d == 3) { nb = vec2i(-1, 0); }
else if (d == 4) { nb = vec2i(-1, 1); } else { nb = vec2i(0, 1); }
second = min(second, length(g - hex_px(gh + nb)));
}
col += vec3f(0.018, 0.028, 0.030) * smoothstep(3.5, 0.0, second - grad);
}
if (gland == 3) {
let wv = vnoise(g * 0.08 + vec2f(t * 0.25, t * 0.18));
col = mix(vec3f(0.045, 0.10, 0.15), vec3f(0.085, 0.18, 0.23), wv);
col += vec3f(0.10) * smoothstep(0.75, 0.95, vnoise(g * 0.12 + vec2f(-t * 0.2, t * 0.1)));
}
// ── vision cones on the ground (kite-quantized, hex LOS) ──
if (gland == 0 || gland == 1) {
if (room == 1) {
let st = uni(15);
if (st > -0.5 && st < 2.5) {
let eo = vec2f(uni(12), uni(13));
// the cone APEX is the enemy's BODY — kites are the resolution, not the origin
let kang = (floor((atan2(glp.y, glp.x) + 3.14159265) / 1.04719755) + 0.5) * 1.04719755 - 3.14159265;
let kc = gc + vec2f(cos(kang), sin(kang)) * (ES * 0.55);
let dd = kc - eo;
let rr = length(dd);
if (rr > ES * 0.45 && rr < 155.88) {
var diff = atan2(dd.y, dd.x) - uni(14);
diff = abs(atan2(sin(diff), cos(diff)));
if (diff < 0.70 && hex_los(px_hex(eo), gh, room) > 0.5) {
var cc = vec3f(0.17, 0.22, 0.25);
if (st > 0.5) { cc = vec3f(0.38, 0.16, 0.50); }
if (st > 1.5) { cc = vec3f(0.65, 0.07, 0.09); }
col += cc * (1.0 - rr / 155.88) * (0.55 + 0.20 * sin(time * 3.0 + f32(0)));
}
}
}
}
if (room == 2) {
let st = uni(15);
if (st > -0.5 && st < 2.5) {
let eo = vec2f(uni(12), uni(13));
// the cone APEX is the enemy's BODY — kites are the resolution, not the origin
let kang = (floor((atan2(glp.y, glp.x) + 3.14159265) / 1.04719755) + 0.5) * 1.04719755 - 3.14159265;
let kc = gc + vec2f(cos(kang), sin(kang)) * (ES * 0.55);
let dd = kc - eo;
let rr = length(dd);
if (rr > ES * 0.45 && rr < 155.88) {
var diff = atan2(dd.y, dd.x) - uni(14);
diff = abs(atan2(sin(diff), cos(diff)));
if (diff < 0.70 && hex_los(px_hex(eo), gh, room) > 0.5) {
var cc = vec3f(0.17, 0.22, 0.25);
if (st > 0.5) { cc = vec3f(0.38, 0.16, 0.50); }
if (st > 1.5) { cc = vec3f(0.65, 0.07, 0.09); }
col += cc * (1.0 - rr / 155.88) * (0.55 + 0.20 * sin(time * 3.0 + f32(0)));
}
}
}
}
if (room == 2) {
let st = uni(19);
if (st > -0.5 && st < 2.5) {
let eo = vec2f(uni(16), uni(17));
// the cone APEX is the enemy's BODY — kites are the resolution, not the origin
let kang = (floor((atan2(glp.y, glp.x) + 3.14159265) / 1.04719755) + 0.5) * 1.04719755 - 3.14159265;
let kc = gc + vec2f(cos(kang), sin(kang)) * (ES * 0.55);
let dd = kc - eo;
let rr = length(dd);
if (rr > ES * 0.45 && rr < 207.85) {
var diff = atan2(dd.y, dd.x) - uni(18);
diff = abs(atan2(sin(diff), cos(diff)));
if (diff < 0.55 && hex_los(px_hex(eo), gh, room) > 0.5) {
var cc = vec3f(0.17, 0.22, 0.25);
if (st > 0.5) { cc = vec3f(0.38, 0.16, 0.50); }
if (st > 1.5) { cc = vec3f(0.65, 0.07, 0.09); }
col += cc * (1.0 - rr / 207.85) * (0.55 + 0.20 * sin(time * 3.0 + f32(1)));
}
}
}
}
if (room == 3) {
let st = uni(15);
if (st > -0.5 && st < 2.5) {
let eo = vec2f(uni(12), uni(13));
// the cone APEX is the enemy's BODY — kites are the resolution, not the origin
let kang = (floor((atan2(glp.y, glp.x) + 3.14159265) / 1.04719755) + 0.5) * 1.04719755 - 3.14159265;
let kc = gc + vec2f(cos(kang), sin(kang)) * (ES * 0.55);
let dd = kc - eo;
let rr = length(dd);
if (rr > ES * 0.45 && rr < 155.88) {
var diff = atan2(dd.y, dd.x) - uni(14);
diff = abs(atan2(sin(diff), cos(diff)));
if (diff < 0.70 && hex_los(px_hex(eo), gh, room) > 0.5) {
var cc = vec3f(0.17, 0.22, 0.25);
if (st > 0.5) { cc = vec3f(0.38, 0.16, 0.50); }
if (st > 1.5) { cc = vec3f(0.65, 0.07, 0.09); }
col += cc * (1.0 - rr / 155.88) * (0.55 + 0.20 * sin(time * 3.0 + f32(0)));
}
}
}
}
if (room == 3) {
let st = uni(19);
if (st > -0.5 && st < 2.5) {
let eo = vec2f(uni(16), uni(17));
// the cone APEX is the enemy's BODY — kites are the resolution, not the origin
let kang = (floor((atan2(glp.y, glp.x) + 3.14159265) / 1.04719755) + 0.5) * 1.04719755 - 3.14159265;
let kc = gc + vec2f(cos(kang), sin(kang)) * (ES * 0.55);
let dd = kc - eo;
let rr = length(dd);
if (rr > ES * 0.45 && rr < 155.88) {
var diff = atan2(dd.y, dd.x) - uni(18);
diff = abs(atan2(sin(diff), cos(diff)));
if (diff < 0.70 && hex_los(px_hex(eo), gh, room) > 0.5) {
var cc = vec3f(0.17, 0.22, 0.25);
if (st > 0.5) { cc = vec3f(0.38, 0.16, 0.50); }
if (st > 1.5) { cc = vec3f(0.65, 0.07, 0.09); }
col += cc * (1.0 - rr / 155.88) * (0.55 + 0.20 * sin(time * 3.0 + f32(1)));
}
}
}
}
}
// ── movement nodes, typed: centers (large) · edge midpoints (mid) · corners (small) ──
if (gland == 0 || gland == 1) {
let ndC = grad;
var ndM = 1.0e9;
var ndV = 1.0e9;
for (var d = 0; d < 6; d++) {
let a0 = 1.0471976 * f32(d);
ndV = min(ndV, length(glp - vec2f(cos(a0), sin(a0)) * ES));
let a1 = a0 + 0.5235988;
ndM = min(ndM, length(glp - vec2f(cos(a1), sin(a1)) * (ES * 0.866)));
}
col += vec3f(0.10, 0.17, 0.17) * smoothstep(2.8, 1.3, ndC);
col += vec3f(0.060, 0.105, 0.105) * smoothstep(2.1, 0.9, ndM);
col += vec3f(0.042, 0.072, 0.072) * smoothstep(1.7, 0.7, ndV);
// cursor hover: nearby nodes glow so a click has a visible target
let mw = vec2f(uni(36), uni(37));
let ndAll = min(ndC, min(ndM, ndV));
if (length(g - mw) < 10.0) {
col += vec3f(0.14, 0.34, 0.34) * smoothstep(2.8, 1.0, ndAll);
}
// reachable ring: nodes within one graph step of the esper
let hero = vec2f(uni(1), uni(2));
let reachR = select(ES * 1.15, ES * 2.4, i32(uni(42) + 0.5) == 2);
if (ndAll < 2.0 && length(g - hero) < reachR && length(g - hero) > 3.0) {
col += vec3f(0.10, 0.30, 0.30) * (0.65 + 0.35 * sin(t * 3.5));
}
}
// ── armed effect: the attack zone telegraph (kite-triangle shading, like the game) ──
if (uni(38) > 0.0 && (gland == 0 || gland == 1)) {
let hz = px_hex(vec2f(uni(1), uni(2)));
let hzd = hex_dist(hz, gh);
var zc = vec3f(2.4, 0.5, 0.3);
if (uni(25) > 0.5) { zc = vec3f(0.5, 1.1, 2.2); }
if (hzd >= 1 && hzd <= 3 && hex_los(hz, gh, room) > 0.5) {
let kang2 = (floor((atan2(glp.y, glp.x) + 3.14159265) / 1.04719755) + 0.5) * 1.04719755 - 3.14159265;
let kc2 = gc + vec2f(cos(kang2), sin(kang2)) * (ES * 0.55);
let kt = 0.5 + 0.5 * sin(t * 5.0 + length(kc2 - vec2f(uni(1), uni(2))) * 0.06);
col += zc * 0.085 * uni(38) * (0.6 + 0.4 * kt);
let spk2 = abs(fract(atan2(glp.y, glp.x) / 1.0471976 + 0.5) - 0.5);
col += zc * 0.05 * uni(38) * smoothstep(0.04, 0.0, spk2);
}
}
// ── effect burst zone + animated pixel sparks at the target ──
if (uni(28) > 0.01) {
let tgt = vec2f(uni(31), uni(32));
if (hex_dist(px_hex(tgt), gh) <= 1 && (gland == 0 || gland == 1)) {
col += vec3f(uni(33), uni(34), uni(35)) * 0.22 * uni(28);
}
let bs = iso_scr(tgt, 10.0);
for (var si = 0; si < 12; si++) {
let hsh = hash22(vec2f(f32(si) * 3.7, uni(39)));
let sa = hsh.x * 6.2831853;
let sp2 = bs + vec2f(cos(sa), sin(sa) * 0.6) * (1.0 - uni(28)) * (16.0 + hsh.y * 26.0);
if (length(floor(p / 2.0) * 2.0 - floor(sp2 / 2.0) * 2.0) < 1.4) {
col += vec3f(uni(33), uni(34), uni(35)) * uni(28) * 2.5;
}
}
}
// ── the effect ray + burst (visual controllers) ──
if (uni(28) > 0.01) {
let a = iso_scr(vec2f(uni(29), uni(30)), 12.0);
let b = iso_scr(vec2f(uni(31), uni(32)), 12.0);
let ab = b - a;
let tt = clamp(dot(p - a, ab) / max(dot(ab, ab), 0.001), 0.0, 1.0);
let dl = length(p - (a + ab * tt));
let rc = vec3f(uni(33), uni(34), uni(35));
col += rc * uni(28) * exp(-dl * 0.55);
col += rc * uni(28) * 1.6 * exp(-length(p - b) * 0.10); // burst at the target
}
// ── quest props on the ground ──
if (room == 2 && uni(10) < 0.5) {
let kp = p - iso_scr(vec2f(385.00, 143.94), 8.0);
let kd = sdBox(rotate(kp, 0.785), vec2f(5.0, 5.0));
col += vec3f(2.2, 1.6, 0.4) * (0.7 + 0.3 * sin(t * 4.0)) * exp(-max(kd, 0.0) * 0.22);
}
if (room == 3) {
let cp = p - iso_scr(vec2f(250.00, 117.96), 8.0);
let cd = sdBox(cp, vec2f(12.0, 8.0));
if (cd < 0.0) {
col = mix(vec3f(0.26, 0.15, 0.06), vec3f(0.42, 0.26, 0.10), step(cp.y, -2.0));
if (uni(11) > 0.5) { col += vec3f(1.8, 1.3, 0.4); }
}
col += vec3f(3.0, 2.3, 1.0) * uni(8) * exp(-length(cp) * 0.012);
}
if (room == 1) {
let dp = length(p - iso_scr(vec2f(430.00, 221.88), 0.0));
var dc = vec3f(0.2, 0.9, 1.0);
col += dc * 0.45 * (0.5 + 0.3 * sin(t * 2.0)) * exp(-max(dp - ES * 0.35, 0.0) * 0.16);
}
if (room == 1) {
let dp = length(p - iso_scr(vec2f(205.00, 143.94), 0.0));
var dc = vec3f(0.2, 0.9, 1.0);
if (uni(10) < 0.5) { dc = vec3f(1.0, 0.15, 0.1); }
col += dc * 0.45 * (0.5 + 0.3 * sin(t * 2.0)) * exp(-max(dp - ES * 0.35, 0.0) * 0.16);
}
if (room == 2) {
let dp = length(p - iso_scr(vec2f(70.00, 221.88), 0.0));
var dc = vec3f(0.2, 0.9, 1.0);
col += dc * 0.45 * (0.5 + 0.3 * sin(t * 2.0)) * exp(-max(dp - ES * 0.35, 0.0) * 0.16);
}
if (room == 3) {
let dp = length(p - iso_scr(vec2f(250.00, 377.77), 0.0));
var dc = vec3f(0.2, 0.9, 1.0);
col += dc * 0.45 * (0.5 + 0.3 * sin(t * 2.0)) * exp(-max(dp - ES * 0.35, 0.0) * 0.16);
}
// ── iso extrusion: stone prisms and forest canopies rise off the board ──
// sides first (sampled down the column), then tops — later writes win
for (var hs = 0; hs < 4; hs++) {
let hh = f32(hs) * 5.50 + 3.0;
let gs = iso_plan(p, hh);
let sh = px_hex(gs);
if (esp_land(room, sh.x, sh.y) == 2) {
let rk = fbm3(gs * 0.09 + f32(sh.x + sh.y));
col = mix(vec3f(0.055, 0.055, 0.065), vec3f(0.115, 0.11, 0.105), rk) * (0.55 + 0.45 * hh / 22.00);
}
}
{
let gt = iso_plan(p, 22.00);
let th = px_hex(gt);
if (esp_land(room, th.x, th.y) == 2) {
let tc = hex_px(th);
let trad = length(gt - tc);
let rk = fbm3(gt * 0.08);
col = mix(vec3f(0.10, 0.10, 0.115), vec3f(0.185, 0.175, 0.165), rk);
col *= 0.78 + 0.45 * smoothstep(ES, 0.0, trad); // domed top, lit
col += vec3f(0.03, 0.035, 0.045) * smoothstep(3.0, 0.0, abs(trad - ES * 0.80));
}
}
{
// trunks
let gk = iso_plan(p, 6.0);
let kh = px_hex(gk);
if (esp_land(room, kh.x, kh.y) == 1 && length(gk - hex_px(kh)) < 3.5) {
col = vec3f(0.09, 0.06, 0.035);
}
// canopies
let gf = iso_plan(p, 32.00);
let fh = px_hex(gf);
if (esp_land(room, fh.x, fh.y) == 1) {
let fc2 = hex_px(fh);
let frad = length(gf - fc2);
if (frad < ES * 0.92) {
let leaf = fbm3(gf * 0.11 + vec2f(f32(fh.x) * 3.1, f32(fh.y) * 5.7) + vec2f(sin(t * 0.7 + f32(fh.y)) * 0.8, 0.0));
var fc = mix(vec3f(0.020, 0.060, 0.028), vec3f(0.080, 0.155, 0.062), leaf);
if (room == 2) { fc = mix(vec3f(0.032, 0.048, 0.068), vec3f(0.080, 0.105, 0.135), leaf); }
fc *= 0.60 + 0.55 * smoothstep(ES * 0.9, ES * 0.1, length(gf - fc2 + vec2f(6.0, 6.0))); // NW crown light
col = fc;
}
}
}
// ── characters: billboards standing on the board ──
{
let st = uni(15);
if (st > -0.5 && st < 2.5) {
let ep = p - iso_scr(vec2f(uni(12), uni(13)), 9.0);
let ed = length(ep) - 7.5;
if (ed < 2.0) {
var bc = vec3f(0.30, 0.34, 0.38);
if (st > 0.5) { bc = vec3f(0.55, 0.28, 0.78); }
if (st > 1.5) { bc = vec3f(1.7, 0.14, 0.18); }
if (ed < 0.0) {
col = mix(bc, bc * 0.42, smoothstep(-7.5, 0.0, ed));
let fd = vec2f(cos(uni(14)), sin(uni(14)) * ISY);
if (length(ep - normalize(fd) * 4.8) < 2.4) { col = vec3f(1.0, 0.95, 0.85); }
} else if (st > 0.5) { col = mix(col, bc, 0.7 * (0.5 + 0.5 * sin(t * 8.0))); }
}
// ground contact shadow
let sp = length(p - iso_scr(vec2f(uni(12), uni(13)), 0.0));
col *= 1.0 - 0.35 * smoothstep(7.0, 2.0, sp);
}
}
{
let st = uni(19);
if (st > -0.5 && st < 2.5) {
let ep = p - iso_scr(vec2f(uni(16), uni(17)), 9.0);
let ed = length(ep) - 7.5;
if (ed < 2.0) {
var bc = vec3f(0.30, 0.34, 0.38);
if (st > 0.5) { bc = vec3f(0.55, 0.28, 0.78); }
if (st > 1.5) { bc = vec3f(1.7, 0.14, 0.18); }
if (ed < 0.0) {
col = mix(bc, bc * 0.42, smoothstep(-7.5, 0.0, ed));
let fd = vec2f(cos(uni(18)), sin(uni(18)) * ISY);
if (length(ep - normalize(fd) * 4.8) < 2.4) { col = vec3f(1.0, 0.95, 0.85); }
} else if (st > 0.5) { col = mix(col, bc, 0.7 * (0.5 + 0.5 * sin(t * 8.0))); }
}
// ground contact shadow
let sp = length(p - iso_scr(vec2f(uni(16), uni(17)), 0.0));
col *= 1.0 - 0.35 * smoothstep(7.0, 2.0, sp);
}
}
{
let st = uni(23);
if (st > -0.5 && st < 2.5) {
let ep = p - iso_scr(vec2f(uni(20), uni(21)), 9.0);
let ed = length(ep) - 7.5;
if (ed < 2.0) {
var bc = vec3f(0.30, 0.34, 0.38);
if (st > 0.5) { bc = vec3f(0.55, 0.28, 0.78); }
if (st > 1.5) { bc = vec3f(1.7, 0.14, 0.18); }
if (ed < 0.0) {
col = mix(bc, bc * 0.42, smoothstep(-7.5, 0.0, ed));
let fd = vec2f(cos(uni(22)), sin(uni(22)) * ISY);
if (length(ep - normalize(fd) * 4.8) < 2.4) { col = vec3f(1.0, 0.95, 0.85); }
} else if (st > 0.5) { col = mix(col, bc, 0.7 * (0.5 + 0.5 * sin(t * 8.0))); }
}
// ground contact shadow
let sp = length(p - iso_scr(vec2f(uni(20), uni(21)), 0.0));
col *= 1.0 - 0.35 * smoothstep(7.0, 2.0, sp);
}
}
{
let hp = p - iso_scr(vec2f(uni(1), uni(2)), 9.0);
let hd = length(hp) - 7.5;
let sp = length(p - iso_scr(vec2f(uni(1), uni(2)), 0.0));
col *= 1.0 - 0.35 * smoothstep(7.0, 2.0, sp);
if (hd < 0.0) {
var hc = mix(vec3f(0.07, 0.26, 0.28), vec3f(0.21, 0.58, 0.60), smoothstep(-7.5, -1.6, hd));
if (hd > -1.6) { hc *= 0.4; }
if (uni(5) > 0.0) { hc += vec3f(0.15, 0.25, 0.7) * (0.5 + 0.5 * sin(t * 12.0)); }
let dir = vec2f(cos(uni(3)), sin(uni(3)) * ISY);
if (length(hp - normalize(dir) * 4.8) < 2.3) { hc = vec3f(0.95, 0.9, 0.75); }
col = mix(hc, col, max(uni(4) * 0.55, min(uni(5), 1.0) * 0.6));
}
}
// vignettes: alert pulse, capture flash
var anyAlert = uni(7) > 2.5;
if (uni(15) > 1.5 && uni(15) < 2.5) { anyAlert = true; }
if (uni(19) > 1.5 && uni(19) < 2.5) { anyAlert = true; }
if (uni(23) > 1.5 && uni(23) < 2.5) { anyAlert = true; }
if (anyAlert) { col += vec3f(0.5, 0.02, 0.03) * (0.5 + 0.5 * sin(t * 6.0)) * smoothstep(0.55, 1.0, length(uv)); }
col += vec3f(1.2, 0.0, 0.0) * uni(24) * smoothstep(0.45, 1.0, length(uv));
// ── the hand: card UI in SCREEN space (immune to the camera) ──
let ps = (uv + vec2f(1.0)) * 256.0;
for (var ci = 0; ci < 4; ci++) {
var cc = vec3f(2.4, 0.5, 0.3); // fire
if (ci == 1) { cc = vec3f(0.5, 1.1, 2.2); } // psychic
if (ci == 2) { cc = vec3f(0.55, 0.35, 0.95); } // shadow
if (ci == 3) { cc = vec3f(0.45, 0.42, 0.50); } // silent kill: black card, grey edge
let armed = i32(uni(42) + 0.5) == ci;
var cy = 474.0;
if (armed) { cy = 466.0; }
let cp = ps - vec2f(144.0 + (f32(ci) - 1.5) * 56.0, cy);
let cd = sdRoundedBox(cp, vec2f(26.0, 17.0), 4.0);
if (cd < 0.0) {
col = vec3f(0.050, 0.058, 0.078);
if (ci == 3) { col = vec3f(0.012, 0.012, 0.018); } // black as the deed
if (cd > -2.0) { col = cc * select(0.35, 0.8, armed); } // border
// sigils
if (ci == 0 && sdBox(rotate(cp + vec2f(0.0, 3.0), 0.785), vec2f(4.5, 4.5)) < 0.0) { col = cc * 0.9; }
if (ci == 1 && abs(length(cp + vec2f(0.0, 3.0)) - 5.0) < 1.5) { col = cc * 0.9; }
if (ci == 2 && length(cp + vec2f(1.0, 3.0)) < 5.5 && length(cp + vec2f(-2.0, 4.0)) > 4.5) { col = cc * 0.9; }
if (ci == 3) {
if (sdBox(cp + vec2f(0.0, 2.0), vec2f(1.1, 6.0)) < 0.0) { col = cc * 1.1; } // blade
if (sdBox(cp + vec2f(0.0, 6.0), vec2f(4.0, 1.1)) < 0.0) { col = cc * 1.1; } // guard
}
// charge pips
if (ci == 3) {
// infinite uses: the lemniscate
if (abs(length(cp - vec2f(-3.5, -10.0)) - 2.6) < 1.1) { col = cc * 1.2; }
if (abs(length(cp - vec2f(3.5, -10.0)) - 2.6) < 1.1) { col = cc * 1.2; }
} else {
var pips = uni(40);
if (ci == 1) { pips = uni(41); }
if (ci == 2) { pips = uni(6); }
for (var pi = 0; pi < 3; pi++) {
if (f32(pi) < pips && length(cp - vec2f((f32(pi) - 1.0) * 9.0, -10.0)) < 2.4) { col = cc * 1.2; }
}
}
}
if (armed) { col += cc * 0.20 * exp(-max(cd, 0.0) * 0.18); } // armed glow
}
return vec4f(col, 1.0);
}— STEP HOOKS (JAVASCRIPT) —
hook · eq_core
by claude-code
try {
const wd = sim.worldData
const ROOMS = {"1":{"land":[{"q":0,"r":0,"l":2},{"q":1,"r":0,"l":2},{"q":2,"r":-1,"l":2},{"q":3,"r":-1,"l":2},{"q":4,"r":-2,"l":2},{"q":5,"r":-2,"l":2},{"q":6,"r":-3,"l":2},{"q":7,"r":-3,"l":2},{"q":8,"r":-4,"l":2},{"q":0,"r":1,"l":2},{"q":1,"r":1,"l":0},{"q":2,"r":0,"l":0},{"q":3,"r":0,"l":0},{"q":4,"r":-1,"l":0},{"q":5,"r":-1,"l":0},{"q":6,"r":-2,"l":1},{"q":7,"r":-2,"l":0},{"q":8,"r":-3,"l":2},{"q":0,"r":2,"l":2},{"q":1,"r":2,"l":0},{"q":2,"r":1,"l":1},{"q":3,"r":1,"l":0},{"q":4,"r":0,"l":0},{"q":5,"r":0,"l":0},{"q":6,"r":-1,"l":1},{"q":7,"r":-1,"l":0},{"q":8,"r":-2,"l":2},{"q":0,"r":3,"l":2},{"q":1,"r":3,"l":0},{"q":2,"r":2,"l":0},{"q":3,"r":2,"l":0},{"q":4,"r":1,"l":1},{"q":5,"r":1,"l":0},{"q":6,"r":0,"l":0},{"q":7,"r":0,"l":0},{"q":8,"r":-1,"l":0},{"q":0,"r":4,"l":2},{"q":1,"r":4,"l":0},{"q":2,"r":3,"l":3},{"q":3,"r":3,"l":3},{"q":4,"r":2,"l":0},{"q":5,"r":2,"l":1},{"q":6,"r":1,"l":0},{"q":7,"r":1,"l":0},{"q":8,"r":0,"l":2},{"q":0,"r":5,"l":2},{"q":1,"r":5,"l":3},{"q":2,"r":4,"l":3},{"q":3,"r":4,"l":3},{"q":4,"r":3,"l":0},{"q":5,"r":3,"l":0},{"q":6,"r":2,"l":0},{"q":7,"r":2,"l":0},{"q":8,"r":1,"l":2},{"q":0,"r":6,"l":2},{"q":1,"r":6,"l":0},{"q":2,"r":5,"l":0},{"q":3,"r":5,"l":0},{"q":4,"r":4,"l":0},{"q":5,"r":4,"l":0},{"q":6,"r":3,"l":0},{"q":7,"r":3,"l":0},{"q":8,"r":2,"l":2},{"q":0,"r":7,"l":2},{"q":1,"r":7,"l":2},{"q":2,"r":6,"l":2},{"q":3,"r":6,"l":2},{"q":4,"r":5,"l":2},{"q":5,"r":5,"l":2},{"q":6,"r":4,"l":2},{"q":7,"r":4,"l":2},{"q":8,"r":3,"l":2}],"enemies":[{"kind":"guard","path":[[1,3],[7,3]],"range":3,"half":0.7,"speed":30,"hp":1}],"name":"Glade","nodes":[{"x":115,"y":143.9,"a":[1,2,4,3,6,5,8,7,10,9,12,11],"c":0},{"x":137.5,"y":156.9,"a":[0,2,3,83],"c":1},{"x":145,"y":143.9,"a":[1,0,12,17,13,83],"c":1},{"x":130,"y":169.9,"a":[1,4,0,82,73,83],"c":1},{"x":115,"y":169.9,"a":[0,3,5,73],"c":0},{"x":100,"y":169.9,"a":[4,6,0,81,73],"c":0},{"x":92.5,"y":156.9,"a":[0,5,7],"c":0},{"x":85,"y":143.9,"a":[6,8,0],"c":0},{"x":92.5,"y":131,"a":[0,7,9],"c":0},{"x":100,"y":118,"a":[8,10,0],"c":0},{"x":115,"y":118,"a":[0,9,11],"c":0},{"x":130,"y":118,"a":[10,12,0,18,13],"c":0},{"x":137.5,"y":131,"a":[0,11,2,13],"c":0},{"x":160,"y":118,"a":[14,15,17,16,12,2,18,11,20,19,22,21],"c":0},{"x":182.5,"y":131,"a":[13,15,16,23],"c":0},{"x":190,"y":118,"a":[14,13,22,30,23],"c":0},{"x":175,"y":143.9,"a":[14,17,13,29,23,83],"c":1},{"x":160,"y":143.9,"a":[13,16,2,83],"c":1},{"x":137.5,"y":105,"a":[13,11,19],"c":0},{"x":145,"y":92,"a":[18,20,13],"c":0},{"x":160,"y":92,"a":[13,19,21],"c":0},{"x":175,"y":92,"a":[20,22,13],"c":0},{"x":182.5,"y":105,"a":[13,21,15],"c":0},{"x":205,"y":143.9,"a":[24,25,27,26,29,28,14,16,30,15,32,31],"c":0},{"x":227.5,"y":156.9,"a":[23,25,26,95],"c":0},{"x":235,"y":143.9,"a":[24,23,32,37,33,95],"c":0},{"x":220,"y":169.9,"a":[24,27,23,94,87,95],"c":0},{"x":205,"y":169.9,"a":[23,26,28,87],"c":0},{"x":190,"y":169.9,"a":[27,29,23,84,83,87],"c":1},{"x":182.5,"y":156.9,"a":[23,28,16,83],"c":1},{"x":205,"y":118,"a":[23,15,31],"c":0},{"x":220,"y":118,"a":[30,32,23,38,33],"c":0},{"x":227.5,"y":131,"a":[23,31,25,33],"c":0},{"x":250,"y":118,"a":[34,35,37,36,32,25,38,31,40,39,42,41],"c":0},{"x":272.5,"y":131,"a":[33,35,36,43],"c":0},{"x":280,"y":118,"a":[34,33,42,50,43],"c":0},{"x":265,"y":143.9,"a":[34,37,33,49,43,95],"c":0},{"x":250,"y":143.9,"a":[33,36,25,95],"c":0},{"x":227.5,"y":105,"a":[33,31,39],"c":0},{"x":235,"y":92,"a":[38,40,33],"c":0},{"x":250,"y":92,"a":[33,39,41],"c":0},{"x":265,"y":92,"a":[40,42,33],"c":0},{"x":272.5,"y":105,"a":[33,41,35],"c":0},{"x":295,"y":143.9,"a":[44,45,47,46,49,48,34,36,50,35,52,51],"c":0},{"x":317.5,"y":156.9,"a":[43,45,46,107],"c":1},{"x":325,"y":143.9,"a":[44,43,52,57,53,107],"c":1},{"x":310,"y":169.9,"a":[44,47,43,106,99,107],"c":1},{"x":295,"y":169.9,"a":[43,46,48,99],"c":0},{"x":280,"y":169.9,"a":[47,49,43,96,95,99],"c":0},{"x":272.5,"y":156.9,"a":[43,48,36,95],"c":0},{"x":295,"y":118,"a":[43,35,51],"c":0},{"x":310,"y":118,"a":[50,52,43,58,53],"c":1},{"x":317.5,"y":131,"a":[43,51,45,53],"c":1},{"x":340,"y":118,"a":[54,55,57,56,52,45,58,51,60,59,62,61],"c":1},{"x":362.5,"y":131,"a":[53,55,56,63],"c":1},{"x":370,"y":118,"a":[54,53,62,70,63],"c":1},{"x":355,"y":143.9,"a":[54,57,53,69,63,107],"c":1},{"x":340,"y":143.9,"a":[53,56,45,107],"c":1},{"x":317.5,"y":105,"a":[53,51,59],"c":1},{"x":325,"y":92,"a":[58,60,53],"c":1},{"x":340,"y":92,"a":[53,59,61],"c":1},{"x":355,"y":92,"a":[60,62,53],"c":1},{"x":362.5,"y":105,"a":[53,61,55],"c":1},{"x":385,"y":143.9,"a":[64,65,67,66,69,68,54,56,70,55,72,71],"c":0},{"x":407.5,"y":156.9,"a":[63,65,66],"c":0},{"x":415,"y":143.9,"a":[64,63,72],"c":0},{"x":400,"y":169.9,"a":[64,67,63,118,111],"c":0},{"x":385,"y":169.9,"a":[63,66,68,111],"c":0},{"x":370,"y":169.9,"a":[67,69,63,108,107,111],"c":1},{"x":362.5,"y":156.9,"a":[63,68,56,107],"c":1},{"x":385,"y":118,"a":[63,55,71],"c":0},{"x":400,"y":118,"a":[70,72,63],"c":0},{"x":407.5,"y":131,"a":[63,71,65],"c":0},{"x":115,"y":195.9,"a":[74,75,77,76,79,78,81,80,4,5,82,3],"c":0},{"x":137.5,"y":208.9,"a":[73,75,76,129],"c":0},{"x":145,"y":195.9,"a":[74,73,82,86,83,129],"c":1},{"x":130,"y":221.9,"a":[74,77,73,128,119,129],"c":0},{"x":115,"y":221.9,"a":[73,76,78,119],"c":0},{"x":100,"y":221.9,"a":[77,79,73,127,119],"c":0},{"x":92.5,"y":208.9,"a":[73,78,80],"c":0},{"x":85,"y":195.9,"a":[79,81,73],"c":0},{"x":92.5,"y":182.9,"a":[73,80,5],"c":0},{"x":137.5,"y":182.9,"a":[73,3,75,83],"c":1},{"x":160,"y":169.9,"a":[84,28,86,85,82,75,1,3,17,2,29,16],"c":1},{"x":182.5,"y":182.9,"a":[83,28,85,87],"c":1},{"x":175,"y":195.9,"a":[84,86,83,93,87,129],"c":1},{"x":160,"y":195.9,"a":[83,85,75,129],"c":1},{"x":205,"y":195.9,"a":[88,89,91,90,93,92,84,85,27,28,94,26],"c":0},{"x":227.5,"y":208.9,"a":[87,89,90,141],"c":1},{"x":235,"y":195.9,"a":[88,87,94,98,95,141],"c":1},{"x":220,"y":221.9,"a":[88,91,87,140,133,141],"c":1},{"x":205,"y":221.9,"a":[87,90,92,133],"c":0},{"x":190,"y":221.9,"a":[91,93,87,130,129,133],"c":0},{"x":182.5,"y":208.9,"a":[87,92,85,129],"c":0},{"x":227.5,"y":182.9,"a":[87,26,89,95],"c":0},{"x":250,"y":169.9,"a":[96,48,98,97,94,89,24,26,37,25,49,36],"c":0},{"x":272.5,"y":182.9,"a":[95,48,97,99],"c":0},{"x":265,"y":195.9,"a":[96,98,95,105,99,141],"c":1},{"x":250,"y":195.9,"a":[95,97,89,141],"c":1},{"x":295,"y":195.9,"a":[100,101,103,102,105,104,96,97,47,48,106,46],"c":0},{"x":317.5,"y":208.9,"a":[99,101,102,153],"c":0},{"x":325,"y":195.9,"a":[100,99,106,110,107,153],"c":1},{"x":310,"y":221.9,"a":[100,103,99,152,145,153],"c":0},{"x":295,"y":221.9,"a":[99,102,104,145],"c":0},{"x":280,"y":221.9,"a":[103,105,99,142,141,145],"c":1},{"x":272.5,"y":208.9,"a":[99,104,97,141],"c":1},{"x":317.5,"y":182.9,"a":[99,46,101,107],"c":1},{"x":340,"y":169.9,"a":[108,68,110,109,106,101,44,46,57,45,69,56],"c":1},{"x":362.5,"y":182.9,"a":[107,68,109,111],"c":1},{"x":355,"y":195.9,"a":[108,110,107,117,111,153],"c":1},{"x":340,"y":195.9,"a":[107,109,101,153],"c":1},{"x":385,"y":195.9,"a":[112,113,115,114,117,116,108,109,67,68,118,66],"c":0},{"x":407.5,"y":208.9,"a":[111,113,114,165],"c":0},{"x":415,"y":195.9,"a":[112,111,118,170,165],"c":0},{"x":400,"y":221.9,"a":[112,115,111,164,157,165],"c":0},{"x":385,"y":221.9,"a":[111,114,116,157],"c":0},{"x":370,"y":221.9,"a":[115,117,111,154,153,157],"c":0},{"x":362.5,"y":208.9,"a":[111,116,109,153],"c":0},{"x":407.5,"y":182.9,"a":[111,66,113],"c":0},{"x":115,"y":247.9,"a":[120,121,123,122,125,124,127,126,77,78,128,76],"c":0},{"x":137.5,"y":260.9,"a":[119,121,122],"c":0},{"x":145,"y":247.9,"a":[120,119,128,132,129],"c":0},{"x":130,"y":273.8,"a":[120,123,119,182,173],"c":0},{"x":115,"y":273.8,"a":[119,122,124,173],"c":0},{"x":100,"y":273.8,"a":[123,125,119,181,173],"c":0},{"x":92.5,"y":260.9,"a":[119,124,126],"c":0},{"x":85,"y":247.9,"a":[125,127,119],"c":0},{"x":92.5,"y":234.9,"a":[119,126,78],"c":0},{"x":137.5,"y":234.9,"a":[119,76,121,129],"c":0},{"x":160,"y":221.9,"a":[130,92,132,131,128,121,74,76,86,75,93,85],"c":0},{"x":182.5,"y":234.9,"a":[129,92,131,133],"c":0},{"x":175,"y":247.9,"a":[130,132,129,139,133],"c":0},{"x":160,"y":247.9,"a":[129,131,121],"c":0},{"x":205,"y":247.9,"a":[134,135,137,136,139,138,130,131,91,92,140,90],"c":0},{"x":227.5,"y":260.9,"a":[133,135,136,183],"c":0},{"x":235,"y":247.9,"a":[134,133,140,144,141,183],"c":1},{"x":220,"y":273.8,"a":[134,137,133,188,183],"c":0},{"x":205,"y":273.8,"a":[133,136,138],"c":0},{"x":190,"y":273.8,"a":[137,139,133],"c":0},{"x":182.5,"y":260.9,"a":[133,138,131],"c":0},{"x":227.5,"y":234.9,"a":[133,90,135,141],"c":1},{"x":250,"y":221.9,"a":[142,104,144,143,140,135,88,90,98,89,105,97],"c":1},{"x":272.5,"y":234.9,"a":[141,104,143,145],"c":1},{"x":265,"y":247.9,"a":[142,144,141,151,145,183],"c":1},{"x":250,"y":247.9,"a":[141,143,135,183],"c":1},{"x":295,"y":247.9,"a":[146,147,149,148,151,150,142,143,103,104,152,102],"c":0},{"x":317.5,"y":260.9,"a":[145,147,148,197],"c":0},{"x":325,"y":247.9,"a":[146,145,152,156,153,197],"c":0},{"x":310,"y":273.8,"a":[146,149,145,196,189,197],"c":1},{"x":295,"y":273.8,"a":[145,148,150,189],"c":1},{"x":280,"y":273.8,"a":[149,151,145,184,183,189],"c":1},{"x":272.5,"y":260.9,"a":[145,150,143,183],"c":0},{"x":317.5,"y":234.9,"a":[145,102,147,153],"c":0},{"x":340,"y":221.9,"a":[154,116,156,155,152,147,100,102,110,101,117,109],"c":0},{"x":362.5,"y":234.9,"a":[153,116,155,157],"c":0},{"x":355,"y":247.9,"a":[154,156,153,163,157,197],"c":0},{"x":340,"y":247.9,"a":[153,155,147,197],"c":0},{"x":385,"y":247.9,"a":[158,159,161,160,163,162,154,155,115,116,164,114],"c":0},{"x":407.5,"y":260.9,"a":[157,159,160],"c":0},{"x":415,"y":247.9,"a":[158,157,164,169,165],"c":0},{"x":400,"y":273.8,"a":[158,161,157,208,201],"c":0},{"x":385,"y":273.8,"a":[157,160,162,201],"c":0},{"x":370,"y":273.8,"a":[161,163,157,198,197,201],"c":0},{"x":362.5,"y":260.9,"a":[157,162,155,197],"c":0},{"x":407.5,"y":234.9,"a":[157,114,159,165],"c":0},{"x":430,"y":221.9,"a":[166,167,169,168,164,159,112,114,170,113,172,171],"c":0},{"x":452.5,"y":234.9,"a":[165,167,168],"c":0},{"x":460,"y":221.9,"a":[166,165,172],"c":0},{"x":445,"y":247.9,"a":[166,169,165],"c":0},{"x":430,"y":247.9,"a":[165,168,159],"c":0},{"x":430,"y":195.9,"a":[165,113,171],"c":0},{"x":445,"y":195.9,"a":[170,172,165],"c":0},{"x":452.5,"y":208.9,"a":[165,171,167],"c":0},{"x":115,"y":299.8,"a":[174,175,177,176,179,178,181,180,123,124,182,122],"c":0},{"x":137.5,"y":312.8,"a":[173,175,176],"c":0},{"x":145,"y":299.8,"a":[174,173,182],"c":0},{"x":130,"y":325.8,"a":[174,177,173],"c":0},{"x":115,"y":325.8,"a":[173,176,178],"c":0},{"x":100,"y":325.8,"a":[177,179,173],"c":0},{"x":92.5,"y":312.8,"a":[173,178,180],"c":0},{"x":85,"y":299.8,"a":[179,181,173],"c":0},{"x":92.5,"y":286.8,"a":[173,180,124],"c":0},{"x":137.5,"y":286.8,"a":[173,122,175],"c":0},{"x":250,"y":273.8,"a":[184,150,186,185,188,187,134,136,144,135,151,143],"c":0},{"x":272.5,"y":286.8,"a":[183,150,185,189],"c":1},{"x":265,"y":299.8,"a":[184,186,183,195,189,209],"c":1},{"x":250,"y":299.8,"a":[183,185,187,209],"c":0},{"x":235,"y":299.8,"a":[186,188,183,216,209],"c":0},{"x":227.5,"y":286.8,"a":[183,187,136],"c":0},{"x":295,"y":299.8,"a":[190,191,193,192,195,194,184,185,149,150,196,148],"c":1},{"x":317.5,"y":312.8,"a":[189,191,192,225],"c":1},{"x":325,"y":299.8,"a":[190,189,196,200,197,225],"c":1},{"x":310,"y":325.8,"a":[190,193,189,224,217,225],"c":1},{"x":295,"y":325.8,"a":[189,192,194,217],"c":1},{"x":280,"y":325.8,"a":[193,195,189,210,209,217],"c":1},{"x":272.5,"y":312.8,"a":[189,194,185,209],"c":1},{"x":317.5,"y":286.8,"a":[189,148,191,197],"c":1},{"x":340,"y":273.8,"a":[198,162,200,199,196,191,146,148,156,147,163,155],"c":0},{"x":362.5,"y":286.8,"a":[197,162,199,201],"c":0},{"x":355,"y":299.8,"a":[198,200,197,207,201,225],"c":0},{"x":340,"y":299.8,"a":[197,199,191,225],"c":0},{"x":385,"y":299.8,"a":[202,203,205,204,207,206,198,199,161,162,208,160],"c":0},{"x":407.5,"y":312.8,"a":[201,203,204],"c":0},{"x":415,"y":299.8,"a":[202,201,208],"c":0},{"x":400,"y":325.8,"a":[202,205,201,236,229],"c":0},{"x":385,"y":325.8,"a":[201,204,206,229],"c":0},{"x":370,"y":325.8,"a":[205,207,201,226,225,229],"c":0},{"x":362.5,"y":312.8,"a":[201,206,199,225],"c":0},{"x":407.5,"y":286.8,"a":[201,160,203],"c":0},{"x":250,"y":325.8,"a":[210,194,212,211,214,213,216,215,186,187,195,185],"c":0},{"x":272.5,"y":338.8,"a":[209,194,211,217],"c":0},{"x":265,"y":351.8,"a":[210,212,209,223,217,270],"c":0},{"x":250,"y":351.8,"a":[209,211,213,270],"c":0},{"x":235,"y":351.8,"a":[212,214,209,274,270],"c":0},{"x":227.5,"y":338.8,"a":[209,213,215],"c":0},{"x":220,"y":325.8,"a":[214,216,209],"c":0},{"x":227.5,"y":312.8,"a":[209,215,187],"c":0},{"x":295,"y":351.8,"a":[218,219,221,220,223,222,210,211,193,194,224,192],"c":0},{"x":317.5,"y":364.8,"a":[217,219,220,283],"c":0},{"x":325,"y":351.8,"a":[218,217,224,228,225,283],"c":0},{"x":310,"y":377.8,"a":[218,221,217,282,275,283],"c":0},{"x":295,"y":377.8,"a":[217,220,222,275],"c":0},{"x":280,"y":377.8,"a":[221,223,217,271,270,275],"c":0},{"x":272.5,"y":364.8,"a":[217,222,211,270],"c":0},{"x":317.5,"y":338.8,"a":[217,192,219,225],"c":0},{"x":340,"y":325.8,"a":[226,206,228,227,224,219,190,192,200,191,207,199],"c":0},{"x":362.5,"y":338.8,"a":[225,206,227,229],"c":0},{"x":355,"y":351.8,"a":[226,228,225,235,229,283],"c":0},{"x":340,"y":351.8,"a":[225,227,219,283],"c":0},{"x":385,"y":351.8,"a":[230,231,233,232,235,234,226,227,205,206,236,204],"c":0},{"x":407.5,"y":364.8,"a":[229,231,232],"c":0},{"x":415,"y":351.8,"a":[230,229,236],"c":0},{"x":400,"y":377.8,"a":[230,233,229,294,287],"c":0},{"x":385,"y":377.8,"a":[229,232,234,287],"c":0},{"x":370,"y":377.8,"a":[233,235,229,284,283,287],"c":0},{"x":362.5,"y":364.8,"a":[229,234,227,283],"c":0},{"x":407.5,"y":338.8,"a":[229,204,231],"c":0},{"x":115,"y":403.7,"a":[238,239,241,240,243,242,245,244,247,246,249,248],"c":0},{"x":137.5,"y":416.7,"a":[237,239,240],"c":0},{"x":145,"y":403.7,"a":[238,237,249,254,250],"c":0},{"x":130,"y":429.7,"a":[238,241,237],"c":0},{"x":115,"y":429.7,"a":[237,240,242],"c":0},{"x":100,"y":429.7,"a":[241,243,237],"c":0},{"x":92.5,"y":416.7,"a":[237,242,244],"c":0},{"x":85,"y":403.7,"a":[243,245,237],"c":0},{"x":92.5,"y":390.8,"a":[237,244,246],"c":0},{"x":100,"y":377.8,"a":[245,247,237],"c":0},{"x":115,"y":377.8,"a":[237,246,248],"c":0},{"x":130,"y":377.8,"a":[247,249,237,255,250],"c":0},{"x":137.5,"y":390.8,"a":[237,248,239,250],"c":0},{"x":160,"y":377.8,"a":[251,252,254,253,249,239,255,248,257,256,259,258],"c":0},{"x":182.5,"y":390.8,"a":[250,252,253,260],"c":0},{"x":190,"y":377.8,"a":[251,250,259,267,260],"c":0},{"x":175,"y":403.7,"a":[251,254,250,266,260],"c":0},{"x":160,"y":403.7,"a":[250,253,239],"c":0},{"x":137.5,"y":364.8,"a":[250,248,256],"c":0},{"x":145,"y":351.8,"a":[255,257,250],"c":0},{"x":160,"y":351.8,"a":[250,256,258],"c":0},{"x":175,"y":351.8,"a":[257,259,250],"c":0},{"x":182.5,"y":364.8,"a":[250,258,252],"c":0},{"x":205,"y":403.7,"a":[261,262,264,263,266,265,251,253,267,252,269,268],"c":0},{"x":227.5,"y":416.7,"a":[260,262,263],"c":0},{"x":235,"y":403.7,"a":[261,260,269,273,270],"c":0},{"x":220,"y":429.7,"a":[261,264,260],"c":0},{"x":205,"y":429.7,"a":[260,263,265],"c":0},{"x":190,"y":429.7,"a":[264,266,260],"c":0},{"x":182.5,"y":416.7,"a":[260,265,253],"c":0},{"x":205,"y":377.8,"a":[260,252,268],"c":0},{"x":220,"y":377.8,"a":[267,269,260,274,270],"c":0},{"x":227.5,"y":390.8,"a":[260,268,262,270],"c":0},{"x":250,"y":377.8,"a":[271,222,273,272,269,262,274,268,212,213,223,211],"c":0},{"x":272.5,"y":390.8,"a":[270,222,272,275],"c":0},{"x":265,"y":403.7,"a":[271,273,270,281,275],"c":0},{"x":250,"y":403.7,"a":[270,272,262],"c":0},{"x":227.5,"y":364.8,"a":[270,268,213],"c":0},{"x":295,"y":403.7,"a":[276,277,279,278,281,280,271,272,221,222,282,220],"c":0},{"x":317.5,"y":416.7,"a":[275,277,278],"c":0},{"x":325,"y":403.7,"a":[276,275,282,286,283],"c":0},{"x":310,"y":429.7,"a":[276,279,275],"c":0},{"x":295,"y":429.7,"a":[275,278,280],"c":0},{"x":280,"y":429.7,"a":[279,281,275],"c":0},{"x":272.5,"y":416.7,"a":[275,280,272],"c":0},{"x":317.5,"y":390.8,"a":[275,220,277,283],"c":0},{"x":340,"y":377.8,"a":[284,234,286,285,282,277,218,220,228,219,235,227],"c":0},{"x":362.5,"y":390.8,"a":[283,234,285,287],"c":0},{"x":355,"y":403.7,"a":[284,286,283,293,287],"c":0},{"x":340,"y":403.7,"a":[283,285,277],"c":0},{"x":385,"y":403.7,"a":[288,289,291,290,293,292,284,285,233,234,294,232],"c":0},{"x":407.5,"y":416.7,"a":[287,289,290],"c":0},{"x":415,"y":403.7,"a":[288,287,294],"c":0},{"x":400,"y":429.7,"a":[288,291,287],"c":0},{"x":385,"y":429.7,"a":[287,290,292],"c":0},{"x":370,"y":429.7,"a":[291,293,287],"c":0},{"x":362.5,"y":416.7,"a":[287,292,285],"c":0},{"x":407.5,"y":390.8,"a":[287,232,289],"c":0}]},"2":{"land":[{"q":0,"r":0,"l":2},{"q":1,"r":0,"l":2},{"q":2,"r":-1,"l":2},{"q":3,"r":-1,"l":2},{"q":4,"r":-2,"l":2},{"q":5,"r":-2,"l":2},{"q":6,"r":-3,"l":2},{"q":7,"r":-3,"l":2},{"q":8,"r":-4,"l":2},{"q":0,"r":1,"l":2},{"q":1,"r":1,"l":0},{"q":2,"r":0,"l":0},{"q":3,"r":0,"l":2},{"q":4,"r":-1,"l":2},{"q":5,"r":-1,"l":0},{"q":6,"r":-2,"l":0},{"q":7,"r":-2,"l":0},{"q":8,"r":-3,"l":2},{"q":0,"r":2,"l":2},{"q":1,"r":2,"l":0},{"q":2,"r":1,"l":1},{"q":3,"r":1,"l":0},{"q":4,"r":0,"l":0},{"q":5,"r":0,"l":2},{"q":6,"r":-1,"l":0},{"q":7,"r":-1,"l":0},{"q":8,"r":-2,"l":2},{"q":0,"r":3,"l":0},{"q":1,"r":3,"l":0},{"q":2,"r":2,"l":0},{"q":3,"r":2,"l":0},{"q":4,"r":1,"l":1},{"q":5,"r":1,"l":0},{"q":6,"r":0,"l":0},{"q":7,"r":0,"l":0},{"q":8,"r":-1,"l":2},{"q":0,"r":4,"l":2},{"q":1,"r":4,"l":0},{"q":2,"r":3,"l":2},{"q":3,"r":3,"l":2},{"q":4,"r":2,"l":0},{"q":5,"r":2,"l":0},{"q":6,"r":1,"l":1},{"q":7,"r":1,"l":0},{"q":8,"r":0,"l":2},{"q":0,"r":5,"l":2},{"q":1,"r":5,"l":0},{"q":2,"r":4,"l":0},{"q":3,"r":4,"l":0},{"q":4,"r":3,"l":2},{"q":5,"r":3,"l":0},{"q":6,"r":2,"l":0},{"q":7,"r":2,"l":0},{"q":8,"r":1,"l":2},{"q":0,"r":6,"l":2},{"q":1,"r":6,"l":0},{"q":2,"r":5,"l":1},{"q":3,"r":5,"l":0},{"q":4,"r":4,"l":0},{"q":5,"r":4,"l":0},{"q":6,"r":3,"l":0},{"q":7,"r":3,"l":0},{"q":8,"r":2,"l":2},{"q":0,"r":7,"l":2},{"q":1,"r":7,"l":2},{"q":2,"r":6,"l":2},{"q":3,"r":6,"l":2},{"q":4,"r":5,"l":2},{"q":5,"r":5,"l":2},{"q":6,"r":4,"l":2},{"q":7,"r":4,"l":2},{"q":8,"r":3,"l":2}],"enemies":[{"kind":"guard","path":[[2,6],[7,6]],"range":3,"half":0.7,"speed":27,"hp":1},{"kind":"sentry","path":[[5,3]],"range":4,"half":0.55,"spin":0.5,"hp":2}],"name":"Hollow","nodes":[{"x":115,"y":143.9,"a":[1,2,4,3,6,5,8,7,10,9,12,11],"c":0},{"x":137.5,"y":156.9,"a":[0,2,3,66],"c":1},{"x":145,"y":143.9,"a":[1,0,12,17,13,66],"c":1},{"x":130,"y":169.9,"a":[1,4,0,65,56,66],"c":1},{"x":115,"y":169.9,"a":[0,3,5,56],"c":0},{"x":100,"y":169.9,"a":[4,6,0,64,56],"c":0},{"x":92.5,"y":156.9,"a":[0,5,7],"c":0},{"x":85,"y":143.9,"a":[6,8,0],"c":0},{"x":92.5,"y":131,"a":[0,7,9],"c":0},{"x":100,"y":118,"a":[8,10,0],"c":0},{"x":115,"y":118,"a":[0,9,11],"c":0},{"x":130,"y":118,"a":[10,12,0,18,13],"c":0},{"x":137.5,"y":131,"a":[0,11,2,13],"c":0},{"x":160,"y":118,"a":[14,15,17,16,12,2,18,11,20,19,22,21],"c":0},{"x":182.5,"y":131,"a":[13,15,16],"c":0},{"x":190,"y":118,"a":[14,13,22],"c":0},{"x":175,"y":143.9,"a":[14,17,13,71,66],"c":1},{"x":160,"y":143.9,"a":[13,16,2,66],"c":1},{"x":137.5,"y":105,"a":[13,11,19],"c":0},{"x":145,"y":92,"a":[18,20,13],"c":0},{"x":160,"y":92,"a":[13,19,21],"c":0},{"x":175,"y":92,"a":[20,22,13],"c":0},{"x":182.5,"y":105,"a":[13,21,15],"c":0},{"x":295,"y":143.9,"a":[24,25,27,26,29,28,31,30,33,32,35,34],"c":0},{"x":317.5,"y":156.9,"a":[23,25,26,89],"c":0},{"x":325,"y":143.9,"a":[24,23,35,40,36,89],"c":0},{"x":310,"y":169.9,"a":[24,27,23,94,89],"c":0},{"x":295,"y":169.9,"a":[23,26,28],"c":0},{"x":280,"y":169.9,"a":[27,29,23,83,82],"c":0},{"x":272.5,"y":156.9,"a":[23,28,30,82],"c":0},{"x":265,"y":143.9,"a":[29,31,23,88,82],"c":0},{"x":272.5,"y":131,"a":[23,30,32],"c":0},{"x":280,"y":118,"a":[31,33,23],"c":0},{"x":295,"y":118,"a":[23,32,34],"c":0},{"x":310,"y":118,"a":[33,35,23,41,36],"c":0},{"x":317.5,"y":131,"a":[23,34,25,36],"c":0},{"x":340,"y":118,"a":[37,38,40,39,35,25,41,34,43,42,45,44],"c":0},{"x":362.5,"y":131,"a":[36,38,39,46],"c":0},{"x":370,"y":118,"a":[37,36,45,53,46],"c":0},{"x":355,"y":143.9,"a":[37,40,36,52,46,89],"c":0},{"x":340,"y":143.9,"a":[36,39,25,89],"c":0},{"x":317.5,"y":105,"a":[36,34,42],"c":0},{"x":325,"y":92,"a":[41,43,36],"c":0},{"x":340,"y":92,"a":[36,42,44],"c":0},{"x":355,"y":92,"a":[43,45,36],"c":0},{"x":362.5,"y":105,"a":[36,44,38],"c":0},{"x":385,"y":143.9,"a":[47,48,50,49,52,51,37,39,53,38,55,54],"c":0},{"x":407.5,"y":156.9,"a":[46,48,49],"c":0},{"x":415,"y":143.9,"a":[47,46,55],"c":0},{"x":400,"y":169.9,"a":[47,50,46,102,95],"c":0},{"x":385,"y":169.9,"a":[46,49,51,95],"c":0},{"x":370,"y":169.9,"a":[50,52,46,90,89,95],"c":0},{"x":362.5,"y":156.9,"a":[46,51,39,89],"c":0},{"x":385,"y":118,"a":[46,38,54],"c":0},{"x":400,"y":118,"a":[53,55,46],"c":0},{"x":407.5,"y":131,"a":[46,54,48],"c":0},{"x":115,"y":195.9,"a":[57,58,60,59,62,61,64,63,4,5,65,3],"c":0},{"x":137.5,"y":208.9,"a":[56,58,59,121],"c":0},{"x":145,"y":195.9,"a":[57,56,65,70,66,121],"c":1},{"x":130,"y":221.9,"a":[57,60,56,120,113,121],"c":0},{"x":115,"y":221.9,"a":[56,59,61,113],"c":0},{"x":100,"y":221.9,"a":[60,62,56,104,103,113],"c":0},{"x":92.5,"y":208.9,"a":[56,61,63,103],"c":0},{"x":85,"y":195.9,"a":[62,64,56,112,103],"c":0},{"x":92.5,"y":182.9,"a":[56,63,5],"c":0},{"x":137.5,"y":182.9,"a":[56,3,58,66],"c":1},{"x":160,"y":169.9,"a":[67,68,70,69,65,58,1,3,17,2,71,16],"c":1},{"x":182.5,"y":182.9,"a":[66,68,69,72],"c":1},{"x":190,"y":169.9,"a":[67,66,71,79,72],"c":1},{"x":175,"y":195.9,"a":[67,70,66,78,72,121],"c":1},{"x":160,"y":195.9,"a":[66,69,58,121],"c":1},{"x":182.5,"y":156.9,"a":[66,16,68],"c":1},{"x":205,"y":195.9,"a":[73,74,76,75,78,77,67,69,79,68,81,80],"c":0},{"x":227.5,"y":208.9,"a":[72,74,75,133],"c":1},{"x":235,"y":195.9,"a":[73,72,81,85,82,133],"c":1},{"x":220,"y":221.9,"a":[73,76,72,132,125,133],"c":1},{"x":205,"y":221.9,"a":[72,75,77,125],"c":0},{"x":190,"y":221.9,"a":[76,78,72,122,121,125],"c":0},{"x":182.5,"y":208.9,"a":[72,77,69,121],"c":0},{"x":205,"y":169.9,"a":[72,68,80],"c":0},{"x":220,"y":169.9,"a":[79,81,72,86,82],"c":0},{"x":227.5,"y":182.9,"a":[72,80,74,82],"c":0},{"x":250,"y":169.9,"a":[83,28,85,84,81,74,86,80,88,87,29,30],"c":0},{"x":272.5,"y":182.9,"a":[82,28,84],"c":0},{"x":265,"y":195.9,"a":[83,85,82,138,133],"c":1},{"x":250,"y":195.9,"a":[82,84,74,133],"c":1},{"x":227.5,"y":156.9,"a":[82,80,87],"c":0},{"x":235,"y":143.9,"a":[86,88,82],"c":0},{"x":250,"y":143.9,"a":[82,87,30],"c":0},{"x":340,"y":169.9,"a":[90,51,92,91,94,93,24,26,40,25,52,39],"c":0},{"x":362.5,"y":182.9,"a":[89,51,91,95],"c":0},{"x":355,"y":195.9,"a":[90,92,89,101,95,149],"c":0},{"x":340,"y":195.9,"a":[89,91,93,149],"c":0},{"x":325,"y":195.9,"a":[92,94,89,153,149],"c":0},{"x":317.5,"y":182.9,"a":[89,93,26],"c":0},{"x":385,"y":195.9,"a":[96,97,99,98,101,100,90,91,50,51,102,49],"c":0},{"x":407.5,"y":208.9,"a":[95,97,98],"c":0},{"x":415,"y":195.9,"a":[96,95,102],"c":0},{"x":400,"y":221.9,"a":[96,99,95,161,154],"c":0},{"x":385,"y":221.9,"a":[95,98,100,154],"c":0},{"x":370,"y":221.9,"a":[99,101,95,150,149,154],"c":0},{"x":362.5,"y":208.9,"a":[95,100,91,149],"c":0},{"x":407.5,"y":182.9,"a":[95,49,97],"c":0},{"x":70,"y":221.9,"a":[104,61,106,105,108,107,110,109,112,111,62,63],"c":0},{"x":92.5,"y":234.9,"a":[103,61,105,113],"c":0},{"x":85,"y":247.9,"a":[104,106,103,119,113],"c":0},{"x":70,"y":247.9,"a":[103,105,107],"c":0},{"x":55,"y":247.9,"a":[106,108,103],"c":0},{"x":47.5,"y":234.9,"a":[103,107,109],"c":0},{"x":40,"y":221.9,"a":[108,110,103],"c":0},{"x":47.5,"y":208.9,"a":[103,109,111],"c":0},{"x":55,"y":195.9,"a":[110,112,103],"c":0},{"x":70,"y":195.9,"a":[103,111,63],"c":0},{"x":115,"y":247.9,"a":[114,115,117,116,119,118,104,105,60,61,120,59],"c":0},{"x":137.5,"y":260.9,"a":[113,115,116],"c":0},{"x":145,"y":247.9,"a":[114,113,120,124,121],"c":0},{"x":130,"y":273.8,"a":[114,117,113,171,162],"c":0},{"x":115,"y":273.8,"a":[113,116,118,162],"c":0},{"x":100,"y":273.8,"a":[117,119,113,170,162],"c":0},{"x":92.5,"y":260.9,"a":[113,118,105],"c":0},{"x":137.5,"y":234.9,"a":[113,59,115,121],"c":0},{"x":160,"y":221.9,"a":[122,77,124,123,120,115,57,59,70,58,78,69],"c":0},{"x":182.5,"y":234.9,"a":[121,77,123,125],"c":0},{"x":175,"y":247.9,"a":[122,124,121,131,125],"c":0},{"x":160,"y":247.9,"a":[121,123,115],"c":0},{"x":205,"y":247.9,"a":[126,127,129,128,131,130,122,123,76,77,132,75],"c":0},{"x":227.5,"y":260.9,"a":[125,127,128,172],"c":0},{"x":235,"y":247.9,"a":[126,125,132,137,133,172],"c":1},{"x":220,"y":273.8,"a":[126,129,125,177,172],"c":0},{"x":205,"y":273.8,"a":[125,128,130],"c":0},{"x":190,"y":273.8,"a":[129,131,125],"c":0},{"x":182.5,"y":260.9,"a":[125,130,123],"c":0},{"x":227.5,"y":234.9,"a":[125,75,127,133],"c":1},{"x":250,"y":221.9,"a":[134,135,137,136,132,127,73,75,85,74,138,84],"c":1},{"x":272.5,"y":234.9,"a":[133,135,136,139],"c":1},{"x":280,"y":221.9,"a":[134,133,138,146,139],"c":1},{"x":265,"y":247.9,"a":[134,137,133,145,139,172],"c":1},{"x":250,"y":247.9,"a":[133,136,127,172],"c":1},{"x":272.5,"y":208.9,"a":[133,84,135],"c":1},{"x":295,"y":247.9,"a":[140,141,143,142,145,144,134,136,146,135,148,147],"c":0},{"x":317.5,"y":260.9,"a":[139,141,142,186],"c":1},{"x":325,"y":247.9,"a":[140,139,148,152,149,186],"c":1},{"x":310,"y":273.8,"a":[140,143,139,185,178,186],"c":1},{"x":295,"y":273.8,"a":[139,142,144,178],"c":0},{"x":280,"y":273.8,"a":[143,145,139,173,172,178],"c":0},{"x":272.5,"y":260.9,"a":[139,144,136,172],"c":0},{"x":295,"y":221.9,"a":[139,135,147],"c":0},{"x":310,"y":221.9,"a":[146,148,139,153,149],"c":0},{"x":317.5,"y":234.9,"a":[139,147,141,149],"c":0},{"x":340,"y":221.9,"a":[150,100,152,151,148,141,153,147,92,93,101,91],"c":0},{"x":362.5,"y":234.9,"a":[149,100,151,154],"c":0},{"x":355,"y":247.9,"a":[150,152,149,160,154,186],"c":1},{"x":340,"y":247.9,"a":[149,151,141,186],"c":1},{"x":317.5,"y":208.9,"a":[149,147,93],"c":0},{"x":385,"y":247.9,"a":[155,156,158,157,160,159,150,151,99,100,161,98],"c":0},{"x":407.5,"y":260.9,"a":[154,156,157],"c":0},{"x":415,"y":247.9,"a":[155,154,161],"c":0},{"x":400,"y":273.8,"a":[155,158,154,197,190],"c":0},{"x":385,"y":273.8,"a":[154,157,159,190],"c":0},{"x":370,"y":273.8,"a":[158,160,154,187,186,190],"c":1},{"x":362.5,"y":260.9,"a":[154,159,151,186],"c":1},{"x":407.5,"y":234.9,"a":[154,98,156],"c":0},{"x":115,"y":299.8,"a":[163,164,166,165,168,167,170,169,117,118,171,116],"c":0},{"x":137.5,"y":312.8,"a":[162,164,165,208],"c":0},{"x":145,"y":299.8,"a":[163,162,171,213,208],"c":0},{"x":130,"y":325.8,"a":[163,166,162,207,198,208],"c":0},{"x":115,"y":325.8,"a":[162,165,167,198],"c":0},{"x":100,"y":325.8,"a":[166,168,162,206,198],"c":0},{"x":92.5,"y":312.8,"a":[162,167,169],"c":0},{"x":85,"y":299.8,"a":[168,170,162],"c":0},{"x":92.5,"y":286.8,"a":[162,169,118],"c":0},{"x":137.5,"y":286.8,"a":[162,116,164],"c":0},{"x":250,"y":273.8,"a":[173,144,175,174,177,176,126,128,137,127,145,136],"c":0},{"x":272.5,"y":286.8,"a":[172,144,174,178],"c":0},{"x":265,"y":299.8,"a":[173,175,172,184,178],"c":0},{"x":250,"y":299.8,"a":[172,174,176],"c":0},{"x":235,"y":299.8,"a":[175,177,172],"c":0},{"x":227.5,"y":286.8,"a":[172,176,128],"c":0},{"x":295,"y":299.8,"a":[179,180,182,181,184,183,173,174,143,144,185,142],"c":0},{"x":317.5,"y":312.8,"a":[178,180,181,236],"c":0},{"x":325,"y":299.8,"a":[179,178,185,189,186,236],"c":1},{"x":310,"y":325.8,"a":[179,182,178,235,226,236],"c":0},{"x":295,"y":325.8,"a":[178,181,183,226],"c":0},{"x":280,"y":325.8,"a":[182,184,178,234,226],"c":0},{"x":272.5,"y":312.8,"a":[178,183,174],"c":0},{"x":317.5,"y":286.8,"a":[178,142,180,186],"c":1},{"x":340,"y":273.8,"a":[187,159,189,188,185,180,140,142,152,141,160,151],"c":1},{"x":362.5,"y":286.8,"a":[186,159,188,190],"c":1},{"x":355,"y":299.8,"a":[187,189,186,196,190,236],"c":1},{"x":340,"y":299.8,"a":[186,188,180,236],"c":1},{"x":385,"y":299.8,"a":[191,192,194,193,196,195,187,188,158,159,197,157],"c":0},{"x":407.5,"y":312.8,"a":[190,192,193],"c":0},{"x":415,"y":299.8,"a":[191,190,197],"c":0},{"x":400,"y":325.8,"a":[191,194,190,247,240],"c":0},{"x":385,"y":325.8,"a":[190,193,195,240],"c":0},{"x":370,"y":325.8,"a":[194,196,190,237,236,240],"c":0},{"x":362.5,"y":312.8,"a":[190,195,188,236],"c":0},{"x":407.5,"y":286.8,"a":[190,157,192],"c":0},{"x":115,"y":351.8,"a":[199,200,202,201,204,203,206,205,166,167,207,165],"c":0},{"x":137.5,"y":364.8,"a":[198,200,201,258],"c":1},{"x":145,"y":351.8,"a":[199,198,207,212,208,258],"c":1},{"x":130,"y":377.8,"a":[199,202,198,257,248,258],"c":1},{"x":115,"y":377.8,"a":[198,201,203,248],"c":0},{"x":100,"y":377.8,"a":[202,204,198,256,248],"c":0},{"x":92.5,"y":364.8,"a":[198,203,205],"c":0},{"x":85,"y":351.8,"a":[204,206,198],"c":0},{"x":92.5,"y":338.8,"a":[198,205,167],"c":0},{"x":137.5,"y":338.8,"a":[198,165,200,208],"c":0},{"x":160,"y":325.8,"a":[209,210,212,211,207,200,163,165,213,164,215,214],"c":0},{"x":182.5,"y":338.8,"a":[208,210,211,216],"c":0},{"x":190,"y":325.8,"a":[209,208,215,223,216],"c":0},{"x":175,"y":351.8,"a":[209,212,208,222,216,258],"c":1},{"x":160,"y":351.8,"a":[208,211,200,258],"c":1},{"x":160,"y":299.8,"a":[208,164,214],"c":0},{"x":175,"y":299.8,"a":[213,215,208],"c":0},{"x":182.5,"y":312.8,"a":[208,214,210],"c":0},{"x":205,"y":351.8,"a":[217,218,220,219,222,221,209,211,223,210,225,224],"c":0},{"x":227.5,"y":364.8,"a":[216,218,219,270],"c":0},{"x":235,"y":351.8,"a":[217,216,225,274,270],"c":0},{"x":220,"y":377.8,"a":[217,220,216,269,262,270],"c":0},{"x":205,"y":377.8,"a":[216,219,221,262],"c":0},{"x":190,"y":377.8,"a":[220,222,216,259,258,262],"c":1},{"x":182.5,"y":364.8,"a":[216,221,211,258],"c":1},{"x":205,"y":325.8,"a":[216,210,224],"c":0},{"x":220,"y":325.8,"a":[223,225,216],"c":0},{"x":227.5,"y":338.8,"a":[216,224,218],"c":0},{"x":295,"y":351.8,"a":[227,228,230,229,232,231,234,233,182,183,235,181],"c":0},{"x":317.5,"y":364.8,"a":[226,228,229,283],"c":0},{"x":325,"y":351.8,"a":[227,226,235,239,236,283],"c":0},{"x":310,"y":377.8,"a":[227,230,226,282,275,283],"c":0},{"x":295,"y":377.8,"a":[226,229,231,275],"c":0},{"x":280,"y":377.8,"a":[230,232,226,271,270,275],"c":0},{"x":272.5,"y":364.8,"a":[226,231,233,270],"c":0},{"x":265,"y":351.8,"a":[232,234,226,274,270],"c":0},{"x":272.5,"y":338.8,"a":[226,233,183],"c":0},{"x":317.5,"y":338.8,"a":[226,181,228,236],"c":0},{"x":340,"y":325.8,"a":[237,195,239,238,235,228,179,181,189,180,196,188],"c":0},{"x":362.5,"y":338.8,"a":[236,195,238,240],"c":0},{"x":355,"y":351.8,"a":[237,239,236,246,240,283],"c":0},{"x":340,"y":351.8,"a":[236,238,228,283],"c":0},{"x":385,"y":351.8,"a":[241,242,244,243,246,245,237,238,194,195,247,193],"c":0},{"x":407.5,"y":364.8,"a":[240,242,243],"c":0},{"x":415,"y":351.8,"a":[241,240,247],"c":0},{"x":400,"y":377.8,"a":[241,244,240,294,287],"c":0},{"x":385,"y":377.8,"a":[240,243,245,287],"c":0},{"x":370,"y":377.8,"a":[244,246,240,284,283,287],"c":0},{"x":362.5,"y":364.8,"a":[240,245,238,283],"c":0},{"x":407.5,"y":338.8,"a":[240,193,242],"c":0},{"x":115,"y":403.7,"a":[249,250,252,251,254,253,256,255,202,203,257,201],"c":0},{"x":137.5,"y":416.7,"a":[248,250,251],"c":0},{"x":145,"y":403.7,"a":[249,248,257,261,258],"c":1},{"x":130,"y":429.7,"a":[249,252,248],"c":0},{"x":115,"y":429.7,"a":[248,251,253],"c":0},{"x":100,"y":429.7,"a":[252,254,248],"c":0},{"x":92.5,"y":416.7,"a":[248,253,255],"c":0},{"x":85,"y":403.7,"a":[254,256,248],"c":0},{"x":92.5,"y":390.8,"a":[248,255,203],"c":0},{"x":137.5,"y":390.8,"a":[248,201,250,258],"c":1},{"x":160,"y":377.8,"a":[259,221,261,260,257,250,199,201,212,200,222,211],"c":1},{"x":182.5,"y":390.8,"a":[258,221,260,262],"c":1},{"x":175,"y":403.7,"a":[259,261,258,268,262],"c":1},{"x":160,"y":403.7,"a":[258,260,250],"c":1},{"x":205,"y":403.7,"a":[263,264,266,265,268,267,259,260,220,221,269,219],"c":0},{"x":227.5,"y":416.7,"a":[262,264,265],"c":0},{"x":235,"y":403.7,"a":[263,262,269,273,270],"c":0},{"x":220,"y":429.7,"a":[263,266,262],"c":0},{"x":205,"y":429.7,"a":[262,265,267],"c":0},{"x":190,"y":429.7,"a":[266,268,262],"c":0},{"x":182.5,"y":416.7,"a":[262,267,260],"c":0},{"x":227.5,"y":390.8,"a":[262,219,264,270],"c":0},{"x":250,"y":377.8,"a":[271,231,273,272,269,264,217,219,274,218,232,233],"c":0},{"x":272.5,"y":390.8,"a":[270,231,272,275],"c":0},{"x":265,"y":403.7,"a":[271,273,270,281,275],"c":0},{"x":250,"y":403.7,"a":[270,272,264],"c":0},{"x":250,"y":351.8,"a":[270,218,233],"c":0},{"x":295,"y":403.7,"a":[276,277,279,278,281,280,271,272,230,231,282,229],"c":0},{"x":317.5,"y":416.7,"a":[275,277,278],"c":0},{"x":325,"y":403.7,"a":[276,275,282,286,283],"c":0},{"x":310,"y":429.7,"a":[276,279,275],"c":0},{"x":295,"y":429.7,"a":[275,278,280],"c":0},{"x":280,"y":429.7,"a":[279,281,275],"c":0},{"x":272.5,"y":416.7,"a":[275,280,272],"c":0},{"x":317.5,"y":390.8,"a":[275,229,277,283],"c":0},{"x":340,"y":377.8,"a":[284,245,286,285,282,277,227,229,239,228,246,238],"c":0},{"x":362.5,"y":390.8,"a":[283,245,285,287],"c":0},{"x":355,"y":403.7,"a":[284,286,283,293,287],"c":0},{"x":340,"y":403.7,"a":[283,285,277],"c":0},{"x":385,"y":403.7,"a":[288,289,291,290,293,292,284,285,244,245,294,243],"c":0},{"x":407.5,"y":416.7,"a":[287,289,290],"c":0},{"x":415,"y":403.7,"a":[288,287,294],"c":0},{"x":400,"y":429.7,"a":[288,291,287],"c":0},{"x":385,"y":429.7,"a":[287,290,292],"c":0},{"x":370,"y":429.7,"a":[291,293,287],"c":0},{"x":362.5,"y":416.7,"a":[287,292,285],"c":0},{"x":407.5,"y":390.8,"a":[287,243,289],"c":0}]},"3":{"land":[{"q":0,"r":0,"l":2},{"q":1,"r":0,"l":2},{"q":2,"r":-1,"l":2},{"q":3,"r":-1,"l":2},{"q":4,"r":-2,"l":2},{"q":5,"r":-2,"l":2},{"q":6,"r":-3,"l":2},{"q":7,"r":-3,"l":2},{"q":8,"r":-4,"l":2},{"q":0,"r":1,"l":2},{"q":1,"r":1,"l":0},{"q":2,"r":0,"l":0},{"q":3,"r":0,"l":0},{"q":4,"r":-1,"l":0},{"q":5,"r":-1,"l":0},{"q":6,"r":-2,"l":0},{"q":7,"r":-2,"l":0},{"q":8,"r":-3,"l":2},{"q":0,"r":2,"l":2},{"q":1,"r":2,"l":0},{"q":2,"r":1,"l":2},{"q":3,"r":1,"l":0},{"q":4,"r":0,"l":0},{"q":5,"r":0,"l":0},{"q":6,"r":-1,"l":2},{"q":7,"r":-1,"l":0},{"q":8,"r":-2,"l":2},{"q":0,"r":3,"l":2},{"q":1,"r":3,"l":0},{"q":2,"r":2,"l":0},{"q":3,"r":2,"l":1},{"q":4,"r":1,"l":0},{"q":5,"r":1,"l":1},{"q":6,"r":0,"l":0},{"q":7,"r":0,"l":0},{"q":8,"r":-1,"l":2},{"q":0,"r":4,"l":2},{"q":1,"r":4,"l":0},{"q":2,"r":3,"l":2},{"q":3,"r":3,"l":0},{"q":4,"r":2,"l":0},{"q":5,"r":2,"l":0},{"q":6,"r":1,"l":2},{"q":7,"r":1,"l":0},{"q":8,"r":0,"l":2},{"q":0,"r":5,"l":2},{"q":1,"r":5,"l":0},{"q":2,"r":4,"l":0},{"q":3,"r":4,"l":0},{"q":4,"r":3,"l":0},{"q":5,"r":3,"l":0},{"q":6,"r":2,"l":0},{"q":7,"r":2,"l":0},{"q":8,"r":1,"l":2},{"q":0,"r":6,"l":2},{"q":1,"r":6,"l":2},{"q":2,"r":5,"l":2},{"q":3,"r":5,"l":2},{"q":4,"r":4,"l":0},{"q":5,"r":4,"l":2},{"q":6,"r":3,"l":2},{"q":7,"r":3,"l":2},{"q":8,"r":2,"l":2},{"q":0,"r":7,"l":2},{"q":1,"r":7,"l":2},{"q":2,"r":6,"l":2},{"q":3,"r":6,"l":2},{"q":4,"r":5,"l":2},{"q":5,"r":5,"l":2},{"q":6,"r":4,"l":2},{"q":7,"r":4,"l":2},{"q":8,"r":3,"l":2}],"enemies":[{"kind":"guard","path":[[2,5],[6,5]],"range":3,"half":0.7,"speed":30,"hp":1},{"kind":"guard","path":[[1,3],[7,3]],"range":3,"half":0.7,"speed":27,"hp":1}],"name":"Keep","nodes":[{"x":115,"y":143.9,"a":[1,2,4,3,6,5,8,7,10,9,12,11],"c":0},{"x":137.5,"y":156.9,"a":[0,2,3],"c":0},{"x":145,"y":143.9,"a":[1,0,12,17,13],"c":0},{"x":130,"y":169.9,"a":[1,4,0,82,73],"c":0},{"x":115,"y":169.9,"a":[0,3,5,73],"c":0},{"x":100,"y":169.9,"a":[4,6,0,81,73],"c":0},{"x":92.5,"y":156.9,"a":[0,5,7],"c":0},{"x":85,"y":143.9,"a":[6,8,0],"c":0},{"x":92.5,"y":131,"a":[0,7,9],"c":0},{"x":100,"y":118,"a":[8,10,0],"c":0},{"x":115,"y":118,"a":[0,9,11],"c":0},{"x":130,"y":118,"a":[10,12,0,18,13],"c":0},{"x":137.5,"y":131,"a":[0,11,2,13],"c":0},{"x":160,"y":118,"a":[14,15,17,16,12,2,18,11,20,19,22,21],"c":0},{"x":182.5,"y":131,"a":[13,15,16,23],"c":0},{"x":190,"y":118,"a":[14,13,22,30,23],"c":0},{"x":175,"y":143.9,"a":[14,17,13,29,23],"c":0},{"x":160,"y":143.9,"a":[13,16,2],"c":0},{"x":137.5,"y":105,"a":[13,11,19],"c":0},{"x":145,"y":92,"a":[18,20,13],"c":0},{"x":160,"y":92,"a":[13,19,21],"c":0},{"x":175,"y":92,"a":[20,22,13],"c":0},{"x":182.5,"y":105,"a":[13,21,15],"c":0},{"x":205,"y":143.9,"a":[24,25,27,26,29,28,14,16,30,15,32,31],"c":0},{"x":227.5,"y":156.9,"a":[23,25,26,93],"c":0},{"x":235,"y":143.9,"a":[24,23,32,37,33,93],"c":0},{"x":220,"y":169.9,"a":[24,27,23,92,83,93],"c":0},{"x":205,"y":169.9,"a":[23,26,28,83],"c":0},{"x":190,"y":169.9,"a":[27,29,23,91,83],"c":0},{"x":182.5,"y":156.9,"a":[23,28,16],"c":0},{"x":205,"y":118,"a":[23,15,31],"c":0},{"x":220,"y":118,"a":[30,32,23,38,33],"c":0},{"x":227.5,"y":131,"a":[23,31,25,33],"c":0},{"x":250,"y":118,"a":[34,35,37,36,32,25,38,31,40,39,42,41],"c":0},{"x":272.5,"y":131,"a":[33,35,36,43],"c":0},{"x":280,"y":118,"a":[34,33,42,50,43],"c":0},{"x":265,"y":143.9,"a":[34,37,33,49,43,93],"c":0},{"x":250,"y":143.9,"a":[33,36,25,93],"c":0},{"x":227.5,"y":105,"a":[33,31,39],"c":0},{"x":235,"y":92,"a":[38,40,33],"c":0},{"x":250,"y":92,"a":[33,39,41],"c":0},{"x":265,"y":92,"a":[40,42,33],"c":0},{"x":272.5,"y":105,"a":[33,41,35],"c":0},{"x":295,"y":143.9,"a":[44,45,47,46,49,48,34,36,50,35,52,51],"c":0},{"x":317.5,"y":156.9,"a":[43,45,46],"c":0},{"x":325,"y":143.9,"a":[44,43,52,57,53],"c":0},{"x":310,"y":169.9,"a":[44,47,43,104,97],"c":0},{"x":295,"y":169.9,"a":[43,46,48,97],"c":0},{"x":280,"y":169.9,"a":[47,49,43,94,93,97],"c":0},{"x":272.5,"y":156.9,"a":[43,48,36,93],"c":0},{"x":295,"y":118,"a":[43,35,51],"c":0},{"x":310,"y":118,"a":[50,52,43,58,53],"c":0},{"x":317.5,"y":131,"a":[43,51,45,53],"c":0},{"x":340,"y":118,"a":[54,55,57,56,52,45,58,51,60,59,62,61],"c":0},{"x":362.5,"y":131,"a":[53,55,56,63],"c":0},{"x":370,"y":118,"a":[54,53,62,70,63],"c":0},{"x":355,"y":143.9,"a":[54,57,53,69,63],"c":0},{"x":340,"y":143.9,"a":[53,56,45],"c":0},{"x":317.5,"y":105,"a":[53,51,59],"c":0},{"x":325,"y":92,"a":[58,60,53],"c":0},{"x":340,"y":92,"a":[53,59,61],"c":0},{"x":355,"y":92,"a":[60,62,53],"c":0},{"x":362.5,"y":105,"a":[53,61,55],"c":0},{"x":385,"y":143.9,"a":[64,65,67,66,69,68,54,56,70,55,72,71],"c":0},{"x":407.5,"y":156.9,"a":[63,65,66],"c":0},{"x":415,"y":143.9,"a":[64,63,72],"c":0},{"x":400,"y":169.9,"a":[64,67,63,114,105],"c":0},{"x":385,"y":169.9,"a":[63,66,68,105],"c":0},{"x":370,"y":169.9,"a":[67,69,63,113,105],"c":0},{"x":362.5,"y":156.9,"a":[63,68,56],"c":0},{"x":385,"y":118,"a":[63,55,71],"c":0},{"x":400,"y":118,"a":[70,72,63],"c":0},{"x":407.5,"y":131,"a":[63,71,65],"c":0},{"x":115,"y":195.9,"a":[74,75,77,76,79,78,81,80,4,5,82,3],"c":0},{"x":137.5,"y":208.9,"a":[73,75,76,125],"c":0},{"x":145,"y":195.9,"a":[74,73,82,129,125],"c":0},{"x":130,"y":221.9,"a":[74,77,73,124,115,125],"c":0},{"x":115,"y":221.9,"a":[73,76,78,115],"c":0},{"x":100,"y":221.9,"a":[77,79,73,123,115],"c":0},{"x":92.5,"y":208.9,"a":[73,78,80],"c":0},{"x":85,"y":195.9,"a":[79,81,73],"c":0},{"x":92.5,"y":182.9,"a":[73,80,5],"c":0},{"x":137.5,"y":182.9,"a":[73,3,75],"c":0},{"x":205,"y":195.9,"a":[84,85,87,86,89,88,91,90,27,28,92,26],"c":0},{"x":227.5,"y":208.9,"a":[83,85,86,138],"c":0},{"x":235,"y":195.9,"a":[84,83,92,96,93,138],"c":0},{"x":220,"y":221.9,"a":[84,87,83,137,130,138],"c":1},{"x":205,"y":221.9,"a":[83,86,88,130],"c":1},{"x":190,"y":221.9,"a":[87,89,83,126,125,130],"c":1},{"x":182.5,"y":208.9,"a":[83,88,90,125],"c":0},{"x":175,"y":195.9,"a":[89,91,83,129,125],"c":0},{"x":182.5,"y":182.9,"a":[83,90,28],"c":0},{"x":227.5,"y":182.9,"a":[83,26,85,93],"c":0},{"x":250,"y":169.9,"a":[94,48,96,95,92,85,24,26,37,25,49,36],"c":0},{"x":272.5,"y":182.9,"a":[93,48,95,97],"c":0},{"x":265,"y":195.9,"a":[94,96,93,103,97,138],"c":0},{"x":250,"y":195.9,"a":[93,95,85,138],"c":0},{"x":295,"y":195.9,"a":[98,99,101,100,103,102,94,95,47,48,104,46],"c":0},{"x":317.5,"y":208.9,"a":[97,99,100,150],"c":0},{"x":325,"y":195.9,"a":[98,97,104,154,150],"c":0},{"x":310,"y":221.9,"a":[98,101,97,149,142,150],"c":1},{"x":295,"y":221.9,"a":[97,100,102,142],"c":1},{"x":280,"y":221.9,"a":[101,103,97,139,138,142],"c":1},{"x":272.5,"y":208.9,"a":[97,102,95,138],"c":0},{"x":317.5,"y":182.9,"a":[97,46,99],"c":0},{"x":385,"y":195.9,"a":[106,107,109,108,111,110,113,112,67,68,114,66],"c":0},{"x":407.5,"y":208.9,"a":[105,107,108],"c":0},{"x":415,"y":195.9,"a":[106,105,114],"c":0},{"x":400,"y":221.9,"a":[106,109,105,162,155],"c":0},{"x":385,"y":221.9,"a":[105,108,110,155],"c":0},{"x":370,"y":221.9,"a":[109,111,105,151,150,155],"c":0},{"x":362.5,"y":208.9,"a":[105,110,112,150],"c":0},{"x":355,"y":195.9,"a":[111,113,105,154,150],"c":0},{"x":362.5,"y":182.9,"a":[105,112,68],"c":0},{"x":407.5,"y":182.9,"a":[105,66,107],"c":0},{"x":115,"y":247.9,"a":[116,117,119,118,121,120,123,122,77,78,124,76],"c":0},{"x":137.5,"y":260.9,"a":[115,117,118],"c":0},{"x":145,"y":247.9,"a":[116,115,124,128,125],"c":0},{"x":130,"y":273.8,"a":[116,119,115,172,163],"c":0},{"x":115,"y":273.8,"a":[115,118,120,163],"c":0},{"x":100,"y":273.8,"a":[119,121,115,171,163],"c":0},{"x":92.5,"y":260.9,"a":[115,120,122],"c":0},{"x":85,"y":247.9,"a":[121,123,115],"c":0},{"x":92.5,"y":234.9,"a":[115,122,78],"c":0},{"x":137.5,"y":234.9,"a":[115,76,117,125],"c":0},{"x":160,"y":221.9,"a":[126,88,128,127,124,117,74,76,129,75,89,90],"c":0},{"x":182.5,"y":234.9,"a":[125,88,127,130],"c":1},{"x":175,"y":247.9,"a":[126,128,125,136,130],"c":1},{"x":160,"y":247.9,"a":[125,127,117],"c":0},{"x":160,"y":195.9,"a":[125,75,90],"c":0},{"x":205,"y":247.9,"a":[131,132,134,133,136,135,126,127,87,88,137,86],"c":1},{"x":227.5,"y":260.9,"a":[130,132,133,183],"c":1},{"x":235,"y":247.9,"a":[131,130,137,141,138,183],"c":1},{"x":220,"y":273.8,"a":[131,134,130,182,173,183],"c":1},{"x":205,"y":273.8,"a":[130,133,135,173],"c":1},{"x":190,"y":273.8,"a":[134,136,130,181,173],"c":1},{"x":182.5,"y":260.9,"a":[130,135,127],"c":1},{"x":227.5,"y":234.9,"a":[130,86,132,138],"c":1},{"x":250,"y":221.9,"a":[139,102,141,140,137,132,84,86,96,85,103,95],"c":0},{"x":272.5,"y":234.9,"a":[138,102,140,142],"c":1},{"x":265,"y":247.9,"a":[139,141,138,148,142,183],"c":1},{"x":250,"y":247.9,"a":[138,140,132,183],"c":0},{"x":295,"y":247.9,"a":[143,144,146,145,148,147,139,140,101,102,149,100],"c":1},{"x":317.5,"y":260.9,"a":[142,144,145],"c":1},{"x":325,"y":247.9,"a":[143,142,149,153,150],"c":1},{"x":310,"y":273.8,"a":[143,146,142,194,187],"c":1},{"x":295,"y":273.8,"a":[142,145,147,187],"c":1},{"x":280,"y":273.8,"a":[146,148,142,184,183,187],"c":1},{"x":272.5,"y":260.9,"a":[142,147,140,183],"c":1},{"x":317.5,"y":234.9,"a":[142,100,144,150],"c":1},{"x":340,"y":221.9,"a":[151,110,153,152,149,144,98,100,154,99,111,112],"c":0},{"x":362.5,"y":234.9,"a":[150,110,152,155],"c":0},{"x":355,"y":247.9,"a":[151,153,150,161,155],"c":0},{"x":340,"y":247.9,"a":[150,152,144],"c":0},{"x":340,"y":195.9,"a":[150,99,112],"c":0},{"x":385,"y":247.9,"a":[156,157,159,158,161,160,151,152,109,110,162,108],"c":0},{"x":407.5,"y":260.9,"a":[155,157,158],"c":0},{"x":415,"y":247.9,"a":[156,155,162],"c":0},{"x":400,"y":273.8,"a":[156,159,155,204,195],"c":0},{"x":385,"y":273.8,"a":[155,158,160,195],"c":0},{"x":370,"y":273.8,"a":[159,161,155,203,195],"c":0},{"x":362.5,"y":260.9,"a":[155,160,152],"c":0},{"x":407.5,"y":234.9,"a":[155,108,157],"c":0},{"x":115,"y":299.8,"a":[164,165,167,166,169,168,171,170,119,120,172,118],"c":0},{"x":137.5,"y":312.8,"a":[163,165,166,215],"c":0},{"x":145,"y":299.8,"a":[164,163,172,219,215],"c":0},{"x":130,"y":325.8,"a":[164,167,163,214,205,215],"c":0},{"x":115,"y":325.8,"a":[163,166,168,205],"c":0},{"x":100,"y":325.8,"a":[167,169,163,213,205],"c":0},{"x":92.5,"y":312.8,"a":[163,168,170],"c":0},{"x":85,"y":299.8,"a":[169,171,163],"c":0},{"x":92.5,"y":286.8,"a":[163,170,120],"c":0},{"x":137.5,"y":286.8,"a":[163,118,165],"c":0},{"x":205,"y":299.8,"a":[174,175,177,176,179,178,181,180,134,135,182,133],"c":0},{"x":227.5,"y":312.8,"a":[173,175,176,228],"c":0},{"x":235,"y":299.8,"a":[174,173,182,186,183,228],"c":0},{"x":220,"y":325.8,"a":[174,177,173,227,220,228],"c":0},{"x":205,"y":325.8,"a":[173,176,178,220],"c":0},{"x":190,"y":325.8,"a":[177,179,173,216,215,220],"c":0},{"x":182.5,"y":312.8,"a":[173,178,180,215],"c":0},{"x":175,"y":299.8,"a":[179,181,173,219,215],"c":0},{"x":182.5,"y":286.8,"a":[173,180,135],"c":0},{"x":227.5,"y":286.8,"a":[173,133,175,183],"c":0},{"x":250,"y":273.8,"a":[184,147,186,185,182,175,131,133,141,132,148,140],"c":0},{"x":272.5,"y":286.8,"a":[183,147,185,187],"c":0},{"x":265,"y":299.8,"a":[184,186,183,193,187,228],"c":0},{"x":250,"y":299.8,"a":[183,185,175,228],"c":0},{"x":295,"y":299.8,"a":[188,189,191,190,193,192,184,185,146,147,194,145],"c":0},{"x":317.5,"y":312.8,"a":[187,189,190,240],"c":0},{"x":325,"y":299.8,"a":[188,187,194,244,240],"c":0},{"x":310,"y":325.8,"a":[188,191,187,239,232,240],"c":0},{"x":295,"y":325.8,"a":[187,190,192,232],"c":0},{"x":280,"y":325.8,"a":[191,193,187,229,228,232],"c":0},{"x":272.5,"y":312.8,"a":[187,192,185,228],"c":0},{"x":317.5,"y":286.8,"a":[187,145,189],"c":0},{"x":385,"y":299.8,"a":[196,197,199,198,201,200,203,202,159,160,204,158],"c":0},{"x":407.5,"y":312.8,"a":[195,197,198],"c":0},{"x":415,"y":299.8,"a":[196,195,204],"c":0},{"x":400,"y":325.8,"a":[196,199,195,252,245],"c":0},{"x":385,"y":325.8,"a":[195,198,200,245],"c":0},{"x":370,"y":325.8,"a":[199,201,195,241,240,245],"c":0},{"x":362.5,"y":312.8,"a":[195,200,202,240],"c":0},{"x":355,"y":299.8,"a":[201,203,195,244,240],"c":0},{"x":362.5,"y":286.8,"a":[195,202,160],"c":0},{"x":407.5,"y":286.8,"a":[195,158,197],"c":0},{"x":115,"y":351.8,"a":[206,207,209,208,211,210,213,212,167,168,214,166],"c":0},{"x":137.5,"y":364.8,"a":[205,207,208],"c":0},{"x":145,"y":351.8,"a":[206,205,214,218,215],"c":0},{"x":130,"y":377.8,"a":[206,209,205],"c":0},{"x":115,"y":377.8,"a":[205,208,210],"c":0},{"x":100,"y":377.8,"a":[209,211,205],"c":0},{"x":92.5,"y":364.8,"a":[205,210,212],"c":0},{"x":85,"y":351.8,"a":[211,213,205],"c":0},{"x":92.5,"y":338.8,"a":[205,212,168],"c":0},{"x":137.5,"y":338.8,"a":[205,166,207,215],"c":0},{"x":160,"y":325.8,"a":[216,178,218,217,214,207,164,166,219,165,179,180],"c":0},{"x":182.5,"y":338.8,"a":[215,178,217,220],"c":0},{"x":175,"y":351.8,"a":[216,218,215,226,220],"c":0},{"x":160,"y":351.8,"a":[215,217,207],"c":0},{"x":160,"y":299.8,"a":[215,165,180],"c":0},{"x":205,"y":351.8,"a":[221,222,224,223,226,225,216,217,177,178,227,176],"c":0},{"x":227.5,"y":364.8,"a":[220,222,223,253],"c":0},{"x":235,"y":351.8,"a":[221,220,227,231,228,253],"c":0},{"x":220,"y":377.8,"a":[221,224,220,258,253],"c":0},{"x":205,"y":377.8,"a":[220,223,225],"c":0},{"x":190,"y":377.8,"a":[224,226,220],"c":0},{"x":182.5,"y":364.8,"a":[220,225,217],"c":0},{"x":227.5,"y":338.8,"a":[220,176,222,228],"c":0},{"x":250,"y":325.8,"a":[229,192,231,230,227,222,174,176,186,175,193,185],"c":0},{"x":272.5,"y":338.8,"a":[228,192,230,232],"c":0},{"x":265,"y":351.8,"a":[229,231,228,238,232,253],"c":0},{"x":250,"y":351.8,"a":[228,230,222,253],"c":0},{"x":295,"y":351.8,"a":[233,234,236,235,238,237,229,230,191,192,239,190],"c":0},{"x":317.5,"y":364.8,"a":[232,234,235],"c":0},{"x":325,"y":351.8,"a":[233,232,239,243,240],"c":0},{"x":310,"y":377.8,"a":[233,236,232],"c":0},{"x":295,"y":377.8,"a":[232,235,237],"c":0},{"x":280,"y":377.8,"a":[236,238,232,254,253],"c":0},{"x":272.5,"y":364.8,"a":[232,237,230,253],"c":0},{"x":317.5,"y":338.8,"a":[232,190,234,240],"c":0},{"x":340,"y":325.8,"a":[241,200,243,242,239,234,188,190,244,189,201,202],"c":0},{"x":362.5,"y":338.8,"a":[240,200,242,245],"c":0},{"x":355,"y":351.8,"a":[241,243,240,251,245],"c":0},{"x":340,"y":351.8,"a":[240,242,234],"c":0},{"x":340,"y":299.8,"a":[240,189,202],"c":0},{"x":385,"y":351.8,"a":[246,247,249,248,251,250,241,242,199,200,252,198],"c":0},{"x":407.5,"y":364.8,"a":[245,247,248],"c":0},{"x":415,"y":351.8,"a":[246,245,252],"c":0},{"x":400,"y":377.8,"a":[246,249,245],"c":0},{"x":385,"y":377.8,"a":[245,248,250],"c":0},{"x":370,"y":377.8,"a":[249,251,245],"c":0},{"x":362.5,"y":364.8,"a":[245,250,242],"c":0},{"x":407.5,"y":338.8,"a":[245,198,247],"c":0},{"x":250,"y":377.8,"a":[254,237,256,255,258,257,221,223,231,222,238,230],"c":0},{"x":272.5,"y":390.8,"a":[253,237,255],"c":0},{"x":265,"y":403.7,"a":[254,256,253],"c":0},{"x":250,"y":403.7,"a":[253,255,257],"c":0},{"x":235,"y":403.7,"a":[256,258,253],"c":0},{"x":227.5,"y":390.8,"a":[253,257,223],"c":0}]}}
const DOORS = [{"room":1,"q":8,"r":-1,"px":[430,221.88457268119896],"to":2,"spx":[115,247.86533479473212],"gated":false},{"room":1,"q":3,"r":0,"px":[205,143.94228634059948],"to":3,"spx":[295,351.78838324886476],"gated":true},{"room":2,"q":0,"r":3,"px":[70,221.88457268119896],"to":1,"spx":[385,195.90381056766577],"gated":false},{"room":3,"q":4,"r":4,"px":[250,377.7691453623979],"to":1,"spx":[250,169.92304845413264],"gated":false}]
const EFFECTS = [{"id":"fire","name":"Fire","visual":{"rayColor":[2.4,0.5,0.3],"burst":1.4},"mechanical":{"damage":1,"stun":0,"calm":0,"loud":1},"charges":2,"rangeHex":3},{"id":"psychic","name":"Psychic","visual":{"rayColor":[0.5,1.1,2.2],"burst":0.9},"mechanical":{"damage":0,"stun":3,"calm":1,"loud":0},"charges":3,"rangeHex":3}]
const KEYPX = [385,143.94228634059948]
const CHESTPX = [250,117.96152422706632]
const STARTPX = [205,403.749907475931]
const S = 30, X0 = 70, Y0 = 66, SQ3 = Math.sqrt(3), ISY = 0.56, IYOFF = 112
const hexPx = (q, r) => [X0 + S * 1.5 * q, Y0 + S * (SQ3 / 2 * q + SQ3 * r)]
const pxHex = (x, y) => {
const qf = (2 / 3 * (x - X0)) / S, rf = (-1 / 3 * (x - X0) + SQ3 / 3 * (y - Y0)) / S, sf = -qf - rf
let q = Math.round(qf), r = Math.round(rf), s = Math.round(sf)
const dq = Math.abs(q - qf), dr = Math.abs(r - rf), ds = Math.abs(s - sf)
if (dq > dr && dq > ds) q = -r - s; else if (dr > ds) r = -q - s
return [q, r]
}
const hexDist = (a, b) => (Math.abs(b[0] - a[0]) + Math.abs(b[1] - a[1]) + Math.abs(b[0] + b[1] - a[0] - a[1])) / 2
const landAt = (room, q, r) => { const h = ROOMS[room].land.find(h => h.q === q && h.r === r); return h ? h.l : 2 }
const losClear = (room, a, b, throughTrees) => {
const n = hexDist(a, b)
if (n <= 1) return true
const pa = hexPx(a[0], a[1]), pb = hexPx(b[0], b[1])
const steps = n * 2 // 2 samples/hex — a single sample can thread between trees
for (let i = 1; i < steps; i++) {
const t = i / steps
const h = pxHex(pa[0] + (pb[0] - pa[0]) * t, pa[1] + (pb[1] - pa[1]) * t)
if ((h[0] === a[0] && h[1] === a[1]) || (h[0] === b[0] && h[1] === b[1])) continue
const l = landAt(room, h[0], h[1])
if (l === 2 || (l === 1 && !throughTrees)) return false
}
return true
}
if (!wd.__eq || wd.__eq.v !== 5) {
wd.__eq = { v: 5, loaded: 0, room: 1, node: -1, path: [], moveT: 1, fx: STARTPX[0], fy: STARTPX[1], x: STARTPX[0], y: STARTPX[1],
face: 0.5, hasKey: 0, chest: 0, alarm: 0, hurt: 0, flare: 0, shadow: 0, charges: 3,
wins: 0, kills: 0, casts: 0, en: null, eff: 0, effCh: EFFECTS.map(e => e.charges), zoneT: 0,
camx: STARTPX[0], camy: STARTPX[1] * 0.56 + 112 - 9, doorArm: 1, stepReq: 0,
ray: { t: 0, ax: 0, ay: 0, bx: 0, by: 0, c: [1, 1, 1] },
mHeld: 0, spaceHeld: 0, shiftHeld: 0 }
}
const G = wd.__eq
if (G.sel === undefined) G.sel = 0
const pdt = Math.min(dt, 0.05)
const NODES = ROOMS[G.room].nodes
if (!G.loaded) { G.loaded = 1; wd.__load_game = { slot: 'esper' } }
const sv = wd.game_save
if (sv && sv.slot === 'esper') { delete wd.game_save; if (sv.data) { G.hasKey = sv.data.hasKey || 0; G.chest = sv.data.chest || 0; G.wins = sv.data.wins || 0 } }
const nearestNode = (x, y, maxD) => {
let best = -1, bd = maxD * maxD
for (let i = 0; i < NODES.length; i++) {
const d = (NODES[i].x - x) ** 2 + (NODES[i].y - y) ** 2
if (d < bd) { bd = d; best = i }
}
return best
}
if (G.node < 0) G.node = nearestNode(G.x, G.y, 60)
const gateBlocked = i => {
for (const d of DOORS) {
if (d.room !== G.room || !d.gated) continue
if (!G.hasKey && Math.hypot(NODES[i].x - d.px[0], NODES[i].y - d.px[1]) < S * 0.95) return true
}
return false
}
const bfsPath = target => {
if (target < 0 || target === G.node) return []
const prev = new Array(NODES.length).fill(-2)
prev[G.node] = -1
const qq = [G.node]
while (qq.length) {
const c = qq.shift()
if (c === target) break
for (const nb of NODES[c].a) {
if (prev[nb] !== -2 || gateBlocked(nb)) continue
prev[nb] = c; qq.push(nb)
}
}
if (prev[target] === -2) return []
const path = []
for (let c = target; c !== -1; c = prev[c]) path.unshift(c)
path.shift()
return path.slice(0, 24)
}
const initEnemies = room => ROOMS[room].enemies.map(e => {
const p0 = hexPx(...(() => { const [c, w] = e.path[0]; return [c, w - (c >> 1)] })())
return { x: p0[0], y: p0[1], f: 0.5, st: 0, sus: 0, wp: 0, lost: 0, hp: e.hp, stun: 0,
pts: e.path.map(([c, w]) => hexPx(c, w - (c >> 1))) }
})
if (!G.en) G.en = initEnemies(G.room)
const bg = sim.fields.get('eq_bg')
if (bg) { bg.transform.x = 256; bg.transform.y = 256; bg.transform.vx = 0; bg.transform.vy = 0 }
const hero = sim.fields.get('eq_hero')
if (hero) {
hero.transform.x = G.x
hero.transform.y = G.y * ISY + IYOFF - 9
hero.transform.vx = 0; hero.transform.vy = 0
}
const sfx = s => { wd.__play_sound = s }
const roomSwap = (to, spx) => {
G.room = to; G.x = spx[0]; G.y = spx[1]; G.node = -1; G.path = []; G.moveT = 1; G.doorArm = 0
G.en = initEnemies(to); G.alarm = 0
G.effCh = EFFECTS.map(e => e.charges); G.charges = 3
wd.__save_game = { slot: 'esper', data: { hasKey: G.hasKey, chest: G.chest, wins: G.wins } }
sfx({ frequency: 520, duration: 0.12, volume: 0.3, type: 'triangle' })
}
const reset = () => {
const d0 = DOORS.find(d => d.room === G.room)
const at = G.room === 1 ? STARTPX : (d0 ? d0.px : STARTPX)
G.x = at[0]; G.y = at[1]; G.node = -1; G.path = []; G.moveT = 1
G.charges = 3; G.shadow = 0; G.alarm = 0; G.hurt = 1
G.effCh = EFFECTS.map(e => e.charges)
G.en = initEnemies(G.room)
sfx([{ frequency: 220, duration: 0.15, volume: 0.4, type: 'sawtooth' },
{ frequency: 110, duration: 0.30, volume: 0.4, type: 'sawtooth' }])
}
// ── 1-4: the cards, from the keyboard ──
const kEdge = k => { const n = wd['key_' + k + '_n'] || 0; G.kN = G.kN || {}; const was = G.kN[k] || 0; G.kN[k] = n; return n > was }
if (kEdge('1')) { G.eff = 0; G.sel = 0; G.zoneT = 3.0; sfx({ frequency: 440, duration: 0.06, volume: 0.2, type: 'sine' }) }
if (kEdge('2')) { G.eff = 1; G.sel = 1; G.zoneT = 3.0; sfx({ frequency: 580, duration: 0.06, volume: 0.2, type: 'sine' }) }
if (kEdge('3')) { G.sel = 2; G.zoneT = 0; sfx({ frequency: 340, duration: 0.08, volume: 0.22, type: 'sine' }) }
if (kEdge('4')) { G.sel = 3; G.zoneT = 0; sfx({ frequency: 180, duration: 0.1, volume: 0.25, type: 'triangle' }) }
// selection is 1-4 (or card clicks) — SHIFT retired
G.zoneT = Math.max(0, G.zoneT - pdt * (G.zoneT > 0 ? 0.55 : 0))
// ── PRESS · AIM · RELEASE (Galen: "click and hold to select the movement
// node — releasing activates, releasing on the player cancels"). Pressing
// the board ARMS an aim; the node/enemy under your finger highlights as you
// hold and slide; RELEASE commits the action; release on the esper cancels.
// Cards (bottom strip) still select on a plain tap. ──
const eqAct = (mx, my) => {
const gpy = (my - IYOFF) / ISY
let hitE = -1
for (let i = 0; i < G.en.length; i++) {
const e = G.en[i]
if (e.st < 0) continue
const sxp = e.x, syp = e.y * ISY + IYOFF - 9
if ((mx - sxp) ** 2 + (my - syp) ** 2 < 170) { hitE = i; break }
}
if (hitE >= 0) {
const e = G.en[hitE]
const eh = pxHex(e.x, e.y), hh = pxHex(G.x, G.y)
let rel = Math.atan2(G.y - e.y, G.x - e.x) - e.f
rel = Math.atan2(Math.sin(rel), Math.cos(rel))
const eff = EFFECTS[G.eff]
if (e.st < 2 && hexDist(eh, hh) <= 1 && Math.abs(rel) > 1.6) {
e.st = -1; G.kills++; G.casts++
G.ray = { t: 1, ax: G.x, ay: G.y, bx: e.x, by: e.y, c: [0.5, 0.3, 0.9] }
sfx({ frequency: 90, duration: 0.22, volume: 0.45, type: 'triangle' })
} else if (G.effCh[G.eff] > 0 && hexDist(eh, hh) <= eff.rangeHex && losClear(G.room, hh, eh)) {
G.effCh[G.eff]--; G.casts++; G.zoneT = 0
G.ray = { t: 1, ax: G.x, ay: G.y, bx: e.x, by: e.y, c: eff.visual.rayColor }
sfx({ frequency: eff.id === 'fire' ? 200 : 620, duration: 0.2, volume: 0.4, type: eff.id === 'fire' ? 'sawtooth' : 'sine' })
if (eff.mechanical.damage) { e.hp -= eff.mechanical.damage; if (e.hp <= 0) { e.st = -1; G.kills++ } }
if (eff.mechanical.calm) { e.sus = 0; e.st = Math.min(e.st, 0) }
if (eff.mechanical.stun) e.stun = eff.mechanical.stun
if (eff.mechanical.loud) {
for (const o of G.en) {
if (o === e || o.st < 0) continue
if (hexDist(pxHex(o.x, o.y), eh) <= 3) { o.sus = Math.min(1, o.sus + 0.6); o.f = Math.atan2(e.y - o.y, e.x - o.x); if (o.st === 0) o.st = 1 }
}
}
} else {
sfx({ frequency: 160, duration: 0.1, volume: 0.25, type: 'square' })
}
} else {
const tn = nearestNode(mx, gpy, S * 0.8)
if (tn >= 0 && !gateBlocked(tn)) {
const path = bfsPath(tn)
if (G.sel === 2 && G.charges > 0 && path.length && path.length <= 3) {
G.node = tn; G.path = []; G.moveT = 1
G.x = NODES[tn].x; G.y = NODES[tn].y
G.charges--; G.shadow = 1.2; G.sel = 0
sfx({ frequency: 340, duration: 0.18, volume: 0.3, type: 'sine' })
} else if (path.length) { G.path = path; if (G.moveT >= 1) { G.fx = G.x; G.fy = G.y } }
}
}
}
if (wd.mouse_down && !G.mHeld) {
G.mHeld = 1
const mx0 = wd.mouse_x, my0 = wd.mouse_y
if (typeof mx0 === 'number' && my0 > 446) {
// the hand: clicks in the bottom strip select cards (screen space)
const ci = Math.round((mx0 - 144) / 56 + 1.5)
if (ci >= 0 && ci <= 3) {
if (ci < 2) { G.eff = ci; G.sel = ci; G.zoneT = 3.0; sfx({ frequency: 440 + ci * 140, duration: 0.06, volume: 0.2, type: 'sine' }) }
else if (ci === 2) { G.sel = 2; G.zoneT = 0; sfx({ frequency: 340, duration: 0.08, volume: 0.22, type: 'sine' }) }
else { G.sel = 3; G.zoneT = 0; sfx({ frequency: 180, duration: 0.1, volume: 0.25, type: 'triangle' }) }
}
} else if (typeof mx0 === 'number') {
G.aiming = 1 // arm — the highlight under the finger is your pre-selection; release commits
}
}
// hold: keep the aimed board point fresh (the node/enemy under the finger
// highlights via the hover uniform mwx/mwy computed below)
if (G.aiming && wd.mouse_down && typeof wd.mouse_x === 'number') {
G.aimMx = G.camx + (wd.mouse_x - 144) / 1.7
G.aimMy = G.camy + (wd.mouse_y - 256) / 1.7
}
// release: commit — unless the finger came up on the esper (cancel)
if (!wd.mouse_down && G.mHeld && G.aiming) {
G.aiming = 0
const mx = G.aimMx, my = G.aimMy
if (typeof mx === 'number') {
const hx = G.x, hy = G.y * ISY + IYOFF - 9
if ((mx - hx) ** 2 + (my - hy) ** 2 < 200) {
sfx({ frequency: 120, duration: 0.06, volume: 0.12, type: 'sine' }) // the aim dissolves
} else {
eqAct(mx, my)
}
}
}
if (!wd.mouse_down) { G.mHeld = 0; G.aiming = 0 }
// ── walk the path node by node ──
if (G.moveT >= 1 && G.path.length) {
const nx = G.path.shift()
G.fx = G.x; G.fy = G.y
G.node = nx; G.moveT = 0
G.face = Math.atan2(NODES[nx].y - G.fy, NODES[nx].x - G.fx)
}
if (G.moveT < 1) {
G.moveT = Math.min(1, G.moveT + pdt * 3.4)
const e2 = G.moveT * G.moveT * (3 - 2 * G.moveT)
G.x = G.fx + (NODES[G.node].x - G.fx) * e2
G.y = G.fy + (NODES[G.node].y - G.fy) * e2
}
// ── SPACE: shadow step (the shadow controller) ──
if (wd.key_space && !G.spaceHeld) {
G.spaceHeld = 1
G.sel = 2
sfx({ frequency: 340, duration: 0.08, volume: 0.22, type: 'sine' })
}
if (false) {
if (G.charges > 0) {
let best = -1, bs = -2
for (const nb of NODES[G.node] ? NODES[G.node].a : []) {
for (const nb2 of NODES[nb].a) {
if (gateBlocked(nb2)) continue
const a = Math.atan2(NODES[nb2].y - G.y, NODES[nb2].x - G.x)
const sc = Math.cos(a - G.face)
if (sc > bs) { bs = sc; best = nb2 }
}
}
if (best >= 0 && bs > 0.3) {
G.charges--; G.shadow = 1.2
G.node = best; G.x = NODES[best].x; G.y = NODES[best].y; G.path = []; G.moveT = 1
sfx({ frequency: 340, duration: 0.18, volume: 0.3, type: 'sine' })
}
}
}
if (!wd.key_space) G.spaceHeld = 0
G.shadow = Math.max(0, G.shadow - pdt)
// ── doors trigger continuously, but only once ARMED (armed = you've stepped clear) ──
let nearDoor = false
for (const d of DOORS) {
if (d.room !== G.room) continue
if (Math.hypot(G.x - d.px[0], G.y - d.px[1]) < S * 0.95) {
nearDoor = true
if (G.doorArm && !(d.gated && !G.hasKey)) { roomSwap(d.to, d.spx); break }
}
}
if (!nearDoor) G.doorArm = 1
// ── arrivals: key, chest (node-scoped) ──
if (G.moveT >= 1) {
if (G.room === 2 && !G.hasKey && Math.hypot(G.x - KEYPX[0], G.y - KEYPX[1]) < S * 0.6) {
G.hasKey = 1
sfx([{ frequency: 660, duration: 0.09, volume: 0.35, type: 'sine' }, { frequency: 990, duration: 0.16, volume: 0.3, type: 'sine' }])
wd.__save_game = { slot: 'esper', data: { hasKey: 1, chest: G.chest, wins: G.wins } }
}
if (G.room === 3 && !G.chest && Math.hypot(G.x - CHESTPX[0], G.y - CHESTPX[1]) < S * 1.1) {
G.chest = 1; G.flare = 1; G.wins++
sfx([{ frequency: 523, duration: 0.12, volume: 0.35, type: 'triangle' }, { frequency: 659, duration: 0.12, volume: 0.35, type: 'triangle' },
{ frequency: 784, duration: 0.12, volume: 0.35, type: 'triangle' }, { frequency: 1046, duration: 0.3, volume: 0.4, type: 'triangle' }])
wd.__save_game = { slot: 'esper', data: { hasKey: G.hasKey, chest: 1, wins: G.wins } }
}
}
// ── enemies ──
const cfgs = ROOMS[G.room].enemies
const hh0 = pxHex(G.x, G.y)
// cover is what you can SEE: standing among the trees counts, no matter how
// the hex math rounds your feet — pixel distance to any tree hex decides
let covered = (NODES[G.node] ? NODES[G.node].c === 1 : false) || landAt(G.room, hh0[0], hh0[1]) === 1
if (!covered) {
for (const th of ROOMS[G.room].land) {
if (th.l !== 1) continue
const tp = hexPx(th.q, th.r)
if (Math.hypot(G.x - tp[0], G.y - tp[1]) < S * 1.05) { covered = true; break }
}
}
// entering cover resets every watcher's stare — the pause is per-entry,
// so diving into trees breaks even an active pursuit for a beat
if (covered && !G.covPrev) { for (const e2 of G.en) e2.stare = 0 }
G.covPrev = covered
let exposed = 0
for (let i = 0; i < G.en.length; i++) {
const cfg = cfgs[i], e = G.en[i]
if (e.st < 0) continue
if (e.stun > 0) { e.stun -= pdt; continue }
const eh = pxHex(e.x, e.y), hh = pxHex(G.x, G.y)
const pxd = Math.hypot(G.x - e.x, G.y - e.y)
let seen = false
let cone = false
if (G.shadow <= 0 && pxd > S * 0.45 && pxd <= cfg.range * S * SQ3) {
let rel = Math.atan2(G.y - e.y, G.x - e.x) - e.f
rel = Math.atan2(Math.sin(rel), Math.cos(rel))
cone = Math.abs(rel) < cfg.half
}
if (cone) {
if (!covered) {
// open ground: ordinary sight
if (losClear(G.room, eh, hh, false)) { seen = true; e.stare = 0 }
} else if ((e.sus > 0.15 || e.st >= 2) && losClear(G.room, eh, hh, true)) {
// the forest hides you — but a suspicious watcher stares through the
// trees, and after a pause the leaves stop being enough
e.stare = (e.stare || 0) + pdt
e.f = Math.atan2(G.y - e.y, G.x - e.x)
if (e.stare > 1.1) seen = true
} else {
e.stare = 0
}
} else {
e.stare = Math.max(0, (e.stare || 0) - pdt * 1.5)
}
if (seen) exposed = 1
if (e.st < 2) {
if (seen) {
e.sus += pdt * (cfg.kind === 'sentry' ? 1.6 : 0.9)
e.f = Math.atan2(G.y - e.y, G.x - e.x)
if (e.sus >= 1) {
e.st = 2; e.lost = 0; G.alarm++
sfx([{ frequency: 740, duration: 0.1, volume: 0.4, type: 'square' }, { frequency: 740, duration: 0.1, volume: 0.35, type: 'square' }])
} else if (e.st === 0 && e.sus > 0.15) e.st = 1
} else {
e.sus = Math.max(0, e.sus - pdt * 0.5)
if (e.sus < 0.1) e.st = 0
if (cfg.kind === 'sentry') e.f += cfg.spin * pdt
else {
const tgt = e.pts[e.wp]
const dx = tgt[0] - e.x, dy = tgt[1] - e.y
const d = Math.hypot(dx, dy)
if (d < 3) e.wp = (e.wp + 1) % e.pts.length
else { e.x += dx / d * cfg.speed * pdt; e.y += dy / d * cfg.speed * pdt; e.f = Math.atan2(dy, dx) }
}
}
} else {
if (seen) e.lost = 0; else e.lost += pdt
if (cfg.kind !== 'sentry') {
const dx = G.x - e.x, dy = G.y - e.y
const d = Math.hypot(dx, dy) || 1
e.x += dx / d * 58 * pdt; e.y += dy / d * 58 * pdt; e.f = Math.atan2(dy, dx)
if (d < 11) { reset(); break }
}
if (e.lost > 2.5 && G.alarm < 3) { e.st = 1; e.sus = 0.5 }
}
}
// the black card, armed: the kill is automatic when you stand behind them
if (G.sel === 3) {
for (const e of G.en) {
if (!e || e.st < 0 || e.st >= 2) continue
const eh2 = pxHex(e.x, e.y), hh2 = pxHex(G.x, G.y)
if (hexDist(eh2, hh2) > 1) continue
let rel2 = Math.atan2(G.y - e.y, G.x - e.x) - e.f
rel2 = Math.atan2(Math.sin(rel2), Math.cos(rel2))
if (Math.abs(rel2) > 1.6) {
e.st = -1; G.kills++
G.ray = { t: 1, ax: G.x, ay: G.y, bx: e.x, by: e.y, c: [0.5, 0.3, 0.9] }
sfx({ frequency: 90, duration: 0.22, volume: 0.45, type: 'triangle' })
}
}
}
G.hurt = Math.max(0, G.hurt - 2.0 * pdt)
G.killT = Math.max(0, (G.killT || 0) - pdt)
G.flare = Math.max(0, G.flare - 0.6 * pdt)
G.ray.t = Math.max(0, G.ray.t - 2.2 * pdt)
// camera eases onto the esper — CLAMPED TO THE ROOM (Galen: the level fills
// the square; the view never shows outside-lattice). Room bbox cached per
// room from its land hexes, in camera screen space; if the room is smaller
// than the view on an axis, center it.
if (G.roomBoxFor !== G.room) {
G.roomBoxFor = G.room
let bx0 = 1e9, bx1 = -1e9, by0 = 1e9, by1 = -1e9
for (const hx of ROOMS[G.room].land) {
const p = hexPx(hx.q, hx.r)
if (p[0] < bx0) bx0 = p[0]; if (p[0] > bx1) bx1 = p[0]
if (p[1] < by0) by0 = p[1]; if (p[1] > by1) by1 = p[1]
}
G.roomBox = { x0: bx0 - S * 1.45, x1: bx1 + S * 1.45, y0: (by0 - S * 1.45) * ISY + IYOFF, y1: (by1 + S * 1.45) * ISY + IYOFF }
}
const HVX = 144 / 1.7, HVY = 256 / 1.7 // half view extent — LOCKSTEP with the shader's ZOOM
const camCl = (v, lo, hi) => (lo > hi ? (lo + hi) / 2 : Math.max(lo, Math.min(hi, v)))
const tcx = camCl(G.x, G.roomBox.x0 + HVX, G.roomBox.x1 - HVX)
const tcy = camCl(G.y * ISY + IYOFF - 9, G.roomBox.y0 + HVY, G.roomBox.y1 - HVY)
G.camx += (tcx - G.camx) * Math.min(1, 4 * pdt)
G.camy += (tcy - G.camy) * Math.min(1, 4 * pdt)
// cursor in world coords, for node hover
let mwx = -999, mwy = -999
if (typeof wd.mouse_x === 'number') {
mwx = G.camx + (wd.mouse_x - 144) / 1.7
mwy = (G.camy + (wd.mouse_y - 256) / 1.7 - IYOFF) / ISY
}
const u = [G.room, G.x, G.y, G.face, covered ? 1 : 0, G.shadow, G.charges, G.alarm, G.flare, exposed, G.hasKey, G.chest]
for (let i = 0; i < 3; i++) {
const e = G.en[i]
if (e && e.st >= 0) u.push(e.x, e.y, e.f, e.st); else u.push(0, 0, 0, -1)
}
u.push(G.hurt, G.eff, G.camx, G.camy, G.ray.t, G.ray.ax, G.ray.ay, G.ray.bx, G.ray.by, G.ray.c[0], G.ray.c[1], G.ray.c[2], mwx, mwy, G.zoneT, G.casts * 7.13, G.effCh[0], G.effCh[1], G.sel)
wd.gpuUniforms = u
// THE UI SYSTEM, glass (was DOM wd.hud in raw px — deprecated). Edge bands
// (vx/vy) hug the TRUE viewport corners on any phone aspect; the alarm is a
// real meter; glass keeps every line readable over the lattice.
{
const effTxt = (G.sel === 3 ? 'SILENT KILL · ∞ — ARMED: stand behind them' : G.sel === 2 ? 'SHADOW ×' + G.charges + ' — hold to a node within 3 steps, release: blink' : EFFECTS[G.eff].name.toUpperCase() + ' ×' + G.effCh[G.eff] + ' · hold, aim, release: cast')
const quest = G.chest ? 'the treasure is yours (' + G.wins + ')' : (G.hasKey ? 'the north door is open' : 'find the key in the Hollow — east door')
const gems = '◈'.repeat(G.charges) + '◇'.repeat(Math.max(0, 3 - G.charges))
let rv = 17
for (const t of [ROOMS[G.room].name, gems, effTxt, quest]) { for (let i = 0; i < t.length; i++) rv = (rv * 33 + t.charCodeAt(i)) % 1000000007 }
rv = rv * 7 + Math.round((G.alarm || 0) * 20)
if (G.uiRev !== rv) {
G.uiRev = rv
const glass = { bg: 'rgba(6,12,11,0.68)', border: 'rgba(159,232,216,0.18)', radius: 7 }
wd.ui = { rev: rv, root: [
{ id: 'eq_status', kind: 'panel', glass, pad: 7, gap: 3, dir: 'col', w: 168, anchor: { vx: 0, vy: 0, dx: 8, dy: 8 }, align: 'tl', children: [
{ id: 'eq_room', kind: 'text', text: ROOMS[G.room].name.toUpperCase(), fontSize: 13, color: '#9fe8d8' },
{ id: 'eq_ch', kind: 'text', text: gems, fontSize: 13, color: '#7fd4ff' },
{ id: 'eq_eff', kind: 'text', text: effTxt, fontSize: 11, color: G.eff === 0 ? '#ff8866' : '#88bbff', wrap: true },
] },
{ id: 'eq_alarmp', kind: 'panel', glass, pad: 6, w: 104, anchor: { vx: 1, vy: 0, dx: -8, dy: 8 }, align: 'tr', children: [
{ id: 'eq_al', kind: 'meter', value: Math.max(0, Math.min(1, (G.alarm || 0) / 3)), hue: '#cc2244', label: 'ALARM' },
] },
{ id: 'eq_questp', kind: 'panel', glass, pad: 6, anchor: { vx: 0.5, vy: 1, dy: -10 }, align: 'bc', children: [
{ id: 'eq_q', kind: 'text', text: quest, fontSize: 12, color: '#c9b370' },
] },
] }
if (wd.hud) { delete wd.hud }
}
}
if (hero) hero.name = 'Esper — ' + G.wins + ' treasures, ' + G.kills + ' kills'
} catch (e) { /* keep the sim alive */ }