# GameOptionUI (P11) —— 游戏设置窗(1:1 迁移 `assets/root/uigameoption.py` `OptionDialog`)。 # # var go := preload("res://ui/game_option_ui.gd").new() # add_child(go) # go.setup(ui_manager, m2client, assets_root) # go.toggle() # 由 system_menu_ui 的 game_option_button 打开 # # 布局走真 `assets/uiscript/uiscript/gameoptiondialog.py`。绑定逐字对照 uigameoption.py: # block_{exchange,party,guild,whisper,friend,party_request}_button (toggle) → # `/setblockmode `(EBlockAction 位:1<<0..1<<5)—— 真聊天命令 # pvp_{peace,revenge,guild,free} (radio) → `/pkmode {0,1,4,2}` —— 真聊天命令 # name_color / target_board / view_chat / always_show_name / show_damage / salestext (radio) # —— 客户端显示开关,持久化到 user://system_option.cfg [gameopt];渲染节点按需读取这些设置。 extends Node # Packet.h EBlockAction const BLOCK_BITS := { "block_exchange_button": 1 << 0, "block_party_button": 1 << 1, "block_guild_button": 1 << 2, "block_whisper_button": 1 << 3, "block_friend_button": 1 << 4, "block_party_request_button": 1 << 5, } # uigameoption: peace /pkmode 0, revenge /pkmode 1, free /pkmode 2, guild /pkmode 4 const PK_CMD := {"pvp_peace": 0, "pvp_revenge": 1, "pvp_free": 2, "pvp_guild": 4} # 客户端显示开关:{radio 名 -> [cfg key, 该按钮代表的值]} const DISPLAY_RADIOS := { "name_color_normal": ["name_color", 0], "name_color_empire": ["name_color", 1], "target_board_no_view": ["target_board", 0], "target_board_view": ["target_board", 1], "view_chat_on_button": ["view_chat", 1], "view_chat_off_button": ["view_chat", 0], "always_show_name_on_button": ["always_show_name", 1], "always_show_name_off_button": ["always_show_name", 0], "show_damage_on_button": ["show_damage", 1], "show_damage_off_button": ["show_damage", 0], "salestext_on_button": ["salestext", 1], "salestext_off_button": ["salestext", 0], } const RADIO_GROUPS := [ ["name_color_normal", "name_color_empire"], ["target_board_no_view", "target_board_view"], ["view_chat_on_button", "view_chat_off_button"], ["always_show_name_on_button", "always_show_name_off_button"], ["show_damage_on_button", "show_damage_off_button"], ["salestext_on_button", "salestext_off_button"], ] const LABELS := { "titlename": "游戏设置", "name_color": "名字颜色", "target_board": "目标框", "pvp_mode": "PK 模式", "block": "屏蔽", "chat": "聊天显示", "always_show_name": "总显示名字", "effect_on_off": "伤害数字", "salestext_on_off": "叫卖文字", "name_color_normal": "普通", "name_color_empire": "阵营", "target_board_no_view": "不看他国", "target_board_view": "看他国", "pvp_peace": "和平", "pvp_revenge": "反击", "pvp_guild": "帮会", "pvp_free": "自由", "block_exchange_button": "交易", "block_party_button": "组队", "block_guild_button": "帮会", "block_whisper_button": "密语", "block_friend_button": "好友", "block_party_request_button": "组队申请", "view_chat_on_button": "开", "view_chat_off_button": "关", "always_show_name_on_button": "开", "always_show_name_off_button": "关", "show_damage_on_button": "开", "show_damage_off_button": "关", "salestext_on_button": "开", "salestext_off_button": "关", } const CFG_PATH := "user://system_option.cfg" var ui: CanvasLayer var client: Node var assets_root := "" var uiscript_dir := "" var _win: Dictionary = {} var _cfg := ConfigFile.new() var _block_mode := 0 # blockMode(本地跟踪 + 服务端回包同步) var _display := {} # cfg [gameopt] 快照 static func config_path() -> String: var override_dir := OS.get_environment("MT_CONFIG_DIR") if override_dir != "": return override_dir.path_join("system_option.cfg") return OS.get_temp_dir().path_join("mtgodot_system_option.cfg") \ if DisplayServer.get_name() == "headless" else CFG_PATH func setup(ui_manager: CanvasLayer, m2client: Node, assets := "") -> void: ui = ui_manager client = m2client assets_root = assets if assets_root == "" and ui and "assets_root" in ui: assets_root = ui.assets_root uiscript_dir = assets_root.path_join("uiscript/uiscript") if not DirAccess.dir_exists_absolute(uiscript_dir): uiscript_dir = assets_root.path_join("uiscript") _cfg.load(config_path()) for k in ["name_color", "target_board", "view_chat", "always_show_name", "show_damage", "salestext"]: _display[k] = int(_cfg.get_value("gameopt", k, 1 if k in ["view_chat", "always_show_name", "show_damage"] else 0)) _block_mode = int(_cfg.get_value("gameopt", "block_mode", 0)) if client and client.has_signal("block_mode_changed"): client.block_mode_changed.connect(_on_server_block_mode) func is_open() -> bool: return not _win.is_empty() and is_instance_valid(_win.get("root")) func toggle() -> void: if is_open(): close() else: open() func close() -> void: if is_open(): ui.close(_win["root"]) _win = {} func open() -> void: if is_open(): return var path := uiscript_dir.path_join("gameoptiondialog.py") if not FileAccess.file_exists(path): push_warning("GameOptionUI: no gameoptiondialog.py at " + path) return _win = ui.open_script(path, assets_root) if not is_open(): return _relabel() _wire() _sync() func _node(nm: String) -> Control: if _win.is_empty(): return null var n = _win.get("nodes", {}).get(nm, null) return n if n is Control else null func _relabel() -> void: for nm in LABELS: var n := _node(nm) if n and n.has_method("set_text"): n.set_text(LABELS[nm]) func _wire() -> void: # 屏蔽 toggle → /setblockmode for nm in BLOCK_BITS: var b := _node(nm) if b is BaseButton: b.toggle_mode = true var bit: int = BLOCK_BITS[nm] b.pressed.connect(func(): _toggle_block(bit)) # PK radio → /pkmode _bind_radio(PK_CMD.keys(), func(nm): if client and client.has_method("say"): client.say(0, "/pkmode %d" % PK_CMD[nm])) # CHAT_TYPE_TALKING # 显示开关 radio → 持久化 for group in RADIO_GROUPS: _bind_radio(group, func(nm): var spec: Array = DISPLAY_RADIOS[nm] _display[spec[0]] = int(spec[1]) _save()) var tb := _node("titlebar") if tb: for x in tb.find_children("*", "BaseButton", true, false): x.pressed.connect(close) func _bind_radio(names: Array, on_pick: Callable) -> void: var boxes := {} for nm: String in names: boxes[nm] = _node(nm) for nm: String in names: var b = boxes[nm] if not (b is BaseButton): continue b.toggle_mode = true var picked: String = nm b.pressed.connect(func(): for other: String in names: if boxes[other] is BaseButton: boxes[other].set_pressed_no_signal(other == picked) on_pick.call(picked)) func _toggle_block(bit: int) -> void: _block_mode ^= bit if client and client.has_method("say"): client.say(0, "/setblockmode %d" % _block_mode) _cfg.set_value("gameopt", "block_mode", _block_mode) var err := _cfg.save(config_path()) if err != OK: push_warning("GameOptionUI: save failed (%s): %s" % [err, config_path()]) _sync_block() # 开窗时把状态回填到控件 func _sync() -> void: _sync_block() for group in RADIO_GROUPS: var key: String = DISPLAY_RADIOS[group[0]][0] var cur := int(_display.get(key, 0)) for nm in group: var b := _node(nm) if b is BaseButton: b.set_pressed_no_signal(int(DISPLAY_RADIOS[nm][1]) == cur) func _sync_block() -> void: for nm in BLOCK_BITS: var b := _node(nm) if b is BaseButton: b.set_pressed_no_signal((_block_mode & int(BLOCK_BITS[nm])) != 0) func _on_server_block_mode(mask: int) -> void: _block_mode = maxi(0, mask) _cfg.set_value("gameopt", "block_mode", _block_mode) _cfg.save(config_path()) _sync_block() func _save() -> void: for k in _display: _cfg.set_value("gameopt", k, int(_display[k])) _cfg.set_value("gameopt", "block_mode", _block_mode) var err := _cfg.save(config_path()) if err != OK: push_warning("GameOptionUI: save failed (%s): %s" % [err, config_path()])