refactor: 重构英雄养成与卡牌系统,移除旧合成机制
1. 移除三合一英雄合成与链式合成逻辑,统一使用升级卡进行英雄等级提升 2. 重构卡池系统:移除卡池等级机制,所有英雄卡牌统一为LV1 3. 重构升级逻辑:改为按UUID精确升级场上英雄,动态生成对应升级卡 4. 更新配置常量:拆分并重构成长倍率、英雄上限等战斗配置 5. 简化抽卡逻辑:不再按卡池等级分发卡牌,改为动态混合基础卡与升级卡 6. 清理废弃代码:移除卡池升级相关的UI、逻辑与配置
This commit is contained in:
@@ -47,28 +47,32 @@ export enum CardSkillType {
|
||||
HeroCall = 6, // 场上己方英雄召唤上场时触发
|
||||
}
|
||||
|
||||
/** 卡池等级定义 */
|
||||
export enum CardLV {
|
||||
LV1 = 1,
|
||||
LV2 = 2,
|
||||
LV3 = 3,
|
||||
LV4 = 4,
|
||||
LV5 = 5,
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡牌技能触发类型
|
||||
* - 命名对齐英雄侧 SkillTriggerType,便于跨模块认知统一
|
||||
* - 枚举值从 1 开始,避免 0 的 falsy 坑(if (trigger_type) 判断出错)
|
||||
*/
|
||||
export enum CardTriggerType {
|
||||
Instant = 1, // 即时触发:使用后立即生效一次
|
||||
Interval = 2, // 定时循环:战斗中按 t_inv 间隔重复触发
|
||||
Field = 3, // 驻场光环:被动生效(仅显式分类,仍由 field 字段驱动)
|
||||
Instant = 1, // 即时触发:使用后立即生效一次
|
||||
Interval = 2, // 定时循环:战斗中按 t_inv 间隔重复触发
|
||||
Field = 3, // 驻场光环:被动生效(仅显式分类,仍由 field 字段驱动)
|
||||
FightStart = 4, // 战斗开始时触发
|
||||
FightEnd = 5, // 战斗结束时触发(每波结束)
|
||||
HeroDead = 6, // 场上己方英雄死亡时触发
|
||||
HeroCall = 7, // 英雄上场时触发(主角召唤 + 技能召唤 + 复活)
|
||||
FightEnd = 5, // 战斗结束时触发(每波结束)
|
||||
HeroDead = 6, // 场上己方英雄死亡时触发
|
||||
HeroCall = 7, // 英雄上场时触发(主角召唤 + 技能召唤 + 复活)
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡池等级占位枚举(已废弃分层语义)。
|
||||
* 新机制下所有英雄卡均属 LV1,不再使用卡池升级。
|
||||
* 保留枚举仅为兼容历史代码引用。
|
||||
*/
|
||||
export enum CardLV {
|
||||
LV1 = 1,
|
||||
LV2 = 2,
|
||||
LV3 = 3,
|
||||
LV4 = 4,
|
||||
LV5 = 5,
|
||||
}
|
||||
|
||||
/** 通用卡牌配置 */
|
||||
@@ -104,7 +108,16 @@ export interface CardConfig {
|
||||
* 注意:与 t_times 语义不同——t_times 控制每波内 Interval 的次数
|
||||
*/
|
||||
trigger_limit?: number;
|
||||
|
||||
/**
|
||||
* 动态升级卡专属:目标英雄 UUID。
|
||||
* 仅当 type === CardType.SpecialUpgrade 且该卡由 buildDrawCards 动态生成时使用。
|
||||
* 使用该卡时精确升级场上对应 UUID 的英雄。
|
||||
*/
|
||||
target_hero_uuid?: number;
|
||||
}
|
||||
|
||||
/** 升级卡折扣表(已废弃,保留以兼容历史引用) */
|
||||
export const CardsUpSet: Record<number, number> = {
|
||||
1: 50,
|
||||
2: 100,
|
||||
@@ -113,70 +126,43 @@ export const CardsUpSet: Record<number, number> = {
|
||||
5: 250,
|
||||
}
|
||||
|
||||
/** 卡池升级每波减免金额 */
|
||||
/** 卡池升级每波减免金额(已废弃,保留以兼容历史引用) */
|
||||
export const CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE = 10
|
||||
/** 卡池默认初始等级 */
|
||||
/** 卡池默认初始等级(已废弃,所有卡牌统一 LV1) */
|
||||
export const CARD_POOL_INIT_LEVEL = CardLV.LV1
|
||||
/** 卡池等级上限(统一由 FightSet.MAX_CARD_POOL_LEVEL 设定,保持单一数据源) */
|
||||
export const CARD_POOL_MAX_LEVEL = FightSet.MAX_CARD_POOL_LEVEL as unknown as CardLV
|
||||
/** 英雄最高等级限制 */
|
||||
/** 卡池等级上限(已废弃,所有卡牌统一 LV1) */
|
||||
export const CARD_POOL_MAX_LEVEL = CardLV.LV1
|
||||
/** 英雄最高等级限制(已废弃,统一由 FightSet.HERO_MAX_LV 控制) */
|
||||
export const CARD_HERO_MAX_LEVEL = 1
|
||||
/** 基础卡池(英雄、技能、功能) */
|
||||
|
||||
/** 基础卡池(英雄、技能、功能) */
|
||||
export const CardPoolList: CardConfig[] = [];
|
||||
|
||||
// 动态生成英雄卡池
|
||||
// 动态生成英雄卡池:所有英雄统一生成 lv1 卡牌
|
||||
HeroList.forEach(uuid => {
|
||||
const hero = HeroInfo[uuid];
|
||||
if (!hero) return;
|
||||
|
||||
const basePoolLv = hero.pool_lv || 1;
|
||||
const baseHeroLv = hero.lv || 1;
|
||||
const baseCost = FightSet.BASE_COST;
|
||||
const baseWeight = 25;
|
||||
|
||||
// 生成从 basePoolLv 到 CARD_POOL_MAX_LEVEL 的卡牌
|
||||
for (let pLv = basePoolLv; pLv <= CARD_POOL_MAX_LEVEL; pLv++) {
|
||||
const offset = pLv - basePoolLv;
|
||||
const targetHeroLv = baseHeroLv + offset;
|
||||
|
||||
// 【修改开始】永远只刷 lv1 等级的英雄卡牌,不再出现某英雄的 lv2 等级卡牌
|
||||
// 设置为 true 则开启该限制。保留原有代码逻辑以便后续有变直接引用。
|
||||
const ONLY_SPAWN_LV1_HERO = true;
|
||||
if (ONLY_SPAWN_LV1_HERO && targetHeroLv > 1) {
|
||||
break;
|
||||
}
|
||||
// 【修改结束】
|
||||
|
||||
// 英雄的最高等级 是MERGE_MAX-1
|
||||
if (targetHeroLv > FightSet.MERGE_MAX - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// cost = baseCost * 3^(lv-1): Lv1=5, Lv2=15, Lv3=45
|
||||
let cost = baseCost;
|
||||
if (targetHeroLv > 1) {
|
||||
cost = baseCost * Math.pow(FightSet.MERGE_NEED, targetHeroLv - 1);
|
||||
}
|
||||
|
||||
CardPoolList.push({
|
||||
uuid: hero.uuid,
|
||||
type: CardType.Hero,
|
||||
cost: cost,
|
||||
weight: baseWeight,
|
||||
pool_lv: pLv as CardLV,
|
||||
kind: CKind.Hero,
|
||||
hero_lv: targetHeroLv,
|
||||
base_pool_lv: basePoolLv
|
||||
});
|
||||
}
|
||||
CardPoolList.push({
|
||||
uuid: hero.uuid,
|
||||
type: CardType.Hero,
|
||||
cost: baseCost,
|
||||
weight: baseWeight,
|
||||
pool_lv: CardLV.LV1,
|
||||
kind: CKind.Hero,
|
||||
hero_lv: 1,
|
||||
base_pool_lv: 1,
|
||||
});
|
||||
});
|
||||
|
||||
// 添加非英雄卡牌 (技能、功能卡)
|
||||
// 体系:wave 由 SKILL_CARD_WAVES 统一配置,每档强度递增(Field 靠 field uuid 区分数值,Interval 靠 overrides 覆写)
|
||||
// wave→pool_lv 映射由 SKILL_CARD_WAVES 索引+1 自动生成(wave 1→lv1, wave 5→lv2, wave 8→lv3)
|
||||
const waveToPoolLv: Record<number, number> = {};
|
||||
SKILL_CARD_WAVES.forEach((w, i) => { waveToPoolLv[w] = i + 1; });
|
||||
// wave→card_lv 映射由 SKILL_CARD_WAVES 索引+1 自动生成(wave 1→lv1, wave 5→lv2, wave 8→lv3)
|
||||
const waveToCardLv: Record<number, number> = {};
|
||||
SKILL_CARD_WAVES.forEach((w, i) => { waveToCardLv[w] = i + 1; });
|
||||
|
||||
const SkillCardData: any[] = [
|
||||
// ==================== wave 1 档(基础强度) ====================
|
||||
@@ -214,7 +200,7 @@ const SkillCardData: any[] = [
|
||||
{ uuid: 8265, skill: 6205, wave: SKILL_CARD_WAVES[1], name: "风墙+", info: "召唤风墙困住敌人,有概率击晕", is_inst: false, t_inv: 5, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 150 } },
|
||||
{ uuid: 8266, skill: 6206, wave: SKILL_CARD_WAVES[1], name: "陨石术+", info: "召唤陨石范围攻击敌人,有概率击晕", is_inst: false, t_inv: 5, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 150 } },
|
||||
|
||||
|
||||
|
||||
{ uuid: 8760, skill: 0, wave: SKILL_CARD_WAVES[1], name: "金币收益+", info: "每回合金币收益+2", is_inst: false, keep_waves: -1, field: [7210], trigger_type: CardTriggerType.Field },
|
||||
{ uuid: 8761, skill: 0, wave: SKILL_CARD_WAVES[1], name: "购买优惠+", info: "购买卡牌费用-2金币", is_inst: false, keep_waves: -1, field: [7212], trigger_type: CardTriggerType.Field },
|
||||
{ uuid: 8762, skill: 0, wave: SKILL_CARD_WAVES[1], name: "刷新优惠", info: "刷新卡牌费用-1金币", is_inst: false, keep_waves: -1, field: [7213], trigger_type: CardTriggerType.Field },
|
||||
@@ -239,7 +225,7 @@ const SkillCardData: any[] = [
|
||||
{ uuid: 8365, skill: 6205, wave: SKILL_CARD_WAVES[2], name: "风墙++", info: "召唤风墙困住敌人,有概率击晕", is_inst: false, t_inv: 4, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 250 } },
|
||||
{ uuid: 8366, skill: 6206, wave: SKILL_CARD_WAVES[2], name: "陨石术++", info: "召唤陨石范围攻击敌人,有概率击晕", is_inst: false, t_inv: 4, keep_waves: -1, trigger_type: CardTriggerType.Interval, overrides: { ap: 250 } },
|
||||
|
||||
|
||||
|
||||
{ uuid: 8810, skill: 0, wave: SKILL_CARD_WAVES[2], name: "金币收益++", info: "每回合金币收益+3", is_inst: false, keep_waves: -1, field: [7410], trigger_type: CardTriggerType.Field },
|
||||
|
||||
{ uuid: 8811, skill: 0, wave: SKILL_CARD_WAVES[2], name: "召唤强化++", info: "召唤触发技能次数+1", is_inst: false, keep_waves: -1, field: [7014], trigger_type: CardTriggerType.Field },
|
||||
@@ -252,27 +238,28 @@ const SkillCardData: any[] = [
|
||||
];
|
||||
|
||||
SkillCardData.forEach(data => {
|
||||
const cardLv = waveToCardLv[data.wave] ?? 1;
|
||||
CardPoolList.push({
|
||||
uuid: data.uuid,
|
||||
skill: data.skill || undefined,
|
||||
type: CardType.Skill,
|
||||
cost: 0,
|
||||
weight: 10,
|
||||
pool_lv: waveToPoolLv[data.wave] as CardLV,
|
||||
pool_lv: CardLV.LV1,
|
||||
wave: data.wave,
|
||||
kind: CKind.Skill,
|
||||
card_lv: waveToPoolLv[data.wave], // wave 1→1, 5→2, 8→3
|
||||
card_lv: cardLv,
|
||||
name: data.name,
|
||||
info: data.info,
|
||||
icon: data.icon, // 【新增】透传自定义图标ID(优先级最高)
|
||||
icon: data.icon,
|
||||
is_inst: data.is_inst,
|
||||
t_times: data.t_times || (data.is_inst ? 1 : 999),
|
||||
t_inv: data.t_inv || 0,
|
||||
keep_waves: data.keep_waves,
|
||||
field: data.field,
|
||||
overrides: data.overrides, // 【修复】原遗漏
|
||||
trigger_type: data.trigger_type, // 【新增】显式触发类型
|
||||
trigger_limit: data.trigger_limit, // 【新增】事件型触发次数上限
|
||||
overrides: data.overrides,
|
||||
trigger_type: data.trigger_type,
|
||||
trigger_limit: data.trigger_limit,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -283,11 +270,13 @@ export enum SpecialRefreshHeroType {
|
||||
Ranged = 2,
|
||||
}
|
||||
|
||||
/** 升级功能卡完整配置 */
|
||||
/** 升级功能卡完整配置(作为动态升级卡的基础模板) */
|
||||
export interface SpecialUpgradeCardConfig extends CardConfig {
|
||||
name: string
|
||||
info: string
|
||||
/** 模板字段:currentLv=0 表示动态卡(实际值由 target_hero_uuid 对应的英雄决定) */
|
||||
currentLv: number
|
||||
/** 模板字段:targetLv=0 表示"+1 级"(实际目标等级 = 当前等级 + 1) */
|
||||
targetLv: number
|
||||
}
|
||||
|
||||
@@ -299,18 +288,16 @@ export interface SpecialRefreshCardConfig extends CardConfig {
|
||||
refreshHeroType: SpecialRefreshHeroType
|
||||
}
|
||||
|
||||
/** 功能卡定义表 */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 升级卡模板。
|
||||
* 仅有 7001 一条记录,作为动态升级卡的基础配置(cost/weight/name/info)。
|
||||
* 实际使用时由 MissionCardComp.buildDrawCards 根据场上英雄动态生成 CardConfig,
|
||||
* 通过 target_hero_uuid 字段绑定具体英雄。
|
||||
*/
|
||||
export const SpecialUpgradeCardList: Record<number, SpecialUpgradeCardConfig> = {
|
||||
7001: {
|
||||
uuid: 7001, type: CardType.SpecialUpgrade, cost: 10, weight: 16, pool_lv: CardLV.LV1, kind: CKind.Card, name: t("scard_name_7001"), info: t("scard_info_7001"),
|
||||
currentLv: 1, targetLv: 2,
|
||||
},
|
||||
7002: {
|
||||
uuid: 7002, type: CardType.SpecialUpgrade, cost: 28, weight: 14, pool_lv: CardLV.LV2, kind: CKind.Card, name: t("scard_name_7002"), info: t("scard_info_7002"),
|
||||
currentLv: 2, targetLv: 3,
|
||||
uuid: 7001, type: CardType.SpecialUpgrade, cost: 10, weight: 18, pool_lv: CardLV.LV1, kind: CKind.Card, name: t("scard_name_7001"), info: t("scard_info_7001"),
|
||||
currentLv: 0, targetLv: 0,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -324,19 +311,15 @@ export const SpecialRefreshCardList: Record<number, SpecialRefreshCardConfig> =
|
||||
refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Ranged,
|
||||
},
|
||||
7103: {
|
||||
uuid: 7103, type: CardType.SpecialRefresh, cost: 4, weight: 12, pool_lv: CardLV.LV2, kind: CKind.Card, name: t("scard_name_7103"), info: t("scard_info_7103"),
|
||||
refreshLv: 3, refreshHeroType: SpecialRefreshHeroType.Any,
|
||||
uuid: 7103, type: CardType.SpecialRefresh, cost: 4, weight: 12, pool_lv: CardLV.LV1, kind: CKind.Card, name: t("scard_name_7103"), info: t("scard_info_7103"),
|
||||
refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Any,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 规范等级到合法区间 [LV1, LV6] */
|
||||
/** 规范等级到合法区间(保留以兼容历史调用,新机制下统一返回 LV1) */
|
||||
const clampCardLv = (lv: number): CardLV => {
|
||||
const value = Math.floor(lv)
|
||||
if (value < CARD_POOL_INIT_LEVEL) return CARD_POOL_INIT_LEVEL
|
||||
if (value > CARD_POOL_MAX_LEVEL) return CARD_POOL_MAX_LEVEL
|
||||
return value as CardLV
|
||||
return CardLV.LV1;
|
||||
}
|
||||
|
||||
/** 单次按权重抽取一张卡 */
|
||||
@@ -368,13 +351,13 @@ const pickCards = (cards: CardConfig[], count: number, unique: boolean = false):
|
||||
return selected
|
||||
}
|
||||
|
||||
/** 获取指定等级可出现的基础卡池 */
|
||||
/**
|
||||
* 获取基础卡池(已废弃 lv 参数,新机制下返回完整卡池)。
|
||||
* @param lv 历史遗留参数,不再生效
|
||||
* @param onlyCurrentLv 历史遗留参数,不再生效
|
||||
*/
|
||||
export const getCardPoolByLv = (lv: number, onlyCurrentLv: boolean = false): CardConfig[] => {
|
||||
const cardLv = clampCardLv(lv)
|
||||
if (onlyCurrentLv) {
|
||||
return CardPoolList.filter(card => card.pool_lv === cardLv)
|
||||
}
|
||||
return CardPoolList.filter(card => card.pool_lv <= cardLv)
|
||||
return CardPoolList;
|
||||
}
|
||||
|
||||
const normalizeTypeFilter = (type: CardType | CardType[]): Set<CardType> => {
|
||||
@@ -382,7 +365,12 @@ const normalizeTypeFilter = (type: CardType | CardType[]): Set<CardType> => {
|
||||
return new Set<CardType>(list)
|
||||
}
|
||||
|
||||
/** 常规发牌:前 3 英雄 + 后 1 其他;支持按类型和等级模式过滤 */
|
||||
/**
|
||||
* 常规发牌:前 3 英雄 + 后 1 其他;支持按类型过滤。
|
||||
* @param lv 历史遗留参数,不再生效
|
||||
* @param type 限定卡牌类型
|
||||
* @param onlyCurrentLv 历史遗留参数,不再生效
|
||||
*/
|
||||
export const getCardsByLv = (
|
||||
lv: number,
|
||||
type?: CardType | CardType[],
|
||||
@@ -401,6 +389,9 @@ export const getCardsByLv = (
|
||||
return [...heroes, ...others]
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用按规则抽卡(已废弃 lv/targetPoolLv/onlyCurrentLv 参数,仅为兼容保留)。
|
||||
*/
|
||||
export const drawCardsByRule = (
|
||||
lv: number,
|
||||
options: {
|
||||
@@ -415,30 +406,11 @@ export const drawCardsByRule = (
|
||||
} = {}
|
||||
): CardConfig[] => {
|
||||
const count = Math.max(0, Math.floor(options.count ?? 4))
|
||||
const onlyCurrentLv = options.onlyCurrentLv ?? false
|
||||
let pool = getCardPoolByLv(lv, onlyCurrentLv)
|
||||
let pool = getCardPoolByLv(lv, options.onlyCurrentLv ?? false)
|
||||
if (options.type !== undefined) {
|
||||
const typeSet = normalizeTypeFilter(options.type)
|
||||
pool = pool.filter(card => typeSet.has(card.type))
|
||||
}
|
||||
if (options.targetPoolLv !== undefined) {
|
||||
// 如果指定了目标卡池等级,则强制从所有配置中筛选该等级的卡牌,无视当前的卡池等级限制
|
||||
pool = CardPoolList.filter(card => card.pool_lv === options.targetPoolLv)
|
||||
if (options.type !== undefined) {
|
||||
const typeSet = normalizeTypeFilter(options.type)
|
||||
pool = pool.filter(card => typeSet.has(card.type))
|
||||
}
|
||||
|
||||
// 如果强制筛选后池子为空(比如开启了 ONLY_SPAWN_LV1_HERO 导致没有高等级英雄卡),
|
||||
// 且需要抽取英雄,则兜底降级回 pool_lv 为 1 的卡池,保证系统不会卡死
|
||||
if (pool.length === 0) {
|
||||
pool = CardPoolList.filter(card => card.pool_lv === 1);
|
||||
if (options.type !== undefined) {
|
||||
const typeSet = normalizeTypeFilter(options.type)
|
||||
pool = pool.filter(card => typeSet.has(card.type))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.heroType !== undefined || options.heroLv !== undefined) {
|
||||
pool = pool.filter(card => {
|
||||
if (card.type !== CardType.Hero) return false
|
||||
@@ -454,7 +426,6 @@ export const drawCardsByRule = (
|
||||
if (options.wave !== undefined) {
|
||||
pool = pool.filter(card => {
|
||||
if (card.type === CardType.Skill) {
|
||||
// 只有 wave 值严格等于当前 wave 的技能卡才会留在池中
|
||||
return card.wave === options.wave;
|
||||
}
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user