diff --git a/assets/script/game/common/config/CardSet.ts b/assets/script/game/common/config/CardSet.ts index 8f80f719..b7fa6511 100644 --- a/assets/script/game/common/config/CardSet.ts +++ b/assets/script/game/common/config/CardSet.ts @@ -253,98 +253,58 @@ function getDefaultPool(type: CardType, level: number = 1): IPoolItem[] { * @param forcedType 强制指定卡牌类型 (用于特殊获取,如商店、技能书等) */ export function getCardOptions(level: number, count: number = 3, excludeUuids: number[] = [], forcedType?: CardType): ICardInfo[] { - // 1. 获取该等级的池配置 - // 如果强制指定类型,则构造一个只包含该类型的配置 - const initialPoolConfigs = forcedType - ? [{ type: forcedType, poolWeight: 100 }] - : (LevelPoolConfigs[level] || [{ type: CardType.Attr, poolWeight: 100 }]); + // 1. 确定类型:强制指定 > 默认为属性 + const type = forcedType !== undefined ? forcedType : CardType.Attr; const result: ICardInfo[] = []; const excludeSet = new Set(excludeUuids); - // 循环获取 count 张卡牌 - for (let i = 0; i < count; i++) { - // 在每一轮获取中,我们使用一个临时的配置列表 - // 这样如果某个类型池空了,我们可以从临时列表中移除它,避免重复选中 - let currentConfigs = [...initialPoolConfigs]; - let cardFound = false; - - while (currentConfigs.length > 0) { - // 2.1 随机决定本次抽取的类型池 - const selectedConfig = weightedRandomPool(currentConfigs); - if (!selectedConfig) break; // 理论上不会发生 - - // 2.2 获取该类型的所有候选卡牌 - // 直接使用默认全池 (传入level以获取该等级特定的默认配置) - const rawCandidates = getDefaultPool(selectedConfig.type, level); - - // 2.3 过滤与构建完整信息 - const validCandidates: ICardInfo[] = []; - for (const item of rawCandidates) { - // 排除全局排除项 - if (excludeSet.has(item.id)) continue; - // 排除本轮已选项 - if (result.find(r => r.uuid === item.id)) continue; - - // 获取详情 - const info = getCardBaseInfo(selectedConfig.type, item.id); - if (!info) continue; - - // Tag 过滤 - if (selectedConfig.tag && info.tag !== selectedConfig.tag) { - continue; - } - - // 赋予配置的权重 - info.weight = item.weight; - validCandidates.push(info); - } - - // 2.4 检查该类型是否有可用卡牌 - if (validCandidates.length > 0) { - // 有卡!随机抽取一张 - const card = weightedRandomCard(validCandidates); - if (card) { - result.push(card); - cardFound = true; - break; // 成功获取一张,跳出内层循环,进行下一张的获取 - } - } else { - // 没卡!从当前配置中移除这个类型,重试 - const index = currentConfigs.indexOf(selectedConfig); - if (index > -1) { - currentConfigs.splice(index, 1); - } - // 继续 while 循环,重新随机类型 - } - } - - // 2.5 如果尝试了所有类型都没找到卡 (极少见兜底) - if (!cardFound) { - // 尝试从属性池硬拿一个不重复的 - const attrItems = getDefaultPool(CardType.Attr); - for (const item of attrItems) { - if (excludeSet.has(item.id) || result.find(r => r.uuid === item.id)) continue; - const info = getCardBaseInfo(CardType.Attr, item.id); - if (info) { - info.weight = 100; - result.push(info); - cardFound = true; - break; - } - } - } + // 2. 获取该类型的候选池 + const rawItems = getDefaultPool(type, level); + + // 3. 构建候选列表(包含完整信息) + const candidates: ICardInfo[] = []; + for (const item of rawItems) { + if (excludeSet.has(item.id)) continue; - // 如果连兜底都找不到(比如所有属性都拿完了),那也没办法了,可能返回少于 count 张 - if (!cardFound) { - console.warn(`[CardSet] 无法为等级 ${level} 找到足够的卡牌选项,当前已选: ${result.length}/${count}`); - break; + const info = getCardBaseInfo(type, item.id); + if (info) { + info.weight = item.weight; + candidates.push(info); } } - // 3. 最终结果洗牌 (虽然逻辑上已经是随机的,但洗牌可以打乱类型顺序) - shuffleArray(result); + // 4. 抽取 + for (let i = 0; i < count; i++) { + if (candidates.length === 0) break; + + const card = weightedRandomCard(candidates); + if (card) { + result.push(card); + // 移除已选,防止重复 + const idx = candidates.indexOf(card); + if (idx > -1) candidates.splice(idx, 1); + } + } + + // 5. 兜底逻辑:如果数量不足,且当前类型不是属性,则用属性卡补齐 + if (result.length < count && type !== CardType.Attr) { + const attrItems = getDefaultPool(CardType.Attr); // 兜底可以用全量属性池 + for (const item of attrItems) { + if (result.length >= count) break; + + // 检查是否已存在(ID去重) + if (excludeSet.has(item.id) || result.some(r => r.uuid === item.id)) continue; + + const info = getCardBaseInfo(CardType.Attr, item.id); + if (info) { + info.weight = 100; + result.push(info); + } + } + } + shuffleArray(result); return result; }