"""战绩加工:最佳武器/载具、载具摧毁数、六维雷达数据与称号评级。""" from functools import reduce from nonebot import logger from ..utils.params import SPECIALIST_NAMES, interval_table, round_data async def get_best_weapon_and_best_vehicle(player_stat): weapon = sorted(player_stat["weapons"], key=lambda k: k["kills"], reverse=True)[0] vehicle = sorted(player_stat["vehicles"], key=lambda k: k["kills"], reverse=True)[0] return weapon, vehicle async def get_best_classes(player_stat): classes_data = player_stat["classes"] data = [obj for obj in classes_data if obj.get("className") != "All"] best_classes = max(data, key=lambda x: x["kills"]) best_classes_name = best_classes["className"] return best_classes_name async def get_vehicle_destroyed(player_stat): vehicle = reduce(lambda acc, x: acc + x["destroyed"], player_stat, 0) return vehicle async def stats_calculator(player_stat, game): per500_data = round_data[game] # 场次(无直接数值反馈,采用胜利数+失败数) match_played = player_stat["wins"] + player_stat["loses"] kd = player_stat["killDeath"] kpm = player_stat["killsPerMinute"] kill_assists = player_stat["killAssists"] if game == "bf3": headshots = float(0) else: headshots = float(player_stat["headshots"].replace("%", "")) spm = player_stat["scorePerMinute"] revives = player_stat["revives"] apr = kill_assists / match_played rpr = revives / match_played # 计算比值 # kd ratio_kd = round(kd / per500_data["kd"], 2) # kpm ratio_kpm = round(kpm / per500_data["kpm"], 2) # 爆头率 ratio_hs = round(headshots / 100 / per500_data["hs"], 2) # 场均救援 ratio_rpr = round(rpr / per500_data["rpr"], 2) # 场均助攻 ratio_apr = round(apr / per500_data["apr"], 2) # 分钟得分 ratio_spm = round(spm / per500_data["spm"], 2) logger.info( "雷达图数据" f"\n生涯KD比值:{ratio_kd}\n生涯Kpm比值:{ratio_kpm}\n生涯准度比值:{ratio_hs}" f"\n场均救援比值:{ratio_rpr}\n场均助攻比值:{ratio_apr}\n分钟得分比值:{ratio_spm}" ) stat_data = [ratio_kd, ratio_apr, ratio_hs, ratio_spm, ratio_kpm, ratio_rpr] # 血腥度 kd*0.5 + kpm *0.4 +hs*0.1 player_blood = kd * 0.5 + kpm * 0.4 + headshots * 0.1 ratio_blood = player_blood / per500_data["blood"] # 贡献度 场均助攻*0.4 + 场均救援*0.4 + spm*0.2 player_dedication = rpr * 0.4 + apr * 0.4 + spm * 0.2 ratio_dedication = player_dedication / per500_data["dedication"] # 全能度 血腥度*0.5 + 贡献度*0.5 player_all_round = player_blood * 0.5 + player_dedication * 0.5 ratio_all_round = player_all_round / per500_data["all_round"] logger.info( "称号评级数据" f"\n血腥度:{player_blood} 比值:{ratio_blood}" f"\n贡献度:{player_dedication} 比值:{ratio_dedication}" f"\n全能度:{player_all_round} 比值:{ratio_all_round}" ) level_designation = get_level_designation( ratio_blood, ratio_dedication, ratio_all_round ) return stat_data, level_designation def get_level_designation(blood, dedication, all_round): feature_type = "" final_max = max([blood, dedication, all_round]) if final_max == blood: feature_type = "blood" elif final_max == dedication: feature_type = "dedication" elif final_max == all_round: feature_type = "all_round" logger.info(f"最高能力:{feature_type}") logger.info(f"最高能力值:{final_max}") final_info = get_feature(final_max, feature_type) return final_info def get_feature(value, feature_type): """根据输入值和数值类别,返回对应特征值""" for (low, high), features in interval_table.items(): if low <= value < high: return features[feature_type] raise ValueError(f"数值 {value} 不在任何区间内") # --------------------------------------------------------------------------- # # BF2042 生涯卡数据(旧 project 卡片槽位 → tracker.gg 数据映射) # --------------------------------------------------------------------------- # _PLACEHOLDER = "–" def _seg_stat(seg: dict, *keys): """从段 stats 取第一个命中的显示值(displayValue 优先)。""" stats = (seg or {}).get("stats") or {} for key in keys: entry = stats.get(key) if isinstance(entry, dict): dv = entry.get("displayValue") if dv is not None: return dv v = entry.get("value") if v is not None: return v elif entry is not None: return entry return _PLACEHOLDER def _seg_value(seg: dict, *keys) -> float: stats = (seg or {}).get("stats") or {} for key in keys: entry = stats.get(key) if isinstance(entry, dict) and entry.get("value") is not None: return float(entry["value"]) return 0.0 def _fmt_minutes_hours(minutes: float) -> str: """tracker timePlayed(分钟) → "X.XX" 小时字符串(旧卡习惯)。""" try: return f"{float(minutes) / 60:.2f}" except (TypeError, ValueError): return _PLACEHOLDER def _s_level(level) -> str: """等级 S 前缀:tracker 的 298 → 'S199'(与旧 gametools 一致)。""" try: lv = int(level) except (TypeError, ValueError): return str(level) return f"S{lv - 99}" if lv - 99 > 0 else str(lv) def _top_rows(segments: list[dict], top: int) -> list[dict]: """武器/载具段 → 卡片 TOP 行(按击杀降序)。""" ranked = sorted(segments or [], key=lambda s: _seg_value(s, "kills"), reverse=True) rows = [] for seg in ranked[:top]: meta = seg.get("metadata") or {} rows.append( { "名称": (meta.get("name") or "").strip() or _PLACEHOLDER, "类别": meta.get("category") or "", "击杀": _seg_stat(seg, "kills"), "爆头率": _seg_stat(seg, "headshotPercentage"), "命中率": _seg_stat(seg, "shotsAccuracy"), "KPM": _seg_stat(seg, "killsPerMinute"), "时长": _fmt_minutes_hours(_seg_value(seg, "timePlayed")), "摧毁": _seg_stat(seg, "destroyedWith"), # 原始值给排序/调试用 "_kills": _seg_value(seg, "kills"), } ) return rows def build_bf2042_career_card( profile: dict, weapon_segs: list[dict] | None, vehicle_segs: list[dict] | None, soldier_segs: list[dict] | None = None, ) -> dict: """tracker.gg 数据 → 旧卡片字段契约(dev/bf2042 的 data 结构)。 字段名沿用旧版(userName/killDeath/infantryKillDeath/secondsPlayed/ dividedKills/humanPrecentage/weapons[].headshots 等),渲染层照旧坐标绘制; tracker 没有的数据(命中率/mvp/标记敌人数/处决等)填占位符 "–"。 """ data = (profile or {}).get("data") or {} segments = data.get("segments") or [] overview = next((s for s in segments if s.get("type") == "overview"), {}) info = data.get("platformInfo") or {} kills = _seg_value(overview, "kills") kd = _seg_value(overview, "kdRatio") kpm = _seg_value(overview, "killsPerMinute") human_kills = _seg_value(overview, "humanKills") human_per = round(100.0 * human_kills / kills, 1) if kills else 0.0 ratio = (human_kills / kills) if kills else 0.0 level = _seg_stat(overview, "level") try: level_s = int(level) - 99 level = f"S {level_s:03d}" if level_s > 0 else f"{int(level):03d}" except (TypeError, ValueError): level = str(level) # 真实 K/D、真实 KPM(旧卡算法:按"对人击杀占比"折算) real_kd = round(kd * ratio, 2) real_kpm = round(kpm * ratio, 2) # 最佳专家(soldier 段→旧 classes/characterName 结构) best = None ranked = sorted( soldier_segs or [], key=lambda s: _seg_value(s, "kills"), reverse=True ) if ranked: seg = ranked[0] raw_name = ((seg.get("metadata") or {}).get("name") or "").strip() best = { "characterName": raw_name, "displayName": SPECIALIST_NAMES.get(raw_name, raw_name), "className": (seg.get("metadata") or {}).get("category") or "", "kills": _seg_stat(seg, "kills"), "kpm": _seg_stat(seg, "killsPerMinute"), "killDeath": _seg_stat(seg, "kdRatio"), "secondsPlayed": round(_seg_value(seg, "timePlayed")), } # 鉴定文案(旧阈值) if kpm > 1.00 and kd > 2 and real_kd > 1.5: verdict, verdict_color = "Pro哥\n你带我走吧T_T", "gold" else: verdict, verdict_color = "薯薯\n别拷打我了哥>_<", "skyblue" def _objects(segs: list[dict], name_key: str, is_vehicle: bool) -> list[dict]: rows = [] for seg in sorted( segs or [], key=lambda x: _seg_value(x, "kills"), reverse=True )[:5]: meta = seg.get("metadata") or {} rows.append( { name_key: (meta.get("name") or "").strip(), "type": meta.get("category") or "", "kills": _seg_stat(seg, "kills"), "headshots": _seg_stat(seg, "headshotPercentage"), "accuracy": _seg_stat(seg, "shotsAccuracy"), "killsPerMinute": _seg_stat(seg, "killsPerMinute"), "timeEquipped": round(_seg_value(seg, "timePlayed")), "destroyed": _seg_stat(seg, "destroyedWith") if is_vehicle else "–", } ) return rows return { "userName": info.get("platformUserHandle") or _PLACEHOLDER, "platform": info.get("platformSlug") or "origin", "level": level, "secondsPlayed": int(_seg_value(overview, "timePlayed") // 3600), "matchesPlayed": _seg_stat(overview, "matches"), "mvp": "–", # tracker 无 "bestSquad": "–", # tracker 无 "playerTakeDowns": _seg_stat(overview, "meleeKills"), "kills": _seg_stat(overview, "kills"), "deaths": _seg_stat(overview, "deaths"), "killDeath": _seg_stat(overview, "kdRatio"), "infantryKillDeath": round(kd * ratio, 2), "killsPerMinute": _seg_stat(overview, "killsPerMinute"), "killsPerMatch": _seg_stat(overview, "killsPerMatch"), "headShots": _seg_stat(overview, "headshotPercentage"), "accuracy": "–", # tracker overview 无整体命中率 "winPercent": _seg_stat(overview, "wlPercentage"), "humanPrecentage": f"{human_per}%", "dividedKills": { "ai": _seg_stat(overview, "aiKills"), "vehicle": _seg_stat(overview, "vehicleKills"), "longDistance": "–", # tracker 无 "inRound": {"longDistance": "–"}, }, "revives": _seg_stat(overview, "revives"), "enemiesSpotted": "–", # tracker 无 "vehiclesDestroyed": _seg_stat(overview, "vehiclesDestroyed"), "bestClass": best, "realKd": real_kd, "realKpm": real_kpm, "verdict": verdict, "verdictColor": verdict_color, "weapons": _objects(weapon_segs or [], "weaponName", False), "vehicles": _objects(vehicle_segs or [], "vehicleName", True), }