import * as exp from "constants" import { HeroInfo, HeroList, HType } from "./heroSet" import { FightSet } from "./GameSet" import { oops } from "db://oops-framework/core/Oops" import { SkillOverrides } from "./SkillSet" class I18nString { constructor(private key: string, private params?: any[]) { } private getTranslated(): string { let str = oops.language.getLangByID(this.key) || this.key; if (this.params && this.params.length > 0) { for (let i = 0; i < this.params.length; i++) { str = str.replace(`{${i}}`, String(this.params[i])); } } return str; } toString() { return this.getTranslated(); } valueOf() { return this.getTranslated(); } toJSON() { return this.getTranslated(); } get length() { return this.toString().length; } } const t = (key: string, ...params: any[]) => new I18nString(key, params) as unknown as string; /** 卡牌大类定义 */ export enum CardType { Hero = 1, Skill = 2, SpecialUpgrade = 3, SpecialRefresh = 4, } /** 卡牌大类定义 */ export enum CKind { Hero = 1, //英雄 Skill = 2, //技能 Card = 3, //卡牌 Potion = 4, //药水 } /** 卡池等级定义 */ export enum CardLV { LV1 = 1, LV2 = 2, LV3 = 3, } /** 通用卡牌配置 */ export interface CardConfig { uuid: number type: CardType cost: number weight: number kind: CKind pool_lv: CardLV hero_lv?: number card_lv?: number base_pool_lv?: number // 技能卡扩展属性 name?: string // 卡牌名称 info?: string // 卡牌描述信息 is_inst?: boolean // 是否即时起效 t_times?: number // 触发次数 t_inv?: number // 触发间隔(秒) } /** * 技能卡池专用卡牌配置(MSkillBoxComp 三选一弹窗使用) * * 在普通 CardConfig 基础上扩展: * - s_uuid : 实际生效的 SkillSet 条目 uuid * - t_num : 触发次数(可选,默认从 SkillSet 读取) * - overrides : 参数覆盖,参考 heroSet 中触发技能的 overrides 字段格式 * * uuid 范围 9000+ 用于和普通 CardPoolList 区分,MissSkillsComp 据此识别。 */ export interface SkillBoxCardConfig extends CardConfig { /** 实际生效的 SkillSet 技能 uuid */ s_uuid: number /** 触发次数(可选,默认 1) */ t_num?: number /** 技能参数覆盖,参考 SkillOverrides */ overrides?: SkillOverrides } export const CardsUpSet: Record = { 1: 50, 2: 100, 3: 150, 4: 200, 5: 250, } /**初始coin数 */ export const CardInitCoins = 10 /** 卡池升级每波减免金额 */ export const CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE = 10 /** 卡池默认初始等级 */ export const CARD_POOL_INIT_LEVEL = CardLV.LV1 /** 卡池等级上限 */ export const CARD_POOL_MAX_LEVEL = CardLV.LV3 /** 英雄最高等级限制 */ export const CARD_HERO_MAX_LEVEL = 1 /** 基础卡池(英雄、技能、功能) */ export const CardPoolList: CardConfig[] = []; // 动态生成英雄卡池 HeroList.forEach(uuid => { const hero = HeroInfo[uuid]; if (!hero) return; const basePoolLv = hero.pool_lv || 1; const baseHeroLv = hero.lv || 1; const baseCost = 5; 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; // 英雄的最高等级 是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: 6401, type: CardType.Skill, cost: 8, weight: 20, pool_lv: 1, kind: CKind.Skill, card_lv: 1, name: t("skill_name_6401"), info: t("skill_info_6401"), is_inst: true, t_times: 1, t_inv: 0 }, { uuid: 6402, type: CardType.Skill, cost: 8, weight: 20, pool_lv: 1, kind: CKind.Skill, card_lv: 1, name: t("skill_name_6402"), info: t("skill_info_6402"), is_inst: true, t_times: 1, t_inv: 0 }, { uuid: 6403, type: CardType.Skill, cost: 8, weight: 20, pool_lv: 1, kind: CKind.Skill, card_lv: 1, name: t("skill_name_6403"), info: t("skill_info_6403"), is_inst: true, t_times: 1, t_inv: 0 }, { uuid: 6404, type: CardType.Skill, cost: 8, weight: 20, pool_lv: 1, kind: CKind.Skill, card_lv: 1, name: t("skill_name_6404"), info: t("skill_info_6404"), is_inst: true, t_times: 1, t_inv: 0 }, { uuid: 6405, type: CardType.Skill, cost: 8, weight: 20, pool_lv: 2, kind: CKind.Skill, card_lv: 1, name: t("skill_name_6405"), info: t("skill_info_6405"), is_inst: true, t_times: 1, t_inv: 0 }, { uuid: 6406, type: CardType.Skill, cost: 8, weight: 20, pool_lv: 2, kind: CKind.Skill, card_lv: 1, name: t("skill_name_6406"), info: t("skill_info_6406"), is_inst: true, t_times: 1, t_inv: 0 }, { uuid: 6304, type: CardType.Skill, cost: 8, weight: 20, pool_lv: 3, kind: CKind.Skill, card_lv: 1, name: t("skill_name_6304"), info: t("skill_info_6304"), is_inst: true, t_times: 1, t_inv: 0 }, { uuid: 6305, type: CardType.Skill, cost: 8, weight: 20, pool_lv: 3, kind: CKind.Skill, card_lv: 1, name: t("skill_name_6305"), info: t("skill_info_6305"), is_inst: true, t_times: 1, t_inv: 0 }, ); export enum SpecialRefreshHeroType { Any = 0, Melee = 1, Ranged = 2, } /** 升级功能卡完整配置 */ export interface SpecialUpgradeCardConfig extends CardConfig { name: string info: string currentLv: number targetLv: number } /** 刷新功能卡完整配置 */ export interface SpecialRefreshCardConfig extends CardConfig { name: string info: string refreshLv: number refreshHeroType: SpecialRefreshHeroType } /** 功能卡定义表 */ export const SpecialUpgradeCardList: Record = { 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, }, } export const SpecialRefreshCardList: Record = { 7101: { uuid: 7101, type: CardType.SpecialRefresh, cost: 3, weight: 14, pool_lv: CardLV.LV1, kind: CKind.Card, name: t("scard_name_7101"), info: t("scard_info_7101"), refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Melee, }, 7102: { uuid: 7102, type: CardType.SpecialRefresh, cost: 3, weight: 14, pool_lv: CardLV.LV1, kind: CKind.Card, name: t("scard_name_7102"), info: t("scard_info_7102"), 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, }, } /** 规范等级到合法区间 [LV1, LV6] */ 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 } /** 单次按权重抽取一张卡 */ const weightedPick = (cards: CardConfig[]): CardConfig | null => { if (cards.length === 0) return null const totalWeight = cards.reduce((total, card) => total + card.weight, 0) let random = Math.random() * totalWeight for (const card of cards) { random -= card.weight if (random <= 0) return card } return cards[cards.length - 1] } /** 连续抽取 count 张卡,允许重复 */ const pickCards = (cards: CardConfig[], count: number): CardConfig[] => { if (cards.length === 0 || count <= 0) return [] const selected: CardConfig[] = [] while (selected.length < count) { const pick = weightedPick(cards) if (!pick) break selected.push(pick) } return selected } /** 获取指定等级可出现的基础卡池 */ 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) } const normalizeTypeFilter = (type: CardType | CardType[]): Set => { const list = Array.isArray(type) ? type : [type] return new Set(list) } /** 常规发牌:前 3 英雄 + 后 1 其他;支持按类型和等级模式过滤 */ export const getCardsByLv = ( lv: number, type?: CardType | CardType[], onlyCurrentLv: boolean = false ): CardConfig[] => { const pool = getCardPoolByLv(lv, onlyCurrentLv) if (type !== undefined) { const typeSet = normalizeTypeFilter(type) const filteredPool = pool.filter(card => typeSet.has(card.type)) return pickCards(filteredPool, 4) } const heroPool = pool.filter(card => card.type === CardType.Hero) const otherPool = pool.filter(card => card.type !== CardType.Hero) const heroes = pickCards(heroPool, 3) const others = pickCards(otherPool, 1) return [...heroes, ...others] } export const drawCardsByRule = ( lv: number, options: { count?: number onlyCurrentLv?: boolean type?: CardType | CardType[] heroType?: HType heroLv?: number targetPoolLv?: number } = {} ): CardConfig[] => { const count = Math.max(0, Math.floor(options.count ?? 4)) const onlyCurrentLv = options.onlyCurrentLv ?? false let pool = getCardPoolByLv(lv, onlyCurrentLv) 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)) } } if (options.heroType !== undefined || options.heroLv !== undefined) { pool = pool.filter(card => { if (card.type !== CardType.Hero) return false const hero = HeroInfo[card.uuid] if (!hero) return false if (options.heroType !== undefined && hero.type !== options.heroType) return false if (options.heroLv !== undefined && card.hero_lv !== options.heroLv) return false return true }) } const picked = pickCards(pool, count) return picked } /** * 固定波次弹出技能三选一弹窗(MissionCardComp 触发) * 5 个波次:1 / 5 / 10 / 15 / 20 */ export const SKILL_BOX_TRIGGER_WAVES: readonly number[] = [1, 5, 10, 15, 20] as const /** * 技能卡池(MSkillBoxComp 三选一) * * 5 个硬编码独立池,key 对应触发波次,value 是该波次可刷出的 SkillBoxCardConfig 列表。 * 每条配置: * - uuid 唯一且 >= 9000,用于 MissSkillsComp 识别 * - s_uuid 指向 SkillSet 中实际生效的技能 * - overrides 参考 heroSet 触发技能格式,对技能参数做具体覆盖 * - cost 强制 0(免费领取) * - is_inst / t_times / t_inv 控制技能使用后行为 */ export const SkillBoxPool: Record = { // 第 1 波:基础技能(伤害/治疗/护盾基础数值) 1: [ { uuid: 9001, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV1, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9001"), info: t("skillbox_info_9001"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6401, t_num: 1, overrides: { ap: 120 } }, { uuid: 9002, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV1, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9002"), info: t("skillbox_info_9002"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6402, t_num: 1, overrides: { ap: 150 } }, { uuid: 9003, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV1, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9003"), info: t("skillbox_info_9003"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6403, t_num: 1, overrides: { ap: 100, hit_count: 2 } }, { uuid: 9004, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV1, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9004"), info: t("skillbox_info_9004"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6404, t_num: 1, overrides: { ap: 110 } }, ], // 第 5 波:中级技能(伤害提升,带附加效果) 5: [ { uuid: 9005, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV2, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9005"), info: t("skillbox_info_9005"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6405, t_num: 1, overrides: { ap: 130, crt: 10 } }, { uuid: 9006, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV2, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9006"), info: t("skillbox_info_9006"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6406, t_num: 1, overrides: { ap: 140, hit_count: 2 } }, { uuid: 9007, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV2, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9007"), info: t("skillbox_info_9007"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6304, t_num: 1, overrides: { ap: 160 } }, { uuid: 9008, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV2, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9008"), info: t("skillbox_info_9008"), is_inst: false, t_times: 3, t_inv: 2, s_uuid: 6401, t_num: 3, overrides: { ap: 100 } }, ], // 第 10 波:高级技能(高伤害/多段/持续) 10: [ { uuid: 9009, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9009"), info: t("skillbox_info_9009"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6305, t_num: 1, overrides: { ap: 200 } }, { uuid: 9010, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9010"), info: t("skillbox_info_9010"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6405, t_num: 1, overrides: { ap: 220, hit_count: 3, crt: 15 } }, { uuid: 9011, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9011"), info: t("skillbox_info_9011"), is_inst: false, t_times: 4, t_inv: 1.5, s_uuid: 6402, t_num: 4, overrides: { ap: 130, frz: 10 } }, { uuid: 9012, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 1, name: t("skillbox_name_9012"), info: t("skillbox_info_9012"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6406, t_num: 1, overrides: { ap: 240, bck: 20 } }, ], // 第 15 波:精英技能(高额暴击/多段) 15: [ { uuid: 9013, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 2, name: t("skillbox_name_9013"), info: t("skillbox_info_9013"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6304, t_num: 1, overrides: { ap: 280, hit_count: 3 } }, { uuid: 9014, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 2, name: t("skillbox_name_9014"), info: t("skillbox_info_9014"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6305, t_num: 1, overrides: { ap: 320, crt: 25 } }, { uuid: 9015, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 2, name: t("skillbox_name_9015"), info: t("skillbox_info_9015"), is_inst: false, t_times: 5, t_inv: 1.2, s_uuid: 6406, t_num: 5, overrides: { ap: 200, hit_count: 2, bck: 30 } }, ], // 第 20 波:终极技能(全屏/极限数值) 20: [ { uuid: 9016, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 3, name: t("skillbox_name_9016"), info: t("skillbox_info_9016"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6304, t_num: 1, overrides: { ap: 400, hit_count: 4, crt: 30 } }, { uuid: 9017, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 3, name: t("skillbox_name_9017"), info: t("skillbox_info_9017"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6305, t_num: 1, overrides: { ap: 500, crt: 40, frz: 20 } }, { uuid: 9018, type: CardType.Skill, cost: 0, weight: 0, pool_lv: CardLV.LV3, kind: CKind.Skill, card_lv: 3, name: t("skillbox_name_9018"), info: t("skillbox_info_9018"), is_inst: true, t_times: 1, t_inv: 0, s_uuid: 6406, t_num: 1, overrides: { ap: 600, hit_count: 5, bck: 50 } }, ], } /** * 从指定波次的技能池中抽取 N 张卡(加权随机,去重,不足循环补齐) * * @param wave 触发波次(1/5/10/15/20),若该波次无池则返回空数组 * @param count 期望返回卡牌数(默认 3) * @returns 抽中的 SkillBoxCardConfig 列表(长度 = count) */ export function getSkillBoxCards(wave: number, count: number = 3): SkillBoxCardConfig[] { const pool = SkillBoxPool[wave] || []; if (pool.length === 0) return []; const result: SkillBoxCardConfig[] = []; const usedIndexes = new Set(); for (let i = 0; i < count; i++) { const available: number[] = []; for (let j = 0; j < pool.length; j++) { if (!usedIndexes.has(j)) available.push(j); } // 池内卡片不足时,允许重复 const candidates = available.length > 0 ? available : Array.from({ length: pool.length }, (_, k) => k); const pick = candidates[Math.floor(Math.random() * candidates.length)]; usedIndexes.add(pick); result.push(pool[pick]); } return result; }