diff --git a/DPSS_Stats.py b/DPSS_Stats.py index 73cc148..d962602 100644 --- a/DPSS_Stats.py +++ b/DPSS_Stats.py @@ -1,6 +1,6 @@ import math DPSStats = {} - +stacking_uptime_Table = {} def moving_average(data, window_size): num_elements = len(data) @@ -68,9 +68,8 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config if skip_fight[player_prof_name]: continue - DPSStats_prof_name = player_prof_name + " " + player_role - if DPSStats_prof_name not in DPSStats: - DPSStats[DPSStats_prof_name] = { + if player_prof_name not in DPSStats: + DPSStats[player_prof_name] = { "account": player["account"], "name": player["name"], "profession": player["profession"], @@ -95,23 +94,23 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config player_damage = damagePS[player_prof_name] - DPSStats[DPSStats_prof_name]["duration"] += fight_time - DPSStats[DPSStats_prof_name]["combatTime"] += combat_time - DPSStats[DPSStats_prof_name]["Damage_Total"] += player_damage[fight_ticks - 1] - DPSStats[DPSStats_prof_name]["Squad_Damage_Total"] += squad_damage_total + DPSStats[player_prof_name]["duration"] += fight_time + 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 for statsTarget in player["statsTargets"]: - DPSStats[DPSStats_prof_name]["Downs"] += statsTarget[0]['downed'] - DPSStats[DPSStats_prof_name]["Kills"] += statsTarget[0]['killed'] + 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 siege_skill_ids: + 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 siege_skill_ids: + if minion_damage_dist['id'] in config.siege_skill_ids: UsedOffensiveSiege[player_prof_name] = True # Coordination_Damage: Damage weighted by coordination with squad @@ -132,7 +131,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[DPSStats_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 * fight.duration # Chunk damage: Damage done within X seconds of target down for index, target in enumerate(fight_json['targets']): @@ -155,13 +154,11 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config player_prof_name = "{{"+player['profession']+"}} "+player['name'] if skip_fight[player_prof_name]: continue - - DPSStats_prof_name = player_prof_name damage_on_target = player["targetDamage1S"][index][0] player_damage = damage_on_target[downIndex] - damage_on_target[startIndex] - DPSStats[DPSStats_prof_name]["Chunk_Damage"][chunk_damage_seconds] += player_damage + DPSStats[player_prof_name]["Chunk_Damage"][chunk_damage_seconds] += player_damage squad_damage_on_target += player_damage if chunk_damage_seconds == 5: @@ -171,9 +168,7 @@ 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'] - DPSStats_prof_name = player_prof_name - - DPSStats[DPSStats_prof_name]["Chunk_Damage_Total"][chunk_damage_seconds] += squad_damage_on_target + DPSStats[player_prof_name]["Chunk_Damage_Total"][chunk_damage_seconds] += squad_damage_on_target # Carrion damage: damage to downs that die for index, target in enumerate(fight_json['targets']): @@ -192,11 +187,10 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config if skip_fight[player_prof_name]: continue - DPSStats_prof_name = player_prof_name damage_on_target = player["targetDamage1S"][index][0] carrion_damage = damage_on_target[dmgEnd] - damage_on_target[dmgStart] - DPSStats[DPSStats_prof_name]["Carrion_Damage"] += carrion_damage + DPSStats[player_prof_name]["Carrion_Damage"] += carrion_damage total_carrion_damage += carrion_damage for i in range(dmgStart, dmgEnd): @@ -206,10 +200,8 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config player_prof_name = "{{"+player['profession']+"}} "+player['name'] if skip_fight[player_prof_name]: continue - - player_role = player_roles[player_prof_name] - DPSStats_prof_name = player_prof_name + " " + player_role - DPSStats[DPSStats_prof_name]["Carrion_Damage_Total"] += total_carrion_damage + + DPSStats[player_prof_name]["Carrion_Damage_Total"] += total_carrion_damage # Burst damage: max damage done in n seconds for player in fight_json['players']: @@ -218,12 +210,11 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config # Exclude Dragon Banner from Burst stats continue - DPSStats_prof_name = player_prof_name player_damage = damagePS[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[DPSStats_prof_name]["Burst_Damage"][i] = max(dmg, DPSStats[DPSStats_prof_name]["Burst_Damage"][i]) + DPSStats[player_prof_name]["Burst_Damage"][i] = max(dmg, DPSStats[player_prof_name]["Burst_Damage"][i]) # Ch5Ca Burst damage: max damage done in n seconds for player in fight_json['players']: @@ -232,7 +223,6 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config # Exclude Dragon Banner from Burst stats continue - DPSStats_prof_name = player_prof_name player_damage_ps = Ch5CaDamage1S[player_prof_name] player_damage = [0] * len(player_damage_ps) player_damage[0] = player_damage_ps[0] @@ -241,8 +231,11 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config 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[DPSStats_prof_name]["Ch5Ca_Burst_Damage"][i] = max(dmg, DPSStats[DPSStats_prof_name]["Ch5Ca_Burst_Damage"][i]) + DPSStats[player_prof_name]["Ch5Ca_Burst_Damage"][i] = max(dmg, DPSStats[player_prof_name]["Ch5Ca_Burst_Damage"][i]) + return DPSStats + +def calculate_stacking_uptimes(fight_json, fight, players_running_healing_addon, config, combat_time, fight_time): # Track Stacking Buff Uptimes damage_with_buff_buffs = ['stability', 'protection', 'aegis', 'might', 'fury', 'resistance', 'resolution', 'quickness', 'swiftness', 'alacrity', 'vigor', 'regeneration'] for player in fight_json['players']: @@ -250,21 +243,18 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config if skip_fight[player_prof_name]: continue - player_role = player_roles[player_prof_name] - DPSStats_prof_name = player_prof_name + " " + player_role - if DPSStats_prof_name not in stacking_uptime_Table: - stacking_uptime_Table[DPSStats_prof_name] = {} - stacking_uptime_Table[DPSStats_prof_name]["account"] = player['account'] - stacking_uptime_Table[DPSStats_prof_name]["name"] = player['name'] - stacking_uptime_Table[DPSStats_prof_name]["profession"] = player['profession'] - stacking_uptime_Table[DPSStats_prof_name]["role"] = player_role - stacking_uptime_Table[DPSStats_prof_name]["duration_might"] = 0 - stacking_uptime_Table[DPSStats_prof_name]["duration_stability"] = 0 - stacking_uptime_Table[DPSStats_prof_name]["might"] = [0] * 26 - stacking_uptime_Table[DPSStats_prof_name]["stability"] = [0] * 26 - stacking_uptime_Table[DPSStats_prof_name]["firebrand_pages"] = {} + if player_prof_name not in stacking_uptime_Table: + stacking_uptime_Table[player_prof_name] = {} + stacking_uptime_Table[player_prof_name]["account"] = player['account'] + stacking_uptime_Table[player_prof_name]["name"] = player['name'] + stacking_uptime_Table[player_prof_name]["profession"] = player['profession'] + stacking_uptime_Table[player_prof_name]["duration_might"] = 0 + stacking_uptime_Table[player_prof_name]["duration_stability"] = 0 + stacking_uptime_Table[player_prof_name]["might"] = [0] * 26 + stacking_uptime_Table[player_prof_name]["stability"] = [0] * 26 + stacking_uptime_Table[player_prof_name]["firebrand_pages"] = {} for buff_name in damage_with_buff_buffs: - stacking_uptime_Table[DPSStats_prof_name]["damage_with_"+buff_name] = [0] * 26 if buff_name == 'might' else [0] * 2 + stacking_uptime_Table[player_prof_name]["damage_with_"+buff_name] = [0] * 26 if buff_name == 'might' else [0] * 2 player_damage = damagePS[player_prof_name] player_damage_per_tick = [player_damage[0]] @@ -287,7 +277,7 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config if buff_name in ['stability', 'might']: uptime = state_end - state_start total_time += uptime - stacking_uptime_Table[DPSStats_prof_name][buff_name][min(stacks, 25)] += uptime + stacking_uptime_Table[player_prof_name][buff_name][min(stacks, 25)] += uptime if buff_name in damage_with_buff_buffs: start_sec = state_start / 1000 @@ -328,36 +318,10 @@ def calculate_dps_stats(fight_json, fight, players_running_healing_addon, config damage_with_stacks += player_damage_per_tick[next_start_sec_int] * (next_start_sec_rem) if buff_name == 'might': - stacking_uptime_Table[DPSStats_prof_name]["damage_with_"+buff_name][min(stacks, 25)] += damage_with_stacks + stacking_uptime_Table[player_prof_name]["damage_with_"+buff_name][min(stacks, 25)] += damage_with_stacks else: - stacking_uptime_Table[DPSStats_prof_name]["damage_with_"+buff_name][min(stacks, 1)] += damage_with_stacks + stacking_uptime_Table[player_prof_name]["damage_with_"+buff_name][min(stacks, 1)] += damage_with_stacks if buff_name in ['stability', 'might']: - stacking_uptime_Table[DPSStats_prof_name]["duration_"+buff_name] += total_time - - if player_prof_name not in FB_Pages: - FB_Pages[player_prof_name] = {} - FB_Pages[player_prof_name]["account"] = player['account'] - FB_Pages[player_prof_name]["name"] = player['name'] - FB_Pages[player_prof_name]["fightTime"] = 0 - FB_Pages[player_prof_name]["firebrand_pages"] = {} - - # Track Firebrand Buffs - tome1_skill_ids = ["41258", "40635", "42449", "40015", "42898"] - tome2_skill_ids = ["45022", "40679", "45128", "42008", "42925"] - tome3_skill_ids = ["42986", "41968", "41836", "40988", "44455"] - tome_skill_ids = [ - *tome1_skill_ids, - *tome2_skill_ids, - *tome3_skill_ids, - ] - - if player['profession'] == "Firebrand" and "rotation" in player: - FB_Pages[player_prof_name]["fightTime"] += player['activeTimes'][0]/1000 - for rotation_skill in player['rotation']: - skill_id = str(rotation_skill['id']) - if skill_id in tome_skill_ids: - pages_data = FB_Pages[player_prof_name]["firebrand_pages"] - pages_data[skill_id] = pages_data.get(skill_id, 0) + len(rotation_skill['skills']) - - return DPSStats \ No newline at end of file + stacking_uptime_Table[player_prof_name]["duration_"+buff_name] += total_time + diff --git a/config_output.py b/config_output.py index 5ddfc1d..0ef9e34 100644 --- a/config_output.py +++ b/config_output.py @@ -177,6 +177,16 @@ 54941, 54953, 21615, 23267, 18792, 18793, 25533, 27927, 30765, 34797 ] +siege_skill_ids = [ + *arrow_cart_skill_ids, + *trebuchet_skill_ids, + *catapult_skill_ids, + *cannon_skill_ids, + *burning_oil_skill_ids, + *dragon_banner_skill_ids, + *golem_skills, +] + exclude_skill_ids = [ *arrow_cart_skill_ids, *trebuchet_skill_ids, diff --git a/output_functions.py b/output_functions.py index 28fb48c..2eec466 100644 --- a/output_functions.py +++ b/output_functions.py @@ -288,8 +288,8 @@ def build_tag_summary(top_stats): tag_summary[commander]["fight_time"] += fight_data["fight_durationMS"] tag_summary[commander]["enemy_killed"] += fight_data["enemy_killed"] tag_summary[commander]["enemy_downed"] += fight_data["enemy_downed"] - tag_summary[commander]["squad_downed"] += fight_data["defenses"]["downCount"] - tag_summary[commander]["squad_deaths"] += fight_data["defenses"]["deadCount"] + tag_summary[commander]["squad_downed"] += fight_data.get("defenses", {}).get("downCount", 0) + tag_summary[commander]["squad_deaths"] += fight_data.get("defenses", {}).get("deadCount", 0) return tag_summary, tag_list @@ -416,14 +416,20 @@ def build_fight_summary(top_stats: dict, caption: str, tid_date_time : str) -> N fight_link = f"[[{fight_data['fight_date']} - {fight_data['fight_end']} - {abbrv}|{fight_data['fight_link']}]]" # Build the row - damage_taken = fight_data['defenses']['damageTaken'] or 1 + damage_taken = fight_data['defenses'].get('damageTaken', 1) + downed = fight_data['statsTargets'].get('downed', 0) + killed = fight_data['statsTargets'].get('killed', 0) + def_down = fight_data['defenses'].get('downCount', 0) + def_dead = fight_data['defenses'].get('deadCount', 0) + dmg_out = fight_data['dpsTargets'].get('damage', 0) + def_barrier = fight_data['defenses'].get('damageBarrier', 0) row += f"|{fight_num} |{fight_link} | {fight_data['fight_duration']}| {fight_data['squad_count']} | {fight_data['non_squad_count']} | {fight_data['enemy_count']} " - row += f"| {fight_data['enemy_Red']}/{fight_data['enemy_Green']}/{fight_data['enemy_Blue']} | {fight_data['statsTargets']['downed']} | {fight_data['statsTargets']['killed']} " - row += f"| {fight_data['defenses']['downCount']} | {fight_data['defenses']['deadCount']} | {fight_data['dpsTargets']['damage']:,}| {fight_data['defenses']['damageTaken']:,}" - row += f"| {fight_data['defenses']['damageBarrier']:,}| {(fight_data['defenses']['damageBarrier'] / damage_taken * 100):.2f}%| {fight_shield_damage:,}" + row += f"| {fight_data['enemy_Red']}/{fight_data['enemy_Green']}/{fight_data['enemy_Blue']} | {downed} | {killed} " + row += f"| {def_down} | {def_dead} | {dmg_out:,}| {damage_taken:,}" + row += f"| {def_barrier:,}| {(def_barrier / damage_taken * 100):.2f}%| {fight_shield_damage:,}" # Calculate the shield damage percentage - if fight_data['dpsTargets']['damage']: - shield_damage_pct = (fight_shield_damage / fight_data['dpsTargets']['damage'] * 100) + if dmg_out: + shield_damage_pct = (fight_shield_damage / dmg_out * 100) else: shield_damage_pct = 0 row += f"| {shield_damage_pct:.2f}%|" @@ -724,6 +730,7 @@ def build_uptime_summary(top_stats: dict, boons: dict, buff_data: dict, caption: # Build the Squad table rows header2 = f"|Squad Average Uptime |<|<|<|<|" for boon_id in boons: + print(f"{boon_id=}") if boon_id not in buff_data: continue if boon_id not in top_stats["overall"]["buffUptimes"]: diff --git a/parser_functions.py b/parser_functions.py index e1851d2..6517284 100644 --- a/parser_functions.py +++ b/parser_functions.py @@ -1297,6 +1297,7 @@ def parse_file(file_path, fight_num, guild_data): fight_name = json_data['fightName'] fight_link = json_data['uploadLinks'][0] dist_to_com = [] + player_in_combat = 0 enemy_engaged_count = sum(1 for enemy in targets if not enemy['isFake']) @@ -1325,6 +1326,10 @@ def parse_file(file_path, fight_num, guild_data): 'enemy_Unk': 0, } + for stat_cat in json_stats: + top_stats['fight'][fight_num].setdefault(stat_cat, {}) + top_stats['overall'].setdefault(stat_cat, {}) + #get commander data commander_tag_positions, dead_tag_mark, dead_tag = get_commander_tag_data(json_data) @@ -1442,8 +1447,8 @@ def parse_file(file_path, fight_num, guild_data): # Initialize dictionaries for player, fight, and overall stats if they don't exist top_stats['player'].setdefault(name_prof, {}).setdefault(stat_cat, {}) - top_stats['fight'][fight_num].setdefault(stat_cat, {}) - top_stats['overall'].setdefault(stat_cat, {}) + #top_stats['fight'][fight_num].setdefault(stat_cat, {}) + #top_stats['overall'].setdefault(stat_cat, {}) # format: player[stat_category][0][stat] if stat_cat in ['defenses', 'support', 'statsAll']: