Skip to content

Commit 3104473

Browse files
committed
A massive lump of a commit. Sorry guys.
This introduces several different utility classes and makes several changes to the player object among other things. I'd split this work up but that'd take too much time and its more important that the code gets up there.
1 parent d4b9f31 commit 3104473

File tree

4 files changed

+107
-14
lines changed

4 files changed

+107
-14
lines changed

sc2reader/objects.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from constants import LOCALIZED_RACES
22
from collections import defaultdict
33

4+
import datetime
5+
46
from .constants import *
57
from .data import GameObject, ABILITIES
6-
from .utils import PersonDict, Selection, read_header, AttributeDict
8+
from .utils import PersonDict, Selection, read_header, AttributeDict, Length
79

810
from collections import namedtuple
911

@@ -39,6 +41,9 @@ def __init__(self,number):
3941
self.players = list()
4042
self.result = "Unknown"
4143

44+
def __iter__(self):
45+
return self.players.__iter__()
46+
4247

4348
class Replay(object):
4449

@@ -52,7 +57,7 @@ def __init__(self, replay_file, **options):
5257
self.build = self.versions[4]
5358
self.release_string = "%s.%s.%s.%s" % tuple(self.versions[1:5])
5459
self.seconds = self.frames/16
55-
self.length = (self.seconds/60, self.seconds%60)
60+
self.length = Length(seconds=self.seconds)
5661

5762
#default values, filled in during file read
5863
self.player_names = list()
@@ -252,6 +257,9 @@ def __repr__(self):
252257
def result(self):
253258
return self.team.result
254259

260+
def format(self, format_string):
261+
return format_string.format(**self.__dict__)
262+
255263
class Event(object):
256264
name = 'BaseEvent'
257265
def apply(self): pass
@@ -298,6 +306,7 @@ class AbilityEvent(Event):
298306
name = 'AbilityEvent'
299307
def __init__(self, framestamp, player, type, code, ability):
300308
super(AbilityEvent, self).__init__(framestamp, player, type, code)
309+
print "{0}: {1}".format(self.name,ability)
301310
self.ability = ability
302311

303312
def apply(self):

sc2reader/parsers.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def parse_selection_event(self, buffer, frames, type, code, pid):
3939
object_types = chain(*[[object_type,]*count for (object_type, count) in object_types])
4040
objects = zip(object_ids, object_types)
4141

42-
return AbilityEvent(frames, pid, type, code, None)
42+
return SelectionEvent(frames, pid, type, code, None)
4343

4444
def parse_hotkey_event(self, buffer, frames, type, code, pid):
4545
hotkey = code >> 4
@@ -108,13 +108,13 @@ def parse_ability_event(self, buffer, frames, type, code, pid):
108108
return TargetAbilityEvent(frames, pid, type, code, ability, target)
109109

110110
else:
111-
return AbilityEvent(frames,pid,type,code,None)
111+
return AbilityEvent(frames,pid,type,code,ability)
112112

113113
elif atype & 0x40: # location/move
114114
if flag == 0x08:
115115
# coordinates (4), ?? (6)
116116
location = buffer.read_coordinate()
117-
buffer.skip(5)
117+
print buffer.read_hex(5)
118118
return LocationAbilityEvent(frames, pid, type, code, None, location)
119119

120120
elif flag in (0x04,0x05,0x07):
@@ -249,7 +249,7 @@ def parse_ability_event(self, buffer, frames, type, code, pid):
249249
return TargetAbilityEvent(frames, pid, type, code, ability, target)
250250

251251
else:
252-
return AbilityEvent(frames,pid,type,code,None)
252+
return AbilityEvent(frames,pid,type,code,ability)
253253

254254
elif atype & 0x40: # location/move ??
255255
h = buffer.read_hex(2)

sc2reader/processors.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from collections import defaultdict
22
from .objects import *
3-
from .utils import key_in_bases, windows_to_unix
3+
from .utils import key_in_bases, windows_to_unix, Length
44
from datetime import datetime
55

66
def Full(replay):
@@ -87,13 +87,13 @@ def Full(replay):
8787
if pdata.result == 1: player.team.result = "Win"
8888
elif pdata.result == 2: player.team.result = "Loss"
8989

90-
player.chosen_race = attributes['Race']
90+
player.pick_race = attributes['Race']
91+
player.play_race = LOCALIZED_RACES.get(pdata.race, pdata.race)
9192
player.difficulty = attributes['Difficulty']
9293
player.type = attributes['Player Type']
9394
player.uid = pdata.bnet.uid
9495
player.subregion = pdata.bnet.subregion
9596
player.handicap = pdata.handicap
96-
player.actual_race = LOCALIZED_RACES.get(pdata.race, pdata.race)
9797

9898
# We need initData for the realm which is required to build the url!
9999
if 'initData' in replay.raw and replay.realm:
@@ -112,6 +112,10 @@ def Full(replay):
112112
replay.player[pid] = player
113113
replay.person[pid] = player
114114

115+
#Create an store an ordered lineup string
116+
for team in replay.teams:
117+
team.lineup = sorted(player.play_race[0].upper() for player in team)
118+
115119
if 'initData' in replay.raw:
116120
# Create observers out of the leftover names gathered from initData
117121
all_players = replay.raw.initData.player_names

sc2reader/utils.py

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import argparse
12
import cStringIO
23
import fnmatch
34
import os
45
import re
56
import struct
7+
import textwrap
68

7-
import exceptions
9+
import sc2reader.exceptions
810

911
from itertools import groupby
1012

@@ -535,20 +537,22 @@ def read_header(file):
535537
return header_data[1].values(),header_data[3]
536538

537539

538-
def _allow(filename, regex):
540+
541+
def _allow(filename, regex=None, allow=True):
539542
name, ext = os.path.splitext(filename)
540543
if ext.lower() != ".sc2replay": return False
541-
return re.match(regex, name) if regex else True
544+
return allow == re.match(regex, name) if regex else allow
542545

543-
def get_files( path, regex=None, exclude=[],
546+
def get_files( path, regex=None, allow=True, exclude=[],
544547
depth=-1, followlinks=False, **extras):
545548

549+
546550
#os.walk and os.path.isfile fail silently. We want to be loud!
547551
if not os.path.exists(path):
548552
raise ValueError("Location `{0}` does not exist".format(path))
549553

550554
# Curry the function to prime it for use in the filter function
551-
allow = lambda filename: _allow(filename, regex)
555+
allow = lambda filename: _allow(filename, regex, allow)
552556

553557
# You can't get more than one file from a file name!
554558
if os.path.isfile(path):
@@ -567,3 +571,79 @@ def get_files( path, regex=None, exclude=[],
567571
depth -= 1
568572

569573
return files
574+
575+
from datetime import timedelta
576+
class Length(timedelta):
577+
@property
578+
def hours(self):
579+
return self.seconds/3600
580+
581+
@property
582+
def mins(self):
583+
return self.seconds/60
584+
585+
@property
586+
def secs(self):
587+
return self.seconds%60
588+
589+
def __str__(self):
590+
if self.hours:
591+
return "{0:0>2}.{1:0>2}.{2:0>2}".format(self.hours,self.mins,self.secs)
592+
else:
593+
return "{0:0>2}.{1:0>2}".format(self.mins,self.secs)
594+
595+
class Formatter(argparse.RawTextHelpFormatter):
596+
"""FlexiFormatter which respects new line formatting and wraps the rest
597+
598+
Example:
599+
>>> parser = argparse.ArgumentParser(formatter_class=FlexiFormatter)
600+
>>> parser.add_argument('a',help='''\
601+
... This argument's help text will have this first long line\
602+
... wrapped to fit the target window size so that your text\
603+
... remains flexible.
604+
...
605+
... 1. This option list
606+
... 2. is still persisted
607+
... 3. and the option strings get wrapped like this\
608+
... with an indent for readability.
609+
...
610+
... You must use backslashes at the end of lines to indicate that\
611+
... you want the text to wrap instead of preserving the newline.
612+
... ''')
613+
614+
Only the name of this class is considered a public API. All the methods
615+
provided by the class are considered an implementation detail.
616+
"""
617+
618+
@classmethod
619+
def new(cls, **options):
620+
return lambda prog: Formatter(prog, **options)
621+
622+
def _split_lines(self, text, width):
623+
lines = list()
624+
main_indent = len(re.match(r'( *)',text).group(1))
625+
# Wrap each line individually to allow for partial formatting
626+
for line in text.splitlines():
627+
628+
# Get this line's indent and figure out what indent to use
629+
# if the line wraps. Account for lists of small variety.
630+
indent = len(re.match(r'( *)',line).group(1))
631+
list_match = re.match(r'( *)(([*-+>]+|\w+\)|\w+\.) +)',line)
632+
if(list_match):
633+
sub_indent = indent + len(list_match.group(2))
634+
else:
635+
sub_indent = indent
636+
637+
# Textwrap will do all the hard work for us
638+
line = self._whitespace_matcher.sub(' ', line).strip()
639+
new_lines = textwrap.wrap(
640+
text=line,
641+
width=width,
642+
initial_indent=' '*(indent-main_indent),
643+
subsequent_indent=' '*(sub_indent-main_indent),
644+
)
645+
646+
# Blank lines get eaten by textwrap, put it back with [' ']
647+
lines.extend(new_lines or [' '])
648+
649+
return lines

0 commit comments

Comments
 (0)