feat(score): 新增亮点成就系统并集成至结算评分
- 添加亮点成就配置文件,定义九类成就及其等级阈值、奖励分数和称号 - 在游戏得分统计数据结构中增加已达成亮点记录字段 - 实现亮点成就判定逻辑,根据玩家表现计算达成的最高等级 - 将亮点成就奖励分数计入总分计算,并在结算界面展示前三个亮点 - 新增动画资源用于界面表现
This commit is contained in:
258
assets/resources/gui/anim/bs4.anim
Normal file
258
assets/resources/gui/anim/bs4.anim
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"__type__": "cc.AnimationClip",
|
||||||
|
"_name": "bs4",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"embeddedPlayerGroups": []
|
||||||
|
},
|
||||||
|
"_native": "",
|
||||||
|
"sample": 15,
|
||||||
|
"speed": 1,
|
||||||
|
"wrapMode": 2,
|
||||||
|
"enableTrsBlending": false,
|
||||||
|
"_duration": 0.6666666666666666,
|
||||||
|
"_hash": 500763545,
|
||||||
|
"_tracks": [
|
||||||
|
{
|
||||||
|
"__id__": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_exoticAnimation": null,
|
||||||
|
"_events": [],
|
||||||
|
"_embeddedPlayers": [],
|
||||||
|
"_additiveSettings": {
|
||||||
|
"__id__": 11
|
||||||
|
},
|
||||||
|
"_auxiliaryCurveEntries": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.VectorTrack",
|
||||||
|
"_binding": {
|
||||||
|
"__type__": "cc.animation.TrackBinding",
|
||||||
|
"path": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"proxy": null
|
||||||
|
},
|
||||||
|
"_channels": [
|
||||||
|
{
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_nComponents": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.TrackPath",
|
||||||
|
"_paths": [
|
||||||
|
"scale"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.Channel",
|
||||||
|
"_curve": {
|
||||||
|
"__id__": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealCurve",
|
||||||
|
"_times": [
|
||||||
|
0,
|
||||||
|
0.3333333333333333,
|
||||||
|
0.6666666666666666
|
||||||
|
],
|
||||||
|
"_values": [
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1.1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preExtrapolation": 1,
|
||||||
|
"postExtrapolation": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.Channel",
|
||||||
|
"_curve": {
|
||||||
|
"__id__": 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealCurve",
|
||||||
|
"_times": [
|
||||||
|
0,
|
||||||
|
0.3333333333333333,
|
||||||
|
0.6666666666666666
|
||||||
|
],
|
||||||
|
"_values": [
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1.1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preExtrapolation": 1,
|
||||||
|
"postExtrapolation": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.Channel",
|
||||||
|
"_curve": {
|
||||||
|
"__id__": 8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealCurve",
|
||||||
|
"_times": [
|
||||||
|
0,
|
||||||
|
0.3333333333333333,
|
||||||
|
0.6666666666666666
|
||||||
|
],
|
||||||
|
"_values": [
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 1,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 1,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 1,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": {
|
||||||
|
"tangentMode": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preExtrapolation": 1,
|
||||||
|
"postExtrapolation": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.Channel",
|
||||||
|
"_curve": {
|
||||||
|
"__id__": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealCurve",
|
||||||
|
"_times": [],
|
||||||
|
"_values": [],
|
||||||
|
"preExtrapolation": 1,
|
||||||
|
"postExtrapolation": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||||
|
"enabled": false,
|
||||||
|
"refClip": null
|
||||||
|
}
|
||||||
|
]
|
||||||
13
assets/resources/gui/anim/bs4.anim.meta
Normal file
13
assets/resources/gui/anim/bs4.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.3",
|
||||||
|
"importer": "animation-clip",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "6bb4dd88-c4ab-4519-8295-a64ad4c6f121",
|
||||||
|
"files": [
|
||||||
|
".cconb"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"name": "bs4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -121,6 +121,8 @@ export class SingletonModuleComp extends ecs.Comp {
|
|||||||
score_defense: 0,
|
score_defense: 0,
|
||||||
score_build: 0,
|
score_build: 0,
|
||||||
score_efficiency: 0,
|
score_efficiency: 0,
|
||||||
|
|
||||||
|
achieved_highlights: [],
|
||||||
} as GameScoreStats,
|
} as GameScoreStats,
|
||||||
|
|
||||||
gold: 0, // 金币数据(MVVM绑定字段)
|
gold: 0, // 金币数据(MVVM绑定字段)
|
||||||
@@ -181,6 +183,7 @@ export class SingletonModuleComp extends ecs.Comp {
|
|||||||
score_defense: 0,
|
score_defense: 0,
|
||||||
score_build: 0,
|
score_build: 0,
|
||||||
score_efficiency: 0,
|
score_efficiency: 0,
|
||||||
|
achieved_highlights: [],
|
||||||
} as GameScoreStats;
|
} as GameScoreStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ export interface GameScoreStats {
|
|||||||
score_defense: number;
|
score_defense: number;
|
||||||
score_build: number;
|
score_build: number;
|
||||||
score_efficiency: number;
|
score_efficiency: number;
|
||||||
|
|
||||||
|
// 已达成的亮点成就记录
|
||||||
|
achieved_highlights: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
131
assets/script/game/common/config/HighlightSet.ts
Normal file
131
assets/script/game/common/config/HighlightSet.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* @file HighlightSet.ts
|
||||||
|
* @description 亮点成就配置文件
|
||||||
|
*
|
||||||
|
* 定义所有亮点成就的等级、触发条件、加分以及称号描述。
|
||||||
|
* 每个亮点都包含5个递进等级,奖励分随等级增加。
|
||||||
|
*/
|
||||||
|
|
||||||
|
export enum HighlightType {
|
||||||
|
CritMaster = "CritMaster", // 暴击大师
|
||||||
|
DeathExpert = "DeathExpert", // 送死达人
|
||||||
|
IronWall = "IronWall", // 铁壁铜墙
|
||||||
|
WindStorm = "WindStorm", // 风暴之王
|
||||||
|
OneHitKill = "OneHitKill", // 一击必杀
|
||||||
|
HealingLight = "HealingLight", // 治愈之光
|
||||||
|
PerfectClear = "PerfectClear", // 完美通关 (特殊,只有一档或布尔判定)
|
||||||
|
LuckyKing = "LuckyKing", // 欧皇附体
|
||||||
|
ThriftyPlayer = "ThriftyPlayer" // 节俭玩家
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HighlightLevel {
|
||||||
|
level: number; // 成就等级 (1-5)
|
||||||
|
threshold: number; // 达成阈值 (如:暴击40次)
|
||||||
|
scoreBonus: number; // 额外加分
|
||||||
|
title: string; // 成就称号
|
||||||
|
desc: string; // 成就描述模板
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HighlightConfig {
|
||||||
|
type: HighlightType;
|
||||||
|
icon: string; // 显示前缀icon,如 "🔥"
|
||||||
|
levels: HighlightLevel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HighlightSet: Record<HighlightType, HighlightConfig> = {
|
||||||
|
[HighlightType.CritMaster]: {
|
||||||
|
type: HighlightType.CritMaster,
|
||||||
|
icon: "🔥",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 20, scoreBonus: 50, title: "初级暴击者", desc: "暴击{0}次" },
|
||||||
|
{ level: 2, threshold: 40, scoreBonus: 100, title: "暴击大师", desc: "暴击{0}次" },
|
||||||
|
{ level: 3, threshold: 60, scoreBonus: 150, title: "致命猎手", desc: "暴击{0}次" },
|
||||||
|
{ level: 4, threshold: 80, scoreBonus: 200, title: "无情处决", desc: "暴击{0}次" },
|
||||||
|
{ level: 5, threshold: 100, scoreBonus: 300, title: "刀刀烈火", desc: "暴击{0}次" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[HighlightType.DeathExpert]: {
|
||||||
|
type: HighlightType.DeathExpert,
|
||||||
|
icon: "💀",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 15, scoreBonus: 50, title: "不怕死", desc: "死亡触发{0}次" },
|
||||||
|
{ level: 2, threshold: 25, scoreBonus: 100, title: "送死达人", desc: "死亡触发{0}次" },
|
||||||
|
{ level: 3, threshold: 40, scoreBonus: 150, title: "亡灵舞者", desc: "死亡触发{0}次" },
|
||||||
|
{ level: 4, threshold: 60, scoreBonus: 200, title: "向死而生", desc: "死亡触发{0}次" },
|
||||||
|
{ level: 5, threshold: 80, scoreBonus: 300, title: "不死灾厄", desc: "死亡触发{0}次" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[HighlightType.IronWall]: {
|
||||||
|
type: HighlightType.IronWall,
|
||||||
|
icon: "🛡️",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 15, scoreBonus: 50, title: "坚固盾牌", desc: "格挡{0}次" },
|
||||||
|
{ level: 2, threshold: 30, scoreBonus: 100, title: "铁壁铜墙", desc: "格挡{0}次" },
|
||||||
|
{ level: 3, threshold: 50, scoreBonus: 150, title: "叹息之墙", desc: "格挡{0}次" },
|
||||||
|
{ level: 4, threshold: 70, scoreBonus: 200, title: "不破之阵", desc: "格挡{0}次" },
|
||||||
|
{ level: 5, threshold: 100, scoreBonus: 300, title: "绝对防御", desc: "格挡{0}次" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[HighlightType.WindStorm]: {
|
||||||
|
type: HighlightType.WindStorm,
|
||||||
|
icon: "⚡",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 10, scoreBonus: 50, title: "迅捷之风", desc: "风怒{0}次" },
|
||||||
|
{ level: 2, threshold: 20, scoreBonus: 100, title: "风暴之王", desc: "风怒{0}次" },
|
||||||
|
{ level: 3, threshold: 35, scoreBonus: 150, title: "狂风骤雨", desc: "风怒{0}次" },
|
||||||
|
{ level: 4, threshold: 50, scoreBonus: 200, title: "无影之手", desc: "风怒{0}次" },
|
||||||
|
{ level: 5, threshold: 70, scoreBonus: 300, title: "神速幻影", desc: "风怒{0}次" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[HighlightType.OneHitKill]: {
|
||||||
|
type: HighlightType.OneHitKill,
|
||||||
|
icon: "🎯",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 100, scoreBonus: 50, title: "重击", desc: "单次伤害{0}" },
|
||||||
|
{ level: 2, threshold: 200, scoreBonus: 100, title: "一击必杀", desc: "单次伤害{0}" },
|
||||||
|
{ level: 3, threshold: 400, scoreBonus: 150, title: "毁天灭地", desc: "单次伤害{0}" },
|
||||||
|
{ level: 4, threshold: 800, scoreBonus: 200, title: "核弹打击", desc: "单次伤害{0}" },
|
||||||
|
{ level: 5, threshold: 1500, scoreBonus: 300, title: "弑神一击", desc: "单次伤害{0}" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[HighlightType.HealingLight]: {
|
||||||
|
type: HighlightType.HealingLight,
|
||||||
|
icon: "💊",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 200, scoreBonus: 50, title: "急救员", desc: "治疗总量{0}" },
|
||||||
|
{ level: 2, threshold: 500, scoreBonus: 100, title: "治愈之光", desc: "治疗总量{0}" },
|
||||||
|
{ level: 3, threshold: 1000, scoreBonus: 150, title: "生命之泉", desc: "治疗总量{0}" },
|
||||||
|
{ level: 4, threshold: 2000, scoreBonus: 200, title: "起死回生", desc: "治疗总量{0}" },
|
||||||
|
{ level: 5, threshold: 4000, scoreBonus: 300, title: "移动泉水", desc: "治疗总量{0}" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[HighlightType.PerfectClear]: {
|
||||||
|
type: HighlightType.PerfectClear,
|
||||||
|
icon: "🏆",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 1, scoreBonus: 500, title: "完美通关", desc: "20回合全胜且全存活" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[HighlightType.LuckyKing]: {
|
||||||
|
type: HighlightType.LuckyKing,
|
||||||
|
icon: "🎲",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 0.6, scoreBonus: 50, title: "手气不错", desc: "刷新命中率{0}%" },
|
||||||
|
{ level: 2, threshold: 0.7, scoreBonus: 100, title: "心想事成", desc: "刷新命中率{0}%" },
|
||||||
|
{ level: 3, threshold: 0.8, scoreBonus: 150, title: "欧皇附体", desc: "刷新命中率{0}%" },
|
||||||
|
{ level: 4, threshold: 0.9, scoreBonus: 200, title: "天选之子", desc: "刷新命中率{0}%" },
|
||||||
|
{ level: 5, threshold: 1.0, scoreBonus: 300, title: "言出法随", desc: "刷新命中率{0}%" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
[HighlightType.ThriftyPlayer]: {
|
||||||
|
type: HighlightType.ThriftyPlayer,
|
||||||
|
icon: "💰",
|
||||||
|
levels: [
|
||||||
|
{ level: 1, threshold: 0.75, scoreBonus: 50, title: "精打细算", desc: "金币使用率{0}%" },
|
||||||
|
{ level: 2, threshold: 0.85, scoreBonus: 100, title: "勤俭持家", desc: "金币使用率{0}%" },
|
||||||
|
{ level: 3, threshold: 0.95, scoreBonus: 150, title: "节俭玩家", desc: "金币使用率{0}%" },
|
||||||
|
{ level: 4, threshold: 0.98, scoreBonus: 200, title: "一毛不拔", desc: "金币使用率{0}%" },
|
||||||
|
{ level: 5, threshold: 1.00, scoreBonus: 300, title: "理财大师", desc: "金币使用率{0}%" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
9
assets/script/game/common/config/HighlightSet.ts.meta
Normal file
9
assets/script/game/common/config/HighlightSet.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "19430536-a842-4669-a112-653747ab59c1",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import { Attrs } from "../common/config/HeroAttrs";
|
|||||||
import { HeroInfo } from "../common/config/heroSet";
|
import { HeroInfo } from "../common/config/heroSet";
|
||||||
import { CKind } from "../common/config/CardSet";
|
import { CKind } from "../common/config/CardSet";
|
||||||
import { ScoreWeights } from "../common/config/ScoreSet";
|
import { ScoreWeights } from "../common/config/ScoreSet";
|
||||||
|
import { HighlightSet, HighlightType, HighlightLevel } from "../common/config/HighlightSet";
|
||||||
import { mLogger } from "../common/Logger";
|
import { mLogger } from "../common/Logger";
|
||||||
|
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
@@ -349,17 +350,60 @@ export class VictoryComp extends CCComp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 得分计算 ========================
|
/**
|
||||||
|
* 获取满足条件的最高等级的亮点成就
|
||||||
|
* @param type 亮点类型
|
||||||
|
* @param value 玩家实际达成的值
|
||||||
|
* @returns 达成的最高等级配置,未达成返回 null
|
||||||
|
*/
|
||||||
|
private getHighestHighlightLevel(type: HighlightType, value: number): HighlightLevel | null {
|
||||||
|
const config = HighlightSet[type];
|
||||||
|
if (!config || !config.levels) return null;
|
||||||
|
|
||||||
|
let highest: HighlightLevel | null = null;
|
||||||
|
for (const levelConfig of config.levels) {
|
||||||
|
if (value >= levelConfig.threshold) {
|
||||||
|
highest = levelConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return highest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算并获取所有达成的最高亮点配置数组,用于加分和UI展示
|
||||||
|
*/
|
||||||
|
private getAchievedHighlights(s: any): { type: HighlightType, config: HighlightLevel, value: number }[] {
|
||||||
|
const achieved: { type: HighlightType, config: HighlightLevel, value: number }[] = [];
|
||||||
|
|
||||||
|
// 计算辅助比例
|
||||||
|
const refreshRatio = s.refresh_count > 0 ? (s.refresh_hit_count / s.refresh_count) : 0;
|
||||||
|
const goldRatio = s.gold_earned > 0 ? (s.gold_spent / s.gold_earned) : 0;
|
||||||
|
|
||||||
|
// 判定表:每个维度对应的值
|
||||||
|
const checkList: { type: HighlightType, value: number }[] = [
|
||||||
|
{ type: HighlightType.CritMaster, value: s.crt_count },
|
||||||
|
{ type: HighlightType.DeathExpert, value: s.dead_trigger_count },
|
||||||
|
{ type: HighlightType.IronWall, value: s.shield_block_count },
|
||||||
|
{ type: HighlightType.WindStorm, value: s.wf_count },
|
||||||
|
{ type: HighlightType.OneHitKill, value: Math.floor(s.highest_dmg) },
|
||||||
|
{ type: HighlightType.HealingLight, value: Math.floor(s.heal_total) },
|
||||||
|
{ type: HighlightType.PerfectClear, value: (s.passed_wave_20 && s.wave_all_alive_count >= 20) ? 1 : 0 },
|
||||||
|
{ type: HighlightType.LuckyKing, value: refreshRatio },
|
||||||
|
{ type: HighlightType.ThriftyPlayer, value: goldRatio }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const item of checkList) {
|
||||||
|
const levelConfig = this.getHighestHighlightLevel(item.type, item.value);
|
||||||
|
if (levelConfig) {
|
||||||
|
achieved.push({ type: item.type, config: levelConfig, value: item.value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return achieved;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算单局总分并更新到 smc.vmdata.scores.score。
|
* 计算单局总分并更新到 smc.vmdata.scores.score。
|
||||||
*
|
|
||||||
* 得分维度:
|
|
||||||
* 1. 战斗行为分 —— 暴击次数、连击触发、闪避、格挡、眩晕、冻结
|
|
||||||
* 2. 伤害转化分 —— 总伤害、平均伤害、反伤、暴击伤害
|
|
||||||
* 3. 击杀得分 —— 近战击杀、远程击杀、精英击杀、Boss 击杀
|
|
||||||
* 4. 生存得分 —— 治疗量、吸血量
|
|
||||||
* 5. 资源得分 —— 经验获取、金币获取
|
|
||||||
*/
|
*/
|
||||||
private calculateTotalScore() {
|
private calculateTotalScore() {
|
||||||
const s = smc.vmdata.scores;
|
const s = smc.vmdata.scores;
|
||||||
@@ -389,14 +433,25 @@ export class VictoryComp extends CCComp {
|
|||||||
const refreshRatio = s.refresh_count > 0 ? (s.refresh_hit_count / s.refresh_count) : 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_efficiency = Math.floor(goldRatio * 100) + Math.floor(refreshRatio * 50);
|
||||||
|
|
||||||
|
// 6. 亮点成就额外加分 (按等级叠加)
|
||||||
|
const achieved = this.getAchievedHighlights(s);
|
||||||
|
s.achieved_highlights = achieved; // 记录已达成的亮点信息
|
||||||
|
|
||||||
|
let highlightBonus = 0;
|
||||||
|
for (const item of achieved) {
|
||||||
|
highlightBonus += item.config.scoreBonus;
|
||||||
|
}
|
||||||
|
|
||||||
// 取整并存储
|
// 取整并存储
|
||||||
s.score = Math.floor(s.score_combat + s.score_output + s.score_defense + s.score_build + s.score_efficiency);
|
s.score = Math.floor(s.score_combat + s.score_output + s.score_defense + s.score_build + s.score_efficiency + highlightBonus);
|
||||||
mLogger.log(this.debugMode, 'VictoryComp', `[VictoryComp] 结算总分: ${s.score}`, {
|
mLogger.log(this.debugMode, 'VictoryComp', `[VictoryComp] 结算总分: ${s.score}`, {
|
||||||
combat: s.score_combat,
|
combat: s.score_combat,
|
||||||
output: s.score_output,
|
output: s.score_output,
|
||||||
defense: s.score_defense,
|
defense: s.score_defense,
|
||||||
build: s.score_build,
|
build: s.score_build,
|
||||||
efficiency: s.score_efficiency
|
efficiency: s.score_efficiency,
|
||||||
|
highlightBonus: highlightBonus,
|
||||||
|
achievedHighlights: achieved
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,40 +501,26 @@ export class VictoryComp extends CCComp {
|
|||||||
this.highlights_container.removeAllChildren();
|
this.highlights_container.removeAllChildren();
|
||||||
|
|
||||||
const s = smc.vmdata.scores;
|
const s = smc.vmdata.scores;
|
||||||
const tags: { name: string, desc: string }[] = [];
|
// 获取所有已达成的亮点(包含对应等级的信息)
|
||||||
|
const achievedList = s.achieved_highlights || [];
|
||||||
|
|
||||||
// 按照 scoring-system.md 设计的触发条件判定
|
// 最多显示前3个亮点(如有优先级需求,可在截取前对 achievedList 进行排序)
|
||||||
if (s.crt_count >= 40) tags.push({ name: "🔥 暴击大师", desc: `暴击${s.crt_count}次` });
|
const displayTags = achievedList.slice(0, 3);
|
||||||
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回合全员存活
|
displayTags.forEach(item => {
|
||||||
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);
|
const tagNode = instantiate(this.highlight_prefab);
|
||||||
// 尝试获取自身或者名为 "label" 的子节点上的 Label 组件
|
|
||||||
const lab = tagNode.getComponent(Label) || tagNode.getChildByName("label")?.getComponent(Label);
|
const lab = tagNode.getComponent(Label) || tagNode.getChildByName("label")?.getComponent(Label);
|
||||||
|
|
||||||
if (lab) {
|
if (lab) {
|
||||||
lab.string = `${tag.name} (${tag.desc})`;
|
// 获取主配置和等级配置
|
||||||
|
const typeConfig = HighlightSet[item.type];
|
||||||
|
const levelConfig = item.config;
|
||||||
|
|
||||||
|
// 格式化描述(替换占位符 {0} 为实际所需达成的阈值或当前值)
|
||||||
|
let descStr = levelConfig.desc.replace("{0}", levelConfig.threshold.toString());
|
||||||
|
|
||||||
|
// 显示:[图标] 称号 (描述)
|
||||||
|
lab.string = `${typeConfig.icon} ${levelConfig.title} (${descStr})`;
|
||||||
}
|
}
|
||||||
this.highlights_container.addChild(tagNode);
|
this.highlights_container.addChild(tagNode);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user