瀏覽代碼

法球僵尸拖尾

181404010226 1 月之前
父節點
當前提交
064615c312
共有 40 個文件被更改,包括 338 次插入5556 次删除
  1. 20 20
      assets/Scenes/GameLevel.scene
  2. 281 34
      assets/assets/Prefabs/EnemyProjectile.prefab
  3. 0 9
      assets/scripts/BulletControl.meta
  4. 0 145
      assets/scripts/BulletControl/BulletEngine.ts
  5. 0 9
      assets/scripts/BulletControl/BulletEngine.ts.meta
  6. 0 266
      assets/scripts/BulletControl/BulletTrailController.ts
  7. 0 9
      assets/scripts/BulletControl/BulletTrailController.ts.meta
  8. 0 116
      assets/scripts/BulletControl/BulletTypes.ts
  9. 0 9
      assets/scripts/BulletControl/BulletTypes.ts.meta
  10. 0 46
      assets/scripts/BulletControl/ConfigLoader.ts
  11. 0 9
      assets/scripts/BulletControl/ConfigLoader.ts.meta
  12. 0 40
      assets/scripts/BulletControl/PortableEventBus.ts
  13. 0 9
      assets/scripts/BulletControl/PortableEventBus.ts.meta
  14. 0 9
      assets/scripts/BulletControl/Prefabs.meta
  15. 0 218
      assets/scripts/BulletControl/Prefabs/Pellet.prefab
  16. 0 13
      assets/scripts/BulletControl/Prefabs/Pellet.prefab.meta
  17. 0 498
      assets/scripts/BulletControl/Prefabs/PelletContainer.prefab
  18. 0 13
      assets/scripts/BulletControl/Prefabs/PelletContainer.prefab.meta
  19. 0 581
      assets/scripts/BulletControl/Prefabs/WeaponBlock.prefab
  20. 0 13
      assets/scripts/BulletControl/Prefabs/WeaponBlock.prefab.meta
  21. 0 118
      assets/scripts/BulletControl/README.md
  22. 0 11
      assets/scripts/BulletControl/README.md.meta
  23. 0 9
      assets/scripts/BulletControl/effects.meta
  24. 0 67
      assets/scripts/BulletControl/effects/BulletCount.ts
  25. 0 9
      assets/scripts/BulletControl/effects/BulletCount.ts.meta
  26. 0 94
      assets/scripts/BulletControl/effects/BulletHitEffect.ts
  27. 0 9
      assets/scripts/BulletControl/effects/BulletHitEffect.ts.meta
  28. 0 82
      assets/scripts/BulletControl/effects/BulletLifecycle.ts
  29. 0 9
      assets/scripts/BulletControl/effects/BulletLifecycle.ts.meta
  30. 0 204
      assets/scripts/BulletControl/effects/BulletTrajectory.ts
  31. 0 9
      assets/scripts/BulletControl/effects/BulletTrajectory.ts.meta
  32. 0 9
      assets/scripts/BulletControl/exported_data.meta
  33. 0 1196
      assets/scripts/BulletControl/exported_data/weapon_config_manager.py
  34. 0 12
      assets/scripts/BulletControl/exported_data/weapon_config_manager.py.meta
  35. 0 1617
      assets/scripts/BulletControl/exported_data/weapons.json
  36. 0 11
      assets/scripts/BulletControl/exported_data/weapons.json.meta
  37. 二進制
      assets/scripts/BulletControl/exported_data/方块武器配置表.xlsx
  38. 0 12
      assets/scripts/BulletControl/exported_data/方块武器配置表.xlsx.meta
  39. 9 4
      assets/scripts/CombatSystem/EnemyWeapon/EnemyProjectileInstance.ts
  40. 28 8
      assets/scripts/CombatSystem/MenuSystem/SoundController.ts

+ 20 - 20
assets/Scenes/GameLevel.scene

@@ -173,7 +173,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": 375,
+      "x": 374.99999999999994,
       "y": 667,
       "z": 0
     },
@@ -828,7 +828,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": 0,
+      "x": 5.684341886080802e-14,
       "y": 0,
       "z": 0
     },
@@ -13627,7 +13627,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": -325,
+      "x": -324.99999999999994,
       "y": 577.3409999999999,
       "z": 0
     },
@@ -13685,7 +13685,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": 375,
+      "x": 374.99999999999994,
       "y": 667,
       "z": 0
     },
@@ -13815,7 +13815,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 750,
+      "width": 749.9999999999999,
       "height": 1334
     },
     "_anchorPoint": {
@@ -22513,7 +22513,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": -357.07890259464057,
+      "x": -357.0789025946405,
       "y": 0,
       "z": 0
     },
@@ -26531,7 +26531,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": 1.884,
+      "x": 1.8839999999999997,
       "y": 621.088,
       "z": 0
     },
@@ -27901,7 +27901,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 735,
+      "width": 734.9999999999999,
       "height": 130
     },
     "_anchorPoint": {
@@ -28155,7 +28155,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 750,
+      "width": 749.9999999999999,
       "height": 1334
     },
     "_anchorPoint": {
@@ -29124,7 +29124,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 750,
+      "width": 749.9999999999999,
       "height": 1334
     },
     "_anchorPoint": {
@@ -35486,7 +35486,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": -14,
+      "x": -13.999999999999943,
       "y": -6,
       "z": 0
     },
@@ -37224,7 +37224,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 750,
+      "width": 749.9999999999999,
       "height": 1334
     },
     "_anchorPoint": {
@@ -37276,7 +37276,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 750,
+      "width": 749.9999999999999,
       "height": 1334
     },
     "_anchorPoint": {
@@ -37402,7 +37402,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": -225,
+      "x": -224.99999999999997,
       "y": 138.5,
       "z": 0
     },
@@ -39256,7 +39256,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": 225,
+      "x": 224.99999999999997,
       "y": 138.5,
       "z": 0
     },
@@ -40474,7 +40474,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 750,
+      "width": 749.9999999999999,
       "height": 469
     },
     "_anchorPoint": {
@@ -41153,7 +41153,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": 304.275125,
+      "x": 304.2751249999999,
       "y": 175.274,
       "z": 0
     },
@@ -45021,7 +45021,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 703.55025,
+      "width": 703.5502499999998,
       "height": 428
     },
     "_anchorPoint": {
@@ -45200,7 +45200,7 @@
     "_prefab": null,
     "_lpos": {
       "__type__": "cc.Vec3",
-      "x": 294.5,
+      "x": 294.49999999999994,
       "y": 486.34000000000003,
       "z": 0
     },
@@ -48798,7 +48798,7 @@
     "__prefab": null,
     "_contentSize": {
       "__type__": "cc.Size",
-      "width": 750,
+      "width": 749.9999999999999,
       "height": 1334
     },
     "_anchorPoint": {

+ 281 - 34
assets/assets/Prefabs/EnemyProjectile.prefab

@@ -17,27 +17,34 @@
     "_objFlags": 0,
     "__editorExtras__": {},
     "_parent": null,
-    "_children": [],
+    "_children": [
+      {
+        "__id__": 2
+      },
+      {
+        "__id__": 8
+      }
+    ],
     "_active": true,
     "_components": [
       {
-        "__id__": 2
+        "__id__": 14
       },
       {
-        "__id__": 4
+        "__id__": 16
       },
       {
-        "__id__": 6
+        "__id__": 18
       },
       {
-        "__id__": 8
+        "__id__": 20
       },
       {
-        "__id__": 10
+        "__id__": 22
       }
     ],
     "_prefab": {
-      "__id__": 12
+      "__id__": 24
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -68,17 +75,195 @@
     },
     "_id": ""
   },
+  {
+    "__type__": "cc.Node",
+    "_name": "TrailEffect",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "_parent": {
+      "__id__": 1
+    },
+    "_children": [],
+    "_active": true,
+    "_components": [
+      {
+        "__id__": 3
+      },
+      {
+        "__id__": 5
+      }
+    ],
+    "_prefab": {
+      "__id__": 7
+    },
+    "_lpos": {
+      "__type__": "cc.Vec3",
+      "x": 0,
+      "y": 0,
+      "z": 0
+    },
+    "_lrot": {
+      "__type__": "cc.Quat",
+      "x": 0,
+      "y": 0,
+      "z": 0,
+      "w": 1
+    },
+    "_lscale": {
+      "__type__": "cc.Vec3",
+      "x": 1,
+      "y": 1,
+      "z": 1
+    },
+    "_mobility": 0,
+    "_layer": 1073741824,
+    "_euler": {
+      "__type__": "cc.Vec3",
+      "x": 0,
+      "y": 0,
+      "z": 0
+    },
+    "_id": ""
+  },
   {
     "__type__": "cc.UITransform",
     "_name": "",
     "_objFlags": 0,
     "__editorExtras__": {},
     "node": {
+      "__id__": 2
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 4
+    },
+    "_contentSize": {
+      "__type__": "cc.Size",
+      "width": 100,
+      "height": 100
+    },
+    "_anchorPoint": {
+      "__type__": "cc.Vec2",
+      "x": 0.5,
+      "y": 0.5
+    },
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "f6zuTN8PFLsoWY8j6+UkZP"
+  },
+  {
+    "__type__": "cc.MotionStreak",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 2
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 6
+    },
+    "_customMaterial": null,
+    "_srcBlendFactor": 2,
+    "_dstBlendFactor": 4,
+    "_color": {
+      "__type__": "cc.Color",
+      "r": 203,
+      "g": 69,
+      "b": 240,
+      "a": 255
+    },
+    "_preview": false,
+    "_fadeTime": 1,
+    "_minSeg": 0.05,
+    "_stroke": 30,
+    "_texture": {
+      "__uuid__": "093b45c8-80d9-49a8-ac5c-bcdbaed2c6bf@6c48a",
+      "__expectedType__": "cc.Texture2D"
+    },
+    "_fastMode": true,
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "d16lFstv9O0JUzrb0my1Fx"
+  },
+  {
+    "__type__": "cc.PrefabInfo",
+    "root": {
+      "__id__": 1
+    },
+    "asset": {
+      "__id__": 0
+    },
+    "fileId": "9dC2z4FwRP371kNZzilYOH",
+    "instance": null,
+    "targetOverrides": null,
+    "nestedPrefabInstanceRoots": null
+  },
+  {
+    "__type__": "cc.Node",
+    "_name": "Image",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "_parent": {
       "__id__": 1
     },
+    "_children": [],
+    "_active": true,
+    "_components": [
+      {
+        "__id__": 9
+      },
+      {
+        "__id__": 11
+      }
+    ],
+    "_prefab": {
+      "__id__": 13
+    },
+    "_lpos": {
+      "__type__": "cc.Vec3",
+      "x": 0,
+      "y": 0,
+      "z": 0
+    },
+    "_lrot": {
+      "__type__": "cc.Quat",
+      "x": 0,
+      "y": 0,
+      "z": 0,
+      "w": 1
+    },
+    "_lscale": {
+      "__type__": "cc.Vec3",
+      "x": 1,
+      "y": 1,
+      "z": 1
+    },
+    "_mobility": 0,
+    "_layer": 1073741824,
+    "_euler": {
+      "__type__": "cc.Vec3",
+      "x": 0,
+      "y": 0,
+      "z": 0
+    },
+    "_id": ""
+  },
+  {
+    "__type__": "cc.UITransform",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 8
+    },
     "_enabled": true,
     "__prefab": {
-      "__id__": 3
+      "__id__": 10
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -94,7 +279,7 @@
   },
   {
     "__type__": "cc.CompPrefabInfo",
-    "fileId": "38OH/Vv/9BMp00Z3SfNrDh"
+    "fileId": "31m1PJTGtAJZqCBJqIvIiM"
   },
   {
     "__type__": "cc.Sprite",
@@ -102,11 +287,11 @@
     "_objFlags": 0,
     "__editorExtras__": {},
     "node": {
-      "__id__": 1
+      "__id__": 8
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 5
+      "__id__": 12
     },
     "_customMaterial": null,
     "_srcBlendFactor": 2,
@@ -139,7 +324,87 @@
   },
   {
     "__type__": "cc.CompPrefabInfo",
-    "fileId": "adz2ftoNNNPK/g/rKe2JTW"
+    "fileId": "3d0mc0/UhNbJ09pWJ7YBnt"
+  },
+  {
+    "__type__": "cc.PrefabInfo",
+    "root": {
+      "__id__": 1
+    },
+    "asset": {
+      "__id__": 0
+    },
+    "fileId": "05BINtu5hFrqSrkSYOt0Ce",
+    "instance": null,
+    "targetOverrides": null,
+    "nestedPrefabInstanceRoots": null
+  },
+  {
+    "__type__": "cc.UITransform",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 1
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 15
+    },
+    "_contentSize": {
+      "__type__": "cc.Size",
+      "width": 70,
+      "height": 13
+    },
+    "_anchorPoint": {
+      "__type__": "cc.Vec2",
+      "x": 0.5,
+      "y": 0.5
+    },
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "38OH/Vv/9BMp00Z3SfNrDh"
+  },
+  {
+    "__type__": "5b808AziiJHu72FiuokUp6U",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 1
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 17
+    },
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "66nN16AptJSq4tkTU77b9G"
+  },
+  {
+    "__type__": "45e52RfjzZAfatmnUHw8J5a",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 1
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 19
+    },
+    "motionStreak": {
+      "__id__": 5
+    },
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "6e6vqJGIFGwLAxbNyHi5ui"
   },
   {
     "__type__": "cc.RigidBody2D",
@@ -151,7 +416,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 7
+      "__id__": 21
     },
     "enabledContactListener": true,
     "bullet": true,
@@ -173,7 +438,7 @@
   },
   {
     "__type__": "cc.CompPrefabInfo",
-    "fileId": "178GmZaPlHibpDQAik2N2H"
+    "fileId": "faDB+bzq9Hp5kwFP+zPyQA"
   },
   {
     "__type__": "cc.CircleCollider2D",
@@ -185,7 +450,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 9
+      "__id__": 23
     },
     "tag": 7,
     "_group": 128,
@@ -203,25 +468,7 @@
   },
   {
     "__type__": "cc.CompPrefabInfo",
-    "fileId": "c3zvMu9rFLjLnFtmXONd1E"
-  },
-  {
-    "__type__": "5b808AziiJHu72FiuokUp6U",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 11
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "66nN16AptJSq4tkTU77b9G"
+    "fileId": "d0nqXbEGBMiroIVna/rof/"
   },
   {
     "__type__": "cc.PrefabInfo",

+ 0 - 9
assets/scripts/BulletControl.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "1.2.0",
-  "importer": "directory",
-  "imported": true,
-  "uuid": "942cd7ec-4011-40a6-bdb4-4e549145846c",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 145
assets/scripts/BulletControl/BulletEngine.ts

@@ -1,145 +0,0 @@
-import { _decorator, Component, Node, Vec3, Prefab, instantiate, Collider2D, Contact2DType, IPhysics2DContact, math } from 'cc';
-import { BulletInitData, WeaponConfig, BulletLifecycleConfig } from './BulletTypes';
-import { ConfigLoader } from './ConfigLoader';
-import { BulletCount } from './effects/BulletCount';
-import { BulletTrajectory as BCBulletTrajectory } from './effects/BulletTrajectory';
-import { BulletHitEffect as BCBulletHitEffect } from './effects/BulletHitEffect';
-import { BulletLifecycle as BCBulletLifecycle } from './effects/BulletLifecycle';
-import { BulletTrailController } from './BulletTrailController';
-
-const { ccclass } = _decorator;
-
-/** 可移植的统一子弹控制器 */
-@ccclass('WeaponBulletController')
-export class WeaponBulletController extends Component {
-  private trajectory: BCBulletTrajectory | null = null;
-  private hitEffect: BCBulletHitEffect | null = null;
-  private lifecycle: BCBulletLifecycle | null = null;
-  private trail: BulletTrailController | null = null;
-
-  private weaponConfig: WeaponConfig | null = null;
-  private isInitialized = false;
-
-  // 图片“上为前进方向”的偏移修正(度)
-  private readonly _orientationOffsetByWeapon: Record<string, number> = {
-    sharp_carrot: -90,
-    hot_pepper: -90,
-    okra_missile: -90,
-    mace_club: -90
-  };
-
-  private getOrientationOffset(): number {
-    const id = this.weaponConfig?.id;
-    if (!id) return 0;
-    return this._orientationOffsetByWeapon[id] ?? 0;
-  }
-
-  public init(initData: BulletInitData) {
-    // 装配配置
-    this.weaponConfig = initData.weaponConfig ?? ConfigLoader.getWeaponConfig(initData.weaponId);
-    if (!this.weaponConfig) {
-      this.node.destroy();
-      return;
-    }
-
-    // 计算方向
-    const direction = (initData.direction?.clone() ?? new Vec3(1, 0, 0)).normalize();
-
-    // 弹道
-    this.trajectory = this.getComponent(BCBulletTrajectory) || this.addComponent(BCBulletTrajectory);
-    const trajCfg = { ...this.weaponConfig.bulletConfig.trajectory, speed: this.weaponConfig.stats.bulletSpeed };
-    this.trajectory.init(trajCfg, direction, initData.firePosition, initData.sourceBlock);
-
-    // 初始位置略微前移,避免贴边重叠
-    const spawnOffset = 30;
-    const pos = this.node.position.clone();
-    this.node.setPosition(pos.x + direction.x * spawnOffset, pos.y + direction.y * spawnOffset);
-
-    // 命中效果
-    this.hitEffect = this.getComponent(BCBulletHitEffect) || this.addComponent(BCBulletHitEffect);
-    this.hitEffect.init(this.weaponConfig.bulletConfig.hitEffects);
-
-    // 生命周期
-    this.lifecycle = this.getComponent(BCBulletLifecycle) || this.addComponent(BCBulletLifecycle);
-    const lc: BulletLifecycleConfig = { ...this.weaponConfig.bulletConfig.lifecycle };
-    if (lc.type === 'return_trip' && !lc.maxRange && this.weaponConfig.stats.range) {
-      lc.maxRange = this.weaponConfig.stats.range;
-    }
-    // 透传 pierce/ricochet 初始计数(若命中效果中有)
-    const pierceInit = this.weaponConfig.bulletConfig.hitEffects.find(e => e.type === 'pierce_damage')?.pierceCount ?? lc.penetration ?? 0;
-    const ricochetInit = this.weaponConfig.bulletConfig.hitEffects.find(e => e.type === 'ricochet_damage')?.ricochetCount ?? lc.ricochetCount ?? 0;
-    this.lifecycle.init(lc, initData.firePosition, pierceInit, ricochetInit);
-
-    // 拖尾(可选)
-    this.trail = this.getComponent(BulletTrailController) || this.addComponent(BulletTrailController);
-
-    // 旋转对齐外观
-    const deg = math.toDegree(Math.atan2(direction.y, direction.x)) + this.getOrientationOffset();
-    const shouldRotate = this.weaponConfig?.bulletConfig?.shouldRotate;
-    const pelletNode = this.node.getChildByName('Pellet');
-    if (shouldRotate === false && pelletNode) {
-      pelletNode.angle = deg;
-    } else {
-      this.node.angle = deg;
-    }
-
-    // 启用持续外观对齐/自旋(贴合两种模式)
-    if (this.trajectory) {
-      this.trajectory.setFacingOptions({
-        shouldRotate: shouldRotate,
-        offsetDeg: this.getOrientationOffset(),
-        targetChildName: pelletNode ? 'Pellet' : undefined,
-        spinSpeedDeg: 720
-      });
-    }
-
-    // 碰撞监听(若存在 Collider2D)
-    const collider = this.getComponent(Collider2D);
-    if (collider) {
-      collider.on(Contact2DType.BEGIN_CONTACT, this.onCollision, this);
-    }
-
-    this.isInitialized = true;
-  }
-
-  private onCollision(self: Collider2D, other: Collider2D, contact: IPhysics2DContact | null) {
-    if (!this.isInitialized || !this.hitEffect || !this.lifecycle) return;
-    const result = this.hitEffect.applyOnHit();
-    this.lifecycle.notifyHit(result);
-    if (this.lifecycle.shouldDestroyNow()) {
-      this.node.destroy();
-      return;
-    }
-  }
-}
-
-/**
- * 工具:批量创建子弹(Prefab 或 Node 模板)
- */
-export class BulletEngine {
-  static createBullets(initData: BulletInitData, bulletTemplate: Prefab | Node): Node[] {
-    // 获取配置
-    const config = initData.weaponConfig ?? ConfigLoader.getWeaponConfig(initData.weaponId);
-    if (!config) return [];
-
-    const dir = (initData.direction?.clone() ?? new Vec3(1, 0, 0)).normalize();
-    const spawns = BulletCount.calculateSpawns(config.bulletConfig.count, initData.firePosition, dir);
-    const nodes: Node[] = [];
-
-    for (const s of spawns) {
-      const node = instantiate(bulletTemplate) as Node;
-      const ctrl = node.getComponent(WeaponBulletController) || node.addComponent(WeaponBulletController);
-      // 处理延迟:先禁用,延时后启用
-      if (s.delayMs > 0) {
-        node.active = false;
-        ctrl.init({ ...initData, firePosition: s.position, direction: s.direction, weaponConfig: config });
-        nodes.push(node);
-        setTimeout(() => { if (node && node.isValid) node.active = true; }, s.delayMs);
-      } else {
-        ctrl.init({ ...initData, firePosition: s.position, direction: s.direction, weaponConfig: config });
-        nodes.push(node);
-      }
-    }
-    return nodes;
-  }
-}

+ 0 - 9
assets/scripts/BulletControl/BulletEngine.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "3d8b3a5b-e045-4697-9b5c-363427c10f50",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 266
assets/scripts/BulletControl/BulletTrailController.ts

@@ -1,266 +0,0 @@
-import { _decorator, Component, Node, MotionStreak, Color, Texture2D } from 'cc';
-const { ccclass, property } = _decorator;
-
-/**
- * 子弹拖尾控制器(可移植副本)
- * 负责管理子弹的拖尾效果
- */
-@ccclass('BulletTrailController')
-export class BulletTrailController extends Component {
-    
-    @property({
-        type: MotionStreak,
-        tooltip: '拖尾组件引用'
-    })
-    public motionStreak: MotionStreak | null = null;
-    private secondaryStreak: MotionStreak | null = null;
-    
-    // 拖尾配置
-    private trailConfig = {
-        fadeTime: 0.5,      // 拖尾消失时间
-        minSeg: 1,          // 最小分段
-        stroke: 30,         // 拖尾宽度
-        fastMode: false     // 快速模式
-    };
-    
-    // 预设颜色
-    private trailColors = {
-        default: new Color(255, 255, 0, 255),    // 黄色
-        white: new Color(255, 255, 255, 255),    // 白色
-        fire: new Color(255, 100, 0, 255),       // 橙红色
-        gold: new Color(255, 200, 80, 255),      // 金黄(更接近外黄效果)
-        ice: new Color(0, 200, 255, 255),        // 冰蓝色
-        poison: new Color(100, 255, 0, 255),     // 毒绿色
-        electric: new Color(200, 0, 255, 255)    // 紫色
-    };
-    
-    onLoad() {
-        this.initializeTrail();
-    }
-    
-    /**
-     * 初始化拖尾效果
-     */
-    private initializeTrail() {
-        if (!this.motionStreak) {
-            // 尝试从子节点中查找MotionStreak组件
-            this.motionStreak = this.getComponentInChildren(MotionStreak);
-            
-            if (!this.motionStreak) {
-                // 尝试从当前节点获取
-                this.motionStreak = this.getComponent(MotionStreak);
-            }
-        }
-        
-        if (this.motionStreak) {
-            this.applyTrailConfig();
-        }
-    }
-    
-    /**
-     * 应用拖尾配置
-     */
-    private applyTrailConfig() {
-        if (!this.motionStreak) return;
-        
-        this.motionStreak.fadeTime = this.trailConfig.fadeTime;
-        this.motionStreak.minSeg = this.trailConfig.minSeg;
-        this.motionStreak.stroke = this.trailConfig.stroke;
-        this.motionStreak.fastMode = this.trailConfig.fastMode;
-        // 保持当前颜色,不在通用配置中强制重置为白色,避免覆盖武器专属颜色
-    }
-    
-    /**
-     * 设置拖尾颜色
-     * @param colorType 颜色类型
-     */
-    public setTrailColor(colorType: 'default' | 'white' | 'fire' | 'ice' | 'poison' | 'electric') {
-        if (!this.motionStreak) return;
-        
-        const color = this.trailColors[colorType];
-        if (color) {
-            this.motionStreak.color = color;
-        }
-    }
-    
-    /**
-     * 设置自定义拖尾颜色
-     * @param color 颜色
-     */
-    public setCustomTrailColor(color: Color) {
-        if (!this.motionStreak) return;
-        
-        this.motionStreak.color = color;
-    }
-    
-    /**
-     * 设置拖尾宽度
-     * @param width 宽度
-     */
-    public setTrailWidth(width: number) {
-        if (!this.motionStreak) return;
-        
-        this.trailConfig.stroke = width;
-        this.motionStreak.stroke = width;
-    }
-    
-    /**
-     * 设置拖尾消失时间
-     * @param fadeTime 消失时间
-     */
-    public setTrailFadeTime(fadeTime: number) {
-        if (!this.motionStreak) return;
-        
-        this.trailConfig.fadeTime = fadeTime;
-        this.motionStreak.fadeTime = fadeTime;
-    }
-
-    /**
-     * 设置拖尾最小分段(越小越平滑,减少三角形感)
-     * @param minSeg 最小分段
-     */
-    public setTrailMinSeg(minSeg: number) {
-        if (!this.motionStreak) return;
-        this.trailConfig.minSeg = minSeg;
-        this.motionStreak.minSeg = minSeg;
-    }
-    
-    /**
-     * 设置拖尾纹理
-     * @param texture 纹理
-     */
-    public setTrailTexture(texture: Texture2D) {
-        if (!this.motionStreak) return;
-        
-        this.motionStreak.texture = texture;
-        if (this.secondaryStreak) {
-            this.secondaryStreak.texture = texture;
-        }
-    }
-
-    /**
-     * 设置快速模式(拖尾响应更灵敏)
-     */
-    public setFastMode(enabled: boolean) {
-        if (!this.motionStreak) return;
-        this.trailConfig.fastMode = enabled;
-        this.motionStreak.fastMode = enabled;
-    }
-
-    /**
-     * 预设:火焰喷射拖尾(用于秋葵导弹)
-     */
-    public applyPresetRocket() {
-        if (!this.motionStreak) return;
-        // 颜色:火焰橙红
-        this.setTrailColor('fire');
-        // 更粗的拖尾,形成喷焰柱效果
-        this.setTrailWidth(36);
-        // 更短的消失时间,拖尾更紧凑
-        this.setTrailFadeTime(0.14);
-        // 更小的分段让曲线更贴合,避免顶部太宽的三角形感
-        this.setTrailMinSeg(0.05);
-        // 启用快速模式,响应更快
-        this.setFastMode(true);
-
-        // 在下一帧再次强制颜色,防止其他初始化流程覆盖颜色(例如材质或默认值)
-        this.scheduleOnce(() => {
-            if (this.motionStreak) {
-                this.motionStreak.color = this.trailColors.fire;
-            }
-        }, 0);
-    }
-    
-    /**
-     * 启用拖尾效果
-     */
-    public enableTrail() {
-        if (this.motionStreak) {
-            this.motionStreak.enabled = true;
-        }
-    }
-    
-    /**
-     * 禁用拖尾效果
-     */
-    public disableTrail() {
-        if (this.motionStreak) {
-            this.motionStreak.enabled = false;
-        }
-    }
-    
-    /**
-     * 重置拖尾效果
-     */
-    public resetTrail() {
-        if (this.motionStreak) {
-            this.motionStreak.reset();
-        }
-        if (this.secondaryStreak) {
-            this.secondaryStreak.reset();
-        }
-    }
-
-    onDestroy() {
-        // 清理资源
-        this.motionStreak = null;
-        this.secondaryStreak = null;
-    }
-
-    /** 创建或获取次级拖尾(用于双层效果) */
-    private getOrCreateSecondaryStreak(): MotionStreak | null {
-        if (!this.motionStreak) return null;
-        if (this.secondaryStreak && this.secondaryStreak.isValid) return this.secondaryStreak;
-        try {
-            const hostNode = this.motionStreak.node;
-            // 为次级拖尾创建一个子节点,避免与主拖尾同节点可能产生的覆盖/冲突
-            const child = new Node('TrailEffectSecondary');
-            hostNode.addChild(child);
-            this.secondaryStreak = child.addComponent(MotionStreak);
-            // 复制基础纹理与参数
-            this.secondaryStreak.texture = this.motionStreak.texture;
-            this.secondaryStreak.minSeg = this.trailConfig.minSeg;
-            this.secondaryStreak.fadeTime = this.trailConfig.fadeTime;
-            this.secondaryStreak.stroke = this.trailConfig.stroke;
-            this.secondaryStreak.fastMode = this.trailConfig.fastMode;
-            return this.secondaryStreak;
-        } catch (e) {
-            console.warn('[BulletTrailController] 创建次级拖尾失败:', e);
-            return null;
-        }
-    }
-
-    /**
-     * 预设:双层喷焰(边缘橙红,中心白色)
-     */
-    public applyPresetRocketDualLayer() {
-        if (!this.motionStreak) return;
-        // 主拖尾:中心白色,稍窄
-        this.setTrailWidth(24);
-        this.setTrailFadeTime(0.5);
-        this.setTrailMinSeg(1);
-        this.setFastMode(false);
-        this.setTrailColor('white');
-
-        // 次级拖尾:边缘橙红,更宽
-        const secondary = this.getOrCreateSecondaryStreak();
-        if (secondary) {
-            secondary.stroke = 30;
-            secondary.fadeTime = 0.5;
-            secondary.minSeg = 1;
-            secondary.fastMode = false;
-            // 使用金黄色增强外层可见性
-            secondary.color = this.trailColors.gold;
-        }
-
-        // 保护色:下一帧再次写入
-        this.scheduleOnce(() => {
-            if (this.motionStreak && this.motionStreak.isValid) {
-                this.motionStreak.color = this.trailColors.white;
-            }
-            if (this.secondaryStreak && this.secondaryStreak.isValid) {
-                this.secondaryStreak.color = this.trailColors.gold;
-            }
-        }, 0);
-    }
-}

+ 0 - 9
assets/scripts/BulletControl/BulletTrailController.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "7b2c4f6e-5167-484f-87c0-4ee801b90af6",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 116
assets/scripts/BulletControl/BulletTypes.ts

@@ -1,116 +0,0 @@
-import { Node, Vec3 } from 'cc';
-
-// 统一的武器与子弹配置类型,便于跨项目复用
-
-export interface WeaponStats {
-  damage: number;
-  fireRate: number; // 每秒发射次数或类似概念
-  range: number;    // 最大射程(像素)
-  bulletSpeed: number; // 逻辑速度(像素/秒)
-}
-
-export interface VisualBulletConfig {
-  bulletImages?: string;           // 贴图资源路径
-  hitEffect?: string | boolean;    // 命中特效资源路径或开关
-  trailEffect?: boolean | string;  // 拖尾效果开关或资源路径
-  explosionEffect?: string;        // 爆炸特效资源路径
-  burnEffect?: string;             // 地面燃烧特效资源路径
-}
-
-export type BulletCountType = 'single' | 'spread' | 'burst';
-export interface BulletCountConfig {
-  type: BulletCountType;
-  amount: number;       // 单发数量或每次 burst 的数量
-  spreadAngle?: number; // 扇形扩散角度(度)
-  burstCount?: number;  // 连发次数
-  burstDelay?: number;  // 连发间隔(秒)
-}
-
-export type BulletTrajectoryType = 'straight' | 'arc' | 'guided';
-export interface BulletTrajectoryConfig {
-  type: BulletTrajectoryType;
-  speed: number;         // 初始速度(像素/秒),若与 stats.bulletSpeed 混用,以调用方为准
-  arcHeight?: number;    // 弧线高度(可选,具体实现可按需使用)
-  homingStrength?: number; // 追踪强度(0~1 建议范围)
-  homingDelay?: number;    // 追踪延迟(秒)
-  rotateSpeed?: number;    // 渐近旋转速度(0~1,越大转向越快)
-}
-
-export type HitEffectType = 'normal_damage' | 'pierce_damage' | 'ricochet_damage' | 'explosion' | 'ground_burn';
-export interface HitEffectConfig {
-  type: HitEffectType;
-  priority: number;   // 效果叠加优先级(数值越小越先应用)
-  damage?: number;    // 基础伤害
-  pierceCount?: number;     // 穿透次数
-  ricochetCount?: number;   // 弹射次数
-  ricochetAngle?: number;   // 弹射角度(度)
-  radius?: number;          // 爆炸半径
-  delay?: number;           // 延迟(秒)
-  duration?: number;        // 地面燃烧持续时间(秒)
-  tickInterval?: number;    // 地面燃烧伤害间隔(秒)
-}
-
-export type LifecycleType = 'hit_destroy' | 'range_limit' | 'ricochet_counter' | 'ground_impact' | 'return_trip';
-export interface BulletLifecycleConfig {
-  type: LifecycleType;
-  maxLifetime?: number; // 最大存活时间(秒)
-  penetration?: number; // 最大穿透计数
-  ricochetCount?: number; // 最大弹射计数
-  returnToOrigin?: boolean; // 是否回到发射源
-  returnDelay?: number;     // 返回延迟(秒)
-  maxRange?: number;        // 最大射程(像素)
-}
-
-export interface BulletConfig {
-  count: BulletCountConfig;
-  trajectory: BulletTrajectoryConfig;
-  hitEffects: HitEffectConfig[];
-  lifecycle: BulletLifecycleConfig;
-  visual?: VisualBulletConfig;
-  shouldRotate?: boolean; // 是否启用持续自旋转或外观朝向
-}
-
-export interface UpgradeConfig { maxLevel: number; levels: Record<string, { cost?: number; damage?: number }>; }
-export interface InGameCostConfig { baseCost: number; shapeCosts: Record<string, number>; }
-
-export interface WeaponConfig {
-  id: string;
-  name: string;
-  type: string;
-  weight?: number;
-  unlockLevel?: number;
-  rarityDamageMultipliers?: number[];
-  stats: WeaponStats;
-  bulletConfig: BulletConfig;
-  visualConfig?: Record<string, any>;
-  upgradeConfig?: UpgradeConfig;
-  inGameCostConfig?: InGameCostConfig;
-}
-
-export interface BulletInitData {
-  weaponId: string;
-  firePosition: Vec3;
-  direction?: Vec3;
-  autoTarget?: boolean;
-  weaponConfig?: WeaponConfig;
-  sourceBlock?: Node | null; // 可选:用于回旋镖返回
-}
-
-export interface SpawnInfo {
-  position: Vec3;   // 发射位置(局部或世界,由调用者约定)
-  direction: Vec3;  // 归一化方向
-  delayMs: number;  // 延迟(毫秒)
-}
-
-export interface HitResult {
-  didHit: boolean;
-  damageApplied?: number;
-  ricochet?: boolean;
-  ricochetAngle?: number;
-  pierced?: boolean;
-  remainingPierce?: number;
-  explosion?: boolean;
-  explosionRadius?: number;
-  spawnBurn?: boolean;
-  burn?: { damagePerTick: number; duration: number; tickInterval: number } | null;
-}

+ 0 - 9
assets/scripts/BulletControl/BulletTypes.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "a594149f-fb7d-4034-af85-a57a734d42fb",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 46
assets/scripts/BulletControl/ConfigLoader.ts

@@ -1,46 +0,0 @@
-import { JsonAsset, resources } from 'cc';
-import { WeaponConfig } from './BulletTypes';
-
-/**
- * 轻量配置加载器:管理 weapons.json 数据
- * - 可通过 setWeaponsData 直接注入(推荐迁移时使用)
- * - 可从 resources 目录加载(路径示例:data/weapons)
- */
-export class ConfigLoader {
-  private static weaponsData: { weapons: WeaponConfig[] } | null = null;
-
-  public static setWeaponsData(data: { weapons: WeaponConfig[] } | null) {
-    this.weaponsData = data;
-  }
-
-  public static async loadWeaponsFromResources(path: string): Promise<void> {
-    return new Promise((resolve, reject) => {
-      resources.load(path, JsonAsset, (err, asset) => {
-        if (err) {
-          reject(err);
-          return;
-        }
-        try {
-          const json = asset.json as any;
-          if (!json || !json.weapons) {
-            throw new Error('Invalid weapons.json structure');
-          }
-          this.weaponsData = json as { weapons: WeaponConfig[] };
-          resolve();
-        } catch (e) {
-          reject(e);
-        }
-      });
-    });
-  }
-
-  public static getWeaponConfig(id: string): WeaponConfig | null {
-    if (!this.weaponsData || !this.weaponsData.weapons) return null;
-    const w = this.weaponsData.weapons.find(x => x.id === id);
-    return w ?? null;
-  }
-
-  public static clearCache() {
-    this.weaponsData = null;
-  }
-}

+ 0 - 9
assets/scripts/BulletControl/ConfigLoader.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "fe914341-6e26-42e4-932c-6adecf41b1ed",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 40
assets/scripts/BulletControl/PortableEventBus.ts

@@ -1,40 +0,0 @@
-type Listener = (...args: any[]) => void;
-
-/**
- * 轻量事件总线(可替换):默认本地发布订阅
- * - 迁移到有全局 EventBus 的项目时,可替换为项目总线
- */
-export class PortableEventBus {
-  private listeners: Map<string, Set<Listener>> = new Map();
-
-  on(event: string, listener: Listener) {
-    if (!this.listeners.has(event)) this.listeners.set(event, new Set());
-    this.listeners.get(event)!.add(listener);
-  }
-
-  once(event: string, listener: Listener) {
-    const wrap: Listener = (...args: any[]) => {
-      this.off(event, wrap);
-      listener(...args);
-    };
-    this.on(event, wrap);
-  }
-
-  off(event: string, listener: Listener) {
-    this.listeners.get(event)?.delete(listener);
-  }
-
-  emit(event: string, ...args: any[]) {
-    const set = this.listeners.get(event);
-    if (!set) return;
-    for (const l of Array.from(set)) {
-      try { l(...args); } catch { /* swallow */ }
-    }
-  }
-}
-
-export const BulletEvents = {
-  BULLET_CREATE_REQUEST: 'BULLET_CREATE_REQUEST',
-  BULLET_HIT: 'BULLET_HIT',
-  BULLET_DESTROY: 'BULLET_DESTROY'
-} as const;

+ 0 - 9
assets/scripts/BulletControl/PortableEventBus.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "97b1d11c-d3c3-46ee-88ff-0915792de34b",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 9
assets/scripts/BulletControl/Prefabs.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "1.2.0",
-  "importer": "directory",
-  "imported": true,
-  "uuid": "13ee0bc1-a54b-4a51-8abc-1c9d5682d52d",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 218
assets/scripts/BulletControl/Prefabs/Pellet.prefab

@@ -1,218 +0,0 @@
-[
-  {
-    "__type__": "cc.Prefab",
-    "_name": "Pellet",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_native": "",
-    "data": {
-      "__id__": 1
-    },
-    "optimizationPolicy": 0,
-    "persistent": false
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "Pellet",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": null,
-    "_children": [],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 2
-      },
-      {
-        "__id__": 4
-      },
-      {
-        "__id__": 6
-      },
-      {
-        "__id__": 8
-      }
-    ],
-    "_prefab": {
-      "__id__": 10
-    },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 0.5,
-      "y": 0.5,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 33554432,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 3
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 57,
-      "height": 57
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.5,
-      "y": 0.5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "01PFH40wdI/5w0W4w5nM/4"
-  },
-  {
-    "__type__": "cc.Sprite",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 5
-    },
-    "_customMaterial": null,
-    "_srcBlendFactor": 2,
-    "_dstBlendFactor": 4,
-    "_color": {
-      "__type__": "cc.Color",
-      "r": 255,
-      "g": 255,
-      "b": 255,
-      "a": 255
-    },
-    "_spriteFrame": null,
-    "_type": 0,
-    "_fillType": 0,
-    "_sizeMode": 1,
-    "_fillCenter": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_fillStart": 0,
-    "_fillRange": 0,
-    "_isTrimmedMode": true,
-    "_useGrayscale": false,
-    "_atlas": null,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "a58czs9GZLJp7XWpINUriv"
-  },
-  {
-    "__type__": "cc.RigidBody2D",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 7
-    },
-    "enabledContactListener": true,
-    "bullet": true,
-    "awakeOnLoad": true,
-    "_group": 8,
-    "_type": 2,
-    "_allowSleep": false,
-    "_gravityScale": 0,
-    "_linearDamping": 0,
-    "_angularDamping": 0,
-    "_linearVelocity": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_angularVelocity": 0,
-    "_fixedRotation": false,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "35W4266NlITpxotlJ97Tir"
-  },
-  {
-    "__type__": "cc.BoxCollider2D",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 9
-    },
-    "tag": 3,
-    "_group": 8,
-    "_density": 1,
-    "_sensor": false,
-    "_friction": 0.2,
-    "_restitution": 0,
-    "_offset": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_size": {
-      "__type__": "cc.Size",
-      "width": 57,
-      "height": 57
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "9bb08vp9FMf4YvjtykcC6W"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "ecZuuI1ixD85bubQwhroh8",
-    "instance": null,
-    "targetOverrides": null
-  }
-]

+ 0 - 13
assets/scripts/BulletControl/Prefabs/Pellet.prefab.meta

@@ -1,13 +0,0 @@
-{
-  "ver": "1.1.50",
-  "importer": "prefab",
-  "imported": true,
-  "uuid": "a1cefeb3-8eae-4781-b774-c7a90890d136",
-  "files": [
-    ".json"
-  ],
-  "subMetas": {},
-  "userData": {
-    "syncNodeName": "Pellet"
-  }
-}

+ 0 - 498
assets/scripts/BulletControl/Prefabs/PelletContainer.prefab

@@ -1,498 +0,0 @@
-[
-  {
-    "__type__": "cc.Prefab",
-    "_name": "PelletContainer",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_native": "",
-    "data": {
-      "__id__": 1
-    },
-    "optimizationPolicy": 0,
-    "persistent": false
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "PelletContainer",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": null,
-    "_children": [
-      {
-        "__id__": 2
-      },
-      {
-        "__id__": 8
-      }
-    ],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 14
-      },
-      {
-        "__id__": 16
-      },
-      {
-        "__id__": 18
-      },
-      {
-        "__id__": 20
-      },
-      {
-        "__id__": 22
-      }
-    ],
-    "_prefab": {
-      "__id__": 24
-    },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": 9.688,
-      "y": 0,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 1,
-      "y": 1,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 33554432,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "TrailEffect",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": {
-      "__id__": 1
-    },
-    "_children": [],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 3
-      },
-      {
-        "__id__": 5
-      }
-    ],
-    "_prefab": {
-      "__id__": 7
-    },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 1,
-      "y": 1,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 33554432,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 2
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 4
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 100,
-      "height": 100
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.5,
-      "y": 0.5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "TrailEffectUITransform001"
-  },
-  {
-    "__type__": "cc.MotionStreak",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 2
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 6
-    },
-    "_customMaterial": null,
-    "_srcBlendFactor": 2,
-    "_dstBlendFactor": 4,
-    "_color": {
-      "__type__": "cc.Color",
-      "r": 255,
-      "g": 255,
-      "b": 255,
-      "a": 255
-    },
-    "_preview": false,
-    "_fadeTime": 0.5,
-    "_minSeg": 1.1,
-    "_stroke": 30,
-    "_texture": {
-      "__uuid__": "87ea3ba6-4cd5-4189-8d1d-9d069ea009a8@6c48a",
-      "__expectedType__": "cc.Texture2D"
-    },
-    "_fastMode": false,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "TrailEffectMotionStreak001"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "TrailEffectPrefabInfo001",
-    "instance": null,
-    "targetOverrides": null,
-    "nestedPrefabInstanceRoots": null
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "Pellet",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": {
-      "__id__": 1
-    },
-    "_children": [],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 9
-      },
-      {
-        "__id__": 11
-      }
-    ],
-    "_prefab": {
-      "__id__": 13
-    },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 1,
-      "y": 1,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 33554432,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 8
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 10
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 57,
-      "height": 57
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.5,
-      "y": 0.5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "4cFfiGSRRNBJ6HQnvWhKN6"
-  },
-  {
-    "__type__": "cc.Sprite",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 8
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 12
-    },
-    "_customMaterial": null,
-    "_srcBlendFactor": 2,
-    "_dstBlendFactor": 4,
-    "_color": {
-      "__type__": "cc.Color",
-      "r": 255,
-      "g": 255,
-      "b": 255,
-      "a": 255
-    },
-    "_spriteFrame": null,
-    "_type": 0,
-    "_fillType": 0,
-    "_sizeMode": 1,
-    "_fillCenter": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_fillStart": 0,
-    "_fillRange": 0,
-    "_isTrimmedMode": true,
-    "_useGrayscale": false,
-    "_atlas": null,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "5eXYZPbTFA2p6UgLI/X6fq"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "b1luMUWcFJmqi7I1A/cZXE",
-    "instance": null,
-    "targetOverrides": null,
-    "nestedPrefabInstanceRoots": null
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 15
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 57,
-      "height": 57
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.5,
-      "y": 0.5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "01PFH40wdI/5w0W4w5nM/4"
-  },
-  {
-    "__type__": "cc.RigidBody2D",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 17
-    },
-    "enabledContactListener": true,
-    "bullet": true,
-    "awakeOnLoad": true,
-    "_group": 8,
-    "_type": 2,
-    "_allowSleep": false,
-    "_gravityScale": 0,
-    "_linearDamping": 0,
-    "_angularDamping": 0,
-    "_linearVelocity": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_angularVelocity": 0,
-    "_fixedRotation": false,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "37zBafaaVBPKJGfhjp2CA5"
-  },
-  {
-    "__type__": "cc.BoxCollider2D",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 19
-    },
-    "tag": 3,
-    "_group": 8,
-    "_density": 1,
-    "_sensor": false,
-    "_friction": 0.2,
-    "_restitution": 0,
-    "_offset": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_size": {
-      "__type__": "cc.Size",
-      "width": 57,
-      "height": 57
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "f6qdCY1HlJnrlAGAQlEG80"
-  },
-  {
-    "__type__": "45e52RfjzZAfatmnUHw8J5a",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 21
-    },
-    "motionStreak": {
-      "__id__": 5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "8ey0IBbt9BvZLCcxeeDHqf"
-  },
-  {
-    "__type__": "cc.CircleCollider2D",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 23
-    },
-    "tag": 6,
-    "_group": 64,
-    "_density": 1,
-    "_sensor": true,
-    "_friction": 0.2,
-    "_restitution": 0,
-    "_offset": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_radius": 250,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "e9fBJ0DcZD168AdMkLad3S"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "ecZuuI1ixD85bubQwhroh8",
-    "instance": null,
-    "targetOverrides": null
-  }
-]

+ 0 - 13
assets/scripts/BulletControl/Prefabs/PelletContainer.prefab.meta

@@ -1,13 +0,0 @@
-{
-  "ver": "1.1.50",
-  "importer": "prefab",
-  "imported": true,
-  "uuid": "cea89076-e1dc-410e-9914-af36cb9d9142",
-  "files": [
-    ".json"
-  ],
-  "subMetas": {},
-  "userData": {
-    "syncNodeName": "PelletContainer"
-  }
-}

+ 0 - 581
assets/scripts/BulletControl/Prefabs/WeaponBlock.prefab

@@ -1,581 +0,0 @@
-[
-  {
-    "__type__": "cc.Prefab",
-    "_name": "WeaponBlock",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_native": "",
-    "data": {
-      "__id__": 1
-    },
-    "optimizationPolicy": 0,
-    "persistent": false
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "WeaponBlock",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": null,
-    "_children": [
-      {
-        "__id__": 2
-      }
-    ],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 16
-      },
-      {
-        "__id__": 18
-      },
-      {
-        "__id__": 20
-      },
-      {
-        "__id__": 22
-      }
-    ],
-    "_prefab": {
-      "__id__": 24
-    },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": -6.766,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 1,
-      "y": 1,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 33554432,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "B1",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": {
-      "__id__": 1
-    },
-    "_children": [
-      {
-        "__id__": 3
-      },
-      {
-        "__id__": 7
-      }
-    ],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 13
-      }
-    ],
-    "_prefab": {
-      "__id__": 15
-    },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 27.912,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 1,
-      "y": 1,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 33554432,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "(0,-1)",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": {
-      "__id__": 2
-    },
-    "_children": [],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 4
-      }
-    ],
-    "_prefab": {
-      "__id__": 6
-    },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": -58.18700000000001,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 1,
-      "y": 1,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 33554432,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 3
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 5
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 48,
-      "height": 48
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.5,
-      "y": 0.5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "4dSMlZahVBhrcqZ2ROix38"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "42mxrhs0pLRbM+S3cu+N+T",
-    "instance": null,
-    "targetOverrides": null,
-    "nestedPrefabInstanceRoots": null
-  },
-  {
-    "__type__": "cc.Node",
-    "_name": "Weapon",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "_parent": {
-      "__id__": 2
-    },
-    "_children": [],
-    "_active": true,
-    "_components": [
-      {
-        "__id__": 8
-      },
-      {
-        "__id__": 10
-      }
-    ],
-    "_prefab": {
-      "__id__": 12
-    },
-    "_lpos": {
-      "__type__": "cc.Vec3",
-      "x": -0.15300000000002,
-      "y": -31.72199999999998,
-      "z": 0
-    },
-    "_lrot": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    },
-    "_lscale": {
-      "__type__": "cc.Vec3",
-      "x": 0.5,
-      "y": 0.5,
-      "z": 1
-    },
-    "_mobility": 0,
-    "_layer": 33554432,
-    "_euler": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 7
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 9
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 69,
-      "height": 118
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.5,
-      "y": 0.5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "a3IgBCthdAMY/kVfrVwrwt"
-  },
-  {
-    "__type__": "cc.Sprite",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 7
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 11
-    },
-    "_customMaterial": null,
-    "_srcBlendFactor": 2,
-    "_dstBlendFactor": 4,
-    "_color": {
-      "__type__": "cc.Color",
-      "r": 255,
-      "g": 255,
-      "b": 255,
-      "a": 255
-    },
-    "_spriteFrame": {
-      "__uuid__": "7efa8afb-730c-43f8-9d0a-5cbf71082a54@f9941",
-      "__expectedType__": "cc.SpriteFrame"
-    },
-    "_type": 0,
-    "_fillType": 0,
-    "_sizeMode": 1,
-    "_fillCenter": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_fillStart": 0,
-    "_fillRange": 0,
-    "_isTrimmedMode": true,
-    "_useGrayscale": false,
-    "_atlas": null,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "8cVih+ArJFb5TbjLHK7rfX"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "a1dnHq16ZOQpQqmkZ1gqhV",
-    "instance": null,
-    "targetOverrides": null,
-    "nestedPrefabInstanceRoots": null
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 2
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 14
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 48,
-      "height": 48
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.5,
-      "y": 0.5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "cftPjiq2REVK8UlOhstbyx"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "baMKTeOjFH54UCx2rLWtK1",
-    "instance": null,
-    "targetOverrides": null,
-    "nestedPrefabInstanceRoots": null
-  },
-  {
-    "__type__": "cc.UITransform",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 17
-    },
-    "_contentSize": {
-      "__type__": "cc.Size",
-      "width": 52,
-      "height": 104
-    },
-    "_anchorPoint": {
-      "__type__": "cc.Vec2",
-      "x": 0.5,
-      "y": 0.5
-    },
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "5fkTSH67BMPbz3A0MS4GUP"
-  },
-  {
-    "__type__": "cc.Sprite",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 19
-    },
-    "_customMaterial": null,
-    "_srcBlendFactor": 2,
-    "_dstBlendFactor": 4,
-    "_color": {
-      "__type__": "cc.Color",
-      "r": 255,
-      "g": 255,
-      "b": 255,
-      "a": 255
-    },
-    "_spriteFrame": {
-      "__uuid__": "7c566ffd-17e6-4932-ad91-4eabe6503c16@f9941",
-      "__expectedType__": "cc.SpriteFrame"
-    },
-    "_type": 0,
-    "_fillType": 0,
-    "_sizeMode": 0,
-    "_fillCenter": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_fillStart": 0,
-    "_fillRange": 0,
-    "_isTrimmedMode": true,
-    "_useGrayscale": false,
-    "_atlas": null,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "24yeuTcLpMw4xJySGZd5jK"
-  },
-  {
-    "__type__": "cc.PolygonCollider2D",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 21
-    },
-    "tag": 2,
-    "_group": 4,
-    "_density": 1,
-    "_sensor": false,
-    "_friction": 0.2,
-    "_restitution": 1,
-    "_offset": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_points": [
-      {
-        "__type__": "cc.Vec2",
-        "x": -19,
-        "y": 52
-      },
-      {
-        "__type__": "cc.Vec2",
-        "x": -26,
-        "y": 45
-      },
-      {
-        "__type__": "cc.Vec2",
-        "x": -26,
-        "y": -45
-      },
-      {
-        "__type__": "cc.Vec2",
-        "x": -19,
-        "y": -52
-      },
-      {
-        "__type__": "cc.Vec2",
-        "x": 19,
-        "y": -52
-      },
-      {
-        "__type__": "cc.Vec2",
-        "x": 26,
-        "y": -45
-      },
-      {
-        "__type__": "cc.Vec2",
-        "x": 26,
-        "y": 45
-      },
-      {
-        "__type__": "cc.Vec2",
-        "x": 19,
-        "y": 52
-      }
-    ],
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "6cijxE1kJCRb8YwnU6o0Xq"
-  },
-  {
-    "__type__": "cc.RigidBody2D",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 23
-    },
-    "enabledContactListener": true,
-    "bullet": false,
-    "awakeOnLoad": true,
-    "_group": 4,
-    "_type": 1,
-    "_allowSleep": true,
-    "_gravityScale": 0,
-    "_linearDamping": 0,
-    "_angularDamping": 0,
-    "_linearVelocity": {
-      "__type__": "cc.Vec2",
-      "x": 0,
-      "y": 0
-    },
-    "_angularVelocity": 0,
-    "_fixedRotation": false,
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "992/Rw09lBQIO6kfz8uFhm"
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 1
-    },
-    "asset": {
-      "__id__": 0
-    },
-    "fileId": "2fgmWEQyhIGr02gKDR/YNr",
-    "instance": null,
-    "targetOverrides": null
-  }
-]

+ 0 - 13
assets/scripts/BulletControl/Prefabs/WeaponBlock.prefab.meta

@@ -1,13 +0,0 @@
-{
-  "ver": "1.1.50",
-  "importer": "prefab",
-  "imported": true,
-  "uuid": "502d3332-20dc-469d-ab76-bbb5df23db3d",
-  "files": [
-    ".json"
-  ],
-  "subMetas": {},
-  "userData": {
-    "syncNodeName": "WeaponBlock"
-  }
-}

+ 0 - 118
assets/scripts/BulletControl/README.md

@@ -1,118 +0,0 @@
-# BulletControl 可移植模块
-
-本目录收纳并抽象了原项目的子弹相关逻辑(不修改原脚本),包括类型定义、弹道、命中效果、生命周期、拖尾控制器,以及一个统一的子弹控制器与批量创建方法,方便迁移到其他 Cocos Creator 项目中复用。
-
-## 目录结构
-
-- `BulletTypes.ts`:统一的类型与配置接口(`WeaponConfig`、`BulletConfig` 等)。
-- `ConfigLoader.ts`:轻量配置加载器(直接注入或从 `resources` 读取 `weapons.json`)。
-- `effects/BulletCount.ts`:子弹生成信息计算(单发、散射、连发),输出位置、方向、延迟。
-- `effects/BulletTrajectory.ts`:弹道控制(`straight`、`arc`、`guided`)。
-- `effects/BulletHitEffect.ts`:命中效果整合(伤害、穿透、弹射、爆炸、燃烧)。
-- `effects/BulletLifecycle.ts`:生命周期控制(存活时间、射程、穿透、弹射、回程)。
-- `PortableEventBus.ts`:可选的本地事件总线(`on/once/off/emit`)。
-- `BulletTrailController.ts`:拖尾效果控制器与预设。
-- `BulletEngine.ts`:统一子弹控制器 `WeaponBulletController` 与批量创建 `BulletEngine.createBullets`。
-
-## 快速接入
-
-1) 复制目录:将 `assets/scripts/BulletControl` 整体拷贝到目标项目(保持相对路径结构)。
-2) 准备配置:
-   - 直接注入:`ConfigLoader.setWeaponsData(loadedJson)`。
-   - 或从资源:将 `weapons.json` 放入 `resources/data/weapons.json`,再调用 `await ConfigLoader.loadWeaponsFromResources('data/weapons')`。
-3) 运行时创建子弹:使用 `BulletEngine.createBullets(initData, bulletPrefabOrNode)` 批量生成。
-
-示例:
-
-```ts
-import { Prefab, Vec3 } from 'cc';
-import { BulletEngine } from './BulletEngine';
-import { ConfigLoader } from './ConfigLoader';
-
-// 预先加载/注入武器配置
-ConfigLoader.setWeaponsData(myWeaponsJson);
-
-const bullets = BulletEngine.createBullets({
-  weaponId: 'sharp_carrot',
-  firePosition: new Vec3(100, 200, 0),
-  direction: new Vec3(1, 0, 0),
-  sourceBlock: someBlockNode
-}, myBulletPrefab /* Prefab | Node */);
-
-// 将 bullets 添加到场景或发射节点下
-bullets.forEach(n => myShootRoot.addChild(n));
-```
-
-## 接口要点(`BulletTypes.ts`)
-
-- `WeaponConfig`:武器整体配置(`id`、`stats`、`bulletConfig`、升级与价格等)。
-- `WeaponStats`:基础数值(伤害、速度、射程、冷却、暴击等)。
-- `BulletConfig`:子弹外观与行为(`visual`、`count`、`trajectory`、`hitEffects`、`lifecycle`、`shouldRotate`)。
-- `BulletInitData`:运行时生成所需(`weaponId|weaponConfig`、`firePosition`、`direction`、`sourceBlock`)。
-
-## 生成与弹道(`effects`)
-
-- 生成模式(`BulletCount`):
-  - `single`:单发。
-  - `scatter`:散射(角度范围内平均分布)。
-  - `burst`:连发(支持每发延迟)。
-- 弹道模式(`BulletTrajectory`):
-  - `straight`:直线,按 `speed` 前进。
-  - `arc`:弧线(采用方向渐近旋转,无重力)。
-  - `guided`:追踪(延迟后按目标方向渐近旋转,`rotateSpeed` 影响转向快慢)。
-  - 可通过 `setTarget(nodeOrPosition)` 设置/更新目标。
-
-## 命中与生命周期
-
-- 命中效果(`BulletHitEffect`):
-  - 伤害、穿透(`pierce_damage`)、弹射(`ricochet_damage`)、爆炸、燃烧等。
-  - `applyOnHit()` 返回命中结果,供生命周期消费计数与销毁判断。
-- 生命周期(`BulletLifecycle`):
-  - 依据存活时间、射程、穿透/弹射剩余、回程逻辑决定是否销毁或折返。
-  - `notifyHit(result)` 消费命中次数;`shouldDestroyNow()` 判断立即销毁;`shouldReturnNow()` 判断进入回程。
-
-## 统一控制器与批量创建(`BulletEngine.ts`)
-
-- `WeaponBulletController`:每颗子弹的统一控制器,负责:
-  - 初始化弹道、命中效果、生命周期与拖尾。
-  - 自动对齐朝向;若 `bulletConfig.shouldRotate === false`,仅旋转子节点 `Pellet`。
-  - 监听 `Collider2D` 的 `BEGIN_CONTACT` 并触发命中与销毁判定。
-- `BulletEngine.createBullets(initData, prefabOrNode)`:按 `BulletCount` 生成信息批量创建子弹(支持延迟激活)。
-
-## 与原项目的差异与兼容
-
-- 本模块为纯逻辑/渲染整合,不直接耦合敌人/伤害系统(在 `applyOnHit()` 返回后由生命周期判断)。
-- 方向修正:针对部分武器贴图(上为前进方向)做了度数修正:`sharp_carrot/hot_pepper/okra_missile/mace_club` 使用 `-90°`。
-- 可选事件总线:`PortableEventBus` 提供最小替换方案(迁移时可替换为项目自带总线)。
-
-## 配置与数据迁移
-
-- 已将以下文件可单独提取:
-  - `assets/data/weapons.json`
-  - `assets/data/excel/方块武器配置/方块武器配置表.xlsx`
-  - `assets/data/excel/weapon_config_manager.py`
-- 推荐在目标项目创建 `assets/exported_data/`,将上述文件复制到该目录,便于独立维护与回溯。
-- 运行时通常只需 `weapons.json`;Excel 与 Python 管理器用于离线生成/校验配置。
-
-## 常见用法片段
-
-- 散射:在配置中设定 `count.mode = 'scatter'` 与 `spreadAngle`;代码不需要特殊处理,`BulletEngine` 将自动分配方向。
-- 连发:设定 `count.mode = 'burst'` 与 `burstDelayMs`;`createBullets` 会按延迟激活。
-- 追踪:将弹道设为 `guided`,在运行时调用 `trajectory.setTarget(targetNode)` 更新目标。
-- 回程/回旋镖:生命周期 `type = 'return_trip'`;命中或距离耗尽后折返。
-
-## 集成注意事项
-
-- Prefab 模板需包含或允许添加:`Collider2D`(若要参与碰撞)、`WeaponBulletController`(将自动添加所需组件)。
-- 旋转策略:若外观应由子节点控制,将 `bulletConfig.shouldRotate` 设为 `false` 并在 Prefab 内使用 `Pellet` 命名的子节点。
-- 物理层:确保 `Collider2D` 所在分组/掩码与敌人/地形匹配,避免误碰撞或不触发。
-- `deltaTime` 驱动:`BulletTrajectory`、`BulletLifecycle` 需要在组件 `update()` 中被调用(已集成于控制器)。
-
-## 迁移步骤简表
-
-1) 复制 `BulletControl` 目录到目标项目的 `assets/scripts/`。
-2) 复制 `assets/exported_data` 中的数据文件(或自行定义位置)。
-3) 使用 `ConfigLoader` 注入或加载 `weapons.json`。
-4) 在你的发射逻辑中,调用 `BulletEngine.createBullets(...)` 创建并挂载到场景。
-
-如需扩展新的弹道/效果/生命周期模式,参考现有类的接口进行实现,并在 `WeaponConfig` 中添加相应配置即可。

+ 0 - 11
assets/scripts/BulletControl/README.md.meta

@@ -1,11 +0,0 @@
-{
-  "ver": "1.0.1",
-  "importer": "text",
-  "imported": true,
-  "uuid": "c1c80a60-88ce-4ca8-85ae-18f668fb0262",
-  "files": [
-    ".json"
-  ],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 9
assets/scripts/BulletControl/effects.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "1.2.0",
-  "importer": "directory",
-  "imported": true,
-  "uuid": "2aabd9b6-edd3-4c0a-9734-58efe947272b",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 67
assets/scripts/BulletControl/effects/BulletCount.ts

@@ -1,67 +0,0 @@
-import { Vec3 } from 'cc';
-import { BulletCountConfig, SpawnInfo } from '../BulletTypes';
-
-/**
- * BulletCount - 计算子弹生成信息(位置、方向、延迟)
- * 仅负责基于数量与扩散/连发逻辑生成 spawn 列表,不包含技能加成。
- */
-export class BulletCount {
-  /**
-   * 计算发射队列
-   */
-  static calculateSpawns(countCfg: BulletCountConfig, firePos: Vec3, direction: Vec3): SpawnInfo[] {
-    const dir = direction?.clone() ?? new Vec3(1, 0, 0);
-    if (dir.length() === 0) dir.set(1, 0, 0);
-    dir.normalize();
-
-    const result: SpawnInfo[] = [];
-    const type = countCfg?.type ?? 'single';
-    const amount = Math.max(1, countCfg?.amount ?? 1);
-    const spreadAngle = countCfg?.spreadAngle ?? 0;
-    const burstCount = Math.max(1, countCfg?.burstCount ?? 1);
-    const burstDelay = Math.max(0, countCfg?.burstDelay ?? 0); // 秒
-
-    if (type === 'single') {
-      for (let i = 0; i < amount; i++) {
-        result.push({ position: firePos.clone(), direction: dir.clone(), delayMs: 0 });
-      }
-      return result;
-    }
-
-    if (type === 'spread') {
-      // 在 [-spread/2, +spread/2] 范围内平均分布
-      const count = amount;
-      const step = count > 1 ? spreadAngle / (count - 1) : 0;
-      const start = -spreadAngle / 2;
-      for (let i = 0; i < count; i++) {
-        const deg = start + step * i;
-        const rad = (deg * Math.PI) / 180;
-        const d = rotateVec(dir, rad);
-        result.push({ position: firePos.clone(), direction: d, delayMs: 0 });
-      }
-      return result;
-    }
-
-    if (type === 'burst') {
-      // 连发:每次 burst 发 amount 发子弹,总计 burstCount 次;每次之间间隔 burstDelay
-      const ms = Math.floor(burstDelay * 1000);
-      for (let b = 0; b < burstCount; b++) {
-        const delayMs = ms * b;
-        for (let i = 0; i < amount; i++) {
-          result.push({ position: firePos.clone(), direction: dir.clone(), delayMs });
-        }
-      }
-      return result;
-    }
-
-    // 默认回退到单发
-    result.push({ position: firePos.clone(), direction: dir.clone(), delayMs: 0 });
-    return result;
-  }
-}
-
-function rotateVec(v: Vec3, rad: number): Vec3 {
-  const c = Math.cos(rad);
-  const s = Math.sin(rad);
-  return new Vec3(v.x * c - v.y * s, v.x * s + v.y * c, v.z);
-}

+ 0 - 9
assets/scripts/BulletControl/effects/BulletCount.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "91801960-951f-448c-bfcb-08e19546cd9c",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 94
assets/scripts/BulletControl/effects/BulletHitEffect.ts

@@ -1,94 +0,0 @@
-import { _decorator, Component } from 'cc';
-import { HitEffectConfig, HitResult } from '../BulletTypes';
-const { ccclass } = _decorator;
-
-/**
- * 命中效果(可移植版):纯逻辑整合,不直接与敌人系统耦合
- */
-@ccclass('BCBulletHitEffect')
-export class BulletHitEffect extends Component {
-  private effects: HitEffectConfig[] = [];
-  private remainingPierce = 0;
-  private remainingRicochet = 0;
-
-  public init(effects: HitEffectConfig[]) {
-    this.effects = [...(effects ?? [])].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
-    // 初始化穿透与弹射计数
-    const pierce = this.effects.find(e => e.type === 'pierce_damage');
-    const ricochet = this.effects.find(e => e.type === 'ricochet_damage');
-    this.remainingPierce = Math.max(0, pierce?.pierceCount ?? 0);
-    this.remainingRicochet = Math.max(0, ricochet?.ricochetCount ?? 0);
-  }
-
-  /**
-   * 计算命中后结果(纯数据),由上层去应用到敌人/场景
-   */
-  public applyOnHit(): HitResult {
-    if (!this.effects || this.effects.length === 0) return { didHit: true, damageApplied: 0 };
-
-    let damage = 0;
-    let ricochet = false;
-    let ricochetAngle: number | undefined = undefined;
-    let explosion = false;
-    let explosionRadius: number | undefined = undefined;
-    let spawnBurn = false;
-    let burnPayload: HitResult['burn'] = null;
-
-    for (const e of this.effects) {
-      switch (e.type) {
-        case 'normal_damage':
-          damage += Math.max(0, e.damage ?? 0);
-          break;
-        case 'pierce_damage':
-          damage += Math.max(0, e.damage ?? 0);
-          // 穿透次数在命中后交由生命周期或外层减少
-          break;
-        case 'ricochet_damage':
-          damage += Math.max(0, e.damage ?? 0);
-          if (this.remainingRicochet > 0) {
-            ricochet = true;
-            ricochetAngle = e.ricochetAngle ?? ricochetAngle;
-          }
-          break;
-        case 'explosion':
-          damage += Math.max(0, e.damage ?? 0);
-          explosion = true;
-          explosionRadius = e.radius ?? explosionRadius;
-          break;
-        case 'ground_burn':
-          // 地面燃烧不直接叠加伤害(由区域 DOT 管理)
-          spawnBurn = true;
-          burnPayload = {
-            damagePerTick: Math.max(0, e.damage ?? 0),
-            duration: Math.max(0, e.duration ?? 0),
-            tickInterval: Math.max(0.05, e.tickInterval ?? 0.5)
-          };
-          break;
-      }
-    }
-
-    const res: HitResult = {
-      didHit: true,
-      damageApplied: damage,
-      ricochet,
-      ricochetAngle,
-      pierced: this.remainingPierce > 0,
-      remainingPierce: this.remainingPierce,
-      explosion,
-      explosionRadius,
-      spawnBurn,
-      burn: burnPayload
-    };
-    return res;
-  }
-
-  /** 在外层确认穿透后调用,减少计数 */
-  public consumePierce() {
-    if (this.remainingPierce > 0) this.remainingPierce -= 1;
-  }
-
-  /** 在外层确认弹射后调用,减少计数 */
-  public consumeRicochet() {
-    if (this.remainingRicochet > 0) this.remainingRicochet -= 1;
-  }
-}

+ 0 - 9
assets/scripts/BulletControl/effects/BulletHitEffect.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "e6c13290-2048-4142-8f1f-59f7630374d8",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 82
assets/scripts/BulletControl/effects/BulletLifecycle.ts

@@ -1,82 +0,0 @@
-import { _decorator, Component, Vec3 } from 'cc';
-import { BulletLifecycleConfig, HitResult } from '../BulletTypes';
-const { ccclass } = _decorator;
-
-/**
- * 生命周期控制(可移植版):范围、时间、穿透/弹射计数、返回发射源等
- */
-@ccclass('BCBulletLifecycle')
-export class BulletLifecycle extends Component {
-  private cfg: BulletLifecycleConfig | null = null;
-  private origin: Vec3 = new Vec3();
-  private lifetime = 0;
-  private distance = 0;
-  private piercingLeft = 0;
-  private ricochetLeft = 0;
-  private shouldReturn = false;
-  private returnTimer = 0;
-
-  public init(cfg: BulletLifecycleConfig, startPos: Vec3, pierceInit?: number, ricochetInit?: number) {
-    this.cfg = { ...cfg };
-    this.origin.set(startPos);
-    this.lifetime = 0;
-    this.distance = 0;
-    this.piercingLeft = Math.max(0, pierceInit ?? (cfg?.penetration ?? 0));
-    this.ricochetLeft = Math.max(0, ricochetInit ?? (cfg?.ricochetCount ?? 0));
-    this.returnTimer = 0;
-    this.shouldReturn = !!cfg?.returnToOrigin;
-  }
-
-  update(dt: number) {
-    if (!this.cfg) return;
-    this.lifetime += dt;
-    const pos = this.node.worldPosition;
-    const dx = pos.x - this.origin.x;
-    const dy = pos.y - this.origin.y;
-    this.distance = Math.sqrt(dx * dx + dy * dy);
-
-    // 处理返回延迟
-    if (this.shouldReturn) {
-      const delay = Math.max(0, this.cfg.returnDelay ?? 0);
-      if (this.lifetime >= delay) {
-        // 上层可以根据 shouldReturnNow 触发回程逻辑(例如改变弹道方向)
-      }
-    }
-  }
-
-  /** 命中时通知,用于更新计数 */
-  public notifyHit(hit: HitResult) {
-    if (hit.pierced) {
-      if (this.piercingLeft > 0) this.piercingLeft -= 1;
-    } else {
-      // 非穿透命中默认视为消耗一次 penetration(例如普通 hit_destroy)
-      if (this.piercingLeft > 0) this.piercingLeft -= 1;
-    }
-    if (hit.ricochet) {
-      if (this.ricochetLeft > 0) this.ricochetLeft -= 1;
-    }
-  }
-
-  /** 是否应该立即销毁 */
-  public shouldDestroyNow(): boolean {
-    if (!this.cfg) return true;
-    // 时间限制
-    const maxLifetime = this.cfg.maxLifetime ?? Infinity;
-    if (this.lifetime > maxLifetime) return true;
-    // 射程限制
-    const maxRange = this.cfg.maxRange ?? Infinity;
-    if (this.cfg.type === 'range_limit' && this.distance > maxRange) return true;
-    // 穿透/弹射计数耗尽
-    if (this.cfg.type === 'hit_destroy' && this.piercingLeft <= 0) return true;
-    if (this.cfg.type === 'ricochet_counter' && this.ricochetLeft <= 0) return true;
-    // ground_impact:由外层根据地面碰撞事件触发销毁,此处不主动判定
-    return false;
-  }
-
-  /** 是否进入回程阶段(供上层改变弹道方向) */
-  public shouldReturnNow(): boolean {
-    if (!this.cfg || !this.shouldReturn) return false;
-    const delay = Math.max(0, this.cfg.returnDelay ?? 0);
-    return this.lifetime >= delay;
-  }
-}

+ 0 - 9
assets/scripts/BulletControl/effects/BulletLifecycle.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "9b21d9f1-2ec6-41cd-bd79-ccc58e755e21",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 204
assets/scripts/BulletControl/effects/BulletTrajectory.ts

@@ -1,204 +0,0 @@
-import { _decorator, Component, Node, Vec3, math } from 'cc';
-import { BulletTrajectoryConfig } from '../BulletTypes';
-const { ccclass } = _decorator;
-
-/**
- * 弹道控制(可移植版,去除重力,采用渐近转向)
- * - straight:匀速直线
- * - arc:弧线(方向按目标或期望方向渐近旋转)
- * - guided:强导航(延迟后按目标方向渐近旋转)
- * 说明:不做命中判定/自动选敌,仅负责轨迹运动。
- */
-@ccclass('BCBulletTrajectory')
-export class BulletTrajectory extends Component {
-  private cfg: BulletTrajectoryConfig | null = null;
-  private velocity: Vec3 = new Vec3(0, 0, 0);
-  private target: Node | null = null;
-  private homingTimer = 0;
-
-  // 渐近转向用方向缓存
-  private arcDir: Vec3 | null = null;
-  private arcTargetDir: Vec3 | null = null;
-  private guidedDir: Vec3 | null = null;
-  private guidedTargetDir: Vec3 | null = null;
-
-  // 外观对齐/自旋选项
-  private facingShouldRotate: boolean | null = null; // shouldRotate === false 时贴合速度
-  private facingOffsetDeg: number = 0;              // 图片朝向修正(度)
-  private facingChildName: string | null = 'Pellet';// 优先旋转的子节点名
-  private autoSpinSpeedDeg: number = 720;           // 自旋速度(度/秒)
-
-  public init(cfg: BulletTrajectoryConfig, direction: Vec3, startPos: Vec3, originNode?: Node | null) {
-    this.cfg = { ...cfg };
-    const speed = Math.max(0, this.cfg.speed ?? 0);
-    const baseDir = direction?.clone() ?? new Vec3(1, 0, 0);
-    if (baseDir.length() === 0) baseDir.set(1, 0, 0);
-    baseDir.normalize();
-
-    // 初始速度(straight)
-    this.velocity.set(baseDir.x * speed, baseDir.y * speed, 0);
-    this.node.setPosition(startPos);
-    this.homingTimer = 0;
-
-    // 为 arc/guided 建立初始偏移与目标方向(便于产生弧线)
-    const sign = Math.random() < 0.5 ? 1 : -1;
-    const radArc = (45 * Math.PI / 180) * sign; // 初始偏转角
-    const c = Math.cos(radArc);
-    const s = Math.sin(radArc);
-    const offsetDir = new Vec3(
-      baseDir.x * c - baseDir.y * s,
-      baseDir.x * s + baseDir.y * c,
-      0
-    ).normalize();
-
-    this.arcDir = offsetDir.clone();
-    this.arcTargetDir = baseDir.clone();
-
-    const radGuided = (35 * Math.PI / 180) * sign;
-    const cg = Math.cos(radGuided);
-    const sg = Math.sin(radGuided);
-    const guidedOffset = new Vec3(
-      baseDir.x * cg - baseDir.y * sg,
-      baseDir.x * sg + baseDir.y * cg,
-      0
-    ).normalize();
-    this.guidedDir = guidedOffset.clone();
-    this.guidedTargetDir = baseDir.clone();
-  }
-
-  public setTarget(node: Node | null) {
-    this.target = node;
-  }
-
-  /**
-   * 配置外观对齐/自旋
-   */
-  public setFacingOptions(opts: { shouldRotate?: boolean; offsetDeg?: number; targetChildName?: string; spinSpeedDeg?: number }) {
-    this.facingShouldRotate = (opts && 'shouldRotate' in opts) ? (opts.shouldRotate ?? null) : this.facingShouldRotate;
-    if (opts && typeof opts.offsetDeg === 'number') this.facingOffsetDeg = opts.offsetDeg;
-    if (opts && typeof opts.targetChildName === 'string') this.facingChildName = opts.targetChildName || null;
-    if (opts && typeof opts.spinSpeedDeg === 'number') this.autoSpinSpeedDeg = opts.spinSpeedDeg;
-  }
-
-  public getCurrentVelocity(): Vec3 {
-    return this.velocity.clone();
-  }
-
-  update(dt: number) {
-    if (!this.cfg) return;
-    const type = this.cfg.type;
-    if (type === 'straight') {
-      this.moveLinear(dt);
-    } else if (type === 'arc') {
-      this.updateArc(dt);
-    } else if (type === 'guided') {
-      this.updateGuided(dt);
-    } else {
-      this.moveLinear(dt);
-    }
-
-    // 更新外观对齐/自旋
-    this.updateFacing(dt);
-  }
-
-  private moveLinear(dt: number) {
-    const dx = this.velocity.x * dt;
-    const dy = this.velocity.y * dt;
-    const p = this.node.position;
-    this.node.setPosition(p.x + dx, p.y + dy);
-  }
-
-  /**
-   * 弧线:按目标或期望方向渐近旋转,保持速率恒定
-   */
-  private updateArc(dt: number) {
-    if (!this.arcDir || !this.arcTargetDir || !this.cfg) {
-      this.moveLinear(dt);
-      return;
-    }
-
-    // 若有目标则按目标方向渐近;否则保持初始目标方向
-    if (this.target && this.target.isValid) {
-      const pos = this.node.worldPosition;
-      const tpos = this.target.worldPosition;
-      const toTarget = new Vec3(tpos.x - pos.x, tpos.y - pos.y, 0).normalize();
-      this.arcTargetDir.set(toTarget);
-    }
-
-    const rotateSpeed = Math.max(0, Math.min(1, this.cfg.rotateSpeed ?? 0.2));
-    const t = Math.min(1, rotateSpeed * dt);
-    const newDir = new Vec3();
-    Vec3.slerp(newDir, this.arcDir, this.arcTargetDir, t);
-    this.arcDir.set(newDir);
-
-    const speed = Math.max(0, this.cfg.speed ?? 0);
-    this.velocity.set(newDir.x * speed, newDir.y * speed, 0);
-    this.moveLinear(dt);
-  }
-
-  /**
-   * Guided:延迟后按目标方向渐近旋转,保持速率恒定
-   */
-  private updateGuided(dt: number) {
-    if (!this.guidedDir || !this.guidedTargetDir || !this.cfg) {
-      this.moveLinear(dt);
-      return;
-    }
-
-    const delay = Math.max(0, this.cfg.homingDelay ?? 0);
-    this.homingTimer += dt;
-    if (this.homingTimer < delay) {
-      // 延迟期:沿初始引导方向匀速飞行
-      const speed = Math.max(0, this.cfg.speed ?? 0);
-      this.velocity.set(this.guidedDir.x * speed, this.guidedDir.y * speed, 0);
-      this.moveLinear(dt);
-      return;
-    }
-
-    // 计算期望方向(若无目标则维持原方向)
-    if (this.target && this.target.isValid) {
-      const pos = this.node.worldPosition;
-      const tpos = this.target.worldPosition;
-      const toTarget = new Vec3(tpos.x - pos.x, tpos.y - pos.y, 0).normalize();
-      this.guidedTargetDir.set(toTarget);
-    }
-
-    const rotateSpeed = Math.max(0, Math.min(1, this.cfg.rotateSpeed ?? 0.25));
-    const strength = Math.max(0, Math.min(1, this.cfg.homingStrength ?? 0.15));
-    const t = Math.min(1, rotateSpeed * (0.8 + strength * 0.4) * dt);
-    const newDir = new Vec3();
-    Vec3.slerp(newDir, this.guidedDir, this.guidedTargetDir, t);
-    this.guidedDir.set(newDir);
-
-    const speed = Math.max(0, this.cfg.speed ?? 0);
-    this.velocity.set(newDir.x * speed, newDir.y * speed, 0);
-    this.moveLinear(dt);
-  }
-
-  /**
-   * 根据当前速度更新外观角度(shouldRotate === false 贴合;否则自旋)
-   */
-  private updateFacing(dt: number) {
-    // 未配置则不处理,保持移植版轻耦合
-    if (this.facingShouldRotate === null) return;
-
-    const vel = this.getCurrentVelocity();
-    if (!vel || (Math.abs(vel.x) < 1e-4 && Math.abs(vel.y) < 1e-4)) return;
-
-    // 目标节点:优先子节点
-    let targetNode: Node | null = null;
-    if (this.facingChildName) {
-      const child = this.node.getChildByName(this.facingChildName);
-      if (child) targetNode = child;
-    }
-    if (!targetNode) targetNode = this.node;
-
-    if (this.facingShouldRotate === false) {
-      const deg = math.toDegree(Math.atan2(vel.y, vel.x)) + this.facingOffsetDeg;
-      targetNode.angle = deg;
-    } else {
-      // 自旋:不强制贴合轨迹切线
-      targetNode.angle += this.autoSpinSpeedDeg * dt;
-    }
-  }
-}

+ 0 - 9
assets/scripts/BulletControl/effects/BulletTrajectory.ts.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "4.0.24",
-  "importer": "typescript",
-  "imported": true,
-  "uuid": "170fc0e4-c175-43a4-b99f-421ee046a47c",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 9
assets/scripts/BulletControl/exported_data.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "1.2.0",
-  "importer": "directory",
-  "imported": true,
-  "uuid": "b09ae2c2-5f74-4a57-885c-6902881ea28c",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 1196
assets/scripts/BulletControl/exported_data/weapon_config_manager.py

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

+ 0 - 12
assets/scripts/BulletControl/exported_data/weapon_config_manager.py.meta

@@ -1,12 +0,0 @@
-{
-  "ver": "1.0.0",
-  "importer": "*",
-  "imported": true,
-  "uuid": "85cfc9d8-018b-4a55-8f3f-12d3a130b968",
-  "files": [
-    ".json",
-    ".py"
-  ],
-  "subMetas": {},
-  "userData": {}
-}

+ 0 - 1617
assets/scripts/BulletControl/exported_data/weapons.json

@@ -1,1617 +0,0 @@
-{
-  "weapons": [
-    {
-      "id": "pea_shooter",
-      "name": "毛豆射手",
-      "type": "single_shot",
-      "weight": 30,
-      "unlockLevel": 0,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 10,
-        "fireRate": 3.0,
-        "range": 500,
-        "bulletSpeed": 40
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "single",
-          "amount": 1,
-          "spreadAngle": 0,
-          "burstCount": 1,
-          "burstDelay": 2
-        },
-        "trajectory": {
-          "type": "straight",
-          "speed": 200,
-          "gravity": 0,
-          "arcHeight": 0,
-          "homingStrength": 0,
-          "homingDelay": 0
-        },
-        "hitEffects": [
-          {
-            "type": "normal_damage",
-            "priority": 1,
-            "damage": 10
-          }
-        ],
-        "lifecycle": {
-          "type": "hit_destroy",
-          "maxLifetime": 5.0,
-          "penetration": 1,
-          "ricochetCount": 0,
-          "returnToOrigin": false
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/010",
-          "hitEffect": "Animation/WeaponTx/tx0002/tx0002",
-          "trailEffect": true
-        }
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/001-1",
-        "attackSound": "data/弹球音效/bean atk"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 25,
-            "damage": 10
-          },
-          "2": {
-            "cost": 50,
-            "damage": 11
-          },
-          "3": {
-            "cost": 75,
-            "damage": 12
-          },
-          "4": {
-            "cost": 100,
-            "damage": 13
-          },
-          "5": {
-            "cost": 125,
-            "damage": 14
-          },
-          "6": {
-            "cost": 150,
-            "damage": 15
-          },
-          "7": {
-            "cost": 175,
-            "damage": 16
-          },
-          "8": {
-            "cost": 200,
-            "damage": 17
-          },
-          "9": {
-            "cost": 225,
-            "damage": 18
-          },
-          "10": {
-            "cost": 250,
-            "damage": 19
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 10,
-        "shapeCosts": {
-          "I": 10,
-          "H-I": 10,
-          "L": 15,
-          "S": 20,
-          "D-T": 20,
-          "T": 15
-        }
-      }
-    },
-    {
-      "id": "sharp_carrot",
-      "name": "尖胡萝卜",
-      "type": "piercing",
-      "weight": 25,
-      "unlockLevel": 1,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 7,
-        "fireRate": 3.0,
-        "range": 500,
-        "bulletSpeed": 20
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "single",
-          "amount": 1,
-          "spreadAngle": 0,
-          "burstCount": 1,
-          "burstDelay": 2
-        },
-        "trajectory": {
-          "type": "straight",
-          "speed": 200,
-          "gravity": 0,
-          "arcHeight": 0,
-          "homingStrength": 0,
-          "homingDelay": 0
-        },
-        "hitEffects": [
-          {
-            "type": "pierce_damage",
-            "priority": 1,
-            "damage": 8,
-            "pierceCount": 999
-          }
-        ],
-        "lifecycle": {
-          "type": "range_limit",
-          "maxLifetime": 5.0,
-          "penetration": 999,
-          "ricochetCount": 0,
-          "returnToOrigin": false,
-          "maxRange": 800
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/002",
-          "hitEffect": "Animation/WeaponTx/tx0002/tx0002",
-          "trailEffect": "Animation/WeaponTx/tx0001/tx0001"
-        },
-        "shouldRotate": false
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/002",
-        "attackSound": "data/弹球音效/cawl"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 25,
-            "damage": 7
-          },
-          "2": {
-            "cost": 50,
-            "damage": 8
-          },
-          "3": {
-            "cost": 75,
-            "damage": 9
-          },
-          "4": {
-            "cost": 100,
-            "damage": 10
-          },
-          "5": {
-            "cost": 125,
-            "damage": 11
-          },
-          "6": {
-            "cost": 150,
-            "damage": 12
-          },
-          "7": {
-            "cost": 175,
-            "damage": 13
-          },
-          "8": {
-            "cost": 200,
-            "damage": 14
-          },
-          "9": {
-            "cost": 225,
-            "damage": 15
-          },
-          "10": {
-            "cost": 250,
-            "damage": 16
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 15,
-        "shapeCosts": {
-          "I": 15,
-          "H-I": 15,
-          "L": 20,
-          "S": 25,
-          "D-T": 25,
-          "T": 20
-        }
-      }
-    },
-    {
-      "id": "saw_grass",
-      "name": "锯齿草",
-      "type": "ricochet_piercing",
-      "weight": 20,
-      "unlockLevel": 3,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 5,
-        "fireRate": 3.0,
-        "range": 500,
-        "bulletSpeed": 30
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "single",
-          "amount": 1,
-          "spreadAngle": 0,
-          "burstCount": 1,
-          "burstDelay": 2
-        },
-        "trajectory": {
-          "type": "straight",
-          "speed": 200,
-          "gravity": 0,
-          "arcHeight": 0,
-          "homingStrength": 0,
-          "homingDelay": 0
-        },
-        "hitEffects": [
-          {
-            "type": "ricochet_damage",
-            "priority": 1,
-            "damage": 8,
-            "ricochetCount": 2,
-            "ricochetAngle": 45
-          },
-          {
-            "type": "pierce_damage",
-            "priority": 2,
-            "damage": 8,
-            "pierceCount": 3
-          }
-        ],
-        "lifecycle": {
-          "type": "ricochet_counter",
-          "maxLifetime": 8.0,
-          "penetration": 3,
-          "ricochetCount": 3,
-          "returnToOrigin": false
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/003",
-          "hitEffect": "Animation/WeaponTx/tx0002/tx0002",
-          "trailEffect": true
-        }
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/003",
-        "attackSound": "data/弹球音效/juchi atk"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 25,
-            "damage": 5
-          },
-          "2": {
-            "cost": 50,
-            "damage": 6
-          },
-          "3": {
-            "cost": 75,
-            "damage": 7
-          },
-          "4": {
-            "cost": 100,
-            "damage": 8
-          },
-          "5": {
-            "cost": 125,
-            "damage": 9
-          },
-          "6": {
-            "cost": 150,
-            "damage": 10
-          },
-          "7": {
-            "cost": 175,
-            "damage": 11
-          },
-          "8": {
-            "cost": 200,
-            "damage": 12
-          },
-          "9": {
-            "cost": 225,
-            "damage": 13
-          },
-          "10": {
-            "cost": 250,
-            "damage": 14
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 20,
-        "shapeCosts": {
-          "I": 20,
-          "H-I": 20,
-          "L": 25,
-          "S": 30,
-          "D-T": 30,
-          "T": 25
-        }
-      }
-    },
-    {
-      "id": "watermelon_bomb",
-      "name": "西瓜炸弹",
-      "type": "explosive",
-      "weight": 15,
-      "unlockLevel": 5,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 5,
-        "fireRate": 3.0,
-        "range": 1000,
-        "bulletSpeed": 20
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "single",
-          "amount": 1,
-          "spreadAngle": 0,
-          "burstCount": 1,
-          "burstDelay": 2
-        },
-        "trajectory": {
-          "type": "arc",
-          "speed": 200,
-          "gravity": 0,
-          "arcHeight": 0,
-          "homingStrength": 0,
-          "homingDelay": 0
-        },
-        "hitEffects": [
-          {
-            "type": "explosion",
-            "priority": 1,
-            "damage": 35,
-            "radius": 100,
-            "delay": 0.1
-          }
-        ],
-        "lifecycle": {
-          "type": "ground_impact",
-          "maxLifetime": 5.0,
-          "penetration": 1,
-          "ricochetCount": 0,
-          "returnToOrigin": false
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/007",
-          "hitEffect": "Animation/WeaponTx/tx0007/tx0007",
-          "trailEffect": true,
-          "explosionEffect": "Animation/WeaponTx/tx0007/tx0007"
-        }
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/007",
-        "attackSound": "data/弹球音效/bomb"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 125,
-            "damage": 5
-          },
-          "2": {
-            "cost": 150,
-            "damage": 6
-          },
-          "3": {
-            "cost": 175,
-            "damage": 7
-          },
-          "4": {
-            "cost": 200,
-            "damage": 8
-          },
-          "5": {
-            "cost": 225,
-            "damage": 9
-          },
-          "6": {
-            "cost": 250,
-            "damage": 10
-          },
-          "7": {
-            "cost": 275,
-            "damage": 11
-          },
-          "8": {
-            "cost": 300,
-            "damage": 12
-          },
-          "9": {
-            "cost": 325,
-            "damage": 13
-          },
-          "10": {
-            "cost": 350,
-            "damage": 14
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 15,
-        "shapeCosts": {
-          "I": 15,
-          "H-I": 15,
-          "L": 20,
-          "S": 25,
-          "D-T": 25,
-          "T": 20
-        }
-      }
-    },
-    {
-      "id": "boomerang_plant",
-      "name": "回旋镖盆栽",
-      "type": "boomerang",
-      "weight": 18,
-      "unlockLevel": 7,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 7,
-        "fireRate": 3.0,
-        "range": 1000,
-        "bulletSpeed": 20
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "single",
-          "amount": 1,
-          "spreadAngle": 0,
-          "burstCount": 1,
-          "burstDelay": 2
-        },
-        "trajectory": {
-          "type": "arc",
-          "speed": 15,
-          "gravity": 0,
-          "rotateSpeed": 0.6,
-          "homingStrength": 0.5,
-          "homingDelay": 0.3
-        },
-        "hitEffects": [
-          {
-            "type": "pierce_damage",
-            "priority": 1,
-            "damage": 10,
-            "pierceCount": 999
-          }
-        ],
-        "lifecycle": {
-          "type": "return_trip",
-          "maxLifetime": 10.0,
-          "penetration": 999,
-          "ricochetCount": 0,
-          "returnToOrigin": true,
-          "returnDelay": 2.0
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/004",
-          "hitEffect": "Animation/WeaponTx/tx0002/tx0002",
-          "trailEffect": true
-        }
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/004",
-        "attackSound": "data/弹球音效/huixuanbiao atk"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 125,
-            "damage": 7
-          },
-          "2": {
-            "cost": 150,
-            "damage": 8
-          },
-          "3": {
-            "cost": 175,
-            "damage": 9
-          },
-          "4": {
-            "cost": 200,
-            "damage": 10
-          },
-          "5": {
-            "cost": 225,
-            "damage": 11
-          },
-          "6": {
-            "cost": 250,
-            "damage": 12
-          },
-          "7": {
-            "cost": 275,
-            "damage": 13
-          },
-          "8": {
-            "cost": 300,
-            "damage": 14
-          },
-          "9": {
-            "cost": 325,
-            "damage": 15
-          },
-          "10": {
-            "cost": 350,
-            "damage": 16
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 15,
-        "shapeCosts": {
-          "I": 15,
-          "H-I": 15,
-          "L": 20,
-          "S": 25,
-          "D-T": 25,
-          "T": 20
-        }
-      }
-    },
-    {
-      "id": "hot_pepper",
-      "name": "炙热辣椒",
-      "type": "area_burn",
-      "weight": 12,
-      "unlockLevel": 9,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 2,
-        "fireRate": 3.0,
-        "range": 500,
-        "bulletSpeed": 20
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "single",
-          "amount": 1,
-          "spreadAngle": 0,
-          "burstCount": 1,
-          "burstDelay": 2
-        },
-        "trajectory": {
-          "type": "straight",
-          "speed": 200,
-          "gravity": 0,
-          "arcHeight": 0,
-          "homingStrength": 0,
-          "homingDelay": 0
-        },
-        "hitEffects": [
-          {
-            "type": "normal_damage",
-            "priority": 1,
-            "damage": 15
-          },
-          {
-            "type": "ground_burn",
-            "priority": 2,
-            "damage": 5,
-            "duration": 3.0,
-            "tickInterval": 0.5
-          }
-        ],
-        "lifecycle": {
-          "type": "hit_destroy",
-          "maxLifetime": 5.0,
-          "penetration": 1,
-          "ricochetCount": 0,
-          "returnToOrigin": false
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/005",
-          "hitEffect": "Animation/WeaponTx/tx0002/tx0002",
-          "trailEffect": true,
-          "burnEffect": "Animation/WeaponBurnAni/燃烧/ui_zhuling"
-        },
-        "shouldRotate": false
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/005",
-        "attackSound": "data/弹球音效/fire"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 125,
-            "damage": 2
-          },
-          "2": {
-            "cost": 150,
-            "damage": 3
-          },
-          "3": {
-            "cost": 175,
-            "damage": 4
-          },
-          "4": {
-            "cost": 200,
-            "damage": 5
-          },
-          "5": {
-            "cost": 225,
-            "damage": 6
-          },
-          "6": {
-            "cost": 250,
-            "damage": 7
-          },
-          "7": {
-            "cost": 275,
-            "damage": 8
-          },
-          "8": {
-            "cost": 300,
-            "damage": 9
-          },
-          "9": {
-            "cost": 325,
-            "damage": 10
-          },
-          "10": {
-            "cost": 350,
-            "damage": 11
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 15,
-        "shapeCosts": {
-          "I": 15,
-          "H-I": 15,
-          "L": 20,
-          "S": 25,
-          "D-T": 25,
-          "T": 20
-        }
-      }
-    },
-    {
-      "id": "cactus_shotgun",
-      "name": "仙人散弹",
-      "type": "shotgun",
-      "weight": 22,
-      "unlockLevel": 11,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 8,
-        "fireRate": 3.0,
-        "range": 500,
-        "bulletSpeed": 40
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "spread",
-          "amount": 5,
-          "spreadAngle": 30,
-          "burstCount": 1,
-          "burstDelay": 2
-        },
-        "trajectory": {
-          "type": "straight",
-          "speed": 200,
-          "gravity": 0,
-          "arcHeight": 0,
-          "homingStrength": 0,
-          "homingDelay": 2
-        },
-        "hitEffects": [
-          {
-            "type": "normal_damage",
-            "priority": 1,
-            "damage": 6
-          }
-        ],
-        "lifecycle": {
-          "type": "range_limit",
-          "maxLifetime": 5.0,
-          "penetration": 1,
-          "ricochetCount": 0,
-          "returnToOrigin": false,
-          "maxRange": 1000
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/008",
-          "hitEffect": "Animation/WeaponTx/tx0002/tx0002",
-          "trailEffect": true
-        }
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/008",
-        "attackSound": "data/弹球音效/xianrenzhang hit"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 325,
-            "damage": 8
-          },
-          "2": {
-            "cost": 350,
-            "damage": 9
-          },
-          "3": {
-            "cost": 375,
-            "damage": 10
-          },
-          "4": {
-            "cost": 400,
-            "damage": 11
-          },
-          "5": {
-            "cost": 425,
-            "damage": 12
-          },
-          "6": {
-            "cost": 450,
-            "damage": 13
-          },
-          "7": {
-            "cost": 475,
-            "damage": 14
-          },
-          "8": {
-            "cost": 500,
-            "damage": 15
-          },
-          "9": {
-            "cost": 525,
-            "damage": 16
-          },
-          "10": {
-            "cost": 550,
-            "damage": 17
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 20,
-        "shapeCosts": {
-          "I": 20,
-          "H-I": 20,
-          "L": 25,
-          "S": 30,
-          "D-T": 30,
-          "T": 25
-        }
-      }
-    },
-    {
-      "id": "okra_missile",
-      "name": "秋葵导弹",
-      "type": "homing_missile",
-      "weight": 8,
-      "unlockLevel": 13,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 15,
-        "fireRate": 3.0,
-        "range": 500,
-        "bulletSpeed": 15
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "single",
-          "amount": 1,
-          "spreadAngle": 0,
-          "burstCount": 1,
-          "burstDelay": 2
-        },
-        "trajectory": {
-          "type": "guided",
-          "speed": 200,
-          "gravity": 0,
-          "arcHeight": 0,
-          "homingStrength": 0,
-          "homingDelay": 0
-        },
-        "hitEffects": [
-          {
-            "type": "explosion",
-            "priority": 1,
-            "damage": 20,
-            "radius": 150,
-            "delay": 0
-          }
-        ],
-        "lifecycle": {
-          "type": "ground_impact",
-          "maxLifetime": 5.0,
-          "penetration": 1,
-          "ricochetCount": 0,
-          "returnToOrigin": false
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/006",
-          "hitEffect": "Animation/WeaponTx/tx0002/tx0002",
-          "trailEffect": true,
-          "explosionEffect": "Animation/WeaponTx/tx0007/tx0007"
-        },
-        "shouldRotate": false
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/006",
-        "attackSound": "data/弹球音效/qiukui hit"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 325,
-            "damage": 15
-          },
-          "2": {
-            "cost": 350,
-            "damage": 16
-          },
-          "3": {
-            "cost": 375,
-            "damage": 17
-          },
-          "4": {
-            "cost": 400,
-            "damage": 18
-          },
-          "5": {
-            "cost": 425,
-            "damage": 19
-          },
-          "6": {
-            "cost": 450,
-            "damage": 20
-          },
-          "7": {
-            "cost": 475,
-            "damage": 21
-          },
-          "8": {
-            "cost": 500,
-            "damage": 22
-          },
-          "9": {
-            "cost": 525,
-            "damage": 23
-          },
-          "10": {
-            "cost": 550,
-            "damage": 24
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 20,
-        "shapeCosts": {
-          "I": 20,
-          "H-I": 20,
-          "L": 25,
-          "S": 30,
-          "D-T": 30,
-          "T": 25
-        }
-      }
-    },
-    {
-      "id": "mace_club",
-      "name": "狼牙棒",
-      "type": "melee",
-      "weight": 20,
-      "unlockLevel": 15,
-      "rarityDamageMultipliers": [
-        1.0,
-        2.0,
-        3.0,
-        4.0,
-        5.0
-      ],
-      "stats": {
-        "damage": 10,
-        "fireRate": 3.0,
-        "range": 500,
-        "bulletSpeed": 35
-      },
-      "bulletConfig": {
-        "count": {
-          "type": "single",
-          "amount": 1,
-          "spreadAngle": 0,
-          "burstCount": 1,
-          "burstDelay": 0
-        },
-        "trajectory": {
-          "type": "straight",
-          "speed": 250,
-          "gravity": 0,
-          "arcHeight": 0,
-          "homingStrength": 0,
-          "homingDelay": 0
-        },
-        "hitEffects": [
-          {
-            "type": "normal_damage",
-            "priority": 1,
-            "damage": 25
-          },
-          {
-            "type": "knockback",
-            "priority": 2,
-            "force": 150
-          }
-        ],
-        "lifecycle": {
-          "type": "range_limit",
-          "maxLifetime": 3.0,
-          "penetration": 2,
-          "ricochetCount": 0,
-          "returnToOrigin": false,
-          "maxRange": 200
-        },
-        "visual": {
-          "bulletImages": "images/PlantsSprite/009",
-          "hitEffect": "Animation/WeaponTx/tx0002/tx0002",
-          "trailEffect": true
-        },
-        "shouldRotate": false
-      },
-      "visualConfig": {
-        "weaponSprites": "images/PlantsSprite/009",
-        "attackSound": "data/弹球音效/mace_club_hit"
-      },
-      "upgradeConfig": {
-        "maxLevel": 10,
-        "levels": {
-          "1": {
-            "cost": 325,
-            "damage": 10
-          },
-          "2": {
-            "cost": 350,
-            "damage": 11
-          },
-          "3": {
-            "cost": 375,
-            "damage": 12
-          },
-          "4": {
-            "cost": 400,
-            "damage": 13
-          },
-          "5": {
-            "cost": 425,
-            "damage": 14
-          },
-          "6": {
-            "cost": 450,
-            "damage": 15
-          },
-          "7": {
-            "cost": 475,
-            "damage": 16
-          },
-          "8": {
-            "cost": 500,
-            "damage": 17
-          },
-          "9": {
-            "cost": 525,
-            "damage": 18
-          },
-          "10": {
-            "cost": 550,
-            "damage": 19
-          }
-        }
-      },
-      "inGameCostConfig": {
-        "baseCost": 15,
-        "shapeCosts": {
-          "I": 15,
-          "H-I": 15,
-          "L": 20,
-          "S": 25,
-          "D-T": 25,
-          "T": 20
-        }
-      }
-    }
-  ],
-  "blockSizes": [
-    {
-      "id": "I",
-      "name": "I形",
-      "shape": [
-        [
-          [
-            1,
-            0,
-            0,
-            0
-          ],
-          [
-            1,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 2,
-      "costMultiplier": 2.0,
-      "description": "最简单的直线形状"
-    },
-    {
-      "id": "H-I",
-      "name": "横I形",
-      "shape": [
-        [
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            1,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 2,
-      "costMultiplier": 2.0,
-      "description": "水平直线形状"
-    },
-    {
-      "id": "L",
-      "name": "L形",
-      "shape": [
-        [
-          [
-            1,
-            0,
-            0,
-            0
-          ],
-          [
-            1,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 3,
-      "costMultiplier": 3.0,
-      "description": "L型左上转角形状"
-    },
-    {
-      "id": "S",
-      "name": "S形",
-      "shape": [
-        [
-          [
-            0,
-            1,
-            1,
-            0
-          ],
-          [
-            1,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 4,
-      "costMultiplier": 4.0,
-      "description": "S型曲线形状"
-    },
-    {
-      "id": "D-T",
-      "name": "倒T形",
-      "shape": [
-        [
-          [
-            0,
-            1,
-            0,
-            0
-          ],
-          [
-            1,
-            1,
-            1,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 4,
-      "costMultiplier": 4.0,
-      "description": "倒T型形状"
-    },
-    {
-      "id": "L2",
-      "name": "L2形",
-      "shape": [
-        [
-          [
-            1,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 3,
-      "costMultiplier": 3.0,
-      "description": "L2型右上转角形状"
-    },
-    {
-      "id": "L3",
-      "name": "L3形",
-      "shape": [
-        [
-          [
-            1,
-            0,
-            0,
-            0
-          ],
-          [
-            1,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 3,
-      "costMultiplier": 3.0,
-      "description": "L3型左下转角形状"
-    },
-    {
-      "id": "L4",
-      "name": "L4形",
-      "shape": [
-        [
-          [
-            0,
-            1,
-            0,
-            0
-          ],
-          [
-            1,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 3,
-      "costMultiplier": 3.0,
-      "description": "L4型右下转角形状"
-    },
-    {
-      "id": "S-F",
-      "name": "S-F形",
-      "shape": [
-        [
-          [
-            1,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            1,
-            1,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 4,
-      "costMultiplier": 4.0,
-      "description": "S型旋转镜像形状"
-    },
-    {
-      "id": "T",
-      "name": "T形",
-      "shape": [
-        [
-          [
-            1,
-            1,
-            1,
-            0
-          ],
-          [
-            0,
-            1,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            0,
-            0,
-            0
-          ]
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ],
-        [
-          0,
-          0,
-          0,
-          0
-        ]
-      ],
-      "gridCount": 4,
-      "costMultiplier": 4.0,
-      "description": "T型形状"
-    }
-  ]
-}

+ 0 - 11
assets/scripts/BulletControl/exported_data/weapons.json.meta

@@ -1,11 +0,0 @@
-{
-  "ver": "2.0.1",
-  "importer": "json",
-  "imported": true,
-  "uuid": "8b87d17f-5a7b-49fd-8a9b-31d9c8b6a616",
-  "files": [
-    ".json"
-  ],
-  "subMetas": {},
-  "userData": {}
-}

二進制
assets/scripts/BulletControl/exported_data/方块武器配置表.xlsx


+ 0 - 12
assets/scripts/BulletControl/exported_data/方块武器配置表.xlsx.meta

@@ -1,12 +0,0 @@
-{
-  "ver": "1.0.0",
-  "importer": "*",
-  "imported": true,
-  "uuid": "50a19cec-1455-451b-b990-39bd1000ccc8",
-  "files": [
-    ".json",
-    ".xlsx"
-  ],
-  "subMetas": {},
-  "userData": {}
-}

+ 9 - 4
assets/scripts/CombatSystem/EnemyWeapon/EnemyProjectileInstance.ts

@@ -24,6 +24,7 @@ export class EnemyProjectileInstance extends Component {
     private rigidBody: RigidBody2D = null;
     private collider: Collider2D = null;
     private sprite: Sprite = null;
+    private imageNode: Node = null;
     
     // 生命周期
     private maxLifetime: number = 5.0; // 最大存活时间
@@ -120,12 +121,16 @@ export class EnemyProjectileInstance extends Component {
             console.log('[EnemyProjectileInstance] 找到Collider2D组件');
         }
         
-        // 获取预制体中已配置的精灵组件
-        this.sprite = this.getComponent(Sprite);
+        // 获取Image子节点上的精灵组件(新结构)
+        this.imageNode = this.node.getChildByName('Image') || null;
+        if (!this.imageNode) {
+            console.warn('[EnemyProjectileInstance] 未找到子节点 Image,尝试在子层级搜索Sprite组件');
+        }
+        this.sprite = this.imageNode ? this.imageNode.getComponent(Sprite) : this.getComponentInChildren(Sprite);
         if (!this.sprite) {
-            console.error('[EnemyProjectileInstance] 预制体中未找到Sprite组件');
+            console.error('[EnemyProjectileInstance] 未找到Sprite组件,请确保在 EnemyProjectile/Image 节点上挂载 Sprite');
         } else {
-            console.log('[EnemyProjectileInstance] 找到Sprite组件');
+            console.log('[EnemyProjectileInstance] 找到Sprite组件(节点:' + (this.sprite.node?.name || '未知') + ')');
         }
     }
     

+ 28 - 8
assets/scripts/CombatSystem/MenuSystem/SoundController.ts

@@ -36,14 +36,14 @@ export class SoundController extends Component {
     @property({ type: ProgressBar, tooltip: '音乐进度条' })
     public musicProgressBar: ProgressBar = null;
     
-    // 震动控制组件
-    @property({ type: Button, tooltip: '震动开关按钮' })
+    // 总音频开关(控制音效与音乐的总开/关)
+    @property({ type: Button, tooltip: '总音频开关:开 按钮' })
     public vibrationOnButton: Button = null;
     
-    @property({ type: Button, tooltip: '震动关闭按钮' })
+    @property({ type: Button, tooltip: '总音频开关:关 按钮' })
     public vibrationOffButton: Button = null;
     
-    @property({ type: Node, tooltip: '震动滑动节点' })
+    @property({ type: Node, tooltip: '总音频开关滑块节点' })
     public vibrationSlideNode: Node = null;
     
     // 音量状态
@@ -162,6 +162,11 @@ export class SoundController extends Component {
         }
         
         this.saveSettings();
+
+        // 同步总开关位置:当音效与音乐均开启时为“开”,否则为“关”
+        this.vibrationEnabled = this.soundEffectEnabled && this.musicEnabled;
+        this.updateVibrationSlideButtonImmediate();
+        this.saveVibrationSetting();
     }
     
     /**
@@ -190,6 +195,11 @@ export class SoundController extends Component {
         }
         
         this.saveSettings();
+
+        // 同步总开关位置:当音效与音乐均开启时为“开”,否则为“关”
+        this.vibrationEnabled = this.soundEffectEnabled && this.musicEnabled;
+        this.updateVibrationSlideButtonImmediate();
+        this.saveVibrationSetting();
     }
     
     /**
@@ -356,10 +366,8 @@ export class SoundController extends Component {
             }
         }
 
-        // 从SaveDataManager加载震动设置
-        if (sdm) {
-            this.vibrationEnabled = sdm.getSetting('vibrationEnabled');
-        }
+        // 总音频开关的显示应与当前音效和音乐状态一致
+        this.vibrationEnabled = this.soundEffectEnabled && this.musicEnabled;
 
         // 更新UI显示
         this.updateUI();
@@ -393,6 +401,8 @@ export class SoundController extends Component {
         }
         
         // 更新震动滑动按钮位置(不使用动画,直接设置位置)
+        // 同步总音频开关位置:当音效与音乐均为开启时显示“开”,否则显示“关”
+        this.vibrationEnabled = this.soundEffectEnabled && this.musicEnabled;
         this.updateVibrationSlideButtonImmediate();
     }
     
@@ -489,6 +499,11 @@ export class SoundController extends Component {
         }
         
         this.saveSettings();
+
+        // 同步总开关视觉状态为“开”
+        this.vibrationEnabled = true;
+        this.updateVibrationSlideButtonImmediate();
+        this.saveVibrationSetting();
     }
     
     /**
@@ -528,5 +543,10 @@ export class SoundController extends Component {
         }
         
         this.saveSettings();
+
+        // 同步总开关视觉状态为“关”
+        this.vibrationEnabled = false;
+        this.updateVibrationSlideButtonImmediate();
+        this.saveVibrationSetting();
     }
 }