作弊器

· · 个人记录

2026年2月24日--OJ运营模拟器 作弊器 v5

用法: F12 → Console → 粘贴整段 → 回车

快捷键:Ctrl + Shift + H 显示/隐藏;Esc 立即隐藏

:::info[OJ 作弊器 v5代码]{close}

(() => {
  const HOTKEY_TOGGLE = { ctrl: true, shift: true, key: "H" };
  const HOTKEY_HIDE = { key: "Escape" };

  if (window.__OJ_CHEAT_PANEL_V5__) {
    window.__OJ_CHEAT_PANEL_V5__.toggle?.();
    console.log("[OJ Cheat] v5 已存在:已切换显示。");
    return;
  }
  const resolveGlobals = () => {
    let G, U;
    try { G = (typeof game !== "undefined") ? game : undefined; } catch { G = undefined; }
    try { U = (typeof ui !== "undefined") ? ui : undefined; } catch { U = undefined; }
    if (!G) G = window.game;
    if (!U) U = window.ui;
    return { G, U };
  };

  const mustHaveGlobals = () => {
    const { G, U } = resolveGlobals();
    if (!G || !U) {
      toast("未检测到 game/ui:请确认已进入游戏页面且脚本加载完成(建议刷新后再执行)。", "bad");
      return false;
    }
    return true;
  };
  const css = (el, obj) => Object.assign(el.style, obj);
  const h = (tag, attrs = {}, children = []) => {
    const el = document.createElement(tag);
    for (const [k, v] of Object.entries(attrs)) {
      if (k === "style") css(el, v);
      else if (k.startsWith("on") && typeof v === "function") el[k] = v;
      else if (k === "class") el.className = v;
      else el.setAttribute(k, v);
    }
    for (const c of children) el.appendChild(typeof c === "string" ? document.createTextNode(c) : c);
    return el;
  };
  const parseNum = (s) => {
    if (s == null) return NaN;
    const t = String(s).trim().replaceAll(",", "");
    if (!t) return NaN;
    const n = Number(t);
    return Number.isFinite(n) ? n : NaN;
  };
  const getRoot = (name) => {
    const { G, U } = resolveGlobals();
    if (name === "game") return G;
    if (name === "ui") return U;
    if (name === "window") return window;
    if (name === "globalThis") return globalThis;
    return undefined;
  };

  const getPath = (path) => {
    const seg = path.split(".");
    let cur = getRoot(seg[0]);
    if (cur == null) return undefined;
    for (let i = 1; i < seg.length; i++) {
      cur = cur?.[seg[i]];
      if (cur == null && i !== seg.length - 1) return undefined;
    }
    return cur;
  };

  const setPath = (path, value) => {
    const seg = path.split(".");
    let cur = getRoot(seg[0]);
    if (cur == null) return false;
    for (let i = 1; i < seg.length - 1; i++) {
      cur = cur?.[seg[i]];
      if (cur == null) return false;
    }
    const last = seg[seg.length - 1];
    if (cur == null) return false;
    cur[last] = value;
    return true;
  };

  const addPath = (path, delta) => {
    const old = getPath(path);
    if (!Number.isFinite(old)) return false;
    return setPath(path, old + delta);
  };
  const LOCKS = new Map(); // path -> number
  let patchedUpdate = false;

  const applyLocks = () => {
    for (const [path, val] of LOCKS.entries()) {
      setPath(path, val);
    }
  };

  const patchUiUpdateOnce = () => {
    if (patchedUpdate) return;
    const { U } = resolveGlobals();
    if (!U?.update || typeof U.update !== "function") return;

    const original = U.update.bind(U);
    U.update = function(...args) {
      const ret = original(...args);
      try { applyLocks(); } catch {}
      try { original(...args); } catch {}
      return ret;
    };

    patchedUpdate = true;
  };

  const safeUpdate = () => {
    const { U } = resolveGlobals();
    patchUiUpdateOnce();
    try { U?.update?.(); } catch {}
    try { U?.renderRD?.(); } catch {}
    try { U?.renderClasses?.(); } catch {}
  };
  const mountWhenReady = (fn) => {
    if (document.body) fn();
    else window.addEventListener("DOMContentLoaded", fn, { once: true });
  };
  const style = h("style", {}, [`
#oj-cheat-v5{
  position: fixed;
  top: 92px;
  right: 22px;
  width: 420px;
  height: 560px;
  z-index: 2147483647;
  font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
  color: rgba(20,23,30,.92);
}
#oj-cheat-v5 *{ box-sizing: border-box; }
#oj-cheat-v5 .card{
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;

  border-radius: 18px;
  border: 1px solid rgba(15,23,42,.10);
  background: linear-gradient(180deg, rgba(255,255,255,.92), rgba(248,250,252,.88));
  box-shadow:
    0 28px 80px rgba(2,6,23,.18),
    0 6px 16px rgba(2,6,23,.10);
  backdrop-filter: blur(10px);
  overflow: hidden;
  resize: both;
  min-width: 360px;
  min-height: 420px;
}
#oj-cheat-v5 .hdr{
  display:flex; justify-content:space-between; align-items:center;
  padding:12px 12px 10px 12px;
  user-select:none;
  cursor: move;
  background: linear-gradient(135deg, rgba(59,130,246,.10), rgba(236,72,153,.08));
  border-bottom: 1px solid rgba(15,23,42,.08);
}
#oj-cheat-v5 .title b{
  font-size: 14px;
  letter-spacing: .2px;
  display: block;
}
#oj-cheat-v5 .subtitle{
  font-size: 11px;
  margin-top: 2px;
  color: rgba(15,23,42,.58);
}
#oj-cheat-v5 .chipRow{ display:flex; gap:6px; margin-top:8px; flex-wrap:wrap; }
#oj-cheat-v5 .chip{
  font-size:11px;
  padding:3px 8px;
  border-radius:999px;
  border:1px solid rgba(15,23,42,.10);
  background: rgba(255,255,255,.70);
  color: rgba(15,23,42,.70);
}
#oj-cheat-v5 .body{
  padding:12px;
  overflow:auto;
  flex: 1;
}
#oj-cheat-v5 .sec{
  border:1px solid rgba(15,23,42,.08);
  background: rgba(255,255,255,.70);
  border-radius:16px;
  padding:10px;
  margin-top:10px;
}
#oj-cheat-v5 .sec:first-child{ margin-top:0; }
#oj-cheat-v5 h4{
  margin:0 0 8px 0;
  font-size:12px;
  letter-spacing:.2px;
  color: rgba(15,23,42,.88);
  display:flex; align-items:center; justify-content:space-between;
}
#oj-cheat-v5 .hint{
  margin-top:8px;
  font-size:11px;
  color: rgba(15,23,42,.55);
  line-height:1.35;
}
#oj-cheat-v5 .grid{
  display:grid;
  grid-template-columns: 1fr 152px;
  gap:10px;
  align-items:start;
  margin-bottom:10px;
}
#oj-cheat-v5 .row{
  display:grid;
  grid-template-columns: 92px 1fr;
  gap:8px;
  align-items:center;
}
#oj-cheat-v5 .label{
  font-size:12px;
  color: rgba(15,23,42,.72);
}
#oj-cheat-v5 input{
  width:100%;
  padding:9px 10px;
  border-radius:12px;
  border:1px solid rgba(15,23,42,.10);
  background: rgba(255,255,255,.90);
  color: rgba(15,23,42,.92);
  outline:none;
  font-size:12px;
}
#oj-cheat-v5 input:focus{
  border-color: rgba(59,130,246,.35);
  box-shadow: 0 0 0 3px rgba(59,130,246,.12);
}
#oj-cheat-v5 .btn{
  border:1px solid rgba(15,23,42,.10);
  background: rgba(255,255,255,.92);
  color: rgba(15,23,42,.88);
  padding:8px 10px;
  border-radius:12px;
  font-size:12px;
  cursor:pointer;
  transition: transform .06s ease, box-shadow .15s ease, border-color .15s ease;
  box-shadow: 0 2px 8px rgba(2,6,23,.05);
}
#oj-cheat-v5 .btn:hover{ border-color: rgba(15,23,42,.18); box-shadow: 0 6px 18px rgba(2,6,23,.08); }
#oj-cheat-v5 .btn:active{ transform: translateY(1px); }
#oj-cheat-v5 .btn.primary{
  background: rgba(59,130,246,.10);
  border-color: rgba(59,130,246,.18);
}
#oj-cheat-v5 .btn.good{
  background: rgba(16,185,129,.10);
  border-color: rgba(16,185,129,.18);
}
#oj-cheat-v5 .btn.bad{
  background: rgba(239,68,68,.10);
  border-color: rgba(239,68,68,.18);
}
#oj-cheat-v5 .btn.ghost{
  background: rgba(255,255,255,.65);
}
#oj-cheat-v5 .btnbar{ display:flex; gap:8px; flex-wrap:wrap; }
#oj-cheat-v5 .footer{
  padding:10px 12px;
  border-top: 1px solid rgba(15,23,42,.08);
  background: rgba(255,255,255,.72);
  display:flex;
  justify-content:space-between;
  align-items:center;
  gap:10px;
}
#oj-cheat-v5 .kbd{
  font-size:11px;
  color: rgba(15,23,42,.55);
  display:flex; gap:6px; align-items:center; flex-wrap:wrap;
}
#oj-cheat-v5 .kbd code{
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size:11px;
  padding:2px 6px;
  border-radius:8px;
  border:1px solid rgba(15,23,42,.10);
  background: rgba(255,255,255,.85);
  color: rgba(15,23,42,.72);
}
#oj-cheat-v5 .toast{
  margin-top:8px;
  padding:8px 10px;
  border-radius:12px;
  font-size:12px;
  line-height:1.35;
  border:1px solid rgba(15,23,42,.10);
  background: rgba(255,255,255,.78);
}
#oj-cheat-v5 .meta{
  margin-top:6px;
  display:flex;
  gap:10px;
  flex-wrap:wrap;
  font-size:11px;
  color: rgba(15,23,42,.58);
}
#oj-cheat-v5 .mono{
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
#oj-cheat-v5 .preview{
  margin-top:6px;
  font-size:11px;
  color: rgba(15,23,42,.62);
  display:flex;
  gap:8px;
  align-items:center;
  flex-wrap:wrap;
}
#oj-cheat-v5 .pill{
  padding:2px 7px;
  border-radius: 999px;
  border:1px solid rgba(15,23,42,.10);
  background: rgba(255,255,255,.85);
}
#oj-cheat-v5 .pill.locked{
  border-color: rgba(59,130,246,.22);
  background: rgba(59,130,246,.10);
  color: rgba(30,64,175,.85);
}
`]);

  // ------- toast -------
  let toastArea;
  const toast = (msg, type = "info") => {
    if (!toastArea) return;
    const map = {
      info: { bd: "rgba(59,130,246,.22)", bg: "rgba(59,130,246,.08)" },
      ok:   { bd: "rgba(16,185,129,.20)", bg: "rgba(16,185,129,.08)" },
      warn: { bd: "rgba(245,158,11,.22)", bg: "rgba(245,158,11,.08)" },
      bad:  { bd: "rgba(239,68,68,.22)",  bg: "rgba(239,68,68,.08)"  },
    };
    const c = map[type] || map.info;
    const t = h("div", { class: "toast", style: { borderColor: c.bd, background: c.bg } }, [msg]);
    toastArea.appendChild(t);
    setTimeout(() => t.remove(), 2400);
  };

  // ------- panel builder -------
  mountWhenReady(() => {
    document.head.appendChild(style);

    const root = h("div", { id: "oj-cheat-v5" });
    const card = h("div", { class: "card" });

    const btnFold = h("button", { class: "btn ghost", title: "折叠/展开" }, ["折叠"]);
    const btnHide = h("button", { class: "btn bad", title: "隐藏(Ctrl+Shift+H 可恢复)" }, ["隐藏"]);

    const header = h("div", { class: "hdr" }, [
      h("div", { class: "title" }, [
        h("b", {}, ["🧪 作弊器 v5"]),
        h("div", { class: "subtitle" }, ["来自Trie2025(uid:1271480)"]),
        h("div", { class: "chipRow" }, [
          h("span", { class: "chip" }, ["设置/增量"]),
          h("span", { class: "chip" }, ["原值→新值预览"]),
          h("span", { class: "chip" }, ["字段锁定"])
        ])
      ]),
      h("div", { style: { display: "flex", gap: "8px" } }, [btnFold, btnHide])
    ]);

    const body = h("div", { class: "body" });
    toastArea = h("div");

    // --- value line helpers ---
    const makeCurrent = (path) => {
      const el = h("span", { class: "mono" }, ["—"]);
      const refresh = () => {
        const v = getPath(path);
        el.textContent = (v === undefined) ? "undefined" : String(v);
      };
      refresh();
      return { el, refresh };
    };

    const makeSetterSection = ({ title, icon, items }) => {
      const sec = h("div", { class: "sec" }, [
        h("h4", {}, [
          `${icon} ${title}`,
          h("span", { style: { fontSize: "11px", color: "rgba(15,23,42,.55)" } }, ["(设置=覆盖,增量=加/减)"])
        ])
      ]);

      const refreshers = [];

      for (const it of items) {
        const current = makeCurrent(it.path);
        refreshers.push(current.refresh);

        const inp = h("input", { placeholder: it.placeholder ?? "如:1e6 / -250 / 0.5 / 1,000,000" });

        const preview = h("div", { class: "preview" }, [
          h("span", { class: "pill" }, ["原值"]),
          h("span", { class: "mono" }, ["—"]),
          h("span", { class: "pill" }, ["→"]),
          h("span", { class: "pill" }, ["新值"]),
          h("span", { class: "mono" }, ["—"])
        ]);

        const [oldEl, newEl] = preview.querySelectorAll(".mono");
        const updatePreview = () => {
          const oldV = getPath(it.path);
          oldEl.textContent = (oldV === undefined) ? "undefined" : String(oldV);

          const n = parseNum(inp.value);
          newEl.textContent = Number.isFinite(n) ? String(n) : "(无效)";
        };
        inp.addEventListener("input", updatePreview);
        updatePreview();

        const lockPill = h("span", { class: "pill" }, ["未锁定"]);
        const refreshLockPill = () => {
          const locked = LOCKS.has(it.path);
          lockPill.textContent = locked ? "已锁定" : "未锁定";
          lockPill.classList.toggle("locked", locked);
        };
        refreshLockPill();

        const btnSet = h("button", {
          class: "btn primary",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            const v = parseNum(inp.value);
            if (!Number.isFinite(v)) return toast(`【${it.name}】输入无效:${inp.value}`, "bad");
            const ok = setPath(it.path, v);
            if (!ok) return toast(`无法写入:${it.path}`, "bad");

            // 如果已锁定,同步锁定值
            if (LOCKS.has(it.path)) LOCKS.set(it.path, v);

            safeUpdate();
            current.refresh();
            updatePreview();
            toast(`已设置【${it.name}】= ${v}`, "ok");
          }
        }, ["设置"]);

        const btnAdd = h("button", {
          class: "btn",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            const d = parseNum(inp.value);
            if (!Number.isFinite(d)) return toast(`【${it.name}】增量无效:${inp.value}`, "bad");
            const ok = addPath(it.path, d);
            if (!ok) return toast(`无法累加(目标不是数值?)${it.path}`, "bad");

            // 如果已锁定,更新锁定值为“累加后”的真实值
            if (LOCKS.has(it.path)) {
              const vNow = getPath(it.path);
              if (Number.isFinite(vNow)) LOCKS.set(it.path, vNow);
            }

            safeUpdate();
            current.refresh();
            updatePreview();
            toast(`已对【${it.name}】增量:${d}`, "ok");
          }
        }, ["增量"]);

        const btnLock = h("button", {
          class: "btn",
          title: "锁定后:每次 ui.update() 都会把该字段回写为锁定值(防 nextMonth / calculateReputation 重算打回)",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            patchUiUpdateOnce();

            if (LOCKS.has(it.path)) {
              LOCKS.delete(it.path);
              refreshLockPill();
              toast(`已解除锁定:${it.name}`, "warn");
              return;
            }
            const vNow = getPath(it.path);
            if (!Number.isFinite(vNow)) return toast(`无法锁定:当前值不是数值(${it.path})`, "bad");
            LOCKS.set(it.path, vNow);
            refreshLockPill();
            toast(`已锁定:${it.name} = ${vNow}`, "ok");
          }
        }, ["锁定"]);

        const left = h("div", {}, [
          h("div", { class: "row" }, [
            h("div", { class: "label" }, [it.name]),
            inp
          ]),
          h("div", { class: "meta" }, [
            h("span", { class: "pill" }, ["当前"]),
            h("span", { class: "mono" }, [current.el]),
            lockPill
          ]),
          preview
        ]);

        sec.appendChild(
          h("div", { class: "grid" }, [
            left,
            h("div", { style: { display: "flex", gap: "8px", justifyContent: "flex-end", flexWrap: "wrap" } }, [
              btnSet, btnAdd, btnLock
            ])
          ])
        );
      }

      sec.appendChild(h("div", { class: "hint" }, [
        "输入支持:科学计数法(1e6、2.5e3)、逗号(1,000,000)、负数(-1e4)。",
        "  若发现“改了马上被打回去”,请对该字段使用【锁定】。"
      ]));

      return { sec, refreshAll: () => refreshers.forEach(fn => fn()) };
    };

    const sec1 = makeSetterSection({
      title: "资源与状态",
      icon: "⚙️",
      items: [
        { name: "资金", path: "game.money" },
        { name: "AP", path: "game.ap" },
        { name: "研发点", path: "game.resources.rd" },
        { name: "学术点", path: "game.resources.acad" },
        { name: "社区点", path: "game.resources.comm" },
        { name: "声誉", path: "game.stats.reputation", placeholder: "提示:声誉会在 nextMonth() 中重算,建议需要时锁定" }
      ]
    });

    const sec2 = makeSetterSection({
      title: "用户与题库",
      icon: "📊",
      items: [
        { name: "普通用户", path: "game.stats.users.normal" },
        { name: "活跃用户", path: "game.stats.users.active" },
        { name: "核心用户", path: "game.stats.users.core" },
        { name: "高质题数", path: "game.stats.problems.high" },
        { name: "普通题数", path: "game.stats.problems.mid" },
        { name: "低质题数", path: "game.stats.problems.low" }
      ]
    });

    const secOps = h("div", { class: "sec" }, [
      h("h4", {}, ["🧩 判定 / 解锁 / 修复"]),
      h("div", { class: "btnbar" }, [
        h("button", {
          class: "btn good",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            const btn = document.getElementById("next-month-btn");
            if (btn) btn.disabled = false;
            toast("已解除“进入下个月”按钮禁用(若存在)。", "ok");
          }
        }, ["解除按钮禁用"]),

        h("button", {
          class: "btn",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            const { G } = resolveGlobals();
            G.stats.consecutive_failure_months = 0;
            safeUpdate();
            toast("已清零:连续故障月数。", "ok");
          }
        }, ["清故障月数"]),

        h("button", {
          class: "btn",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            const { G } = resolveGlobals();
            G.stats.comm_fail_months = 0;
            safeUpdate();
            toast("已清零:社区违规累计月数。", "ok");
          }
        }, ["清社区违规月数"]),

        h("button", {
          class: "btn primary",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            const { G } = resolveGlobals();
            for (const k in G.features_unlocked) G.features_unlocked[k] = true;
            safeUpdate();
            toast("已全部解锁功能(features_unlocked)。", "ok");
          }
        }, ["全部解锁"]),

        h("button", {
          class: "btn",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            const { G } = resolveGlobals();
            if (G.flags) {
              G.flags.fundraising_penalty_val = 0;
              G.flags.temp_rep_penalty = 0;
            }
            safeUpdate();
            toast("已清除:募捐惩罚/临时声誉惩罚。", "ok");
          }
        }, ["清惩罚值"]),

        h("button", {
          class: "btn",
          onclick: () => {
            LOCKS.clear();
            toast("已清空所有锁定字段。", "warn");
          }
        }, ["清空锁定"])
      ]),
      h("div", { class: "hint" }, [
        "说明:锁定机制会在 ui.update() 后自动回写,适合测试“逻辑重算覆盖写”的漏洞面。"
      ])
    ]);

    body.appendChild(sec1.sec);
    body.appendChild(sec2.sec);
    body.appendChild(secOps);
    body.appendChild(toastArea);

    const footer = h("div", { class: "footer" }, [
      h("div", { class: "kbd" }, [
        "快捷键:",
        h("code", {}, ["Ctrl"]), "+", h("code", {}, ["Shift"]), "+", h("code", {}, ["H"]),
        "切换;",
        h("code", {}, ["Esc"]), "隐藏"
      ]),
      h("div", { style: { display: "flex", gap: "8px" } }, [
        h("button", {
          class: "btn",
          onclick: () => {
            if (!mustHaveGlobals()) return;
            safeUpdate();
            sec1.refreshAll(); sec2.refreshAll();
            toast("已刷新 UI & 当前值。", "ok");
          }
        }, ["刷新UI/值"])
      ])
    ]);

    card.appendChild(header);
    card.appendChild(body);
    card.appendChild(footer);
    root.appendChild(card);
    document.body.appendChild(root);

    // ------- fold/hide -------
    let folded = false;
    const setFold = (v) => {
      folded = v;
      body.style.display = folded ? "none" : "block";
      footer.style.display = folded ? "none" : "flex";
      btnFold.textContent = folded ? "展开" : "折叠";
    };
    btnFold.onclick = () => setFold(!folded);

    const setVisible = (v) => { root.style.display = v ? "block" : "none"; };
    btnHide.onclick = () => { setVisible(false); toast("面板已隐藏(Ctrl+Shift+H 可恢复)。", "warn"); };

    let dragging = false, dx = 0, dy = 0;
    header.addEventListener("mousedown", (e) => {
      dragging = true;
      const rect = root.getBoundingClientRect();
      dx = e.clientX - rect.left;
      dy = e.clientY - rect.top;
      root.style.left = rect.left + "px";
      root.style.top = rect.top + "px";
      root.style.right = "auto";
      e.preventDefault();
    });
    const onMove = (e) => {
      if (!dragging) return;
      root.style.left = Math.max(8, e.clientX - dx) + "px";
      root.style.top = Math.max(8, e.clientY - dy) + "px";
    };
    const onUp = () => dragging = false;
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);

    // ------- hotkeys -------
    const onKeydown = (e) => {
      if (
        e.ctrlKey === !!HOTKEY_TOGGLE.ctrl &&
        e.shiftKey === !!HOTKEY_TOGGLE.shift &&
        e.key.toUpperCase() === HOTKEY_TOGGLE.key
      ) {
        e.preventDefault();
        setVisible(root.style.display === "none");
        return;
      }
      if (e.key === HOTKEY_HIDE.key) setVisible(false);
    };
    document.addEventListener("keydown", onKeydown, true);

    // ------- expose handle -------
    window.__OJ_CHEAT_PANEL_V5__ = {
      show() { setVisible(true); },
      hide() { setVisible(false); },
      toggle() { setVisible(root.style.display === "none"); },
      fold(v) { setFold(!!v); },
      refresh() { safeUpdate(); sec1.refreshAll(); sec2.refreshAll(); },
      locks: LOCKS, 
      remove() {
        try { document.removeEventListener("keydown", onKeydown, true); } catch {}
        try { document.removeEventListener("mousemove", onMove); } catch {}
        try { document.removeEventListener("mouseup", onUp); } catch {}
        root.remove();
        delete window.__OJ_CHEAT_PANEL_V5__;
      }
    };

    console.log("[OJ Cheat] v5 已加载:window.__OJ_CHEAT_PANEL_V5__(Ctrl+Shift+H / Esc)");
    toast("面板已加载:可拖拽(标题栏)、可拉伸(右下角拖拽/resize)。", "ok");

    try { sec1.refreshAll(); sec2.refreshAll(); } catch {}
  });
})();

:::

✅ 2026年1月15日--OItrainer 作弊器3.4.1

用法: 安装油猴后,将代码完整复制后,点击新建脚本,覆盖模板代码。

快捷键: Ctrl + Shift + H 显示/隐藏

:::info[OItrainer 作弊器3.4.1代码]{close}

// ==UserScript==
// @name         OItrainer 作弊器3.4.1
// @namespace    https://seve42.github.io/OItrainer/
// @version      3.4.1
// @description  ---
// @match        https://seve42.github.io/OItrainer/game.html*
// @run-at       document-end
// @grant        unsafeWindow
// ==/UserScript==

(function () {
 'use strict';
 const W = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;

 const REPUTATION_LOCK = 100;

 let stressLockEnabled = false;
 let timeFrozen = false;
 let panelVisible = false;

 const state = {
   width: 420,
   lastRenderSig: '',
   hidden: false,
 };

 const LS_KEY_HIDE_ALL = '__oi_hide_all_ui__';

 const BAR_H = 56;
 const BAR_TOP = 10;
 const GAP = 10;
 const PANEL_TOP = BAR_TOP + BAR_H + GAP;

 const stressDescStore = new WeakMap();

 let weekKey = null;
 let frozenWeekValue = null;
 let weekOrigDesc = null;
 let weekFreezeMode = 'define';

 function getWeekKey(game) {
   const keys = ['week', 'time', 'turn', 'round'];
   for (const k of keys) {
     if (k in game && typeof game[k] === 'number') return k;
   }

   for (const k of Object.keys(game)) {
     if (typeof game[k] === 'number' && /week|turn|round|time/i.test(k)) return k;
   }
   return null;
 }

 function freezeTime(game) {
   if (!weekKey) weekKey = getWeekKey(game);
   if (!weekKey) {
     toast('未找到周数变量(week/time/turn/round)');
     timeFrozen = false;
     return;
   }

   if (frozenWeekValue == null) frozenWeekValue = game[weekKey];
   if (weekOrigDesc == null) {
     try { weekOrigDesc = Object.getOwnPropertyDescriptor(game, weekKey) || null; } catch { weekOrigDesc = null; }
   }

   try {
     Object.defineProperty(game, weekKey, {
       configurable: true,
       get() { return frozenWeekValue; },
       set(_) {}
     });
     weekFreezeMode = 'define';
   } catch {

     weekFreezeMode = 'assign';
     try { game[weekKey] = frozenWeekValue; } catch {}
   }

   timeFrozen = true;
 }

 function unfreezeTime(game) {
   if (!weekKey) weekKey = getWeekKey(game);
   if (!weekKey) {
     timeFrozen = false;
     frozenWeekValue = null;
     weekOrigDesc = null;
     weekFreezeMode = 'define';
     return;
   }

   try {
     if (weekOrigDesc) {
       Object.defineProperty(game, weekKey, weekOrigDesc);
     } else {
         //
       try { delete game[weekKey]; } catch {}
       if (frozenWeekValue != null) {
         try { game[weekKey] = frozenWeekValue; } catch {}
       }
     }
   } catch {
       //
   }

   timeFrozen = false;
   frozenWeekValue = null;
   weekOrigDesc = null;
   weekFreezeMode = 'define';
   toast('时间已恢复流动');
 }

 function loadHideState() {
   try {
     const v = localStorage.getItem(LS_KEY_HIDE_ALL);
     if (v === '1') state.hidden = true;
     else if (v === '0') state.hidden = false;
   } catch {}
 }
 function saveHideState() {
   try { localStorage.setItem(LS_KEY_HIDE_ALL, state.hidden ? '1' : '0'); } catch {}
 }

 function waitFor(fn, t = 20000) {
   const s = Date.now();
   return new Promise((res, rej) => {
     const id = setInterval(() => {
       try {
         if (fn()) { clearInterval(id); res(true); }
         else if (Date.now() - s > t) { clearInterval(id); rej(new Error('timeout')); }
       } catch {}
     }, 50);
   });
 }

 const el = (tag, css, html) => {
   const e = document.createElement(tag);
   if (css) e.style.cssText = css;
   if (html != null) e.innerHTML = html;
   return e;
 };

 const fmt = (n) => {
   try { return Number(n).toLocaleString('zh-CN'); } catch { return String(n); }
 };

 const parseNum = (s) => {
   if (s == null) return null;
   s = String(s).trim().replace(/[,,\s_]/g, '');
   const v = Number(s);
   return Number.isFinite(v) ? v : null;
 };

 function safeText(s) {
   return String(s ?? '').replace(/[&<>"']/g, (m) => ({
     '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
   }[m]));
 }

 function getBudget(g) { return g.budget ?? g.money ?? g.fund ?? g.cash ?? 0; }
 function setBudget(g, v) {
   if ('budget' in g) g.budget = v;
   else if ('money' in g) g.money = v;
   else if ('fund' in g) g.fund = v;
   else if ('cash' in g) g.cash = v;
 }

 function getStudents(g) { return g.students ?? g.studentList ?? g.roster ?? g.players ?? []; }
 function getStudentCount(g) { const a = getStudents(g); return Array.isArray(a) ? a.length : 0; }

 function trySetRep(g, v) {
   const keys = ['reputation', 'rep', 'prestige', 'fame'];
   for (const k of keys) { if (k in g) g[k] = v; }
   if (g.school) for (const k of keys) { if (k in g.school) g.school[k] = v; }
 }

 function lockNum(o, k, v) {
   try {
     Object.defineProperty(o, k, { configurable: true, get() { return v; }, set(_) {} });
     o[k] = v;
   } catch {}
 }

 function applyRepLock(g) {
   const keys = ['reputation', 'rep', 'prestige', 'fame'];
   keys.forEach(k => { if (k in g) lockNum(g, k, REPUTATION_LOCK); });
   if (g.school) keys.forEach(k => { if (k in g.school) lockNum(g.school, k, REPUTATION_LOCK); });
   trySetRep(g, REPUTATION_LOCK);
 }

 function initials(name) {
   const str = String(name ?? '').trim();
   if (!str) return 'S';
   const c = str[0];
   return c.toUpperCase ? c.toUpperCase() : c;
 }

 function lockStudentStress(student) {
   if (!student || typeof student !== 'object') return;

   let stored = stressDescStore.get(student);
   if (!stored) {
     const hadStress = Object.prototype.hasOwnProperty.call(student, 'stress');
     const hadPressure = Object.prototype.hasOwnProperty.call(student, 'pressure');
     stored = {
       hadStress,
       hadPressure,
       stress: hadStress ? Object.getOwnPropertyDescriptor(student, 'stress') : null,
       pressure: hadPressure ? Object.getOwnPropertyDescriptor(student, 'pressure') : null,
     };
     stressDescStore.set(student, stored);
   }

   try { student.stress = 0; } catch {}
   try { student.pressure = 0; } catch {}

   try { Object.defineProperty(student, 'stress', { configurable: true, get() { return 0; }, set(_) {} }); } catch {}
   try { Object.defineProperty(student, 'pressure', { configurable: true, get() { return 0; }, set(_) {} }); } catch {}
 }

 function unlockStudentStress(student) {
   if (!student || typeof student !== 'object') return;
   const stored = stressDescStore.get(student);
   if (!stored) return;

   try {
     if (stored.hadStress) {
       if (stored.stress) Object.defineProperty(student, 'stress', stored.stress);
       else delete student.stress;
     } else delete student.stress;
   } catch {}

   try {
     if (stored.hadPressure) {
       if (stored.pressure) Object.defineProperty(student, 'pressure', stored.pressure);
       else delete student.pressure;
     } else delete student.pressure;
   } catch {}

   stressDescStore.delete(student);
 }

 function applyStressLock(game) {
   const arr = getStudents(game);
   for (const s of arr) lockStudentStress(s);
 }

 function removeStressLock(game) {
   const arr = getStudents(game);
   for (const s of arr) unlockStudentStress(s);
 }

 function addStyle() {
   const s = el('style', null, `
     :root{
       --ui-bg: rgba(255,255,255,.92);
       --ui-bg2: rgba(245,245,245,.88);
       --ui-border: rgba(180,180,180,.28);
       --ui-text: #1f1f1f;
       --ui-sub: rgba(0,0,0,.58);
       --ui-line: rgba(0,0,0,.06);
       --ui-shadow: 0 10px 32px rgba(0,0,0,.08);
       --ui-shadow2: 0 10px 26px rgba(0,0,0,.10);
       --ui-radius: 16px;
       --ui-radius2: 12px;
       --ui-blur: blur(12px);
       --ui-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono","Courier New", monospace;
       --bar-top: ${BAR_TOP}px;
       --bar-h: ${BAR_H}px;
       --panel-top: ${PANEL_TOP}px;
     }

     #__oi_bar__, #__oi_panel__{
       font-family: ui-sans-serif,system-ui,-apple-system,"PingFang SC","Microsoft YaHei",Arial,sans-serif;
       color: var(--ui-text);
     }

     #__oi_bar__{
       position: fixed; top: var(--bar-top); left: 10px; right: 10px;
       height: var(--bar-h);
       display:flex; align-items:center; justify-content:space-between;
       padding: 0 14px;
       border-radius: var(--ui-radius);
       border: 1px solid var(--ui-border);
       background: linear-gradient(180deg,var(--ui-bg),var(--ui-bg2));
       box-shadow: var(--ui-shadow);
       backdrop-filter: var(--ui-blur);
       z-index: 99999;
     }

     #__oi_bar__ .left{
       display:flex; align-items:center; gap:10px;
       font-weight: 750; letter-spacing:.2px;
     }
     #__oi_bar__ .badge{
       height: 26px; padding: 0 10px;
       display:inline-flex; align-items:center; gap:8px;
       border: 1px solid var(--ui-border);
       border-radius: 999px;
       background: rgba(255,255,255,.6);
       box-shadow: 0 6px 18px rgba(0,0,0,.04);
       font-size: 12.5px;
       color: var(--ui-sub);
       user-select:none;
     }
     #__oi_bar__ .money{
       font-weight: 900;
       font-size: 16px;
       letter-spacing: .2px;
       font-family: var(--ui-mono);
     }
     #__oi_bar__ .mid{
       display:flex; align-items:center; gap:10px;
       min-width: 420px;
       justify-content: center;
     }
     #__oi_bar__ .right{
       display:flex; align-items:center; gap:8px;
     }

     #__oi_bar__ button{
       height: 34px; padding: 0 12px;
       border-radius: var(--ui-radius2);
       border: 1px solid var(--ui-border);
       background: rgba(255,255,255,.75);
       color: var(--ui-text);
       font-size: 13px;
       cursor:pointer;
       transition: transform .12s ease, background .12s ease, border-color .12s ease, box-shadow .12s ease;
     }
     #__oi_bar__ button:hover{
       background: rgba(255,255,255,.92);
       border-color: rgba(120,120,120,.28);
       box-shadow: 0 10px 20px rgba(0,0,0,.06);
       transform: translateY(-1px);
     }
     #__oi_bar__ button:active{ transform: translateY(0px); box-shadow: none; }

     #__oi_panel__{
       position: fixed;
       top: var(--panel-top);
       right: calc(-1px - var(--panel-w, 420px));
       width: var(--panel-w, 420px);
       height: calc(100% - var(--panel-top));
       z-index: 99998;
       border-left: 1px solid var(--ui-border);
       background: rgba(255,255,255,.95);
       backdrop-filter: var(--ui-blur);
       box-shadow: var(--ui-shadow2);
       display:flex; flex-direction:column;
       transition: right .28s ease;
       border-top-left-radius: var(--ui-radius);
       border-bottom-left-radius: var(--ui-radius);
       overflow: hidden;
     }
     #__oi_panel__.show{ right: 0; }

     #__oi_panel__ .hdr{
       padding: 14px 14px 12px 14px;
       border-bottom: 1px solid var(--ui-line);
       display:flex; align-items:center; justify-content:space-between;
       gap: 10px;
       background: linear-gradient(180deg, rgba(255,255,255,.92), rgba(245,245,245,.86));
     }
     #__oi_panel__ .hdr .title{
       font-size: 14.5px; font-weight: 800;
       display:flex; align-items:center; gap:10px;
     }
     #__oi_panel__ .hdr .sub{
       font-size: 12px; color: var(--ui-sub);
       margin-top: 2px;
     }
     #__oi_panel__ .hdr .actions{
       display:flex; align-items:center; gap:8px;
     }
     #__oi_panel__ .iconbtn{
       width: 34px; height: 34px;
       border-radius: 12px;
       border: 1px solid var(--ui-border);
       background: rgba(255,255,255,.75);
       cursor:pointer;
       display:flex; align-items:center; justify-content:center;
       transition: background .12s ease, transform .12s ease;
     }
     #__oi_panel__ .iconbtn:hover{ background: rgba(255,255,255,.92); transform: translateY(-1px); }

     #__oi_panel__ .body{
       padding: 12px 12px 14px 12px;
       overflow:auto;
       flex: 1;
     }

     .pill{
       display:inline-flex; align-items:center; gap:6px;
       padding: 2px 8px;
       border-radius: 999px;
       border: 1px solid var(--ui-border);
       background: rgba(255,255,255,.7);
       font-size: 12px;
       color: var(--ui-sub);
     }

     .cards{
       display:flex;
       flex-direction:column;
       gap: 10px;
     }

     .card{
       border: 1px solid var(--ui-line);
       border-radius: 14px;
       background: rgba(255,255,255,.86);
       box-shadow: 0 10px 20px rgba(0,0,0,.04);
       overflow:hidden;
     }
     .card:hover{ border-color: rgba(120,120,120,.22); }

     .card-h{
       display:flex; align-items:center; justify-content:space-between;
       gap: 10px;
       padding: 10px 10px;
       user-select:none;
     }
     .avatar{
       width: 34px; height: 34px;
       border-radius: 12px;
       border: 1px solid var(--ui-border);
       background: linear-gradient(180deg, rgba(255,255,255,.9), rgba(245,245,245,.85));
       box-shadow: 0 10px 18px rgba(0,0,0,.04);
       display:flex; align-items:center; justify-content:center;
       font-weight: 900;
       font-family: var(--ui-mono);
       color: rgba(0,0,0,.55);
       flex: 0 0 auto;
     }
     .h-main{
       display:flex; align-items:center; gap:10px;
       min-width: 0;
       flex: 1;
     }
     .name{
       font-weight: 850;
       font-size: 13.6px;
       letter-spacing: .2px;
       overflow:hidden;
       text-overflow:ellipsis;
       white-space:nowrap;
     }
     .meta{
       display:flex; gap:6px; flex-wrap:wrap;
       margin-top: 2px;
     }
     .chip{
       display:inline-flex; align-items:center; gap:6px;
       padding: 2px 8px;
       border-radius: 999px;
       border: 1px solid var(--ui-border);
       background: rgba(255,255,255,.72);
       color: var(--ui-sub);
       font-size: 12px;
     }
     .v{
       font-family: var(--ui-mono);
       color: rgba(0,0,0,.70);
     }

     #__oi_resizer__{
       position: fixed;
       top: var(--panel-top);
       right: calc(var(--panel-w, 420px));
       width: 10px;
       height: calc(100% - var(--panel-top));
       z-index: 99999;
       cursor: ew-resize;
       display:none;
     }
     #__oi_resizer__.show{ display:block; }

     #__oi_toast__{
       position: fixed;
       left: 12px;
       bottom: 12px;
       padding: 10px 12px;
       border-radius: 14px;
       border: 1px solid var(--ui-border);
       background: rgba(255,255,255,.9);
       box-shadow: var(--ui-shadow);
       backdrop-filter: var(--ui-blur);
       color: var(--ui-text);
       font-size: 12.5px;
       z-index: 100000;
       opacity: 0;
       transform: translateY(10px);
       transition: opacity .18s ease, transform .18s ease;
       pointer-events: none;
     }
     #__oi_toast__.show{ opacity: 1; transform: translateY(0px); }
   `);
   document.head.appendChild(s);
 }

 function toast(msg) {
   const t = document.getElementById('__oi_toast__');
   if (!t) return;
   t.textContent = msg;
   t.classList.add('show');
   clearTimeout(toast._id);
   toast._id = setTimeout(() => t.classList.remove('show'), 1100);
 }

 function ensureToast() {
   if (document.getElementById('__oi_toast__')) return;
   const t = el('div', '', '');
   t.id = '__oi_toast__';
   document.body.appendChild(t);
 }

 function setPanelWidth(px) {
   const w = Math.max(320, Math.min(720, px | 0));
   state.width = w;
   document.documentElement.style.setProperty('--panel-w', `${w}px`);
   const r = document.getElementById('__oi_resizer__');
   if (r) r.style.right = `${w}px`;
 }

 function updateVisibility() {
   const bar = document.getElementById('__oi_bar__');
   const panel = document.getElementById('__oi_panel__');
   const resizer = document.getElementById('__oi_resizer__');
   const toastEl = document.getElementById('__oi_toast__');
   const display = state.hidden ? 'none' : '';

   if (bar) bar.style.display = display;
   if (panel) panel.style.display = display;
   if (resizer) resizer.style.display = state.hidden ? 'none' : (panelVisible ? 'block' : 'none');
   if (toastEl) toastEl.style.display = display;
 }

 function makePanel() {
   if (document.getElementById('__oi_panel__')) return;

   document.documentElement.style.setProperty('--panel-w', `${state.width}px`);

   const panel = el('div', '', '');
   panel.id = '__oi_panel__';
   panel.innerHTML = `
     <div class="hdr">
       <div>
         <div class="title">
             学生
           <span class="pill" style="margin-left:6px;">作弊器</span>
           <span id="__oi_countpill__" class="pill">0 人</span>
         </div>
         <div class="sub">拖拽左侧调宽度 · Ctrl+Shift+H 隐藏/显示整个插件</div>
       </div>
       <div class="actions">
         <button class="iconbtn" id="__oi_refresh__">⟳</button>
         <button class="iconbtn" id="__oi_close__">✕</button>
       </div>
     </div>

     <div class="body" id="__oi_body__"></div>
   `;
   document.body.appendChild(panel);

   const resizer = el('div', '', '');
   resizer.id = '__oi_resizer__';
   document.body.appendChild(resizer);

   panel.querySelector('#__oi_close__').onclick = () => togglePanel(false);
   panel.querySelector('#__oi_refresh__').onclick = () => { state.lastRenderSig = ''; toast('已刷新'); };

   let dragging = false, startX = 0, startW = state.width;
   const onMove = (e) => {
     if (!dragging) return;
     const x = e.clientX ?? (e.touches && e.touches[0] && e.touches[0].clientX);
     if (typeof x !== 'number') return;
     const dx = startX - x;
     setPanelWidth(startW + dx);
     e.preventDefault?.();
   };
   const onUp = () => {
     if (!dragging) return;
     dragging = false;
     document.body.style.userSelect = '';
     toast(`侧栏宽度:${state.width}px`);
     window.removeEventListener('mousemove', onMove);
     window.removeEventListener('mouseup', onUp);
     window.removeEventListener('touchmove', onMove, { passive: false });
     window.removeEventListener('touchend', onUp);
   };
   const onDown = (e) => {
     dragging = true;
     startX = e.clientX ?? (e.touches && e.touches[0] && e.touches[0].clientX) ?? 0;
     startW = state.width;
     document.body.style.userSelect = 'none';
     window.addEventListener('mousemove', onMove);
     window.addEventListener('mouseup', onUp);
     window.addEventListener('touchmove', onMove, { passive: false });
     window.addEventListener('touchend', onUp);
     e.preventDefault?.();
   };
   resizer.addEventListener('mousedown', onDown);
   resizer.addEventListener('touchstart', onDown, { passive: false });

   window.addEventListener('keydown', (e) => {
     if (e.key === 'Escape' && panelVisible) togglePanel(false);
   });

   setPanelWidth(state.width);
 }

 function togglePanel(on) {
   panelVisible = (typeof on === 'boolean') ? on : !panelVisible;
   const panel = document.getElementById('__oi_panel__');
   const resizer = document.getElementById('__oi_resizer__');
   if (!panel || !resizer) return;
   if (state.hidden) return;

   panel.classList.toggle('show', panelVisible);
   resizer.classList.toggle('show', panelVisible);

   if (panelVisible) {
     state.lastRenderSig = '';
     toast('侧栏已打开');
   } else {
     toast('侧栏已关闭');
   }
 }

 function renderPanel(game) {
   if (!panelVisible || state.hidden) return;

   const body = document.getElementById('__oi_body__');
   const pill = document.getElementById('__oi_countpill__');
   if (!body || !pill) return;

   const list = getStudents(game);

   let sig = `${list.length}|`;
   for (let i = 0; i < Math.min(10, list.length); i++) {
     const s = list[i] || {};
     sig += `${s.name ?? s.nick ?? ''}|${s.stress ?? s.pressure ?? ''}||`;
   }
   if (sig === state.lastRenderSig) return;
   state.lastRenderSig = sig;

   pill.textContent = `${list.length} 人`;

   const rows = list.map((s, idx) => {
     const obj = (s && typeof s === 'object') ? s : {};
     const name = (('name' in obj) ? obj.name : (('nick' in obj) ? obj.nick : `学生${idx + 1}`));
     const stress = (('stress' in obj) ? obj.stress : (('pressure' in obj) ? obj.pressure : '-'));
     return { name, stress };
   });

   let html = `<div class="cards">`;
   for (const r of rows) {
     html += `
       <div class="card">
         <div class="card-h">
           <div class="h-main">
             <div class="avatar">${safeText(initials(r.name))}</div>
             <div style="min-width:0;">
               <div class="name">${safeText(r.name)}</div>
               <div class="meta">
                 <span class="chip">压力&nbsp;<span class="v">${safeText(r.stress)}</span></span>
               </div>
             </div>
           </div>
         </div>
       </div>
     `;
   }
   html += `</div>`;
   body.innerHTML = html;
 }

 function makeBar(game) {
   if (document.getElementById('__oi_bar__')) return;

   const bar = el('div', '', '');
   bar.id = '__oi_bar__';

   const left = el('div', '', `
     <div class="left">
       <span>作弊器</span>

       <span class="badge">声誉锁 100</span>

       <span class="badge">
         来源:
         <a href="https://www.luogu.com.cn/user/1271480"
            target="_blank" rel="noopener"
            style="color:inherit;text-decoration:none;font-weight:700;">
           Trie2025
         </a>
       </span>
     </div>
   `);

   const mid = el('div', '', `
     <div class="mid">
       <span class="money" id="__oi_money__">¥0</span>
       <span class="badge" id="__oi_info__">学生:0 | 压力锁:关 | 周数:-</span>
     </div>
   `);

   const right = el('div', 'display:flex;align-items:center;gap:8px;', '');

   const mkBtn = (txt, fn) => {
     const b = el('button', '', txt);
     b.onclick = fn;
     return b;
   };

   right.appendChild(mkBtn(' 改经费', () => {
     const cur = getBudget(game);
     const input = prompt('输入经费(支持科学计数法,如 2e9、1.5e8)', String(cur));
     const v = parseNum(input);
     if (v == null) return;
     setBudget(game, v);
     toast('经费已更新');
     if (W.renderAll) try { W.renderAll(); } catch {}
     state.lastRenderSig = '';
   }));

   right.appendChild(mkBtn('压力锁', () => {
     stressLockEnabled = !stressLockEnabled;
     if (stressLockEnabled) applyStressLock(game);
     else removeStressLock(game);
     toast(`压力锁:${stressLockEnabled ? '开(恒为0)' : '关'}`);
     state.lastRenderSig = '';
   }));

   right.appendChild(mkBtn('时间定格', () => {
     if (!timeFrozen) {
       weekKey = null;
       weekOrigDesc = null;
       frozenWeekValue = null;
       freezeTime(game);
       if (timeFrozen) toast(`时间已定格(${weekKey ?? 'week'} = ${frozenWeekValue})`);
     } else {
       unfreezeTime(game);
     }
   }));

   right.appendChild(mkBtn('学生侧栏', () => {
     togglePanel();
   }));

   bar.append(left, mid, right);
   document.body.appendChild(bar);

   document.body.insertBefore(el('div', `height:${PANEL_TOP}px;`), document.body.firstChild);

   let lastMoney = null, lastCnt = null, lastStress = null, lastFreeze = null, lastWeekText = null;

   function tick() {
     trySetRep(game, REPUTATION_LOCK);
     if (stressLockEnabled) applyStressLock(game);

     if (timeFrozen) {
       freezeTime(game);
       if (weekFreezeMode === 'assign' && weekKey && frozenWeekValue != null) {
         try { game[weekKey] = frozenWeekValue; } catch {}
       }
     }

     if (state.hidden) return;

     const money = getBudget(game);
     const cnt = getStudentCount(game);

     if (money !== lastMoney) {
       const m = document.getElementById('__oi_money__');
       if (m) m.textContent = `¥${fmt(money)}`;
       lastMoney = money;
     }

     if (!weekKey) weekKey = getWeekKey(game);
     const weekVal = (weekKey && (weekKey in game)) ? game[weekKey] : '-';
     const weekText = `${weekVal}${(timeFrozen && frozenWeekValue != null) ? '(定格)' : ''}`;

     if (cnt !== lastCnt || lastStress !== stressLockEnabled || lastFreeze !== timeFrozen || lastWeekText !== weekText) {
       const i = document.getElementById('__oi_info__');
       if (i) i.textContent = `学生:${cnt} | 压力锁:${stressLockEnabled ? '开' : '关'} | 周数:${weekText}`;
       lastCnt = cnt;
       lastStress = stressLockEnabled;
       lastFreeze = timeFrozen;
       lastWeekText = weekText;
     }

     renderPanel(game);
   }

   tick();
   setInterval(tick, 600);
 }

 function bindHideHotkey() {
   window.addEventListener('keydown', (e) => {
     const key = (e.key || '').toLowerCase();
     if (e.ctrlKey && e.shiftKey && key === 'h') {
       state.hidden = !state.hidden;
       saveHideState();

       if (state.hidden) {
         toast('插件已隐藏(再次按 Ctrl+Shift+H 显示)');
         setTimeout(updateVisibility, 80);
       } else {
         updateVisibility();
         toast('插件已显示(Ctrl+Shift+H 可再次隐藏)');
       }
       e.preventDefault?.();
     }
   }, true);
 }

 loadHideState();
 addStyle();
 ensureToast();
 bindHideHotkey();

 waitFor(() => !!W.game).then(() => {
   const g = W.game;
   applyRepLock(g);

   makePanel();
   makeBar(g);

   updateVisibility();
   if (!state.hidden) toast('作弊器 UI 已加载');
 }).catch(() => {});
})();

:::

更多游戏的作弊器将持续更新

提示:代码由ChatGpt辅助完成,请谨慎食用。

如果你觉得这篇文章很好,请给我一个关注,谢谢。

*✅表示作者以同意制作。(若作者不同意欢迎私信删除)