From 3bff0eefc3493b56e2b662208ea06ae095d242cc Mon Sep 17 00:00:00 2001 From: lili Date: Wed, 22 Jul 2026 22:23:19 -0700 Subject: [PATCH] feat(release): wire dogfood staging product flow --- deploy/smoke-test.sh | 621 +++++++++++++----- deploy/tests/test-dogfood-smoke.sh | 113 ++++ game-admin/.env.stage | 18 +- game-studio/.env.staging | 7 +- game-studio/public/mock-manifests/9001.json | 1 - game-studio/public/mock-manifests/run_u5u2.sh | 54 -- .../public/mock-manifests/u5u2_walk.py | 392 ----------- game-studio/src/host/GamePlayer.vue | 20 +- game-studio/src/store/user.ts | 11 +- game-studio/src/views/dashboard/Dashboard.vue | 29 +- game-studio/src/views/login/Login.vue | 11 +- game-studio/src/vite-env.d.ts | 4 +- 12 files changed, 642 insertions(+), 639 deletions(-) create mode 100755 deploy/tests/test-dogfood-smoke.sh delete mode 100644 game-studio/public/mock-manifests/9001.json delete mode 100644 game-studio/public/mock-manifests/run_u5u2.sh delete mode 100644 game-studio/public/mock-manifests/u5u2_walk.py diff --git a/deploy/smoke-test.sh b/deploy/smoke-test.sh index 7763dbb5..d59c4cfd 100755 --- a/deploy/smoke-test.sh +++ b/deploy/smoke-test.sh @@ -1,182 +1,487 @@ #!/usr/bin/env bash -# ============================================================================= -# 造梦/绘境AI · staging 五链路冒烟门(deploy smoke gate) -# ----------------------------------------------------------------------------- -# 用途:每次 staging 部署后跑一遍,**秒级**确认 5 条端到端闭环的关键 API 在岗、 -# 鉴权/CORS/质量分回路结构正常。这是「部署后是否健康」的就绪检查(liveness/ -# readiness),**不是**深度 e2e(深度 e2e 走 agent-loop orchestrator 批跑)。 -# -# 设计原则: -# - 默认 **以读为主**(health/列表/feed/钱包/包体),可随时重复跑;唯一写=studio/draft 建一条 -# 草稿(status 0,不发布、无下游副作用、可忽略),用于验证创作入口+鉴权+gameId 分配在岗; -# - `--deep` 才走重写路径(generate 入队触发真实生成 + 遥测事件 + 互动),会落库,按需开启; -# - 每条链路逐项 PASS/FAIL,末尾总判 + 退出码(0=全过,1=有 FAIL)——可挂 CI/部署脚本。 -# -# 用法: -# bash deploy/smoke-test.sh # 默认 staging 只读冒烟 -# BASE=http://100.64.0.7:48080 TOKEN=test1 bash deploy/smoke-test.sh -# bash deploy/smoke-test.sh --deep # 含写路径(生成入队+遥测事件) -# -# 约定:内网地址/测试 token 可入仓(内网非密);**真实密钥严禁写入本文件**。 -# ============================================================================= +# 绘境AI staging 冒烟门。 +# 默认模式只做公开读 readiness;--dogfood-e2e 才执行真实创作、审核、试玩会话和遥测写链。 set -uo pipefail -BASE="${BASE:-http://100.64.0.7:48080}" # staging 后端(mini-desktop) -TOKEN="${TOKEN:-test1}" # staging 批跑/冒烟测试态 token(种子创作者) +BASE="${BASE:-http://100.64.0.7:48080}" +STUDIO_BASE="${STUDIO_BASE:-http://100.64.0.7:4173}" +ORIGIN="${ORIGIN:-$STUDIO_BASE}" TENANT="${TENANT:-1}" -ORIGIN="${ORIGIN:-http://100.64.0.7:4173}" # 前端 origin(验 CORS 同源放行) -DEEP=0 -[ "${1:-}" = "--deep" ] && DEEP=1 +MODE="readiness" +if [ "${1:-}" = "--dogfood-e2e" ]; then + MODE="dogfood-e2e" +elif [ -n "${1:-}" ]; then + printf '未知参数:%s\n用法:%s [--dogfood-e2e]\n' "$1" "$0" >&2 + exit 2 +fi -PASS=0; FAIL=0; FAILED_ITEMS=() +PASS=0 +FAIL=0 +FAILED_ITEMS=() +LAST_HTTP="" +LAST_BODY="" +BODY_FILE="$(mktemp)" +RUN_ID="${RUN_ID:-$(date -u +%Y%m%dT%H%M%SZ)-$$}" +EVIDENCE_OUT="${EVIDENCE_OUT:-/tmp/dogfood-e2e-${RUN_ID}.json}" +TASK_CHAIN_ID="" +AIGC_TASK_ID="" +GAME_ID="" +VERSION_ID="" +SESSION_ID="" +TELEMETRY_TRACE_ID="" +ARTIFACT_HASH="" +E2E_STATUS="not-started" +FAILURE_ITEM="" -# 颜色(非 TTY 自动降级为纯文本) -if [ -t 1 ]; then G='\033[0;32m'; R='\033[0;31m'; Y='\033[0;33m'; B='\033[0;34m'; N='\033[0m'; else G=''; R=''; Y=''; B=''; N=''; fi +cleanup() { + rm -f "$BODY_FILE" +} +trap cleanup EXIT -_log() { printf "%b\n" "$*"; } -_head() { printf "\n%b== %s ==%b\n" "$B" "$1" "$N"; } +if [ -t 1 ]; then + G='\033[0;32m'; R='\033[0;31m'; B='\033[0;34m'; N='\033[0m' +else + G=''; R=''; B=''; N='' +fi -# api METHOD PATH [DATA] —— 返回 "HTTP_CODE\nBODY";鉴权头正确引用(防 'Bearer test1' 空格被拆) -api() { - local method="$1" path="$2" data="${3:-}" - local url="$path"; [[ "$path" == http* ]] || url="$BASE$path" +log() { printf "%b\n" "$*"; } +head_line() { printf "\n%b== %s ==%b\n" "$B" "$1" "$N"; } + +# 请求响应分别放在全局 LAST_HTTP/LAST_BODY,避免 token 或响应体进入日志。 +request() { + local token="$1" method="$2" path="$3" data="${4:-}" + local url="$path" + local -a args + [[ "$path" == http://* || "$path" == https://* ]] || url="$BASE$path" + args=(-sS --connect-timeout 5 --max-time 30 -o "$BODY_FILE" -w '%{http_code}' -X "$method" + -H "tenant-id: $TENANT" -H "Origin: $ORIGIN") + [ -n "$token" ] && args+=(-H "Authorization: Bearer $token") if [ -n "$data" ]; then - curl -s -w $'\n%{http_code}' -X "$method" "$url" \ - -H "Authorization: Bearer $TOKEN" -H "tenant-id: $TENANT" \ - -H "Origin: $ORIGIN" -H "Content-Type: application/json" -d "$data" 2>/dev/null + args+=(-H 'Content-Type: application/json' --data "$data") + fi + LAST_HTTP="$(curl "${args[@]}" "$url" 2>/dev/null || printf '000')" + LAST_BODY="$(<"$BODY_FILE")" +} + +# 从 stdin JSON 按点路径读取标量;缺字段或非 JSON 返回空串。 +json_path() { + local path="$1" + python3 -c ' +import json,sys +path=sys.argv[1].split(".") if sys.argv[1] else [] +try: + value=json.load(sys.stdin) + for part in path: + value=value[int(part)] if isinstance(value,list) else value.get(part) + if value is None: break + if value is None: print("") + elif isinstance(value,bool): print("true" if value else "false") + elif isinstance(value,(dict,list)): print(json.dumps(value,separators=(",",":"),ensure_ascii=False)) + else: print(value) +except Exception: + print("") +' "$path" +} + +body_value() { + printf '%s' "$LAST_BODY" | json_path "$1" +} + +envelope_ok() { + [ "$LAST_HTTP" = "200" ] && [ "$(body_value code)" = "0" ] +} + +mark_pass() { + PASS=$((PASS + 1)) + log " ${G}PASS${N} $1" +} + +mark_fail() { + FAIL=$((FAIL + 1)) + FAILED_ITEMS+=("$1") + log " ${R}FAIL${N} $1" +} + +# 狗粮安全边界业务码:逐字对账后端各模块 ErrorCodeConstants,禁止用“任意失败”冒充安全门生效。 +CODE_PASSWORD_REGISTER_DISABLED=1002090007 # system PASSWORD_REGISTER_DISABLED = 1_002_090_007 +CODE_DOGFOOD_SMS_DISABLED=1002090008 # system DOGFOOD_SMS_DISABLED = 1_002_090_008 +CODE_AD_DOGFOOD_DISABLED=1111002006 # ad AD_DOGFOOD_DISABLED = 1_111_002_006 +CODE_TRADE_DOGFOOD_DISABLED=1106008000 # trade TRADE_DOGFOOD_MONETIZATION_DISABLED = 1_106_008_000 + +expect_business_code() { + local name="$1" expected="$2" token="$3" method="$4" path="$5" data="${6:-}" + request "$token" "$method" "$path" "$data" + local actual + actual="$(body_value code)" + if [ "$LAST_HTTP" = "200" ] && [ "$actual" = "$expected" ]; then + mark_pass "${name}(业务码=${expected})" else - curl -s -w $'\n%{http_code}' -X "$method" "$url" \ - -H "Authorization: Bearer $TOKEN" -H "tenant-id: $TENANT" \ - -H "Origin: $ORIGIN" 2>/dev/null + e2e_fail "$name 未返回预期业务码(expected=$expected actual=${actual:-none} http=${LAST_HTTP})" fi } -# check NAME METHOD PATH MODE [EXTRA] [DATA] -# MODE=health → HTTP 200 即过(actuator) -# MODE=envelope → HTTP 200 ∧ huijing body code==0 即过 -# MODE=envelope_nonempty → 再要求 data 非空(list 长度≥1 或 data 非 null) -# EXTRA:envelope_nonempty 下可传 jq 风格 python 取数表达式(默认探 data.list 或 data) -check() { - local name="$1" method="$2" path="$3" mode="$4" data="${5:-}" - local out code body - out="$(api "$method" "$path" "$data")" - code="$(printf '%s' "$out" | tail -n1)" - body="$(printf '%s' "$out" | sed '$d')" - local ok=0 detail="" - case "$mode" in - health) - [ "$code" = "200" ] && ok=1; detail="http=$code" ;; - envelope|envelope_nonempty) - if [ "$code" = "200" ]; then - # 解析 huijing 信封 {code,data};envelope_nonempty 再判 data 是否非空 - local pyout - pyout="$(printf '%s' "$body" | python3 -c " -import sys,json -try: d=json.load(sys.stdin) -except Exception as e: print('ERR|parse'); sys.exit() -c=d.get('code') -data=d.get('data') -if isinstance(data,dict): n=len(data.get('list')) if isinstance(data.get('list'),list) else (1 if data else 0) -elif isinstance(data,list): n=len(data) -elif data is None: n=0 -else: n=1 -print('%s|%s'%(c,n)) -" 2>/dev/null)" - local ecode="${pyout%%|*}" ecount="${pyout##*|}" - if [ "$ecode" = "0" ]; then - if [ "$mode" = "envelope_nonempty" ]; then - [ "${ecount:-0}" -ge 1 ] 2>/dev/null && ok=1 || ok=0 - detail="code=0 count=$ecount" - else ok=1; detail="code=0"; fi - else detail="http=$code code=$ecode"; fi - else detail="http=$code"; fi ;; - esac - if [ "$ok" = "1" ]; then PASS=$((PASS+1)); _log " ${G}✓${N} $name ($detail)"; else FAIL=$((FAIL+1)); FAILED_ITEMS+=("$name [$detail]"); _log " ${R}✗${N} $name ($detail)"; fi +read_check() { + local name="$1" path="$2" require_data="${3:-0}" + request "" GET "$path" + if envelope_ok; then + if [ "$require_data" = "1" ] && ! printf '%s' "$LAST_BODY" | python3 -c ' +import json,sys +value=json.load(sys.stdin).get("data") +assert value is not None and value != [] and value != {} and value != "" +' >/dev/null 2>&1; then + mark_fail "${name}(data 为空)" + else + mark_pass "$name" + fi + else + mark_fail "${name}(http=${LAST_HTTP} code=$(body_value code))" + fi } -_log "${B}绘境AI staging 五链路冒烟门${N} BASE=$BASE TOKEN=${TOKEN:0:2}*** deep=$DEEP" +require_env() { + local name + for name in "$@"; do + if [ -z "${!name:-}" ]; then + log "${R}缺少必填环境变量:$name${N}" >&2 + exit 2 + fi + done +} -# ---------------- 基础设施 + 鉴权 ---------------- -_head "基础设施 / 鉴权" -check "infra: actuator/health" GET /actuator/health health -check "auth: passport/me(token 有效)" GET /app-api/passport/me envelope +write_evidence() { + local status="$1" + mkdir -p "$(dirname "$EVIDENCE_OUT")" + E2E_STATUS="$status" RUN_ID="$RUN_ID" TASK_CHAIN_ID="$TASK_CHAIN_ID" AIGC_TASK_ID="$AIGC_TASK_ID" \ + GAME_ID="$GAME_ID" VERSION_ID="$VERSION_ID" SESSION_ID="$SESSION_ID" \ + TELEMETRY_TRACE_ID="$TELEMETRY_TRACE_ID" ARTIFACT_HASH="$ARTIFACT_HASH" FAILURE_ITEM="$FAILURE_ITEM" \ + python3 - "$EVIDENCE_OUT" <<'PY' +import json,os,sys +def number(name): + raw=os.environ.get(name,"") + return int(raw) if raw.isdigit() else None +doc={ + "runId":os.environ["RUN_ID"], "status":os.environ["E2E_STATUS"], + "taskChainId":number("TASK_CHAIN_ID"), "aigcTaskId":number("AIGC_TASK_ID"), + "gameId":number("GAME_ID"), "versionId":number("VERSION_ID"), + "sessionId":number("SESSION_ID"), "telemetryTraceId":os.environ.get("TELEMETRY_TRACE_ID") or None, + "artifactHash":os.environ.get("ARTIFACT_HASH") or None, + "result":{"verdict":os.environ["E2E_STATUS"],"failedItem":os.environ.get("FAILURE_ITEM") or None}, + "browserQaResult":"pending", +} +with open(sys.argv[1],"w",encoding="utf-8") as f: + json.dump(doc,f,ensure_ascii=False,indent=2) +PY + chmod 600 "$EVIDENCE_OUT" +} -# ---------------- 链路① 创作 → 生成 → 预览试玩 ---------------- -_head "链路① 创作→生成→预览" -# 四模板就绪(执行器 banner 同源:getTemplateList 应返 clicker/merge/idle/tycoon) -out="$(api GET /app-api/aigc/template/list)" -tcount="$(printf '%s' "$out" | sed '$d' | python3 -c "import sys,json -try: d=json.load(sys.stdin); data=d.get('data') or []; print(len(data) if isinstance(data,list) else 0) -except: print(-1)" 2>/dev/null)" -if [ "${tcount:-0}" -ge 4 ] 2>/dev/null; then PASS=$((PASS+1)); _log " ${G}✓${N} aigc/template/list(四模板就绪 count=${tcount})"; else FAIL=$((FAIL+1)); FAILED_ITEMS+=("template/list count=$tcount"); _log " ${R}✗${N} aigc/template/list(期望≥4,实=${tcount})"; fi -check "studio/draft(创建草稿拿 gameId)" POST /app-api/studio/draft envelope_nonempty '{"title":"冒烟门-clicker","templateId":"clicker"}' +e2e_fail() { + local message="$1" + E2E_STATUS="failed" + FAILURE_ITEM="$message" + write_evidence "$E2E_STATUS" + log "${R}DOGFOOD_E2E_FAIL${N} $message" + log "证据:$EVIDENCE_OUT" + exit 1 +} -# ---------------- 链路② 发布 → 审核 → 游戏流 ---------------- -_head "链路② 发布→审核→游戏流" -check "feed/stream(内容池非空)" GET "/app-api/feed/stream?size=5" envelope_nonempty -check "feed/zones(分区结构)" GET /app-api/feed/zones envelope -check "project/my(创作者项目区)" GET "/app-api/project/my?pageNo=1&pageSize=5" envelope +e2e_require_ok() { + local name="$1" + if envelope_ok; then + mark_pass "$name" + else + e2e_fail "${name}(http=${LAST_HTTP} code=$(body_value code))" + fi +} -# ---------------- 链路③ 游戏流 → 试玩 → 互动 → 分享 ---------------- -_head "链路③ 试玩→互动→分享" -# 真实取包走 GET /app-api/runtime/package/{versionId}(descriptor 含 entry/manifest/sandbox); -# 注意:feed item 的 packageUrl 字段恒为 null,**不是**取包入口(runtime.ts:25 实证)。 -VID="$(api GET "/app-api/feed/stream?size=1" | sed '$d' | python3 -c " -import sys,json -try: - d=json.load(sys.stdin); lst=(d.get('data') or {}).get('list') or [] - print(lst[0].get('versionId','') if lst else '') -except: print('')" 2>/dev/null)" -if [ -n "$VID" ] && [ "$VID" != "None" ]; then - check "runtime/package/{versionId}(试玩取包 vid=${VID})" GET "/app-api/runtime/package/$VID" envelope -else FAIL=$((FAIL+1)); FAILED_ITEMS+=("feed 首条无 versionId"); _log " ${R}✗${N} feed 首条无 versionId(无法验取包)"; fi - -# ---------------- 链路④ 广告 → 收益 → 钱包 ---------------- -_head "链路④ 广告→收益→钱包" -check "ad/slot/list-enabled(广告位)" GET /app-api/ad/slot/list-enabled envelope -check "trade/account/mine(钱包)" GET /app-api/trade/account/mine envelope -check "trade/income/page(收益流水)" GET "/app-api/trade/income/page?pageNo=1&pageSize=5" envelope - -# ---------------- 链路⑤ 数据回路(遥测→质量分→feed 重排)---------------- -_head "链路⑤ 数据回路(质量分回灌)" -# 结构断言:feed 首条携带 qualityScore(feed 排序由质量分驱动 = 回路已回灌) -QSCORE="$(api GET "/app-api/feed/stream?size=1" | sed '$d' | python3 -c " -import sys,json -try: - d=json.load(sys.stdin); lst=(d.get('data') or {}).get('list') or [] - print(lst[0].get('qualityScore') if lst and lst[0].get('qualityScore') is not None else 'NONE') -except: print('NONE')" 2>/dev/null)" -if [ "$QSCORE" != "NONE" ] && [ -n "$QSCORE" ]; then PASS=$((PASS+1)); _log " ${G}✓${N} feed 携带 qualityScore=${QSCORE}(质量分回路已回灌)"; else FAIL=$((FAIL+1)); FAILED_ITEMS+=("feed 无 qualityScore"); _log " ${R}✗${N} feed 首条无 qualityScore"; fi - -# ---------------- --deep:写路径(生成入队 + 遥测事件)---------------- -if [ "$DEEP" = "1" ]; then - _head "深度(--deep)写路径:创作→生成入队" - # 真实创作链写路径:create_draft → studio_generate(契约 backend_gw.py:123/130)。 - # generate body = {sessionId: draft.id, prompt}(sessionId=draft 的 id 非 gameId)。 - # draft 不设 templateId:便宜档真实流「一句话不选模板」→ 后端归一 generic → 分派闸(templateId=='generic')投外置 worker :9501; - # 旧四模板名(idle/clicker 等)会撞硬相等分派闸、fall-through 到进程内 LLM 旧路、到不了 worker(见 staging-ops §8)。 - # 入队触发 executor 派发 worker(异步产包经回调落版本),本冒烟**只验入队 200/code=0**,不等生成完成。 - # 不验 telemetry/interact 写:① 遥测回路产出已由链路⑤(feed 携 qualityScore)结构性验证; - # ② 完整深度 e2e(play→judge→publish)是 agent-loop orchestrator 批跑的职责,不与冒烟门重叠。 - SID="$(api POST /app-api/studio/draft '{"title":"冒烟门-deep-generic"}' | sed '$d' | python3 -c "import sys,json -try: print((json.load(sys.stdin).get('data') or {}).get('id','')) -except: print('')" 2>/dev/null)" - if [ -n "$SID" ] && [ "$SID" != "None" ]; then - check "studio/generate(入队 sessionId=${SID})" POST /app-api/studio/generate envelope \ - "{\"sessionId\":$SID,\"prompt\":\"一个挂机种树攒果实的放置小游戏\"}" - else FAIL=$((FAIL+1)); FAILED_ITEMS+=("deep: draft 无 id"); _log " ${R}✗${N} deep: draft 未拿到 sessionId(id)"; fi +# 写模式具有明确副作用,账号凭据与人工确认必须在任何网络请求前齐备。 +if [ "$MODE" = "dogfood-e2e" ]; then + require_env DOGFOOD_CREATOR_USERNAME DOGFOOD_CREATOR_PASSWORD \ + DOGFOOD_REVIEWER_USERNAME DOGFOOD_REVIEWER_PASSWORD \ + DOGFOOD_READONLY_USERNAME DOGFOOD_READONLY_PASSWORD \ + DOGFOOD_E2E_ACK + if [ "$DOGFOOD_E2E_ACK" != "I_ACKNOWLEDGE_DOGFOOD_WRITES" ]; then + log "${R}DOGFOOD_E2E_ACK 无效;必须显式设置为 I_ACKNOWLEDGE_DOGFOOD_WRITES${N}" >&2 + exit 2 + fi fi -# ---------------- 总判 ---------------- -_head "总判" -TOTAL=$((PASS+FAIL)) -if [ "$FAIL" = "0" ]; then - _log "${G}SMOKE_PASS${N} $PASS/$TOTAL 全过" - echo "SMOKE_VERDICT=PASS" - exit 0 +head_line "公开只读 readiness" +request "" GET /actuator/health +if [ "$LAST_HTTP" = "200" ]; then mark_pass "backend health"; else mark_fail "backend health(http=${LAST_HTTP})"; fi +read_check "feed zones" /app-api/feed/zones 1 +read_check "feed stream" "/app-api/feed/stream?size=1" 1 + +FIRST_VERSION_ID="" +request "" GET "/app-api/feed/stream?size=1" +if envelope_ok; then + FIRST_VERSION_ID="$(body_value data.list.0.versionId)" +fi +if [ -n "$FIRST_VERSION_ID" ]; then + read_check "published runtime package(versionId=${FIRST_VERSION_ID})" "/app-api/runtime/package/${FIRST_VERSION_ID}?scene=play" 1 else - _log "${R}SMOKE_FAIL${N} $PASS/$TOTAL 过,$FAIL 项 FAIL:" - for it in "${FAILED_ITEMS[@]}"; do _log " ${R}-${N} $it"; done - echo "SMOKE_VERDICT=FAIL" + mark_fail "feed 首条缺 versionId,无法校验公开运行包" +fi + +if [ "$MODE" = "readiness" ]; then + head_line "总判" + if [ "$FAIL" -eq 0 ]; then + log "${G}SMOKE_PASS${N} $PASS/$((PASS + FAIL)) 全过(默认模式无写请求)" + log "SMOKE_VERDICT=PASS" + exit 0 + fi + log "${R}SMOKE_FAIL${N} $PASS/$((PASS + FAIL)) 通过" + printf ' - %s\n' "${FAILED_ITEMS[@]}" + log "SMOKE_VERDICT=FAIL" exit 1 fi + +# 以下模式会创建并发布一款带 RUN_ID 的游戏、写试玩会话和遥测事件,不做隐式清理。 +E2E_TIMEOUT_SEC="${E2E_TIMEOUT_SEC:-1200}" +POLL_INTERVAL_SEC="${POLL_INTERVAL_SEC:-5}" +E2E_PROMPT="${E2E_PROMPT:-做一个躲避陨石、可以计分并有明确结束状态的小游戏}" +E2E_TITLE="${E2E_TITLE:-dogfood-${RUN_ID}}" +export DOGFOOD_CREATOR_USERNAME DOGFOOD_CREATOR_PASSWORD DOGFOOD_REVIEWER_USERNAME \ + DOGFOOD_REVIEWER_PASSWORD DOGFOOD_READONLY_USERNAME DOGFOOD_READONLY_PASSWORD \ + E2E_PROMPT E2E_TITLE RUN_ID + +head_line "真实账号登录(凭据不落日志)" +CREATOR_LOGIN_BODY="$(python3 - <<'PY' +import json,os +print(json.dumps({"username":os.environ["DOGFOOD_CREATOR_USERNAME"],"password":os.environ["DOGFOOD_CREATOR_PASSWORD"]})) +PY +)" +request "" POST /app-api/passport/password-login "$CREATOR_LOGIN_BODY" +e2e_require_ok "creator password login" +CREATOR_TOKEN="$(body_value data.accessToken)" +CREATOR_USER_ID="$(body_value data.userId)" +CREATOR_FLAG="$(body_value data.creatorFlag)" +[ -n "$CREATOR_TOKEN" ] || e2e_fail "creator 登录响应缺 accessToken" +[ "$CREATOR_FLAG" = "1" ] || e2e_fail "creatorFlag=${CREATOR_FLAG},不是预置创作者" + +admin_login() { + local role="$1" user_var="$2" pass_var="$3" + local payload + export DOGFOOD_LOGIN_USERNAME="${!user_var}" DOGFOOD_LOGIN_PASSWORD="${!pass_var}" + payload="$(python3 - <<'PY' +import json,os +print(json.dumps({"username":os.environ["DOGFOOD_LOGIN_USERNAME"],"password":os.environ["DOGFOOD_LOGIN_PASSWORD"]})) +PY +)" + request "" POST /admin-api/system/auth/login "$payload" + envelope_ok || e2e_fail "$role 登录失败(http=$LAST_HTTP code=$(body_value code))" + ADMIN_LOGIN_TOKEN="$(body_value data.accessToken)" + [ -n "$ADMIN_LOGIN_TOKEN" ] || e2e_fail "$role 登录响应缺 accessToken" +} +ADMIN_LOGIN_TOKEN="" +admin_login reviewer DOGFOOD_REVIEWER_USERNAME DOGFOOD_REVIEWER_PASSWORD +REVIEWER_TOKEN="$ADMIN_LOGIN_TOKEN" +admin_login readonly DOGFOOD_READONLY_USERNAME DOGFOOD_READONLY_PASSWORD +READONLY_TOKEN="$ADMIN_LOGIN_TOKEN" +mark_pass "reviewer / readonly password login" + +head_line "狗粮安全边界精确负例" +# 用户名最长 30 位,保留 dogreg + RUN_ID 数字前 24 位。 +REGISTER_NEGATIVE_BODY="$(REGISTER_NAME="dogreg${RUN_ID//[^0-9]/}" python3 - <<'PY' +import json,os +print(json.dumps({"username":os.environ["REGISTER_NAME"][:30],"password":"DogfoodNoRegister1"})) +PY +)" +expect_business_code "password register disabled" "$CODE_PASSWORD_REGISTER_DISABLED" "" POST \ + /app-api/passport/password-register "$REGISTER_NEGATIVE_BODY" +expect_business_code "SMS send disabled" "$CODE_DOGFOOD_SMS_DISABLED" "" POST \ + /app-api/passport/send-sms-code '{"mobile":"13800000000"}' +expect_business_code "ad slot disabled" "$CODE_AD_DOGFOOD_DISABLED" "$CREATOR_TOKEN" GET \ + /app-api/ad/slot/list-enabled +expect_business_code "charge balance disabled" "$CODE_TRADE_DOGFOOD_DISABLED" "$CREATOR_TOKEN" GET \ + /app-api/trade/charge/points-balance +expect_business_code "withdraw page disabled" "$CODE_TRADE_DOGFOOD_DISABLED" "$CREATOR_TOKEN" GET \ + '/app-api/trade/withdraw/page?pageNo=1&pageSize=1' + +head_line "创建并等待生成终态" +CREATE_BODY="$(python3 - <<'PY' +import json,os +print(json.dumps({"title":os.environ["E2E_TITLE"],"prompt":os.environ["E2E_PROMPT"], + "templateId":"generic","idempotencyKey":"dogfood-"+os.environ["RUN_ID"]},ensure_ascii=False)) +PY +)" +request "$CREATOR_TOKEN" POST /app-api/studio/create "$CREATE_BODY" +e2e_require_ok "studio one-step create" +TASK_CHAIN_ID="$(body_value data.id)" +AIGC_TASK_ID="$(body_value data.aigcTaskId)" +GAME_ID="$(body_value data.gameId)" +[ -n "$TASK_CHAIN_ID" ] && [ -n "$GAME_ID" ] || e2e_fail "create 响应缺 taskChainId/gameId" + +deadline=$((SECONDS + E2E_TIMEOUT_SEC)) +while [ "$SECONDS" -lt "$deadline" ]; do + request "$CREATOR_TOKEN" GET "/app-api/studio/task/$TASK_CHAIN_ID" + envelope_ok || e2e_fail "轮询任务链失败(http=$LAST_HTTP code=$(body_value code))" + task_status="$(body_value data.status)" + AIGC_TASK_ID="$(body_value data.aigcTaskId)" + GAME_ID="$(body_value data.gameId)" + VERSION_ID="$(body_value data.versionId)" + if [ "$task_status" = "2" ]; then break; fi + [ "$task_status" != "3" ] || e2e_fail "生成进入失败终态(taskChainId=${TASK_CHAIN_ID} aigcTaskId=${AIGC_TASK_ID})" + sleep "$POLL_INTERVAL_SEC" +done +[ "${task_status:-}" = "2" ] || e2e_fail "等待生成终态超时 ${E2E_TIMEOUT_SEC}s" +[ -n "$VERSION_ID" ] || e2e_fail "生成完成但 versionId 为空" +mark_pass "generation terminal success(taskChainId=${TASK_CHAIN_ID} versionId=${VERSION_ID})" + +head_line "预览包与发布门禁" +request "$CREATOR_TOKEN" GET "/app-api/runtime/package/$VERSION_ID?scene=preview" +e2e_require_ok "creator preview package descriptor" +ARTIFACT_HASH="$(body_value data.checksum)" +if ! [[ "$ARTIFACT_HASH" =~ ^[0-9a-f]{64}$ ]]; then + e2e_fail "预览运行包缺少合法 SHA-256 checksum" +fi +request "$CREATOR_TOKEN" GET "/app-api/runtime/package/$VERSION_ID/manifest?scene=preview" +if [ "$LAST_HTTP" = "200" ] && printf '%s' "$LAST_BODY" | python3 -m json.tool >/dev/null 2>&1; then + mark_pass "creator preview manifest" +else + e2e_fail "预览 manifest 不可读(http=${LAST_HTTP})" +fi + +LAUNCH_ZONE_ID="${DOGFOOD_ZONE_ID:-}" +if [ -z "$LAUNCH_ZONE_ID" ]; then + request "" GET /app-api/feed/zones + envelope_ok || e2e_fail "无法读取发布专区" + LAUNCH_ZONE_ID="$(body_value data.0.id)" +fi +[ -n "$LAUNCH_ZONE_ID" ] || e2e_fail "没有可用发布专区,请设置 DOGFOOD_ZONE_ID" +PUBLISH_BODY="{\"versionId\":$VERSION_ID,\"launchZoneId\":$LAUNCH_ZONE_ID}" +request "$CREATOR_TOKEN" POST "/app-api/project/$GAME_ID/publish" "$PUBLISH_BODY" +e2e_require_ok "submit publish" +[ "$(body_value data.admitted)" = "true" ] || e2e_fail "发布门禁未全通过:$(body_value data.gates)" + +# 扫完整个主 feed:0=找到并输出 versionId,1=确认到底仍不存在,2=请求/游标异常导致无法下结论。 +scan_main_feed() { + local cursor="" page encoded found + for page in $(seq 1 20); do + if [ -n "$cursor" ]; then + encoded="$(python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.argv[1],safe=""))' "$cursor")" + request "" GET "/app-api/feed/stream?size=30&cursor=$encoded" + else + request "" GET "/app-api/feed/stream?size=30" + fi + envelope_ok || return 2 + found="$(printf '%s' "$LAST_BODY" | python3 -c 'import json,sys; gid=int(sys.argv[1]); d=json.load(sys.stdin); xs=(d.get("data") or {}).get("list",[]); print(next((x.get("versionId","") for x in xs if int(x.get("gameId",-1))==gid),""))' "$GAME_ID")" + if [ -n "$found" ]; then printf '%s' "$found"; return 0; fi + [ "$(body_value data.hasMore)" = "true" ] || return 1 + cursor="$(body_value data.nextCursor)" + [ -n "$cursor" ] || return 2 + done + return 2 +} + +head_line "审核队列与 RBAC 负验" +request "$REVIEWER_TOKEN" GET "/admin-api/project/review/page?pageNo=1&pageSize=100&status=1" +e2e_require_ok "reviewer review queue" +if ! printf '%s' "$LAST_BODY" | python3 -c 'import json,sys; gid=int(sys.argv[1]); d=json.load(sys.stdin); assert any(int(x.get("id",-1))==gid for x in (d.get("data") or {}).get("list",[]))' "$GAME_ID"; then + e2e_fail "新游戏未进入审核队列(gameId=${GAME_ID})" +fi +REVIEW_BODY="{\"gameId\":$GAME_ID,\"versionId\":$VERSION_ID,\"decision\":1,\"reason\":\"dogfood $RUN_ID\"}" +request "$READONLY_TOKEN" POST /admin-api/project/review "$REVIEW_BODY" +if envelope_ok; then + e2e_fail "readonly 账号竟可审核,RBAC 负验失败" +fi +request "$REVIEWER_TOKEN" GET "/admin-api/project/review/page?pageNo=1&pageSize=100&status=1" +if ! envelope_ok || ! printf '%s' "$LAST_BODY" | python3 -c 'import json,sys; gid=int(sys.argv[1]); d=json.load(sys.stdin); assert any(int(x.get("id",-1))==gid for x in (d.get("data") or {}).get("list",[]))' "$GAME_ID"; then + e2e_fail "readonly 拒绝后审核对象状态发生变化" +fi +mark_pass "readonly 审核写权限被拒绝且状态未变" + +PRE_APPROVE_VERSION="$(scan_main_feed)" +PRE_APPROVE_SCAN_RC=$? +if [ "$PRE_APPROVE_SCAN_RC" -eq 0 ]; then + e2e_fail "审核通过前 gameId=$GAME_ID 已进入主 feed(versionId=${PRE_APPROVE_VERSION})" +elif [ "$PRE_APPROVE_SCAN_RC" -eq 1 ]; then + mark_pass "pre-approve game absent from main feed" +else + e2e_fail "审核通过前无法完成主 feed 全量缺席证明" +fi + +request "$REVIEWER_TOKEN" POST /admin-api/project/review "$REVIEW_BODY" +e2e_require_ok "reviewer approve" + +head_line "发布态、feed 与公开运行包" +deadline=$((SECONDS + 90)) +project_status="" +while [ "$SECONDS" -lt "$deadline" ]; do + request "$CREATOR_TOKEN" GET "/app-api/project/$GAME_ID" + if envelope_ok; then project_status="$(body_value data.status)"; fi + [ "$project_status" = "4" ] && break + sleep 2 +done +[ "$project_status" = "4" ] || e2e_fail "审核通过后项目未进入 published(4)" +mark_pass "project published status=4" + +FEED_VERSION_ID="$(scan_main_feed)" +POST_APPROVE_SCAN_RC=$? +[ "$POST_APPROVE_SCAN_RC" -eq 0 ] || e2e_fail "审核通过后未能在主 feed 找到 gameId=${GAME_ID}(scanRc=${POST_APPROVE_SCAN_RC})" +[ "$FEED_VERSION_ID" = "$VERSION_ID" ] || e2e_fail "feed versionId=$FEED_VERSION_ID 与生成版本 $VERSION_ID 不一致" +mark_pass "exact game appears in main feed" +request "" GET "/app-api/runtime/package/$VERSION_ID?scene=play" +e2e_require_ok "public play package descriptor" +request "" GET "/app-api/runtime/package/$VERSION_ID/manifest?scene=play" +if [ "$LAST_HTTP" = "200" ] && printf '%s' "$LAST_BODY" | python3 -m json.tool >/dev/null 2>&1; then + mark_pass "public play manifest" +else + e2e_fail "公开试玩 manifest 不可读(http=${LAST_HTTP})" +fi + +head_line "试玩会话与遥测回流" +request "$READONLY_TOKEN" GET "/admin-api/telemetry/game-stat/$GAME_ID" +e2e_require_ok "readonly telemetry query" +BASE_PLAY_COUNT="$(body_value data.playCount)"; BASE_PLAY_COUNT="${BASE_PLAY_COUNT:-0}" +BASE_END_COUNT="$(body_value data.playEndCount)"; BASE_END_COUNT="${BASE_END_COUNT:-0}" +TELEMETRY_TRACE_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')" +CLIENT_PLAY_TOKEN="ptk-${RUN_ID:0:36}" +SESSION_BODY="{\"gameId\":$GAME_ID,\"versionId\":$VERSION_ID,\"clientPlayToken\":\"$CLIENT_PLAY_TOKEN\",\"scene\":\"play\",\"traceId\":\"$TELEMETRY_TRACE_ID\"}" +request "$CREATOR_TOKEN" POST /app-api/runtime/session/start "$SESSION_BODY" +e2e_require_ok "runtime session start" +SESSION_ID="$(body_value data.sessionId)" +[ -n "$SESSION_ID" ] || e2e_fail "session start 未返回 sessionId" +request "$CREATOR_TOKEN" POST /app-api/runtime/session/end "{\"sessionId\":$SESSION_ID,\"durationMs\":1200,\"endReason\":\"game_end\"}" +e2e_require_ok "runtime session end" + +export GAME_ID VERSION_ID SESSION_ID TELEMETRY_TRACE_ID CREATOR_USER_ID +TELEMETRY_BODY="$(python3 - <<'PY' +import json,os,time,uuid +now=int(time.time()*1000) +base={"schemaVersion":"v1","traceId":os.environ["TELEMETRY_TRACE_ID"], + "sessionId":os.environ["SESSION_ID"],"user":{"userId":os.environ["CREATOR_USER_ID"]}, + "context":{"gameId":os.environ["GAME_ID"],"versionId":os.environ["VERSION_ID"],"deviceType":"desktop"}} +start={**base,"eventId":str(uuid.uuid4()),"event":"game_play_start","ts":now,"props":{}} +end={**base,"eventId":str(uuid.uuid4()),"event":"game_play_end","ts":now+1200, + "props":{"duration_ms":1200,"completed":True}} +print(json.dumps({"batch":[start,end]},separators=(",",":"))) +PY +)" +request "$CREATOR_TOKEN" POST /app-api/telemetry/events/batch "$TELEMETRY_BODY" +e2e_require_ok "telemetry batch admission" +[ "$(body_value data.accepted)" = "2" ] && [ "$(body_value data.rejected)" = "0" ] || \ + e2e_fail "遥测批量未全部受理(accepted=$(body_value data.accepted) rejected=$(body_value data.rejected))" + +deadline=$((SECONDS + 120)) +PLAY_COUNT="$BASE_PLAY_COUNT"; END_COUNT="$BASE_END_COUNT" +while [ "$SECONDS" -lt "$deadline" ]; do + request "$READONLY_TOKEN" GET "/admin-api/telemetry/game-stat/$GAME_ID" + if envelope_ok; then + PLAY_COUNT="$(body_value data.playCount)"; PLAY_COUNT="${PLAY_COUNT:-0}" + END_COUNT="$(body_value data.playEndCount)"; END_COUNT="${END_COUNT:-0}" + if [ "$PLAY_COUNT" -gt "$BASE_PLAY_COUNT" ] 2>/dev/null && [ "$END_COUNT" -gt "$BASE_END_COUNT" ] 2>/dev/null; then break; fi + fi + sleep 3 +done +if [ "$PLAY_COUNT" -le "$BASE_PLAY_COUNT" ] 2>/dev/null || [ "$END_COUNT" -le "$BASE_END_COUNT" ] 2>/dev/null; then + e2e_fail "遥测 MQ 聚合在 120s 内未回流(play ${BASE_PLAY_COUNT}->${PLAY_COUNT}, end ${BASE_END_COUNT}->${END_COUNT})" +fi +mark_pass "telemetry persisted and aggregated(play=${PLAY_COUNT} end=${END_COUNT})" + +E2E_STATUS="passed" +write_evidence "$E2E_STATUS" +head_line "总判" +log "${G}DOGFOOD_E2E_PASS${N} gameId=$GAME_ID versionId=$VERSION_ID sessionId=$SESSION_ID" +log "浏览器真玩仍是独立硬门:$STUDIO_BASE/play/$GAME_ID/$VERSION_ID" +log "证据:$EVIDENCE_OUT" +log "DOGFOOD_E2E_VERDICT=PASS" diff --git a/deploy/tests/test-dogfood-smoke.sh b/deploy/tests/test-dogfood-smoke.sh new file mode 100755 index 00000000..9af34158 --- /dev/null +++ b/deploy/tests/test-dogfood-smoke.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +# 内部狗粮 smoke 回归:用假 HTTP 服务锁住默认无副作用与写模式 fail-closed 前置门。 +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" +SMOKE="$ROOT_DIR/deploy/smoke-test.sh" +TMP_DIR="$(mktemp -d)" +REQUEST_LOG="$TMP_DIR/requests.log" +trap 'rm -rf "$TMP_DIR"' EXIT + +fail() { + printf 'FAIL: %s\n' "$1" >&2 + exit 1 +} + +assert_contains() { + local file="$1" expected="$2" + grep -Fq "$expected" "$file" || fail "$file 缺少:$expected" +} + +assert_exit_code() { + local expected="$1" actual="$2" message="$3" + [ "$actual" -eq "$expected" ] || fail "$message(expected=$expected actual=$actual)" +} + +# 假 curl 只实现 readiness 所需响应,并记录方法与 URL;测试不访问任何真实服务。 +mkdir -p "$TMP_DIR/bin" +cat >"$TMP_DIR/bin/curl" <<'FAKE_CURL' +#!/usr/bin/env bash +set -euo pipefail +method="GET" +output="" +url="" +while [ "$#" -gt 0 ]; do + case "$1" in + -X) method="$2"; shift 2 ;; + -o) output="$2"; shift 2 ;; + -w|-H|--connect-timeout|--max-time|--data) shift 2 ;; + -sS) shift ;; + http://*|https://*) url="$1"; shift ;; + *) shift ;; + esac +done +printf '%s %s\n' "$method" "$url" >>"$REQUEST_LOG" +case "$url" in + */actuator/health) + body='{"status":"UP"}' ;; + */app-api/feed/zones) + body='{"code":0,"data":[{"id":1}]}' ;; + */app-api/feed/stream*) + body='{"code":0,"data":{"list":[{"gameId":11,"versionId":22}],"hasMore":false}}' ;; + */app-api/runtime/package/22*) + body='{"code":0,"data":{"gameId":11,"versionId":22,"checksum":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}' ;; + *) + body='{"code":404,"data":null}' ;; +esac +printf '%s' "$body" >"$output" +printf '200' +FAKE_CURL +chmod +x "$TMP_DIR/bin/curl" + +export PATH="$TMP_DIR/bin:$PATH" +export REQUEST_LOG + +bash -n "$SMOKE" + +# 构建配置只能包含内网地址和模式开关,不能烘焙登录 token。 +assert_contains "$ROOT_DIR/game-studio/.env.staging" 'VITE_STUDIO_TOKEN=' +assert_contains "$ROOT_DIR/game-studio/.env.staging" 'VITE_DOGFOOD_MODE=true' +if grep -Eq '^VITE_STUDIO_TOKEN=.+$' "$ROOT_DIR/game-studio/.env.staging"; then + fail 'game-studio staging 构建烘焙了 token' +fi +assert_contains "$ROOT_DIR/game-admin/.env.stage" "VITE_BASE_URL='http://100.64.0.7:48080'" +if find "$ROOT_DIR/game-studio/public/mock-manifests" -type f -print -quit 2>/dev/null | grep -q .; then + fail 'staging public 目录仍包含退役 mock/CDP 走查资产' +fi + +# 写模式必须显式确认副作用,且在任何 HTTP 请求前 fail-closed。 +assert_contains "$SMOKE" 'DOGFOOD_E2E_ACK' +set +e +PATH="$TMP_DIR/bin:$PATH" "$SMOKE" --dogfood-e2e >"$TMP_DIR/missing-env.out" 2>&1 +missing_env_rc=$? +set -e +assert_exit_code 2 "$missing_env_rc" '缺凭据时没有 fail-closed' +[ ! -s "$REQUEST_LOG" ] || fail '缺凭据时已发出网络请求' + +set +e +DOGFOOD_CREATOR_USERNAME=creator DOGFOOD_CREATOR_PASSWORD=creator-pass \ + DOGFOOD_REVIEWER_USERNAME=reviewer DOGFOOD_REVIEWER_PASSWORD=reviewer-pass \ + DOGFOOD_READONLY_USERNAME=readonly DOGFOOD_READONLY_PASSWORD=readonly-pass \ + PATH="$TMP_DIR/bin:$PATH" "$SMOKE" --dogfood-e2e >"$TMP_DIR/missing-ack.out" 2>&1 +missing_ack_rc=$? +set -e +assert_exit_code 2 "$missing_ack_rc" '缺 ACK 时没有 fail-closed' +[ ! -s "$REQUEST_LOG" ] || fail '缺 ACK 时已发出网络请求' + +# 默认 readiness 只能发送 GET,且假服务完整返回时应通过。 +PATH="$TMP_DIR/bin:$PATH" "$SMOKE" >"$TMP_DIR/readiness.out" 2>&1 +assert_contains "$TMP_DIR/readiness.out" 'SMOKE_VERDICT=PASS' +if grep -Ev '^GET ' "$REQUEST_LOG" | grep -q .; then + fail '默认 readiness 发出了非 GET 请求' +fi + +# 未知参数同样必须在发请求前拒绝。 +: >"$REQUEST_LOG" +set +e +PATH="$TMP_DIR/bin:$PATH" "$SMOKE" --deep >"$TMP_DIR/unknown.out" 2>&1 +unknown_rc=$? +set -e +assert_exit_code 2 "$unknown_rc" '未知参数未拒绝' +[ ! -s "$REQUEST_LOG" ] || fail '未知参数触发了网络请求' + +printf 'PASS: dogfood smoke fail-closed regression\n' diff --git a/game-admin/.env.stage b/game-admin/.env.stage index 78af4907..bbe6afb4 100644 --- a/game-admin/.env.stage +++ b/game-admin/.env.stage @@ -3,8 +3,8 @@ NODE_ENV=production VITE_DEV=false -# 请求路径 -VITE_BASE_URL='http://api-dashboard.huijing.iocoder.cn' +# staging 后端固定走 mini-desktop 内网地址,禁止误连上游演示环境。 +VITE_BASE_URL='http://100.64.0.7:48080' # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务 VITE_UPLOAD_TYPE=server @@ -21,14 +21,16 @@ VITE_DROP_CONSOLE=true # 是否sourcemap VITE_SOURCEMAP=false -# 打包路径 -VITE_BASE_PATH='http://static-vue3.huijing.iocoder.cn/' +# staging 由独立 4174 端口提供静态站点。 +VITE_BASE_PATH='/' # 输出路径 VITE_OUT_DIR=dist-stage -# 商城H5会员端域名 -VITE_MALL_H5_DOMAIN='http://mall.huijing.iocoder.cn' +# staging 后端未启用租户、验证码与 API 加密,前端必须逐项对齐。 +VITE_APP_TENANT_ENABLE=false +VITE_APP_CAPTCHA_ENABLE=false +VITE_APP_API_ENCRYPT_ENABLE=false -# GoView域名 -VITE_GOVIEW_URL='http://127.0.0.1:3000' \ No newline at end of file +# 绘境业务页面必须走真实 staging API,不启用本地业务 mock。 +VITE_APP_WANXIANG_MOCK=false diff --git a/game-studio/.env.staging b/game-studio/.env.staging index d9ebc699..1d178be0 100644 --- a/game-studio/.env.staging +++ b/game-studio/.env.staging @@ -1,5 +1,6 @@ # game-studio 联调 env(vite --mode staging)。staging 后端在 mini-desktop。 -# 非密钥:仅后端地址 + studio 登录态 token(后端可识别的固定测试态),故入库供 mini-desktop 克隆联调 -# (根 .gitignore 已显式豁免 !game-studio/.env.staging)。 +# staging 只烘焙公开地址与狗粮模式,不烘焙任何账号 token。 +# 用户必须走真实用户名密码登录,token 仅落浏览器 localStorage。 VITE_API_BASE=http://100.64.0.7:48080 -VITE_STUDIO_TOKEN=test1 +VITE_STUDIO_TOKEN= +VITE_DOGFOOD_MODE=true diff --git a/game-studio/public/mock-manifests/9001.json b/game-studio/public/mock-manifests/9001.json deleted file mode 100644 index 2e0fc135..00000000 --- a/game-studio/public/mock-manifests/9001.json +++ /dev/null @@ -1 +0,0 @@ -{"schemaVersion":"1.0","gameId":"1","versionId":"9001","templateId":"generic","gameConfig":{"title":"本地预览 · 点目标","winScore":3},"assets":[],"manifest":{"runtimeVersion":"1.0.0","entry":"index.html","preloadPolicy":"eager","bundleSize":211825,"checksum":"3710e8dff7fbe565ca919325a90117a4e3bd4e4627ec438bea9a3ca25f98f101"},"meta":{"title":"本地预览 · 点目标","summary":"E1 适配器产物(__GameBundle)经本地 mock 预览试玩——闭合 create→预览可玩闭环。","cover":"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==","ageRating":"all"},"engineBundle":"\"use strict\";var __GameBundle=(()=>{var Fi=Object.defineProperty;var Gc=Object.getOwnPropertyDescriptor;var Fc=Object.getOwnPropertyNames;var jc=Object.prototype.hasOwnProperty;var Nc=(n,t,e)=>()=>{if(e)throw e[0];try{return n&&(t=n(n=0)),t}catch(s){throw e=[s],s}};var Ur=(n,t)=>{for(var e in t)Fi(n,e,{get:t[e],enumerable:!0})},Bc=(n,t,e,s)=>{if(t&&typeof t==\"object\"||typeof t==\"function\")for(let i of Fc(t))!jc.call(n,i)&&i!==e&&Fi(n,i,{get:()=>t[i],enumerable:!(s=Gc(t,i))||s.enumerable});return n};var zc=n=>Bc(Fi({},\"__esModule\",{value:!0}),n);var Oc={};Ur(Oc,{ASSERT:()=>f,BLACK:()=>nt,BLUE:()=>Ka,Box2dDistanceJoint:()=>yi,Box2dFrictionJoint:()=>qo,Box2dGearJoint:()=>Jo,Box2dJoint:()=>Jt,Box2dKinematicObject:()=>Go,Box2dMotorJoint:()=>Xo,Box2dObject:()=>gs,Box2dPinJoint:()=>No,Box2dPlugin:()=>mi,Box2dPrismaticJoint:()=>Vo,Box2dPulleyJoint:()=>Ho,Box2dRaycastResult:()=>gi,Box2dRevoluteJoint:()=>zo,Box2dRopeJoint:()=>Bo,Box2dStaticObject:()=>pi,Box2dTargetJoint:()=>jo,Box2dTileLayer:()=>Fo,Box2dWeldJoint:()=>Wo,Box2dWheelJoint:()=>Uo,CLEAR_BLACK:()=>re,CLEAR_WHITE:()=>$e,CYAN:()=>$l,CanvasLayer:()=>si,Color:()=>xt,Ease:()=>xi,EngineObject:()=>Me,GRAY:()=>Xl,GREEN:()=>$a,ImageFont:()=>Ks,LOG:()=>yt,Light:()=>_o,LightSystemPlugin:()=>Eo,MAGENTA:()=>Kl,Medal:()=>di,NewgroundsMedal:()=>To,NewgroundsPlugin:()=>Ao,ORANGE:()=>Yl,PI:()=>q,PURPLE:()=>Qa,Particle:()=>ii,ParticleEmitter:()=>yo,PathFinder:()=>Yo,PathFinderNode:()=>vi,PostProcessPlugin:()=>Co,RED:()=>nr,RandomGenerator:()=>uo,Sound:()=>rs,SoundInstance:()=>ei,TextureInfo:()=>ke,TileCollisionLayer:()=>cs,TileInfo:()=>we,TileLayer:()=>as,TileLayerData:()=>hn,Timer:()=>xe,Tween:()=>bi,UIButton:()=>ps,UICheckbox:()=>Do,UILayout:()=>ko,UIObject:()=>Zt,UISlider:()=>Io,UISystemPlugin:()=>Po,UIText:()=>fi,UITextInput:()=>Lo,UITile:()=>Oo,UIVideo:()=>Mo,Vector2:()=>it,WHITE:()=>V,YELLOW:()=>Ya,ZzFXMusic:()=>Ro,abs:()=>Z,atan2:()=>Zo,audioContext:()=>lt,audioDefaultSampleRate:()=>Bn,audioIsRunning:()=>os,audioMasterGain:()=>Ie,box2d:()=>m,box2dDebug:()=>Jr,box2dInit:()=>hd,box2dSetDebug:()=>ud,cameraAngle:()=>Y,cameraFit:()=>uh,cameraPos:()=>wt,cameraScale:()=>W,canvasClearColor:()=>We,canvasColorTiles:()=>wi,canvasFixedSize:()=>An,canvasMaxAspect:()=>Tn,canvasMaxSize:()=>wn,canvasMinAspect:()=>Hn,canvasPixelRatio:()=>qe,canvasPixelated:()=>or,ceil:()=>Qn,clamp:()=>j,combineCanvases:()=>br,cos:()=>rt,debug:()=>Dt,debugCircle:()=>Ft,debugClear:()=>jl,debugLine:()=>ye,debugOverlap:()=>co,debugOverlay:()=>Ce,debugPoint:()=>Qo,debugPointSize:()=>Ia,debugPoly:()=>Fl,debugRect:()=>gt,debugScreenshot:()=>ka,debugShowErrors:()=>Nl,debugText:()=>Sn,debugVideoCaptureIsActive:()=>me,debugVideoCaptureStart:()=>Ga,debugVideoCaptureStop:()=>Fa,debugWatermark:()=>Si,distanceAngle:()=>Wl,distanceWrap:()=>tr,drawCanvas2D:()=>mn,drawCircle:()=>$s,drawCircleGradient:()=>ah,drawContext:()=>vt,drawCount:()=>ve,drawCrescent:()=>pd,drawEllipse:()=>nc,drawEllipseGradient:()=>sc,drawLine:()=>rn,drawLineList:()=>fo,drawNineSlice:()=>Cc,drawNineSliceScreen:()=>dd,drawPoly:()=>On,drawRect:()=>Ys,drawRectGradient:()=>ih,drawRegularPoly:()=>ec,drawText:()=>ch,drawTextScreen:()=>He,drawTextureWrapped:()=>oh,drawThreeSlice:()=>Ec,drawThreeSliceScreen:()=>fd,drawTile:()=>_t,enablePhysicsSolver:()=>Ei,engineAddPlugin:()=>gn,engineImageFont:()=>ac,engineInit:()=>Il,engineName:()=>Bs,engineObjects:()=>mt,engineObjectsCallback:()=>kl,engineObjectsCollect:()=>Da,engineObjectsCollide:()=>Ko,engineObjectsDestroy:()=>Ml,engineObjectsRaycast:()=>Gl,engineObjectsUpdate:()=>Oa,engineVersion:()=>zs,fetchJSON:()=>Ql,floor:()=>bt,fontDefault:()=>ys,formatTime:()=>sr,frame:()=>Pa,frameRate:()=>Ll,gamepadConnected:()=>go,gamepadDirectionEmulateStick:()=>dr,gamepadDpad:()=>po,gamepadIsDown:()=>Ae,gamepadPrimary:()=>pt,gamepadStick:()=>ss,gamepadStickCount:()=>cc,gamepadVibrate:()=>Ch,gamepadVibrateStop:()=>Eh,gamepadWasPressed:()=>Ar,gamepadWasReleased:()=>Ah,gamepadsEnable:()=>_i,getCameraSize:()=>oc,getCrescentPoints:()=>_c,getNoteFrequency:()=>fc,getPaused:()=>Ol,glAntialias:()=>Lr,glCanvas:()=>Q,glCircleSides:()=>ms,glClearCanvas:()=>bc,glClearRect:()=>Tc,glCompileShader:()=>wo,glContext:()=>g,glCopyToContext:()=>kr,glCreateProgram:()=>Gn,glCreateTexture:()=>Oi,glDeleteTexture:()=>vc,glDraw:()=>hs,glDrawColoredPoints:()=>jr,glDrawOutlineTransform:()=>Fr,glDrawPoints:()=>wc,glDrawPointsTransform:()=>Gr,glDrawUntextured:()=>Sc,glEnable:()=>M,glFlush:()=>Tt,glSetAntialias:()=>Zh,glSetRenderTarget:()=>qn,glSetTexture:()=>Ir,glSetTextureData:()=>Mr,glSetTextureWrap:()=>xc,gravity:()=>jt,headlessMode:()=>N,hsl:()=>_e,hypot:()=>ja,inputClear:()=>Ts,inputClearKey:()=>wr,inputMouseMoveThreshold:()=>Sr,inputPreventDefault:()=>Cn,inputWASDEmulateDirection:()=>Xn,isArray:()=>ie,isColor:()=>z,isFullscreen:()=>rc,isIntersecting:()=>Ja,isNumber:()=>R,isOnScreen:()=>hh,isOverlapping:()=>Ws,isPowerOfTwo:()=>Zn,isStringLike:()=>J,isTouchDevice:()=>Kt,isUsingGamepad:()=>_n,isVector2:()=>L,keyDirection:()=>wh,keyIsDown:()=>Et,keyWasPressed:()=>Lt,keyWasReleased:()=>Tr,lastInputDevice:()=>Ve,lerp:()=>Nt,lerpAngle:()=>ql,lerpWrap:()=>za,lightSystem:()=>U,lineTest:()=>Ua,log2:()=>Na,mainCanvas:()=>F,mainCanvasSize:()=>D,mainContext:()=>at,max:()=>X,medalDisplaySize:()=>ui,medalDisplaySlideTime:()=>En,medalDisplayTime:()=>li,medals:()=>ds,medalsForEach:()=>Di,medalsInit:()=>sd,medalsPreventUnlock:()=>Nr,medalsReset:()=>id,min:()=>$,mod:()=>$t,mouseDelta:()=>xr,mouseDeltaScreen:()=>fe,mouseInWindow:()=>Qs,mouseIsDown:()=>$n,mousePos:()=>Te,mousePosScreen:()=>Gt,mouseWasPressed:()=>ns,mouseWasReleased:()=>Th,mouseWheel:()=>vr,nearestPowerOfTwo:()=>Hl,newgrounds:()=>fs,noise1D:()=>su,noise2D:()=>iu,objectDefaultAngleDamping:()=>lr,objectDefaultDamping:()=>cr,objectDefaultFriction:()=>hr,objectDefaultMass:()=>ar,objectDefaultRestitution:()=>ur,objectMaxSpeed:()=>Re,oscillate:()=>Va,particleEmitRateScale:()=>qs,paused:()=>be,percent:()=>It,percentLerp:()=>Ul,playSamples:()=>Pr,pointerLockExit:()=>Ph,pointerLockIsActive:()=>lc,pointerLockRequest:()=>Rh,postProcess:()=>Pt,primitiveCount:()=>Se,rand:()=>ct,randBool:()=>Wa,randColor:()=>lo,randInCircle:()=>Xa,randInt:()=>er,randSign:()=>qa,randVec2:()=>Ha,readSaveData:()=>eu,rgb:()=>B,round:()=>Vs,saveCanvas:()=>Za,saveDataURL:()=>ts,saveText:()=>Zl,screenToWorld:()=>Ss,screenToWorldDelta:()=>ic,screenToWorldTransform:()=>ws,setAdditiveBlendMode:()=>es,setCameraAngle:()=>ru,setCameraPos:()=>ou,setCameraScale:()=>au,setCanvasClearColor:()=>uu,setCanvasColorTiles:()=>lu,setCanvasFixedSize:()=>pu,setCanvasMaxAspect:()=>fu,setCanvasMaxSize:()=>hu,setCanvasMinAspect:()=>du,setCanvasPixelRatio:()=>yu,setCanvasPixelated:()=>tc,setCursor:()=>gh,setDebugKey:()=>sh,setDebugWatermark:()=>nh,setEnablePhysicsSolver:()=>Cu,setFontDefault:()=>mu,setGLCircleSides:()=>Su,setGLEnable:()=>vu,setGamepadDirectionEmulateStick:()=>ku,setGamepadsEnable:()=>Mu,setGravity:()=>Du,setHeadlessMode:()=>xu,setInputMouseMoveThreshold:()=>bh,setInputPreventDefault:()=>mh,setInputWASDEmulateDirection:()=>Gu,setMedalDisplaySize:()=>ad,setMedalDisplaySlideTime:()=>rd,setMedalDisplayTime:()=>od,setMedalsPreventUnlock:()=>cd,setObjectDefaultAngleDamping:()=>Ru,setObjectDefaultDamping:()=>_u,setObjectDefaultFriction:()=>Lu,setObjectDefaultMass:()=>Eu,setObjectDefaultRestitution:()=>Pu,setObjectMaxSpeed:()=>Ou,setParticleEmitRateScale:()=>Iu,setPaused:()=>Dl,setShowSplashScreen:()=>bu,setSoundDefaultRange:()=>th,setSoundDefaultTaper:()=>eh,setSoundEnable:()=>Qu,setSoundVolume:()=>Zu,setTileDefaultBleed:()=>Au,setTileDefaultPadding:()=>Tu,setTileDefaultSize:()=>wu,setTilesPixelated:()=>gu,setTimeScale:()=>cu,setTouchGamepadAlpha:()=>Xu,setTouchGamepadAnalog:()=>Wu,setTouchGamepadButtonCount:()=>zu,setTouchGamepadCenterButtonSize:()=>Bu,setTouchGamepadDisplayTime:()=>Yu,setTouchGamepadEnable:()=>ju,setTouchGamepadFloating:()=>qu,setTouchGamepadLeftButtonCount:()=>Vu,setTouchGamepadLeftStick:()=>Ju,setTouchGamepadPassthrough:()=>Nu,setTouchGamepadRightStick:()=>Uu,setTouchGamepadSize:()=>Hu,setTouchGamepadVibration:()=>$u,setTouchInputEnable:()=>Fu,setVibrateEnable:()=>Ku,shareURL:()=>tu,showSplashScreen:()=>rr,sign:()=>Yt,sin:()=>tt,smoothStep:()=>Us,soundDefaultRange:()=>gr,soundDefaultTaper:()=>yr,soundEnable:()=>zt,soundVolume:()=>un,speak:()=>Uh,speakStop:()=>Wh,tan:()=>Ba,textureInfos:()=>Qe,tile:()=>vs,tileCollisionGetData:()=>ni,tileCollisionLayers:()=>Xe,tileCollisionRaycast:()=>pc,tileCollisionTest:()=>ze,tileDefaultBleed:()=>Ci,tileDefaultPadding:()=>Ai,tileDefaultSize:()=>Ti,tileLayersLoad:()=>Hh,tilesPixelated:()=>dn,time:()=>Wt,timeDelta:()=>$o,timeReal:()=>Ye,timeScale:()=>ir,toggleFullscreen:()=>ph,touchGamepadAlpha:()=>pr,touchGamepadAnalog:()=>bs,touchGamepadButtonCount:()=>Fn,touchGamepadCenterButtonSize:()=>oe,touchGamepadDisplayTime:()=>Yn,touchGamepadEnable:()=>Ke,touchGamepadFloating:()=>Ze,touchGamepadLeftButtonCount:()=>jn,touchGamepadLeftStick:()=>yn,touchGamepadPassthrough:()=>fr,touchGamepadRightStick:()=>Nn,touchGamepadSize:()=>Bt,touchGamepadVibration:()=>Hs,touchInputEnable:()=>on,tweenProperty:()=>gd,tweenStopAll:()=>yd,tweenUpdate:()=>Lc,uiDebug:()=>zr,uiSetDebug:()=>ld,uiSystem:()=>C,usingGamepadInput:()=>Sh,usingKeyboardInput:()=>vh,usingMouseInput:()=>xh,vec2:()=>x,vibrate:()=>Cr,vibrateEnable:()=>xs,vibrateStop:()=>_h,workCanvas:()=>Oe,workContext:()=>Je,workReadCanvas:()=>ge,workReadContext:()=>Pe,worldToScreen:()=>bn,worldToScreenDelta:()=>lh,writeSaveData:()=>nu,zzfx:()=>qh,zzfxG:()=>Pi,zzfxM:()=>Ac});function Ol(){return be}function Dl(n=!0){be=n}function gn(n,t,e,s){f(!an.find(o=>o.update===n&&o.render===t&&o.glContextLost===e&&o.glContextRestored===s));let i=new ro(n,t,e,s);an.push(i)}async function Il(n,t,e,s,i,o=[],a){if(La&&console.log(`${Bs} Engine v${zs}`),f(!at,\"engine already initialized\"),at)return;f(ie(o),\"pass in images as array\"),document.body||document.documentElement.appendChild(document.createElement(\"body\")),a||(a=document.body),n||(n=()=>{}),t||(t=()=>{}),e||(e=()=>{}),s||(s=()=>{}),i||(i=()=>{});function r(){D=x(F.width,F.height),at.imageSmoothingEnabled=!dn,So()}function c(y=0){let b=y-so;so||(b=0),so=y,(Dt||Si)&&(Js=Nt(Js,1e3/(b||1),.05));let v=Dt&&Et(\"Equal\"),w=Dt&&Et(\"Minus\"),_=v?10:w?.1:1;Ye+=b*_/1e3;let S=ir*_;b*=S,de+=be?0:b,S<=1&&(de=$(de,50));let E=!1;if(be){E=!0,l(),Ta(),an.forEach(T=>{var P;return(P=T.update)==null?void 0:P.call(T)});for(let T of mt)T.parent||T.updateTransforms();xa(),e(),Aa(),me()&&A()}else{let T=0;for(de<0&&de>-9&&(T=de,de=0);de>=0;de-=1e3/60)Wt=Pa++/60,E=!0,l(),Ta(),t(),an.forEach(P=>{var I;return(I=P.update)==null?void 0:I.call(P)}),Oa(),xa(),e(),Aa(),me()&&A();de+=T}me()||A(),requestAnimationFrame(c);function A(){if(!N){E||l(),r(),s(),mt.sort((T,P)=>T.renderOrder-P.renderOrder);for(let T of mt)T.destroyed||T.render();i(),an.forEach(T=>{var P;return(P=T.render)==null?void 0:P.call(T)}),Oh(),zl(),Tt(),Jl(),ve=0,Se=0}}}function l(){if(!N){if(An.x){D=An.copy();let y=innerWidth/innerHeight,b=An.x/An.y,v=yTn){let E=D.y*Tn|0;D.x=$(E,wn.x)}else if(w0&&!M&&(at.fillStyle=We.toString(),at.fillRect(0,0,D.x,D.y),at.fillStyle=nt.toString()),at.lineJoin=\"round\",at.lineCap=\"round\"}}if(N)return d();$h(a);let u=\"margin:0;overflow:hidden;background:#000;user-select:none;-webkit-user-select:none;touch-action:none;-webkit-touch-callout:none\";a.style.cssText=u,F=a.appendChild(document.createElement(\"canvas\")),vt=at=F.getContext(\"2d\"),Lh(),Vh(),Bl();let h=\"position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)\";F.style.cssText=h,Q&&(Q.style.cssText=h),tc(or),l(),So(),Oe=new OffscreenCanvas(64,64),Je=Oe.getContext(\"2d\"),ge=new OffscreenCanvas(64,64),Pe=ge.getContext(\"2d\",{willReadFrequently:!0});let p=o.map((y,b)=>Sa(b,y));return o.length||p.push(Sa(0)),p.push(yh()),rr&&p.push(new Promise(y=>{let b=0;v();function v(){Ts(),nd(b+=.01),b>1?y():setTimeout(v,16)}})),await Promise.all(p),d();async function d(){await n(),c()}}function Oa(){Ko=mt.filter(t=>t.collideSolidObjects);function n(t){if(!t.destroyed){t.update();for(let e of t.children)n(e)}}for(let t of mt)if(!(t.parent||t.destroyed)){t.update(),t.updatePhysics();for(let e of t.children)n(e);t.updateTransforms()}mt=mt.filter(t=>!t.destroyed)}function Ml(n=!0){for(let t of mt)t.parent||t.destroy(n);mt=mt.filter(t=>!t.destroyed)}function Da(n,t,e=mt){let s=[];if(n)if(t instanceof it)for(let i of e)i.isOverlapping(n,t)&&s.push(i);else{let i=t*t;for(let o of e)n.distanceSquared(o.pos)e(i))}function Gl(n,t,e=mt){let s=[];for(let i of e)i.collideRaycast&&Ja(n,t,i.pos,i.size)&&(se&>(i.pos,i.size,\"#f00\"),s.push(i));return se&&ye(n,t,s.length?\"#f00\":\"#00f\",.02),s}function f(n,...t){if(!n)throw console.assert(n,...t),new Error(\"Assert failed!\")}function yt(...n){console.log(...n)}function gt(n,t=x(),e=V,s=0,i=0,o=!1,a=!1){f(L(n),\"pos must be a vec2\"),f(L(t),\"size must be a vec2\"),f(J(e)||z(e),\"color is invalid\"),f(R(s),\"time must be a number\"),f(R(i),\"angle must be a number\"),z(e)&&(e=e.toString());let r=new xe(s);Ue.push({pos:n.copy(),size:t.copy(),color:e,timer:r,angle:i,fill:o,screenSpace:a})}function Fl(n,t,e=V,s=0,i=0,o=!1,a=!1){f(L(n),\"pos must be a vec2\"),f(ie(t),\"points must be an array\"),f(J(e)||z(e),\"color is invalid\"),f(R(s),\"time must be a number\"),f(R(i),\"angle must be a number\"),z(e)&&(e=e.toString()),n=n.copy(),t=t.map(c=>c.copy());let r=new xe(s);Ue.push({pos:n,points:t,color:e,timer:r,angle:i,fill:o,screenSpace:a})}function Ft(n,t=0,e=V,s=0,i=!1,o=!1){f(L(n),\"pos must be a vec2\"),f(R(t),\"size must be a number\"),f(J(e)||z(e),\"color is invalid\"),f(R(s),\"time must be a number\"),z(e)&&(e=e.toString()),n=n.copy();let a=new xe(s);Ue.push({pos:n,size:t,color:e,timer:a,angle:0,fill:i,screenSpace:o})}function Qo(n,t,e,s,i=!1){gt(n,void 0,t,e,s,!1,i)}function ye(n,t,e,s=.1,i=0,o=!1){f(L(n),\"posA must be a vec2\"),f(L(t),\"posB must be a vec2\"),f(R(s),\"width must be a number\");let a=x((t.x-n.x)/2,(t.y-n.y)/2),r=x(s,a.length()*2);gt(n.add(a),r,e,i,a.angle(),!0,o)}function co(n,t,e,s,i,o,a=!1){f(L(n),\"posA must be a vec2\"),f(L(e),\"posB must be a vec2\"),f(L(t),\"sizeA must be a vec2\"),f(L(s),\"sizeB must be a vec2\");let r=x($(n.x-t.x/2,e.x-s.x/2),$(n.y-t.y/2,e.y-s.y/2)),c=x(X(n.x+t.x/2,e.x+s.x/2),X(n.y+t.y/2,e.y+s.y/2));gt(r.lerp(c,.5),c.subtract(r),i,o,0,!1,a)}function Sn(n,t,e=1,s=V,i=0,o=0,a=\"monospace\",r=!1){f(J(n),\"text must be a string\"),f(L(t),\"pos must be a vec2\"),f(R(e),\"size must be a number\"),f(J(s)||z(s),\"color is invalid\"),f(R(i),\"time must be a number\"),f(R(o),\"angle must be a number\"),f(J(a),\"font must be a string\"),z(s)&&(s=s.toString()),t=t.copy();let c=new xe(i);Ue.push({text:n,pos:t,size:e,color:s,timer:c,angle:o,font:a,screenSpace:r})}function jl(){Ue=[]}function ka(){ao=1}function Nl(){let n=e=>{document.body.style=\"background-color:#111;margin:8px\",document.body.innerHTML=\"
\"+e},t=console.assert;console.assert=(e,...s)=>{if(t(e,...s),!e){let i=s.join(\" \"),o=new Error().stack;throw`Assertion failed!\n`+i+`\n`+o}},onunhandledrejection=e=>n(e.reason.stack||e.reason),onerror=(e,s,i,o)=>n(`${e}\n${s}\nLn ${i}, Col ${o}`)}function Bl(){La&&console.warn(\"LittleJS DEBUG build loaded. Use the release build for production.\")}function xa(){Dt&&(Lt(Ma)&&(Ce=!Ce),Ce&&(Lt(\"Digit1\")&&(ne=!ne,cn=!1),Lt(\"Digit2\")&&(cn=!cn,ne=!1),Lt(\"Digit3\")&&(ln=!ln),Lt(\"Digit4\")&&(se=!se),Lt(\"Digit5\")&&ka(),Lt(\"Digit7\")&&(Ln=!Ln)),me()?(!Ce||Lt(\"Digit6\"))&&Fa():Ce&&Lt(\"Digit6\")&&Ga())}function zl(){if(me())return;Tt();let n=ve,t=Se;ao&&(br(),Za(F),ao=0);let e=at;if(ln&&_i){let o=0;for(let a=0;a<8;a++)go(a)&&o++;for(let a=0;a<8;a++){if(!go(a))continue;let r=1,c=.2,l=wt.add(x(-r*2,((o-1)/2-a)*r*3));Sn(a,l.add(x(-r,r)),1),a===pt&&Sn(\"Main\",l.add(x(-r*2,0)),1,\"#0f0\");let u=pe[a].length;for(let p=0;p{e.save();let o=i.pos,a=1,r=i.angle;if(i.screenSpace||(o=bn(i.pos),a=W,r-=Y),e.translate(o.x|0,o.y|0),e.rotate(r),e.scale(1,i.text?1:-1),e.fillStyle=i.color,e.strokeStyle=i.color,i.text!==void 0)e.font=i.size*a+\"px \"+i.font,e.textAlign=\"center\",e.textBaseline=\"middle\",e.fillText(i.text,0,0);else if(i.points!==void 0){e.beginPath();for(let c of i.points){let l=c.scale(a).floor();e.lineTo(l.x,l.y)}e.closePath(),i.fill&&e.fill(),e.stroke()}else if(i.size===0||i.size.x===0&&i.size.y===0){let c=Ia*a;e.fillRect(-c/2,-1,c,3),e.fillRect(-1,-c/2,3,c)}else if(i.size.x!==void 0){let c=i.size.scale(a).floor(),l=c.x,u=c.y;i.fill&&e.fillRect(-l/2|0,-u/2|0,l,u),e.strokeRect(-l/2|0,-u/2|0,l,u)}else e.beginPath(),e.arc(0,0,i.size*a/2,0,9),i.fill&&e.fill(),e.stroke();e.restore()}),Ue=Ue.filter(i=>i.timer<0),s){let i=pc(s.pos,Te);i&&Ys(i.floor().add(x(.5)),x(1),B(0,1,1,.3),0,!1),rn(Te,s.pos,.1,i?B(1,0,0,.5):B(0,1,0,.5),void 0,void 0,!1);let o=\"mouse pos = \"+Te;Xe.length&&(o+=`\nmouse collision = `+ni(Te)),o+=`\n\n--- object info ---\n`,o+=s.toString(),He(o,Gt,24,B(),.05,void 0,\"center\",\"monospace\")}{e.save(),e.fillStyle=\"#fff\",e.textAlign=\"left\",e.textBaseline=\"top\",e.font=\"20px monospace\",e.shadowColor=\"#000\",e.shadowBlur=9;let a=9,r=0,c=24;if(Ce){e.fillText(`${Bs} v${zs}`,a,r+=c/2),e.fillText(\"Time: \"+sr(Wt),a,r+=c),e.fillText(\"FPS: \"+Js.toFixed(1)+(M?\" WebGL\":\" Canvas2D\"),a,r+=c),e.fillText(\"Objects: \"+mt.length,a,r+=c),e.fillText(\"Draw Calls: \"+ve,a,r+=c),e.fillText(\"Primitives: \"+Se,a,r+=c),e.fillText(\"---------\",a,r+=c),e.fillStyle=\"#f00\",e.fillText(\"ESC: Debug Overlay\",a,r+=c),e.fillStyle=ne?\"#f00\":\"#fff\",e.fillText(\"1: Debug Physics\",a,r+=c),e.fillStyle=cn?\"#f00\":\"#fff\",e.fillText(\"2: Debug Particles\",a,r+=c),e.fillStyle=ln?\"#f00\":\"#fff\",e.fillText(\"3: Debug Gamepads\",a,r+=c),e.fillStyle=se?\"#f00\":\"#fff\",e.fillText(\"4: Debug Raycasts\",a,r+=c),e.fillStyle=\"#fff\",e.fillText(\"5: Save Screenshot\",a,r+=c),e.fillText(\"6: Toggle Video Capture\",a,r+=c),e.fillStyle=Ln?\"#f00\":\"#fff\",e.fillText(\"7: Debug Sound\",a,r+=c);let l=\"\",u=\"\";for(let h in H[0])Et(h,0)&&(parseInt(h)<3?u+=h+\" \":l+=h+\" \");u&&e.fillText(\"Mouse: \"+u,a,r+=c),l&&e.fillText(\"Keys: \"+l,a,r+=c);for(let h=1;hs.push(r.data),i.onstop=()=>{let r=new Blob(s,{type:\"video/webm\"}),c=URL.createObjectURL(r);ts(c,\"capture.webm\",1e3)};let o,a;if(zt){a=new ConstantSourceNode(lt,{offset:0}),a.connect(Ie),a.start(),o=lt.createMediaStreamDestination(),Ie.connect(o);for(let r of o.stream.getAudioTracks())n.addTrack(r)}try{i.start()}catch{yt(\"Video capture not supported in this browser!\"),a==null||a.stop();return}yt(\"Video capture started.\"),Ee={mediaRecorder:i,captureTimer:e,videoTrack:t,silentAudioSource:a,audioStreamDestination:o}}function Fa(){var n,t,e;f(me(),\"Not capturing video!\"),yt(`Video capture ended. ${Ee.captureTimer.get().toFixed(2)} seconds recorded.`),ee.style.display=\"none\",(n=Ee.silentAudioSource)==null||n.stop(),(t=Ee.mediaRecorder)==null||t.stop(),(e=Ee.videoTrack)==null||e.stop(),Ee=void 0}function Vl(){f(me(),\"Not capturing video!\"),br(),Ee.videoTrack.requestFrame(),ee.textContent=\"\\u25CF REC \"+sr(Ee.captureTimer)}function Ht(n){if(Dt){let t=Object.keys(n),e={};t.forEach(s=>e[s]=n[s]),t.forEach(s=>{Object.defineProperty(n,s,{get:()=>e[s],set:i=>{f(!1,`Cannot modify engine constant. Attempted to set constant (${n}) property '${s}' to '${i}'.`)},enumerable:!0})})}return Object.freeze(n)}function $t(n,t=1){return(n%t+t)%t}function j(n,t=0,e=1){return ne?e:n}function It(n,t,e){return(e-=t)?j((n-t)/e):0}function Nt(n,t,e){return n+j(e)*(t-n)}function Ul(n,t,e,s,i){return Nt(s,i,It(n,t,e))}function tr(n,t,e=1){f(e>0,\"distanceWrap wrapSize must be > 0\");let s=(n-t)%e;return s*2%e-s}function za(n,t,e,s=1){return n+j(e)*tr(t,n,s)}function Wl(n,t){return tr(n,t,2*q)}function ql(n,t,e){return za(n,t,e,2*q)}function Us(n){return n*n*(3-2*n)}function Zn(n){return!(n&n-1)}function Hl(n){return 2**Qn(Na(n))}function Ws(n,t,e,s=x()){let i=(n.x-e.x)*2,o=(n.y-e.y)*2,a=t.x+s.x,r=t.y+s.y;return Z(i)p)return!1;h=X(y,h)}else{if(y0?r.x+1:r.x,b=h>0?r.y+1:r.y,v=c?(y-n.x)/c:1/0,w=l?(b-n.y)/l:1/0,_=0,S=v,E=w,A=pr.x&&(T.x=r.x+1-P),I.yr.y&&(T.y=r.y+1-P),s&&(A?s.set(-u,0):s.set(0,-h)),T}(A=SURL.revokeObjectURL(n),e)}function tu(n,t,e){var s;f(J(n),\"shareURL requires title string\"),f(J(t),\"shareURL requires url string\"),(s=navigator.share)==null||s.call(navigator,{title:n,url:t}).then(()=>e==null?void 0:e())}function eu(n,t){f(J(n),\"loadData requires saveName string\");let e={};try{let s=localStorage[n];if(s)try{e=JSON.parse(s)}catch{yt(\"readSaveData: corrupt JSON for\",n,\"\\u2014 using defaults\")}}catch{yt(\"readSaveData: localStorage unavailable \\u2014 using defaults\")}return{...t,...e}}function nu(n,t){f(J(n),\"saveData requires saveName string\");try{localStorage[n]=JSON.stringify(t)}catch{yt(\"writeSaveData: failed to write\",n)}}function ho(n){let t=(n|0)^2654435769;return t=Math.imul(t^t>>>16,2246822507),t=Math.imul(t^t>>>13,3266489909),t^=t>>>16,(t>>>0)/2**32}function su(n){let t=bt(n);return Nt(ho(t),ho(t+1),Us(n-t))}function iu(n,t){let e=bt(n),s=bt(t),i=Us(n-e),o=Us(t-s),a=(r,c)=>ho(r+c*374761393);return Nt(Nt(a(e,s),a(e+1,s),i),Nt(a(e,s+1),a(e+1,s+1),i),o)}function ou(n){wt=n.copy()}function ru(n){Y=n}function au(n){W=n}function cu(n){ir=n}function lu(n){wi=n}function uu(n){We=n.copy()}function hu(n){wn=n.copy()}function du(n){Hn=n}function fu(n){Tn=n}function pu(n){An=n.copy()}function tc(n){or=n,F&&(F.style.imageRendering=n?\"pixelated\":\"\"),Q&&(Q.style.imageRendering=n?\"pixelated\":\"\")}function gu(n){dn=n}function yu(n){qe=n}function mu(n){ys=n}function bu(n){rr=n}function xu(n){N=n}function vu(n){if(n&&!vo){console.warn(\"Can not enable WebGL if it was disabled on start.\");return}M=n,Q&&(Q.style.display=n?\"\":\"none\")}function Su(n){ms=n}function wu(n){Ti=n.copy()}function Tu(n){Ai=n}function Au(n){Ci=n}function Cu(n){Ei=n}function Eu(n){ar=n}function _u(n){cr=n}function Ru(n){lr=n}function Pu(n){ur=n}function Lu(n){hr=n}function Ou(n){Re=n}function Du(n){jt=n.copy()}function Iu(n){qs=n}function Mu(n){_i=n}function ku(n){dr=n}function Gu(n){Xn=n}function Fu(n){on=n}function ju(n){Ke=n}function Nu(n){fr=n}function Bu(n){oe=n}function zu(n){Fn=n,n>0&&(Nn=!1)}function Ju(n){yn=n,n&&(jn=0)}function Vu(n){jn=n,n>0&&(yn=!1)}function Uu(n){Nn=n,n&&(Fn=0)}function Wu(n){bs=n}function qu(n){Ze=n}function Hu(n){Bt=n}function Xu(n){pr=n}function Yu(n){Yn=n}function $u(n){Hs=n}function Ku(n){xs=n}function Qu(n){zt=n}function Zu(n){un=n,zt&&!N&&Ie&&(Ie.gain.value=n)}function th(n){gr=n}function eh(n){yr=n}function nh(n){Si=n}function sh(n){Ma=n}function Xs(n){return n.r>=1&&n.g>=1&&n.b>=1}function mr(n){return n.r<=0&&n.g<=0&&n.b<=0&&n.a<=0}function vs(n=0,t=Ti,e=0,s=Ai,i=Ci){if(f(L(n)||typeof n==\"number\",\"index must be a vec2 or number\"),f(L(t)||typeof t==\"number\",\"size must be a vec2 or number\"),f(R(e)||e instanceof ke,\"texture must be a number or TextureInfo\"),f(R(s),\"padding must be a number\"),N)return new we;typeof t==\"number\"&&(f(t>0),t=new it(t,t));let o=typeof e==\"number\"?Qe[e]:e;f(o instanceof ke,\"tile texture is not loaded\"),f(o.size.x>0,\"tile texture is not loaded\");let a=t.x+s*2,r=t.y+s*2,c,l;if(typeof n==\"number\"){let h=o.size.x/a|0;c=n%h,l=n/h|0}else c=n.x,l=n.y;let u=new it(c*a+s,l*r+s);return new we(u,t,o,s,i)}function _t(n,t=x(1),e,s=V,i=0,o,a,r=M,c=!1,l){var p;f(L(n),\"pos must be a vec2\"),f(L(t),\"size must be a vec2\"),f(z(s),\"color is invalid\"),f(R(i),\"angle must be a number\"),f(!a||z(a),\"additiveColor must be a color\"),f(!l||!r,\"context only supported in canvas 2D mode\");let u=e==null?void 0:e.textureInfo,h=(p=e==null?void 0:e.bleed)!=null?p:0;if(r&&M)if(f(!!g,\"WebGL is not enabled!\"),c&&([n,t,i]=ws(n,t,i)),u){let d=u.sizeInverse,y=e.pos.x*d.x,b=e.pos.y*d.y,v=e.size.x*d.x,w=e.size.y*d.y;if(Ir(u.glTexture),h){let _=d.x*h,S=d.y*h;hs(n.x,n.y,o?-t.x:t.x,t.y,i,y+_,b+S,y-_+v,b-S+w,s.rgbaInt(),a&&a.rgbaInt())}else hs(n.x,n.y,o?-t.x:t.x,t.y,i,y,b,y+v,b+w,s.rgbaInt(),a&&a.rgbaInt())}else{let d=a?s.add(a):s;Sc(n.x,n.y,t.x,t.y,i,d.rgbaInt())}else++ve,++Se,mn(n,t,i,o,d=>{if(u){d.scale(1,-1);let y=e.pos.x,b=e.pos.y,v=e.size.x,w=e.size.y;fh(d,u.image,y,b,v,w,-.5,-.5,1,1,s,a,h)}else{let y=a?s.add(a):s;d.fillStyle=y.toString(),d.fillRect(-.5,-.5,1,1)}},c,l)}function Ys(n,t,e,s,i,o,a){_t(n,t,void 0,e,s,!1,void 0,i,o,a)}function ih(n,t,e=V,s=$e,i=0,o=M,a=!1,r){if(f(L(n),\"pos must be a vec2\"),f(L(t),\"size must be a vec2\"),f(z(e)&&z(s),\"color is invalid\"),f(R(i),\"angle must be a number\"),f(!r||!o,\"context only supported in canvas 2D mode\"),o&&M){f(!!g,\"WebGL is not enabled!\"),a&&(n=Ss(n),t=t.scale(1/W),i+=Y);let c=[],l=[],u=t.x/2,h=t.y/2,p=e.rgbaInt(),d=s.rgbaInt(),y=rt(-i),b=tt(-i);for(let v=4;v--;){let w=v&1?u:-u,_=v&2?h:-h,S=w*y-_*b,E=w*b+_*y,A=v&2?p:d;c.push(x(n.x+S,n.y+E)),l.push(A)}jr(c,l)}else++ve,++Se,mn(n,t,i,!1,c=>{let l=c.createLinearGradient(0,.5,0,-.5);l.addColorStop(0,e.toString()),l.addColorStop(1,s.toString()),c.fillStyle=l,c.fillRect(-.5,-.5,1,1)},a,r)}function oh(n,t,e,s=0,i=V,o=0,a,r=M,c=!1,l){if(f(L(n),\"pos must be a vec2\"),f(L(t),\"size must be a vec2\"),f(L(e),\"wrapCount must be a vec2\"),f(z(i),\"color is invalid\"),f(R(o),\"angle must be a number\"),f(!a||z(a),\"additiveColor must be a color\"),f(!l||!r,\"context only supported in canvas 2D mode\"),f(!(s instanceof we),\"pass a TextureInfo or texture index, not a TileInfo \\u2014 use tileInfo.textureInfo\"),N)return;let u=typeof s==\"number\"?Qe[s]:s;if(f(u instanceof ke,\"texture not loaded\"),f(u.size.x>0,\"texture not loaded\"),f(u.wrap,\"drawTextureWrapped requires a wrap-enabled texture; call textureInfo.setWrap(true) first\"),r&&M){f(!!g,\"WebGL is not enabled!\"),c&&([n,t,o]=ws(n,t,o)),Ir(u.glTexture),hs(n.x,n.y,t.x,t.y,o,0,0,e.x,e.y,i.rgbaInt(),a&&a.rgbaInt());return}++ve,++Se,c||(n=bn(n),t=t.scale(W),o-=Y);let h=!wi||(a?Xs(i.add(a))&&a.a<=0:Xs(i)),p=!h&&a&&!mr(a),d=h?u.image:dh(u.image,i,a);l=l||vt,l.save(),l.translate(n.x+.5,n.y+.5),l.rotate(o),l.globalAlpha=p?1:i.a;let y=l.createPattern(d,\"repeat\"),b=new DOMMatrix().translate(-t.x/2,-t.y/2).scale(t.x/(e.x*d.width),t.y/(e.y*d.height));y.setTransform(b),l.fillStyle=y,l.fillRect(-t.x/2,-t.y/2,t.x,t.y),l.globalAlpha=1,l.restore()}function fo(n,t=.1,e=V,s=!1,i=x(),o=0,a=M,r=!1,c){if(f(ie(n),\"points must be an array\"),f(R(t),\"width must be a number\"),f(z(e),\"color is invalid\"),f(L(i),\"pos must be a vec2\"),f(R(o),\"angle must be a number\"),f(!c||!a,\"context only supported in canvas 2D mode\"),a&&M){f(!!g,\"WebGL is not enabled!\");let l=x(1);r&&([i,l,o]=ws(i,l,o)),Fr(n,e.rgbaInt(),t,i.x,i.y,l.x,l.y,o,s)}else++ve,++Se,mn(i,x(1),o,!1,l=>{l.strokeStyle=e.toString(),l.lineWidth=t,l.beginPath();for(let u=0;u0&&Fr(n,s.rgbaInt(),e,i.x,i.y,l.x,l.y,o)}else mn(i,x(1),o,!1,l=>{l.fillStyle=t.toString(),l.beginPath();for(let u of n)l.lineTo(u.x,u.y);l.closePath(),l.fill(),e&&(l.strokeStyle=s.toString(),l.lineWidth=e,l.stroke())},r,c)}function nc(n,t=x(1),e=V,s=0,i=0,o=nt,a=M,r=!1,c){f(L(n),\"pos must be a vec2\"),f(L(t),\"size must be a vec2\"),f(z(e)&&z(o),\"color is invalid\"),f(R(s),\"angle must be a number\"),f(R(i),\"lineWidth must be a number\"),f(i>=0,\"lineWidth must be a positive value or 0\"),f(!c||!a,\"context only supported in canvas 2D mode\"),i=j(i,0,$(t.x,t.y)),a&&M?ec(n,t,ms,e,i,o,s,a,r,c):mn(n,x(1),s,!1,l=>{l.fillStyle=e.toString(),l.beginPath(),l.ellipse(0,0,t.x/2,t.y/2,0,0,9),l.fill(),i&&(l.strokeStyle=o.toString(),l.lineWidth=i,l.stroke())},r,c)}function $s(n,t=1,e=V,s=0,i=nt,o=M,a=!1,r){f(R(t),\"size must be a number\"),nc(n,x(t),e,0,s,i,o,a,r)}function sc(n,t=x(1),e=V,s=$e,i=0,o=M,a=!1,r){if(f(L(n),\"pos must be a vec2\"),f(L(t),\"size must be a vec2\"),f(z(e)&&z(s),\"color is invalid\"),f(R(i),\"angle must be a number\"),f(!r||!o,\"context only supported in canvas 2D mode\"),!N)if(o&&M){f(!!g,\"WebGL is not enabled!\"),a&&(n=Ss(n),t=t.scale(1/W),i+=Y);let c=ms,l=t.x/2,u=t.y/2,h=e.rgbaInt(),p=s.rgbaInt(),d=rh++,y=rt(-i),b=tt(-i),v=E=>{let A=tt(E)*l,T=rt(E)*u;return x(n.x+A*y-T*b,n.y+A*b+T*y)},w=d%c/c*q*2,_=[v(w)],S=[p];for(let E=c;E--;){let A=(E+d)%c/c*q*2;_.push(n),S.push(h),_.push(v(A)),S.push(p)}jr(_,S)}else++ve,++Se,mn(n,t,i,!1,c=>{let l=c.createRadialGradient(0,0,0,0,0,.5);l.addColorStop(0,e.toString()),l.addColorStop(1,s.toString()),c.fillStyle=l,c.beginPath(),c.ellipse(0,0,.5,.5,0,0,9),c.fill()},a,r)}function ah(n,t=1,e=V,s=$e,i=M,o=!1,a){f(R(t),\"size must be a number\"),sc(n,x(t),e,s,0,i,o,a)}function mn(n,t,e=0,s=!1,i,o=!1,a=vt){f(L(n),\"pos must be a vec2\"),f(L(t),\"size must be a vec2\"),f(R(e),\"angle must be a number\"),f(typeof i==\"function\",\"drawFunction must be a function\"),o||(n=bn(n),t=t.scale(W),e-=Y),a.save(),a.translate(n.x+.5,n.y+.5),a.rotate(e),a.scale(s?-t.x:t.x,-t.y),i(a),a.restore()}function ch(n,t,e=1,s=V,i=0,o=nt,a=\"center\",r=ys,c=\"\",l,u=0,h=vt){t=bn(t),e*=W,i*=W,u-=Y,u*=-1,He(n,t,e,s,i,o,a,r,c,l,u,h)}function He(n,t,e,s=V,i=0,o=nt,a=\"center\",r=ys,c=\"\",l,u=0,h=vt){f(J(n),\"text must be a string\"),f(L(t),\"pos must be a vec2\"),f(R(e),\"size must be a number\"),f(z(s),\"color must be a color\"),f(R(i),\"lineWidth must be a number\"),f(z(o),\"lineColor must be a color\"),f([\"left\",\"center\",\"right\"].includes(a),\"align must be left, center, or right\"),f(J(r),\"font must be a string\"),f(J(c),\"fontStyle must be a string\"),f(R(u),\"angle must be a number\");let p=(n+\"\").split(`\n`),d=t.y-(p.length-1)*e/2;h.save(),h.fillStyle=s.toString(),h.strokeStyle=o.toString(),h.lineWidth=i,h.textAlign=a,h.font=c+\" \"+e+\"px \"+r,h.textBaseline=\"middle\",h.translate(t.x,d),h.rotate(-u);let y=0;p.forEach(b=>{i&&h.strokeText(b,0,y,l),h.fillText(b,0,y,l),y+=e}),h.restore()}async function Sa(n,t){f(R(n),\"textureIndex must be a number\"),f(!Qe[n],\"textureIndex is already loaded!\"),f(!t||J(t),\"image src must be a string\");let e=new Image;t&&await new Promise(s=>{e.onerror=e.onload=s,e.crossOrigin=\"anonymous\",e.src=t}),Qe[n]=new ke(e)}function Ss(n){f(L(n),\"screenPos must be a vec2\");let t=(n.x-D.x/2+.5)/W,e=(n.y-D.y/2+.5)/-W;if(Y){let s=rt(-Y),i=tt(-Y),o=t*s-e*i,a=t*i+e*s;t=o,e=a}return new it(t+wt.x,e+wt.y)}function bn(n){f(L(n),\"worldPos must be a vec2\");let t=n.x-wt.x,e=n.y-wt.y;if(Y){let s=rt(Y),i=tt(Y),o=t*s-e*i,a=t*i+e*s;t=o,e=a}return new it(t*W+D.x/2-.5,e*-W+D.y/2-.5)}function ic(n){f(L(n),\"screenDelta must be a vec2\");let t=n.x/W,e=n.y/-W;if(Y){let s=rt(-Y),i=tt(-Y),o=t*s-e*i,a=t*i+e*s;t=o,e=a}return new it(t,e)}function lh(n){f(L(n),\"worldDelta must be a vec2\");let t=n.x,e=n.y;if(Y){let s=rt(Y),i=tt(Y),o=t*s-e*i,a=t*i+e*s;t=o,e=a}return new it(t*W,e*-W)}function ws(n,t,e=0){return f(L(n),\"screenPos must be a vec2\"),f(L(t),\"screenSize must be a vec2\"),f(R(e),\"screenAngle must be a number\"),[Ss(n),t.scale(1/W),e+Y]}function oc(){return D.scale(1/W)}function uh(n,t,e,s){f(L(n),\"center must be a vec2\"),f(L(t),\"size must be a vec2\");let i=p(e),o=p(s),a=t.x+i.left+i.right,r=t.y+i.top+i.bottom,c=D.x-o.left-o.right,l=D.y-o.top-o.bottom;if(!(a>0&&r>0&&c>0&&l>0))return W;W=$(c/a,l/r);let u=x(i.right-i.left,i.top-i.bottom).scale(.5),h=x(o.right-o.left,o.top-o.bottom).scale(.5/W);return wt=n.add(u).add(h),W;function p(d){return(d===void 0||R(d))&&(d=x(d)),L(d)?{top:d.y,right:d.x,bottom:d.y,left:d.x}:{top:d.top||0,right:d.right||0,bottom:d.bottom||0,left:d.left||0}}}function hh(n,t=0){if(f(L(n),\"pos must be a vec2\"),f(L(t)||R(t),\"size must be a vec2 or number\"),!W)return!1;let e=n.x-wt.x,s=n.y-wt.y;if(Y){let a=rt(Y),r=tt(Y),c=e*a-s*r,l=e*r+s*a;e=c,s=l}e*=W*2,s*=-W*2,t instanceof it&&(t=t.length()),t*=W;let i=D.x,o=D.y;return e+t>-i&&e-t-o&&s-t{n.onerror=n.onload=r,n.crossOrigin=\"anonymous\",n.src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAeAQMAAABnrVXaAAAABlBMVEUAAAD///+l2Z/dAAAAAXRSTlMAQObYZgAAAjpJREFUOMu9kzFu2zAUhn+CAROgqrk+B2l0BWYxMjlXeYaAtFtbdA1sGgHqRQfI0CNkSG5AwYB0BQ8d5Bsomwah6CPVeGg6tEPzAxLwyI+P78cP4u9lNO9OoMKnLMOobG5020/yaj/MrRcCGh1gBbyiLTPJEYaIiom5KM9Jq7KgynMGtb6L4GL4MF2H4LQKCXTvDVw2I4MsgZT7QLExdiutH+D08VOP3INXRrWX1/mmpbkNgAPYRVANb4xpcegYvhiNbIXauQICEjBuYLfMakaakWQeXxiZ0VDtuJCKs3ztMV59QtsHJNcRxDzfdL21ty3PrfIcXTN+E+GFAv6T5nbT9jd50/WFxb5ksdAv49qS6ouymG66ji08UMT6moykYLAo+V0j23GN4m829ZySAD5K7QsBfQTvOG8eE+gTeGYRAmnNAubN3hf5Zv9tJWDHp/VTuaSm7SN4fyINQqaNO3RMVxvpSPXnOChnRNvFcGY0gnwiPswYwTKVPE0zVtX3mTEIOoFzaqLrGuJaV+Uqumb71fVk/VoOH3cdLNQP/FHi8hV0CQNoqBZsUPlLPMsdCJro9QAaQQ0woDy9BJm0eTxCFnO9srcYlhNVlfR2EyTrph1uUtbUtAJifwRgrKuYdXVHeb0YI3QpawohQHkloI3J5FuVwI5ORxC9k2Tuz9Ir1IjgeIPGMHYkAZe2RuYkmWFmt3gGbTPOmBUWVTmRmHtGrfpzG/yuQNOKa6gBB/WA9khitPgl6/GP+gl2Af6tCbvaygAAAABJRU5ErkJggg==\"});let t=x(),e=x(8),s=1,i=0,o=new ke(n),a=new we(t,e,o,s,i);ac=new Ks(a)}function mh(n=!0){Cn=n}function bh(n){Sr=n}function xh(){return Ve===\"mouse\"}function vh(){return Ve===\"keyboard\"}function Sh(){return Ve===\"gamepad\"}function wr(n,t=0,e=!0,s=!0,i=!0){H[t]&&(H[t][n]&=~((e?1:0)|(s?2:0)|(i?4:0)))}function Ts(){H.length=0,H[0]=[],Qt.length=0,fn.length=0,As.length=0,pe.length=0,nn.length=0}function Et(n,t=0){var e;return f(J(n),\"key must be a number or string\"),f(t>0||typeof n!=\"number\"||n<3,\"use code string for keyboard\"),!!(((e=H[t])==null?void 0:e[n])&1)}function Lt(n,t=0){var e;return f(J(n),\"key must be a number or string\"),f(t>0||typeof n!=\"number\"||n<3,\"use code string for keyboard\"),!!(((e=H[t])==null?void 0:e[n])&2)}function Tr(n,t=0){var e;return f(J(n),\"key must be a number or string\"),f(t>0||typeof n!=\"number\"||n<3,\"use code string for keyboard\"),!!(((e=H[t])==null?void 0:e[n])&4)}function wh(n=\"ArrowUp\",t=\"ArrowDown\",e=\"ArrowLeft\",s=\"ArrowRight\"){f(J(n),\"up key must be a string\"),f(J(t),\"down key must be a string\"),f(J(e),\"left key must be a string\"),f(J(s),\"right key must be a string\");let i=o=>Et(o)?1:0;return x(i(s)-i(e),i(n)-i(t))}function $n(n){return f(R(n),\"mouse button must be a number\"),Et(n)}function ns(n){return f(R(n),\"mouse button must be a number\"),Lt(n)}function Th(n){return f(R(n),\"mouse button must be a number\"),Tr(n)}function Ae(n,t=pt){return f(R(n),\"button must be a number\"),f(R(t),\"gamepad must be a number\"),Et(n,t+1)}function Ar(n,t=pt){return f(R(n),\"button must be a number\"),f(R(t),\"gamepad must be a number\"),Lt(n,t+1)}function Ah(n,t=pt){return f(R(n),\"button must be a number\"),f(R(t),\"gamepad must be a number\"),Tr(n,t+1)}function ss(n,t=pt){var e,s;return f(R(n),\"stick must be a number\"),f(R(t),\"gamepad must be a number\"),(s=(e=pe[t])==null?void 0:e[n])!=null?s:x()}function po(n=pt){var t;return f(R(n),\"gamepad must be a number\"),(t=nn[n])!=null?t:x()}function go(n=pt){return f(R(n),\"gamepad must be a number\"),!!H[n+1]}function cc(n=pt){var t,e;return f(R(n),\"gamepad must be a number\"),(e=(t=pe[n])==null?void 0:t.length)!=null?e:0}function Ch(n=pt,t=200,e=1,s=1,i=0){var a,r,c;if(f(R(n),\"gamepad must be a number\"),!xs||N)return;let o=(a=navigator==null?void 0:navigator.getGamepads)==null?void 0:a.call(navigator)[n];(c=(r=o==null?void 0:o.vibrationActuator)==null?void 0:r.playEffect)==null||c.call(r,\"dual-rumble\",{duration:t,strongMagnitude:e,weakMagnitude:s,startDelay:i})}function Eh(n=pt){var e,s,i;if(f(R(n),\"gamepad must be a number\"),!xs||N)return;let t=(e=navigator==null?void 0:navigator.getGamepads)==null?void 0:e.call(navigator)[n];(i=(s=t==null?void 0:t.vibrationActuator)==null?void 0:s.reset)==null||i.call(s)}function Cr(n=100){var t;f(R(n)||ie(n),\"pattern must be a number or array\"),xs&&!N&&((t=navigator==null?void 0:navigator.vibrate)==null||t.call(navigator,n))}function _h(){Cr(0)}function Rh(){var n;!Kt&&((n=F.requestPointerLock)==null||n.call(F))}function Ph(){var n;(n=document.exitPointerLock)==null||n.call(document)}function lc(){return document.pointerLockElement===F}function Lh(){if(N)return;document.addEventListener(\"keydown\",n),document.addEventListener(\"keyup\",t),document.addEventListener(\"mousedown\",s),document.addEventListener(\"mouseup\",i),document.addEventListener(\"mousemove\",o),document.addEventListener(\"mouseleave\",a),document.addEventListener(\"wheel\",r,{passive:!1}),document.addEventListener(\"contextmenu\",c),document.addEventListener(\"blur\",l),Kt&&on&&u();function n(p){if(p.repeat||(H[0][p.code]=3,Xn&&(H[0][e(p.code)]=3)),!Cn||!p.cancelable||!document.hasFocus()||p.ctrlKey||p.metaKey||p.altKey||b(p.target)||b(document.activeElement))return;let d=typeof p.key==\"string\"&&p.key.length===1;([\"ArrowUp\",\"ArrowDown\",\"ArrowLeft\",\"ArrowRight\",\"Space\",\"Tab\",\"Backspace\"].includes(p.code)||d)&&p.preventDefault();function b(v){let w=v==null?void 0:v.tagName;return(v==null?void 0:v.isContentEditable)||[\"INPUT\",\"TEXTAREA\",\"SELECT\"].includes(w)}}function t(p){H[0][p.code]=H[0][p.code]&2|4,Xn&&(H[0][e(p.code)]=4)}function e(p){return Xn?p===\"KeyW\"?\"ArrowUp\":p===\"KeyS\"?\"ArrowDown\":p===\"KeyA\"?\"ArrowLeft\":p===\"KeyD\"?\"ArrowRight\":p:p}function s(p){if(Kt&&on)return;zt&&!N&<&&!os()&<.resume(),H[0][p.button]=3;let d=Gt;Gt=h(x(p.x,p.y)),fe=fe.add(Gt.subtract(d)),Cn&&p.cancelable&&document.hasFocus()&&p.preventDefault()}function i(p){Kt&&on||(H[0][p.button]=H[0][p.button]&2|4)}function o(p){Qs=!0;let d=Gt;Gt=h(x(p.x,p.y));let y=lc()?x(p.movementX,p.movementY):Gt.subtract(d);fe=fe.add(y)}function a(){Qs=!1}function r(p){p.ctrlKey||(vr+=Yt(p.deltaY)),Cn&&p.cancelable&&document.hasFocus()&&p.preventDefault()}function c(p){p.preventDefault()}function l(){Ts(),Le.clear(),Qt.length=0,fn.length=0,As.length=0}function u(){document.addEventListener(\"touchstart\",y=>d(y),{passive:!1}),document.addEventListener(\"touchmove\",y=>d(y),{passive:!1}),document.addEventListener(\"touchend\",y=>d(y),{passive:!1});let p;function d(y){if(on){if(zt&&!N&<&&!os()&<.resume(),!Ke||fr){let b=S=>is.includes(S.target)||S.target===sn,v=[];for(let S of y.touches)b(S)||v.push(S);let w=v.length,_=0;if(w){let S=x(v[0].clientX,v[0].clientY),E=Gt;Gt=h(S),p?fe=fe.add(Gt.subtract(E)):H[0][_]=3}else p&&(H[0][_]=H[0][_]&2|4);p=w}return Cn&&y.cancelable&&document.hasFocus()&&y.preventDefault(),!0}}}function h(p){let d=F.getBoundingClientRect(),y=It(p.x,d.left,d.right),b=It(p.y,d.top,d.bottom);return x(y*F.width,b*F.height)}}function Ta(){if(N)return;!(on&&Kt)&&!document.hasFocus()&&Ts(),Te=Ss(Gt),xr=ic(fe),Dh(),t(),n();function n(){let e=$n(0)||$n(1)||$n(2)||fe.length()>Sr,s=!1;for(let o=cc();o--&&!s;)s=ss(o).lengthSquared()>.04;for(let o=17;o--&&!s;)s=Ae(o);let i=!1;for(let o in H[0])if(isNaN(+o)&&H[0][o]&1){i=!0;break}s?Ve=\"gamepad\":e?Ve=\"mouse\":i&&(Ve=\"keyboard\"),_n=Ve===\"gamepad\"}function t(){var a,r,c,l,u,h,p;let e=d=>{let v=w=>w>.3?It(w,.3,.8):w<-.3?-It(-w,.3,.8):0;return x(v(d.x),v(-d.y)).clampLength()};if(Ke&&Kt){if(f(!yn||!jn,\"set touchGamepadLeftStick or touchGamepadLeftButtonCount, not both\"),f(!Nn||!Fn,\"set touchGamepadRightStick or touchGamepadButtonCount, not both\"),!Dn.isSet())return;pt=0;let d=(a=pe[0])!=null?a:pe[0]=[],y=(r=nn[0])!=null?r:nn[0]=x();d.length=0,y.set();for(let v=0;v<2;v++){if(!Ge(v))continue;let w=Rr(v);d[w]=x();let _=(c=fn[v])!=null?c:x();if(bs)d[w]=e(_);else if(_.lengthSquared()>.3){let S=j(Vs(_.x),-1,1),E=j(Vs(_.y),-1,1);d[w]=x(S,-E).clampLength(),w||y.set(S,-E)}}let b=(l=H[1])!=null?l:H[1]=[];for(let v=12;v--;){let w=Ae(v,0);b[v]=Qt[v]?w?1:3:w?4:0,Hs&&b[v]===3&&(v===9||Ih(v))&&Cr(Hs)}return}try{if(!_i||!(navigator!=null&&navigator.getGamepads))return}catch{return}if(!Dt&&!document.hasFocus())return;let s=8,i=navigator.getGamepads(),o=$(s,i.length);for(let d=0;d>1]=e(x(y.axes[S],y.axes[S+1]));let _=!1;for(let S=y.buttons.length;S--;){let E=y.buttons[S],A=Ae(S,d);b[S]=E.pressed?A?1:3:A?4:0,E.pressed&&(!E.value||E.value>.9)&&(_=!0)}_&&(io[d]=!0,io[pt]||(pt=d)),y.mapping===\"standard\"&&w.set((Ae(15,d)&&1)-(Ae(14,d)&&1),(Ae(12,d)&&1)-(Ae(13,d)&&1)),dr&&(w.x||w.y)&&(v[0]=w.clampLength())}Ke&&_n&&Dn.unset()}}function Aa(){if(!N){for(let n of H)for(let t in n)n[t]&=1;vr=0,xr=x(),fe=x()}}function Oh(){Fh()}function Dh(){if(Be||!Ke||!Kt||N||!document.body)return;let n=Be=document.createElement(\"div\");n.style.cssText=\"position:fixed;inset:0;z-index:50;pointer-events:none;opacity:0;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;transition:opacity .2s;box-sizing:border-box;padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)\";let t=uc=document.createElement(\"div\");t.style.cssText=\"position:relative;width:100%;height:100%;pointer-events:none\",n.appendChild(t);let e=Er=document.createElementNS(ti,\"svg\");e.style.cssText=\"position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;fill:none;stroke:#fff;stroke-width:3\",t.appendChild(e);let s=()=>{let i=document.createElement(\"div\");return i.style.cssText=\"position:absolute;pointer-events:auto;touch-action:none\",i.addEventListener(\"pointerdown\",o=>zh(o,i)),i.addEventListener(\"pointermove\",o=>Jh(o)),i.addEventListener(\"pointerup\",o=>Ca(o)),i.addEventListener(\"pointercancel\",o=>Ca(o)),t.appendChild(i),i};is[0]=s(),is[1]=s(),sn=s(),addEventListener(\"resize\",()=>In=!0),document.body.appendChild(n),In=!0}function Ri(){return uc.getBoundingClientRect()}function Ge(n){return n?Nn:yn}function xn(n){return n?Fn:jn}function _r(n){return n?0:4}function Rr(n){return n&&yn?1:0}function Mn(n){return Ge(n)||xn(n)>0}function Ih(n){for(let t=0;t<2;t++){let e=_r(t);if(!Ge(t)&&n>=e&&no.style.cssText=\"position:absolute;pointer-events:auto;touch-action:none;\"+a;if(be){for(let o of is)o.style.display=\"none\";oe?(i(sn,\"inset:0\"),sn.style.display=\"\"):sn.style.display=\"none\"}else{for(let a=0;a<2;a++){let r=is[a],c=a?\"right\":\"left\";if(r.style.display=Mn(a)?\"\":\"none\",Ze){let l=Mn(a?0:1)?\"50%\":\"100%\";i(r,`${c}:0;bottom:0;width:${l};height:60%`)}else i(r,`${c}:0;bottom:0;width:${3*s}px;height:${3*s}px`)}sn.style.display=oe?\"\":\"none\";let o=oe;i(sn,`left:50%;top:50%;width:${2*o}px;height:${2*o}px;transform:translate(-50%,-50%)`)}kh(t,e),In=!1}function kh(n,t){let e=Er;for(;e.firstChild;)e.removeChild(e.firstChild);let s=hc={face:[],thumb:[]},i=Bt,o=(r,c,l,u)=>{let h=document.createElementNS(ti,\"circle\");return h.setAttribute(\"cx\",r),h.setAttribute(\"cy\",c),h.setAttribute(\"r\",l),u&&h.setAttribute(\"fill\",u),e.appendChild(h),h},a=r=>{let c=i*.18,l=i*.5,u=r.x,h=r.y,p=document.createElementNS(ti,\"path\");p.setAttribute(\"d\",`M ${u-c} ${h-l} H ${u+c} V ${h-c} H ${u+l} V ${h+c} H ${u+c} V ${h+l} H ${u-c} V ${h+c} H ${u-l} V ${h-c} H ${u-c} Z`),e.appendChild(p)};for(let r=0;r<2;r++){let c=xn(r),l=_r(r),u=De(r,n,t);if(Ge(r))bs?o(u.x,u.y,i/2):a(u),s.thumb[r]=o(u.x,u.y,i/4,\"#fff\");else if(c===1)s.face[l]=o(u.x,u.y,i/2,\"#000\");else for(let h=0;h2?p:$(p,c-1);d=d===3?2:d===2?3:d;let y=x().setDirection(p,i/2);c===2&&(y.x*=-1),r||(y.x*=-1);let b=u.add(y);s.face[l+d]=o(b.x,b.y,i/4,\"#000\")}}Dt&&ln&&Gh(n,t)}function Gh(n,t){let e=Bt,s=Er,i=(a,r,c)=>{let l=document.createElementNS(ti,a);for(let u in r)l.setAttribute(u,r[u]);l.setAttribute(\"stroke\",c),l.setAttribute(\"stroke-width\",2),l.setAttribute(\"fill\",\"none\"),s.appendChild(l)},o=(a,r,c)=>i(\"circle\",{cx:a.x,cy:a.y,r},c);i(\"line\",{x1:n/2,y1:0,x2:n/2,y2:t},\"#0f0\");for(let a=0;a<2;a++)if(Ge(a))if(Ze){let r=t*.4,c=!Mn(a?0:1),l=c?0:a?n/2:0;i(\"rect\",{x:l,y:r,width:c?n:n/2,height:t-r},\"#0ff\")}else o(De(a,n,t),2*e,\"#0ff\");else xn(a)>=1&&o(De(a,n,t),e,\"#0ff\");if(oe){o(x(n/2,t/2),oe,\"#ff0\");for(let a=0;a<2;a++)Mn(a)&&o(De(a,n,t),2*e,\"#f0f\")}}function Fh(){var l;if(!Be||N)return;if(!Ke||!Kt){Be.style.display!==\"none\"&&(Be.style.display=\"none\",Le.clear(),Qt.length=0,fn.length=0,As.length=0);return}Be.style.display=\"\";let n=Dt&&ln,t=[Fn,jn,yn,Nn,bs,Bt,Ze,oe,be,n].join();t!==wa&&(wa=t,In=!0),In&&Mh();let e=Yn?It(Dn.get(),Yn+1,Yn):1,s=n||Dn.isSet()&&e>0&&!be;if(Be.style.opacity=s?n?1:e*pr:0,!s)return;let i=Ri(),o=i.width,a=i.height,r=Bt,c=hc;if(c){for(let u=0;u<2;u++)if(Ge(u)&&c.thumb[u]){let p=De(u,o,a).add(((l=fn[u])!=null?l:x()).scale(r/2));c.thumb[u].setAttribute(\"cx\",p.x),c.thumb[u].setAttribute(\"cy\",p.y)}for(let u=0;u=Bt)return-1;if(i===1)return o;let r=a.subtract(t);n||(r.x*=-1);let c=i===2?r.xo:r&&De(a,t,e).distance(n)<2*s)return{role:\"stick\",side:a}}else if(xn(a)>=1){let c=Nh(a,n,t,e);if(c>=0)return{role:\"face\",btn:c}}}if(oe){for(let a=0;a<2;a++)if(Mn(a)&&De(a,t,e).distance(n)<2*s)return;if(x(t/2,e/2).distance(n)h.getChannelData(v).set(b)),p.buffer=h,p.playbackRate.value=e,p.loop=i,a=a||lt.createGain(),a.gain.value=t,a.connect(Ie);let d=new StereoPannerNode(lt,{pan:j(s,-1,1)});p.connect(d).connect(a),p.addEventListener(\"ended\",()=>{a.disconnect(),d.disconnect(),c&&c(p)});let y=r*e;return p.start(0,y),Dt&&Ln&&yt(\"sound\",\"vol\",t.toFixed(2),\"rate\",e.toFixed(2),\"pan\",s.toFixed(2),i?\"loop\":\"\"),p}function qh(...n){return Pr([Pi(...n)])}function Pi(n=1,t=.05,e=220,s=0,i=0,o=.1,a=0,r=1,c=0,l=0,u=0,h=0,p=0,d=0,y=0,b=0,v=0,w=1,_=0,S=0,E=0){let A=Bn,T=q*2,P=c*=500*T/A/A,I=e*=(1+ct(t,-t))*T/A,ut=0,ot=0,k=0,G=1,et,ft=[],Rt=0,K=0,At=0,ht,ae=2,ce=T*Z(E)*2/A,Mt=rt(ce),tn=tt(ce)/2/ae,le=1+tn,Cs=-2*Mt/le,Es=(1-tn)/le,_s=(1+Yt(E)*Mt)/2/le,Ii=-(Yt(E)+Mt)/le,Mi=_s,zn=0,Jn=0,Rs=0,Ps=0;for(s=s*A||9,_*=A,i*=A,o*=A,v*=A,l*=500*T/A**3,y*=T/A,u*=T/A,h*=A,p=p*A|0,et=s+_+i+o+v|0;K1?a>2?a>3?a>4?Rt/T%14?At:Yt(At)*Z(At)**r)*(KK?0:(Kh&&(e+=u,I+=u,G=0),p&&!(++ot%p)&&(e=I,c=P,G||(G=1));return ft}function ni(n,t=!0){for(let e of Xe)if((!t||e.isSolid)&&n.arrayCheck(e.size)){let s=e.getCollisionData(n);if(s)return s}return 0}function ze(n,t=x(),e,s=!0){for(let i of Xe)if((!s||i.isSolid)&&i.collisionTest(n,t,e))return i}function pc(n,t,e,s,i=!0){let o,a,r,c=s&&x();for(let l of Xe)if(!i||l.isSolid){let u=l.collisionRaycast(n,t,e,c);if(u){let h=n.distanceSquared(u);(o===void 0||h{M=!1,Q.style.display=\"none\",e.preventDefault(),yt(\"WebGL context lost! Switching to Canvas2d rendering.\");for(let s of Kn)s.glTexture=void 0;qt=void 0,St=0,pn=!1,an.forEach(s=>{var i;return(i=s.glContextLost)==null?void 0:i.call(s)})}),Q.addEventListener(\"webglcontextrestored\",()=>{M=!0,Q.style.display=\"\",yt(\"WebGL context restored, reinitializing...\"),t();for(let e of Kn)e.glTexture=Oi(e.image,e.wrap);an.forEach(e=>{var s;return(s=e.glContextRestored)==null?void 0:s.call(e)})});function t(){oi=Gn(`#version 300 es\nprecision highp float;uniform mat4 m;in vec2 g;in vec4 p,u,c,a;in float r;out vec2 v;out vec4 d,e;void main(){vec2 s=(g-.5)*p.zw;gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);v=mix(u.xw,u.zy,g);d=c;e=a;}`,`#version 300 es\nprecision highp float;uniform sampler2D s;in vec2 v;in vec4 d,e;out vec4 c;void main(){c=texture(s,v)*d+e;}`),ri=Gn(`#version 300 es\nprecision highp float;uniform mat4 m;in vec2 p;in vec4 c;out vec4 d;void main(){gl_Position=m*vec4(p,1,1);d=c;}`,`#version 300 es\nprecision highp float;in vec4 d;out vec4 c;void main(){c=d;}`);let e=new ArrayBuffer(ci);Ot=new Float32Array(e),ls=new Uint32Array(e),mo=g.createBuffer(),Rn=g.createBuffer(),Or=g.createFramebuffer(),St=0;let s=new Float32Array([0,0,1,0,0,1,1,1]);g.bindBuffer(g.ARRAY_BUFFER,Rn),g.bufferData(g.ARRAY_BUFFER,s,g.STATIC_DRAW);let i,o,a,r=(c,l,u,h,p=0)=>{let d=g.getAttribLocation(o,c),y=u===1,b=u&&a;g.enableVertexAttribArray(d),g.vertexAttribPointer(d,h,l,y,b,i),g.vertexAttribDivisor(d,p),i+=h*u};bo=g.createVertexArray(),g.bindVertexArray(bo),i=0,o=oi,a=gc,g.bindBuffer(g.ARRAY_BUFFER,Rn),r(\"g\",g.FLOAT,0,2),g.bindBuffer(g.ARRAY_BUFFER,mo),g.bufferData(g.ARRAY_BUFFER,ci,g.DYNAMIC_DRAW),r(\"p\",g.FLOAT,4,4,1),r(\"u\",g.FLOAT,4,4,1),r(\"c\",g.UNSIGNED_BYTE,1,4,1),r(\"a\",g.UNSIGNED_BYTE,1,4,1),r(\"r\",g.FLOAT,4,1,1),xo=g.createVertexArray(),g.bindVertexArray(xo),i=0,o=ri,a=yc,r(\"p\",g.FLOAT,4,2),r(\"c\",g.UNSIGNED_BYTE,1,4)}}function us(n=!1){!n&&!pn||(Tt(),pn=!1,g.useProgram(oi),g.bindVertexArray(bo))}function mc(){pn||(Tt(),pn=!0,g.useProgram(ri),g.bindVertexArray(xo))}function So(n=!0){if(!M||!g)return;f(!St,\"glPreRender called with unflushed batch.\"),ai||(Q.width=D.x,Q.height=D.y),g.viewport(0,0,D.x,D.y),n&&bc();let t=x(2*W).divide(D);ai&&(t.y=-t.y);let e=wt.rotate(-Y),s=x(-1).subtract(e.multiply(t)),i=rt(Y),o=tt(Y),a=[t.x*i,t.y*o,0,0,-t.x*o,t.y*i,0,0,1,1,1,0,s.x,s.y,0,1],r=(c,l,u)=>{g.useProgram(c);let h=g.getUniformLocation(c,l);g.uniformMatrix4fv(h,!1,u)};r(ri,\"m\",a),r(oi,\"m\",a),g.activeTexture(g.TEXTURE0),Qe[0]&&(qt=Qe[0].glTexture,g.bindTexture(g.TEXTURE_2D,qt)),g.bindBuffer(g.ARRAY_BUFFER,mo),vn=kn=!1,us(!0)}function bc(){if(!g)return;let n=We;g.clearColor(n.r,n.g,n.b,n.a),g.clear(g.COLOR_BUFFER_BIT)}function Ir(n){!g||n===qt||(Tt(),qt=n,g.bindTexture(g.TEXTURE_2D,qt))}function xc(n,t=!0){if(!g||!n)return;let e=n===qt;e?Tt():g.bindTexture(g.TEXTURE_2D,n);let s=t?g.REPEAT:g.CLAMP_TO_EDGE;g.texParameteri(g.TEXTURE_2D,g.TEXTURE_WRAP_S,s),g.texParameteri(g.TEXTURE_2D,g.TEXTURE_WRAP_T,s),!e&&qt&&g.bindTexture(g.TEXTURE_2D,qt)}function wo(n,t){if(!g)return;let e=g.createShader(t);if(g.shaderSource(e,n),g.compileShader(e),Dt&&!g.getShaderParameter(e,g.COMPILE_STATUS))throw g.getShaderInfoLog(e);return e}function Gn(n,t){if(!g)return;let e=g.createProgram();if(g.attachShader(e,wo(n,g.VERTEX_SHADER)),g.attachShader(e,wo(t,g.FRAGMENT_SHADER)),g.linkProgram(e),Dt&&!g.getProgramParameter(e,g.LINK_STATUS))throw g.getProgramInfoLog(e);return e}function Oi(n,t=!1){if(!g)return;let e=g.createTexture(),s=!1;if(n!=null&&n.width)Mr(e,n),g.bindTexture(g.TEXTURE_2D,e),s=!dn&&Zn(n.width)&&Zn(n.height);else{let r=new Uint8Array([255,255,255,255]);g.bindTexture(g.TEXTURE_2D,e),g.texImage2D(g.TEXTURE_2D,0,g.RGBA,1,1,0,g.RGBA,g.UNSIGNED_BYTE,r)}let i=dn?g.NEAREST:g.LINEAR,o=s?g.LINEAR_MIPMAP_LINEAR:i;g.texParameteri(g.TEXTURE_2D,g.TEXTURE_MAG_FILTER,i),g.texParameteri(g.TEXTURE_2D,g.TEXTURE_MIN_FILTER,o);let a=t?g.REPEAT:g.CLAMP_TO_EDGE;return g.texParameteri(g.TEXTURE_2D,g.TEXTURE_WRAP_S,a),g.texParameteri(g.TEXTURE_2D,g.TEXTURE_WRAP_T,a),s&&g.generateMipmap(g.TEXTURE_2D),g.bindTexture(g.TEXTURE_2D,qt),e}function vc(n){g&&g.deleteTexture(n)}function Mr(n,t){g&&(f((t==null?void 0:t.width)>0,\"Invalid image data.\"),g.bindTexture(g.TEXTURE_2D,n),g.texImage2D(g.TEXTURE_2D,0,g.RGBA,g.RGBA,g.UNSIGNED_BYTE,t),!dn&&Zn(t.width)&&Zn(t.height)&&g.generateMipmap(g.TEXTURE_2D),g.bindTexture(g.TEXTURE_2D,qt))}function Kh(n){N||(Kn.add(n),g&&(n.glTexture?Mr(n.glTexture,n.image):n.glTexture=Oi(n.image,n.wrap)))}function Qh(n){if(N)return;Kn.delete(n);let t=n.glTexture;n.glTexture=void 0,vc(t)}function Tt(){if(M&&g&&St){let n=kn?g.ONE:g.ONE_MINUS_SRC_ALPHA;g.blendFuncSeparate(g.SRC_ALPHA,n,g.ONE,n),g.enable(g.BLEND);let t=St*(pn?Li:Dr);g.bufferSubData(g.ARRAY_BUFFER,0,Ot,0,t),pn?g.drawArrays(g.TRIANGLE_STRIP,0,St):g.drawArraysInstanced(g.TRIANGLE_STRIP,0,4,St),++ve,Se+=St,St=0}kn=vn}function kr(n){!M||!g||(Tt(),n.drawImage(Q,0,0))}function Zh(n=!0){f(!Q,\"must be called before engineInit\"),Lr=n}function hs(n,t,e,s,i=0,o=0,a=0,r=1,c=1,l=-1,u=0){(St>=Yh||kn!==vn)&&Tt(),us();let h=St++*Dr;Ot[h++]=n,Ot[h++]=t,Ot[h++]=e,Ot[h++]=s,Ot[h++]=o,Ot[h++]=a,Ot[h++]=r,Ot[h++]=c,ls[h++]=l,ls[h++]=u,Ot[h++]=i}function Sc(n,t,e,s,i,o){hs(n,t,e,s,i,0,0,0,0,0,o)}function Gr(n,t,e,s,i,o,a,r=!0){let c=[],l=tt(-a),u=rt(-a);for(let p of n){let d=p.x*i,y=p.y*o;c.push(x(e+u*d-l*y,s+l*d+u*y))}let h=r?ed(c):c;wc(h,t)}function Fr(n,t,e,s,i,o,a,r,c=!0){let l=td(n,e,c);Gr(l,t,s,i,o,a,r,!1)}function wc(n,t){if(!M||n.length<3)return;let e=n.length+2;if((St+e>=Pn||kn!==vn)&&Tt(),f(e=Pn)return;mc();let s=St*Li;for(let i=e;i--;){let o=j(i-1,0,e-3),a=n[o];Ot[s++]=a.x,Ot[s++]=a.y,ls[s++]=t}St+=e}function jr(n,t){if(!M||n.length<3)return;let e=n.length+2;if((St+e>=Pn||kn!==vn)&&Tt(),f(e=Pn)return;mc();let s=St*Li;for(let i=e;i--;){let o=j(i-1,0,e-3),a=n[o],r=t[o];Ot[s++]=a.x,Ot[s++]=a.y,ls[s++]=r}St+=e}function qn(n,t=!1){n?(ai=n,g.bindFramebuffer(g.FRAMEBUFFER,Or),g.framebufferTexture2D(g.FRAMEBUFFER,g.COLOR_ATTACHMENT0,g.TEXTURE_2D,n,0),So(t)):(Tt(),ai=void 0,g.bindFramebuffer(g.FRAMEBUFFER,null),g.viewport(0,0,D.x,D.y))}function Tc(n,t,e,s){M&&(g.enable(g.SCISSOR_TEST),g.scissor(n,t,e,s),g.clearColor(0,0,0,0),g.clear(g.COLOR_BUFFER_BIT),g.disable(g.SCISSOR_TEST))}function td(n,t,e=!0){if(n.length<2)return[];let s=t/2,i=[],o=n.length,a=1e-6,r=10;for(let c=0;ca?-d/y:0,S=y>a?p/y:0,E=w>a?-v/w:0,A=w>a?b/w:0,T=_+E,P=S+A,I=(T*T+P*P)**.5;if(Ia){let G=$(1/k,r);T*=G,P*=G}}let ut=x(u.x-T*s,u.y-P*s),ot=x(u.x+T*s,u.y+P*s);i.push(ut),i.push(ot)}return i.length>1&&e&&(i.push(i[0]),i.push(i[1])),i}function ed(n){if(n.length<3)return[];let t=(d,y,b)=>(y.x-d.x)*(b.y-d.y)-(y.y-d.y)*(b.x-d.x);(d=>{let y=0;for(let b=d.length;b--;){let v=(b+1)%d.length;y+=d[b].cross(d[v])}return y})(n)<0&&(n=n.slice().reverse());let s=1e-9,i=(d,y,b,v)=>{let w=t(y,b,d),_=t(b,v,d),S=t(v,y,d),E=(w<-s?1:0)+(_<-s?1:0)+(S<-s?1:0),A=(w>s?1:0)+(_>s?1:0)+(S>s?1:0);return!(E&&A)},o=[];for(let d=0;d3&&r++{if(E>=0){let T=s.fillStyle=s.createLinearGradient(v,w,_,S);T.addColorStop(0,h(E,2)),T.addColorStop(1,h(E,1))}else s.fillStyle=\"#000\";E>=-1?(s.fill(),A&&s.stroke()):s.stroke()},c=(v,w,_,S=0,E=2*q,A,T)=>{s.beginPath(),s.arc(v,w,_,d*S,d*E),r(v,w-_,v,w+_,A,T)},l=(v,w,_,S,E)=>{s.beginPath(),s.rect(v,w,_,S*d),r(v,w+S,v+_,w,E)},u=(v,w,_,S)=>{s.beginPath();for(let E of v)s.lineTo(E.x,E.y);s.closePath(),r(0,_,0,_+S,w)},h=(v,w)=>w?`hsl(${[.95,.56,.13][v%3]*360} 99%${[0,50,75][w]}%)`:\"#000\",p=Va(1,1,n),d=It(p,.1,.5),y=$(6,$(o,a)/99);s.translate(o/2,a/2),s.scale(y,y),s.translate(-40,-35),d<1&&s.setLineDash([99*d,99]),s.lineJoin=s.lineCap=\"round\",s.lineWidth=.1+d*1.9;{let w=\"LittleJS\";s.font=\"900 15.5px arial\",s.lineWidth=.1+d*3.9,s.textAlign=\"center\",s.textBaseline=\"top\",l(11,55,59,8*d,-1),s.beginPath();let _=0;for(let S=0;S5?1:0),s[S?\"strokeText\":\"fillText\"](w[E],P,54+.5,17*d),A+=T}s.lineWidth=.1+d*1.9,l(3,54,73,0)}l(7,15,26,-7,0),l(25,15,8,25,-1),l(10,40,15,-25,1),l(14,21,7,9,2),l(38,20,6,-6,2),l(49,20,10,-6,0);let b=[x(44,8),x(64,8),x(59,8+6*d),x(49,8+6*d)];u(b,2,8,6*d),l(44,8,20,-7,0);for(let v=5;v--;)c(59-v*6*d,30,10,0,2*q,1,0);c(59,30,4,0,7,2),l(35,20,24,0),c(59,30,10),c(47,30,10,q/2,q*3/2),c(35,30,10,q/2,q*3/2),l(7,40,13,7,-1),l(17,40,43,14,-1);for(let v=3;v--;)for(let w=2;w--;)c(17+15*v,47,w?7:1,0,2*q,2);for(let v=2;v--;){let w=6,_=7,S=53+w*d*v,E=[x(S+_,54),x(S,40),x(S+w*d,40),x(S+_+w*d,54)];u(E,0,40,14)}s.restore()}function sd(n){if(hi=n,!en){let e={};try{e=JSON.parse(localStorage[n]||\"{}\")}catch{e={}}Di(s=>{s.unlocked=!!(e[s.id]&&e[s.id].unlocked)}),Br()}gn(void 0,t);function t(){if(!Ns.length)return;let e=Ns[0],s=Ye-js;if(!js)js=Ye;else if(s>li)js=0,Ns.shift();else{let i=li-En,o=si?(s-i)/En:0;e.render(o)}}}function Di(n){Object.values(ds).forEach(t=>n(t))}function id(){Di(n=>n.unlocked=!1),Br()}function Br(){if(!hi)return;let n={};Di(t=>{let e={name:t.name,description:t.description,icon:t.icon,unlocked:t.unlocked};t.image&&(e.src=t.image.src),n[t.id]=e}),localStorage[hi]=JSON.stringify(n)}function od(n){li=n}function rd(n){En=n}function ad(n){ui=n.copy()}function cd(n){Nr=n}function Ac(n,t,e,s=125){let i,o,a,r,c,l,u,h,p,d,y,b,v,w=0,_,S=[],E=[],A=[],T=0,P=0,I=1,ut={},ot=Bn/s*60>>2;for(;I;T++)S=[I=h=b=0],e.forEach((k,G)=>{for(u=t[k][T]||[0,0,0],I|=t[k][T]&&1,_=b+(t[k][0].length-2-(h?0:1))*ot,v=G===e.length-1,i=2,a=b;iot-99&&p&&y<1?y+=1/99:0)l=(1-y)*S[w++]/2||0,E[a]=(E[a]||0)-l*P+l,A[a]=(A[a++]||0)+l*P+l;c&&(y=c%1,P=u[1]||0,(c|=0)&&(S=ut[[d=u[w=0]||0,c]]=ut[[d,c]]||(r=[...n[d]],r[2]=(r[2]||220)*2**(c/12-1),c>0?Pi(...r):[])))}b=_});return[E,A]}function ld(n){zr=typeof n==\"boolean\"?n?1:0:n}function ud(n){Jr=n}async function hd(){return new mi(await Box2D()),e(),gn(n,t),m;function n(){if(!be){m.step(),m.objects=m.objects.filter(s=>!s.destroyed);for(let s of m.objects)s.body&&(s.pos=m.vec2From(s.body.GetPosition()),s.angle=-s.body.GetAngle())}}function t(){(Jr||ne)&&m.world.DrawDebugData()}function e(){let i=new m.instance.JSDraw,o=l=>new xt(l.get_r(),l.get_g(),l.get_b()),a=l=>o(m.instance.wrapPointer(l,m.instance.b2Color)),r=l=>a(l).scale(1,.8),c=(l,u)=>{let h=[];for(let p=u;p--;)h.push(m.vec2FromPointer(l+p*8));return h};i.DrawSegment=function(l,u,h){h=r(h),l=m.vec2FromPointer(l),u=m.vec2FromPointer(u),rn(l,u,.1,h,x(),0,!1)},i.DrawPolygon=function(l,u,h){h=r(h);let p=c(l,u);On(p,$e,.1,h,x(),0,!1)},i.DrawSolidPolygon=function(l,u,h){h=r(h);let p=c(l,u);On(p,h,0,h,x(),0,!1)},i.DrawCircle=function(l,u,h){h=r(h),l=m.vec2FromPointer(l),$s(l,u*2,$e,.1,h,!1)},i.DrawSolidCircle=function(l,u,h,p){p=r(p),l=m.vec2FromPointer(l),h=m.vec2FromPointer(h).scale(u),$s(l,u*2,p,.1,p,!1),rn(x(),h,.1,p,l,0,!1)},i.DrawTransform=function(l){l=m.instance.wrapPointer(l,m.instance.b2Transform);let u=m.vec2From(l.get_p()),h=-l.get_q().GetAngle(),p=x(1,0),d=B(.75,0,0,.8),y=x(0,1),b=B(0,.75,0,.8);rn(x(),p,.1,d,u,h,!1),rn(x(),y,.1,b,u,h,!1)},i.AppendFlags(m.instance.b2Draw.e_shapeBit),i.AppendFlags(m.instance.b2Draw.e_jointBit),m.world.SetDebugDraw(i)}}function dd(n,t,e,s=32,i=2,o=0){Cc(n,t,e,V,s,nt,i,o,!1,!0)}function Cc(n,t,e,s,i=1,o,a=.05,r=0,c=M,l,u){let h=e.offset(e.size),p=t.add(x(a-i*2)),d=x(i),y=t.scale(.5).subtract(d.scale(.5)),b=l?-1:1,v=l?-r:r;_t(n,p,h,s,r,!1,o,c,l,u);for(let w=4;w--;){let _=w%2,S=y.multiply(x(_?w===1?1:-1:0,_?0:w?-1:1)),E=x(_?i:p.x,_?p.y:i),A=h.offset(e.size.multiply(x(w===1?1:w===3?-1:0,w===0?-b:w===2?b:0)));_t(n.add(S.rotate(v)),E,A,s,r,!1,o,c,l,u)}for(let w=4;w--;){let _=w>1,S=w&&w<3,E=y.multiply(x(_?-1:1,S?-1:1)),A=h.offset(e.size.multiply(x(_?-1:1,S?b:-b)));_t(n.add(E.rotate(v)),d,A,s,r,!1,o,c,l,u)}}function fd(n,t,e,s=32,i=2,o=0){Ec(n,t,e,V,s,nt,i,o,!1,!0)}function Ec(n,t,e,s,i=1,o,a=.05,r=0,c=M,l,u){let h=e.frame(0),p=e.frame(1),d=e.frame(2),y=t.add(x(a-i*2)),b=x(i),v=t.scale(.5).subtract(b.scale(.5)),w=l?-1:1,_=l?-r:r;_t(n,y,d,s,r,!1,o,c,l,u);for(let S=4;S--;){let E=r+S*q/2,A=S%2,T=v.multiply(x(A?S===1?1:-1:0,A?0:S?-w:w)),P=x(A?y.y:y.x,i);_t(n.add(T.rotate(_)),P,p,s,E,!1,o,c,l,u)}for(let S=4;S--;){let E=r+S*q/2,A=!S||S>2,T=S>1,P=v.multiply(x(A?-1:1,T?-w:w));_t(n.add(P.rotate(_)),b,h,s,E,!1,o,c,l,u)}}function pd(n,t=1,e=0,s=V,i=0,o=!1,a=0,r=nt,c=M,l=!1,u){let h=_c(x(),t,e,0,o);On(h,s,a,r,n,i,c,l,u)}function _c(n,t=1,e=0,s=0,i=!1,o=ms){f(L(n),\"pos must be a vec2\"),f(R(t)&&R(e),\"size and percent must be numbers\");let a=$t(e*4,4);a>=2&&(s+=q),a=a<=2?a-1:3-a,i&&(a=-a,s+=q);let r=[],c=X(3,o>>1),l=t/2;for(let u=0;u<=c;u++){let h=u/c*q;r.push(x(l*rt(h),l*tt(h)).rotate(s).add(n))}for(let u=c;u>=0;u--){let h=u/c*q;r.push(x(l*rt(h),-l*a*tt(h)).rotate(s).add(n))}return r}function Ra(n){return n&&typeof n.lerp==\"function\"}function gd(n,t,e,s,i=1,o={}){f(n!=null&&typeof n==\"object\",\"tweenProperty target must be an object\"),f(J(t)&&t.length>0,\"tweenProperty propertyPath must be a non-empty string\");let a=t.split(\".\"),r=a.pop(),c=l=>{let u=n;for(let h of a)u=u[h];u[r]=l};return new bi(c,e,s,i,o)}function Rc(n){n.loopRemaining!==1/0&&n.loopRemaining<=1||(n.loopRemaining!==1/0&&(n.loopRemaining-=1),n.life=n.duration,n.thenCallback=()=>Rc(n),Ut.push(n),n.callback(n.interp(n.duration)))}function Pc(n){if(n.loopRemaining!==1/0&&n.loopRemaining<=1)return;n.loopRemaining!==1/0&&(n.loopRemaining-=1);let t=n.start;n.start=n.end,n.end=t,n.life=n.duration,n.thenCallback=()=>Pc(n),Ut.push(n),n.callback(n.interp(n.duration))}function Lc(n,t){n===void 0?(n=Wt-Ea,t=Ye-_a,Ea=Wt,_a=Ye):t===void 0&&(t=n);for(let e=Ut.length;e--;){let s=Ut[e];if(s.paused)continue;let i=s.useRealTime?t:n;if(!(i<=0))if(s.life-=i,s.life>0)s.callback(s.interp(s.life));else{s.callback(s.interp(0)),Ut.splice(e,1);let o=s.thenCallback;s.thenCallback=void 0,o&&o()}}}function yd(){for(let n of Ut)n.thenCallback=void 0;Ut.length=0}var Bs,zs,Ll,$o,mt,Ko,Pa,Wt,Ye,be,so,de,Js,La,an,ro,Dt,Ia,Si,Ma,Ce,Ue,ne,se,cn,ln,Ln,ao,Ee,ee,q,Z,bt,Qn,Vs,$,X,Yt,ja,Na,tt,rt,Ba,Zo,uo,it,xt,V,$e,nt,re,Xl,nr,Yl,Ya,$a,$l,Ka,Qa,Kl,xe,wt,Y,W,ir,wi,We,wn,Hn,Tn,An,or,dn,qe,ys,rr,N,M,ms,Ti,Ai,Ci,Ei,ar,cr,lr,ur,hr,Re,jt,qs,_i,dr,Xn,on,Ke,fr,oe,Fn,yn,jn,Nn,bs,Ze,Bt,pr,Yn,Hs,xs,zt,un,gr,yr,Me,F,at,vt,Oe,Je,ge,Pe,D,Qe,ve,Se,we,ke,rh,ac,Ks,Te,Gt,xr,fe,vr,Qs,_n,Ve,Sr,Cn,pt,Kt,H,pe,nn,io,Dn,Qt,fn,Zs,As,Le,Be,uc,Er,hc,is,sn,In,wa,ti,lt,Ie,Bn,rs,ei,Xe,hn,si,as,cs,yo,Xh,ii,Q,g,Lr,oi,ri,pn,vn,kn,qt,mo,Rn,Ot,ls,St,Kn,bo,xo,Or,ai,vo,ci,Dr,gc,Yh,Li,yc,Pn,en,li,En,ui,Nr,ds,Ns,hi,js,di,fs,To,Ao,Pt,Co,U,Eo,_o,Ro,C,zr,Po,Zt,fi,Lo,Oo,ps,Do,Io,Mo,ko,m,Jr,gs,pi,Go,Fo,gi,Jt,jo,yi,No,Bo,zo,Jo,Vo,Uo,Wo,qo,Ho,Xo,mi,Ut,Ea,_a,bi,xi,md,oo,vi,Yo,Dc=Nc(()=>{\"use strict\";Bs=\"LittleJS\",zs=\"1.18.19\",Ll=60,$o=.016666666666666666,mt=[],Ko=[],Pa=0,Wt=0,Ye=0,be=!1;so=0,de=0,Js=0,La=!0,an=[],ro=class{constructor(t,e,s,i){this.update=t,this.render=e,this.glContextLost=s,this.glContextRestored=i}};Dt=!0,Ia=.5,Si=!0,Ma=\"Escape\",Ce=!1,Ue=[],ne=!1,se=!1,cn=!1,ln=!1,Ln=!1;q=Math.PI,Z=Math.abs,bt=Math.floor,Qn=Math.ceil,Vs=Math.round,$=Math.min,X=Math.max,Yt=n=>Math.sign(n),ja=(...n)=>Math.hypot(...n),Na=n=>Math.log2(n),tt=Math.sin,rt=Math.cos,Ba=Math.tan,Zo=Math.atan2;uo=class{constructor(t=123456789){f(t!==0,\"RandomGenerator seed must be non-zero (xorshift is fixed at 0)\"),this.seed=t}float(t=1,e=0){return this.seed^=this.seed<<13,this.seed^=this.seed>>>17,this.seed^=this.seed<<5,e+(t-e)*((this.seed>>>0)/2**32)}int(t,e=0){return bt(this.float(t,e))}bool(t=.5){return this.float().5?1:-1}floatSign(t=1,e=0){let s=$(t,e),o=X(t,e)-s,a=this.float(o*2);return at?this.scale(t/e):this.copy()}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}reflect(t,e=1){return this.subtract(t.scale((1+e)*this.dot(t)))}angle(){return Zo(this.x,this.y)}setAngle(t=0,e=1){return Vt(t),Vt(e),this.x=e*tt(t),this.y=e*rt(t),this}rotate(t){Vt(t);let e=rt(-t),s=tt(-t);return new n(this.x*e-this.y*s,this.x*s+this.y*e)}setDirection(t,e=1){return Vt(t),Vt(e),t=$t(t,4),f(t===0||t===1||t===2||t===3,\"Vector2.setDirection() direction must be an integer between 0 and 3.\"),this.x=t%2?t-1?-e:e:0,this.y=t%2?0:t?-e:e,this}direction(){return Z(this.x)>Z(this.y)?this.x<0?3:1:this.y<0?2:0}abs(){return new n(Z(this.x),Z(this.y))}floor(){return new n(bt(this.x),bt(this.y))}snap(t){return Vt(t),new n(bt(this.x*t)/t,bt(this.y*t)/t)}mod(t=1){return new n($t(this.x,t),$t(this.y,t))}area(){return Z(this.x*this.y)}lerp(t,e){va(t),Vt(e);let s=j(e);return new n(t.x*s+this.x*(1-s),t.y*s+this.y*(1-s))}arrayCheck(t){return this.x>=0&&this.y>=0&&this.x(u=$t(u,1))*6<1?c+(l-c)*6*u:u*2<1?l:u*3<2?c+(l-c)*(4-u*6):c;return this.r=r(a,o,t+1/3),this.g=r(a,o,t),this.b=r(a,o,t-1/3),this.a=i,Wn(this),this}HSLA(){let t=j(this.r),e=j(this.g),s=j(this.b),i=j(this.a),o=X(t,e,s),a=$(t,e,s),r=(o+a)/2,c=0,l=0;if(o!==a){let u=o-a;l=r>.5?u/(2-o-a):u/(o+a),t===o?c=(e-s)/u+(e((s=j(s)*255|0)<16?\"0\":\"\")+s.toString(16);return\"#\"+e(this.r)+e(this.g)+e(this.b)+(t?e(this.a):\"\")}setHex(t){if(f(J(t),\"Color hex code must be a string\"),f(t[0]===\"#\",\"Color hex code must start with #\"),f([4,5,7,9].includes(t.length),\"Invalid hex\"),t.length<6){let e=s=>j(parseInt(t[s],16)/15);this.r=e(1),this.g=e(2),this.b=e(3),this.a=t.length===5?e(4):1}else{let e=s=>j(parseInt(t.slice(s,s+2),16)/255);this.r=e(1),this.g=e(3),this.b=e(5),this.a=t.length===9?e(7):1}return Wn(this),this}rgbaInt(){let t=j(this.r)*255|0,e=j(this.g)*255<<8,s=j(this.b)*255<<16,i=j(this.a)*255<<24;return t+e+s+i}isValid(){return R(this.r)&&R(this.g)&&R(this.b)&&R(this.a)}},V=Ht(B()),$e=Ht(B(1,1,1,0)),nt=Ht(B(0,0,0)),re=Ht(B(0,0,0,0)),Xl=Ht(B(.5,.5,.5)),nr=Ht(B(1,0,0)),Yl=Ht(B(1,.5,0)),Ya=Ht(B(1,1,0)),$a=Ht(B(0,1,0)),$l=Ht(B(0,1,1)),Ka=Ht(B(0,0,1)),Qa=Ht(B(.5,0,1)),Kl=Ht(B(1,0,1)),xe=class{constructor(t,e=!1){f(t===void 0||R(t),\"Constructed Timer is invalid.\",t),this.useRealTime=e;let s=this.getGlobalTime();this.time=t===void 0?void 0:s+t,this.setTime=t}set(t=0){f(R(t),\"Timer is invalid.\",t);let e=this.getGlobalTime();this.time=e+t,this.setTime=t}setUseRealTime(t=!0){f(!this.isSet(),\"Cannot change global time setting while timer is set.\"),this.useRealTime=t}unset(){this.time=void 0}isSet(){return this.time!==void 0}active(){return this.getGlobalTime()=this.time}get(){return this.isSet()?this.getGlobalTime()-this.time:0}getPercent(){return this.isSet()?this.setTime?1-It(this.time-this.getGlobalTime(),0,this.setTime):1:0}getSetTime(){return this.isSet()?this.setTime:0}getGlobalTime(){return this.useRealTime?Ye:Wt}toString(){return this.isSet()?Z(this.get())+\" seconds \"+(this.get()<0?\"before\":\"after\"):\"unset\"}valueOf(){return this.get()}};wt=x(),Y=0,W=32,ir=1,wi=!0,We=re,wn=x(1920,1080),Hn=0,Tn=0,An=x(),or=!1,dn=!0,qe=1,ys=\"arial\",rr=!1,N=!1,M=!0,ms=32,Ti=x(16),Ai=0,Ci=0,Ei=!0,ar=1,cr=1,lr=1,ur=0,hr=.8,Re=1,jt=x(),qs=1,_i=!0,dr=!0,Xn=!0,on=!0,Ke=!1,fr=!1,oe=0,Fn=4,yn=!0,jn=0,Nn=!1,bs=!0,Ze=!1,Bt=100,pr=.3,Yn=3,Hs=0,xs=!0,zt=!0,un=.3,gr=40,yr=.7;Me=class n{constructor(t=x(),e=x(1),s,i=0,o=V,a=0){f(L(t),\"object pos must be a vec2\"),f(L(e),\"object size must be a vec2\"),f(!s||s instanceof we,\"object tileInfo should be a TileInfo or undefined\"),f(typeof i==\"number\"&&isFinite(i),\"object angle should be a number\"),f(z(o),\"object color should be a valid rgba color\"),f(typeof a==\"number\",\"object renderOrder should be a number\"),this.pos=t.copy(),this.size=e.copy(),this.drawSize=void 0,this.tileInfo=s,this.angle=i,this.color=o.copy(),this.additiveColor=void 0,this.mirror=!1,this.destroyed=!1,this.mass=ar,this.damping=cr,this.angleDamping=lr,this.restitution=ur,this.friction=hr,this.gravityScale=1,this.renderOrder=a,this.velocity=x(),this.angleVelocity=0,this.spawnTime=Wt,this.children=[],this.clampSpeed=!0,this.groundObject=void 0,this.parent=void 0,this.localPos=x(),this.localAngle=0,this.collideTiles=!1,this.collideSolidObjects=!1,this.isSolid=!1,this.collideRaycast=!1,mt.push(this)}updateTransforms(){let t=this.parent;if(t){let e=t.getMirrorSign(),s=this.localPos,i=t.pos,o=s.x*e,a=s.y,r=t.angle;if(r){let c=rt(-r),l=tt(-r);this.pos.set(o*c-a*l+i.x,o*l+a*c+i.y)}else this.pos.set(o+i.x,a+i.y);this.angle=e*this.localAngle+r}for(let e of this.children)e.updateTransforms()}updatePhysics(){if(f(!this.parent),this.destroyed)return;this.clampSpeed&&(this.velocity.x=j(this.velocity.x,-Re,Re),this.velocity.y=j(this.velocity.y,-Re,Re));let t=this.pos.copy();if(this.velocity.x*=this.damping,this.velocity.y*=this.damping,this.mass&&(this.velocity.x+=jt.x*this.gravityScale,this.velocity.y+=jt.y*this.gravityScale),this.pos.x+=this.velocity.x,this.pos.y+=this.velocity.y,this.angle+=this.angleVelocity*=this.angleDamping,f(this.angleDamping>=0&&this.angleDamping<=1),f(this.damping>=0&&this.damping<=1),!Ei||!this.mass)return;let e=this.velocity.y<0&&jt.y<0||this.velocity.y>0&&jt.y>0;if(this.groundObject){let s=X(this.friction,this.groundObject.friction),i=this.groundObject.velocity.x;this.velocity.x=i+(this.velocity.x-i)*s,this.groundObject=void 0}if(this.collideSolidObjects)for(let i of Ko){if(i.destroyed||i.parent||i===this||!this.isSolid&&!i.isSolid||!this.isOverlappingObject(i))continue;let o=this.collideWithObject(i),a=i.collideWithObject(this);if(!o||!a)continue;if(Ws(t,this.size,i.pos,i.size)){let p=t.subtract(i.pos),d=p.length(),b=d<.001?x(0,1):p.scale(.001/d);this.velocity=this.velocity.add(b),i.mass&&(i.velocity=i.velocity.subtract(b)),ne&&co(this.pos,this.size,i.pos,i.size,\"#f00\");continue}let r=this.size.add(i.size),c=(t.y-i.pos.y)*2>r.y+jt.y,l=Z(t.y-i.pos.y)*20?-1:1)>0?bt(t.y-this.size.y/2+1)+this.size.y/2+.001:Qn(t.y+this.size.y/2-1)-this.size.y/2-.001;if(Z(u-this.pos.y)<.1&&!ze(x(this.pos.x,u),this.size,this)){this.pos.y=u,ne&>(this.pos,this.size,\"#ff0\");return}this.pos.x=t.x,this.velocity.x*=-a}if(o||!i){if(e){let c=this.size.y/2+1e-4;this.pos.y=jt.y<0?bt(t.y-this.size.y/2)+c:Qn(t.y+this.size.y/2)-c,this.groundObject=s}else this.pos.y=t.y,this.groundObject=void 0;this.velocity.y*=-a}ne&>(this.pos,this.size,\"#f00\")}}}update(){}render(){_t(this.pos,this.drawSize||this.size,this.tileInfo,this.color,this.angle,this.mirror,this.additiveColor)}renderLight(){}destroy(t=!1){var e;if(!this.destroyed){this.destroyed=!0,(e=this.parent)==null||e.removeChild(this);for(let s of this.children)s.parent=void 0,s.destroy(t)}}localToWorld(t){return this.pos.add(t.rotate(this.angle))}worldToLocal(t){return t.subtract(this.pos).rotate(-this.angle)}localToWorldVector(t){return t.rotate(this.angle)}worldToLocalVector(t){return t.rotate(-this.angle)}collideWithTile(t,e){return t>0}collideWithObject(t){return!0}getUp(t=1){return x().setAngle(this.angle,t)}getRight(t=1){return x().setAngle(this.angle+q/2,t)}getAliveTime(){return Wt-this.spawnTime}getSpeed(){return this.velocity.length()}applyAcceleration(t){this.mass&&(this.velocity=this.velocity.add(t))}applyAngularAcceleration(t){this.mass&&(this.angleVelocity+=t)}applyForce(t){this.mass&&this.applyAcceleration(t.scale(1/this.mass))}getMirrorSign(){return this.mirror?-1:1}addChild(t,e=x(),s=0){return f(!this.destroyed,\"cannot add child to destroyed object\"),this.destroyed||(f(!t.parent&&!this.children.includes(t)),f(t instanceof n,\"child must be an EngineObject\"),f(t!==this,\"cannot add self as child\"),this.children.push(t),t.parent=this,t.localPos=e.copy(),t.localAngle=s,t.updateTransforms()),t}removeChild(t){f(t.parent===this&&this.children.includes(t)),this.children.splice(this.children.indexOf(t),1),t.parent=void 0}isOverlappingObject(t){return this.isOverlapping(t.pos,t.size)}isOverlapping(t,e=x()){return Ws(this.pos,this.size,t,e)}setCollision(t=!0,e=!0,s=!0,i=!0){f(t||!e,\"solid objects must be set to collide\"),this.collideSolidObjects=t,this.isSolid=e,this.collideTiles=s,this.collideRaycast=i}toString(){let t=\"type = \"+this.constructor.name;return(this.pos.x||this.pos.y)&&(t+=`\npos = `+this.pos),(this.velocity.x||this.velocity.y)&&(t+=`\nvelocity = `+this.velocity),(this.size.x||this.size.y)&&(t+=`\nsize = `+this.size),this.angle&&(t+=`\nangle = `+this.angle.toFixed(3)),this.color&&(t+=`\ncolor = `+this.color),t}renderDebugInfo(){if(!Dt)return;let t=this.collideTiles||this.collideSolidObjects||this.isSolid;if(!t&&!this.parent)return;let e=x(X(this.size.x,.2),X(this.size.y,.2)),s=B(this.collideTiles?1:0,this.collideSolidObjects?1:0,this.isSolid?1:0,.5);gt(this.pos,e,s,0,this.angle,t),this.parent&>(this.pos,e.scale(.8),B(1,1,1,.5),0,this.angle),this.parent&&ye(this.pos,this.parent.pos,B(1,1,1,.5),.5)}},D=x(),Qe=[];we=class n{constructor(t=x(),e=Ti,s=Qe[0],i=Ai,o=Ci){this.pos=t.copy(),this.size=e.copy(),this.padding=i,this.textureInfo=s,this.bleed=o}offset(t){return new n(this.pos.add(t),this.size,this.textureInfo,this.padding,this.bleed)}frame(t){f(typeof t==\"number\");let e=this.size.x+this.padding*2,s=t*e;return f(s+this.size.x<=this.textureInfo.size.x,\"frame extends beyond texture width!\"),this.offset(new it(s))}setFullImage(t=this.textureInfo){return this.textureInfo=t,this.pos=new it,this.size=t.size.copy(),this.bleed=this.padding=0,this}tile(t){return vs(t,this.size,this.textureInfo,this.padding,this.bleed)}},ke=class{constructor(t,e=!0,s=!1){this.image=t,this.size=t?x(t.width,t.height):x(),this.sizeInverse=t?x(1/t.width,1/t.height):x(),this.glTexture=void 0,this.wrap=s,e&&this.createWebGLTexture()}createWebGLTexture(){Kh(this)}destroyWebGLTexture(){Qh(this)}hasWebGL(){return!!this.glTexture}setWrap(t=!0){this.wrap=t,xc(this.glTexture,t)}};rh=0;Ks=class{constructor(t){f(!!t,\"tileInfo is required for ImageFont\"),this.tileInfo=t.frame(0)}drawText(t,e,s=1,i,o,a,r){f(L(s)||typeof s==\"number\",\"size must be a vec2 or number\"),typeof s==\"number\"?(f(s>0),s*=W,s=new it(s,s)):s=s.scale(W),this.drawTextScreen(t,bn(e),s,i,o,a,r)}drawTextScreen(t,e,s,i=!0,o=V,a=M,r){f(J(t),\"text must be a string\"),f(L(e),\"pos must be a vec2\"),f(L(s)||typeof s==\"number\",\"size must be a vec2 or number\"),f(z(o),\"color must be a color\"),s=typeof s==\"number\"?new it(s,s):s;let c=new it,l=this.tileInfo,u=l.padding,h=l.size.x+u*2,p=l.size.y+u*2,d=l.textureInfo.size.x/h|0;(t+\"\").split(`\n`).forEach((y,b)=>{let v=i?(y.length-1)*s.x/2:0;for(let w=y.length;w--;){let _=y.charCodeAt(w),S=_<32||_>127?95:_-32,E=S%d,A=S/d|0;l.pos.x=E*h+u,l.pos.y=A*p+u,c.x=e.x+w*s.x-v|0,c.y=e.y+b*s.y|0,_t(c,s,l,o,0,!1,void 0,a,!0,r)}})}};Te=x(),Gt=x(),xr=x(),fe=x(),vr=0,Qs=!0,_n=!1,Ve=\"mouse\",Sr=6,Cn=!0,pt=0,Kt=!N&&window.ontouchstart!==void 0;H=[[]],pe=[],nn=[],io=[],Dn=new xe,Qt=[],fn=[],Zs=[],As=[],Le=new Map,is=[],In=!0;ti=\"http://www.w3.org/2000/svg\";lt=new AudioContext,Bn=44100;rs=class{constructor(t,e,s=gr,i=yr,o){var a;if(!(!zt||N)){if(f(!t||ie(t)||J(t),\"asset must be a file name or zzfx array\"),f(e===void 0||R(e),\"randomness must be a number\"),f(e===void 0||e>=0&&e<=1,\"randomness must be between 0 and 1\"),f(R(s),\"range must be a number\"),f(R(i),\"taper must be a number\"),this.range=s,this.taper=i,this.randomness=e!=null?e:0,this.sampleRate=Bn,this.loadedPercent=0,this.onloadCallback=o,ie(t)){let r=t.slice(),c=e!=null?e:.05,l=1;this.randomness=(a=r[l])!=null?a:c,r[l]=0,this.sampleChannels=[Pi(...r)],this.loadedPercent=1,o==null||o(this)}else if(typeof t==\"string\"){let r=t;this.loadSound(r)}}}play(t,e=1,s=1,i=1,o=!1,a=!1){if(f(!t||L(t),\"pos must be a vec2\"),f(R(e),\"volume must be a number\"),f(R(s),\"pitch must be a number\"),f(R(i),\"randomnessScale must be a number\"),!zt||N||!this.sampleChannels)return;let r;if(t){let u=this.range;if(u){let h=wt.distanceSquared(t);if(h>u*u)return;e*=It(h**.5,u,u*this.taper)}r=bn(t).x*2/F.width-1}let c=s+s*this.randomness*i*ct(-1,1),l=new ei(this,e,c,r,o,a);return Dt&&Ln&&t&&(Ft(t,.5,\"#0ff\",.5,!0),this.range&&(Ft(t,2*this.range,\"#0ff\",.5),Ft(t,2*this.range*this.taper,\"#0ff\",.5)),Sn(\"vol \"+e.toFixed(2)+\" pitch \"+c.toFixed(2),t,.5,\"#0ff\",.5)),l}playMusic(t=1,e=!0,s=!1){return this.play(void 0,t,1,0,e,s)}playNote(t=0,e,s){f(R(t),\"semitoneOffset must be a number\");let i=fc(t,1);return this.play(e,s,i,0)}getDuration(){var t,e;return((e=(t=this.sampleChannels)==null?void 0:t[0])==null?void 0:e.length)/this.sampleRate||0}isLoaded(){return this.loadedPercent===1}async loadSound(t){var c;let e=await fetch(t);if(!e.ok)throw new Error(`Failed to load sound from ${t}: ${e.status} ${e.statusText}`);let s=await e.arrayBuffer(),i=await lt.decodeAudioData(s),o=i.numberOfChannels,a=1e5,r=[];for(let l=0;lsetTimeout(v,0));let d=$(p+a,h);for(;p=0,\"Sound volume must be positive or zero\"),f(s>=0,\"Sound rate must be positive or zero\"),f(R(i),\"Sound pan must be a number\"),this.sound=t,this.volume=e,this.rate=s,this.pan=i,this.loop=o,this.pausedTime=0,this.startTime=void 0,this.gainNode=void 0,this.source=void 0,this.onendedCallback=r=>{r===this.source&&(this.source=void 0)},a||this.start()}start(t=0){f(t>=0,\"Sound start offset must be positive or zero\"),this.isPlaying()&&this.stop(),this.gainNode=lt.createGain(),this.source=Pr(this.sound.sampleChannels,this.volume,this.rate,this.pan,this.loop,this.sound.sampleRate,this.gainNode,t,this.onendedCallback),this.source?(this.startTime=lt.currentTime-t,this.pausedTime=void 0):(this.startTime=void 0,this.pausedTime=0)}setVolume(t){f(t>=0,\"Sound volume must be positive or zero\"),this.volume=t,this.gainNode&&(this.gainNode.gain.value=t)}stop(t=0){if(f(t>=0,\"Sound fade time must be positive or zero\"),this.isPlaying())if(t){let e=lt.currentTime,s=e+t;this.gainNode.gain.cancelScheduledValues(e),this.gainNode.gain.setValueAtTime(this.volume,e),this.gainNode.gain.linearRampToValueAtTime(0,s),this.source.stop(s)}else this.source.stop();this.pausedTime=0,this.source=void 0,this.startTime=void 0}pause(){this.isPaused()||(this.pausedTime=this.getCurrentTime(),this.source.stop(),this.source=void 0,this.startTime=void 0)}resume(){this.isPaused()&&this.start(this.pausedTime)}isPlaying(){return!!this.source}isPaused(){return!this.isPlaying()}getCurrentTime(){if(!this.isPlaying())return this.pausedTime;let t=this.getDuration();return t?$t(lt.currentTime-this.startTime,t):0}getDuration(){return this.rate?this.sound.getDuration()/this.rate:0}getSource(){return this.source}};Xe=[];hn=class{constructor(t,e=0,s=!1,i=new xt){this.tile=t,this.direction=e,this.mirror=s,this.color=i.copy()}clear(){this.tile=this.direction=0,this.mirror=!1,this.color=new xt}},si=class extends Me{constructor(t,e,s=0,i=0,o=x(512),a=!0){var r;f(L(o),\"canvasSize must be a Vector2\"),super(t,e,void 0,s,V,i),this.canvas=N?void 0:new OffscreenCanvas(o.x,o.y),this.context=(r=this.canvas)==null?void 0:r.getContext(\"2d\"),this.textureInfo=new ke(this.canvas,a),this.mass=0}destroy(){this.destroyed||(this.textureInfo.destroyWebGLTexture(),super.destroy())}render(){this.draw(this.pos,this.size,this.color,this.angle,this.mirror,this.additiveColor)}draw(t,e,s=V,i=0,o=!1,a,r=!1,c){let l=new we().setFullImage(this.textureInfo),u=this.hasWebGL();_t(t,e,l,s,i,o,a,u,r,c)}updateWebGL(){this.textureInfo.createWebGLTexture()}hasWebGL(){return M&&this.textureInfo.hasWebGL()}},as=class extends si{constructor(t,e,s=vs(),i=0,o=!0){let a=s?e.multiply(s.size):e;if(super(t,e,0,i,a,o),this.tileInfo=void 0,this.data=[],this.isUsingWebGL=!1,N){this.render=()=>{},this.redraw=()=>{},this.redrawStart=()=>{},this.redrawEnd=()=>{},this.drawTileData=()=>{},this.redrawTileData=()=>{},this.drawLayerTile=()=>{},this.drawLayerRect=()=>{},this.drawTile=()=>{},this.drawRect=()=>{},this.clearLayerRect=()=>{};return}s&&(this.tileInfo=s.frame(0),this.tileInfo.bleed=0);for(let r=this.size.area();r--;)this.data.push(new hn)}setData(t,e,s=!1){if(t=t.floor(),f(L(t),\"layerPos must be a Vector2\"),f(e instanceof hn,\"data must be a TileLayerData\"),!t.arrayCheck(this.size)||(this.data[(t.y|0)*this.size.x+(t.x|0)]=e,!s))return;vt===this.context?this.drawTileData(t):this.redrawTileData(t)}clearData(t,e=!1){this.setData(t,new hn,e)}getData(t){return f(L(t),\"layerPos must be a Vector2\"),t.arrayCheck(this.size)?this.data[(t.y|0)*this.size.x+(t.x|0)]:void 0}update(){!M&&this.isUsingWebGL&&(this.isUsingWebGL=!1,this.redraw())}render(){f(vt!==this.context,\"must call redrawEnd() after drawing tiles!\");let t=this.drawSize||this.size,e=this.pos.add(t.scale(.5));this.draw(e,this.size,this.color,this.angle,this.mirror,this.additiveColor)}onRedraw(){}redraw(){this.redrawStart(!0);for(let t=this.size.x;t--;)for(let e=this.size.y;e--;)this.drawTileData(x(t,e),!1);this.isUsingWebGL&&Tt(),this.onRedraw(),this.redrawEnd()}redrawStart(t=!1){var s,i;if(!this.context)return;f(vt!==this.context),this.savedRenderSettings=[vt,D,wt,W,We],vt=this.context;let e=(i=(s=this.tileInfo)==null?void 0:s.size)!=null?i:x(1);D=this.size.multiply(e),We=re,wt=this.size.multiply(e).scale(.5),W=1,this.isUsingWebGL=this.hasWebGL(),this.isUsingWebGL?qn(this.textureInfo.glTexture,t):(this.context.imageSmoothingEnabled=!dn,t&&(this.canvas.width=D.x,this.canvas.height=D.y))}redrawEnd(){this.context&&(f(vt===this.context),this.isUsingWebGL&&qn(),[vt,D,wt,W,We]=this.savedRenderSettings)}drawTileData(t,e=!0){var r,c;if(!this.context)return;f(vt===this.context,\"must call redrawStart() before drawing tiles\");let s=(c=(r=this.tileInfo)==null?void 0:r.size)!=null?c:x(1),i=t.multiply(s);e&&this.clearLayerRect(i,s);let o=this.getData(t);if(!o||!o.tile)return;let a=this.tileInfo&&this.tileInfo.tile(o.tile);this.drawLayerTile(i,s,a,o.color,o.direction*q/2,o.mirror)}redrawTileData(t,e=!0){this.context&&(f(vt!==this.context,\"redrawStart() should not be active when calling redrawTileData(), instead use drawTileData()\"),this.redrawStart(),this.drawTileData(t,e),this.redrawEnd())}drawLayerTile(t,e=x(1),s,i=V,o=0,a,r){let c=t.add(e.scale(.5));_t(c,e,s,i,o,a,r,this.isUsingWebGL)}drawLayerRect(t,e,s,i=0){this.drawLayerTile(t,e,void 0,s,i)}drawTile(t,e=x(1),s,i=new xt,o=0,a=!1){t=t.subtract(this.pos).multiply(this.tileInfo.size),e=e.multiply(this.tileInfo.size),t.y=this.canvas.height-t.y;let r=D;D=x(this.canvas.width,this.canvas.height);let c=this.hasWebGL();c&&qn(this.textureInfo.glTexture);let l=c?void 0:this.context;_t(t,e,s,i,o,a,void 0,c,!0,l),c&&qn(),D=r}drawRect(t,e,s,i){this.drawTile(t,e,void 0,s,i)}clearLayerRect(t,e){f(vt===this.context,\"must call redrawStart() before clearing tiles\");let s=t.x,i=this.canvas.height-t.y-e.y;this.hasWebGL()?Tc(s,i,e.x,e.y):this.context.clearRect(s,i,e.x,e.y)}},cs=class extends as{constructor(t,e,s=vs(),i=0,o=!0){super(t,e.floor(),s,i,o),this.collisionData=[],this.initCollision(this.size),Xe.push(this),this.isSolid=!0}destroy(){if(this.destroyed)return;let t=Xe.indexOf(this);f(t>=0,\"tile collision layer not found in array\"),t>=0&&Xe.splice(t,1),super.destroy()}initCollision(t){f(L(t),\"size must be a Vector2\"),this.size=t.floor(),this.collisionData=[],this.collisionData.length=t.area(),this.collisionData.fill(0)}setCollisionData(t,e=1){f(L(t),\"layerPos must be a Vector2\");let s=(t.y|0)*this.size.x+(t.x|0);t.arrayCheck(this.size)&&(this.collisionData[s]=e)}clearCollisionData(t){this.setCollisionData(t,0)}getCollisionData(t){f(L(t),\"layerPos must be a Vector2\");let e=(t.y|0)*this.size.x+(t.x|0);return t.arrayCheck(this.size)?this.collisionData[e]:0}collisionTest(t,e=new it,s){f(L(t)&&L(e),\"pos and size must be Vector2s\"),f(!s||typeof s==\"function\"||s instanceof Me,\"callbackObject must be a function or EngineObject\");let i=s?typeof s==\"function\"?(p,d)=>s(p,d):(p,d)=>s.collideWithTile(p,d):()=>!0,o=t.x-this.pos.x,a=t.y-this.pos.y;if(o+e.x/2<0||o-e.x/2>this.size.x||a+e.y/2<0||a-e.y/2>this.size.y)return!1;let r=X(o-e.x/2|0,0),c=X(a-e.y/2|0,0),l=$(X(o+e.x/2,r+1),this.size.x),u=$(X(a+e.y/2,c+1),this.size.y),h=new it;for(let p=c;ps(l,u):(l,u)=>s.collideWithTile(l,u):l=>l>0,a=l=>{let u=this.getCollisionData(r.set(l.x-this.pos.x,l.y-this.pos.y));return u&&o(u,l)},r=new it,c=Ua(t,e,a,i);if(se&&c){let l=c.floor().add(x(.5));gt(l,x(1),\"#f008\"),ye(t,e,\"#00f\",.02),ye(t,c,\"#f00\",.02),Qo(c,\"#0f0\"),i&&ye(c,c.add(i),\"#ff0\",.02)}return c}},yo=class extends Me{constructor(t,e,s=0,i=0,o=100,a=q,r,c=V,l=V,u=$e,h=$e,p=.5,d=.1,y=1,b=.1,v=.05,w=1,_=1,S=0,E=q,A=.1,T=.2,P=!1,I=!1,ut=!0,ot=I?1e9:0,k=!1){super(t,x(),r,e,void 0,ot),this.emitCircle=typeof s==\"number\",this.emitSize=typeof s==\"number\"?x(s):s.copy(),this.emitTime=i,this.emitRate=o,this.emitConeAngle=a,this.colorStartA=c.copy(),this.colorStartB=l.copy(),this.colorEndA=u.copy(),this.colorEndB=h.copy(),this.randomColorLinear=ut,this.particleTime=p,this.sizeStart=d,this.sizeEnd=y,this.speed=b,this.angleSpeed=v,this.damping=w,this.angleDamping=_,this.gravityScale=S,this.particleConeAngle=E,this.fadeRate=A,this.randomness=T,this.collideTiles=P,this.additive=I,this.localSpace=k,this.trailScale=0,this.particleCreateCallback=void 0,this.particleDestroyCallback=void 0,this.particleCollideCallback=void 0,this.velocityInheritance=0,this.emitTimeBuffer=0,this.particles=[],this.previousAngle=this.angle,this.previousPos=this.pos.copy()}update(){if(f(this.angleDamping>=0&&this.angleDamping<=1),f(this.damping>=0&&this.damping<=1),this.velocityInheritance){let s=this.velocityInheritance;this.velocity.x=s*(this.pos.x-this.previousPos.x),this.velocity.y=s*(this.pos.y-this.previousPos.y),this.angleVelocity=s*(this.angle-this.previousAngle),this.previousAngle=this.angle,this.previousPos.x=this.pos.x,this.previousPos.y=this.pos.y}if(this.isActive()){if(this.emitRate&&qs){let s=1/this.emitRate/qs;for(this.emitTimeBuffer+=$o;this.emitTimeBuffer>0;this.emitTimeBuffer-=s)this.emitParticle()}}else this.particles.length===0&&this.destroy(!0);let t=this.particles,e=0;for(let s=0;s_+_*ct(s,-s),o=i(this.particleTime),a=i(this.sizeStart),r=i(this.sizeEnd),c=i(this.speed),l=i(this.angleSpeed)*qa(),u=ct(this.emitConeAngle,-this.emitConeAngle),h=lo(this.colorStartA,this.colorStartB,this.randomColorLinear),p=lo(this.colorEndA,this.colorEndB,this.randomColorLinear),d=this.localSpace?u:this.angle+u,y=x(c*tt(d),c*rt(d)),b=l;!this.localSpace&&this.velocityInheritance>0&&(y.x+=this.velocity.x,y.y+=this.velocity.y,b+=this.angleVelocity);let v=new ii(this,t,e,h,p,o,a,r,y,b);return this.particles.push(v),(w=this.particleCreateCallback)==null||w.call(this,v),v}updatePhysics(){}render(){for(let t of this.particles)t.render()}isActive(){return!this.emitTime||this.getAliveTime()0&&(this.destroyed=!1,this.emitTime=-1))}},Xh=new it,ii=class{constructor(t,e,s,i,o,a,r,c,l=x(),u=0){this.emitter=t,this.pos=e,this.angle=s,this.size=x(r),this.color=i.copy(),this.colorStart=i,this.colorEnd=o,this.lifeTime=a,this.sizeStart=r,this.sizeEnd=c,this.velocity=l,this.angleVelocity=u,this.spawnTime=Wt,this.mirror=Wa(),this.groundObject=void 0,this.destroyed=!1,this.tileInfo=t.tileInfo}update(){let t=this.emitter,e=t.damping,s=t.angleDamping,i=t.restitution,o=t.friction,a=t.gravityScale,r=t.collideTiles,c=t.particleCollideCallback;if(this.lifeTime>0&&Wt-this.spawnTime>this.lifeTime){this.destroy();return}let l=this.pos.copy();if(this.velocity.x*=e,this.velocity.y*=e,this.pos.x+=this.velocity.x+=jt.x*a,this.pos.y+=this.velocity.y+=jt.y*a,this.angle+=this.angleVelocity*=s,!Ei||!r)return;let u=this.velocity.lengthSquared();if(u>Re*Re){let p=Re/u**.5;this.velocity.x*=p,this.velocity.y*=p}this.groundObject=void 0;let h=c?p=>{let d=ni(p);return d&&c(this,d,p)}:p=>ni(p)>0;if(h(this.pos)){let p=ze(this.pos);if(!h(l)){let d=h(x(this.pos.x,l.y)),y=h(x(l.x,this.pos.y)),b=X(i,p.restitution),v=X(o,p.friction);d&&(this.pos.x=l.x,this.velocity.x*=-b,this.velocity.y*=v),(y||!d)&&((this.velocity.y<0&&jt.y<0||this.velocity.y>0&&jt.y>0)&&(this.groundObject=p),this.pos.y=l.y,this.velocity.y*=-b,this.velocity.x*=v),ne&>(this.pos,this.size,\"#f00\")}}}destroy(){let t=this.emitter.particleDestroyCallback,e=this.colorEnd;this.color.set(e.r,e.g,e.b,e.a),this.size.set(this.sizeEnd,this.sizeEnd),this.destroyed=!0,t==null||t(this)}render(){let t=this.emitter,e=t.localSpace,s=t.additive,i=t.trailScale,o=t.fadeRate/2,a=this.lifeTime>0?$((Wt-this.spawnTime)/this.lifeTime,1):1,r=1-a,c=r*this.sizeStart+a*this.sizeEnd,l=x(c),u=a1-o?(1-a)/o:1;this.color.r=r*this.colorStart.r+a*this.colorEnd.r,this.color.g=r*this.colorStart.g+a*this.colorEnd.g,this.color.b=r*this.colorStart.b+a*this.colorEnd.b,this.color.a=(r*this.colorStart.a+a*this.colorEnd.a)*u;let h=Xh.set(this.pos.x,this.pos.y),p=this.angle;if(e){let d=t.angle,y=rt(-d),b=tt(-d);h.set(t.pos.x+h.x*y-h.y*b,t.pos.y+h.x*b+h.y*y),p+=d}if(s&&es(),i){let d=e?this.velocity.rotate(t.angle):this.velocity,y=d.length();if(y){let b=y*i;l.y=X(l.x,b),p=Zo(d.x,d.y),_t(h,l,this.tileInfo,this.color,p,this.mirror)}}else _t(h,l,this.tileInfo,this.color,p,this.mirror);s&&es(!1),cn&>(h,l,\"#f005\",0,p)}},Lr=!0,vo=!0,ci=5e5,Dr=11,gc=Dr*4,Yh=ci/gc|0,Li=3,yc=Li*4,Pn=ci/yc|0;en=!1,li=5,En=.5,ui=x(640,80),Nr=!1,ds={},Ns=[];di=class{constructor(t,e,s=\"\",i=\"\\u{1F3C6}\",o){f(t>=0&&!ds[t]),this.id=t,this.name=e,this.description=s,this.icon=i,this.unlocked=!1,this.image=void 0,o&&((this.image=new Image).src=o),ds[t]=this}unlock(){Nr||this.unlocked||(f(hi,\"save name must be set\"),this.unlocked=!0,Br(),Ns.push(this))}render(t=0){let e=at,s=$(ui.x,F.width),i=ui.y,o=F.width-s,a=-i*t,r=_e(0,0,.9);e.save(),e.beginPath(),e.fillStyle=r.toString(),e.strokeStyle=nt.toString(),e.lineWidth=3,e.rect(o,a,s,i),e.fill(),e.stroke(),e.clip();let c=x(.1,.05).scale(i),l=i-2*c.x;this.renderIcon(x(o+c.x+l/2,a+i/2),l);let u=i*.5,h=i*.3,p=x(o+l+2*c.x,a+c.y*2+u/2),d=s-l-3*c.x;He(this.name,p,u,nt,0,void 0,\"left\",void 0,void 0,d),p.y=a+i-c.y*2-h/2,He(this.description,p,h,nt,0,void 0,\"left\",void 0,void 0,d),e.restore()}renderIcon(t,e){this.image?at.drawImage(this.image,t.x-e/2,t.y-e/2,e,e):He(this.icon,t,e*.7,nt)}};To=class extends di{constructor(t,e,s,i,o){super(t,e,s,i,o)}unlock(){super.unlock(),fs&&fs.unlockMedal(this.id)}},Ao=class{constructor(t,e,s){var c,l,u;f(!fs,\"there can only be one newgrounds object\"),f(!e||s,\"must provide cryptojs if there is a cipher\"),fs=this,this.app_id=t,this.cipher=e,this.cryptoJS=s,this.host=location?location.hostname:\"\";let i=new URL(location.href);if(this.session_id=i.searchParams.get(\"ngio_session_id\"),!this.session_id)return;let o=this.call(\"Medal.getList\");if(!o||!o.result||o.result.error){en&&yt(\"Newgrounds session unavailable; skipping plugin init\"),this.medals=[],this.scoreboards=[];return}this.medals=((c=o.result.data)==null?void 0:c.medals)||[],en&&yt(this.medals);for(let h of this.medals){let p=ds[h.id];p&&(p.image=new Image,p.image.src=h.icon,p.name=h.name,p.description=h.description,p.unlocked=h.unlocked,p.difficulty=h.difficulty,p.value=h.value,p.value&&(p.description=p.description+` (${p.value})`))}let a=this.call(\"ScoreBoard.getBoards\");this.scoreboards=((u=(l=a==null?void 0:a.result)==null?void 0:l.data)==null?void 0:u.scoreboards)||[],en&&yt(this.scoreboards);let r=60*1e3;setInterval(()=>this.call(\"Gateway.ping\",0,!0),r)}unlockMedal(t){return this.call(\"Medal.unlock\",{id:t},!0)}postScore(t,e){return this.call(\"ScoreBoard.postScore\",{id:t,value:e},!0)}getScores(t,e,s=0,i=0,o=10){return this.call(\"ScoreBoard.getScores\",{id:t,user:e,social:s,skip:i,limit:o})}logView(){return this.call(\"App.logView\",{host:this.host},!0)}call(t,e,s=!1){let i={component:t,parameters:e};if(this.cipher){let l=this.cryptoJS,u=l.enc.Base64.parse(this.cipher),h=l.lib.WordArray.random(16),p=l.AES.encrypt(JSON.stringify(i),u,{iv:h});i.secure=l.enc.Base64.stringify(h.concat(p.ciphertext)),i.parameters=0}let o={app_id:this.app_id,session_id:this.session_id,call:i},a=new FormData;a.append(\"input\",JSON.stringify(o));let r=new XMLHttpRequest;r.open(\"POST\",\"https://newgrounds.io/gateway_v3.php\",!en&&s);try{r.send(a)}catch(l){en&&yt(\"newgrounds call failed\",l);return}return en&&yt(r.responseText),r.responseText&&JSON.parse(r.responseText)}},Co=class{constructor(t,e=!1,s=!1){f(!Pt,\"Post process already initialized\"),f(!(e&&s),\"Post process cannot both include main canvas and use feedback texture\"),Pt=this,t||(t=\"void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}\"),this.shader=void 0,this.texture=void 0,this.vao=void 0,i(),gn(void 0,r,o,a);function i(){if(N)return;if(!M){console.warn(\"PostProcessPlugin: WebGL not enabled!\");return}Pt.texture=Oi(),Pt.shader=Gn(`#version 300 es\nprecision highp float;in vec2 p;void main(){gl_Position=vec4(p+p-1.,1,1);}`,`#version 300 es\nprecision highp float;uniform sampler2D iChannel0;uniform vec3 iResolution;uniform float iTime;out vec4 c;\n`+t+`\nvoid main(){mainImage(c,gl_FragCoord.xy);c.a=1.;}`),Pt.vao=g.createVertexArray(),g.bindVertexArray(Pt.vao),g.bindBuffer(g.ARRAY_BUFFER,Rn);let c=8,l=g.getAttribLocation(Pt.shader,\"p\");g.enableVertexAttribArray(l),g.vertexAttribPointer(l,2,g.FLOAT,!1,c,0)}function o(){Pt.shader=void 0,Pt.texture=void 0,yt(\"PostProcessPlugin: WebGL context lost\")}function a(){i(),yt(\"PostProcessPlugin: WebGL context restored\")}function r(){if(N||!M)return;Tt(),g.bindFramebuffer(g.FRAMEBUFFER,null),g.useProgram(Pt.shader),g.bindVertexArray(Pt.vao),g.pixelStorei(g.UNPACK_FLIP_Y_WEBGL,!0),g.disable(g.BLEND),g.activeTexture(g.TEXTURE0),g.bindTexture(g.TEXTURE_2D,Pt.texture),e?(Oe.width=D.x,Oe.height=D.y,kr(Je),Je.drawImage(F,0,0),F.width|=0,g.texImage2D(g.TEXTURE_2D,0,g.RGBA,g.RGBA,g.UNSIGNED_BYTE,Oe)):s||g.texImage2D(g.TEXTURE_2D,0,g.RGBA,g.RGBA,g.UNSIGNED_BYTE,Q);let c=l=>g.getUniformLocation(Pt.shader,l);g.uniform1i(c(\"iChannel0\"),0),g.uniform1f(c(\"iTime\"),Wt),g.uniform3f(c(\"iResolution\"),F.width,F.height,1),g.drawArrays(g.TRIANGLE_STRIP,0,4),s&&g.texImage2D(g.TEXTURE_2D,0,g.RGBA,g.RGBA,g.UNSIGNED_BYTE,Q),g.pixelStorei(g.UNPACK_FLIP_Y_WEBGL,!1),us(!0)}}},Eo=class{constructor(t,e){f(!U,\"LightSystemPlugin already initialized\"),f(!Pt,\"LightSystemPlugin must be created before PostProcessPlugin\"),U=this,this.enabled=!0,this.ambientColor=(e||nt).copy(),this.textureSize=t?t.copy():void 0,this.texture=void 0,this.lightShader=void 0,this.compositeShader=void 0,this.lightVAO=void 0,this.compositeVAO=void 0,s(),gn(void 0,i,o,a);function s(){if(N)return;if(!M){console.warn(\"LightSystemPlugin: WebGL not enabled!\");return}U.textureSize||(U.textureSize=D.copy()),U.texture=g.createTexture(),g.bindTexture(g.TEXTURE_2D,U.texture),g.texImage2D(g.TEXTURE_2D,0,g.RGBA,U.textureSize.x,U.textureSize.y,0,g.RGBA,g.UNSIGNED_BYTE,null),g.texParameteri(g.TEXTURE_2D,g.TEXTURE_MAG_FILTER,g.LINEAR),g.texParameteri(g.TEXTURE_2D,g.TEXTURE_MIN_FILTER,g.LINEAR),g.texParameteri(g.TEXTURE_2D,g.TEXTURE_WRAP_S,g.CLAMP_TO_EDGE),g.texParameteri(g.TEXTURE_2D,g.TEXTURE_WRAP_T,g.CLAMP_TO_EDGE),U.lightShader=Gn(`#version 300 es\nprecision highp float;uniform mat4 m;uniform vec2 lightPos;uniform float radius;in vec2 g;out vec2 vWorldPos;void main(){vec2 worldP=lightPos+(g-.5)*2.*radius;gl_Position=m*vec4(worldP,1,1);vWorldPos=worldP;}`,`#version 300 es\nprecision highp float;uniform vec2 lightPos;uniform float radius;uniform float fadeRange;uniform vec4 color;in vec2 vWorldPos;out vec4 c;void main(){float dist=distance(vWorldPos,lightPos);float t=clamp((radius-dist)/max(fadeRange,1e-6),0.,1.);c=vec4(color.rgb*t*color.a,1.);}`),U.compositeShader=Gn(`#version 300 es\nprecision highp float;in vec2 p;void main(){gl_Position=vec4(p+p-1.,1,1);}`,`#version 300 es\nprecision highp float;uniform sampler2D s;uniform vec3 iResolution;out vec4 c;void main(){vec2 uv=gl_FragCoord.xy/iResolution.xy;c=vec4(texture(s,uv).rgb,1.);}`),U.lightVAO=g.createVertexArray(),g.bindVertexArray(U.lightVAO),g.bindBuffer(g.ARRAY_BUFFER,Rn);let r=g.getAttribLocation(U.lightShader,\"g\");g.enableVertexAttribArray(r),g.vertexAttribPointer(r,2,g.FLOAT,!1,8,0),U.compositeVAO=g.createVertexArray(),g.bindVertexArray(U.compositeVAO),g.bindBuffer(g.ARRAY_BUFFER,Rn);let c=g.getAttribLocation(U.compositeShader,\"p\");g.enableVertexAttribArray(c),g.vertexAttribPointer(c,2,g.FLOAT,!1,8,0)}function i(){if(N||!M||!U.enabled||!U.texture)return;Tt();let r=vn,c=U.ambientColor;g.bindFramebuffer(g.FRAMEBUFFER,Or),g.framebufferTexture2D(g.FRAMEBUFFER,g.COLOR_ATTACHMENT0,g.TEXTURE_2D,U.texture,0),g.viewport(0,0,U.textureSize.x,U.textureSize.y),g.clearColor(c.r,c.g,c.b,c.a),g.clear(g.COLOR_BUFFER_BIT),es(),g.enable(g.BLEND),g.blendFunc(g.ONE,g.ONE);for(let u of mt)u.destroyed||u.renderLight();Tt(),g.bindFramebuffer(g.FRAMEBUFFER,null),g.viewport(0,0,D.x,D.y),g.useProgram(U.compositeShader),g.bindVertexArray(U.compositeVAO),g.activeTexture(g.TEXTURE0),g.bindTexture(g.TEXTURE_2D,U.texture);let l=U.compositeShader;g.uniform1i(g.getUniformLocation(l,\"s\"),0),g.uniform3f(g.getUniformLocation(l,\"iResolution\"),F.width,F.height,1),g.blendFunc(g.DST_COLOR,g.ZERO),g.drawArrays(g.TRIANGLE_STRIP,0,4),qt&&g.bindTexture(g.TEXTURE_2D,qt),es(r),us(!0)}function o(){U.texture=void 0,U.lightShader=void 0,U.compositeShader=void 0,U.lightVAO=void 0,U.compositeVAO=void 0,yt(\"LightSystemPlugin: WebGL context lost\")}function a(){s(),yt(\"LightSystemPlugin: WebGL context restored\")}}drawLight(t){if(N||!M||!this.lightShader)return;Tt(),g.useProgram(this.lightShader),g.bindVertexArray(this.lightVAO);let e=x(2*W).divide(D),s=wt.rotate(-Y),i=x(-1).subtract(s.multiply(e)),o=rt(Y),a=tt(Y),r=[e.x*o,e.y*a,0,0,-e.x*a,e.y*o,0,0,1,1,1,0,i.x,i.y,0,1],c=this.lightShader;g.uniformMatrix4fv(g.getUniformLocation(c,\"m\"),!1,r),g.uniform2f(g.getUniformLocation(c,\"lightPos\"),t.pos.x,t.pos.y),g.uniform1f(g.getUniformLocation(c,\"radius\"),t.radius),g.uniform1f(g.getUniformLocation(c,\"fadeRange\"),t.fadeRange);let l=t.color;g.uniform4f(g.getUniformLocation(c,\"color\"),l.r,l.g,l.b,l.a),g.drawArrays(g.TRIANGLE_STRIP,0,4),us(!0)}},_o=class extends Me{constructor(t,e,s,i){super(t,x(1),void 0,0,s),f(R(e)&&e>=0,\"Light radius must be a non-negative number\"),f(i===void 0||R(i)&&i>=0,\"Light fadeRange must be a non-negative number when provided\"),this.radius=e,this.fadeRange=i===void 0?e:i}render(){}renderLight(){U&&U.drawLight(this)}},Ro=class extends rs{constructor(t){super(void 0),!(!zt||N)&&(this.randomness=0,this.sampleChannels=Ac(...t),this.sampleRate=Bn)}playMusic(t=1,e=!0){return super.play(void 0,t,1,0,e)}};zr=0;Po=class{constructor(t=at){f(!C,\"UI system already initialized\"),C=this,this.activateOnPress=!1,this.defaultColor=V,this.defaultLineColor=nt,this.defaultTextColor=nt,this.defaultButtonColor=_e(0,0,.7),this.defaultHoverColor=_e(0,0,.9),this.defaultDisabledColor=_e(0,0,.3),this.defaultGradientColor=void 0,this.defaultLineWidth=4,this.defaultCornerRadius=0,this.defaultTextFitScale=.8,this.defaultFont=ys,this.defaultSoundPress=void 0,this.defaultSoundRelease=void 0,this.defaultSoundClick=void 0,this.defaultShadowColor=re,this.defaultShadowBlur=5,this.defaultShadowOffset=x(5),this.nativeHeight=0,this.navigationObject=void 0,this.navigationTimer=new xe(void 0,!0),this.navigationDelay=.2,this.navigationDirection=1,this.navigationMode=!1,this.uiObjects=[],this.uiContext=t,this.activeObject=void 0,this.hoverObject=void 0,this.lastHoverObject=void 0,this.confirmDialog=void 0,this._keyInputObject=void 0,this._onKeyDown=o=>{var a;return(a=this._keyInputObject)==null?void 0:a.onKeyDown(o)},gn(s,i);function e(o){let a,r;o.parent?(a=o.parent.nativePos,r=o.parent.size):(a=C.screenToNative(D.scale(.5)),r=C.nativeHeight?x(D.x*C.nativeHeight/D.y,C.nativeHeight):D);let c=o.anchor;o.nativePos=a.add(r.multiply(c).scale(.5)).subtract(o.size.multiply(c).scale(.5)).add(o.localPos)}function s(){C.activeObject&&!C.activeObject.visible&&(C.activeObject=void 0),C.lastHoverObject=C.hoverObject,C.hoverObject=void 0,ns(0)&&(C.navigationMode=!1,C.navigationObject=void 0),C.keyInputObject&&(C.activeObject=C.keyInputObject,C.hoverObject=C.keyInputObject,C.navigationMode=!1,C.navigationObject=void 0);let o=C.getNavigableObjects();if(!o.length)C.navigationObject=void 0;else if(!C.keyInputObject){if(o.includes(C.navigationObject)||(C.navigationObject=void 0),Kt||C.navigationMode&&!C.navigationObject&&(C.navigationObject=o.find(r=>r.navigationAutoSelect)),!C.navigationTimer.active()){let r=Yt(C.getNavigationDirection());if(r){let c;if(C.navigationObject){let l=o.indexOf(C.navigationObject),u=$t(l+r,o.length);c=o[u]}else if(c=o.find(l=>l.navigationAutoSelect),!c){let l=r>0?0:o.length-1;c=o[l]}C.navigationObject!==c&&(C.navigationMode=!0,C.hoverObject=void 0,C.navigationObject=c,C.navigationTimer.set(C.navigationDelay),c.soundPress&&c.soundPress.play())}}C.navigationObject&&C.getNavigationWasPressed()&&C.navigationObject.navigatePressed()}for(let r=C.uiObjects.length;r--;){let c=C.uiObjects[r];c.parent||a(c)}C.uiObjects=C.uiObjects.filter(r=>!r.destroyed);function a(r){if(!(r.destroyed||!r.visible)){e(r);for(let c=r.children.length;c--;){let l=r.children[c];l&&a(l)}r.destroyed||r.update()}}}function i(){let o=C.uiContext;if(o.save(),C.nativeHeight){let r=D.y/C.nativeHeight;o.translate(-r*D.x/2,0),o.scale(r,r),o.translate(D.x/2/r,0)}function a(r){if(r.visible){e(r),r.render();for(let c of r.children)a(c)}}if(C.uiObjects.forEach(r=>r.parent||a(r)),zr>0){let r=function(c,l=!0){l&&(l=!!c.visible),e(c),c.renderDebug(l);for(let u of c.children)r(u,l)};C.uiObjects.forEach(c=>c.parent||r(c))}o.restore()}}drawRect(t,e,s=V,i=0,o=nt,a=0,r,c=nt,l=0,u=x()){f(L(t),\"pos must be a vec2\"),f(L(e),\"size must be a vec2\"),f(z(s),\"color must be a color\"),f(R(i),\"lineWidth must be a number\"),f(z(o),\"lineColor must be a color\"),f(R(a),\"cornerRadius must be a number\");let h=C.uiContext;if(r){let p=h.createLinearGradient(t.x,t.y-e.y/2,t.x,t.y+e.y/2),d=s.toString();p.addColorStop(0,d),p.addColorStop(.5,r.toString()),p.addColorStop(1,d),h.fillStyle=p}else h.fillStyle=s.toString();(l||u.x||u.y)&&c.a>0&&(h.shadowColor=c.toString(),h.shadowBlur=l,h.shadowOffsetX=u.x,h.shadowOffsetY=u.y),h.beginPath(),a&&h.roundRect?h.roundRect(t.x-e.x/2,t.y-e.y/2,e.x,e.y,a):h.rect(t.x-e.x/2,t.y-e.y/2,e.x,e.y),h.fill(),h.shadowColor=\"#0000\",i&&o.a>0&&(h.strokeStyle=o.toString(),h.lineWidth=i,h.stroke())}drawLine(t,e,s=C.defaultLineWidth,i=C.defaultLineColor){f(L(t),\"posA must be a vec2\"),f(L(e),\"posB must be a vec2\"),f(R(s),\"lineWidth must be a number\"),f(z(i),\"lineColor must be a color\");let o=C.uiContext;o.strokeStyle=i.toString(),o.lineWidth=s,o.beginPath(),o.lineTo(t.x,t.y),o.lineTo(e.x,e.y),o.stroke()}drawTile(t,e,s,i=C.defaultColor,o=0,a=!1,r=nt,c=0,l=x()){let u=C.uiContext;(c||l.x||l.y)&&r.a>0&&(u.shadowColor=r.toString(),u.shadowBlur=c,u.shadowOffsetX=l.x,u.shadowOffsetY=l.y),_t(t,e,s,i,o,a,re,!1,!0,u),u.shadowColor=\"#0000\"}drawText(t,e,s,i=C.defaultColor,o=C.defaultLineWidth,a=C.defaultLineColor,r=\"center\",c=C.defaultFont,l=\"\",u=!0,h=void 0,p=nt,d=0,y=x()){let b=C.uiContext;p.a>0&&(h&&He(t,e.add(h),s.y,p,o,a,r,c,l,u?s.x:void 0,0,b),(d||y.x||y.y)&&(b.shadowColor=p.toString(),b.shadowBlur=d,b.shadowOffsetX=y.x,b.shadowOffsetY=y.y)),He(t,e,s.y,i,o,a,r,c,l,u?s.x:void 0,0,b),b.shadowColor=\"#0000\"}setupDragAndDrop(t,e,s,i){if(this._dragListeners)for(let[a,r]of this._dragListeners)document.removeEventListener(a,r);this._dragListeners=[];let o=(a,r)=>{let c=l=>{l.preventDefault(),a&&a(l)};document.addEventListener(r,c),this._dragListeners.push([r,c])};o(t,\"drop\"),o(e,\"dragenter\"),o(s,\"dragleave\"),o(i,\"dragover\")}screenToNative(t){if(!C.nativeHeight)return t;let e=D.y/C.nativeHeight,s=1/e,i=t.copy();return i.x+=e*D.x/2,i.x*=s,i.y*=s,i.x-=s*D.x/2,i}get keyInputObject(){return this._keyInputObject}set keyInputObject(t){let e=!!this._keyInputObject;this._keyInputObject=t,!e&&t?document.addEventListener(\"keydown\",this._onKeyDown):e&&!t&&document.removeEventListener(\"keydown\",this._onKeyDown)}destroyObjects(){for(let t of this.uiObjects)t.parent||t.destroy();this.uiObjects=this.uiObjects.filter(t=>!t.destroyed),this.activeObject=void 0,this.hoverObject=void 0,this.lastHoverObject=void 0,this.keyInputObject=void 0}getNavigableObjects(){function t(s){if(!(!s.visible||s.disabled)){s.isInteractive()&&s.navigationIndex!==void 0&&e.push(s);for(let i=s.children.length;i--;)t(s.children[i])}}let e=[];for(let s=C.uiObjects.length;s--;){let i=C.uiObjects[s];C.confirmDialog&&i!==C.confirmDialog||i.parent||t(i)}return e.sort((s,i)=>s.navigationIndex-i.navigationIndex),e}getNavigationDirection(){let t=C.navigationDirection===1,e=C.navigationDirection===2;if(_n){let l=ss(0,pt),u=po(pt);return e?-(l.y||u.y)||l.x||u.x:t?-(l.y||u.y):l.x||u.x}let s=\"ArrowUp\",i=\"ArrowDown\",o=\"ArrowLeft\",a=\"ArrowRight\";if(e)return Et(s)||Et(o)?-1:Et(i)||Et(a)?1:0;let r=t?s:o,c=t?i:a;return Et(r)?-1:Et(c)?1:0}getNavigationOtherDirection(){if(C.navigationDirection===2)return 0;let t=C.navigationDirection===1;if(_n){let i=ss(0,pt),o=po(pt);return t?i.x||o.x:i.y||o.y}let e=t?\"ArrowLeft\":\"ArrowUp\",s=t?\"ArrowRight\":\"ArrowDown\";return Et(e)?-1:Et(s)?1:0}getNavigationWasPressed(){return _n?Ar(0,pt):Lt(\"Space\")||Lt(\"Enter\")}showConfirmDialog(t=\"Are you sure?\",e,s,i=x(500,250),o=\"Escape\"){f(!C.confirmDialog);let a=C.navigationDirection;C.navigationDirection=2;let r=new Zt(x(),i);C.confirmDialog=r,r.onRender=()=>{let d=_e(0,0,0,.7);C.drawRect(x(),x(1e9),d)},r.onUpdate=()=>{Lt(o)&&p()},r.isMouseOverlapping=()=>!0;let c=50,l=new fi(x(0,-50),x(i.x-c,70),t);r.addChild(l);let u=new ps(x(-80,50),x(120,70),\"Yes\");u.textHeight=40,u.navigationIndex=1,u.hoverColor=_e(0,1,.5),u.onClick=()=>{p(),e&&e()},r.addChild(u);let h=new ps(x(80,50),x(120,70),\"No\");h.textHeight=40,h.navigationIndex=2,h.navigationAutoSelect=!0,h.onClick=()=>{p(),s&&s()},r.addChild(h);function p(){f(C.confirmDialog===r),r.destroy(),C.confirmDialog=void 0,C.navigationDirection=a,Ts()}}},Zt=class{constructor(t=x(),e=x()){var s,i;f(L(t),\"ui object pos must be a vec2\"),f(L(e),\"ui object size must be a vec2\"),this.localPos=t.copy(),this.nativePos=t.copy(),this.size=e.copy(),this.color=C.defaultColor.copy(),this.activeColor=void 0,this.text=void 0,this.disabledColor=C.defaultDisabledColor.copy(),this.disabled=!1,this.textColor=C.defaultTextColor.copy(),this.hoverColor=C.defaultHoverColor.copy(),this.lineColor=C.defaultLineColor.copy(),this.gradientColor=C.defaultGradientColor?C.defaultGradientColor.copy():void 0,this.lineWidth=C.defaultLineWidth,this.cornerRadius=C.defaultCornerRadius,this.font=C.defaultFont,this.fontStyle=void 0,this.textWidth=void 0,this.textHeight=void 0,this.textFitScale=C.defaultTextFitScale,this.textShadow=void 0,this.textLineColor=C.defaultLineColor.copy(),this.textLineWidth=0,this.visible=!0,this.children=[],this.parent=void 0,this.extraTouchSize=0,this.soundPress=C.defaultSoundPress,this.soundRelease=C.defaultSoundRelease,this.soundClick=C.defaultSoundClick,this.interactive=!1,this.dragActivate=!1,this.canBeHover=!0,this.shadowColor=(s=C.defaultShadowColor)==null?void 0:s.copy(),this.shadowBlur=C.defaultShadowBlur,this.shadowOffset=(i=C.defaultShadowOffset)==null?void 0:i.copy(),this.navigationIndex=void 0,this.navigationAutoSelect=!1,this.anchor=x(),C.uiObjects.push(this)}addChild(t){return f(!t.parent&&!this.children.includes(t)),this.children.push(t),t.parent=this,t}removeChild(t){f(t.parent===this&&this.children.includes(t)),this.children.splice(this.children.indexOf(t),1),t.parent=void 0}destroy(){var t;if(!this.destroyed){C.activeObject===this&&(C.activeObject=void 0),C.hoverObject===this&&(C.hoverObject=void 0),C.lastHoverObject===this&&(C.lastHoverObject=void 0),C.navigationObject===this&&(C.navigationObject=void 0),C.keyInputObject===this&&(C.keyInputObject=void 0),this.destroyed=1,(t=this.parent)==null||t.removeChild(this);for(let e of this.children)e.parent=void 0,e.destroy();this.children.length=0}}isMouseOverlapping(){if(!Qs)return!1;let t=Kt?this.size.add(x(this.extraTouchSize||0)):this.size,e=C.screenToNative(Gt);return Ws(this.nativePos,t,e)}update(){if(this.onUpdate(),this.disabled&&(this===C.activeObject&&(C.activeObject=void 0),this===C.keyInputObject&&(C.keyInputObject=void 0)),C.keyInputObject)return;let t=C.lastHoverObject===this,e=this.isActiveObject(),s=$n(0),i=this.dragActivate?s:ns(0);this.canBeHover&&(C.navigationMode||(i||e||!s&&!Kt)&&!C.hoverObject&&this.isMouseOverlapping()&&(C.hoverObject=this)),this.isHoverObject()&&(this.disabled||(i&&this.interactive&&((!this.dragActivate||!t||ns(0))&&this.onPress(),this.soundPress&&this.soundPress.play(),C.activeObject&&!e&&C.activeObject.onRelease(),C.activeObject=this,C.activateOnPress&&this.click(!this.soundPress)),C.activateOnPress||!s&&this.isActiveObject()&&this.interactive&&this.click()),i&&wr(0,0,0,1,0)),e&&(!s||this.dragActivate&&!this.isHoverObject())&&(this.onRelease(),this.soundRelease&&this.soundRelease.play(),C.activeObject=void 0),this.isHoverObject()!==t&&(this.isHoverObject()?this.onEnter():this.onLeave())}render(){if(this.onRender(),!this.size.x||!this.size.y)return;let t=this.isNavigationObject(),e=t?this.color:this.interactive&&this.isActiveObject()&&!this.disabled?this.color:this.lineColor,s=t?this.hoverColor:this.disabled?this.disabledColor:this.interactive?this.isActiveObject()?this.activeColor||this.hoverColor:this.isHoverObject()?this.hoverColor:this.color:this.color,i=this.lineWidth*(t?1.5:1);C.drawRect(this.nativePos,this.size,s,i,e,this.cornerRadius,this.gradientColor,this.shadowColor,this.shadowBlur,this.shadowOffset)}getTextSize(){return x(this.textWidth||this.textFitScale*this.size.x,this.textHeight||this.textFitScale*this.size.y)}navigatePressed(){this.click()}isHoverObject(){return C.hoverObject===this}isActiveObject(){return C.activeObject===this}isNavigationObject(){return C.navigationObject===this}isKeyInputObject(){return C.keyInputObject===this}isInteractive(){return this.interactive&&this.visible&&!this.disabled}toString(){let t=\"type = \"+this.constructor.name;return this.text&&(t+=`\ntext = `+this.text),(this.nativePos.x||this.nativePos.y)&&(t+=`\nnativePos = `+this.nativePos),(this.localPos.x||this.localPos.y)&&(t+=`\nlocalPos = `+this.localPos),(this.size.x||this.size.y)&&(t+=`\nsize = `+this.size),this.color&&(t+=`\ncolor = `+this.color),t}renderDebug(t=!0){let e=t?this.isHoverObject()?Ya:this.disabled?Qa:this.interactive?nr:Ka:$a;C.drawRect(this.nativePos,this.size,re,4,e)}click(t=!0){this.onClick(),t&&this.soundClick&&this.soundClick.play()}onUpdate(){}onRender(){}onEnter(){}onLeave(){}onPress(){}onRelease(){}onClick(){}onChange(){}},fi=class extends Zt{constructor(t,e,s=\"\",i=\"center\",o=C.defaultFont){super(t,e),f(J(s),\"ui text must be a string\"),f([\"left\",\"center\",\"right\"].includes(i),\"ui text align must be left, center, or right\"),f(J(o),\"ui text font must be a string\"),this.text=s,this.align=i,this.font=o,this.canBeHover=!1,this.color=re,this.shadowColor=re,this.gradientColor=void 0,this.lineWidth=0,this.textFitScale=1}render(){super.render();let t=this.getTextSize();C.drawText(this.text,this.nativePos,t,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow,this.shadowColor,this.shadowBlur,this.shadowOffset)}},Lo=class extends Zt{constructor(t,e,s=\"\"){super(t,e),f(J(s),\"ui text must be a string\"),this.maxLength=0,this.text=s,this.interactive=!0,this.canBeHover=!0}click(){C.keyInputObject=this,this.onClick()}stopEditing(){this.isKeyInputObject()&&(this.soundRelease&&this.soundRelease.play(),C.activeObject=void 0,C.keyInputObject=void 0,this.onChange())}onKeyDown(t){let e=t.code,s=t.key;e===\"Backspace\"?this.text=this.text.slice(0,-1):e===\"Enter\"||e===\"Escape\"?this.stopEditing():s.length===1&&(!this.maxLength||this.text.lengththis.size.y,s=e?this.size.y:this.size.x,i=e?this.size.x:this.size.y,o=e?this.nativePos.x:this.nativePos.y,a=i-s,r=o-a/2,c=o+a/2,l=C.screenToNative(Gt);this.value=e?It(l.x,r,c):It(l.y,c,r)}else if(this.isNavigationObject()){let e=C.getNavigationOtherDirection();C.navigationTimer.active()||(this.value=j(this.value+e*.01))}this.value===t||this.onChange()}render(){super.render();let t=this.size.x>this.size.y,e=t?this.size.x:this.size.y,s=t?this.size.y:this.size.x;if(this.fillMode){let o=$(s,this.cornerRadius*2),a=Nt(o,e,this.value),r=(a-e)*(t?.5:-.5),c=this.nativePos.add(t?x(r,0):x(0,r)),l=this.disabled?this.disabledColor:this.handleColor,u=t?x(a,this.size.y):x(this.size.x,a);C.drawRect(c,u,l,this.lineWidth,this.lineColor,this.cornerRadius,this.gradientColor)}else{let o=j(t?this.value:1-this.value),a=(e-s)*(o-.5),r=this.nativePos.add(t?x(a,0):x(0,a)),c=this.disabled?this.disabledColor:this.handleColor,l=x(s);C.drawRect(r,l,c,this.lineWidth,this.lineColor,this.cornerRadius,this.gradientColor)}let i=this.getTextSize();C.drawText(this.text,this.nativePos,i,this.textColor,this.textLineWidth,this.textLineColor,this.align,this.font,this.fontStyle,!0,this.textShadow)}navigatePressed(){this.value=this.value?0:1,this.onChange(),this.onRelease(),super.navigatePressed()}},Mo=class extends Zt{constructor(t,e,s,i=!1,o=!1,a=1){super(t,e||x()),f(J(s),\"video src must be a string\"),f(R(a),\"video volume must be a number\"),this.color=nt,this.cornerRadius=0,this.volume=a,this.video=document.createElement(\"video\"),this.video.loop=o,this.video.volume=j(a*un),this.video.muted=!zt,this.video.style.display=\"none\",this.video.src=s,document.body.appendChild(this.video),i&&this.play()}async play(){try{await this.video.play()}catch{}}pause(){this.video.pause()}stop(){this.video.pause(),this.video.currentTime=0}isLoading(){return this.video.readyState=1,\"ui layout columns must be a number >= 1\"),f(R(s),\"ui layout gap must be a number\"),f(R(i),\"ui layout padding must be a number\"),this.columns=e,this.gap=s,this.padding=i,o&&(this.color=re,this.gradientColor=void 0,this.lineWidth=0,this.shadowColor=re),this.relayout()}addChild(t){return super.addChild(t),this.relayout(),t}removeChild(t){super.removeChild(t),this.relayout()}relayout(){let t=this.children.length;if(!t){this.size=x(this.padding*2);return}let e=this.columns,s=Qn(t/e),i=new Array(e).fill(0),o=new Array(s).fill(0);for(let p=0;p=0&&m.objects.splice(t,1),super.destroy()}updatePhysics(){}render(){this.tileInfo?super.render():this.drawFixtures(this.color,this.lineColor,this.lineWidth)}renderDebugInfo(){let t=!this.getIsAwake(),e=this.getBodyType()===m.bodyTypeStatic,s=B(t?1:0,t?1:0,e?1:0,.5);this.drawFixtures(s)}drawFixtures(t=V,e=nt,s=.1,i,o){this.getFixtureList().forEach(a=>{m.castShapeObject(a.GetShape()).GetType()!==m.instance.b2Shape.e_edge&&m.drawFixture(a,this.pos,this.angle,t,e,s,i,o)}),this.edgeLists.forEach(a=>fo(a,s,e,!1,this.pos,this.angle)),this.edgeLoops.forEach(a=>fo(a,s,e,!0,this.pos,this.angle))}beginContact(t){}endContact(t){}addShape(t,e=1,s=.2,i=0,o=!1){f(R(e),\"density must be a number\"),f(R(s),\"friction must be a number\"),f(R(i),\"restitution must be a number\");let a=new m.instance.b2FixtureDef;return a.set_shape(t),a.set_density(e),a.set_friction(s),a.set_restitution(i),a.set_isSensor(o),this.body.CreateFixture(a)}addBox(t=x(1),e=x(),s=0,i,o,a,r){f(L(t),\"size must be a Vector2\"),f(t.x>0&&t.y>0,\"size must be positive\"),f(L(e),\"offset must be a Vector2\"),f(R(s),\"angle must be a number\");let c=new m.instance.b2PolygonShape;return c.SetAsBox(t.x/2,t.y/2,m.vec2dTo(e),-s),this.addShape(c,i,o,a,r)}addPoly(t,e,s,i,o){f(ie(t),\"points must be an array\");function a(c){f(3<=c.length&&c.length<=8);let l=m.instance._malloc(c.length*8);for(let p=0,d=0;p>2]=c[p].x,d+=4,m.instance.HEAPF32[l+d>>2]=c[p].y,d+=4;let u=m.instance.wrapPointer(l,m.instance.b2Vec2),h=new m.instance.b2PolygonShape;return h.Set(u,c.length),m.instance._free(l),h}let r=a(t);return this.addShape(r,e,s,i,o)}addRegularPoly(t=1,e=8,s,i,o,a){f(R(t)&&t>0,\"diameter must be a positive number\"),f(R(e)&&e>2,\"sides must be a positive number greater than 2\");let r=[],c=t/2;for(let l=e;l--;)r.push(x(c,0).rotate((l+.5)/e*q*2));return this.addPoly(r,s,i,o,a)}addRandomPoly(t=1,e,s,i,o){f(R(t)&&t>0,\"diameter must be a positive number\");let a=er(3,9),r=[],c=t/2;for(let l=a;l--;)r.push(x(ct(c/2,c*1.5),0).rotate(l/a*q*2));return this.addPoly(r,e,s,i,o)}addCircle(t=1,e=x(),s,i,o,a){f(R(t)&&t>0,\"diameter must be a positive number\"),f(L(e),\"offset must be a Vector2\");let r=new m.instance.b2CircleShape;return r.set_m_p(m.vec2dTo(e)),r.set_m_radius(t/2),this.addShape(r,s,i,o,a)}addEdge(t,e,s,i,o,a){f(L(t),\"point1 must be a Vector2\"),f(L(e),\"point2 must be a Vector2\");let r=new m.instance.b2EdgeShape;return r.Set(m.vec2dTo(t),m.vec2dTo(e)),this.addShape(r,s,i,o,a)}addEdgeList(t,e,s,i,o){f(ie(t),\"points must be an array\");let a=[],r=[];for(let c=0;ct[$t(l,t.length)];for(let l=0;lthis.destroyFixture(t))}getCenterOfMass(){return m.vec2From(this.body.GetWorldCenter())}getLinearVelocity(){return m.vec2From(this.body.GetLinearVelocity())}getAngularVelocity(){return this.body.GetAngularVelocity()}getMass(){return this.body.GetMass()}getInertia(){return this.body.GetInertia()}getIsAwake(){return this.body.IsAwake()}getBodyType(){return this.body.GetType()}getSpeed(){return this.getLinearVelocity().length()}setTransform(t,e){this.pos=t,this.angle=e,this.body.SetTransform(m.vec2dTo(t),-e)}setPosition(t){this.setTransform(t,this.body.GetAngle())}setAngle(t){this.setTransform(m.vec2From(this.body.GetPosition()),t)}setLinearVelocity(t){this.body.SetLinearVelocity(m.vec2dTo(t))}setAngularVelocity(t){this.body.SetAngularVelocity(t)}setLinearDamping(t){this.body.SetLinearDamping(t)}setAngularDamping(t){this.body.SetAngularDamping(t)}setGravityScale(t=1){this.body.SetGravityScale(this.gravityScale=t)}setBullet(t=!0){this.body.SetBullet(t)}setAwake(t=!0){this.body.SetAwake(t)}setBodyType(t){this.body.SetType(t)}setSleepingAllowed(t=!0){this.body.SetSleepingAllowed(t)}setFixedRotation(t=!0){this.body.SetFixedRotation(t)}setCenterOfMass(t){this.setMassData(t)}setMass(t){this.setMassData(void 0,t)}setMomentOfInertia(t){this.setMassData(void 0,void 0,t)}resetMassData(){this.body.ResetMassData()}setMassData(t,e,s){let i=new m.instance.b2MassData;this.body.GetMassData(i),t!==void 0&&i.set_center(m.vec2dTo(t)),e!==void 0&&i.set_mass(e),s!==void 0&&i.set_I(s),this.body.SetMassData(i)}setFilterData(t=0,e=0,s=0){this.getFixtureList().forEach(i=>{let o=i.GetFilterData();o.set_categoryBits(t),o.set_maskBits(65535&~e),o.set_groupIndex(s)})}setSensor(t=!0){this.getFixtureList().forEach(e=>e.SetSensor(t))}applyForce(t,e){e||(e=this.getCenterOfMass()),this.setAwake(),this.body.ApplyForce(m.vec2dTo(t),m.vec2dTo(e))}applyAcceleration(t,e){e||(e=this.getCenterOfMass()),this.setAwake();let s=t.scale(this.getMass());this.body.ApplyLinearImpulse(m.vec2dTo(s),m.vec2dTo(e))}applyImpulse(t,e){e||(e=this.getCenterOfMass()),this.setAwake(),this.body.ApplyLinearImpulse(m.vec2dTo(t),m.vec2dTo(e))}applyTorque(t){this.setAwake(),this.body.ApplyTorque(t)}applyAngularAcceleration(t){this.setAwake(),this.body.ApplyAngularImpulse(t*this.getInertia())}applyAngularImpulse(t){this.setAwake(),this.body.ApplyAngularImpulse(t)}hasFixtures(){return!m.isNull(this.body.GetFixtureList())}getFixtureList(){let t=[];for(let e=this.body.GetFixtureList();!m.isNull(e);)t.push(e),e=e.GetNext();return t}hasJoints(){return!m.isNull(this.body.GetJointList())}getJointList(){let t=[];for(let e=this.body.GetJointList();!m.isNull(e);)t.push(e),e=e.get_next();return t}},pi=class extends gs{constructor(t,e,s,i=0,o,a=0){let r=m.bodyTypeStatic;super(t,e,s,i,o,r,a)}},Go=class extends gs{constructor(t,e,s,i=0,o,a=0){let r=m.bodyTypeKinematic;super(t,e,s,i,o,r,a)}},Fo=class extends pi{constructor(t){f(t instanceof cs,\"tileLayer must be a TileCollisionLayer\"),super(t.pos,t.size),this.tileLayer=t,this.addChild(t)}render(){}buildCollision(t=.2,e=0){this.destroyAllFixtures(),this.pos=this.tileLayer.pos.copy(),this.size=this.tileLayer.size.copy();let s=[],i=(a,r)=>a+r*this.size.x,o=(a,r)=>!s[i(a,r)]&&this.tileLayer.getCollisionData(x(a,r))>0;for(let a=0;ai.fractiono.pos.distanceSquared(t)0,\"Tween duration must be > 0\"),this.callback=t,this.start=e,this.end=s,this.duration=i,this.life=i,this.ease=o.ease||xi.LINEAR,this.useRealTime=!!o.useRealTime,this.paused=!!o.paused,this.thenCallback=void 0,this.loopRemaining=0,Ut.push(this),t(this.interp(i))}setEase(t){return this.ease=t,this}then(t){return this.thenCallback=t,this.loopRemaining=0,this}loop(t=1/0){return this.loopRemaining=t,this.thenCallback=()=>Rc(this),this}pingPong(t=1/0){return this.loopRemaining=t,this.thenCallback=()=>Pc(this),this}pause(){this.paused=!0}resume(){this.paused=!1}restart(){this.life=this.duration,this.paused=!1,Ut.indexOf(this)<0&&Ut.push(this),this.callback(this.interp(this.duration))}isActive(){return!this.paused&&Ut.indexOf(this)>=0}getPercent(){return It(this.duration-this.life,0,this.duration)}getValue(){return this.interp(this.life)}interp(t){let e=this.ease((this.duration-t)/this.duration);return Ra(this.start)?this.start.lerp(this.end,e):this.start+(this.end-this.start)*e}stop(){let t=Ut.indexOf(this);t>=0&&Ut.splice(t,1),this.thenCallback=void 0}},xi={LINEAR:n=>n,POWER:n=>t=>t**n,SINE:n=>1-rt(n*(q/2)),CIRC:n=>1-(1-n*n)**.5,EXPO:n=>n===0?0:2**(10*n-10),BACK:n=>n*n*(2.70158*n-1.70158),ELASTIC:n=>n===0?0:n===1?1:-(2**(10*n-10))*tt((37-40*n)*q/6),SPRING:n=>1-(tt(q*(1-n)*(.2+2.5*(1-n)**3))*n**2.2+(1-n))*(1+1.2*n),BOUNCE:n=>{let t=1-n,e;return t<4/11?e=7.5625*t*t:t<8/11?e=7.5625*(t-=6/11)*t+.75:t<10/11?e=7.5625*(t-=9/11)*t+.9375:e=7.5625*(t-=10.5/11)*t+.984375,1-e},IN:n=>n,OUT:n=>t=>1-n(1-t),IN_OUT:n=>xi.PIECEWISE(n,xi.OUT(n)),PIECEWISE:(...n)=>{let t=n.length;return e=>{let s=e*t-1e-9>>0;return(n[s]((e-s/t)*t)+s)/t}},BEZIER:(n,t,e,s)=>{let i=o=>{let a=1-o,r=3*a*a*o,c=3*a*o*o,l=o**3;return[r*n+c*e+l,r*t+c*s+l]};return o=>{let a=0,r=1;for(let c=0;c<128;c++){let l=(a+r)/2,[u,h]=i(l);if(Z(u-o)<1e-5)return h;u=this.size.x||e>=this.size.y?null:this.nodes[t+e*this.size.x]}worldToTile(t){let e=this.tileLayer?this.tileLayer.pos.x:0,s=this.tileLayer?this.tileLayer.pos.y:0;return x(bt(t.x-e),bt(t.y-s))}tileToWorld(t,e){let s=this.tileLayer?this.tileLayer.pos.x:0,i=this.tileLayer?this.tileLayer.pos.y:0;return x(t+.5+s,e+.5+i)}buildNodeData(){let t=this.size.x,e=this.size.y,s=this.tileLayer?this.tileLayer.pos.x:0,i=this.tileLayer?this.tileLayer.pos.y:0;for(let o=0;o0&&(c?l>0&>(r.posWorld,oo,B(1,0,0,$(.2,l*.05)),this.debugTime):gt(r.posWorld,oo,B(1,0,0,.25),this.debugTime))}}aStarSearch(t,e){f(t&&e,\"aStarSearch needs both endpoints\"),f(t!==e,\"aStarSearch: start and end must differ \\u2014 caller should handle trivial case\"),f(t.walkable&&e.walkable,\"aStarSearch: endpoints must be walkable\");let s=[t];t.isOpen=!0;let i=0;for(;s.length>0;){let o=0,a=s[0].f;for(let c=1;cthis.maxLoop)break;r.isOpen=!1,s.splice(o,1),r.isClosed=!0,this.debug&&this.debugTime>0&>(r.posWorld,oo,B(1,1,1,.05),this.debugTime);for(let c=-1;c<=1;++c)for(let l=-1;l<=1;++l){if(l===0&&c===0)continue;let u=this.getNode(r.pos.x+l,r.pos.y+c);if(!u||!u.walkable||u.isClosed)continue;let h=1;if(l!==0&&c!==0){let v=this.getNode(r.pos.x+l,r.pos.y);if(!v||!v.walkable)continue;let w=this.getNode(r.pos.x,r.pos.y+c);if(!w||!w.walkable)continue;h=md}let p=r.g+h+u.cost;if(!u.isOpen)u.isOpen=!0,s.push(u);else if(p>=u.g)continue;u.parent=r,u.g=p;let d=Z(e.pos.x-u.pos.x),y=Z(e.pos.y-u.pos.y),b=X(d,y)+(Math.SQRT2-1)*$(d,y);u.f=u.g+b*this.heuristicWeight}}return e.parent!==null}getNearestClearNode(t,e=10,s=!0){f(L(t),\"worldPos must be a Vector2\"),s&&this.buildNodeData();let i=this.tileLayer?this.tileLayer.pos.x:0,o=this.tileLayer?this.tileLayer.pos.y:0,a=bt(t.x-i),r=bt(t.y-o);for(let c=0;c<=e;++c){let l=null,u=0;for(let h=-c;h<=c;++h)for(let p=-c;p<=c;++p){if(c>0&&Z(p)!==c&&Z(h)!==c)continue;let d=this.getNode(a+p,r+h);if(!d||!d.isClear())continue;let y=d.posWorld.x-t.x,b=d.posWorld.y-t.y,v=y*y+b*b;(!l||v0&&Ft(i.posWorld,.3,B(.5,0,.5,.5),this.debugTime),t.splice(e,1),e=X(1,e-1);continue}else if(c===2){this.debug&&this.debugTime>0&&Ft(i.posWorld,.3,B(1,0,0,.5),this.debugTime);let d,y;s.pos.y===i.pos.y&&o.pos.x===i.pos.x?(d=s.pos.x,y=o.pos.y):(d=o.pos.x,y=s.pos.y);let b=this.getNode(d,y);if(b&&b.isClear()){t.splice(e,1),e=X(1,e-1);continue}}else if(c===5){this.debug&&this.debugTime>0&&Ft(i.posWorld,.3,B(1,1,0,.5),this.debugTime);let d=e>=2?t[e-2]:s,y,b,v,w;l===0||h===0?(y=o.pos.x,b=i.pos.y,v=s.pos.x,w=i.pos.y):(y=i.pos.x,b=o.pos.y,v=i.pos.x,w=s.pos.y);let _=y-d.pos.x,S=b-d.pos.y,E=v-d.pos.x,A=w-d.pos.y,T=_*_+S*S,P=E*E+A*A,I=T0&&Ft(i.posWorld,.3,B(0,1,0,.5),this.debugTime),l===h&&u===p){++e;continue}else{let d,y;s.pos.y===o.pos.y?(d=i.pos.x,y=s.pos.y):(d=s.pos.x,y=i.pos.y);let b=this.getNode(d,y);if(b&&b.isClear()){t[e]=b,e=X(1,e-1);continue}}++e}}smoothPathStringPull(t){if(t.length<=2)return;for(let i of t)if(!i.isClear())return;let e=t.slice();t.length=0,t.push(e[0]);let s=0;for(let i=1;i0&&ye(o.posWorld,t[t.length-1].posWorld,B(0,0,1,.3),.02,this.debugTime);continue}for(;s=1;--e){let s=t[e-1],i=t[e],o=t[e+1];(i.pos.x-s.pos.x)*(o.pos.y-s.pos.y)===(i.pos.y-s.pos.y)*(o.pos.x-s.pos.x)&&t.splice(e,1)}}isNodeClear(t,e){let s=this.getNode(t,e);return s!==null&&s.isClear()}isLineClear(t,e){f(L(t)&&L(e),\"isLineClear needs Vector2 endpoints\"),f(this.isNodeClear(t.x,t.y)&&this.isNodeClear(e.x,e.y),\"isLineClear endpoints must be in-bounds and clear\");let s=e.x-t.x,i=e.y-t.y,o=Z(s),a=Z(i),r=Yt(s),c=Yt(i),l=t.x,u=t.y;if(a===o){for(;l!==e.x;){if(l!==t.x&&(!this.isNodeClear(l,u)||!this.isNodeClear(l,u-c))||!this.isNodeClear(l,u+c))return!1;l+=r,u+=c}if(!this.isNodeClear(e.x,e.y-c))return!1}else if(ar.posWorld.copy());if(this.debug&&this.debugTime>0&&a.length>0){for(let r=1;rbd,bootGameHost:()=>vd,default:()=>xd});var Ls=class{constructor(t){this._state=(t==null?2654435769:t)>>>0}next(){this._state=this._state+1831565813>>>0;let t=this._state;return t=Math.imul(t^t>>>15,t|1),t^=t+Math.imul(t^t>>>7,t|61),((t^t>>>14)>>>0)/4294967296}range(t,e){return t+this.next()*(e-t)}reseed(t){this._state=(t==null?2654435769:t)>>>0}},ji=class{constructor(t){this._clock=t||Vc(),this._origin=this._clock()}nowMs(){return this._clock()}elapsedMs(){return this._clock()-this._origin}},Ni=class{constructor(t){this._time=t,this._subs=new Map}sourceFor(t){let e=this,s=t==null?null:t;return{on(i,o){if(e._assertType(i),typeof o!=\"function\")throw new Error(\"[HostDevInputBridge] on \\u7684 handler \\u5FC5\\u987B\\u662F\\u51FD\\u6570\");let a=e._subs.get(i)||[],r={handler:o,scope:s};return a.push(r),e._subs.set(i,a),{cancel(){let c=e._subs.get(i);if(!c)return;let l=c.indexOf(r);l>=0&&c.splice(l,1)}}},off(i,o){e._assertType(i);let a=e._subs.get(i);if(!a)return;let r=a.findIndex(c=>c.handler===o&&c.scope===s);r>=0&&a.splice(r,1)}}}_emit(t,e){this._assertType(t);let s=e||{},i={type:t,x:typeof s.x==\"number\"?s.x:0,y:typeof s.y==\"number\"?s.y:0,key:typeof s.key==\"string\"?s.key:\"\",tMs:this._time.nowMs()},o=this._subs.get(t);if(!o||o.length===0)return;let a=o.slice();for(let r of a)try{r.handler(i)}catch(c){Fe(\"[host-dev input] \\u8F93\\u5165 handler \\u629B\\u9519\\uFF08\\u5DF2\\u9694\\u79BB\\uFF09type=\"+t,c)}}disposeScope(t){if(t==null)return 0;let e=0;for(let[s,i]of this._subs){let o=i.filter(a=>a.scope!==t);e+=i.length-o.length,o.length===0?this._subs.delete(s):this._subs.set(s,o)}return e}_subscriptionCount(){let t=0;for(let e of this._subs.values())t+=e.length;return t}_assertType(t){if(!Bi.has(t))throw new Error(\"[HostDevInputBridge] \\u975E\\u6CD5\\u8F93\\u5165\\u4E8B\\u4EF6\\u7C7B\\u578B\\uFF1A\"+String(t)+\"\\uFF08\\u4EC5\\u652F\\u6301 \"+Array.from(Bi).join(\"/\")+\"\\uFF09\")}},Bi=new Set([\"pointerdown\",\"pointermove\",\"pointerup\",\"keydown\",\"keyup\"]);function Wr(n){let t=n||{},e=new ji(t.clock),s=new Ls(t.seed),i=t.seed==null?2654435769:t.seed>>>0,o=new Ni(e),a=typeof t.audioFactory==\"function\"?t.audioFactory:null,r=null,c=!1,l=typeof t.engineFactory==\"function\"?t.engineFactory:null,u=null,h=!1,p=new Map,d=0,y=0;function b(){if(r)return r;if(!a)return c||(Ji(\"[host-dev audio] \\u672A\\u6CE8\\u5165 audioFactory\\uFF0CgetAudioContext \\u8FD4\\u56DE null\\uFF08P6 \\u987B\\u5BB9\\u9519\\u964D\\u7EA7\\uFF1B\\u96C6\\u6210\\u6BB5\\u6CE8\\u5165\\u771F\\u5B9E\\u5DE5\\u5382\\uFF09\"),c=!0),null;try{r=a()}catch(E){Fe(\"[host-dev audio] audioFactory \\u629B\\u9519\\uFF0CgetAudioContext \\u964D\\u7EA7\\u8FD4\\u56DE null\",E),r=null}return r}function v(){if(u)return u;if(!l)return h||(Ji(\"[host-dev engine] \\u672A\\u6CE8\\u5165 engineFactory\\uFF0CgetEngine \\u8FD4\\u56DE null\\uFF08\\u63D2\\u4EF6\\u987B\\u5BB9\\u9519\\u964D\\u7EA7\\uFF1A\\u7C92\\u5B50\\u9000\\u53D7\\u63A7\\u9762\\u81EA\\u7BA1/\\u5408\\u6210\\u6838\\u9000 vendored/\\u6570\\u5B66\\u9000\\u5185\\u7F6E\\uFF1B\\u96C6\\u6210\\u6BB5\\u6CE8\\u5165\\u771F\\u5B9E\\u80CC\\u4E66\\uFF09\"),h=!0),null;try{u=l()}catch(E){Fe(\"[host-dev engine] engineFactory \\u629B\\u9519\\uFF0CgetEngine \\u964D\\u7EA7\\u8FD4\\u56DE null\",E),u=null}return u}function w(E,A){return{getContext2d(){return t.context2d==null?null:t.context2d},onFrame(P){let I=d++;return p.set(I,P),{cancel(){p.delete(I)}}},getInput(){return o.sourceFor(E)},getAudioContext(){return b()},getEngine(){return v()},time:e,random:A}}let _=w(null,s),S={context:_,tick(E){y+=1;let A=Array.from(p.values());for(let T of A)try{T(E,y)}catch(P){Fe(\"[host-dev tick] \\u5E27\\u56DE\\u8C03\\u629B\\u9519\\uFF08\\u5DF2\\u9694\\u79BB\\uFF09frame=\"+y,P)}},frameCount(){return y},inputBridge:o,deriveContextFor(E,A){let T=Jc(i,E),P=new Ls(T);return w(A==null?E:A,P)}};return Object.defineProperty(_,zi,{value:S,enumerable:!1,writable:!1,configurable:!1}),S}var zi=Symbol(\"zaomeng.coreProtocol.contextBundle\"),Os=class{constructor(){this._plugins=new Map,this._initOrder=[],this._initErrors=[],this._phase=\"idle\",this._disposed=!1,this._context=void 0,this._pluginContexts=new Map}register(t){if(!t||typeof t!=\"object\")throw new Error(\"[PluginRegistry] register \\u9700\\u8981\\u4E00\\u4E2A\\u63D2\\u4EF6\\u5BF9\\u8C61\");if(typeof t.name!=\"string\"||t.name.length===0)throw new Error(\"[PluginRegistry] \\u63D2\\u4EF6\\u7F3A\\u5C11\\u5408\\u6CD5 name\");if(typeof t.version!=\"string\"||t.version.length===0)throw new Error(\"[PluginRegistry] \\u63D2\\u4EF6 \"+t.name+\" \\u7F3A\\u5C11\\u5408\\u6CD5 version\");if(typeof t.init!=\"function\")throw new Error(\"[PluginRegistry] \\u63D2\\u4EF6 \"+t.name+\" \\u7F3A\\u5C11 init \\u51FD\\u6570\");if(t.dispose!=null&&typeof t.dispose!=\"function\")throw new Error(\"[PluginRegistry] \\u63D2\\u4EF6 \"+t.name+\" \\u7684 dispose \\u5FC5\\u987B\\u662F\\u51FD\\u6570\\u6216\\u7701\\u7565\");if(this._plugins.has(t.name))throw new Error(\"[PluginRegistry] \\u63D2\\u4EF6\\u91CD\\u540D\\u62D2\\u7EDD\\uFF1A\"+t.name+\" \\u5DF2\\u6CE8\\u518C\");return this._plugins.set(t.name,t),this}has(t){return this._plugins.has(t)}get(t){return this._plugins.get(t)}list(){return Array.from(this._plugins.keys())}initAll(){if(this._phase===\"initialized\")return Ji(\"[PluginRegistry] initAll \\u91CD\\u590D\\u8C03\\u7528\\uFF0C\\u5DF2\\u5FFD\\u7565\\uFF08\\u5DF2\\u5904\\u4E8E initialized\\uFF09\"),this._initResult();if(this._phase===\"disposed\")throw new Error(\"[PluginRegistry] \\u5DF2 dispose \\u7684\\u6CE8\\u518C\\u5668\\u4E0D\\u53EF\\u518D initAll\");if(this._context==null)throw new Error(\"[PluginRegistry] \\u672A\\u6CE8\\u5165\\u53D7\\u63A7\\u4E0A\\u4E0B\\u6587\\uFF1A\\u8BF7\\u5728 initAll() \\u4E4B\\u524D\\u8C03\\u7528 useContext(context)\\u3002\\uFF08host-dev \\u7528\\u6CD5\\uFF1Aconst { context } = createHostDevContext(); reg.useContext(context); reg.initAll();\\uFF09\");for(let[t,e]of this._plugins){let s=this._missingDeps(e);if(s.length>0){let o=new Error(\"[PluginRegistry] \\u63D2\\u4EF6 \"+t+\" \\u4F9D\\u8D56\\u7F3A\\u5931\\uFF1A\"+s.join(\", \")+\"\\uFF08\\u672A\\u6CE8\\u518C\\uFF09\");this._initErrors.push({name:t,error:o}),Fe(\"[PluginRegistry] \\u8DF3\\u8FC7 init\\uFF08\\u4F9D\\u8D56\\u7F3A\\u5931\\uFF0C\\u5DF2\\u9694\\u79BB\\uFF09\\uFF1A\"+t,o);continue}let i=this._contextFor(t);try{e.init(i),this._initOrder.push(t)}catch(o){let a=o instanceof Error?o:new Error(String(o));this._initErrors.push({name:t,error:a}),Fe(\"[PluginRegistry] \\u63D2\\u4EF6 init \\u629B\\u9519\\uFF08\\u5DF2\\u9694\\u79BB\\uFF0C\\u4E0D\\u8FDE\\u5750\\uFF09\\uFF1A\"+t,a)}}return this._phase=\"initialized\",this._initResult()}_contextFor(t){let e=this._pluginContexts.get(t);if(e)return e;let s=this._context&&this._context[zi],i;return s&&typeof s.deriveContextFor==\"function\"?i=s.deriveContextFor(t,t):i=this._context,this._pluginContexts.set(t,i),i}disposeAll(){if(this._disposed)return{disposed:[],errors:[]};let t=[],e=[],s=this._context&&this._context[zi],i=s&&s.inputBridge;for(let o=this._initOrder.length-1;o>=0;o--){let a=this._initOrder[o],r=this._plugins.get(a);if(r&&typeof r.dispose==\"function\")try{r.dispose(),t.push(a)}catch(c){let l=c instanceof Error?c:new Error(String(c));e.push({name:a,error:l}),Fe(\"[PluginRegistry] \\u63D2\\u4EF6 dispose \\u629B\\u9519\\uFF08\\u5DF2\\u9694\\u79BB\\uFF09\\uFF1A\"+a,l)}if(i&&typeof i.disposeScope==\"function\")try{i.disposeScope(a)}catch(c){Fe(\"[PluginRegistry] \\u56DE\\u6536\\u63D2\\u4EF6\\u8F93\\u5165\\u8BA2\\u9605\\u629B\\u9519\\uFF08\\u5DF2\\u9694\\u79BB\\uFF09\\uFF1A\"+a,c)}}return this._disposed=!0,this._phase=\"disposed\",{disposed:t,errors:e}}useContext(t){if(this._phase!==\"idle\")throw new Error(\"[PluginRegistry] useContext \\u5FC5\\u987B\\u5728 initAll() \\u4E4B\\u524D\\u8C03\\u7528\\uFF08\\u5F53\\u524D\\u9636\\u6BB5\\uFF1A\"+this._phase+\"\\uFF09\\u3002\\u8BF7\\u5728\\u4EFB\\u4F55 initAll/disposeAll \\u4E4B\\u524D\\u6CE8\\u5165\\u53D7\\u63A7\\u4E0A\\u4E0B\\u6587\\u3002\");if(t==null||typeof t!=\"object\")throw new Error(\"[PluginRegistry] useContext \\u9700\\u8981\\u4E00\\u4E2A\\u53D7\\u63A7\\u4E0A\\u4E0B\\u6587\\u5BF9\\u8C61\\u3002\\uFF08host-dev \\u7528\\u6CD5\\uFF1Aconst { context } = createHostDevContext(); reg.useContext(context);\\uFF09\");return this._context=t,this}_missingDeps(t){return!Array.isArray(t.dependencies)||t.dependencies.length===0?[]:t.dependencies.filter(e=>!this._plugins.has(e))}_initResult(){return{ok:this._initErrors.length===0,initialized:this._initOrder.slice(),errors:this._initErrors.slice()}}};function Jc(n,t){let e=2166136261,s=16777619,i=n>>>0;for(let a=0;a<32;a+=8){let r=i>>>a&255;e^=r,e=Math.imul(e,s)>>>0}let o=String(t);for(let a=0;a>>0,e^=r>>>8&255,e=Math.imul(e,s)>>>0}return e>>>0}function Vc(){if(typeof globalThis!=\"undefined\"&&globalThis.performance&&typeof globalThis.performance.now==\"function\")return()=>globalThis.performance.now();if(typeof process!=\"undefined\"&&process.hrtime&&typeof process.hrtime.bigint==\"function\"){let n=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-n)/1e6}return()=>Date.now()}function Fe(n,t){let e=t&&t.stack?t.stack:t&&t.message?t.message:t;typeof console!=\"undefined\"&&typeof console.error==\"function\"&&console.error(n+(e?\"\\uFF1A\"+e:\"\"))}function Ji(n){typeof console!=\"undefined\"&&typeof console.warn==\"function\"&&console.warn(n)}var Td=Object.freeze(Array.from(Bi)),qr=\"core-protocol-v0\";var Hr=Object.freeze([\"t_boot\",\"t_plugins_ready\",\"t_game_init\",\"t_first_paint\",\"t_input_bound\",\"t_game_start\"]),Vi=Hr.reduce((n,t,e)=>(n[t]=e,n),Object.create(null)),Uc=new Set(Hr);function Ui(n){let t=2166136261;for(let e=0;e>>0).toString(16).padStart(8,\"0\")}var Xr=Ui(\"runtime-probe::seed::v1\");function Ds(n){return n===null||typeof n!=\"object\"?JSON.stringify(n):Array.isArray(n)?\"[\"+n.map(Ds).join(\",\")+\"]\":\"{\"+Object.keys(n).sort().map(e=>JSON.stringify(e)+\":\"+Ds(n[e])).join(\",\")+\"}\"}function Yr(n){let t=Ds(n.extra==null?null:n.extra),e=Ds(Array.isArray(n.pluginSet)?n.pluginSet:[]);return[\"seq=\"+n.seq,\"anchor=\"+n.anchor,\"tMono=\"+n.tMono,\"buildSha=\"+(n.buildSha||\"\"),\"pluginSet=\"+e,\"extra=\"+t].join(\"|\")}function Wc(n){return Math.round(n*1e3)/1e3}function qc(n){if(!Array.isArray(n))return{ok:!1,reason:\"records_not_array\"};let t=Xr,e=-1;for(let s=0;stypeof E==\"string\").slice().sort():[],i=typeof t.sink==\"function\"?t.sink:null,o=t.autoBoot!==!1,a=typeof t.inputAnchorType==\"string\"?t.inputAnchorType:\"pointerdown\",r=()=>0,c=!1,l=0,u=Xr,h=[],p=new Set,d=-1,y=null,b=!1,v=!1,w=!1;function _(E,A){let T=null;if(!Uc.has(E))T=\"unknown_anchor\";else{p.has(E)&&(T=T?T+\";duplicate_anchor\":\"duplicate_anchor\");let I=Vi[E];IE.time.nowMs(),c=!0,o&&!v&&_(\"t_boot\",{auto:!0}),y=E.getInput().on(a,A=>{b||(b=!0,_(\"t_input_bound\",{firstInput:!0,type:A.type}))}),_(\"t_input_bound\",{stage:\"registered\",type:a})},dispose(){w||(y&&(y.cancel(),y=null),w=!0)},mark:_,getRecords(){return h.map(E=>({...E}))},toJSONL(){return h.map(E=>JSON.stringify(E)).join(`\n`)},verify(){return qc(h)}}}function Hc(n,t){let e=t&&t.stack?t.stack:t&&t.message?t.message:t;typeof console!=\"undefined\"&&typeof console.error==\"function\"&&console.error(n+(e?\"\\uFF1A\"+e:\"\"))}function Kr(n){let t=n.LJS,e=n.engineMath,s=n.engineMode,i=n.recHook||(()=>{});if(s!==\"real\")return null;let o=e;return{math:{lerp:(r,c,l)=>(i(\"math.lerp\"),o.lerp(r,c,l)),smoothStep:r=>(i(\"math.smoothStep\"),o.smoothStep(r)),easing:Object.fromEntries(Object.keys(o.easing).map(r=>[r,c=>(i(\"math.easing.\"+r),o.easing[r](c))]))},particles:{spawnEmitter(r){i(\"particles.spawnEmitter\");let c=t.screenToWorld(t.vec2(r.pos.x,r.pos.y)),l=t.cameraScale,u=r.emitTime!=null?r.emitTime:r.count!=null?1/60:0,h=r.emitRate!=null?r.emitRate:r.count!=null?r.count*60:100,p=(r.speed!=null?r.speed:0)/l/60,d=r.damping!=null?r.damping:1,y=r.colorStart||{r:1,g:1,b:1,a:1},b=r.colorEnd||{r:1,g:1,b:1,a:0},v=t.rgb(y.r,y.g,y.b,y.a),w=t.rgb(b.r,b.g,b.b,b.a),_=new t.ParticleEmitter(c,r.angle!=null?r.angle:0,(r.emitSize!=null?r.emitSize:0)/l,u,h,r.coneAngle!=null?r.coneAngle:Math.PI,void 0,v,v,w,w,r.particleTime!=null?r.particleTime:.5,(r.sizeStart!=null?r.sizeStart:6)/l,(r.sizeEnd!=null?r.sizeEnd:0)/l,p,0,d,1,r.gravityScale!=null?r.gravityScale:0,Math.PI,.1,0,!1,r.additive!=null?r.additive:!1,!0);return{isActive(){return _.isActive()},stop(){_.destroyed||_.destroy(!0)}}}},audio:{synth:{synthSfx:r=>{i(\"audio.synth.synthSfx\");try{return t.zzfxG(...r)}catch(c){return console.error(\"[host][engine-audio] zzfxG \\u5408\\u6210\\u5F02\\u5E38\\uFF0C\\u8FD4 null\\uFF1A\"+(c&&c.message?c.message:c)),null}},synthSong:(r,c,l,u)=>{i(\"audio.synth.synthSong\");try{return t.zzfxM(r,c,l,u)}catch(h){return console.error(\"[host][engine-audio] zzfxM \\u5408\\u6210\\u5F02\\u5E38\\uFF0C\\u8FD4 null\\uFF1A\"+(h&&h.message?h.message:h)),null}}}}}}function te(n){return n<0?0:n>1?1:n}function Qr(n){let t=n.Ease,e=t.POWER(2),s=t.POWER(3),i=t.OUT(e),o=t.IN_OUT(e),a=t.OUT(s),r=t.IN_OUT(s),c=t.OUT(t.BACK),l=t.OUT(t.ELASTIC);return{lerp:(u,h,p)=>n.lerp(u,h,p),smoothStep:u=>n.smoothStep(u),easing:{linear:u=>t.LINEAR(te(u)),quadIn:u=>e(te(u)),quadOut:u=>i(te(u)),quadInOut:u=>o(te(u)),cubicIn:u=>s(te(u)),cubicOut:u=>a(te(u)),cubicInOut:u=>r(te(u)),backIn:u=>t.BACK(te(u)),backOut:u=>c(te(u)),elasticIn:u=>t.ELASTIC(te(u)),elasticOut:u=>l(te(u))}}}var je=2,Is=1/60;async function Zr(n){let t=n.canvas,e=n.statusEl||null,s=n.engine,i=n.seed>>>0,o=n.mode||\"play\",a=n.engineMode||\"real\",r=n.viewport.w,c=n.viewport.h,l=n.factory,u=n.buildFactoryOpts||(()=>{}),h=n.plugins||{},p=n.registerOrder||Object.values(h),d=n.onReady||(()=>{}),y=n.recHook||(()=>{});function b(O){e&&(e.textContent=O)}let v=[];window.addEventListener(\"error\",O=>v.push({type:\"error\",message:String(O&&O.message||O)})),window.addEventListener(\"unhandledrejection\",O=>v.push({type:\"unhandledrejection\",message:String(O&&O.reason&&O.reason.message||O&&O.reason||O)}));let w=null,_=!1;function S(){if(!_)return null;if(w)return w;let O=window.AudioContext||window.webkitAudioContext;return O?(w=new O,w):null}let E=0,A=null,T=null,P=null,I=null,ut=[],ot={},k=null;function G(O,st){try{P&&P.mark(O,st)}catch(Xt){v.push({type:\"mark\",message:String(Xt&&Xt.message||Xt)})}}function et(){T&&typeof T._refreshChecklist==\"function\"&&T._refreshChecklist(ot)}async function ft(O,st){A=Wr({context2d:O,seed:i,audioFactory:S,clock:()=>E,engineFactory:()=>Kr({LJS:a===\"real\"?s:null,engineMath:a===\"real\"?Qr(s):null,engineMode:a,recHook:y})});let Xt=new Os;for(let ue of p)Xt.register(ue);Xt.useContext(A.context),I=Xt.initAll(),ut=Xt.list().concat([\"runtime-probe\"]),P=$r({pluginSet:ut,inputAnchorType:\"pointerdown\",autoBoot:!0});let Ct=A.deriveContextFor(\"runtime-probe\",\"runtime-probe\");G(\"t_boot\",{by:\"game-host\",stage:\"pre-init\",engineMode:a}),G(\"t_plugins_ready\",{initialized:I.initialized.length,ok:I.ok});let Gi=u(A.context,{bundle:A,plugins:h,getRealAudioCtx:()=>w});T=l(Gi),await T.init({ctx:A.context,mainContext:O,canvas:st(),seed:i}),k=T&&typeof T._forensicsView==\"function\"?T._forensicsView():null,G(\"t_game_init\",{hasForensics:!!k}),K(),G(\"t_first_paint\",{frame:A.frameCount()});try{P.init(Ct)}catch(ue){v.push({type:\"probe-init\",message:String(ue&&ue.message||ue)})}G(\"t_game_start\",{mode:o,engineMode:a}),et(),d(ae,{bundle:A,game:T,probe:P,checklist:ot,uncaught:v,pluginSet:ut,initRes:I,getGameForensics:()=>k,mark:G,stepOneFrame:K})}let Rt=null;function K(O){let st=typeof O==\"number\"?O:Is;T&&(E+=st*1e3,T.update(st),a===\"stub\"&&Rt&&T.render(Rt),et())}function At(O){for(let st=0;st{}),T&&typeof T._onAudioUnlock==\"function\"&&T._onAudioUnlock()}let ae={seed:i,mode:o,engineMode:a,protocol:qr,frameCount:()=>A?A.frameCount():0,stepFrames:At,startRaf:()=>Es(),stopRaf:()=>_s(),tap(O,st){ht(),A&&(A.inputBridge._emit(\"pointerdown\",{x:O,y:st}),A.inputBridge._emit(\"pointerup\",{x:O,y:st}))},do(O){T&&typeof T._handleAction==\"function\"&&T._handleAction(O)},state:()=>k?k.state():{phase:\"booting\"}};function ce(O){let st=A.inputBridge;function Xt(Ct,Gi){let ue=O.getBoundingClientRect();return{x:(Ct-ue.left)/ue.width*r,y:(Gi-ue.top)/ue.height*c}}O.addEventListener(\"pointerdown\",Ct=>{ht(),st._emit(\"pointerdown\",Xt(Ct.clientX,Ct.clientY))}),O.addEventListener(\"pointermove\",Ct=>{st._emit(\"pointermove\",Xt(Ct.clientX,Ct.clientY))}),O.addEventListener(\"pointerup\",Ct=>{st._emit(\"pointerup\",Xt(Ct.clientX,Ct.clientY))}),window.addEventListener(\"keydown\",Ct=>{ht(),st._emit(\"keydown\",{key:Ct.key})}),window.addEventListener(\"keyup\",Ct=>st._emit(\"keyup\",{key:Ct.key}))}let Mt=0,tn=!1,le=0;function Cs(O){if(!tn)return;le===0&&(le=O);let st=(O-le)/1e3;le=O,st>.05&&(st=.05),K(st),Mt=window.requestAnimationFrame(Cs)}function Es(){a===\"stub\"&&(tn||(tn=!0,le=0,Mt=window.requestAnimationFrame(Cs)))}function _s(){tn=!1,Mt&&window.cancelAnimationFrame(Mt),Mt=0}function Ii(O){O.width=r*je,O.height=c*je,O.style.width=r+\"px\",O.style.height=c+\"px\";let st=O.getContext(\"2d\",{willReadFrequently:!0});return st.setTransform(je,0,0,je,0,0),st}async function Mi(){let O=Ii(t);return Rt=O,await ft(O,()=>t),ce(t),o===\"evidence\"?b(`evidence \\u5C31\\u7EEA seed=${i} \\xB7 \\u7528 host \\u9A71\\u52A8\\uFF08stepFrames/tap\\uFF09`):(b(`seed=${i} \\xB7 \\u70B9\\u5F00\\u59CB\\uFF08\\u9996\\u6B21\\u70B9\\u51FB\\u5F00\\u58F0\\uFF09`),Es()),ae}let zn=()=>{},Jn=()=>{};function Rs(){window.__gameHostEngineInitFired=!0}function Ps(){zn()}function Vr(){}function Ic(){Jn()}function Mc(){}async function kc(O){await ft(O,()=>s.mainCanvas),ce(s.mainCanvas),zn=()=>{T&&(E+=Is*1e3,T.update(Is))},Jn=()=>{if(T){O.save(),O.setTransform(je,0,0,je,0,0);try{O.imageSmoothingEnabled=!1}catch{}T.render(O),O.restore()}},s.mainCanvas&&(s.mainCanvas.id=\"game-engine\"),window.__engine={frame(){return s.frame},time(){return s.time},timeReal(){return s.timeReal},paused(){return s.paused},timeScale(){return s.timeScale},snapshot(){return{frame:s.frame,time:s.time,timeReal:s.timeReal,paused:s.paused,timeScale:s.timeScale}}},b(`engine RAF running \\xB7 seed=${i} engine=real`)}if(a!==\"real\")return await Mi();if(!s){let O=\"real \\u901A\\u9053\\u672A\\u6CE8\\u5165\\u5F15\\u64CE\\uFF08opts.engine \\u7F3A\\u5931\\uFF09\\uFF1A\\u8BF7\\u7ECF entry \\u5305\\u88C5\\u5C42\\u542F\\u52A8\\uFF08host \\u4E0D\\u76F4\\u63A5 import \\u5F15\\u64CE\\uFF0CQ4/Option C\\uFF09\";throw b(\"ENGINE INJECT MISSING: \"+O),new Error(O)}s.setGLEnable(!1),s.setShowSplashScreen(!1),s.setDebugWatermark(!1),s.setCanvasClearColor(s.BLACK),s.setCanvasFixedSize(s.vec2(r*je,c*je));let ki=document.createElement(\"div\");return ki.id=\"engine-root\",document.body.appendChild(ki),await s.engineInit(Rs,Ps,Vr,Ic,Mc,[],ki).then(()=>kc(s.mainContext)).catch(O=>{throw window.__gameHostBootError=String(O&&O.message||O),b(\"ENGINE INIT FAILED: \"+window.__gameHostBootError),O}),ae}var Wi=class{constructor(t){let e=t||{},s=typeof e.cellSize==\"number\"&&e.cellSize>0?e.cellSize:64;this._cellSize=s,this._cells=new Map,this._entries=new Map,this._seq=0}get size(){return this._entries.size}_cellRange(t){let e=this._cellSize,s=Math.floor((t.x-t.hw)/e),i=Math.floor((t.x+t.hw)/e),o=Math.floor((t.y-t.hh)/e),a=Math.floor((t.y+t.hh)/e);return{cx0:s,cy0:o,cx1:i,cy1:a}}insert(t,e){this._entries.has(t)&&this.remove(t);let{cx0:s,cy0:i,cx1:o,cy1:a}=this._cellRange(e),r=[];for(let l=i;l<=a;l++)for(let u=s;u<=o;u++){let h=u+\",\"+l,p=this._cells.get(h);p||(p=new Set,this._cells.set(h,p)),p.add(t),r.push(h)}let c={x:e.x,y:e.y,hw:e.hw,hh:e.hh};this._entries.set(t,{id:t,box:c,seq:this._seq++,cells:r})}remove(t){let e=this._entries.get(t);if(!e)return!1;for(let s of e.cells){let i=this._cells.get(s);i&&(i.delete(t),i.size===0&&this._cells.delete(s))}return this._entries.delete(t),!0}query(t){let{cx0:e,cy0:s,cx1:i,cy1:o}=this._cellRange(t),a=new Set;for(let r=s;r<=o;r++)for(let c=e;c<=i;c++){let l=this._cells.get(c+\",\"+r);if(l)for(let u of l)a.add(u)}return this._collect(a)}queryPoint(t,e){let s=this._cellSize,i=Math.floor(t/s),o=Math.floor(e/s),a=this._cells.get(i+\",\"+o);return a?this._collect(new Set(a)):[]}clear(){this._cells.clear(),this._entries.clear(),this._seq=0}_collect(t){let e=[];for(let s of t){let i=this._entries.get(s);i&&e.push(i)}return e.sort((s,i)=>s.seq-i.seq),e.map(s=>({id:s.id,box:s.box}))}};function ta(n){let t=n||{},e=new Wi({cellSize:t.cellSize}),s=!1;return{name:\"collision\",version:\"1.0.0\",dependencies:[],spatial:e,init(o){},dispose(){s||(e.clear(),s=!0)}}}function ea(n){let t=!1;return{name:\"physics-lite\",version:\"1.0.0\",dependencies:[],init(s){},dispose(){t||(t=!0)}}}var Xc=[\"pointerdown\",\"pointermove\",\"pointerup\",\"keydown\",\"keyup\"];function kt(n){return n<0?0:n>1?1:n}var Yc=n=>(n=kt(n),n*n),$c=n=>(n=kt(n),1-(1-n)*(1-n)),Kc=n=>(n=kt(n),n<.5?2*n*n:1-Math.pow(-2*n+2,2)/2),Qc=n=>(n=kt(n),n*n*n),Zc=n=>{n=kt(n);let t=1-n;return 1-t*t*t},tl=n=>(n=kt(n),n<.5?4*n*n*n:1-Math.pow(-2*n+2,3)/2),ia=2*Math.PI/3,na=2*Math.PI/4.5,el=n=>(n=kt(n),n===0?0:n===1?1:-Math.pow(2,10*n-10)*Math.sin((n*10-10.75)*ia)),nl=n=>(n=kt(n),n===0?0:n===1?1:Math.pow(2,-10*n)*Math.sin((n*10-.75)*ia)+1),sl=n=>(n=kt(n),n===0?0:n===1?1:n<.5?-(Math.pow(2,20*n-10)*Math.sin((20*n-11.125)*na))/2:Math.pow(2,-20*n+10)*Math.sin((20*n-11.125)*na)/2+1),ks=1.70158,Ms=ks*1.525,oa=ks+1,il=n=>(n=kt(n),oa*n*n*n-ks*n*n),ol=n=>{n=kt(n);let t=n-1;return 1+oa*t*t*t+ks*t*t},rl=n=>(n=kt(n),n<.5?Math.pow(2*n,2)*((Ms+1)*2*n-Ms)/2:(Math.pow(2*n-2,2)*((Ms+1)*(n*2-2)+Ms)+2)/2),al=n=>kt(n),sa=Object.freeze({linear:al,quadIn:Yc,quadOut:$c,quadInOut:Kc,cubicIn:Qc,cubicOut:Zc,cubicInOut:tl,elasticIn:el,elasticOut:nl,elasticInOut:sl,backIn:il,backOut:ol,backInOut:rl});var cl=[\"linear\",\"quadIn\",\"quadOut\",\"quadInOut\",\"cubicIn\",\"cubicOut\",\"cubicInOut\",\"elasticIn\",\"elasticOut\",\"elasticInOut\",\"backIn\",\"backOut\",\"backInOut\"];function ll(n){let t=n&&n.easing;if(!t)return sa;let e=i=>typeof t[i]==\"function\"?o=>t[i](kt(o)):sa[i],s={};for(let i of cl)s[i]=e(i);return Object.freeze(s)}var qi=class{constructor(t,e,s){let i=s||{};this._time=e,this._windowMs=typeof i.windowMs==\"number\"&&i.windowMs>0?i.windowMs:120;let o=Array.isArray(i.types)&&i.types.length>0?i.types:Xc;this._records=new Map,this._subs=[],this._disposed=!1;for(let a of o){this._records.set(a,null);let r=t.on(a,c=>{this._records.set(a,c.tMs)});this._subs.push(r)}}consume(t,e){let s=typeof e==\"number\"?e:this._time.nowMs(),i=this._records.get(t);return i==null?!1:s-i<=this._windowMs?(this._records.set(t,null),!0):(this._records.set(t,null),!1)}peek(t,e){let s=typeof e==\"number\"?e:this._time.nowMs(),i=this._records.get(t);return i==null?!1:s-i<=this._windowMs}clear(){for(let t of this._records.keys())this._records.set(t,null)}dispose(){if(!this._disposed){for(let t of this._subs)try{t.cancel()}catch(e){}this._subs=[],this._disposed=!0}}};function ra(n){let t=n||{},e=null,s=null,i=!1;return{name:\"gamefeel\",version:\"1.0.0\",dependencies:[],get inputBuffer(){return e},get easing(){return ll(s)},init(a){let r=a.getInput();e=new qi(r,a.time,{windowMs:t.inputBufferMs,types:t.inputTypes});let c=a.getEngine();s=c?c.math:null},dispose(){i||(e&&(e.dispose(),e=null),s=null,i=!0)}}}function Gs(n,t){let e=t<0?0:t>1?1:t;switch(n&&n.kind){case\"constant\":return n.value;case\"linear\":return n.from+(n.to-n.from)*e;case\"easeInOut\":{let s=e*e*(3-2*e);return n.from+(n.to-n.from)*s}case\"decay\":{let s=n.power==null?2:n.power,i=Math.pow(1-e,s);return n.to+(n.from-n.to)*i}default:return 0}}var ul=Object.freeze({burst:Object.freeze({mode:\"burst\",count:24,angle:0,spread:Math.PI,speedMin:80,speedMax:160,lifeMin:.4,lifeMax:.8,gravity:0,drag:0,sizeCurve:{kind:\"decay\",from:6,to:0,power:1.5},alphaCurve:{kind:\"linear\",from:1,to:0}}),trail:Object.freeze({mode:\"continuous\",rate:60,angle:Math.PI,spread:.25,speedMin:20,speedMax:50,lifeMin:.2,lifeMax:.5,gravity:0,drag:1.5,sizeCurve:{kind:\"easeInOut\",from:4,to:0},alphaCurve:{kind:\"decay\",from:.8,to:0,power:2}}),drift:Object.freeze({mode:\"continuous\",rate:12,angle:Math.PI/2,spread:.6,speedMin:10,speedMax:30,lifeMin:1.5,lifeMax:3,gravity:8,drag:.5,sizeCurve:{kind:\"constant\",value:3},alphaCurve:{kind:\"decay\",from:.7,to:0,power:1}})});function hl(n){let t=ul[n];return t?Hi(t):null}function Hi(n){let t={};for(let e of Object.keys(n)){let s=n[e];t[e]=s&&typeof s==\"object\"?Hi(s):s}return t}function dl(n,t,e){let s=n.mode===\"continuous\";return{pos:{x:t,y:e},angle:n.angle,coneAngle:n.spread,emitSize:0,emitTime:s?0:1/60,emitRate:s?n.rate||0:void 0,count:s?void 0:n.count||0,particleTime:(n.lifeMin+n.lifeMax)/2,speed:(n.speedMin+n.speedMax)/2,gravityScale:n.gravity||0,damping:n.drag!=null?1-n.drag/60:1,sizeStart:Gs(n.sizeCurve,0),sizeEnd:Gs(n.sizeCurve,1),colorStart:{r:1,g:1,b:1,a:Gs(n.alphaCurve,0)},colorEnd:{r:1,g:1,b:1,a:Gs(n.alphaCurve,1)},additive:!1}}function aa(n){let t=n||{},e=null,s=null,i=null,o=new Map,a=0,r={isActive(){return!1},stop(){}},c=0,l=1,u=0,h=0,p=0,d={x:0,y:0,remain:0,dur:0,amplitude:0,frequency:0,_phase:0},y={alpha:0,remain:0,dur:0,peak:0},b={scale:1,remain:0,dur:0,amplitude:0,frequency:0},v=!1;function w(A){if(u>0)if(u-=A,u<=0)u=0,l=1;else{let T=h>0?1-u/h:1;l=p+(1-p)*T}if(d.remain>0)if(d.remain-=A,d.remain<=0)d.remain=0,d.x=0,d.y=0;else{let T=d.remain/d.dur,P=d.amplitude*T;d._phase+=d.frequency*A*Math.PI*2,d.x=Math.sin(d._phase)*P,d.y=Math.cos(d._phase*1.3)*P}if(y.remain>0&&(y.remain-=A,y.remain<=0?(y.remain=0,y.alpha=0):y.alpha=y.peak*(y.remain/y.dur)),b.remain>0)if(b.remain-=A,b.remain<=0)b.remain=0,b.scale=1;else{let T=b.remain/b.dur,P=(1-T)*b.frequency*Math.PI*2;b.scale=1+Math.sin(P)*b.amplitude*T}}function _(A){c+=1,w(A)}function S(A){let T=A!==void 0?A:e?e.getContext2d():null;if(T&&y.alpha>0){T.globalAlpha=y.alpha,T.fillStyle=\"#ffffff\";let P=T.canvas?T.canvas.width:0,I=T.canvas?T.canvas.height:0;T.fillRect(0,0,P,I),T.globalAlpha=1}}return{name:\"particles-juice\",version:\"1.0.0\",dependencies:[],init(A){var T;e=A,i=((T=A.getEngine())==null?void 0:T.particles)||null,typeof t.seed==\"number\"&&A.random.reseed(t.seed),t.autoStep!==!1&&(s=A.onFrame(P=>{_(P)}))},dispose(){v||(s&&(s.cancel(),s=null),o.forEach(A=>{try{A.stop()}catch{}}),o.clear(),l=1,u=0,d.remain=0,d.x=0,d.y=0,y.remain=0,y.alpha=0,b.remain=0,b.scale=1,v=!0)},spawnEmitter(A,T,P,I){let ut=typeof A==\"string\"?hl(A):Hi(A);if(!ut)return-1;I&&(ut=Object.assign(ut,I));let ot=a++;if(!i)return o.set(ot,r),ot;let k=i.spawnEmitter(dl(ut,T,P));return o.set(ot,k),ot},stopEmitter(A){let T=o.get(A);if(T)try{T.stop()}catch{}},step(A){_(A)},render(A){S(A)},particleCount(){return 0},emitterCount(){let A=0;for(let T of o.values())T.isActive()&&(A+=1);return A},hitStop(A,T){h=Math.max(0,A),u=h,p=T==null?0:Math.max(0,Math.min(1,T)),l=h>0?p:1},getTimeScale(){return l},shakeScreen(A,T,P){d.dur=Math.max(0,A),d.remain=d.dur,d.amplitude=Math.max(0,T),d.frequency=P==null?30:P,d._phase=0,d.x=0,d.y=0},getShakeOffset(){return{x:d.x,y:d.y}},flashScreen(A,T){y.dur=Math.max(0,A),y.remain=y.dur,y.peak=T==null?.8:Math.max(0,Math.min(1,T)),y.alpha=y.dur>0?y.peak:0},getFlashAlpha(){return y.alpha},pulseScale(A,T,P){b.dur=Math.max(0,A),b.remain=b.dur,b.amplitude=T==null?.2:T,b.frequency=P==null?4:P,b.scale=1},getPulseScale(){return b.scale},juiceSnapshot(){return{timeScale:l,shake:{x:d.x,y:d.y},flashAlpha:y.alpha,pulseScale:b.scale}},probe(){return{particles:0,emitters:o.size,dropped:0,steps:c,disposed:v}}}}function ca(n){let t=dt(n.r)/255,e=dt(n.g)/255,s=dt(n.b)/255,i=Math.max(t,e,s),o=Math.min(t,e,s),a=(i+o)/2,r=0,c=0,l=i-o;return l>1e-12&&(c=a>.5?l/(2-i-o):l/(i+o),i===t?r=(e-s)/l%6:i===e?r=(s-t)/l+2:r=(t-e)/l+4,r*=60,r<0&&(r+=360)),{h:r,s:c,l:a}}function la(n){let t=(n.h%360+360)%360,e=Ne(n.s),s=Ne(n.l);if(e<1e-12){let r=Math.round(s*255);return{r,g:r,b:r}}let i=s<.5?s*(1+e):s+e-s*e,o=2*s-i,a=t/360;return{r:Math.round(Xi(o,i,a+1/3)*255),g:Math.round(Xi(o,i,a)*255),b:Math.round(Xi(o,i,a-1/3)*255)}}function Xi(n,t,e){let s=e;return s<0&&(s+=1),s>1&&(s-=1),s<1/6?n+(t-n)*6*s:s<1/2?t:s<2/3?n+(t-n)*(2/3-s)*6:n}function dt(n){return n<0?0:n>255?255:n}function Ne(n){return n<0?0:n>1?1:n}function fl(n,t){let e=gl(n.from,t);if(e>=0&&e({r:dt(r.r),g:dt(r.g),b:dt(r.b)})),to:o}}function Yi(n,t,e){return n+(t-n)*e}function bl(n,t){let e=ca(n),s=e.h+(t.hue||0),i=Ne(e.s*(t.sat==null?1:t.sat)),o=Ne(e.l*(t.light==null?1:t.light));return la({h:s,s:i,l:o})}var xl=Object.freeze([Object.freeze([0,8,2,10]),Object.freeze([12,4,14,6]),Object.freeze([3,11,1,9]),Object.freeze([15,7,13,5])]);function ua(n){let t=n||{},e=null,s=!1,i={vignette:{enabled:$i(t.vignette&&t.vignette.enabled,!1),strength:Ki(t.vignette&&t.vignette.strength,.5)},dither:{enabled:$i(t.dither&&t.dither.enabled,!1),strength:Ki(t.dither&&t.dither.strength,.5)},scanline:{enabled:$i(t.scanline&&t.scanline.enabled,!1),strength:Ki(t.scanline&&t.scanline.strength,.5)}};function o(r){let c=r!==void 0?r:e?e.getContext2d():null;if(!c)return;let l=c.canvas?c.canvas.width:0,u=c.canvas?c.canvas.height:0;if(i.vignette.enabled&&i.vignette.strength>0&&l>0&&u>0){let h=c.createRadialGradient(l/2,u/2,Math.min(l,u)*.25,l/2,u/2,Math.max(l,u)*.75);h.addColorStop(0,\"rgba(0,0,0,0)\"),h.addColorStop(1,\"rgba(0,0,0,\"+i.vignette.strength.toFixed(3)+\")\"),c.fillStyle=h,c.fillRect(0,0,l,u)}if(i.scanline.enabled&&i.scanline.strength>0&&l>0&&u>0){c.fillStyle=\"rgba(0,0,0,\"+(i.scanline.strength*.5).toFixed(3)+\")\";for(let h=0;h0&&l>0&&u>0&&typeof c.getImageData==\"function\"&&typeof c.putImageData==\"function\"){let h=c.getImageData(0,0,l,u);vl(h,i.dither.strength),c.putImageData(h,0,0)}}return{name:\"palette-post\",version:\"1.0.0\",dependencies:[],init(r){e=r},dispose(){s||(e=null,s=!0)},applyPaletteMap(r,c){return fl(r,c)},applyPaletteMapToBuffer(r,c){return pl(r,c)},lerpPaletteMap(r,c,l){return ml(r,c,l)},shiftHsl(r,c){return bl(r,c)},rgbToHsl(r){return ca(r)},hslToRgb(r){return la(r)},setPost(r){if(r)for(let c of[\"vignette\",\"dither\",\"scanline\"])r[c]&&(typeof r[c].enabled==\"boolean\"&&(i[c].enabled=r[c].enabled),typeof r[c].strength==\"number\"&&(i[c].strength=Ne(r[c].strength)))},getPost(){return{vignette:{enabled:i.vignette.enabled,strength:i.vignette.strength},dither:{enabled:i.dither.enabled,strength:i.dither.strength},scanline:{enabled:i.scanline.enabled,strength:i.scanline.strength}}},renderPost(r){o(r)},probe(){return{disposed:s,post:this.getPost()}}}}function vl(n,t){let e=Ne(t);if(e<=0)return n;let{data:s,width:i}=n,a=255/(Math.max(2,Math.round(16-e*14))-1);for(let r=0;r+3>2)%i,l=Math.floor((r>>2)/i),u=(xl[l&3][c&3]/16-.5)*a;for(let h=0;h<3;h++){let p=s[r+h]+u,d=Math.round(p/a)*a;s[r+h]=d<0?0:d>255?255:d}}return n}function $i(n,t){return typeof n==\"boolean\"?n:t}function Ki(n,t){return typeof n==\"number\"?Ne(n):t}var Zi=Object.freeze({blip:Object.freeze({name:\"blip\",params:Object.freeze([.6,0,880,0,.03,.06,1,1.5])}),thud:Object.freeze({name:\"thud\",params:Object.freeze([.8,0,90,0,.04,.18,0,1,0,0,0,0,0,4])}),chime:Object.freeze({name:\"chime\",params:Object.freeze([.5,0,660,.02,.12,.3,1,2])})}),ha=Object.keys(Zi);function Sl(n){let t=[];return n==null||typeof n!=\"object\"?{ok:!1,errors:[\"song \\u5FC5\\u987B\\u662F\\u5BF9\\u8C61\"]}:(!Array.isArray(n.instruments)||n.instruments.length===0?t.push(\"instruments \\u5FC5\\u987B\\u662F\\u975E\\u7A7A\\u6570\\u7EC4\"):n.instruments.forEach((e,s)=>{(!Array.isArray(e)||e.some(i=>typeof i!=\"number\"||!Number.isFinite(i)))&&t.push(`instruments[${s}] \\u5FC5\\u987B\\u662F\\u6709\\u9650\\u6570\\u5B57\\u6570\\u7EC4\\uFF08ZzFX \\u53C2\\u6570\\u5305\\uFF09`)}),!Array.isArray(n.patterns)||n.patterns.length===0?t.push(\"patterns \\u5FC5\\u987B\\u662F\\u975E\\u7A7A\\u6570\\u7EC4\"):n.patterns.forEach((e,s)=>{if(!Array.isArray(e)||e.length===0){t.push(`patterns[${s}] \\u5FC5\\u987B\\u662F\\u975E\\u7A7A\\u6570\\u7EC4\\uFF08\\u81F3\\u5C11\\u4E00\\u4E2A\\u58F0\\u90E8\\u901A\\u9053\\uFF09`);return}e.forEach((i,o)=>{if(!Array.isArray(i)||i.length<2){t.push(`patterns[${s}][${o}] \\u901A\\u9053\\u81F3\\u5C11\\u542B [\\u4E50\\u5668\\u7D22\\u5F15, \\u58F0\\u50CF]`);return}if(i.some(c=>typeof c!=\"number\"||!Number.isFinite(c))){t.push(`patterns[${s}][${o}] \\u901A\\u9053\\u5FC5\\u987B\\u5168\\u4E3A\\u6709\\u9650\\u6570\\u5B57`);return}let a=i[0]|0;Array.isArray(n.instruments)&&(a<0||a>=n.instruments.length)&&t.push(`patterns[${s}][${o}] \\u4E50\\u5668\\u7D22\\u5F15 ${a} \\u8D8A\\u754C\\uFF08instruments \\u957F\\u5EA6 ${n.instruments.length}\\uFF09`);let r=i[1];(r<-1||r>1)&&t.push(`patterns[${s}][${o}] \\u58F0\\u50CF ${r} \\u8D8A\\u754C\\uFF08\\u5E94 -1..+1\\uFF09`)})}),!Array.isArray(n.sequence)||n.sequence.length===0?t.push(\"sequence \\u5FC5\\u987B\\u662F\\u975E\\u7A7A\\u6570\\u7EC4\"):n.sequence.forEach((e,s)=>{if(typeof e!=\"number\"||!Number.isInteger(e)){t.push(`sequence[${s}] \\u5FC5\\u987B\\u662F\\u6574\\u6570 pattern \\u7D22\\u5F15`);return}Array.isArray(n.patterns)&&(e<0||e>=n.patterns.length)&&t.push(`sequence[${s}] \\u7D22\\u5F15 ${e} \\u8D8A\\u754C\\uFF08patterns \\u957F\\u5EA6 ${n.patterns.length}\\uFF09`)}),n.bpm!=null&&(typeof n.bpm!=\"number\"||!(n.bpm>0))&&t.push(\"bpm \\u5FC5\\u987B\\u662F\\u6B63\\u6570\"),{ok:t.length===0,errors:t})}function he(n){return typeof n!=\"number\"||Number.isNaN(n)||n<0?0:n>1?1:n}function wl(n,t){switch(n){case\"ease-in\":return t*t;case\"ease-out\":return Math.sqrt(t);case\"constant\":return 1;default:return t}}function da(n,t){let e=he(t);return n==null||!Array.isArray(n.voices)?[]:n.voices.map(s=>{let i=String(s&&s.id!=null?s.id:\"\"),o=Array.isArray(s&&s.channelIndices)?s.channelIndices.slice():[],a=typeof s.minIntensity==\"number\"?he(s.minIntensity):0,r=typeof s.maxIntensity==\"number\"?he(s.maxIntensity):1,c=typeof s.volumeCurve==\"string\"?s.volumeCurve:\"linear\",l=typeof s.baseVolume==\"number\"?he(s.baseVolume):1;if(er)return{id:i,on:!1,volume:0,channelIndices:o};let u=r-a,h=u<=0?1:(e-a)/u,p=he(wl(c,h)*l);return{id:i,on:!0,volume:p,channelIndices:o}})}function fa(n,t,e,s){try{let i=t[0]||[],o=t[1]||i,a=Math.max(i.length,o.length);if(a===0)return null;let c=n.createBuffer(2,a,44100);c.getChannelData(0).set(i),c.getChannelData(1).set(o.length?o:i);let l=n.createBufferSource();l.buffer=c,l.loop=!!s;let u=n.createGain();return u.gain.value=he(e*.3),l.connect(u),u.connect(n.destination),l.start(),{stop(){try{l.stop()}catch(h){}try{l.disconnect(),u.disconnect()}catch(h){}}}}catch(i){return to(\"[audio-music] \\u53D7\\u63A7\\u97F3\\u9891\\u64AD\\u653E\\u5F02\\u5E38\\uFF0C\\u964D\\u7EA7\\u4E3A\\u9759\\u9ED8\",i),null}}function pa(n){let t=n||{},e=typeof t.masterVolume==\"number\"?he(t.masterVolume):1,s=()=>null,i=null,o=null,a={voices:[]},r=1,c=null,l=!1,u=0,h=!1,p=!1;function d(){let S=s();return S==null?(u+=1,h||(Qi(\"[audio-music] \\u65E0\\u53D7\\u63A7\\u97F3\\u9891\\u4E0A\\u4E0B\\u6587\\uFF08getAudioContext \\u8FD4\\u56DE null\\uFF09\\uFF0C\\u5168\\u90E8\\u53D1\\u58F0\\u9759\\u9ED8 no-op\\uFF08\\u8BC4\\u4F30\\u73AF\\u5883\\u65E0\\u58F0\\u5361\\u65F6\\u7684\\u5BB9\\u9519\\u964D\\u7EA7\\uFF09\"),h=!0),null):S}function y(){if(!o||!i)return null;try{let S=typeof o.bpm==\"number\"&&o.bpm>0?o.bpm:125;return i.synthSong(o.instruments,o.patterns,o.sequence,S)}catch(S){return to(\"[audio-music] \\u66F2\\u8C31\\u6E32\\u67D3\\u5F02\\u5E38\\uFF0C\\u964D\\u7EA7\\u4E3A\\u9759\\u9ED8\",S),null}}function b(){let S=da(a,r);if(S.length===0)return 1;let E=0;for(let A of S)A.on&&(E=Math.max(E,A.volume));return E}function v(S,E){typeof E==\"number\"&&(r=he(E));let A=d();if(A==null||!o)return;w();let T=y();if(!T)return;let P=he(e*b());c=fa(A,T,P,S),l=!!S&&c!=null}function w(){c&&(c.stop(),c=null),l=!1}return{name:\"audio-music\",version:\"1.0.0\",dependencies:[],init(S){s=()=>S.getAudioContext();let E=S.getEngine&&S.getEngine();i=E?E.audio.synth:null},dispose(){p||(w(),p=!0)},loadSong(S){let E=Sl(S);return E.ok?o={instruments:S.instruments,patterns:S.patterns,sequence:S.sequence,bpm:typeof S.bpm==\"number\"?S.bpm:125}:Qi(\"[audio-music] \\u66F2\\u8C31\\u6821\\u9A8C\\u5931\\u8D25\\uFF0C\\u672A\\u8F7D\\u5165\\uFF1A\"+E.errors.join(\"\\uFF1B\")),E},play(S){v(!1,S)},stop(){w()},loop(S){v(!0,S)},setIntensityLayering(S){a=S&&Array.isArray(S.voices)?{voices:S.voices}:{voices:[]}},setIntensity(S){r=he(S)},resolveVoices(S){return da(a,S)},playSfx(S){let E=Zi[S];if(!E){Qi(\"[audio-music] \\u672A\\u77E5\\u97F3\\u6548\\u9884\\u8BBE\\uFF1A\"+String(S));return}let A=d();if(A!=null)try{let T=i?i.synthSfx(E.params):null;if(!T)return;fa(A,[T,T],e,!1)}catch(T){to(\"[audio-music] \\u97F3\\u6548\\u64AD\\u653E\\u5F02\\u5E38\\uFF0C\\u964D\\u7EA7\\u4E3A\\u9759\\u9ED8 name=\"+S,T)}},getPreset(S){return Zi[S]},listPresets(){return ha.slice()},probe(){return{hasAudio:s()!=null,hasSong:o!=null,intensity:r,voiceCount:a.voices.length,looping:l,silentNoops:u,presetCount:ha.length}}}}function to(n,t){let e=t&&t.stack?t.stack:t&&t.message?t.message:t;typeof console!=\"undefined\"&&typeof console.error==\"function\"&&console.error(n+(e?\"\\uFF1A\"+e:\"\"))}function Qi(n){typeof console!=\"undefined\"&&typeof console.warn==\"function\"&&console.warn(n)}var Tl=\"zaomeng.save\";function Al(n){if(typeof TextEncoder!=\"undefined\")return new TextEncoder().encode(n).length;if(typeof Buffer!=\"undefined\"&&typeof Buffer.byteLength==\"function\")return Buffer.byteLength(n,\"utf8\");let t=0;for(let e=0;e=55296&&s<=56319?(t+=4,e++):t+=3}return t}function no(){let n=new Map;return{getItem(t){return n.has(t)?n.get(t):null},setItem(t,e){n.set(t,e)},removeItem(t){n.delete(t)},keys(){return Array.from(n.keys())}}}function ga(){if(typeof localStorage==\"undefined\")throw new Error(\"[save-progress] createLocalStorageAdapter \\u9700\\u8981\\u5168\\u5C40 localStorage\\uFF08\\u4EC5\\u6D4F\\u89C8\\u5668\\u96C6\\u6210\\u6BB5\\u53EF\\u7528\\uFF09\\u3002node/host-dev \\u8BF7\\u7528\\u7F3A\\u7701\\u5185\\u5B58 adapter \\u6216 createMemoryAdapter()\\u3002\");return{getItem(n){return localStorage.getItem(n)},setItem(n,t){localStorage.setItem(n,t)},removeItem(n){localStorage.removeItem(n)},keys(){let n=[];for(let t=0;t0?t.namespace:Tl;if(e.indexOf(\":\")>=0)throw new Error('[save-progress] namespace \\u4E0D\\u5F97\\u542B\\u5206\\u9694\\u7B26 \":\"\\uFF08\\u5F53\\u524D=\"'+e+'\"\\uFF09');let s=typeof t.maxValueBytes==\"number\"&&t.maxValueBytes>0?Math.floor(t.maxValueBytes):32768,i=!Cl(t.adapter),o=i?no():t.adapter,a=e+\":\",r=0,c=0,l=!1;function u(d){return typeof d!=\"string\"||d.length===0?a+String(d):a+d}function h(){try{let d=o.keys();return Array.isArray(d)?d.filter(y=>typeof y==\"string\"&&y.indexOf(a)===0):[]}catch(d){return Vn(\"[save-progress] adapter.keys() \\u5F02\\u5E38\\uFF0C\\u964D\\u7EA7\\u8FD4\\u56DE\\u7A7A\\u96C6\",d),[]}}return{name:\"save-progress\",version:\"1.0.0\",dependencies:[],init(d){h()},dispose(){},get(d,y){if(!eo(d))return y;let b;try{b=o.getItem(u(d))}catch(v){return Vn(\"[save-progress] adapter.getItem \\u5F02\\u5E38\\uFF0C\\u8FD4\\u56DE\\u9ED8\\u8BA4\\u503C key=\"+d,v),y}if(b==null)return y;try{return JSON.parse(b)}catch{return r+=1,l||(Un(\"[save-progress] \\u68C0\\u6D4B\\u5230\\u635F\\u574F\\u5B58\\u6863\\uFF08\\u975E\\u6CD5 JSON\\uFF09\\uFF0C\\u5DF2\\u5BB9\\u9519\\u56DE\\u9000\\u9ED8\\u8BA4\\u503C key=\"+d),l=!0),y}},set(d,y){if(!eo(d))return Un(\"[save-progress] set \\u6536\\u5230\\u975E\\u6CD5 key\\uFF0C\\u62D2\\u7EDD\\u5199\\u5165\\uFF1A\"+String(d)),!1;let b;try{b=JSON.stringify(y)}catch{return c+=1,Un(\"[save-progress] \\u503C\\u4E0D\\u53EF JSON \\u5E8F\\u5217\\u5316\\uFF08\\u5FAA\\u73AF\\u5F15\\u7528?\\uFF09\\uFF0C\\u62D2\\u7EDD\\u5199\\u5165 key=\"+d),!1}if(b===void 0)return c+=1,Un(\"[save-progress] \\u503C\\u5E8F\\u5217\\u5316\\u4E3A undefined\\uFF08\\u5982\\u7EAF undefined/\\u51FD\\u6570\\uFF09\\uFF0C\\u62D2\\u7EDD\\u5199\\u5165 key=\"+d),!1;let v=Al(b);if(v>s)return c+=1,Un(\"[save-progress] \\u503C\\u8D85\\u5B57\\u8282\\u4E0A\\u9650\\u62D2\\u7EDD\\u5199\\u5165 key=\"+d+\"\\uFF08\"+v+\" > \"+s+\" bytes\\uFF09\"),!1;try{return o.setItem(u(d),b),!0}catch(w){return c+=1,Vn(\"[save-progress] adapter.setItem \\u5F02\\u5E38\\uFF0C\\u5199\\u5165\\u5931\\u8D25 key=\"+d,w),!1}},remove(d){if(eo(d))try{o.removeItem(u(d))}catch(y){Vn(\"[save-progress] adapter.removeItem \\u5F02\\u5E38\\uFF08\\u5DF2\\u5FFD\\u7565\\uFF09key=\"+d,y)}},clear(){let d=h(),y=0;for(let b of d)try{o.removeItem(b),y+=1}catch(v){Vn(\"[save-progress] clear \\u65F6 removeItem \\u5F02\\u5E38\\uFF08\\u5DF2\\u8DF3\\u8FC7\\uFF09fullKey=\"+b,v)}return y},probe(){return{namespace:e,maxValueBytes:s,usingMemoryAdapter:i,size:h().length,corruptRecoveries:r,rejectedWrites:c}}}}function eo(n){return typeof n==\"string\"&&n.length>0}function Cl(n){return!!n&&typeof n.getItem==\"function\"&&typeof n.setItem==\"function\"&&typeof n.removeItem==\"function\"&&typeof n.keys==\"function\"}function Vn(n,t){let e=t&&t.stack?t.stack:t&&t.message?t.message:t;typeof console!=\"undefined\"&&typeof console.error==\"function\"&&console.error(n+(e?\"\\uFF1A\"+e:\"\"))}function Un(n){typeof console!=\"undefined\"&&typeof console.warn==\"function\"&&console.warn(n)}var El={w:390,h:844};function ma(n){let t=((n&&n.seed)!=null?n.seed:0)>>>0,e=ta({cellSize:96}),s=ea(),i=ra({inputBufferMs:260,inputTypes:[\"pointerdown\"]}),o=aa({seed:t,maxParticles:600}),a=ua({vignette:{enabled:!0,strength:.22}}),r=pa({masterVolume:.8}),c;try{c=ga()}catch(p){console.warn(\"[generic] localStorage \\u4E0D\\u53EF\\u7528\\uFF0C\\u5B58\\u6863\\u9000\\u5185\\u5B58 adapter\\uFF1A\"+(p&&p.message)),c=no()}let l=ya({namespace:\"zaomeng-generic\",adapter:c});return{plugins:{collision:e,physics:s,gamefeel:i,juice:o,palettePost:a,audioMusic:r,save:l},registerOrder:[e,s,i,o,a,l,r],viewport:El,buildFactoryOpts:(p,d)=>({runtime:{plugins:d.plugins,bundle:d.bundle,getRealAudioCtx:d.getRealAudioCtx}})}}var _l=new Set([\"hit-nearest-target\"]);function Rl(n,t,e){if(typeof n!=\"string\")return!1;let s=n.trim().split(/\\s+/);if(s.length!==3)return!1;let[i,o,a]=s,r=t[i];if(typeof r!=\"number\")return!1;let c=a.startsWith(\"config.\")?e[a.slice(7)]:Number(a);if(typeof c!=\"number\"||Number.isNaN(c))return!1;switch(o){case\">=\":return r>=c;case\">\":return r>c;case\"==\":return r===c;case\"<=\":return r<=c;case\"<\":return r[b.id,b])),h=r[0],p=h&&Array.isArray(h.entityRefs)?new Set(h.entityRefs):null,d=p?o.filter(b=>p.has(b.id)):o;function y(b){let v=Array.isArray(b.components)?b.components:[];for(let w of v){let _=u.get(w);if(_&&_.kind===\"render\")return _}return null}return function(){let v=null,w=null,_=0,S=[],E={phase:\"booting\",score:0,result:null,t:0};function A(k){v=k.ctx,S=d.map(G=>{let et=y(G)||{},ft=G.transform&&G.transform.position||{x:0,y:0};return{id:G.id,x:ft.x,y:ft.y,w:typeof et.w==\"number\"?et.w:48,h:typeof et.h==\"number\"?et.h:48,color:typeof et.color==\"string\"?et.color:\"#3cf\",hit:!1}}),w=v.getInput().on(\"pointerdown\",T),E.phase=\"playing\"}function T(k){if(E.phase!==\"playing\")return;_=v.time.nowMs();let G=k.x||0,et=k.y||0;for(let ft of l)if(!(ft.trigger!==\"input\"||!_l.has(ft.op))&&ft.op===\"hit-nearest-target\"){let Rt=typeof s.hitRadius==\"number\"?s.hitRadius:60,K=null,At=Rt*Rt;for(let ht of S){if(ht.hit)continue;let ae=ht.x+ht.w/2,ce=ht.y+ht.h/2,Mt=(ae-G)*(ae-G)+(ce-et)*(ce-et);Mt<=At&&(K=ht,At=Mt)}if(K){K.hit=!0,E.score+=1;let ht=v.getEngine&&v.getEngine();if(ht){let ae=K.x+K.w/2,ce=K.y+K.h/2;if(ht.particles)try{ht.particles.spawnEmitter({pos:{x:ae,y:ce},count:20,speed:120,particleTime:.5,colorStart:{r:1,g:.8,b:.2,a:1},colorEnd:{r:1,g:.3,b:0,a:0}})}catch{}if(ht.audio&&ht.audio.synth)try{ht.audio.synth.synthSfx([1,.05,260,,,.15,,1.3])}catch{}}}}}function P(k){if(v&&(_=v.time.nowMs(),E.t=_/1e3),E.phase===\"playing\"){for(let G of c)if(Rl(G.condition,E,s)&&(G.outcome===\"win\"||G.outcome===\"lose\")){E.phase=\"gameover\",E.result=G.outcome;break}}}function I(k){k.fillStyle=\"#0d1117\",k.fillRect(0,0,390,844);let G=.5+.5*Math.sin(E.t*2);k.fillStyle=\"rgba(80,160,255,\"+(.25+.5*G).toFixed(3)+\")\",k.fillRect(0,0,390,6);for(let ft of S)k.fillStyle=ft.hit?\"#2a3340\":ft.color,k.fillRect(ft.x,ft.y,ft.w,ft.h);let et=typeof s.winScore==\"number\"?s.winScore:S.length;k.fillStyle=\"#cdd9e5\",k.font=\"20px monospace\",k.fillText(\"Score: \"+E.score+\"/\"+et,20,40),E.phase===\"gameover\"&&(k.fillStyle=E.result===\"win\"?\"#4ade80\":\"#f87171\",k.font=\"32px monospace\",k.fillText(E.result===\"win\"?\"YOU WIN\":\"GAME OVER\",110,430))}function ut(){return{seed:1,state:()=>{let k=typeof s.winScore==\"number\"?s.winScore:S.length;return{phase:E.phase,score:E.score,result:E.result,progress:k?E.score/k:0,tickModel:i.tickModel||null,targets:S.map((G,et)=>({idx:et,x:G.x+G.w/2,y:G.y+G.h/2,occupied:G.hit}))}}}}function ot(){try{w&&w.cancel()}catch{}w=null,S=[]}return{init:A,update:P,render:I,_forensicsView:ut,destroy:ot}}}var Pl={schemaVersion:\"1.0\",sourceHash:\"0000000000000000000000000000000000000000000000000000000000000000\",profile:{tickModel:\"event\",inputModel:\"discrete-choice\",progressModel:\"metric\"},gameDefinition:{entities:[{id:\"t1\",transform:{position:{x:80,y:200}},components:[\"r-target\"]},{id:\"t2\",transform:{position:{x:170,y:420}},components:[\"r-target\"]},{id:\"t3\",transform:{position:{x:260,y:620}},components:[\"r-target\"]}],components:[{id:\"r-target\",kind:\"render\",shape:\"rect\",w:64,h:64,color:\"#ffcc33\"}],behaviors:[{id:\"b-tap\",trigger:\"input\",op:\"hit-nearest-target\"}],scenes:[{id:\"s1\",entityRefs:[\"t1\",\"t2\",\"t3\"]}],rules:[{id:\"win\",condition:\"score >= config.winScore\",outcome:\"win\"}]},config:{winScore:3,hitRadius:60}},Fs=ba(Pl);var bd=Fs,xd=Fs;async function vd(n){let t=n||{},e=t.engine||await Promise.resolve().then(()=>(Dc(),Oc)),s=(t.seed!=null?t.seed:305441741)>>>0,i=ma({seed:s});return Zr({canvas:t.canvas,statusEl:t.statusEl||null,engine:e,seed:s,mode:t.mode||\"play\",engineMode:\"real\",viewport:i.viewport,factory:Fs,plugins:i.plugins,registerOrder:i.registerOrder,buildFactoryOpts:i.buildFactoryOpts,recHook:t.recHook||null,onReady:t.onReady||null})}return zc(Sd);})();\n","provenance":{"model":"adapter-probe","traceId":"local-preview-9001"}}
\ No newline at end of file
diff --git a/game-studio/public/mock-manifests/run_u5u2.sh b/game-studio/public/mock-manifests/run_u5u2.sh
deleted file mode 100644
index 53622630..00000000
--- a/game-studio/public/mock-manifests/run_u5u2.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env bash
-# U5+U2 真 UI 走查 runbook(mini-desktop 本地执行)。
-# chrome 生命周期:起 headless chrome(:9222) → 探活 → 跑 python 走查 → 杀 chrome 净场。
-# 自身输出重定向到 /tmp/u5u2_out.txt(CDP §2 坑5:避免继承 ssh fd 被断连杀;本次一次性同步跑,输出兜底落文件)。
-set -u
-exec >/tmp/u5u2_out.txt 2>&1
-echo "=== RUN_U5U2 START $(date -Is) ==="
-
-SHOT=/tmp/u5u2
-mkdir -p "$SHOT"
-CHROME=/usr/bin/google-chrome
-PROF=/tmp/u5u2_chrome_prof
-rm -rf "$PROF"; mkdir -p "$PROF"
-
-# 先清掉可能残留的 9222 chrome
-pkill -f "remote-debugging-port=9222" 2>/dev/null || true
-sleep 1
-
-# 起 headless chrome(CDP §2:no-sandbox + no-zygote + remote-allow-origins=*)
-"$CHROME" \
-  --headless=new --no-sandbox --no-zygote \
-  --remote-debugging-port=9222 --remote-allow-origins=* \
-  --user-data-dir="$PROF" \
-  --window-size=420,900 \
-  --autoplay-policy=no-user-gesture-required \
-  about:blank >/tmp/u5u2_chrome.log 2>&1 &
-CHROME_PID=$!
-echo "chrome pid=$CHROME_PID"
-
-# 探活:起后必 curl /json/version 验活再用(最多 ~10s)
-UP=0
-for i in $(seq 1 20); do
-  if curl -s --max-time 2 http://localhost:9222/json/version >/dev/null 2>&1; then
-    UP=1; echo "chrome :9222 UP after ${i} tries"; break
-  fi
-  sleep 0.5
-done
-if [ "$UP" != "1" ]; then
-  echo "FATAL: chrome :9222 not up"; cat /tmp/u5u2_chrome.log; kill "$CHROME_PID" 2>/dev/null; exit 1
-fi
-curl -s --max-time 3 http://localhost:9222/json/version | head -c 200; echo
-
-# 跑走查
-echo "=== PYTHON WALK ==="
-python3 /tmp/u5u2_walk.py
-RC=$?
-echo "=== PYTHON RC=$RC ==="
-
-# 净场
-kill "$CHROME_PID" 2>/dev/null || true
-pkill -f "remote-debugging-port=9222" 2>/dev/null || true
-echo "=== screenshots ==="
-ls -la "$SHOT"
-echo "=== RUN_U5U2 DONE $(date -Is) rc=$RC ==="
diff --git a/game-studio/public/mock-manifests/u5u2_walk.py b/game-studio/public/mock-manifests/u5u2_walk.py
deleted file mode 100644
index 481b1614..00000000
--- a/game-studio/public/mock-manifests/u5u2_walk.py
+++ /dev/null
@@ -1,392 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-U5(8 页走查) + U2(审核→发布) 真 UI 走查 —— 单文件 CDP 客户端(websocket-client 直驱)。
-在 mini-desktop 本地驱动 headless chrome(:9222) 走 localhost 前端(staging-ops §1 认可的 e2e 门):
-
-U5 studio(:4173) 6 页:feed流 / 试玩(play) / 创作(create,draft) / 创作者主页(creator) / 素材中心(material) / 玩法中心(template)
-U5 admin (:4174) 2 页:经营看板(operation-dashboard) / 审核队列(review)
-U2 admin 审核→发布:审核队列点「通过」approve 真实待审游戏(已 seed 9324 saa-e2e-breakout) → 验转发布态。
-
-逐页断言(每页落 PAGE_VERDICT):
-  - 关键元素渲染(DOM 实查 selector)
-  - 控制台零未捕获异常(Runtime.exceptionThrown / console.error 累计)
-  - 零 5xx(Network.responseReceived 累计状态码 >=500)
-  - 真数据(非空壳:列表/卡片/指标计数 > 0)
-鉴权:studio 受保护页用 localStorage 注入 test1 token(key=wanxiang_token,见 store/user.ts)过 requiresAuth 守卫(只读走查口径);
-      tenant-id=1 由前端请求拦截器无条件注入,无需手动设。admin 走真实登录(芋道源码/admin/admin123)。
-不改后端/DB(seed 已在脚本外经真实 publish 路径完成),仅 UI 走查取证。
-"""
-import json, base64, time, sys, urllib.request
-import websocket  # websocket-client
-
-CDP_HTTP = "http://localhost:9222"
-STUDIO = "http://localhost:4173"
-ADMIN = "http://localhost:4174"
-SHOT_DIR = "/tmp/u5u2"
-STUDIO_TOKEN = "test1"
-
-# U2 seed: 经真实 publish 路径已置 9324(saa-e2e-breakout) 为 status=1 审核中
-U2_TARGET_NAME = "saa-e2e-breakout"
-U2_TARGET_ID = "9324"
-
-import os
-os.makedirs(SHOT_DIR, exist_ok=True)
-
-
-def new_target(url="about:blank"):
-    """Chrome 111+ 必须用 PUT /json/new 建 target。"""
-    req = urllib.request.Request(f"{CDP_HTTP}/json/new?{url}", method="PUT")
-    d = json.load(urllib.request.urlopen(req, timeout=10))
-    return d["webSocketDebuggerUrl"], d["id"]
-
-
-class CDP:
-    def __init__(self, ws_url):
-        self.ws = websocket.create_connection(ws_url, max_size=None, timeout=45,
-                                              suppress_origin=True)
-        self._id = 0
-        self.console = []   # 累积 console error / exception
-        self.http5xx = []   # 累积 5xx 响应
-        self.http4xx = []   # 累积 4xx 响应(参考,不计入 pass)
-
-    def _collect(self, msg):
-        m = msg.get("method")
-        if m == "Log.entryAdded":
-            e = msg["params"]["entry"]
-            if e.get("level") == "error":
-                self.console.append({"src": "log", "level": "error", "text": (e.get("text") or "")[:240]})
-        elif m == "Runtime.consoleAPICalled":
-            t = msg["params"].get("type")
-            if t == "error":
-                args = msg["params"].get("args", [])
-                txt = " ".join(str(a.get("value", a.get("description", ""))) for a in args)[:240]
-                self.console.append({"src": "console", "level": "error", "text": txt})
-        elif m == "Runtime.exceptionThrown":
-            d = msg["params"].get("exceptionDetails", {})
-            self.console.append({"src": "exception", "level": "error",
-                                 "text": (d.get("text") or json.dumps(d.get("exception", {}), ensure_ascii=False))[:240]})
-        elif m == "Network.responseReceived":
-            r = msg["params"].get("response", {})
-            st = r.get("status", 0)
-            url = (r.get("url") or "")[:160]
-            if st >= 500:
-                self.http5xx.append({"status": st, "url": url})
-            elif 400 <= st < 500:
-                self.http4xx.append({"status": st, "url": url})
-
-    def send(self, method, params=None, timeout=45):
-        self._id += 1
-        mid = self._id
-        self.ws.send(json.dumps({"id": mid, "method": method, "params": params or {}}))
-        end = time.time() + timeout
-        while time.time() < end:
-            self.ws.settimeout(max(0.1, end - time.time()))
-            try:
-                msg = json.loads(self.ws.recv())
-            except Exception:
-                continue
-            self._collect(msg)
-            if msg.get("id") == mid:
-                if "error" in msg:
-                    raise RuntimeError(f"{method}: {msg['error']}")
-                return msg.get("result", {})
-        raise TimeoutError(method)
-
-    def drain(self, secs=1.0):
-        end = time.time() + secs
-        while time.time() < end:
-            self.ws.settimeout(max(0.05, end - time.time()))
-            try:
-                msg = json.loads(self.ws.recv())
-            except Exception:
-                continue
-            self._collect(msg)
-
-    def evaljs(self, expr, timeout=45):
-        r = self.send("Runtime.evaluate",
-                      {"expression": expr, "returnByValue": True, "awaitPromise": True}, timeout)
-        if "exceptionDetails" in r:
-            return {"__js_error__": json.dumps(r.get("exceptionDetails"), ensure_ascii=False)[:300]}
-        return r.get("result", {}).get("value")
-
-    def nav(self, url, settle=5):
-        self.send("Page.navigate", {"url": url})
-        time.sleep(settle)
-        self.drain(0.8)
-
-    def shot(self, name):
-        r = self.send("Page.captureScreenshot", {"format": "png"})
-        path = f"{SHOT_DIR}/{name}"
-        with open(path, "wb") as f:
-            f.write(base64.b64decode(r["data"]))
-        return path
-
-    def reset_net_log(self):
-        """每页走查前清网络/console 累计,使断言只针对当前页。"""
-        self.console = []
-        self.http5xx = []
-        self.http4xx = []
-
-    def close(self):
-        try:
-            self.ws.close()
-        except Exception:
-            pass
-
-
-# ---------- JS 片段 ----------
-
-# 注入 studio 登录态(test1):必须在 :4173 同 origin 已开 target 内 setItem(localStorage 按 origin 隔离)
-def js_inject_studio_token():
-    return ("(()=>{try{"
-            f"localStorage.setItem('wanxiang_token','{STUDIO_TOKEN}');"
-            f"localStorage.setItem('wanxiang_user_id','1');"
-            "localStorage.setItem('wanxiang_creator_flag','1');"
-            "localStorage.setItem('wanxiang_nickname','走查创作者');"
-            "return 'token-set:'+localStorage.getItem('wanxiang_token');"
-            "}catch(e){return 'err:'+e.message}})()")
-
-# 通用页面信息 dump
-JS_DUMP = r"""(()=>{
-  const btns=[...document.querySelectorAll('button')].map(e=>(e.innerText||'').replace(/\s+/g,'')).filter(Boolean).slice(0,30);
-  return {href:location.href, title:document.title, buttons:btns,
-          bodyText:(document.body.innerText||'').replace(/\s+/g,' ').slice(0,400)};
-})()"""
-
-# 计数若干 selector 命中数(真数据/渲染断言用);返回 {sel:count}
-def js_count(selectors):
-    arr = json.dumps(selectors)
-    return (r"""(()=>{const sels=%s;const o={};
-      for(const s of sels){try{o[s]=document.querySelectorAll(s).length}catch(e){o[s]=-1}}
-      o.__href=location.href; o.__bodyLen=(document.body.innerText||'').replace(/\s+/g,'').length;
-      return o;})()""" % arr)
-
-# admin el-table 行 dump(审核队列用)
-JS_QUEUE = r"""(()=>{
-  const rows=[...document.querySelectorAll('.el-table__row')].map(r=>{
-    const cells=[...r.querySelectorAll('td')].map(td=>(td.innerText||'').trim().replace(/\s+/g,' ').slice(0,40));
-    const btns=[...r.querySelectorAll('button')].map(b=>(b.innerText||'').replace(/\s+/g,''));
-    return {cells, btns};
-  });
-  const empty=document.querySelector('.el-table__empty-text');
-  return {href:location.href, rowCount:rows.length, rows:rows.slice(0,8),
-          emptyText:empty?(empty.innerText||'').trim():'',
-          bodyText:(document.body.innerText||'').replace(/\s+/g,' ').slice(0,260)};
-})()"""
-
-JS_DUMP_LOGIN = r"""(()=>{
-  const inp=[...document.querySelectorAll('input')].map(e=>({type:e.type,ph:e.placeholder||''}));
-  const btn=[...document.querySelectorAll('button')].map(e=>(e.innerText||'').replace(/\s+/g,'')).filter(Boolean);
-  return {href:location.href, inputs:inp, buttons:btn};
-})()"""
-
-JS_CLICK_LOGIN = r"""(()=>{
-  const b=[...document.querySelectorAll('button')].find(e=>(e.innerText||'').replace(/\s+/g,'')==='登录');
-  if(b){b.click();return 'clicked-login';}
-  return 'no-login-btn';
-})()"""
-
-def js_click_pass(name):
-    return r"""(()=>{
-      const rows=[...document.querySelectorAll('.el-table__row')];
-      for(const r of rows){
-        if((r.innerText||'').includes('%s')){
-          const p=[...r.querySelectorAll('button')].find(b=>(b.innerText||'').replace(/\s+/g,'')==='通过');
-          if(p){p.click();return 'clicked-pass';}
-          return 'row-matched-no-pass-btn|btns:'+[...r.querySelectorAll('button')].map(b=>(b.innerText||'').replace(/\s+/g,'')).join(',');
-        }
-      }
-      return 'target-not-in-rows|count:'+rows.length;
-    })()""" % name
-
-JS_CONFIRM = r"""(()=>{
-  const btns=[...document.querySelectorAll('.el-message-box button,.el-overlay button,.el-dialog button')];
-  const ok=btns.find(b=>{const t=(b.innerText||'').replace(/\s+/g,'');return t==='确定'||t==='确认';});
-  if(ok){ok.click();return 'confirmed';}
-  return 'no-confirm|btns:'+btns.map(b=>(b.innerText||'').replace(/\s+/g,'')).join(',');
-})()"""
-
-
-def out(tag, obj):
-    print(f"@@{tag}@@ " + json.dumps(obj, ensure_ascii=False), flush=True)
-
-
-def page_verdict(c, page, counts, render_ok, data_ok, extra=None):
-    """汇总单页断言:渲染 / console 零异常 / 零 5xx / 真数据。"""
-    v = {
-        "page": page,
-        "url": counts.get("__href", ""),
-        "render_ok": render_ok,
-        "console_errors": c.console[:8],
-        "console_error_count": len(c.console),
-        "http5xx": c.http5xx[:6],
-        "http5xx_count": len(c.http5xx),
-        "data_ok": data_ok,
-        "counts": {k: v2 for k, v2 in counts.items() if not k.startswith("__")},
-        "pass": bool(render_ok and data_ok and len(c.console) == 0 and len(c.http5xx) == 0),
-    }
-    if extra:
-        v["extra"] = extra
-    out("PAGE_VERDICT", v)
-    return v
-
-
-def main():
-    ws_url, tid = new_target("about:blank")
-    c = CDP(ws_url)
-    c.send("Page.enable")
-    c.send("Runtime.enable")
-    c.send("Log.enable")
-    c.send("Network.enable")
-
-    verdicts = []
-
-    # ======================================================================
-    #  STUDIO 段:先建 :4173 origin 注入 token,再逐页走
-    # ======================================================================
-    c.nav(f"{STUDIO}/feed", settle=6)  # 先到 feed(无需登录)建立 origin
-    print("INJECT_TOKEN:", c.evaljs(js_inject_studio_token()), flush=True)
-
-    # ---- U5-1 feed 流 ----
-    c.reset_net_log()
-    c.nav(f"{STUDIO}/feed", settle=7)
-    c.shot("u5_1_feed.png")
-    counts = c.evaljs(js_count([".feed", ".feed__zone", ".feed__page", ".game-card,[class*=card]"]))
-    out("FEED_DUMP", c.evaljs(JS_DUMP))
-    render = (counts.get(".feed", 0) > 0)
-    data = (counts.get(".feed__page", 0) > 0)  # 至少一张卡片页
-    verdicts.append(page_verdict(c, "studio/feed", counts, render, data))
-
-    # ---- U5-2 试玩 play(已发布游戏 9306/93112) ----
-    c.reset_net_log()
-    c.nav(f"{STUDIO}/play/9306/93112", settle=9)  # 试玩页含 iframe 宿主,留足加载时间
-    c.shot("u5_2_play.png")
-    counts = c.evaljs(js_count([".play,[class*=play]", "iframe", ".loading-bar,[class*=loading]", "[class*=error]"]))
-    out("PLAY_DUMP", c.evaljs(JS_DUMP))
-    # 渲染=有 iframe 宿主挂载;真数据=非错误态(无 error 容器主导) + iframe 在
-    render = (counts.get("iframe", 0) > 0)
-    data = (counts.get("iframe", 0) > 0)
-    verdicts.append(page_verdict(c, "studio/play", counts, render, data,
-                                 extra={"note": "试玩=GamePlayer iframe 宿主挂载;游戏内真玩另见 game-e2e harness"}))
-
-    # ---- U5-3 创作 create(draft,requiresAuth) ----
-    c.reset_net_log()
-    c.nav(f"{STUDIO}/create", settle=7)
-    c.shot("u5_3_create.png")
-    counts = c.evaljs(js_count([".create-page", ".create-head", "textarea", "button"]))
-    out("CREATE_DUMP", c.evaljs(JS_DUMP))
-    href = counts.get("__href", "")
-    render = (counts.get(".create-page", 0) > 0) and ("/login" not in href)
-    data = (counts.get("textarea", 0) > 0)  # 创作输入框在=页面真渲染(非被守卫弹回登录)
-    verdicts.append(page_verdict(c, "studio/create", counts, render, data,
-                                 extra={"guard_bounced_to_login": "/login" in href}))
-
-    # ---- U5-4 创作者主页 creator/1(PUBLIC,9 作品) ----
-    c.reset_net_log()
-    c.nav(f"{STUDIO}/creator/1", settle=7)
-    c.shot("u5_4_creator.png")
-    counts = c.evaljs(js_count([".creator", ".creator__stats", ".creator__stat",
-                                ".game-card,[class*=card],[class*=work]", "[class*=empty]"]))
-    out("CREATOR_DUMP", c.evaljs(JS_DUMP))
-    render = (counts.get(".creator", 0) > 0)
-    data = (counts.get(".creator__stat", 0) > 0)  # 统计卡(发布数/播放/点赞)渲染=真数据
-    verdicts.append(page_verdict(c, "studio/creator", counts, render, data))
-
-    # ---- U5-5 素材中心 material(requiresAuth) ----
-    c.reset_net_log()
-    c.nav(f"{STUDIO}/studio/material", settle=7)
-    c.shot("u5_5_material.png")
-    counts = c.evaljs(js_count([".material", ".material__head", ".material__tabs",
-                                ".material__upload", "[class*=empty]", "[class*=asset],[class*=grid] img,[class*=item]"]))
-    out("MATERIAL_DUMP", c.evaljs(JS_DUMP))
-    href = counts.get("__href", "")
-    render = (counts.get(".material", 0) > 0) and ("/login" not in href)
-    # 素材库可能为空(空态=合法非空壳);真渲染=头部+上传入口在
-    data = (counts.get(".material__head", 0) > 0 and counts.get(".material__upload", 0) > 0)
-    verdicts.append(page_verdict(c, "studio/material", counts, render, data,
-                                 extra={"guard_bounced_to_login": "/login" in href,
-                                        "note": "素材库空态合法;断言取头部+上传入口渲染"}))
-
-    # ---- U5-6 玩法中心 template(requiresAuth,5 品类) ----
-    c.reset_net_log()
-    c.nav(f"{STUDIO}/studio/template", settle=7)
-    c.shot("u5_6_template.png")
-    counts = c.evaljs(js_count([".tpl", ".tpl__head", ".tpl__list",
-                                ".tpl__card,[class*=card],[class*=item]", "[class*=empty]"]))
-    out("TEMPLATE_DUMP", c.evaljs(JS_DUMP))
-    href = counts.get("__href", "")
-    render = (counts.get(".tpl", 0) > 0) and ("/login" not in href)
-    data = (counts.get(".tpl__list", 0) > 0)  # 模板列表容器在(5 品类)
-    verdicts.append(page_verdict(c, "studio/template", counts, render, data,
-                                 extra={"guard_bounced_to_login": "/login" in href}))
-
-    # ======================================================================
-    #  ADMIN 段:真实登录 → 经营看板 + 审核队列(U5) → U2 审核通过
-    # ======================================================================
-    c.nav(f"{ADMIN}/", settle=8)
-    c.shot("u5_admin_login.png")
-    out("ADMIN_LOGIN_PAGE", c.evaljs(JS_DUMP_LOGIN))
-    print("ADMIN_CLICK_LOGIN:", c.evaljs(JS_CLICK_LOGIN), flush=True)
-    time.sleep(7)
-    c.drain(1.0)
-    print("ADMIN_AFTER_LOGIN_HREF:", c.evaljs("location.href"), flush=True)
-
-    # ---- U5-7 经营看板 operation-dashboard(跨模块 DAU/GMV/生成量/发布量) ----
-    c.reset_net_log()
-    c.nav(f"{ADMIN}/wanxiang/operation-dashboard", settle=8)
-    c.shot("u5_7_operation_dashboard.png")
-    counts = c.evaljs(js_count(["[class*=card]", "[class*=statistic],[class*=metric]",
-                                ".el-card", "canvas,[class*=chart],[class*=echarts]", "[class*=empty]"]))
-    out("OPDASH_DUMP", c.evaljs(JS_DUMP))
-    href = counts.get("__href", "")
-    render = ("/login" not in href) and (counts.get("__bodyLen", 0) > 20)
-    data = (counts.get(".el-card", 0) > 0 or counts.get("[class*=card]", 0) > 0
-            or counts.get("[class*=statistic],[class*=metric]", 0) > 0)
-    verdicts.append(page_verdict(c, "admin/operation-dashboard", counts, render, data,
-                                 extra={"guard_bounced_to_login": "/login" in href}))
-
-    # ---- U5-8 审核队列 review(含 U2 seed 待审游戏) ----
-    c.reset_net_log()
-    c.nav(f"{ADMIN}/wanxiang/review", settle=8)
-    c.shot("u5_8_review.png")
-    q = c.evaljs(JS_QUEUE)
-    counts = c.evaljs(js_count([".el-table", ".el-table__row", ".el-table__empty-text"]))
-    out("REVIEW_QUEUE", q)
-    href = counts.get("__href", "")
-    render = (counts.get(".el-table", 0) > 0) and ("/login" not in href)
-    data = (counts.get(".el-table__row", 0) > 0)  # 队列有待审行(seed 9324)
-    rv = page_verdict(c, "admin/review", counts, render, data,
-                      extra={"queue_rows": q.get("rows", []), "row_count": q.get("rowCount", 0),
-                             "guard_bounced_to_login": "/login" in href})
-    verdicts.append(rv)
-
-    # ======================================================================
-    #  U2 审核→发布:在审核队列点 9324(saa-e2e-breakout)「通过」
-    # ======================================================================
-    u2 = {"target_id": U2_TARGET_ID, "target_name": U2_TARGET_NAME}
-    c.reset_net_log()
-    c.shot("u2_before.png")
-    before_q = c.evaljs(JS_QUEUE)
-    u2["before_queue_rows"] = before_q.get("rowCount", 0)
-    print("U2_CLICK_PASS:", c.evaljs(js_click_pass(U2_TARGET_NAME)), flush=True)
-    time.sleep(2)
-    c.shot("u2_dialog.png")
-    print("U2_CONFIRM:", c.evaljs(JS_CONFIRM), flush=True)
-    time.sleep(5)  # 等审核 API(同事务 publish) + 队列刷新
-    c.drain(1.5)
-    c.shot("u2_after.png")
-    after_q = c.evaljs(JS_QUEUE)
-    u2["after_queue_rows"] = after_q.get("rowCount", 0)
-    u2["queue_decremented"] = (after_q.get("rowCount", 0) < before_q.get("rowCount", 99))
-    u2["http5xx_during_review"] = c.http5xx[:6]
-    u2["console_errors_during_review"] = c.console[:6]
-    out("U2_REVIEW_RESULT", u2)
-
-    c.close()
-    out("WALK_SUMMARY", {"verdicts": verdicts, "u2": u2})
-    print("=== U5U2_WALK_DONE ===", flush=True)
-
-
-if __name__ == "__main__":
-    main()
diff --git a/game-studio/src/host/GamePlayer.vue b/game-studio/src/host/GamePlayer.vue
index c4d4dda4..1b2eed52 100644
--- a/game-studio/src/host/GamePlayer.vue
+++ b/game-studio/src/host/GamePlayer.vue
@@ -35,6 +35,9 @@ import { gateStorageKey, gateStorageWrite } from './storageGate';
 import { buildIframeSrcdoc, buildDemoPackage, fetchAndVerifyManifest } from './inject';
 import { getRuntimePackage } from './runtimeApi';
 
+/** 狗粮阶段不展示任何假广告/假支付 UI;请求仍须显式失败回包,避免游戏悬挂等待。 */
+const dogfoodMode = import.meta.env.VITE_DOGFOOD_MODE === 'true';
+
 // —— props(spec §3.4)——
 const props = withDefaults(defineProps(), {
   traceId: '',
@@ -403,6 +406,17 @@ function handleStorage(payload: unknown, direction: MessageDirection, requestId?
 /** 广告请求 → 抛 adRequest + 弹桩层 */
 function handleAdRequest(p: AdRequestPayload, requestId?: string): void {
   emit('adRequest', { slotId: p.slotId, type: p.adType });
+  if (dogfoodMode) {
+    if (requestId) {
+      bridge?.post(
+        'ad',
+        p.adType === 'rewarded' ? { rewarded: false } : { shown: false },
+        traceId.value,
+        requestId,
+      );
+    }
+    return;
+  }
   overlay.kind = 'ad';
   overlay.adType = p.adType;
   overlay.slotId = p.slotId;
@@ -413,6 +427,10 @@ function handleAdRequest(p: AdRequestPayload, requestId?: string): void {
 /** 支付请求 → 抛 payRequest + 弹桩层 */
 function handlePayRequest(p: PayRequestPayload, requestId?: string): void {
   emit('payRequest', { orderId: p.orderId, amount: p.amount });
+  if (dogfoodMode) {
+    if (requestId) bridge?.post('pay', { paid: false }, traceId.value, requestId);
+    return;
+  }
   overlay.kind = 'pay';
   overlay.orderId = p.orderId;
   overlay.amount = p.amount;
@@ -558,7 +576,7 @@ function onIframeLoad(): void {
     
 
     
-    
+