wowlog 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 23fa9df161b2b00445e4683a520d31bc45ca4565
4
+ data.tar.gz: 80b89738db900bbbc15e23f806a9f471d19e0538
5
+ SHA512:
6
+ metadata.gz: 706c78624bd52c03cea003e96f4a878a90cd6b233e32292efb6d0f6f393234a83d45690c4552ebd70984fe475d7aa00f8e748974be67e11c928d41ec05f066ca
7
+ data.tar.gz: 8bf1b59be8198cf441f66edcada09623e55c293b3be889e5eb5ccbbba7d5123124d7d95e213cdf91e2cfeeb9c28c1d5cf8fb0601b8d30f9881877d86738cafc8
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wowlog.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Masayoshi Mizutani
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # Wowlog
2
+
3
+ Wowlog is Parser Library for World of Warcraft Combat Log to analyze your combat.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'wowlog'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install wowlog
18
+
19
+ ## Usage
20
+
21
+ require 'wowlog'
22
+ psr = Wowlog::Parser.new
23
+ File.open('/path/to/WowCombatLog.txt', 'r') do |fd|
24
+ fd.each do |line|
25
+ ev = psr.parse_line(line)
26
+ puts ev
27
+ # => {"event"=>"SPELL_HEAL", "sourceGUID"=>"0x0300000007F97AFF", "sourceName"=>"Muret", ...}
28
+ end
29
+ end
30
+
31
+
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it ( https://github.com/[my-github-username]/wowlog/fork )
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Copyright (c) 2014 Masayoshi Mizutani <muret@haeena.net>
5
+ # All rights reserved.
6
+ # *
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18
+ # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ # POSSIBILITY OF SUCH DAMAGE.
27
+ #
28
+
29
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
30
+ require 'wowlog'
31
+ require 'pp'
32
+
33
+ psr = Wowlog::Parser.new
34
+
35
+ File.open(ARGV[0], 'r') { |fd|
36
+ out_fd = nil
37
+ fname = nil
38
+ fd.each { |line|
39
+ ev = psr.parse_line(line)
40
+
41
+ if ev['event'] == 'ENCOUNTER_START'
42
+ enc_name = ev['encounterName'].gsub(/\s/, '_')
43
+ fname = "WowCombatLog_#{ev['timestamp']}_#{enc_name}_#{ev['groupSize']}man.txt"
44
+ out_fd = File.open(fname, "w")
45
+ puts "Open: #{fname}"
46
+ end
47
+
48
+ if !(out_fd.nil?)
49
+ out_fd.write(line)
50
+ end
51
+
52
+ if ev['event'] == 'ENCOUNTER_END'
53
+ puts "Close: #{fname}"
54
+ out_fd.close
55
+ out_fd = nil
56
+ end
57
+ }
58
+ }
59
+
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Copyright (c) 2014 Masayoshi Mizutani <muret@haeena.net>
5
+ # All rights reserved.
6
+ # *
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18
+ # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ # POSSIBILITY OF SUCH DAMAGE.
27
+ #
28
+
29
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
30
+ require 'wowlog'
31
+ require 'pp'
32
+
33
+ psr = Wowlog::Parser.new
34
+
35
+ File.open(ARGV[0], 'r') { |fd|
36
+ fd.each { |line|
37
+ ev = psr.parse_line(line)
38
+ begin
39
+ pp ev
40
+ rescue => e
41
+ # ignore
42
+ end
43
+ }
44
+ }
@@ -0,0 +1,485 @@
1
+ #
2
+ # Copyright (c) 2014 Masayoshi Mizutani <muret@haeena.net>
3
+ # All rights reserved.
4
+ # *
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions
7
+ # are met:
8
+ # 1. Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # 2. Redistributions in binary form must reproduce the above copyright
11
+ # notice, this list of conditions and the following disclaimer in the
12
+ # documentation and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
+ # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16
+ # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+ #
26
+
27
+ require "wowlog/version"
28
+ require 'csv'
29
+ require 'pp'
30
+
31
+ $unit_stat = Hash.new { |h,k| h[k] = 0 }
32
+ $unit_none = 0
33
+
34
+
35
+ module Wowlog
36
+
37
+ #
38
+ # ---------------------------------------------------------
39
+ # Basic class for all column parser
40
+ # ---------------------------------------------------------
41
+ #
42
+
43
+ class ColumnParser
44
+ UNIT_FLAG_MAP = {
45
+ 0x00000001 => 'AFFILIATION_MINE',
46
+ 0x00000002 => 'AFFILIATION_PARTY',
47
+ 0x00000004 => 'AFFILIATION_RAID',
48
+ 0x00000008 => 'AFFILIATION_OUTSIDER',
49
+ 0x00000010 => 'REACTION_FRIENDLY',
50
+ 0x00000020 => 'REACTION_NEUTRAL',
51
+ 0x00000040 => 'REACTION_HOSTILE',
52
+ 0x00000100 => 'CONTROL_PLAYER',
53
+ 0x00000200 => 'CONTROL_NPC',
54
+ 0x00000400 => 'TYPE_PLAYER',
55
+ 0x00000800 => 'TYPE_NPC',
56
+ 0x00001000 => 'TYPE_PET',
57
+ 0x00002000 => 'TYPE_GUARDIAN',
58
+ 0x00004000 => 'TYPE_OBJECT',
59
+ }
60
+
61
+ OBJECT_SPECIAL_MASK = 0xFFFF0000
62
+ UNIT_SPECIAL_FLAG_MAP = {
63
+ 0x00010000 => 'TARGET',
64
+ 0x00020000 => 'FOCUS',
65
+ 0x00040000 => 'MAINTANK',
66
+ 0x00080000 => 'MAINASSIST',
67
+ 0x00100000 => 'RAIDTARGET1',
68
+ 0x00200000 => 'RAIDTARGET2',
69
+ 0x00400000 => 'RAIDTARGET3',
70
+ 0x00800000 => 'RAIDTARGET4',
71
+ 0x01000000 => 'RAIDTARGET5',
72
+ 0x02000000 => 'RAIDTARGET6',
73
+ 0x04000000 => 'RAIDTARGET7',
74
+ 0x08000000 => 'RAIDTARGET8',
75
+ 0x80000000 => 'NONE',
76
+ }
77
+
78
+ SCHOOL_FLAG_MAP = {
79
+ 0x1 => 'Physical',
80
+ 0x2 => 'Holy',
81
+ 0x4 => 'Fire',
82
+ 0x8 => 'Nature',
83
+ 0x10 => 'Frost',
84
+ 0x20 => 'Shadow',
85
+ 0x40 => 'Arcane',
86
+ }
87
+
88
+ PT_MAP = {
89
+ -2 => 'health',
90
+ 0 => 'mana',
91
+ 1 => 'rage',
92
+ 2 => 'focus',
93
+ 3 => 'energy',
94
+ 4 => 'pet happiness',
95
+ 5 => 'runes',
96
+ 6 => 'runic power'
97
+ }
98
+
99
+ def initialize
100
+ @flag_cache = {}
101
+ end
102
+
103
+ def parse_unit_flag(val)
104
+ f = val.hex
105
+ return [] if f == 0
106
+ return @flag_cache[f] unless @flag_cache[f].nil?
107
+
108
+ res = []
109
+ UNIT_FLAG_MAP.each { |k, v| res.push(v) if (f & k) > 0 }
110
+ if (f & OBJECT_SPECIAL_MASK) > 0
111
+ UNIT_SPECIAL_FLAG_MAP.each { |k, v| res.push(v) if (f & k) > 0 }
112
+ end
113
+
114
+ @flag_cache[f] = res
115
+ return res
116
+ end
117
+
118
+ def parse_school_flag(val)
119
+ f = val.hex
120
+ res = SCHOOL_FLAG_MAP.select { |k, v| (f & k) > 0 }
121
+ return res.values
122
+ end
123
+
124
+ def resolv_power_type(pt); return PT_MAP[pt]; end
125
+
126
+
127
+ def int(v); return v.to_i; end
128
+ def parse(cols, obj = {}); return cols, obj; end
129
+ end
130
+
131
+ class EventParser < ColumnParser
132
+ def parse(cols, obj = {})
133
+ obj['event'] = cols.shift
134
+ return cols, obj
135
+ end
136
+ end
137
+
138
+ class EncountEvent < EventParser
139
+ def parse(cols, obj); end
140
+ end
141
+
142
+ class ActionEvent < EventParser
143
+ def parse(cols, obj)
144
+ cols, obj = super(cols, obj)
145
+ obj['sourceGUID'] = cols.shift
146
+ obj['sourceName'] = cols.shift
147
+ obj['sourceFlags'] = parse_unit_flag(cols.shift)
148
+ obj['sourceFlags2'] = parse_unit_flag(cols.shift)
149
+ obj['destGUID'] = cols.shift
150
+ obj['destName'] = cols.shift
151
+ obj['destFlags'] = parse_unit_flag(cols.shift)
152
+ obj['destFlags2'] = parse_unit_flag(cols.shift)
153
+ return cols, obj
154
+ end
155
+ end
156
+
157
+ #
158
+ # ---------------------------------------------------------
159
+ # Prefix Parser Set
160
+ # ---------------------------------------------------------
161
+ #
162
+
163
+ class SpellParser < ActionEvent
164
+ def parse(cols, obj)
165
+ cols, obj = super(cols, obj)
166
+ obj['spellId'] = cols.shift
167
+ obj['spellName'] = cols.shift
168
+ obj['spellSchool'] = parse_school_flag(cols.shift)
169
+ return cols, obj
170
+ end
171
+ end
172
+
173
+ class SwingParser < ActionEvent
174
+ def parse(cols, obj); return super(cols, obj); end
175
+ end
176
+
177
+ class EnvParser < ActionEvent
178
+ def parse(cols, obj)
179
+ cols, obj = super(cols, obj)
180
+ obj['environmentalType'] = cols.shift
181
+ return cols, obj
182
+ end
183
+ end
184
+
185
+ #
186
+ # ---------------------------------------------------------
187
+ # Suffix Parser Set
188
+ # ---------------------------------------------------------
189
+ #
190
+
191
+ class DamageParser < ColumnParser
192
+ def parse(cols, obj)
193
+ cols, obj = super(cols, obj)
194
+ cols.shift(8) # shift 8 columns because unknown parameters
195
+
196
+ obj['amount'] = int(cols.shift)
197
+ obj['overkill'] = cols.shift
198
+ obj['school'] = parse_school_flag(cols.shift)
199
+ obj['resisted'] = int(cols.shift)
200
+ obj['blocked'] = int(cols.shift)
201
+ obj['absorbed'] = int(cols.shift)
202
+ obj['critical'] = (cols.shift != 'nil')
203
+ obj['glancing'] = (cols.shift != 'nil')
204
+ obj['crushing'] = (cols.shift != 'nil')
205
+ return cols, obj
206
+ end
207
+ end
208
+
209
+ class MissParser < ColumnParser
210
+ def parse(cols, obj)
211
+ cols, obj = super(cols, obj)
212
+ obj['missType'] = cols.shift
213
+ obj['isOffHand'] = cols.shift if cols.size > 0
214
+ obj['amountMissed'] = cols.shift if cols.size > 0
215
+ return cols, obj
216
+ end
217
+ end
218
+
219
+ class HealParser < ColumnParser
220
+ def parse(cols, obj)
221
+ cols, obj = super(cols, obj)
222
+ cols.shift(8) # shift 8 columns because unknown parameters
223
+
224
+ obj['amount'] = int(cols[0])
225
+ obj['overhealing'] = int(cols[1])
226
+ obj['absorbed'] = int(cols[2])
227
+ obj['critical'] = (cols[3] != 'nil')
228
+ cols.shift(4)
229
+ return cols, obj
230
+ end
231
+ end
232
+
233
+ class EnergizeParser < ColumnParser
234
+ def parse(cols, obj)
235
+ cols, obj = super(cols, obj)
236
+ cols.shift(8) # shift 8 columns because unknown parameters
237
+
238
+ obj['amount'] = int(cols[0])
239
+ obj['powerType'] = resolv_power_type(cols[1])
240
+ cols.shift(2)
241
+ return cols, obj
242
+ end
243
+ end
244
+
245
+ class DrainParser < ColumnParser
246
+ def parse(cols, obj)
247
+ cols, obj = super(cols, obj)
248
+ obj['amount'] = int(cols[0])
249
+ obj['powerType'] = resolv_power_type(cols[1])
250
+ obj['extraAmount'] = int(cols[2])
251
+ cols.shift(3)
252
+ return cols, obj
253
+ end
254
+ end
255
+
256
+ class LeechParser < ColumnParser
257
+ def parse(cols, obj)
258
+ cols, obj = super(cols, obj)
259
+ obj['amount'] = int(cols[0])
260
+ obj['powerType'] = resolv_power_type(cols[1])
261
+ obj['extraAmount'] = int(cols[2])
262
+ cols.shift(3)
263
+ return cols, obj
264
+ end
265
+ end
266
+
267
+ class SpellBlockParser < ColumnParser
268
+ def parse(cols, obj)
269
+ cols, obj = super(cols, obj)
270
+ obj['extraSpellID'] = cols[0]
271
+ obj['extraSpellName'] = cols[1]
272
+ obj['extraSchool'] = parse_school_flag(cols[2])
273
+ cols.shift(3)
274
+
275
+ obj['auraType'] = cols.shift if cols.size > 0
276
+ return cols, obj
277
+ end
278
+ end
279
+
280
+ class ExtraAttackParser < ColumnParser
281
+ def parse(cols, obj)
282
+ cols, obj = super(cols, obj)
283
+ obj['amount'] = int(cols.shift)
284
+ return cols, obj
285
+ end
286
+ end
287
+
288
+ class AuraParser < ColumnParser
289
+ def parse(cols, obj)
290
+ cols, obj = super(cols, obj)
291
+ obj['auraType'] = cols.shift
292
+ obj['amount'] = int(cols.shift) if cols.size > 0
293
+ obj['auraExtra1'] = cols.shift if cols.size > 0
294
+ obj['auraExtra2'] = cols.shift if cols.size > 0
295
+ return cols, obj
296
+ end
297
+ end
298
+
299
+ class AuraDoseParser < ColumnParser
300
+ def parse(cols, obj)
301
+ cols, obj = super(cols, obj)
302
+ obj['auraType'] = cols.shift
303
+ obj['powerType'] = resolv_power_type(cols.shift) if cols.size > 0
304
+ return cols, obj
305
+ end
306
+ end
307
+
308
+ class AuraBrokenParser < ColumnParser
309
+ def parse(cols, obj)
310
+ cols, obj = super(cols, obj)
311
+ obj['extraSpellID'] = cols.shift
312
+ obj['extraSpellName'] = cols.shift
313
+ obj['extraSchool'] = parse_school_flag(cols.shift)
314
+ obj['auraType'] = cols.shift
315
+ return cols, obj
316
+ end
317
+ end
318
+
319
+ class CastFailedParser < ColumnParser
320
+ def parse(cols, obj)
321
+ cols, obj = super(cols, obj)
322
+ obj['failedType'] = cols.shift
323
+ return cols, obj
324
+ end
325
+ end
326
+
327
+ #
328
+ # ---------------------------------------------------------
329
+ # Special Event Parser Set
330
+ # ---------------------------------------------------------
331
+ #
332
+
333
+ class EnchantParser < ColumnParser
334
+ def parse(cols, obj)
335
+ cols, obj = super(cols, obj)
336
+ obj['spellName'] = cols[0]
337
+ obj['itemID'] = cols[1]
338
+ obj['itemName'] = cols[2]
339
+ cols.shift(3)
340
+ return cols, obj
341
+ end
342
+ end
343
+
344
+
345
+ class EncountParser < EventParser
346
+ def parse(cols, obj)
347
+ cols, obj = super(cols, obj)
348
+ obj['encounterID'] = cols[0]
349
+ obj['encounterName'] = cols[1]
350
+ obj['difficultyID'] = cols[2]
351
+ obj['groupSize'] = cols[3]
352
+ cols.shift(4)
353
+
354
+ obj['success'] = (cols.shift == '1') if cols.size > 0
355
+ return cols, obj
356
+ end
357
+ end
358
+
359
+ class VoidParser < ColumnParser
360
+ def parse(cols, obj); return super(cols, obj); end
361
+ end
362
+
363
+ #
364
+ # ---------------------------------------------------------
365
+ # Main Parser
366
+ # ---------------------------------------------------------
367
+ #
368
+ class Parser
369
+ def initialize
370
+ @ev_prefix = {
371
+ 'SWING' => [SwingParser.new],
372
+ 'SPELL_BUILDING' => [SpellParser.new],
373
+ 'SPELL_PERIODIC' => [SpellParser.new],
374
+ 'SPELL' => [SpellParser.new],
375
+ 'RANGE' => [SpellParser.new],
376
+ 'ENVIRONMENTAL' => [EnvParser.new],
377
+ 'DAMAGE_SHIELD' => [SpellParser.new, DamageParser.new],
378
+ 'DAMAGE_SPLIT' => [SpellParser.new, DamageParser.new],
379
+ 'DAMAGE_SHIELD_MISSED' => [SpellParser.new, MissParser.new],
380
+ 'ENCHANT_APPLIED' => [EnchantParser.new],
381
+ 'ENCHANT_REMOVED' => [EnchantParser.new],
382
+ 'PARTY_KILL' => [VoidParser.new],
383
+ 'UNIT_DIED' => [VoidParser.new],
384
+ 'UNIT_DESTROYED' => [VoidParser.new],
385
+ 'ENCOUNTER_START' => [EncountParser.new],
386
+ 'ENCOUNTER_END' => [EncountParser.new],
387
+ }
388
+
389
+ @ev_suffix = {
390
+ '_DAMAGE' => DamageParser.new,
391
+ '_MISSED' => MissParser.new,
392
+ '_HEAL' => HealParser.new,
393
+ '_ENERGIZE' => EnergizeParser.new,
394
+ '_DRAIN' => DrainParser.new,
395
+ '_LEECH' => LeechParser.new,
396
+ '_INTERRUPT' => SpellBlockParser.new,
397
+ '_DISPEL' => SpellBlockParser.new,
398
+ '_DISPEL_FAILED' => SpellBlockParser.new,
399
+ '_STOLEN' => SpellBlockParser.new,
400
+ '_EXTRA_ATTACKS' => ExtraAttackParser.new,
401
+ '_AURA_APPLIED' => AuraParser.new,
402
+ '_AURA_REMOVED' => AuraParser.new,
403
+ '_AURA_APPLIED_DOSE' => AuraDoseParser.new,
404
+ '_AURA_REMOVED_DOSE' => AuraDoseParser.new,
405
+ '_AURA_REFRESH' => AuraDoseParser.new,
406
+ '_AURA_BROKEN' => AuraParser.new,
407
+ '_AURA_BROKEN_SPELL' => AuraBrokenParser.new,
408
+ '_CAST_START' => nil,
409
+ '_CAST_SUCCESS' => nil,
410
+ '_CAST_FAILED' => CastFailedParser.new,
411
+ '_INSTAKILL' => nil,
412
+ '_DURABILITY_DAMAGE' => nil,
413
+ '_DURABILITY_DAMAGE_ALL' => nil,
414
+ '_CREATE' => nil,
415
+ '_SUMMON' => nil,
416
+ '_RESURRECT' => nil,
417
+ }
418
+ end
419
+
420
+ def parse_cols(cols)
421
+ orig_txt = cols.join(',')
422
+ ev_orig = cols[0]
423
+ event = cols[0]
424
+
425
+ psr_seq = []
426
+ p_psr = @ev_prefix.inject(['', nil]) { |m, (k, v)|
427
+ (event.start_with?(k) and m[0].size < k.size) ? [k, v] : m
428
+ }
429
+ psr_seq += p_psr[1]
430
+
431
+ event = event[(p_psr[0].size)..-1]
432
+ s_psr = @ev_suffix.inject(['', nil]) { |m, (k, v)|
433
+ (event.start_with?(k) and m[0].size < k.size) ? [k, v] : m
434
+ }
435
+ psr_seq << s_psr[1] unless s_psr[1].nil?
436
+
437
+ obj = {}
438
+ psr_seq.each do |psr|
439
+ cols, obj = psr.parse(cols, obj)
440
+ end
441
+
442
+ return obj
443
+ end
444
+
445
+ def parse_line(line)
446
+ terms = line.split(' ')
447
+ raise "Invalid format, '#{line.strip}'" if terms.size < 3
448
+
449
+ # Parse timestamp and adjust error of milli second
450
+ datetime = terms[0..1].join(' ')
451
+ ms = datetime.scan(/\.(\d+)$/)[0][0]
452
+ parsed_ts = Time.parse(datetime)
453
+ ts = parsed_ts.to_i.to_f + (ms.to_f / 1000)
454
+
455
+ # rebuild CSV part
456
+ csv_txt = terms[2..-1].join(' ')
457
+ cols = CSV.parse(csv_txt)[0]
458
+
459
+ # parse CSV part
460
+ obj = parse_cols(cols)
461
+ obj['timestamp'] = ts
462
+ return obj
463
+ end
464
+ end
465
+
466
+ #
467
+ # ---------------------------------------------------------
468
+ # Utilities
469
+ # ---------------------------------------------------------
470
+ #
471
+
472
+ SCHOOL_COLOR = {
473
+ "Physical" => "#FFFF00",
474
+ "Holy" => "#FFE680",
475
+ "Fire" => "#FF8000",
476
+ "Nature" => "#4DFF4D",
477
+ "Frost" => "#80FFFF",
478
+ "Shadow" => "#8080FF",
479
+ "Arcane" => "#FF80FF",
480
+ }
481
+ def resolv_school_color(school)
482
+ return SCHOOL_COLOR[school]
483
+ end
484
+
485
+ end
@@ -0,0 +1,3 @@
1
+ module Wowlog
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'wowlog/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "wowlog"
8
+ spec.version = Wowlog::VERSION
9
+ spec.authors = ["Masayoshi Mizutani"]
10
+ spec.email = ["muret@haeena.net"]
11
+ spec.summary = %q{World of Warcraft Combat Log Parser}
12
+ spec.description = %q{Wowlog is Parser Library for World of Warcraft Combat Log to analyze your combat.}
13
+ spec.homepage = "https://github.com/m-mizutani/wowlog"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency 'rake', '~> 0'
23
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wowlog
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Masayoshi Mizutani
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Wowlog is Parser Library for World of Warcraft Combat Log to analyze
42
+ your combat.
43
+ email:
44
+ - muret@haeena.net
45
+ executables:
46
+ - wl_extract_encount
47
+ - wowlogpp
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - ".gitignore"
52
+ - Gemfile
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - bin/wl_extract_encount
57
+ - bin/wowlogpp
58
+ - lib/wowlog.rb
59
+ - lib/wowlog/version.rb
60
+ - wowlog.gemspec
61
+ homepage: https://github.com/m-mizutani/wowlog
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.2.2
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: World of Warcraft Combat Log Parser
85
+ test_files: []