1 Commits

Author SHA1 Message Date
walkpan
882f2b8536 refactor: 优化战斗流程与英雄死亡处理逻辑
1. 移除HeroAttrsComp中多余的空行
2. 修改英雄死亡处理:直接销毁实体而非移至墓地
3. 调整波次更迭逻辑:直接进入BattleStart而非PrepareStart
4. 简化fight_ready方法:移除死亡英雄复活入场逻辑
5. 调整抽卡面板与卡牌池逻辑:战斗阶段保留抽卡面板且仅刷英雄卡
6. 将enterPreparePhase改为public方法
2026-05-15 19:00:11 +08:00
13 changed files with 460 additions and 395 deletions

View File

@@ -99,7 +99,7 @@
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 200, "x": 200,
"y": 177, "y": 177.92700000000002,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -266,8 +266,8 @@
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 91.434, "x": 100,
"y": 99.82, "y": 177.92700000000002,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -434,8 +434,8 @@
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 110.644, "x": 0,
"y": 234.75, "y": 177.92700000000002,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -602,8 +602,8 @@
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": -47.56, "x": -100,
"y": 261.112, "y": 177.92700000000002,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -770,8 +770,8 @@
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": -30.737, "x": -200,
"y": 68.374, "y": 177.92700000000002,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {

View File

@@ -1382,7 +1382,7 @@
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": -330, "x": -330,
"y": 372.03, "y": 390.888,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -2604,7 +2604,7 @@
"__id__": 148 "__id__": 148
} }
], ],
"_active": true, "_active": false,
"_components": [ "_components": [
{ {
"__id__": 156 "__id__": 156
@@ -2621,8 +2621,8 @@
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 259.067, "x": 0,
"y": 158.268, "y": 208.926,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -2715,7 +2715,7 @@
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
"width": 510.00000000000006, "width": 720,
"height": 50 "height": 50
}, },
"_anchorPoint": { "_anchorPoint": {
@@ -3166,7 +3166,7 @@
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
"width": 510.00000000000006, "width": 720,
"height": 50 "height": 50
}, },
"_anchorPoint": { "_anchorPoint": {
@@ -3236,8 +3236,8 @@
}, },
"_alignFlags": 40, "_alignFlags": 40,
"_target": null, "_target": null,
"_left": 4.06699999999995, "_left": 0,
"_right": -4.06699999999995, "_right": 0,
"_top": 0, "_top": 0,
"_bottom": 0, "_bottom": 0,
"_horizontalCenter": 0, "_horizontalCenter": 0,
@@ -3316,7 +3316,7 @@
"_left": 30, "_left": 30,
"_right": 515, "_right": 515,
"_top": 0, "_top": 0,
"_bottom": 242.02999999999997, "_bottom": 260.888,
"_horizontalCenter": 0, "_horizontalCenter": 0,
"_verticalCenter": 0, "_verticalCenter": 0,
"_isAbsLeft": true, "_isAbsLeft": true,

View File

@@ -22,26 +22,26 @@
"__id__": 2 "__id__": 2
}, },
{ {
"__id__": 276 "__id__": 286
}, },
{ {
"__id__": 285 "__id__": 295
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 297 "__id__": 307
}, },
{ {
"__id__": 299 "__id__": 309
}, },
{ {
"__id__": 301 "__id__": 311
} }
], ],
"_prefab": { "_prefab": {
"__id__": 303 "__id__": 313
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -94,17 +94,17 @@
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 269 "__id__": 279
}, },
{ {
"__id__": 271 "__id__": 281
}, },
{ {
"__id__": 273 "__id__": 283
} }
], ],
"_prefab": { "_prefab": {
"__id__": 275 "__id__": 285
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -6090,7 +6090,7 @@
}, },
{ {
"__type__": "cc.PrefabInstance", "__type__": "cc.PrefabInstance",
"fileId": "87n/wcZ7tIPbu7m1+Q/KV3", "fileId": "c8OWze/z9IMpmg5hcYAPwM",
"prefabRootNode": { "prefabRootNode": {
"__id__": 1 "__id__": 1
}, },
@@ -6112,6 +6112,21 @@
}, },
{ {
"__id__": 268 "__id__": 268
},
{
"__id__": 269
},
{
"__id__": 271
},
{
"__id__": 273
},
{
"__id__": 275
},
{
"__id__": 277
} }
], ],
"removedComponents": [] "removedComponents": []
@@ -6149,14 +6164,14 @@
"__prefab": { "__prefab": {
"__id__": 263 "__id__": 263
}, },
"_alignFlags": 2, "_alignFlags": 18,
"_target": null, "_target": null,
"_left": 0, "_left": 0,
"_right": 0, "_right": 0,
"_top": 0, "_top": 0,
"_bottom": 0, "_bottom": 0,
"_horizontalCenter": 0, "_horizontalCenter": -76.841,
"_verticalCenter": 0, "_verticalCenter": 200,
"_isAbsLeft": true, "_isAbsLeft": true,
"_isAbsRight": true, "_isAbsRight": true,
"_isAbsTop": true, "_isAbsTop": true,
@@ -6171,7 +6186,7 @@
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
"fileId": "bcCrVWMRBOaLOq1wdUZD1Y" "fileId": "3eiYz0maFGj5G3KaylC3ro"
}, },
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
@@ -6199,8 +6214,8 @@
], ],
"value": { "value": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": -143.895, "x": -76.841,
"y": 640, "y": 840,
"z": 0 "z": 0
} }
}, },
@@ -6235,6 +6250,111 @@
"z": 0 "z": 0
} }
}, },
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 270
},
"propertyPath": [
"_lpos"
],
"value": {
"__type__": "cc.Vec3",
"x": 200,
"y": 0,
"z": 0
}
},
{
"__type__": "cc.TargetInfo",
"localID": [
"bdA9GHaYtL34EFtEibL2hR"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 272
},
"propertyPath": [
"_lpos"
],
"value": {
"__type__": "cc.Vec3",
"x": 100,
"y": 0,
"z": 0
}
},
{
"__type__": "cc.TargetInfo",
"localID": [
"adFmHHJ5BBAY0CazvLDgGw"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 274
},
"propertyPath": [
"_lpos"
],
"value": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
}
},
{
"__type__": "cc.TargetInfo",
"localID": [
"96QFNOz55OyZEByIPMUwVi"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 276
},
"propertyPath": [
"_lpos"
],
"value": {
"__type__": "cc.Vec3",
"x": -100,
"y": 0,
"z": 0
}
},
{
"__type__": "cc.TargetInfo",
"localID": [
"82phyYrMBJLLQftj0f4fIS"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 278
},
"propertyPath": [
"_lpos"
],
"value": {
"__type__": "cc.Vec3",
"x": -200,
"y": 0,
"z": 0
}
},
{
"__type__": "cc.TargetInfo",
"localID": [
"aenGhipwJAKZ3DgYqvrUgU"
]
},
{ {
"__type__": "cc.UITransform", "__type__": "cc.UITransform",
"_name": "", "_name": "",
@@ -6245,7 +6365,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 270 "__id__": 280
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -6273,7 +6393,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 272 "__id__": 282
}, },
"_alignFlags": 21, "_alignFlags": 21,
"_target": null, "_target": null,
@@ -6309,7 +6429,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 274 "__id__": 284
}, },
"home_btn": { "home_btn": {
"__id__": 45 "__id__": 45
@@ -6346,14 +6466,14 @@
"__id__": 1 "__id__": 1
}, },
"_prefab": { "_prefab": {
"__id__": 277 "__id__": 287
}, },
"__editorExtras__": {} "__editorExtras__": {}
}, },
{ {
"__type__": "cc.PrefabInfo", "__type__": "cc.PrefabInfo",
"root": { "root": {
"__id__": 276 "__id__": 286
}, },
"asset": { "asset": {
"__uuid__": "26bff847-cd29-48a5-bbfa-c3e2dbda688d", "__uuid__": "26bff847-cd29-48a5-bbfa-c3e2dbda688d",
@@ -6361,7 +6481,7 @@
}, },
"fileId": "5a9CMsVQhKP5Y+UJfTKPbx", "fileId": "5a9CMsVQhKP5Y+UJfTKPbx",
"instance": { "instance": {
"__id__": 278 "__id__": 288
}, },
"targetOverrides": null "targetOverrides": null
}, },
@@ -6375,19 +6495,19 @@
"mountedComponents": [], "mountedComponents": [],
"propertyOverrides": [ "propertyOverrides": [
{ {
"__id__": 279 "__id__": 289
}, },
{ {
"__id__": 281 "__id__": 291
}, },
{ {
"__id__": 282 "__id__": 292
}, },
{ {
"__id__": 283 "__id__": 293
}, },
{ {
"__id__": 284 "__id__": 294
} }
], ],
"removedComponents": [] "removedComponents": []
@@ -6395,7 +6515,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 280 "__id__": 290
}, },
"propertyPath": [ "propertyPath": [
"_name" "_name"
@@ -6411,7 +6531,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 280 "__id__": 290
}, },
"propertyPath": [ "propertyPath": [
"_lpos" "_lpos"
@@ -6426,7 +6546,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 280 "__id__": 290
}, },
"propertyPath": [ "propertyPath": [
"_lrot" "_lrot"
@@ -6442,7 +6562,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 280 "__id__": 290
}, },
"propertyPath": [ "propertyPath": [
"_euler" "_euler"
@@ -6457,7 +6577,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 280 "__id__": 290
}, },
"propertyPath": [ "propertyPath": [
"_active" "_active"
@@ -6471,14 +6591,14 @@
"__id__": 1 "__id__": 1
}, },
"_prefab": { "_prefab": {
"__id__": 286 "__id__": 296
}, },
"__editorExtras__": {} "__editorExtras__": {}
}, },
{ {
"__type__": "cc.PrefabInfo", "__type__": "cc.PrefabInfo",
"root": { "root": {
"__id__": 285 "__id__": 295
}, },
"asset": { "asset": {
"__uuid__": "56aee962-4a5e-45ae-a779-999444d06d18", "__uuid__": "56aee962-4a5e-45ae-a779-999444d06d18",
@@ -6486,7 +6606,7 @@
}, },
"fileId": "cboM54s0hM07XCtrpFp0/b", "fileId": "cboM54s0hM07XCtrpFp0/b",
"instance": { "instance": {
"__id__": 287 "__id__": 297
}, },
"targetOverrides": null "targetOverrides": null
}, },
@@ -6500,25 +6620,25 @@
"mountedComponents": [], "mountedComponents": [],
"propertyOverrides": [ "propertyOverrides": [
{ {
"__id__": 288 "__id__": 298
}, },
{ {
"__id__": 290 "__id__": 300
}, },
{ {
"__id__": 291 "__id__": 301
}, },
{ {
"__id__": 292 "__id__": 302
}, },
{ {
"__id__": 293 "__id__": 303
}, },
{ {
"__id__": 295 "__id__": 305
}, },
{ {
"__id__": 296 "__id__": 306
} }
], ],
"removedComponents": [] "removedComponents": []
@@ -6526,7 +6646,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 289 "__id__": 299
}, },
"propertyPath": [ "propertyPath": [
"_name" "_name"
@@ -6542,7 +6662,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 289 "__id__": 299
}, },
"propertyPath": [ "propertyPath": [
"_lpos" "_lpos"
@@ -6557,7 +6677,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 289 "__id__": 299
}, },
"propertyPath": [ "propertyPath": [
"_lrot" "_lrot"
@@ -6573,7 +6693,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 289 "__id__": 299
}, },
"propertyPath": [ "propertyPath": [
"_euler" "_euler"
@@ -6588,7 +6708,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 294 "__id__": 304
}, },
"propertyPath": [ "propertyPath": [
"_top" "_top"
@@ -6604,7 +6724,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 294 "__id__": 304
}, },
"propertyPath": [ "propertyPath": [
"_alignFlags" "_alignFlags"
@@ -6614,7 +6734,7 @@
{ {
"__type__": "CCPropertyOverrideInfo", "__type__": "CCPropertyOverrideInfo",
"targetInfo": { "targetInfo": {
"__id__": 294 "__id__": 304
}, },
"propertyPath": [ "propertyPath": [
"_bottom" "_bottom"
@@ -6631,7 +6751,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 298 "__id__": 308
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -6659,7 +6779,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 300 "__id__": 310
}, },
"_alignFlags": 45, "_alignFlags": 45,
"_target": null, "_target": null,
@@ -6695,7 +6815,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 302 "__id__": 312
}, },
"debugMode": false, "debugMode": false,
"_id": "" "_id": ""
@@ -6717,10 +6837,10 @@
"targetOverrides": null, "targetOverrides": null,
"nestedPrefabInstanceRoots": [ "nestedPrefabInstanceRoots": [
{ {
"__id__": 285 "__id__": 295
}, },
{ {
"__id__": 276 "__id__": 286
}, },
{ {
"__id__": 257 "__id__": 257

View File

@@ -176,54 +176,43 @@ export const HeroInfo: Record<number, heroInfo> = {
/* /*
*=============怪物配置列表================ *=============怪物配置列表================
* 基础近战型(lv:1) SPEED:800 |AP:12 | HP:360 | skills[0].cd=0.65 * 基础近战型(lv:1) SPEED:800 |AP:12 | HP:120 | skills[0].cd=0.65
* 重型坦克型(lv:1) SPEED:800 |AP:30 | HP:1050 | skills[0].cd=2 * 重型坦克型(lv:1) SPEED:800 |AP:30 | HP:350 | skills[0].cd=2
* 远程dps(lv:1) SPEED:800 |AP:45 | HP:240 | skills[0].cd=1.5 * 远程dps(lv:1) SPEED:800 |AP:45 | HP:80 | skills[0].cd=1.5
* 远程辅助(lv:1) SPEED:800 |AP:20 | HP:240 | skills[0].cd=1 * 远程辅助(lv:1) SPEED:800 |AP:20 | HP:80 | skills[0].cd=1
* 精英 (lv:1) SPEED:800 |AP:20 | HP:4500 | skills[0].cd=1 * 精英 (lv:1) SPEED:800 |AP:20 | HP:1500 | skills[0].cd=1
*/ */
//============== 兽人系列 =============== //============== 兽人系列 ===============
// 近战型 // 近战型
6001:{uuid:6001,name:"兽人战士",path:"mo1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:360,ap:12,speed:100, 6001:{uuid:6001,name:"兽人战士",path:"mo1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:120,ap:12,speed:100,
skills:{6001:{uuid:6001,lv:1,cd:0.65,ccd:0}},info:""}, skills:{6001:{uuid:6001,lv:1,cd:0.65,ccd:0}},info:""},
6002:{uuid:6002,name:"兽人斥候",path:"mo3", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:360,ap:12,speed:100, 6002:{uuid:6002,name:"兽人斥候",path:"mo3", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:120,ap:12,speed:100,
skills:{6001:{uuid:6001,lv:1,cd:0.65,ccd:0}},info:""}, skills:{6001:{uuid:6001,lv:1,cd:0.65,ccd:0}},info:""},
6003:{uuid:6003,name:"兽人卫士",path:"mo4", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:1050,ap:30,speed:100, 6003:{uuid:6003,name:"兽人卫士",path:"mo4", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:350,ap:30,speed:100,
skills:{6001:{uuid:6001,lv:1,cd:2,ccd:0}},info:""}, skills:{6001:{uuid:6001,lv:1,cd:2,ccd:0}},info:""},
6004:{uuid:6004,name:"兽人射手",path:"mo2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:240,ap:45,speed:100, 6004:{uuid:6004,name:"兽人射手",path:"mo2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:80,ap:45,speed:100,
skills:{6001:{uuid:6101,lv:1,cd:1.5,ccd:0}},info:""}, skills:{6001:{uuid:6101,lv:1,cd:1.5,ccd:0}},info:""},
6005:{uuid:6005,name:"兽人法师",path:"mo5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:240,ap:20,speed:100, 6005:{uuid:6005,name:"兽人法师",path:"mo5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:80,ap:20,speed:100,
skills:{6001:{uuid:6203,lv:1,cd:1.5,ccd:0}},info:""}, skills:{6001:{uuid:6203,lv:1,cd:1.5,ccd:0}},info:""},
6006:{uuid:6006,name:"兽人首领",path:"mo6", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:4500,ap:20,speed:100, 6006:{uuid:6006,name:"兽人首领",path:"mo6", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:1500,ap:20,speed:100,
skills:{6002:{uuid:6002,lv:1,cd:2,ccd:0}},info:""}, skills:{6002:{uuid:6002,lv:1,cd:2,ccd:0}},info:""},
//============== 亡灵系列 =============== //============== 亡灵系列 ===============
// 近战型 // 近战型
6101:{uuid:6101,name:"亡灵战士",path:"mud1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:360,ap:12,speed:100, 6101:{uuid:6101,name:"亡灵战士",path:"mud1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:120,ap:12,speed:100,
skills:{6001:{uuid:6001,lv:1,cd:0.65,ccd:0}},info:""}, skills:{6001:{uuid:6001,lv:1,cd:0.65,ccd:0}},info:""},
6103:{uuid:6103,name:"亡灵斥候",path:"mud3", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:360,ap:12,speed:100, 6103:{uuid:6103,name:"亡灵斥候",path:"mud3", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:120,ap:12,speed:100,
skills:{6001:{uuid:6001,lv:1,cd:0.65,ccd:0}},info:""}, skills:{6001:{uuid:6001,lv:1,cd:0.65,ccd:0}},info:""},
6102:{uuid:6102,name:"亡灵射手",path:"mud2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:240,ap:45,speed:100, 6102:{uuid:6102,name:"亡灵射手",path:"mud2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:80,ap:45,speed:100,
skills:{6001:{uuid:6101,lv:1,cd:1.5,ccd:0}},info:""}, skills:{6001:{uuid:6101,lv:1,cd:1.5,ccd:0}},info:""},
// 6105:{uuid:6105,name:"兽人法师",path:"mud5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:240,ap:20,speed:100, // 6105:{uuid:6105,name:"兽人法师",path:"mud5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:80,ap:20,speed:100,
// skills:{6001:{uuid:6001,lv:1,cd:1,ccd:0},6003:{uuid:6003,lv:1,cd:10,ccd:0}},info:""}, // skills:{6001:{uuid:6001,lv:1,cd:1,ccd:0},6003:{uuid:6003,lv:1,cd:10,ccd:0}},info:""},
// 6. 精英/BOSS型 // 6. 精英/BOSS型
6104:{uuid:6104,name:"亡灵法师",path:"mud4", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:1050,ap:30,speed:100, 6104:{uuid:6104,name:"亡灵法师",path:"mud4", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:350,ap:30,speed:100,
skills:{6204:{uuid:6204,lv:1,cd:2,ccd:0},6206:{uuid:6206,lv:1,cd:10,ccd:0}},info:""}, skills:{6204:{uuid:6204,lv:1,cd:2,ccd:0},6206:{uuid:6206,lv:1,cd:10,ccd:0}},info:""},
6105:{uuid:6105,name:"亡灵首领",path:"mud5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:4500,ap:20,speed:100, 6105:{uuid:6105,name:"亡灵首领",path:"mud5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:1500,ap:20,speed:100,
skills:{6002:{uuid:6002,lv:1,cd:2,ccd:0},6005:{uuid:6005,lv:1,cd:10,ccd:0}},info:""}, skills:{6002:{uuid:6002,lv:1,cd:2,ccd:0},6005:{uuid:6005,lv:1,cd:10,ccd:0}},info:""},
//============== 特殊类型 (Bomber, Summoner, Assassin, Splitter) ===============
6201:{uuid:6201,name:"哥布林自爆兵",path:"mo2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:180,ap:80,speed:150,
skills:{6001:{uuid:6001,lv:1,cd:1,ccd:0}},info:"自爆兵"},
6202:{uuid:6202,name:"骷髅自爆兵",path:"mud2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:180,ap:80,speed:150,
skills:{6001:{uuid:6001,lv:1,cd:1,ccd:0}},info:"自爆兵"},
6203:{uuid:6203,name:"深渊召唤师",path:"hm2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:300,ap:15,speed:80,
skills:{6001:{uuid:6203,lv:1,cd:2,ccd:0}},info:"召唤师"},
6204:{uuid:6204,name:"暗影刺客",path:"hc1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:270,ap:55,speed:200,
skills:{6001:{uuid:6001,lv:1,cd:0.5,ccd:0}},info:"刺客"},
6205:{uuid:6205,name:"分裂软泥",path:"mo1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:450,ap:20,speed:90,
skills:{6001:{uuid:6001,lv:1,cd:1,ccd:0}},info:"分裂怪"},
}; };
export const HeroList: number[] = [ export const HeroList: number[] = [

View File

@@ -320,7 +320,6 @@ export class HeroAttrsComp extends ecs.Comp {
this.maxSkillDistance = 0; this.maxSkillDistance = 0;
this.minSkillDistance = 0; this.minSkillDistance = 0;
this.is_dead = false; this.is_dead = false;
this.is_count_dead = false; this.is_count_dead = false;
this.is_atking = false; this.is_atking = false;

View File

@@ -446,14 +446,13 @@ export class HeroViewComp extends CCComp {
} }
if(this.model.fac === FacSet.HERO){ if(this.model.fac === FacSet.HERO){
// 英雄移到玩家看不到的墓地 // 英雄直接销毁,不再进入墓地
this.node.setPosition(v3(-2000, -2000, 0));
const collider = this.node.getComponent(Collider2D); const collider = this.node.getComponent(Collider2D);
if (collider) { if (collider) {
collider.enabled = false; collider.enabled = false;
} }
// 隐藏UI
this.top_node.active = false; this.top_node.active = false;
this.ent.destroy();
} else { } else {
// 🔥 方案B治理性措施 - 在销毁实体前先禁用碰撞体,从源头减少"尸体"参与碰撞 // 🔥 方案B治理性措施 - 在销毁实体前先禁用碰撞体,从源头减少"尸体"参与碰撞
const collider = this.node.getComponent(Collider2D); const collider = this.node.getComponent(Collider2D);

View File

@@ -63,7 +63,7 @@ const { ccclass, property } = _decorator;
@ecs.register('MissionCard', false) @ecs.register('MissionCard', false)
export class MissionCardComp extends CCComp { export class MissionCardComp extends CCComp {
/** 是否启用调试日志 */ /** 是否启用调试日志 */
private debugMode: boolean = false; private debugMode: boolean = true;
/** 卡牌槽位宽度(像素),用于水平等距布局 */ /** 卡牌槽位宽度(像素),用于水平等距布局 */
private readonly cardWidth: number = 175; private readonly cardWidth: number = 175;
/** 按钮正常缩放 */ /** 按钮正常缩放 */
@@ -433,13 +433,6 @@ export class MissionCardComp extends CCComp {
const payload = args ?? event; const payload = args ?? event;
if (!payload) return; if (!payload) return;
if (this.isBattlePhase) {
payload.cancel = true;
payload.reason = "battle_phase";
oops.gui.toast("战斗阶段无法召唤英雄");
return;
}
const current = this.getAliveHeroCount(); const current = this.getAliveHeroCount();
this.syncMissionHeroData(current); this.syncMissionHeroData(current);
const heroMax = this.getMissionHeroMaxNum(); const heroMax = this.getMissionHeroMaxNum();
@@ -671,16 +664,10 @@ export class MissionCardComp extends CCComp {
private enterBattlePhase() { private enterBattlePhase() {
if (!this.cards_node || !this.cards_node.isValid) return; if (!this.cards_node || !this.cards_node.isValid) return;
this.initCardsPanelPos(); this.initCardsPanelPos();
// 战斗阶段不再隐藏抽卡面板 // 战斗阶段抽卡面板不再收起
// Tween.stopAllByTarget(this.cards_node); this.cards_node.active = true;
// tween(this.cards_node) Tween.stopAllByTarget(this.cards_node);
// .to(this.cardsPanelMoveDuration, { scale: this.cardsHideScale }) this.cards_node.setScale(this.cardsShowScale);
// .call(() => {
// if (this.cards_node && this.cards_node.isValid) {
// this.cards_node.active = false;
// }
// })
// .start();
this.cachedHInfoComps.forEach(comp => { this.cachedHInfoComps.forEach(comp => {
if (comp && comp.isValid) { if (comp && comp.isValid) {
@@ -693,7 +680,8 @@ export class MissionCardComp extends CCComp {
private buildDrawCards(): CardConfig[] { private buildDrawCards(): CardConfig[] {
let targetType: CardType | CardType[] | undefined = undefined; let targetType: CardType | CardType[] | undefined = undefined;
if (this.isBattlePhase) { if (this.isBattlePhase) {
targetType = CardType.Skill; // 战斗阶段只刷英雄卡牌,不刷其他卡牌
targetType = CardType.Hero;
} else { } else {
targetType = [CardType.Hero, CardType.SpecialRefresh]; targetType = [CardType.Hero, CardType.SpecialRefresh];
} }

View File

@@ -398,7 +398,7 @@ export class MissionComp extends CCComp {
const label = phaseNode.getComponent(Label); const label = phaseNode.getComponent(Label);
if (label) { if (label) {
const wave = Math.max(1, this.currentWave || (smc.vmdata && smc.vmdata.mission_data ? smc.vmdata.mission_data.level : 1) || 1); const wave = Math.max(1, this.currentWave || (smc.vmdata && smc.vmdata.mission_data ? smc.vmdata.mission_data.level : 1) || 1);
label.string = `${wave}/15`; label.string = `${wave}/30`;
} }
// 阶段切换动感表现:只在进入战斗阶段跳动一下,让流程充满心流体验 // 阶段切换动感表现:只在进入战斗阶段跳动一下,让流程充满心流体验
@@ -473,8 +473,8 @@ export class MissionComp extends CCComp {
smc.vmdata.scores.wave_all_alive_count++; smc.vmdata.scores.wave_all_alive_count++;
} }
// 【评分系统 - 战绩分】判断是否通过最后一关(第15回合) // 【评分系统 - 战绩分】判断是否通过最后一关(第30回合)
if (this.currentWave === 15) { if (this.currentWave === 30) {
smc.vmdata.scores.passed_wave_20 = true; smc.vmdata.scores.passed_wave_20 = true;
} }
} }
@@ -520,9 +520,9 @@ export class MissionComp extends CCComp {
case MissionPhase.BattleEnd: case MissionPhase.BattleEnd:
// BattleEnd 计时结束后,如果是因为全灭或手动调用的 fight_end进入 Settle // BattleEnd 计时结束后,如果是因为全灭或手动调用的 fight_end进入 Settle
// 需要注意的是open_Victory / fight_end 现在只需切换到 BattleEnd 即可Settle 由这里自动接管 // 需要注意的是open_Victory / fight_end 现在只需切换到 BattleEnd 即可Settle 由这里自动接管
// 如果游戏正在运行(波次更迭),则自动进入 PrepareStart 阶段 // 如果游戏正在运行(波次更迭),直接进入下一波的 BattleStart不再进入 PrepareStart
if (smc.mission.play && !smc.mission.pause) { if (smc.mission.play && !smc.mission.pause) {
this.changePhase(MissionPhase.PrepareStart); this.changePhase(MissionPhase.BattleStart);
} else { } else {
this.changePhase(MissionPhase.Settle); this.changePhase(MissionPhase.Settle);
@@ -566,7 +566,7 @@ export class MissionComp extends CCComp {
* - 显示开始按钮 * - 显示开始按钮
* - 触发英雄战斗结束技能 * - 触发英雄战斗结束技能
*/ */
private enterPreparePhase() { enterPreparePhase() {
this.changePhase(MissionPhase.PrepareStart); this.changePhase(MissionPhase.PrepareStart);
} }
@@ -853,16 +853,15 @@ export class MissionComp extends CCComp {
}); });
this.handleHeroWipe(heroCount); this.handleHeroWipe(heroCount);
// 怪物全灭检测:如果战斗阶段场上没有任何活着的怪物,且待刷新的怪物队列也为空,直接结束战斗进入下一波的准备阶段 // 怪物全灭检测:如果战斗阶段场上没有任何活着的怪物,直接结束战斗进入下一波的准备阶段
const pendingCount = smc.vmdata.mission_data.pending_mon_num || 0; if (monsterCount === 0 && smc.mission.play && !smc.mission.pause && this.currentPhase === MissionPhase.Battle) {
if (monsterCount === 0 && pendingCount === 0 && smc.mission.play && !smc.mission.pause && this.currentPhase === MissionPhase.Battle) {
let heroesAliveRatio = heroCount / 6.0; // 假设最大 6 个站位,或者直接基于存活数算比例 let heroesAliveRatio = heroCount / 6.0; // 假设最大 6 个站位,或者直接基于存活数算比例
// 如果能获取当前已部署英雄数最好,这里简化处理,大于 4 个就算高存活 // 如果能获取当前已部署英雄数最好,这里简化处理,大于 4 个就算高存活
heroesAliveRatio = Math.min(1.0, heroCount / 4.0); heroesAliveRatio = Math.min(1.0, heroCount / 4.0);
spawningEngine.updateAdaptive(heroesAliveRatio, this.clearTime); spawningEngine.updateAdaptive(heroesAliveRatio, this.clearTime);
if (this.currentWave >= 15) { if (this.currentWave >= 30) {
// 15 波通关 // 30 波通关
this.open_Victory(null, false); this.open_Victory(null, false);
} else { } else {
oops.message.dispatchEvent("TimeUpAdvanceWave"); oops.message.dispatchEvent("TimeUpAdvanceWave");

View File

@@ -121,24 +121,14 @@ export class MissionHeroCompComp extends CCComp {
} }
} }
/** 战斗准备阶段:重置出战英雄计数,恢复满血重新登场 */ /** 战斗准备阶段:重置出战英雄计数,恢复满血 */
fight_ready(){ fight_ready(){
const heroes = this.getAllHeroes(); const heroes = this.getAllHeroes();
smc.vmdata.mission_data.hero_num = heroes.length; smc.vmdata.mission_data.hero_num = heroes.length;
for (let i = 0; i < heroes.length; i++) { for (let i = 0; i < heroes.length; i++) {
const hero = heroes[i]; const hero = heroes[i];
const model = hero.get(HeroAttrsComp); const model = hero.get(HeroAttrsComp);
const view = hero.get(HeroViewComp); if (model) {
if (model && view) {
if (model.is_dead) {
view.alive();
const landingPos = this.pickPositionForHero([hero.eid]);
// 不再直接设置位置,而是播放下落入场动画
// 计算出出生点(空中)
const spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
view.node.setPosition(spawnPos);
hero.playDropAnim(spawnPos, landingPos.y);
}
model.dirty_hp = true; model.dirty_hp = true;
} }
} }

View File

@@ -98,14 +98,6 @@ export class MissionMonCompComp extends CCComp {
private pendingMonsters: GeneratedMonster[] = []; private pendingMonsters: GeneratedMonster[] = [];
/** 增量刷怪计时器 */ /** 增量刷怪计时器 */
private spawnTimer: number = 0; private spawnTimer: number = 0;
/** 分段刷怪阶段 (1, 2, 3) */
private currentSpawnPhase: number = 1;
/** 下一阶段刷怪的延迟计时器 */
private phaseDelayTimer: number = 0;
/** 当前阶段目标生成的怪物总数 */
private phaseTargetCount: number = 0;
/** 当前阶段已生成的怪物数 */
private phaseSpawnedCount: number = 0;
// ======================== 生命周期 ======================== // ======================== 生命周期 ========================
@@ -123,8 +115,6 @@ export class MissionMonCompComp extends CCComp {
* 3. 逐步从 pendingMonsters 队列中生成怪物(受 stop_spawn_mon 限制)。 * 3. 逐步从 pendingMonsters 队列中生成怪物(受 stop_spawn_mon 限制)。
*/ */
protected update(dt: number): void { protected update(dt: number): void {
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
if(!smc.mission.play) return if(!smc.mission.play) return
if(smc.mission.pause) return if(smc.mission.pause) return
if(smc.mission.stop_mon_action) return; if(smc.mission.stop_mon_action) return;
@@ -134,35 +124,20 @@ export class MissionMonCompComp extends CCComp {
if(smc.mission.stop_spawn_mon) return; if(smc.mission.stop_spawn_mon) return;
// 逐步刷怪逻辑 (分 3 段刷出) // 逐步刷怪逻辑
if (this.pendingMonsters.length > 0) { if (this.pendingMonsters.length > 0) {
// 如果当前阶段的怪物已经刷完,则进入延迟等待下一阶段
if (this.phaseSpawnedCount >= this.phaseTargetCount && this.currentSpawnPhase < 3) {
this.phaseDelayTimer -= dt;
if (this.phaseDelayTimer <= 0) {
this.currentSpawnPhase++;
this.phaseSpawnedCount = 0;
this.phaseTargetCount = this.currentSpawnPhase === 3 ?
this.pendingMonsters.length :
Math.ceil(this.pendingMonsters.length / (4 - this.currentSpawnPhase));
}
return;
}
this.spawnTimer += dt; this.spawnTimer += dt;
// 控制刷怪速率:例如每 0.2 秒刷 1-2 只 // 控制刷怪速率:例如每 0.2 秒刷 1-2 只
if (this.spawnTimer > 0.2) { if (this.spawnTimer > 0.2) {
this.spawnTimer = 0; this.spawnTimer = 0;
// 一次出 2 只,加快进度 // 一次出 2 只,加快进度
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
if (this.pendingMonsters.length === 0 || this.phaseSpawnedCount >= this.phaseTargetCount) break; if (this.pendingMonsters.length === 0) break;
const monData = this.pendingMonsters.shift()!; const monData = this.pendingMonsters.shift()!;
const lane = this.pickBalancedLane(); const lane = this.pickBalancedLane();
console.log(`[MissionMonComp] [Phase ${this.currentSpawnPhase}] 准备生成怪物 UUID=${monData.uuid}, 剩余数量=${this.pendingMonsters.length}`);
this.addMonsterAt(lane, this.laneIndices[lane], monData); this.addMonsterAt(lane, this.laneIndices[lane], monData);
this.laneIndices[lane]++; this.laneIndices[lane]++;
this.waveSpawnedCount++; this.waveSpawnedCount++;
this.phaseSpawnedCount++;
} }
} }
} }
@@ -190,32 +165,6 @@ export class MissionMonCompComp extends CCComp {
start() { start() {
} }
private setupWaveData(monsters: GeneratedMonster[]) {
this.pendingMonsters = monsters;
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
this.waveTargetCount = monsters.length;
// 初始化分段刷怪状态
this.currentSpawnPhase = 1;
this.phaseSpawnedCount = 0;
// 第一段生成 1/3 的怪物
this.phaseTargetCount = Math.ceil(this.pendingMonsters.length / 3);
// 每段之间的延迟时间,可以根据需要调整,例如 3 秒
this.phaseDelayTimer = 3.0;
let hasBoss = monsters.some(m => m.isBoss);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
const uuids = monsters.map(m => m.uuid);
console.log(`[MissionMonComp] 波次 ${this.currentWave} 怪物 UUID 列表:`, uuids);
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
total: this.waveTargetCount,
bossWave: hasBoss,
});
}
/** /**
* 战斗准备:重置所有运行时状态并开始第一波。 * 战斗准备:重置所有运行时状态并开始第一波。
*/ */
@@ -234,7 +183,15 @@ export class MissionMonCompComp extends CCComp {
// 预生成第一波数据以获取数量和 Boss 信息 // 预生成第一波数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave); const monsters = spawningEngine.generateWave(this.currentWave);
this.setupWaveData(monsters); this.pendingMonsters = monsters;
this.waveTargetCount = monsters.length;
let hasBoss = monsters.some(m => m.isBoss);
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
total: this.waveTargetCount,
bossWave: hasBoss,
});
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System"); mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
} }
@@ -300,7 +257,15 @@ export class MissionMonCompComp extends CCComp {
// 预生成新一波数据以获取数量和 Boss 信息 // 预生成新一波数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave); const monsters = spawningEngine.generateWave(this.currentWave);
this.setupWaveData(monsters); this.pendingMonsters = monsters;
this.waveTargetCount = monsters.length;
let hasBoss = monsters.some(m => m.isBoss);
oops.message.dispatchEvent(GameEvent.NewWave, {
wave: this.currentWave,
total: this.waveTargetCount,
bossWave: hasBoss,
});
} }
private onPhasePrepareEnd() { private onPhasePrepareEnd() {

View File

@@ -3,7 +3,7 @@
* @description 肉鸽刷怪系统 v2 —— 三层程序化生成架构 * @description 肉鸽刷怪系统 v2 —— 三层程序化生成架构
* *
* 架构:蓝图模板(节奏) + 权重填充(内容) + 自适应微调(数值) * 架构:蓝图模板(节奏) + 权重填充(内容) + 自适应微调(数值)
* 主线 15 波 / 5 阶梯 / 每档 3 波(恢复→攀升→高潮) * 主线 30 波 / 10 阶梯 / 每档 3 波(恢复→攀升→高潮)
* 10 种怪物 + 8 种词缀 + 自适应难度 ±15% * 10 种怪物 + 8 种词缀 + 自适应难度 ±15%
* 通关后可选无限模式(分层推进) * 通关后可选无限模式(分层推进)
* *
@@ -86,47 +86,47 @@ export interface AffixConfig {
export const AffixConfigs: Record<AffixType, AffixConfig> = { export const AffixConfigs: Record<AffixType, AffixConfig> = {
[AffixType.Elite]: { [AffixType.Elite]: {
name: "精英", hpMultiplier: 1.5, apMultiplier: 1.3, name: "精英", hpMultiplier: 1.5, apMultiplier: 1.3,
cost: 20, tierMin: 3, description: "+50% HP, +30% AP", cost: 20, tierMin: 5, description: "+50% HP, +30% AP",
}, },
[AffixType.Berserk]: { [AffixType.Berserk]: {
name: "狂暴", hpMultiplier: 1.0, apMultiplier: 1.0, name: "狂暴", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 15, tierMin: 3, description: "攻速 ×1.5 (行为层实现)", cost: 15, tierMin: 5, description: "攻速 ×1.5 (行为层实现)",
}, },
[AffixType.Shield]: { [AffixType.Shield]: {
name: "护盾", hpMultiplier: 1.0, apMultiplier: 1.0, name: "护盾", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 25, tierMin: 3, description: "开局带 抵御2次 伤害吸收盾", cost: 25, tierMin: 6, description: "开局带 抵御2次 伤害吸收盾",
}, },
[AffixType.Regen]: { [AffixType.Regen]: {
name: "再生", hpMultiplier: 1.0, apMultiplier: 1.0, name: "再生", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 20, tierMin: 4, description: "每秒回复 2% HP", cost: 20, tierMin: 7, description: "每秒回复 2% HP",
}, },
[AffixType.Swift]: { [AffixType.Swift]: {
name: "疾速", hpMultiplier: 1.0, apMultiplier: 1.0, name: "疾速", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 10, tierMin: 4, description: "移速 ×2", cost: 10, tierMin: 7, description: "移速 ×2",
}, },
[AffixType.Giant]: { [AffixType.Giant]: {
name: "巨型", hpMultiplier: 2.0, apMultiplier: 1.5, name: "巨型", hpMultiplier: 2.0, apMultiplier: 1.5,
cost: 30, tierMin: 4, description: "×2 体型, +100% HP, +50% AP", cost: 30, tierMin: 8, description: "×2 体型, +100% HP, +50% AP",
}, },
[AffixType.Chain]: { [AffixType.Chain]: {
name: "连锁", hpMultiplier: 1.0, apMultiplier: 1.0, name: "连锁", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 20, tierMin: 5, description: "攻击附带 50% 溅射伤害", cost: 20, tierMin: 9, description: "攻击附带 50% 溅射伤害",
}, },
[AffixType.SummonerA]: { [AffixType.SummonerA]: {
name: "召唤", hpMultiplier: 1.0, apMultiplier: 1.0, name: "召唤", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 25, tierMin: 5, description: "每 8 秒召唤 1 个小怪", cost: 25, tierMin: 10, description: "每 8 秒召唤 1 个小怪",
}, },
[AffixType.CritRes]: { [AffixType.CritRes]: {
name: "坚韧", hpMultiplier: 1.0, apMultiplier: 1.0, name: "坚韧", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 15, tierMin: 3, description: "+暴击抗性", cost: 15, tierMin: 6, description: "+暴击抗性",
}, },
[AffixType.FreezeRes]: { [AffixType.FreezeRes]: {
name: "防寒", hpMultiplier: 1.0, apMultiplier: 1.0, name: "防寒", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 15, tierMin: 3, description: "+冰冻抗性", cost: 15, tierMin: 6, description: "+冰冻抗性",
}, },
[AffixType.KnockbackRes]: { [AffixType.KnockbackRes]: {
name: "稳固", hpMultiplier: 1.0, apMultiplier: 1.0, name: "稳固", hpMultiplier: 1.0, apMultiplier: 1.0,
cost: 15, tierMin: 3, description: "+击退抗性", cost: 15, tierMin: 6, description: "+击退抗性",
}, },
} }
@@ -221,16 +221,16 @@ export interface MonsterBaseStats {
* @see MonsterBaseStats 字段说明 * @see MonsterBaseStats 字段说明
*/ */
export const MonsterStats: Record<MonType, MonsterBaseStats> = { export const MonsterStats: Record<MonType, MonsterBaseStats> = {
[MonType.Melee]: { hp: 360, ap: 12, cost: 30, isBoss: false }, [MonType.Melee]: { hp: 120, ap: 12, cost: 30, isBoss: false },
[MonType.Heavy]: { hp: 1050, ap: 30, cost: 50, isBoss: false }, [MonType.Heavy]: { hp: 350, ap: 30, cost: 50, isBoss: false },
[MonType.Long]: { hp: 240, ap: 45, cost: 40, isBoss: false }, [MonType.Long]: { hp: 80, ap: 45, cost: 40, isBoss: false },
[MonType.Support]: { hp: 240, ap: 20, cost: 50, isBoss: false }, [MonType.Support]: { hp: 80, ap: 20, cost: 50, isBoss: false },
[MonType.Bomber]: { hp: 180, ap: 80, cost: 35, isBoss: false }, [MonType.Bomber]: { hp: 60, ap: 80, cost: 35, isBoss: false },
[MonType.Summoner]: { hp: 300, ap: 15, cost: 60, isBoss: false }, [MonType.Summoner]: { hp: 100, ap: 15, cost: 60, isBoss: false },
[MonType.Assassin]: { hp: 270, ap: 55, cost: 45, isBoss: false }, [MonType.Assassin]: { hp: 90, ap: 55, cost: 45, isBoss: false },
[MonType.Splitter]: { hp: 450, ap: 20, cost: 55, isBoss: false }, [MonType.Splitter]: { hp: 150, ap: 20, cost: 55, isBoss: false },
[MonType.MeleeBoss]: { hp: 4500, ap: 20, cost: 200, isBoss: true }, [MonType.MeleeBoss]: { hp: 1500, ap: 20, cost: 200, isBoss: true },
[MonType.LongBoss]: { hp: 1050, ap: 30, cost: 200, isBoss: true }, [MonType.LongBoss]: { hp: 350, ap: 30, cost: 200, isBoss: true },
} }
// ======================== 阶梯Tier配置 ======================== // ======================== 阶梯Tier配置 ========================
@@ -250,29 +250,38 @@ export interface TierConfig {
} }
/** Boss 出现的 Tier 集合MiniBoss 或 MajorBoss */ /** Boss 出现的 Tier 集合MiniBoss 或 MajorBoss */
const BOSS_TIERS = new Set([1, 2, 3, 4, 5]) const BOSS_TIERS = new Set([2, 4, 5, 7, 9, 10])
/** MajorBoss高难度 Boss出现的 Tier 集合 */ /** MajorBoss高难度 Boss出现的 Tier 集合 */
const MAJOR_BOSS_TIERS = new Set([3, 5]) const MAJOR_BOSS_TIERS = new Set([5, 10])
/** /**
* 5 阶梯配置表 * 10 阶梯配置表
* key = 阶梯编号 1-5value = 该阶梯的完整配置 * key = 阶梯编号 1-10value = 该阶梯的完整配置
* 主线 15 波映射wave 1-3 → T1, wave 4-6 → T2, ..., wave 13-15 → T5 * 主线 30 波映射wave 1-3 → T1, wave 4-6 → T2, ..., wave 28-30 → T10
*/ */
export const TierConfigs: Record<number, TierConfig> = { export const TierConfigs: Record<number, TierConfig> = {
1: { multiplier: 1.0, budget: 500, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long], isBossTier: false }, 1: { multiplier: 1.0, budget: 100, availableTypes: [MonType.Melee], isBossTier: false },
2: { multiplier: 1.6, budget: 1000, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support], isBossTier: true }, 2: { multiplier: 1.3, budget: 150, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long], isBossTier: true },
3: { multiplier: 2.5, budget: 1800, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin], isBossTier: true }, 3: { multiplier: 1.6, budget: 200, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long], isBossTier: false },
4: { multiplier: 3.8, budget: 3000, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true }, 4: { multiplier: 1.9, budget: 260, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support], isBossTier: true },
5: { multiplier: 5.5, budget: 5000, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true }, 5: { multiplier: 2.3, budget: 340, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber], isBossTier: true },
6: { multiplier: 2.8, budget: 440, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin], isBossTier: false },
7: { multiplier: 3.3, budget: 560, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true },
8: { multiplier: 3.9, budget: 700, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: false },
9: { multiplier: 4.6, budget: 860, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true },
10: { multiplier: 5.5, budget: 1050, availableTypes: [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Support, MonType.Bomber, MonType.Assassin, MonType.Summoner, MonType.Splitter], isBossTier: true },
} }
/** /**
* 获取指定阶梯的配置 * 获取指定阶梯的配置
* 支持 Tier 1-5(查表)和 Tier 6+无限模式递推计算T(n) = T(n-1) × 1.2 * 支持 Tier 1-10(查表)和 Tier 11+无限模式递推计算T(n) = T(n-1) × 1.2
* *
* @param tier - 阶梯编号1-5 主线,6+ 无限模式) * @param tier - 阶梯编号1-10 主线,11+ 无限模式)
* @returns TierConfig 该阶梯的完整配置 * @returns TierConfig 该阶梯的完整配置
*
* @example
* getTierConfig(1) // { multiplier: 1.0, budget: 100, ... }
* getTierConfig(11) // { multiplier: 6.6, budget: 1260, ... } (5.5 × 1.2)
*/ */
export function getTierConfig(tier: number): TierConfig { export function getTierConfig(tier: number): TierConfig {
if (TierConfigs[tier]) return TierConfigs[tier] if (TierConfigs[tier]) return TierConfigs[tier]
@@ -296,12 +305,13 @@ export function getTierConfig(tier: number): TierConfig {
/** /**
* 每个 Tier 的基础词缀触发概率0.0-1.0 * 每个 Tier 的基础词缀触发概率0.0-1.0
* T1-T2: 0%期无词缀) * T1-T4: 0%教学期无词缀)
* T3: 15%, T4: 30%, T5: 50% * T5: 10%, T6: 15%, T7: 25%, T8: 30%, T9: 40%, T10: 50%
* 最终概率 = baseAffixChance × roleMultiplier * 最终概率 = baseAffixChance × roleMultiplier
*/ */
export const BaseAffixChance: Record<number, number> = { export const BaseAffixChance: Record<number, number> = {
1: 0, 2: 0, 3: 0.15, 4: 0.30, 5: 0.50, 1: 0, 2: 0, 3: 0, 4: 0,
5: 0.10, 6: 0.15, 7: 0.25, 8: 0.30, 9: 0.40, 10: 0.50,
} }
/** /**
@@ -371,100 +381,100 @@ export interface BlueprintTemplate {
export const BlueprintTemplates: BlueprintTemplate[] = [ export const BlueprintTemplates: BlueprintTemplate[] = [
// ---- REST 类 ---- // ---- REST 类 ----
{ id: "R1", type: TemplateType.REST, tierMin: 1, allowAffix: false, { id: "R1", type: TemplateType.REST, tierMin: 1, allowAffix: false,
slots: [{ typePool: [MonType.Melee], countMin: 5, countMax: 10, weight: 1.0 }] }, slots: [{ typePool: [MonType.Melee], countMin: 1, countMax: 2, weight: 1.0 }] },
{ id: "R2", type: TemplateType.REST, tierMin: 1, allowAffix: false, { id: "R2", type: TemplateType.REST, tierMin: 2, allowAffix: false,
slots: [{ typePool: [MonType.Melee, MonType.Heavy], countMin: 5, countMax: 15, weight: 1.0 }] }, slots: [{ typePool: [MonType.Melee, MonType.Heavy], countMin: 1, countMax: 3, weight: 1.0 }] },
{ id: "R3", type: TemplateType.REST, tierMin: 3, allowAffix: false, { id: "R3", type: TemplateType.REST, tierMin: 5, allowAffix: false,
slots: [{ typePool: [MonType.Melee, MonType.Long], countMin: 10, countMax: 15, weight: 1.0 }] }, slots: [{ typePool: [MonType.Melee, MonType.Long], countMin: 2, countMax: 3, weight: 1.0 }] },
// ---- NORMAL 类 ---- // ---- NORMAL 类 ----
{ id: "N1", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false, { id: "N1", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false,
slots: [{ typePool: [MonType.Melee], countMin: 10, countMax: 20, weight: 1.0 }] }, slots: [{ typePool: [MonType.Melee], countMin: 2, countMax: 4, weight: 1.0 }] },
{ id: "N2", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false, { id: "N2", type: TemplateType.NORMAL, tierMin: 2, allowAffix: false,
slots: [ slots: [
{ typePool: [MonType.Melee], countMin: 5, countMax: 15, weight: 0.6 }, { typePool: [MonType.Melee], countMin: 1, countMax: 3, weight: 0.6 },
{ typePool: [MonType.Long, MonType.Heavy], countMin: 5, countMax: 10, weight: 0.4 }, { typePool: [MonType.Long, MonType.Heavy], countMin: 1, countMax: 2, weight: 0.4 },
] }, ] },
{ id: "N3", type: TemplateType.NORMAL, tierMin: 2, allowAffix: true, { id: "N3", type: TemplateType.NORMAL, tierMin: 4, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 15, weight: 0.5 }, { typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 3, weight: 0.5 },
{ typePool: [MonType.Long], countMin: 5, countMax: 10, weight: 0.3 }, { typePool: [MonType.Long], countMin: 1, countMax: 2, weight: 0.3 },
{ typePool: [MonType.Support], countMin: 5, countMax: 5, weight: 0.2 }, { typePool: [MonType.Support], countMin: 1, countMax: 1, weight: 0.2 },
] }, ] },
{ id: "N4", type: TemplateType.NORMAL, tierMin: 3, allowAffix: true, { id: "N4", type: TemplateType.NORMAL, tierMin: 6, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 20, weight: 0.4 }, { typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 4, weight: 0.4 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 5, countMax: 15, weight: 0.3 }, { typePool: [MonType.Long, MonType.Assassin], countMin: 1, countMax: 3, weight: 0.3 },
{ typePool: [MonType.Support, MonType.Bomber], countMin: 5, countMax: 10, weight: 0.3 }, { typePool: [MonType.Support, MonType.Bomber], countMin: 1, countMax: 2, weight: 0.3 },
] }, ] },
// ---- MIXED 类 ---- // ---- MIXED 类 ----
{ id: "M1", type: TemplateType.MIXED, tierMin: 1, allowAffix: true, { id: "M1", type: TemplateType.MIXED, tierMin: 3, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 15, weight: 0.4 }, { typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 3, weight: 0.4 },
{ typePool: [MonType.Long], countMin: 5, countMax: 10, weight: 0.3 }, { typePool: [MonType.Long], countMin: 1, countMax: 2, weight: 0.3 },
{ typePool: [MonType.Support], countMin: 5, countMax: 5, weight: 0.3 }, { typePool: [MonType.Support], countMin: 1, countMax: 1, weight: 0.3 },
] }, ] },
{ id: "M2", type: TemplateType.MIXED, tierMin: 3, allowAffix: true, { id: "M2", type: TemplateType.MIXED, tierMin: 5, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 20, weight: 0.3 }, { typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 4, weight: 0.3 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 10, countMax: 15, weight: 0.3 }, { typePool: [MonType.Long, MonType.Assassin], countMin: 2, countMax: 3, weight: 0.3 },
{ typePool: [MonType.Bomber], countMin: 5, countMax: 10, weight: 0.2 }, { typePool: [MonType.Bomber], countMin: 1, countMax: 2, weight: 0.2 },
{ typePool: [MonType.Support], countMin: 5, countMax: 5, weight: 0.2 }, { typePool: [MonType.Support], countMin: 1, countMax: 1, weight: 0.2 },
] }, ] },
{ id: "M3", type: TemplateType.MIXED, tierMin: 4, allowAffix: true, { id: "M3", type: TemplateType.MIXED, tierMin: 7, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 15, countMax: 20, weight: 0.3 }, { typePool: [MonType.Melee, MonType.Heavy], countMin: 3, countMax: 4, weight: 0.3 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 10, countMax: 15, weight: 0.25 }, { typePool: [MonType.Long, MonType.Assassin], countMin: 2, countMax: 3, weight: 0.25 },
{ typePool: [MonType.Summoner, MonType.Splitter], countMin: 5, countMax: 10, weight: 0.25 }, { typePool: [MonType.Summoner, MonType.Splitter], countMin: 1, countMax: 2, weight: 0.25 },
{ typePool: [MonType.Bomber, MonType.Support], countMin: 5, countMax: 10, weight: 0.2 }, { typePool: [MonType.Bomber, MonType.Support], countMin: 1, countMax: 2, weight: 0.2 },
] }, ] },
// ---- ELITE 类 ---- // ---- ELITE 类 ----
{ id: "E1", type: TemplateType.ELITE, tierMin: 3, allowAffix: true, { id: "E1", type: TemplateType.ELITE, tierMin: 5, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 5, countMax: 10, weight: 0.5, forceAffix: true }, { typePool: [MonType.Melee, MonType.Heavy], countMin: 1, countMax: 2, weight: 0.5, forceAffix: true },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 5, countMax: 10, weight: 0.5, forceAffix: true }, { typePool: [MonType.Long, MonType.Assassin], countMin: 1, countMax: 2, weight: 0.5, forceAffix: true },
] }, ] },
{ id: "E2", type: TemplateType.ELITE, tierMin: 4, allowAffix: true, { id: "E2", type: TemplateType.ELITE, tierMin: 7, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.Heavy], countMin: 5, countMax: 10, weight: 0.3, forceAffix: true }, { typePool: [MonType.Heavy], countMin: 1, countMax: 2, weight: 0.3, forceAffix: true },
{ typePool: [MonType.Assassin, MonType.Splitter], countMin: 5, countMax: 10, weight: 0.4, forceAffix: true }, { typePool: [MonType.Assassin, MonType.Splitter], countMin: 1, countMax: 2, weight: 0.4, forceAffix: true },
{ typePool: [MonType.Bomber], countMin: 5, countMax: 10, weight: 0.3, forceAffix: true }, { typePool: [MonType.Bomber], countMin: 1, countMax: 2, weight: 0.3, forceAffix: true },
] }, ] },
// ---- BOSS 类 ---- // ---- BOSS 类 ----
{ id: "B1", type: TemplateType.BOSS, tierMin: 1, allowAffix: true, { id: "B1", type: TemplateType.BOSS, tierMin: 2, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.MeleeBoss], countMin: 1, countMax: 1, weight: 1.0 }, { typePool: [MonType.MeleeBoss], countMin: 1, countMax: 1, weight: 1.0 },
{ typePool: [MonType.Melee], countMin: 10, countMax: 15, weight: 0.6 }, { typePool: [MonType.Melee], countMin: 2, countMax: 3, weight: 0.6 },
{ typePool: [MonType.Long], countMin: 0, countMax: 10, weight: 0.4 }, { typePool: [MonType.Long], countMin: 0, countMax: 2, weight: 0.4 },
] }, ] },
{ id: "B2", type: TemplateType.BOSS, tierMin: 2, allowAffix: true, { id: "B2", type: TemplateType.BOSS, tierMin: 4, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.MeleeBoss], countMin: 1, countMax: 1, weight: 1.0 }, { typePool: [MonType.MeleeBoss], countMin: 1, countMax: 1, weight: 1.0 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 15, weight: 0.5 }, { typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 3, weight: 0.5 },
{ typePool: [MonType.Long, MonType.Support], countMin: 5, countMax: 10, weight: 0.5 }, { typePool: [MonType.Long, MonType.Support], countMin: 1, countMax: 2, weight: 0.5 },
] }, ] },
{ id: "B3", type: TemplateType.BOSS, tierMin: 3, allowAffix: true, { id: "B3", type: TemplateType.BOSS, tierMin: 5, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.MeleeBoss, MonType.LongBoss], countMin: 1, countMax: 1, weight: 1.0 }, { typePool: [MonType.MeleeBoss, MonType.LongBoss], countMin: 1, countMax: 1, weight: 1.0 },
{ typePool: [MonType.Melee, MonType.Heavy], countMin: 10, countMax: 20, weight: 0.4 }, { typePool: [MonType.Melee, MonType.Heavy], countMin: 2, countMax: 4, weight: 0.4 },
{ typePool: [MonType.Long, MonType.Assassin], countMin: 5, countMax: 15, weight: 0.3 }, { typePool: [MonType.Long, MonType.Assassin], countMin: 1, countMax: 3, weight: 0.3 },
{ typePool: [MonType.Bomber, MonType.Support], countMin: 5, countMax: 10, weight: 0.3 }, { typePool: [MonType.Bomber, MonType.Support], countMin: 1, countMax: 2, weight: 0.3 },
] }, ] },
{ id: "B4", type: TemplateType.BOSS, tierMin: 4, allowAffix: true, { id: "B4", type: TemplateType.BOSS, tierMin: 7, allowAffix: true,
slots: [ slots: [
{ typePool: [MonType.MeleeBoss, MonType.LongBoss], countMin: 1, countMax: 1, weight: 1.0 }, { typePool: [MonType.MeleeBoss, MonType.LongBoss], countMin: 1, countMax: 1, weight: 1.0 },
{ typePool: [MonType.Heavy], countMin: 10, countMax: 15, weight: 0.3 }, { typePool: [MonType.Heavy], countMin: 2, countMax: 3, weight: 0.3 },
{ typePool: [MonType.Assassin, MonType.Splitter], countMin: 5, countMax: 10, weight: 0.3 }, { typePool: [MonType.Assassin, MonType.Splitter], countMin: 1, countMax: 2, weight: 0.3 },
{ typePool: [MonType.Summoner, MonType.Support], countMin: 5, countMax: 10, weight: 0.2 }, { typePool: [MonType.Summoner, MonType.Support], countMin: 1, countMax: 2, weight: 0.2 },
{ typePool: [MonType.Bomber], countMin: 5, countMax: 10, weight: 0.2 }, { typePool: [MonType.Bomber], countMin: 1, countMax: 2, weight: 0.2 },
] }, ] },
// ---- 教程专用 ---- // ---- 教程专用 ----
{ id: "TUTORIAL", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false, { id: "TUTORIAL", type: TemplateType.NORMAL, tierMin: 1, allowAffix: false,
slots: [{ typePool: [MonType.Melee], countMin: 2, countMax: 2, weight: 1.0 }] }, slots: [{ typePool: [MonType.Melee], countMin: 1, countMax: 1, weight: 1.0 }] },
] ]
// ======================== 自适应难度配置 ======================== // ======================== 自适应难度配置 ========================
@@ -588,17 +598,17 @@ export class RogueSpawningEngine {
private consecutiveTemplateCount = 0 private consecutiveTemplateCount = 0
/** /**
* 生成指定波次的怪物列表(主线 1-15 波) * 生成指定波次的怪物列表(主线 1-30 波)
* *
* 内部流程: * 内部流程:
* 1. 根据 waveNumber 计算 tier 和 waveInTier * 1. 根据 waveNumber 计算 tier 和 waveInTier
* 2. W1 特殊处理(固定教程模板:5 个 Melee * 2. W1 特殊处理(固定教程模板:1 个 Melee
* 3. 根据 waveInTier 和 isBossTier 选择蓝图模板 * 3. 根据 waveInTier 和 isBossTier 选择蓝图模板
* 4. 计算难度预算 = base_budget × template_modifier × adaptive_factor * 4. 计算难度预算 = base_budget × template_modifier × adaptive_factor
* 5. 按模板槽位填充怪物、应用词缀、计算最终属性 * 5. 按模板槽位填充怪物、应用词缀、计算最终属性
* *
* @param waveNumber - 波次编号1-15 * @param waveNumber - 波次编号1-30
* 1-3 → Tier 1, 4-6 → Tier 2, ..., 13-15 → Tier 5 * 1-3 → Tier 1, 4-6 → Tier 2, ..., 28-30 → Tier 10
* < 1 返回空数组 * < 1 返回空数组
* *
* @returns GeneratedMonster[] 该波次的怪物实例数组,按 spawnIndex 排列。 * @returns GeneratedMonster[] 该波次的怪物实例数组,按 spawnIndex 排列。
@@ -606,9 +616,9 @@ export class RogueSpawningEngine {
* 每次调用结果不同(随机生成),如需固定结果请在同一次调用中缓存。 * 每次调用结果不同(随机生成),如需固定结果请在同一次调用中缓存。
* *
* @example * @example
* // 生成第 1 波(教程:5 个 Melee * // 生成第 1 波(教程:1 个 Melee
* const w1 = engine.generateWave(1) * const w1 = engine.generateWave(1)
* // w1 = [{ uuid: 6001, type: 0, hp: 120, ap: 12, affixes: [], isBoss: false, spawnIndex: 0 }, ...] * // w1 = [{ uuid: 6001, type: 0, hp: 120, ap: 12, affixes: [], isBoss: false, spawnIndex: 0 }]
* *
* // 生成第 6 波Tier 2 Boss 波) * // 生成第 6 波Tier 2 Boss 波)
* const w6 = engine.generateWave(6) * const w6 = engine.generateWave(6)
@@ -624,7 +634,7 @@ export class RogueSpawningEngine {
if (waveNumber === 1) { if (waveNumber === 1) {
return this.spawnFromTemplate( return this.spawnFromTemplate(
BlueprintTemplates.find(t => t.id === "TUTORIAL")!, BlueprintTemplates.find(t => t.id === "TUTORIAL")!,
1, getTierConfig(1).budget * this.adaptiveFactor 1, 1
) )
} }
@@ -641,11 +651,11 @@ export class RogueSpawningEngine {
/** /**
* 生成无限模式指定层的指定波 * 生成无限模式指定层的指定波
* *
* 无限模式结构:每层 4 波REST → NORMAL → MIXED → BOSSTier 从 6 开始递增。 * 无限模式结构:每层 4 波REST → NORMAL → MIXED → BOSSTier 从 11 开始递增。
* 属性倍率按 T(n) = T(n-1) × 1.2 递推,无上限。 * 属性倍率按 T(n) = T(n-1) × 1.2 递推,无上限。
* *
* @param layer - 层编号(从 1 开始) * @param layer - 层编号(从 1 开始)
* Tier = 5 + layerlayer=1 → Tier 6, multiplier=6.6 * Tier = 10 + layerlayer=1 → Tier 11, multiplier=6.6
* @param waveInLayer - 层内波次编号1-4 * @param waveInLayer - 层内波次编号1-4
* 1: REST恢复波 * 1: REST恢复波
* 2: NORMAL标准波 * 2: NORMAL标准波
@@ -655,12 +665,12 @@ export class RogueSpawningEngine {
* @returns GeneratedMonster[] 该波的怪物实例数组 * @returns GeneratedMonster[] 该波的怪物实例数组
* *
* @example * @example
* // 无限模式第 1 层第 4 波Boss 波Tier 6 * // 无限模式第 1 层第 4 波Boss 波Tier 11
* const boss = engine.generateInfiniteWave(1, 4) * const boss = engine.generateInfiniteWave(1, 4)
* // boss 包含 1 个 Boss + 若干小怪,属性倍率 = 6.6x * // boss 包含 1 个 Boss + 若干小怪,属性倍率 = 6.6x
*/ */
generateInfiniteWave(layer: number, waveInLayer: number): GeneratedMonster[] { generateInfiniteWave(layer: number, waveInLayer: number): GeneratedMonster[] {
const tier = 5 + layer const tier = 10 + layer
const tierConfig = getTierConfig(tier) const tierConfig = getTierConfig(tier)
let templateType: TemplateType let templateType: TemplateType
@@ -674,7 +684,7 @@ export class RogueSpawningEngine {
} }
const templates = BlueprintTemplates.filter(t => const templates = BlueprintTemplates.filter(t =>
t.type === templateType && t.tierMin <= 5 t.type === templateType && t.tierMin <= 10
) )
const template = this.pickRandomTemplate(templates) const template = this.pickRandomTemplate(templates)
const budget = Math.round( const budget = Math.round(
@@ -878,7 +888,7 @@ export class RogueSpawningEngine {
// 预算利用率检查 (目标 >= 70%) // 预算利用率检查 (目标 >= 70%)
let totalCost = budget - remainingBudget let totalCost = budget - remainingBudget
if (budget > 0 && totalCost / budget < 0.7 && template.id !== "TUTORIAL") { if (budget > 0 && totalCost / budget < 0.7) {
const tierConfig = getTierConfig(tier) const tierConfig = getTierConfig(tier)
const type = MonType.Melee const type = MonType.Melee
const stats = MonsterStats[type] const stats = MonsterStats[type]
@@ -972,7 +982,7 @@ export class RogueSpawningEngine {
* 将 generateWave() 的结果按怪物类型合并,返回简化的 [{type, count}] 格式。 * 将 generateWave() 的结果按怪物类型合并,返回简化的 [{type, count}] 格式。
* 注意:此方法不返回 HP/AP 值,调用方需自行处理属性计算或改用 generateWave()。 * 注意:此方法不返回 HP/AP 值,调用方需自行处理属性计算或改用 generateWave()。
* *
* @param waveNumber - 波次编号1-15 * @param waveNumber - 波次编号1-30
* @returns IWaveSlot[] 按怪物类型合并后的数组 * @returns IWaveSlot[] 按怪物类型合并后的数组
* - type: MonType 枚举值 * - type: MonType 枚举值
* - count: 该类型的怪物总数 * - count: 该类型的怪物总数
@@ -980,8 +990,8 @@ export class RogueSpawningEngine {
* *
* @example * @example
* engine.getWaveSlotConfig(6) * engine.getWaveSlotConfig(6)
* // [{ type: 8, count: 1 }, { type: 0, count: 10 }, { type: 2, count: 5 }] * // [{ type: 8, count: 1 }, { type: 0, count: 2 }, { type: 2, count: 1 }]
* // = 1 个 MeleeBoss + 10 个 Melee + 5 个 Long * // = 1 个 MeleeBoss + 2 个 Melee + 1 个 Long
*/ */
getWaveSlotConfig(waveNumber: number): IWaveSlot[] { getWaveSlotConfig(waveNumber: number): IWaveSlot[] {
const generated = this.generateWave(waveNumber) const generated = this.generateWave(waveNumber)
@@ -1025,14 +1035,14 @@ export function getWaveSlotConfig(waveNumber: number): IWaveSlot[] {
} }
/** /**
* 向后兼容:默认占位配置(波次 > 15 或异常时的兜底配置) * 向后兼容:默认占位配置(波次 > 30 或异常时的兜底配置)
* 20 个 Melee + 15 个 Long + 5 个 Support + 5 个 Bomber * 4 个 Melee + 3 个 Long + 1 个 Support + 1 个 Bomber
*/ */
export const DefaultWaveSlot: IWaveSlot[] = [ export const DefaultWaveSlot: IWaveSlot[] = [
{ type: MonType.Melee, count: 20 }, { type: MonType.Melee, count: 4 },
{ type: MonType.Long, count: 15 }, { type: MonType.Long, count: 3 },
{ type: MonType.Support, count: 5 }, { type: MonType.Support, count: 1 },
{ type: MonType.Bomber, count: 5 }, { type: MonType.Bomber, count: 1 },
] ]
/** /**
@@ -1043,14 +1053,14 @@ export const DefaultWaveSlot: IWaveSlot[] = [
* *
* @example * @example
* WaveSlotConfig[5] // 动态生成第 5 波的 IWaveSlot[] * WaveSlotConfig[5] // 动态生成第 5 波的 IWaveSlot[]
* WaveSlotConfig[15] // 动态生成第 15 波的 IWaveSlot[] * WaveSlotConfig[30] // 动态生成第 30 波的 IWaveSlot[]
*/ */
export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = new Proxy( export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = new Proxy(
{} as { [wave: number]: IWaveSlot[] }, {} as { [wave: number]: IWaveSlot[] },
{ {
get(_target, prop: string) { get(_target, prop: string) {
const wave = parseInt(prop, 10) const wave = parseInt(prop, 10)
if (!isNaN(wave) && wave >= 1 && wave <= 15) { if (!isNaN(wave) && wave >= 1 && wave <= 30) {
return spawningEngine.getWaveSlotConfig(wave) return spawningEngine.getWaveSlotConfig(wave)
} }
if (prop === "toJSON") return () => ({}) if (prop === "toJSON") return () => ({})
@@ -1058,7 +1068,7 @@ export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = new Proxy(
}, },
has(_target, prop: string) { has(_target, prop: string) {
const wave = parseInt(prop, 10) const wave = parseInt(prop, 10)
return !isNaN(wave) && wave >= 1 && wave <= 15 return !isNaN(wave) && wave >= 1 && wave <= 30
}, },
} }
) )

View File

@@ -7,7 +7,7 @@
## Overview ## Overview
肉鸽刷怪系统是控制每局游戏怪物生成的核心引擎。它从固定 15 波硬编码配置演进为**蓝图模板 + 权重填充 + 自适应微调**的三层程序化生成架构。主线 15 波分为 5 个难度阶梯(每档 3 波),每档内遵循"恢复→攀升→高潮"的心流节奏。10 种怪物类型配合 8 种词缀修饰,使同一基础怪物产生多种变体。通关后玩家可选进入无限模式,以分层推进方式无限挑战。自适应难度引擎追踪玩家表现,在 ±15% 范围内微调强度,确保始终处于心流通道内。 肉鸽刷怪系统是控制每局游戏怪物生成的核心引擎。它从固定 30 波硬编码配置演进为**蓝图模板 + 权重填充 + 自适应微调**的三层程序化生成架构。主线 30 波分为 10 个难度阶梯(每档 3 波),每档内遵循"恢复→攀升→高潮"的心流节奏。10 种怪物类型配合 8 种词缀修饰,使同一基础怪物产生多种变体。通关后玩家可选进入无限模式,以分层推进方式无限挑战。自适应难度引擎追踪玩家表现,在 ±15% 范围内微调强度,确保始终处于心流通道内。
## Player Fantasy ## Player Fantasy
@@ -34,17 +34,22 @@
2. **权重填充层**:根据蓝图中的槽位定义,从怪物池中按权重随机抽取具体怪物,填充数量 2. **权重填充层**:根据蓝图中的槽位定义,从怪物池中按权重随机抽取具体怪物,填充数量
3. **自适应微调层**:根据玩家历史表现,对难度预算进行 ±15% 浮动调整 3. **自适应微调层**:根据玩家历史表现,对难度预算进行 ±15% 浮动调整
**规则 25 阶梯 × 3 波结构** **规则 210 阶梯 × 3 波结构**
主线 15 波分为 5 个阶梯Tier每个 Tier 3 波: 主线 30 波分为 10 个阶梯Tier每个 Tier 3 波:
| Tier | 波次 | 主题 | 新引入元素 | Boss | | Tier | 波次 | 主题 | 新引入元素 | Boss |
|------|------|------|-----------|------| |------|------|------|-----------|------|
| 1 | W1-3 | 入门教程 | Melee, Long, Heavy | - | | 1 | W1-3 | 入门教程 | Melee | - |
| 2 | W4-6 | 辅助登场 | Support | W6 MiniBoss | | 2 | W4-6 | 远程登场 | Long, Heavy | W6 MiniBoss |
| 3 | W7-9 | 刺客与自爆 | Bomber, Assassin + 词缀系统首次出现 | **W9 MajorBoss** | | 3 | W7-9 | 刺客登场 | Assassin突后排威胁 | - |
| 4 | W10-12 | 召唤与分裂 | Summoner, Splitter + 词缀大量出现 | W12 MiniBoss | | 4 | W10-12 | 辅助登场 | Support + 近远程刺协同 | W12 MiniBoss |
| 5 | W13-15 | 最终 Boss | 全兵种混合,全词缀组合 | **W15 FinalBoss** | | 5 | W13-15 | 中期 Boss | Bomber, Summoner + 词缀系统首次出现 | **W15 MajorBoss** |
| 6 | W16-18 | 分裂时代 | Splitter死亡触发型 | - |
| 7 | W19-21 | 精英时代 | 词缀大量出现(全种类) | W21 MiniBoss |
| 8 | W22-24 | 全兵种混合 | 所有类型协同 | - |
| 9 | W25-27 | 终极压力 | 极限数量 | W27 MiniBoss |
| 10 | W28-30 | 最终 Boss | 全词缀组合 | **W30 FinalBoss** |
**规则 3档内三拍节奏** **规则 3档内三拍节奏**
@@ -151,37 +156,37 @@ function fill_wave(template, budget, tier, adaptive_factor):
**REST 类型tier_min=1** **REST 类型tier_min=1**
| ID | 槽位配置 | 说明 | | ID | 槽位配置 | 说明 |
|----|---------|------| |----|---------|------|
| REST_01 | 5-10 × Melee(权重10) | 纯近战休息波 | | REST_01 | 1-2 × Melee(权重10) | 纯近战休息波 |
| REST_02 | 5 × Melee(8) + 5 × Long(4) | 轻混合休息波 | | REST_02 | 1 × Melee(8) + 1 × Long(4) | 轻混合休息波 |
| REST_03 | 10 × 随机(权重均分) | 随机轻波 | | REST_03 | 2 × 随机(权重均分) | 随机轻波 |
**NORMAL 类型tier_min=1** **NORMAL 类型tier_min=1**
| ID | 槽位配置 | 说明 | | ID | 槽位配置 | 说明 |
|----|---------|------| |----|---------|------|
| NORM_01 | 15-20 × Melee(10) | 近战群 | | NORM_01 | 3-4 × Melee(10) | 近战群 |
| NORM_02 | 10 × Melee(6) + 10 × Long(6) | 标准混合 | | NORM_02 | 2 × Melee(6) + 2 × Long(6) | 标准混合 |
| NORM_03 | 10 × Melee(4) + 5 × Long(6) + 5 × Heavy(4) | 重型混合 | | NORM_03 | 2 × Melee(4) + 1 × Long(6) + 1 × Heavy(4) | 重型混合 |
**MIXED 类型tier_min=2** **MIXED 类型tier_min=3**
| ID | 槽位配置 | 说明 | | ID | 槽位配置 | 说明 |
|----|---------|------| |----|---------|------|
| MIX_01 | 10 × Melee(4) + 10 × Long(4) + 5 × Assassin(4) | 刺客突袭 | | MIX_01 | 2 × Melee(4) + 2 × Long(4) + 1 × Assassin(4) | 刺客突袭 |
| MIX_02 | 5 × Heavy(6) + 10 × Long(6) + 5 × Support(3) | 远程压制 | | MIX_02 | 1 × Heavy(6) + 2 × Long(6) + 1 × Support(3) | 远程压制 |
| MIX_03 | 10 × Melee(4) + 5 × Long(4) + 5 × Assassin(4) + 5 × Bomber(3) | 多线威胁 | | MIX_03 | 2 × Melee(4) + 1 × Long(4) + 1 × Assassin(4) + 1 × Bomber(3) | 多线威胁 |
**ELITE 类型tier_min=3, allow_affix=true** **ELITE 类型tier_min=5, allow_affix=true**
| ID | 槽位配置 | 说明 | | ID | 槽位配置 | 说明 |
|----|---------|------| |----|---------|------|
| ELIT_01 | 10-15 × 随机(权重均分, 必带词缀) | 精英小队 | | ELIT_01 | 2-3 × 随机(权重均分, 必带词缀) | 精英小队 |
| ELIT_02 | 5 × Heavy(必带词缀) + 5 × Support(必带词缀) | 重装精英 | | ELIT_02 | 1 × Heavy(必带词缀) + 1 × Support(必带词缀) | 重装精英 |
| ELIT_03 | 10 × Assassin(必带词缀) + 5 × Long(必带词缀) | 精锐突袭 | | ELIT_03 | 2 × Assassin(必带词缀) + 1 × Long(必带词缀) | 精锐突袭 |
**BOSS 类型tier_min=1, mandatory_slots 包含 Boss** **BOSS 类型tier_min=2, mandatory_slots 包含 Boss**
| ID | 槽位配置 | 说明 | | ID | 槽位配置 | 说明 |
|----|---------|------| |----|---------|------|
| BOSS_01 | 1 × MeleeBoss(强制) + 10-15 × Melee | 近战 Boss + 小怪 | | BOSS_01 | 1 × MeleeBoss(强制) + 2-3 × Melee | 近战 Boss + 小怪 |
| BOSS_02 | 1 × LongBoss(强制) + 5-10 × Support | 远程 Boss + 治疗 | | BOSS_02 | 1 × LongBoss(强制) + 1-2 × Support | 远程 Boss + 治疗 |
| BOSS_03 | 1 × MeleeBoss 或 LongBoss(强制) + 5 × Assassin + 5 × Bomber | Boss + 精锐护卫 | | BOSS_03 | 1 × MeleeBoss 或 LongBoss(强制) + 1 × Assassin + 1 × Bomber | Boss + 精锐护卫 |
**规则 6词缀系统** **规则 6词缀系统**
@@ -189,14 +194,14 @@ function fill_wave(template, budget, tier, adaptive_factor):
| 词缀 | 效果 | 首次出现 Tier | | 词缀 | 效果 | 首次出现 Tier |
|------|------|-------------| |------|------|-------------|
| Elite | +50% HP, +30% AP | T3 | | Elite | +50% HP, +30% AP | T5 |
| Berserk | 攻速 ×1.5 | T3 | | Berserk | 攻速 ×1.5 | T5 |
| Shield | 开局带伤害吸收盾20% HP | T3 | | Shield | 开局带伤害吸收盾20% HP | T6 |
| Regen | 每秒回复 2% HP | T4 | | Regen | 每秒回复 2% HP | T7 |
| Swift | 移速 ×2 | T4 | | Swift | 移速 ×2 | T7 |
| Giant | ×2 体型, +100% HP, +50% AP | T4 | | Giant | ×2 体型, +100% HP, +50% AP | T8 |
| Chain | 攻击附带 50% 溅射伤害 | T5 | | Chain | 攻击附带 50% 溅射伤害 | T9 |
| Summoner | 每 8 秒召唤 1 个小怪Melee1 级属性) | T5 | | Summoner | 每 8 秒召唤 1 个小怪Melee1 级属性) | T10 |
词缀触发概率: 词缀触发概率:
@@ -204,8 +209,8 @@ function fill_wave(template, budget, tier, adaptive_factor):
affix_chance = min(base_affix_chance[tier] × role_multiplier, 1.0) affix_chance = min(base_affix_chance[tier] × role_multiplier, 1.0)
base_affix_chance: base_affix_chance:
T1-T2: 0% T1-T4: 0%
T3: 15%, T4: 30%, T5: 50% T5: 10%, T6: 15%, T7: 25%, T8: 30%, T9: 40%, T10: 50%
role_multiplier: role_multiplier:
普通怪: 1.0x 普通怪: 1.0x
@@ -292,23 +297,23 @@ tier_multiplier = tier_multiplier_table[tier]
| Variable | Symbol | Type | Range | Description | | Variable | Symbol | Type | Range | Description |
|----------|--------|------|-------|-------------| |----------|--------|------|-------|-------------|
| tier | T | int | 1-5 (主线), 6+ (无限) | 当前难度阶梯 | | tier | T | int | 1-10 (主线), 11+ (无限) | 当前难度阶梯 |
| tier_multiplier | TM | float | 1.0-5.5 | 属性倍率 | | tier_multiplier | TM | float | 1.0-5.5 | 属性倍率 |
**Tier Multiplier 表:** **Tier Multiplier 表:**
| Tier | Multiplier | | Tier | Multiplier | Tier | Multiplier |
|------|-----------| |------|-----------|------|-----------|
| T1 | 1.0x | | T1 | 1.0x | T6 | 2.8x |
| T2 | 1.6x | | T2 | 1.3x | T7 | 3.3x |
| T3 | 2.5x | | T3 | 1.6x | T8 | 3.9x |
| T4 | 3.8x | | T4 | 1.9x | T9 | 4.6x |
| T5 | 5.5x | | T5 | 2.3x | T10 | 5.5x |
**无限模式扩展:** T(n) = T(n-1) × 1.2T6=6.6x, T7=7.9x, ... **无限模式扩展:** T(n) = T(n-1) × 1.2T11=6.6x, T12=7.9x, ...
**Output Range:** 1.0x (T1) to 5.5x (T5 主线), 无限模式无限递增 **Output Range:** 1.0x (T1) to 5.5x (T10 主线), 无限模式无限递增
**Example:** 基础 Melee 怪在 T3 的属性 = 120×2.5=300 HP, 12×2.5=30 AP **Example:** 基础 Melee 怪在 T5 的属性 = 120×2.3=276 HP, 12×2.3=27.6 AP
### F2: 难度预算 ### F2: 难度预算
@@ -323,13 +328,13 @@ wave_budget = base_budget[tier] × template_modifier[type]
**Base Budget 表:** **Base Budget 表:**
| Tier | Budget | | Tier | Budget | Tier | Budget |
|------|--------| |------|--------|------|--------|
| T1 | 500 | | T1 | 100 | T6 | 440 |
| T2 | 1000 | | T2 | 150 | T7 | 560 |
| T3 | 1800 | | T3 | 200 | T8 | 700 |
| T4 | 3000 | | T4 | 260 | T9 | 860 |
| T5 | 5000 | | T5 | 340 | T10 | 1050 |
**Template Modifier** **Template Modifier**
- REST: 0.5x - REST: 0.5x
@@ -338,8 +343,8 @@ wave_budget = base_budget[tier] × template_modifier[type]
- ELITE: 0.8x - ELITE: 0.8x
- BOSS: 1.5x - BOSS: 1.5x
**Output Range:** 250 (T1 REST) to 7500 (T5 BOSS) **Output Range:** 50 (T1 REST) to 1575 (T10 BOSS)
**Example:** T3 NORMAL 波 → budget = 1800 × 1.0 = 1800 → 可生成约 40 个 Melee 或 20 个 Melee + 10 个 Long + 5 个 Support **Example:** T5 NORMAL 波 → budget = 340 × 1.0 = 340 → 可生成约 8 个 Melee 或 4 个 Melee + 2 个 Long + 1 个 Support
### F3: 怪物最终属性 ### F3: 怪物最终属性
@@ -354,8 +359,8 @@ final_ap = base_ap × tier_multiplier × affix_ap_multiplier × adaptive_factor
| affix_hp/ap_multiplier | AHM/AAM | float | 1.0-2.5 | 词缀属性倍率加法叠加Elite+Giant 最大 2.5 | | affix_hp/ap_multiplier | AHM/AAM | float | 1.0-2.5 | 词缀属性倍率加法叠加Elite+Giant 最大 2.5 |
| adaptive_factor | AF | float | 0.85-1.15 | 自适应系数 | | adaptive_factor | AF | float | 0.85-1.15 | 自适应系数 |
**Output Range:** HP 51 (Bomber, T1, no affix, AF=0.85) to 19,031+ (MeleeBoss, T6 无限, Elite+Giant, AF=1.15) **Output Range:** HP 51 (Bomber, T1, no affix, AF=0.85) to 19,031+ (MeleeBoss, T15 无限, Elite+Giant, AF=1.15)
**Example:** Melee 怪在 T4, 带 Elite 词缀, AF=1.0 → HP=120×3.8×1.5×1.0=684, AP=12×3.8×1.3×1.0=59.28 **Example:** Melee 怪在 T7, 带 Elite 词缀, AF=1.0 → HP=120×3.3×1.5×1.0=594, AP=12×3.3×1.3×1.0=51.5
### F4: 自适应难度系数 ### F4: 自适应难度系数
@@ -419,15 +424,15 @@ delta calculation per wave:
| 旋钮 | 当前值 | 安全范围 | 超出后果 | | 旋钮 | 当前值 | 安全范围 | 超出后果 |
|------|--------|---------|---------| |------|--------|---------|---------|
| `tier_multiplier_table[T1-T5]` | 1.0-5.5 | 0.5-10.0 | 过低→后期无挑战;过高→后期不可打 | | `tier_multiplier_table[T1-T10]` | 1.0-5.5 | 0.5-10.0 | 过低→后期无挑战;过高→后期不可打 |
| `base_budget[T1-T5]` | 500-5000 | 250-10000 | 过低→怪物太少无聊;过高→怪物太多卡顿 | | `base_budget[T1-T10]` | 100-1050 | 50-2000 | 过低→怪物太少无聊;过高→怪物太多卡顿 |
| `template_modifier[REST]` | 0.5x | 0.3-0.8 | 过低→恢复波太弱无意义;过高→节奏混乱 | | `template_modifier[REST]` | 0.5x | 0.3-0.8 | 过低→恢复波太弱无意义;过高→节奏混乱 |
| `template_modifier[BOSS]` | 1.5x | 1.0-2.5 | 过低→Boss 无感过高→Boss 波必团灭 | | `template_modifier[BOSS]` | 1.5x | 1.0-2.5 | 过低→Boss 无感过高→Boss 波必团灭 |
| `adaptive_factor_min` | 0.85 | 0.7-0.95 | 过低→自适应降难太明显 | | `adaptive_factor_min` | 0.85 | 0.7-0.95 | 过低→自适应降难太明显 |
| `adaptive_factor_max` | 1.15 | 1.05-1.3 | 过高→自适应加难太明显 | | `adaptive_factor_max` | 1.15 | 1.05-1.3 | 过高→自适应加难太明显 |
| `adaptive_delta_per_wave` | 0.03 | 0.01-0.08 | 过低→反应迟钝;过高→体验不稳定 | | `adaptive_delta_per_wave` | 0.03 | 0.01-0.08 | 过低→反应迟钝;过高→体验不稳定 |
| `target_clear_time` | 15.0s | 5.0-30.0 | 影响自适应判断阈值 | | `target_clear_time` | 15.0s | 5.0-30.0 | 影响自适应判断阈值 |
| `base_affix_chance[T3-T5]` | 15%-50% | 0%-80% | 过低→词缀系统无意义;过高→全是精英怪 | | `base_affix_chance[T5-T10]` | 10%-50% | 0%-80% | 过低→词缀系统无意义;过高→全是精英怪 |
| `bomber_explosion_multiplier` | 200% | 100%-400% | 爆炸伤害占 AP 的比例 | | `bomber_explosion_multiplier` | 200% | 100%-400% | 爆炸伤害占 AP 的比例 |
| `splitter_child_hp_ratio` | 50% | 25%-75% | 分裂子体 HP 占母体比例 | | `splitter_child_hp_ratio` | 50% | 25%-75% | 分裂子体 HP 占母体比例 |
| `splitter_child_ap_ratio` | 50% | 25%-75% | 分裂子体 AP 占母体比例 | | `splitter_child_ap_ratio` | 50% | 25%-75% | 分裂子体 AP 占母体比例 |
@@ -435,30 +440,30 @@ delta calculation per wave:
| `summoner_max_minions` | 3 | 1-5 | 场上最多同时存在的小怪数 | | `summoner_max_minions` | 3 | 1-5 | 场上最多同时存在的小怪数 |
| `minion_lifetime` | 30s | 10-60s | 小怪存活上限 | | `minion_lifetime` | 30s | 10-60s | 小怪存活上限 |
| `infinite_tier_growth_rate` | 1.2x | 1.1-1.5 | 无限模式每档递增率 | | `infinite_tier_growth_rate` | 1.2x | 1.1-1.5 | 无限模式每档递增率 |
| `monster_types_available[T1-T5]` | 见规则2 | - | 每档可用怪物类型集合 | | `monster_types_available[T1-T10]` | 见规则2 | - | 每档可用怪物类型集合 |
| `affix_mutual_exclusion` | Giant×Swift, Regen×Shield | - | 互斥词缀对 | | `affix_mutual_exclusion` | Giant×Swift, Regen×Shield | - | 互斥词缀对 |
## Acceptance Criteria ## Acceptance Criteria
### 核心波次生成 ### 核心波次生成
- **GIVEN** 游戏开始adaptive_factor=1.0**WHEN** 进入 W1**THEN** 生成 2 个 Melee 怪T1, 无词缀HP=120, AP=12。 - **GIVEN** 游戏开始adaptive_factor=1.0**WHEN** 进入 W1**THEN** 生成 1 个 Melee 怪T1, 无词缀HP=120, AP=12。
- **GIVEN** 当前 Tier 1 W2攀升波**WHEN** 模板选取完成,**THEN** 模板类型为 NORMAL 或 MIXED且模板的怪物槽位池中包含 Long 类型cost=40。运行 100 次抽取Long 类型出现在槽位池中的比例为 100%。 - **GIVEN** 当前 Tier 2 W2攀升波**WHEN** 模板选取完成,**THEN** 模板类型为 NORMAL 或 MIXED且模板的怪物槽位池中包含 Long 类型cost=40。运行 100 次抽取Long 类型出现在槽位池中的比例为 100%。
- **GIVEN** W6Boss 波),**WHEN** 模板选取完成,**THEN** 模板类型为 BOSSmandatory_slots 包含 Boss 类型,至少生成 1 个 MeleeBoss + 10-15 个普通怪。 - **GIVEN** W6Boss 波),**WHEN** 模板选取完成,**THEN** 模板类型为 BOSSmandatory_slots 包含 Boss 类型,至少生成 1 个 MeleeBoss + 0-3 个普通怪。
- **GIVEN** Tier 2 W1REST 波),**WHEN** 模板选取完成,**THEN** 模板类型为 RESTtemplate_modifier=0.5x。 - **GIVEN** Tier 3 W1REST 波),**WHEN** 模板选取完成,**THEN** 模板类型为 RESTtemplate_modifier=0.5x。
- **GIVEN** Tier 3 W3非 Boss 高潮波),**WHEN** 模板选取完成,**THEN** 模板类型为 MIXEDtemplate_modifier=1.2x。 - **GIVEN** Tier 4 W3非 Boss 高潮波),**WHEN** 模板选取完成,**THEN** 模板类型为 MIXEDtemplate_modifier=1.2x。
### 预算系统 ### 预算系统
- **GIVEN** Tier 3 NORMAL 波budget=1800, AF 不影响预算),**WHEN** 填充怪物完成,**THEN** 所有怪物成本总和在 1800 × 0.7 到 1800 × 1.0 之间(即 1260-1800且至少生成 15 只怪物。 - **GIVEN** Tier 5 NORMAL 波budget=340, AF 不影响预算),**WHEN** 填充怪物完成,**THEN** 所有怪物成本总和在 340 × 0.7 到 340 × 1.0 之间(即 238-340且至少生成 3 只怪物。
- **GIVEN** 预算不足以生成任何怪物,**WHEN** 填充阶段,**THEN** 保底生成 1 个 Melee 怪(忽略预算限制)。 - **GIVEN** 预算不足以生成任何怪物,**WHEN** 填充阶段,**THEN** 保底生成 1 个 Melee 怪(忽略预算限制)。
### 词缀系统 ### 词缀系统
- **GIVEN** Tier 3 正常波(30+ 只怪),**WHEN** 运行 1000 次统计测试,**THEN** 词缀触发率落在 13%-17% 区间90% 置信度),且所有触发的词缀类型均为 Elite, Berserk 或 Shield 等 T3 可用词缀。单次运行中无词缀怪物比例在 20%-50% 区间内。 - **GIVEN** Tier 5 正常波(10+ 只怪),**WHEN** 运行 1000 次统计测试,**THEN** 词缀触发率落在 8%-12% 区间90% 置信度),且所有触发的词缀类型均为 Elite Berserk。单次运行中无词缀怪物比例在 20%-50% 区间内。
- **GIVEN** Tier 5 FinalBossW15**WHEN** 词缀应用完成,**THEN** FinalBoss 拥有 2-3 个词缀且任意两个词缀的组合不属于互斥组Giant+Swift, Regen+Shield, Summoner类型+Summoner词缀。运行 100 次模拟0 次出现互斥词缀同时生效。 - **GIVEN** Tier 10 FinalBossW30**WHEN** 词缀应用完成,**THEN** FinalBoss 拥有 2-3 个词缀且任意两个词缀的组合不属于互斥组Giant+Swift, Regen+Shield, Summoner类型+Summoner词缀。运行 100 次模拟0 次出现互斥词缀同时生效。
- **GIVEN** Tier 4 正常波,**WHEN** 词缀应用完成,**THEN** 每只普通怪最多拥有 1 个词缀。运行 1000 次模拟0 次出现普通怪拥有 >1 个词缀的情况。 - **GIVEN** Tier 7 正常波,**WHEN** 词缀应用完成,**THEN** 每只普通怪最多拥有 1 个词缀。运行 1000 次模拟0 次出现普通怪拥有 >1 个词缀的情况。
- **GIVEN** Tier 4 W12 MiniBoss**WHEN** 词缀应用完成,**THEN** MiniBoss 拥有 0-2 个词缀0 次超过 2 个词缀。 - **GIVEN** Tier 7 W21 MiniBoss**WHEN** 词缀应用完成,**THEN** MiniBoss 拥有 0-2 个词缀0 次超过 2 个词缀。
- **GIVEN** 两个互斥词缀Giant + Swift同时被选中**WHEN** 词缀应用阶段,**THEN** 保留优先级更高的词缀Giant丢弃 Swift。 - **GIVEN** 两个互斥词缀Giant + Swift同时被选中**WHEN** 词缀应用阶段,**THEN** 保留优先级更高的词缀Giant丢弃 Swift。
- **GIVEN** 两个互斥词缀Regen + Shield同时被选中**WHEN** 词缀应用阶段,**THEN** 保留优先级更高的词缀Shield丢弃 Regen。 - **GIVEN** 两个互斥词缀Regen + Shield同时被选中**WHEN** 词缀应用阶段,**THEN** 保留优先级更高的词缀Shield丢弃 Regen。
@@ -480,13 +485,13 @@ delta calculation per wave:
### 属性计算 ### 属性计算
- **GIVEN** Tier 4 Melee 怪,带 Elite 词缀(+50% HP, +30% APAF=1.0**WHEN** 属性计算完成,**THEN** HP = 120 × 3.8 × 1.5 × 1.0 = 684AP = 12 × 3.8 × 1.3 × 1.0 = 59.28。 - **GIVEN** Tier 7 Melee 怪,带 Elite 词缀(+50% HP, +30% APAF=1.0**WHEN** 属性计算完成,**THEN** HP = 120 × 3.3 × 1.5 × 1.0 = 594AP = 12 × 3.3 × 1.3 × 1.0 = 51.48。
- **GIVEN** 任何怪物,**WHEN** 最终属性计算完成base × tier_multiplier × affix_multiplier × adaptive_factor**THEN** final_hp ≥ 1 且 final_ap ≥ 1。 - **GIVEN** 任何怪物,**WHEN** 最终属性计算完成base × tier_multiplier × affix_multiplier × adaptive_factor**THEN** final_hp ≥ 1 且 final_ap ≥ 1。
### 无限模式与模板多样性 ### 无限模式与模板多样性
- **GIVEN** 主线 W15 通关,**WHEN** 玩家选择进入无限模式,**THEN** 从 T6 开始,每层 4 波REST→NORMAL→MIXED→BOSS属性倍率 = tier_multiplier_table[T5] × infinite_tier_growth_rate = 5.5 × 1.2 = 6.6x。 - **GIVEN** 主线 W30 通关,**WHEN** 玩家选择进入无限模式,**THEN** 从 T11 开始,每层 4 波REST→NORMAL→MIXED→BOSS属性倍率 = tier_multiplier_table[T10] × infinite_tier_growth_rate = 5.5 × 1.2 = 6.6x。
- **GIVEN** 无限模式 T7**WHEN** 属性倍率计算,**THEN** tier_multiplier = 6.6 × 1.2 = 7.92x。 - **GIVEN** 无限模式 T12**WHEN** 属性倍率计算,**THEN** tier_multiplier = 6.6 × 1.2 = 7.92x。
- **GIVEN** 同一模板id 相同)被连续抽取 3 次,**WHEN** 第 4 波模板选取,**THEN** 选出的模板类型与连续 3 次的类型不同(如连续 3 次 NORMAL 则强制从 REST/MIXED/ELITE/BOSS 池中抽取)。 - **GIVEN** 同一模板id 相同)被连续抽取 3 次,**WHEN** 第 4 波模板选取,**THEN** 选出的模板类型与连续 3 次的类型不同(如连续 3 次 NORMAL 则强制从 REST/MIXED/ELITE/BOSS 池中抽取)。
### 游戏结束条件 ### 游戏结束条件
@@ -541,6 +546,6 @@ delta calculation per wave:
| 4 | 蓝图模板池具体需要多少个模板? | 策划 | 待定 | 目前每类定义了 3 个示例模板,正式版每类需要 5-8 个 | | 4 | 蓝图模板池具体需要多少个模板? | 策划 | 待定 | 目前每类定义了 3 个示例模板,正式版每类需要 5-8 个 |
| 5 | 新怪物类型Bomber/Summoner/Assassin/Splitter/Heavy的 UUID 编号段? | 开发 | 待定 | 需与 HeroAttrs.ts 中的编号体系对齐需独立怪物定义文档UUID、AI 行为树、资源规范) | | 5 | 新怪物类型Bomber/Summoner/Assassin/Splitter/Heavy的 UUID 编号段? | 开发 | 待定 | 需与 HeroAttrs.ts 中的编号体系对齐需独立怪物定义文档UUID、AI 行为树、资源规范) |
| 6 | 飞行怪移除后,现有 Fly/FlyBoss 的 UUID 是否回收? | 开发 | 待定 | 影响资源管理 | | 6 | 飞行怪移除后,现有 Fly/FlyBoss 的 UUID 是否回收? | 开发 | 待定 | 影响资源管理 |
| 7 | **英雄战力曲线验证** — 怪物缩放5.5×)需对照玩家 DPS 曲线校准 | 策划 | 待定 | 缺少英雄成长 GDD所有平衡数值未验证。需定义T1/T3/T5 预期团队 DPS、每波怪物预期存活时间 | | 7 | **英雄战力曲线验证** — 怪物缩放5.5×)需对照玩家 DPS 曲线校准 | 策划 | 待定 | 缺少英雄成长 GDD所有平衡数值未验证。需定义T1/T5/T10 预期团队 DPS、每波怪物预期存活时间 |
| 8 | **Bomber 爆炸 AOE 半径** — AC 引用了 AOE_RADIUS 但未在调参旋钮中定义 | 策划 | 待定 | 需添加到 Tuning Knobs 表 | | 8 | **Bomber 爆炸 AOE 半径** — AC 引用了 AOE_RADIUS 但未在调参旋钮中定义 | 策划 | 待定 | 需添加到 Tuning Knobs 表 |
| 9 | **MajorBoss vs MiniBoss 机械区别** — 当前仅数值差异,是否有特殊技能/阶段转换? | 策划 | 待定 | W9/W15 MajorBoss 需要与非 MajorBoss 的体验差异 | | 9 | **MajorBoss vs MiniBoss 机械区别** — 当前仅数值差异,是否有特殊技能/阶段转换? | 策划 | 待定 | W15 MajorBoss 需要与非 MajorBoss 的体验差异 |

View File

@@ -20,6 +20,7 @@ export class SkillView extends CCComp {
@property({ type: CCInteger }) @property({ type: CCInteger })
atk_y: number = 0 atk_y: number = 0
@property({ tooltip: "是否启用调试日志" })
private debugMode: boolean = false; private debugMode: boolean = false;
anim:Animation=null; anim:Animation=null;