|
|
@@ -38,7 +38,14 @@ try:
|
|
|
WEAPON_MANAGER_AVAILABLE = True
|
|
|
except ImportError:
|
|
|
WEAPON_MANAGER_AVAILABLE = False
|
|
|
- print("警告: 武器配置管理器未找到,武器配置功能将不可用")
|
|
|
+ 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 未找到,敌人配置功能将不可用")
|
|
|
|
|
|
class SkillConfigImporter:
|
|
|
"""技能配置表导入工具类"""
|
|
|
@@ -457,10 +464,14 @@ class ConfigManagerGUI:
|
|
|
enemy_btn_frame = ttk.Frame(right_frame)
|
|
|
enemy_btn_frame.grid(row=3, 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))
|
|
|
+ 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)
|
|
|
+
|
|
|
+
|
|
|
|
|
|
# 底部状态栏
|
|
|
self.status_var = tk.StringVar()
|
|
|
@@ -1169,246 +1180,7 @@ class ConfigManagerGUI:
|
|
|
|
|
|
return skills_config
|
|
|
|
|
|
- def parse_enemy_multi_sheet_data(self, all_sheets_data, filename):
|
|
|
- """解析敌人配置的多工作表数据"""
|
|
|
- enemies_config = []
|
|
|
-
|
|
|
- try:
|
|
|
- # 获取敌人基础配置数据(支持中英文工作表名)
|
|
|
- enemy_info = None
|
|
|
- for sheet_name in ['敌人基础配置', 'Enemy Config', 'enemies', '敌人配置']:
|
|
|
- if sheet_name in all_sheets_data:
|
|
|
- enemy_info = all_sheets_data[sheet_name]
|
|
|
- break
|
|
|
-
|
|
|
- if enemy_info is None:
|
|
|
- print("错误: 未找到敌人基础配置工作表(支持的工作表名: 敌人基础配置, Enemy Config, enemies, 敌人配置)")
|
|
|
- return enemies_config
|
|
|
-
|
|
|
- # 获取其他配置工作表
|
|
|
- combat_sheet = all_sheets_data.get('战斗配置')
|
|
|
- movement_sheet = all_sheets_data.get('移动配置')
|
|
|
- visual_sheet = all_sheets_data.get('视觉配置')
|
|
|
- audio_sheet = all_sheets_data.get('音频配置')
|
|
|
- special_sheet = all_sheets_data.get('特殊能力配置')
|
|
|
- boss_sheet = all_sheets_data.get('BOSS配置')
|
|
|
-
|
|
|
- def get_field_value(row, cn_field, en_fields, default_value, value_type=str):
|
|
|
- """获取字段值,支持中英文字段名"""
|
|
|
- for field in [cn_field] + (en_fields if isinstance(en_fields, list) else [en_fields]):
|
|
|
- if field in row and pd.notna(row[field]):
|
|
|
- try:
|
|
|
- return value_type(row[field])
|
|
|
- except (ValueError, TypeError):
|
|
|
- continue
|
|
|
- return default_value
|
|
|
-
|
|
|
- def find_row_by_id(sheet, enemy_id):
|
|
|
- """根据敌人ID查找对应行"""
|
|
|
- if sheet is None:
|
|
|
- return None
|
|
|
- for _, row in sheet.iterrows():
|
|
|
- if str(row.get('敌人ID', '')) == enemy_id:
|
|
|
- return row
|
|
|
- return None
|
|
|
-
|
|
|
- # 处理每个敌人(支持中英文字段名)
|
|
|
- for _, enemy_row in enemy_info.iterrows():
|
|
|
- # 获取敌人ID(支持中英文字段名)
|
|
|
- enemy_id_field = None
|
|
|
- for field in ['id', '敌人ID', 'enemy_id', 'ID']:
|
|
|
- if field in enemy_row and pd.notna(enemy_row[field]):
|
|
|
- enemy_id_field = field
|
|
|
- break
|
|
|
-
|
|
|
- if enemy_id_field is None:
|
|
|
- continue
|
|
|
-
|
|
|
- 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),
|
|
|
- '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),
|
|
|
- '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'),
|
|
|
- 'projectileType': get_field_value(combat_row, '投射物类型', ['projectileType'], 'none'),
|
|
|
- 'projectileSpeed': get_field_value(combat_row, '投射物速度', ['projectileSpeed'], 100, float),
|
|
|
- 'canBlock': get_field_value(combat_row, '能否格挡', ['canBlock'], False, bool),
|
|
|
- 'blockChance': get_field_value(combat_row, '格挡几率', ['blockChance'], 0.0, float),
|
|
|
- 'blockDamageReduction': get_field_value(combat_row, '格挡伤害减免', ['blockDamageReduction'], 0.5, float)
|
|
|
- })
|
|
|
-
|
|
|
- # 获取移动配置
|
|
|
- movement_row = find_row_by_id(movement_sheet, enemy_id)
|
|
|
- if movement_row is not None:
|
|
|
- enemy_data.update({
|
|
|
- 'movementPattern': get_field_value(movement_row, '移动模式', ['movementPattern'], 'walk_forward'),
|
|
|
- 'patrolRange': get_field_value(movement_row, '巡逻范围', ['patrolRange'], 100, int),
|
|
|
- 'chaseRange': get_field_value(movement_row, '追击范围', ['chaseRange'], 200, int),
|
|
|
- 'rotationSpeed': get_field_value(movement_row, '旋转速度', ['rotationSpeed'], 180, float),
|
|
|
- 'moveType': get_field_value(movement_row, '移动类型', ['moveType'], 'straight'),
|
|
|
- 'swingAmplitude': get_field_value(movement_row, '摆动幅度', ['swingAmplitude'], 0.0, float),
|
|
|
- 'swingFrequency': get_field_value(movement_row, '摆动频率', ['swingFrequency'], 0.0, float),
|
|
|
- 'speedVariation': get_field_value(movement_row, '速度变化', ['speedVariation'], 0.1, float)
|
|
|
- })
|
|
|
-
|
|
|
- # 获取视觉配置
|
|
|
- visual_row = find_row_by_id(visual_sheet, enemy_id)
|
|
|
- if visual_row is not None:
|
|
|
- enemy_data.update({
|
|
|
- 'spritePath': get_field_value(visual_row, '精灵路径', ['spritePath'], f'enemies/{enemy_id}'),
|
|
|
- 'scale': get_field_value(visual_row, '缩放比例', ['scale'], 1.0, float),
|
|
|
- 'animationSpeed': get_field_value(visual_row, '动画速度', ['animationSpeed'], 1.0, float),
|
|
|
- 'flipX': get_field_value(visual_row, '水平翻转', ['flipX'], False, bool),
|
|
|
- 'idleAnimation': get_field_value(visual_row, '待机动画', ['idleAnimation'], 'idle'),
|
|
|
- 'walkAnimation': get_field_value(visual_row, '行走动画', ['walkAnimation'], 'walk'),
|
|
|
- 'attackAnimation': get_field_value(visual_row, '攻击动画', ['attackAnimation'], 'attack'),
|
|
|
- 'deathAnimation': get_field_value(visual_row, '死亡动画', ['deathAnimation'], 'dead'),
|
|
|
- 'weaponProp': get_field_value(visual_row, '武器道具', ['weaponProp'], '')
|
|
|
- })
|
|
|
-
|
|
|
- # 获取音频配置
|
|
|
- audio_row = find_row_by_id(audio_sheet, enemy_id)
|
|
|
- if audio_row is not None:
|
|
|
- enemy_data.update({
|
|
|
- 'attackSound': get_field_value(audio_row, '攻击音效', ['attackSound'], 'enemy_attack'),
|
|
|
- 'deathSound': get_field_value(audio_row, '死亡音效', ['deathSound'], 'enemy_death'),
|
|
|
- 'hitSound': get_field_value(audio_row, '受击音效', ['hitSound'], 'enemy_hit'),
|
|
|
- 'walkSound': get_field_value(audio_row, '行走音效', ['walkSound'], ''),
|
|
|
- 'blockSound': get_field_value(audio_row, '格挡音效', ['blockSound'], ''),
|
|
|
- 'stealthSound': get_field_value(audio_row, '隐身音效', ['stealthSound'], ''),
|
|
|
- 'armorBreakSound': get_field_value(audio_row, '护甲破碎音效', ['armorBreakSound'], ''),
|
|
|
- 'fuseSound': get_field_value(audio_row, '引信音效', ['fuseSound'], '')
|
|
|
- })
|
|
|
-
|
|
|
- # 获取特殊能力配置
|
|
|
- special_abilities = []
|
|
|
- if special_sheet is not None:
|
|
|
- for _, special_row in special_sheet.iterrows():
|
|
|
- if str(special_row.get('敌人ID', '')) == enemy_id:
|
|
|
- ability_type = get_field_value(special_row, '能力类型', ['abilityType'], '')
|
|
|
- if ability_type: # 只添加有效的特殊能力
|
|
|
- special_abilities.append({
|
|
|
- 'type': ability_type,
|
|
|
- 'damage': get_field_value(special_row, '伤害', ['damage'], 0, int),
|
|
|
- 'range': get_field_value(special_row, '范围', ['range'], 0, float),
|
|
|
- 'cooldown': get_field_value(special_row, '冷却时间', ['cooldown'], 0, float)
|
|
|
- })
|
|
|
- if special_abilities:
|
|
|
- enemy_data['specialAbilities'] = special_abilities
|
|
|
-
|
|
|
- # 获取BOSS配置
|
|
|
- boss_row = find_row_by_id(boss_sheet, enemy_id)
|
|
|
- if boss_row is not None:
|
|
|
- is_boss = get_field_value(boss_row, '是否BOSS', ['isBoss'], False, bool)
|
|
|
- if is_boss:
|
|
|
- enemy_data['bossConfig'] = {
|
|
|
- 'isBoss': True,
|
|
|
- 'phases': get_field_value(boss_row, '阶段数', ['phases'], 1, int),
|
|
|
- 'enrageThreshold': get_field_value(boss_row, '狂暴阈值', ['enrageThreshold'], 0.3, float),
|
|
|
- 'enrageDamageMultiplier': get_field_value(boss_row, '狂暴伤害倍数', ['enrageDamageMultiplier'], 1.5, float),
|
|
|
- 'enrageSpeedMultiplier': get_field_value(boss_row, '狂暴速度倍数', ['enrageSpeedMultiplier'], 1.3, float)
|
|
|
- }
|
|
|
-
|
|
|
- # 转换为嵌套结构
|
|
|
- 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)} 个敌人")
|
|
|
-
|
|
|
- except Exception as e:
|
|
|
- print(f"解析敌人多工作表数据时出错: {e}")
|
|
|
- import traceback
|
|
|
- traceback.print_exc()
|
|
|
-
|
|
|
- return enemies_config
|
|
|
+
|
|
|
|
|
|
def parse_multi_sheet_data(self, all_sheets_data, filename):
|
|
|
"""解析通用多工作表数据"""
|
|
|
@@ -1789,6 +1561,71 @@ class ConfigManagerGUI:
|
|
|
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_vertical_config(self):
|
|
|
"""导入纵向表格配置"""
|
|
|
# 读取现有JSON配置
|
|
|
@@ -2327,241 +2164,7 @@ class ConfigManagerGUI:
|
|
|
print(f"配置类型检测出错: {e}")
|
|
|
return 'unknown'
|
|
|
|
|
|
- def _convert_enemy_data(self, item):
|
|
|
- """转换敌人数据格式 - 支持嵌套结构"""
|
|
|
- try:
|
|
|
- print(f"开始转换敌人数据: {item}")
|
|
|
- # 支持中英文字段名
|
|
|
- enemy_id = item.get('id', item.get('敌人ID', ''))
|
|
|
- enemy_name = item.get('name', item.get('敌人名称', ''))
|
|
|
- enemy_type = item.get('type', item.get('敌人类型', ''))
|
|
|
- print(f"敌人ID: {enemy_id}, 敌人名称: {enemy_name}, 类型: {enemy_type}")
|
|
|
-
|
|
|
- # 检查必要字段
|
|
|
- if not enemy_id:
|
|
|
- print(f"跳过无效敌人数据: 缺少敌人ID - {item}")
|
|
|
- return None
|
|
|
-
|
|
|
- # 获取基础属性
|
|
|
- health = item.get('health', item.get('生命值', 100))
|
|
|
- speed = item.get('speed', item.get('移动速度', 50))
|
|
|
- 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))
|
|
|
- block_damage_reduction = item.get('blockDamageReduction', item.get('格挡伤害减免', 0.5))
|
|
|
-
|
|
|
- # 获取移动相关属性
|
|
|
- movement_pattern = item.get('movementPattern', item.get('移动模式', 'direct'))
|
|
|
- patrol_range = item.get('patrolRange', item.get('巡逻范围', 100))
|
|
|
- chase_range = item.get('chaseRange', item.get('追击范围', 200))
|
|
|
-
|
|
|
- # 获取视觉配置
|
|
|
- # 敌人ID到动画编号的映射
|
|
|
- enemy_to_ani = {
|
|
|
- 'normal_zombie': '001',
|
|
|
- 'roadblock_zombie': '002',
|
|
|
- 'wandering_zombie': '003',
|
|
|
- 'mage_zombie': '004',
|
|
|
- 'archer_zombie': '005',
|
|
|
- 'stealth_zombie': '006',
|
|
|
- 'bucket_zombie': '007',
|
|
|
- 'barrel_zombie': '008',
|
|
|
- 'boss1_gatekeeper': '009',
|
|
|
- 'boss2_gravedigger': '010',
|
|
|
- 'boss3_cyborg': '011'
|
|
|
- }
|
|
|
- ani_num = enemy_to_ani.get(enemy_id, '001')
|
|
|
- default_sprite_path = f'Animation/EnemyAni/{ani_num}'
|
|
|
- sprite_path = item.get('spritePath', item.get('精灵路径', default_sprite_path))
|
|
|
- scale = item.get('scale', item.get('缩放', 1.0))
|
|
|
-
|
|
|
- # 获取音效配置
|
|
|
- attack_sound = item.get('attackSound', item.get('攻击音效', 'enemy_attack'))
|
|
|
- death_sound = item.get('deathSound', item.get('死亡音效', 'enemy_death'))
|
|
|
-
|
|
|
- # 构建嵌套结构的配置
|
|
|
- result = {
|
|
|
- 'id': enemy_id,
|
|
|
- 'name': enemy_name,
|
|
|
- 'type': enemy_type,
|
|
|
- 'stats': {
|
|
|
- 'health': health,
|
|
|
- 'maxHealth': health,
|
|
|
- 'defense': defense,
|
|
|
- 'speed': speed
|
|
|
- },
|
|
|
- 'movement': {
|
|
|
- 'pattern': movement_pattern,
|
|
|
- 'speed': speed,
|
|
|
- 'patrolRange': patrol_range,
|
|
|
- 'chaseRange': chase_range,
|
|
|
- 'rotationSpeed': item.get('rotationSpeed', item.get('旋转速度', 180)),
|
|
|
- 'moveType': item.get('moveType', item.get('移动类型', 'straight')),
|
|
|
- 'swingAmplitude': item.get('swingAmplitude', item.get('摆动幅度', 0.0)),
|
|
|
- 'swingFrequency': item.get('swingFrequency', item.get('摆动频率', 0.0)),
|
|
|
- 'speedVariation': item.get('speedVariation', item.get('速度变化', 0.1))
|
|
|
- },
|
|
|
- 'combat': {
|
|
|
- 'attackDamage': attack_damage,
|
|
|
- 'attackRange': attack_range,
|
|
|
- 'attackSpeed': attack_speed,
|
|
|
- 'canBlock': can_block,
|
|
|
- 'blockChance': block_chance,
|
|
|
- 'blockDamageReduction': block_damage_reduction,
|
|
|
- 'attackCooldown': 1.0 / attack_speed if attack_speed > 0 else 1.0,
|
|
|
- 'attackType': item.get('attackType', item.get('攻击类型', 'melee')),
|
|
|
- 'attackDelay': item.get('attackDelay', item.get('攻击延迟', 0.5)),
|
|
|
- 'weaponType': item.get('weaponType', item.get('武器类型', 'none')),
|
|
|
- 'projectileType': item.get('projectileType', item.get('投射物类型', 'none')),
|
|
|
- 'projectileSpeed': item.get('projectileSpeed', item.get('投射物速度', 100))
|
|
|
- },
|
|
|
- 'visualConfig': {
|
|
|
- 'spritePath': sprite_path,
|
|
|
- 'scale': scale,
|
|
|
- 'animationSpeed': item.get('animationSpeed', item.get('动画速度', 1.0)),
|
|
|
- 'flipX': item.get('flipX', item.get('水平翻转', False)),
|
|
|
- 'tint': item.get('tint', item.get('着色', '#FFFFFF')),
|
|
|
- 'animations': {
|
|
|
- 'idle': item.get('idleAnimation', item.get('待机动画', 'idle')),
|
|
|
- 'walk': item.get('walkAnimation', item.get('行走动画', 'walk')),
|
|
|
- 'attack': item.get('attackAnimation', item.get('攻击动画', 'attack')),
|
|
|
- 'death': item.get('deathAnimation', item.get('死亡动画', 'dead'))
|
|
|
- },
|
|
|
- 'weaponProp': item.get('weaponProp', item.get('武器道具', ''))
|
|
|
- },
|
|
|
- 'audioConfig': {
|
|
|
- 'attackSound': attack_sound,
|
|
|
- 'deathSound': death_sound,
|
|
|
- 'hitSound': item.get('hitSound', item.get('受击音效', 'enemy_hit')),
|
|
|
- 'walkSound': item.get('walkSound', item.get('行走音效', '')),
|
|
|
- 'blockSound': item.get('blockSound', item.get('格挡音效', '')),
|
|
|
- 'stealthSound': item.get('stealthSound', item.get('隐身音效', '')),
|
|
|
- 'armorBreakSound': item.get('armorBreakSound', item.get('护甲破碎音效', '')),
|
|
|
- 'fuseSound': item.get('fuseSound', item.get('引信音效', '')),
|
|
|
- 'volume': item.get('volume', item.get('音量', 1.0))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- # 添加特殊能力配置
|
|
|
- special_abilities = item.get('specialAbilities', [])
|
|
|
- if special_abilities:
|
|
|
- result['specialAbilities'] = special_abilities
|
|
|
-
|
|
|
- # 添加BOSS配置
|
|
|
- boss_config = item.get('bossConfig', {})
|
|
|
- if boss_config and boss_config.get('isBoss', False):
|
|
|
- result['bossConfig'] = boss_config
|
|
|
-
|
|
|
- # 根据敌人类型添加特殊配置
|
|
|
- result = self._add_enemy_special_config(result, enemy_type, item)
|
|
|
-
|
|
|
- print(f"成功转换敌人数据: {enemy_id}")
|
|
|
- return result
|
|
|
- except Exception as e:
|
|
|
- print(f"转换敌人数据失败: {e} - 数据: {item}")
|
|
|
- return None
|
|
|
-
|
|
|
- def _add_enemy_special_config(self, result, enemy_type, item):
|
|
|
- """根据敌人类型添加特殊配置"""
|
|
|
- try:
|
|
|
- # 根据敌人类型添加特殊能力和配置
|
|
|
- special_abilities = []
|
|
|
-
|
|
|
- if enemy_type == 'stealth_zombie':
|
|
|
- # 隐身僵尸特殊配置
|
|
|
- special_abilities.append({
|
|
|
- 'type': 'stealth',
|
|
|
- 'duration': item.get('stealthDuration', item.get('隐身持续时间', 3.0)),
|
|
|
- 'cooldown': item.get('stealthCooldown', item.get('隐身冷却时间', 8.0)),
|
|
|
- 'alpha': item.get('stealthAlpha', item.get('隐身透明度', 0.3))
|
|
|
- })
|
|
|
- result['stealthConfig'] = {
|
|
|
- 'canStealth': True,
|
|
|
- 'stealthDuration': item.get('stealthDuration', item.get('隐身持续时间', 3.0)),
|
|
|
- 'stealthCooldown': item.get('stealthCooldown', item.get('隐身冷却时间', 8.0)),
|
|
|
- 'stealthAlpha': item.get('stealthAlpha', item.get('隐身透明度', 0.3))
|
|
|
- }
|
|
|
-
|
|
|
- elif enemy_type == 'bucket_zombie':
|
|
|
- # 铁桶僵尸特殊配置
|
|
|
- result['armorConfig'] = {
|
|
|
- 'hasArmor': True,
|
|
|
- 'armorHealth': item.get('armorHealth', item.get('护甲血量', 50)),
|
|
|
- 'armorDefense': item.get('armorDefense', item.get('护甲防御', 5)),
|
|
|
- 'armorSprite': item.get('armorSprite', item.get('护甲精灵', 'bucket_armor'))
|
|
|
- }
|
|
|
-
|
|
|
- elif enemy_type == 'explosive_zombie':
|
|
|
- # 火药桶僵尸特殊配置
|
|
|
- special_abilities.append({
|
|
|
- 'type': 'explosion',
|
|
|
- 'damage': item.get('explosionDamage', item.get('爆炸伤害', 30)),
|
|
|
- 'radius': item.get('explosionRadius', item.get('爆炸半径', 80)),
|
|
|
- 'triggerOnDeath': True
|
|
|
- })
|
|
|
- result['explosiveConfig'] = {
|
|
|
- 'explosionDamage': item.get('explosionDamage', item.get('爆炸伤害', 30)),
|
|
|
- 'explosionRadius': item.get('explosionRadius', item.get('爆炸半径', 80)),
|
|
|
- 'fuseTime': item.get('fuseTime', item.get('引爆时间', 2.0)),
|
|
|
- 'chainExplosion': item.get('chainExplosion', item.get('连锁爆炸', False))
|
|
|
- }
|
|
|
-
|
|
|
- elif enemy_type == 'ranged_enemy':
|
|
|
- # 远程敌人特殊配置
|
|
|
- result['rangedConfig'] = {
|
|
|
- 'projectileType': item.get('projectileType', item.get('弹药类型', 'bullet')),
|
|
|
- 'projectileSpeed': item.get('projectileSpeed', item.get('弹药速度', 100)),
|
|
|
- '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))
|
|
|
- }
|
|
|
-
|
|
|
- elif 'boss' in enemy_type.lower():
|
|
|
- # BOSS特殊配置
|
|
|
- special_abilities.extend([
|
|
|
- {
|
|
|
- 'type': 'charge_attack',
|
|
|
- '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('范围伤害', item.get('attack', item.get('攻击力', 10)) * 1.5)),
|
|
|
- 'radius': item.get('areaRadius', item.get('范围半径', 100)),
|
|
|
- 'cooldown': item.get('areaCooldown', item.get('范围攻击冷却', 12.0))
|
|
|
- }
|
|
|
- ])
|
|
|
- result['bossConfig'] = {
|
|
|
- 'isBoss': True,
|
|
|
- 'phases': item.get('phases', item.get('阶段数', 1)),
|
|
|
- 'enrageThreshold': item.get('enrageThreshold', item.get('狂暴阈值', 0.3)),
|
|
|
- 'enrageDamageMultiplier': item.get('enrageDamageMultiplier', item.get('狂暴伤害倍数', 1.5)),
|
|
|
- 'enrageSpeedMultiplier': item.get('enrageSpeedMultiplier', item.get('狂暴速度倍数', 1.3))
|
|
|
- }
|
|
|
-
|
|
|
- # 添加特殊能力到结果中
|
|
|
- if special_abilities:
|
|
|
- result['specialAbilities'] = special_abilities
|
|
|
-
|
|
|
- return result
|
|
|
-
|
|
|
- except Exception as e:
|
|
|
- print(f"添加特殊配置失败: {e}")
|
|
|
- return result
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2672,99 +2275,7 @@ 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):
|
|
|
"""恢复默认配置"""
|