181404010226 пре 5 месеци
родитељ
комит
edd0bf550e
48 измењених фајлова са 1679 додато и 834 уклоњено
  1. 402 216
      assets/Scenes/GameLevel.scene
  2. 1 1
      assets/assets/Prefabs/Block001.prefab
  3. 1 1
      assets/assets/Prefabs/Block002.prefab
  4. 1 1
      assets/assets/Prefabs/Block003.prefab
  5. 4 1
      assets/assets/Prefabs/Block004.prefab
  6. 4 1
      assets/assets/Prefabs/Block005.prefab
  7. BIN
      assets/resources/data/excel/关卡配置/关卡配置表.xlsx
  8. 37 21
      assets/resources/data/excel/启动配置工具.bat
  9. BIN
      assets/resources/data/excel/敌人配置表.backup.xlsx
  10. 0 12
      assets/resources/data/excel/敌人配置表.backup.xlsx.meta
  11. 0 157
      assets/resources/data/excel/敌人配置表说明.md
  12. 0 11
      assets/resources/data/excel/敌人配置表说明.md.meta
  13. BIN
      assets/resources/data/excel/方块武器配置/方块武器配置表.xlsx
  14. 0 125
      assets/resources/data/excel/更新说明.md
  15. 0 11
      assets/resources/data/excel/更新说明.md.meta
  16. 0 21
      assets/resources/data/excel/生成技能配置表.bat
  17. 0 12
      assets/resources/data/excel/生成技能配置表.bat.meta
  18. 7 7
      assets/resources/data/levels/Level1.json
  19. 2 2
      assets/resources/data/levels/Level2.json
  20. 7 0
      assets/resources/data/levels/Level4.json
  21. 51 18
      assets/scripts/Animations/BallAni.ts
  22. 15 4
      assets/scripts/Animations/HPBarAnimation.ts
  23. 16 0
      assets/scripts/Animations/MoneyAni.ts
  24. 36 24
      assets/scripts/CombatSystem/BallController.ts
  25. 29 10
      assets/scripts/CombatSystem/BlockManager.ts
  26. 48 13
      assets/scripts/CombatSystem/BlockSelection/GameBlockSelection.ts
  27. 1 8
      assets/scripts/CombatSystem/BulletEffects/BulletCount.ts
  28. 1 6
      assets/scripts/CombatSystem/BulletEffects/BulletHitEffect.ts
  29. 2 12
      assets/scripts/CombatSystem/BulletEffects/BulletLifecycle.ts
  30. 2 13
      assets/scripts/CombatSystem/BulletEffects/BulletTrajectory.ts
  31. 15 0
      assets/scripts/CombatSystem/EnemyController.ts
  32. 54 7
      assets/scripts/CombatSystem/EnemyInstance.ts
  33. 412 0
      assets/scripts/CombatSystem/GameEnd.ts
  34. 9 0
      assets/scripts/CombatSystem/GameEnd.ts.meta
  35. 1 1
      assets/scripts/CombatSystem/SkillSelection/SkillManager.ts
  36. 3 3
      assets/scripts/CombatSystem/SkillSelection/SkillSelectionController.ts
  37. 36 36
      assets/scripts/CombatSystem/WeaponBullet.ts
  38. 70 30
      assets/scripts/Core/ConfigManager.ts
  39. 1 2
      assets/scripts/FourUI/MainSystem/MainUIControlller.ts
  40. 86 2
      assets/scripts/FourUI/UpgradeSystem/UpgradeAni.ts
  41. 37 2
      assets/scripts/FourUI/UpgradeSystem/UpgradeController.ts
  42. 13 32
      assets/scripts/LevelSystem/GameManager.ts
  43. 1 0
      assets/scripts/LevelSystem/IN_game.ts
  44. 26 7
      assets/scripts/LevelSystem/SaveDataManager.ts
  45. 17 2
      assets/scripts/LevelSystem/StartGame.ts
  46. 6 2
      assets/scripts/LevelSystem/UIStateManager.ts
  47. 82 0
      test_enemy_health_fix.js
  48. 143 0
      僵尸血量显示异常修复说明.md

Разлика између датотеке није приказан због своје велике величине
+ 402 - 216
assets/Scenes/GameLevel.scene


+ 1 - 1
assets/assets/Prefabs/Block001.prefab

@@ -429,7 +429,7 @@
       "__id__": 19
     },
     "_customMaterial": {
-      "__uuid__": "76a76f1d-d1a0-4d66-b032-e1b0a442bf0c",
+      "__uuid__": "fda095cb-831d-4601-ad94-846013963de8",
       "__expectedType__": "cc.Material"
     },
     "_srcBlendFactor": 2,

+ 1 - 1
assets/assets/Prefabs/Block002.prefab

@@ -429,7 +429,7 @@
       "__id__": 19
     },
     "_customMaterial": {
-      "__uuid__": "76a76f1d-d1a0-4d66-b032-e1b0a442bf0c",
+      "__uuid__": "fda095cb-831d-4601-ad94-846013963de8",
       "__expectedType__": "cc.Material"
     },
     "_srcBlendFactor": 2,

+ 1 - 1
assets/assets/Prefabs/Block003.prefab

@@ -520,7 +520,7 @@
       "__id__": 23
     },
     "_customMaterial": {
-      "__uuid__": "76a76f1d-d1a0-4d66-b032-e1b0a442bf0c",
+      "__uuid__": "fda095cb-831d-4601-ad94-846013963de8",
       "__expectedType__": "cc.Material"
     },
     "_srcBlendFactor": 2,

+ 4 - 1
assets/assets/Prefabs/Block004.prefab

@@ -610,7 +610,10 @@
     "__prefab": {
       "__id__": 27
     },
-    "_customMaterial": null,
+    "_customMaterial": {
+      "__uuid__": "fda095cb-831d-4601-ad94-846013963de8",
+      "__expectedType__": "cc.Material"
+    },
     "_srcBlendFactor": 2,
     "_dstBlendFactor": 4,
     "_color": {

+ 4 - 1
assets/assets/Prefabs/Block005.prefab

@@ -610,7 +610,10 @@
     "__prefab": {
       "__id__": 27
     },
-    "_customMaterial": null,
+    "_customMaterial": {
+      "__uuid__": "fda095cb-831d-4601-ad94-846013963de8",
+      "__expectedType__": "cc.Material"
+    },
     "_srcBlendFactor": 2,
     "_dstBlendFactor": 4,
     "_color": {

BIN
assets/resources/data/excel/关卡配置/关卡配置表.xlsx


+ 37 - 21
assets/resources/data/excel/启动配置工具.bat

@@ -2,30 +2,46 @@
 chcp 65001 >nul
 cd /d "%~dp0"
 
+REM Activate virtual environment
+if exist "..\..\..\..\.\.venv\Scripts\activate.bat" (
+    call "..\..\..\..\.\.venv\Scripts\activate.bat"
+    echo Virtual environment activated: .venv
+) else if exist "..\..\..\..\.\.venv\Scripts\activate" (
+    call "..\..\..\..\.\.venv\Scripts\activate"
+    echo Virtual environment activated: .venv
+) else if exist "..\..\..\..\.\.venv\pyvenv.cfg" (
+    set "VIRTUAL_ENV=%~dp0..\..\..\..\.\.venv"
+    set "PATH=%VIRTUAL_ENV%\Scripts;%PATH%"
+    echo Virtual environment path set: .venv
+) else (
+    echo Virtual environment not found, using system Python
+)
+echo.
+
 :start
 echo ========================================
-echo 游戏配置管理工具 - 多配置表支持
+echo Game Configuration Management Tool - Multi-table Support
 echo ========================================
 echo.
-echo 请选择要执行的操作:
-echo 1. 启动GUI配置管理工具 (Excel导入到JSON)
-echo 2. 将JSON配置转换为Excel表格
-echo 3. 验证Excel文件内容
-echo 4. 退出
+echo Please select an operation:
+echo 1. Launch GUI Configuration Management Tool (Excel to JSON)
+echo 2. Convert JSON configuration to Excel tables
+echo 3. Verify Excel file content
+echo 4. Exit
 echo.
-set /p choice=请输入选项 (1-4): 
+set /p choice=Please enter option (1-4): 
 
 if "%choice%"=="1" goto gui_tool
 if "%choice%"=="2" goto json_to_excel
 if "%choice%"=="3" goto verify_excel
 if "%choice%"=="4" goto exit
-echo 无效选项,请重新选择
+echo Invalid option, please choose again
 pause
 goto start
 
 :gui_tool
 echo.
-echo 启动GUI配置管理工具...
+echo Starting GUI Configuration Management Tool...
 echo.
 python config_manager.py
 if errorlevel 1 (
@@ -34,11 +50,11 @@ if errorlevel 1 (
         python3 config_manager.py
         if errorlevel 1 (
             echo.
-            echo 无法启动Python环境
-            echo 请确保Python已安装并添加到PATH
-            echo 或手动运行: py config_manager.py
+            echo Unable to start Python environment
+            echo Please ensure Python is installed and added to PATH
+            echo Or run manually: py config_manager.py
             echo.
-            echo 如果pandas库未安装,请运行:
+            echo If pandas library is not installed, please run:
             echo pip install pandas openpyxl
             echo.
             pause
@@ -49,7 +65,7 @@ goto end
 
 :json_to_excel
 echo.
-echo 将JSON配置转换为Excel表格...
+echo Converting JSON configuration to Excel tables...
 echo.
 python json_to_excel.py
 if errorlevel 1 (
@@ -58,20 +74,20 @@ if errorlevel 1 (
         python3 json_to_excel.py
         if errorlevel 1 (
             echo.
-            echo 转换失败,请检查Python环境和依赖库
+            echo Conversion failed, please check Python environment and dependencies
             echo.
             pause
         )
     )
 )
 echo.
-echo 转换完成!按任意键继续...
+echo Conversion completed! Press any key to continue...
 pause
 goto start
 
 :verify_excel
 echo.
-echo 验证Excel文件内容...
+echo Verifying Excel file content...
 echo.
 python verify_excel.py
 if errorlevel 1 (
@@ -80,22 +96,22 @@ if errorlevel 1 (
         python3 verify_excel.py
         if errorlevel 1 (
             echo.
-            echo 验证失败,请检查Python环境和依赖库
+            echo Verification failed, please check Python environment and dependencies
             echo.
             pause
         )
     )
 )
 echo.
-echo 验证完成!按任意键继续...
+echo Verification completed! Press any key to continue...
 pause
 goto start
 
 :exit
-echo 退出程序...
+echo Exiting program...
 exit /b 0
 
 :end
 echo.
-echo 操作完成!
+echo Operation completed!
 pause

BIN
assets/resources/data/excel/敌人配置表.backup.xlsx


+ 0 - 12
assets/resources/data/excel/敌人配置表.backup.xlsx.meta

@@ -1,12 +0,0 @@
-{
-  "ver": "1.0.0",
-  "importer": "*",
-  "imported": true,
-  "uuid": "c4641c3b-6c76-418f-9cd2-db64ecb9d487",
-  "files": [
-    ".json",
-    ".xlsx"
-  ],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 157
assets/resources/data/excel/敌人配置表说明.md

@@ -1,157 +0,0 @@
-# 敌人配置表说明文档
-
-## 概述
-
-本文档说明了基于 `enemies.json` 文件生成的敌人配置Excel表格的结构和内容。该表格包含了游戏中所有敌人的详细配置信息,分为9个工作表进行组织。
-
-## 文件信息
-
-**文件位置**: `d:/CocosGame/Pong/assets/resources/data/excel/敌人配置表.xlsx`
-
-**数据源**: `d:/CocosGame/Pong/assets/resources/data/enemies.json`
-
-**动画资源**: `d:/CocosGame/Pong/assets/resources/Animation/EnemyAni/`
-
-## 工作表详细说明
-
-### 1. 敌人基础配置
-
-包含所有敌人的基础属性信息:
-
-- **敌人ID**: 唯一标识符
-- **敌人名称**: 显示名称
-- **敌人类型**: 敌人分类(basic, armored, wanderer, ranged_caster, etc.)
-- **稀有度**: 稀有程度(common, uncommon, rare, boss)
-- **权重**: 生成权重值
-- **生命值**: 生命点数
-- **移动速度**: 移动速度
-- **攻击力**: 攻击伤害
-- **攻击范围**: 攻击距离
-- **攻击速度**: 攻击频率
-- **防御力**: 防御数值
-- **金币奖励**: 击杀奖励
-
-### 2. 战斗配置
-
-包含敌人的战斗相关配置:
-
-- **敌人ID/名称**: 对应的敌人信息
-- **攻击类型**: 攻击方式(melee, magic_projectile, arrow_projectile, etc.)
-- **攻击延迟**: 攻击前摇时间
-- **攻击冷却**: 攻击间隔时间
-- **可格挡**: 是否可以格挡攻击
-- **格挡几率**: 格挡成功率
-- **武器类型**: 武器种类(可选)
-- **投射物类型**: 投射物类型(可选)
-- **投射物速度**: 投射物飞行速度(可选)
-
-### 3. 移动配置
-
-包含敌人的移动行为配置:
-
-- **敌人ID/名称**: 对应的敌人信息
-- **移动类型**: 移动方式(straight, sway)
-- **移动模式**: 移动轨迹(walk_forward, zigzag_forward)
-- **速度变化**: 速度随机变化范围
-- **摆动幅度**: 左右摆动范围(可选)
-- **摆动频率**: 摆动周期频率(可选)
-
-### 4. 视觉配置
-
-包含敌人的视觉表现配置:
-
-- **敌人ID/名称**: 对应的敌人信息
-- **精灵预制体**: Spine动画资源路径(如:@EnemyAni/001/001)
-- **缩放比例**: 显示缩放倍数
-- **水平翻转**: 是否水平镜像
-- **待机动画**: 待机状态动画名称
-- **行走动画**: 移动状态动画名称
-- **攻击动画**: 攻击状态动画名称
-- **死亡动画**: 死亡状态动画名称
-- **武器道具**: 武器道具路径(可选)
-- **护甲道具**: 护甲道具路径(可选)
-- **特殊道具**: 特殊道具路径(可选)
-
-### 5. 音频配置
-
-包含敌人的音效配置:
-
-- **敌人ID/名称**: 对应的敌人信息
-- **行走音效**: 移动时播放的音效
-- **攻击音效**: 攻击时播放的音效
-- **死亡音效**: 死亡时播放的音效
-- **受伤音效**: 受伤时播放的音效(可选)
-- **格挡音效**: 格挡时播放的音效(可选)
-- **隐身音效**: 隐身时播放的音效(可选)
-- **护甲破碎音效**: 护甲破碎时播放的音效(可选)
-- **引信音效**: 引信燃烧时播放的音效(可选)
-- **召唤音效**: 召唤时播放的音效(可选)
-- **激光音效**: 激光发射时播放的音效(可选)
-
-### 6. 特殊能力配置
-
-包含敌人的特殊技能配置:
-
-- **敌人ID/名称**: 对应的敌人信息
-- **特殊能力**: 特殊技能名称(如:invisibility, armor_break, ranged_attack等)
-
-### 7. BOSS配置
-
-包含BOSS敌人的特殊配置:
-
-- **敌人ID/名称**: 对应的BOSS信息
-- **阶段数**: 战斗阶段总数
-- **阶段血量阈值**: 进入下一阶段的血量比例
-- **狂暴速度加成**: 狂暴状态下的速度倍数(可选)
-- **狂暴伤害加成**: 狂暴状态下的伤害倍数(可选)
-- **狂暴攻速加成**: 狂暴状态下的攻击速度倍数(可选)
-- **召唤单位类型**: 召唤的单位类型(可选)
-- **召唤数量**: 单次召唤的数量(可选)
-- **召唤冷却**: 召唤技能的冷却时间(可选)
-- **激光伤害**: 激光攻击的伤害值(可选)
-- **激光射程**: 激光攻击的射程(可选)
-- **激光充能时间**: 激光充能准备时间(可选)
-- **激光冷却时间**: 激光技能冷却时间(可选)
-
-### 8. 生成权重配置
-
-包含不同稀有度敌人的生成权重:
-
-- **稀有度**: 稀有度等级(common, uncommon, rare, boss)
-- **生成权重**: 随机生成时的权重值
-
-### 9. 波次进度配置
-
-包含不同游戏阶段的敌人配置:
-
-- **波次类型**: 游戏阶段类型(earlyWaves, midWaves, lateWaves, bossWaves)
-- **敌人ID**: 该阶段出现的敌人ID
-
-## 动画资源说明
-
-敌人的视觉表现通过Spine动画系统实现:
-
-- **资源路径**: `@EnemyAni/001/001` 格式指向 `resources/Animation/EnemyAni/001/` 目录
-- **文件结构**: 每个敌人动画包含 `.json`、`.atlas`、`.png` 文件
-- **动画状态**: idle(待机)、walk(行走)、attack(攻击)、dead(死亡)等
-
-## 使用建议
-
-1. **策划调整**: 可直接在Excel中修改数值,然后导回JSON格式
-2. **平衡性测试**: 通过调整生命值、攻击力、速度等属性进行游戏平衡
-3. **新敌人添加**: 参考现有敌人的配置结构添加新的敌人类型
-4. **动画资源**: 确保 `spritePrefab` 路径对应的动画资源文件存在
-
-## 技术说明
-
-- **生成工具**: 使用Python脚本 `create_enemies_excel.py` 自动生成
-- **依赖库**: pandas、openpyxl
-- **中文注释**: 每个工作表的第二行包含字段的中文说明
-- **数据完整性**: 保持与源JSON文件的数据一致性
-
-## 更新流程
-
-1. 修改 `enemies.json` 文件
-2. 运行 `python create_enemies_excel.py` 重新生成Excel表格
-3. 检查生成的表格内容是否正确
-4. 根据需要调整脚本或JSON配置

+ 0 - 11
assets/resources/data/excel/敌人配置表说明.md.meta

@@ -1,11 +0,0 @@
-{
-  "ver": "1.0.1",
-  "importer": "text",
-  "imported": true,
-  "uuid": "4f321a53-0b8f-4022-83bf-8f9339729457",
-  "files": [
-    ".json"
-  ],
-  "subMetas": {},
-  "userData": {}
-}

BIN
assets/resources/data/excel/方块武器配置/方块武器配置表.xlsx


+ 0 - 125
assets/resources/data/excel/更新说明.md

@@ -1,125 +0,0 @@
-# Excel配置表更新说明
-
-## 概述
-
-根据最新的JSON配置文件,已成功更新了以下Excel配置表:
-
-1. **方块武器配置表_更新.xlsx** - 基于 `weapons.json` 的最新武器配置
-2. **关卡配置表_完整版_更新.xlsx** - 基于 `levels/` 目录下所有关卡JSON文件的配置
-
-## 更新内容详情
-
-### 1. 方块武器配置表更新
-
-**文件位置**: `d:/CocosGame/Pong/assets/resources/data/excel/方块武器配置/方块武器配置表_更新_v2.xlsx`
-
-**包含工作表**:
-
-#### 武器基础配置
-- **ID**: 武器唯一标识符
-- **名称**: 武器显示名称
-- **类型**: 武器类型(single_shot, piercing, explosive等)
-- **稀有度**: 武器稀有度(common, uncommon, rare, epic)
-- **权重**: 武器在随机生成中的权重
-- **伤害**: 基础伤害值
-- **射速**: 射击频率
-- **射程**: 武器射程
-- **子弹速度**: 子弹飞行速度
-
-
-#### 子弹配置
-- **武器ID/名称**: 对应的武器信息
-- **子弹数量类型**: single, spread, burst
-- **子弹数量**: 每次射击的子弹数量
-- **散射角度**: 多发子弹的散射角度
-- **连发数量**: 连发模式下的子弹数量
-- **连发延迟**: 连发间隔时间
-- **轨迹类型**: straight, arc, homing等
-- **轨迹速度**: 子弹轨迹速度
-- **重力**: 重力影响
-- **生命周期类型**: 子弹消失条件
-- **最大生存时间**: 子弹最大存在时间
-- **穿透次数**: 可穿透敌人数量
-- **反弹次数**: 可反弹次数
-- **是否返回原点**: 回旋镖类型武器
-
-#### 方块形状配置
-- **ID**: 形状标识符
-- **名称**: 形状名称
-- **形状矩阵**: 4x4矩阵表示的方块形状
-
-#### 稀有度权重
-- **稀有度**: 稀有度等级
-- **权重**: 对应的权重值
-
-### 2. 关卡配置表更新
-
-**文件位置**: `d:/CocosGame/Pong/assets/resources/data/excel/关卡配置/关卡配置表_完整版_更新_v2.xlsx`
-
-**包含工作表**:
-
-#### 关卡基础配置
-- **关卡ID**: 关卡标识符(Level1, Level2等)
-- **关卡名称**: 关卡显示名称
-- **场景**: 关卡场景类型
-- **描述**: 关卡描述信息
-- **可用武器**: 该关卡可使用的武器列表
-- **初始生命**: 玩家初始生命值
-- **时间限制**: 关卡时间限制(秒)
-- **难度**: 关卡难度等级
-
-#### 波次配置
-- **关卡ID**: 所属关卡
-- **波次ID**: 波次编号
-- **敌人种类数**: 该波次包含的敌人种类数量
-
-#### 敌人配置
-- **关卡ID**: 所属关卡
-- **波次ID**: 所属波次
-- **敌人类型**: 敌人类型名称
-- **数量**: 该类型敌人的数量
-- **生成间隔**: 敌人生成间隔时间
-- **生成延迟**: 该类型敌人开始生成的延迟时间
-- **特性**: 敌人特殊能力和特征
-
-## 主要更新特性
-
-### 关卡配置的重要改进
-
-1. **多敌人类型支持**: 每个波次现在可以配置多种不同类型的敌人,而不是之前的单一敌人类型
-
-2. **独立生成控制**: 每种敌人类型都有独立的:
-   - 数量控制
-   - 生成间隔
-   - 生成延迟
-   - 特性配置
-
-3. **策略深度增强**: 通过多敌人类型的组合,提供更丰富的游戏策略和挑战
-
-4. **武器配置优化**: 删除了无用的精度(accuracy)字段,简化配置结构
-
-### 武器配置的完整性
-
-1. **详细的子弹配置**: 包含完整的子弹行为参数
-2. **视觉效果配置**: 包含特效和音效配置
-3. **方块形状系统**: 完整的俄罗斯方块形状配置
-4. **稀有度系统**: 平衡的稀有度权重分配
-
-## 使用建议
-
-1. **备份原文件**: 在使用新配置前,建议备份原有的Excel文件
-2. **逐步测试**: 建议先在测试环境中验证新配置的正确性
-3. **平衡性调整**: 根据实际游戏体验,可能需要调整数值平衡
-4. **版本控制**: 建议将配置文件纳入版本控制系统
-
-## 技术说明
-
-- 更新脚本: `update_excel_from_json.py`
-- 数据源: `weapons.json` 和 `levels/` 目录下的JSON文件
-- 生成工具: Python pandas + openpyxl
-- 编码格式: UTF-8
-
-## 后续维护
-
-当JSON配置文件发生变化时,可以重新运行 `update_excel_from_json.py` 脚本来更新Excel文件,确保配置的一致性。
-

+ 0 - 11
assets/resources/data/excel/更新说明.md.meta

@@ -1,11 +0,0 @@
-{
-  "ver": "1.0.1",
-  "importer": "text",
-  "imported": true,
-  "uuid": "251ca7b8-e7b7-4ec9-911e-5e4af8593ee0",
-  "files": [
-    ".json"
-  ],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 21
assets/resources/data/excel/生成技能配置表.bat

@@ -1,21 +0,0 @@
-@echo off
-chcp 65001 >nul
-cd /d "%~dp0"
-echo =======================================
-echo      技能配置Excel生成工具
-echo =======================================
-echo.
-echo 正在生成技能配置Excel表格...
-echo.
-python generate_skill_excel.py
-echo.
-echo 生成完成!
-echo.
-echo 生成的文件:局外技能配置表_新生成.xlsx
-echo.
-echo 使用说明:
-echo 1. 打开生成的Excel文件编辑配置
-echo 2. 运行 skill_config_importer.py 导入修改后的配置
-echo 3. 或者运行 导入技能配置.bat 批处理文件
-echo.
-pause

+ 0 - 12
assets/resources/data/excel/生成技能配置表.bat.meta

@@ -1,12 +0,0 @@
-{
-  "ver": "1.0.0",
-  "importer": "*",
-  "imported": true,
-  "uuid": "9eee48f6-3f46-4e73-bc6f-da3eac1bea9c",
-  "files": [
-    ".bat",
-    ".json"
-  ],
-  "subMetas": {},
-  "userData": {}
-}

+ 7 - 7
assets/resources/data/levels/Level1.json

@@ -7,7 +7,7 @@
     "毛豆射手",
     "尖胡萝卜"
   ],
-  "coinReward": 100,
+  "coinReward": 50,
   "diamondReward": 10,
   "timeLimit": 300,
   "difficulty": "normal",
@@ -18,7 +18,7 @@
       "enemies": [
         {
           "enemyType": "normal_zombie",
-          "count": 8,
+          "count": 2,
           "spawnInterval": 3.0,
           "spawnDelay": 0.0,
           "characteristics": "中速移动, 无技能"
@@ -30,8 +30,8 @@
       "enemies": [
         {
           "enemyType": "normal_zombie",
-          "count": 10,
-          "spawnInterval": 2.5,
+          "count": 2,
+          "spawnInterval": 2.0,
           "spawnDelay": 0.0,
           "characteristics": "中速移动, 无技能"
         }
@@ -42,15 +42,15 @@
       "enemies": [
         {
           "enemyType": "normal_zombie",
-          "count": 6,
+          "count": 2,
           "spawnInterval": 2.0,
           "spawnDelay": 0.0,
           "characteristics": "中速移动, 无技能"
         },
         {
           "enemyType": "roadblock_zombie",
-          "count": 3,
-          "spawnInterval": 8.0,
+          "count": 2,
+          "spawnInterval": 1.0,
           "spawnDelay": 12.0,
           "characteristics": "高生命, 慢速移动"
         }

+ 2 - 2
assets/resources/data/levels/Level2.json

@@ -19,8 +19,8 @@
       "enemies": [
         {
           "enemyType": "normal_zombie",
-          "count": 10,
-          "spawnInterval": 2.8,
+          "count": 2,
+          "spawnInterval": 2.0,
           "spawnDelay": 0.0,
           "characteristics": "中速移动, 无技能"
         }

+ 7 - 0
assets/resources/data/levels/Level4.json

@@ -17,6 +17,13 @@
     {
       "waveId": 1,
       "enemies": [
+        {
+          "enemyType": "normal_zombie",
+          "count": 15,
+          "spawnInterval": 2.0,
+          "spawnDelay": 0.0,
+          "characteristics": "中速移动, 无技能"
+        },
         {
           "enemyType": "bucket_zombie",
           "count": 3,

+ 51 - 18
assets/scripts/Animations/BallAni.ts

@@ -13,6 +13,10 @@ export class BallAni extends Component {
     private static impactEffectSkeleton: sp.SkeletonData = null;
     private static isLoading: boolean = false;
     
+    // 白色描边材质缓存
+    private static whiteMaterial: Material = null;
+    private static isMaterialLoading: boolean = false;
+    
     // 当前播放中的特效节点列表
     private activeEffects: Node[] = [];
     
@@ -22,6 +26,8 @@ export class BallAni extends Component {
     start() {
         // 预加载撞击特效资源
         this.preloadImpactEffect();
+        // 预加载白色描边材质
+        this.preloadWhiteMaterial();
     }
     
     /**
@@ -103,7 +109,8 @@ export class BallAni extends Component {
         const sprite = blockNode.getComponent(Sprite);
         
         // 保存原始材质和Sprite缩放
-        const originalMaterial = sprite ? sprite.material : null;
+        // 优先保存customMaterial,如果没有则保存当前material
+        const originalMaterial = sprite ? (sprite.customMaterial || sprite.material) : null;
         const originalSpriteScale = sprite ? new Vec3(1, 1, 1) : new Vec3(1, 1, 1);
         
         
@@ -111,27 +118,18 @@ export class BallAni extends Component {
             // 有敌人时播放Sprite缩放动画(不影响碰撞体积)
             const shrinkSpriteScale = new Vec3(0.7, 0.7, 1);
             
-            // 应用材质
+            // 应用白色描边材质
             console.log('[BallAni] 检查材质状态:', {
                 hasSprite: !!sprite,
-                hasCustomMaterial: sprite ? !!sprite.customMaterial : false,
+                hasWhiteMaterial: !!BallAni.whiteMaterial,
                 currentMaterial: sprite ? sprite.material : null
             });
             
-            if (sprite && sprite.customMaterial) {
-                sprite.material = sprite.customMaterial;
-                console.log('[BallAni] 应用自定义材质成功');
+            if (sprite && BallAni.whiteMaterial) {
+                sprite.material = BallAni.whiteMaterial;
+                console.log('[BallAni] 应用白色描边材质成功');
             } else if (sprite) {
-                // 动态加载灰色材质
-                const grayMaterialPath = 'shaders/ui-sprite-white-material.mtl';
-                resources.load(grayMaterialPath, Material, (err, material) => {
-                    if (!err && material && sprite && sprite.isValid) {
-                        sprite.material = material;
-                        console.log('[BallAni] 动态加载并应用灰色材质成功');
-                    } else {
-                        console.log('[BallAni] 无法加载灰色材质,不使用任何材质');
-                    }
-                });
+                console.log('[BallAni] 白色描边材质未加载,跳过材质应用');
             }
             
             // 对Sprite节点进行缩放动画(不影响方块碰撞体积)
@@ -147,10 +145,24 @@ export class BallAni extends Component {
                     });
                     
                     if (sprite && originalMaterial) {
-                        sprite.material = originalMaterial;
+                        // 如果原始材质是customMaterial,则恢复customMaterial
+                        if (sprite.customMaterial === originalMaterial) {
+                            sprite.material = sprite.customMaterial;
+                        } else {
+                            sprite.material = originalMaterial;
+                        }
                         console.log('[BallAni] 恢复原始材质成功');
+                    } else if (sprite) {
+                        // 如果没有保存的原始材质,尝试恢复到customMaterial
+                        if (sprite.customMaterial) {
+                            sprite.material = sprite.customMaterial;
+                            console.log('[BallAni] 恢复到customMaterial');
+                        } else {
+                            sprite.material = null;
+                            console.log('[BallAni] 恢复到默认材质');
+                        }
                     } else {
-                        console.log('[BallAni] 无法恢复原始材质 - 原始材质丢失');
+                        console.log('[BallAni] 无法恢复原始材质 - Sprite组件丢失');
                     }
                     console.log('[BallAni] 方块动画完成,恢复原状');
                     // 动画完成,从活动列表中移除
@@ -197,6 +209,27 @@ export class BallAni extends Component {
         });
     }
     
+    /**
+     * 预加载白色描边材质
+     */
+    private preloadWhiteMaterial() {
+        if (BallAni.whiteMaterial || BallAni.isMaterialLoading) {
+            return;
+        }
+        
+        BallAni.isMaterialLoading = true;
+        const materialPath = 'shaders/ui-sprite-white-material';
+        resources.load(materialPath, Material, (err, material: Material) => {
+            BallAni.isMaterialLoading = false;
+            if (err || !material) {
+                console.warn('[BallAni] 加载白色描边材质失败:', err);
+                return;
+            }
+            BallAni.whiteMaterial = material;
+            console.log('[BallAni] 白色描边材质加载成功');
+        });
+    }
+    
     /**
      * 在指定位置播放撞击特效
      * @param worldPosition 世界坐标位置

+ 15 - 4
assets/scripts/Animations/HPBarAnimation.ts

@@ -25,6 +25,7 @@ export class HPBarAnimation extends Component {
     private yellowProgressBar: ProgressBar = null;
     private currentProgress: number = 1.0;
     private targetProgress: number = 1.0;
+    private currentTween: any = null; // 当前正在运行的动画
     
     start() {
         this.initializeComponents();
@@ -40,12 +41,10 @@ export class HPBarAnimation extends Component {
      
         // 获取红色血条组件
         if (this.redBarNode) {
-            console.log(`[HPBarAnimation] [${enemyName}] 红色血条节点已设置:`, this.redBarNode.name);
             this.redProgressBar = this.redBarNode.getComponent(ProgressBar);
             if (this.redProgressBar) {
                 this.currentProgress = this.redProgressBar.progress;
                 this.targetProgress = this.currentProgress;
-                console.log(`[HPBarAnimation] [${enemyName}] 红色血条组件初始化成功,当前进度:`, this.currentProgress);
             } else {
                 console.error(`[HPBarAnimation] [${enemyName}] 红色血条节点上未找到ProgressBar组件!`);
             }
@@ -55,7 +54,6 @@ export class HPBarAnimation extends Component {
         
         // 获取黄色血条组件
         if (this.yellowBarNode) {
-            console.log(`[HPBarAnimation] [${enemyName}] 黄色血条节点已设置:`, this.yellowBarNode.name);
             this.yellowProgressBar = this.yellowBarNode.getComponent(ProgressBar);
             if (this.yellowProgressBar) {
                 // 黄色血条初始进度与红色血条同步
@@ -141,6 +139,12 @@ export class HPBarAnimation extends Component {
     private playDamageAnimation(newProgress: number) {
         console.log(`[HPBarAnimation] 开始播放伤害动画:${this.currentProgress} -> ${newProgress}`);
         
+        // 停止当前正在运行的动画,避免动画冲突
+        if (this.currentTween) {
+            this.currentTween.stop();
+            this.currentTween = null;
+        }
+        
         this.targetProgress = newProgress;
         
         // 保存原始血量作为黄色血条起始位置
@@ -160,7 +164,7 @@ export class HPBarAnimation extends Component {
         this.updateBarDisplay();
         
         // 创建滑动动画,先暂停0.4秒再滑动
-        tween({ progress: originalProgress })
+        this.currentTween = tween({ progress: originalProgress })
             .delay(0.4) // 暂停0.4秒
             .to(0.6, { progress: newProgress }, {
                 onUpdate: (target) => {
@@ -176,6 +180,8 @@ export class HPBarAnimation extends Component {
                         this.yellowProgressBar.progress = newProgress;
                         this.updateBarDisplay();
                     }
+                    // 清除动画引用
+                    this.currentTween = null;
                 }
             })
             .start();
@@ -196,5 +202,10 @@ export class HPBarAnimation extends Component {
     }
     
     onDestroy() {
+        // 清理正在运行的动画,防止内存泄漏
+        if (this.currentTween) {
+            this.currentTween.stop();
+            this.currentTween = null;
+        }
     }
 }

+ 16 - 0
assets/scripts/Animations/MoneyAni.ts

@@ -51,6 +51,22 @@ export class MoneyAni extends Component {
     
     onLoad() {
         this.saveDataManager = SaveDataManager.getInstance();
+        
+        // 监听奖励动画事件
+        EventBus.getInstance().on('PLAY_REWARD_ANIMATION', this.onPlayRewardAnimation, this);
+    }
+    
+    /**
+     * 处理奖励动画事件
+     */
+    private onPlayRewardAnimation(data: {money: number, diamonds: number}) {
+        console.log('[MoneyAni] 接收到奖励动画事件:', data);
+        this.playRewardAnimation(data.money, data.diamonds);
+    }
+    
+    onDestroy() {
+        // 移除事件监听器
+        EventBus.getInstance().off('PLAY_REWARD_ANIMATION', this.onPlayRewardAnimation, this);
     }
     
     /**

+ 36 - 24
assets/scripts/CombatSystem/BallController.ts

@@ -1,6 +1,7 @@
 import { _decorator, Component, Node, Vec2, Vec3, UITransform, Collider2D, Contact2DType, IPhysics2DContact, RigidBody2D, Prefab, instantiate, find, CircleCollider2D, JsonAsset } from 'cc';
 import { PhysicsManager } from '../Core/PhysicsManager';
-import { WeaponBullet, BulletInitData, WeaponConfig } from './WeaponBullet';
+import { WeaponBullet, BulletInitData } from './WeaponBullet';
+import { WeaponConfig } from '../Core/ConfigManager';
 import EventBus, { GameEvents } from '../Core/EventBus';
 import { PersistentSkillManager } from '../FourUI/SkillSystem/PersistentSkillManager';
 import { BallAni } from '../Animations/BallAni';
@@ -248,26 +249,25 @@ export class BallController extends Component {
         
         // 监听创建额外球事件
         eventBus.on(GameEvents.BALL_CREATE_ADDITIONAL, this.onBallCreateAdditionalEvent, this);
+        
+        // 监听子弹发射检查事件
+        eventBus.on(GameEvents.BALL_FIRE_BULLET, this.onBallFireBulletEvent, this);
     }
 
     /**
      * 处理游戏暂停事件
      */
     private onGamePauseEvent() {
-        console.log('[BallController] 接收到游戏暂停事件');
-        // 根据需求,暂停时小球继续运动但不发射子弹
-        // 子弹发射的控制已经通过GamePause.isBulletFireEnabled()实现
-        // 这里可以添加其他暂停相关的逻辑
+        console.log('[BallController] 接收到游戏暂停事件,暂停小球运动');
+        this.pauseBall();
     }
 
     /**
      * 处理游戏恢复事件
      */
     private onGameResumeEvent() {
-        console.log('[BallController] 接收到游戏恢复事件');
-        // 恢复时重新启用子弹发射
-        // 子弹发射的控制已经通过GamePause.isBulletFireEnabled()实现
-        // 这里可以添加其他恢复相关的逻辑
+        console.log('[BallController] 接收到游戏恢复事件,恢复小球运动');
+        this.resumeBall();
     }
     
     /**
@@ -301,6 +301,21 @@ export class BallController extends Component {
         console.log('[BallController] 接收到创建额外球事件');
         this.createAdditionalBall();
     }
+    
+    /**
+     * 处理子弹发射检查事件
+     */
+    private onBallFireBulletEvent(blockNode: Node, fireWorldPos: Vec3) {
+        // 如果游戏暂停,则不允许发射子弹
+        if (this.isPaused) {
+            console.log('[BallController] 游戏暂停中,阻止子弹发射');
+            return;
+        }
+        
+        // 如果游戏未暂停,则继续执行子弹发射逻辑
+        const weaponConfig: WeaponConfig | null = (blockNode as any)['weaponConfig'] || null;
+        this.createAndFireBullet(fireWorldPos, weaponConfig);
+    }
 
     // 计算游戏边界(使用GameArea节点)
     calculateGameBounds() {
@@ -1108,25 +1123,22 @@ export class BallController extends Component {
         
         // 查找方块中的Weapon节点
         const weaponNode = this.findWeaponNode(blockNode);
-        if (!weaponNode) {
-            const blockWorldPos = blockNode.worldPosition;
-            const weaponConfig2: WeaponConfig | null = (blockNode as any)['weaponConfig'] || null;
-            this.createAndFireBullet(blockWorldPos, weaponConfig2);
-            return;
-        }
-        
-        // 获取武器的世界坐标作为发射位置
         let firePosition: Vec3;
-        try {
-            firePosition = weaponNode.worldPosition;
-        } catch (error) {
-            // 备用方案:使用方块坐标
+        
+        if (!weaponNode) {
             firePosition = blockNode.worldPosition;
+        } else {
+            // 获取武器的世界坐标作为发射位置
+            try {
+                firePosition = weaponNode.worldPosition;
+            } catch (error) {
+                // 备用方案:使用方块坐标
+                firePosition = blockNode.worldPosition;
+            }
         }
         
-        // 创建并发射子弹
-        const weaponConfig: WeaponConfig | null = (blockNode as any)['weaponConfig'] || null;
-        this.createAndFireBullet(firePosition, weaponConfig);
+        // 通过事件系统发射子弹,这样可以被暂停检查拦截
+        EventBus.getInstance().emit(GameEvents.BALL_FIRE_BULLET, blockNode, firePosition);
     }
     
     /**

+ 29 - 10
assets/scripts/CombatSystem/BlockManager.ts

@@ -810,6 +810,13 @@ export class BlockManager extends Component {
         block.position = rootTargetLocalPos;
         this.markOccupiedPositions(block, targetGrid);
         
+        // 当方块从kuang区域拖到网格时,恢复植物图标的正常缩放
+        const weaponNode = block.getChildByName('Weapon');
+        if (weaponNode) {
+            weaponNode.setScale(1.0, 1.0, 1.0);
+            console.log(`[BlockManager] 方块 ${block.name} 放置到网格,植物图标缩放恢复为正常大小`);
+        }
+        
         return true;
     }
     
@@ -1050,11 +1057,7 @@ export class BlockManager extends Component {
         
         console.log('[BlockManager] 清理后方块总数:', this.blocks.length);
         
-        // 重新生成方块
-        console.log('[BlockManager] 开始重新生成方块');
-        this.generateRandomBlocksInKuang();
-        
-        console.log('[BlockManager] 游戏重置完成');
+        console.log('[BlockManager] 游戏重置完成 - 方块生成将由StartGame流程处理');
     }
     
     // 清理已放置的方块
@@ -1259,7 +1262,7 @@ export class BlockManager extends Component {
         const shapeId = this.getBlockShape(block);
         if (!shapeId) {
             // 如果无法获取形状,使用基础价格
-            this.setBlockPriceByRarity(priceNode, weaponConfig.rarity);
+            this.setBlockPriceByRarity(priceNode, weaponConfig.rarity || 'common');
             return;
         }
 
@@ -1267,7 +1270,7 @@ export class BlockManager extends Component {
         const inGameCostConfig = weaponConfig.inGameCostConfig;
         if (!inGameCostConfig) {
             // 如果没有局内金币配置,使用原有的稀有度定价方式
-            this.setBlockPriceByRarity(priceNode, weaponConfig.rarity);
+            this.setBlockPriceByRarity(priceNode, weaponConfig.rarity || 'common');
             return;
         }
 
@@ -1297,8 +1300,11 @@ export class BlockManager extends Component {
     
     // 设置方块的武器外观
     private setupBlockWeaponVisual(block: Node, weaponConfig: WeaponConfig) {
+        // 确保方块节点上也有 weaponConfig 属性
+        block['weaponConfig'] = weaponConfig;
+        
         // 设置方块的稀有度颜色
-        this.setBlockRarityColor(block, weaponConfig.rarity);
+        this.setBlockRarityColor(block, weaponConfig.rarity || 'common');
         
         // 加载武器图标
         this.loadWeaponIcon(block, weaponConfig);
@@ -1410,6 +1416,13 @@ export class BlockManager extends Component {
             spriteFrame && spriteFrame.isValid) {
             weaponSprite.spriteFrame = spriteFrame;
             
+            // 检查方块是否在kuang区域,如果是则缩放植物图标至0.45倍
+            const blockLocation = this.blockLocations.get(block);
+            if (blockLocation === 'kuang') {
+                weaponNode.setScale(0.45, 0.45, 1);
+                console.log(`[BlockManager] 设置kuang区域植物图标缩放: 0.45倍`);
+            }
+            
             // 应用武器图标的位置偏移和旋转
             this.rotateWeaponIconByShape(weaponNode, blockShapeId, weaponConfig.id);
         }
@@ -1419,11 +1432,11 @@ export class BlockManager extends Component {
     // 武器类型和方块形状组合的位置偏移配置(相对于方块中心的偏移量)
     private readonly WEAPON_SHAPE_POSITION_OFFSETS: { [weaponId: string]: { [shapeId: string]: { x: number, y: number } } } = {
         'pea_shooter': {
-            'I': { x: 3, y: -3 },      // 毛豆射手竖条形状,轻微向下偏移
+            'I': { x: 4, y: -3 },      // 毛豆射手竖条形状,轻微向下偏移
             'H-I': { x: 0, y: 5 },     // 毛豆射手横条形状,居中
             'L': { x: 1, y: -10 },     // 毛豆射手L型,轻微向左下偏移
             'S': { x: 5, y: -1 },      // 毛豆射手S型,轻微向右下偏移
-            'D-T': { x: 3, y: -1 }     // 毛豆射手倒T型,轻微向下偏移
+            'D-T': { x: 4, y: -1 }     // 毛豆射手倒T型,轻微向下偏移
         },
         'sharp_carrot': {
             'I': { x: 0, y: -1 },      // 尖胡萝卜竖条形状,轻微向下偏移
@@ -2009,12 +2022,16 @@ export class BlockManager extends Component {
                             const upgradedConfig = { ...currentConfig };
                             upgradedConfig.rarity = nextRarity;
                             this.blockWeaponConfigs.set(target, upgradedConfig);
+                            // 同时更新方块节点的 weaponConfig 属性,供 BallController 使用
+                            (target as any)['weaponConfig'] = upgradedConfig;
                             console.log(`[BlockManager] 武器配置升级: ${originalWeaponName} 稀有度 ${rarity} -> ${nextRarity}`);
                         } else {
                             // 如果没有关卡配置限制,正常升级稀有度
                             const upgradedConfig = { ...currentConfig };
                             upgradedConfig.rarity = nextRarity;
                             this.blockWeaponConfigs.set(target, upgradedConfig);
+                            // 同时更新方块节点的 weaponConfig 属性,供 BallController 使用
+                            (target as any)['weaponConfig'] = upgradedConfig;
                             console.log(`[BlockManager] 武器配置升级: ${originalWeaponName} 稀有度 ${rarity} -> ${nextRarity}`);
                         }
                     } catch (weaponError) {
@@ -2023,6 +2040,8 @@ export class BlockManager extends Component {
                         const upgradedConfig = { ...currentConfig };
                         upgradedConfig.rarity = nextRarity;
                         this.blockWeaponConfigs.set(target, upgradedConfig);
+                        // 同时更新方块节点的 weaponConfig 属性,供 BallController 使用
+                        (target as any)['weaponConfig'] = upgradedConfig;
                         console.log(`[BlockManager] 武器配置升级(降级处理): ${originalWeaponName} 稀有度 ${rarity} -> ${nextRarity}`);
                     }
                 }

+ 48 - 13
assets/scripts/CombatSystem/BlockSelection/GameBlockSelection.ts

@@ -123,6 +123,9 @@ export class GameBlockSelection extends Component {
         // 如果还未初始化,则进行初始化
         if (!this.isInitialized) {
             this.initializeComponent();
+        } else {
+            // 如果已经初始化,重新设置事件监听器(因为onDisable时会移除)
+            this.setupEventListeners();
         }
         this.initDebugDraw();
     }
@@ -621,16 +624,28 @@ export class GameBlockSelection extends Component {
     // 设置方块拖拽事件
     public setupBlockDragEvents(block: Node) {
         block.on(Node.EventType.TOUCH_START, (event: EventTouch) => {
-            if (this.blockManager.gameStarted && this.blockManager.blockLocations.get(block) === 'grid') {
-                if (!this.canMoveBlock(block)) {
-                    return;
-                }
+            console.log('[GameBlockSelection] TOUCH_START - gameStarted:', this.blockManager.gameStarted);
+            
+            // 检查游戏是否已开始
+            if (!this.blockManager.gameStarted) {
+                console.log('[GameBlockSelection] 游戏未开始,拒绝拖拽');
+                return;
+            }
+            
+            // 只对grid区域的方块检查移动限制,kuang区域的方块可以自由拖拽
+            const blockLocation = this.blockManager.blockLocations.get(block);
+            console.log('[GameBlockSelection] 方块位置:', blockLocation, '可移动检查:', blockLocation === 'grid' ? this.canMoveBlock(block) : true);
+            
+            if (blockLocation === 'grid' && !this.canMoveBlock(block)) {
+                console.log('[GameBlockSelection] grid区域方块移动被限制');
+                return;
             }
             
+            console.log('[GameBlockSelection] 开始拖拽方块,位置:', blockLocation);
             this.currentDragBlock = block;
             this.startPos = event.getUILocation();
             this.blockStartPos.set(block.position);
-            this.currentDragBlock['startLocation'] = this.blockManager.blockLocations.get(block);
+            this.currentDragBlock['startLocation'] = blockLocation;
             
             // 设置拖动状态,隐藏价格标签
             block['isDragging'] = true;
@@ -642,10 +657,15 @@ export class GameBlockSelection extends Component {
         }, this);
         
         block.on(Node.EventType.TOUCH_MOVE, (event: EventTouch) => {
-            if (this.blockManager.gameStarted && this.blockManager.blockLocations.get(block) === 'grid') {
-                if (!this.canMoveBlock(block)) {
-                    return;
-                }
+            // 检查游戏是否已开始
+            if (!this.blockManager.gameStarted) {
+                return;
+            }
+            
+            // 只对grid区域的方块检查移动限制,kuang区域的方块可以自由拖拽
+            const blockLocation = this.blockManager.blockLocations.get(block);
+            if (blockLocation === 'grid' && !this.canMoveBlock(block)) {
+                return;
             }
             
             if (!this.currentDragBlock) return;
@@ -665,10 +685,15 @@ export class GameBlockSelection extends Component {
         }, this);
         
         block.on(Node.EventType.TOUCH_END, async (event: EventTouch) => {
-            if (this.blockManager.gameStarted && this.blockManager.blockLocations.get(block) === 'grid') {
-                if (!this.canMoveBlock(block)) {
-                    return;
-                }
+            // 检查游戏是否已开始
+            if (!this.blockManager.gameStarted) {
+                return;
+            }
+            
+            // 只对grid区域的方块检查移动限制,kuang区域的方块可以自由拖拽
+            const blockLocation = this.blockManager.blockLocations.get(block);
+            if (blockLocation === 'grid' && !this.canMoveBlock(block)) {
+                return;
             }
             
             if (this.currentDragBlock) {
@@ -795,6 +820,16 @@ export class GameBlockSelection extends Component {
         this.blockManager.blockLocations.set(this.currentDragBlock, 'kuang');
         this.blockManager.showPriceLabel(this.currentDragBlock);
         
+        // 当方块返回kuang区域时,重新设置植物图标缩放至0.45倍
+        const b1Node = this.currentDragBlock.getChildByName('B1');
+        if (b1Node) {
+            const weaponNode = b1Node.getChildByName('Weapon');
+            if (weaponNode) {
+                weaponNode.setScale(0.45, 0.45, 1);
+                console.log(`[GameBlockSelection] 方块返回kuang区域,设置植物图标缩放: 0.45倍`);
+            }
+        }
+        
         if (startLocation === 'grid') {
             const price = this.blockManager.getBlockPrice(this.currentDragBlock);
             this.blockManager.refundPlayerCoins(price);

+ 1 - 8
assets/scripts/CombatSystem/BulletEffects/BulletCount.ts

@@ -1,6 +1,7 @@
 import { _decorator, Component, Node, Vec3, instantiate } from 'cc';
 import { SkillManager } from '../SkillSelection/SkillManager'; // 导入SkillManager(用于多重射击技能)
 import { WeaponBullet } from '../WeaponBullet'; // 正确导入WeaponBullet
+import { BulletCountConfig } from '../../Core/ConfigManager';
 const { ccclass, property } = _decorator;
 
 /**
@@ -8,14 +9,6 @@ const { ccclass, property } = _decorator;
  * 负责根据配置生成不同数量和模式的子弹
  */
 
-export interface BulletCountConfig {
-    type: 'single' | 'spread' | 'burst';  // 发射模式
-    amount: number;                       // 子弹数量
-    spreadAngle: number;                  // 散射角度(度)
-    burstCount: number;                   // 连发数量
-    burstDelay: number;                   // 连发间隔(秒)
-}
-
 export interface BulletSpawnInfo {
     position: Vec3;      // 发射位置
     direction: Vec3;     // 发射方向

+ 1 - 6
assets/scripts/CombatSystem/BulletEffects/BulletHitEffect.ts

@@ -1,5 +1,6 @@
 import { _decorator, Component, Node, Vec3, find, instantiate, Prefab, UITransform, resources, sp, CircleCollider2D } from 'cc';
 import { BulletTrajectory } from './BulletTrajectory';
+import { HitEffectConfig } from '../../Core/ConfigManager';
 const { ccclass, property } = _decorator;
 
 /**
@@ -7,12 +8,6 @@ const { ccclass, property } = _decorator;
  * 负责处理可叠加的命中效果
  */
 
-export interface HitEffectConfig {
-    type: 'normal_damage' | 'pierce_damage' | 'explosion' | 'ground_burn' | 'ricochet_damage';
-    priority: number;      // 优先级,数字越小优先级越高
-    params: any;          // 效果参数
-}
-
 export interface ExplosionParams {
     damage: number;
     radius: number;

+ 2 - 12
assets/scripts/CombatSystem/BulletEffects/BulletLifecycle.ts

@@ -1,5 +1,6 @@
 import { _decorator, Component, Node, Vec3, Vec2, find, UITransform, RigidBody2D } from 'cc';
 import { BulletTrajectory } from './BulletTrajectory';
+import { BulletLifecycleConfig } from '../../Core/ConfigManager';
 const { ccclass, property } = _decorator;
 
 /**
@@ -7,17 +8,6 @@ const { ccclass, property } = _decorator;
  * 负责管理子弹的生存时间和销毁条件
  */
 
-export interface BulletLifecycleConfig {
-    type: 'hit_destroy' | 'range_limit' | 'ricochet_counter' | 'ground_impact' | 'return_trip' | 'ground_impact_with_effect' | 'target_impact';
-    maxLifetime: number;          // 最大生存时间(秒)
-    penetration: number;          // 穿透次数
-    ricochetCount: number;        // 弹射次数
-    returnToOrigin: boolean;      // 是否返回原点
-    returnDelay?: number;         // 返回延迟(秒)
-    maxRange?: number;            // 最大射程
-    effectDuration?: number;      // 效果持续时间(用于地面效果)
-}
-
 export interface LifecycleState {
     elapsedTime: number;          // 已存活时间
     hitCount: number;             // 命中次数
@@ -421,4 +411,4 @@ export class BulletLifecycle extends Component {
         
         return true;
     }
-} 
+}

+ 2 - 13
assets/scripts/CombatSystem/BulletEffects/BulletTrajectory.ts

@@ -1,4 +1,5 @@
 import { _decorator, Component, Node, Vec2, Vec3, RigidBody2D, find } from 'cc';
+import { BulletTrajectoryConfig } from '../../Core/ConfigManager';
 const { ccclass, property } = _decorator;
 
 /**
@@ -6,18 +7,6 @@ const { ccclass, property } = _decorator;
  * 负责控制子弹的运动轨迹
  */
 
-export interface BulletTrajectoryConfig {
-    type: 'straight' | 'parabolic' | 'homing' | 'arc';  // 弹道类型
-    speed: number;                                      // 初始速度
-    gravity: number;                                    // 重力影响 (arc/straight 可为0)
-    homingStrength: number;                             // 追踪强度 (0-1)
-    homingDelay: number;                                // 追踪延迟(秒)
-    /**
-     * 旋转速率 (0~1)。仅在 arc 弹道中使用,值越大转向越快。
-     */
-    rotateSpeed?: number;
-}
-
 export interface TrajectoryState {
     initialVelocity: Vec3;     // 初始速度
     currentVelocity: Vec3;     // 当前速度
@@ -425,4 +414,4 @@ export class BulletTrajectory extends Component {
         
         return true;
     }
-} 
+}

+ 15 - 0
assets/scripts/CombatSystem/EnemyController.ts

@@ -242,6 +242,7 @@ export class EnemyController extends BaseSingleton {
      * 处理游戏暂停事件
      */
     private onGamePauseEvent() {
+        console.log('[EnemyController] 接收到游戏暂停事件,暂停所有敌人');
         this.pauseAllEnemies();
         this.pauseSpawning();
     }
@@ -250,6 +251,7 @@ export class EnemyController extends BaseSingleton {
      * 处理游戏恢复事件
      */
     private onGameResumeEvent() {
+        console.log('[EnemyController] 接收到游戏恢复事件,恢复所有敌人');
         this.resumeAllEnemies();
         
         // 通过事件检查游戏状态,决定是否恢复敌人生成
@@ -309,6 +311,7 @@ export class EnemyController extends BaseSingleton {
      */
     private pauseAllEnemies(): void {
         const activeEnemies = this.getActiveEnemies();
+        console.log(`[EnemyController] 暂停 ${activeEnemies.length} 个敌人`);
         
         for (const enemy of activeEnemies) {
             if (!enemy || !enemy.isValid) continue;
@@ -329,6 +332,12 @@ export class EnemyController extends BaseSingleton {
                 rigidBody.sleep();
             }
 
+            // 暂停EnemyInstance组件
+            const enemyInstance = enemy.getComponent('EnemyInstance');
+            if (enemyInstance && typeof (enemyInstance as any).pause === 'function') {
+                (enemyInstance as any).pause();
+            }
+
             // 暂停敌人AI组件(如果有的话)
             const enemyAI = enemy.getComponent('EnemyAI');
             if (enemyAI && typeof (enemyAI as any).pause === 'function') {
@@ -363,6 +372,12 @@ export class EnemyController extends BaseSingleton {
                 }
             }
 
+            // 恢复EnemyInstance组件
+            const enemyInstance = enemy.getComponent('EnemyInstance');
+            if (enemyInstance && typeof (enemyInstance as any).resume === 'function') {
+                (enemyInstance as any).resume();
+            }
+
             // 恢复敌人AI组件
             const enemyAI = enemy.getComponent('EnemyAI');
             if (enemyAI && typeof (enemyAI as any).resume === 'function') {

+ 54 - 7
assets/scripts/CombatSystem/EnemyInstance.ts

@@ -74,6 +74,9 @@ export class EnemyInstance extends Component {
     
     // 血条动画组件
     private hpBarAnimation: HPBarAnimation | null = null;
+    
+    // 暂停状态标记
+    private isPaused: boolean = false;
 
 
     start() {
@@ -260,7 +263,12 @@ export class EnemyInstance extends Component {
     
     // 更新血量显示
     updateHealthDisplay() {
-        const healthProgress = this.health / this.maxHealth;
+        // 确保血量值在有效范围内
+        this.health = Math.max(0, Math.min(this.maxHealth, this.health));
+        
+        const healthProgress = this.maxHealth > 0 ? this.health / this.maxHealth : 0;
+        
+        console.log(`[EnemyInstance] 更新血量显示: ${this.health}/${this.maxHealth} (${(healthProgress * 100).toFixed(1)}%)`);
         
         // 使用血条动画组件更新血条
         if (this.hpBarAnimation) {
@@ -281,7 +289,8 @@ export class EnemyInstance extends Component {
         if (hpLabel) {
             const label = hpLabel.getComponent(Label);
             if (label) {
-                label.string = this.health.toString();
+                // 显示整数血量值
+                label.string = Math.ceil(this.health).toString();
             }
         }
     }
@@ -293,22 +302,32 @@ export class EnemyInstance extends Component {
             return;
         }
         
-        this.health -= damage;
-        console.log(`[EnemyInstance] 敌人受到伤害: ${damage}, 剩余血量: ${this.health}`);
+        // 确保伤害值为正数
+        if (damage <= 0) {
+            console.warn(`[EnemyInstance] 无效的伤害值: ${damage}`);
+            return;
+        }
+        
+        // 计算新的血量,确保不会低于0
+        const newHealth = Math.max(0, this.health - damage);
+        const actualDamage = this.health - newHealth;
+        this.health = newHealth;
+        
+        console.log(`[EnemyInstance] 敌人受到伤害: ${actualDamage}, 剩余血量: ${this.health}/${this.maxHealth}`);
         
         // 显示伤害数字动画(在敌人头顶)
         // 优先使用EnemyController节点上的DamageNumberAni组件实例
         if (this.controller) {
             const damageAni = this.controller.getComponent(DamageNumberAni);
             if (damageAni) {
-                damageAni.showDamageNumber(damage, this.node.worldPosition, isCritical);
+                damageAni.showDamageNumber(actualDamage, this.node.worldPosition, isCritical);
             } else {
                 // 如果没有找到组件实例,使用静态方法作为备用
-                DamageNumberAni.showDamageNumber(damage, this.node.worldPosition, isCritical);
+                DamageNumberAni.showDamageNumber(actualDamage, this.node.worldPosition, isCritical);
             }
         } else {
             // 如果没有controller引用,使用静态方法
-            DamageNumberAni.showDamageNumber(damage, this.node.worldPosition, isCritical);
+            DamageNumberAni.showDamageNumber(actualDamage, this.node.worldPosition, isCritical);
         }
         
         // 更新血量显示和动画
@@ -344,6 +363,11 @@ export class EnemyInstance extends Component {
     }
 
     update(deltaTime: number) {
+        // 如果敌人被暂停,则不执行任何更新逻辑
+        if (this.isPaused) {
+            return;
+        }
+        
         if (this.state === EnemyState.MOVING) {
             this.updateMovement(deltaTime);
         } else if (this.state === EnemyState.ATTACKING) {
@@ -514,4 +538,27 @@ export class EnemyInstance extends Component {
         this.node.getWorldPosition(pos);         // 取死亡敌人的世界坐标
         coin.worldPosition = pos;                // 金币就在敌人身上出现
     }
+    
+    /**
+     * 暂停敌人
+     */
+    public pause(): void {
+        this.isPaused = true;
+        console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 已暂停`);
+    }
+    
+    /**
+     * 恢复敌人
+     */
+    public resume(): void {
+        this.isPaused = false;
+        console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 已恢复`);
+    }
+    
+    /**
+     * 检查是否暂停
+     */
+    public isPausedState(): boolean {
+        return this.isPaused;
+    }
 }

+ 412 - 0
assets/scripts/CombatSystem/GameEnd.ts

@@ -0,0 +1,412 @@
+import { _decorator, Component, Node, Label, Button } from 'cc';
+import { SaveDataManager } from '../LevelSystem/SaveDataManager';
+import { InGameManager, GameState } from '../LevelSystem/IN_game';
+import EventBus, { GameEvents } from '../Core/EventBus';
+const { ccclass, property } = _decorator;
+
+/**
+ * 游戏结算界面管理器
+ * 负责处理游戏结束后的奖励显示和双倍奖励功能
+ */
+@ccclass('GameEnd')
+export class GameEnd extends Component {
+    
+    // === UI节点引用 ===
+    @property({
+        type: Button,
+        tooltip: '双倍奖励按钮 (Canvas/GameEnd/double)'
+    })
+    public doubleButton: Button = null;
+    
+    @property({
+        type: Label,
+        tooltip: '钞票数量显示 '
+    })
+    public moneyLabel: Label = null;
+    
+    @property({
+        type: Label,
+        tooltip: '钻石数量显示 '
+    })
+    public diamondLabel: Label = null;
+    
+    @property({
+        type: Button,
+        tooltip: '继续按钮 (Canvas/GameEnd/Continue)'
+    })
+    public continueButton: Button = null;
+    
+    @property({
+        type: InGameManager,
+        tooltip: '游戏管理器组件'
+    })
+    public inGameManager: InGameManager = null;
+    
+    // 动画相关属性已移除,现在由MoneyAni组件负责处理奖励动画
+    
+    // === 私有属性 ===
+    private saveDataManager: SaveDataManager = null;
+    public currentRewards: {money: number, diamonds: number};
+    private hasDoubledReward: boolean = false;
+    private isGameSuccess: boolean = false;
+    
+    start() {
+        console.log('[GameEnd] start方法被调用');
+        // start方法只在节点首次激活时调用一次
+        // 如果节点初始状态为false,start不会被调用
+    }
+    
+    onEnable() {
+        console.log('[GameEnd] onEnable方法被调用,节点已激活');
+        
+        // 初始化管理器
+        this.initializeManagers();
+        
+        // UI节点已通过装饰器挂载,无需自动查找
+        
+        // 绑定按钮事件
+        this.bindButtonEvents();
+        
+        // 监听游戏事件
+        this.setupEventListeners();
+        
+        // 初始化UI状态
+        this.initializeUI();
+        
+        // 检查当前游戏状态并处理奖励(解决时序问题)
+        this.checkAndHandleGameState();
+    }
+    
+    /**
+     * 初始化管理器
+     */
+    private initializeManagers() {
+        this.saveDataManager = SaveDataManager.getInstance();
+        // InGameManager已通过装饰器挂载,无需查找
+    }
+
+    /**
+     * 绑定按钮事件
+     */
+    private bindButtonEvents() {
+        if (this.doubleButton) {
+            this.doubleButton.node.on(Button.EventType.CLICK, this.onDoubleButtonClick, this);
+        }
+        
+        if (this.continueButton) {
+            this.continueButton.node.on(Button.EventType.CLICK, this.onContinueButtonClick, this);
+        }
+    }
+    
+    /**
+     * 设置事件监听器
+     */
+    private setupEventListeners() {
+        console.log('[GameEnd] 开始设置事件监听器');
+        const eventBus = EventBus.getInstance();
+        
+        // 监听游戏成功事件
+        eventBus.on(GameEvents.GAME_SUCCESS, this.onGameSuccess, this);
+        console.log('[GameEnd] 已注册GAME_SUCCESS事件监听器');
+        
+        // 监听游戏失败事件
+        eventBus.on(GameEvents.GAME_DEFEAT, this.onGameDefeat, this);
+        console.log('[GameEnd] 已注册GAME_DEFEAT事件监听器');
+        
+        // 监听UI重置事件
+        eventBus.on(GameEvents.RESET_UI_STATES, this.onResetUI, this);
+        console.log('[GameEnd] 事件监听器设置完成');
+    }
+    
+    /**
+     * 初始化UI状态
+     */
+    private initializeUI() {
+        // 不强制隐藏面板,让UIStateManager控制面板显示状态
+        // this.node.active = false;
+        
+        // 重置状态
+        this.hasDoubledReward = false;
+        this.currentRewards = {money: 0, diamonds: 0};
+        
+        console.log('[GameEnd] UI状态初始化完成,面板状态由UIStateManager控制');
+    }
+    
+    /**
+     * 检查当前游戏状态并处理奖励(解决时序问题)
+     */
+    private checkAndHandleGameState() {
+        console.log('[GameEnd] 检查当前游戏状态');
+        
+        if (!this.inGameManager) {
+            console.log('[GameEnd] InGameManager未初始化,无法检查游戏状态');
+            return;
+        }
+        
+        const currentState = this.inGameManager.getCurrentState();
+        console.log('[GameEnd] 当前游戏状态:', currentState);
+        
+        // 如果游戏已经结束,主动处理奖励
+        if (currentState === GameState.SUCCESS) {
+            console.log('[GameEnd] 检测到游戏成功状态,主动处理奖励');
+            this.isGameSuccess = true;
+            this.calculateAndShowRewards();
+        } else if (currentState === GameState.DEFEAT) {
+            console.log('[GameEnd] 检测到游戏失败状态,主动处理奖励');
+            this.isGameSuccess = false;
+            this.calculateAndShowRewards();
+        }
+    }
+    
+    /**
+     * 处理游戏成功事件
+     */
+    private onGameSuccess() {
+        console.log('[GameEnd] 接收到GAME_SUCCESS事件');
+        console.log('[GameEnd] 游戏成功,准备显示奖励');
+        this.isGameSuccess = true;
+        this.calculateAndShowRewards();
+    }
+    
+    /**
+     * 处理游戏失败事件
+     */
+    private onGameDefeat() {
+        console.log('[GameEnd] 接收到GAME_DEFEAT事件');
+        console.log('[GameEnd] 游戏失败,准备显示奖励');
+        this.isGameSuccess = false;
+        this.calculateAndShowRewards();
+    }
+    
+    /**
+     * 计算并显示奖励
+     */
+    private async calculateAndShowRewards() {
+        console.log('[GameEnd] 开始计算并显示奖励');
+        
+        if (!this.saveDataManager) {
+            console.error('[GameEnd] SaveDataManager未初始化');
+            return;
+        }
+        
+        const currentLevel = this.saveDataManager.getCurrentLevel();
+        console.log(`[GameEnd] 当前关卡: ${currentLevel}, 游戏成功: ${this.isGameSuccess}`);
+        
+        try {
+            if (this.isGameSuccess) {
+                // 游戏成功,给予完整奖励
+                console.log('[GameEnd] 准备给予成功奖励');
+                await this.saveDataManager.giveCompletionRewards(currentLevel);
+                console.log('[GameEnd] 已给予成功奖励');
+            } else {
+                // 游戏失败,给予按比例奖励
+                const totalWaves = this.inGameManager?.levelWaves?.length || 1;
+                const completedWaves = this.inGameManager ? Math.max(0, this.inGameManager.getCurrentWave() - 1) : 0;
+                
+                console.log(`[GameEnd] 准备给予失败奖励,完成波数: ${completedWaves}/${totalWaves}`);
+                await this.saveDataManager.giveFailureRewards(currentLevel, completedWaves, totalWaves);
+                console.log(`[GameEnd] 已给予失败奖励,完成波数: ${completedWaves}/${totalWaves}`);
+            }
+            
+            // 获取奖励数据并显示
+            this.currentRewards = this.saveDataManager.getLastRewards();
+            console.log('[GameEnd] 获取到的奖励数据:', this.currentRewards);
+            this.updateRewardDisplay();
+            
+            // 显示结算面板
+            this.showEndPanel();
+            
+        } catch (error) {
+            console.error('[GameEnd] 计算奖励时出错:', error);
+        }
+    }
+    
+    /**
+     * 更新奖励显示
+     */
+    private updateRewardDisplay() {
+        if (this.moneyLabel) {
+            this.moneyLabel.string = this.currentRewards.money.toString();
+        }
+        
+        if (this.diamondLabel) {
+            this.diamondLabel.string = this.currentRewards.diamonds.toString();
+        }
+        
+        console.log(`[GameEnd] 更新奖励显示 - 金币: ${this.currentRewards.money}, 钻石: ${this.currentRewards.diamonds}`);
+    }
+    
+    /**
+     * 显示结算面板
+     */
+    private showEndPanel() {
+        this.node.active = true;
+        
+        // 重置双倍奖励状态
+        this.hasDoubledReward = false;
+        if (this.doubleButton) {
+            this.doubleButton.interactable = true;
+        }
+        
+        console.log('[GameEnd] 结算面板已显示');
+        
+
+    }
+    
+    /**
+     * 双倍按钮点击事件
+     */
+    private onDoubleButtonClick() {
+        if (this.hasDoubledReward) {
+            console.log('[GameEnd] 已经获得过双倍奖励');
+            return;
+        }
+        
+        console.log('[GameEnd] 点击双倍奖励按钮');
+        
+        // 这里可以添加观看广告的逻辑
+        // 暂时直接给予双倍奖励
+        this.giveDoubleReward();
+    }
+    
+    /**
+     * 给予双倍奖励
+     */
+    private giveDoubleReward() {
+        if (!this.saveDataManager || this.hasDoubledReward) {
+            return;
+        }
+        
+        // 计算双倍奖励
+        const doubleMoney = this.currentRewards.money;
+        const doubleDiamonds = this.currentRewards.diamonds;
+        
+        // 添加额外奖励到玩家账户
+        if (doubleMoney > 0) {
+            this.saveDataManager.addMoney(doubleMoney, 'double_reward');
+        }
+        
+        if (doubleDiamonds > 0) {
+            this.saveDataManager.addDiamonds(doubleDiamonds, 'double_reward');
+        }
+        
+        // 更新当前奖励显示(显示双倍后的数值)
+        this.currentRewards.money += doubleMoney;
+        this.currentRewards.diamonds += doubleDiamonds;
+        this.updateRewardDisplay();
+        
+
+        
+        // 标记已获得双倍奖励
+        this.hasDoubledReward = true;
+        
+        // 禁用双倍按钮
+        if (this.doubleButton) {
+            this.doubleButton.interactable = false;
+        }
+        
+        console.log(`[GameEnd] 双倍奖励已给予 - 额外金币: ${doubleMoney}, 额外钻石: ${doubleDiamonds}`);
+        
+        // 触发货币变化事件
+        EventBus.getInstance().emit(GameEvents.CURRENCY_CHANGED);
+    }
+    
+    /**
+     * 继续按钮点击事件
+     */
+    private onContinueButtonClick() {
+        console.log('[GameEnd] 点击继续按钮');
+        
+        // 派发事件给MoneyAni播放奖励动画
+        const rewards = this.saveDataManager.getLastRewards();
+        console.log('[GameEnd] 派发奖励动画事件,奖励数据:', rewards);
+        EventBus.getInstance().emit('PLAY_REWARD_ANIMATION', {
+            money: rewards.money,
+            diamonds: rewards.diamonds
+        });
+        
+        // 触发返回主菜单事件
+        EventBus.getInstance().emit('CONTINUE_CLICK');
+        
+        // 隐藏结算面板
+        this.hideEndPanel();
+    }
+    
+    /**
+     * 隐藏结算面板
+     */
+    private hideEndPanel() {
+        this.node.active = false;
+        console.log('[GameEnd] 结算面板已隐藏');
+    }
+    
+    /**
+     * 重置UI状态
+     */
+    private onResetUI() {
+        console.log('[GameEnd] 重置UI状态');
+        this.hideEndPanel();
+        this.hasDoubledReward = false;
+        this.currentRewards = {money: 0, diamonds: 0};
+    }
+    
+    /**
+     * 获取当前奖励信息(用于外部查询)
+     */
+    public getCurrentRewards(): {money: number, diamonds: number} {
+        return {...this.currentRewards};
+    }
+    
+    /**
+     * 检查是否已获得双倍奖励
+     */
+    public hasGotDoubleReward(): boolean {
+        return this.hasDoubledReward;
+    }
+    
+    
+    
+
+    
+
+    
+
+    
+
+    
+
+    
+
+    
+
+    
+
+    
+
+    onDisable() {
+        console.log('[GameEnd] onDisable方法被调用,节点已禁用');
+        // 清理事件监听
+        const eventBus = EventBus.getInstance();
+        eventBus.off(GameEvents.GAME_SUCCESS, this.onGameSuccess, this);
+        eventBus.off(GameEvents.GAME_DEFEAT, this.onGameDefeat, this);
+        eventBus.off(GameEvents.RESET_UI_STATES, this.onResetUI, this);
+        console.log('[GameEnd] 事件监听器已清理');
+    }
+    
+    protected onDestroy() {
+        // 清理事件监听
+        const eventBus = EventBus.getInstance();
+        eventBus.off(GameEvents.GAME_SUCCESS, this.onGameSuccess, this);
+        eventBus.off(GameEvents.GAME_DEFEAT, this.onGameDefeat, this);
+        eventBus.off(GameEvents.RESET_UI_STATES, this.onResetUI, this);
+        
+        // 清理按钮事件
+        if (this.doubleButton) {
+            this.doubleButton.node.off(Button.EventType.CLICK, this.onDoubleButtonClick, this);
+        }
+        
+        if (this.continueButton) {
+            this.continueButton.node.off(Button.EventType.CLICK, this.onContinueButtonClick, this);
+        }
+    }
+}

+ 9 - 0
assets/scripts/CombatSystem/GameEnd.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "cda086d9-eef4-483e-b333-a82af8bb0886",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 1 - 1
assets/scripts/CombatSystem/SkillSelection/SkillManager.ts

@@ -8,7 +8,7 @@ export interface SkillData {
     id: string;
     name: string;
     description: string;
-    icon: string;
+    iconPath: string;
     maxLevel: number;
     currentLevel: number;
     effects: any;

+ 3 - 3
assets/scripts/CombatSystem/SkillSelection/SkillSelectionController.ts

@@ -118,13 +118,13 @@ export class SkillSelectionController extends Component {
                     const sprite = skillSprite.getComponent(Sprite);
                     if (sprite) {
                         // 加载技能图标 - 参考UpgradeController的加载方式,在路径后添加'/spriteFrame'
-                        const framePath = `${skillData.icon}/spriteFrame`;
+                        const framePath = `${skillData.iconPath}/spriteFrame`;
                         resources.load(framePath, SpriteFrame, (err, spriteFrame) => {
                             if (!err && spriteFrame && sprite && sprite.isValid) {
                                 sprite.spriteFrame = spriteFrame;
-                                console.log(`技能图标加载成功: ${skillData.icon}`);
+                                console.log(`技能图标加载成功: ${skillData.iconPath}`);
                             } else if (err) {
-                                console.warn(`加载技能图标失败: ${skillData.icon}`, err);
+                                console.warn(`加载技能图标失败: ${skillData.iconPath}`, err);
                             }
                         });
                     }

+ 36 - 36
assets/scripts/CombatSystem/WeaponBullet.ts

@@ -1,9 +1,9 @@
 import { _decorator, Component, Node, Vec2, Vec3, RigidBody2D, Collider2D, Contact2DType, IPhysics2DContact, find, Prefab, instantiate, UITransform, resources, sp, JsonAsset, Sprite, SpriteFrame, math } from 'cc';
-import { BulletCount, BulletCountConfig, BulletSpawnInfo } from './BulletEffects/BulletCount';
-import { BulletTrajectory, BulletTrajectoryConfig } from './BulletEffects/BulletTrajectory';
-import { BulletHitEffect, HitEffectConfig, HitResult } from './BulletEffects/BulletHitEffect';
-import { BulletLifecycle, BulletLifecycleConfig } from './BulletEffects/BulletLifecycle';
-import { ConfigManager } from '../Core/ConfigManager';
+import { BulletCount, BulletSpawnInfo } from './BulletEffects/BulletCount';
+import { BulletTrajectory } from './BulletEffects/BulletTrajectory';
+import { BulletHitEffect, HitResult } from './BulletEffects/BulletHitEffect';
+import { BulletLifecycle } from './BulletEffects/BulletLifecycle';
+import { ConfigManager, WeaponConfig, BulletCountConfig, BulletTrajectoryConfig, HitEffectConfig, BulletLifecycleConfig } from '../Core/ConfigManager';
 import  EventBus,{ GameEvents } from '../Core/EventBus';
 import { PersistentSkillManager } from '../FourUI/SkillSystem/PersistentSkillManager';
 import { SaveDataManager } from '../LevelSystem/SaveDataManager';
@@ -22,36 +22,6 @@ const { ccclass, property } = _decorator;
  * 从weapons.json读取配置并分发给各模块
  */
 
-export interface WeaponConfig {
-    id: string;
-    name: string;
-    bulletConfig: {
-        count: BulletCountConfig;
-        trajectory: BulletTrajectoryConfig;
-        hitEffects: HitEffectConfig[];
-        lifecycle: BulletLifecycleConfig;
-        visual: {
-            bulletPrefab: string;
-            hitEffect: string;
-            trailEffect?: string;
-            muzzleFlash: string;
-        };
-    };
-    stats: {
-        damage: number;
-        fireRate: number;
-        range: number;
-        bulletSpeed: number;
-        accuracy: number;
-    };
-    // 运行时计算的实际数值(应用技能加成后)
-    runtimeStats?: {
-        finalDamage: number;
-        finalCritDamage: number;
-        critChance: number;
-    };
-}
-
 export interface BulletInitData {
     weaponId: string;           // 武器ID,用于查找配置
     firePosition: Vec3;         // 发射位置
@@ -661,6 +631,15 @@ export class WeaponBullet extends Component {
             }
         }
         
+        // 应用稀有度伤害倍数(合成升级效果)
+        // 从武器配置中获取稀有度,如果不存在则默认为 'common'
+        const rarity = this.weaponConfig.rarity || 'common';
+        const rarityMultiplier = this.getRarityDamageMultiplier(rarity);
+        if (rarityMultiplier > 1) {
+            baseDamage = baseDamage * rarityMultiplier;
+            console.log(`[WeaponBullet] 稀有度伤害倍数应用 - 稀有度: ${rarity}, 倍数: ${rarityMultiplier}, 最终基础伤害: ${baseDamage}`);
+        }
+        
         const skillManager = PersistentSkillManager.getInstance();
         if (!skillManager) {
             // 如果没有技能管理器,使用升级后的基础数值
@@ -688,7 +667,7 @@ export class WeaponBullet extends Component {
             critChance
         };
         
-        console.log(`[WeaponBullet] 完整伤害计算完成 - 武器ID: ${this.weaponId}, 基础伤害: ${this.weaponConfig.stats.damage}, 升级后伤害: ${baseDamage}, 最终伤害: ${finalDamage}, 暴击伤害: ${finalCritDamage}, 暴击率: ${(critChance * 100).toFixed(1)}%`);
+        console.log(`[WeaponBullet] 完整伤害计算完成 - 武器ID: ${this.weaponId}, 原始伤害: ${this.weaponConfig.stats.damage}, 稀有度: ${rarity}, 计算后基础伤害: ${baseDamage}, 最终伤害: ${finalDamage}, 暴击伤害: ${finalCritDamage}, 暴击率: ${(critChance * 100).toFixed(1)}%`);
     }
     
     /**
@@ -711,4 +690,25 @@ export class WeaponBullet extends Component {
     public getCritChance(): number {
         return this.weaponConfig?.runtimeStats?.critChance || 0.1;
     }
+    
+    /**
+     * 根据稀有度获取伤害倍数
+     * 合成升级效果:2级伤害*2,3级伤害*4,以此类推
+     */
+    private getRarityDamageMultiplier(rarity: string): number {
+        switch (rarity) {
+            case 'common':
+                return 1; // 1级,无倍数
+            case 'uncommon':
+                return 2; // 2级,2倍伤害
+            case 'rare':
+                return 4; // 3级,4倍伤害
+            case 'epic':
+                return 8; // 4级,8倍伤害
+            case 'legendary':
+                return 16; // 5级,16倍伤害
+            default:
+                return 1;
+        }
+    }
 }

+ 70 - 30
assets/scripts/Core/ConfigManager.ts

@@ -4,59 +4,99 @@ const { ccclass, property } = _decorator;
 
 // 局内金币花费配置接口
 export interface InGameCostConfig {
-    baseCostPerGrid: number;
+    baseCost: number;
     shapeCosts: {
         [shapeId: string]: number;
     };
 }
 
+// 弹道配置接口
+export interface BulletTrajectoryConfig {
+    type: string;
+    speed: number;
+    gravity: number;
+    arcHeight: number;
+    homingStrength: number;
+    homingDelay: number;
+    rotateSpeed?: number;  // 旋转速率 (0~1),仅在 arc 弹道中使用
+}
+
+// 弹药数量配置接口
+export interface BulletCountConfig {
+    type: string;
+    amount: number;
+    spreadAngle: number;
+    burstCount: number;
+    burstDelay: number;
+}
+
+// 命中效果配置接口
+export interface HitEffectConfig {
+    type: string;
+    priority: number;
+    params: {
+        damage: number;
+        [key: string]: any;
+    };
+}
+
+// 弹药生命周期配置接口
+export interface BulletLifecycleConfig {
+    type: string;
+    maxLifetime: number;
+    penetration: number;
+    ricochetCount: number;
+    returnToOrigin: boolean;
+    returnDelay?: number;     // 返回延迟(秒)
+    maxRange?: number;        // 最大射程
+    effectDuration?: number;  // 效果持续时间(用于地面效果)
+}
+
 // 武器配置接口
 export interface WeaponConfig {
     id: string;
     name: string;
     type: string;
-    rarity: string;
+    rarity?: string; // 可选字段,支持动态稀有度(合成时可变)
     stats: {
         damage: number;
         fireRate: number;
         range: number;
         bulletSpeed: number;
-        penetration: number;
-        accuracy: number;
-        explosionRadius?: number;
-        explosionDamage?: number;
-        burnDuration?: number;
-        burnDamage?: number;
-        returnSpeed?: number;
-        homingStrength?: number;
+        accuracy?: number;
     };
     bulletConfig: {
-        bulletType: string;
-        bulletPrefab: string;
-        hitEffect: string;
-        trailEffect?: string;
-        ricochetCount?: number;
-        ricochetAngle?: number;
-        explosionDelay?: number;
-        burnEffect?: string;
-        arcHeight?: number;
-        returnDelay?: number;
-        homingDelay?: number;
-    };
-    attackPattern: {
-        type: string;
-        projectileCount: number;
-        spreadAngle: number;
-        burstCount: number;
-        burstDelay: number;
+        count: BulletCountConfig;
+        trajectory: BulletTrajectoryConfig;
+        hitEffects: HitEffectConfig[];
+        lifecycle: BulletLifecycleConfig;
+        visual: {
+            bulletPrefab: string;
+            hitEffect: string;
+            trailEffect?: string;
+            muzzleFlash: string;
+        };
     };
     visualConfig: {
         weaponSprites: {
-            [size: string]: string;
+            [shapeId: string]: string;
         };
-        muzzleFlash: string;
         fireSound: string;
     };
+    upgradeConfig: {
+        maxLevel: number;
+        levels: {
+            [level: string]: {
+                cost: number;
+            };
+        };
+    };
+    // 运行时计算的实际数值(应用技能加成后)
+    runtimeStats?: {
+        finalDamage: number;
+        finalCritDamage: number;
+        critChance: number;
+    };
     inGameCostConfig?: InGameCostConfig;
 }
 

+ 1 - 2
assets/scripts/FourUI/MainSystem/MainUIControlller.ts

@@ -113,9 +113,8 @@ export class MainUIController extends Component {
     
     // 检查金币是否足够
     if (currentMoney < upgradeCost) {
-      console.log(`[MainUIController] 金币不足,需要${upgradeCost},当前${currentMoney}`);
       EventBus.getInstance().emit(GameEvents.SHOW_TOAST, {
-        message: `金币不足,需要${upgradeCost}金币`,
+        message: `钞票不足,需要${upgradeCost}钞票`,
         duration: 2.0
       });
       return;

+ 86 - 2
assets/scripts/FourUI/UpgradeSystem/UpgradeAni.ts

@@ -1,4 +1,4 @@
-import { _decorator, Component, Node, tween, Vec3 } from 'cc';
+import { _decorator, Component, Node, Tween, tween,Vec3 } from 'cc';
 
 const { ccclass, property } = _decorator;
 
@@ -75,8 +75,19 @@ export class UpgradeAni extends Component {
      */
     public playWeaponUpgradeAnimation(weaponIconNode: Node): Promise<void> {
         return new Promise((resolve) => {
+            if (!weaponIconNode) {
+                resolve();
+                return;
+            }
+            
+            // 停止之前的动画,防止快速点击时动画冲突
+            Tween.stopAllByTarget(weaponIconNode);
+            
+            // 重置到原始缩放状态,确保动画从正确的状态开始
+            weaponIconNode.setScale(Vec3.ONE);
+            
             // 保存原始缩放值
-            const originalScale = weaponIconNode.scale.clone();
+            const originalScale = Vec3.ONE.clone();
             
             // 创建缩放动画:放大到1.5倍再立即缩小回原始大小
             const scaleAnimation = tween(weaponIconNode)
@@ -91,4 +102,77 @@ export class UpgradeAni extends Component {
             scaleAnimation.call(() => resolve()).start();
         });
     }
+    
+    /**
+     * UP图标提示动画
+     * 播放缩小到0.7倍再放大到原来大小的重复动画,直到条件不满足为止
+     * @param upIconNode UP图标节点
+     * @param checkCondition 检查条件的回调函数,返回false时停止动画
+     */
+    public playUpIconAnimation(upIconNode: Node, checkCondition?: () => boolean): void {
+        if (!upIconNode) return;
+        
+        // 停止之前的动画
+        Tween.stopAllByTarget(upIconNode);
+        
+        // 保存原始缩放值
+        const originalScale = upIconNode.scale.clone();
+        
+        // 如果没有提供检查条件,使用默认的无限重复动画
+        if (!checkCondition) {
+            const scaleAnimation = tween(upIconNode)
+                .to(0.5, { scale: new Vec3(originalScale.x * 0.7, originalScale.y * 0.7, originalScale.z) }, {
+                    easing: 'sineInOut'
+                })
+                .to(0.5, { scale: originalScale }, {
+                    easing: 'sineInOut'
+                })
+                .repeatForever(); // 无限重复
+            
+            scaleAnimation.start();
+            return;
+        }
+        
+        // 带条件检查的动画循环
+        const playAnimationCycle = () => {
+            // 在每次动画循环开始前检查条件
+            if (!checkCondition()) {
+                // 条件不满足,停止动画并恢复原始缩放
+                this.stopUpIconAnimation(upIconNode);
+                return;
+            }
+            
+            // 创建一次完整的缩放动画循环
+            const scaleAnimation = tween(upIconNode)
+                .to(0.5, { scale: new Vec3(originalScale.x * 0.7, originalScale.y * 0.7, originalScale.z) }, {
+                    easing: 'sineInOut'
+                })
+                .to(0.5, { scale: originalScale }, {
+                    easing: 'sineInOut'
+                })
+                .call(() => {
+                    // 一次循环完成后,递归调用下一次循环
+                    playAnimationCycle();
+                });
+            
+            scaleAnimation.start();
+        };
+        
+        // 开始第一次动画循环
+        playAnimationCycle();
+    }
+    
+    /**
+     * 停止UP图标动画
+     * @param upIconNode UP图标节点
+     */
+    public stopUpIconAnimation(upIconNode: Node): void {
+        if (!upIconNode) return;
+        
+        // 停止所有动画
+        Tween.stopAllByTarget(upIconNode);
+        
+        // 恢复原始缩放
+        upIconNode.setScale(Vec3.ONE);
+    }
 }

+ 37 - 2
assets/scripts/FourUI/UpgradeSystem/UpgradeController.ts

@@ -58,6 +58,7 @@ export class UpgradeController extends Component {
     @property(Label) panelCurrentDamage: Label = null;         // Canvas/UpgradeUI/UpgradePanel/NumberBack/CurrentDamage
     @property(Label) panelCostLabel: Label = null;             // Canvas/UpgradeUI/UpgradePanel/UpgradeBtn/CostLabel
     @property(Button) panelUpgradeBtn: Button = null;          // Canvas/UpgradeUI/UpgradePanel/UpgradeBtn
+    @property(Node) upIcon: Node = null;                       // Canvas/UpgradeUI/UpgradePanel/UpgradeBtn/UP图标
     
     // 武器节点预制体
     @property(Prefab) lockedWeaponPrefab: Prefab = null;    // Lock.prefab
@@ -502,6 +503,11 @@ export class UpgradeController extends Component {
      * 关闭升级面板
      */
     private async closeUpgradePanel() {
+        // 停止UP图标动画
+        if (this.upIcon && this.upgradeAni) {
+            this.upgradeAni.stopUpIconAnimation(this.upIcon);
+        }
+        
         // 使用动画隐藏升级面板
         if (this.upgradeAni) {
             await this.upgradeAni.hidePanel();
@@ -580,6 +586,35 @@ export class UpgradeController extends Component {
             }
         }
         
+        // 检查钞票是否足够,控制UP图标显示和动画
+        const playerMoney = this.saveDataManager.getMoney();
+        if (weaponData.level < maxLevel && playerMoney >= upgradeCost) {
+            // 钞票足够,显示UP图标并播放动画
+            if (this.upIcon) {
+                this.upIcon.active = true;
+                if (this.upgradeAni) {
+                    // 传入检查条件:钞票是否足够且未达到最大等级
+                    const checkCondition = () => {
+                        const currentMoney = this.saveDataManager.getMoney();
+                        const currentWeaponData = this.saveDataManager.getWeapon(this.currentSelectedWeapon);
+                        const currentUpgradeCost = this.saveDataManager.getWeaponUpgradeCost(this.currentSelectedWeapon);
+                        return currentWeaponData && 
+                               currentWeaponData.level < maxLevel && 
+                               currentMoney >= currentUpgradeCost;
+                    };
+                    this.upgradeAni.playUpIconAnimation(this.upIcon, checkCondition);
+                }
+            }
+        } else {
+            // 钞票不足或已满级,隐藏UP图标并停止动画
+            if (this.upIcon) {
+                this.upIcon.active = false;
+                if (this.upgradeAni) {
+                    this.upgradeAni.stopUpIconAnimation(this.upIcon);
+                }
+            }
+        }
+        
         // 升级按钮始终保持可点击状态,通过Toast显示各种提示
         console.log(`[UpgradeController] 升级按钮保持可交互状态`);
     }
@@ -700,8 +735,8 @@ export class UpgradeController extends Component {
         console.log(`[UpgradeController] 条件判断: ${playerMoney} < ${cost} = ${playerMoney < cost}`);
         
         if (playerMoney < cost) {
-            console.log(`[UpgradeController] 金币不足 - 需要: ${cost}, 当前: ${playerMoney}`);
-            EventBus.getInstance().emit(GameEvents.SHOW_TOAST, { message: `金币不足,需要${cost}金币`, duration: 2.0 });
+            console.log(`[UpgradeController] 钞票不足 - 需要: ${cost}, 当前: ${playerMoney}`);
+            EventBus.getInstance().emit(GameEvents.SHOW_TOAST, { message: `钞票不足,需要${cost}钞票`, duration: 2.0 });
             return;
         }
         

+ 13 - 32
assets/scripts/LevelSystem/GameManager.ts

@@ -248,11 +248,12 @@ export class GameManager extends Component {
         
         // 游戏状态管理已迁移到 InGameManager
         // UI显示控制已迁移到 UIStateManager
+        // 奖励处理已迁移到 GameEnd.ts
         
         // 注意:不在这里切换到MAIN_MENU状态,保持IN_GAME状态
         // 只有用户点击成功界面的按钮时才切换到主界面
 
-        // 执行游戏成功逻辑
+        // 执行游戏成功逻辑(仅记录时间和关卡完成)
         this.onGameSuccess();
     }
 
@@ -264,11 +265,12 @@ export class GameManager extends Component {
         
         // 游戏状态管理已迁移到 InGameManager
         // UI显示控制已迁移到 UIStateManager
+        // 奖励处理已迁移到 GameEnd.ts
         
         // 注意:不在这里切换到MAIN_MENU状态,保持IN_GAME状态
         // 只有用户点击失败界面的按钮时才切换到主界面
 
-        // 执行游戏失败逻辑
+        // 执行游戏失败逻辑(仅记录时间和统计)
         this.onGameDefeat().catch(error => {
             console.error('[GameManager] 游戏失败处理出错:', error);
         });
@@ -430,25 +432,15 @@ export class GameManager extends Component {
     private async onGameDefeat() {
         this.gameEndTime = Date.now();
         
-        // 记录游戏失败到存档
+        // 记录游戏失败到存档(不包含奖励处理,奖励已迁移到GameEnd.ts)
         if (this.saveDataManager) {
             const currentLevel = this.saveDataManager.getCurrentLevel();
             this.saveDataManager.failLevel(currentLevel);
             
-            // 计算波数完成比例并给予失败奖励
-            // 波数信息已迁移到 InGameManager,通过InGameManager获取
-            const inGameManager = this.getInGameManager();
-            const totalWaves = inGameManager?.levelWaves?.length || 1;
-            const completedWaves = inGameManager ? Math.max(0, inGameManager.getCurrentWave() - 1) : 0;
-            const waveCompletionRatio = completedWaves / totalWaves;
-            
-            console.log(`[GameManager] 游戏失败 - 完成波数: ${completedWaves}/${totalWaves}, 比例: ${(waveCompletionRatio * 100).toFixed(1)}%`);
-            
-            // 给予基于波数比例的失败奖励
-            await this.saveDataManager.giveFailureRewards(currentLevel, completedWaves, totalWaves);
-            
             // 更新统计数据
             this.saveDataManager.updateStatistic('totalTimePlayed', this.getGameDuration());
+            
+            console.log(`[GameManager] 游戏失败记录已保存,关卡: ${currentLevel}`);
         }
     }
 
@@ -456,20 +448,10 @@ export class GameManager extends Component {
     private async onGameSuccess() {
         this.gameEndTime = Date.now();
         
-        await this.giveReward();
+        // 奖励处理已迁移到GameEnd.ts,这里只处理关卡完成记录
         this.onLevelComplete();
     }
 
-    // === 给予奖励 ===
-    private async giveReward() {
-        if (!this.saveDataManager) return;
-
-        const currentLevel = this.saveDataManager.getCurrentLevel();
-        
-        // 给予JSON配置中的基础奖励
-        await this.saveDataManager.giveCompletionRewards(currentLevel);
-    }
-
     // === 处理关卡完成 ===
     private onLevelComplete() {
         if (!this.saveDataManager) return;
@@ -477,12 +459,14 @@ export class GameManager extends Component {
         const currentLevel = this.saveDataManager.getCurrentLevel();
         const gameTime = this.getGameDuration();
         
-        // 记录关卡完成到存档
+        // 记录关卡完成到存档(不包含奖励处理,奖励已迁移到GameEnd.ts)
         this.saveDataManager.completeLevel(currentLevel, 0, gameTime);
         
         // 更新统计数据
         this.saveDataManager.updateStatistic('totalTimePlayed', gameTime);
         this.saveDataManager.updateStatistic('totalEnemiesDefeated', this.totalEnemiesSpawned);
+        
+        console.log(`[GameManager] 关卡完成记录已保存,关卡: ${currentLevel}, 游戏时长: ${gameTime}秒`);
     }
     
     // clearPreviousGameRecord方法已被移除,其功能已整合到onMainMenuClick中
@@ -576,11 +560,8 @@ export class GameManager extends Component {
         const mainUIController = this.mainUI.getComponent('MainUIController' as any);
         
         if (mainUIController) {
-            // 游戏状态检查已迁移到 InGameManager,这里使用默认的奖励动画返回
-            if (typeof (mainUIController as any).onReturnToMainUIWithReward === 'function') {
-                console.log('[GameManager] 调用带奖励动画的返回主界面方法');
-                (mainUIController as any).onReturnToMainUIWithReward();
-            } else if (typeof (mainUIController as any).onReturnToMainUI === 'function') {
+            // 奖励动画已迁移到GameEnd.ts中,这里只需要普通返回主界面
+            if (typeof (mainUIController as any).onReturnToMainUI === 'function') {
                 console.log('[GameManager] 调用普通返回主界面方法');
                 (mainUIController as any).onReturnToMainUI();
             } else {

+ 1 - 0
assets/scripts/LevelSystem/IN_game.ts

@@ -436,6 +436,7 @@ export class InGameManager extends Component {
             // 如果波次未结束但能量已满,也需要触发技能选择
             console.log('[InGameManager] 能量已满但波次未结束,触发技能选择');
             this.currentState = GameState.BLOCK_SELECTION;
+            this.onEnergyFull();
         }
     }
 

+ 26 - 7
assets/scripts/LevelSystem/SaveDataManager.ts

@@ -552,8 +552,8 @@ export class SaveDataManager {
             this.playerData.statistics.highestLevel = levelId;
         }
         
-        // 计算并给予奖励
-        this.giveCompletionRewards(levelId);
+        // 奖励处理已迁移到GameEnd.ts中
+        // this.giveCompletionRewards(levelId);
         
         this.savePlayerData();
     }
@@ -644,7 +644,7 @@ export class SaveDataManager {
         }
         
         if (this.playerData.money < amount) {
-            console.log(`[SaveDataManager] spendMoney失败 - 局外金币不足: 需要=${amount}, 当前=${this.playerData.money}`);
+            console.log(`[SaveDataManager] spendMoney失败 - 钞票不足: 需要=${amount}, 当前=${this.playerData.money}`);
             return false;
         }
         
@@ -957,6 +957,8 @@ export class SaveDataManager {
      */
     public async getLevelRewardsFromConfig(levelId: number): Promise<{money: number, diamonds: number} | null> {
         try {
+            console.log(`[SaveDataManager] 开始获取关卡${levelId}的奖励配置`);
+            
             // 由于不支持动态导入,改为直接导入
             const configManager = LevelConfigManager.getInstance();
             
@@ -968,6 +970,7 @@ export class SaveDataManager {
             const levelConfig = await configManager.getLevelConfig(levelId);
             if (levelConfig && levelConfig.levelSettings && (levelConfig.levelSettings as any).rewards) {
                 const rewards = (levelConfig.levelSettings as any).rewards;
+                console.log(`[SaveDataManager] 从LevelConfigManager获取到奖励:`, rewards);
                 return {
                     money: rewards.coins || 0,
                     diamonds: rewards.diamonds || 0
@@ -976,6 +979,7 @@ export class SaveDataManager {
             
             // 如果JSON中没有奖励配置,尝试直接从JSON文件读取
              const jsonPath = `data/levels/Level${levelId}`;
+             console.log(`[SaveDataManager] 尝试从JSON文件加载: ${jsonPath}`);
              return new Promise((resolve) => {
                  resources.load(jsonPath, JsonAsset, (err, asset) => {
                      if (err || !asset) {
@@ -985,19 +989,26 @@ export class SaveDataManager {
                      }
                      
                      const jsonData = asset.json;
+                     console.log(`[SaveDataManager] JSON数据:`, jsonData);
+                     
                      // 修复字段名称映射:直接从JSON根级别读取coinReward和diamondReward
                      if (jsonData && (jsonData.coinReward !== undefined || jsonData.diamondReward !== undefined)) {
-                         resolve({
+                         const rewardData = {
                              money: jsonData.coinReward || 0,
                              diamonds: jsonData.diamondReward || 0
-                         });
+                         };
+                         console.log(`[SaveDataManager] 从JSON根级别获取到奖励:`, rewardData);
+                         resolve(rewardData);
                      } else if (jsonData && jsonData.rewards) {
                          // 兼容嵌套在rewards对象中的情况
-                         resolve({
+                         const rewardData = {
                              money: jsonData.rewards.coins || jsonData.rewards.coinReward || 0,
                              diamonds: jsonData.rewards.diamonds || jsonData.rewards.diamondReward || 0
-                         });
+                         };
+                         console.log(`[SaveDataManager] 从JSON.rewards获取到奖励:`, rewardData);
+                         resolve(rewardData);
                      } else {
+                         console.warn(`[SaveDataManager] JSON中未找到奖励配置`);
                          resolve(null);
                      }
                  });
@@ -1013,8 +1024,11 @@ export class SaveDataManager {
      * 给予关卡完成奖励(从JSON配置获取)
      */
     public async giveCompletionRewards(levelId: number): Promise<void> {
+        console.log(`[SaveDataManager] 开始给予关卡${levelId}完成奖励`);
+        
         // 尝试从JSON配置文件获取奖励数据
         const configRewards = await this.getLevelRewardsFromConfig(levelId);
+        console.log(`[SaveDataManager] 获取到的配置奖励:`, configRewards);
         
         let actualCoins = 0;
         let actualDiamonds = 0;
@@ -1024,11 +1038,15 @@ export class SaveDataManager {
             if (configRewards.money > 0) {
                 this.addMoney(configRewards.money, `level_${levelId}_complete`);
                 actualCoins = configRewards.money;
+                console.log(`[SaveDataManager] 添加金币: ${configRewards.money}`);
             }
             if (configRewards.diamonds > 0) {
                 this.addDiamonds(configRewards.diamonds, `level_${levelId}_complete`);
                 actualDiamonds = configRewards.diamonds;
+                console.log(`[SaveDataManager] 添加钻石: ${configRewards.diamonds}`);
             }
+        } else {
+            console.warn(`[SaveDataManager] 未获取到配置奖励,使用默认值0`);
         }
         
         // 特殊关卡额外奖励(里程碑奖励)
@@ -1038,6 +1056,7 @@ export class SaveDataManager {
         
         // 存储最近的奖励记录
         this.lastRewards = {money: actualCoins, diamonds: actualDiamonds};
+        console.log(`[SaveDataManager] 最终奖励记录:`, this.lastRewards);
     }
     
     /**

+ 17 - 2
assets/scripts/LevelSystem/StartGame.ts

@@ -76,7 +76,22 @@ export class StartGame extends Component {
             console.log('[StartGame] GameBlockSelection确认回调设置完成');
         }
         
-        // 3. 生成初始方块
+        // 3. 设置BlockManager的gameStarted标志(在生成方块之前设置)
+        console.log('[StartGame] 设置BlockManager的gameStarted标志');
+        const blockManagerNode = find('Canvas/GameLevelUI/BlockController');
+        if (blockManagerNode) {
+            const blockManager = blockManagerNode.getComponent('BlockManager');
+            if (blockManager) {
+                (blockManager as any).onGameStart();
+                console.log('[StartGame] BlockManager gameStarted标志已设置');
+            } else {
+                console.warn('[StartGame] BlockManager组件未找到');
+            }
+        } else {
+            console.warn('[StartGame] BlockManager节点未找到');
+        }
+        
+        // 4. 生成初始方块(在gameStarted标志设置之后)
         console.log('[StartGame] 生成初始方块');
         const blockSelectionUINode = find('Canvas/GameLevelUI/BlockSelectionUI');
         if (blockSelectionUINode) {
@@ -91,7 +106,7 @@ export class StartGame extends Component {
             console.warn('[StartGame] diban容器节点未找到');
         }
         
-        // 4. 播放diban上移动画和镜头下移动画
+        // 5. 播放diban上移动画和镜头下移动画
         console.log('[StartGame] 播放diban上移动画和镜头下移动画');
         const cameraNode = find('Canvas/Camera');
         if (cameraNode) {

+ 6 - 2
assets/scripts/LevelSystem/UIStateManager.ts

@@ -78,9 +78,11 @@ export class UIStateManager extends BaseSingleton {
             return;
         }
         
+        // 奖励处理已迁移到GameEnd.ts,这里只处理EndLabel显示和面板显示
         if (this.endPanel) {
-            this.endPanel.active = true;
             this.setEndLabelText('SUCCESS');
+            this.endPanel.active = true;
+            console.log('[UIStateManager] 显示GameEnd面板 - SUCCESS');
         }
     }
 
@@ -91,9 +93,11 @@ export class UIStateManager extends BaseSingleton {
             return;
         }
         
+        // 奖励处理已迁移到GameEnd.ts,这里只处理EndLabel显示和面板显示
         if (this.endPanel) {
-            this.endPanel.active = true;
             this.setEndLabelText('DEFEAT');
+            this.endPanel.active = true;
+            console.log('[UIStateManager] 显示GameEnd面板 - DEFEAT');
         }
     }
 

+ 82 - 0
test_enemy_health_fix.js

@@ -0,0 +1,82 @@
+/**
+ * 测试敌人血量显示修复效果
+ * 用于验证僵尸短时间被多个植物击中后血量显示是否正常
+ */
+
+const { find } = require('cc');
+
+// 测试函数:模拟多次快速伤害
+function testMultipleDamage() {
+    console.log('=== 开始测试敌人血量显示修复 ===');
+    
+    // 查找敌人控制器
+    const enemyControllerNode = find('Canvas/GameLevelUI/EnemyController');
+    if (!enemyControllerNode) {
+        console.error('未找到EnemyController节点');
+        return;
+    }
+    
+    const enemyController = enemyControllerNode.getComponent('EnemyController');
+    if (!enemyController) {
+        console.error('未找到EnemyController组件');
+        return;
+    }
+    
+    // 获取当前活跃的敌人
+    const activeEnemies = enemyController.getActiveEnemies();
+    if (activeEnemies.length === 0) {
+        console.log('当前没有活跃的敌人,请先生成敌人');
+        return;
+    }
+    
+    const testEnemy = activeEnemies[0];
+    const enemyInstance = testEnemy.getComponent('EnemyInstance');
+    
+    if (!enemyInstance) {
+        console.error('敌人节点没有EnemyInstance组件');
+        return;
+    }
+    
+    console.log(`测试目标: ${enemyInstance.getEnemyName()}`);
+    console.log(`初始血量: ${enemyInstance.health}/${enemyInstance.maxHealth}`);
+    
+    // 模拟短时间内多次受伤
+    const damages = [5, 3, 7, 2, 4]; // 不同的伤害值
+    let delay = 0;
+    
+    damages.forEach((damage, index) => {
+        setTimeout(() => {
+            if (enemyInstance && enemyInstance.node && enemyInstance.node.isValid) {
+                console.log(`第${index + 1}次伤害: ${damage}`);
+                enemyInstance.takeDamage(damage, Math.random() > 0.8); // 20%暴击率
+                console.log(`当前血量: ${enemyInstance.health}/${enemyInstance.maxHealth}`);
+            }
+        }, delay);
+        delay += 100; // 每次间隔100毫秒
+    });
+    
+    // 5秒后检查最终状态
+    setTimeout(() => {
+        if (enemyInstance && enemyInstance.node && enemyInstance.node.isValid) {
+            console.log('=== 测试完成 ===');
+            console.log(`最终血量: ${enemyInstance.health}/${enemyInstance.maxHealth}`);
+            console.log('如果血量显示正常且动画流畅,说明修复成功');
+        } else {
+            console.log('敌人已被消灭');
+        }
+    }, 5000);
+}
+
+// 导出测试函数
+if (typeof module !== 'undefined' && module.exports) {
+    module.exports = { testMultipleDamage };
+}
+
+// 如果在浏览器环境中直接运行
+if (typeof window !== 'undefined') {
+    window.testEnemyHealthFix = testMultipleDamage;
+    console.log('测试函数已挂载到 window.testEnemyHealthFix()');
+}
+
+console.log('敌人血量显示修复测试脚本已加载');
+console.log('使用方法: 在游戏中生成敌人后,调用 testMultipleDamage() 函数进行测试');

+ 143 - 0
僵尸血量显示异常修复说明.md

@@ -0,0 +1,143 @@
+# 僵尸血量显示异常修复说明
+
+## 问题描述
+僵尸短时间被多个植物击中后,血量显示出现异常,包括:
+- 血条动画错乱
+- 血量数值显示不准确
+- 多个动画同时播放导致视觉混乱
+
+## 问题原因分析
+
+### 1. 动画冲突问题
+在 `HPBarAnimation.ts` 中,当敌人短时间内多次受伤时:
+- 每次调用 `playDamageAnimation()` 都会创建新的 tween 动画
+- 多个动画同时运行,互相干扰
+- 没有机制停止之前的动画,导致动画状态混乱
+
+### 2. 血量计算精度问题
+在 `EnemyInstance.ts` 中:
+- 直接进行血量减法运算,可能产生浮点数精度问题
+- 没有对血量边界进行严格检查
+- 血量显示时没有进行整数化处理
+
+### 3. 并发调用保护不足
+- 缺少对重复伤害的有效过滤
+- 没有对无效伤害值的检查
+
+## 修复方案
+
+### 1. 修复动画冲突 (HPBarAnimation.ts)
+
+#### 添加动画状态管理
+```typescript
+private currentTween: any = null; // 当前正在运行的动画
+```
+
+#### 修改 playDamageAnimation 方法
+```typescript
+private playDamageAnimation(newProgress: number) {
+    // 停止当前正在运行的动画,避免动画冲突
+    if (this.currentTween) {
+        this.currentTween.stop();
+        this.currentTween = null;
+    }
+    
+    // ... 其他代码
+    
+    // 保存动画引用
+    this.currentTween = tween({ progress: originalProgress })
+        // ... 动画配置
+        .start();
+}
+```
+
+#### 添加资源清理
+```typescript
+onDestroy() {
+    // 清理正在运行的动画,防止内存泄漏
+    if (this.currentTween) {
+        this.currentTween.stop();
+        this.currentTween = null;
+    }
+}
+```
+
+### 2. 修复血量计算 (EnemyInstance.ts)
+
+#### 改进 takeDamage 方法
+```typescript
+takeDamage(damage: number, isCritical: boolean = false) {
+    // 确保伤害值为正数
+    if (damage <= 0) {
+        console.warn(`[EnemyInstance] 无效的伤害值: ${damage}`);
+        return;
+    }
+    
+    // 计算新的血量,确保不会低于0
+    const newHealth = Math.max(0, this.health - damage);
+    const actualDamage = this.health - newHealth;
+    this.health = newHealth;
+    
+    // ... 其他代码
+}
+```
+
+#### 改进 updateHealthDisplay 方法
+```typescript
+updateHealthDisplay() {
+    // 确保血量值在有效范围内
+    this.health = Math.max(0, Math.min(this.maxHealth, this.health));
+    
+    const healthProgress = this.maxHealth > 0 ? this.health / this.maxHealth : 0;
+    
+    // 显示整数血量值
+    label.string = Math.ceil(this.health).toString();
+}
+```
+
+## 修复效果
+
+### 1. 动画表现改善
+- ✅ 消除了多个血条动画同时播放的问题
+- ✅ 血条动画现在会正确地停止之前的动画再开始新的
+- ✅ 动画状态管理更加可靠
+
+### 2. 血量显示准确性
+- ✅ 血量计算更加精确,避免浮点数问题
+- ✅ 血量显示始终为整数
+- ✅ 血量值严格限制在有效范围内
+
+### 3. 性能优化
+- ✅ 避免了无效的动画创建
+- ✅ 正确清理动画资源,防止内存泄漏
+- ✅ 添加了输入验证,提高代码健壮性
+
+## 测试方法
+
+使用提供的测试脚本 `test_enemy_health_fix.js`:
+
+1. 在游戏中生成敌人
+2. 在控制台运行测试函数:
+   ```javascript
+   testMultipleDamage();
+   ```
+3. 观察敌人血量显示是否正常
+4. 检查血条动画是否流畅
+
+## 相关文件
+
+- `assets/scripts/Animations/HPBarAnimation.ts` - 血条动画组件
+- `assets/scripts/CombatSystem/EnemyInstance.ts` - 敌人实例组件
+- `test_enemy_health_fix.js` - 测试脚本
+
+## 注意事项
+
+1. 修复后需要重新编译项目
+2. 建议在不同类型的敌人上测试修复效果
+3. 如果仍有问题,请检查控制台日志获取更多调试信息
+
+## 版本信息
+
+- 修复日期: 2024年
+- 影响组件: HPBarAnimation, EnemyInstance
+- 兼容性: Cocos Creator 3.x

Неке датотеке нису приказане због велике количине промена