|
20 | 20 | from sc2reader.data import builds as datapacks |
21 | 21 | from sc2reader.events import AbilityEvent, CameraEvent, HotkeyEvent, SelectionEvent |
22 | 22 | from sc2reader.exceptions import SC2ReaderLocalizationError |
23 | | -from sc2reader.objects import Player, Observer, Team, PlayerSummary, Graph, DepotFile |
| 23 | +from sc2reader.objects import Player, Observer, Team, PlayerSummary, Graph, DepotFile, BuildEntry |
24 | 24 | from sc2reader.constants import REGIONS, LOCALIZED_RACES, GAME_SPEED_FACTOR, LOBBY_PROPERTIES |
25 | 25 |
|
26 | 26 |
|
@@ -731,7 +731,7 @@ def __init__(self, summary_file, filename=None, lang='enUS', **options): |
731 | 731 | self.winners = list() |
732 | 732 | self.player = dict() |
733 | 733 | self.settings = dict() |
734 | | - self.player_stats = defaultdict(dict) |
| 734 | + self.player_stats = dict() |
735 | 735 | self.player_settings = defaultdict(dict) |
736 | 736 | self.build_orders = defaultdict(list) |
737 | 737 | self.image_urls = list() |
@@ -761,7 +761,7 @@ def __init__(self, summary_file, filename=None, lang='enUS', **options): |
761 | 761 | self.load_map_info() |
762 | 762 | self.load_settings() |
763 | 763 | self.load_player_stats() |
764 | | - self.load_player_builds() |
| 764 | + # self.load_player_builds() |
765 | 765 | self.load_players() |
766 | 766 |
|
767 | 767 | self.game_type = self.settings['Teams'].replace(" ","") |
@@ -922,12 +922,155 @@ def use_property(prop, player=None): |
922 | 922 | value = prop.values[player_setting][0] |
923 | 923 | self.player_settings[index][name] = translation[(uid, value)] |
924 | 924 |
|
| 925 | + def load_player_stats(self): |
| 926 | + translation = self.translations[self.opt.lang] |
| 927 | + |
| 928 | + stat_items = sum([p[0] for p in self.parts[3:]],[]) |
| 929 | + |
| 930 | + for item in stat_items: |
| 931 | + # Each stat item is laid out as follows |
| 932 | + # |
| 933 | + # { |
| 934 | + # 0: {0:999, 1:translation_id}, |
| 935 | + # 1: [ [{p1values},...], [{p2values},...], ...] |
| 936 | + # } |
| 937 | + stat_id = item[0][1] |
| 938 | + if stat_id in translation: |
| 939 | + # Assume anything under 1 million is a normal score screen item |
| 940 | + # Build order ids are generally 16 million+ |
| 941 | + if stat_id < 1000000: |
| 942 | + stat_name = translation[stat_id] |
| 943 | + for pid, value in enumerate(item[1]): |
| 944 | + if not value: continue |
| 945 | + |
| 946 | + if stat_name in ('Army Value','Resource Collection Rate','Upgrade Spending','Workers Active'): |
| 947 | + # Each point entry for the graph is laid out as follows |
| 948 | + # |
| 949 | + # {0:Value, 1:0, 2:Time} |
| 950 | + # |
| 951 | + # The 2nd part of the tuple appears to always be zero and |
| 952 | + # the time is in seconds of game time. |
| 953 | + xy = [(point[2], point[0]) for point in value] |
| 954 | + value = Graph([], [], xy_list=xy) |
| 955 | + else: |
| 956 | + value = value[0][0] |
| 957 | + |
| 958 | + self.player_stats.setdefault(pid, dict())[stat_name] = value |
| 959 | + else: |
| 960 | + # Each build item represents one ability and contains |
| 961 | + # a list of all the uses of that ability by each player |
| 962 | + # up to the first 64 successful actions in the game. |
| 963 | + for pindex, commands in enumerate(item[1]): |
| 964 | + for command in commands: |
| 965 | + self.build_orders[pindex].append(BuildEntry( |
| 966 | + supply=command[0], |
| 967 | + total_supply=command[1]&0xff, |
| 968 | + time=(command[2] >> 8) / 16, |
| 969 | + order=stat_name, |
| 970 | + build_index=command[1] >> 16 |
| 971 | + )) |
| 972 | + else: |
| 973 | + print "Echo some sort of issue here for ",stat_id |
| 974 | + |
| 975 | + # Once we've compiled all the build commands we need to make |
| 976 | + # sure they are properly sorted for presentation. |
| 977 | + for build_order in self.build_orders.values(): |
| 978 | + build_order.sort(key=lambda x: x.build_index) |
| 979 | + |
| 980 | + def load_players(self): |
| 981 | + for index, struct in enumerate(self.parts[0][3]): |
| 982 | + if not struct[0][1]: continue # Slot is closed |
| 983 | + |
| 984 | + player = PlayerSummary(struct[0][0]) |
| 985 | + stats = self.player_stats[index] |
| 986 | + settings = self.player_settings[index] |
| 987 | + player.is_ai = not isinstance(struct[0][1], dict) |
| 988 | + if not player.is_ai: |
| 989 | + player.gateway = self.gateway |
| 990 | + player.subregion = struct[0][1][0][2] |
| 991 | + player.region = REGIONS[player.gateway].get(player.subregion, 'Unknown') |
| 992 | + player.bnetid = struct[0][1][0][3] |
| 993 | + player.unknown1 = struct[0][1][0] |
| 994 | + player.unknown2 = struct[0][1][1] |
| 995 | + |
| 996 | + # Either a referee or a spectator, nothing else to do |
| 997 | + if settings['Participant Role'] != 'Participant': |
| 998 | + self.observers.append(player) |
| 999 | + continue |
| 1000 | + |
| 1001 | + player.play_race = LOBBY_PROPERTIES[0xBB9][1].get(struct[2], None) |
| 1002 | + |
| 1003 | + player.is_winner = isinstance(struct[1],dict) and struct[1][0] == 0 |
| 1004 | + if player.is_winner: |
| 1005 | + self.winners.append(player.pid) |
| 1006 | + |
| 1007 | + team_id = int(settings['Team'].split(' ')[1]) |
| 1008 | + if team_id not in self.teams: |
| 1009 | + self.teams[team_id] = Team(team_id) |
| 1010 | + player.team = self.teams[team_id] |
| 1011 | + self.teams[team_id].players.append(player) |
| 1012 | + |
| 1013 | + # We can just copy these settings right over |
| 1014 | + player.color = utils.Color(name=settings.get('Color', None)) |
| 1015 | + player.pick_race = settings.get('Race', None) |
| 1016 | + player.handicap = settings.get('Handicap', None) |
| 1017 | + |
| 1018 | + # Overview Tab |
| 1019 | + player.resource_score = stats.get('Resources', None) |
| 1020 | + player.structure_score = stats.get('Structures', None) |
| 1021 | + player.unit_score = stats.get('Units', None) |
| 1022 | + player.overview_score = stats.get('Overview', None) |
| 1023 | + |
| 1024 | + # Units Tab |
| 1025 | + player.units_killed = stats.get('Killed Unit Count', None) |
| 1026 | + player.structures_built = stats.get('Structures Built', None) |
| 1027 | + player.units_trained = stats.get('Units Trained', None) |
| 1028 | + player.structures_razed = stats.get('Structures Razed Count', None) |
| 1029 | + |
| 1030 | + # Graphs Tab |
| 1031 | + player.army_graph = stats.get('Army Value') |
| 1032 | + player.income_graph = stats.get('Resource Collection Rate', None) |
| 1033 | + |
| 1034 | + # HotS Stats |
| 1035 | + # TODO: Add the XP stats? |
| 1036 | + # 'Units Produced XP' |
| 1037 | + # 'Killed Unit XP' |
| 1038 | + # 'Structures Produced XP' |
| 1039 | + # 'Structures Razed XP' |
| 1040 | + # 'Technology XP' |
| 1041 | + player.upgrade_spending_graph = stats.get('Upgrade Spending', None) |
| 1042 | + player.workers_active_graph = stats.get('Workers Active', None) |
| 1043 | + player.combat_efficiency = stats.get('Combat Efficiency',None) |
| 1044 | + player.supply_efficiency = stats.get('Supply Efficiency', None) |
| 1045 | + player.apm = stats.get('APM', None) |
| 1046 | + |
| 1047 | + # Economic Breakdown Tab |
| 1048 | + if isinstance(player.income_graph, Graph): |
| 1049 | + # TODO: Is this algorithm right? |
| 1050 | + values = player.income_graph.values |
| 1051 | + player.income_rate = sum(values)/len(values) |
| 1052 | + else: |
| 1053 | + # In old s2gs files the field with this name was actually a number not a graph |
| 1054 | + player.income_rate = player.income_graph |
| 1055 | + player.income_graph = None |
| 1056 | + |
| 1057 | + player.avg_unspent_resources = stats.get('Average Unspent Resources', None) |
| 1058 | + player.workers_created = stats.get('Workers Created', None) |
| 1059 | + |
| 1060 | + # Build Orders Tab |
| 1061 | + player.build_order = self.build_orders.get(index, None) |
| 1062 | + |
| 1063 | + self.players.append(player) |
| 1064 | + self.player[player.pid] = player |
| 1065 | + |
| 1066 | + """ |
925 | 1067 | def load_player_stats(self): |
926 | 1068 | if len(self.parts) < 4: return |
927 | 1069 | translation = self.translations[self.opt.lang] |
928 | 1070 |
|
929 | 1071 | # Part[3][0][:] and Part[4][0][1] are filled with summary stats |
930 | | - # for the players in the game. Each stat item is laid out as follows |
| 1072 | + # for the players in the game. |
| 1073 | + # Each stat item is laid out as follows |
931 | 1074 | # |
932 | 1075 | # {0: {0:999, 1:translation_id}, 1: [ [{0: Value, 1:0, 2:871???}], [], ...] |
933 | 1076 | # |
@@ -996,72 +1139,7 @@ def load_player_builds(self): |
996 | 1139 | # sure they are properly sorted for presentation. |
997 | 1140 | for build_order in self.build_orders.values(): |
998 | 1141 | build_order.sort(key=lambda x: x.build_index) |
999 | | - |
1000 | | - def load_players(self): |
1001 | | - for index, struct in enumerate(self.parts[0][3]): |
1002 | | - if not struct[0][1]: continue # Slot is closed |
1003 | | - |
1004 | | - player = PlayerSummary(struct[0][0]) |
1005 | | - stats = self.player_stats[index] |
1006 | | - settings = self.player_settings[index] |
1007 | | - player.is_ai = not isinstance(struct[0][1], dict) |
1008 | | - if not player.is_ai: |
1009 | | - player.gateway = self.gateway |
1010 | | - player.subregion = struct[0][1][0][2] |
1011 | | - player.region = REGIONS[player.gateway].get(player.subregion, 'Unknown') |
1012 | | - player.bnetid = struct[0][1][0][3] |
1013 | | - player.unknown1 = struct[0][1][0] |
1014 | | - player.unknown2 = struct[0][1][1] |
1015 | | - |
1016 | | - # Either a referee or a spectator, nothing else to do |
1017 | | - if settings['Participant Role'] != 'Participant': |
1018 | | - self.observers.append(player) |
1019 | | - continue |
1020 | | - |
1021 | | - player.play_race = LOBBY_PROPERTIES[0xBB9][1].get(struct[2], None) |
1022 | | - |
1023 | | - player.is_winner = isinstance(struct[1],dict) and struct[1][0] == 0 |
1024 | | - if player.is_winner: |
1025 | | - self.winners.append(player.pid) |
1026 | | - |
1027 | | - team_id = int(settings['Team'].split(' ')[1]) |
1028 | | - if team_id not in self.teams: |
1029 | | - self.teams[team_id] = Team(team_id) |
1030 | | - player.team = self.teams[team_id] |
1031 | | - self.teams[team_id].players.append(player) |
1032 | | - |
1033 | | - # We can just copy these settings right over |
1034 | | - # TODO: Get the hex from the color string? |
1035 | | - player.color = utils.Color(name=settings.get('Color', None)) |
1036 | | - player.pick_race = settings.get('Race', None) |
1037 | | - player.handicap = settings.get('Handicap', None) |
1038 | | - |
1039 | | - # Overview Tab |
1040 | | - player.resource_score = stats.get('Resources', None) |
1041 | | - player.structure_score = stats.get('Structures', None) |
1042 | | - player.unit_score = stats.get('Units', None) |
1043 | | - player.overview_score = stats.get('Overview', None) |
1044 | | - |
1045 | | - # Economic Breakdown Tab |
1046 | | - player.avg_unspent_resources = stats.get('Average Unspent Resources', None) |
1047 | | - player.resource_collection_rate = stats.get('Resource Collection Rate', None) |
1048 | | - player.workers_created = stats.get('Workers Created', None) |
1049 | | - |
1050 | | - # Units Tab |
1051 | | - player.units_killed = stats.get('Killed Unit Count', None) |
1052 | | - player.structures_built = stats.get('Structures Built', None) |
1053 | | - player.units_trained = stats.get('Units Trained', None) |
1054 | | - player.structures_razed = stats.get('Structures Razed Count', None) |
1055 | | - |
1056 | | - # Graphs Tab |
1057 | | - player.army_graph = stats.get('Army Graph') |
1058 | | - player.income_graph = stats.get('Income Graph', None) |
1059 | | - |
1060 | | - # Build Orders Tab |
1061 | | - player.build_order = self.build_orders.get(index, None) |
1062 | | - |
1063 | | - self.players.append(player) |
1064 | | - self.player[player.pid] = player |
| 1142 | + """ |
1065 | 1143 |
|
1066 | 1144 | def __str__(self): |
1067 | 1145 | return "{0} - {1} {2}".format(self.start_time,self.game_length, |
|
0 commit comments