Files
pixelheros/assets/script/game/common/config/CardSet.ts
walkpan e3676c2775 feat(config): 更新技能卡牌配置以适配备战期玩法
- 将攻击性技能卡替换为增益/辅助类技能
- 新增单体/群体攻击、生命、全能及护盾技能
- 调整卡牌权重和等级池分布,确保备战期平衡性
2026-04-06 09:44:54 +08:00

289 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as exp from "constants"
import { HeroInfo, HType } from "./heroSet"
import { FightSet } from "./GameSet"
/** 卡牌大类定义 */
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,
LV4 = 4,
LV5 = 5,
}
/** 通用卡牌配置 */
export interface CardConfig {
uuid: number
type: CardType
cost: number
weight: number
kind: CKind
pool_lv: CardLV
hero_lv?: number
card_lv?:number
}
export const CardsUpSet: Record<number, number> = {
1: 50,
2: 100,
3: 150,
4: 200,
5: 250,
}
/**初始coin数 */
export const CardInitCoins = 4
/** 卡池升级每波减免金额 */
export const CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE = 10
/** 卡池默认初始等级 */
export const CARD_POOL_INIT_LEVEL = CardLV.LV1
/** 卡池等级上限 */
export const CARD_POOL_MAX_LEVEL = CardLV.LV5
/** 英雄最高等级限制 */
export const CARD_HERO_MAX_LEVEL = 3
/** 基础卡池(英雄、技能、功能) */
export const HERO_LV2_INIT_PROB = 0
export const HERO_LV2_PROB_INC_PER_LV = 0.02
export const CardPoolList: CardConfig[] = [
{ uuid: 5001, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 1, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5101, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 1, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5201, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 1, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5301, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 1, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5003, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 2, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5102, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 2, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5302, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 2, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5002, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 3, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5103, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 3, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5202, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 3, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5004, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 4, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5104, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 4, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5303, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 4, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5105, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 5, kind: CKind.Hero, hero_lv: 1 },
{ uuid: 5304, type: CardType.Hero, cost: 3, weight: 25, pool_lv: 5, kind: CKind.Hero, hero_lv: 1 },
// 技能卡牌 (以增益/辅助为主,因为在备战期没有敌人)
{ uuid: 6401, type: CardType.Skill, cost: 2, weight: 20, pool_lv: 1, kind: CKind.Skill, card_lv: 1 }, // 单体攻击
{ uuid: 6402, type: CardType.Skill, cost: 2, weight: 20, pool_lv: 1, kind: CKind.Skill, card_lv: 1 }, // 单体生命
{ uuid: 6403, type: CardType.Skill, cost: 3, weight: 20, pool_lv: 2, kind: CKind.Skill, card_lv: 1 }, // 单体全能
{ uuid: 6404, type: CardType.Skill, cost: 4, weight: 20, pool_lv: 3, kind: CKind.Skill, card_lv: 1 }, // 群体攻击
{ uuid: 6405, type: CardType.Skill, cost: 4, weight: 20, pool_lv: 3, kind: CKind.Skill, card_lv: 1 }, // 群体生命
{ uuid: 6406, type: CardType.Skill, cost: 5, weight: 20, pool_lv: 4, kind: CKind.Skill, card_lv: 1 }, // 群体全能
{ uuid: 6304, type: CardType.Skill, cost: 3, weight: 20, pool_lv: 2, kind: CKind.Skill, card_lv: 1 }, // 群体治疗
{ uuid: 6305, type: CardType.Skill, cost: 4, weight: 20, pool_lv: 4, kind: CKind.Skill, card_lv: 1 }, // 群体护盾
{ uuid: 7001, type: CardType.SpecialUpgrade, cost: 6, weight: 16, pool_lv: 1 ,kind: CKind.Card },
{ uuid: 7002, type: CardType.SpecialUpgrade, cost: 6, weight: 14, pool_lv: 2 ,kind: CKind.Card },
{ uuid: 7101, type: CardType.SpecialRefresh, cost: 4, weight: 14, pool_lv: 1 ,kind: CKind.Card },
{ uuid: 7102, type: CardType.SpecialRefresh, cost: 4, weight: 14, pool_lv: 1 ,kind: CKind.Card },
{ uuid: 7103, type: CardType.SpecialRefresh, cost: 5, weight: 12, pool_lv: 2 ,kind: CKind.Card },
]
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 interface SkillCardConfig extends CardConfig {
name: string
info: string
}
export const SkillCardList: Record<number, SkillCardConfig> = {
6401: { uuid: 6401, type: CardType.Skill, cost: 2, weight: 20, pool_lv: 1, kind: CKind.Skill, card_lv: 1, name: "单体攻击", info: "随机1个友方+5攻击" },
6402: { uuid: 6402, type: CardType.Skill, cost: 2, weight: 20, pool_lv: 1, kind: CKind.Skill, card_lv: 1, name: "单体生命", info: "随机1个友方+20最大生命值" },
6403: { uuid: 6403, type: CardType.Skill, cost: 3, weight: 20, pool_lv: 2, kind: CKind.Skill, card_lv: 1, name: "单体全能", info: "随机1个友方+2攻击,+10最大生命值" },
6404: { uuid: 6404, type: CardType.Skill, cost: 4, weight: 20, pool_lv: 3, kind: CKind.Skill, card_lv: 1, name: "群体攻击", info: "随机3个友方+2攻击" },
6405: { uuid: 6405, type: CardType.Skill, cost: 4, weight: 20, pool_lv: 3, kind: CKind.Skill, card_lv: 1, name: "群体生命", info: "随机3个友方+10最大生命值" },
6406: { uuid: 6406, type: CardType.Skill, cost: 5, weight: 20, pool_lv: 4, kind: CKind.Skill, card_lv: 1, name: "群体全能", info: "为随机3个友方单位增加攻击力和生命上限" },
6304: { uuid: 6304, type: CardType.Skill, cost: 3, weight: 20, pool_lv: 2, kind: CKind.Skill, card_lv: 1, name: "神圣治疗", info: "恢复场上随机3个友方单位的生命值" },
6305: { uuid: 6305, type: CardType.Skill, cost: 4, weight: 20, pool_lv: 4, kind: CKind.Skill, card_lv: 1, name: "群体护盾", info: "随机3个友方获得2次伤害免疫" },
}
/** 功能卡定义表 */
export const SpecialUpgradeCardList: Record<number, SpecialUpgradeCardConfig> = {
7001: { uuid: 7001,type: CardType.SpecialUpgrade,cost: 6,weight: 16,pool_lv: CardLV.LV1,kind:CKind.Card,name:"战术晋升",info: "升级场上随机1个1级英雄到2级",
currentLv: 1, targetLv: 2,
},
7002: { uuid: 7002,type: CardType.SpecialUpgrade,cost: 6,weight: 14,pool_lv: CardLV.LV2,kind:CKind.Card,name:"进阶战术",info: "升级场上随机1个2级英雄到3级",
currentLv: 2, targetLv: 3,
},
}
export const SpecialRefreshCardList: Record<number, SpecialRefreshCardConfig> = {
7101: { uuid: 7101,type: CardType.SpecialRefresh,cost: 4,weight: 14,pool_lv: CardLV.LV1,kind:CKind.Card,name:"近战征召",info: "刷新卡池,都是近战英雄",
refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Melee,
},
7102: { uuid: 7102,type: CardType.SpecialRefresh,cost: 4,weight: 14,pool_lv: CardLV.LV1,kind:CKind.Card,name:"远程征召",info: "刷新卡池,都是远程英雄",
refreshLv: 0, refreshHeroType: SpecialRefreshHeroType.Ranged,
},
7103: { uuid: 7103,type: CardType.SpecialRefresh,cost: 5,weight: 12,pool_lv: CardLV.LV2,kind:CKind.Card,name:"精英筛选",info: "刷新卡池都是3级卡池等级英雄",
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
}
/** 根据概率尝试将抽到的英雄卡的 hero_lv 提升到 2 */
const applyHeroLv2Probability = (cards: CardConfig[], currentPoolLv: number): CardConfig[] => {
return cards.map(card => {
if (card.type === CardType.Hero && card.hero_lv === 1) {
const prob = HERO_LV2_INIT_PROB + HERO_LV2_PROB_INC_PER_LV * (currentPoolLv - card.pool_lv)
if (Math.random() < prob) {
// 升级到2级时花费呈指数增长 (1级cost * MERGE_NEED^(2-1))
return { ...card, hero_lv: 2, cost: card.cost * Math.pow(FightSet.MERGE_NEED, 1) }
}
}
return card
})
}
/** 获取指定等级可出现的基础卡池 */
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<CardType> => {
const list = Array.isArray(type) ? type : [type]
return new Set<CardType>(list)
}
/** 常规发牌:前 2 英雄 + 后 2 其他;支持按类型和等级模式过滤 */
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 applyHeroLv2Probability(pickCards(filteredPool, 4), lv)
}
const heroPool = pool.filter(card => card.type === CardType.Hero)
const otherPool = pool.filter(card => card.type !== CardType.Hero)
const heroes = pickCards(heroPool, 2)
const others = pickCards(otherPool, 2)
return applyHeroLv2Probability([...heroes, ...others], lv)
}
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)
// 如果明确指定了需要的 heroLv则不触发升级概率
if (options.heroLv !== undefined) {
return picked
}
return applyHeroLv2Probability(picked, lv)
}