-
Notifications
You must be signed in to change notification settings - Fork 11
/
stat_classes.py
209 lines (170 loc) · 13 KB
/
stat_classes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env python3
from dataclasses import dataclass,field
from enum import Enum
class StatType(Enum):
TOTAL = 1 # top total stat value over all fights
CONSISTENT = 2 # top consistency value over all fights = achieved top in most fights
AVERAGE = 3 # top average value over all fights
PERCENTAGE = 6 # top consistency percentage = times top / fights present
# This class stores information about a player. Note that a different profession will be treated as a new player / character.
@dataclass
class Player:
account: str # account name
name: str # character name
profession: str # profession name
num_fights_present: dict = field(default_factory=dict) # the number of fight the player was involved in regarding a certain stat
attendance_percentage: dict = field(default_factory=dict) # for each stat the percentage of fight duration the player was involved in out of all fights, where data was available for a given stat
# For each stat, the duration of all fights the player was involved in. Depends on whether a stat could be found in the log (e.g. whether the healing addon was active),
# and on which duration type is used for computing the avg for the stat ('total', 'active', 'in_combat', 'not_running_back')
duration_present: dict = field(default_factory=dict)
# for each stat, the sum of duration present * (squad members -1) of all fights the player was involved in;
# analogue to duration_present (see above)
normalization_time_allies: dict = field(default_factory=dict)
swapped_build: bool = False # a different player character or specialization with this account name was in some of the fights
# fields for all stats defined in config
consistency_stats: dict = field(default_factory=dict) # how many times did this player get into top for each stat?
total_stats: dict = field(default_factory=dict) # what's the total value for this player for each stat?
average_stats: dict = field(default_factory=dict) # what's the average stat per second for this player? (exception: deaths are per minute)
portion_top_stats: dict = field(default_factory=dict) # what percentage of fights did this player get into top for each stat, in relation to the number of fights they were involved in?
# = consistency_stats/num_fights_present
stats_per_fight: list = field(default_factory=list) # what's the value of each stat for this player in each fight?
def initialize(self, config):
self.duration_present = {key: 0 for key in config.stats_to_compute}
self.num_fights_present = {key: 0 for key in config.stats_to_compute}
self.normalization_time_allies = {key: 0 for key in config.stats_to_compute}
self.total_stats = {key: 0 for key in config.stats_to_compute}
for stat in config.squad_buff_abbrev.values():
self.total_stats[stat] = {'gen': 0, 'uptime': 0}
self.average_stats = {key: 0 for key in config.stats_to_compute}
self.consistency_stats = {key: 0 for key in config.stats_to_compute}
self.portion_top_stats = {key: 0 for key in config.stats_to_compute}
# This class stores information about a fight
@dataclass
class Fight:
skipped: bool = False # a fight is skipped in the top stats computation if number of enemies or allies is too small, or if it is too short
duration: int = 0 # duration of the fight in seconds
total_stats: dict = field(default_factory=dict) # what's the overall total value for the whole squad for each stat in this fight?
avg_stats: dict = field(default_factory=dict) # what's the overall average value for the whole squad for each stat in this fight?
enemies: int = 0 # number of enemy players involved
allies: int = 0 # number of squad players involved
kills: int = 0 # number of kills
start_time: str = "" # start time of the fight
squad_composition: dict = field(default_factory=dict) # squad composition of the fight (how many of which class)
tag_positions_until_death: list = field(default_factory=list) # position of the commander until he died (empty if no com was found or more than one com was found)
polling_rate: int = 150 # polling rate of position data as read from json (could get overwritten)
inch_to_pixel: float = 0.009 # inch to pixel conversion value; different for some maps -> might get overwritten
# This class stores the configuration for running the top stats.
@dataclass
class Config:
num_players_listed: dict = field(default_factory=dict) # How many players will be listed who achieved top stats most often for each stat?
num_players_considered_top: dict = field(default_factory=dict) # How many players are considered to be "top" in each fight for each stat?
min_attendance_portion_for_percentage: float = 0. # For what portion of all fights does a player need to be there to be considered for "percentage" awards?
min_attendance_percentage_for_average: float = 0. # For what percentage of all fights does a player need to be there to be considered for "jack of all trades" awards?
portion_of_top_for_total: float = 0. # What portion of the top total player stat does someone need to reach to be considered for total awards?
portion_of_top_for_consistent: float = 0. # What portion of the total stat of the top consistent player does someone need to reach to be considered for consistency awards?
portion_of_top_for_percentage: float = 0. # What portion of the consistency stat of the top consistent player does someone need to reach to be considered for percentage awards?
sort_xls_by: dict = field(default_factory=dict) # Which column(s) should the xls be sorted by?
min_allied_players: int = 0 # minimum number of allied players to consider a fight in the stats
min_fight_duration: int = 0 # minimum duration of a fight to be considered in the stats
min_enemy_players: int = 0 # minimum number of enemies to consider a fight in the stats
stat_names: dict = field(default_factory=dict) # the names under which the stats appear in the output
profession_abbreviations: dict = field(default_factory=dict) # the names under which each profession appears in the output
stat_descriptions: dict = field(default_factory=dict) # the description for each stat
relevant_classes: dict = field(default_factory=dict) # which classes are relevant for which stat?
empty_stats: dict = field(default_factory=dict) # stat values to initialize player stats per fight
stats_to_compute: list = field(default_factory=list) # all stats that should be computed
duration_for_averages: dict = field(default_factory=dict) # which duration type should be used to compute the avg for each stat? (one of 'total', 'active', 'in_combat', 'not_running_back')
squad_buff_ids: dict = field(default_factory=dict) # dict of squad buff name to buff id as read from buffMap
self_buff_ids: dict = field(default_factory=dict) # dict of self buff name to buff id as read from buffMap
buffs_stacking_duration: list = field(default_factory=list) # list of squad_buff names stacking duration
buffs_stacking_intensity: list = field(default_factory=list) # list of squad_buff names stacking intensity
buffs_not_stacking: list = field(default_factory=list) # list of squad_buff names that do not stack intensity or duration (e.g. auras)
squad_buff_abbrev: dict = field(default_factory=dict) # abbreviations of squad buff names
self_buff_abbrev: dict = field(default_factory=dict) # abbreviations of self buff names
errors: list = field(default_factory=list)
log_level: str = "info"
xls_column_names: list = field(default_factory=list)
# fills a Config with the given input
def fill_config(config_input, log):
config = Config()
if hasattr(config_input, "num_players_listed"):
config.num_players_listed = config_input.num_players_listed
else:
config.num_players_listed = dict()
for stat in config_input.stats_to_compute:
if stat not in config.num_players_listed:
config.num_players_listed[stat] = config_input.num_players_listed_default
if hasattr(config_input, "num_players_considered_top"):
config.num_players_considered_top = config_input.num_players_considered_top
else:
config.num_players_considered_top = dict()
for stat in config_input.stats_to_compute:
if stat not in config.num_players_considered_top:
config.num_players_considered_top[stat] = config_input.num_players_considered_top_default
if hasattr(config_input, "duration_for_averages"):
config.duration_for_averages = config_input.duration_for_averages
else:
config.duration_for_averages = dict()
for stat in config_input.stats_to_compute:
if stat not in config.duration_for_averages:
config.duration_for_averages[stat] = config_input.duration_for_averages_default
if hasattr(config_input, "sort_xls_by"):
config.sort_xls_by = config_input.sort_xls_by
else:
config.sort_xls_by = dict()
for stat in config_input.stats_to_compute:
if stat not in config.sort_xls_by:
config.sort_xls_by[stat] = config_input.default_sort_xls_by
config.min_attendance_portion_for_percentage = config_input.attendance_percentage_for_percentage/100.
config.min_attendance_percentage_for_average = config_input.attendance_percentage_for_average
config.portion_of_top_for_consistent = config_input.percentage_of_top_for_consistent/100.
config.portion_of_top_for_total = config_input.percentage_of_top_for_total/100.
config.portion_of_top_for_percentage = config_input.percentage_of_top_for_percentage/100.
config.min_allied_players = config_input.min_allied_players
config.min_fight_duration = config_input.min_fight_duration
config.min_enemy_players = config_input.min_enemy_players
config.files_to_write = config_input.files_to_write
config.stat_names = config_input.stat_names
config.stat_descriptions = config_input.stat_descriptions
config.profession_abbreviations = config_input.profession_abbreviations
config.relevant_classes = config_input.relevant_classes_for_stat
config.stats_to_compute = config_input.stats_to_compute
config.squad_buff_abbrev["Stability"] = 'stab'
config.squad_buff_abbrev["Protection"] = 'prot'
config.squad_buff_abbrev["Aegis"] = 'aegis'
config.squad_buff_abbrev["Resistance"] = 'resist'
config.squad_buff_abbrev["Regeneration"] = 'regen'
config.squad_buff_abbrev["Might"] = 'might'
config.squad_buff_abbrev["Fury"] = 'fury'
config.squad_buff_abbrev["Quickness"] = 'quick'
config.squad_buff_abbrev["Alacrity"] = 'alac'
config.squad_buff_abbrev["Resolution"] = 'resolution'
config.squad_buff_abbrev["Swiftness"] = 'swift'
config.squad_buff_abbrev["Vigor"] = 'vigor'
config.squad_buff_abbrev["Superspeed"] = 'speed'
config.squad_buff_abbrev["Stealth"] = 'stealth'
config.squad_buff_abbrev["Chaos Aura"] = 'chaos_aura'
config.squad_buff_abbrev["Fire Aura"] = 'fire_aura'
config.squad_buff_abbrev["Frost Aura"] = 'frost_aura'
config.squad_buff_abbrev["Light Aura"] = 'light_aura'
config.squad_buff_abbrev["Magnetic Aura"] = 'magnetic_aura'
config.squad_buff_abbrev["Shocking Aura"] = 'shocking_aura'
config.squad_buff_abbrev["Dark Aura"] = 'dark_aura'
config.self_buff_abbrev["Explosive Entrance"] = 'explosive_entrance'
config.self_buff_abbrev["Explosive Temper"] = 'explosive_temper'
config.self_buff_abbrev["Big Boomer"] = 'big_boomer'
config.self_buff_abbrev["Med Kit"] = 'med_kit'
config.empty_stats = {stat: -1 for stat in config.stats_to_compute}
for stat in config.squad_buff_abbrev.values():
config.empty_stats[stat] = {'gen': -1, 'uptime': -1}
config.empty_stats['duration_present'] = {stat: 0 for stat in config.stats_to_compute}
config.empty_stats['num_fights_present'] = {stat: 0 for stat in config.stats_to_compute}
config.empty_stats['normalization_time_allies'] = {stat: 0 for stat in config.stats_to_compute}
config.empty_stats['present_in_fight'] = False
config.xls_column_names = config_input.xls_column_names
if config_input.log_level == "debug" or config_input.log_level == "warning" or config_input.log_level == "info":
config.log_level = config_input.log_level
else:
print("log level "+config_input.log_level+" is not available. Using \"info\" instead.")
config.log_level = "info"
return config