| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- BallController配置管理工具 - 简化版
- 图形界面版本,不依赖pandas,支持浏览文件并选择Excel/CSV表格进行配置导入
- 功能:
- 1. 图形界面浏览项目文件
- 2. 多选Excel/CSV配置表格
- 3. 预览配置内容
- 4. 一键导入配置到JSON
- 5. 配置备份和恢复
- 作者: AI Assistant
- 日期: 2024
- """
- import tkinter as tk
- from tkinter import ttk, filedialog, messagebox, scrolledtext
- import json
- import os
- import csv
- from datetime import datetime
- from pathlib import Path
- import threading
- # 尝试导入pandas,如果失败则使用纯CSV模式
- try:
- import pandas as pd
- PANDAS_AVAILABLE = True
- except ImportError:
- PANDAS_AVAILABLE = False
- print("警告: pandas未安装,将使用纯CSV模式(不支持Excel文件)")
- # 导入武器配置管理器
- try:
- from weapon_config_manager import WeaponConfigManager
- WEAPON_MANAGER_AVAILABLE = True
- except ImportError:
- WEAPON_MANAGER_AVAILABLE = False
- print("警告: weapon_config_manager.py 未找到,武器配置功能将不可用")
- try:
- from enemy_config_manager import EnemyConfigManager
- ENEMY_MANAGER_AVAILABLE = True
- except ImportError:
- ENEMY_MANAGER_AVAILABLE = False
- print("警告: enemy_config_manager.py 未找到,敌人配置功能将不可用")
- try:
- from level_config_manager import LevelConfigManager
- LEVEL_MANAGER_AVAILABLE = True
- except ImportError:
- LEVEL_MANAGER_AVAILABLE = False
- print("警告: level_config_manager.py 未找到,关卡配置功能将不可用")
- # 导入新的配置管理器模块
- try:
- from ball_controller_config_manager import BallControllerConfigManager
- BALL_CONTROLLER_MANAGER_AVAILABLE = True
- except ImportError:
- BALL_CONTROLLER_MANAGER_AVAILABLE = False
- print("警告: ball_controller_config_manager.py 未找到,球控制器配置功能将不可用")
- try:
- from skill_config_manager import SkillConfigManager
- SKILL_CONFIG_MANAGER_AVAILABLE = True
- except ImportError:
- SKILL_CONFIG_MANAGER_AVAILABLE = False
- print("警告: skill_config_manager.py 未找到,局外技能配置功能将不可用")
- try:
- from game_skill_config_manager import GameSkillConfigManager
- GAME_SKILL_MANAGER_AVAILABLE = True
- except ImportError:
- GAME_SKILL_MANAGER_AVAILABLE = False
- print("警告: game_skill_config_manager.py 未找到,游戏内技能配置功能将不可用")
- try:
- from shop_config_manager import ShopConfigManager
- SHOP_MANAGER_AVAILABLE = True
- except ImportError:
- SHOP_MANAGER_AVAILABLE = False
- print("警告: shop_config_manager.py 未找到,商店配置功能将不可用")
- try:
- from ball_price_config_manager import BallPriceConfigManager
- BALL_PRICE_MANAGER_AVAILABLE = True
- except ImportError:
- BALL_PRICE_MANAGER_AVAILABLE = False
- print("警告: ball_price_config_manager.py 未找到,小球价格配置功能将不可用")
- def parse_skill_types(self, df):
- """解析技能类型配置"""
- skill_types = []
-
- # 读取所有数据行(pandas已经自动处理了标题行)
- for index, row in df.iterrows():
-
- # 检查是否为空行
- if pd.isna(row.iloc[0]):
- continue
-
- try:
- skill_type = {
- "id": int(row.iloc[0]) if not pd.isna(row.iloc[0]) else 0,
- "name": str(row.iloc[1]) if not pd.isna(row.iloc[1]) else "",
- "displayName": str(row.iloc[2]) if not pd.isna(row.iloc[2]) else "",
- "nameTemplate": str(row.iloc[3]) if not pd.isna(row.iloc[3]) else "",
- "type": str(row.iloc[4]) if not pd.isna(row.iloc[4]) else ""
- }
- skill_types.append(skill_type)
- except Exception as e:
- print(f"解析技能类型第 {index+1} 行失败: {e}")
- continue
-
- return skill_types
-
- def parse_skill_groups(self, df):
- """解析技能组配置"""
- skill_groups = []
-
- # 读取所有数据行(pandas已经自动处理了标题行)
- for index, row in df.iterrows():
-
- # 检查是否为空行
- if pd.isna(row.iloc[0]):
- continue
-
- try:
- skill_group = {
- "group": int(row.iloc[0]) if not pd.isna(row.iloc[0]) else 1,
- "effectPercent": int(row.iloc[1]) if not pd.isna(row.iloc[1]) else 0,
- "diamondCost": int(row.iloc[2]) if not pd.isna(row.iloc[2]) else 0
- }
- skill_groups.append(skill_group)
- except Exception as e:
- print(f"解析技能组第 {index+1} 行失败: {e}")
- continue
-
- return skill_groups
-
-
- def validate_config(self, config):
- """验证配置数据"""
- errors = []
-
- # 验证技能类型
- if not config.get("skillTypes"):
- errors.append("技能类型配置为空")
- else:
- for i, skill_type in enumerate(config["skillTypes"]):
- if not skill_type.get("name"):
- errors.append(f"技能类型 {i+1} 缺少名称")
- if not skill_type.get("type"):
- errors.append(f"技能类型 {i+1} 缺少类型标识")
-
- # 验证技能组
- if not config.get("skillGroups"):
- errors.append("技能组配置为空")
- else:
- groups = [sg["group"] for sg in config["skillGroups"]]
- if len(groups) != len(set(groups)):
- errors.append("技能组编号存在重复")
-
- # 验证基本配置
- if config.get("totalGroups", 0) <= 0:
- errors.append("总组数必须大于0")
- if config.get("skillsPerGroup", 0) <= 0:
- errors.append("每组技能数必须大于0")
-
- return errors
-
- def backup_json(self):
- """备份当前JSON文件(已禁用)"""
- # 不再创建备份文件,直接返回成功
- return True
-
- def save_json_config(self, config):
- """保存配置到JSON文件"""
- try:
- with open(self.json_file, 'w', encoding='utf-8') as f:
- json.dump(config, f, ensure_ascii=False, indent=2)
- print(f"配置已保存到: {self.json_file}")
- return True
- except Exception as e:
- print(f"保存JSON文件失败: {e}")
- return False
-
- class ConfigManagerGUI:
- def __init__(self):
- self.root = tk.Tk()
- self.root.title("游戏配置管理工具")
- self.root.geometry("1000x700")
- self.root.resizable(True, True)
-
- # 配置文件路径 - 动态获取项目根目录
- # 从当前脚本位置向上查找项目根目录
- current_dir = Path(__file__).parent
- self.project_root = current_dir.parent.parent.parent.parent # 从excel目录向上4级到项目根目录
- self.excel_dir = current_dir
-
- # 手动配置变量
- self.selected_json_path = None
- self.format_type = 'horizontal' # 默认为横向表格
- self.multi_sheet = False
- self.sheet_names = []
- self.param_types = {}
-
- # 当前选择的配置映射
- self.current_mapping = None
- # 移除json_config_path,现在使用selected_json_path
- self.param_types = {}
-
- # 默认配置值
- self.default_config = {
- 'baseSpeed': 60,
- 'maxReflectionRandomness': 0.2,
- 'antiTrapTimeWindow': 5.0,
- 'antiTrapHitThreshold': 5,
- 'deflectionAttemptThreshold': 3,
- 'antiTrapDeflectionMultiplier': 3.0,
- 'FIRE_COOLDOWN': 0.05,
- 'ballRadius': 10,
- 'gravityScale': 0,
- 'linearDamping': 0,
- 'angularDamping': 0,
- 'colliderGroup': 2,
- 'colliderTag': 1,
- 'friction': 0,
- 'restitution': 1,
- 'safeDistance': 50,
- 'edgeOffset': 20,
- 'sensor': False
- }
-
- self.selected_files = []
- self.config_data = {}
-
- # 初始化配置管理器类引用
- self.weapon_config_manager_class = WeaponConfigManager if WEAPON_MANAGER_AVAILABLE else None
- self.enemy_config_manager_class = EnemyConfigManager if ENEMY_MANAGER_AVAILABLE else None
- self.level_config_manager_class = LevelConfigManager if LEVEL_MANAGER_AVAILABLE else None
- self.ball_controller_config_manager_class = BallControllerConfigManager if BALL_CONTROLLER_MANAGER_AVAILABLE else None
- self.skill_config_manager_class = SkillConfigManager if SKILL_CONFIG_MANAGER_AVAILABLE else None
- self.game_skill_config_manager_class = GameSkillConfigManager if GAME_SKILL_MANAGER_AVAILABLE else None
- self.shop_config_manager_class = ShopConfigManager if SHOP_MANAGER_AVAILABLE else None
- self.ball_price_config_manager_class = BallPriceConfigManager if BALL_PRICE_MANAGER_AVAILABLE else None
-
- # 初始化新的配置管理器模块
- self.init_config_managers()
-
- self.setup_ui()
- self.load_current_config()
-
- def setup_ui(self):
- """设置用户界面"""
- # 主框架
- main_frame = ttk.Frame(self.root, padding="10")
- main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
-
- # 配置根窗口的网格权重
- self.root.columnconfigure(0, weight=1)
- self.root.rowconfigure(0, weight=1)
- main_frame.columnconfigure(1, weight=1)
- main_frame.rowconfigure(2, weight=1)
-
- # 标题
- title_label = ttk.Label(main_frame, text="游戏配置管理工具",
- font=("Arial", 16, "bold"))
- title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
-
- # 左侧面板 - 文件选择
- file_types_text = "Excel/CSV配置文件选择" if PANDAS_AVAILABLE else "CSV配置文件选择"
- left_frame = ttk.LabelFrame(main_frame, text=file_types_text, padding="10")
- left_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10))
- left_frame.columnconfigure(0, weight=1)
- left_frame.rowconfigure(3, weight=1)
-
- # 文件浏览按钮
- browse_frame = ttk.Frame(left_frame)
- browse_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
- browse_frame.columnconfigure(0, weight=1)
-
- browse_text = "浏览Excel/CSV文件" if PANDAS_AVAILABLE else "浏览CSV文件"
- ttk.Button(browse_frame, text=browse_text,
- command=self.browse_files).grid(row=0, column=0, sticky=(tk.W, tk.E))
- ttk.Button(browse_frame, text="扫描项目目录",
- command=self.scan_project_files).grid(row=0, column=1, padx=(10, 0))
-
- # JSON文件选择区域
- json_frame = ttk.LabelFrame(left_frame, text="JSON输出路径选择", padding="5")
- json_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
- json_frame.columnconfigure(1, weight=1)
-
- ttk.Label(json_frame, text="输出路径:").grid(row=0, column=0, sticky=tk.W)
- self.json_path_var = tk.StringVar()
- self.json_path_entry = ttk.Entry(json_frame, textvariable=self.json_path_var, state='readonly')
- self.json_path_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0))
- ttk.Button(json_frame, text="浏览", command=self.browse_json_path).grid(row=0, column=2, padx=(5, 0))
-
- # 配置选项
- config_frame = ttk.LabelFrame(left_frame, text="配置选项", padding="5")
- config_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- # 表格格式选择
- ttk.Label(config_frame, text="表格格式:").grid(row=0, column=0, sticky=tk.W)
- self.format_var = tk.StringVar(value="horizontal")
- format_frame = ttk.Frame(config_frame)
- format_frame.grid(row=0, column=1, sticky=tk.W, padx=(5, 0))
- ttk.Radiobutton(format_frame, text="横向", variable=self.format_var, value="horizontal").pack(side=tk.LEFT)
- ttk.Radiobutton(format_frame, text="纵向", variable=self.format_var, value="vertical").pack(side=tk.LEFT, padx=(10, 0))
-
- # 多工作表选项
- self.multi_sheet_var = tk.BooleanVar()
- ttk.Checkbutton(config_frame, text="多工作表文件", variable=self.multi_sheet_var,
- command=self.on_multi_sheet_change).grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=(5, 0))
-
- # 单个JSON选项
- self.single_json_var = tk.BooleanVar(value=True)
- ttk.Checkbutton(config_frame, text="单个json", variable=self.single_json_var,
- command=self.on_single_json_change).grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=(5, 0))
-
- # 文件列表
- list_frame = ttk.Frame(left_frame)
- list_frame.grid(row=3, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
- list_frame.columnconfigure(0, weight=1)
- list_frame.rowconfigure(0, weight=1)
-
- self.file_listbox = tk.Listbox(list_frame, selectmode=tk.MULTIPLE, height=15)
- scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.file_listbox.yview)
- self.file_listbox.configure(yscrollcommand=scrollbar.set)
-
- self.file_listbox.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
- scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
-
- # 文件操作按钮
- file_btn_frame = ttk.Frame(left_frame)
- file_btn_frame.grid(row=4, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- ttk.Button(file_btn_frame, text="预览选中文件",
- command=self.preview_selected_files).pack(side=tk.LEFT)
- ttk.Button(file_btn_frame, text="清空选择",
- command=self.clear_selection).pack(side=tk.LEFT, padx=(10, 0))
-
- # 右侧面板 - 配置预览和操作
- right_frame = ttk.LabelFrame(main_frame, text="配置预览与操作", padding="10")
- right_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S))
- right_frame.columnconfigure(0, weight=1)
- right_frame.rowconfigure(0, weight=1)
-
- # 配置预览文本框
- self.preview_text = scrolledtext.ScrolledText(right_frame, height=20, width=50)
- self.preview_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
-
- # 操作按钮 - 移除了导入配置、备份当前配置、恢复默认配置按钮
- btn_frame = ttk.Frame(right_frame)
- btn_frame.grid(row=1, column=0, sticky=(tk.W, tk.E))
-
- # 球控制器配置按钮
- ball_controller_btn_frame = ttk.Frame(right_frame)
- ball_controller_btn_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- if BALL_CONTROLLER_MANAGER_AVAILABLE:
- ttk.Button(ball_controller_btn_frame, text="导入球控制器配置",
- command=self.import_ball_controller_config).pack(side=tk.LEFT)
- else:
- ttk.Label(ball_controller_btn_frame, text="球控制器配置管理器不可用",
- foreground="red").pack(side=tk.LEFT)
-
- # 局外技能配置按钮
- skill_config_btn_frame = ttk.Frame(right_frame)
- skill_config_btn_frame.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- if SKILL_CONFIG_MANAGER_AVAILABLE:
- ttk.Button(skill_config_btn_frame, text="导入局外技能配置",
- command=self.import_skill_config_new).pack(side=tk.LEFT)
- else:
- ttk.Label(skill_config_btn_frame, text="局外技能配置管理器不可用",
- foreground="red").pack(side=tk.LEFT)
-
- # 游戏内技能配置按钮
- game_skill_btn_frame = ttk.Frame(right_frame)
- game_skill_btn_frame.grid(row=4, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- if GAME_SKILL_MANAGER_AVAILABLE:
- ttk.Button(game_skill_btn_frame, text="导入游戏内技能配置",
- command=self.import_game_skill_config).pack(side=tk.LEFT)
- else:
- ttk.Label(game_skill_btn_frame, text="游戏内技能配置管理器不可用",
- foreground="red").pack(side=tk.LEFT)
-
- # 武器配置专用按钮
- weapon_btn_frame = ttk.Frame(right_frame)
- weapon_btn_frame.grid(row=5, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- if WEAPON_MANAGER_AVAILABLE:
- ttk.Button(weapon_btn_frame, text="导入武器配置",
- command=self.import_weapon_config).pack(side=tk.LEFT)
- else:
- ttk.Label(weapon_btn_frame, text="武器配置管理器不可用",
- foreground="red").pack(side=tk.LEFT)
-
- # 敌人配置专用按钮
- enemy_btn_frame = ttk.Frame(right_frame)
- enemy_btn_frame.grid(row=6, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- if ENEMY_MANAGER_AVAILABLE:
- ttk.Button(enemy_btn_frame, text="导入敌人配置",
- command=self.import_enemy_config).pack(side=tk.LEFT)
- else:
- ttk.Label(enemy_btn_frame, text="敌人配置管理器不可用",
- foreground="red").pack(side=tk.LEFT)
-
- # 关卡配置专用按钮
- level_btn_frame = ttk.Frame(right_frame)
- level_btn_frame.grid(row=7, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- if LEVEL_MANAGER_AVAILABLE:
- ttk.Button(level_btn_frame, text="导入关卡配置",
- command=self.import_level_config).pack(side=tk.LEFT)
- else:
- ttk.Label(level_btn_frame, text="关卡配置管理器不可用",
- foreground="red").pack(side=tk.LEFT)
-
- # 商店配置专用按钮
- shop_btn_frame = ttk.Frame(right_frame)
- shop_btn_frame.grid(row=8, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- if SHOP_MANAGER_AVAILABLE:
- ttk.Button(shop_btn_frame, text="导入商店配置",
- command=self.import_shop_config).pack(side=tk.LEFT)
- else:
- ttk.Label(shop_btn_frame, text="商店配置管理器不可用",
- foreground="red").pack(side=tk.LEFT)
-
- # 小球价格配置专用按钮
- ball_price_btn_frame = ttk.Frame(right_frame)
- ball_price_btn_frame.grid(row=9, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
-
- if BALL_PRICE_MANAGER_AVAILABLE:
- ttk.Button(ball_price_btn_frame, text="导入小球价格配置",
- command=self.import_ball_price_config).pack(side=tk.LEFT)
- else:
- ttk.Label(ball_price_btn_frame, text="小球价格配置管理器不可用",
- foreground="red").pack(side=tk.LEFT)
-
- # 底部状态栏
- self.status_var = tk.StringVar()
- self.status_var.set("就绪")
- status_bar = ttk.Label(main_frame, textvariable=self.status_var,
- relief=tk.SUNKEN, anchor=tk.W)
- status_bar.grid(row=5, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(10, 0))
-
- # 绑定事件
- self.file_listbox.bind('<<ListboxSelect>>', self.on_file_select)
-
- def browse_files(self):
- """浏览Excel/CSV文件"""
- if PANDAS_AVAILABLE:
- title = "选择Excel/CSV配置文件"
- filetypes = [("Excel文件", "*.xlsx *.xls"), ("CSV文件", "*.csv"), ("文本文件", "*.txt"), ("所有文件", "*.*")]
- else:
- title = "选择CSV配置文件"
- filetypes = [("CSV文件", "*.csv"), ("文本文件", "*.txt"), ("所有文件", "*.*")]
-
- files = filedialog.askopenfilenames(
- title=title,
- filetypes=filetypes,
- initialdir=str(self.excel_dir)
- )
-
- if files:
- self.file_listbox.delete(0, tk.END)
- for file in files:
- self.file_listbox.insert(tk.END, file)
- self.status_var.set(f"已选择 {len(files)} 个文件")
-
- def scan_project_files(self):
- """扫描项目目录中的Excel/CSV文件"""
- self.status_var.set("正在扫描项目目录...")
-
- def scan_thread():
- config_files = []
-
- # 扫描常见的配置目录
- scan_dirs = [
- self.project_root / "assets/resources/data",
- self.project_root / "assets/resources/config",
- self.project_root / "assets/excel",
- self.project_root / "config",
- self.project_root / "data",
- self.project_root
- ]
-
- # 根据pandas可用性选择扫描的文件类型
- patterns = ['*.xlsx', '*.xls', '*.csv', '*.txt'] if PANDAS_AVAILABLE else ['*.csv', '*.txt']
-
- for scan_dir in scan_dirs:
- if scan_dir.exists():
- for pattern in patterns:
- config_files.extend(scan_dir.rglob(pattern))
-
- # 更新UI
- self.root.after(0, self.update_file_list, config_files)
-
- threading.Thread(target=scan_thread, daemon=True).start()
-
- def browse_json_path(self):
- """浏览JSON输出路径"""
- # 根据用户选择的选项组合决定选择文件还是目录
- multi_sheet = self.multi_sheet_var.get()
- single_json = self.single_json_var.get()
-
- if multi_sheet and not single_json:
- # 多工作表文件且不是单个JSON:输出到目录(每个sheet一个JSON文件)
- path = filedialog.askdirectory(
- title="选择JSON输出目录",
- initialdir=str(self.project_root / "assets/resources/data")
- )
- else:
- # 其他情况:输出单个JSON文件
- path = filedialog.askopenfilename(
- title="选择JSON输出文件",
- filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")],
- initialdir=str(self.project_root / "assets/resources/data")
- )
-
- if path:
- self.json_path_var.set(path)
- self.selected_json_path = Path(path)
- self.status_var.set(f"已选择输出路径: {Path(path).name}")
-
- def on_multi_sheet_change(self):
- """多工作表选项变化处理"""
- # 清空当前选择的JSON路径,让用户重新选择
- self.json_path_var.set("")
-
- def on_single_json_change(self):
- """单个JSON选项变化处理"""
- # 清空当前选择的JSON路径,让用户重新选择
- self.json_path_var.set("")
- self.selected_json_path = None
-
- if self.multi_sheet_var.get():
- self.status_var.set("多工作表模式:请选择输出目录")
- else:
- self.status_var.set("单工作表模式:请选择输出JSON文件")
-
- def update_file_list(self, files):
- """更新文件列表"""
- self.file_listbox.delete(0, tk.END)
- for file in files:
- self.file_listbox.insert(tk.END, str(file))
-
- file_type_text = "Excel/CSV文件" if PANDAS_AVAILABLE else "CSV文件"
- self.status_var.set(f"找到 {len(files)} 个{file_type_text}")
-
- def on_file_select(self, event):
- """文件选择事件处理"""
- selection = self.file_listbox.curselection()
- if selection:
- self.selected_files = [self.file_listbox.get(i) for i in selection]
- # 根据选择的文件设置配置映射
- self._set_config_mapping_for_files()
- self.preview_selected_files()
- self.status_var.set(f"已选择 {len(selection)} 个文件")
-
- def preview_selected_files(self):
- """预览选择的文件内容"""
- if not self.selected_files:
- self.preview_text.delete(1.0, tk.END)
- return
-
- preview_content = ""
- for file_path in self.selected_files:
- try:
- file_path_obj = Path(file_path)
- preview_content += f"文件: {file_path_obj.name}\n"
- preview_content += f"路径: {file_path}\n"
-
- if PANDAS_AVAILABLE and file_path.endswith(('.xlsx', '.xls')):
- # Excel文件预览
- try:
- xl_file = pd.ExcelFile(file_path)
- preview_content += f"工作表: {', '.join(xl_file.sheet_names)}\n"
-
- # 预览第一个工作表的前几行
- first_sheet = xl_file.sheet_names[0]
- df = pd.read_excel(file_path, sheet_name=first_sheet, nrows=5)
- preview_content += f"\n{first_sheet} 预览 (前5行):\n"
- preview_content += df.to_string(index=False) + "\n"
- except Exception as e:
- preview_content += f"Excel预览失败: {e}\n"
-
- elif file_path.endswith(('.csv', '.txt')):
- # CSV/TXT文件预览
- try:
- with open(file_path, 'r', encoding='utf-8') as f:
- lines = [f.readline().strip() for _ in range(5)]
- preview_content += "文件预览 (前5行):\n"
- preview_content += "\n".join(lines) + "\n"
- except Exception as e:
- preview_content += f"文件预览失败: {e}\n"
-
- preview_content += "-" * 50 + "\n\n"
-
- except Exception as e:
- preview_content += f"处理文件 {file_path} 时出错: {e}\n\n"
-
- # 更新预览文本框
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(1.0, preview_content)
-
- def _set_config_mapping_for_files(self):
- """根据选择的文件设置配置映射"""
- if not self.selected_files:
- return
-
- # 获取第一个文件名来判断配置类型
- first_file = Path(self.selected_files[0])
- filename = first_file.name.lower()
-
- # 根据文件名设置配置映射
- if '武器' in filename or 'weapon' in filename:
- self.current_mapping = {
- 'format_type': 'horizontal',
- 'param_types': {
- 'ID': str,
- '名称': str,
- '伤害': int,
- '射程': int,
- '射速': float,
- '弹药容量': int,
- '重装时间': float,
- '解锁等级': int,
- '价格': int,
- '描述': str,
- '最大等级': int,
- # 英文字段支持
- 'weaponId': str,
- 'name': str,
- 'damage': int,
- 'range': int,
- 'fireRate': float,
- 'ammoCapacity': int,
- 'reloadTime': float,
- 'unlockLevel': int,
- 'price': int,
- 'description': str,
- 'maxLevel': int
- }
- }
- elif '敌人' in filename or 'enemy' in filename:
- self.current_mapping = {
- 'format_type': 'multi_sheet',
- 'multi_sheet': True,
- 'param_types': {
- # 基础配置字段
- '敌人ID': ('id', str),
- '敌人名称': ('name', str),
- '敌人类型': ('type', str),
- '生命值': ('stats.health', int),
- '最大生命值': ('stats.maxHealth', int),
- '防御力': ('stats.defense', int),
- '移动速度': ('stats.speed', float),
-
- # 战斗配置字段
- '攻击伤害': ('combat.attackDamage', int),
- '攻击范围': ('combat.attackRange', int),
- '攻击速度': ('combat.attackSpeed', float),
- '能否格挡': ('combat.canBlock', bool),
- '格挡几率': ('combat.blockChance', float),
- '格挡伤害减免': ('combat.blockDamageReduction', float),
- '攻击冷却': ('combat.attackCooldown', float),
- '攻击类型': ('combat.attackType', str),
- '攻击延迟': ('combat.attackDelay', float),
- '武器类型': ('combat.weaponType', str),
- '投射物类型': ('combat.projectileType', str),
- '投射物速度': ('combat.projectileSpeed', float),
-
- # 移动配置字段
- '移动模式': ('movement.pattern', str),
- '巡逻范围': ('movement.patrolRange', int),
- '追击范围': ('movement.chaseRange', int),
- '旋转速度': ('movement.rotationSpeed', float),
- '移动类型': ('movement.moveType', str),
- '摆动幅度': ('movement.swingAmplitude', float),
- '摆动频率': ('movement.swingFrequency', float),
- '速度变化': ('movement.speedVariation', float),
-
- # 视觉配置字段
- '精灵路径': ('visualConfig.spritePath', str),
- '缩放比例': ('visualConfig.scale', float),
- '动画速度': ('visualConfig.animationSpeed', float),
- '水平翻转': ('visualConfig.flipX', bool),
- '待机动画': ('visualConfig.animations.idle', str),
- '行走动画': ('visualConfig.animations.walk', str),
- '攻击动画': ('visualConfig.animations.attack', str),
- '死亡动画': ('visualConfig.animations.death', str),
- '武器道具': ('visualConfig.weaponProp', str),
-
- # 音频配置字段
- '攻击音效': ('audioConfig.attackSound', str),
- '死亡音效': ('audioConfig.deathSound', str),
- '受击音效': ('audioConfig.hitSound', str),
- '行走音效': ('audioConfig.walkSound', str),
- '格挡音效': ('audioConfig.blockSound', str),
- '隐身音效': ('audioConfig.stealthSound', str),
- '护甲破碎音效': ('audioConfig.armorBreakSound', str),
- '引信音效': ('audioConfig.fuseSound', str),
-
- # 特殊能力配置字段
- '能力类型': ('specialAbilities.type', str),
- '伤害': ('specialAbilities.damage', int),
- '范围': ('specialAbilities.range', float),
- '冷却时间': ('specialAbilities.cooldown', float),
-
- # BOSS配置字段
- '是否BOSS': ('bossConfig.isBoss', bool),
- '阶段数': ('bossConfig.phases', int),
- '狂暴阈值': ('bossConfig.enrageThreshold', float),
- '狂暴伤害倍数': ('bossConfig.enrageDamageMultiplier', float),
- '狂暴速度倍数': ('bossConfig.enrageSpeedMultiplier', float),
-
- # 英文字段支持
- 'id': ('id', str),
- 'name': ('name', str),
- 'type': ('type', str),
- 'health': ('stats.health', int),
- 'maxHealth': ('stats.maxHealth', int),
- 'defense': ('stats.defense', int),
- 'speed': ('stats.speed', float),
- 'attackDamage': ('combat.attackDamage', int),
- 'attackRange': ('combat.attackRange', int),
- 'attackSpeed': ('combat.attackSpeed', float)
- }
- }
- elif '技能' in filename or 'skill' in filename:
- self.current_mapping = {
- 'format_type': 'horizontal',
- 'param_types': {
- 'ID': str,
- '名称': str,
- '伤害': int,
- '冷却时间': float,
- '消耗': int,
- '描述': str,
- # 英文字段支持
- 'skillId': str,
- 'skillName': str,
- 'damage': int,
- 'cooldown': float,
- 'cost': int,
- 'description': str
- }
- }
- elif '关卡' in filename or 'level' in filename:
- self.current_mapping = {
- 'format_type': 'horizontal',
- 'param_types': {
- 'ID': str,
- '名称': str,
- '敌人列表': str,
- '波数': int,
- '难度': int,
- # 英文字段支持
- 'levelId': str,
- 'name': str,
- 'enemies': str,
- 'waves': int,
- 'difficulty': int
- }
- }
- elif 'ballcontroller' in filename or '球控制' in filename or '标准配置表' in filename:
- # BallController配置映射(纵向格式:参数名-数值)
- self.current_mapping = {
- 'format_type': 'vertical',
- 'param_types': {
- 'baseSpeed': float,
- 'maxReflectionRandomness': float,
- 'antiTrapTimeWindow': float,
- 'antiTrapHitThreshold': int,
- 'deflectionAttemptThreshold': int,
- 'antiTrapDeflectionMultiplier': float,
- 'FIRE_COOLDOWN': float,
- 'ballRadius': float,
- 'gravityScale': float,
- 'linearDamping': float,
- 'angularDamping': float,
- 'colliderGroup': int,
- 'colliderTag': int,
- 'friction': float,
- 'restitution': float,
- 'safeDistance': float,
- 'edgeOffset': float,
- 'sensor': bool,
- 'maxAttempts': int
- }
- }
- else:
- # 默认配置映射
- self.current_mapping = {
- 'format_type': 'horizontal',
- 'param_types': {}
- }
-
- print(f"为文件 {filename} 设置配置映射: {self.current_mapping['format_type']} 格式")
- print(f"支持的参数类型: {list(self.current_mapping['param_types'].keys())}")
-
-
-
-
-
-
-
-
-
-
-
- def clear_selection(self):
- """清空选择"""
- self.file_listbox.selection_clear(0, tk.END)
- self.preview_text.delete(1.0, tk.END)
- self.config_data = {}
- self.status_var.set("已清空选择")
- self.load_current_config()
-
- def load_current_config(self):
- """加载当前配置"""
- try:
- if not self.selected_json_path:
- self.preview_text.insert(tk.END, "请先选择JSON输出路径\n")
- return
-
- json_path = Path(self.selected_json_path)
- if json_path.exists():
- with open(json_path, 'r', encoding='utf-8') as f:
- current_config = json.load(f)
-
- self.preview_text.insert(tk.END, f"=== 当前配置 ({json_path.name}) ===\n")
-
- # 根据配置类型显示不同的预览格式
- if self.format_var.get() == 'horizontal':
- # 横向表格配置(如敌人、武器)
- if 'enemies' in current_config:
- self.preview_text.insert(tk.END, f"敌人配置 ({len(current_config['enemies'])} 个):\n")
- for i, enemy in enumerate(current_config['enemies'][:5]): # 只显示前5个
- self.preview_text.insert(tk.END, f" {i+1}. {enemy.get('name', enemy.get('id', 'Unknown'))}\n")
- if len(current_config['enemies']) > 5:
- self.preview_text.insert(tk.END, f" ... 还有 {len(current_config['enemies']) - 5} 个\n")
-
- if 'weapons' in current_config:
- self.preview_text.insert(tk.END, f"\n武器配置 ({len(current_config['weapons'])} 个):\n")
- for i, weapon in enumerate(current_config['weapons'][:5]): # 只显示前5个
- self.preview_text.insert(tk.END, f" {i+1}. {weapon.get('name', weapon.get('id', 'Unknown'))}\n")
- if len(current_config['weapons']) > 5:
- self.preview_text.insert(tk.END, f" ... 还有 {len(current_config['weapons']) - 5} 个\n")
- else:
- # 纵向表格配置(如BallController)
- self.preview_text.insert(tk.END, json.dumps(current_config, indent=2, ensure_ascii=False))
-
- file_hint = "Excel/CSV文件" if PANDAS_AVAILABLE else "CSV文件"
- self.preview_text.insert(tk.END, f"\n\n请选择{file_hint}进行配置导入...\n")
- else:
- self.preview_text.insert(tk.END, "配置文件不存在\n")
- except Exception as e:
- self.preview_text.insert(tk.END, f"加载当前配置失败: {e}\n")
-
-
-
-
- def import_weapon_config(self):
- """导入武器配置"""
- try:
- if not WEAPON_MANAGER_AVAILABLE:
- messagebox.showerror("错误", "武器配置管理器不可用,请检查weapon_config_manager.py文件是否存在")
- return
-
- # 创建武器配置管理器
- excel_dir = Path(self.excel_dir)
- weapon_excel_file = excel_dir / "方块武器配置" / "方块武器配置表.xlsx"
- weapon_json_file = excel_dir.parent / "weapons.json"
-
- if not weapon_excel_file.exists():
- messagebox.showwarning("警告", f"未找到武器配置文件: {weapon_excel_file}")
- return
-
- # 使用WeaponConfigManager导入配置
- weapon_manager = WeaponConfigManager(
- excel_file_path=str(weapon_excel_file),
- json_file_path=str(weapon_json_file)
- )
-
- # 在后台线程中执行导入
- def import_thread():
- try:
- success = weapon_manager.import_weapon_config()
-
- # 在主线程中更新UI
- def update_ui():
- if success:
- messagebox.showinfo("成功", "武器配置导入成功!")
- self.status_var.set("武器配置导入成功")
- # 更新预览文本
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(tk.END, "武器配置导入成功\n\n配置已从Excel文件合并到JSON文件中")
- else:
- messagebox.showerror("错误", "武器配置导入失败,请查看控制台输出")
- self.status_var.set("武器配置导入失败")
-
- self.root.after(0, update_ui)
-
- except Exception as e:
- def show_error():
- import traceback
- error_details = traceback.format_exc()
- print(f"导入武器配置失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"导入武器配置失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- self.status_var.set("武器配置导入失败")
-
- self.root.after(0, show_error)
-
- # 启动导入线程
- self.status_var.set("正在导入武器配置...")
- thread = threading.Thread(target=import_thread)
- thread.daemon = True
- thread.start()
-
- except Exception as e:
- import traceback
- error_details = traceback.format_exc()
- print(f"启动武器配置导入失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"启动武器配置导入失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
-
- def import_enemy_config(self):
- """导入敌人配置"""
- try:
- if not ENEMY_MANAGER_AVAILABLE:
- messagebox.showerror("错误", "敌人配置管理器不可用,请检查enemy_config_manager.py文件是否存在")
- return
-
- # 创建敌人配置管理器
- excel_dir = Path(self.excel_dir)
- enemy_excel_file = excel_dir / "敌人配置表.xlsx"
- enemy_json_file = excel_dir.parent / "enemies.json"
-
- if not enemy_excel_file.exists():
- messagebox.showwarning("警告", f"未找到敌人配置文件: {enemy_excel_file}")
- return
-
- # 使用EnemyConfigManager导入配置
- enemy_manager = EnemyConfigManager(
- excel_path=str(enemy_excel_file),
- json_path=str(enemy_json_file)
- )
-
- # 在后台线程中执行导入
- def import_thread():
- try:
- success = enemy_manager.import_config()
-
- # 在主线程中更新UI
- def update_ui():
- if success:
- messagebox.showinfo("成功", "敌人配置导入成功!")
- self.status_var.set("敌人配置导入成功")
- # 更新预览文本
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(tk.END, "敌人配置导入成功\n\n配置已从Excel文件合并到JSON文件中")
- else:
- messagebox.showerror("错误", "敌人配置导入失败,请查看控制台输出")
- self.status_var.set("敌人配置导入失败")
-
- self.root.after(0, update_ui)
-
- except Exception as e:
- def show_error():
- import traceback
- error_details = traceback.format_exc()
- print(f"导入敌人配置失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"导入敌人配置失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- self.status_var.set("敌人配置导入失败")
-
- self.root.after(0, show_error)
-
- # 启动导入线程
- self.status_var.set("正在导入敌人配置...")
- thread = threading.Thread(target=import_thread)
- thread.daemon = True
- thread.start()
-
- except Exception as e:
- import traceback
- error_details = traceback.format_exc()
- print(f"启动敌人配置导入失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"启动敌人配置导入失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
-
- def import_level_config(self):
- """导入关卡配置"""
- try:
- if not LEVEL_MANAGER_AVAILABLE:
- messagebox.showerror("错误", "关卡配置管理器不可用,请检查level_config_manager.py文件是否存在")
- return
-
- # 创建关卡配置管理器
- excel_dir = Path(self.excel_dir)
- level_excel_file = excel_dir / "关卡配置" / "关卡配置表.xlsx"
- levels_dir = excel_dir.parent / "levels"
-
- if not level_excel_file.exists():
- messagebox.showwarning("警告", f"未找到关卡配置文件: {level_excel_file}")
- return
-
- if not levels_dir.exists():
- messagebox.showwarning("警告", f"未找到关卡配置目录: {levels_dir}")
- return
-
- # 使用LevelConfigManager导入配置
- level_manager = LevelConfigManager(
- excel_path=str(level_excel_file),
- levels_dir=str(levels_dir)
- )
-
- # 在后台线程中执行导入
- def import_thread():
- try:
- success = level_manager.import_from_excel()
-
- # 在主线程中更新UI
- def update_ui():
- if success:
- messagebox.showinfo("成功", "关卡配置导入成功!")
- self.status_var.set("关卡配置导入成功")
- # 更新预览文本
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(tk.END, "关卡配置导入成功\n\n配置已从Excel文件合并到各个关卡JSON文件中")
- else:
- messagebox.showerror("错误", "关卡配置导入失败,请查看控制台输出")
- self.status_var.set("关卡配置导入失败")
-
- self.root.after(0, update_ui)
-
- except Exception as e:
- def show_error():
- import traceback
- error_details = traceback.format_exc()
- print(f"导入关卡配置失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"导入关卡配置失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- self.status_var.set("关卡配置导入失败")
-
- self.root.after(0, show_error)
-
- # 启动导入线程
- self.status_var.set("正在导入关卡配置...")
- thread = threading.Thread(target=import_thread)
- thread.daemon = True
- thread.start()
-
- except Exception as e:
- import traceback
- error_details = traceback.format_exc()
- print(f"启动关卡配置导入失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"启动关卡配置导入失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
-
- def import_ball_controller_config(self):
- """导入球控制器配置"""
- try:
- if not BALL_CONTROLLER_MANAGER_AVAILABLE:
- messagebox.showerror("错误", "球控制器配置管理器不可用,请检查ball_controller_config_manager.py文件是否存在")
- return
-
- # 创建球控制器配置管理器
- excel_dir = Path(self.excel_dir)
- ball_controller_excel_file = excel_dir / "BallController标准配置表.xlsx"
- ball_controller_json_file = excel_dir.parent / "ballController.json"
-
- if not ball_controller_excel_file.exists():
- messagebox.showwarning("警告", f"未找到球控制器配置文件: {ball_controller_excel_file}")
- return
-
- # 使用BallControllerConfigManager导入配置
- ball_controller_manager = BallControllerConfigManager(
- excel_file_path=str(ball_controller_excel_file),
- json_file_path=str(ball_controller_json_file)
- )
-
- # 在后台线程中执行导入
- def import_thread():
- try:
- success = ball_controller_manager.import_ball_controller_config()
-
- # 在主线程中更新UI
- def update_ui():
- if success:
- messagebox.showinfo("成功", "球控制器配置导入成功!")
- self.status_var.set("球控制器配置导入成功")
- # 更新预览文本
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(tk.END, "球控制器配置导入成功\n\n配置已从Excel文件合并到JSON文件中")
- else:
- messagebox.showerror("错误", "球控制器配置导入失败,请查看控制台输出")
- self.status_var.set("球控制器配置导入失败")
-
- self.root.after(0, update_ui)
-
- except Exception as e:
- def show_error():
- import traceback
- error_details = traceback.format_exc()
- print(f"导入球控制器配置失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"导入球控制器配置失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- self.status_var.set("球控制器配置导入失败")
-
- self.root.after(0, show_error)
-
- # 启动导入线程
- self.status_var.set("正在导入球控制器配置...")
- thread = threading.Thread(target=import_thread)
- thread.daemon = True
- thread.start()
-
- except Exception as e:
- import traceback
- error_details = traceback.format_exc()
- print(f"启动球控制器配置导入失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"启动球控制器配置导入失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
-
- def import_skill_config_new(self):
- """导入局外技能配置"""
- try:
- if not SKILL_CONFIG_MANAGER_AVAILABLE:
- messagebox.showerror("错误", "局外技能配置管理器不可用,请检查skill_config_manager.py文件是否存在")
- return
-
- # 创建局外技能配置管理器
- excel_dir = Path(self.excel_dir)
- skill_excel_file = excel_dir / "局外技能配置表.xlsx"
- skill_json_file = excel_dir.parent / "skill_config.json"
-
- if not skill_excel_file.exists():
- messagebox.showwarning("警告", f"未找到局外技能配置文件: {skill_excel_file}")
- return
-
- # 使用SkillConfigManager导入配置
- skill_manager = SkillConfigManager(
- excel_file_path=str(skill_excel_file),
- json_file_path=str(skill_json_file)
- )
-
- # 在后台线程中执行导入
- def import_thread():
- try:
- success = skill_manager.import_skill_config()
-
- # 在主线程中更新UI
- def update_ui():
- if success:
- messagebox.showinfo("成功", "局外技能配置导入成功!")
- self.status_var.set("局外技能配置导入成功")
- # 更新预览文本
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(tk.END, "局外技能配置导入成功\n\n配置已从Excel文件合并到JSON文件中")
- else:
- messagebox.showerror("错误", "局外技能配置导入失败,请查看控制台输出")
- self.status_var.set("局外技能配置导入失败")
-
- self.root.after(0, update_ui)
-
- except Exception as e:
- def show_error():
- import traceback
- error_details = traceback.format_exc()
- print(f"导入局外技能配置失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"导入局外技能配置失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- self.status_var.set("局外技能配置导入失败")
-
- self.root.after(0, show_error)
-
- # 启动导入线程
- self.status_var.set("正在导入局外技能配置...")
- thread = threading.Thread(target=import_thread)
- thread.daemon = True
- thread.start()
-
- except Exception as e:
- import traceback
- error_details = traceback.format_exc()
- print(f"启动局外技能配置导入失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"启动局外技能配置导入失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
-
- def import_game_skill_config(self):
- """导入游戏内技能配置"""
- try:
- if not GAME_SKILL_MANAGER_AVAILABLE:
- messagebox.showerror("错误", "游戏内技能配置管理器不可用,请检查game_skill_config_manager.py文件是否存在")
- return
-
- # 创建游戏内技能配置管理器
- excel_dir = Path(self.excel_dir)
- game_skill_excel_file = excel_dir / "技能配置表.xlsx"
- game_skill_json_file = excel_dir.parent / "skill.json"
-
- if not game_skill_excel_file.exists():
- messagebox.showwarning("警告", f"未找到游戏内技能配置文件: {game_skill_excel_file}")
- return
-
- # 使用GameSkillConfigManager导入配置
- game_skill_manager = GameSkillConfigManager(
- excel_file_path=str(game_skill_excel_file),
- json_file_path=str(game_skill_json_file)
- )
-
- # 在后台线程中执行导入
- def import_thread():
- try:
- success = game_skill_manager.import_game_skill_config()
-
- # 在主线程中更新UI
- def update_ui():
- if success:
- messagebox.showinfo("成功", "游戏内技能配置导入成功!")
- self.status_var.set("游戏内技能配置导入成功")
- # 更新预览文本
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(tk.END, "游戏内技能配置导入成功\n\n配置已从Excel文件合并到JSON文件中")
- else:
- messagebox.showerror("错误", "游戏内技能配置导入失败,请查看控制台输出")
- self.status_var.set("游戏内技能配置导入失败")
-
- self.root.after(0, update_ui)
-
- except Exception as e:
- def show_error():
- import traceback
- error_details = traceback.format_exc()
- print(f"导入游戏内技能配置失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"导入游戏内技能配置失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- self.status_var.set("游戏内技能配置导入失败")
-
- self.root.after(0, show_error)
-
- # 启动导入线程
- self.status_var.set("正在导入游戏内技能配置...")
- thread = threading.Thread(target=import_thread)
- thread.daemon = True
- thread.start()
-
- except Exception as e:
- import traceback
- error_details = traceback.format_exc()
- print(f"启动游戏内技能配置导入失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"启动游戏内技能配置导入失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
-
- def import_shop_config(self):
- """导入商店配置"""
- try:
- if not SHOP_MANAGER_AVAILABLE:
- messagebox.showerror("错误", "商店配置管理器不可用,请检查shop_config_manager.py文件是否存在")
- return
-
- # 创建商店配置管理器
- excel_dir = Path(self.excel_dir)
- shop_excel_file = excel_dir / "商店配置" / "商店配置表.xlsx"
- shop_json_file = excel_dir.parent / "shop.json"
-
- if not shop_excel_file.exists():
- messagebox.showwarning("警告", f"未找到商店配置文件: {shop_excel_file}")
- return
-
- # 使用ShopConfigManager导入配置
- shop_manager = ShopConfigManager(
- excel_file_path=str(shop_excel_file),
- json_file_path=str(shop_json_file)
- )
-
- # 在后台线程中执行导入
- def import_thread():
- try:
- success = shop_manager.import_config()
-
- # 在主线程中更新UI
- def update_ui():
- if success:
- messagebox.showinfo("成功", "商店配置导入成功!")
- self.status_var.set("商店配置导入成功")
- # 更新预览文本
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(tk.END, "商店配置导入成功\n\n配置已从Excel文件合并到JSON文件中")
- else:
- messagebox.showerror("错误", "商店配置导入失败,请查看控制台输出")
- self.status_var.set("商店配置导入失败")
-
- self.root.after(0, update_ui)
-
- except Exception as e:
- def show_error():
- import traceback
- error_details = traceback.format_exc()
- print(f"导入商店配置失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"导入商店配置失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- self.status_var.set("商店配置导入失败")
-
- self.root.after(0, show_error)
-
- # 启动导入线程
- self.status_var.set("正在导入商店配置...")
- thread = threading.Thread(target=import_thread)
- thread.daemon = True
- thread.start()
-
- except Exception as e:
- import traceback
- error_details = traceback.format_exc()
- print(f"启动商店配置导入失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"启动商店配置导入失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
-
- def import_ball_price_config(self):
- """导入小球价格配置"""
- try:
- if not BALL_PRICE_MANAGER_AVAILABLE:
- messagebox.showerror("错误", "小球价格配置管理器不可用,请检查ball_price_config_manager.py文件是否存在")
- return
-
- # 创建小球价格配置管理器
- excel_dir = Path(self.excel_dir)
- ball_price_excel_file = excel_dir / "小球价格配置" / "小球价格配置表.xlsx"
- ball_price_json_file = excel_dir.parent / "ball_price_config.json"
-
- if not ball_price_excel_file.exists():
- messagebox.showwarning("警告", f"未找到小球价格配置文件: {ball_price_excel_file}")
- return
-
- # 使用BallPriceConfigManager导入配置
- ball_price_manager = BallPriceConfigManager(
- excel_file_path=str(ball_price_excel_file),
- json_file_path=str(ball_price_json_file)
- )
-
- # 在后台线程中执行导入
- def import_thread():
- try:
- success = ball_price_manager.import_config()
-
- # 在主线程中更新UI
- def update_ui():
- if success:
- messagebox.showinfo("成功", "小球价格配置导入成功!")
- self.status_var.set("小球价格配置导入成功")
- # 更新预览文本
- self.preview_text.delete(1.0, tk.END)
- self.preview_text.insert(tk.END, "小球价格配置导入成功\n\n配置已从Excel文件合并到JSON文件中")
- else:
- messagebox.showerror("错误", "小球价格配置导入失败,请查看控制台输出")
- self.status_var.set("小球价格配置导入失败")
-
- self.root.after(0, update_ui)
-
- except Exception as e:
- def show_error():
- import traceback
- error_details = traceback.format_exc()
- print(f"导入小球价格配置失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"导入小球价格配置失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- self.status_var.set("小球价格配置导入失败")
-
- self.root.after(0, show_error)
-
- # 启动导入线程
- self.status_var.set("正在导入小球价格配置...")
- thread = threading.Thread(target=import_thread)
- thread.daemon = True
- thread.start()
-
- except Exception as e:
- import traceback
- error_details = traceback.format_exc()
- print(f"启动小球价格配置导入失败,详细错误信息:")
- print(error_details)
- messagebox.showerror("错误", f"启动小球价格配置导入失败: {str(e)}\n\n详细错误信息已输出到控制台,请查看。")
- def init_config_managers(self):
- """初始化新的配置管理器模块"""
- try:
- # 初始化武器配置管理器
- if self.weapon_config_manager_class:
- self.weapon_config_manager = self.weapon_config_manager_class()
- print("武器配置管理器初始化成功")
- else:
- self.weapon_config_manager = None
- print("武器配置管理器不可用")
-
- # 初始化敌人配置管理器
- if self.enemy_config_manager_class:
- self.enemy_config_manager = self.enemy_config_manager_class()
- print("敌人配置管理器初始化成功")
- else:
- self.enemy_config_manager = None
- print("敌人配置管理器不可用")
-
- # 初始化关卡配置管理器
- if self.level_config_manager_class:
- self.level_config_manager = self.level_config_manager_class()
- print("关卡配置管理器初始化成功")
- else:
- self.level_config_manager = None
- print("关卡配置管理器不可用")
-
- # 初始化球控制器配置管理器
- if self.ball_controller_config_manager_class:
- self.ball_controller_config_manager = self.ball_controller_config_manager_class()
- print("球控制器配置管理器初始化成功")
- else:
- self.ball_controller_config_manager = None
- print("球控制器配置管理器不可用")
-
- # 初始化技能配置管理器
- if self.skill_config_manager_class:
- self.skill_config_manager = self.skill_config_manager_class()
- print("技能配置管理器初始化成功")
- else:
- self.skill_config_manager = None
- print("技能配置管理器不可用")
-
- # 初始化游戏内技能配置管理器
- if self.game_skill_config_manager_class:
- self.game_skill_config_manager = self.game_skill_config_manager_class()
- print("游戏内技能配置管理器初始化成功")
- else:
- self.game_skill_config_manager = None
- print("游戏内技能配置管理器不可用")
-
- except Exception as e:
- print(f"配置管理器初始化失败: {e}")
-
- def run(self):
- """运行应用"""
- self.root.mainloop()
- def main():
- """主函数"""
- try:
- app = ConfigManagerGUI()
- app.run()
- except Exception as e:
- print(f"启动应用失败: {e}")
- input("按回车键退出...")
- if __name__ == "__main__":
- main()
|