Compare commits
5 Commits
aab38e3233
...
b97ea5027d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b97ea5027d | ||
|
|
b588fd06a0 | ||
|
|
83d5792b48 | ||
|
|
c0166f9d03 | ||
|
|
afe6fb1bc0 |
400
assets/resources/gui/element/highlight.prefab
Normal file
400
assets/resources/gui/element/highlight.prefab
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"__type__": "cc.Prefab",
|
||||||
|
"_name": "highlight",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_native": "",
|
||||||
|
"data": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"optimizationPolicy": 0,
|
||||||
|
"persistent": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "highlight",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": null,
|
||||||
|
"_children": [
|
||||||
|
{
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 14
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 16
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 1073741824,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "icon",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_children": [],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 7
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": -49.62,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 1073741824,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 4
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 32,
|
||||||
|
"height": 32
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "5fJ3wyWeRFN7kPc9OuAXmV"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Sprite",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 6
|
||||||
|
},
|
||||||
|
"_customMaterial": null,
|
||||||
|
"_srcBlendFactor": 2,
|
||||||
|
"_dstBlendFactor": 4,
|
||||||
|
"_color": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 255,
|
||||||
|
"g": 255,
|
||||||
|
"b": 255,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_spriteFrame": {
|
||||||
|
"__uuid__": "cb93c900-b440-4571-91d1-7da1636e3d73@846cc",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
"_type": 0,
|
||||||
|
"_fillType": 0,
|
||||||
|
"_sizeMode": 0,
|
||||||
|
"_fillCenter": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"_fillStart": 0,
|
||||||
|
"_fillRange": 0,
|
||||||
|
"_isTrimmedMode": true,
|
||||||
|
"_useGrayscale": false,
|
||||||
|
"_atlas": null,
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "d6OnY0P9tJC4k9RdVsxNpm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "0eDkgu/69LuKtcD32qwUbN",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null,
|
||||||
|
"nestedPrefabInstanceRoots": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "Label",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_children": [],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 11
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 13
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": -24.655,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 1073741824,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 8
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 10
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 100,
|
||||||
|
"height": 50.4
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "99pcVUFvRDqbDRk7G8XlSo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Label",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 8
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 12
|
||||||
|
},
|
||||||
|
"_customMaterial": null,
|
||||||
|
"_srcBlendFactor": 2,
|
||||||
|
"_dstBlendFactor": 4,
|
||||||
|
"_color": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 255,
|
||||||
|
"g": 255,
|
||||||
|
"b": 255,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_string": "暴击大师",
|
||||||
|
"_horizontalAlign": 0,
|
||||||
|
"_verticalAlign": 1,
|
||||||
|
"_actualFontSize": 21,
|
||||||
|
"_fontSize": 20,
|
||||||
|
"_fontFamily": "Arial",
|
||||||
|
"_lineHeight": 40,
|
||||||
|
"_overflow": 2,
|
||||||
|
"_enableWrapText": true,
|
||||||
|
"_font": null,
|
||||||
|
"_isSystemFontUsed": true,
|
||||||
|
"_spacingX": 0,
|
||||||
|
"_isItalic": false,
|
||||||
|
"_isBold": true,
|
||||||
|
"_isUnderline": false,
|
||||||
|
"_underlineHeight": 2,
|
||||||
|
"_cacheMode": 0,
|
||||||
|
"_enableOutline": true,
|
||||||
|
"_outlineColor": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 0,
|
||||||
|
"g": 0,
|
||||||
|
"b": 0,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_outlineWidth": 2,
|
||||||
|
"_enableShadow": false,
|
||||||
|
"_shadowColor": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 0,
|
||||||
|
"g": 0,
|
||||||
|
"b": 0,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_shadowOffset": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 2,
|
||||||
|
"y": 2
|
||||||
|
},
|
||||||
|
"_shadowBlur": 2,
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "14kI2KPGNNkr7zcLFslGP+"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "33KfRxefJMI47rEEufEkMV",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null,
|
||||||
|
"nestedPrefabInstanceRoots": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 15
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 100,
|
||||||
|
"height": 100
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "0aAqXsadlAkrim8Re1CWIN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "3aBcBjdQdI9LIyPDmuxuPb",
|
||||||
|
"targetOverrides": null
|
||||||
|
}
|
||||||
|
]
|
||||||
13
assets/resources/gui/element/highlight.prefab.meta
Normal file
13
assets/resources/gui/element/highlight.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "12df63de-cda7-4be2-8e23-ea387fbba6f5",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "highlight"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -92,16 +92,35 @@ export class SingletonModuleComp extends ecs.Comp {
|
|||||||
// 生存统计
|
// 生存统计
|
||||||
heal_total: 0, // 治疗总量
|
heal_total: 0, // 治疗总量
|
||||||
lifesteal_total: 0, // 吸血总量
|
lifesteal_total: 0, // 吸血总量
|
||||||
|
shield_block_count: 0,
|
||||||
|
dead_trigger_count: 0,
|
||||||
|
|
||||||
// 资源统计
|
// 资源统计
|
||||||
exp_total: 0, // 经验总数
|
exp_total: 0, // 经验总数
|
||||||
gold_total: 0, // 金币总数
|
gold_total: 0, // 金币总数
|
||||||
|
gold_earned: 0,
|
||||||
|
gold_spent: 0,
|
||||||
|
refresh_count: 0,
|
||||||
|
refresh_hit_count: 0,
|
||||||
|
|
||||||
// 击杀统计
|
// 击杀统计
|
||||||
melee_kill_count: 0, // 近战怪击杀数量
|
melee_kill_count: 0, // 近战怪击杀数量
|
||||||
remote_kill_count: 0, // 远程怪击杀数量
|
remote_kill_count: 0, // 远程怪击杀数量
|
||||||
elite_kill_count: 0, // 精英怪击杀数量
|
elite_kill_count: 0, // 精英怪击杀数量
|
||||||
boss_kill_count: 0, // Boss击杀数
|
boss_kill_count: 0, // Boss击杀数
|
||||||
|
|
||||||
|
// 战绩统计
|
||||||
|
wave_win_count: 0,
|
||||||
|
wave_remain_monsters: 0,
|
||||||
|
wave_all_alive_count: 0,
|
||||||
|
passed_wave_20: false,
|
||||||
|
highest_dmg: 0,
|
||||||
|
|
||||||
|
score_combat: 0,
|
||||||
|
score_output: 0,
|
||||||
|
score_defense: 0,
|
||||||
|
score_build: 0,
|
||||||
|
score_efficiency: 0,
|
||||||
} as GameScoreStats,
|
} as GameScoreStats,
|
||||||
|
|
||||||
gold: 0, // 金币数据(MVVM绑定字段)
|
gold: 0, // 金币数据(MVVM绑定字段)
|
||||||
@@ -120,6 +139,51 @@ export class SingletonModuleComp extends ecs.Comp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置单局评分数据
|
||||||
|
* 在每次新战斗开始时调用,确保上一局的得分不会带入新一局
|
||||||
|
*/
|
||||||
|
resetScores() {
|
||||||
|
this.vmdata.scores = {
|
||||||
|
score: 0,
|
||||||
|
crt_count: 0,
|
||||||
|
wf_count: 0,
|
||||||
|
dod_count: 0,
|
||||||
|
back_count: 0,
|
||||||
|
stun_count: 0,
|
||||||
|
freeze_count: 0,
|
||||||
|
total_dmg: 0,
|
||||||
|
atk_count: 0,
|
||||||
|
avg_dmg: 0,
|
||||||
|
thorns_dmg: 0,
|
||||||
|
crit_dmg_total: 0,
|
||||||
|
heal_total: 0,
|
||||||
|
lifesteal_total: 0,
|
||||||
|
shield_block_count: 0,
|
||||||
|
dead_trigger_count: 0,
|
||||||
|
exp_total: 0,
|
||||||
|
gold_total: 0,
|
||||||
|
gold_earned: 0,
|
||||||
|
gold_spent: 0,
|
||||||
|
refresh_count: 0,
|
||||||
|
refresh_hit_count: 0,
|
||||||
|
melee_kill_count: 0,
|
||||||
|
remote_kill_count: 0,
|
||||||
|
elite_kill_count: 0,
|
||||||
|
boss_kill_count: 0,
|
||||||
|
wave_win_count: 0,
|
||||||
|
wave_remain_monsters: 0,
|
||||||
|
wave_all_alive_count: 0,
|
||||||
|
passed_wave_20: false,
|
||||||
|
highest_dmg: 0,
|
||||||
|
score_combat: 0,
|
||||||
|
score_output: 0,
|
||||||
|
score_defense: 0,
|
||||||
|
score_build: 0,
|
||||||
|
score_efficiency: 0,
|
||||||
|
} as GameScoreStats;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== 数据管理方法 ====================
|
// ==================== 数据管理方法 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -54,16 +54,36 @@ export interface GameScoreStats {
|
|||||||
// 生存统计
|
// 生存统计
|
||||||
heal_total: number; // 治疗总量
|
heal_total: number; // 治疗总量
|
||||||
lifesteal_total: number;// 吸血总量
|
lifesteal_total: number;// 吸血总量
|
||||||
|
shield_block_count: number; // 格挡次数
|
||||||
|
dead_trigger_count: number; // 死亡触发次数
|
||||||
|
|
||||||
// 资源统计
|
// 资源统计
|
||||||
exp_total: number; // 经验总数
|
exp_total: number; // 经验总数
|
||||||
gold_total: number; // 金币总数
|
gold_total: number; // 金币总数
|
||||||
|
gold_earned: number; // 总收入金币
|
||||||
|
gold_spent: number; // 消耗金币
|
||||||
|
refresh_count: number; // 刷新次数
|
||||||
|
refresh_hit_count: number; // 刷新命中次数(刷新后选中卡)
|
||||||
|
|
||||||
// 击杀统计
|
// 击杀统计
|
||||||
melee_kill_count: number; // 近战怪击杀数量
|
melee_kill_count: number; // 近战怪击杀数量
|
||||||
remote_kill_count: number; // 远程怪击杀数量
|
remote_kill_count: number; // 远程怪击杀数量
|
||||||
elite_kill_count: number; // 精英怪击杀数量
|
elite_kill_count: number; // 精英怪击杀数量
|
||||||
boss_kill_count: number; // Boss击杀数
|
boss_kill_count: number; // Boss击杀数
|
||||||
|
|
||||||
|
// 战绩统计
|
||||||
|
wave_win_count: number; // 回合胜利次数
|
||||||
|
wave_remain_monsters: number; // 累计每回合留存敌人数量
|
||||||
|
wave_all_alive_count: number; // 全员存活胜利次数
|
||||||
|
passed_wave_20: boolean; // 是否通过第20回合
|
||||||
|
highest_dmg: number; // 单次最高伤害
|
||||||
|
|
||||||
|
// 最终结算分
|
||||||
|
score_combat: number;
|
||||||
|
score_output: number;
|
||||||
|
score_defense: number;
|
||||||
|
score_build: number;
|
||||||
|
score_efficiency: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
119
assets/script/game/common/config/scoring-system.md
Normal file
119
assets/script/game/common/config/scoring-system.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# 评分系统设计 — Draftmaster Arena
|
||||||
|
|
||||||
|
*Created: 2026-04-25*
|
||||||
|
*Status: Draft*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
每局游戏(20回合)结束后或进入无限模式 失败后 计算总分,从5个维度综合评估玩家表现。不同流派在不同维度各有优势,没有唯一最佳刷分流派。
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 维度1:战绩分
|
||||||
|
|
||||||
|
衡量生存能力——活几回合、赢几场。
|
||||||
|
|
||||||
|
| 事件 | 分值 |
|
||||||
|
|------|------|
|
||||||
|
| 每回合胜利 | +100分 |
|
||||||
|
| 每回合每留存1个敌人 | -15分 |
|
||||||
|
| 全员存活胜利 | +50分(额外) |
|
||||||
|
| 通过第20回合 | +500分(通关奖励) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 维度2:输出分
|
||||||
|
|
||||||
|
衡量伤害能力——团队火力如何。
|
||||||
|
|
||||||
|
| 事件 | 分值 |
|
||||||
|
|------|------|
|
||||||
|
| 团队伤害总量 | 每100伤害 = +10分 |
|
||||||
|
| 暴击次数 | 每次 +5分 |
|
||||||
|
| 风怒次数 | 每次 +5分 |
|
||||||
|
| 单次最高伤害 | 记录值 × 2分 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 维度3:防御分
|
||||||
|
|
||||||
|
衡量生存能力——团队多能扛。
|
||||||
|
|
||||||
|
| 事件 | 分值 |
|
||||||
|
|------|------|
|
||||||
|
| 格挡次数(护盾低于次数) | 每次 +5分 |
|
||||||
|
| 治疗总量 | 每50治疗 = +10分 |
|
||||||
|
| 死亡触发次数 | 每次 +8分 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 维度4:构建分
|
||||||
|
|
||||||
|
衡量阵容构建质量——团队配合程度。
|
||||||
|
|
||||||
|
待完善
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 维度5:效率分
|
||||||
|
|
||||||
|
衡量资源利用——金币花得值不值。
|
||||||
|
|
||||||
|
| 事件 | 分值 |
|
||||||
|
|------|------|
|
||||||
|
| 金币使用率 | 使用金币 / 总收入 × 100分 |
|
||||||
|
| 刷新命中率 | 刷新后选中卡 / 刷新次数 × 50分 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 评分结算信息
|
||||||
|
|
||||||
|
│ 🏆 战绩 2,350 ████ │
|
||||||
|
│ ⚔️ 输出 1,080 ███ │
|
||||||
|
│ 🛡️ 防御 520 ██ │
|
||||||
|
│ 🃏 构建 280 █ │
|
||||||
|
│ 💰 效率 50 ▏ │
|
||||||
|
│ │
|
||||||
|
│ 亮点: │
|
||||||
|
│ 🔥 暴击大师 (暴击47次) │
|
||||||
|
│ 💀 送死达人 (死亡32次) │
|
||||||
|
│ 🎯 通关奖励 (+500) │
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 流派与评分的关系
|
||||||
|
|
||||||
|
| 流派 | 高分维度 | 低分维度 | 策略 |
|
||||||
|
|------|---------|---------|------|
|
||||||
|
| 暴击流 | 输出↑↑ | 防御↓ | 火力碾压 |
|
||||||
|
| 护盾流 | 防御↑↑ | 输出↓ | 持久消耗 |
|
||||||
|
| 攻击流 | 输出↑ | 构建→ | 纯伤害堆叠 |
|
||||||
|
| 受伤流 | 防御↑ | 战绩→ | 越打越硬 |
|
||||||
|
| 风怒流 | 输出↑↑ | 防御↓ | 连击爆发 |
|
||||||
|
| 死亡流 | 防御↑ 构建↑ | 战绩↓ | 死亡也是得分 |
|
||||||
|
| 召唤流 | 构建↑↑ 防御↑ | 输出→ | 配合丰富 |
|
||||||
|
|
||||||
|
混合流派得分最均衡——暴击+死亡 = 高输出 + 高防御分。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 亮点系统(成就标签)
|
||||||
|
|
||||||
|
每局结束后,根据表现自动授予1-3个亮点标签:
|
||||||
|
|
||||||
|
| 亮点 | 触发条件 |
|
||||||
|
|------|---------|
|
||||||
|
| 🔥 暴击大师 | 暴击次数 ≥ 40 |
|
||||||
|
| 💀 送死达人 | 死亡触发 ≥ 25次 |
|
||||||
|
| 🛡️ 铁壁铜墙 | 格挡次数 ≥ 30 |
|
||||||
|
| ⚡ 风暴之王 | 风怒次数 ≥ 20 |
|
||||||
|
| 🎯 一击必杀 | 单次伤害 ≥ 200 |
|
||||||
|
| 💊 治愈之光 | 治疗总量 ≥ 500 |
|
||||||
|
| 🃏 流派大师 | 完成3个流派构建 |
|
||||||
|
| ♻️ 合成狂人 | 合成次数 ≥ 8 |
|
||||||
|
| 🏆 完美通关 | 20回合全胜 |
|
||||||
|
| 🎲 欧皇附体 | 刷新命中率 ≥ 80% |
|
||||||
|
| 💰 节俭玩家 | 金币使用率 ≥ 95% |
|
||||||
11
assets/script/game/common/config/scoring-system.md.meta
Normal file
11
assets/script/game/common/config/scoring-system.md.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.1",
|
||||||
|
"importer": "text",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "8127fa12-7890-4bad-9dff-eeb9e1e4d6c9",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -144,12 +144,20 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
if (isCrit) {
|
if (isCrit) {
|
||||||
damage = Math.floor(damage * (1 + FightSet.CRIT_DAMAGE / 100));
|
damage = Math.floor(damage * (1 + FightSet.CRIT_DAMAGE / 100));
|
||||||
reDate.isCrit=true;
|
reDate.isCrit=true;
|
||||||
|
if (damageEvent.Attrs.fac === FacSet.HERO) {
|
||||||
|
// 【评分系统 - 输出分】统计暴击次数与暴击造成的总伤害
|
||||||
|
smc.vmdata.scores.crt_count++;
|
||||||
|
smc.vmdata.scores.crit_dmg_total += damage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after crit",damage)
|
mLogger.log(this.debugMode, 'HeroAtkSystem', " after crit",damage)
|
||||||
// 护盾吸收
|
// 护盾吸收
|
||||||
const shieldResult = this.absorbShield(TAttrsComp, damage);
|
const shieldResult = this.absorbShield(TAttrsComp, damage);
|
||||||
damage = shieldResult.remainingDamage;
|
damage = shieldResult.remainingDamage;
|
||||||
|
if (shieldResult.absorbedDamage > 0) {
|
||||||
|
// 【评分系统 - 防御分】统计护盾成功抵挡伤害的次数
|
||||||
|
smc.vmdata.scores.shield_block_count += shieldResult.absorbedDamage;
|
||||||
|
}
|
||||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after shield",damage)
|
mLogger.log(this.debugMode, 'HeroAtkSystem', " after shield",damage)
|
||||||
// 显示护盾吸收飘字
|
// 显示护盾吸收飘字
|
||||||
if (shieldResult.absorbedDamage > 0 && targetView) {
|
if (shieldResult.absorbedDamage > 0 && targetView) {
|
||||||
@@ -160,6 +168,14 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
// TAttrsComp.hp -= damage; // 应用伤害到数据层
|
// TAttrsComp.hp -= damage; // 应用伤害到数据层
|
||||||
TAttrsComp.add_hp(-damage); // 使用 add_hp 以触发 dirty_hp 和 UI 更新
|
TAttrsComp.add_hp(-damage); // 使用 add_hp 以触发 dirty_hp 和 UI 更新
|
||||||
|
|
||||||
|
if (damageEvent.Attrs.fac === FacSet.HERO) {
|
||||||
|
// 【评分系统 - 输出分】统计团队造成的总伤害以及单次最高伤害记录
|
||||||
|
smc.vmdata.scores.total_dmg += damage;
|
||||||
|
if (damage > smc.vmdata.scores.highest_dmg) {
|
||||||
|
smc.vmdata.scores.highest_dmg = damage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 增加受击计数并触发 atked 技能
|
// 增加受击计数并触发 atked 技能
|
||||||
TAttrsComp.atked_count++;
|
TAttrsComp.atked_count++;
|
||||||
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
||||||
@@ -301,6 +317,10 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
|||||||
|
|
||||||
for (let i = 0; i < triggerCount; i++) {
|
for (let i = 0; i < triggerCount; i++) {
|
||||||
TAttrsComp.dead.forEach((uuid: number) => {
|
TAttrsComp.dead.forEach((uuid: number) => {
|
||||||
|
if (TAttrsComp.fac === FacSet.HERO) {
|
||||||
|
// 【评分系统 - 防御分】统计死亡触发技能的生效次数
|
||||||
|
smc.vmdata.scores.dead_trigger_count++;
|
||||||
|
}
|
||||||
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
|
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
|
||||||
s_uuid: uuid,
|
s_uuid: uuid,
|
||||||
heroAttrs: TAttrsComp,
|
heroAttrs: TAttrsComp,
|
||||||
|
|||||||
@@ -429,6 +429,10 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
const addHp = Math.floor(sAp*_cAttrsComp.ap/100);
|
const addHp = Math.floor(sAp*_cAttrsComp.ap/100);
|
||||||
model.add_hp(addHp);
|
model.add_hp(addHp);
|
||||||
target.health(addHp);
|
target.health(addHp);
|
||||||
|
if (_cAttrsComp.fac === FacSet.HERO) {
|
||||||
|
// 【评分系统 - 防御分】统计团队造成的总治疗量
|
||||||
|
smc.vmdata.scores.heal_total += addHp;
|
||||||
|
}
|
||||||
} else if (kind === SkillKind.Shield && sAp !== 0) {
|
} else if (kind === SkillKind.Shield && sAp !== 0) {
|
||||||
const addShield = Math.max(0, Math.floor(sAp));
|
const addShield = Math.max(0, Math.floor(sAp));
|
||||||
model.add_shield(addShield);
|
model.add_shield(addShield);
|
||||||
|
|||||||
@@ -331,6 +331,10 @@ export class CardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
// 扣除金币
|
// 扣除金币
|
||||||
this.setMissionCoin(currentCoin - cardCost);
|
this.setMissionCoin(currentCoin - cardCost);
|
||||||
|
// 【评分系统 - 效率分】记录购卡消耗的金币,以及刷新后的选中卡次数(命中率分子)
|
||||||
|
smc.vmdata.scores.gold_spent += cardCost;
|
||||||
|
smc.vmdata.scores.refresh_hit_count++;
|
||||||
|
|
||||||
oops.message.dispatchEvent(GameEvent.CoinAdd, {
|
oops.message.dispatchEvent(GameEvent.CoinAdd, {
|
||||||
syncOnly: true,
|
syncOnly: true,
|
||||||
delta: -cardCost
|
delta: -cardCost
|
||||||
|
|||||||
@@ -298,6 +298,9 @@ export class MissionCardComp extends CCComp {
|
|||||||
const v = typeof payload === 'number' ? payload : (payload?.delta ?? payload?.value ?? 0);
|
const v = typeof payload === 'number' ? payload : (payload?.delta ?? payload?.value ?? 0);
|
||||||
if (v === 0) return;
|
if (v === 0) return;
|
||||||
this.setMissionCoin(this.getMissionCoin() + v);
|
this.setMissionCoin(this.getMissionCoin() + v);
|
||||||
|
// 【评分系统 - 效率分】精确统计整局游戏的总收入与额外支出(如卖卡、技能扣费等)
|
||||||
|
if (v > 0) smc.vmdata.scores.gold_earned += v;
|
||||||
|
else smc.vmdata.scores.gold_spent -= v;
|
||||||
this.updateCoinAndCostUI();
|
this.updateCoinAndCostUI();
|
||||||
this.playCoinChangeAnim(v > 0);
|
this.playCoinChangeAnim(v > 0);
|
||||||
}
|
}
|
||||||
@@ -554,6 +557,9 @@ export class MissionCardComp extends CCComp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setMissionCoin(currentCoin - cost);
|
this.setMissionCoin(currentCoin - cost);
|
||||||
|
// 【评分系统 - 效率分】记录因刷新卡池消耗的金币,以及刷新次数
|
||||||
|
smc.vmdata.scores.gold_spent += cost;
|
||||||
|
smc.vmdata.scores.refresh_count++;
|
||||||
this.playCoinChangeAnim(false);
|
this.playCoinChangeAnim(false);
|
||||||
this.updateCoinAndCostUI();
|
this.updateCoinAndCostUI();
|
||||||
mLogger.log(this.debugMode, "MissionCardComp", "click draw", {
|
mLogger.log(this.debugMode, "MissionCardComp", "click draw", {
|
||||||
|
|||||||
@@ -445,6 +445,33 @@ export class MissionComp extends CCComp {
|
|||||||
smc.mission.in_fight = false;
|
smc.mission.in_fight = false;
|
||||||
smc.vmdata.mission_data.in_fight = false;
|
smc.vmdata.mission_data.in_fight = false;
|
||||||
smc.mission.stop_spawn_mon = true;
|
smc.mission.stop_spawn_mon = true;
|
||||||
|
|
||||||
|
if (smc.mission.play && !smc.mission.pause) {
|
||||||
|
// 【评分系统 - 战绩分】每回合胜利加分
|
||||||
|
smc.vmdata.scores.wave_win_count++;
|
||||||
|
// 【评分系统 - 战绩分】记录每回合结束时场上留存的敌人数量(扣分项)
|
||||||
|
smc.vmdata.scores.wave_remain_monsters += smc.vmdata.mission_data.mon_num;
|
||||||
|
|
||||||
|
let allAlive = true;
|
||||||
|
let hasHero = false;
|
||||||
|
ecs.query(this.heroAttrsMatcher).forEach(entity => {
|
||||||
|
const attrs = entity.get(HeroAttrsComp);
|
||||||
|
if (attrs && attrs.fac === FacSet.HERO) {
|
||||||
|
hasHero = true;
|
||||||
|
if (attrs.is_dead) allAlive = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 【评分系统 - 战绩分】记录全员存活的胜利回合数(额外加分)
|
||||||
|
if (hasHero && allAlive) {
|
||||||
|
smc.vmdata.scores.wave_all_alive_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【评分系统 - 战绩分】判断是否通过最后一关(第20回合)
|
||||||
|
if (this.currentWave === 20) {
|
||||||
|
smc.vmdata.scores.passed_wave_20 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 触发战斗结束技能(fend)
|
// 触发战斗结束技能(fend)
|
||||||
this.triggerHeroBattleSkills(false);
|
this.triggerHeroBattleSkills(false);
|
||||||
|
|
||||||
@@ -682,7 +709,13 @@ export class MissionComp extends CCComp {
|
|||||||
this.heapTrendBaseMB = -1;
|
this.heapTrendBaseMB = -1;
|
||||||
this.monsterCountSyncTimer = 0;
|
this.monsterCountSyncTimer = 0;
|
||||||
this.lastPrepareCoinWave = 0;
|
this.lastPrepareCoinWave = 0;
|
||||||
|
|
||||||
|
// 重置所有的战局得分数据,防止上一局的数据污染
|
||||||
|
smc.resetScores();
|
||||||
|
|
||||||
smc.vmdata.mission_data.coin = Math.max(0, Math.floor(CardInitCoins));
|
smc.vmdata.mission_data.coin = Math.max(0, Math.floor(CardInitCoins));
|
||||||
|
// 【评分系统 - 效率分】记录初始获得的金币收入
|
||||||
|
smc.vmdata.scores.gold_earned += smc.vmdata.mission_data.coin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 波次管理 ========================
|
// ======================== 波次管理 ========================
|
||||||
@@ -761,6 +794,8 @@ export class MissionComp extends CCComp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
smc.vmdata.mission_data.coin = Math.max(0, Math.floor((smc.vmdata.mission_data.coin ?? 0) + reward));
|
smc.vmdata.mission_data.coin = Math.max(0, Math.floor((smc.vmdata.mission_data.coin ?? 0) + reward));
|
||||||
|
// 【评分系统 - 效率分】记录每波次发放的金币奖励收入
|
||||||
|
smc.vmdata.scores.gold_earned += reward;
|
||||||
this.lastPrepareCoinWave = wave;
|
this.lastPrepareCoinWave = wave;
|
||||||
oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: reward, syncOnly: true });
|
oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: reward, syncOnly: true });
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
* - ScoreWeights(ScoreSet)—— 得分权重配置
|
* - ScoreWeights(ScoreSet)—— 得分权重配置
|
||||||
* - GameEvent.MissionEnd / MissionStart —— 游戏生命周期事件
|
* - GameEvent.MissionEnd / MissionStart —— 游戏生命周期事件
|
||||||
*/
|
*/
|
||||||
import { _decorator, instantiate, Label ,Prefab,Node} from "cc";
|
import { _decorator, instantiate, Label ,Prefab,Node, Sprite, Animation, AnimationClip, resources, UITransform, Widget } from "cc";
|
||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||||||
@@ -29,6 +29,8 @@ import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
|||||||
import { HeroViewComp } from "../hero/HeroViewComp";
|
import { HeroViewComp } from "../hero/HeroViewComp";
|
||||||
import { FacSet } from "../common/config/GameSet";
|
import { FacSet } from "../common/config/GameSet";
|
||||||
import { Attrs } from "../common/config/HeroAttrs";
|
import { Attrs } from "../common/config/HeroAttrs";
|
||||||
|
import { HeroInfo } from "../common/config/heroSet";
|
||||||
|
import { CKind } from "../common/config/CardSet";
|
||||||
import { ScoreWeights } from "../common/config/ScoreSet";
|
import { ScoreWeights } from "../common/config/ScoreSet";
|
||||||
import { mLogger } from "../common/Logger";
|
import { mLogger } from "../common/Logger";
|
||||||
|
|
||||||
@@ -43,6 +45,35 @@ const { ccclass, property } = _decorator;
|
|||||||
@ccclass('VictoryComp')
|
@ccclass('VictoryComp')
|
||||||
@ecs.register('Victory', false)
|
@ecs.register('Victory', false)
|
||||||
export class VictoryComp extends CCComp {
|
export class VictoryComp extends CCComp {
|
||||||
|
|
||||||
|
@property(Node)
|
||||||
|
mvp_node=null!
|
||||||
|
|
||||||
|
// ======================== 结算 UI 绑定 ========================
|
||||||
|
@property({ type: Label, tooltip: "总分文本" })
|
||||||
|
total_score_label: Label = null!;
|
||||||
|
|
||||||
|
@property({ type: Node, tooltip: "战绩分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||||
|
combat_node: Node = null!;
|
||||||
|
|
||||||
|
@property({ type: Node, tooltip: "输出分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||||
|
output_node: Node = null!;
|
||||||
|
|
||||||
|
@property({ type: Node, tooltip: "防御分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||||
|
defense_node: Node = null!;
|
||||||
|
|
||||||
|
@property({ type: Node, tooltip: "构建分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||||
|
build_node: Node = null!;
|
||||||
|
|
||||||
|
@property({ type: Node, tooltip: "效率分节点 (需包含 score_label 和 progress_bar 子节点)" })
|
||||||
|
efficiency_node: Node = null!;
|
||||||
|
|
||||||
|
@property({ type: Node, tooltip: "亮点成就标签的容器" })
|
||||||
|
highlights_container: Node = null!;
|
||||||
|
|
||||||
|
@property({ type: Prefab, tooltip: "亮点成就标签预制体" })
|
||||||
|
highlight_prefab: Prefab = null!;
|
||||||
|
|
||||||
/** 调试日志开关 */
|
/** 调试日志开关 */
|
||||||
debugMode: boolean = false;
|
debugMode: boolean = false;
|
||||||
/** 奖励等级(预留) */
|
/** 奖励等级(预留) */
|
||||||
@@ -88,6 +119,234 @@ export class VictoryComp extends CCComp {
|
|||||||
|
|
||||||
// 计算总分
|
// 计算总分
|
||||||
this.calculateTotalScore();
|
this.calculateTotalScore();
|
||||||
|
|
||||||
|
// 渲染分数UI和亮点标签
|
||||||
|
this.renderScores();
|
||||||
|
|
||||||
|
// 显示MVP英雄
|
||||||
|
const mvp = this.getMVPHero();
|
||||||
|
this.renderMVPHero(mvp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== MVP 英雄 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取战斗中最厉害的英雄(根据等级和攻击力)
|
||||||
|
*/
|
||||||
|
private getMVPHero(): HeroAttrsComp | null {
|
||||||
|
let mvp: HeroAttrsComp | null = null;
|
||||||
|
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||||
|
const model = entity.get(HeroAttrsComp);
|
||||||
|
if (!model || model.fac !== FacSet.HERO) return;
|
||||||
|
if (!mvp) {
|
||||||
|
mvp = model;
|
||||||
|
} else {
|
||||||
|
if (model.lv > mvp.lv) {
|
||||||
|
mvp = model;
|
||||||
|
} else if (model.lv === mvp.lv && model.ap > mvp.ap) {
|
||||||
|
mvp = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return mvp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染 MVP 英雄,逻辑参考 CardComp 长按放大的 UI 显示
|
||||||
|
*/
|
||||||
|
private renderMVPHero(mvp: HeroAttrsComp | null) {
|
||||||
|
if (!this.mvp_node) return;
|
||||||
|
|
||||||
|
if (!mvp) {
|
||||||
|
this.mvp_node.active = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mvp_node.active = true;
|
||||||
|
|
||||||
|
const uuid = mvp.hero_uuid;
|
||||||
|
const hero = HeroInfo[uuid];
|
||||||
|
if (!hero) return;
|
||||||
|
|
||||||
|
const kindName = CKind[CKind.Hero];
|
||||||
|
|
||||||
|
// 节点查找
|
||||||
|
const BG_node = this.mvp_node.getChildByName("BG");
|
||||||
|
const HF_node = this.mvp_node.getChildByName("HF");
|
||||||
|
const NF_node = this.mvp_node.getChildByName("NF");
|
||||||
|
const lv_node = this.mvp_node.getChildByName("lv");
|
||||||
|
const name_node = this.mvp_node.getChildByName("name");
|
||||||
|
const ap_node = this.mvp_node.getChildByName("ap");
|
||||||
|
const hp_node = this.mvp_node.getChildByName("hp");
|
||||||
|
const oinfo_node = this.mvp_node.getChildByName("oinfo");
|
||||||
|
const icon_node = this.mvp_node.getChildByName("icon");
|
||||||
|
const hbNode = this.mvp_node.getChildByName("HB");
|
||||||
|
const cost_node = this.mvp_node.getChildByName("cost");
|
||||||
|
|
||||||
|
// ---- 背景与边框 ----
|
||||||
|
if (BG_node) {
|
||||||
|
BG_node.children.forEach(child => {
|
||||||
|
child.active = (child.name === kindName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HF_node) {
|
||||||
|
HF_node.active = true;
|
||||||
|
// HF_node.children.forEach(child => {
|
||||||
|
// child.active = (child.name === kindName);
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NF_node) {
|
||||||
|
NF_node.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hbNode) hbNode.active = false;
|
||||||
|
|
||||||
|
// ---- 卡牌等级标识 ----
|
||||||
|
const cardLvStr = `lv${mvp.pool_lv || 1}`;
|
||||||
|
if (lv_node) {
|
||||||
|
lv_node.children.forEach(child => {
|
||||||
|
if (child.name === "light") {
|
||||||
|
child.active = false;
|
||||||
|
} else if (child.name === "bg") {
|
||||||
|
child.active = true;
|
||||||
|
} else {
|
||||||
|
child.active = (child.name === cardLvStr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 调整尺寸 (模拟放大状态) ----
|
||||||
|
const isEnlarged = true; // 结算界面的卡牌默认处于放大显示状态
|
||||||
|
const uiTrans = this.mvp_node.getComponent(UITransform);
|
||||||
|
if (uiTrans) {
|
||||||
|
uiTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||||||
|
const widget = this.mvp_node.getComponent(Widget);
|
||||||
|
if (widget) widget.updateAlignment();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BG_node) {
|
||||||
|
const bgTrans = BG_node.getComponent(UITransform);
|
||||||
|
if (bgTrans) {
|
||||||
|
bgTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||||||
|
const widget = BG_node.getComponent(Widget);
|
||||||
|
if (widget) widget.updateAlignment();
|
||||||
|
}
|
||||||
|
BG_node.children.forEach(child => {
|
||||||
|
const childTrans = child.getComponent(UITransform);
|
||||||
|
if (childTrans) {
|
||||||
|
childTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||||||
|
const widget = child.getComponent(Widget);
|
||||||
|
if (widget) widget.updateAlignment();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HF_node) {
|
||||||
|
const hfTrans = HF_node.getComponent(UITransform);
|
||||||
|
if (hfTrans) {
|
||||||
|
hfTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||||||
|
const widget = HF_node.getComponent(Widget);
|
||||||
|
if (widget) widget.updateAlignment();
|
||||||
|
}
|
||||||
|
HF_node.children.forEach(child => {
|
||||||
|
const childTrans = child.getComponent(UITransform);
|
||||||
|
if (childTrans) {
|
||||||
|
childTrans.setContentSize(isEnlarged ? 230 : 170, isEnlarged ? 340 : 230);
|
||||||
|
const widget = child.getComponent(Widget);
|
||||||
|
if (widget) widget.updateAlignment();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mvp_node.children.forEach(child => {
|
||||||
|
const widget = child.getComponent(Widget);
|
||||||
|
if (widget) widget.updateAlignment();
|
||||||
|
child.children.forEach(subChild => {
|
||||||
|
const subWidget = subChild.getComponent(Widget);
|
||||||
|
if (subWidget) subWidget.updateAlignment();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---- 文本信息 ----
|
||||||
|
const heroLv = mvp.lv || 1;
|
||||||
|
const suffix = heroLv >= 2 ? "★".repeat(heroLv - 1) : "";
|
||||||
|
if (name_node) {
|
||||||
|
const label = name_node.getComponent(Label);
|
||||||
|
if (label) label.string = `${suffix}${hero.name || ""}${suffix}`;
|
||||||
|
const currentPos = name_node.position;
|
||||||
|
name_node.setPosition(currentPos.x, isEnlarged ? 8 : -70, currentPos.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ap_node) {
|
||||||
|
ap_node.active = true;
|
||||||
|
const valNode = ap_node.getChildByName("val");
|
||||||
|
if (valNode) {
|
||||||
|
const label = valNode.getComponent(Label);
|
||||||
|
if (label) label.string = `${Math.floor(mvp.ap)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hp_node) {
|
||||||
|
hp_node.active = true;
|
||||||
|
const valNode = hp_node.getChildByName("val");
|
||||||
|
if (valNode) {
|
||||||
|
const label = valNode.getComponent(Label);
|
||||||
|
if (label) label.string = `${Math.floor(mvp.hp_max)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oinfo_node) {
|
||||||
|
oinfo_node.active = isEnlarged;
|
||||||
|
const infoLabel = oinfo_node.getChildByName("info")?.getComponent(Label);
|
||||||
|
if (infoLabel) infoLabel.string = `${hero.info || ""}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cost_node) {
|
||||||
|
cost_node.active = false; // 结算时不显示金币费用
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 图标动画 ----
|
||||||
|
if (icon_node) {
|
||||||
|
this.updateHeroAnimation(icon_node, uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateHeroAnimation(node: Node, uuid: number) {
|
||||||
|
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||||||
|
if (sprite) sprite.spriteFrame = null;
|
||||||
|
|
||||||
|
const hero = HeroInfo[uuid];
|
||||||
|
if (!hero) return;
|
||||||
|
|
||||||
|
const anim = node.getComponent(Animation) || node.addComponent(Animation);
|
||||||
|
|
||||||
|
// clear animation clips
|
||||||
|
const clips = anim.clips;
|
||||||
|
for (let i = clips.length - 1; i >= 0; i--) {
|
||||||
|
const clip = clips[i];
|
||||||
|
if (clip) anim.removeClip(clip);
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = `game/heros/hero/${hero.path}/idle`;
|
||||||
|
resources.load(path, AnimationClip, (err, clip) => {
|
||||||
|
if (err || !clip) {
|
||||||
|
mLogger.log(this.debugMode, "VictoryComp", `load hero animation failed ${uuid}`, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// avoid state conflict
|
||||||
|
if (!this.node.isValid || !this.mvp_node || !this.mvp_node.active) return;
|
||||||
|
|
||||||
|
const currentClips = anim.clips;
|
||||||
|
for (let i = currentClips.length - 1; i >= 0; i--) {
|
||||||
|
const c = currentClips[i];
|
||||||
|
if (c) anim.removeClip(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
anim.addClip(clip);
|
||||||
|
anim.play("idle");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 得分计算 ========================
|
// ======================== 得分计算 ========================
|
||||||
@@ -104,39 +363,126 @@ export class VictoryComp extends CCComp {
|
|||||||
*/
|
*/
|
||||||
private calculateTotalScore() {
|
private calculateTotalScore() {
|
||||||
const s = smc.vmdata.scores;
|
const s = smc.vmdata.scores;
|
||||||
let totalScore = 0;
|
|
||||||
|
// 1. 战绩分:衡量生存能力——活几回合、赢几场。
|
||||||
|
s.score_combat = (s.wave_win_count * 100)
|
||||||
|
- (s.wave_remain_monsters * 15)
|
||||||
|
+ (s.wave_all_alive_count * 50)
|
||||||
|
+ (s.passed_wave_20 ? 500 : 0);
|
||||||
|
|
||||||
// 1. 战斗行为分
|
// 2. 输出分:衡量伤害能力——团队火力如何。
|
||||||
totalScore += s.crt_count * ScoreWeights.CRT_KILL;
|
s.score_output = Math.floor(s.total_dmg / 100) * 10
|
||||||
totalScore += s.wf_count * ScoreWeights.WF_TRIGGER;
|
+ (s.crt_count * 5)
|
||||||
totalScore += s.dod_count * ScoreWeights.DODGE_SUCCESS;
|
+ (s.wf_count * 5)
|
||||||
totalScore += s.back_count * ScoreWeights.BACK_SUCCESS;
|
+ (s.highest_dmg * 2);
|
||||||
totalScore += s.stun_count * ScoreWeights.STUN_SUCCESS;
|
|
||||||
totalScore += s.freeze_count * ScoreWeights.FREEZE_SUCCESS;
|
|
||||||
|
|
||||||
// 2. 伤害转化分
|
// 3. 防御分:衡量生存能力——团队多能扛。
|
||||||
totalScore += s.total_dmg * ScoreWeights.DMG_FACTOR;
|
s.score_defense = (s.shield_block_count * 5)
|
||||||
totalScore += s.avg_dmg * ScoreWeights.AVG_DMG_FACTOR;
|
+ Math.floor(s.heal_total / 50) * 10
|
||||||
totalScore += s.thorns_dmg * ScoreWeights.THORNS_DMG_FACTOR;
|
+ (s.dead_trigger_count * 8);
|
||||||
totalScore += s.crit_dmg_total * ScoreWeights.CRIT_DMG_FACTOR;
|
|
||||||
|
|
||||||
// 3. 击杀得分
|
// 4. 构建分:衡量阵容构建质量——团队配合程度。
|
||||||
totalScore += s.melee_kill_count * ScoreWeights.KILL_MELEE;
|
s.score_build = 0; // 待完善
|
||||||
totalScore += s.remote_kill_count * ScoreWeights.KILL_REMOTE;
|
|
||||||
totalScore += s.elite_kill_count * ScoreWeights.KILL_ELITE;
|
|
||||||
totalScore += s.boss_kill_count * ScoreWeights.KILL_BOSS;
|
|
||||||
|
|
||||||
// 4. 生存得分
|
// 5. 效率分:衡量资源利用——金币花得值不值。
|
||||||
totalScore += s.heal_total * ScoreWeights.HEAL_FACTOR;
|
const goldRatio = s.gold_earned > 0 ? (s.gold_spent / s.gold_earned) : 0;
|
||||||
totalScore += s.lifesteal_total * ScoreWeights.LIFESTEAL_FACTOR;
|
const refreshRatio = s.refresh_count > 0 ? (s.refresh_hit_count / s.refresh_count) : 0;
|
||||||
|
s.score_efficiency = Math.floor(goldRatio * 100) + Math.floor(refreshRatio * 50);
|
||||||
// 5. 资源得分
|
|
||||||
totalScore += s.exp_total * ScoreWeights.EXP_FACTOR;
|
|
||||||
totalScore += s.gold_total * ScoreWeights.GOLD_FACTOR;
|
|
||||||
|
|
||||||
// 取整并存储
|
// 取整并存储
|
||||||
s.score = Math.floor(totalScore);
|
s.score = Math.floor(s.score_combat + s.score_output + s.score_defense + s.score_build + s.score_efficiency);
|
||||||
mLogger.log(this.debugMode, 'VictoryComp', `[VictoryComp] 结算总分: ${s.score}`);
|
mLogger.log(this.debugMode, 'VictoryComp', `[VictoryComp] 结算总分: ${s.score}`, {
|
||||||
|
combat: s.score_combat,
|
||||||
|
output: s.score_output,
|
||||||
|
defense: s.score_defense,
|
||||||
|
build: s.score_build,
|
||||||
|
efficiency: s.score_efficiency
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染得分条与亮点标签
|
||||||
|
* 依赖各维度对应的UI节点(需要在Cocos Creator中拖入绑定)
|
||||||
|
*/
|
||||||
|
private renderScores() {
|
||||||
|
const s = smc.vmdata.scores;
|
||||||
|
|
||||||
|
// 渲染总分
|
||||||
|
if (this.total_score_label) {
|
||||||
|
this.total_score_label.string = `${s.score}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通用渲染单个维度的函数
|
||||||
|
const renderDim = (node: Node, score: number, maxScore: number) => {
|
||||||
|
if (!node) return;
|
||||||
|
const lab = node.getChildByName("score_label")?.getComponent(Label);
|
||||||
|
if (lab) lab.string = `${score}`;
|
||||||
|
|
||||||
|
const bar = node.getChildByName("progress_bar")?.getComponent(Sprite);
|
||||||
|
if (bar) {
|
||||||
|
// 根据该维度得分占“预期满分”的比例设置进度条(fillRange)
|
||||||
|
bar.fillRange = Math.min(1, Math.max(0, score / maxScore));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: 进度条的最大值可按设计期望自行调整,目前为占位预估值
|
||||||
|
renderDim(this.combat_node, s.score_combat, 3000);
|
||||||
|
renderDim(this.output_node, s.score_output, 3000);
|
||||||
|
renderDim(this.defense_node, s.score_defense, 1500);
|
||||||
|
renderDim(this.build_node, s.score_build, 1000);
|
||||||
|
renderDim(this.efficiency_node, s.score_efficiency, 200);
|
||||||
|
|
||||||
|
// 渲染成就亮点标签
|
||||||
|
this.renderHighlights();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据当前局数据匹配并生成对应的亮点标签(成就)
|
||||||
|
*/
|
||||||
|
private renderHighlights() {
|
||||||
|
if (!this.highlights_container || !this.highlight_prefab) return;
|
||||||
|
|
||||||
|
// 先清空原有的标签
|
||||||
|
this.highlights_container.removeAllChildren();
|
||||||
|
|
||||||
|
const s = smc.vmdata.scores;
|
||||||
|
const tags: { name: string, desc: string }[] = [];
|
||||||
|
|
||||||
|
// 按照 scoring-system.md 设计的触发条件判定
|
||||||
|
if (s.crt_count >= 40) tags.push({ name: "🔥 暴击大师", desc: `暴击${s.crt_count}次` });
|
||||||
|
if (s.dead_trigger_count >= 25) tags.push({ name: "💀 送死达人", desc: `死亡触发${s.dead_trigger_count}次` });
|
||||||
|
if (s.shield_block_count >= 30) tags.push({ name: "🛡️ 铁壁铜墙", desc: `格挡${s.shield_block_count}次` });
|
||||||
|
if (s.wf_count >= 20) tags.push({ name: "⚡ 风暴之王", desc: `风怒${s.wf_count}次` });
|
||||||
|
if (s.highest_dmg >= 200) tags.push({ name: "🎯 一击必杀", desc: `单次伤害${Math.floor(s.highest_dmg)}` });
|
||||||
|
if (s.heal_total >= 500) tags.push({ name: "💊 治愈之光", desc: `治疗总量${Math.floor(s.heal_total)}` });
|
||||||
|
|
||||||
|
// 🏆 完美通关:通关且20回合全员存活
|
||||||
|
if (s.passed_wave_20 && s.wave_all_alive_count >= 20) {
|
||||||
|
tags.push({ name: "🏆 完美通关", desc: "20回合全胜" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 效率类判定
|
||||||
|
const refreshRatio = s.refresh_count > 0 ? (s.refresh_hit_count / s.refresh_count) : 0;
|
||||||
|
if (refreshRatio >= 0.8) {
|
||||||
|
tags.push({ name: "🎲 欧皇附体", desc: `刷新命中率${Math.floor(refreshRatio * 100)}%` });
|
||||||
|
}
|
||||||
|
|
||||||
|
const goldRatio = s.gold_earned > 0 ? (s.gold_spent / s.gold_earned) : 0;
|
||||||
|
if (goldRatio >= 0.95) {
|
||||||
|
tags.push({ name: "💰 节俭玩家", desc: `金币使用率${Math.floor(goldRatio * 100)}%` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最多显示前3个亮点
|
||||||
|
const displayTags = tags.slice(0, 3);
|
||||||
|
displayTags.forEach(tag => {
|
||||||
|
const tagNode = instantiate(this.highlight_prefab);
|
||||||
|
// 尝试获取自身或者名为 "label" 的子节点上的 Label 组件
|
||||||
|
const lab = tagNode.getComponent(Label) || tagNode.getChildByName("label")?.getComponent(Label);
|
||||||
|
if (lab) {
|
||||||
|
lab.string = `${tag.name} (${tag.desc})`;
|
||||||
|
}
|
||||||
|
this.highlights_container.addChild(tagNode);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 操作入口 ========================
|
// ======================== 操作入口 ========================
|
||||||
|
|||||||
Reference in New Issue
Block a user