← 返回

应用详情

饿殍:明末千里行

版本 v1 · 453.1 KB
ID: epao_mingmo_qianlixing
⬇ 下载 CIP 🔍 返回列表
📝 应用描述
明末崇祯六年,洛阳至扬州千里护送。文字冒险多结局,忠于原作剧情,约 20 分钟。
📋 详细信息
应用ID epao_mingmo_qianlixing
名称 饿殍:明末千里行
版本 v1
文件大小 453.1 KB
上传时间 2026-07-29 21:09:05
状态 ✅ 已启用
权限 storage
📁 资源文件 (6)
assets/scene_depart.webp 查看 »
assets/icon.webp 查看 »
assets/scene_inn.webp 查看 »
assets/scene_river.webp 查看 »
assets/scene_luoyang.webp 查看 »
assets/scene_yangzhou.webp 查看 »
💡 界面预览 (main.lua)
📄 Lua 源码
-- =============================================================
-- 饿殍:明末千里行 —— Oldchat CIP 小程序 v1.0
-- 原作:零创游戏《饿殍:明末千里行》
-- =============================================================
-- 严格遵循 CIP 规范:
--   1. 顶层只 return 一个 ui.page
--   2. 受控更新用 app.set_text / set_image / set_visible
--   3. 仅用 storage 权限,离线运行
--   4. main.lua 大小控制在 512 KiB 以内
-- =============================================================

-- ---------- 存档 ----------
local STORAGE_KEY = "epao_save_v1"

local function load_state()
  local raw = app.storage_get(STORAGE_KEY)
  if not raw or raw == "" then return nil end
  local node = raw:match("node:([^|]+)")
  if not node then return nil end
  local flags_str = raw:match("flags:([^|]*)") or ""
  local flags = {}
  for flag in flags_str:gmatch("[^,]+") do
    if flag ~= "" then flags[flag] = true end
  end
  return { node = node, flags = flags }
end

local function save_state()
  local list = {}
  for k, v in pairs(state.flags) do
    if v then list[#list+1] = k end
  end
  local raw = "node:" .. state.node .. "|flags:" .. table.concat(list, ",")
  app.storage_set(STORAGE_KEY, raw)
end

-- ---------- 状态(全局) ----------
state = {
  node  = "menu",
  flags = {},
}

local saved = load_state()
if saved and type(saved.node) == "string" and saved.node ~= "" then
  state.node  = saved.node
  state.flags = saved.flags or {}
end

-- ---------- 故事数据 ----------
-- 每个节点:
--   chapter, title, image?, prose, choices = { {label, next, flag?}, ... }
local FLAG_NAME = {
  soft         = "忍让",
  warm         = "温和",
  spoke        = "她开了口",
  guard_on     = "警觉",
  feign        = "装醉",
  feign_ok     = "装醉未露",
  told_truth   = "揭开真相",
  silver_left  = "留下银两",
  wounded      = "受伤",
  fought       = "见血",
  spared       = "未下死手",
  departed     = "离洛阳",
}

local S = {}

S.prologue = {
  chapter = "序章",
  title   = "东关客店 · 崇祯六年腊月廿三",
  image   = "scene_luoyang",
  prose =
    "洛阳东关,腊月廿三,小雪。\n\n" ..
    "我从北市接了活,顶着一身寒气走进东关这家小客店,推门便闻到一股炭火味。" ..
    "堂中坐着赵掌柜,穿一身半旧的石青棉袍,头戴暖帽,见我来了,挥挥手把堂倌屏退。\n\n" ..
    "『良爷,坐。』他从袖中摸出一只小布包,推过来。\n\n" ..
    "我打开。\n\n" ..
    "三十两,纹银,码得整齐,光看色相便知是官银。我做了十几年脚夫,见过的银子不少,但这样的整锭摆在面前,心里仍是一跳。\n\n" ..
    "『这一单,送到扬州。』赵掌柜压低声音,『送一个人。亲眷女孩,父母双亡,投奔扬州姑母。』\n\n" ..
    "我没应声,只是端起那碗已经温好的黄酒,抿了一口。\n\n" ..
    "赵掌柜朝里屋扬了扬下巴。门帘一动,一个孩子被领了出来。\n\n" ..
    "她低着头,不见礼,也不抬头。瘦,十四五岁上下。面有病色,颊上不见半点血色。发枯黄,拿一根粗布条子系着。\n\n" ..
    "她走到屋中央,站定,像一截插在土里的枯枝。\n\n" ..
    "一双手很瘦,指节突出,手背上隐约有旧伤的痕迹。\n\n" ..
    "她没看我。\n\n" ..
    "—— 这趟活,说不上凶险,却也说不上干净。",
  choices = {
    { label = "收下银子,带她启程",            next = "act1_depart" },
    { label = "再问赵掌柜几句",               next = "ask_zhao" },
    { label = "拒绝这单活",                   next = "refuse" },
  }
}

S.ask_zhao = {
  chapter = "序章",
  title   = "再问一句",
  image   = "scene_luoyang",
  prose =
    "我把银子推回去一寸。\n\n" ..
    "『这孩子……真是您家外甥女?』\n\n" ..
    "赵掌柜的笑意僵了一瞬。他没正面回答,只是端起酒碗,慢慢喝了一口。\n\n" ..
    "『良爷,做咱们这行的,最忌讳多问。』\n\n" ..
    "『您只管送到扬州,人交给接头那位,事毕拿尾款。旁的事,烂在肚子里。』\n\n" ..
    "『三十两,够一家老小过两年。』\n\n" ..
    "他看着我的眼睛,声音压得更低:『这孩子是死契。活着送到,您就算完了。』\n\n" ..
    "门外又飘起雪。屋里炭火噼啪。\n\n" ..
    "—— 我听懂了。",
  choices = {
    { label = "收下银子,带她启程",            next = "act1_depart" },
    { label = "还是不接",                      next = "refuse" },
  }
}

S.refuse = {
  chapter = "序章 · 终",
  title   = "放下银子",
  image   = "scene_luoyang",
  prose =
    "我没接。\n\n" ..
    "『赵掌柜,这趟活儿,我接不下。』\n\n" ..
    "赵掌柜脸色一沉,看了我很久。他没再劝,只是把银子收回袖中,把孩子重新带回了里屋。\n\n" ..
    "我站在雪里,看着门帘落下。\n\n" ..
    "—— 后来我听说,那孩子最后是被另一个人领走的。\n\n" ..
    "再后来,洛阳城被流寇围了三天,死了很多人。\n\n" ..
    "赵掌柜的小院,也被一把火烧了。\n\n" ..
    "我常常想,那一夜我若是接了银子,事情会不会不一样?又常常想,我没有接,是不是对的?\n\n" ..
    "—— 有些事,没有下文,也算下文。",
  choices = {
    { label = "回到主菜单",                    next = "menu" },
  }
}

S.act1_depart = {
  chapter = "第一章",
  title   = "出洛阳 · 腊月风寒",
  image   = "scene_depart",
  prose =
    "腊月廿四清晨,天还没全亮,我便领着她出了洛阳东门。\n\n" ..
    "她跟在我身后三步远,背着一只粗布包袱,一路上一句话也不说。我问她叫什么,她不答。我问她多大了,她也不答。我问她是哪里人,她只是轻轻摇了摇头。\n\n" ..
    "出了城,洛水桥头,桥面结着一层薄冰。我扶她过去的时候,她躲了一下我的手,自己小心翼翼地摸着桥栏,一步一步挪了过去。\n\n" ..
    "过了桥,官道两旁是荒芜的田。地里只剩枯黄的麦茬,偶尔能见到新起的坟包。新土的颜色比旧土深,新坟前往往插着一根白幡,在风里抖。\n\n" ..
    "走到午时,我俩在路边一座破亭子里歇脚。\n\n" ..
    "我掏出自己带的两个冷炊饼,递了一个给她。\n\n" ..
    "她没接。\n\n" ..
    "她在自己包袱里,翻找了好一会儿,摸出一块油纸包着的肉——夹在热蒸饼里,带着余温,香气扑鼻。\n\n" ..
    "她没分我。她也不看我。\n\n" ..
    "她只是把那肉从油纸里小心取出来,小口小口地、仔细地吃。\n\n" ..
    "我嚼着自己的冷炊饼。嚼着嚼着,听她吃那肉,听得清清楚楚。\n\n" ..
    "—— 那肉的味儿,闻起来,不太像羊肉。\n\n" ..
    "我张了张嘴,又合上了。",
  choices = {
    { label = "忍住不开口,假装没看见",            next = "act1_silent", flag = "soft" },
    { label = "问她一句:『这是哪里买的?』",        next = "act1_ask",    flag = "spoke" },
    { label = "把冷炊饼也递过去,低声说:『吃吧。』", next = "act1_share",  flag = "warm" },
  }
}

S.act1_silent = {
  chapter = "第一章 · 风雪",
  title   = "没说出口",
  image   = "scene_depart",
  prose =
    "我没说话。\n\n" ..
    "我嚼完我的冷炊饼,把油纸拍了拍,塞回怀里。\n\n" ..
    "她吃完了那块肉,舔了舔手指,把油纸也仔细折好,塞进自己的包袱。\n\n" ..
    "她始终没看我一眼。\n\n" ..
    "—— 我忽然想:这一路上,她要吃几块这样的肉?我能不能护住她,把她送到扬州?送到之后,她会被怎么安排?\n\n" ..
    "我没问。\n\n" ..
    "—— 我只是站起身,拍了拍屁股上的土,重新上路。\n\n" ..
    "她跟在我身后,三步远,跟出城时一模一样。",
  choices = {
    { label = "继续赶路",                            next = "act2_river" },
  }
}

S.act1_ask = {
  chapter = "第一章 · 风雪",
  title   = "问了一句",
  image   = "scene_depart",
  prose =
    "『这是哪里买的?』\n\n" ..
    "她停了一下。\n\n" ..
    "那半块肉还捏在手里,她抬头看了我一眼,眼神里带着一种我说不上来的东西——像是警惕,又像是询问。\n\n" ..
    "『……过路的人给的。』\n\n" ..
    "声音很轻,像怕被谁听见。\n\n" ..
    "—— 这是她说的第一句话。\n\n" ..
    "她把剩下的肉用油纸仔细包好,塞回包袱深处。\n\n" ..
    "我没再问。我只是又看了她一眼。\n\n" ..
    "她低下头,又成了那个一截枯枝一样的女孩子。\n\n" ..
    "—— 但我心里明白,从这一句起,事情就不一样了。",
  choices = {
    { label = "继续赶路",                            next = "act2_river" },
  }
}

S.act1_share = {
  chapter = "第一章 · 风雪",
  title   = "递了过去",
  image   = "scene_depart",
  prose =
    "我没说话,只是把手里的冷炊饼也递过去,低声说:『吃吧。』\n\n" ..
    "她愣了一下。\n\n" ..
    "那是这一路上她第一次正眼看我。眼睛里有一点我说不上来的东西——不是感激,倒像是一个人在判断另一个人会不会害她。\n\n" ..
    "她没接炊饼。\n\n" ..
    "她看了看手里的肉,又看了看我。\n\n" ..
    "最后她把剩下的那半块肉,掰了一小块下来,放在油纸上,朝我推了推。\n\n" ..
    "我看了看那块肉。\n\n" ..
    "我没吃。我只把油纸收好,还给了她。\n\n" ..
    "她把那小块肉,又自己吃了。\n\n" ..
    "—— 我后来想,她其实是想分我的。可她不知道,我是嫌那肉的味道。\n\n" ..
    "—— 我更不知道,她是从哪里弄来这肉的。",
  choices = {
    { label = "继续赶路",                            next = "act2_river" },
  }
}

S.act2_river = {
  chapter = "第二章",
  title   = "孟津 · 黄河渡口",
  image   = "scene_river",
  prose =
    "行至孟津渡口,已近年关。\n\n" ..
    "远远便听见人声嘈杂,像潮水。\n\n" ..
    "我领着她走近。河滩上黑压压一片,挤满了要过河的流民。男女老幼,衣衫褴褛,面黄肌瘦,有的抱着孩子,有的扶着老人,有的推着独轮车,车上一只破箱、一床烂被。\n\n" ..
    "路边的土里插着许多草标,有的还带着血。草标上写着价:『女孩八岁,三斗米』『男孩六岁,四斗米』『老母能洗衣做饭,半斗米』。\n\n" ..
    "半斗米。\n\n" ..
    "我下意识攥紧了刀柄。\n\n" ..
    "船家蹲在船头,见了我这副打扮,问:『客官是过河?』我点头。船家叹一口气,低声说:『今年这河上漂的尸体,比往年多十倍。能走的都往南跑。』\n\n" ..
    "我领着她沿河滩走。忽然我闻到一股腥臭。\n\n" ..
    "我低头。\n\n" ..
    "一处被乱石盖着的尸首,被啃得七零八落。头发散在地上。手指被剁去了三根。\n\n" ..
    "我下意识伸手,把身后那孩子的眼睛挡了一下。\n\n" ..
    "她没挣扎。\n\n" ..
    "在渡口,一个穿破袄的青年,站在一块大石头上,远远看着我们这一老一少。\n\n" ..
    "他的眼神不像流民。倒像是在认人。",
  choices = {
    { label = "怒目瞪回去,把那青年逼退",            next = "act2_glare",  flag = "guard_on" },
    { label = "装作没看见,低头带穗上船",            next = "act2_ignore" },
    { label = "把穗护在身后,手按刀柄",              next = "act2_cover",  flag = "guard_on" },
  }
}

S.act2_glare = {
  chapter = "第二章 · 河风",
  title   = "怒目相向",
  image   = "scene_river",
  prose =
    "我转过身,直直瞪着那青年。\n\n" ..
    "他的目光僵了一瞬。\n\n" ..
    "我没出声。我只是把刀从鞘里抽出半寸,又送回去。\n\n" ..
    "—— 这是江湖上最明白的警告。\n\n" ..
    "他低了头,慢慢退进了人堆里。\n\n" ..
    "我领着穗上船。\n\n" ..
    "船离岸的时候,我余光扫到那青年还站在那块大石头上,看我们的方向。\n\n" ..
    "—— 后来我才明白,他大概已经确认了什么。\n\n" ..
    "—— 他确认的,不是我们的路,是穗。",
  choices = {
    { label = "上船,过河",                          next = "act3_inn" },
  }
}

S.act2_ignore = {
  chapter = "第二章 · 河风",
  title   = "装作没看见",
  image   = "scene_river",
  prose =
    "我拽了拽穗的袖口,没让她回头,径直上了船。\n\n" ..
    "船上挤得厉害,人贴着人。她被挤到了我身前,我用肩膀给她挡出一小块地方。\n\n" ..
    "她抬头看了我一眼,眼神里有一点感激。\n\n" ..
    "船离岸。\n\n" ..
    "我余光扫到那青年还站在岸边,看我们的方向。\n\n" ..
    "—— 他没有跟上来。\n\n" ..
    "船上有人哭,有人咳嗽,有人低声哄着怀里的孩子。河风带着水腥气,吹得人脸上发疼。\n\n" ..
    "我突然想起来,我已经三天没睡过一个囫囵觉。\n\n" ..
    "—— 但我不能睡。这一路,事多。",
  choices = {
    { label = "上船,过河",                          next = "act3_inn" },
  }
}

S.act2_cover = {
  chapter = "第二章 · 河风",
  title   = "把她护在身后",
  image   = "scene_river",
  prose =
    "我侧身,把穗挡在身后,手按在了刀柄上。\n\n" ..
    "那青年的目光和我撞了一下。\n\n" ..
    "几息之后,他才慢慢转身,消失在了人堆里。\n\n" ..
    "—— 他的手,一直揣在怀里,看不清有没有东西。\n\n" ..
    "我领着穗上船,一路上都把她挡在身侧。她不说话,只是跟得很紧。\n\n" ..
    "船离岸时,我又回头看了一眼。\n\n" ..
    "—— 那块大石头上,空了。\n\n" ..
    "我心里却越发不踏实:他没跟上来,是不是绕了路,先去前面等我们?\n\n" ..
    "船家说再有一日就到对岸。我点头,心里默默算着,前面还有什么要防的。",
  choices = {
    { label = "上船,过河",                          next = "act3_inn" },
  }
}

S.act3_inn = {
  chapter = "第三章",
  title   = "中州 · 小客店",
  image   = "scene_inn",
  prose =
    "过了黄河,又走了两日。\n\n" ..
    "行至中州(今郑州一带)一处小镇,天色已晚,投到路边一家小客店。\n\n" ..
    "店不大,门脸陈旧,但还能住人。要了间房,一床一桌一盏油灯。简陋,但能挡风。\n\n" ..
    "掌柜姓李,五十多岁,左腿瘸,见我领着个孩子,只看了一眼,没多问。端来两碗热汤面、一壶浊酒。\n\n" ..
    "穗吃得很少。她把面夹起来又放下,反复几次,最后只喝了半碗汤。\n\n" ..
    "我问她:『不饿?』\n\n" ..
    "她摇了摇头。\n\n" ..
    "—— 我注意到,她那只包袱,从进城起,就被她紧紧抱在怀里,从不离手。\n\n" ..
    "夜里,油灯跳了跳。\n\n" ..
    "有人敲门。\n\n" ..
    "我握住了枕边的朴刀。\n\n" ..
    "『客官,借宿一晚。』外面传来一个男人的声音,沙哑,像是走了很远的路,『同乡,搭个伴。』\n\n" ..
    "我犹豫了一息。开了门。\n\n" ..
    "进来的是个五十岁上下的男人,灰布袄,黑面皮,鞋上全是泥。一双眼睛,带着一种说不清的疲惫。\n\n" ..
    "他看了看床上的穗,又看了看我。\n\n" ..
    "『这姑娘是您的女儿?』\n\n" ..
    "『受人之托,送往扬州。』我说。\n\n" ..
    "他没再问。他只是笑了笑,坐到桌边,从怀里摸出一壶酒,倒了三碗。\n\n" ..
    "灯又跳了一下。\n\n" ..
    "穗背对着我们,像是睡着了。\n\n" ..
    "那男人端起酒碗,没喝,只是看着油灯,然后低声说——\n\n" ..
    "『若您能放过她,我愿出更高价。』",
  choices = {
    { label = "接过酒,问他:『你是她的什么人?』",  next = "act3_ask_who" },
    { label = "不接酒,手按刀柄:『喝完这碗,就走。』", next = "act3_warn" },
    { label = "装醉,看他下一步要做什么",          next = "act3_feign",  flag = "feign" },
  }
}

S.act3_ask_who = {
  chapter = "第三章 · 油灯下",
  title   = "问了一句",
  image   = "scene_inn",
  prose =
    "我接过酒,没喝,放在桌角。\n\n" ..
    "『你是她的什么人?』\n\n" ..
    "他沉默了很久。\n\n" ..
    "油灯的火苗在他脸上跳。他的眼眶红了一下,又沉下去了。\n\n" ..
    "『—— 她爹。』\n\n" ..
    "我看了他一眼,没说话。\n\n" ..
    "『不。叔父。』他改口说,声音更哑,『我是她叔父。她爹死了。她娘也死了。家里欠了债,卖了她。』\n\n" ..
    "『如今我后悔了。』\n\n" ..
    "『带不走她,我心里这关过不去。』\n\n" ..
    "『—— 求您,放过她。』\n\n" ..
    "他忽然起身,要朝我跪下。我伸手拦住了他。\n\n" ..
    "油灯又跳了一下。床上,穗背对着我们,可她的肩,抖了一下。\n\n" ..
    "—— 我心里明白:她没睡着。",
  choices = {
    { label = "把真相告诉她,让她自己决定",        next = "act3_truth",  flag = "told_truth" },
    { label = "继续装睡,看他下一步要做什么",      next = "act3_wait" },
    { label = "让他今晚就走,别再生事",            next = "act3_expel" },
  }
}

S.act3_warn = {
  chapter = "第三章 · 油灯下",
  title   = "下了逐客令",
  image   = "scene_inn",
  prose =
    "我没接酒。手按在刀柄上,看着他。\n\n" ..
    "『喝完这碗,就走。』\n\n" ..
    "他看了我很久。\n\n" ..
    "他端起酒碗,真的喝完了。\n\n" ..
    "然后他站起来,朝我点了点头。\n\n" ..
    "走到门口,他回头,低声说:『这孩子,是被她亲爹卖去扬州的。您……真的明白吗?』\n\n" ..
    "我没回答。\n\n" ..
    "门关上了。\n\n" ..
    "夜里再没动静。\n\n" ..
    "—— 可是,我心里开始不踏实了。\n\n" ..
    "床上的穗,翻了个身,背对着我。我看不见她的脸。\n\n" ..
    "—— 我猜,她也没睡着。",
  choices = {
    { label = "第二天启程",                        next = "act4_decide" },
  }
}

S.act3_feign = {
  chapter = "第三章 · 油灯下",
  title   = "装醉",
  image   = "scene_inn",
  prose =
    "我接过酒,装作喝了一大口,却只让酒沾了沾唇。\n\n" ..
    "我开始说些醉话,拍桌子,骂官府,骂这世道——一直演到趴在桌上『睡着』。\n\n" ..
    "那男人在桌边坐到油灯燃尽,没再说一句话。\n\n" ..
    "最后,我听见他叹了口气。\n\n" ..
    "他起身,在桌上放下了几块碎银——比他给我的多得多。\n\n" ..
    "然后他走了。\n\n" ..
    "—— 走之前,他低声说了一句:『——对不起。』\n\n" ..
    "我听着他走远了,才睁开眼。\n\n" ..
    "油灯已经快燃尽了。床上的穗,背对着我,肩头还在抖。\n\n" ..
    "—— 我心里清楚:她也知道,她是谁。",
  choices = {
    { label = "第二天启程",                        next = "act4_decide", flag = "feign_ok" },
  }
}

S.act3_truth = {
  chapter = "第三章 · 真相(1)",
  title   = "她的身世",
  image   = "scene_inn",
  prose =
    "我转过头,看着床上的穗。\n\n" ..
    "『起来。』我说。\n\n" ..
    "她坐起来,没说话。\n\n" ..
    "我从怀里摸出三十两银子的凭条,放在桌上。\n\n" ..
    "『这趟活儿,赵掌柜给了我三十两。』我说,『你叔父欠的债,也差不多这个数。』\n\n" ..
    "她低着头,没反应。\n\n" ..
    "『你叔父来了。刚才,就在外头坐着。』\n\n" ..
    "她的肩膀抖了一下。\n\n" ..
    "『他后悔了。他想带你走。』\n\n" ..
    "她终于抬头了。\n\n" ..
    "—— 我看见她眼睛里的东西,不是恨,不是怕,是一种比这两样都更深的东西。\n\n" ..
    "『……是爹让叔父卖的我。』她说,声音很轻,『娘死了以后,爹就把我卖了。叔父也拦不住。』\n\n" ..
    "我沉默了。\n\n" ..
    "『—— 我不回去。』她说,『回去,还是要被卖的。』\n\n" ..
    "我点头。\n\n" ..
    "门外,那男人一直没有进来。",
  choices = {
    { label = "让她自己决定:走还是留",            next = "act4_decide", flag = "told_truth" },
  }
}

S.act3_wait = {
  chapter = "第三章 · 真相(2)",
  title   = "一夜未动",
  image   = "scene_inn",
  prose =
    "我没动,她也没动。\n\n" ..
    "那男人在桌边坐到油灯燃尽,没再说一句话。\n\n" ..
    "最后,只听见一声很轻的叹息。\n\n" ..
    "他起身,在桌上放下了几块碎银——比他给我的多得多。\n\n" ..
    "然后他走了。\n\n" ..
    "—— 第二天我醒来的时候,那几块银子还在桌上。\n\n" ..
    "穗比我先醒。\n\n" ..
    "她蹲在桌边,看着那几块银子,看了很久。\n\n" ..
    "她没拿。她站起身,把那几块银子推到我面前。\n\n" ..
    "『—— 给您。』她说,『算路费。』\n\n" ..
    "我看了她一眼,没接。我把银子收进我自己的包袱,她看了我一眼,没说话。\n\n" ..
    "—— 后来我明白:她是想让我带她走。",
  choices = {
    { label = "第二天启程",                        next = "act4_decide", flag = "silver_left" },
  }
}

S.act3_expel = {
  chapter = "第三章 · 真相(3)",
  title   = "逐客",
  image   = "scene_inn",
  prose =
    "我把他赶了出去。\n\n" ..
    "门关上的一刻,我看见他眼睛里的光灭了。\n\n" ..
    "夜里,门外一直没有动静。\n\n" ..
    "可是到了后半夜,忽然听见房顶上有瓦片响。\n\n" ..
    "—— 有人下来了。\n\n" ..
    "我翻身坐起,把刀握在手里,低声对穗说:『别出声。』\n\n" ..
    "她点了点头,把自己缩成一团,蹲在床角。\n\n" ..
    "门闩被顶开。\n\n" ..
    "—— 来的人不止一个。一个是那个同乡客,另一个,是个蒙面汉子,手里攥着短刀。",
  choices = {
    { label = "拔刀迎敌",                          next = "act3_fight" },
    { label = "把穗压在身下,装睡",                next = "act3_shield" },
  }
}

S.act3_fight = {
  chapter = "第三章 · 血夜",
  title   = "拔刀",
  image   = "scene_inn",
  prose =
    "我抄起枕边的朴刀,从床上翻身而起。\n\n" ..
    "屋里很黑。油灯早就灭了。\n\n" ..
    "我听见穗一声短促的惊叫,随即被自己捂住了嘴。\n\n" ..
    "我挥刀——",
  choices = {
    { label = "一刀劈下,结果了蒙面汉",            next = "act3_kill",  flag = "fought" },
    { label = "只削落他的刀,逼退二人",            next = "act3_wound", flag = "spared" },
  }
}

S.act3_shield = {
  chapter = "第三章 · 血夜",
  title   = "挡在身下",
  image   = "scene_inn",
  prose =
    "我翻身把穗压在身下,被子盖住她。\n\n" ..
    "一刀落下来,我左肩一凉——\n\n" ..
    "—— 然后我听见朴刀出鞘的声响,是我自己的手替我拔的。\n\n" ..
    "血从肩上流下来,热乎乎的。\n\n" ..
    "我把刀反手一送,听见有人闷哼一声。\n\n" ..
    "—— 然后一切都安静了。",
  choices = {
    { label = "血战之后……",                       next = "act3_after_wounded", flag = "wounded" },
  }
}

S.act3_kill = {
  chapter = "第三章 · 血夜(1)",
  title   = "见血",
  image   = "scene_inn",
  prose =
    "那一刀快,我自己都没反应过来。\n\n" ..
    "蒙面汉子倒下去的时候,血溅到了我脸上。\n\n" ..
    "同乡客一声不吭,从后窗翻出去,跑了。\n\n" ..
    "我喘着气。屋里全是血腥味。\n\n" ..
    "穗缩在床角,一声不吭地看着地上的尸体。\n\n" ..
    "—— 她的眼睛,在油灯的余烬里,亮得吓人。\n\n" ..
    "我蹲下身,合上那汉子的眼皮。然后把刀在死人衣服上擦了擦,收回鞘。\n\n" ..
    "—— 第二天,我和李掌柜一起把那具尸首埋在了后院枣树下。\n\n" ..
    "没人问,没人说。",
  choices = {
    { label = "天亮,继续上路",                    next = "act4_decide", flag = "fought" },
  }
}

S.act3_wound = {
  chapter = "第三章 · 血夜(2)",
  title   = "未下死手",
  image   = "scene_inn",
  prose =
    "我一刀削落了蒙面汉手里的短刀。\n\n" ..
    "短刀在地上咣当一声。\n\n" ..
    "他二人对视一眼,翻窗跑了。\n\n" ..
    "我喘着气坐回床沿。左臂发麻,衣服上有一道口子,但没见血。\n\n" ..
    "—— 穗第一次主动伸手,按在了我肩头。\n\n" ..
    "她的手冰凉。\n\n" ..
    "『……谢谢。』她说。\n\n" ..
    "—— 这是她第一次对我说这两个字。\n\n" ..
    "我没回答。我只是拍了拍她的头,像拍一个孩子。",
  choices = {
    { label = "天亮,继续上路",                    next = "act4_decide", flag = "spared" },
  }
}

S.act3_after_wounded = {
  chapter = "第三章 · 血夜(3)",
  title   = "血醒",
  image   = "scene_inn",
  prose =
    "我醒来时,已经是第二天晌午。\n\n" ..
    "伤口用布条缠着,缠得并不高明。\n\n" ..
    "穗坐在床边,手里还拿着剩下的布条。\n\n" ..
    "—— 她不会说话,可她救了我。\n\n" ..
    "她见我醒了,把手里的水碗递给我。\n\n" ..
    "我喝了几口,问她:『他人呢?』\n\n" ..
    "她摇头。\n\n" ..
    "—— 我知道,他们跑了。\n\n" ..
    "我慢慢坐起来,左肩的伤口疼得我吸了一口凉气。\n\n" ..
    "穗伸手扶了我一把。\n\n" ..
    "—— 我忽然想:这孩子,不能再送到扬州了。",
  choices = {
    { label = "谢过她,继续上路",                  next = "act4_decide", flag = "wounded" },
  }
}

S.act4_decide = {
  chapter = "第四章",
  title   = "汴梁城外 · 一条岔路",
  image   = "scene_yangzhou",
  prose =
    "过了中州,又走了几日。\n\n" ..
    "到了汴梁(今开封)城外,已是腊月廿九。\n\n" ..
    "城门口的官道上,人车稀少。年关将至,能回家的都回了家,回不了家的,就地缩在城外的破庙里熬着。\n\n" ..
    "我站在一个三岔路口。\n\n" ..
    "一条往东,三百里到扬州。按原计划,送到就拿尾款,事毕。\n\n" ..
    "一条往南,二百里去汝州,再往西可以绕到南阳。\n\n" ..
    "一条往西,回洛阳。\n\n" ..
    "我看着身边这个瘦小的女孩子。\n\n" ..
    "她没看我。她看着脚下的土,好像早就知道会有这一刻。\n\n" ..
    "—— 我心里明白:这趟活儿,从头到尾就是个把孩子卖去扬州的活儿。\n\n" ..
    "—— 她可能被卖去做妾,做婢,做下处。三十两银子,不是她的身价,是她的活命钱。\n\n" ..
    "—— 可我若把她送回她叔父那里,她还是要被卖的。\n\n" ..
    "我叹了口气。\n\n" ..
    "风从北边吹来,带着雪的味道。",
  choices = {
    { label = "按原计划,送她去扬州",              next = "end_yangzhou_farewell" },
    { label = "给她留下银两,让她自己决定去留",    next = "end_let_go" },
    { label = "不送她去扬州,带她走另一条路",      next = "end_other_road" },
    { label = "回洛阳找她叔父,把人还给他",        next = "end_find_uncle" },
  }
}

S.end_yangzhou_farewell = {
  chapter = "终章 · 扬州别离",
  title   = "扬州别离",
  image   = "scene_yangzhou",
  prose =
    "我还是把她送到了扬州。\n\n" ..
    "一路上她没再说话。\n\n" ..
    "到扬州时,正是除夕。烟雨江南,白墙黛瓦,运河两岸挂着红灯笼。\n\n" ..
    "绸缎庄的接应人是个四十多岁的胖掌柜,验过文书,付了尾款。\n\n" ..
    "走的时候,她回头看了我一眼。\n\n" ..
    "没说话。\n\n" ..
    "我朝她点了点头,转身出了门。\n\n" ..
    "门在我身后合上的时候,我听见里头那胖掌柜说:『送到后院去,洗干净。』\n\n" ..
    "—— 我停了脚步。\n\n" ..
    "—— 我没回头。\n\n" ..
    "我走出巷口,走上了回洛阳的官道。\n\n" ..
    "三十里后,天降大雪。我坐在路边的一座破亭子里,喝完了那壶剩下的酒。\n\n" ..
    "—— 后来我听说,扬州城数月后被流寇攻破,城里死了很多人。\n\n" ..
    "—— 那孩子,再没人知道她的下落。\n\n" ..
    "【 结局一 · 扬州别离 】\n\n" ..
    "三十两银子,够一家老小过两年。\n\n" ..
    "我心里,再也过不去。",
  choices = {
    { label = "回到主菜单",                        next = "menu" },
  }
}

S.end_let_go = {
  chapter = "终章 · 留她独行",
  title   = "留她独行",
  image   = "scene_depart",
  prose =
    "我数出十两纹银,用布包好,放在她手里。\n\n" ..
    "『前面三十里就是汴梁城。』我说,『你——自己看着办。』\n\n" ..
    "她抬起头,看了我很久。\n\n" ..
    "『……为什么?』\n\n" ..
    "『因为这事我办不了。』我说。\n\n" ..
    "我没等她回答。我转身走了。\n\n" ..
    "—— 走了很久,我都没回头。\n\n" ..
    "—— 我不敢回头。\n\n" ..
    "我走出三里地,在路边一棵枯树下,站了很久。\n\n" ..
    "后来我听说,在汴梁城里,有人见过一个瘦小的女孩,背着一只粗布包袱,朝着南边走。\n\n" ..
    "再后来,就没人听说过了。\n\n" ..
    "【 结局二 · 留她独行 】\n\n" ..
    "我后来常常想:她到底去了哪里?她有没有活过那个冬天?\n\n" ..
    "—— 我不知道。",
  choices = {
    { label = "回到主菜单",                        next = "menu" },
  }
}

S.end_other_road = {
  chapter = "终章 · 复行数十步",
  title   = "复行数十步",
  image   = "scene_depart",
  prose =
    "我没去扬州。\n\n" ..
    "我带她走了一条往南的旧路——去汝州,去南阳,去那种流寇都懒得去的小地方。\n\n" ..
    "半路上,她开始愿意说话。\n\n" ..
    "她说她叫穗。她说那一年发大水,她爹病死了,她娘也病死,叔父卖了她。\n\n" ..
    "『……你恨他吗?』有一天我问她。\n\n" ..
    "她想了很久,说:『不恨。我只是不明白。』\n\n" ..
    "『为什么爹要卖我?』\n\n" ..
    "我没回答。\n\n" ..
    "—— 我也想不明白。\n\n" ..
    "我们到了一个小镇,在城西租了一间小屋,开了个小小的车马店。\n\n" ..
    "我管修车、喂马。她管做饭、洗碗。\n\n" ..
    "她的眼睛,慢慢开始有光。\n\n" ..
    "有一年除夕,她问我:『叔,您为什么救我?』\n\n" ..
    "我想了很久,说:『—— 我也不知道。』\n\n" ..
    "她笑了,我也笑了。\n\n" ..
    "【 结局三 · 复行数十步 】\n\n" ..
    "陶潜有诗:『既自以心为形役,奚惆怅而独悲?』\n\n" ..
    "—— 有些路,走了就走了。",
  choices = {
    { label = "回到主菜单",                        next = "menu" },
  }
}

S.end_find_uncle = {
  chapter = "终章 · 客死他乡",
  title   = "客死他乡",
  image   = "scene_river",
  prose =
    "我带着她,原路返回,去找她叔父。\n\n" ..
    "—— 我想把人还给他,银子我也不要了,我就当没接过这活。\n\n" ..
    "可走到孟津渡口的时候,天降大雪,路上不太平。\n\n" ..
    "那夜,我们借宿在河滩边一座破庙里。\n\n" ..
    "半夜里,庙外有动静。\n\n" ..
    "—— 不是流民。\n\n" ..
    "是一伙披着羊皮的人。\n\n" ..
    "我挥刀。挥不动。一刀落空,又一刀落空。\n\n" ..
    "他们人多。我只听得见穗在喊。\n\n" ..
    "—— 后来,一切都没了。\n\n" ..
    "我醒来时,天已经大亮。\n\n" ..
    "我躺在雪地里,左肋有一道很深的伤口。\n\n" ..
    "—— 穗,不在了。\n\n" ..
    "—— 我没找见她。\n\n" ..
    "我爬出破庙,走了三里地,倒在雪里。\n\n" ..
    "—— 再也没起来。\n\n" ..
    "【 结局四 · 客死他乡 】\n\n" ..
    "我后来听人说,她被一个路过的人贩子带走了。\n\n" ..
    "—— 也有人说,她自己走的。\n\n" ..
    "—— 我都不信,也不愿再想。",
  choices = {
    { label = "回到主菜单",                        next = "menu" },
  }
}

-- ---------- 受控更新 ----------
local function asset_url(name)
  local u = app.asset(name)
  if u and u ~= "" then return u end
  return "assets/" .. name
end

local function status_text()
  local marks = {}
  local order = {"soft", "warm", "spoke", "guard_on", "feign", "feign_ok",
                 "told_truth", "silver_left", "wounded", "fought", "spared"}
  for _, k in ipairs(order) do
    if state.flags[k] then
      marks[#marks+1] = FLAG_NAME[k] or k
    end
  end
  if #marks == 0 then return "📜 标记: (无)" end
  return "📜 标记:" .. table.concat(marks, " · ")
end

local function goto_node(n)
  state.node = n
  save_state()
  render()
end

local function click(i)
  if state.node == "menu" then
    if i == 1 then
      local s = load_state()
      if s and S[s.node] then goto_node(s.node) end
    elseif i == 2 then
      state.flags = {}
      goto_node("prologue")
    end
    return
  end
  local node = S[state.node]
  if not node or not node.choices then return end
  local c = node.choices[i]
  if not c then return end
  if c.flag then state.flags[c.flag] = true end
  goto_node(c.next)
end

function render()
  if state.node == "menu" then
    app.set_text("chapter_title", "")
    app.set_visible("chapter_title", false)
    app.set_image("scene_img", asset_url("icon.webp"))
    app.set_visible("scene_img", true)
    app.set_text("prose",
      "明末崇祯六年(1633),腊月。\n\n" ..
      "我,良爷,一个做了十多年脚夫的中年汉子。\n" ..
      "今日接了一单私活:护送一位『亲眷女孩』,从洛阳送往扬州。\n\n" ..
      "三十两纹银。\n" ..
      "千里路程。\n\n" ..
      "—— 这趟活,说不上凶险,却也说不上干净。")
    app.set_text("status", "")
    app.set_visible("status", false)

    local s = load_state()
    if s and S[s.node] then
      app.set_text("c1", "▶ 继续上次剧情(【" .. S[s.node].chapter .. "】)")
      app.set_visible("c1", true)
    else
      app.set_visible("c1", false)
    end
    app.set_text("c2", "▶ 开始新游戏")
    app.set_visible("c2", true)
    app.set_visible("c3", false)
    app.set_visible("c4", false)
    app.set_visible("menu_btn", false)
    return
  end

  local node = S[state.node]
  if not node then
    state.node = "menu"
    save_state()
    return render()
  end

  app.set_text("chapter_title", "【" .. node.chapter .. "】" .. node.title)
  app.set_visible("chapter_title", true)

  if node.image then
    app.set_image("scene_img", asset_url(node.image .. ".webp"))
    app.set_visible("scene_img", true)
  else
    app.set_visible("scene_img", false)
  end

  app.set_text("prose", node.prose)
  app.set_text("status", status_text())
  app.set_visible("status", true)

  for i = 1, 4 do
    local btn = "c" .. i
    local c = node.choices and node.choices[i]
    if c then
      app.set_text(btn, "「" .. c.label .. "」")
      app.set_visible(btn, true)
    else
      app.set_visible(btn, false)
    end
  end
  app.set_visible("menu_btn", true)
end

-- ---------- 静态 page(顶层) ----------
local page = ui.page({
  title = "饿殍:明末千里行",
  children = {
    ui.text({
      id    = "chapter_title",
      text  = "",
      size  = 17,
      color = "#6FACC9",
      margin = 6,
    }),
    ui.image({
      id     = "scene_img",
      url    = asset_url("icon.webp"),
      height = 180,
      margin = 4,
    }),
    ui.text({
      id     = "prose",
      text   = "加载中……",
      size   = 15,
      margin = 6,
    }),
    ui.text({
      id     = "status",
      text   = "",
      size   = 12,
      color  = "#888888",
      margin = 4,
    }),
    ui.spacer({ height = 6 }),
    ui.button({ id = "c1", text = "", margin = 4, on_click = function() click(1) end }),
    ui.button({ id = "c2", text = "", margin = 4, on_click = function() click(2) end }),
    ui.button({ id = "c3", text = "", margin = 4, on_click = function() click(3) end }),
    ui.button({ id = "c4", text = "", margin = 4, on_click = function() click(4) end }),
    ui.spacer({ height = 8 }),
    ui.button({
      id    = "menu_btn",
      text  = "↩ 返回主菜单",
      margin = 4,
      on_click = function() goto_node("menu") end,
    }),
  }
})

-- 触发首次渲染
render()

return page
⬇ 下载 CIP 文件 🔍 返回列表