From b960cb3822c16e68a4eab5aab3cad19f15cb80c8 Mon Sep 17 00:00:00 2001 From: Drevarr Date: Sat, 7 Dec 2024 19:24:40 -0500 Subject: [PATCH] Intial collection for DPS Stats to json --- DPSS_Stats.py | 79 ++---------------- output_functions.py | 3 +- parser_functions.py | 190 +++++++++++++++++++++++++++++++++++++++++++- tw5_top_stats.py | 2 +- 4 files changed, 199 insertions(+), 75 deletions(-) diff --git a/DPSS_Stats.py b/DPSS_Stats.py index 080ddd7..a3d6c07 100644 --- a/DPSS_Stats.py +++ b/DPSS_Stats.py @@ -1,7 +1,5 @@ import math DPSStats = {} -damagePS = {} -stacking_uptime_Table = {} def moving_average(data, window_size): num_elements = len(data) @@ -14,25 +12,10 @@ def moving_average(data, window_size): return ma -damage_PS = {} - -def get_damage_ps(fight_json, fight, players_running_healing_addon, config, combat_time, fight_time): - fight_ticks = len(fight_json['players'][0]["damage1S"][0]) - for index, target in enumerate(fight_json['targets']): - if 'enemyPlayer' in target and target['enemyPlayer'] == True: - for player in fight_json['players']: - player_prof_name = "{{"+player['profession']+"}} "+player['name'] - if player_prof_name not in damage_PS: - damage_PS[player_prof_name ] = [0] * fight_ticks - - damage_on_target = player["targetDamage1S"][index][0] - for i in range(fight_ticks): - damage_PS[player_prof_name ][i] += damage_on_target[i] - - -def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config, combat_time, fight_time): +def calculate_dps_stats(fight_json): fight_ticks = len(fight_json['players'][0]["damage1S"][0]) + duration = round(fight_json['durationMS']/1000) damagePS = {} for index, target in enumerate(fight_json['targets']): @@ -46,44 +29,25 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config for i in range(fight_ticks): damagePS[player_prof_name ][i] += damage_on_target[i] - skip_fight = {} - for player in fight_json['players']: - player_prof_name = "{{"+player['profession']+"}} "+player['name'] - - if player['notInSquad']: - skip_fight[player_prof_name] = True - continue - - if 'dead' in player['combatReplayData'] and len(player['combatReplayData']['dead']) > 0 and (combat_time / fight_time) < 0.4: - skip_fight[player_prof_name] = True - else: - skip_fight[player_prof_name] = False - squad_damage_per_tick = [] for fight_tick in range(fight_ticks - 1): squad_damage_on_tick = 0 for player in fight_json['players']: player_prof_name = "{{"+player['profession']+"}} "+player['name'] - if skip_fight[player_prof_name]: - continue - player_damage = damagePS[player_prof_name] squad_damage_on_tick += player_damage[fight_tick + 1] - player_damage[fight_tick] squad_damage_per_tick.append(squad_damage_on_tick) squad_damage_total = sum(squad_damage_per_tick) - squad_damage_per_tick_ma = moving_average(squad_damage_per_tick, 1) + squad_damage_per_tick_ma = calculate_moving_average(squad_damage_per_tick, 1) squad_damage_ma_total = sum(squad_damage_per_tick_ma) CHUNK_DAMAGE_SECONDS = 21 Ch5CaDamage1S = {} - UsedOffensiveSiege = {} for player in fight_json['players']: player_prof_name = "{{"+player['profession']+"}} "+player['name'] - if skip_fight[player_prof_name]: - continue - + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) if player_prof_name not in DPSStats: DPSStats[player_prof_name] = { "account": player["account"], @@ -106,11 +70,10 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config Ch5CaDamage1S[player_prof_name] = [0] * fight_ticks - UsedOffensiveSiege[player_prof_name] = False player_damage = damagePS[player_prof_name] - DPSStats[player_prof_name]["duration"] += fight_time + DPSStats[player_prof_name]["duration"] += duration DPSStats[player_prof_name]["combatTime"] += combat_time DPSStats[player_prof_name]["Damage_Total"] += player_damage[fight_ticks - 1] DPSStats[player_prof_name]["Squad_Damage_Total"] += squad_damage_total @@ -119,22 +82,12 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config DPSStats[player_prof_name]["Downs"] += statsTarget[0]['downed'] DPSStats[player_prof_name]["Kills"] += statsTarget[0]['killed'] - for damage_dist in player['totalDamageDist'][0]: - if damage_dist['id'] in config.siege_skill_ids: - UsedOffensiveSiege[player_prof_name] = True - - if "minions" in player: - for minion in player["minions"]: - for minion_damage_dist in minion["totalDamageDist"][0]: - if minion_damage_dist['id'] in config.siege_skill_ids: - UsedOffensiveSiege[player_prof_name] = True - # Coordination_Damage: Damage weighted by coordination with squad player_damage_per_tick = [player_damage[0]] for fight_tick in range(fight_ticks - 1): player_damage_per_tick.append(player_damage[fight_tick + 1] - player_damage[fight_tick]) - player_damage_ma = moving_average(player_damage_per_tick, 1) + player_damage_ma = calculate_moving_average(player_damage_per_tick, 1) for fight_tick in range(fight_ticks - 1): player_damage_on_tick = player_damage_ma[fight_tick] @@ -147,7 +100,7 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config squad_damage_percent = squad_damage_on_tick / squad_damage_ma_total - DPSStats[player_prof_name]["Coordination_Damage"] += player_damage_on_tick * squad_damage_percent * fight.duration + DPSStats[player_prof_name]["Coordination_Damage"] += player_damage_on_tick * squad_damage_percent * duration # Chunk damage: Damage done within X seconds of target down for index, target in enumerate(fight_json['targets']): @@ -168,9 +121,6 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config squad_damage_on_target = 0 for player in fight_json['players']: player_prof_name = "{{"+player['profession']+"}} "+player['name'] - if skip_fight[player_prof_name]: - continue - damage_on_target = player["targetDamage1S"][index][0] player_damage = damage_on_target[downIndex] - damage_on_target[startIndex] @@ -200,9 +150,6 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config total_carrion_damage = 0 for player in fight_json['players']: player_prof_name = "{{"+player['profession']+"}} "+player['name'] - if skip_fight[player_prof_name]: - continue - damage_on_target = player["targetDamage1S"][index][0] carrion_damage = damage_on_target[dmgEnd] - damage_on_target[dmgStart] @@ -214,18 +161,11 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config for player in fight_json['players']: player_prof_name = "{{"+player['profession']+"}} "+player['name'] - if skip_fight[player_prof_name]: - continue - DPSStats[player_prof_name]["Carrion_Damage_Total"] += total_carrion_damage # Burst damage: max damage done in n seconds for player in fight_json['players']: player_prof_name = "{{"+player['profession']+"}} "+player['name'] - if skip_fight[player_prof_name] or UsedOffensiveSiege[player_prof_name]: - # Exclude Dragon Banner from Burst stats - continue - player_damage = damagePS[player_prof_name] for i in range(1, CHUNK_DAMAGE_SECONDS): for fight_tick in range(i, fight_ticks): @@ -235,10 +175,6 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config # Ch5Ca Burst damage: max damage done in n seconds for player in fight_json['players']: player_prof_name = "{{"+player['profession']+"}} "+player['name'] - if skip_fight[player_prof_name] or UsedOffensiveSiege[player_prof_name]: - # Exclude Dragon Banner from Burst stats - continue - player_damage_ps = Ch5CaDamage1S[player_prof_name] player_damage = [0] * len(player_damage_ps) player_damage[0] = player_damage_ps[0] @@ -249,5 +185,4 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config dmg = player_damage[fight_tick] - player_damage[fight_tick - i] DPSStats[player_prof_name]["Ch5Ca_Burst_Damage"][i] = max(dmg, DPSStats[player_prof_name]["Ch5Ca_Burst_Damage"][i]) - return DPSStats diff --git a/output_functions.py b/output_functions.py index 0f81008..73bb4e4 100644 --- a/output_functions.py +++ b/output_functions.py @@ -2328,7 +2328,7 @@ def write_data_to_db(top_stats: dict, last_fight: str) -> None: print("Database updated.") -def output_top_stats_json(top_stats: dict, buff_data: dict, skill_data: dict, damage_mod_data: dict, high_scores: dict, personal_damage_mod_data: dict, personal_buff_data: dict, fb_pages: dict, mechanics: dict, minions: dict, death_on_tag: dict, outfile: str) -> None: +def output_top_stats_json(top_stats: dict, buff_data: dict, skill_data: dict, damage_mod_data: dict, high_scores: dict, personal_damage_mod_data: dict, personal_buff_data: dict, fb_pages: dict, mechanics: dict, minions: dict, death_on_tag: dict, DPSStats: dict, outfile: str) -> None: """Print the top_stats dictionary as a JSON object to the console.""" json_dict = {} @@ -2349,6 +2349,7 @@ def output_top_stats_json(top_stats: dict, buff_data: dict, skill_data: dict, da json_dict["minions"] = {key: value for key, value in minions.items()} json_dict["death_on_tag"] = {key: value for key, value in death_on_tag.items()} json_dict['players_running_healing_addon'] = top_stats['players_running_healing_addon'] + json_dict["DPSStats"] = {key: value for key, value in DPSStats.items()} with open(outfile, 'w') as json_file: json.dump(json_dict, json_file, indent=4) diff --git a/parser_functions.py b/parser_functions.py index 2726188..34a63b7 100644 --- a/parser_functions.py +++ b/parser_functions.py @@ -47,7 +47,7 @@ Run_Back = 5000 death_on_tag = {} commander_tag_positions = {} - +DPSStats = {} def determine_log_type_and_extract_fight_name(fight_name: str) -> tuple: """ @@ -329,6 +329,192 @@ def sum_breakpoints(breakpoints): combat_time += end - start return combat_time +def calculate_dps_stats(fight_json): + + fight_ticks = len(fight_json['players'][0]["damage1S"][0]) + duration = round(fight_json['durationMS']/1000) + + damage_ps = {} + for index, target in enumerate(fight_json['targets']): + if 'enemyPlayer' in target and target['enemyPlayer'] == True: + for player in fight_json['players']: + player_prof_name = player['profession'] + " " + player['name'] + if player_prof_name not in damage_ps: + damage_ps[player_prof_name] = [0] * fight_ticks + + damage_on_target = player["targetDamage1S"][index][0] + for i in range(fight_ticks): + damage_ps[player_prof_name][i] += damage_on_target[i] + + squad_damage_per_tick = [] + for fight_tick in range(fight_ticks - 1): + squad_damage_on_tick = 0 + for player in fight_json['players']: + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) + if combat_time: + player_prof_name = player['profession'] + " " + player['name'] + player_damage = damage_ps[player_prof_name] + squad_damage_on_tick += player_damage[fight_tick + 1] - player_damage[fight_tick] + squad_damage_per_tick.append(squad_damage_on_tick) + + squad_damage_total = sum(squad_damage_per_tick) + squad_damage_per_tick_ma = calculate_moving_average(squad_damage_per_tick, 1) + squad_damage_ma_total = sum(squad_damage_per_tick_ma) + + CHUNK_DAMAGE_SECONDS = 21 + ch5_ca_damage_1s = {} + + for player in fight_json['players']: + player_prof_name = player['profession'] + " " + player['name'] + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) + if combat_time: + if player_prof_name not in DPSStats: + DPSStats[player_prof_name] = { + "account": player["account"], + "name": player["name"], + "profession": player["profession"], + "duration": 0, + "combatTime": 0, + "coordinationDamage": 0, + "chunkDamage": [0] * CHUNK_DAMAGE_SECONDS, + "chunkDamageTotal": [0] * CHUNK_DAMAGE_SECONDS, + "carrionDamage": 0, + "carrionDamageTotal": 0, + "damageTotal": 0, + "squadDamageTotal": 0, + "burstDamage": [0] * CHUNK_DAMAGE_SECONDS, + "ch5CaBurstDamage": [0] * CHUNK_DAMAGE_SECONDS, + "downs": 0, + "kills": 0, + } + + ch5_ca_damage_1s[player_prof_name] = [0] * fight_ticks + + player_damage = damage_ps[player_prof_name] + + DPSStats[player_prof_name]["duration"] += duration + DPSStats[player_prof_name]["combatTime"] += combat_time + DPSStats[player_prof_name]["damageTotal"] += player_damage[fight_ticks - 1] + DPSStats[player_prof_name]["squadDamageTotal"] += squad_damage_total + + for stats_target in player["statsTargets"]: + DPSStats[player_prof_name]["downs"] += stats_target[0]['downed'] + DPSStats[player_prof_name]["kills"] += stats_target[0]['killed'] + + # Coordination_Damage: Damage weighted by coordination with squad + player_damage_per_tick = [player_damage[0]] + for fight_tick in range(fight_ticks - 1): + player_damage_per_tick.append(player_damage[fight_tick + 1] - player_damage[fight_tick]) + + player_damage_ma = calculate_moving_average(player_damage_per_tick, 1) + + for fight_tick in range(fight_ticks - 1): + player_damage_on_tick = player_damage_ma[fight_tick] + if player_damage_on_tick == 0: + continue + + squad_damage_on_tick = squad_damage_per_tick_ma[fight_tick] + if squad_damage_on_tick == 0: + continue + + squad_damage_percent = squad_damage_on_tick / squad_damage_ma_total + + DPSStats[player_prof_name]["coordinationDamage"] += player_damage_on_tick * squad_damage_percent * duration + + # Chunk damage: Damage done within X seconds of target down + for index, target in enumerate(fight_json['targets']): + if 'enemyPlayer' in target and target['enemyPlayer'] == True and 'combatReplayData' in target and len(target['combatReplayData']['down']): + for chunk_damage_seconds in range(1, CHUNK_DAMAGE_SECONDS): + targetDowns = dict(target['combatReplayData']['down']) + for targetDownsIndex, (downKey, downValue) in enumerate(targetDowns.items()): + downIndex = math.ceil(downKey / 1000) + startIndex = max(0, math.ceil(downKey / 1000) - chunk_damage_seconds) + if targetDownsIndex > 0: + lastDownKey, lastDownValue = list(targetDowns.items())[targetDownsIndex - 1] + lastDownIndex = math.ceil(lastDownKey / 1000) + if lastDownIndex == downIndex: + # Probably an ele in mist form + continue + startIndex = max(startIndex, lastDownIndex) + + squad_damage_on_target = 0 + for player in fight_json['players']: + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) + if combat_time: + player_prof_name = player['profession'] + " " + player['name'] + damage_on_target = player["targetDamage1S"][index][0] + player_damage = damage_on_target[downIndex] - damage_on_target[startIndex] + + DPSStats[player_prof_name]["chunkDamage"][chunk_damage_seconds] += player_damage + squad_damage_on_target += player_damage + + if chunk_damage_seconds == 5: + for i in range(startIndex, downIndex): + ch5_ca_damage_1s[player_prof_name][i] += damage_on_target[i + 1] - damage_on_target[i] + + for player in fight_json['players']: + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) + if combat_time: + player_prof_name = player['profession'] + " " + player['name'] + + DPSStats[player_prof_name]["chunkDamageTotal"][chunk_damage_seconds] += squad_damage_on_target + + # Carrion damage: damage to downs that die + for index, target in enumerate(fight_json['targets']): + if 'enemyPlayer' in target and target['enemyPlayer'] == True and 'combatReplayData' in target and len(target['combatReplayData']['dead']): + targetDeaths = dict(target['combatReplayData']['dead']) + targetDowns = dict(target['combatReplayData']['down']) + for deathKey, deathValue in targetDeaths.items(): + for downKey, downValue in targetDowns.items(): + if deathKey == downValue: + dmgEnd = math.ceil(deathKey / 1000) + dmgStart = math.ceil(downKey / 1000) + + total_carrion_damage = 0 + for player in fight_json['players']: + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) + if combat_time: + player_prof_name = player['profession'] + " " + player['name'] + damage_on_target = player["targetDamage1S"][index][0] + carrion_damage = damage_on_target[dmgEnd] - damage_on_target[dmgStart] + + DPSStats[player_prof_name]["carrionDamage"] += carrion_damage + total_carrion_damage += carrion_damage + + for i in range(dmgStart, dmgEnd): + ch5_ca_damage_1s[player_prof_name][i] += damage_on_target[i + 1] - damage_on_target[i] + + for player in fight_json['players']: + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) + if combat_time: + player_prof_name = player['profession'] + " " + player['name'] + DPSStats[player_prof_name]["carrionDamageTotal"] += total_carrion_damage + + # Burst damage: max damage done in n seconds + for player in fight_json['players']: + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) + if combat_time: + player_prof_name = player['profession'] + " " + player['name'] + player_damage = damage_ps[player_prof_name] + for i in range(1, CHUNK_DAMAGE_SECONDS): + for fight_tick in range(i, fight_ticks): + dmg = player_damage[fight_tick] - player_damage[fight_tick - i] + DPSStats[player_prof_name]["burstDamage"][i] = max(dmg, DPSStats[player_prof_name]["burstDamage"][i]) + + # Ch5Ca Burst damage: max damage done in n seconds + for player in fight_json['players']: + combat_time = round(sum_breakpoints(get_combat_time_breakpoints(player)) / 1000) + if combat_time: + player_prof_name = player['profession'] + " " + player['name'] + player_damage_ps = ch5_ca_damage_1s[player_prof_name] + player_damage = [0] * len(player_damage_ps) + player_damage[0] = player_damage_ps[0] + for i in range(1, len(player_damage)): + player_damage[i] = player_damage[i - 1] + player_damage_ps[i] + for i in range(1, CHUNK_DAMAGE_SECONDS): + for fight_tick in range(i, fight_ticks): + dmg = player_damage[fight_tick] - player_damage[fight_tick - i] + DPSStats[player_prof_name]["ch5CaBurstDamage"][i] = max(dmg, DPSStats[player_prof_name]["ch5CaBurstDamage"][i]) def get_player_stats_targets(statsTargets: dict, name: str, profession: str, fight_num: int, fight_time: int) -> None: fight_stat_value= 0 @@ -1343,6 +1529,8 @@ def parse_file(file_path, fight_num, guild_data): log_type, fight_name = determine_log_type_and_extract_fight_name(fight_name) + calculate_dps_stats(json_data) + top_stats['overall']['last_fight'] = f"{fight_date}-{fight_end}" #Initialize fight_num stats top_stats['fight'][fight_num] = { diff --git a/tw5_top_stats.py b/tw5_top_stats.py index bc62ba3..0e6ce81 100644 --- a/tw5_top_stats.py +++ b/tw5_top_stats.py @@ -267,7 +267,7 @@ write_tid_list_to_json(tid_list, args.output_filename) if write_all_data_to_json: - output_top_stats_json(top_stats, buff_data, skill_data, damage_mod_data, high_scores, personal_damage_mod_data, personal_buff_data, fb_pages, mechanics, minions, death_on_tag, args.json_output_filename) + output_top_stats_json(top_stats, buff_data, skill_data, damage_mod_data, high_scores, personal_damage_mod_data, personal_buff_data, fb_pages, mechanics, minions, death_on_tag, DPSStats, args.json_output_filename) if db_update: write_data_to_db(top_stats, top_stats['overall']['last_fight'])