==========================================================

《雨夜,他没让你走》—— 单文件完整版

含:主剧本 / 限时选项 / 手机聊天 / 双轴 HUD /

CG图鉴 / 结局收集 / 沈亦舟线 / 续章 /

BAD END 强制触发 / 多周目记忆

==========================================================

==========================================================

一、定义区

==========================================================

define AUDIO = False

---------- 角色 ----------

define mc = Character("我", color="#d8b0c0") define l = Character("林砚", color="#8fa8d8", who_outlines=[(2, "#00000080")]) define shen = Character("沈亦舟", color="#a8d8a8", who_outlines=[(2, "#00000080")]) define n = Character("同桌", color="#999999") define v = Character(None, what_color="#b8b8c8", what_italic=True)

---------- 全局变量 ----------

default aff = 0 default danger = 0 default aff_shen = 0 default flag_phone = False default show_aff = False default bad_end_triggered = False default danger_warn = 10 default danger_break = 16 default danger_lock = 22 default chat_log = []

---------- 持久化数据 ----------

default persistent.cg_unlocked = [] default persistent.endings_unlocked = [] default persistent.shen_unlocked = False default persistent.playthrough = 0 default persistent.unlocked_memories = []

---------- 转场与特效 ----------

define shake_s = Shake((0, 0, 0, 0), 0.4, dist=12) define shake_h = Shake((0, 0, 0, 0), 0.6, dist=28)

---------- 立绘位置 ----------

transform stand: xalign 0.5 yalign 1.0

transform stand_left: xalign 0.22 yalign 1.0

transform stand_right: xalign 0.78 yalign 1.0

transform stand_center: xalign 0.5 yalign 1.0

---------- 占位图(换成真图:把 Solid(...) 换成 "images/xxx.png")----------

image bg black = Solid("#000000") image bg rain = Solid("#141a26") image bg door = Solid("#1a1a22") image bg school = Solid("#22242e") image bg roof = Solid("#2a1e28")

image lin normal = Solid("#3d4a63", xsize=340, ysize=700) image lin close = Solid("#5a3d4a", xsize=700, ysize=700) image lin smile = Solid("#4a5d7a", xsize=340, ysize=700) image lin dark = Solid("#241a24", xsize=340, ysize=700)

image shen normal = Solid("#3a4a3a", xsize=340, ysize=700) image shen close = Solid("#4a5a4a", xsize=700, ysize=700) image shen dark = Solid("#1a2a1a", xsize=340, ysize=700)

==========================================================

二、init python(所有逻辑)

==========================================================

init python: # ---------- 音轨 ---------- renpy.music.register_channel("heartbeat", mixer="sfx", loop=True)

# ---------- CG 数据 ----------
CG_LIST = [
    ("cg_rain",  "雨夜·伞下",   "images/cg/cg_rain.png",  "#1e2638"),
    ("cg_hand",  "指尖·冰凉",   "images/cg/cg_hand.png",  "#2a1e28"),
    ("cg_roof",  "天台·失控",   "images/cg/cg_roof.png",  "#3a1e1e"),
    ("cg_shen",  "书库·靠近",   "images/cg/cg_shen.png",  "#1e2a1e"),
    ("cg_lock",  "笼·门后",     "images/cg/cg_lock.png",  "#0a0a0a"),
    ("cg_rain2", "共犯·雨里",   "images/cg/cg_rain2.png", "#141a26"),
    ("cg_after", "续章·清晨",   "images/cg/cg_after.png", "#2a2218"),
]

# ---------- 结局数据 ----------
ENDING_LIST = [
    ("end_cage",   "笼",   "囚禁结局",       "#666666"),
    ("end_co",     "共犯", "真结局",         "#d8a0a0"),
    ("end_heal",   "松手", "救赎结局",       "#8fa8d8"),
    ("end_escape", "未读", "逃离结局",       "#888888"),
    ("end_shen_a", "书页", "沈亦舟线·停留",   "#a8d8a8"),
    ("end_shen_b", "空白", "沈亦舟线·决裂",   "#d8d8a8"),
    ("end_after",  "还早", "续章结局·软",     "#e0c8a0"),
    ("end_hard",   "门外", "续章结局·硬",     "#666666"),
    ("end_dark",   "同归", "续章结局·黑",     "#3a1e1e"),
    ("be_break",   "崩",   "BAD END · 崩坏",  "#8a1e1e"),
    ("be_lock",    "锁",   "BAD END · 囚禁",  "#3a0a0a"),
]

# ---------- 记忆碎片 ----------
MEMORY_LIST = [
    ("mem_1", "第一次见面",   "他第一次看见你,不是在天台。"),
    ("mem_2", "那把伞",       "他买了七把一模一样的伞。只为了给你一把。"),
    ("mem_3", "后排的椅子",   "他曾经在你教室后排,坐了一整个学期。"),
    ("mem_4", "沈亦舟的日记", "他写你的名字,写了三年。"),
    ("mem_5", "通讯录",       "他手机里,只有你一个人。"),
    ("mem_6", "凌晨四点",     "他每天凌晨四点醒来,确认你还在。"),
]

# ---------- 解锁工具 ----------
def unlock_cg(cid):
    if cid not in persistent.cg_unlocked:
        persistent.cg_unlocked.append(cid)

def unlock_ending(eid):
    if eid not in persistent.endings_unlocked:
        persistent.endings_unlocked.append(eid)

def cg_unlocked(cid):
    return cid in persistent.cg_unlocked

def ending_unlocked(eid):
    return eid in persistent.endings_unlocked

def unlock_memory(mid):
    if mid not in persistent.unlocked_memories:
        persistent.unlocked_memories.append(mid)

def memory_unlocked(mid):
    return mid in persistent.unlocked_memories

# ---------- 周目 ----------
def is_ngplus():
    return persistent.playthrough >= 1

def ng_plus_count():
    return persistent.playthrough

def clear_playthrough():
    persistent.playthrough += 1
    store.bad_end_triggered = False

# ---------- HUD 开关 ----------
def toggle_show_aff():
    store.show_aff = not store.show_aff

config.keymap["toggle_aff"] = ["h"]
config.underlay.append(renpy.Keymap(toggle_aff=toggle_show_aff))

# ---------- Bad End 检查 ----------
def check_danger(warn=None, brk=None, lock=None):
    if store.bad_end_triggered:
        return
    w = warn if warn is not None else store.danger_warn
    b = brk  if brk  is not None else store.danger_break
    k = lock if lock is not None else store.danger_lock
    if store.danger >= k:
        store.bad_end_triggered = True
        renpy.jump("be_lock")
    elif store.danger >= b:
        store.bad_end_triggered = True
        renpy.jump("be_break")
    elif store.danger >= w:
        renpy.show_screen("danger_edge")

def danger_rank():
    d = store.danger
    if d >= 22: return ("临界", "#ff2244")
    if d >= 16: return ("危险", "#ff5577")
    if d >= 10: return ("警告", "#ffaa44")
    if d >= 5:  return ("微感", "#cccc66")
    return ("平静", "#888888")

# ---------- 手机聊天工具 ----------
def chat_clear():
    store.chat_log = []
    renpy.restart_interaction()

def chat_add(who, text):
    store.chat_log.append((who, text))
    renpy.restart_interaction()

def chat_typing(delay=1.0, who="him"):
    store.chat_log.append((who, "……"))
    renpy.restart_interaction()
    renpy.pause(delay, hard=False)
    store.chat_log.pop()
    renpy.restart_interaction()

def chat_pop():
    if store.chat_log:
        store.chat_log.pop()
        renpy.restart_interaction()

# ---------- 挂载 overlay ----------
config.overlay_screens.append("aff_hud")
config.overlay_screens.append("main_menu_extra")
config.overlay_screens.append("main_menu_ngplus")

==========================================================

三、Screens

==========================================================

---------- 限时选项 ----------

screen timed_choice(question, choices, timeout=5.0): modal True zorder 100

bar:
    value AnimatedValue(0.0, 1.0, delay=timeout)
    xsize 640
    ysize 4
    xalign 0.5
    ypos 0.68
    left_bar Solid("#ff3355")
    right_bar Solid("#ffffff20")
    thumb None
    bar_invert True

timer timeout action Return("__timeout__")

vbox:
    xalign 0.5
    ypos 0.74
    spacing 14

    text question:
        size 26
        color "#ffffff"
        xalign 0.5
        outlines [(2, "#000000")]

    for label_text, val in choices:
        textbutton label_text:
            action Return(val)
            xsize 560
            text_size 22
            text_xalign 0.5

---------- 危险边缘视觉 ----------

screen danger_edge(): zorder 80 add Solid("#ff000022") timer 1.6 action Hide("danger_edge")

---------- 双轴 HUD ----------

screen aff_hud(): zorder 90 if show_aff: frame: xalign 0.99 yalign 0.02 background "#0a0a0acc" padding (20, 16) vbox: spacing 10

            text "── 状态 ──":
                size 16
                color "#888888"

            hbox:
                spacing 6
                text "♥" size 22 color "#8fa8d8"
                text " 林砚好感":
                    size 20
                    color "#c8d8f0"
                text "[aff]":
                    size 20
                    color "#8fa8d8"

            hbox:
                spacing 6
                text "☠" size 22 color "#ff5577"
                text " 危险值":
                    size 20
                    color "#f0c8c8"
                text "[danger]":
                    size 20
                    color "#ff5577"

            if aff_shen > 0 or persistent.shen_unlocked:
                hbox:
                    spacing 6
                    text "♠" size 22 color "#d8a0a0"
                    text " 亦舟好感":
                        size 20
                        color "#f0c8d8"
                    text "[aff_shen]":
                        size 20
                        color "#d8a0a0"

            text "按 H 键收起":
                size 14
                color "#666666"

---------- 手机聊天 ----------

screen phone_chat(contact="林砚", avatar_color="#3d4a63"): modal True zorder 100

add Solid("#00000099")

frame:
    xalign 0.5
    yalign 0.5
    xsize 520
    ysize 760
    background "#0a0a0a"
    padding (0, 0)

    vbox:
        spacing 0

        frame:
            xsize 520
            ysize 42
            background "#1a1a1a"
            hbox:
                text "9:41":
                    size 16
                    color "#ffffff"
                    xpos 24
                    yalign 0.5
                text "5G  ▮▮▮":
                    size 13
                    color "#ffffff"
                    xpos 400
                    yalign 0.5

        frame:
            xsize 520
            ysize 64
            background "#262626"
            hbox:
                xpos 16
                yalign 0.5
                spacing 12
                frame:
                    xsize 40
                    ysize 40
                    background avatar_color
                text contact:
                    size 22
                    color "#ffffff"
                    yalign 0.5

        frame:
            xsize 520
            ysize 540
            background "#101010"
            padding (14, 14)

            viewport:
                yinitial 1.0
                mousewheel True
                draggable True
                scrollbars None

                vbox:
                    spacing 12
                    for who, text in chat_log:
                        $ is_me = (who == "me")
                        hbox:
                            if is_me:
                                null width 110
                                frame:
                                    background "#3d6b3d"
                                    padding (14, 10)
                                    text text:
                                        size 18
                                        color "#ffffff"
                            else:
                                frame:
                                    background "#2a2a2a"
                                    padding (14, 10)
                                    text text:
                                        size 18
                                        color "#ffffff"
                                null width 110

        frame:
            xsize 520
            ysize 114
            background "#1a1a1a"
            text "—— 选择你的回复 ——":
                size 13
                color "#555555"
                xalign 0.5
                yalign 0.5

screen phone_choice(items): zorder 110 vbox: xalign 0.5 ypos 0.78 spacing 8 for i in items: textbutton i.caption: action i.action xsize 460 background "#1f3f1f" hover_background "#2f5f2f" padding (12, 10) text_size 18 text_color "#ffffff" text_xalign 0.5

---------- CG 图鉴 ----------

screen cg_gallery(): tag menu add Solid("#0a0a0a")

vbox:
    xalign 0.5
    ypos 0.05
    spacing 20
    text "CG 图鉴":
        size 40
        color "#ffffff"
        xalign 0.5

grid 4 2:
    xalign 0.5
    ypos 0.22
    spacing 16

    for cid, name, path, color in CG_LIST:
        if cg_unlocked(cid):
            button:
                action Show("cg_viewer", cid=cid, name=name, path=path, color=color)
                xsize 240
                ysize 135
                background color
                hover_background "#ffffff40"
                text name:
                    size 16
                    color "#ffffff"
                    xalign 0.5
                    yalign 0.9
        else:
            frame:
                xsize 240
                ysize 135
                background "#1a1a1a"
                text "???":
                    size 24
                    color "#333333"
                    xalign 0.5
                    yalign 0.5

textbutton "返回":
    action Return()
    xalign 0.5
    yalign 0.95
    text_size 24

screen cg_viewer(cid, name, path, color): modal True zorder 200 add Solid("#000000") add color text name: xalign 0.5 yalign 0.08 size 32 color "#ffffff" textbutton "关闭": action Hide("cg_viewer") xalign 0.95 yalign 0.05 text_size 22

---------- 结局收集 ----------

screen ending_gallery(): tag menu add Solid("#0a0a0a")

vbox:
    xalign 0.5
    ypos 0.05
    spacing 20
    text "结局收集":
        size 40
        color "#ffffff"
        xalign 0.5
    text "已解锁 [len(persistent.endings_unlocked)] / [len(ENDING_LIST)]":
        size 20
        color "#888888"
        xalign 0.5

vbox:
    xalign 0.5
    ypos 0.2
    spacing 10

    for eid, name, desc, color in ENDING_LIST:
        if ending_unlocked(eid):
            frame:
                xsize 620
                background "#151515"
                padding (24, 12)
                hbox:
                    spacing 20
                    text name size 24 color color
                    text desc size 18 color "#999999"
                    text "✓" size 22 color "#66cc66"
        else:
            frame:
                xsize 620
                background "#0e0e0e"
                padding (24, 12)
                hbox:
                    spacing 20
                    text "???" size 24 color "#333333"
                    text "未解锁" size 18 color "#333333"

textbutton "返回":
    action Return()
    xalign 0.5
    yalign 0.95
    text_size 24

---------- 记忆碎片 ----------

screen memory_gallery(): tag menu add Solid("#0a0a0a")

vbox:
    xalign 0.5
    ypos 0.05
    spacing 16
    text "记忆碎片":
        size 40
        color "#ffffff"
        xalign 0.5
    text "已解锁 [len(persistent.unlocked_memories)] / [len(MEMORY_LIST)]":
        size 18
        color "#777777"
        xalign 0.5

vbox:
    xalign 0.5
    ypos 0.22
    spacing 12

    for mid, name, desc in MEMORY_LIST:
        if memory_unlocked(mid):
            frame:
                xsize 660
                background "#141414"
                padding (24, 14)
                vbox:
                    spacing 4
                    text name size 22 color "#d8b0c0"
                    text desc size 16 color "#999999"
        else:
            frame:
                xsize 660
                background "#0e0e0e"
                padding (24, 14)
                vbox:
                    spacing 4
                    text "???" size 22 color "#2a2a2a"
                    text "—— 未解锁 ——" size 16 color "#222222"

textbutton "返回":
    action Return()
    xalign 0.5
    yalign 0.95
    text_size 24

---------- 主菜单入口 ----------

screen main_menu_extra(): zorder 100 vbox: xalign 0.05 yalign 0.1 spacing 12 textbutton "CG 图鉴": action ShowMenu("cg_gallery") text_size 22 text_color "#cccccc" text_hover_color "#ffffff" textbutton "结局收集": action ShowMenu("ending_gallery") text_size 22 text_color "#cccccc" text_hover_color "#ffffff"

screen main_menu_ngplus(): zorder 100 vbox: xalign 0.05 yalign 0.22 spacing 12 textbutton "记忆碎片": action ShowMenu("memory_gallery") text_size 22 text_color "#cccccc" text_hover_color "#ffffff"

==========================================================

四、剧本

==========================================================

---------- 序章 ----------

label start: scene bg black with fade

if is_ngplus():
    $ renpy.notify("二周目 · 记忆碎片已激活")
    $ unlock_memory("mem_1")
    v "“又见面了。”"
    v "“这一次,你会做同样的选择吗?”"
    $ unlock_memory("mem_2")

if AUDIO:
    play music "rain_loop.ogg" fadein 2.0

"雨下得很大。"
"我没带伞,也没人接我。"
"巷子很窄,路灯坏了一半,光一闪一闪的。"
"我加快脚步。"
"身后的脚步声,也快了。"
"——不,是我快了,他也快了。"

scene bg rain
with Dissolve(1.0)

if AUDIO:
    stop music fadeout 1.0
    play heartbeat "heartbeat.ogg" fadein 0.5

"我停下。"
"他也停下。"
"我回头。"

show lin normal at stand
with Dissolve(0.4)

$ unlock_cg("cg_rain")

with shake_s
"伞沿滴着水,落在我的鞋尖上。"
"他离我很近。"
"近到我能闻到他身上的味道——雨水、烟草,还有一点点血腥味。"

l "这么晚,去哪了?"
"他笑得很轻。"
"像在问一句无关紧要的话。"

---------- 限时选项 1 ----------

label q1: $ r = renpy.call_screen("timed_choice", question = "他在等你回答。", choices = [("老实回答", "honest"), ("撒谎", "lie"), ("反问他", "ask")], timeout = 6.0)

if r == "__timeout__":
    jump q1_timeout
elif r == "honest":
    jump q1_honest
elif r == "lie":
    jump q1_lie
else:
    jump q1_ask

label q1_honest: mc "……图书馆。" $ aff += 1 l "嗯。" l "我知道。" $ danger += 1 "他说得很自然。" "可我没告诉过任何人。" jump q1_after

label q1_lie: mc "……便利店。" with shake_h if AUDIO: play sound "thunder.ogg" "他笑了。" l "便利店。" l "你身上一点关东煮的味道都没有。" $ danger += 3 $ flag_phone = True l "再想一次。" "他的手指擦过我的下巴。" "很凉。" jump q1_after

label q1_ask: mc "……你为什么在这里?" $ aff += 2 $ danger += 1 "他愣了一下。" "然后笑得像被戳中了什么。" l "因为你在这里。" l "这个理由,够不够?" jump q1_after

label q1_timeout: "我张了张嘴。" "什么都没说出来。" $ danger += 2 l "……不说话啊。" l "那我就当你默认了。" l "默认你今晚,是故意走这条路的。" jump q1_after

label q1_after: "他撑着伞,往我这边偏了偏。" "伞面很小。" "他半个肩膀,全湿了。"

menu:
    "往他那边靠":
        $ aff += 2
        "我往他那边挪了半步。"
        "他僵了一下。"
        l "……别动。"
        l "再近一点,我就不保证自己还是人了。"

    "保持距离":
        $ danger += 1
        "我没有动。"
        l "……怕我?"
        "他低声笑。"
        l "怕就对了。"

$ check_danger()
jump phone_demo

---------- 手机聊天 ----------

label phone_demo: $ chat_clear() show screen phone_chat("林砚", "#3d4a63")

$ chat_add("him", "在吗")
$ chat_typing(0.6, "him")
$ chat_add("him", "睡了吗")
$ chat_typing(0.9, "him")
$ chat_add("him", "我看到你房间的灯还亮着")

menu (screen="phone_choice"):
    "睡了":
        $ chat_add("me", "睡了")
        $ chat_typing(0.5, "him")
        $ chat_add("him", "骗人")
        $ chat_add("him", "灯还亮着")
        jump phone_demo_2

    "你怎么知道":
        $ chat_add("me", "你怎么知道")
        $ chat_typing(0.8, "him")
        $ chat_add("him", "我一直在楼下")
        $ danger += 2
        jump phone_demo_2

    "(不回复)":
        $ chat_add("me", "……")
        $ chat_typing(1.2, "him")
        $ chat_add("him", "不说话,我就当你默认了")
        $ danger += 1
        jump phone_demo_2

label phone_demo_2: $ chat_typing(0.8, "him") $ chat_add("him", "下来") $ chat_add("him", "给你三分钟")

menu (screen="phone_choice"):
    "好":
        $ chat_add("me", "好")
        $ chat_typing(0.4, "him")
        $ chat_add("him", "乖")
    "不去":
        $ chat_add("me", "不去")
        $ chat_typing(0.6, "him")
        $ chat_add("him", "那我上去")
        $ danger += 2
    "(关机)":
        $ chat_add("me", "……")
        $ chat_add("him", "你关机了。")
        $ chat_add("him", "没关系。")
        $ chat_add("him", "我有钥匙。")
        $ danger += 4

hide screen phone_chat
with Dissolve(0.4)

"手机黑屏了。"
"雨还在下。"

$ check_danger()
jump ch1_door

---------- 第一章 ----------

label ch1_door: scene bg door with fade

if AUDIO:
    stop heartbeat fadeout 1.0
    play music "tense.ogg" fadein 1.5

show lin normal at stand
with Dissolve(0.4)

"到了楼下。"
"我伸手去掏钥匙。"
"他忽然按住我的手腕。"

$ unlock_cg("cg_hand")

with shake_s
l "手机,给我。"

label q2: $ r = renpy.call_screen("timed_choice", question = "他的手指没有松开。", choices = [("把手机给他", "give"), ("拒绝", "refuse"), ("问他为什么", "why")], timeout = 4.0)

if r == "__timeout__":
    jump q2_timeout
elif r == "give":
    jump q2_give
elif r == "refuse":
    jump q2_refuse
else:
    jump q2_why

label q2_give: $ danger += 2 $ aff -= 1 "我把手机递了过去。" "他接过去,当着我的面,划开屏幕。" "通讯录里,一个名字消失了。" l "好了。" l "干净了。" jump q2_after

label q2_refuse: $ aff += 2 $ danger += 2 mc "不给。" "他盯着我看了很久。" l "……好。" l "那你记住了。" l "从今天起,你的手机里,最好只有我一个人。" jump q2_after

label q2_why: $ danger += 1 $ aff += 1 mc "为什么?" l "因为我想知道,你的一天里,有多少是我不知道的。" l "全部。" l "我想知道全部。" jump q2_after

label q2_timeout: $ danger += 3 "我没有动。" "他也没有动。" "我们就这样僵持着。" "最后,是他先松了手。" l "……算了。" l "今天先放过你。" jump q2_after

label q2_after: if flag_phone: with shake_h l "对了。" l "你刚才说,你去便利店。" "他把手机塞回我手里。" l "下次撒谎,记得把味道也换了。" $ danger += 2

"他转身走进雨里。"
"走了两步,又停下。"

show lin smile at stand
with Dissolve(0.3)

l "明天,别迟到。"
"——我没告诉过他,我几点上课。"

hide lin
with Dissolve(0.4)

$ check_danger()
jump ch2

---------- 第二章 ----------

label ch2: scene bg school with fade

if AUDIO:
    stop music fadeout 1.0
    play music "school.ogg" fadein 1教室.5

"第二天。都"
"我的桌上,放着一份早餐。安静"
"还有一把伞。"
"——不是我昨天弄丢的那把,是新的。"

了 show shen normal at stand_right 。 with Dissolve(0.4)

shen "你的伞,昨天落在图书馆了。"
"他把一把伞放在我桌上。"
"和林砚送的那把,一模一样。"

n "喂,昨天有人来问过你的课表。"
n "就是三班那个林砚。"
n "他不是出了名的不理人吗……"

"话音未落。"
"教室门口安静了一瞬。"

hide shen
show lin dark at stand
with Dissolve(0.4)

"他站在那里。"
"目光落在我桌上。"
"两把一模一样的伞。"

with shake_s
l "……"

label q3: $ r = renpy.call_screen("timed_choice", question = "空气凝固了。", choices = [("解释", "explain"), ("沉默", "silent"), ("收下沈亦舟的伞", "take")], timeout = 5.0)

if r == "__timeout__":
    jump q3_silent
elif r == "explain":
    jump q3_explain
elif r == "take":
    jump q3_take
else:
    jump q3_silent

label q3_explain: mc "伞是沈亦舟还给我的。" mc "我昨天落在图书馆了。" $ aff += 2 $ danger += 1 "林砚看了沈亦舟一眼。" "那一眼很平。" "平得像一潭水,下面全是东西。" l "嗯。" l "知道了。" "他把自己的那把伞,收进了书包。" jump q3_menu

label q3_silent: "我什么都没说。" $ danger += 3 "他也没说。" "他只是走过来,把那两把伞,一起拿走了。" "一把扔进垃圾桶。" "一把放进我的桌肚。" l "以后,只准用我给的。" jump q3_menu

label q3_take: "我伸手,拿起了沈亦舟的那把。" with shake_h $ danger += 5 $ aff -= 2 "整个" "林砚笑了。" "那是我第一次见他笑得那么真。" l "……好。" l "真好。" "他转身走了。" "沈亦舟看了我一眼,欲言又止。" jump q3_menu

label q3_menu: $ check_danger()

menu:
    "追出去,找林砚":
        jump ch3_roof
    "留下来,和沈亦舟说话":
        $ aff_shen += 2
        jump shen_route

---------- 终章 · 天台 ----------

label ch3_roof: scene bg roof with fade

if AUDIO:
    stop music fadeout 1.0
    play heartbeat "heartbeat.ogg" fadein 1.0
    play music "dark.ogg" fadein 2.0

if is_ngplus():
    $ unlock_memory("mem_3")
    v "“他曾经在你教室后排,坐了一整个学期。”"
    v "“你从来没回头。”"

"放学后,我在天台被他堵住。"

show lin close at stand
with Dissolve(0.5)

$ unlock_cg("cg_roof")

l "那把伞,你收了。"
"他的声音很轻。"
"轻得像在说一件和自己无关的事。"

l "我数过。"
l "从你收下那把伞,到现在,一共是四小时十七分钟。"
l "这四小时十七分钟里,我一直在想一件事。"

with shake_h
l "——要不要,把你锁起来。"

"风很大。"
"吹得我眼睛发酸。"

"我看着他。"
"他看着我。"

"然后我听见自己说——"

label ending_check: "[好感度 [aff] / 危险值 [danger]]" with Dissolve(0.5)

if danger >= 12 and aff < 8:
    jump end_cage
elif danger >= 8 and aff >= 8:
    jump end_co
elif aff >= 10 and danger < 8:
    jump end_heal
else:
    jump end_escape

---------- 结局 A:笼 ----------

label end_cage: scene bg black with fade if AUDIO: stop heartbeat fadeout 2.0

$ unlock_cg("cg_lock")

"他没让我回家。"
"我也没有反抗。"
"那扇门关上的时候,我听见自己心跳的声音。"
"很响。"
"响得像在鼓掌。"

$ unlock_ending("end_cage")
$ clear_playthrough()
"{color=#666666}—— END A · 笼{/color}"
return

---------- 结局 B:共犯(接续章)----------

label end_co: if AUDIO: stop heartbeat fadeout 1.5

$ unlock_cg("cg_rain2")

l "我们都有病。"
mc "我知道。"
l "那就一起病着。"
"他把我拉进怀里。"
"很用力。"
"用力到我几乎不能呼吸。"

if AUDIO:
    play music "rain_loop.ogg" fadein 2.0

"雨又下起来了。"
"这一次,我没有躲。"

$ unlock_ending("end_co")
jump after_co

---------- 结局 C:松手 ----------

label end_heal: if AUDIO: stop heartbeat fadeout 2.0

mc "林砚。"
mc "你松手。"
l "……"
mc "然后我教你怎么好好爱人。"
"他愣住了。"
"很久很久。"
"久到我以为他不会回答了。"
"然后,他松开了手。"

if AUDIO:
    play music "heal.ogg" fadein 2.0

"那天的风很轻。"
"他站在我面前,像一个终于学会呼吸的人。"

$ unlock_ending("end_heal")
$ clear_playthrough()
"{color=#8fa8d8}—— END C · 松手{/color}"
return

---------- 结局 D:未读 ----------

label end_escape: scene bg black with fade if AUDIO: stop heartbeat fadeout 2.0

"我转学了。"
"换了号码,换了城市。"
"半年后,我收到一封信。"
"没有署名。"
"只有一句话——"

"{color=#888888}“我还在找。”{/color}"

$ unlock_ending("end_escape")
$ clear_playthrough()
"{color=#666666}—— END D · 未读{/color}"
return

==========================================================

五、沈亦舟线

==========================================================

label shen_route: $ persistent.shen_unlocked = True

scene bg school
with fade

if AUDIO:
    stop music fadeout 1.0
    play music "shen.ogg" fadein 2.0

"林砚走了。"
"教室里的空气还没缓过来。"

show shen normal at stand_center
with Dissolve(0.4)

shen "……你还好吗?"
"沈亦舟的声音很轻。"
"轻得像怕惊动什么。"

mc "……嗯。"
shen "那把伞,是我多事了。"
shen "对不起。"
"他垂下眼睛。"
"睫毛很长,投下一小片阴影。"

menu:
    "安慰他":
        $ aff_shen += 2
        mc "不是你的错。"
        shen "……谢谢。"
        "他抬起头,笑了一下。"
        "很干净的笑。"
        "干净得让人放心。"
    "问他怎么知道我的课表":
        $ aff_shen += 3
        $ danger += 1
        mc "你怎么知道我伞落在图书馆了?"
        shen "……"
        "他没有马上回答。"
        shen "我经常看到你。"
        shen "在各种地方。"
        shen "你可能没注意到我。"
        "他顿了顿。"
        shen "不过没关系。"
        shen "我记得就够了。"

$ check_danger()
jump shen_ch2

label shen_ch2: scene bg school with fade

if AUDIO:
    stop music fadeout 1.0
    play music "shen.ogg" fadein 2.0

"接下来的三天。"
"林砚没来上课。"
"沈亦舟开始出现在我身边。"

show shen normal at stand_center
with Dissolve(0.4)

shen "书库缺人整理,你要来吗?"
shen "只有我们两个人。"
shen "很安静。"

menu:
    "答应":
        $ aff_shen += 2
        jump shen_library
    "拒绝":
        $ aff_shen -= 1
        $ danger += 1
        mc "不了,我还有事。"
        "他笑了一下。"
        shen "……好。"
        shen "那明天呢?"
        shen "后天?"
        shen "总有一天你会有空的。"
        "他语气温和。"
        "温和得让人无法拒绝。"
        $ aff_shen += 1
        jump shen_library

label shen_library: scene bg door with fade

"书库在最顶层。"
"没有窗,只有一盏旧灯。"
"门关上时,声音很轻。"

show shen normal at stand_center
with Dissolve(0.5)

$ unlock_cg("cg_shen")

shen "给你。"
"他递给我一本书。"
"封面很旧。"
"《如何照顾一个人》。"

mc "……这是什么?"
shen "我看的书。"
shen "从很久以前就开始看了。"
shen "因为我想知道,怎么才能让别人不离开我。"

"他看着我。"
"眼神安静得可怕。"

shen "林砚那套方法,是错的。"
shen "锁住一个人,只会让她想逃。"
shen "我不会那样。"
shen "我会让你自己走回来。"

menu:
    "问他为什么是我":
        $ aff_shen += 2
        mc "为什么是我?"
        shen "因为你从来不看别人。"
        shen "你只看着自己脚下。"
        shen "所以我想成为,你抬头时看见的第一个人。"

    "站起来想走":
        $ danger += 2
        "我站起来。"
        "他没有拦我。"
        "只是轻轻说了一句——"
        shen "门锁了。"
        shen "别急。"
        shen "我们还有时间。"

    "靠近他":
        $ aff_shen += 3
        $ danger += 1
        "我往前走了一步。"
        "他愣了一下。"
        shen "……你不怕?"
        mc "不怕。"
        shen "那你很快就要怕了。"
        "他伸手,指尖碰到我的发梢。"
        shen "因为我会比林砚,更慢,更久。"

$ check_danger()
jump shen_ch3

label shen_ch3: scene bg roof with fade

if AUDIO:
    stop music fadeout 1.0
    play heartbeat "heartbeat.ogg" fadein 1.0
    play music "tense.ogg" fadein 2.0

"第二天。"
"天台的门被人从外面推开。"

show shen normal at stand_right
with Dissolve(0.4)

shen "你来了。"

show lin dark at stand_left
with Dissolve(0.4)

l "……你们,在这里做什么。"

"两个人都没有看对方。"
"都在看我。"

with shake_s
l "过来。"
shen "别过去。"

"空气里像有电流。"

label shen_final_choice: $ r = renpy.call_screen("timed_choice", question = "只有 4 秒。", choices = [("走向林砚", "lin"), ("走向沈亦舟", "shen"), ("谁都不走", "none")], timeout = 4.0)

if r == "__timeout__":
    jump shen_none
elif r == "lin":
    jump shen_lin
elif r == "shen":
    jump shen_shen
else:
    jump shen_none

label shen_lin: $ danger += 2 $ aff += 2 "我走向林砚。" show shen dark at stand_right shen "……" "沈亦舟笑了。" shen "好。" shen "那我等。" shen "我最擅长的,就是等。" hide shen with Dissolve(0.4) l "……你选了我。" l "那就别后悔。" jump end_co

label shen_shen: $ aff_shen += 3 $ aff -= 1 "我走向沈亦舟。" show lin dark at stand_left "林砚没有动。" l "……嗯。" l "我知道了。" "他转身。" "背影很直。" hide lin with Dissolve(0.4) shen "谢谢。" shen "你放心。" shen "我不会像他那样。" shen "我会让你,慢慢地,再也走不掉。" jump end_shen_a

label shen_none: "我没有动。" "谁都没有走。" with shake_h l "……你想清楚了。" shen "我也一样。" "两个人几乎同时说话。" "然后同时沉默。" "风很大。" "我听见自己心跳的声音。"

if aff_shen >= 6:
    jump end_shen_a
else:
    jump end_shen_b

---------- 结局 E:书页 ----------

label end_shen_a: scene bg black with fade $ unlock_ending("end_shen_a") if AUDIO: stop heartbeat fadeout 2.0 play music "shen.ogg" fadein 2.0

"后来我搬进了沈亦舟住的那条街。"
"每天他都会在楼下等我。"
"风雨无阻。"
"他的伞,永远比我大一点。"
"他的步子,永远比我慢半步。"
"他说这样可以看着我。"
"我说好。"

"很久以后,我才意识到——"
"我已经想不起来,自己上次一个人出门,是什么时候了。"

$ clear_playthrough()
"{color=#a8d8a8}—— END E · 书页{/color}"
return

---------- 结局 F:空白 ----------

label end_shen_b: scene bg black with fade $ unlock_ending("end_shen_b") if AUDIO: stop heartbeat fadeout 2.0

"那天以后,沈亦舟再也没出现在我身边。"
"林砚也没有。"
"两个人都像从未存在过。"

"只有一件事变了。"
"我再也没有一个人走过夜路。"

"因为我总觉得——"
"身后还有脚步声。"

$ clear_playthrough()
"{color=#d8d8a8}—— END F · 空白{/color}"
return

==========================================================

六、续章 · 共犯之后

==========================================================

label after_co: scene bg black with fade

if AUDIO:
    stop heartbeat fadeout 2.0
    play music "rain_loop.ogg" fadein 2.0

"从那天起,我和林砚住在了同一个屋檐下。"
"他没锁门。"
"但他没收了我的伞。"

scene bg door
with fade

show lin normal at stand_center
with Dissolve(0.5)

"第一周,一切正常。"
"他做饭,我洗碗。"
"他送我上学,接我回家。"
"像一对普通情侣。"

"第二周,我开始发现——"
"家里的窗帘,从来没有拉开过。"

l "外面太吵。"
l "你不觉得吗?"

menu:
    "附和他":
        $ aff += 1
        $ danger += 1
        mc "……嗯。"
        "他笑了。"
        l "乖。"
    "拉开窗帘":
        $ danger += 2
        $ aff -= 1
        "我走到窗边。"
        "伸手。"
        with shake_s
        "他按住我的手。"
        l "别。"
        l "光太亮。"
        l "你会看清楚很多东西。"
        l "看了,就回不去了。"

jump after_ch2

label after_ch2: scene bg school with fade

if AUDIO:
    stop music fadeout 1.0
    play music "tense.ogg" fadein 2.0

"那天在学校,有人塞给我一封信。"
"没有署名。"
"里面只有一句话——"

v "“他还好吗?”"

"字迹很干净。"
"像刚洗过的手。"

show lin dark at stand_center
with Dissolve(0.4)

"放学的时候,林砚站在校门口。"
"手里拿着一把新伞。"
"和信纸一样的颜色。"

l "今天有人给你东西了吗。"

menu:
    "说实话":
        $ aff += 2
        mc "……有。一封信。"
        "他看了我很久。"
        l "……很好。"
        l "你学会不骗我了。"
        l "这封信,我不追究。"
        l "但有下一次——"
        "他没说完。"
    "说没有":
        $ danger += 3
        mc "没有。"
        "他笑了。"
        "笑得比雨还冷。"
        l "……嗯。"
        l "那就当没有。"
        "他把伞递给我。"
        "伞柄上,绕着一根头发。"
        "是下午那封信上的,颜色。"
        $ unlock_cg("cg_after")

$ check_danger()
jump after_ch3

label after_ch3: scene bg black with fade

if AUDIO:
    stop heartbeat "heartbeat.ogg" fadein 1.0

"那天晚上,我做了一个梦。"
"梦里有人问我——"

v "“你为什么不走?”"

"我想了很久。"
"然后我听见自己说——"

menu:
    "“因为我怕他一个人。”":
        $ aff += 2
        jump after_end_soft
    "“因为我不知道还能去哪。”":
        $ danger += 2
        jump after_end_hard
    "“因为我想看看,他到底能坏到什么程度。”":
        $ danger += 4
        $ aff += 1
        jump after_end_dark

---------- 结局 G:还早 ----------

label after_end_soft: scene bg black with fade $ unlock_ending("end_after") if AUDIO: stop heartbeat fadeout 2.0 play music "heal.ogg" fadein 2.0

"我醒来的时候,他坐在床边。"
"眼睛是红的。"
"像一夜没睡。"

show lin normal at stand_center
with Dissolve(0.5)

l "……你说梦话了。"
mc "我说什么了?"
l "你说,你怕我一个人。"
"他低下头。"
l "……对不起。"
"那是他第一次道歉。"

"也许有些病,可以一起治好。"
"也许不行。"
"但至少今天早上——"
"窗帘,是拉开的。"

$ clear_playthrough()
"{color=#e0c8a0}—— END G · 还早{/color}"
return

---------- 结局 H:门外 ----------

label after_end_hard: scene bg black with fade $ unlock_ending("end_hard")

"我醒来的时候,房间是空的。"
"桌上放着一把钥匙,和一张纸。"
"纸上只有一行字——"

v "“去吧。”"

"我拿起钥匙。"
"走到门口。"
"门没有锁。"

"我推开门。"
"外面是雨。"
"很大。"

"我站了很久。"
"最后,我把门又关上了。"

$ clear_playthrough()
"{color=#666666}—— END H · 门外{/color}"
return

---------- 结局 I:同归 ----------

label after_end_dark: scene bg black with fade $ unlock_ending("end_dark") if AUDIO: stop heartbeat fadeout 1.0 play music "dark.ogg" fadein 2.0

"他在黑暗里笑出了声。"
"很轻。"

show lin dark at stand_center
with Dissolve(0.5)

l "……你终于,和我一样了。"
"他伸手,摸到我的脸。"
l "别怕。"
l "我不会让你出去的。"
l "但我也不会一个人出去。"

"那扇门,从那以后,再也没有打开过。"

$ clear_playthrough()
"{color=#3a1e1e}—— END I · 同归{/color}"
return

==========================================================

七、BAD END

==========================================================

label be_break: scene bg black with fade if AUDIO: stop music fadeout 1.0 play music "dark.ogg" fadein 2.0

"我听见什么东西断了。"
"不是骨头。"
"是比骨头更里面的东西。"

"他站在门口,背对着光。"
"看不清表情。"

v "“你不该走到这一步的。”"
v "“但既然到了——”"

with shake_h

"门合上了。"
"声音很轻。"

$ unlock_ending("be_break")
$ clear_playthrough()
"{color=#8a1e1e}—— BAD END · 崩{/color}"
return

label be_lock: scene bg black with fade if AUDIO: stop music fadeout 1.0 play music "dark.ogg" fadein 2.0

"钥匙落进雨里的声音,很轻。"
"轻得像一句告白。"

"他没有回头。"
"我也没有喊。"

v "“从今天起,你不用再走出去了。”"
v "“外面太危险。”"

with shake_h

"窗帘再也没有拉开过。"
"日历停在那一天。"
"我们都没有提过‘以后’这两个字。"

$ unlock_ending("be_lock")
$ clear_playthrough()
"{color=#3a0a0a}—— BAD END · 锁{/color}"
return