|
|
@@ -434,6 +434,15 @@ class ConfigManagerGUI:
|
|
|
ttk.Button(btn_frame, text="恢复默认配置",
|
|
|
command=self.restore_default_config).pack(side=tk.LEFT, padx=(10, 0))
|
|
|
|
|
|
+ # 敌人配置专用按钮
|
|
|
+ enemy_btn_frame = ttk.Frame(right_frame)
|
|
|
+ enemy_btn_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
|
|
|
+
|
|
|
+ ttk.Button(enemy_btn_frame, text="修复敌人JSON",
|
|
|
+ command=self.fix_enemies_json).pack(side=tk.LEFT)
|
|
|
+ ttk.Button(enemy_btn_frame, text="自动导入敌人配置",
|
|
|
+ command=self.auto_import_enemies_from_excel).pack(side=tk.LEFT, padx=(10, 0))
|
|
|
+
|
|
|
# 底部状态栏
|
|
|
self.status_var = tk.StringVar()
|
|
|
self.status_var.set("就绪")
|
|
|
@@ -598,25 +607,87 @@ class ConfigManagerGUI:
|
|
|
}
|
|
|
elif '敌人' in filename or 'enemy' in filename:
|
|
|
self.current_mapping = {
|
|
|
- 'format_type': 'horizontal',
|
|
|
+ 'format_type': 'multi_sheet',
|
|
|
+ 'multi_sheet': True,
|
|
|
'param_types': {
|
|
|
- 'ID': str,
|
|
|
- '名称': str,
|
|
|
- '生命值': int,
|
|
|
- '攻击力': int,
|
|
|
- '防御力': int,
|
|
|
- '速度': float,
|
|
|
- '经验值': int,
|
|
|
- '掉落金币': int,
|
|
|
+ # 基础配置字段
|
|
|
+ '敌人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),
|
|
|
+
|
|
|
# 英文字段支持
|
|
|
- 'enemyId': str,
|
|
|
- 'name': str,
|
|
|
- 'hp': int,
|
|
|
- 'attack': int,
|
|
|
- 'defense': int,
|
|
|
- 'speed': float,
|
|
|
- 'exp': int,
|
|
|
- 'gold': int
|
|
|
+ '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:
|
|
|
@@ -765,17 +836,27 @@ class ConfigManagerGUI:
|
|
|
# 多工作表模式:读取所有工作表
|
|
|
print(f"多工作表模式处理文件: {file_path.name}")
|
|
|
|
|
|
- # 获取所有工作表名称
|
|
|
- excel_file = pd.ExcelFile(file_path)
|
|
|
- sheet_names = excel_file.sheet_names
|
|
|
- print(f"发现工作表: {sheet_names}")
|
|
|
+ # 获取所有工作表名称(避免缓存问题)
|
|
|
+ try:
|
|
|
+ # 先尝试读取第一个工作表来获取所有工作表名称
|
|
|
+ excel_file = pd.ExcelFile(file_path)
|
|
|
+ sheet_names = excel_file.sheet_names
|
|
|
+ excel_file.close() # 关闭文件句柄
|
|
|
+ print(f"发现工作表: {sheet_names}")
|
|
|
+ except Exception as e:
|
|
|
+ print(f"获取工作表名称失败: {e}")
|
|
|
+ return config
|
|
|
|
|
|
all_sheets_data = {}
|
|
|
for sheet_name in sheet_names:
|
|
|
try:
|
|
|
- df = pd.read_excel(file_path, sheet_name=sheet_name)
|
|
|
+ # 每次都重新读取文件,避免缓存
|
|
|
+ df = pd.read_excel(file_path, sheet_name=sheet_name, engine='openpyxl')
|
|
|
all_sheets_data[sheet_name] = df
|
|
|
print(f"成功读取工作表: {sheet_name}, 数据行数: {len(df)}")
|
|
|
+ # 打印第一行数据用于调试
|
|
|
+ if len(df) > 0 and sheet_name == '敌人基础配置':
|
|
|
+ print(f"敌人基础配置第一行数据: {df.iloc[0].to_dict()}")
|
|
|
except Exception as e:
|
|
|
print(f"读取工作表 {sheet_name} 时出错: {e}")
|
|
|
|
|
|
@@ -1125,23 +1206,24 @@ class ConfigManagerGUI:
|
|
|
|
|
|
enemy_id = str(enemy_row[enemy_id_field])
|
|
|
|
|
|
- # 基础配置
|
|
|
+ # 基础配置(不包含攻击伤害,攻击伤害从战斗配置表获取)
|
|
|
enemy_data = {
|
|
|
'id': enemy_id,
|
|
|
'name': get_field_value(enemy_row, '敌人名称', ['name', 'enemy_name'], ''),
|
|
|
'type': get_field_value(enemy_row, '敌人类型', ['type', 'enemy_type'], 'basic'),
|
|
|
'health': get_field_value(enemy_row, '生命值', ['health', 'hp'], 100, int),
|
|
|
'speed': get_field_value(enemy_row, '移动速度', ['speed', 'move_speed'], 1.0, float),
|
|
|
- 'attack': get_field_value(enemy_row, '攻击力', ['attack', 'damage'], 10, int),
|
|
|
'attackRange': get_field_value(enemy_row, '攻击范围', ['attackRange', 'attack_range', 'range'], 1.0, float),
|
|
|
'attackSpeed': get_field_value(enemy_row, '攻击速度', ['attackSpeed', 'attack_speed'], 1.0, float),
|
|
|
- 'defense': get_field_value(enemy_row, '防御力', ['defense'], 0, int)
|
|
|
+ 'defense': get_field_value(enemy_row, '防御力', ['defense'], 0, int),
|
|
|
+ 'attackDamage': 10 # 默认值,将从战斗配置表覆盖
|
|
|
}
|
|
|
|
|
|
# 获取战斗配置
|
|
|
combat_row = find_row_by_id(combat_sheet, enemy_id)
|
|
|
if combat_row is not None:
|
|
|
enemy_data.update({
|
|
|
+ 'attackDamage': get_field_value(combat_row, '攻击伤害', ['attackDamage', 'attack'], 10, int),
|
|
|
'attackType': get_field_value(combat_row, '攻击类型', ['attackType'], 'melee'),
|
|
|
'attackDelay': get_field_value(combat_row, '攻击延迟', ['attackDelay'], 0.5, float),
|
|
|
'weaponType': get_field_value(combat_row, '武器类型', ['weaponType'], 'none'),
|
|
|
@@ -1224,8 +1306,80 @@ class ConfigManagerGUI:
|
|
|
'enrageSpeedMultiplier': get_field_value(boss_row, '狂暴速度倍数', ['enrageSpeedMultiplier'], 1.3, float)
|
|
|
}
|
|
|
|
|
|
- enemies_config.append(enemy_data)
|
|
|
+ # 转换为嵌套结构
|
|
|
+ nested_enemy_data = {
|
|
|
+ 'id': enemy_data['id'],
|
|
|
+ 'name': enemy_data['name'],
|
|
|
+ 'type': enemy_data['type'],
|
|
|
+ 'stats': {
|
|
|
+ 'health': enemy_data.get('health', 100),
|
|
|
+ 'maxHealth': enemy_data.get('health', 100),
|
|
|
+ 'defense': enemy_data.get('defense', 0),
|
|
|
+ 'speed': enemy_data.get('speed', 50)
|
|
|
+ },
|
|
|
+ 'movement': {
|
|
|
+ 'pattern': enemy_data.get('movementPattern', 'direct'),
|
|
|
+ 'speed': enemy_data.get('speed', 50),
|
|
|
+ 'patrolRange': enemy_data.get('patrolRange', 100),
|
|
|
+ 'chaseRange': enemy_data.get('chaseRange', 200),
|
|
|
+ 'rotationSpeed': enemy_data.get('rotationSpeed', 180),
|
|
|
+ 'moveType': enemy_data.get('moveType', 'straight'),
|
|
|
+ 'swingAmplitude': enemy_data.get('swingAmplitude', 0.0),
|
|
|
+ 'swingFrequency': enemy_data.get('swingFrequency', 0.0),
|
|
|
+ 'speedVariation': enemy_data.get('speedVariation', 0.1)
|
|
|
+ },
|
|
|
+ 'combat': {
|
|
|
+ 'attackDamage': enemy_data.get('attackDamage', 10),
|
|
|
+ 'attackRange': enemy_data.get('attackRange', 100),
|
|
|
+ 'attackSpeed': enemy_data.get('attackSpeed', 1.0),
|
|
|
+ 'canBlock': enemy_data.get('canBlock', False),
|
|
|
+ 'blockChance': enemy_data.get('blockChance', 0.0),
|
|
|
+ 'blockDamageReduction': enemy_data.get('blockDamageReduction', 0.5),
|
|
|
+ 'attackCooldown': enemy_data.get('attackCooldown', 1.0),
|
|
|
+ 'attackType': enemy_data.get('attackType', 'melee'),
|
|
|
+ 'attackDelay': enemy_data.get('attackDelay', 0.5),
|
|
|
+ 'weaponType': enemy_data.get('weaponType', 'none'),
|
|
|
+ 'projectileType': enemy_data.get('projectileType', 'none'),
|
|
|
+ 'projectileSpeed': enemy_data.get('projectileSpeed', 100.0)
|
|
|
+ },
|
|
|
+ 'visualConfig': {
|
|
|
+ 'spritePath': enemy_data.get('spritePath', f'Animation/EnemyAni/001'),
|
|
|
+ 'scale': enemy_data.get('scale', 1.0),
|
|
|
+ 'animationSpeed': enemy_data.get('animationSpeed', 1.0),
|
|
|
+ 'flipX': enemy_data.get('flipX', False),
|
|
|
+ 'tint': enemy_data.get('tint', '#FFFFFF'),
|
|
|
+ 'animations': {
|
|
|
+ 'idle': 'idle',
|
|
|
+ 'walk': 'walk',
|
|
|
+ 'attack': 'attack',
|
|
|
+ 'death': 'dead'
|
|
|
+ },
|
|
|
+ 'weaponProp': enemy_data.get('weaponProp', '')
|
|
|
+ },
|
|
|
+ 'audioConfig': {
|
|
|
+ 'attackSound': enemy_data.get('attackSound', 'enemy_attack'),
|
|
|
+ 'deathSound': enemy_data.get('deathSound', 'enemy_death'),
|
|
|
+ 'hitSound': enemy_data.get('hitSound', 'enemy_hit'),
|
|
|
+ 'walkSound': enemy_data.get('walkSound', ''),
|
|
|
+ 'blockSound': enemy_data.get('blockSound', ''),
|
|
|
+ 'stealthSound': enemy_data.get('stealthSound', ''),
|
|
|
+ 'armorBreakSound': enemy_data.get('armorBreakSound', ''),
|
|
|
+ 'fuseSound': enemy_data.get('fuseSound', ''),
|
|
|
+ 'volume': enemy_data.get('volume', 1.0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ # 添加特殊能力配置
|
|
|
+ if 'specialAbilities' in enemy_data:
|
|
|
+ nested_enemy_data['specialAbilities'] = enemy_data['specialAbilities']
|
|
|
+
|
|
|
+ # 添加BOSS配置
|
|
|
+ if 'bossConfig' in enemy_data:
|
|
|
+ nested_enemy_data['bossConfig'] = enemy_data['bossConfig']
|
|
|
+
|
|
|
+ enemies_config.append(nested_enemy_data)
|
|
|
print(f"处理敌人: {enemy_data['id']} - {enemy_data['name']}")
|
|
|
+ print(f"敌人 {enemy_data['id']} 的生命值: {nested_enemy_data['stats']['health']}")
|
|
|
|
|
|
print(f"成功解析敌人配置,共 {len(enemies_config)} 个敌人")
|
|
|
|
|
|
@@ -1242,6 +1396,10 @@ class ConfigManagerGUI:
|
|
|
if '方块武器配置表' in filename:
|
|
|
return self.parse_weapon_multi_sheet_data(all_sheets_data, filename)
|
|
|
|
|
|
+ # 处理敌人配置表的多工作表
|
|
|
+ if '敌人配置表' in filename or '敌人' in filename:
|
|
|
+ return self.parse_enemy_multi_sheet_data(all_sheets_data, filename)
|
|
|
+
|
|
|
# 这里可以添加其他多工作表文件的处理逻辑
|
|
|
# 目前只返回第一个工作表的数据
|
|
|
if all_sheets_data:
|
|
|
@@ -1825,9 +1983,13 @@ class ConfigManagerGUI:
|
|
|
print("开始处理纵向表格配置...")
|
|
|
self._import_vertical_config()
|
|
|
elif format_type == 'horizontal':
|
|
|
- # 处理横向表格(如敌人配置、武器配置)
|
|
|
+ # 处理横向表格(如武器配置)
|
|
|
print("开始处理横向表格配置...")
|
|
|
self._import_horizontal_config()
|
|
|
+ elif format_type == 'multi_sheet':
|
|
|
+ # 处理多工作表格式(如敌人配置)
|
|
|
+ print("开始处理多工作表配置...")
|
|
|
+ self._import_multi_sheet_config() # 使用专门的多工作表处理逻辑
|
|
|
else:
|
|
|
raise ValueError(f"未知的格式类型: {format_type}")
|
|
|
|
|
|
@@ -2229,6 +2391,58 @@ class ConfigManagerGUI:
|
|
|
# 刷新预览
|
|
|
self.clear_selection()
|
|
|
|
|
|
+ def _import_multi_sheet_config(self):
|
|
|
+ """导入多工作表配置(直接使用解析后的数据,不再通过_convert_enemy_data转换)"""
|
|
|
+ print(f"多工作表配置导入开始...")
|
|
|
+ print(f"配置数据结构: {list(self.config_data.keys()) if self.config_data else 'None'}")
|
|
|
+
|
|
|
+ try:
|
|
|
+ # 读取现有JSON配置
|
|
|
+ if self.selected_json_path.exists():
|
|
|
+ with open(self.selected_json_path, 'r', encoding='utf-8') as f:
|
|
|
+ current_config = json.load(f)
|
|
|
+ else:
|
|
|
+ current_config = self.default_config.copy()
|
|
|
+
|
|
|
+ # 直接使用解析后的多工作表数据
|
|
|
+ if isinstance(self.config_data, list):
|
|
|
+ # 如果config_data是列表(如敌人配置),直接使用
|
|
|
+ print(f"使用解析后的敌人配置数据,共 {len(self.config_data)} 个敌人")
|
|
|
+ current_config['enemies'] = self.config_data
|
|
|
+
|
|
|
+ # 添加元数据
|
|
|
+ current_config['metadata'] = {
|
|
|
+ 'last_updated': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
|
|
+ 'totalEnemies': len(self.config_data),
|
|
|
+ 'conversionMethod': 'excel_multi_sheet_direct'
|
|
|
+ }
|
|
|
+
|
|
|
+ elif isinstance(self.config_data, dict):
|
|
|
+ # 如果config_data是字典,合并到current_config
|
|
|
+ print(f"合并字典格式的配置数据")
|
|
|
+ current_config.update(self.config_data)
|
|
|
+
|
|
|
+ # 写入更新后的配置
|
|
|
+ print(f"写入配置文件: {self.selected_json_path}")
|
|
|
+ print(f"配置内容预览: {str(current_config)[:200]}...")
|
|
|
+
|
|
|
+ with open(self.selected_json_path, 'w', encoding='utf-8') as f:
|
|
|
+ json.dump(current_config, f, indent=2, ensure_ascii=False)
|
|
|
+
|
|
|
+ print("多工作表配置文件写入成功")
|
|
|
+ messagebox.showinfo("成功", f"多工作表配置导入成功!\n更新了配置数据")
|
|
|
+ self.status_var.set("多工作表配置导入成功")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ print(f"导入多工作表配置失败: {e}")
|
|
|
+ import traceback
|
|
|
+ traceback.print_exc()
|
|
|
+ messagebox.showerror("错误", f"导入多工作表配置失败: {str(e)}")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 刷新预览
|
|
|
+ self.clear_selection()
|
|
|
+
|
|
|
def _detect_config_type(self):
|
|
|
"""根据数据内容和工作表名称自动识别配置类型"""
|
|
|
try:
|
|
|
@@ -2316,11 +2530,19 @@ class ConfigManagerGUI:
|
|
|
# 获取基础属性
|
|
|
health = item.get('health', item.get('生命值', 100))
|
|
|
speed = item.get('speed', item.get('移动速度', 50))
|
|
|
- attack = item.get('attack', item.get('攻击力', 10))
|
|
|
+ attack_damage = item.get('attackDamage', item.get('攻击伤害', 10))
|
|
|
attack_range = item.get('range', item.get('攻击范围', 100))
|
|
|
attack_speed = item.get('attackSpeed', item.get('攻击速度', 1.0))
|
|
|
defense = item.get('defense', item.get('防御力', 0))
|
|
|
|
|
|
+ # 调试信息
|
|
|
+ print(f"转换敌人 {enemy_id} 的属性: health={health}, speed={speed}, attackDamage={attack_damage}, defense={defense}")
|
|
|
+ print(f"原始数据中的字段: {list(item.keys())}")
|
|
|
+ if '生命值' in item:
|
|
|
+ print(f"找到生命值字段,值为: {item['生命值']}")
|
|
|
+ if 'health' in item:
|
|
|
+ print(f"找到health字段,值为: {item['health']}")
|
|
|
+
|
|
|
# 获取格挡相关属性
|
|
|
can_block = item.get('canBlock', item.get('可格挡', False))
|
|
|
block_chance = item.get('blockChance', item.get('格挡概率', 0.0))
|
|
|
@@ -2378,7 +2600,7 @@ class ConfigManagerGUI:
|
|
|
'speedVariation': item.get('speedVariation', item.get('速度变化', 0.1))
|
|
|
},
|
|
|
'combat': {
|
|
|
- 'attackDamage': attack,
|
|
|
+ 'attackDamage': attack_damage,
|
|
|
'attackRange': attack_range,
|
|
|
'attackSpeed': attack_speed,
|
|
|
'canBlock': can_block,
|
|
|
@@ -2487,7 +2709,7 @@ class ConfigManagerGUI:
|
|
|
result['rangedConfig'] = {
|
|
|
'projectileType': item.get('projectileType', item.get('弹药类型', 'bullet')),
|
|
|
'projectileSpeed': item.get('projectileSpeed', item.get('弹药速度', 100)),
|
|
|
- 'projectileDamage': item.get('projectileDamage', item.get('弹药伤害', result['stats']['attack'])),
|
|
|
+ 'projectileDamage': item.get('projectileDamage', item.get('弹药伤害', item.get('attack', item.get('攻击力', 10)))),
|
|
|
'fireRate': item.get('fireRate', item.get('射速', 1.0)),
|
|
|
'accuracy': item.get('accuracy', item.get('精度', 0.9))
|
|
|
}
|
|
|
@@ -2497,13 +2719,13 @@ class ConfigManagerGUI:
|
|
|
special_abilities.extend([
|
|
|
{
|
|
|
'type': 'charge_attack',
|
|
|
- 'damage': item.get('chargeDamage', item.get('冲锋伤害', result['stats']['attack'] * 2)),
|
|
|
+ 'damage': item.get('chargeDamage', item.get('冲锋伤害', item.get('attack', item.get('攻击力', 10)) * 2)),
|
|
|
'range': item.get('chargeRange', item.get('冲锋范围', 150)),
|
|
|
'cooldown': item.get('chargeCooldown', item.get('冲锋冷却', 8.0))
|
|
|
},
|
|
|
{
|
|
|
'type': 'area_attack',
|
|
|
- 'damage': item.get('areaDamage', item.get('范围伤害', result['stats']['attack'] * 1.5)),
|
|
|
+ 'damage': item.get('areaDamage', item.get('范围伤害', item.get('attack', item.get('攻击力', 10)) * 1.5)),
|
|
|
'radius': item.get('areaRadius', item.get('范围半径', 100)),
|
|
|
'cooldown': item.get('areaCooldown', item.get('范围攻击冷却', 12.0))
|
|
|
}
|
|
|
@@ -2917,6 +3139,100 @@ class ConfigManagerGUI:
|
|
|
print(f"转换关卡数据时出错: {e}")
|
|
|
return None
|
|
|
|
|
|
+ def fix_enemies_json(self):
|
|
|
+ """修复enemies.json文件 - 删除attack字段"""
|
|
|
+ try:
|
|
|
+ # 获取enemies.json路径
|
|
|
+ enemies_json_path = Path(self.json_path_var.get()) if self.json_path_var.get() else None
|
|
|
+ if not enemies_json_path or not enemies_json_path.exists():
|
|
|
+ # 尝试默认路径
|
|
|
+ enemies_json_path = Path(os.path.dirname(__file__)).parent / "enemies.json"
|
|
|
+ if not enemies_json_path.exists():
|
|
|
+ messagebox.showerror("错误", "未找到enemies.json文件")
|
|
|
+ return False
|
|
|
+
|
|
|
+ # 备份原文件
|
|
|
+ backup_path = f"{enemies_json_path}.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
|
+ with open(enemies_json_path, 'r', encoding='utf-8') as src:
|
|
|
+ with open(backup_path, 'w', encoding='utf-8') as dst:
|
|
|
+ dst.write(src.read())
|
|
|
+
|
|
|
+ # 读取JSON文件
|
|
|
+ with open(enemies_json_path, 'r', encoding='utf-8') as f:
|
|
|
+ data = json.load(f)
|
|
|
+
|
|
|
+ # 递归删除attack字段
|
|
|
+ def remove_attack_fields(obj):
|
|
|
+ if isinstance(obj, dict):
|
|
|
+ if 'attack' in obj:
|
|
|
+ del obj['attack']
|
|
|
+ for value in obj.values():
|
|
|
+ remove_attack_fields(value)
|
|
|
+ elif isinstance(obj, list):
|
|
|
+ for item in obj:
|
|
|
+ remove_attack_fields(item)
|
|
|
+
|
|
|
+ remove_attack_fields(data)
|
|
|
+
|
|
|
+ # 写回文件
|
|
|
+ with open(enemies_json_path, 'w', encoding='utf-8') as f:
|
|
|
+ json.dump(data, f, ensure_ascii=False, indent=2)
|
|
|
+
|
|
|
+ messagebox.showinfo("成功", f"已修复enemies.json文件\n备份文件: {backup_path}\n已删除所有attack字段")
|
|
|
+ return True
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("错误", f"修复enemies.json失败: {str(e)}")
|
|
|
+ return False
|
|
|
+
|
|
|
+ def auto_import_enemies_from_excel(self):
|
|
|
+ """自动从Excel导入敌人配置"""
|
|
|
+ try:
|
|
|
+ # 查找敌人配置表.xlsx
|
|
|
+ excel_dir = Path(os.path.dirname(__file__))
|
|
|
+ excel_file = excel_dir / "敌人配置表.xlsx"
|
|
|
+
|
|
|
+ if not excel_file.exists():
|
|
|
+ messagebox.showerror("错误", f"未找到敌人配置表.xlsx文件: {excel_file}")
|
|
|
+ return False
|
|
|
+
|
|
|
+ # 读取Excel文件
|
|
|
+ if not PANDAS_AVAILABLE:
|
|
|
+ messagebox.showerror("错误", "pandas未安装,无法读取Excel文件")
|
|
|
+ return False
|
|
|
+
|
|
|
+ all_sheets = pd.read_excel(excel_file, sheet_name=None)
|
|
|
+
|
|
|
+ # 解析敌人配置
|
|
|
+ enemies_config = self.parse_enemy_multi_sheet_data(all_sheets, "敌人配置表.xlsx")
|
|
|
+
|
|
|
+ if not enemies_config:
|
|
|
+ messagebox.showerror("错误", "未能解析到任何敌人配置")
|
|
|
+ return False
|
|
|
+
|
|
|
+ # 获取输出路径
|
|
|
+ enemies_json_path = Path(self.json_path_var.get()) if self.json_path_var.get() else None
|
|
|
+ if not enemies_json_path:
|
|
|
+ enemies_json_path = excel_dir.parent / "enemies.json"
|
|
|
+
|
|
|
+ # 备份现有文件
|
|
|
+ if enemies_json_path.exists():
|
|
|
+ backup_path = f"{enemies_json_path}.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
|
+ with open(enemies_json_path, 'r', encoding='utf-8') as src:
|
|
|
+ with open(backup_path, 'w', encoding='utf-8') as dst:
|
|
|
+ dst.write(src.read())
|
|
|
+
|
|
|
+ # 写入新配置
|
|
|
+ with open(enemies_json_path, 'w', encoding='utf-8') as f:
|
|
|
+ json.dump(enemies_config, f, ensure_ascii=False, indent=2)
|
|
|
+
|
|
|
+ messagebox.showinfo("成功", f"已成功导入{len(enemies_config)}个敌人配置到: {enemies_json_path}")
|
|
|
+ return True
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("错误", f"自动导入敌人配置失败: {str(e)}")
|
|
|
+ return False
|
|
|
+
|
|
|
def restore_default_config(self):
|
|
|
"""恢复默认配置"""
|
|
|
result = messagebox.askyesno("确认", "确定要恢复默认配置吗?\n当前配置将被覆盖!")
|