#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 武器配置管理器 从config_manager.py中提取的武器相关配置管理功能 支持从Excel读取武器配置并与现有JSON配置合并 """ import json import os from pathlib import Path from datetime import datetime try: import pandas as pd PANDAS_AVAILABLE = True except ImportError: PANDAS_AVAILABLE = False print("警告: pandas未安装,无法处理Excel文件") class WeaponConfigManager: """武器配置管理器""" def __init__(self, excel_file_path=None, json_file_path=None): """初始化武器配置管理器 Args: excel_file_path: Excel配置文件路径 json_file_path: JSON配置文件路径 """ self.script_dir = Path(__file__).parent # 设置默认路径 if excel_file_path is None: self.excel_file = self.script_dir / "方块武器配置" / "方块武器配置表.xlsx" else: self.excel_file = Path(excel_file_path) if json_file_path is None: self.json_file = self.script_dir.parent / "weapons.json" else: self.json_file = Path(json_file_path) print(f"Excel文件路径: {self.excel_file}") print(f"JSON文件路径: {self.json_file}") # 武器配置映射 self.weapon_mapping = { 'format_type': 'horizontal', 'param_types': { 'ID': str, '名称': str, '类型': str, '权重': int, '伤害': int, '射速': float, '射程': int, '子弹速度': int, # 方块价格配置字段 '基础每格成本': int, 'I形状成本': int, 'H-I形状成本': int, 'L形状成本': int, 'S形状成本': int, 'D-T形状成本': int, # 英文字段支持 'id': str, 'name': str, 'type': str, 'weight': int, 'damage': int, 'fireRate': float, 'range': int, 'bulletSpeed': int, 'baseCost': int, 'I_shape_cost': int, 'HI_shape_cost': int, 'L_shape_cost': int, 'S_shape_cost': int, 'DT_shape_cost': int } } def load_existing_json_config(self): """加载现有的JSON配置文件""" try: if self.json_file.exists(): with open(self.json_file, 'r', encoding='utf-8') as f: config = json.load(f) print(f"成功加载现有JSON配置,包含 {len(config.get('weapons', []))} 个武器") return config else: print(f"JSON文件不存在,将创建新配置: {self.json_file}") return {'weapons': [], 'blockSizes': []} except Exception as e: print(f"加载JSON配置失败: {e}") return {'weapons': [], 'blockSizes': []} def read_excel_config(self): """读取Excel配置文件""" if not PANDAS_AVAILABLE: raise Exception("pandas未安装,无法读取Excel文件") if not self.excel_file.exists(): raise Exception(f"Excel文件不存在: {self.excel_file}") try: # 读取所有工作表 all_sheets = pd.read_excel(self.excel_file, sheet_name=None) print(f"成功读取Excel文件,包含工作表: {list(all_sheets.keys())}") return all_sheets except Exception as e: raise Exception(f"读取Excel文件失败: {e}") def parse_weapon_multi_sheet_data(self, all_sheets_data): """解析武器配置表的多工作表数据""" weapons_config = {'weapons': []} try: # 解析武器基础配置工作表 base_sheet = None for sheet_name in ['武器基础配置', 'Weapon Config', 'weapons', '武器配置']: if sheet_name in all_sheets_data: base_sheet = all_sheets_data[sheet_name] break if base_sheet is not None: base_config = self.parse_config_data(base_sheet) if 'items' in base_config: weapons_config['weapons'] = base_config['items'] print(f"成功解析武器基础配置,共{len(base_config['items'])}个武器") # 解析武器升级费用配置工作表 upgrade_cost_sheet = None for sheet_name in ['武器升级费用配置', 'Weapon Upgrade Cost', 'upgrade_costs', '升级费用']: if sheet_name in all_sheets_data: upgrade_cost_sheet = all_sheets_data[sheet_name] print(f"找到升级费用配置工作表: {sheet_name}") break if upgrade_cost_sheet is not None: self._parse_upgrade_cost_data(upgrade_cost_sheet, weapons_config['weapons']) # 解析游戏内成本配置工作表 cost_sheet = None for sheet_name in ['游戏内成本配置', 'In Game Cost', 'cost_config', '成本配置']: if sheet_name in all_sheets_data: cost_sheet = all_sheets_data[sheet_name] print(f"找到游戏内成本配置工作表: {sheet_name}") break if cost_sheet is not None: self._parse_cost_config_data(cost_sheet, weapons_config['weapons']) # 解析方块形状配置工作表 block_shape_sheet = None for sheet_name in ['方块形状配置', 'Block Shape Config', 'block_shapes', '形状配置']: if sheet_name in all_sheets_data: block_shape_sheet = all_sheets_data[sheet_name] print(f"找到方块形状配置工作表: {sheet_name}") break if block_shape_sheet is not None: weapons_config['blockSizes'] = self._parse_block_shape_data(block_shape_sheet) return weapons_config except Exception as e: print(f"解析武器配置失败: {e}") return {'weapons': []} def parse_config_data(self, df): """解析配置数据""" try: items = [] # 检查第一行是否为表头 first_row = df.iloc[0] if len(df) > 0 else None is_header = False if first_row is not None: first_cell = str(first_row.iloc[0]).strip() if len(first_row) > 0 else "" if first_cell in ['武器ID', 'ID', 'weapon_id', 'weaponId']: is_header = True print(f"检测到表头行,第一列内容: {first_cell}") for index, row in df.iterrows(): if is_header and index == 0: # 跳过表头 continue # 转换行数据为字典 item = {} for col_index, value in enumerate(row): if col_index < len(df.columns): col_name = df.columns[col_index] if pd.notna(value) and str(value).strip(): # 根据映射转换数据类型 param_type = self.weapon_mapping['param_types'].get(col_name, str) try: if param_type == int: item[col_name] = int(float(value)) elif param_type == float: item[col_name] = float(value) else: item[col_name] = str(value).strip() except (ValueError, TypeError): item[col_name] = str(value).strip() # 检查是否有有效的武器ID weapon_id = item.get('ID') or item.get('id') or item.get('武器ID') if weapon_id and str(weapon_id).strip(): items.append(item) return {'items': items} except Exception as e: print(f"解析配置数据失败: {e}") return {'items': []} def _parse_upgrade_cost_data(self, upgrade_cost_sheet, weapons_list): """解析升级费用配置数据""" try: print(f"开始处理升级费用配置,工作表行数: {len(upgrade_cost_sheet)}") upgrade_cost_data = [] # 检查第一行是否为表头 first_row = upgrade_cost_sheet.iloc[0] if len(upgrade_cost_sheet) > 0 else None is_header = False if first_row is not None: first_cell = str(first_row.iloc[0]).strip() if len(first_row) > 0 else "" if first_cell in ['武器ID', 'ID', 'weapon_id', 'weaponId']: is_header = True print(f"检测到表头行,第一列内容: {first_cell}") for index, row in upgrade_cost_sheet.iterrows(): if is_header and index == 0: # 跳过表头 continue # 支持多种武器ID字段名 weapon_id = None for id_field in ['武器ID', 'ID', 'weapon_id', 'weaponId']: if id_field in row and pd.notna(row[id_field]): weapon_id = row[id_field] break if weapon_id is None: weapon_id = row.iloc[0] if len(row) > 0 else None if weapon_id and str(weapon_id).strip(): upgrade_levels = {} # 从第5列开始是等级1-10的费用,从第15列开始是等级1-10的伤害 for level in range(1, 11): cost_col_index = 4 + (level - 1) damage_col_index = 14 + (level - 1) level_config = {} # 处理费用 if cost_col_index < len(row): cost = row.iloc[cost_col_index] if cost and str(cost).strip() and str(cost) != 'nan': try: level_config['cost'] = int(float(cost)) except (ValueError, TypeError): pass # 处理伤害 if damage_col_index < len(row): damage = row.iloc[damage_col_index] if damage and str(damage).strip() and str(damage) != 'nan': try: level_config['damage'] = int(float(damage)) except (ValueError, TypeError): pass if level_config: upgrade_levels[str(level)] = level_config if upgrade_levels: upgrade_cost_data.append({ 'weapon_id': str(weapon_id).strip(), 'levels': upgrade_levels }) # 将升级费用配置合并到武器数据中 for weapon in weapons_list: weapon_id = weapon.get('ID', '') or weapon.get('id', '') if weapon_id: matching_upgrade = None for upgrade_data in upgrade_cost_data: if upgrade_data['weapon_id'] == weapon_id: matching_upgrade = upgrade_data break if matching_upgrade: weapon['upgradeConfig'] = { 'maxLevel': 10, 'levels': matching_upgrade['levels'] } print(f"✓ 为武器 {weapon_id} 添加了升级费用配置") except Exception as e: print(f"解析升级费用配置失败: {e}") def _parse_cost_config_data(self, cost_sheet, weapons_list): """解析游戏内成本配置数据""" try: print(f"开始处理游戏内成本配置,工作表行数: {len(cost_sheet)}") # 检查第一行是否为表头 first_row = cost_sheet.iloc[0] if len(cost_sheet) > 0 else None is_header = False if first_row is not None: first_cell = str(first_row.iloc[0]).strip() if len(first_row) > 0 else "" if first_cell in ['武器ID', 'ID', 'weapon_id', 'weaponId']: is_header = True for index, row in cost_sheet.iterrows(): if is_header and index == 0: # 跳过表头 continue # 获取武器ID weapon_id = None for id_field in ['武器ID', 'ID', 'weapon_id', 'weaponId']: if id_field in row and pd.notna(row[id_field]): weapon_id = str(row[id_field]).strip() break if weapon_id is None: weapon_id = str(row.iloc[0]).strip() if len(row) > 0 else None if weapon_id: # 查找对应的武器并添加成本配置 for weapon in weapons_list: w_id = weapon.get('ID', '') or weapon.get('id', '') if w_id == weapon_id: # 构建成本配置 base_cost = 5 # 默认基础成本 shape_costs = {} # 读取基础成本 for field in ['武器基础售价', 'baseCost', '基础成本']: if field in row and pd.notna(row[field]): try: base_cost = int(float(row[field])) break except (ValueError, TypeError): pass # 读取各形状成本 shape_fields = { 'I': ['I形状成本', 'I形状', 'I_shape', 'I'], 'H-I': ['H-I形状成本', 'H-I形状', 'HI_shape', 'H-I'], 'L': ['L形状成本', 'L形状', 'L_shape', 'L'], 'S': ['S形状成本', 'S形状', 'S_shape', 'S'], 'D-T': ['D-T形状成本', 'D-T形状', 'DT_shape', 'D-T'], 'L2': ['L2形状成本', 'L2形状', 'L2_shape', 'L2'], 'L3': ['L3形状成本', 'L3形状', 'L3_shape', 'L3'], 'L4': ['L4形状成本', 'L4形状', 'L4_shape', 'L4'], 'F-S': ['F-S形状成本', 'F-S形状', 'FS_shape', 'F-S'], 'T': ['T形状成本', 'T形状', 'T_shape', 'T'] } for shape_key, field_names in shape_fields.items(): for field_name in field_names: if field_name in row and pd.notna(row[field_name]): try: shape_costs[shape_key] = int(float(row[field_name])) break except (ValueError, TypeError): pass weapon['inGameCostConfig'] = { 'baseCost': base_cost, 'shapeCosts': shape_costs } print(f"✓ 为武器 {weapon_id} 添加了游戏内成本配置") break except Exception as e: print(f"解析游戏内成本配置失败: {e}") def _parse_block_shape_data(self, block_shape_sheet): """解析方块形状配置数据""" try: block_shapes = [] # 检查第一行是否为表头 first_row = block_shape_sheet.iloc[0] if len(block_shape_sheet) > 0 else None is_header = False if first_row is not None: first_cell = str(first_row.iloc[0]).strip() if len(first_row) > 0 else "" if first_cell in ['ID', 'id', '形状ID', 'shape_id']: is_header = True print(f"检测到方块形状配置表头行,第一列内容: {first_cell}") for index, row in block_shape_sheet.iterrows(): if is_header and index == 0: # 跳过表头 continue # 获取方块形状数据 shape_id = None shape_name = None shape_matrix = None grid_count = None cost_multiplier = None description = None # 获取ID for field in ['ID', 'id', '形状ID', 'shape_id']: if field in row and pd.notna(row[field]): shape_id = str(row[field]).strip() break if shape_id is None: shape_id = str(row.iloc[0]).strip() if len(row) > 0 else None # 获取名称 for field in ['名称', 'name', 'Name', '形状名称']: if field in row and pd.notna(row[field]): shape_name = str(row[field]).strip() break if shape_name is None and len(row) > 1: shape_name = str(row.iloc[1]).strip() if pd.notna(row.iloc[1]) else None # 获取形状矩阵 for field in ['形状矩阵', 'shape', 'matrix', '矩阵']: if field in row and pd.notna(row[field]): shape_matrix = str(row[field]).strip() break if shape_matrix is None and len(row) > 2: shape_matrix = str(row.iloc[2]).strip() if pd.notna(row.iloc[2]) else None # 获取占用格数 for field in ['占用格数', 'gridCount', 'grid_count', '格数']: if field in row and pd.notna(row[field]): try: grid_count = int(float(row[field])) break except (ValueError, TypeError): pass if grid_count is None and len(row) > 3: try: grid_count = int(float(row.iloc[3])) if pd.notna(row.iloc[3]) else None except (ValueError, TypeError): pass # 获取成本倍数 for field in ['成本倍数', 'costMultiplier', 'cost_multiplier', '倍数']: if field in row and pd.notna(row[field]): try: cost_multiplier = int(float(row[field])) break except (ValueError, TypeError): pass if cost_multiplier is None and len(row) > 4: try: cost_multiplier = int(float(row.iloc[4])) if pd.notna(row.iloc[4]) else None except (ValueError, TypeError): pass # 获取描述 for field in ['描述', 'description', 'Description', '说明']: if field in row and pd.notna(row[field]): description = str(row[field]).strip() break if description is None and len(row) > 5: description = str(row.iloc[5]).strip() if pd.notna(row.iloc[5]) else None # 如果有有效的形状ID,则创建形状配置 if shape_id: # 解析形状矩阵 shape_array = self._parse_shape_matrix(shape_matrix) block_shape = { "id": shape_id, "name": shape_name or shape_id, "shape": shape_array, "gridCount": grid_count or len([cell for row in shape_array for cell in row if cell == 1]), "costMultiplier": cost_multiplier or grid_count or 1, "description": description or f"{shape_name or shape_id}形状" } block_shapes.append(block_shape) print(f"✓ 添加方块形状配置: {shape_id} ({shape_name})") return block_shapes except Exception as e: print(f"解析方块形状配置失败: {e}") return [] def _parse_shape_matrix(self, shape_matrix_str): """解析形状矩阵字符串为二维数组""" try: if not shape_matrix_str: return [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] shape_matrix_str = str(shape_matrix_str).strip() # 尝试解析JSON格式的矩阵字符串,如 "[0, 1, 0, 0], [1, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0]" if '[' in shape_matrix_str and ']' in shape_matrix_str: try: # 添加外层方括号使其成为有效的JSON数组 json_str = '[' + shape_matrix_str + ']' import json shape_array = json.loads(json_str) # 确保是4x4矩阵 while len(shape_array) < 4: shape_array.append([0, 0, 0, 0]) for i in range(len(shape_array)): if len(shape_array[i]) < 4: shape_array[i].extend([0] * (4 - len(shape_array[i]))) shape_array[i] = shape_array[i][:4] return shape_array[:4] except (json.JSONDecodeError, ValueError) as e: print(f"JSON解析失败: {e}, 尝试其他解析方式") # 按换行符分割行(原有逻辑保留作为备用) lines = shape_matrix_str.split('\n') shape_array = [] for line in lines: line = line.strip() if line: # 将每个字符转换为数字 row = [int(char) for char in line if char in '01'] # 确保每行有4个元素 while len(row) < 4: row.append(0) shape_array.append(row[:4]) # 只取前4个元素 # 确保有4行 while len(shape_array) < 4: shape_array.append([0, 0, 0, 0]) return shape_array[:4] # 只取前4行 except Exception as e: print(f"解析形状矩阵失败: {e}, 使用默认矩阵") return [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] def merge_weapon_configs(self, existing_config, excel_config): """合并现有JSON配置和Excel配置""" try: print("开始合并武器配置...") # 创建现有武器的映射表(按ID索引) existing_weapons_map = {} for weapon in existing_config.get('weapons', []): weapon_id = weapon.get('id') if weapon_id: existing_weapons_map[weapon_id] = weapon print(f"现有武器数量: {len(existing_weapons_map)}") print(f"Excel武器数量: {len(excel_config.get('weapons', []))}") # 处理Excel中的武器数据 merged_weapons = [] for excel_weapon in excel_config.get('weapons', []): weapon_id = excel_weapon.get('ID') or excel_weapon.get('id') if not weapon_id: continue # 转换Excel数据为标准格式 converted_weapon = self._convert_weapon_data( excel_weapon, existing_weapons_map.get(weapon_id) ) if converted_weapon: merged_weapons.append(converted_weapon) print(f"✓ 处理武器: {weapon_id}") # 添加Excel中没有但现有配置中存在的武器 excel_weapon_ids = {w.get('ID') or w.get('id') for w in excel_config.get('weapons', [])} for weapon_id, existing_weapon in existing_weapons_map.items(): if weapon_id not in excel_weapon_ids: merged_weapons.append(existing_weapon) print(f"✓ 保留现有武器: {weapon_id}") # 构建最终配置 merged_config = existing_config.copy() merged_config['weapons'] = merged_weapons # 合并方块形状配置 if 'blockSizes' in excel_config: merged_config['blockSizes'] = excel_config['blockSizes'] print(f"✓ 更新方块形状配置,共{len(excel_config['blockSizes'])}个形状") print(f"合并完成,最终武器数量: {len(merged_weapons)}") return merged_config except Exception as e: print(f"合并武器配置失败: {e}") return existing_config def _convert_weapon_data(self, item, existing_weapon=None): """转换武器数据格式""" try: # 支持中英文字段名 weapon_id = item.get('id', item.get('ID', '')) weapon_name = item.get('name', item.get('名称', '')) if not weapon_id: print(f"跳过无效武器数据: 缺少武器ID - {item}") return None # 获取基础属性 damage = item.get('damage', item.get('伤害', 10)) fire_rate = item.get('fireRate', item.get('射速', 1.0)) weapon_range = item.get('range', item.get('射程', 100)) bullet_speed = item.get('bulletSpeed', item.get('子弹速度', 100)) weapon_type = item.get('type', item.get('类型', '')) weight = item.get('weight', item.get('权重', 1)) # 推断武器类型(如果为空) if not weapon_type: weapon_type = self._infer_weapon_type(weapon_id) # 设置默认权重(如果为空) if weight == 1: weight = 20 # 默认权重 # 构建基础武器配置 result = { 'id': weapon_id, 'name': weapon_name, 'type': weapon_type, 'weight': weight, 'stats': { 'damage': damage, 'fireRate': fire_rate, 'range': weapon_range, 'bulletSpeed': min(bullet_speed, 50) # 限制子弹速度 } } # 如果有现有武器配置,保留其bulletConfig和visualConfig if existing_weapon: if 'bulletConfig' in existing_weapon: result['bulletConfig'] = existing_weapon['bulletConfig'] print(f"为武器 {weapon_id} 保留现有的bulletConfig") else: result['bulletConfig'] = self._generate_bullet_config(weapon_id, weapon_type, damage, weapon_range) if 'visualConfig' in existing_weapon: result['visualConfig'] = existing_weapon['visualConfig'] print(f"为武器 {weapon_id} 保留现有的visualConfig") else: result['visualConfig'] = self._generate_visual_config(weapon_id, weapon_name) else: # 生成默认配置 result['bulletConfig'] = self._generate_bullet_config(weapon_id, weapon_type, damage, weapon_range) result['visualConfig'] = self._generate_visual_config(weapon_id, weapon_name) # 添加升级配置(如果Excel中有) if 'upgradeConfig' in item: result['upgradeConfig'] = item['upgradeConfig'] print(f"为武器 {weapon_id} 添加升级配置") # 添加游戏内成本配置(如果Excel中有) if 'inGameCostConfig' in item: result['inGameCostConfig'] = item['inGameCostConfig'] print(f"为武器 {weapon_id} 添加游戏内成本配置") return result except Exception as e: print(f"转换武器数据失败: {e} - 数据: {item}") return None def _infer_weapon_type(self, weapon_id): """根据武器ID推断武器类型""" if 'shotgun' in weapon_id or 'cactus' in weapon_id: return 'shotgun' elif 'bomb' in weapon_id or 'pepper' in weapon_id: return 'explosive' elif 'missile' in weapon_id: return 'homing_missile' elif 'boomerang' in weapon_id: return 'boomerang' elif 'saw' in weapon_id: return 'ricochet_piercing' elif 'carrot' in weapon_id: return 'piercing' else: return 'single_shot' def _generate_bullet_config(self, weapon_id, weapon_type, damage, weapon_range): """生成子弹配置""" # 基础配置模板 base_config = { 'count': {'type': 'single', 'amount': 1, 'spreadAngle': 0, 'burstCount': 1, 'burstDelay': 0}, 'trajectory': {'type': 'straight', 'speed': 200, 'gravity': 0, 'arcHeight': 0, 'homingStrength': 0, 'homingDelay': 0}, 'hitEffects': [{'type': 'normal_damage', 'priority': 1, 'damage': damage}], 'lifecycle': {'type': 'hit_destroy', 'maxLifetime': 5.0, 'penetration': 1, 'ricochetCount': 0, 'returnToOrigin': False}, 'visual': { 'bulletImages': f'images/PlantsSprite/{sprite_id}', 'hitEffect': 'Animation/WeaponTx/tx0002/tx0002', 'trailEffect': True } } # 根据武器类型调整配置 if weapon_type == 'shotgun': base_config['count'] = {'type': 'spread', 'amount': 5, 'spreadAngle': 30, 'burstCount': 1, 'burstDelay': 0} base_config['lifecycle']['type'] = 'range_limit' base_config['lifecycle']['maxRange'] = weapon_range * 2 elif weapon_type == 'piercing': base_config['hitEffects'] = [{'type': 'pierce_damage', 'priority': 1, 'damage': damage, 'pierceCount': 999}] base_config['lifecycle'] = {'type': 'range_limit', 'maxLifetime': 5.0, 'penetration': 999, 'ricochetCount': 0, 'returnToOrigin': False, 'maxRange': weapon_range * 2} elif weapon_type == 'explosive': base_config['trajectory']['type'] = 'arc' base_config['hitEffects'] = [{'type': 'explosion', 'priority': 1, 'damage': damage + 20, 'radius': 100, 'delay': 0.1}] base_config['lifecycle']['type'] = 'ground_impact' base_config['visual']['hitEffect'] = 'Animation/WeaponTx/tx0007/tx0007' base_config['visual']['explosionEffect'] = 'Animation/WeaponTx/tx0007/tx0007' return base_config def _generate_visual_config(self, weapon_id, weapon_name): """生成视觉配置""" # 根据武器ID生成图片编号 weapon_sprite_map = { 'pea_shooter': '001-1', 'sharp_carrot': '002', 'saw_grass': '003', 'watermelon_bomb': '007', 'boomerang_plant': '004', 'hot_pepper': '005', 'cactus_shotgun': '008', 'okra_missile': '006', 'mace_club': '009' } sprite_id = weapon_sprite_map.get(weapon_id, '001') return { 'weaponSprites': f'images/PlantsSprite/{sprite_id}', 'fireSound': f'audio/{weapon_id}_shot' } def backup_json_config(self): """备份现有JSON配置""" try: if self.json_file.exists(): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") backup_file = self.json_file.parent / f"{self.json_file.stem}_backup_{timestamp}.json" with open(self.json_file, 'r', encoding='utf-8') as src: with open(backup_file, 'w', encoding='utf-8') as dst: dst.write(src.read()) print(f"配置已备份到: {backup_file}") return backup_file else: print("JSON文件不存在,无需备份") return None except Exception as e: print(f"备份配置失败: {e}") return None def save_json_config(self, config): """保存配置到JSON文件""" try: # 确保目录存在 self.json_file.parent.mkdir(parents=True, exist_ok=True) 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 def import_weapon_config(self): """导入武器配置的主方法""" try: print("开始导入武器配置...") # 1. 加载现有JSON配置 existing_config = self.load_existing_json_config() # 2. 读取Excel配置 excel_sheets = self.read_excel_config() # 3. 解析Excel数据 excel_config = self.parse_weapon_multi_sheet_data(excel_sheets) # 4. 合并配置 merged_config = self.merge_weapon_configs(existing_config, excel_config) # 5. 备份现有配置 self.backup_json_config() # 6. 保存新配置 if self.save_json_config(merged_config): print("武器配置导入成功!") return True else: print("武器配置保存失败!") return False except Exception as e: print(f"导入武器配置失败: {e}") return False def sync_json_to_excel(self): """将JSON配置同步到Excel文件""" try: print("开始将JSON配置同步到Excel文件...") # 导入生成器模块 from generate_excel_from_json import WeaponExcelGenerator # 创建Excel生成器 generator = WeaponExcelGenerator( json_file_path=str(self.json_file), excel_output_path=str(self.excel_file) ) # 生成Excel文件 success = generator.generate_excel_file() if success: print("✓ JSON配置已成功同步到Excel文件") return True else: print("✗ JSON配置同步到Excel文件失败") return False except Exception as e: print(f"同步JSON到Excel失败: {e}") return False def sync_excel_to_json(self): """将Excel配置同步到JSON文件""" try: print("开始将Excel配置同步到JSON文件...") # 使用现有的导入方法 success = self.import_weapon_config() if success: print("✓ Excel配置已成功同步到JSON文件") return True else: print("✗ Excel配置同步到JSON文件失败") return False except Exception as e: print(f"同步Excel到JSON失败: {e}") return False def show_sync_menu(self): """显示同步菜单""" while True: print("\n武器配置同步工具") print("=" * 50) print("1. 从JSON同步到Excel (推荐)") print("2. 从Excel同步到JSON") print("3. 查看文件状态") print("4. 退出") print("=" * 50) choice = input("请选择操作 (1-4): ").strip() if choice == '1': print("\n正在从JSON同步到Excel...") success = self.sync_json_to_excel() if success: print("🎉 同步完成!Excel文件已更新") else: print("❌ 同步失败!") elif choice == '2': print("\n正在从Excel同步到JSON...") success = self.sync_excel_to_json() if success: print("🎉 同步完成!JSON文件已更新") else: print("❌ 同步失败!") elif choice == '3': self.show_file_status() elif choice == '4': print("\n再见!") break else: print("\n❌ 无效选择,请重新输入") def show_file_status(self): """显示文件状态""" print("\n文件状态信息") print("-" * 30) # JSON文件状态 if self.json_file.exists(): json_mtime = datetime.fromtimestamp(self.json_file.stat().st_mtime) print(f"✓ JSON文件: {self.json_file}") print(f" 最后修改: {json_mtime.strftime('%Y-%m-%d %H:%M:%S')}") try: with open(self.json_file, 'r', encoding='utf-8') as f: config = json.load(f) weapon_count = len(config.get('weapons', [])) print(f" 武器数量: {weapon_count}") except Exception as e: print(f" 读取失败: {e}") else: print(f"❌ JSON文件不存在: {self.json_file}") print() # Excel文件状态 if self.excel_file.exists(): excel_mtime = datetime.fromtimestamp(self.excel_file.stat().st_mtime) print(f"✓ Excel文件: {self.excel_file}") print(f" 最后修改: {excel_mtime.strftime('%Y-%m-%d %H:%M:%S')}") try: if PANDAS_AVAILABLE: sheets = pd.read_excel(self.excel_file, sheet_name=None) print(f" 工作表数量: {len(sheets)}") print(f" 工作表名称: {list(sheets.keys())}") else: print(" 无法读取详细信息 (pandas未安装)") except Exception as e: print(f" 读取失败: {e}") else: print(f"❌ Excel文件不存在: {self.excel_file}") def main(): """主函数""" print("武器配置管理器") print("=" * 50) # 创建武器配置管理器 manager = WeaponConfigManager() # 显示同步菜单 manager.show_sync_menu() if __name__ == "__main__": main()