|
5 | 5 |
|
6 | 6 | from collections import namedtuple
|
7 | 7 |
|
| 8 | +from sc2reader import utils |
8 | 9 | from sc2reader.constants import *
|
9 | 10 |
|
10 | 11 | Location = namedtuple('Location',('x','y'))
|
@@ -38,7 +39,7 @@ class Team(object):
|
38 | 39 | #: pick races are not reflected in this string
|
39 | 40 | lineup = str()
|
40 | 41 |
|
41 |
| - def __init__(self,number): |
| 42 | + def __init__(self, number): |
42 | 43 | self.number = number
|
43 | 44 | self.players = list()
|
44 | 45 | self.result = "Unknown"
|
@@ -72,145 +73,215 @@ def __repr__(self):
|
72 | 73 | def __str__(self):
|
73 | 74 | return "[%s] %s: %s" % (self.player, self.name, self.value)
|
74 | 75 |
|
75 |
| -class Person(object): |
| 76 | + |
| 77 | +class Entity(object): |
| 78 | + """ |
| 79 | + :param integer sid: The entity's unique slot id. |
| 80 | + :param dict slot_data: The slot data associated with this entity |
76 | 81 | """
|
77 |
| - :param integer pid: The person's unique id in this game. |
78 |
| - :param string name: The person's battle.net name |
| 82 | + def __init__(self, sid, slot_data): |
| 83 | + #: The entity's unique in-game slot id |
| 84 | + self.sid = int(sid) |
79 | 85 |
|
80 |
| - Base class for :class:`Player` and :class:`Observer` classes. |
| 86 | + #: The entity's replay.initData slot data |
| 87 | + self.slot_data = slot_data |
81 | 88 |
|
82 |
| - Contains attributes shared by all starcraft II clients in a game. |
83 |
| - """ |
| 89 | + #: The player's handicap as set prior to game start, ranges from 50-100 |
| 90 | + self.handicap = slot_data['handicap'] |
84 | 91 |
|
85 |
| - #: The person's unique in this game |
86 |
| - pid = int() |
| 92 | + #: The entity's team number. None for observers |
| 93 | + self.team_id = slot_data['team_id']+1 |
87 | 94 |
|
88 |
| - #: The person's battle.net name |
89 |
| - name = str() |
| 95 | + #: A flag indicating if the person is a human or computer |
| 96 | + #: Really just a shortcut for isinstance(entity, User) |
| 97 | + self.is_human = slot_data['control'] == 2 |
90 | 98 |
|
91 |
| - #: A flag indicating the player's observer status. |
92 |
| - #: Really just a shortcut for isinstance(obj, Observer). |
93 |
| - is_observer = bool() |
| 99 | + #: A flag indicating the entity's observer status. |
| 100 | + #: Really just a shortcut for isinstance(entity, Observer). |
| 101 | + self.is_observer = slot_data['observe'] != 0 |
94 | 102 |
|
95 |
| - #: A flag indicating if the person is a human or computer |
96 |
| - is_human = bool() |
| 103 | + #: A flag marking this entity as a referee (can talk to players) |
| 104 | + self.is_referee = slot_data['observe'] == 2 |
97 | 105 |
|
98 |
| - #: A list of :class:`~sc2reader.events.message.ChatEvent` objects representing all of the chat |
99 |
| - #: messages the person sent during the game |
100 |
| - messages = list() |
| 106 | + #: The unique Battle.net account identifier in the form of |
| 107 | + #: <region_id>-S2-<subregion>-<toon_id> |
| 108 | + self.toon_handle = slot_data['toon_handle'] |
101 | 109 |
|
102 |
| - #: A list of :class:`Event` objects representing all the game events |
103 |
| - #: generated by the person over the course of the game |
104 |
| - events = list() |
| 110 | + toon_handle = self.toon_handle or "0-S2-0-0" |
| 111 | + parts = toon_handle.split("-") |
105 | 112 |
|
106 |
| - #: A flag indicating if this person was the one who recorded the game. |
107 |
| - recorder = bool() |
| 113 | + #: The Battle.net region the entity is registered to |
| 114 | + self.region = GATEWAY_LOOKUP[int(parts[0])] |
108 | 115 |
|
109 |
| - #: A flag indicating if the person is a computer or human |
110 |
| - is_human = bool() |
| 116 | + #: Deprecated, see Entity.region |
| 117 | + self.gateway = self.region |
111 | 118 |
|
112 |
| - #: The player's region. |
113 |
| - region = str() |
| 119 | + #: The Battle.net subregion the entity is registered to |
| 120 | + self.subregion = int(parts[2]) |
114 | 121 |
|
115 |
| - def __init__(self, pid, name): |
116 |
| - self.pid = pid |
117 |
| - self.name = name |
118 |
| - self.is_observer = bool() |
119 |
| - self.messages = list() |
| 122 | + #: The Battle.net acount identifier. Used to construct the |
| 123 | + #: bnet profile url. This value can be zero for games |
| 124 | + #: played offline when a user was not logged in to battle.net. |
| 125 | + self.toon_id = int(parts[3]) |
| 126 | + |
| 127 | + #: A list of :class:`Event` objects representing all the game events |
| 128 | + #: generated by the person over the course of the game |
120 | 129 | self.events = list()
|
121 |
| - self.camera_events = list() |
122 |
| - self.ability_events = list() |
123 |
| - self.selection_events = list() |
124 |
| - self.is_human = bool() |
125 |
| - self.region = str() |
126 |
| - self.recorder = False # Actual recorder will be determined using the replay.message.events file |
127 |
| - |
128 |
| -class Observer(Person): |
129 |
| - """ |
130 |
| - Extends :class:`Person`. |
131 | 130 |
|
132 |
| - Represents observers in the game. |
133 |
| - """ |
| 131 | + #: A list of :class:`~sc2reader.events.message.ChatEvent` objects representing all of the chat |
| 132 | + #: messages the person sent during the game |
| 133 | + self.messages = list() |
134 | 134 |
|
135 |
| - def __init__(self, pid, name): |
136 |
| - super(Observer,self).__init__(pid, name) |
137 |
| - self.is_observer = True |
138 |
| - self.is_human = True |
| 135 | + def format(self, format_string): |
| 136 | + return format_string.format(**self.__dict__) |
139 | 137 |
|
140 |
| - def __repr__(self): |
141 |
| - return str(self) |
142 |
| - def __str__(self): |
143 |
| - return "Player {0} - {1}".format(self.pid, self.name) |
144 | 138 |
|
145 |
| -class Player(Person): |
| 139 | +class Player(object): |
146 | 140 | """
|
147 |
| - Extends :class:`Person`. |
148 |
| -
|
149 |
| - Represents an active player in the game. Observers are represented via the |
150 |
| - :class:`Observer` class. |
| 141 | + :param integer pid: The player's unique player id. |
| 142 | + :param dict detail_data: The detail data associated with this player |
| 143 | + :param dict attribute_data: The attribute data associated with this player |
151 | 144 | """
|
| 145 | + def __init__(self, pid, detail_data, attribute_data): |
| 146 | + #: The player's unique in-game player id |
| 147 | + self.pid = int(pid) |
152 | 148 |
|
153 |
| - URL_TEMPLATE = "http://%s.battle.net/sc2/en/profile/%s/%s/%s/" |
| 149 | + #: The replay.details data on this player |
| 150 | + self.detail_data = detail_data |
154 | 151 |
|
155 |
| - #: A reference to the player's :class:`Team` object |
156 |
| - team = None |
| 152 | + #: The replay.attributes.events data on this player |
| 153 | + self.attribute_data = attribute_data |
157 | 154 |
|
158 |
| - #: A reference to a :class:`~sc2reader.utils.Color` object representing the player's color |
159 |
| - color = None |
| 155 | + #: The player result, one of "Win", "Loss", or None |
| 156 | + self.result = None |
| 157 | + if detail_data.result == 1: |
| 158 | + self.result = "Win" |
| 159 | + elif detail_data.result == 2: |
| 160 | + self.result = "Loss" |
160 | 161 |
|
161 |
| - #: The race the player picked prior to the game starting. |
162 |
| - #: Protoss, Terran, Zerg, Random |
163 |
| - pick_race = str() |
| 162 | + #: A reference to the player's :class:`Team` object |
| 163 | + self.team = None |
164 | 164 |
|
165 |
| - #: The race the player ultimately wound up playing. |
166 |
| - #: Protoss, Terran, Zerg |
167 |
| - play_race = str() |
168 |
| - |
169 |
| - #: The difficulty setting for the player. Always Medium for human players. |
170 |
| - #: Very Easy, Easy, Medium, Hard, Harder, Very hard, Elite, Insane, |
171 |
| - #: Cheater 2 (Resources), Cheater 1 (Vision) |
172 |
| - difficulty = str() |
| 165 | + #: The race the player picked prior to the game starting. |
| 166 | + #: One of Protoss, Terran, Zerg, Random |
| 167 | + self.pick_race = attribute_data.get('Race', 'Unknown') |
173 | 168 |
|
174 |
| - #: The player's handicap as set prior to game start, ranges from 50-100 |
175 |
| - handicap = int() |
| 169 | + #: The difficulty setting for the player. Always Medium for human players. |
| 170 | + #: Very Easy, Easy, Medium, Hard, Harder, Very hard, Elite, Insane, |
| 171 | + #: Cheater 2 (Resources), Cheater 1 (Vision) |
| 172 | + self.difficulty = attribute_data.get('Difficulty', 'Unknown') |
176 | 173 |
|
177 |
| - #: The subregion with in the player's region |
178 |
| - subregion = int() |
| 174 | + #: The race the player played the game with. |
| 175 | + #: One of Protoss, Terran, Zerg |
| 176 | + self.play_race = LOCALIZED_RACES.get(detail_data.race, detail_data.race) |
179 | 177 |
|
180 |
| - #: The player's bnet uid for his region/subregion. |
181 |
| - #: Used to construct the bnet profile url. This value can be zero for games |
182 |
| - #: played offline when a user was not logged in to battle.net. |
183 |
| - uid = int() |
| 178 | + #: A reference to a :class:`~sc2reader.utils.Color` object representing the player's color |
| 179 | + self.color = utils.Color(**detail_data.color._asdict()) |
184 | 180 |
|
185 |
| - def __init__(self, pid, name): |
186 |
| - super(Player,self).__init__(pid, name) |
187 |
| - self.is_observer = False |
188 |
| - |
189 |
| - #: A list of references to the units the player had this game |
| 181 | + #: A list of references to the :class:`~sc2reader.data.Unit` objects the player had this game |
190 | 182 | self.units = list()
|
191 | 183 |
|
192 |
| - #: A list of references to the units that the player killed this game |
| 184 | + #: A list of references to the :class:`~sc2reader.data.Unit` objects that the player killed this game |
193 | 185 | self.killed_units = list()
|
194 | 186 |
|
| 187 | + #: The Battle.net region the entity is registered to |
| 188 | + self.region = GATEWAY_LOOKUP[detail_data.bnet.gateway] |
| 189 | + |
| 190 | + #: Deprecated, see `Player.region` |
| 191 | + self.gateway = self.region |
| 192 | + |
| 193 | + #: The Battle.net subregion the entity is registered to |
| 194 | + self.subregion = detail_data.bnet.subregion |
| 195 | + |
| 196 | + #: The Battle.net acount identifier. Used to construct the |
| 197 | + #: bnet profile url. This value can be zero for games |
| 198 | + #: played offline when a user was not logged in to battle.net. |
| 199 | + self.toon_id = detail_data.bnet.uid |
| 200 | + |
| 201 | + |
| 202 | +class User(object): |
| 203 | + """ |
| 204 | + :param integer uid: The user's unique user id |
| 205 | + :param dict init_data: The init data associated with this user |
| 206 | + """ |
| 207 | + #: The Battle.net profile url template |
| 208 | + URL_TEMPLATE = "http://{region}.battle.net/sc2/en/profile/{toon_id}/{subregion}/{name}/" |
| 209 | + |
| 210 | + def __init__(self, uid, init_data): |
| 211 | + #: The user's unique in-game user id |
| 212 | + self.uid = int(uid) |
| 213 | + |
| 214 | + #: The replay.initData data on this user |
| 215 | + self.init_data = init_data |
| 216 | + |
| 217 | + #: The user's Battle.net clan tag at the time of the game |
| 218 | + self.clan_tag = init_data['clan_tag'] |
| 219 | + |
| 220 | + #: The user's Battle.net name at the time of the game |
| 221 | + self.name = init_data['name'] |
| 222 | + |
| 223 | + #: The user's combined Battle.net race levels |
| 224 | + self.combined_race_levels = init_data['combined_race_levels'] |
| 225 | + |
| 226 | + #: The user's highest leauge in the current season |
| 227 | + self.highest_league = init_data['highest_league'] |
| 228 | + |
| 229 | + #: A flag indicating if this person was the one who recorded the game. |
| 230 | + #: This is deprecated because it doesn't actually work. |
| 231 | + self.recorder = None |
195 | 232 |
|
196 | 233 | @property
|
197 | 234 | def url(self):
|
198 |
| - """The player's formatted battle.net profile url""" |
199 |
| - return self.URL_TEMPLATE % (self.gateway, self.uid, self.subregion, self.name) |
| 235 | + """The player's formatted Battle.net profile url""" |
| 236 | + return self.URL_TEMPLATE.format(**self.__dict__) |
200 | 237 |
|
201 |
| - def __str__(self): |
202 |
| - return "Player %s - %s (%s)" % (self.pid, self.name, self.play_race) |
203 | 238 |
|
204 |
| - @property |
205 |
| - def result(self): |
206 |
| - """The game result for this player: Win, Loss, Unknown""" |
207 |
| - return self.team.result if self.team else "Unknown" |
| 239 | +class Observer(Entity, User): |
| 240 | + """ |
| 241 | + :param integer sid: The entity's unique slot id. |
| 242 | + :param dict slot_data: The slot data associated with this entity |
| 243 | + :param integer uid: The user's unique user id |
| 244 | + :param dict init_data: The init data associated with this user |
| 245 | + :param integer pid: The player's unique player id. |
| 246 | + """ |
| 247 | + def __init__(self, sid, slot_data, uid, init_data, pid): |
| 248 | + Entity.__init__(self, sid, slot_data) |
| 249 | + User.__init__(self, uid, init_data) |
208 | 250 |
|
209 |
| - def format(self, format_string): |
210 |
| - return format_string.format(**self.__dict__) |
| 251 | + #: The player id of the observer. Only meaningful in pre 2.0.4 replays |
| 252 | + self.pid = pid |
211 | 253 |
|
212 |
| - def __repr__(self): |
213 |
| - return str(self) |
| 254 | + |
| 255 | +class Computer(Entity, Player): |
| 256 | + """ |
| 257 | + :param integer sid: The entity's unique slot id. |
| 258 | + :param dict slot_data: The slot data associated with this entity |
| 259 | + :param integer pid: The player's unique player id. |
| 260 | + :param dict detail_data: The detail data associated with this player |
| 261 | + :param dict attribute_data: The attribute data associated with this player |
| 262 | + """ |
| 263 | + def __init__(self, sid, slot_data, pid, detail_data, attribute_data): |
| 264 | + Entity.__init__(self, sid, slot_data) |
| 265 | + Player.__init__(self, pid, detail_data, attribute_data) |
| 266 | + |
| 267 | + #: The auto-generated in-game name for this computer player |
| 268 | + self.name = detail_data.name |
| 269 | + |
| 270 | + |
| 271 | +class Participant(Entity, User, Player): |
| 272 | + """ |
| 273 | + :param integer sid: The entity's unique slot id. |
| 274 | + :param dict slot_data: The slot data associated with this entity |
| 275 | + :param integer uid: The user's unique user id |
| 276 | + :param dict init_data: The init data associated with this user |
| 277 | + :param integer pid: The player's unique player id. |
| 278 | + :param dict detail_data: The detail data associated with this player |
| 279 | + :param dict attribute_data: The attribute data associated with this player |
| 280 | + """ |
| 281 | + def __init__(self, sid, slot_data, uid, init_data, pid, detail_data, attribute_data): |
| 282 | + Entity.__init__(self, sid, slot_data) |
| 283 | + User.__init__(self, uid, init_data) |
| 284 | + Player.__init__(self, pid, detail_data, attribute_data) |
214 | 285 |
|
215 | 286 |
|
216 | 287 | class PlayerSummary():
|
|
0 commit comments