ConfigManager.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  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. rarity: string;
  130. health: number;
  131. speed: number;
  132. attack: number;
  133. range: number;
  134. attackSpeed: number;
  135. defense: number;
  136. goldReward: number;
  137. explosionDamage?: number;
  138. explosionRadius?: number;
  139. movement: {
  140. type: string;
  141. pattern: string;
  142. speedVariation: number;
  143. swayAmplitude?: number;
  144. swayFrequency?: number;
  145. };
  146. combat: {
  147. attackType: string;
  148. attackDelay: number;
  149. attackCooldown: number;
  150. weaponType?: string;
  151. projectileType?: string;
  152. projectileSpeed?: number;
  153. canBlock: boolean;
  154. blockChance: number;
  155. };
  156. visualConfig: {
  157. spritePath: string;
  158. animations: {
  159. [state: string]: string;
  160. };
  161. scale: number;
  162. flipX: boolean;
  163. weapon?: string;
  164. armor?: string;
  165. prop?: string;
  166. };
  167. audioConfig: {
  168. [sound: string]: string;
  169. };
  170. specialAbilities: string[];
  171. stealthConfig?: {
  172. stealthDuration: number;
  173. stealthCooldown: number;
  174. revealOnAttack: boolean;
  175. visibilityAlpha: number;
  176. };
  177. armorConfig?: {
  178. armorHealth: number;
  179. armorReduction: number;
  180. breakThreshold: number;
  181. };
  182. explosionConfig?: {
  183. explosionDelay: number;
  184. explosionEffect: string;
  185. damageRadius: number;
  186. knockbackForce: number;
  187. };
  188. bossConfig?: {
  189. phases: number;
  190. phaseHealthThreshold: number;
  191. enrageBonus?: {
  192. speed: number;
  193. damage: number;
  194. attackSpeed: number;
  195. };
  196. summonAbility?: {
  197. minionType: string;
  198. summonCount: number;
  199. summonCooldown: number;
  200. };
  201. laserAbility?: {
  202. damage: number;
  203. range: number;
  204. chargeTime: number;
  205. cooldown: number;
  206. };
  207. };
  208. projectileConfig?: {
  209. bulletImages: string;
  210. hitEffect: string;
  211. trailEffect?: string;
  212. };
  213. }
  214. @ccclass('ConfigManager')
  215. export class ConfigManager extends BaseSingleton {
  216. // 仅用于类型声明,实例由 BaseSingleton 在运行时动态维护
  217. public static _instance: ConfigManager;
  218. private weaponsConfig: any = null;
  219. private enemiesConfig: any = null;
  220. private configLoaded: boolean = false;
  221. /**
  222. * BaseSingleton 首次实例化回调
  223. */
  224. protected init() {
  225. console.log('[ConfigManager] 开始初始化配置管理器');
  226. this.loadConfigs();
  227. }
  228. // 加载所有配置文件
  229. private async loadConfigs() {
  230. console.log('[ConfigManager] 开始加载配置文件');
  231. this.configLoaded = false;
  232. try {
  233. // 确保resources bundle已经准备好
  234. await this.ensureResourcesBundle();
  235. // 加载武器配置
  236. console.log('[ConfigManager] 开始加载武器配置');
  237. await this.loadWeaponsConfig();
  238. console.log('[ConfigManager] 武器配置加载完成');
  239. // 加载敌人配置
  240. console.log('[ConfigManager] 开始加载敌人配置');
  241. await this.loadEnemiesConfig();
  242. console.log('[ConfigManager] 敌人配置加载完成');
  243. // 注意:球控制器配置现在通过BallController组件的装饰器直接加载
  244. console.log('[ConfigManager] 球控制器配置通过装饰器加载,跳过ConfigManager加载');
  245. this.configLoaded = true;
  246. console.log('[ConfigManager] ✅ 所有配置文件加载成功,配置管理器初始化完成');
  247. } catch (error) {
  248. console.error('[ConfigManager] ❌ 配置文件加载失败:', error);
  249. this.configLoaded = false;
  250. // 延迟重试加载
  251. console.log('[ConfigManager] 将在3秒后重试加载配置');
  252. setTimeout(() => {
  253. console.log('[ConfigManager] 开始重试加载配置');
  254. this.loadConfigs();
  255. }, 3000);
  256. }
  257. }
  258. // 确保resources bundle已经准备好
  259. private ensureResourcesBundle(): Promise<void> {
  260. return new Promise((resolve) => {
  261. console.log('[ConfigManager] 检查resources bundle状态...');
  262. // 增加延迟时间,确保Cocos Creator资源系统完全初始化
  263. setTimeout(() => {
  264. console.log('[ConfigManager] resources bundle初始化等待完成');
  265. resolve();
  266. }, 2000); // 增加到2秒
  267. });
  268. }
  269. // 加载武器配置
  270. private loadWeaponsConfig(): Promise<void> {
  271. return new Promise((resolve, reject) => {
  272. console.log('[ConfigManager] 开始加载武器配置...');
  273. resources.load('data/weapons', JsonAsset, (err, asset) => {
  274. if (err) {
  275. console.error('[ConfigManager] 武器配置文件加载失败:', err);
  276. reject(err);
  277. return;
  278. }
  279. if (!asset || !asset.json) {
  280. console.error('[ConfigManager] 武器配置文件内容为空');
  281. reject(new Error('武器配置文件内容为空'));
  282. return;
  283. }
  284. this.weaponsConfig = asset.json as any;
  285. // 验证配置完整性
  286. console.log('[ConfigManager] 验证武器配置完整性...');
  287. if (!this.weaponsConfig.weapons || !Array.isArray(this.weaponsConfig.weapons)) {
  288. console.error('[ConfigManager] 武器配置格式错误:缺少weapons数组');
  289. reject(new Error('武器配置格式错误'));
  290. return;
  291. }
  292. console.log(`[ConfigManager] 武器配置加载成功,共${this.weaponsConfig.weapons.length}个武器`);
  293. // 检查blockSizes配置
  294. if (this.weaponsConfig.blockSizes && Array.isArray(this.weaponsConfig.blockSizes)) {
  295. console.log(`[ConfigManager] ✅ blockSizes配置加载成功,共${this.weaponsConfig.blockSizes.length}个形状`);
  296. this.weaponsConfig.blockSizes.forEach(shape => {
  297. console.log(`[ConfigManager] - 形状: ${shape.id} (${shape.name})`);
  298. });
  299. } else {
  300. console.warn('[ConfigManager] ⚠️ blockSizes配置缺失或格式错误');
  301. }
  302. resolve();
  303. });
  304. });
  305. }
  306. // 加载敌人配置
  307. private loadEnemiesConfig(): Promise<void> {
  308. return new Promise((resolve, reject) => {
  309. console.log('加载敌人配置...');
  310. resources.load('data/enemies', JsonAsset, (err, asset) => {
  311. if (err) {
  312. console.error('敌人配置文件加载失败:', err);
  313. reject(err);
  314. return;
  315. }
  316. if (!asset || !asset.json) {
  317. console.error('敌人配置文件内容为空');
  318. reject(new Error('敌人配置文件内容为空'));
  319. return;
  320. }
  321. this.enemiesConfig = asset.json as any;
  322. console.log('✅ 敌人配置加载成功');
  323. resolve();
  324. });
  325. });
  326. }
  327. // 随机获取武器配置
  328. public getRandomWeapon(rarity?: string): WeaponConfig | null {
  329. if (!this.weaponsConfig || !this.weaponsConfig.weapons || this.weaponsConfig.weapons.length === 0) {
  330. console.warn('武器配置未加载或为空');
  331. return null;
  332. }
  333. if (rarity) {
  334. // 按稀有度筛选
  335. const filteredWeapons = this.weaponsConfig.weapons.filter(weapon => weapon.rarity === rarity);
  336. if (filteredWeapons.length === 0) {
  337. console.warn(`没有找到稀有度为 ${rarity} 的武器`);
  338. return null;
  339. }
  340. const randomIndex = Math.floor(Math.random() * filteredWeapons.length);
  341. return filteredWeapons[randomIndex];
  342. }
  343. // 使用稀有度权重系统进行随机选择
  344. const rarityWeights = this.weaponsConfig.rarityWeights;
  345. if (!rarityWeights) {
  346. console.warn('稀有度权重配置未找到,使用随机选择');
  347. const randomIndex = Math.floor(Math.random() * this.weaponsConfig.weapons.length);
  348. return this.weaponsConfig.weapons[randomIndex];
  349. }
  350. // 计算总权重
  351. // 计算总权重(兼容ES5)
  352. const totalWeight = Object.keys(rarityWeights).reduce((sum: number, key: string) => sum + (rarityWeights[key] as number), 0);
  353. const randomValue = Math.random() * totalWeight;
  354. // 根据权重选择稀有度
  355. let currentWeight = 0;
  356. let selectedRarity = 'common';
  357. // 兼容ES5的写法遍历对象
  358. for (let rarity in rarityWeights) {
  359. if (rarityWeights.hasOwnProperty(rarity)) {
  360. const weight = rarityWeights[rarity];
  361. currentWeight += weight as number;
  362. if (randomValue <= currentWeight) {
  363. selectedRarity = rarity;
  364. break;
  365. }
  366. }
  367. // 从选中的稀有度中随机选择武器
  368. const weaponsOfRarity = this.weaponsConfig.weapons.filter(weapon => weapon.rarity === selectedRarity);
  369. if (weaponsOfRarity.length === 0) {
  370. console.warn(`稀有度 ${selectedRarity} 没有可用武器,使用随机选择`);
  371. const randomIndex = Math.floor(Math.random() * this.weaponsConfig.weapons.length);
  372. return this.weaponsConfig.weapons[randomIndex];
  373. }
  374. const randomIndex = Math.floor(Math.random() * weaponsOfRarity.length);
  375. const selectedWeapon = weaponsOfRarity[randomIndex];
  376. console.log(`随机选择武器: ${selectedWeapon.name} (${selectedRarity})`);
  377. return selectedWeapon;
  378. }
  379. }
  380. // 随机获取敌人配置
  381. public getRandomEnemy(rarity?: string): EnemyConfig | null {
  382. // 修复:enemies.json是直接的数组结构,不需要.enemies包装
  383. if (!this.enemiesConfig || !Array.isArray(this.enemiesConfig) || this.enemiesConfig.length === 0) {
  384. console.warn('敌人配置未加载或为空');
  385. return null;
  386. }
  387. if (rarity) {
  388. // 按稀有度筛选
  389. const filteredEnemies = this.enemiesConfig.filter(enemy => enemy.rarity === rarity);
  390. if (filteredEnemies.length === 0) {
  391. console.warn(`没有找到稀有度为 ${rarity} 的敌人`);
  392. return null;
  393. }
  394. const randomIndex = Math.floor(Math.random() * filteredEnemies.length);
  395. return filteredEnemies[randomIndex];
  396. }
  397. // 直接从敌人列表中随机选择
  398. const randomIndex = Math.floor(Math.random() * this.enemiesConfig.length);
  399. return this.enemiesConfig[randomIndex];
  400. }
  401. // 根据ID获取武器配置
  402. public getWeaponById(id: string): WeaponConfig | null {
  403. if (!this.weaponsConfig) return null;
  404. return this.weaponsConfig.weapons.find(weapon => weapon.id === id) || null;
  405. }
  406. // 根据ID获取敌人配置
  407. public getEnemyById(id: string): EnemyConfig | null {
  408. // 修复:enemies.json是直接的数组结构,不需要.enemies包装
  409. if (!this.enemiesConfig || !Array.isArray(this.enemiesConfig)) return null;
  410. return this.enemiesConfig.find(enemy => enemy.id === id) || null;
  411. }
  412. /**
  413. * 获取敌人名称到ID的映射
  414. */
  415. public getNameToIdMapping(): { [key: string]: string } | null {
  416. if (!this.enemiesConfig) return null;
  417. return this.enemiesConfig.nameToIdMapping || null;
  418. }
  419. // 获取所有武器配置
  420. public getAllWeapons(): WeaponConfig[] {
  421. return this.weaponsConfig?.weapons || [];
  422. }
  423. // 获取所有敌人配置
  424. public getAllEnemies(): EnemyConfig[] {
  425. // 修复:enemies.json是直接的数组结构,不需要.enemies包装
  426. return Array.isArray(this.enemiesConfig) ? this.enemiesConfig : [];
  427. }
  428. // 根据稀有度获取武器列表
  429. public getWeaponsByRarity(rarity: string): WeaponConfig[] {
  430. if (!this.weaponsConfig) return [];
  431. return this.weaponsConfig.weapons.filter(weapon => weapon.rarity === rarity);
  432. }
  433. // 根据稀有度获取敌人列表
  434. public getEnemiesByRarity(rarity: string): EnemyConfig[] {
  435. // 修复:enemies.json是直接的数组结构,不需要.enemies包装
  436. if (!this.enemiesConfig || !Array.isArray(this.enemiesConfig)) return [];
  437. return this.enemiesConfig.filter(enemy => enemy.rarity === rarity);
  438. }
  439. // 获取方块尺寸列表(已更新为形状ID)
  440. public getBlockSizes(): string[] {
  441. if (!this.weaponsConfig || !this.weaponsConfig.blockSizes) {
  442. return ['I', 'H-I', 'L', 'S', 'D-T'];
  443. }
  444. // 从blockSizes配置中提取形状ID
  445. return this.weaponsConfig.blockSizes.map((shape: any) => shape.id);
  446. }
  447. // 获取方块形状配置列表
  448. public getBlockShapes(): any[] {
  449. if (!this.weaponsConfig) {
  450. console.warn('[ConfigManager] 武器配置未加载,无法获取方块形状配置');
  451. console.warn('[ConfigManager] 配置加载状态:', this.configLoaded);
  452. return [];
  453. }
  454. if (!this.weaponsConfig.blockSizes) {
  455. console.warn('[ConfigManager] 方块形状配置(blockSizes)未找到');
  456. console.warn('[ConfigManager] 可用的配置字段:', Object.keys(this.weaponsConfig));
  457. return [];
  458. }
  459. if (!Array.isArray(this.weaponsConfig.blockSizes)) {
  460. console.error('[ConfigManager] blockSizes不是数组格式:', typeof this.weaponsConfig.blockSizes);
  461. return [];
  462. }
  463. console.log(`[ConfigManager] 成功获取${this.weaponsConfig.blockSizes.length}个方块形状配置`);
  464. return this.weaponsConfig.blockSizes;
  465. }
  466. // 获取波次进展配置(已废弃,波次配置现在通过关卡系统管理)
  467. public getWaveProgression(): any {
  468. console.warn('[ConfigManager] getWaveProgression已废弃,波次配置现在通过关卡系统管理');
  469. return {};
  470. }
  471. // 根据波次获取合适的敌人(简化版本,直接返回随机敌人)
  472. public getEnemyForWave(waveNumber: number): EnemyConfig | null {
  473. // 修复:波次配置现在通过关卡系统管理,这里简化为返回随机敌人
  474. // 具体的波次敌人配置由EnemyController通过关卡配置处理
  475. console.log(`[ConfigManager] 为波次 ${waveNumber} 获取随机敌人`);
  476. return this.getRandomEnemy();
  477. }
  478. // 检查配置是否已加载
  479. public isConfigLoaded(): boolean {
  480. return this.configLoaded;
  481. }
  482. // 获取球控制器配置
  483. // getBallControllerConfig方法已移除,球控制器配置现在通过装饰器直接在BallController中加载
  484. }