################################################################################ ## 初始化 ################################################################################ init offset = -1 ################################################################################ ## 样式 ################################################################################ style default: properties gui.text_properties() language gui.language style input: properties gui.text_properties("input", accent=True) adjust_spacing False style hyperlink_text: properties gui.text_properties("hyperlink", accent=True) hover_underline True style gui_text: properties gui.text_properties("interface") style button: properties gui.button_properties("button") style button_text is gui_text: properties gui.text_properties("button") yalign 0.5 style label_text is gui_text: properties gui.text_properties("label", accent=True) style prompt_text is gui_text: properties gui.text_properties("prompt") style bar: ysize gui.bar_size left_bar Frame("gui/bar/left.png", gui.bar_borders, tile=gui.bar_tile) right_bar Frame("gui/bar/right.png", gui.bar_borders, tile=gui.bar_tile) style vbar: xsize gui.bar_size top_bar Frame("gui/bar/top.png", gui.vbar_borders, tile=gui.bar_tile) bottom_bar Frame("gui/bar/bottom.png", gui.vbar_borders, tile=gui.bar_tile) style scrollbar: ysize gui.scrollbar_size base_bar Frame("gui/scrollbar/horizontal_[prefix_]bar.png", gui.scrollbar_borders, tile=gui.scrollbar_tile) thumb Frame("gui/scrollbar/horizontal_[prefix_]thumb.png", gui.scrollbar_borders, tile=gui.scrollbar_tile) style vscrollbar: xsize gui.scrollbar_size base_bar Frame("gui/scrollbar/vertical_[prefix_]bar.png", gui.vscrollbar_borders, tile=gui.scrollbar_tile) thumb Frame("gui/scrollbar/vertical_[prefix_]thumb.png", gui.vscrollbar_borders, tile=gui.scrollbar_tile) style slider: ysize gui.slider_size base_bar Frame("gui/slider/horizontal_[prefix_]bar.png", gui.slider_borders, tile=gui.slider_tile) thumb "gui/slider/horizontal_[prefix_]thumb.png" style vslider: xsize gui.slider_size base_bar Frame("gui/slider/vertical_[prefix_]bar.png", gui.vslider_borders, tile=gui.slider_tile) thumb "gui/slider/vertical_[prefix_]thumb.png" style frame: padding gui.frame_borders.padding background Frame("gui/frame.png", gui.frame_borders, tile=gui.frame_tile) ################################################################################ ## 游戏内屏幕 ################################################################################ ## 对话屏幕 ######################################################################## ## ## 对话屏幕用于向用户显示对话。它需要两个参数,who 和 what,分别是叙述角色的名字 ## 和所叙述的文本。(如果没有名字,参数 who 可以是 None。) ## ## 此屏幕必须创建一个 id 为 what 的文本可视控件,因为 Ren'Py 使用它来管理文本显 ## 示。它还可以创建 id 为 who 和 id 为 window 的可视控件来应用样式属性。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#say screen say(who, what): style_prefix "say" # 主文本框 window: id "window" background "gui/textbox_bg.png" xalign 0.5 yalign 1.0 yoffset -30 # 距离底部30像素 xsize 1400 ysize 240 text what: id "what" xpos 60 ypos 30 xsize 1280 color "#FFFFFF" outlines [(2, "#000000", 0, 0)] # 黑色描边 # 名字框 - 悬浮在文本框上方 if who is not None: window: id "namebox" background "gui/namebox_bg.png" xpos 480 # 左右位置 ypos 811 # 上下位置 (根据实际调整) xsize 180 ysize 50 text who: id "who" xalign 0.5 yalign 0.5 color "#FFFFFF" outlines [] ## 通过 Character 对象使名称框可用于样式化。 init python: config.character_id_prefixes.append('namebox') ## 齿轮图标 Displayable 类 import math import pygame class GearDisplayable(renpy.Displayable): """ 创建一个现代简洁风格的齿轮图标 Displayable 参数: - size: 图标大小(像素) - color: 齿轮颜色(十六进制) - alpha: 透明度 (0.0-1.0) """ def __init__(self, size=48, color="#FFFFFF", alpha=0.85): super(GearDisplayable, self).__init__() self.size = size self.color = color self.alpha = alpha def render(self, width, height, st, at): # 创建渲染对象 render = renpy.Render(self.size, self.size) # 创建surface surface = pygame.Surface((self.size, self.size), pygame.SRCALPHA) # 解析颜色 color_rgb = tuple(int(self.color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) color_with_alpha = color_rgb + (int(self.alpha * 255),) center = self.size // 2 outer_radius = self.size // 2 - 4 # 外圈半径 inner_radius = int(outer_radius * 0.4) # 内圆半径 tooth_height = int(outer_radius * 0.25) # 齿高 num_teeth = 8 # 齿数 # 绘制齿轮路径点 points = [] for i in range(num_teeth * 2): angle = (i * 360 / (num_teeth * 2)) * math.pi / 180 if i % 2 == 0: # 齿尖 radius = outer_radius else: # 齿根 radius = outer_radius - tooth_height x = center + int(radius * math.cos(angle)) y = center + int(radius * math.sin(angle)) points.append((x, y)) # 绘制齿轮外圈 pygame.draw.polygon(surface, color_with_alpha, points, 0) # 绘制内圆(镂空效果) pygame.draw.circle(surface, (0, 0, 0, 0), (center, center), inner_radius) # 绘制中心圆(增强设计感) center_circle_radius = int(inner_radius * 0.5) pygame.draw.circle(surface, color_with_alpha, (center, center), center_circle_radius, 0) # 将surface绘制到render render.blit(surface, (0, 0)) return render def create_settings_gear_hover(size=96): """创建hover状态的齿轮(更亮)""" return GearDisplayable(size=size, color="#FFFFFF", alpha=1.0) def create_settings_gear_idle(size=96): """创建idle状态的齿轮""" return GearDisplayable(size=size, color="#FFFFFF", alpha=0.9) ## 圆形画廊/日期图标 Displayable 类 class PageDisplayable(renpy.Displayable): """圆形画廊图标:白圆 + 深色纸张 + 白色线条""" def __init__(self, size=48, alpha=0.85): super(PageDisplayable, self).__init__() self.size = size self.alpha = alpha def render(self, width, height, st, at): size = self.size a = int(self.alpha * 255) surface = pygame.Surface((size, size), pygame.SRCALPHA) center = size // 2 radius = size // 2 - 4 # 白色圆形背景 pygame.draw.circle(surface, (255, 255, 255, a), (center, center), radius) # 深色纸张(圆角矩形,手动实现以兼容 renpy.pygame) pw = int(radius * 0.78) ph = int(radius * 1.05) px = center - pw // 2 py = center - ph // 2 paper_color = (32, 42, 68, min(a + 30, 255)) cr = 4 # 圆角半径 pygame.draw.rect(surface, paper_color, (px + cr, py, pw - 2 * cr, ph)) pygame.draw.rect(surface, paper_color, (px, py + cr, pw, ph - 2 * cr)) pygame.draw.circle(surface, paper_color, (px + cr, py + cr), cr) pygame.draw.circle(surface, paper_color, (px + pw - cr, py + cr), cr) pygame.draw.circle(surface, paper_color, (px + cr, py + ph - cr), cr) pygame.draw.circle(surface, paper_color, (px + pw - cr, py + ph - cr), cr) # 三条白色横线 lc = (210, 225, 245, int(self.alpha * 210)) lx0 = px + int(pw * 0.16) lx1 = px + int(pw * 0.84) for i in range(3): ly = py + int(ph * 0.28) + i * int(ph * 0.22) pygame.draw.line(surface, lc, (lx0, ly), (lx1, ly), max(2, size // 48)) render = renpy.Render(size, size) render.blit(surface, (0, 0)) return render def visit(self): return [] def create_gallery_page_idle(size=96): return PageDisplayable(size=size, alpha=0.85) def create_gallery_page_hover(size=96): return PageDisplayable(size=size, alpha=1.0) ## 游戏内日期字符串函数 import datetime as _dt def get_game_date_str(): """根据 game_day 和 game_period 返回完整游戏内日期字符串""" base = _dt.date(store.game_start_year, store.game_start_month, store.game_start_day) current = base + _dt.timedelta(days=store.game_day - 1) period_map = { "morning": "上午", "afternoon": "下午", "after_school": "放学后", "evening": "傍晚", "night": "深夜", } period_text = period_map.get(store.game_period, "") return "{}年{}月{}日 {}".format(current.year, current.month, current.day, period_text) ## 画廊数据与辅助函数 GALLERY_PHOTOS = [ { "id": "pearl", "file": "images/gallery/photo_pearl.png", "title": "珍珠耳环", "desc": "某个无聊的午后,\n镜子里映出一张陌生的脸。\n古典与现代在此刻奇异地重叠。", "date": "2029.09.05", "always_unlocked": True, }, { "id": "monalisa", "file": "images/gallery/photo_monalisa.jpg", "title": "永恒的微笑", "desc": "AI说,你和她很像。\n我觉得,那是在骂我。", "date": "2029.09.05", "always_unlocked": True, }, { "id": "corridor", "file": "images/gallery/photo_corridor.jpg", "title": "考前", "desc": "考场门外,时间凝固了一样。\n走廊的灯光把影子拉得很长。", "date": "2029.09.10", "always_unlocked": True, }, { "id": "sleeping", "file": "images/gallery/photo_sleeping.jpg", "title": "力学之美", "desc": "一切运动都趋向静止。\n这是物理定律,也是我的日常。\n\n——任懿涛", "date": "2029.10.01", "always_unlocked": False, }, ] def is_gallery_photo_unlocked(photo): if photo["always_unlocked"]: return True if photo["id"] == "sleeping": return store.gallery_photo4_unlocked return False def is_photo_new(photo_id): seen = persistent.gallery_seen if seen is None: return True return photo_id not in seen def mark_photo_seen(photo_id): if persistent.gallery_seen is None: persistent.gallery_seen = set() persistent.gallery_seen.add(photo_id) def has_any_new_gallery_photo(): for photo in GALLERY_PHOTOS: if is_gallery_photo_unlocked(photo) and is_photo_new(photo["id"]): return True return False def clear_all_game_data(): """清除所有存档和持久化数据,然后重启游戏。""" for slot, _, _, _ in renpy.list_saved_games(regexp=r'.+'): renpy.unlink_save(slot) persistent._clear(progress=True) renpy.save_persistent() renpy.full_restart() style window is default style say_label is default style say_dialogue is default style say_thought is say_dialogue style namebox is default style namebox_label is say_label style window: xalign 0.5 xfill True yalign gui.textbox_yalign ysize gui.textbox_height background Image("gui/textbox.png", xalign=0.5, yalign=1.0) style namebox: xpos gui.name_xpos xanchor gui.name_xalign xsize gui.namebox_width ypos gui.name_ypos ysize gui.namebox_height background Frame("gui/namebox.png", gui.namebox_borders, tile=gui.namebox_tile, xalign=gui.name_xalign) padding gui.namebox_borders.padding style say_label: properties gui.text_properties("name", accent=True) xalign gui.name_xalign yalign 0.5 style say_dialogue: properties gui.text_properties("dialogue") xpos gui.dialogue_xpos xsize gui.dialogue_width ypos gui.dialogue_ypos adjust_spacing False # 增强的对话文字样式 color "#FFFFFF" size 28 outlines [(2, "#000000", 0, 0)] line_spacing 8 ## 输入屏幕 ######################################################################## ## ## 此屏幕用于显示 renpy.input。prompt 参数用于传递文本提示。 ## ## 此屏幕必须创建一个 id 为 input 的输入可视控件来接受各种输入参数。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#input screen input(prompt): style_prefix "input" window: vbox: xanchor gui.dialogue_text_xalign xpos gui.dialogue_xpos xsize gui.dialogue_width ypos gui.dialogue_ypos text prompt style "input_prompt" input id "input" style input_prompt is default style input_prompt: xalign gui.dialogue_text_xalign properties gui.text_properties("input_prompt") style input: xalign gui.dialogue_text_xalign xmaximum gui.dialogue_width ## 选择屏幕 ######################################################################## ## ## 此屏幕用于显示由 menu 语句生成的游戏内选项。参数 items 是一个对象列表,每个对 ## 象都有字幕和动作字段。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#choice screen choice(items): style_prefix "choice" vbox: for i in items: textbutton i.caption action i.action style choice_vbox is vbox style choice_button is button style choice_button_text is button_text style choice_vbox: xalign 0.5 ypos 405 yanchor 0.5 spacing gui.choice_spacing style choice_button is default: properties gui.button_properties("choice_button") style choice_button_text is default: properties gui.text_properties("choice_button") ## 快捷菜单屏幕 ###################################################################### ## ## 快捷菜单显示于游戏内,以便于访问游戏外的菜单。 screen quick_menu(): ## 确保该菜单出现在其他屏幕之上, zorder 100 if quick_menu: hbox: style_prefix "quick" style "quick_menu" textbutton _("回退") action Rollback() textbutton _("历史") action ShowMenu('history') textbutton _("快进") action Skip() alternate Skip(fast=True, confirm=True) textbutton _("自动") action Preference("auto-forward", "toggle") textbutton _("保存") action ShowMenu('save') textbutton _("快存") action QuickSave() textbutton _("快读") action QuickLoad() textbutton _("设置") action ShowMenu('preferences') ## 此代码确保只要用户没有主动隐藏界面,就会在游戏中显示 quick_menu 屏幕。 init python: config.overlay_screens.append("quick_menu") default quick_menu = True style quick_menu is hbox style quick_button is default style quick_button_text is button_text style quick_menu: xalign 0.5 yalign 1.0 style quick_button: properties gui.button_properties("quick_button") style quick_button_text: properties gui.text_properties("quick_button") ################################################################################ ## HUD Overlay - 游戏内浮动图标 ################################################################################ ## 该屏幕在游戏进行时显示在左上角,提供快速访问菜单的图标按钮 screen hud_overlay(): ## 确保在所有UI之上 zorder 101 ## 只在游戏进行时显示,不在主菜单显示 if not main_menu and not renpy.context()._menu: ## 桌面版 - 左上角齿轮图标 if renpy.variant("pc") or renpy.variant("web"): vbox: xpos 40 ypos 40 imagebutton: idle create_settings_gear_idle(96) hover create_settings_gear_hover(96) action ShowMenu() tooltip "游戏菜单" ## 右上角画廊/日期图标(1920 - 96 - 40 = 1784) fixed: xpos 1784 ypos 40 xysize (96, 96) button: xfill True yfill True background create_gallery_page_idle(96) hover_background create_gallery_page_hover(96) action ToggleScreen("gallery_panel") tooltip "画廊 / 日期" if has_any_new_gallery_photo(): frame: xanchor 1.0 xpos 96 yanchor 0.0 ypos 0 xysize (36, 20) background "#e03030" padding (3, 2, 3, 2) text "NEW": xalign 0.5 yalign 0.5 color "#ffffff" size 13 bold True ## 移动端 - 稍大的图标,更易点击 elif renpy.variant("touch") or renpy.variant("small"): vbox: xpos 30 ypos 30 imagebutton: idle create_settings_gear_idle(112) hover create_settings_gear_hover(112) action ShowMenu() ## 移动端增大点击区域 xysize (120, 120) ## 移动端右上角画廊/日期图标 fixed: xpos 1730 ypos 30 xysize (112, 112) button: xfill True yfill True background create_gallery_page_idle(112) hover_background create_gallery_page_hover(112) action ToggleScreen("gallery_panel") if has_any_new_gallery_photo(): frame: xanchor 1.0 xpos 112 yanchor 0.0 ypos 0 xysize (36, 20) background "#e03030" padding (3, 2, 3, 2) text "NEW": xalign 0.5 yalign 0.5 color "#ffffff" size 13 bold True ## 注册HUD overlay为持久显示的屏幕 init python: config.overlay_screens.append("hud_overlay") ################################################################################ ## 画廊/日期面板 ################################################################################ screen gallery_panel(): zorder 150 modal True ## 全屏半透明背景,点击关闭 button: xfill True yfill True background "#00000066" action Hide("gallery_panel") ## 右侧侧边栏面板(340×1080) frame: xanchor 1.0 xpos 1920 yanchor 0.0 ypos 0 xysize (340, 1080) background "#0d0f1ef0" padding (0, 0, 0, 0) vbox: xfill True spacing 0 ## 顶部:日期 + 关闭按钮 frame: xfill True ysize 72 background None padding (24, 0, 40, 0) hbox: xfill True yalign 0.5 spacing 8 text get_game_date_str(): color "#a8c8f0" size 26 yalign 0.5 font "fonts/AlibabaPuHuiTi-3-55-Regular.ttf" frame: xfill True background None textbutton "×": action Hide("gallery_panel") text_size 34 text_color "#a8c8f0" text_hover_color "#ffffff" yalign 0.5 ## 分割线 frame: xfill True ysize 1 background "#ffffff22" ## 工具栏标题 frame: xfill True background None padding (24, 16, 24, 8) text "工具栏": color "#778899" size 22 font "fonts/AlibabaPuHuiTi-3-55-Regular.ttf" ## 画廊图标按钮 button: xalign 0.5 ysize 160 background None hover_background "#ffffff11" action Function(renpy.show_screen, "yitao_gallery") vbox: xalign 0.5 yalign 0.5 spacing 10 frame: xalign 0.5 xysize (112, 112) background None padding (0, 0, 0, 0) add "images/gallery/icon_gallery.png": fit "contain" xsize 112 ysize 112 text "画廊": xalign 0.5 color "#cccccc" size 22 font "fonts/AlibabaPuHuiTi-3-55-Regular.ttf" ## ESC 键关闭 key "game_menu" action Hide("gallery_panel") ################################################################################ ## YITAO-REN GALLERY 主画廊屏幕 ################################################################################ screen yitao_gallery(): zorder 155 modal True ## 全屏深色背景 frame: xfill True yfill True background "#0a0c1a" padding (0, 0, 0, 0) vbox: xfill True yfill True spacing 0 ## 顶栏(高 80px) frame: xfill True ysize 80 background "#0d0f28cc" padding (48, 0, 48, 0) hbox: xfill True yalign 0.5 spacing 0 text "YITAO-REN GALLERY": color "#c8d8f0" size 32 yalign 0.5 font "fonts/AlibabaPuHuiTi-3-55-Regular.ttf" frame: xfill True background None textbutton "×": action Hide("yitao_gallery") text_size 38 text_color "#a8c8f0" text_hover_color "#ffffff" yalign 0.5 ## 照片卡片区(占剩余空间,垂直居中) frame: xfill True yfill True background None padding (0, 0, 0, 0) hbox: xalign 0.5 yalign 0.5 spacing 24 for photo in GALLERY_PHOTOS: $ _pid = photo["id"] $ _unlocked = is_gallery_photo_unlocked(photo) $ _is_new = _unlocked and is_photo_new(_pid) ## 单张卡片(白色边框 = NEW) frame: xysize (425, 567) yalign 0.5 background ("#ffffff" if _is_new else "#1a1c2a") padding ((3, 3, 3, 3) if _is_new else (0, 0, 0, 0)) if _unlocked: button: xfill True yfill True background None hover_background "#ffffff14" action [Function(mark_photo_seen, _pid), Function(renpy.show_screen, "gallery_detail", photo=photo)] add photo["file"]: fit "cover" xsize 425 ysize 567 else: ## 未解锁:锁定状态 frame: xfill True yfill True background "#111320" padding (0, 0, 0, 0) text "?": xalign 0.5 yalign 0.5 color "#445566" size 80 ## NEW 角标(绝对定位于卡片右上角) if _is_new: frame: xanchor 1.0 xpos 422 yanchor 0.0 ypos 0 xysize (60, 28) background "#e03030" padding (4, 2, 4, 2) text "NEW": xalign 0.5 yalign 0.5 color "#ffffff" size 16 bold True key "game_menu" action Hide("yitao_gallery") ################################################################################ ## 画廊详情屏幕 ################################################################################ screen gallery_detail(photo): zorder 160 modal True ## 全屏背景 frame: xfill True yfill True background "#0a0c1a" padding (0, 0, 0, 0) vbox: xfill True yfill True spacing 0 ## 顶栏(高 72px) frame: xfill True ysize 72 background "#0d0f28cc" padding (40, 0, 40, 0) textbutton "← YITAO-REN GALLERY": action Hide("gallery_detail") text_size 26 text_color "#a8c8f0" text_hover_color "#ffffff" yalign 0.5 ## 主体区域(左图右文) frame: xfill True yfill True background None padding (0, 0, 0, 0) hbox: xfill True yfill True spacing 0 ## 左栏:图片(55%,xsize 1056) frame: xsize 1056 yfill True background None padding (0, 0, 0, 0) add photo["file"]: xalign 0.5 yalign 0.5 fit "contain" xsize 960 ysize 1008 ## 右栏:文字信息(45%,xsize 864) frame: xsize 864 yfill True background "#0d0f28" padding (60, 0, 60, 0) vbox: xfill True yalign 0.3 spacing 20 ## 照片标题 frame: xfill True background None padding (0, 40, 0, 16) text photo["title"]: color "#ffffff" size 52 font "fonts/AlibabaPuHuiTi-3-55-Regular.ttf" ## 水平分割线 frame: xfill True ysize 1 background "#ffffff44" ## 描述文字 frame: xfill True background None padding (0, 20, 0, 20) text photo["desc"]: color "#aaaaaa" size 26 font "fonts/AlibabaPuHuiTi-3-55-Regular.ttf" line_spacing 12 ## 发布日期 text photo["date"]: color "#666666" size 20 font "fonts/AlibabaPuHuiTi-3-55-Regular.ttf" key "game_menu" action Hide("gallery_detail") ################################################################################ ## 标题和游戏菜单屏幕 ################################################################################ ## 导航屏幕 ######################################################################## ## ## 该屏幕包含在标题菜单和游戏菜单中,并提供导航到其他菜单,以及启动游戏。 screen navigation(): vbox: style_prefix "navigation" xpos gui.navigation_xpos yalign 0.5 spacing gui.navigation_spacing if main_menu: textbutton _("新游戏") action Start() # 开始游戏 else: textbutton _("历史") action ShowMenu("history") textbutton _("保存") action ShowMenu("save") textbutton _("读取游戏") action ShowMenu("load") textbutton _("设置") action ShowMenu("preferences") if _in_replay: textbutton _("结束回放") action EndReplay(confirm=True) elif not main_menu: textbutton _("标题菜单") action MainMenu() textbutton _("关于") action ShowMenu("about") if renpy.variant("pc") or (renpy.variant("web") and not renpy.variant("mobile")): ## “帮助”对移动设备来说并非必需或相关。 textbutton _("帮助") action ShowMenu("help") if renpy.variant("pc"): ## 退出按钮在 iOS 上是被禁止使用的,在安卓和网页上也不是必要的。 textbutton _("退出") action Quit(confirm=not main_menu) style navigation_button is gui_button style navigation_button_text is gui_button_text style navigation_button: size_group "navigation" properties gui.button_properties("navigation_button") style navigation_button_text: properties gui.text_properties("navigation_button") ## 标题菜单屏幕 ###################################################################### ## ## 用于在 Ren'Py 启动时显示标题菜单。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#main-menu screen main_menu(): ## 此语句可确保替换掉任何其他菜单屏幕。 tag menu add gui.main_menu_background ## 此空框可使标题菜单变暗。 frame: style "main_menu_frame" ## use 语句将其他的屏幕包含进此屏幕。标题屏幕的实际内容在导航屏幕中。 use navigation if gui.show_name: vbox: style "main_menu_vbox" text "[config.name!t]": style "main_menu_title" text "[config.version]": style "main_menu_version" style main_menu_frame is empty style main_menu_vbox is vbox style main_menu_text is gui_text style main_menu_title is main_menu_text style main_menu_version is main_menu_text style main_menu_frame: xsize 420 yfill True background "gui/overlay/main_menu.png" style main_menu_vbox: xalign 1.0 xoffset -30 xmaximum 1200 yalign 1.0 yoffset -30 style main_menu_text: properties gui.text_properties("main_menu", accent=True) style main_menu_title: properties gui.text_properties("title") style main_menu_version: properties gui.text_properties("version") ## 游戏菜单屏幕 ###################################################################### ## ## 此屏幕列出了游戏菜单的基本共同结构。可使用屏幕标题调用,并显示背景、标题和导 ## 航菜单。 ## ## scroll 参数可以是 None,也可以是 viewport 或 vpgrid。此屏幕旨在与一个或多个子 ## 屏幕同时使用,这些子屏幕将被嵌入(放置)在其中。 screen game_menu(title, scroll=None, yinitial=0.0, spacing=0): style_prefix "game_menu" if main_menu: add gui.main_menu_background else: add gui.game_menu_background frame: style "game_menu_outer_frame" hbox: ## 导航部分的预留空间。 frame: style "game_menu_navigation_frame" frame: style "game_menu_content_frame" if scroll == "viewport": viewport: yinitial yinitial scrollbars "vertical" mousewheel True draggable True pagekeys True side_yfill True vbox: spacing spacing transclude elif scroll == "vpgrid": vpgrid: cols 1 yinitial yinitial scrollbars "vertical" mousewheel True draggable True pagekeys True side_yfill True spacing spacing transclude else: transclude use navigation textbutton _("返回"): style "return_button" action Return() label title if main_menu: key "game_menu" action ShowMenu("main_menu") style game_menu_outer_frame is empty style game_menu_navigation_frame is empty style game_menu_content_frame is empty style game_menu_viewport is gui_viewport style game_menu_side is gui_side style game_menu_scrollbar is gui_vscrollbar style game_menu_label is gui_label style game_menu_label_text is gui_label_text style return_button is navigation_button style return_button_text is navigation_button_text style game_menu_outer_frame: bottom_padding 45 top_padding 180 background "gui/overlay/game_menu.png" style game_menu_navigation_frame: xsize 420 yfill True style game_menu_content_frame: left_margin 60 right_margin 30 top_margin 15 style game_menu_viewport: xsize 1380 style game_menu_vscrollbar: unscrollable gui.unscrollable style game_menu_side: spacing 15 style game_menu_label: xpos 75 ysize 180 style game_menu_label_text: size 75 color gui.accent_color yalign 0.5 style return_button: xpos gui.navigation_xpos yalign 1.0 yoffset -45 ## 关于屏幕 ######################################################################## ## ## 此屏幕提供有关游戏和 Ren'Py 的制作人员和版权信息。 ## ## 此屏幕没有什么特别之处,因此它也可以作为一个例子来说明如何制作一个自定义屏 ## 幕。 screen about(): tag menu ## 此 use 语句将 game_menu 屏幕包含到了这个屏幕内。子级 vbox 将包含在 ## game_menu 屏幕的 viewport 内。 use game_menu(_("关于"), scroll="viewport"): style_prefix "about" vbox: label "[config.name!t]" text _("版本 [config.version!t]\n") ## gui.about 通常在 options.rpy 中设置。 if gui.about: text "[gui.about!t]\n" text _("引擎:{a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only]\n\n[renpy.license!t]") style about_label is gui_label style about_label_text is gui_label_text style about_text is gui_text style about_label_text: size gui.label_text_size ## 读取和保存屏幕 ##################################################################### ## ## 这些屏幕负责让用户保存游戏并能够再次读取。由于它们几乎完全一样,因此这两个屏 ## 幕都是以第三个屏幕 file_slots 来实现的。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#save https://doc.renpy.cn/zh- ## CN/screen_special.html#load screen save(): tag menu use file_slots(_("保存")) screen load(): tag menu use file_slots(_("读取游戏")) screen file_slots(title): default page_name_value = FilePageNameInputValue(pattern=_("第 {} 页"), auto=_("自动存档"), quick=_("快速存档")) use game_menu(title): fixed: ## 此代码确保输入控件在任意按钮执行前可以获取 enter 事件。 order_reverse True ## 页面名称,可以通过单击按钮进行编辑。 button: style "page_label" key_events True xalign 0.5 action page_name_value.Toggle() input: style "page_label_text" value page_name_value ## 存档位网格。 grid gui.file_slot_cols gui.file_slot_rows: style_prefix "slot" xalign 0.5 yalign 0.5 spacing gui.slot_spacing for i in range(gui.file_slot_cols * gui.file_slot_rows): $ slot = i + 1 button: action FileAction(slot) has vbox add FileScreenshot(slot) xalign 0.5 text FileTime(slot, format=_("{#file_time}%Y-%m-%d %H:%M"), empty=_("空存档位")): style "slot_time_text" text FileSaveName(slot): style "slot_name_text" key "save_delete" action FileDelete(slot) ## 用于访问其他页面的按钮。 vbox: style_prefix "page" xalign 0.5 yalign 1.0 hbox: xalign 0.5 spacing gui.page_spacing textbutton _("<") action FilePagePrevious() key "save_page_prev" action FilePagePrevious() if config.has_autosave: textbutton _("{#auto_page}A") action FilePage("auto") if config.has_quicksave: textbutton _("{#quick_page}Q") action FilePage("quick") ## range(1, 10) 给出 1 到 9 之间的数字。 for page in range(1, 10): textbutton "[page]" action FilePage(page) textbutton _(">") action FilePageNext() key "save_page_next" action FilePageNext() if config.has_sync: if CurrentScreenName() == "save": textbutton _("上传同步"): action UploadSync() xalign 0.5 else: textbutton _("下载同步"): action DownloadSync() xalign 0.5 style page_label is gui_label style page_label_text is gui_label_text style page_button is gui_button style page_button_text is gui_button_text style slot_button is gui_button style slot_button_text is gui_button_text style slot_time_text is slot_button_text style slot_name_text is slot_button_text style page_label: xpadding 75 ypadding 5 xalign 0.5 style page_label_text: textalign 0.5 layout "subtitle" hover_color gui.hover_color style page_button: properties gui.button_properties("page_button") style page_button_text: properties gui.text_properties("page_button") style slot_button: properties gui.button_properties("slot_button") style slot_button_text: properties gui.text_properties("slot_button") ## 设置屏幕 ######################################################################## ## ## 设置屏幕允许用户配置游戏,使其更适合自己。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#preferences screen preferences(): tag menu use game_menu(_("设置"), scroll="viewport"): vbox: hbox: box_wrap True if renpy.variant("pc") or renpy.variant("web"): vbox: style_prefix "radio" label _("显示") textbutton _("窗口") action Preference("display", "window") textbutton _("全屏") action Preference("display", "fullscreen") vbox: style_prefix "check" label _("快进") textbutton _("未读文本") action Preference("skip", "toggle") textbutton _("选项后继续") action Preference("after choices", "toggle") textbutton _("忽略转场") action InvertSelected(Preference("transitions", "toggle")) ## 可在此处添加 radio_pref 或 check_pref 类型的额外 vbox,以添加 ## 额外的创建者定义的偏好设置。 null height (4 * gui.pref_spacing) hbox: style_prefix "slider" box_wrap True vbox: label _("文字速度") bar value Preference("text speed") label _("自动前进时间") bar value Preference("auto-forward time") vbox: if config.has_music: label _("音乐音量") hbox: bar value Preference("music volume") if config.has_sound: label _("音效音量") hbox: bar value Preference("sound volume") if config.sample_sound: textbutton _("测试") action Play("sound", config.sample_sound) if config.has_voice: label _("语音音量") hbox: bar value Preference("voice volume") if config.sample_voice: textbutton _("测试") action Play("voice", config.sample_voice) if config.has_music or config.has_sound or config.has_voice: null height gui.pref_spacing textbutton _("全部静音"): action Preference("all mute", "toggle") style "mute_all_button" null height (4 * gui.pref_spacing) hbox: style_prefix "check" box_wrap True vbox: label _("危险操作") textbutton _("清除所有数据"): action Confirm( _("此操作将清除所有存档和游戏进度,且无法撤销。\n确定要继续吗?"), yes=Function(clear_all_game_data), no=Hide("confirm") ) style "mute_all_button" style pref_label is gui_label style pref_label_text is gui_label_text style pref_vbox is vbox style radio_label is pref_label style radio_label_text is pref_label_text style radio_button is gui_button style radio_button_text is gui_button_text style radio_vbox is pref_vbox style check_label is pref_label style check_label_text is pref_label_text style check_button is gui_button style check_button_text is gui_button_text style check_vbox is pref_vbox style slider_label is pref_label style slider_label_text is pref_label_text style slider_slider is gui_slider style slider_button is gui_button style slider_button_text is gui_button_text style slider_pref_vbox is pref_vbox style mute_all_button is check_button style mute_all_button_text is check_button_text style pref_label: top_margin gui.pref_spacing bottom_margin 3 style pref_label_text: yalign 1.0 style pref_vbox: xsize 338 style radio_vbox: spacing gui.pref_button_spacing style radio_button: properties gui.button_properties("radio_button") foreground "gui/button/radio_[prefix_]foreground.png" style radio_button_text: properties gui.text_properties("radio_button") style check_vbox: spacing gui.pref_button_spacing style check_button: properties gui.button_properties("check_button") foreground "gui/button/check_[prefix_]foreground.png" style check_button_text: properties gui.text_properties("check_button") style slider_slider: xsize 525 style slider_button: properties gui.button_properties("slider_button") yalign 0.5 left_margin 15 style slider_button_text: properties gui.text_properties("slider_button") style slider_vbox: xsize 675 ## 历史屏幕 ######################################################################## ## ## 这是一个向用户显示对话历史的屏幕。虽然此屏幕没有什么特别之处,但它必须访问储 ## 存在 _history_list 中的对话历史记录。 ## ## https://doc.renpy.cn/zh-CN/history.html screen history(): tag menu ## 避免预缓存此屏幕,因为它可能非常大。 predict False use game_menu(_("历史"), scroll=("vpgrid" if gui.history_height else "viewport"), yinitial=1.0, spacing=gui.history_spacing): style_prefix "history" for h in _history_list: window: ## 此代码可确保如果 history_height 为 None 时仍可正常显示条目。 has fixed: yfit True if h.who: label h.who: style "history_name" substitute False ## 从 Character 对象中获取叙述角色的文字颜色,如果设置了 ## 的话。 if "color" in h.who_args: text_color h.who_args["color"] $ what = renpy.filter_text_tags(h.what, allow=gui.history_allow_tags) text what: substitute False if not _history_list: label _("尚无对话历史记录。") ## 此代码决定了允许在历史记录屏幕上显示哪些标签。 define gui.history_allow_tags = { "alt", "noalt", "rt", "rb", "art" } style history_window is empty style history_name is gui_label style history_name_text is gui_label_text style history_text is gui_text style history_label is gui_label style history_label_text is gui_label_text style history_window: xfill True ysize gui.history_height style history_name: xpos gui.history_name_xpos xanchor gui.history_name_xalign ypos gui.history_name_ypos xsize gui.history_name_width style history_name_text: min_width gui.history_name_width textalign gui.history_name_xalign style history_text: xpos gui.history_text_xpos ypos gui.history_text_ypos xanchor gui.history_text_xalign xsize gui.history_text_width min_width gui.history_text_width textalign gui.history_text_xalign layout ("subtitle" if gui.history_text_xalign else "tex") style history_label: xfill True style history_label_text: xalign 0.5 ## 帮助屏幕 ######################################################################## ## ## 提供有关键盘和鼠标映射信息的屏幕。它使用其它屏幕(keyboard_help、mouse_help ## 和 gamepad_help)来显示实际的帮助内容。 screen help(): tag menu default device = "keyboard" use game_menu(_("帮助"), scroll="viewport"): style_prefix "help" vbox: spacing 23 hbox: textbutton _("键盘") action SetScreenVariable("device", "keyboard") textbutton _("鼠标") action SetScreenVariable("device", "mouse") if GamepadExists(): textbutton _("手柄") action SetScreenVariable("device", "gamepad") if device == "keyboard": use keyboard_help elif device == "mouse": use mouse_help elif device == "gamepad": use gamepad_help screen keyboard_help(): hbox: label _("回车") text _("推进对话并激活界面。") hbox: label _("空格") text _("在没有选择的情况下推进对话。") hbox: label _("方向键") text _("导航界面。") hbox: label _("Esc") text _("访问游戏菜单。") hbox: label _("键盘") text _("按住时快进对话。") hbox: label _("Tab") text _("切换对话快进。") hbox: label _("上一页") text _("回退至先前的对话。") hbox: label _("下一页") text _("向前至后来的对话。") hbox: label "H" text _("隐藏用户界面。") hbox: label "S" text _("截图。") hbox: label "V" text _("切换辅助{a=https://doc.renpy.cn/zh-CN/self_voicing.html}机器朗读{/a}。") hbox: label "Shift+A" text _("打开无障碍菜单。") screen mouse_help(): hbox: label _("左键点击") text _("推进对话并激活界面。") hbox: label _("中键点击") text _("隐藏用户界面。") hbox: label _("右键点击") text _("访问游戏菜单。") hbox: label _("鼠标滚轮上") text _("回退至先前的对话。") hbox: label _("鼠标滚轮下") text _("向前至后来的对话。") screen gamepad_help(): hbox: label _("右扳机键\nA/底键") text _("推进对话并激活界面。") hbox: label _("左扳机键\n左肩键") text _("回退至先前的对话。") hbox: label _("右肩键") text _("向前至后来的对话。") hbox: label _("十字键,摇杆") text _("导航界面。") hbox: label _("开始,向导,B/右键") text _("访问游戏菜单。") hbox: label _("Y/顶键") text _("隐藏用户界面。") textbutton _("校准") action GamepadCalibrate() style help_button is gui_button style help_button_text is gui_button_text style help_label is gui_label style help_label_text is gui_label_text style help_text is gui_text style help_button: properties gui.button_properties("help_button") xmargin 12 style help_button_text: properties gui.text_properties("help_button") style help_label: xsize 375 right_padding 30 style help_label_text: size gui.text_size xalign 1.0 textalign 1.0 ################################################################################ ## 其他屏幕 ################################################################################ ## 确认屏幕 ######################################################################## ## ## 当 Ren'Py 需要询问用户有关确定或取消的问题时,会调用确认屏幕。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#confirm screen confirm(message, yes_action, no_action): ## 显示此屏幕时,确保其他屏幕无法输入。 modal True zorder 200 style_prefix "confirm" add "gui/overlay/confirm.png" frame: vbox: xalign .5 yalign .5 spacing 45 label _(message): style "confirm_prompt" xalign 0.5 hbox: xalign 0.5 spacing 150 textbutton _("确定") action yes_action textbutton _("取消") action no_action ## 右键点击退出并答复 no(取消)。 key "game_menu" action no_action style confirm_frame is gui_frame style confirm_prompt is gui_prompt style confirm_prompt_text is gui_prompt_text style confirm_button is gui_medium_button style confirm_button_text is gui_medium_button_text style confirm_frame: background Frame([ "gui/confirm_frame.png", "gui/frame.png"], gui.confirm_frame_borders, tile=gui.frame_tile) padding gui.confirm_frame_borders.padding xalign .5 yalign .5 style confirm_prompt_text: textalign 0.5 layout "subtitle" style confirm_button: properties gui.button_properties("confirm_button") style confirm_button_text: properties gui.text_properties("confirm_button") ## 快进指示屏幕 ###################################################################### ## ## skip_indicator 屏幕用于指示快进正在进行中。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#skip-indicator screen skip_indicator(): zorder 100 style_prefix "skip" frame: hbox: spacing 9 text _("正在快进") text "▸" at delayed_blink(0.0, 1.0) style "skip_triangle" text "▸" at delayed_blink(0.2, 1.0) style "skip_triangle" text "▸" at delayed_blink(0.4, 1.0) style "skip_triangle" ## 此变换用于一个接一个地闪烁箭头。 transform delayed_blink(delay, cycle): alpha .5 pause delay block: linear .2 alpha 1.0 pause .2 linear .2 alpha 0.5 pause (cycle - .4) repeat style skip_frame is empty style skip_text is gui_text style skip_triangle is skip_text style skip_frame: ypos gui.skip_ypos background Frame("gui/skip.png", gui.skip_frame_borders, tile=gui.frame_tile) padding gui.skip_frame_borders.padding style skip_text: size gui.notify_text_size style skip_triangle: ## 我们必须使用包含“▸”(黑色右旋小三角)字形的字体。 font "DejaVuSans.ttf" ## 通知屏幕 ######################################################################## ## ## 通知屏幕用于向用户显示消息。(例如,当游戏快速保存或进行截屏时。) ## ## https://doc.renpy.cn/zh-CN/screen_special.html#notify-screen screen notify(message): zorder 100 style_prefix "notify" frame at notify_appear: text "[message!tq]" timer 3.25 action Hide('notify') transform notify_appear: on show: alpha 0 linear .25 alpha 1.0 on hide: linear .5 alpha 0.0 style notify_frame is empty style notify_text is gui_text style notify_frame: ypos gui.notify_ypos background Frame("gui/notify.png", gui.notify_frame_borders, tile=gui.frame_tile) padding gui.notify_frame_borders.padding style notify_text: properties gui.text_properties("notify") ## NVL 模式屏幕 #################################################################### ## ## 此屏幕用于 NVL 模式的对话和菜单。 ## ## https://doc.renpy.cn/zh-CN/screen_special.html#nvl screen nvl(dialogue, items=None): window: style "nvl_window" has vbox: spacing gui.nvl_spacing ## 在 vpgrid 或 vbox 中显示对话框。 if gui.nvl_height: vpgrid: cols 1 yinitial 1.0 use nvl_dialogue(dialogue) else: use nvl_dialogue(dialogue) ## 显示菜单,如果给定的话。如果 config.narrator_menu 设置为 True,则菜单 ## 可能显示不正确。 for i in items: textbutton i.caption: action i.action style "nvl_button" add SideImage() xalign 0.0 yalign 1.0 screen nvl_dialogue(dialogue): for d in dialogue: window: id d.window_id fixed: yfit gui.nvl_height is None if d.who is not None: text d.who: id d.who_id text d.what: id d.what_id ## 此语句控制一次可以显示的 NVL 模式条目的最大数量。 define config.nvl_list_length = gui.nvl_list_length style nvl_window is default style nvl_entry is default style nvl_label is say_label style nvl_dialogue is say_dialogue style nvl_button is button style nvl_button_text is button_text style nvl_window: xfill True yfill True background "gui/nvl.png" padding gui.nvl_borders.padding style nvl_entry: xfill True ysize gui.nvl_height style nvl_label: xpos gui.nvl_name_xpos xanchor gui.nvl_name_xalign ypos gui.nvl_name_ypos yanchor 0.0 xsize gui.nvl_name_width min_width gui.nvl_name_width textalign gui.nvl_name_xalign style nvl_dialogue: xpos gui.nvl_text_xpos xanchor gui.nvl_text_xalign ypos gui.nvl_text_ypos xsize gui.nvl_text_width min_width gui.nvl_text_width textalign gui.nvl_text_xalign layout ("subtitle" if gui.nvl_text_xalign else "tex") style nvl_thought: xpos gui.nvl_thought_xpos xanchor gui.nvl_thought_xalign ypos gui.nvl_thought_ypos xsize gui.nvl_thought_width min_width gui.nvl_thought_width textalign gui.nvl_thought_xalign layout ("subtitle" if gui.nvl_text_xalign else "tex") style nvl_button: properties gui.button_properties("nvl_button") xpos gui.nvl_button_xpos xanchor gui.nvl_button_xalign style nvl_button_text: properties gui.text_properties("nvl_button") ## 对话气泡屏幕 ###################################################################### ## ## 对话气泡屏幕用于以对话气泡的形式向玩家显示对话。对话气泡屏幕的参数与 say 屏幕 ## 相同,必须创建一个 id 为 what 的可视控件,并且可以创建 id 为 namebox、who 和 ## window 的可视控件。 ## ## https://doc.renpy.cn/zh-CN/bubble.html#bubble-screen screen bubble(who, what): style_prefix "bubble" window: id "window" if who is not None: window: id "namebox" style "bubble_namebox" text who: id "who" text what: id "what" default ctc = None showif ctc: add ctc style bubble_window is empty style bubble_namebox is empty style bubble_who is default style bubble_what is default style bubble_window: xpadding 30 top_padding 5 bottom_padding 5 style bubble_namebox: xalign 0.5 style bubble_who: xalign 0.5 textalign 0.5 color "#000" style bubble_what: align (0.5, 0.5) text_align 0.5 layout "subtitle" color "#000" define bubble.frame = Frame("gui/bubble.png", 55, 55, 55, 95) define bubble.thoughtframe = Frame("gui/thoughtbubble.png", 55, 55, 55, 55) define bubble.properties = { "bottom_left" : { "window_background" : Transform(bubble.frame, xzoom=1, yzoom=1), "window_bottom_padding" : 27, }, "bottom_right" : { "window_background" : Transform(bubble.frame, xzoom=-1, yzoom=1), "window_bottom_padding" : 27, }, "top_left" : { "window_background" : Transform(bubble.frame, xzoom=1, yzoom=-1), "window_top_padding" : 27, }, "top_right" : { "window_background" : Transform(bubble.frame, xzoom=-1, yzoom=-1), "window_top_padding" : 27, }, "thought" : { "window_background" : bubble.thoughtframe, } } define bubble.expand_area = { "bottom_left" : (0, 0, 0, 22), "bottom_right" : (0, 0, 0, 22), "top_left" : (0, 22, 0, 0), "top_right" : (0, 22, 0, 0), "thought" : (0, 0, 0, 0), } ################################################################################ ## 移动设备界面 ################################################################################ style pref_vbox: variant "medium" xsize 675 ## 由于可能没有鼠标,我们将快捷菜单替换为一个使用更少、更大按钮的版本,这样更容 ## 易触摸。 screen quick_menu(): variant "touch" zorder 100 if quick_menu: hbox: style "quick_menu" style_prefix "quick" textbutton _("回退") action Rollback() textbutton _("快进") action Skip() alternate Skip(fast=True, confirm=True) textbutton _("自动") action Preference("auto-forward", "toggle") textbutton _("菜单") action ShowMenu() style window: variant "small" background "gui/phone/textbox.png" style radio_button: variant "small" foreground "gui/phone/button/radio_[prefix_]foreground.png" style check_button: variant "small" foreground "gui/phone/button/check_[prefix_]foreground.png" style nvl_window: variant "small" background "gui/phone/nvl.png" style main_menu_frame: variant "small" background "gui/phone/overlay/main_menu.png" style game_menu_outer_frame: variant "small" background "gui/phone/overlay/game_menu.png" style game_menu_navigation_frame: variant "small" xsize 510 style game_menu_content_frame: variant "small" top_margin 0 style game_menu_viewport: variant "small" xsize 1305 style pref_vbox: variant "small" xsize 600 style bar: variant "small" ysize gui.bar_size left_bar Frame("gui/phone/bar/left.png", gui.bar_borders, tile=gui.bar_tile) right_bar Frame("gui/phone/bar/right.png", gui.bar_borders, tile=gui.bar_tile) style vbar: variant "small" xsize gui.bar_size top_bar Frame("gui/phone/bar/top.png", gui.vbar_borders, tile=gui.bar_tile) bottom_bar Frame("gui/phone/bar/bottom.png", gui.vbar_borders, tile=gui.bar_tile) style scrollbar: variant "small" ysize gui.scrollbar_size base_bar Frame("gui/phone/scrollbar/horizontal_[prefix_]bar.png", gui.scrollbar_borders, tile=gui.scrollbar_tile) thumb Frame("gui/phone/scrollbar/horizontal_[prefix_]thumb.png", gui.scrollbar_borders, tile=gui.scrollbar_tile) style vscrollbar: variant "small" xsize gui.scrollbar_size base_bar Frame("gui/phone/scrollbar/vertical_[prefix_]bar.png", gui.vscrollbar_borders, tile=gui.scrollbar_tile) thumb Frame("gui/phone/scrollbar/vertical_[prefix_]thumb.png", gui.vscrollbar_borders, tile=gui.scrollbar_tile) style slider: variant "small" ysize gui.slider_size base_bar Frame("gui/phone/slider/horizontal_[prefix_]bar.png", gui.slider_borders, tile=gui.slider_tile) thumb "gui/phone/slider/horizontal_[prefix_]thumb.png" style vslider: variant "small" xsize gui.slider_size base_bar Frame("gui/phone/slider/vertical_[prefix_]bar.png", gui.vslider_borders, tile=gui.slider_tile) thumb "gui/phone/slider/vertical_[prefix_]thumb.png" style slider_vbox: variant "small" xsize None style slider_slider: variant "small" xsize 900