《躺平发育》规则、像素复刻依据与心理机制

研究日期:2026-10-09。本文是原包静态研究,不是实机试玩报告。 微信登录已恢复并已录制首局,首局未成功入房后失败,其后已恢复原窗口并补录失败结算无损 PNG 与 240 秒录像。实机截图、操作与录像事实由 01 试玩报告单独记录;本文仍仅陈述静态源码证据,不把代码分析称作实机测量。以下把「代码已证实」「UI 文案已证实」「推断」「待验证」分开。

证据与解码方法

原始主程序是 artifacts/original/unpacked/js/game.js 的 define("js/bundle.js", ...) 模块,另有启动模块 js/game.js。artifacts/original/unpacked/game.js 注册 Babel helpers、Laya 运行库、微信适配器、SDK、启动 UI 与 index.js 等 38 个 define 模块。

本文引用的行号均为 静态解码格式化文件,不是另写的游戏逻辑。对应原始来源始终是上述 js/bundle.js。工具 decode-fortify.mjs 只解析 AST、反转字符串和进行有限常量替换,不运行原游戏或 SDK:

游戏闭环与状态

UI 文案已证实: 开局红色倒计时要求离开走廊;进入空房间后上床,睡觉赚金币、建造装备;发现床上有人应离开;门遭攻击时可维修;别人房间灯熄灭时被告诫不要查看或拆遗物。原包还故意把蜜獾商店写成不可进入/购买的禁忌。来源:bundle 常量 Wi,格式化文件 1236(以搜索「如果出现红色倒计时」定位更可靠)。这些是怪谈式叙事,不能把「不要买道具」直接当作程序禁止购买。

代码已证实: 内部事件支持 GMSelectMap、GMStartGame、GMGameOver、GMPauseGame、GMResumeGame;地图管理器 T_ 的 yzk 注册处理方法分别为 eux/lax/zrr/cfi/kzg。来源:事件表 4576–4582;注册 310260–310345。可据此把原生状态分成大厅/地图选择 → 匹配/入场 → 移动找房 → 房间发展/战斗 → 结算,但完整转移守卫仍需动态核对。

代码已证实: 玩家伤害方法 okc(t) 减少 HP,普通分支 HP<=0 发出玩家受击/淘汰事件,并有免死/天使例外。淘汰处理清除床、关闭房间灯、显示「玩家:…已被淘汰!」,自己被淘汰会触发 GMGameOver(false)。来源:37885–37950、72440–72510、89115–89220。伥鬼、天使、猎梦者模式有不同结算分支;不可把经典模式的判断用于所有模式。

后续核查更新: 普通击杀胜利、真实入场倒计时、原攻击伤害、撤退寻路/回血、经验阈值和维修冷却/恢复量已追加源码证明与浏览器原引擎回归,见 原包玩法契约 和 81 项回归证据。完整原版双端对照与全部特殊装备组合仍待验证。代码存在大量扁平化条件和模式覆盖;未逐项完成路径证明的部分不应写成固定规则。原代码事件 TrollLogicState、MainLgTrollUpData 已恢复(4626、4597),说明有明确怪物状态/升级消息,但仅这些符号不足以证明数值。

资源与升级:原值保留

资源 gold1 是局内金币、gold2 是局内闪电;不要与 Z_.instance.player.gold 的持久钱包混同。累计方法 ciu.eer(roomId,gold,electricity) 将金币保留一位小数,累加当前值与 gold1_Count/gold2_Count。来源:335360–335414(搜索方法 eer)。床生产过程 woq(t) 的真实金币公式为 pR × 房间 qoj × 房间 zql × yrn,基础 pR 从配置第 4 列读入,可能被增益修改。来源:189770–189845,qhl(t,i) 设置 t.pR=i[3]。

原配置每行形状为 [名字, 金币价格, 闪电价格, 主属性, 射程/第二属性, 前置建筑键]。金币/闪电成本有直接扣费证据:309378–309440 比较 gold1>=s[1] && gold2>=s[2] 后扣除两值;igw(type,level) 返回配置的 level-1 行(343027–343205)。以下为 sml 基础配置(336173–336350):

床等级 名称 金币 闪电 基础金币/秒 前置
1 发霉小床 0 0 1 —
2 湿冷小床 25 0 2 —
3 破旧小床 50 0 4 door_2
4 简陋的床 100 0 8 —
5 标准床铺 200 0 16 door_5
6 温暖床铺 400 0 32 —
7 精装床铺 800 16 65 door_8
8 标准大床 1600 32 128 door_10
9 豪华大床 3200 65 256 door_11
10 多功能大床 6400 128 512 door_12

这些产量也有展示文案交叉证据(338915–338931)。65 是原值,不能「修正」成 64。

门 金币 闪电 配置生命属性
铁门1 / 2 / 3 / 4 / 5 0 / 16 / 32 / 65 / 128 0 50 / 80 / 160 / 200 / 250
钢门1 / 2 / 3 / 4 / 5 256 / 512 / 1024 / 2048 / 4096 0 / 16 / 32 / 65 / 128 320 / 640 / 1280 / 2560 / 5120
振金门1 / 2 / 3 8192 / 16384 / 32768 256 / 512 / 1024 10240 / 20480 / 40960

来源:336289–336304;具体运行时生命可能受模式、装备和增益覆盖。

炮台 金币 闪电 攻击 射程 前置
笤帚炮台 8 0 4 4 —
铲铲炮台 16 0 8 4.5 —
鱼叉炮台 32 0 16 5 —
喷射水枪 65 0 32 5.5 —
高压水枪 256 0 65 6 game_1
气泡发射器 512 0 128 6.5 —
魔力气泡机 1024 32 256 7 game_2
驱魔气泡机 2048 65 512 7.5 —
初级激光器 4096 128 1024 8 game_3
恶魔激光器 8192 256 2048 8.5 game_4
圣光生成器 16384 512 4096 9 game_5
圣光发射器 32768 1024 8192 9.5 game_6
圣光炮台 65536 2048 16384 10 —

来源:336228–336246。game_1 等对应「发霉游戏机」起的闪电生产链。基础游戏机金币成本 200/400/800/1600/3200/6400/12800/25600/51200/102400;配置主属性 1/2/4/6/8/10/15/20/25/30(336325–336341)。第一台 UI 写「1闪电/2s」,后面写「闪电/s」(338900–338913),不能把第一级配置值 1 直接解释为 1/秒。

模式覆盖已证实: igw 在 Nightmare 取 eno、Troll 取 ceg,Normal 且地图 0 且 ciu.dkw 为真时也取 ceg,Plot、CanotDie、Compound 另有配置(343027–343205)。ceg 炮台第 3–6 级金币价格为 40/88/176/440,而基础为 32/65/256/512(336708–336744)。因此复刻必须保留模式/开关选择;单凭一张表不能宣称完全同款。

代码已证实: 拆除普通建筑可退回 floor(金币成本/2) 与 floor(闪电成本/2),特殊对象有例外,64 特意转成 65。来源:解码未格式化文件字符 1,826,000 附近;证据 JSON 可回溯原串。建筑数量达到上限或无法落位时有返还原成本分支(字符 1,851,000–1,854,000);这和主动拆除不是同一规则。

原生坐标与视觉实现

Main.json Scene 设计尺寸 640×1136。bundle 舞台也是 width=640、height=1136、scaleMode=fixedwidth、screenMode=vertical、alignV=top、alignH=left(362049–362060)。首次初始化走 Laya WebGL,载入 version.json、fileconfig.json 后进入 bx.instance.init()(361425–361535)。

StartGame.json UI 根宽 640、高 1386,ground 中心 (320,693),部分背景素材高 1136。UI 使用比设计高更长的背景和中心锚点;不能把 1386 当作所有屏幕的设计高度。

buildMenu.json 建筑浮层宽 540、高 140、pivot=(270,70),图标 110×110(中心 (68,67)),升级按钮 176×106(中心 (439,65),pivot=(88,53))。menu.json 宽 570,升级标题 (283,40),build 内容 (285,139)、540×360。应原样恢复 pivot、sizeGrid、字体、层级和 skin,CSS 凭感觉绘图无法达到像素匹配。

MatchingUI.json 根 640×1386;head 面板中心 x320、y206,640×766;bottomBox x320、y984,640×402。GameOverUI.json 根 640×1386,有 red 和 lp 叠层。动态布局仍需在目标手机比例对照截图确认。

地图配置 map0 为 43 行 ×37 列,其他地图有 30×30、41×41、50×50 等;是不同 tile 编号的矩阵。原包有明确坐标转换事件 MGMGetMapPosTScenePos/MGMGetScenePosTMapPos/MGMGetMapPosTDataPos(4534–4536)。目前未证明 tile 编号全部语义和格子缩放,不能把地图矩阵直接画成每格固定屏幕像素。

装备配置 90 行,type=0…4,part 字段区分部位,包含 val/newModeVal/lev。它不是建筑升级表;例如木刀 val 从 .4→.8→1.2→2.4…,铁锤等两种模式值不同。复刻中混用装备与炮台成长表会产生规则偏差。

AI、单机与联网边界

代码已证实: 事件表有 AIStartGame、AIAddGold、GetAIRoomIndex、CallUpAIHelp、AI 人数限制及 AI 回收;源码还有 AI 比较其它房间床等级后自动购买升级的路径(309205–309440)。这证明有本地模拟同伴/对手的体系,不等于每个显示头像都是真人在线匹配。

代码已证实: 平台包装对象初始化 this.bpu=Laya.Browser.window.wx(343910),广告路径调用 createRewardedVideoAd、onLoad/onError、用户成功/失败回调(解码字符 2,581,200 附近)。启动读取远程分享配置,另有远程游戏配置、地图/活动/资源 URL(28110、361545)。原配置 config.json 写 version=3.5.15、gm=false、h5=true;h5 是包内字段,不足以证明这个发布包无需微信。

待验证: 是否有实时对战或服务端决定战斗结果。bundle 未检出 connectSocket,但 Laya/SDK 模块本身可能包含网络桥;缺少某个 API 字面不能作为「纯离线」证据。浏览器 demo 可复用本地模拟闭环,并把广告、分享、登录、活动配置等平台功能明确适配;不能假装线上匹配、真正奖励或账号同步成功。

为什么可能触动用户心理(推断)

以下为设计推断,来自规则与文案,并非已测用户心理或实机行为统计。

  1. 低门槛与持续掌控感。 上床后被动赚钱,点击一个对象升级便立刻改变视觉与产量;把动作负担降到最低,同时不断给出有意义选择。依据:床产量链、金币浮动动画、建筑成本与升级事件。
  2. 安全屋幻想与损失厌恶。 房间是个人领地,门是可见防线;修门能把「马上失去一切」转为即时挽救。依据:门生命链、玩家淘汰后清床熄灯、怪谈安全指令。
  3. 增长与防御的机会成本。 买床提高后续收入,买门提高即时存活,买炮降低长期威胁。相同金币只能满足其中一项;前置门/游戏机把「只堆经济」限制为多条链的权衡。依据:升级价格、前置键与双资源扣费。
  4. 先等待、再反击的情绪弧线。 文案先制造不得下床的无力感,装备从笤帚到圣光炮台提供力量幻想。依据:规则 Wi 与炮台命名/攻击成长。尚未实测怪物强度曲线,不能称其精确制造某个时间点的爽感。
  5. 怪谈的好奇心冲突。 「千万不要」「无法提供保护」的警告同时暗示商店、遗物、窗户有隐藏内容,玩家可能更想点击。依据:Wi、公告窗户怪人、商店道具路径。文案是悬疑诱因,是否实际转化必须观察。
  6. 伪社会情境与比较压力。 其它房间有头像、床等级、淘汰提示和遗产;即使由 AI 驱动,也能让个人防守有「别人发展比我快/别人死了」的参照。依据:AI 升级比较、淘汰广播、事件表。不可说成真实多人社交事实。
  7. 失败后的再次尝试与广告奖励。 已积累的房间发展带来不舍,奖励视频、免费/视频按钮和日次数上限可能借助这种情绪促使继续。依据:353235–353275 的 freeBtn/videoBtn、广告回调、每日上限文案。必须在实机确认按钮对应复活、抽奖或资源补给;现阶段不把具体变现结果写死。

恢复试玩后需要补证的关键帧

大厅与规则页;匹配页;倒计时找房;第一张床与初始资源;床/门/炮台的升级菜单;第一次猎梦者到门与维修前后;破门淘汰/成功结算;广告入口但不主动消费;相同设备比例下原版与 web 的逐帧截图。记录当前模式、地图、计时、金币/闪电、建筑等级、敌人血量与每一步动作;这样才能把本文静态证据变成可验证的像素与规则复刻验收标准。

浏览器适配补证:存档与输入桥

首页隐藏项是进度门槛

showBtn(65446–65515)使用 player.vgb 与配置阈值比较;vgb(328380)返回 _win+_lose。默认阈值 vhj=yde=sqg=1(319265–319269)。零局新档隐藏六角色选择框 select;零局也隐藏商店 ces=Shop、ghostBtn、coupleBtn;shareBtn 同样要一局。节点绑定关系见 66869–66922。因此原账号与全新浏览器账号首页不同可以由进度解释,不应先归因于平台差异。

排行榜另由 Id.instance(Id).isOpen 控制,getter 读取 tw.get('isOpenGameLev')(解码未格式化字符 297307),默认 false。其数据初始化在跨日登录、老用户达到一定局数等条件下开启(字符 336906–337850)。这只是入口显示条件,实际联网排行榜还要另行适配。

自造本地样本,不复制账号

player 构造的旧 key=PlayerData,现 key=PlayerDataItem(319983 附近,解码字符 2409830)。当前 duo() 将整个 _data JSON 保存到 Laya.LocalStorage.setItem(rmb,…);rmb getter 返回 ctg=PlayerDataItem。pdt(t)(325100 附近,字符 2437236)逐字段合并默认数据,缺失字段保留默认,因此可安全使用小型样本:

{
  "_gold": 206,
  "_fragment": 2,
  "_playerSelectIndex": 3,
  "_playerSkin": [0, 1, 2, 3],
  "_win": 0,
  "_lose": 1
}

206/2 是基准 UI 展示值,仅用于自造视觉对照样本,不代表连接了微信钱包或获得奖励。后续视觉核对灰衬衫眼镜角色是第四格索引 3,因此样本应选择 3;_playerSelectIndex 是零基索引。原默认 _playerSkin=[0];原类 skin 的 [0,6,2,3,4,5] 不是安全的解锁存档列表;直接加入 6 会导致部分选择节点为空。当前对照使用已验证样本 [0,1,2,3],不要把这份测试状态称作原账号存档。排行榜存档键另为 isOpenGameLev,其底层 tw 存储结构与 PlayerDataItem 是不同系统。

WASD 必须桥接原移动

内部所有者链为 T_.boc (hw) → hw.nlc (EM) → EM.thy (Vg)。T_ 在资源加载完成后构造(解码字符 2774250);它不是可直接从场景注册表找到的 Laya Script 单例。EM 构造摇杆宽/高 242、中心 (121,121)、半径 vwl=79,创建 Vg。

这些仅是静态适配建议,需以实测走廊移动、碰墙、入房上床、暂停和结算后的按键行为确认。