Fixed it! I hadn't taken into account reinforcements, which have extra sets of parentheses, or the fact that some unit variants have parentheses (like the Hover tank (Standard) here). Also, turns out missiles weren't being taken into account!
Hopefully all of this is now fixed, along with the thing where two mechs with the same name but different factions would get bunched together
I have a feeling changing their name in Megamek might screw things over, so no need to rename units now. Thanks for the debug information, got me everything I needed! And ... you know, hopefully I didn't break anything, which is totally possible
import re
with open ("round.txt", "r") as myfile:
data=myfile.readlines()
data = [line.strip('\n').strip('\r').strip('\t') for line in data]
data = list(filter("".__ne__, data))
class Unit:
def __init__(self, name, faction):
self.name = name
self.faction = faction
self.timesFired = 0
self.timesFiredHit = 0
self.timesFiredAt = 0
self.timesFiredAtHit = 0
self.damageTaken = 0
self.damageDealt = 0
self.kick = False
self.kicked = 0
self.punch = 0
self.punched = 0
self.physTarget = None
self.physDamage = 0
self.lastPartHit = None
self.crits = []
self.destroyedParts = []
self.destroyed = ""
self.prone = False
self.immobile = False
def __str__(self):
return "("+self.faction+") " + self.name
def asList(self):
l = []
l.append("("+ self.faction +") " + self.name)
if self.timesFired > 0:
l.append(" Fired " + str(self.timesFired) + " times, hit " + str(self.timesFiredHit) + " times for " +str(self.damageDealt)+" damage")
if self.damageTaken > 0 or self.timesFiredAt > 0:
l.append(" Fired at " + str(self.timesFiredAt) + " times, hit " + str(self.timesFiredAtHit) + " times for " + str(self.damageTaken) +" damage")
if self.kick:
pd = " for " + str(self.physDamage) + " damage" if (self.physDamage > 0) else " and missed!"
l.append(" Kicked " + str(self.physTarget) + pd)
if self.punch > 0:
pd = " " + str(self.punch) +" times for " + str(self.physDamage) + " damage" if (self.physDamage > 0) else " and missed!"
l.append(" Punched " + str(self.physTarget) + pd)
if self.punched > 0 or self.kicked > 0:
l.append(" Was punched " + str(self.punched) + " times and kicked " + str(self.kicked) + " times.")
if len(self.crits) > 0:
l.append(" Crits received: " + ", ".join(self.crits))
if len(self.destroyedParts) > 0:
l.append(" Destroyed parts: " + ", ".join(self.destroyedParts))
if self.prone:
l.append(" PRONE")
if self.immobile:
l.append(" IMMOBILE")
if len(self.destroyed) > 0:
l.append(" DESTROYED ("+self.destroyed+")")
return l
def getNextLine():
global data
line = data[0]
data = data[1:]
return line
unitFire = re.compile('Weapons fire for ([^\(]*(?: *\([^\)]+\))?) \(([^\)]+?(?: *\(Reinforcements\))?)\)')
unitPhys = re.compile('Physical attacks for ([^\(]*(?: *\([^\)]+\))?) \(([^\)]+?(?: *\(Reinforcements\))?)\)')
weaponsFire = re.compile('[ ]*([a-zA-Z/0-9 ]+) at ([^\(]*(?: *\([^\)]+\))?) \(([^\)]+?(?: *\(Reinforcements\))?)\); needs \d+, rolls \d+ : (misses|hits)[ ]*(?:\([^\)]+\))?[ ]*([A-Z]+)?')
missileFire = re.compile('[ ]*([a-zA-Z/0-9 ]+) at ([^\(]*(?: *\([^\)]+\))?) \(([^\)]+?(?: *\(Reinforcements\))?)\); needs \d+, rolls \d+ : (misses|\d+ missile\(s\) hit)')
damageTaken = re.compile('[ ]*([^\(]*(?: *\([^\)]+\))?) \(([^\)]+?(?: *\(Reinforcements\))?)\) takes (\d+) damage to ([A-Z]+)')
physAttack = re.compile('[ ]*(Kick|Punch)[ ]*(?:\([^\)]+\))? at ([^\(]*(?: *\([^\)]+\))?) \(([^\)]+?(?: *\(Reinforcements\))?)\); needs \d+, rolls \d+ : (misses|hits)[ ]*(?:\([^\)]+\))?[ ]*([A-Z]+)?')
falling = re.compile('[ ]*([^\(]*(?: *\([^\)]+\))?) \(([^\)]+?(?: *\(Reinforcements\))?)\) falls ')
critical = re.compile('CRITICAL HIT on ([^.]+).')
destroyedParts = re.compile('SECTION DESTROYED')
destroyed = re.compile('\*\*\* ([^\(]*(?: *\([^\)]+\))?) \(([^\)]+?(?: *\(Reinforcements\))?)\) DESTROYED by ([^!]+)! \*\*\*')
transferDamage = re.compile('(\d+) damage transfers to [A-Z]+')
units = dict()
def getUnit(name, faction):
global units
if name+faction not in units:
units[name+faction] = Unit(name, faction)
return units[name+faction]
currentUnit = None
targetUnit = None
physical = False
while len(data) > 0:
line = getNextLine()
# Check if this is a firing line
r = unitFire.match(line)
if r is not None:
physical = False
currentUnit = getUnit(r.group(1), r.group(2))
targetUnit = None
continue
r = unitPhys.match(line)
if r is not None:
physical = True
currentUnit = getUnit(r.group(1), r.group(2))
targetUnit = None
continue
r = weaponsFire.search(line)
if r is not None:
currentUnit.timesFired += 1
targetUnit = getUnit(r.group(2), r.group(3))
targetUnit.timesFiredAt += 1
if r.group(4) == "hits":
currentUnit.timesFiredHit += 1
targetUnit.timesFiredAtHit += 1
r = missileFire.search(line)
if r is not None:
currentUnit.timesFired += 1
targetUnit = getUnit(r.group(2), r.group(3))
targetUnit.timesFiredAt += 1
if r.group(4) != "misses":
currentUnit.timesFiredHit += 1
targetUnit.timesFiredAtHit += 1
continue
r = physAttack.search(line)
if r is not None:
targetUnit = getUnit(r.group(2), r.group(3))
currentUnit.physTarget = targetUnit
if r.group(1) == "Kick":
currentUnit.kick = True
if r.group(4) == "hits":
targetUnit.kicked += 1
elif r.group(1) == "Punch":
currentUnit.punch += 1
if r.group(4) == "hits":
targetUnit.punched += 1
continue
r = falling.search(line)
if r is not None:
currentUnit = None
targetUnit = getUnit(r.group(1), r.group(2))
targetUnit.prone = True
r = damageTaken.search(line)
if r is not None:
targetUnit.damageTaken += int(r.group(3))
targetUnit.lastPartHit = r.group(4)
if currentUnit is None:
continue
if physical:
currentUnit.physDamage += int(r.group(3))
else:
currentUnit.damageDealt += int(r.group(3))
continue
r = transferDamage.search(line)
if r is not None:
targetUnit.damageTaken -= int(r.group(1))
if physical and currentUnit is not None:
currentUnit.physDamage -= int(r.group(1))
elif not physical and currentUnit is not None:
currentUnit.damageDealt -= int(r.group(1))
r = critical.search(line)
if r is not None:
targetUnit.crits.append(r.group(1) + "["+targetUnit.lastPartHit+"]")
r = destroyedParts.search(line)
if r is not None:
targetUnit.destroyedParts.append(targetUnit.lastPartHit)
r = destroyed.search(line)
if r is not None:
unit = getUnit(r.group(1), r.group(2))
unit.destroyed = r.group(3)
factions = {}
def getFaction(name):
global factions
if name not in factions:
factions[name] = {}
return factions[name]
for unit in units.values():
f = getFaction(unit.faction)
f[unit.name] = unit
output = []
sortedFactions = sorted(factions.keys())
for factionName in sortedFactions:
unitList = getFaction(factionName)
sortedUnitList = sorted(unitList.keys())
for unitName in sortedUnitList:
output = output + unitList[unitName].asList() + [""]
with open ("round2.txt", "w") as myfile:
myfile.write("\n".join(output))
Could someone explain the two types of ratings to me? Does it make a difference which one you've selected? The official rules seem to mostly reference the dragoons letter rating, but the tutorials I've seen tell us to use the other one. Not sure what to do.
Also, once your admins are strong enough and it'd take 40xp to increase administration, definitely consider upping the negotiation skill! Turns out this allows you to reroll parts of the contracts, which can be an awesome way to buff them up, especially if you got some unlucky rolls.
Also, looking forward to seeing Zhou's lance totally not get ripped to pieces