319 lines
12 KiB
Python
Executable File
319 lines
12 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import os, re, sys
|
|
from time import strftime
|
|
from datetime import datetime
|
|
|
|
#import matplotlib.pyplot as plt
|
|
|
|
## Declaring the points for first line plot
|
|
#X1 = [1,2,3,4,5]
|
|
#Y1 = [2,4,6,8,10]
|
|
|
|
## Setting the figure size
|
|
#fig = plt.figure(figsize=(10,5))
|
|
|
|
## plotting the first plot
|
|
#plt.plot(X1, Y1, label = "plot 1")
|
|
|
|
## Declaring the points for second line plot
|
|
#X2 = [1,2,3,4,5]
|
|
#Y2 = [1,4,9,16,25]
|
|
|
|
## plotting the second plot
|
|
#plt.plot(X2, Y2, label = "plot 2")
|
|
|
|
## Labeling the X-axis
|
|
#plt.xlabel('X-axis')
|
|
|
|
## Labeling the Y-axis
|
|
#plt.ylabel('Y-axis')
|
|
|
|
## Give a title to the graph
|
|
#plt.title('Two plots on the same graph')
|
|
|
|
## Show a legend on the plot
|
|
#plt.legend()
|
|
|
|
##Saving the plot as an image
|
|
#fig.savefig('line plot.png', bbox_inches='tight', dpi=150)
|
|
|
|
##Showing the plot
|
|
#plt.show()
|
|
|
|
|
|
|
|
|
|
# [03/16/2012 22:53:45] [@Asdffljklasdfj] [@Asdffljklasdfj] [Unnatural Might {1781509484707840}] [RemoveEffect {836045448945478}: Unnatural Might {1781509484707840}] ()
|
|
# [21:18:16.172] [@Comendatore] [@Comendatore] [] [Event {836045448945472}: EnterCombat {836045448945489}] (Asation (8 Player Story))
|
|
#[date] [src] [target] [ability] [effect] (value) <threat>
|
|
#src = [foo {id}] or [@player]
|
|
# ability = abil {id}
|
|
# effect = type:effect
|
|
# value = # or # type
|
|
# roll? int
|
|
|
|
listEncounters = [
|
|
## The Dread Fortress
|
|
'Nefra, Who Bars the Way {3266533082005504}','Gate Commander Draxus {3273924720721920}','Grob\'thok, Who Feeds the Forge {3273929015689216}','Corruptor Zero {3273933310656512}','Dread Master Brontes {3273937605623808}',
|
|
## Eternity Vault
|
|
'Soa {2289823159156736}',
|
|
##
|
|
'The Writhing Horror {2938874321960960}','Heirad {2938002443599872}','Ciphas {2938011033534464}','Kel\'sara {2938006738567168}','Operator IX {2942606648541184}','Kephess the Undying {2937620191510528}','The Terror From Beyond {2978340776443904}','The Terror From Beyond {3025233229381632}',
|
|
##
|
|
'The Eyeless {3328380611067904}','Golden Fury {3232800408862720}','Golden Fury {3232817588731904}',
|
|
## The False Emperor
|
|
'Tregg the Destroyer {1690314444111872}','Jindo Krey {770959514533888}','HK-47 {770955219566592}','Chondrus Berani {1690331623981056}','Sith Entity {1695962326106112}','Forsaken Sith Lord {781830076760064}','Darth Malgus {770963809501184}',
|
|
## Battle of Rishi
|
|
'Marko Ka {3533564083699712}','Rarrook {3533559788732416}','Lord Vodd {3533585558536192}','Shield Squadron Unit 1 {3533594148470784}','Darth Yun {4257506591244288}',
|
|
## Objective Meridian
|
|
'Commander Aster {4258021987319808}','Darth Malgus {4257502296276992}',
|
|
## Heart of Ruin
|
|
'Colossal Monolith {3541140406009856}',
|
|
##
|
|
'Toxxun {3340011382505472}',
|
|
## Mutated Geonosian Queen
|
|
'Mutated Geonosian Queen {4197299739688960}',
|
|
##
|
|
'SCYVA {4108140513591296}'
|
|
]
|
|
|
|
dmgTypes = ['energy','elemental','kinetic','internal']
|
|
instTypes = ['Story','Veteran','Master']
|
|
zoneNames = [
|
|
# Planet zones
|
|
'Corellia','Coruscant','CZ-198','Rishi','Taris','Tatooine','Voss','Yavin 4','Ziost',
|
|
# Instances zones
|
|
'Asation','Lair of the Eyeless','Toborro\'s Palace Courtyard','The False Emperor','Battle of Rishi','Objective Meridian','Hive of the Mountain Queen',
|
|
# Operations Zones
|
|
'Valley of the Machine Gods'
|
|
]
|
|
|
|
class Event:
|
|
regex = r'[\[<\(]([^\[<\(\]>\)]*)[\]>\)]'
|
|
def __init__(self, x):
|
|
data = re.findall(Event.regex, x)
|
|
data.append('')
|
|
self.to_string = ' '.join(data)
|
|
|
|
dateandtime = datetime.strptime(list(sys.argv[1].split("_"))[1]+" "+data[0], '%Y-%m-%d %H:%M:%S.%f')
|
|
|
|
self.date = dateandtime
|
|
self.source = Entity(data[1])
|
|
self.target = Entity(data[2])
|
|
self.ability = Ability(data[3])
|
|
self.effect = Effect(data[4])
|
|
self.value = Value(data[5])
|
|
self.threat = Threat(data[6])
|
|
|
|
class Entity:
|
|
def __init__(self, x):
|
|
if not x:
|
|
self.name = x
|
|
self.player = False
|
|
#print(x+" is not a player !")
|
|
elif x[0] == '@':
|
|
self.name = x[1:]
|
|
self.player = True
|
|
#print(x+" is a player !")
|
|
else:
|
|
self.name = x
|
|
self.player = False
|
|
#print(x+" is not a player !")
|
|
|
|
class Ability:
|
|
def __init__(self, x):
|
|
self.name = x
|
|
|
|
class Effect:
|
|
def __init__(self, x):
|
|
if not x:
|
|
self.type = ''
|
|
self.detail = ''
|
|
return
|
|
foo = x.split(':')
|
|
if len(foo) > 1:
|
|
self.type = foo[0]
|
|
self.detail = foo[1]
|
|
else:
|
|
self.type = x
|
|
self.detail = ''
|
|
|
|
#class Value:
|
|
#def __init__(self, x):
|
|
#if not x:
|
|
#self.value = 0
|
|
#self.type = ''
|
|
#return
|
|
#foo = x.replace('*','').split(' ')
|
|
#self.value = int(foo[0])
|
|
#if len(foo) > 1:
|
|
#self.type = foo[1]
|
|
#else:
|
|
#self.type=''
|
|
|
|
# [20:06:53.053] [@Comendatore] [@Comendatore] [] [Event {836045448945472}: EnterCombat {836045448945489}] (Ziost)
|
|
# [21:31:45.974] [@Comendatore] [@Comendatore] [] [Event {836045448945472}: EnterCombat {836045448945489}] (Hive of the Mountain Queen (8 Player Story))
|
|
# [21:32:18.105] [@Bidobulle] [@Comendatore] [Slow-release Medpac {3406415871868928}] [ApplyEffect {836045448945477}: Heal {836045448945500}] (5683*)
|
|
# [21:32:19.499] [@Bidobulle] [@Comendatore] [Slow-release Medpac {3406415871868928}] [ApplyEffect {836045448945477}: Heal {836045448945500}] (3641)
|
|
# [22:37:11.036] [@Comendatore] [Colossal Monolith {3541140406009856}:15814000004760] [Shocked {2044520397013249}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (165 energy {836045448940874}) <1646>
|
|
# [22:37:46.168] [@Comendatore] [Colossal Monolith {3541140406009856}:15814000004760] [Ion Storm {3389987621961728}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (20879* elemental {836045448940875}) <52197>
|
|
# [22:37:51.623] [Colossal Monolith {3541140406009856}:15814000004760] [@Comendatore] [Great Slam {3543287889657856}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (86 kinetic {836045448940873} -shield {836045448945509} (86 absorbed {836045448945511})) <86>
|
|
# [22:38:30.231] [Colossal Monolith {3541140406009856}:15814000004760] [@Comendatore] [Bleeding {3546045258662192}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (10668 internal {836045448940876}) <10668>
|
|
|
|
class Value:
|
|
def __init__(self, x):
|
|
if not x:
|
|
self.value = 0
|
|
self.type = ''
|
|
return
|
|
foo = x.replace('*','')
|
|
#print(foo)
|
|
#if not re.search('[a-zA-Z]+', x):
|
|
#print(foo)
|
|
if any(z in foo for z in dmgTypes) and not any(y in foo for y in instTypes) or not any(w in foo for w in zoneNames) or foo.isdigit():
|
|
foo = x.replace('*','').split(' ')
|
|
if foo[0].isdigit():
|
|
self.value = int(foo[0])
|
|
else:
|
|
self.value = 0
|
|
if len(foo) > 1:
|
|
self.type = foo[1]
|
|
else:
|
|
self.type=''
|
|
else:
|
|
#print(foo)
|
|
self.type = ''
|
|
|
|
class Threat:
|
|
def __init__(self, x):
|
|
if not x:
|
|
self.value = 0
|
|
self.type = ''
|
|
return
|
|
foo = x.replace('*','').split(' ')
|
|
self.value = int(foo[0])
|
|
if len(foo) > 1:
|
|
self.type = foo[1]
|
|
else:
|
|
self.type=''
|
|
|
|
#date, source, target, ability, effect.type/detail, value.value/type, threat
|
|
# TODO:
|
|
# multiple sources
|
|
# more interesting output, graphs? What about that metric site? Google charts?
|
|
# App engine seems fun
|
|
# Details for abilities:
|
|
# dmg per ability
|
|
# Combine multiple entries per ability
|
|
def main():
|
|
if len(sys.argv) == 1:
|
|
filename = 'combat_2020-07-25_22_39_58_532658.txt'
|
|
print('File not specified, defaulting to ' + filename)
|
|
else:
|
|
filename = sys.argv[1]
|
|
file = open(filename, 'r')
|
|
events = []
|
|
for line in file.readlines():
|
|
events.append(Event(line))
|
|
|
|
players = set([event.source.name for event in events])
|
|
fights = []
|
|
isBoss = False
|
|
fight = []
|
|
for event in events:
|
|
if 'EnterCombat' in event.effect.detail:
|
|
fight = []
|
|
#fight.extend(fight)
|
|
#isBoss = False
|
|
|
|
if 'Death' in event.effect.detail:
|
|
#print(event.effect.detail)
|
|
#if any(t in event.target.name for t in listEncounters):
|
|
fight.append(event)
|
|
#print(listEncounters)
|
|
#isBoss = False
|
|
for event in fight:
|
|
##print(event.source.name)
|
|
if any(t in event.target.name for t in listEncounters):
|
|
isBoss = True
|
|
if isBoss:
|
|
fights.append(fight)
|
|
fight = []
|
|
continue
|
|
|
|
if 'ExitCombat' in event.effect.detail:
|
|
#if any(t in event.effect.detail for t in ['ExitCombat','Death']):
|
|
fight.append(event)
|
|
#print(listEncounters)
|
|
isBoss = False
|
|
for event in fight:
|
|
#print(event.source.name)
|
|
if any(t in event.target.name for t in listEncounters):
|
|
isBoss = True
|
|
if isBoss:
|
|
fights.append(fight)
|
|
fight = []
|
|
continue
|
|
|
|
fight.append(event)
|
|
|
|
for i, fight in enumerate(fights):
|
|
duration = fight[-1].date - fight[0].date
|
|
for event in fight:
|
|
if any(t in event.target.name for t in listEncounters):
|
|
namedTarget = event.target.name.split(' {')
|
|
namedTarget = namedTarget[0]
|
|
#print('\nCombat Statistics for Fight # %d (duration: %s)' %(i+1, str(duration)))
|
|
print('\nCombat Statistics for Fight #%d -> %s (%s)' %(i+1, namedTarget, str(duration)))
|
|
print("Started: " + str(fight[0].date) +" Ended: " + str(fight[-1].date))
|
|
for player in players:
|
|
if '{' not in player:
|
|
dmg = 0
|
|
tdmg = 0
|
|
heal = 0
|
|
theal = 0
|
|
threat = 0
|
|
death = False
|
|
for event in fight:
|
|
## Domages done by player (DPS)
|
|
if event.source.name == player and 'Damage ' in event.effect.detail:
|
|
dmg += event.value.value
|
|
## Domages taken by player (DTPS)
|
|
if event.target.name == player and 'Damage ' in event.effect.detail:
|
|
tdmg += event.value.value
|
|
## Heal done by player (HPS)
|
|
if event.source.name == player and 'Heal ' in event.effect.detail:
|
|
heal += event.value.value
|
|
## Heal received by player (HTPS)
|
|
if event.target.name == player and 'Heal ' in event.effect.detail:
|
|
theal += event.value.value
|
|
## Generated threat by player (TPS)
|
|
if event.source.name == player and event.threat.value:
|
|
threat += event.threat.value
|
|
## Dead
|
|
if event.source.name == player and 'Death' in event.effect.detail:
|
|
death = True
|
|
|
|
secs = duration.seconds
|
|
|
|
if not secs:
|
|
secs = 1
|
|
if dmg:
|
|
print('%s did %d damage in %s (%d DPS)' % (player, dmg, str(duration), dmg/secs))
|
|
if tdmg:
|
|
print('%s took %d damage in %s (%d DTPS)' % (player, tdmg, str(duration), tdmg/secs))
|
|
if heal:
|
|
print('%s did %d heals in %s (%d HPS)' % (player, heal, str(duration), heal/secs))
|
|
if theal:
|
|
print('%s received %d heals in %s (%d HTPS)' % (player, theal, str(duration), theal/secs))
|
|
if threat:
|
|
print('%s did %d threat in %s (%d TPS)' % (player, threat, str(duration), threat/secs))
|
|
if death:
|
|
print('%s died on %s' % (player, namedTarget))
|
|
|
|
|
|
#print("\n".join([event.to_string for event in events]))
|
|
if __name__ == "__main__":
|
|
main()
|