2 Commits

Author SHA1 Message Date
walkpan
e5e379aecc feat: 调整怪物属性与波次生成逻辑,新增测试脚本
1.  全怪物基础属性翻倍调整,同步更新英雄配置表
2.  修改模板M1的最低生效等级为1
3.  调整首波生成预算计算方式,修复低预算利用率检查逻辑
4.  新增spawn测试脚本,调整UI预制体布局参数
2026-05-15 20:42:24 +08:00
walkpan
9687adb559 docs: 更新肉鸽刷怪系统文档与配置至15波5阶梯版本
同步调整了文档中的波次、阶梯、词缀、预算等全部数值配置,同时更新了RogueConfig.ts中的代码配置,将原30波10阶梯架构重构为15波5阶梯版本,包括调整怪物生成模板、属性倍率、词缀解锁等级和无限模式起始阶梯等核心参数。
2026-05-15 20:27:36 +08:00
7 changed files with 303 additions and 480 deletions

View File

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

View File

@@ -22,26 +22,26 @@
"__id__": 2
},
{
"__id__": 286
"__id__": 272
},
{
"__id__": 295
"__id__": 281
}
],
"_active": true,
"_components": [
{
"__id__": 307
"__id__": 293
},
{
"__id__": 309
"__id__": 295
},
{
"__id__": 311
"__id__": 297
}
],
"_prefab": {
"__id__": 313
"__id__": 299
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -94,17 +94,17 @@
"_active": true,
"_components": [
{
"__id__": 279
"__id__": 265
},
{
"__id__": 281
"__id__": 267
},
{
"__id__": 283
"__id__": 269
}
],
"_prefab": {
"__id__": 285
"__id__": 271
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -6090,108 +6090,32 @@
},
{
"__type__": "cc.PrefabInstance",
"fileId": "c8OWze/z9IMpmg5hcYAPwM",
"fileId": "87n/wcZ7tIPbu7m1+Q/KV3",
"prefabRootNode": {
"__id__": 1
},
"mountedChildren": [],
"mountedComponents": [
{
"__id__": 260
}
],
"mountedComponents": [],
"propertyOverrides": [
{
"__id__": 260
},
{
"__id__": 262
},
{
"__id__": 263
},
{
"__id__": 264
},
{
"__id__": 266
},
{
"__id__": 267
},
{
"__id__": 268
},
{
"__id__": 269
},
{
"__id__": 271
},
{
"__id__": 273
},
{
"__id__": 275
},
{
"__id__": 277
}
],
"removedComponents": []
},
{
"__type__": "cc.MountedComponentsInfo",
"targetInfo": {
"__id__": 261
},
"components": [
{
"__id__": 262
}
]
},
{
"__type__": "cc.TargetInfo",
"localID": [
"f1ya2LoYBB547vP13Ydezp"
]
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {
"mountedRoot": {
"__id__": 257
}
},
"node": {
"__id__": 257
},
"_enabled": true,
"__prefab": {
"__id__": 263
},
"_alignFlags": 18,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 0,
"_bottom": 0,
"_horizontalCenter": -76.841,
"_verticalCenter": 200,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 0,
"_originalHeight": 0,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "3eiYz0maFGj5G3KaylC3ro"
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 265
"__id__": 261
},
"propertyPath": [
"_name"
@@ -6207,22 +6131,22 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 265
"__id__": 261
},
"propertyPath": [
"_lpos"
],
"value": {
"__type__": "cc.Vec3",
"x": -76.841,
"y": 840,
"x": -143.895,
"y": 622.066,
"z": 0
}
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 265
"__id__": 261
},
"propertyPath": [
"_lrot"
@@ -6238,7 +6162,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 265
"__id__": 261
},
"propertyPath": [
"_euler"
@@ -6250,111 +6174,6 @@
"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",
"_name": "",
@@ -6365,7 +6184,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 280
"__id__": 266
},
"_contentSize": {
"__type__": "cc.Size",
@@ -6393,7 +6212,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 282
"__id__": 268
},
"_alignFlags": 21,
"_target": null,
@@ -6429,7 +6248,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 284
"__id__": 270
},
"home_btn": {
"__id__": 45
@@ -6466,14 +6285,14 @@
"__id__": 1
},
"_prefab": {
"__id__": 287
"__id__": 273
},
"__editorExtras__": {}
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 286
"__id__": 272
},
"asset": {
"__uuid__": "26bff847-cd29-48a5-bbfa-c3e2dbda688d",
@@ -6481,7 +6300,7 @@
},
"fileId": "5a9CMsVQhKP5Y+UJfTKPbx",
"instance": {
"__id__": 288
"__id__": 274
},
"targetOverrides": null
},
@@ -6495,19 +6314,19 @@
"mountedComponents": [],
"propertyOverrides": [
{
"__id__": 289
"__id__": 275
},
{
"__id__": 291
"__id__": 277
},
{
"__id__": 292
"__id__": 278
},
{
"__id__": 293
"__id__": 279
},
{
"__id__": 294
"__id__": 280
}
],
"removedComponents": []
@@ -6515,7 +6334,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 290
"__id__": 276
},
"propertyPath": [
"_name"
@@ -6531,7 +6350,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 290
"__id__": 276
},
"propertyPath": [
"_lpos"
@@ -6546,7 +6365,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 290
"__id__": 276
},
"propertyPath": [
"_lrot"
@@ -6562,7 +6381,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 290
"__id__": 276
},
"propertyPath": [
"_euler"
@@ -6577,7 +6396,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 290
"__id__": 276
},
"propertyPath": [
"_active"
@@ -6591,14 +6410,14 @@
"__id__": 1
},
"_prefab": {
"__id__": 296
"__id__": 282
},
"__editorExtras__": {}
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 295
"__id__": 281
},
"asset": {
"__uuid__": "56aee962-4a5e-45ae-a779-999444d06d18",
@@ -6606,7 +6425,7 @@
},
"fileId": "cboM54s0hM07XCtrpFp0/b",
"instance": {
"__id__": 297
"__id__": 283
},
"targetOverrides": null
},
@@ -6620,25 +6439,25 @@
"mountedComponents": [],
"propertyOverrides": [
{
"__id__": 298
"__id__": 284
},
{
"__id__": 300
"__id__": 286
},
{
"__id__": 301
"__id__": 287
},
{
"__id__": 302
"__id__": 288
},
{
"__id__": 303
"__id__": 289
},
{
"__id__": 305
"__id__": 291
},
{
"__id__": 306
"__id__": 292
}
],
"removedComponents": []
@@ -6646,7 +6465,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 299
"__id__": 285
},
"propertyPath": [
"_name"
@@ -6662,7 +6481,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 299
"__id__": 285
},
"propertyPath": [
"_lpos"
@@ -6677,7 +6496,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 299
"__id__": 285
},
"propertyPath": [
"_lrot"
@@ -6693,7 +6512,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 299
"__id__": 285
},
"propertyPath": [
"_euler"
@@ -6708,7 +6527,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 304
"__id__": 290
},
"propertyPath": [
"_top"
@@ -6724,7 +6543,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 304
"__id__": 290
},
"propertyPath": [
"_alignFlags"
@@ -6734,7 +6553,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 304
"__id__": 290
},
"propertyPath": [
"_bottom"
@@ -6751,7 +6570,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 308
"__id__": 294
},
"_contentSize": {
"__type__": "cc.Size",
@@ -6779,7 +6598,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 310
"__id__": 296
},
"_alignFlags": 45,
"_target": null,
@@ -6815,7 +6634,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 312
"__id__": 298
},
"debugMode": false,
"_id": ""
@@ -6837,10 +6656,10 @@
"targetOverrides": null,
"nestedPrefabInstanceRoots": [
{
"__id__": 295
"__id__": 281
},
{
"__id__": 286
"__id__": 272
},
{
"__id__": 257

View File

@@ -176,43 +176,54 @@ export const HeroInfo: Record<number, heroInfo> = {
/*
*=============怪物配置列表================
* 基础近战型(lv:1) SPEED:800 |AP:12 | HP:120 | skills[0].cd=0.65
* 重型坦克型(lv:1) SPEED:800 |AP:30 | HP:350 | skills[0].cd=2
* 远程dps(lv:1) SPEED:800 |AP:45 | HP:80 | skills[0].cd=1.5
* 远程辅助(lv:1) SPEED:800 |AP:20 | HP:80 | skills[0].cd=1
* 精英 (lv:1) SPEED:800 |AP:20 | HP:1500 | skills[0].cd=1
* 基础近战型(lv:1) SPEED:800 |AP:12 | HP:360 | skills[0].cd=0.65
* 重型坦克型(lv:1) SPEED:800 |AP:30 | HP:1050 | skills[0].cd=2
* 远程dps(lv:1) SPEED:800 |AP:45 | HP:240 | skills[0].cd=1.5
* 远程辅助(lv:1) SPEED:800 |AP:20 | HP:240 | skills[0].cd=1
* 精英 (lv:1) SPEED:800 |AP:20 | HP:4500 | skills[0].cd=1
*/
//============== 兽人系列 ===============
// 近战型
6001:{uuid:6001,name:"兽人战士",path:"mo1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:120,ap:12,speed:100,
6001:{uuid:6001,name:"兽人战士",path:"mo1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:360,ap:12,speed:100,
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:120,ap:12,speed:100,
6002:{uuid:6002,name:"兽人斥候",path:"mo3", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:360,ap:12,speed:100,
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:350,ap:30,speed:100,
6003:{uuid:6003,name:"兽人卫士",path:"mo4", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:1050,ap:30,speed:100,
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:80,ap:45,speed:100,
6004:{uuid:6004,name:"兽人射手",path:"mo2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:240,ap:45,speed:100,
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:80,ap:20,speed:100,
6005:{uuid:6005,name:"兽人法师",path:"mo5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:240,ap:20,speed:100,
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:1500,ap:20,speed:100,
6006:{uuid:6006,name:"兽人首领",path:"mo6", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:4500,ap:20,speed:100,
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:120,ap:12,speed:100,
6101:{uuid:6101,name:"亡灵战士",path:"mud1", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:360,ap:12,speed:100,
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:120,ap:12,speed:100,
6103:{uuid:6103,name:"亡灵斥候",path:"mud3", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:360,ap:12,speed:100,
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:80,ap:45,speed:100,
6102:{uuid:6102,name:"亡灵射手",path:"mud2", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:240,ap:45,speed:100,
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:80,ap:20,speed:100,
// 6105:{uuid:6105,name:"兽人法师",path:"mud5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:240,ap:20,speed:100,
// skills:{6001:{uuid:6001,lv:1,cd:1,ccd:0},6003:{uuid:6003,lv:1,cd:10,ccd:0}},info:""},
// 6. 精英/BOSS型
6104:{uuid:6104,name:"亡灵法师",path:"mud4", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:350,ap:30,speed:100,
6104:{uuid:6104,name:"亡灵法师",path:"mud4", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Long,hp:1050,ap:30,speed:100,
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:1500,ap:20,speed:100,
6105:{uuid:6105,name:"亡灵首领",path:"mud5", fac:FacSet.MON,cards_lv:1,lv:1,type:HType.Melee,hp:4500,ap:20,speed:100,
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[] = [

View File

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

View File

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

4
test_spawn.js Normal file
View File

@@ -0,0 +1,4 @@
import { spawningEngine } from "./assets/script/game/map/RogueConfig.js";
console.log("Wave 1:", spawningEngine.generateWave(1).length);
console.log("Wave 2:", spawningEngine.generateWave(2).length);
console.log("Wave 3:", spawningEngine.generateWave(3).length);

4
test_spawn.ts Normal file
View File

@@ -0,0 +1,4 @@
import { spawningEngine } from "./assets/script/game/map/RogueConfig";
console.log("Wave 1:", spawningEngine.generateWave(1).length);
console.log("Wave 2:", spawningEngine.generateWave(2).length);
console.log("Wave 3:", spawningEngine.generateWave(3).length);