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, // 治疗总量
|
||||
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, // 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,
|
||||
|
||||
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; // 治疗总量
|
||||
lifesteal_total: number;// 吸血总量
|
||||
shield_block_count: number; // 格挡次数
|
||||
dead_trigger_count: number; // 死亡触发次数
|
||||
|
||||
// 资源统计
|
||||
exp_total: number; // 经验总数
|
||||
gold_total: number; // 金币总数
|
||||
gold_earned: number; // 总收入金币
|
||||
gold_spent: number; // 消耗金币
|
||||
refresh_count: number; // 刷新次数
|
||||
refresh_hit_count: number; // 刷新命中次数(刷新后选中卡)
|
||||
|
||||
// 击杀统计
|
||||
melee_kill_count: number; // 近战怪击杀数量
|
||||
remote_kill_count: number; // 远程怪击杀数量
|
||||
elite_kill_count: number; // 精英怪击杀数量
|
||||
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) {
|
||||
damage = Math.floor(damage * (1 + FightSet.CRIT_DAMAGE / 100));
|
||||
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)
|
||||
// 护盾吸收
|
||||
const shieldResult = this.absorbShield(TAttrsComp, damage);
|
||||
damage = shieldResult.remainingDamage;
|
||||
if (shieldResult.absorbedDamage > 0) {
|
||||
// 【评分系统 - 防御分】统计护盾成功抵挡伤害的次数
|
||||
smc.vmdata.scores.shield_block_count += shieldResult.absorbedDamage;
|
||||
}
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after shield",damage)
|
||||
// 显示护盾吸收飘字
|
||||
if (shieldResult.absorbedDamage > 0 && targetView) {
|
||||
@@ -160,6 +168,14 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
||||
// TAttrsComp.hp -= damage; // 应用伤害到数据层
|
||||
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 技能
|
||||
TAttrsComp.atked_count++;
|
||||
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
||||
@@ -301,6 +317,10 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
|
||||
|
||||
for (let i = 0; i < triggerCount; i++) {
|
||||
TAttrsComp.dead.forEach((uuid: number) => {
|
||||
if (TAttrsComp.fac === FacSet.HERO) {
|
||||
// 【评分系统 - 防御分】统计死亡触发技能的生效次数
|
||||
smc.vmdata.scores.dead_trigger_count++;
|
||||
}
|
||||
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
|
||||
s_uuid: uuid,
|
||||
heroAttrs: TAttrsComp,
|
||||
|
||||
@@ -429,6 +429,10 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
const addHp = Math.floor(sAp*_cAttrsComp.ap/100);
|
||||
model.add_hp(addHp);
|
||||
target.health(addHp);
|
||||
if (_cAttrsComp.fac === FacSet.HERO) {
|
||||
// 【评分系统 - 防御分】统计团队造成的总治疗量
|
||||
smc.vmdata.scores.heal_total += addHp;
|
||||
}
|
||||
} else if (kind === SkillKind.Shield && sAp !== 0) {
|
||||
const addShield = Math.max(0, Math.floor(sAp));
|
||||
model.add_shield(addShield);
|
||||
|
||||
@@ -331,6 +331,10 @@ export class CardComp extends CCComp {
|
||||
}
|
||||
// 扣除金币
|
||||
this.setMissionCoin(currentCoin - cardCost);
|
||||
// 【评分系统 - 效率分】记录购卡消耗的金币,以及刷新后的选中卡次数(命中率分子)
|
||||
smc.vmdata.scores.gold_spent += cardCost;
|
||||
smc.vmdata.scores.refresh_hit_count++;
|
||||
|
||||
oops.message.dispatchEvent(GameEvent.CoinAdd, {
|
||||
syncOnly: true,
|
||||
delta: -cardCost
|
||||
|
||||
@@ -298,6 +298,9 @@ export class MissionCardComp extends CCComp {
|
||||
const v = typeof payload === 'number' ? payload : (payload?.delta ?? payload?.value ?? 0);
|
||||
if (v === 0) return;
|
||||
this.setMissionCoin(this.getMissionCoin() + v);
|
||||
// 【评分系统 - 效率分】精确统计整局游戏的总收入与额外支出(如卖卡、技能扣费等)
|
||||
if (v > 0) smc.vmdata.scores.gold_earned += v;
|
||||
else smc.vmdata.scores.gold_spent -= v;
|
||||
this.updateCoinAndCostUI();
|
||||
this.playCoinChangeAnim(v > 0);
|
||||
}
|
||||
@@ -554,6 +557,9 @@ export class MissionCardComp extends CCComp {
|
||||
return;
|
||||
}
|
||||
this.setMissionCoin(currentCoin - cost);
|
||||
// 【评分系统 - 效率分】记录因刷新卡池消耗的金币,以及刷新次数
|
||||
smc.vmdata.scores.gold_spent += cost;
|
||||
smc.vmdata.scores.refresh_count++;
|
||||
this.playCoinChangeAnim(false);
|
||||
this.updateCoinAndCostUI();
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "click draw", {
|
||||
|
||||
@@ -445,6 +445,33 @@ export class MissionComp extends CCComp {
|
||||
smc.mission.in_fight = false;
|
||||
smc.vmdata.mission_data.in_fight = false;
|
||||
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)
|
||||
this.triggerHeroBattleSkills(false);
|
||||
|
||||
@@ -682,7 +709,13 @@ export class MissionComp extends CCComp {
|
||||
this.heapTrendBaseMB = -1;
|
||||
this.monsterCountSyncTimer = 0;
|
||||
this.lastPrepareCoinWave = 0;
|
||||
|
||||
// 重置所有的战局得分数据,防止上一局的数据污染
|
||||
smc.resetScores();
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: reward, syncOnly: true });
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* - ScoreWeights(ScoreSet)—— 得分权重配置
|
||||
* - 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 { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
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 { FacSet } from "../common/config/GameSet";
|
||||
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 { mLogger } from "../common/Logger";
|
||||
|
||||
@@ -43,6 +45,35 @@ const { ccclass, property } = _decorator;
|
||||
@ccclass('VictoryComp')
|
||||
@ecs.register('Victory', false)
|
||||
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;
|
||||
/** 奖励等级(预留) */
|
||||
@@ -88,6 +119,234 @@ export class VictoryComp extends CCComp {
|
||||
|
||||
// 计算总分
|
||||
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() {
|
||||
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. 战斗行为分
|
||||
totalScore += s.crt_count * ScoreWeights.CRT_KILL;
|
||||
totalScore += s.wf_count * ScoreWeights.WF_TRIGGER;
|
||||
totalScore += s.dod_count * ScoreWeights.DODGE_SUCCESS;
|
||||
totalScore += s.back_count * ScoreWeights.BACK_SUCCESS;
|
||||
totalScore += s.stun_count * ScoreWeights.STUN_SUCCESS;
|
||||
totalScore += s.freeze_count * ScoreWeights.FREEZE_SUCCESS;
|
||||
// 2. 输出分:衡量伤害能力——团队火力如何。
|
||||
s.score_output = Math.floor(s.total_dmg / 100) * 10
|
||||
+ (s.crt_count * 5)
|
||||
+ (s.wf_count * 5)
|
||||
+ (s.highest_dmg * 2);
|
||||
|
||||
// 2. 伤害转化分
|
||||
totalScore += s.total_dmg * ScoreWeights.DMG_FACTOR;
|
||||
totalScore += s.avg_dmg * ScoreWeights.AVG_DMG_FACTOR;
|
||||
totalScore += s.thorns_dmg * ScoreWeights.THORNS_DMG_FACTOR;
|
||||
totalScore += s.crit_dmg_total * ScoreWeights.CRIT_DMG_FACTOR;
|
||||
// 3. 防御分:衡量生存能力——团队多能扛。
|
||||
s.score_defense = (s.shield_block_count * 5)
|
||||
+ Math.floor(s.heal_total / 50) * 10
|
||||
+ (s.dead_trigger_count * 8);
|
||||
|
||||
// 3. 击杀得分
|
||||
totalScore += s.melee_kill_count * ScoreWeights.KILL_MELEE;
|
||||
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. 构建分:衡量阵容构建质量——团队配合程度。
|
||||
s.score_build = 0; // 待完善
|
||||
|
||||
// 4. 生存得分
|
||||
totalScore += s.heal_total * ScoreWeights.HEAL_FACTOR;
|
||||
totalScore += s.lifesteal_total * ScoreWeights.LIFESTEAL_FACTOR;
|
||||
|
||||
// 5. 资源得分
|
||||
totalScore += s.exp_total * ScoreWeights.EXP_FACTOR;
|
||||
totalScore += s.gold_total * ScoreWeights.GOLD_FACTOR;
|
||||
// 5. 效率分:衡量资源利用——金币花得值不值。
|
||||
const goldRatio = s.gold_earned > 0 ? (s.gold_spent / s.gold_earned) : 0;
|
||||
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);
|
||||
|
||||
// 取整并存储
|
||||
s.score = Math.floor(totalScore);
|
||||
mLogger.log(this.debugMode, 'VictoryComp', `[VictoryComp] 结算总分: ${s.score}`);
|
||||
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}`, {
|
||||
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