5 Commits

Author SHA1 Message Date
panw
52ed41bec1 refactor: 统一英雄属性中卡牌等级字段名称为pool_lv
将HeroAttrsComp中的card_lv字段重命名为pool_lv,以更准确地反映其表示英雄池等级的含义。同时更新所有相关引用点,包括HInfoComp的显示逻辑、Hero的加载方法以及MissionHeroCompComp的召唤队列和合成逻辑,确保数据一致性。
2026-04-02 17:36:00 +08:00
panw
a63360f493 feat: 引入英雄等级提升机制并重构卡牌等级字段
- 将卡牌配置中的 `lv` 字段重命名为 `pool_lv` 以明确表示池等级
- 新增英雄等级提升概率机制,高池等级抽卡有概率获得高英雄等级卡牌
- 更新相关组件以适配新的字段名和英雄等级逻辑
- 修复事件 payload 中 `card_lv` 到 `pool_lv` 的字段映射
2026-04-02 17:01:33 +08:00
panw
781e88e2d7 feat: 新增卡牌等级系统并调整英雄合成规则
- 在 HeroAttrsComp 中添加 card_lv 属性,用于独立记录卡牌等级
- 修改 Hero 加载逻辑,支持传入 card_lv 参数
- 更新 HInfoComp 的 UI 刷新逻辑,根据英雄等级和卡牌等级显示不同的边框和等级图标
- 调整 MissionHeroComp 的合成规则:所需合成数量从 2 改为 3,最高合成等级从 3 改为 2
- 在召唤队列和合成流程中传递并处理 card_lv 数据,确保卡牌等级在合成过程中得以保留
2026-04-02 16:40:23 +08:00
panw
a14513dcdf feat(card): 添加卡牌等级视觉区分与配置调整
- 在卡牌预制件中新增背景(BG_node)、普通边框(NF_node)和高阶边框(HF_node)节点
- 根据卡牌等级和英雄等级动态显示对应的背景和边框样式
- 调整卡牌等级枚举定义,移除LV6并重命名为CardLV
- 为CardConfig接口添加card_lv字段以支持独立卡牌等级
- 优化buff提示框的布局和字体大小
2026-04-02 16:31:06 +08:00
panw
588c935c18 feat(卡牌): 添加卡牌大类标识并更新UI显示
- 在CardSet中新增CKind枚举,区分英雄、技能、卡牌、药水等大类
- 在卡牌配置中增加kind字段,并更新所有卡牌配置
- 在CardComp组件中添加Ckind_node属性,用于显示卡牌大类图标
- 重构card.prefab,将大类图标节点重命名为更具语义的名称(如lv1-lv5),并调整节点激活状态
- 在卡牌初始化和重置时,根据cardData.kind动态显示对应的大类图标
2026-04-02 15:39:08 +08:00
11 changed files with 3761 additions and 1804 deletions

View File

@@ -1782,7 +1782,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -71.73,
"x": -61.45,
"y": 0.963,
"z": 0
},
@@ -1823,8 +1823,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 25.98046875,
"height": 81.6
"width": 17.6552734375,
"height": 56.4
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -1862,7 +1862,7 @@
"_string": "-",
"_horizontalAlign": 0,
"_verticalAlign": 1,
"_actualFontSize": 60,
"_actualFontSize": 35,
"_fontSize": 35,
"_fontFamily": "Arial",
"_lineHeight": 40,
@@ -1919,7 +1919,7 @@
},
"_alignFlags": 8,
"_target": null,
"_left": -34.720234375000004,
"_left": -30.557636718750004,
"_right": 0,
"_top": 0,
"_bottom": 0,
@@ -2014,7 +2014,7 @@
"_overflow": 0,
"_enableWrapText": true,
"_font": {
"__uuid__": "983a109b-a5e3-4ba7-84c5-1c53817ba177",
"__uuid__": "eca7fc51-87d0-4bb0-89e1-aa9c39411bc6",
"__expectedType__": "cc.BitmapFont"
},
"_isSystemFontUsed": false,
@@ -3822,7 +3822,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -36.930390625,
"x": -41.0830078125,
"y": 0,
"z": 0
},
@@ -4011,8 +4011,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 49.31,
"height": 50
"width": 57.615234375,
"height": 65
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -4056,11 +4056,8 @@
"_lineHeight": 50,
"_overflow": 0,
"_enableWrapText": true,
"_font": {
"__uuid__": "983a109b-a5e3-4ba7-84c5-1c53817ba177",
"__expectedType__": "cc.BitmapFont"
},
"_isSystemFontUsed": false,
"_font": null,
"_isSystemFontUsed": true,
"_spacingX": 0,
"_isItalic": false,
"_isBold": false,

View File

@@ -180,7 +180,7 @@
"__id__": 2
},
"_children": [],
"_active": true,
"_active": false,
"_components": [
{
"__id__": 4
@@ -351,14 +351,14 @@
},
{
"__type__": "cc.Node",
"_name": "green",
"_name": "lv1",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 2
},
"_children": [],
"_active": false,
"_active": true,
"_components": [
{
"__id__": 12
@@ -529,7 +529,7 @@
},
{
"__type__": "cc.Node",
"_name": "blue",
"_name": "lv2",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -707,7 +707,7 @@
},
{
"__type__": "cc.Node",
"_name": "zhise",
"_name": "lv3",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -885,7 +885,7 @@
},
{
"__type__": "cc.Node",
"_name": "yellow",
"_name": "lv4",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -1063,7 +1063,7 @@
},
{
"__type__": "cc.Node",
"_name": "red",
"_name": "lv5",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -2742,7 +2742,7 @@
},
{
"__type__": "cc.Node",
"_name": "N",
"_name": "NF",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -2818,7 +2818,7 @@
"__id__": 118
},
"_children": [],
"_active": true,
"_active": false,
"_components": [
{
"__id__": 120
@@ -2989,14 +2989,14 @@
},
{
"__type__": "cc.Node",
"_name": "green",
"_name": "lv1",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 118
},
"_children": [],
"_active": false,
"_active": true,
"_components": [
{
"__id__": 128
@@ -3167,7 +3167,7 @@
},
{
"__type__": "cc.Node",
"_name": "blue",
"_name": "lv2",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -3345,7 +3345,7 @@
},
{
"__type__": "cc.Node",
"_name": "zhise",
"_name": "lv3",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -3523,7 +3523,7 @@
},
{
"__type__": "cc.Node",
"_name": "yellow",
"_name": "lv4",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -3701,7 +3701,7 @@
},
{
"__type__": "cc.Node",
"_name": "red",
"_name": "lv5",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -3956,7 +3956,7 @@
},
{
"__type__": "cc.Node",
"_name": "H",
"_name": "HF",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -4032,7 +4032,7 @@
"__id__": 172
},
"_children": [],
"_active": true,
"_active": false,
"_components": [
{
"__id__": 174
@@ -4203,7 +4203,7 @@
},
{
"__type__": "cc.Node",
"_name": "green",
"_name": "lv1",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -4381,7 +4381,7 @@
},
{
"__type__": "cc.Node",
"_name": "blue",
"_name": "lv2",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -4559,7 +4559,7 @@
},
{
"__type__": "cc.Node",
"_name": "zhise",
"_name": "lv3",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -4737,7 +4737,7 @@
},
{
"__type__": "cc.Node",
"_name": "yellow",
"_name": "lv4",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -4915,7 +4915,7 @@
},
{
"__type__": "cc.Node",
"_name": "red",
"_name": "lv5",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -5360,6 +5360,8 @@
"__id__": 0
},
"fileId": "d6lvh87QJBqrQKgDnhSWth",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -7932,7 +7934,7 @@
},
{
"__type__": "cc.Node",
"_name": "kind",
"_name": "CKind",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -7963,8 +7965,8 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 75.54,
"y": 106.256,
"x": 0,
"y": -102.964,
"z": 0
},
"_lrot": {
@@ -7976,8 +7978,8 @@
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"x": 0.8,
"y": 0.8,
"z": 1
},
"_mobility": 0,
@@ -7992,7 +7994,7 @@
},
{
"__type__": "cc.Node",
"_name": "hero",
"_name": "Hero",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8014,7 +8016,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"y": 265.066,
"z": 0
},
"_lrot": {
@@ -8122,11 +8124,13 @@
"__id__": 0
},
"fileId": "f3MmyLb9lML5mXGMUZsqPK",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "card",
"_name": "Card",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8148,7 +8152,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"y": 265.066,
"z": 0
},
"_lrot": {
@@ -8259,11 +8263,13 @@
"__id__": 0
},
"fileId": "dfmEZyL9NF05PFQxDRYO6o",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "skill",
"_name": "Skill",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8285,7 +8291,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"y": 265.066,
"z": 0
},
"_lrot": {
@@ -8402,7 +8408,7 @@
},
{
"__type__": "cc.Node",
"_name": "buff",
"_name": "Potion",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8424,7 +8430,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"y": 265.066,
"z": 0
},
"_lrot": {
@@ -8641,6 +8647,18 @@
"cost_node": {
"__id__": 233
},
"Ckind_node": {
"__id__": 343
},
"BG_node": {
"__id__": 2
},
"NF_node": {
"__id__": 118
},
"HF_node": {
"__id__": 172
},
"_id": ""
},
{

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,15 +8,21 @@ export enum CardType {
SpecialUpgrade = 3,
SpecialRefresh = 4,
}
/** 卡牌大类定义 */
export enum CKind {
Hero = 1, //英雄
Skill = 2, //技能
Card = 3, //卡牌
Potion = 4, //药水
}
/** 卡池等级定义 */
export enum CardKind {
export enum CardLV {
LV1 = 1,
LV2 = 2,
LV3 = 3,
LV4 = 4,
LV5 = 5,
LV6 = 6,
}
/** 通用卡牌配置 */
@@ -25,8 +31,10 @@ export interface CardConfig {
type: CardType
cost: number
weight: number
lv: CardKind
kind: CKind
pool_lv: CardLV
hero_lv?: number
card_lv?:number
}
export const CardsUpSet: Record<number, number> = {
1: 50,
@@ -35,67 +43,47 @@ export const CardsUpSet: Record<number, number> = {
4: 200,
5: 250,
}
/**初始coin数 */
export const CardInitCoins = 4
/** 卡池升级每波减免金额 */
export const CARD_POOL_UPGRADE_DISCOUNT_PER_WAVE = 10
/** 卡池默认初始等级 */
export const CARD_POOL_INIT_LEVEL = CardKind.LV1
export const CARD_POOL_INIT_LEVEL = CardLV.LV1
/** 卡池等级上限 */
export const CARD_POOL_MAX_LEVEL = CardKind.LV6
export const CARD_POOL_MAX_LEVEL = CardLV.LV5
/** 英雄最高等级限制 */
export const CARD_HERO_MAX_LEVEL = 3
/** 基础卡池(英雄、技能、功能) */
export const HERO_LV2_INIT_PROB = 0.1
export const HERO_LV2_PROB_INC_PER_LV = 0.05
export const CardPoolList: CardConfig[] = [
{ uuid: 5001, type: CardType.Hero, cost: 3, weight: 25, lv: 1, hero_lv: 1 },
{ uuid: 5101, type: CardType.Hero, cost: 3, weight: 25, lv: 1, hero_lv: 1 },
{ uuid: 5201, type: CardType.Hero, cost: 3, weight: 25, lv: 1, hero_lv: 1 },
{ uuid: 5301, type: CardType.Hero, cost: 3, weight: 25, lv: 1, hero_lv: 1 },
{ 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, lv: 2, hero_lv: 1 },
{ uuid: 5102, type: CardType.Hero, cost: 3, weight: 25, lv: 2, hero_lv: 1 },
{ uuid: 5302, type: CardType.Hero, cost: 3, weight: 25, lv: 2, 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: 5001, type: CardType.Hero, cost: 3, weight: 25, lv: 3, hero_lv: 2 },
{ uuid: 5101, type: CardType.Hero, cost: 3, weight: 25, lv: 3, hero_lv: 2 },
{ uuid: 5201, type: CardType.Hero, cost: 3, weight: 25, lv: 3, hero_lv: 2 },
{ uuid: 5301, type: CardType.Hero, cost: 3, weight: 25, lv: 3, hero_lv: 2 },
{ 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: 5002, type: CardType.Hero, cost: 3, weight: 25, lv: 3, hero_lv: 1 },
{ uuid: 5103, type: CardType.Hero, cost: 3, weight: 25, lv: 3, hero_lv: 1 },
{ uuid: 5202, type: CardType.Hero, cost: 3, weight: 25, lv: 3, 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: 5003, type: CardType.Hero, cost: 3, weight: 25, lv: 4, hero_lv: 2 },
{ uuid: 5102, type: CardType.Hero, cost: 3, weight: 25, lv: 4, hero_lv: 2 },
{ uuid: 5302, type: CardType.Hero, cost: 3, weight: 25, lv: 4, hero_lv: 2 },
{ uuid: 5004, type: CardType.Hero, cost: 3, weight: 25, lv: 4, hero_lv: 1 },
{ uuid: 5104, type: CardType.Hero, cost: 3, weight: 25, lv: 4, hero_lv: 1 },
{ uuid: 5303, type: CardType.Hero, cost: 3, weight: 25, lv: 4, 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: 5001, type: CardType.Hero, cost: 3, weight: 25, lv: 5, hero_lv: 3 },
{ uuid: 5101, type: CardType.Hero, cost: 3, weight: 25, lv: 5, hero_lv: 3 },
{ uuid: 5201, type: CardType.Hero, cost: 3, weight: 25, lv: 5, hero_lv: 3 },
{ uuid: 5301, type: CardType.Hero, cost: 3, weight: 25, lv: 5, hero_lv: 3 },
{ uuid: 5002, type: CardType.Hero, cost: 3, weight: 25, lv: 5, hero_lv: 2 },
{ uuid: 5103, type: CardType.Hero, cost: 3, weight: 25, lv: 5, hero_lv: 2 },
{ uuid: 5202, type: CardType.Hero, cost: 3, weight: 25, lv: 5, hero_lv: 2 },
{ uuid: 5105, type: CardType.Hero, cost: 3, weight: 25, lv: 5, hero_lv: 1 },
{ uuid: 5003, type: CardType.Hero, cost: 3, weight: 25, lv: 6, hero_lv: 3 },
{ uuid: 5102, type: CardType.Hero, cost: 3, weight: 25, lv: 6, hero_lv: 3 },
{ uuid: 5302, type: CardType.Hero, cost: 3, weight: 25, lv: 6, hero_lv: 3 },
{ uuid: 5304, type: CardType.Hero, cost: 3, weight: 25, lv: 6, hero_lv: 1 },
{ uuid: 7001, type: CardType.SpecialUpgrade, cost: 6, weight: 16, lv: 1 },
{ uuid: 7002, type: CardType.SpecialUpgrade, cost: 6, weight: 14, lv: 2 },
{ uuid: 7101, type: CardType.SpecialRefresh, cost: 4, weight: 14, lv: 1 },
{ uuid: 7102, type: CardType.SpecialRefresh, cost: 4, weight: 14, lv: 1 },
{ uuid: 7103, type: CardType.SpecialRefresh, cost: 5, weight: 12, lv: 2 },
{ 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 },
]
@@ -126,22 +114,22 @@ export interface SpecialRefreshCardConfig extends CardConfig {
export const SpecialUpgradeCardList: Record<number, SpecialUpgradeCardConfig> = {
7001: { uuid: 7001,type: CardType.SpecialUpgrade,cost: 6,weight: 16,lv: CardKind.LV1,name:"战术晋升",info: "升级场上随机1个1级英雄到2级",
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,lv: CardKind.LV2,name:"进阶战术",info: "升级场上随机1个2级英雄到3级",
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,lv: CardKind.LV1,name:"近战征召",info: "刷新卡池,都是近战英雄",
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,lv: CardKind.LV1,name:"远程征召",info: "刷新卡池,都是远程英雄",
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,lv: CardKind.LV2,name:"精英筛选",info: "刷新卡池都是3级卡池等级英雄",
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,
},
}
@@ -149,11 +137,11 @@ export const SpecialRefreshCardList: Record<number, SpecialRefreshCardConfig> =
/** 规范等级到合法区间 [LV1, LV6] */
const clampCardLv = (lv: number): CardKind => {
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 CardKind
return value as CardLV
}
/** 单次按权重抽取一张卡 */
@@ -180,13 +168,26 @@ const pickCards = (cards: CardConfig[], count: number): CardConfig[] => {
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) {
return { ...card, hero_lv: 2 }
}
}
return card
})
}
/** 获取指定等级可出现的基础卡池 */
export const getCardPoolByLv = (lv: number, onlyCurrentLv: boolean = false): CardConfig[] => {
const cardLv = clampCardLv(lv)
if (onlyCurrentLv) {
return CardPoolList.filter(card => card.lv === cardLv)
return CardPoolList.filter(card => card.pool_lv === cardLv)
}
return CardPoolList.filter(card => card.lv <= cardLv)
return CardPoolList.filter(card => card.pool_lv <= cardLv)
}
const normalizeTypeFilter = (type: CardType | CardType[]): Set<CardType> => {
@@ -204,13 +205,13 @@ export const getCardsByLv = (
if (type !== undefined) {
const typeSet = normalizeTypeFilter(type)
const filteredPool = pool.filter(card => typeSet.has(card.type))
return pickCards(filteredPool, 4)
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 [...heroes, ...others]
return applyHeroLv2Probability([...heroes, ...others], lv)
}
export const drawCardsByRule = (
@@ -240,5 +241,10 @@ export const drawCardsByRule = (
return true
})
}
return pickCards(pool, count)
const picked = pickCards(pool, count)
// 如果明确指定了需要的 heroLv则不触发升级概率
if (options.heroLv !== undefined) {
return picked
}
return applyHeroLv2Probability(picked, lv)
}

View File

@@ -63,7 +63,7 @@ export class Hero extends ecs.Entity {
* 2) 初始化表现与属性数据
* 3) 播放下落入场并在落地后启用碰撞与移动
*/
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001, dropToY:number = pos.y,hero_lv:number=1) {
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001, dropToY:number = pos.y,hero_lv:number=1, pool_lv:number=1) {
// 英雄始终朝右,表现缩放固定为正向
scale = 1
// 英雄等级在当前规则下上限为 3避免超配表范围
@@ -98,6 +98,7 @@ export class Hero extends ecs.Entity {
model.hero_uuid = uuid;
model.hero_name = hero.name;
model.lv = hero_lv;
model.pool_lv = pool_lv;
model.type = hero.type;
model.fac = FacSet.HERO;

View File

@@ -14,6 +14,7 @@ export class HeroAttrsComp extends ecs.Comp {
hero_uuid: number = 1001;
hero_name: string = "hero";
lv: number = 1;
pool_lv: number = 1;
type: number = 0; // 0近战 1远程 2辅助
fac: number = 0; // 0:hero 1:monster
// ==================== 基础属性(有初始值) ====================

View File

@@ -2,7 +2,7 @@ import { mLogger } from "../common/Logger";
import { _decorator, Animation, AnimationClip, EventTouch, Label, Node, NodeEventType, Sprite, SpriteAtlas, Tween, tween, UIOpacity, Vec3, resources } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { CardConfig, CardType, SpecialRefreshCardList, SpecialUpgradeCardList } from "../common/config/CardSet";
import { CardConfig, CardType, SpecialRefreshCardList, SpecialUpgradeCardList, CKind } from "../common/config/CardSet";
import { CardUseComp } from "./CardUseComp";
import { HeroInfo } from "../common/config/heroSet";
import { SkillSet } from "../common/config/SkillSet";
@@ -35,7 +35,15 @@ export class CardComp extends CCComp {
icon_node=null!
@property(Node)
cost_node=null!
@property(Node)
Ckind_node=null!
@property(Node)
BG_node=null!
@property(Node)
NF_node=null!
@property(Node)
HF_node=null!
card_cost:number=0
card_type:CardType=CardType.Hero
card_uuid:number=0
@@ -184,7 +192,8 @@ export class CardComp extends CCComp {
cancel: false,
reason: "",
uuid: this.cardData.uuid,
hero_lv: this.cardData.hero_lv
hero_lv: this.cardData.hero_lv ?? 1,
card_lv: this.cardData.pool_lv ?? 1
};
oops.message.dispatchEvent(GameEvent.UseHeroCard, guard);
if (guard.cancel) {
@@ -367,6 +376,31 @@ export class CardComp extends CCComp {
if (this.opacityComp) this.opacityComp.opacity = 255;
this.node.setPosition(this.restPosition);
if (this.Ckind_node) {
const kindName = CKind[this.cardData.kind];
this.Ckind_node.children.forEach(child => {
child.active = (child.name === kindName);
});
}
const cardLvStr = `lv${this.cardData.pool_lv}`;
if (this.BG_node) {
this.BG_node.children.forEach(child => {
child.active = (child.name === cardLvStr);
});
}
const card_lv_val = this.cardData.card_lv ?? 1;
const isHighLevel = (this.cardData.hero_lv ?? 0) > 1 || card_lv_val > 1;
if (this.HF_node) this.HF_node.active = isHighLevel;
if (this.NF_node) this.NF_node.active = !isHighLevel;
const activeFrameNode = isHighLevel ? this.HF_node : this.NF_node;
if (activeFrameNode) {
activeFrameNode.children.forEach(child => {
child.active = (child.name === cardLvStr);
});
}
if(this.card_type===CardType.Hero){
const hero = HeroInfo[this.card_uuid];
@@ -380,7 +414,7 @@ export class CardComp extends CCComp {
const specialCard = this.card_type === CardType.SpecialUpgrade
? SpecialUpgradeCardList[this.card_uuid]
: SpecialRefreshCardList[this.card_uuid];
this.setLabel(this.name_node, `${specialCard?.name || ""}Lv.${this.cardData.lv}`);
this.setLabel(this.name_node, `${specialCard?.name || ""}Lv.${this.cardData.pool_lv}`);
this.info_node.active = false;
this.oinfo_node.active = true;
this.oinfo_node.getChildByName("info").getComponent(Label).string = `${specialCard?.info || ""}`;
@@ -449,6 +483,16 @@ export class CardComp extends CCComp {
this.setLabel(this.cost_node, "");
if (this.info_node) this.info_node.active = false;
if (this.oinfo_node) this.oinfo_node.active = false;
if (this.Ckind_node) {
this.Ckind_node.children.forEach(child => {
child.active = false;
});
}
if (this.BG_node) {
this.BG_node.children.forEach(child => child.active = false);
}
if (this.HF_node) this.HF_node.active = false;
if (this.NF_node) this.NF_node.active = false;
this.clearIconAnimation(this.icon_node as Node);
const sprite = this.icon_node?.getComponent(Sprite) || this.icon_node?.getComponentInChildren(Sprite);
if (sprite) sprite.spriteFrame = null;

View File

@@ -18,6 +18,13 @@ export class HInfoComp extends CCComp {
icon_node=null!
@property(Node)
sell_node=null!
@property(Node)
NF_node=null!
@property(Node)
HF_node=null!
private eid: number = 0;
private model: HeroAttrsComp | null = null;
private apLabel: Label | null = null;
@@ -43,7 +50,19 @@ export class HInfoComp extends CCComp {
refresh() {
if (!this.model) return;
this.updateLevelUI();
const isHighLevel = (this.model.lv ?? 0) > 1;
if (this.HF_node) this.HF_node.active = isHighLevel;
if (this.NF_node) this.NF_node.active = !isHighLevel;
const activeFrameNode = isHighLevel ? this.HF_node : this.NF_node;
if (activeFrameNode) {
const cardLvStr = `lv${this.model.pool_lv ?? 1}`;
activeFrameNode.children.forEach(child => {
child.active = (child.name === cardLvStr);
});
}
const heroUuid = this.model.hero_uuid ?? 0;
if (heroUuid !== this.iconHeroUuid) {
this.iconHeroUuid = heroUuid;
@@ -80,16 +99,7 @@ export class HInfoComp extends CCComp {
return current.getComponent(Label) || current.getComponentInChildren(Label);
}
private updateLevelUI() {
if (!this.model) return;
const lvNode = this.node.getChildByName("lv");
if (!lvNode) return;
lvNode.active = true
const lv2 = lvNode.getChildByName("lv2");
if (lv2) lv2.active = this.model.lv >= 2;
const lv3 = lvNode.getChildByName("lv3");
if (lv3) lv3.active = this.model.lv >= 3;
}
private updateHeroAnimation(node: Node, uuid: number, token: number) {
if (!node) return;

View File

@@ -234,6 +234,7 @@ export class MissionCardComp extends CCComp {
if (current >= heroMax) {
const heroUuid = Number(payload?.uuid ?? 0);
const heroLv = Math.max(1, Math.floor(Number(payload?.hero_lv ?? 1)));
const cardLv = Math.max(1, Math.floor(Number(payload?.pool_lv ?? 1)));
if (this.canUseHeroCardByMerge(heroUuid, heroLv)) {
payload.cancel = false;
payload.reason = "";

View File

@@ -31,15 +31,15 @@ export class MissionHeroCompComp extends CCComp {
/** 当前英雄数量缓存 */
current_hero_num:number=-1
/** 合成规则2 合 1 或 3 合 1 */
merge_need_count:number=2
merge_need_count:number=3
/** 允许合成的最高等级 */
merge_max_lv:number=3
merge_max_lv:number=2
/** 是否正在执行一次合成流程 */
is_merging:boolean=false
/** 是否正在消费召唤队列,防止并发处理 */
is_processing_queue:boolean=false
/** 召唤请求队列,保证召唤与合成串行 */
summon_queue:{ uuid: number; hero_lv: number }[]=[]
summon_queue:{ uuid: number; hero_lv: number; pool_lv: number }[]=[]
/** 预留英雄列表 */
heros:any=[]
onLoad(){
@@ -84,19 +84,21 @@ export class MissionHeroCompComp extends CCComp {
/** 召唤请求入口:归一化参数并进入串行队列 */
private async call_hero(event: string, args: any){
const uuid = Number(args?.uuid ?? 1001);
const hero_lv = Math.max(1, Number(args?.hero_lv ?? 1));
this.summon_queue.push({ uuid, hero_lv });
const payload = args ?? event;
const uuid = Number(payload?.uuid ?? 1001);
const hero_lv = Math.max(1, Number(payload?.hero_lv ?? 1));
const pool_lv = Math.max(1, Number(payload?.pool_lv ?? 1));
this.summon_queue.push({ uuid, hero_lv, pool_lv });
this.processSummonQueue();
}
/** 添加英雄:固定出生点上方生成,再落至落点 */
private addHero(uuid:number=1001,hero_lv:number=1) {
private addHero(uuid:number=1001,hero_lv:number=1, pool_lv:number=1) {
console.log("addHero uuid:",uuid)
let hero = ecs.getEntity<Hero>(Hero);
let scale = 1
const landingPos = this.resolveHeroLandingPos(uuid);
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv);
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv);
return hero;
}
@@ -115,8 +117,8 @@ export class MissionHeroCompComp extends CCComp {
}
/** 添加合成后的新英雄,并覆盖为聚合后的属性 */
private addMergedHero(uuid:number, hero_lv:number, ap:number, hp_max:number): number {
const hero = this.addHero(uuid, hero_lv);
private addMergedHero(uuid:number, hero_lv:number, pool_lv:number, ap:number, hp_max:number): number {
const hero = this.addHero(uuid, hero_lv, pool_lv);
const model = hero.get(HeroAttrsComp);
if (!model) return hero_lv;
model.ap = Math.max(0, ap);
@@ -184,7 +186,7 @@ export class MissionHeroCompComp extends CCComp {
while (this.summon_queue.length > 0) {
const payload = this.summon_queue.shift();
if (!payload) continue;
await this.handleSingleSummon(payload.uuid, payload.hero_lv);
await this.handleSingleSummon(payload.uuid, payload.hero_lv, payload.pool_lv);
}
} finally {
this.is_processing_queue = false;
@@ -192,8 +194,8 @@ export class MissionHeroCompComp extends CCComp {
}
/** 处理单次召唤:先生成,再检测是否触发合成,再尝试链式合成 */
private async handleSingleSummon(uuid: number, hero_lv: number) {
this.addHero(uuid, hero_lv);
private async handleSingleSummon(uuid: number, hero_lv: number, pool_lv: number = 1) {
this.addHero(uuid, hero_lv, pool_lv);
if (!this.canMergeLevel(hero_lv)) return;
const needCount = this.getMergeNeedCount();
const aliveHeroes = this.getAliveHeroes();
@@ -201,8 +203,8 @@ export class MissionHeroCompComp extends CCComp {
if (mergeHeroes.length !== needCount) return;
this.is_merging = true;
try {
const mergedLv = await this.mergeGroupHeroes(mergeHeroes, uuid, hero_lv);
await this.tryChainMerge(uuid, mergedLv);
const mergedLv = await this.mergeGroupHeroes(mergeHeroes, uuid, hero_lv, pool_lv);
await this.tryChainMerge(uuid, mergedLv, pool_lv);
} finally {
this.is_merging = false;
}
@@ -256,7 +258,7 @@ export class MissionHeroCompComp extends CCComp {
}
/** 执行一次完整合成:聚合属性、销毁素材、播放特效、生成高一级英雄 */
private async mergeGroupHeroes(mergeHeroes: Hero[], uuid: number, hero_lv: number): Promise<number> {
private async mergeGroupHeroes(mergeHeroes: Hero[], uuid: number, hero_lv: number, pool_lv: number): Promise<number> {
let sumAp = 0;
let sumHpMax = 0;
for (let i = 0; i < mergeHeroes.length; i++) {
@@ -269,11 +271,11 @@ export class MissionHeroCompComp extends CCComp {
const spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
await this.mergeDestroyAtBirth(mergeHeroes, spawnPos);
await this.playMergeBoomFx(spawnPos);
return this.addMergedHero(uuid, Math.min(this.merge_max_lv, hero_lv + 1), sumAp, sumHpMax);
return this.addMergedHero(uuid, Math.min(this.merge_max_lv, hero_lv + 1), pool_lv, sumAp, sumHpMax);
}
/** 链式合成:当前等级合成完成后,继续尝试更高等级,直到条件不满足 */
private async tryChainMerge(uuid: number, startLv: number) {
private async tryChainMerge(uuid: number, startLv: number, pool_lv: number) {
let checkLv = Math.max(1, startLv);
const needCount = this.getMergeNeedCount();
let guard = 0;
@@ -291,7 +293,7 @@ export class MissionHeroCompComp extends CCComp {
if (mergeHeroes.length < needCount) {
break;
}
checkLv = await this.mergeGroupHeroes(mergeHeroes, uuid, checkLv);
checkLv = await this.mergeGroupHeroes(mergeHeroes, uuid, checkLv, pool_lv);
}
}