ConfigManager.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. import { _decorator, resources, JsonAsset } from 'cc';
  2. import { BaseSingleton } from './BaseSingleton';
  3. const { ccclass, property } = _decorator;
  4. // 局内金币花费配置接口
  5. export interface InGameCostConfig {
  6. baseCost: number;
  7. shapeCosts: {
  8. [shapeId: string]: number;
  9. };
  10. }
  11. // 弹道配置接口
  12. export interface BulletTrajectoryConfig {
  13. type: string;
  14. speed: number;
  15. gravity: number;
  16. arcHeight: number;
  17. homingStrength: number;
  18. homingDelay: number;
  19. rotateSpeed?: number; // 旋转速率 (0~1),仅在 arc 弹道中使用
  20. }
  21. // 弹药数量配置接口
  22. export interface BulletCountConfig {
  23. type: string;
  24. amount: number;
  25. spreadAngle: number;
  26. burstCount: number;
  27. burstDelay: number;
  28. }
  29. // 命中效果配置接口
  30. export interface HitEffectConfig {
  31. type: string;
  32. priority: number;
  33. damage?: number;
  34. pierceCount?: number;
  35. radius?: number;
  36. delay?: number;
  37. duration?: number;
  38. tickInterval?: number;
  39. ricochetCount?: number;
  40. ricochetAngle?: number;
  41. [key: string]: any;
  42. }
  43. // 弹药生命周期配置接口
  44. export interface BulletLifecycleConfig {
  45. type: string;
  46. maxLifetime: number;
  47. penetration: number;
  48. ricochetCount: number;
  49. returnToOrigin: boolean;
  50. returnDelay?: number; // 返回延迟(秒)
  51. maxRange?: number; // 最大射程
  52. effectDuration?: number; // 效果持续时间(用于地面效果)
  53. }
  54. // 武器配置接口
  55. export interface WeaponConfig {
  56. id: string;
  57. name: string;
  58. type: string;
  59. rarity?: string; // 可选字段,支持动态稀有度(合成时可变)
  60. stats: {
  61. damage: number;
  62. fireRate: number;
  63. range: number;
  64. bulletSpeed: number;
  65. accuracy?: number;
  66. };
  67. bulletConfig: {
  68. count: BulletCountConfig;
  69. trajectory: BulletTrajectoryConfig;
  70. hitEffects: HitEffectConfig[];
  71. lifecycle: BulletLifecycleConfig;
  72. visual: {
  73. bulletImages: string;
  74. hitEffect: string;
  75. trailEffect?: string;
  76. explosionEffect?: string;
  77. };
  78. };
  79. visualConfig: {
  80. weaponSprites: {
  81. [shapeId: string]: string;
  82. };
  83. attackSound: string;
  84. };
  85. upgradeConfig: {
  86. maxLevel: number;
  87. levels: {
  88. [level: string]: {
  89. cost: number;
  90. damage?: number;
  91. };
  92. };
  93. };
  94. // 运行时计算的实际数值(应用技能加成后)
  95. runtimeStats?: {
  96. finalDamage: number;
  97. finalCritDamage: number;
  98. critChance: number;
  99. };
  100. inGameCostConfig?: InGameCostConfig;
  101. }
  102. // 球控制器配置接口
  103. export interface BallControllerConfig {
  104. baseSpeed: number;
  105. maxReflectionRandomness: number;
  106. antiTrapTimeWindow: number;
  107. antiTrapHitThreshold: number;
  108. deflectionAttemptThreshold: number;
  109. antiTrapDeflectionMultiplier: number;
  110. FIRE_COOLDOWN: number;
  111. ballRadius: number;
  112. gravityScale: number;
  113. linearDamping: number;
  114. angularDamping: number;
  115. colliderGroup: number;
  116. colliderTag: number;
  117. friction: number;
  118. restitution: number;
  119. safeDistance: number;
  120. edgeOffset: number;
  121. sensor: boolean;
  122. maxAttempts: number;
  123. }
  124. // 敌人配置接口
  125. export interface EnemyConfig {
  126. id: string;
  127. name: string;
  128. type: string;
  129. stats: {
  130. health: number;
  131. maxHealth: number;
  132. defense: number;
  133. speed: number;
  134. dropEnergy: number;
  135. };
  136. movement: {
  137. pattern: string;
  138. speed: number;
  139. patrolRange: number;
  140. chaseRange: number;
  141. rotationSpeed: number;
  142. moveType: string;
  143. swingAmplitude: number;
  144. swingFrequency: number;
  145. speedVariation: number;
  146. };
  147. combat: {
  148. attackDamage: number;
  149. attackRange: number;
  150. attackSpeed: number;
  151. canBlock: boolean;
  152. blockChance: number;
  153. blockDamageReduction: number;
  154. attackCooldown: number;
  155. attackType: string;
  156. attackDelay: number;
  157. weaponType: string;
  158. projectileType: string;
  159. projectileSpeed: number;
  160. };
  161. visualConfig: {
  162. spritePath: string;
  163. scale: number;
  164. animationSpeed: number;
  165. flipX: boolean;
  166. tint: string;
  167. animations: {
  168. [state: string]: string;
  169. };
  170. weaponProp: string;
  171. };
  172. audioConfig: {
  173. attackSound: string;
  174. deathSound: string;
  175. hitSound: string;
  176. walkSound: string;
  177. blockSound: string;
  178. stealthSound: string;
  179. armorBreakSound: string;
  180. fuseSound: string;
  181. volume: number;
  182. };
  183. visual?: {
  184. sprite_path: string;
  185. scale: number;
  186. animation_speed: number;
  187. flip_horizontal: boolean;
  188. animations: {
  189. [state: string]: string;
  190. };
  191. weapon_prop: string;
  192. };
  193. audio?: {
  194. attack_sound: string;
  195. death_sound: string;
  196. hit_sound: string;
  197. walk_sound: string;
  198. block_sound: string;
  199. stealth_sound: string;
  200. armor_break_sound: string;
  201. fuse_sound: string;
  202. };
  203. boss: {
  204. is_boss: boolean;
  205. phases: number;
  206. rage_threshold: number;
  207. rage_damage_multiplier: number;
  208. rage_speed_multiplier: number;
  209. };
  210. special_abilities: Array<{
  211. type: string;
  212. damage: number;
  213. range: number;
  214. cooldown: number;
  215. }>
  216. }
  217. @ccclass('ConfigManager')
  218. export class ConfigManager extends BaseSingleton {
  219. // 仅用于类型声明,实例由 BaseSingleton 在运行时动态维护
  220. public static _instance: ConfigManager;
  221. private weaponsConfig: any = null;
  222. private enemiesConfig: any = null;
  223. private configLoaded: boolean = false;
  224. /**
  225. * BaseSingleton 首次实例化回调
  226. */
  227. protected init() {
  228. console.log('[ConfigManager] 开始初始化配置管理器');
  229. this.loadConfigs();
  230. }
  231. // 加载所有配置文件
  232. private async loadConfigs() {
  233. console.log('[ConfigManager] 开始加载配置文件');
  234. this.configLoaded = false;
  235. try {
  236. // 确保resources bundle已经准备好
  237. await this.ensureResourcesBundle();
  238. // 加载武器配置
  239. console.log('[ConfigManager] 开始加载武器配置');
  240. await this.loadWeaponsConfig();
  241. console.log('[ConfigManager] 武器配置加载完成');
  242. // 加载敌人配置
  243. console.log('[ConfigManager] 开始加载敌人配置');
  244. await this.loadEnemiesConfig();
  245. console.log('[ConfigManager] 敌人配置加载完成');
  246. // 注意:球控制器配置现在通过BallController组件的装饰器直接加载
  247. console.log('[ConfigManager] 球控制器配置通过装饰器加载,跳过ConfigManager加载');
  248. this.configLoaded = true;
  249. console.log('[ConfigManager] ✅ 所有配置文件加载成功,配置管理器初始化完成');
  250. } catch (error) {
  251. console.error('[ConfigManager] ❌ 配置文件加载失败:', error);
  252. this.configLoaded = false;
  253. // 延迟重试加载
  254. console.log('[ConfigManager] 将在3秒后重试加载配置');
  255. setTimeout(() => {
  256. console.log('[ConfigManager] 开始重试加载配置');
  257. this.loadConfigs();
  258. }, 3000);
  259. }
  260. }
  261. // 确保resources bundle已经准备好
  262. private ensureResourcesBundle(): Promise<void> {
  263. return new Promise((resolve) => {
  264. console.log('[ConfigManager] 检查resources bundle状态...');
  265. // 增加延迟时间,确保Cocos Creator资源系统完全初始化
  266. setTimeout(() => {
  267. console.log('[ConfigManager] resources bundle初始化等待完成');
  268. resolve();
  269. }, 2000); // 增加到2秒
  270. });
  271. }
  272. // 加载武器配置
  273. private loadWeaponsConfig(): Promise<void> {
  274. return new Promise((resolve, reject) => {
  275. console.log('[ConfigManager] 开始加载武器配置...');
  276. resources.load('data/weapons', JsonAsset, (err, asset) => {
  277. if (err) {
  278. console.error('[ConfigManager] 武器配置文件加载失败:', err);
  279. reject(err);
  280. return;
  281. }
  282. if (!asset || !asset.json) {
  283. console.error('[ConfigManager] 武器配置文件内容为空');
  284. reject(new Error('武器配置文件内容为空'));
  285. return;
  286. }
  287. this.weaponsConfig = asset.json as any;
  288. // 验证配置完整性
  289. console.log('[ConfigManager] 验证武器配置完整性...');
  290. if (!this.weaponsConfig.weapons || !Array.isArray(this.weaponsConfig.weapons)) {
  291. console.error('[ConfigManager] 武器配置格式错误:缺少weapons数组');
  292. reject(new Error('武器配置格式错误'));
  293. return;
  294. }
  295. console.log(`[ConfigManager] 武器配置加载成功,共${this.weaponsConfig.weapons.length}个武器`);
  296. // 检查blockSizes配置
  297. if (this.weaponsConfig.blockSizes && Array.isArray(this.weaponsConfig.blockSizes)) {
  298. console.log(`[ConfigManager] ✅ blockSizes配置加载成功,共${this.weaponsConfig.blockSizes.length}个形状`);
  299. this.weaponsConfig.blockSizes.forEach(shape => {
  300. console.log(`[ConfigManager] - 形状: ${shape.id} (${shape.name})`);
  301. });
  302. } else {
  303. console.warn('[ConfigManager] ⚠️ blockSizes配置缺失或格式错误');
  304. }
  305. resolve();
  306. });
  307. });
  308. }
  309. // 加载敌人配置
  310. private loadEnemiesConfig(): Promise<void> {
  311. return new Promise((resolve, reject) => {
  312. console.log('加载敌人配置...');
  313. resources.load('data/enemies', JsonAsset, (err, asset) => {
  314. if (err) {
  315. console.error('敌人配置文件加载失败:', err);
  316. reject(err);
  317. return;
  318. }
  319. if (!asset || !asset.json) {
  320. console.error('敌人配置文件内容为空');
  321. reject(new Error('敌人配置文件内容为空'));
  322. return;
  323. }
  324. this.enemiesConfig = asset.json as any;
  325. console.log('✅ 敌人配置加载成功');
  326. resolve();
  327. });
  328. });
  329. }
  330. // 随机获取武器配置
  331. public getRandomWeapon(rarity?: string): WeaponConfig | null {
  332. if (!this.weaponsConfig || !this.weaponsConfig.weapons || this.weaponsConfig.weapons.length === 0) {
  333. console.warn('武器配置未加载或为空');
  334. return null;
  335. }
  336. if (rarity) {
  337. // 按稀有度筛选
  338. const filteredWeapons = this.weaponsConfig.weapons.filter(weapon => weapon.rarity === rarity);
  339. if (filteredWeapons.length === 0) {
  340. console.warn(`没有找到稀有度为 ${rarity} 的武器`);
  341. return null;
  342. }
  343. const randomIndex = Math.floor(Math.random() * filteredWeapons.length);
  344. return filteredWeapons[randomIndex];
  345. }
  346. // 使用稀有度权重系统进行随机选择
  347. const rarityWeights = this.weaponsConfig.rarityWeights;
  348. if (!rarityWeights) {
  349. console.warn('稀有度权重配置未找到,使用随机选择');
  350. const randomIndex = Math.floor(Math.random() * this.weaponsConfig.weapons.length);
  351. return this.weaponsConfig.weapons[randomIndex];
  352. }
  353. // 计算总权重
  354. // 计算总权重(兼容ES5)
  355. const totalWeight = Object.keys(rarityWeights).reduce((sum: number, key: string) => sum + (rarityWeights[key] as number), 0);
  356. const randomValue = Math.random() * totalWeight;
  357. // 根据权重选择稀有度
  358. let currentWeight = 0;
  359. let selectedRarity = 'common';
  360. // 兼容ES5的写法遍历对象
  361. for (let rarity in rarityWeights) {
  362. if (rarityWeights.hasOwnProperty(rarity)) {
  363. const weight = rarityWeights[rarity];
  364. currentWeight += weight as number;
  365. if (randomValue <= currentWeight) {
  366. selectedRarity = rarity;
  367. break;
  368. }
  369. }
  370. // 从选中的稀有度中随机选择武器
  371. const weaponsOfRarity = this.weaponsConfig.weapons.filter(weapon => weapon.rarity === selectedRarity);
  372. if (weaponsOfRarity.length === 0) {
  373. console.warn(`稀有度 ${selectedRarity} 没有可用武器,使用随机选择`);
  374. const randomIndex = Math.floor(Math.random() * this.weaponsConfig.weapons.length);
  375. return this.weaponsConfig.weapons[randomIndex];
  376. }
  377. const randomIndex = Math.floor(Math.random() * weaponsOfRarity.length);
  378. const selectedWeapon = weaponsOfRarity[randomIndex];
  379. console.log(`随机选择武器: ${selectedWeapon.name} (${selectedRarity})`);
  380. return selectedWeapon;
  381. }
  382. }
  383. // 随机获取敌人配置
  384. public getRandomEnemy(): EnemyConfig | null {
  385. // 修复:enemies.json是直接的数组结构,不需要.enemies包装
  386. if (!this.enemiesConfig || !Array.isArray(this.enemiesConfig) || this.enemiesConfig.length === 0) {
  387. console.warn('敌人配置未加载或为空');
  388. return null;
  389. }
  390. // 直接从敌人列表中随机选择
  391. const randomIndex = Math.floor(Math.random() * this.enemiesConfig.length);
  392. return this.enemiesConfig[randomIndex];
  393. }
  394. // 根据ID获取武器配置
  395. public getWeaponById(id: string): WeaponConfig | null {
  396. if (!this.weaponsConfig) return null;
  397. return this.weaponsConfig.weapons.find(weapon => weapon.id === id) || null;
  398. }
  399. // 根据ID获取敌人配置
  400. public getEnemyById(id: string): EnemyConfig | null {
  401. // 修复:enemies.json是直接的数组结构,不需要.enemies包装
  402. if (!this.enemiesConfig || !Array.isArray(this.enemiesConfig)) return null;
  403. return this.enemiesConfig.find(enemy => enemy.id === id) || null;
  404. }
  405. /**
  406. * 获取敌人名称到ID的映射
  407. */
  408. public getNameToIdMapping(): { [key: string]: string } | null {
  409. if (!this.enemiesConfig) return null;
  410. return this.enemiesConfig.nameToIdMapping || null;
  411. }
  412. // 获取所有武器配置
  413. public getAllWeapons(): WeaponConfig[] {
  414. return this.weaponsConfig?.weapons || [];
  415. }
  416. // 获取所有敌人配置
  417. public getAllEnemies(): EnemyConfig[] {
  418. // 修复:enemies.json是直接的数组结构,不需要.enemies包装
  419. return Array.isArray(this.enemiesConfig) ? this.enemiesConfig : [];
  420. }
  421. // 根据稀有度获取武器列表
  422. public getWeaponsByRarity(rarity: string): WeaponConfig[] {
  423. if (!this.weaponsConfig) return [];
  424. return this.weaponsConfig.weapons.filter(weapon => weapon.rarity === rarity);
  425. }
  426. // 获取方块尺寸列表(已更新为形状ID)
  427. public getBlockSizes(): string[] {
  428. if (!this.weaponsConfig || !this.weaponsConfig.blockSizes) {
  429. return ['I', 'H-I', 'L', 'S', 'D-T'];
  430. }
  431. // 从blockSizes配置中提取形状ID
  432. return this.weaponsConfig.blockSizes.map((shape: any) => shape.id);
  433. }
  434. // 获取方块形状配置列表
  435. public getBlockShapes(): any[] {
  436. if (!this.weaponsConfig) {
  437. console.warn('[ConfigManager] 武器配置未加载,无法获取方块形状配置');
  438. console.warn('[ConfigManager] 配置加载状态:', this.configLoaded);
  439. return [];
  440. }
  441. if (!this.weaponsConfig.blockSizes) {
  442. console.warn('[ConfigManager] 方块形状配置(blockSizes)未找到');
  443. console.warn('[ConfigManager] 可用的配置字段:', Object.keys(this.weaponsConfig));
  444. return [];
  445. }
  446. if (!Array.isArray(this.weaponsConfig.blockSizes)) {
  447. console.error('[ConfigManager] blockSizes不是数组格式:', typeof this.weaponsConfig.blockSizes);
  448. return [];
  449. }
  450. console.log(`[ConfigManager] 成功获取${this.weaponsConfig.blockSizes.length}个方块形状配置`);
  451. return this.weaponsConfig.blockSizes;
  452. }
  453. // 获取波次进展配置(已废弃,波次配置现在通过关卡系统管理)
  454. public getWaveProgression(): any {
  455. console.warn('[ConfigManager] getWaveProgression已废弃,波次配置现在通过关卡系统管理');
  456. return {};
  457. }
  458. // 根据波次获取合适的敌人(简化版本,直接返回随机敌人)
  459. public getEnemyForWave(waveNumber: number): EnemyConfig | null {
  460. // 修复:波次配置现在通过关卡系统管理,这里简化为返回随机敌人
  461. // 具体的波次敌人配置由EnemyController通过关卡配置处理
  462. console.log(`[ConfigManager] 为波次 ${waveNumber} 获取随机敌人`);
  463. return this.getRandomEnemy();
  464. }
  465. // 检查配置是否已加载
  466. public isConfigLoaded(): boolean {
  467. return this.configLoaded;
  468. }
  469. // 获取球控制器配置
  470. // getBallControllerConfig方法已移除,球控制器配置现在通过装饰器直接在BallController中加载
  471. }