From ea795f0d34bf0e3e993f869f3f32c520445ca088 Mon Sep 17 00:00:00 2001 From: gered Date: Sat, 27 Feb 2016 16:14:16 -0500 Subject: [PATCH] add parser code from old test project sources --- vwowrla.core/project.clj | 3 +- vwowrla.core/resources/encounters.edn | 38 + .../resources/non_combat_starting_auras.txt | 3 + .../resources/non_combat_starting_skills.txt | 4 + .../resources/problem_entity_names.txt | 89 ++ vwowrla.core/src/vwowrla/core/encounters.clj | 464 +++++++++++ vwowrla.core/src/vwowrla/core/handlers.clj | 161 ++++ vwowrla.core/src/vwowrla/core/matchers.clj | 765 ++++++++++++++++++ vwowrla.core/src/vwowrla/core/parser.clj | 86 ++ vwowrla.core/src/vwowrla/core/preparsing.clj | 73 ++ vwowrla.core/src/vwowrla/core/utils.clj | 75 ++ .../core/matchers/aura_gained_test.clj | 102 +++ .../vwowrla/core/matchers/aura_lost_test.clj | 90 +++ .../test/vwowrla/core/matchers/cast_test.clj | 96 +++ .../core/matchers/damage_reflected_test.clj | 56 ++ .../test/vwowrla/core/matchers/death_test.clj | 38 + .../core/matchers/dot_damages_target_test.clj | 97 +++ .../core/matchers/matchers_test_utils.clj | 13 + .../matchers/melee_avoided_by_target_test.clj | 124 +++ .../matchers/melee_damage_to_target_test.clj | 303 +++++++ .../core/matchers/other_damage_test.clj | 126 +++ .../core/matchers/resource_gained_test.clj | 299 +++++++ .../matchers/skill_avoided_by_target_test.clj | 659 +++++++++++++++ .../matchers/skill_damage_to_target_test.clj | 235 ++++++ .../core/matchers/skill_heals_target_test.clj | 100 +++ .../skill_interrupted_by_target_test.clj | 89 ++ .../skill_performed_on_target_test.clj | 79 ++ .../core/matchers/special_gained_test.clj | 53 ++ .../test/vwowrla/core/parser_test.clj | 16 + .../test/vwowrla/core/preparsing_test.clj | 55 ++ 30 files changed, 4390 insertions(+), 1 deletion(-) create mode 100644 vwowrla.core/resources/encounters.edn create mode 100644 vwowrla.core/resources/non_combat_starting_auras.txt create mode 100644 vwowrla.core/resources/non_combat_starting_skills.txt create mode 100644 vwowrla.core/resources/problem_entity_names.txt create mode 100644 vwowrla.core/src/vwowrla/core/encounters.clj create mode 100644 vwowrla.core/src/vwowrla/core/handlers.clj create mode 100644 vwowrla.core/src/vwowrla/core/matchers.clj create mode 100644 vwowrla.core/src/vwowrla/core/parser.clj create mode 100644 vwowrla.core/src/vwowrla/core/preparsing.clj create mode 100644 vwowrla.core/src/vwowrla/core/utils.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/aura_gained_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/aura_lost_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/cast_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/damage_reflected_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/death_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/dot_damages_target_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/matchers_test_utils.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/melee_avoided_by_target_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/melee_damage_to_target_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/other_damage_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/resource_gained_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/skill_avoided_by_target_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/skill_damage_to_target_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/skill_heals_target_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/skill_interrupted_by_target_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/skill_performed_on_target_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/matchers/special_gained_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/parser_test.clj create mode 100644 vwowrla.core/test/vwowrla/core/preparsing_test.clj diff --git a/vwowrla.core/project.clj b/vwowrla.core/project.clj index db6faf8..c1ad7e5 100644 --- a/vwowrla.core/project.clj +++ b/vwowrla.core/project.clj @@ -6,4 +6,5 @@ :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/tools.logging "0.3.1"] - [log4j "1.2.16"]]) + [log4j "1.2.16"] + [cheshire "5.5.0"]]) diff --git a/vwowrla.core/resources/encounters.edn b/vwowrla.core/resources/encounters.edn new file mode 100644 index 0000000..3b0302c --- /dev/null +++ b/vwowrla.core/resources/encounters.edn @@ -0,0 +1,38 @@ +{"Lucifron" {:entities {"Lucifron" {:count 1} + "Flamewaker Protector" {:count 2}}} + + "Magmadar" {:entities {"Magmadar" {:count 1}}} + + "Gehennas" {:entities {"Gehennas" {:count 1} + "Flamewaker" {:count 2}}} + + "Garr" {:entities {"Garr" {:count 1} + "Firesworn" {:count 8}}} + + "Baron Geddon" {:entities {"Baron Geddon" {:count 1}}} + + "Shazzrah" {:entities {"Shazzrah" {:count 1}}} + + "Sulfuron Harbinger" {:entities {"Sulfuron Harbinger" {:count 1} + "Flamewaker Priest" {:count 4}}} + + "Golemagg the Incinerator" {:entities {"Golemagg the Incinerator" {:count 1} + "Core Rager" {:count 2}}} + + "Majordomo Executus" {:entities {"Majordomo Executus" {:count 1 + :must-kill-count 0 + :ignore-interactions-with ["Ragnaros"] + :ignore-skills ["Summon Ragnaros"]} + "Flamewaker Healer" {:count 4} + "Flamewaker Elite" {:count 4}} + :trigger-on-damage? true + :trigger-on-debuff? true} + + "Ragnaros" {:entities {"Ragnaros" {:count 1 + :ignore-interactions-with ["Majordomo Executus"]} + "Son of Flame" {:count 8 + :must-kill-count 0}} + :trigger-on-damage? true + :trigger-on-debuff? true} + + "Onyxia" {:entities {"Onyxia" {:count 1}}}} diff --git a/vwowrla.core/resources/non_combat_starting_auras.txt b/vwowrla.core/resources/non_combat_starting_auras.txt new file mode 100644 index 0000000..fd402ce --- /dev/null +++ b/vwowrla.core/resources/non_combat_starting_auras.txt @@ -0,0 +1,3 @@ +Hunter's Mark +Detect Magic +Mind Soothe \ No newline at end of file diff --git a/vwowrla.core/resources/non_combat_starting_skills.txt b/vwowrla.core/resources/non_combat_starting_skills.txt new file mode 100644 index 0000000..3f4a846 --- /dev/null +++ b/vwowrla.core/resources/non_combat_starting_skills.txt @@ -0,0 +1,4 @@ +Hunter's Mark +Detect Magic +Mind Soothe +Distract \ No newline at end of file diff --git a/vwowrla.core/resources/problem_entity_names.txt b/vwowrla.core/resources/problem_entity_names.txt new file mode 100644 index 0000000..74d3fdc --- /dev/null +++ b/vwowrla.core/resources/problem_entity_names.txt @@ -0,0 +1,89 @@ +"Plucky" Johnson's Human Form +Alzzin's Minion +Antu'sul +Anub'shiah +Arin'sor +Arugal's Voidwalker +Atal'ai Deathwalker's Spirit +Chok'sul +Commander Gor'shak +Darkreaver's Fallen Charger +Death's Head Acolyte +Death's Head Adept +Death's Head Cultist +Death's Head Geomancer +Death's Head Necromancer +Death's Head Priest +Death's Head Sage +Death's Head Seer +Death's Head Ward Keeper +Doctor Weavil's Flying Machine +Dreka'Sur +Eliza's Guard +Faldreas Goeth'Shael +Father Winter's Helper +Fellicent's Shade +Flik's Frog +Franclorn's Spirit +Gizlock's Dummy +Great-father Winter's Helper +Greatfather Winter's Helper +Gunther's Visage +Guse's War Rider +Hammertoe's Spirit +Helcular's Remains +Hukku's Imp +Hukku's Succubus +Hukku's Voidwalker +Ichman's Gryphon +Jen'shan +Jezelle's Felhunter +Jezelle's Felsteed +Jezelle's Imp +Jezelle's Succubus +Jezelle's Voidwalker +Jeztor's War Rider +Jin'sora +Jugkar Grim'rod's Image +Krakle's Thermometer +Kurzen's Agent +Lord Azrethoc's Image +Maiden's Virtue Crewman +Merithra's Wake +Mulverick's War Rider +Nefarian's Troops +Nijel's Point Guard +Noxxion's Spawn +Officer Vu'Shalay +Onyxia's Elite Guard +Rak'shiri +Ralo'shan the Eternal Watcher +Ribbly's Crony +Ryson's Eye in the Sky +Sartura's Royal Guard +Sentinel Glynda Nal'Shea +Sergeant Ba'sha +Servant of Antu'sul +Sharpbeak's Father +Sharpbeak's Mother +Slidore's Gryphon +Slim's Friend +Sneed's Shredder +Sri'skulk +The Master's Eye +Twilight's Hammer Ambassador +Twilight's Hammer Executioner +Twilight's Hammer Torturer +Tyrion's Spybot +Umi's Mechanical Yeti +Varo'then's Ghost +Vipore's Gryphon +Warug's Bodyguard +Warug's Target Dummy +Winna's Kitten +Winter's Little Helper +Wizzlecrank's Shredder +Wrenix's Gizmotronic Apparatus +Xiggs Fuselighter's Flyingmachine +Ysida's Trigger +Zaetar's Spirit \ No newline at end of file diff --git a/vwowrla.core/src/vwowrla/core/encounters.clj b/vwowrla.core/src/vwowrla/core/encounters.clj new file mode 100644 index 0000000..01e62de --- /dev/null +++ b/vwowrla.core/src/vwowrla/core/encounters.clj @@ -0,0 +1,464 @@ +(ns vwowrla.core.encounters + (:import + (java.util Date)) + (:require + [clojure.java.io :as io] + [clojure.tools.logging :refer [info error]] + [cheshire.core :as json]) + (:use + vwowrla.core.utils)) + +(def defined-encounters (get-edn-resource "encounters.edn")) +(def non-combat-starting-auras (get-text-resource-as-lines "non_combat_starting_auras.txt")) +(def non-combat-starting-skills (get-text-resource-as-lines "non_combat_starting_skills.txt")) + +(def wipe-or-timeout-period (* 60 1000)) + +(declare calculate-encounter-stats) +(declare count-currently-dead) +(declare get-entity-last-activity) + +(defn find-defined-encounter-name + "returns the name of a defined encounter which includes the given entity in it's + list of trigger entities. returns nil if there is no encounter which includes the + given entity" + [entity-name] + (->> defined-encounters + (filter (fn [[_ {:keys [entities]}]] + (->> entities + (filter #(= (first %) entity-name)) + (first)))) + (ffirst))) + +(defn find-past-encounters + "return a list of all previously parsed encounters (successful and not) matching the encounter name" + [encounter-name data] + (->> (:encounters data) + (filter #(= (:name %) encounter-name)))) + +(defn any-successful-encounters? + "returns true if there are any successful parsed encounters matching the encounter name" + [encounter-name data] + (->> (find-past-encounters encounter-name data) + (map :wipe-or-timeout?) + (filter false?) + (empty?) + (not))) + +(defn update-active-encounter + "updates the active encounter using function f which will take the current active + encounter and any supplied args, returning a new active encounter which is + 'updated' in the original full parsed data and then finally returned." + [data f & args] + (apply update-in data [:active-encounter] f args)) + +(defn update-all-entities + "updates all entities in the encounter using function f which takes the current + entity and any supplied args, returning a new entity which is 'updated' in the + original encounter. returns the encounter with the modified entity data." + [encounter f & args] + (reduce + (fn [encounter [entity-name entity]] + (assoc-in encounter [:entities entity-name] (apply f entity args))) + encounter + (:entities encounter))) + +(defn update-all-active-encounter-entities + "updates all entities in the current active encounter in the full parsed data + using function f which takes the current entity and any supplied args, returning + a new entity which is 'updated' in the original encounter. returns the updated + full parsed data." + [data f & args] + (update-active-encounter data #(update-all-entities % f args))) + +(defn update-entity + "updates an entity in the full parsed data's active encounter using function f + which takes the current entity and any supplied args, returning the new entity + which is 'updated' in the active encounter. returns the updated full parsed data." + [data entity-name f & args] + (apply update-in data [:active-encounter :entities entity-name] f args)) + +(defn update-entity-field + "updates a specific field within an entity pointed to by ks in the full parsed + data's active encounter using function f which takes the current entity and any + supplied args, returning the new entity which is 'updated' in the active encounter. + returns the updated full parsed data." + [data entity-name ks f & args] + (apply update-in data (concat [:active-encounter :entities entity-name] ks) f args)) + +(defn- ignore-interaction? + [entity-name ignore-entity-list {:keys [target-name source-name] :as parsed-line}] + (and (or (= entity-name target-name) + (= entity-name source-name)) + (or (contained-in? target-name ignore-entity-list) + (contained-in? source-name ignore-entity-list)))) + +(defn ignored-interaction-event? + "returns true if the given parsed combat log line is between entities that have + been specified to ignore interactions between for the purposes of detecting + an encounter trigger" + [encounter parsed-line] + (->> (:entities encounter) + (filter + (fn [[entity-name entity-props]] + (seq (:ignore-interactions-with entity-props)))) + (filter + (fn [[entity-name entity-props]] + (ignore-interaction? entity-name (:ignore-interactions-with entity-props) parsed-line))) + (seq))) + +(defn- ignore-skill? + [entity-name ignore-skill-list {:keys [source-name skill] :as parsed-line}] + (and (= entity-name source-name) + (contained-in? skill ignore-skill-list))) + +(defn ignored-skill-event? + "returns true if the given parsed combat log line is for an encounter entity + that is using a skill that has been specifically indicated should be ignored + for the purposes of triggering an encounter" + [encounter parsed-line] + (->> (:entities encounter) + (filter + (fn [[entity-name entity-props]] + (seq (:ignore-skills entity-props)))) + (filter + (fn [[entity-name entity-props]] + (ignore-skill? entity-name (:ignore-skills entity-props) parsed-line))) + (seq))) + +;;; +;;; encounter start/stop +;;; + +(defn detect-encounter-triggered + "determines if the parsed combat log line is for an event involving any specific encounter entities which + should cause an encounter to begin, returning the name of the encounter if it should begin, or nil if no + encounter begin was detected" + [{:keys [target-name source-name damage aura-name type skill] :as parsed-line} data] + (if-let [encounter-name (or (find-defined-encounter-name target-name) + (find-defined-encounter-name source-name))] + (if (and (not (any-successful-encounters? encounter-name data)) + (not (contained-in? aura-name non-combat-starting-auras)) + (not (contained-in? skill non-combat-starting-skills))) + (let [encounter (get defined-encounters encounter-name)] + (cond + (ignored-interaction-event? encounter parsed-line) + nil + + (ignored-skill-event? encounter parsed-line) + nil + + ; if either of these are defined, then their criteria MUST pass to + ; trigger an encounter + (or (:trigger-on-damage? encounter) + (:trigger-on-aura? encounter) + (:trigger-on-buff? encounter) + (:trigger-on-debuff? encounter)) + (cond + (and (:trigger-on-damage? encounter) damage) encounter-name + (and (:trigger-on-aura? encounter) aura-name) encounter-name + (and (:trigger-on-buff? encounter) (= :buff type)) encounter-name + (and (:trigger-on-debuff? encounter) (= :debuff type)) encounter-name) + + :else + encounter-name))))) + +(defn begin-encounter + "sets up a new active encounter in the parsed data, returning the new parsed data set ready to use for + parsing a new encounter." + [encounter-name {:keys [timestamp line] :as parsed-line} data] + (info "Beginning encounter" (str "\"" encounter-name "\"") "detected on line:" line) + (assoc data :active-encounter + {:name encounter-name + :started-at timestamp + :entities {} + :skills {} + :trigger-entities (get-in defined-encounters [encounter-name :entities])})) + +(defn detect-encounter-end + "determines if the currently active encounter should end based on the active encounter parsed data. + returns :killed if the encounter should end due to a successful kill, :wipe-or-timeout if the + encounter was found to be over due to a raid wipe or other non-activity timeout, or nil if the + active encounter is not over yet." + [{:keys [^Date timestamp] :as parsed-line} data] + (let [trigger-entites (get-in data [:active-encounter :trigger-entities])] + (cond + (every? + (fn [[entity-name {:keys [count must-kill-count]}]] + (let [count-dead (count-currently-dead data entity-name)] + (>= count-dead (or must-kill-count count)))) + trigger-entites) + :killed + + (every? + (fn [[entity-name _]] + ; HACK: what is the right thing to do when the entity we want to check the activity of hasn't even been + ; added to the encounter's entity list yet? most likely because the encounter has probably just begun + ; and there have been no combat log lines yet for one or more of the trigger entities. + ; should we have a minimum encounter length time? something like 15-30 seconds? that also feels hacky... + (>= (- (.getTime timestamp) + (.getTime (or (get-entity-last-activity entity-name data) + timestamp))) + wipe-or-timeout-period)) + trigger-entites) + :wipe-or-timeout))) + +(defn end-encounter + "ends the current active encounter in the parsed data, moving it from :active-encounter and inserted it into the + end of the :encounters list. finalizes the encounter by performing various final entity statistic calculations and + marks the encounter as successful or not. returns the new parsed data set without any active encounter set." + [{:keys [timestamp line] :as parsed-line} encounter-end-cause data] + (let [wipe-or-timeout? (= encounter-end-cause :wipe-or-timeout)] + (info "Ending encounter" (str "\"" (get-in data [:active-encounter :name]) "\"") "detected on line:" line) + (if wipe-or-timeout? + (info "Encounter ending due to wipe or trigger entity activity timeout (unsuccessful encounter kill attempt).")) + (let [data (-> data + (update-active-encounter assoc :ended-at timestamp) + (update-active-encounter assoc :wipe-or-timeout? wipe-or-timeout?) + (calculate-encounter-stats))] + (-> data + (assoc-in [:active-encounter] nil) + (update-in [:encounters] #(conj %1 (:active-encounter data))))))) + +;;; +;;; entity manipulation +;;; + +(defn touch-entity + "updates an entity within the current active encounter by resetting it's :last-activity-at timestamp + or adds a new entity under the given name to the active encounter if it does not already exist. returns + the new parsed data set with the updated entity information." + [data entity-name timestamp] + (if-not (get-in data [:active-encounter :entities entity-name]) + (assoc-in data [:active-encounter :entities entity-name] + {:name entity-name + :added-at timestamp + :last-activity-at timestamp + :damage-out-total 0 + :damage-out-totals {} + :damage-in-total 0 + :damage-in-totals {} + :alive-dps 0 + :encounter-dps 0 + :damage-out {} + :damage-out-by-entity {} + :damage-in {} + :damage-in-by-entity {} + :casts {} + :other-powers {} + :auras {} + :deaths [] + :resurrections [] + :alive-duration 0}) + (assoc-in data [:active-encounter :entities entity-name :last-activity-at] timestamp))) + +(defn get-entity-last-activity + [entity-name data] + (get-in data [:active-encounter :entities entity-name :last-activity-at])) + +(defn get-entity-alive-time + "returns the number of milliseconds of the encounter that the entity was alive for" + [{:keys [deaths resurrections] :as entity} {:keys [started-at ended-at] :as encounter}] + ;(println (:name entity)) + ;(println "started-at" started-at) + ;(println "ended-at " ended-at) + ;(println "deaths " deaths) + ;(println "resurrects" resurrections) + (if (and (= 0 (count deaths)) + (= 0 (count resurrections))) + (:duration encounter) + (let [segments (as-> (concat + (map (fn [death] {:status :dead :at (:timestamp death)}) deaths) + (map (fn [resurrection] {:status :alive :at (:timestamp resurrection)}) resurrections) + [{:status :end :at ended-at}]) x + (remove empty? x) + (sort-by :at x))] + ;(println ">" segments) + (reduce + (fn [{:keys [total current-status from] :as result} {:keys [status at]}] + ;(println ">>" current-status from status at) + (cond + ; is the first state change we find a resurrect? (e.g. they were dead when the fight began) + (and (nil? current-status) + (= :alive status)) + (assoc result + :current-status :alive + :from at) + + ; first state change we find is a death + (and (nil? current-status) + (= :dead status)) + (assoc result + :current-status :dead + :from at + :total (+ total (time-between from at))) + + ; resurrected after a death + (and (= :dead current-status) + (= :alive status)) + (assoc result + :current-status :alive + :from at) + + ; another death state when already dead? this can happen if there are multiple entities in the encounter + ; with the same name. pretty much impossible to separate them with just the info in the combat log + ; available to us. so, we just tack on the time since the last death since at least one of the entities + ; was alive for that entire time period. in this way, the "entity alive time" for the entity with this + ; name will just be a counter of "at least one entity with this name was alive" + (and (= :dead current-status) + (= :dead status)) + (assoc result + :from at + :total (+ total (time-between from at))) + + ; fight has ended (always should be the last iteration, just return the total value here) + (= :end status) + (if (= :dead current-status) + total + (+ total (time-between from at))))) + {:total 0 + :from started-at} + segments)))) + +(defn finalize-entity-auras + [entity timestamp] + ; TODO + entity) + +(defn finalize-entities + [data] + (update-active-encounter + data + (fn [encounter] + (-> encounter + (update-all-entities + #(assoc % :alive-duration (get-entity-alive-time % encounter))) + (update-all-entities + #(assoc % + :encounter-dps (Math/round ^double + (/ (:damage-out-total %) + (/ (:duration encounter) + 1000))) + :alive-dps (Math/round ^double + (/ (:damage-out-total %) + (/ (:alive-duration %) + 1000))))) + (update-all-entities finalize-entity-auras (:ended-at encounter)))))) + +(defn calculate-encounter-stats + [data] + (-> data + (update-active-encounter + (fn [{:keys [started-at ended-at] :as encounter}] + (assoc encounter :duration (time-between started-at ended-at)))) + (finalize-entities))) + +(defn count-currently-dead + [data entity-name] + (if-let [entity (get-in data [:active-encounter :entities entity-name])] + (let [num-deaths (count (:deaths entity)) + num-resurrects (count (:resurrections entity))] + (- num-deaths num-resurrects)) + 0)) + +(defn update-damage-averages + [{:keys [num-hits total-hit-damage total-crit-damage num-crits] :as totals}] + (-> totals + (update-in [:average-hit] #(if (> num-hits 0) (int (/ total-hit-damage num-hits)) %)) + (update-in [:average-crit] #(if (> num-crits 0) (int (/ total-crit-damage num-crits)) %)))) + +(defn add-from-damage-properties + [totals + {:keys [damage damage-type hit-type crit? partial-absorb partial-resist partial-block avoidance-method] :as damage-properties}] + (let [damage (or damage 0)] + (-> (or totals {:damage 0 + :max-hit 0 + :min-hit 0 + :total-hit-damage 0 + :average-hit 0 + :max-crit 0 + :min-crit 0 + :total-crit-damage 0 + :average-crit 0 + :num-total-hits 0 + :num-hits 0 + :num-crits 0 + :num-glancing 0 + :num-crushing 0 + :num-partial-absorb 0 + :num-partial-resist 0 + :num-partial-block 0 + :num-miss 0 + :num-dodge 0 + :num-parry 0 + :num-resist 0 + :num-absorb 0 + :num-evade 0 + :num-immune 0}) + (update-in [:damage] #(+ % damage)) + (update-in [:max-hit] #(if (and (not crit?) (not avoidance-method)) (max % damage) %)) + (update-in [:min-hit] #(if (and (not crit?) (not avoidance-method)) (if (> damage 0) (if (= % 0) damage (min % damage)) %) %)) + (update-in [:total-hit-damage] #(if (and (not crit?) (not avoidance-method)) (+ % damage) %)) + (update-in [:max-crit] #(if (and crit? (not avoidance-method)) (max % damage) %)) + (update-in [:min-crit] #(if (and crit? (not avoidance-method)) (if (> damage 0) (if (= % 0) damage (min % damage)) %) %)) + (update-in [:total-crit-damage] #(if (and crit? (not avoidance-method)) (+ % damage) %)) + (update-in [:num-total-hits] #(if-not avoidance-method (inc %) %)) + (update-in [:num-hits] #(if (and (not avoidance-method) (not crit?)) (inc %) %)) + (update-in [:num-crits] #(if (and (not avoidance-method) crit?) (inc %) %)) + (update-in [:num-glancing] #(if (= hit-type :glancing) (inc %) %)) + (update-in [:num-crushing] #(if (= hit-type :crushing) (inc %) %)) + (update-in [:num-partial-absorb] #(if partial-absorb (inc %) %)) + (update-in [:num-partial-resist] #(if partial-resist (inc %) %)) + (update-in [:num-partial-block] #(if partial-block (inc %) %)) + (update-in [:num-miss] #(if (= avoidance-method :miss) (inc %) %)) + (update-in [:num-dodge] #(if (= avoidance-method :dodge) (inc %) %)) + (update-in [:num-parry] #(if (= avoidance-method :parry) (inc %) %)) + (update-in [:num-resist] #(if (= avoidance-method :resist) (inc %) %)) + (update-in [:num-absorb] #(if (= avoidance-method :absorb) (inc %) %)) + (update-in [:num-evade] #(if (= avoidance-method :evade) (inc %) %)) + (update-in [:num-immune] #(if (= avoidance-method :immune) (inc %) %)) + (update-damage-averages)))) + +(defn entity-takes-damage + [data entity-name from-entity-name {:keys [skill damage damage-type] :as damage-properties} timestamp] + (-> data + (update-entity-field entity-name [:damage-in-total] #(if damage (+ (or % 0) damage) %)) + (update-entity-field entity-name [:damage-in-totals damage-type] #(if damage (+ (or % 0) damage) %)) + (update-entity-field entity-name [:damage-in skill] #(add-from-damage-properties % damage-properties)) + (update-entity-field entity-name [:damage-in-by-entity from-entity-name skill] #(add-from-damage-properties % damage-properties)))) + +(defn entity-deals-damage + [data entity-name to-entity-name {:keys [skill damage damage-type] :as damage-properties} timestamp] + (-> data + (update-entity-field entity-name [:damage-out-total] #(if damage (+ (or % 0) damage) %)) + (update-entity-field entity-name [:damage-out-totals damage-type] #(if damage (+ (or % 0) damage) %)) + (update-entity-field entity-name [:damage-out skill] #(add-from-damage-properties % damage-properties)) + (update-entity-field entity-name [:damage-out-by-entity to-entity-name skill] #(add-from-damage-properties % damage-properties)))) + +;;; +;;; main combat log entry processing entry points +;;; + +(defn process-source-to-target-damage + [source-name target-name damage-properties timestamp data] + (-> data + (touch-entity source-name timestamp) + (touch-entity target-name timestamp) + (entity-takes-damage target-name source-name damage-properties timestamp) + (entity-deals-damage source-name target-name damage-properties timestamp)) + ) + +(defn process-entity-death + [entity-name timestamp data] + (-> data + (touch-entity entity-name timestamp) + (update-entity-field entity-name [:deaths] #(conj % {:timestamp timestamp})) + (update-entity entity-name finalize-entity-auras timestamp))) + +(defn process-source-to-target-cast + [source-name target-name skill-name timestamp data] + data) + +(defn process-entity-cast + [entity-name skill-name timestamp data] + data) diff --git a/vwowrla.core/src/vwowrla/core/handlers.clj b/vwowrla.core/src/vwowrla/core/handlers.clj new file mode 100644 index 0000000..67e89e5 --- /dev/null +++ b/vwowrla.core/src/vwowrla/core/handlers.clj @@ -0,0 +1,161 @@ +(ns vwowrla.core.handlers + (:require + [vwowrla.core.encounters :as encounters])) + +(defmulti handle-event + (fn [{:keys [event]} _] + (keyword event))) + +(defmethod handle-event :skill-damage-to-target + [{:keys [source-name skill target-name damage damage-type absorbed resisted blocked crit? timestamp] :as parsed} data] + (encounters/process-source-to-target-damage + source-name + target-name + {:skill skill + :actual-skill? true + :damage damage + :damage-type (or damage-type :physical) + :crit? crit? + :partial-absorb absorbed + :partial-resist resisted + :partial-block blocked} + timestamp + data)) + +(defmethod handle-event :skill-avoided-by-target + [{:keys [source-name target-name skill avoidance-method timestamp] :as parsed} data] + (encounters/process-source-to-target-damage + source-name + target-name + {:skill skill + :actual-skill? true + :avoidance-method avoidance-method} + timestamp + data)) + +(defmethod handle-event :damage-reflected + [{:keys [source-name target-name damage damage-type timestamp] :as parsed} data] + (encounters/process-source-to-target-damage + source-name + target-name + {:skill "Reflect" + :actual-skill? false + :damage damage + :damage-type damage-type + :crit? false} + timestamp + data)) + +(defmethod handle-event :melee-damage-to-target + [{:keys [source-name target-name damage damage-type hit-type absorbed resisted blocked crit? timestamp] :as parsed} data] + (encounters/process-source-to-target-damage + source-name + target-name + {:skill "Melee" + :actual-skill? false + :damage damage + :damage-type (or damage-type :physical) + :hit-type hit-type + :crit? crit? + :partial-absorb absorbed + :partial-resist resisted + :partial-block blocked} + timestamp + data)) + +(defmethod handle-event :melee-avoided-by-target + [{:keys [source-name target-name avoidance-method timestamp] :as parsed} data] + (encounters/process-source-to-target-damage + source-name + target-name + {:skill "Melee" + :actual-skill? false + :avoidance-method avoidance-method} + timestamp + data)) + +(defmethod handle-event :skill-interrupted-by-target + [{:keys [source-name target-name skill timestamp] :as parsed} data] + data) + +(defmethod handle-event :dot-damages-target + [{:keys [source-name skill target-name damage damage-type absorbed resisted timestamp] :as parsed} data] + (encounters/process-source-to-target-damage + source-name + target-name + {:skill skill + :actual-skill? true + :damage damage + :damage-type damage-type + :crit? false + :partial-absorb absorbed + :partial-resist resisted} + timestamp + data)) + +(defmethod handle-event :cast-begins + [{:keys [source-name skill spell? timestamp] :as parsed} data] + ; don't think we really care about this ? + data) + +(defmethod handle-event :skill-performed-on-target + [{:keys [source-name target-name skill spell? extra timestamp] :as parsed} data] + (encounters/process-source-to-target-cast source-name target-name skill timestamp data)) + +(defmethod handle-event :cast + [{:keys [source-name skill spell? timestamp] :as parsed} data] + (encounters/process-entity-cast source-name skill timestamp data)) + +(defmethod handle-event :skill-heals-target + [{:keys [source-name skill crit? target-name amount timestamp] :as parsed} data] + data) + +(defmethod handle-event :resource-gained + [{:keys [target-name amount resource-type source-name skill timestamp] :as parsed} data] + data) + +(defmethod handle-event :resource-lost + [{:keys [target-name amount resource-type source-name skill timestamp] :as parsed} data] + (condp = resource-type + :health (encounters/process-source-to-target-damage + source-name + target-name + {:skill skill + :actual-skill? true + :damage amount + :damage-type :physical + :crit? false} + timestamp + data) + data)) + +(defmethod handle-event :special-gained + [{:keys [target-name special source timestamp] :as parsed} data] + data) + +(defmethod handle-event :aura-gained + [{:keys [target-name aura-name type stacks timestamp] :as parsed} data] + data) + +(defmethod handle-event :aura-lost + [{:keys [target-name aura-name faded? stacks timestamp] :as parsed} data] + data) + +(defmethod handle-event :other-damage + [{:keys [target-name damage damage-type resisted absorbed source timestamp] :as parsed} data] + data) + +(defmethod handle-event :death + [{:keys [source-name timestamp] :as parsed} data] + (encounters/process-entity-death source-name timestamp data)) + + +(defmethod handle-event :ignored + [{:keys [line] :as parsed} data] + #_(println "[WARN] *** IGNORED ***" line) + data) + +(defmethod handle-event :default + [{:keys [line] :as parsed} data] + (println "[WARN] *** UNRECOGNIZED ***" line) + data) diff --git a/vwowrla.core/src/vwowrla/core/matchers.clj b/vwowrla.core/src/vwowrla/core/matchers.clj new file mode 100644 index 0000000..6403dd6 --- /dev/null +++ b/vwowrla.core/src/vwowrla/core/matchers.clj @@ -0,0 +1,765 @@ +(ns vwowrla.core.matchers + (:use + vwowrla.core.utils)) + +;;; *** IMPORTANT!! *** +;;; The order that the matchers are listed is **NOT** completely arbitrary!! +;;; Some of the regex patterns won't ever have a problem matching some line they shouldn't, but some others +;;; can match the wrong lines if the regex patterns appear after others. e.g. the skill/spell and melee patterns. +;;; The unit tests should pick this up if something gets moved out of the proper order, but be very careful!! + +(def regex-matchers + [ + + ;;; --------------------------------------------------------------------------------------------- + ;;; CATCH-ALL REGEX MATCHERS + ;;; These are only here to prevent extra "unrecognized" warnings while parsing logs for messages + ;;; that we simply don't care at all about. + ;;; NOTE: these are at the beginning so we catch them first and move on. otherwise some could + ;;; potentially get caught and misinterpreted as other types of combat log messages + + {:event :ignored :regex #"^You fail to cast (.+): (.+)\.$"} + {:event :ignored :regex #"^You fail to perform (.+): (.+)\.$"} + {:event :ignored :regex #"^You have slain (.+)!$"} + {:event :ignored :regex #"^(.+) is slain by (.+)!$"} + {:event :ignored :regex #"^(.+) (?:creates|create) (.+)\.$"} + {:event :ignored :regex #"^Your pet begins eating a (.+)\.$"} + {:event :ignored :regex #"^(.+)'s pet begins eating a (.+)\.$"} + {:event :ignored :regex #"^Your (.+) is reflected back by (.+)\.$"} + {:event :ignored :regex #"^(.+?)'s (.+) is reflected back by (.+)\.$"} + {:event :ignored :regex #"^(.+) is destroyed\.$"} + {:event :ignored :regex #"^Your (.+) reputation has increased by (\d+)\.$"} + {:event :ignored :regex #"^Your equipped items suffer a 10% durability loss\.$"} + {:event :ignored :regex #"^(.+) dies, honorable kill (.+)$"} + ; TODO: keep an eye on these types of entries. seems safe to ignore so far with Onyxia + MC though... + {:event :ignored :regex #"^(.+) is killed by (.+)\.$"} + + ;;; --------------------------------------------------------------------------------------------- + ;;; SKILL/SPELL DAMAGE + + {:regex #"^Your (.+) (hits|crits) (.+) for (\d+) (.+) damage\.(?: \((\d+) resisted\))?(?: \((\d+) absorbed\))?$" + :id :skill-damages-target-elemental-self + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :args #(hash-map + :skill %1 + :crit? (= %2 "crits") + :target-name %3 + :damage (->int %4) + :damage-type (->kw %5) + :resisted (->int %6) + :absorbed (->int %7) + :source-name "you")} + + {:regex #"^Your (.+) (hits|crits) (.+) for (\d+) damage\.(?: \((\d+) blocked\))?(?: \((\d+) absorbed\))?$" + :id :skill-damages-target-self + :logfmt :skill-damages-target + :event :skill-damage-to-target + :args #(hash-map + :skill %1 + :crit? (= %2 "crits") + :target-name %3 + :damage (->int %4) + :blocked (->int %5) + :absorbed (->int %6) + :source-name "you")} + + {:regex #"^Your (.+) (hits|crits) (.+) for (\d+)\.(?: \((\d+) blocked\))?(?: \((\d+) absorbed\))?$" + :id :skill-damages-target-short-self + :logfmt :skill-damages-target-short + :event :skill-damage-to-target + :args #(hash-map + :skill %1 + :crit? (= %2 "crits") + :target-name %3 + :damage (->int %4) + :blocked (->int %5) + :absorbed (->int %6) + :source-name "you")} + + {:regex #"^(.+?)'s (.+) (hits|crits) (.+) for (\d+) (.+) damage\.(?: \((\d+) resisted\))?(?: \((\d+) absorbed\))?$" + :id :skill-damages-target-elemental + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :args #(hash-map + :source-name %1 + :skill %2 + :crit? (= %3 "crits") + :target-name %4 + :damage (->int %5) + :damage-type (->kw %6) + :resisted (->int %7) + :absorbed (->int %8))} + + ; TODO: is this ever emitted in a combat log? why did i add this one... ? + {:regex #"^(.+?)'s (.+) (hits|crits) (.+) for (\d+) damage\.(?: \((\d+) blocked\))?(?: \((\d+) absorbed\))?$" + :id :skill-damages-target + :logfmt :skill-damages-target + :event :skill-damage-to-target + :args #(hash-map + :source-name %1 + :skill %2 + :crit? (= %3 "crits") + :target-name %4 + :damage (->int %5) + :blocked (->int %6) + :absorbed (->int %7))} + + {:regex #"^(.+?)'s (.+) (hits|crits) (.+) for (\d+)\.(?: \((\d+) blocked\))?(?: \((\d+) absorbed\))?$" + :id :skill-damages-target-short + :logfmt :skill-damages-target-short + :event :skill-damage-to-target + :args #(hash-map + :source-name %1 + :skill %2 + :crit? (= %3 "crits") + :target-name %4 + :damage (->int %5) + :blocked (->int %6) + :absorbed (->int %7))} + + ;;; --------------------------------------------------------------------------------------------- + ;;; SKILL/SPELL MISSES / FULL-ABSORBS / FULL-RESISTS + + {:regex #"^Your (.+) missed (.+)\.$" + :id :skill-miss-self + :logfmt :skill-miss + :event :skill-avoided-by-target + :args #(hash-map + :skill %1 + :target-name %2 + :source-name "you" + :avoidance-method :miss)} + + {:regex #"^(.+?)'s (.+) (?:missed|misses) (.+)\.$" + :id :skill-miss + :logfmt :skill-miss + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name %3 + :avoidance-method :miss)} + + {:regex #"^Your (.+) was parried by (.+)\.$" + :id :skill-parry-self + :logfmt :skill-parry + :event :skill-avoided-by-target + :args #(hash-map + :skill %1 + :target-name %2 + :source-name "you" + :avoidance-method :parry)} + + {:regex #"^(.+?)'s (.+) was parried by (.+)\.$" + :id :skill-parry + :logfmt :skill-parry + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name %3 + :avoidance-method :parry)} + + {:regex #"^(.+?)'s (.+) was parried\.$" + :id :skill-parry-implied-self + :logfmt :skill-parry + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name "you" + :avoidance-method :parry)} + + {:regex #"^Your (.+) was blocked by (.+)\.$" + :id :skill-block-self + :logfmt :skill-block + :event :skill-avoided-by-target + :args #(hash-map + :skill %1 + :target-name %2 + :source-name "you" + :avoidance-method :block)} + + {:regex #"^(.+?)'s (.+) was blocked by (.+)\.$" + :id :skill-block + :logfmt :skill-block + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name %3 + :avoidance-method :block)} + + {:regex #"^Your (.+) was dodged by (.+)\.$" + :id :skill-dodge-self + :logfmt :skill-dodge + :event :skill-avoided-by-target + :args #(hash-map + :skill %1 + :target-name %2 + :source-name "you" + :avoidance-method :dodge)} + + {:regex #"^(.+?)'s (.+) was dodged by (.+)\.$" + :id :skill-dodge + :logfmt :skill-dodge + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name %3 + :avoidance-method :dodge)} + + {:regex #"^(.+?)'s (.+) was dodged\.$" + :id :skill-dodge-implied-self + :logfmt :skill-dodge + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name "you" + :avoidance-method :dodge)} + + {:regex #"^Your (.+) was evaded by (.+)\.$" + :id :skill-evade-self + :logfmt :skill-evade + :event :skill-avoided-by-target + :args #(hash-map + :skill %1 + :target-name %2 + :source-name "you" + :avoidance-method :evade)} + + {:regex #"^(.+?)'s (.+) was evaded by (.+)\.$" + :id :skill-evade + :logfmt :skill-evade + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name %3 + :avoidance-method :evade)} + + {:regex #"^(.+) (?:resist|resists) your (.+)\.$" + :id :skill-resist-self + :logfmt :skill-resist + :event :skill-avoided-by-target + :args #(hash-map + :target-name %1 + :skill %2 + :source-name "you" + :avoidance-method :resist)} + + {:regex #"^(.+?) (?:resist|resists) (.+?)'s (.+)\.$" + :id :skill-resist + :logfmt :skill-resist + :event :skill-avoided-by-target + :args #(hash-map + :target-name %1 + :source-name %2 + :skill %3 + :avoidance-method :resist)} + + ; i don't think target is ever "you" for this one + {:regex #"^(.+?)'s (.+) is absorbed by (.+)\.$" + :id :skill-absorb + :logfmt :skill-absorb + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name %3 + :avoidance-method :absorb)} + + {:regex #"^Your (.+) is absorbed by (.+)\.$" + :id :skill-absorb-self + :logfmt :skill-absorb + :event :skill-avoided-by-target + :args #(hash-map + :skill %1 + :target-name %2 + :source-name "you" + :avoidance-method :absorb)} + + {:regex #"^(.+?) (?:absorb|absorbs) (.+?)'s (.+)\.$" + :id :skill-absorb-2 + :logfmt :skill-absorb-2 + :event :skill-avoided-by-target + :args #(hash-map + :target-name %1 + :source-name %2 + :skill %3 + :avoidance-method :absorb)} + + ; i don't think target is ever "you" for this one + {:regex #"^(.+?)'s (.+) was resisted by (.+)\.$" + :id :skill-resist-2 + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name %3 + :avoidance-method :resist)} + + {:regex #"^Your (.+) was resisted by (.+)\.$" + :id :skill-resist-2-self + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :args #(hash-map + :skill %1 + :target-name %2 + :source-name "you" + :avoidance-method :resist)} + + {:regex #"^(.+?)'s (.+) was resisted\.$" + :id :skill-resist-implied-self + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name "you" + :avoidance-method :resist)} + + {:regex #"^Your (.+) failed\. (.+) is immune\.$" + :id :skill-immune-self + :logfmt :skill-immune + :event :skill-avoided-by-target + :args #(hash-map + :skill %1 + :target-name %2 + :source-name "you" + :avoidance-method :immune)} + + {:regex #"^(.+?)'s (.+) fails\. (.+) is immune\.$" + :id :skill-immune + :logfmt :skill-immune + :event :skill-avoided-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name %3 + :avoidance-method :immune)} + + ;;; --------------------------------------------------------------------------------------------- + ;;; REFLECTS + + {:regex #"^(.+) (?:reflects|reflect) (\d+) (.+) damage to (.+)\.$" + :id :target-reflects-elemental-damage + :logfmt :target-reflects-elemental-damage + :event :damage-reflected + :args #(hash-map + :source-name %1 + :damage (->int %2) + :damage-type (->kw %3) + :target-name %4)} + + ;;; --------------------------------------------------------------------------------------------- + ;;; MELEE DAMAGE + + {:regex #"^(.+) (hit|hits|crit|crits) (.+) for (\d+)\.(?: \((glancing)\))?(?: \((crushing)\))?(?: \((\d+) blocked\))?(?: \((\d+) absorbed\))?$" + :id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :args #(hash-map + :source-name %1 + :crit? (one-of? %2 "crit" "crits") + :target-name %3 + :damage (->int %4) + :hit-type (cond + (= %5 "glancing") :glancing + (= %6 "crushing") :crushing) + :blocked (->int %7) + :absorbed (->int %8))} + + {:regex #"^(.+) (hit|hits|crit|crits) (.+) for (\d+) (.+) damage\.(?: \((glancing)\))?(?: \((crushing)\))?(?: \((\d+) resisted\))?(?: \((\d+) absorbed\))?$" + :id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :args #(hash-map + :source-name %1 + :crit? (one-of? %2 "crit" "crits") + :target-name %3 + :damage (->int %4) + :damage-type (->kw %5) + :hit-type (cond + (= %6 "glancing") :glancing + (= %7 "crushing") :crushing) + :resisted (->int %8) + :absorbed (->int %9))} + + ;;; --------------------------------------------------------------------------------------------- + ;;; MELEE DAMAGE AVOIDANCE (ABSORB/RESIST/MISS/BLOCK/DODGE/PARRY/EVADE) + + {:regex #"^(.+) (?:attack|attacks)\. (.+) (?:absorb|absorbs) all the damage\.$" + :id :melee-full-absorb + :logfmt :melee-full-absorb + :event :melee-avoided-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :avoidance-method :absorb)} + + {:regex #"^(.+) (?:attack|attacks)\. (.+) (?:resist|resists) all the damage\.$" + :id :melee-full-resist + :logfmt :melee-full-resist + :event :melee-avoided-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :avoidance-method :resist)} + + {:regex #"^(.+) (?:miss|misses) (.+)\.$" + :id :melee-miss + :logfmt :melee-miss + :event :melee-avoided-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :avoidance-method :miss)} + + {:regex #"^(.+) (?:attack|attacks)\. (.+) (?:parry|parries)\.$" + :id :melee-parry + :logfmt :melee-parry + :event :melee-avoided-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :avoidance-method :parry)} + + {:regex #"^(.+) (?:attack|attacks)\. (.+) (?:dodge|dodges)\.$" + :id :melee-dodge + :logfmt :melee-dodge + :event :melee-avoided-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :avoidance-method :dodge)} + + {:regex #"^(.+) (?:attack|attacks)\. (.+) (?:block|blocks)\.$" + :id :melee-block + :logfmt :melee-block + :event :melee-avoided-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :avoidance-method :block)} + + {:regex #"^(.+) (?:attack|attacks)\. (.+) (?:evade|evades)\.$" + :id :melee-evade + :logfmt :melee-evade + :event :melee-avoided-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :avoidance-method :evade)} + + {:regex #"^(.+) (?:attacks|attack) but (.+) is immune\.$" + :id :melee-immune + :logfmt :melee-immune + :event :melee-avoided-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :avoidance-method :immune)} + + ;;; --------------------------------------------------------------------------------------------- + ;;; SPELL INTERRUPTION + + {:regex #"^(.+?) (?:interrupts|interrupt) (.+?)'s (.+)\.$" + :id :skill-interrupt + :logfmt :skill-interrupt + :event :skill-interrupted-by-target + :args #(hash-map + :source-name %1 + :target-name %2 + :skill %3)} + + {:regex #"^(.+) (?:interrupts|interrupt) your (.+)\.$" + :id :skill-interrupt-self + :logfmt :skill-interrupt + :event :skill-interrupted-by-target + :args #(hash-map + :source-name %1 + :skill %2 + :target-name "you")} + + ;;; --------------------------------------------------------------------------------------------- + ;;; DOT + ;;; Note that these are not used for just DoT's in the traditional sense (e.g. from applied debuffs, such as curses) + ;;; but also channeled spells such as Blizzard / Arcane Missle ticks generate these same combat log events. + + {:regex #"^(.+?) (?:suffers|suffer) (\d+) (.+?) damage from (.+?)'s (.+)\.(?: \((\d+) resisted\))?(?: \((\d+) absorbed\))?$" + :id :dot-damages-target + :logfmt :dot-damages-target + :event :dot-damages-target + :args #(hash-map + :target-name %1 + :damage (->int %2) + :damage-type (->kw %3) + :source-name %4 + :skill %5 + :resisted (->int %6) + :absorbed (->int %7))} + + {:regex #"^(.+) (?:suffers|suffer) (\d+) (.+) damage from your (.+)\.(?: \((\d+) resisted\))?(?: \((\d+) absorbed\))?$" + :id :dot-damages-target-self + :logfmt :dot-damages-target + :event :dot-damages-target + :args #(hash-map + :target-name %1 + :damage (->int %2) + :damage-type (->kw %3) + :skill %4 + :resisted (->int %5) + :absorbed (->int %6) + :source-name "you")} + + ;;; --------------------------------------------------------------------------------------------- + ;;; CAST NOTIFICATION / INSTANT CAST ABILITIES + + ; always source != "you" .. ? + {:regex #"^(.+) (?:begins|begin) to (perform|cast) (.+)\.$" + :id :cast-begins + :logfmt :cast-begins + :event :cast-begins + :args #(hash-map + :source-name %1 + :spell? (= %2 "cast") + :skill %3)} + + {:regex #"^(.+) (cast|casts|performs|perform) (.+) on (.+): (.+)\.$" + :id :skill-performed-on-target + :logfmt :skill-performed-on-target + :event :skill-performed-on-target + :args #(hash-map + :source-name %1 + :spell? (one-of? %2 "casts" "cast") + :skill %3 + :target-name %4 + :extra %5)} + + {:regex #"^(.+) (cast|casts|performs|perform) (.+) on (.+)\.$" + :id :skill-performed-on-target + :logfmt :skill-performed-on-target + :event :skill-performed-on-target + :args #(hash-map + :source-name %1 + :spell? (one-of? %2 "casts" "cast") + :skill %3 + :target-name %4)} + + ; only for instant cast stuff .. ? + {:regex #"^(.+) (casts|cast|performs|perform) (.+)\.$" + :id :cast + :logfmt :cast + :event :cast + :args #(hash-map + :source-name %1 + :spell? (one-of? %2 "casts" "cast") + :skill %3)} + + ;;; --------------------------------------------------------------------------------------------- + ;;; DIRECT HEALING FROM SKILLS (not HoT's) + + {:regex #"^Your (.+?)(?: (critically))? heals (.+) for (\d+)\.$" + :id :skill-heals-target-self + :logfmt :skill-heals-target + :event :skill-heals-target + :args #(hash-map + :skill %1 + :crit? (= %2 "critically") + :target-name %3 + :amount (->int %4) + :source-name "you")} + + {:regex #"^(.+?)'s (.+?)(?: (critically))? heals (.+) for (\d+)\.$" + :id :skill-heals-target + :logfmt :skill-heals-target + :event :skill-heals-target + :args #(hash-map + :source-name %1 + :skill %2 + :crit? (= %3 "critically") + :target-name %4 + :amount (->int %5))} + + ;;; --------------------------------------------------------------------------------------------- + ;;; BONUS HEALING / MANA / RESOURCE REGEN (i.e. not heals, but received via other effects) + + {:regex #"^(.+?) (?:gain|gains) (\d+) (health|Mana|Rage|Energy|Happiness) from (.+?)'s (.+)\.$" + :id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :args #(hash-map + :target-name %1 + :amount (->int %2) + :resource-type (->kw %3) + :source-name %4 + :skill %5)} + + ; apparently for this one, target will never be "you" .. ? + {:regex #"^(.+) (?:gain|gains) (\d+) (health|Mana|Rage|Energy|Happiness) from your (.+)\.$" + :id :resource-gained-from-skill-self + :logfmt :resource-gained-from-skill + :event :resource-gained + :args #(hash-map + :target-name %1 + :amount (->int %2) + :resource-type (->kw %3) + :skill %4 + :source-name "you")} + + ; this seems to only ever be for target=you and never for health potions ? the name after + ; "from" at the end always seems to refer to a skill, never an entity. "you" seems to always + ; be the implied source entity... so basically, target=you and source=you always ... ? + ; (for this line, seen mana potions, mana gems, talents that restore mana ...) + {:regex #"^(.+) (?:gain|gains) (\d+) (health|Mana|Rage|Energy|Happiness) from (.+)\.$" + :id :resource-gained + :logfmt :resource-gained + :event :resource-gained + :args #(hash-map + :target-name %1 + :amount (->int %2) + :resource-type (->kw %3) + :skill %4 + :source-name %1)} + + {:regex #"^(.+?)'s (.+) drains (\d+) (health|Mana|Rage|Energy|Happiness) from (.+)\.$" + :id :resource-drained-from-skill + :logfmt :resource-drained-from-skill + :event :resource-lost + :args #(hash-map + :source-name %1 + :skill %2 + :amount (->int %3) + :resource-type (->kw %4) + :target-name %5)} + + ; TODO: how does this one look if target=you? does the end bit after "from" just say "you"? + {:regex #"^Your (.+) drains (\d+) (health|Mana|Rage|Energy|Happiness) from (.+)\.$" + :id :resource-drained-from-skill-self + :logfmt :resource-drained-from-skill + :event :resource-lost + :args #(hash-map + :skill %1 + :amount (->int %2) + :resource-type (->kw %3) + :target-name %4 + :source-name "you")} + + + ;;; --------------------------------------------------------------------------------------------- + ;;; OTHER SPECIAL ABILITY/BUFF GAINS + ;;; note that these are for things like skill procs or whatever that show up in the combat log + ;;; with text similar to that of buffs/debuffs but these are not buffs/debuffs. they need to be + ;;; caught first or they will be misinterpreted as buffs by the regex's in the next section + + {:regex #"^(.+) (?:gain|gains) (.+) through (.+)\.$" + :id :special-gained + :logfmt :special-gained + :event :special-gained + :args #(hash-map + :target-name %1 + :special %2 + ; NOTE: could be an entity name or a skill/talent name + :source %3)} + + ;;; --------------------------------------------------------------------------------------------- + ;;; BUFF/DEBUFF + + {:regex #"^(.+) (?:gain|gains) (.+?)(?: \((\d+)\))?\.$" + :id :aura-buff-gained + :logfmt :aura-buff-gained + :event :aura-gained + :args #(hash-map + :target-name %1 + :aura-name %2 + :stacks (->int %3) + :type :buff)} + + {:regex #"^(.+) (?:is|are) afflicted by (.+?)(?: \((\d+)\))?\.$" + :id :aura-debuff-gained + :logfmt :aura-debuff-gained + :event :aura-gained + :args #(hash-map + :target-name %1 + :aura-name %2 + :stacks (->int %3) + :type :debuff)} + + {:regex #"^(.+) fades from (.+)\.$" + :id :aura-fades + :logfmt :aura-fades + :event :aura-lost + :args #(hash-map + :aura-name %1 + :target-name %2 + :faded? true)} + + {:regex #"^Your (.+) is removed\.$" + :id :aura-removed-self + :logfmt :aura-removed + :event :aura-lost + :args #(hash-map + :aura-name %1 + :target-name "you" + :faded? false)} + + {:regex #"^(.+?)'s (.+) is removed\.$" + :id :aura-removed + :logfmt :aura-removed + :event :aura-lost + :args #(hash-map + :target-name %1 + :aura-name %2 + :faded? false)} + + ;;; --------------------------------------------------------------------------------------------- + ;;; ENVIRONMENTAL / OTHER DAMAGE + + {:regex #"^(.+) (?:suffers|suffer) (\d+) points of (.+) damage\.(?: \((\d+) resisted\))?(?: \((\d+) absorbed\))?$" + :id :environmental-damage + :logfmt :environmental-damage + :event :other-damage + :args #(hash-map + :target-name %1 + :damage (->int %2) + :damage-type (->kw %3) + :resisted (->int %4) + :absorbed (->int %5))} + + {:regex #"^(.+) (?:lose|loses) (\d+) health for swimming in lava\.(?: \((\d+) resisted\))?(?: \((\d+) absorbed\))?$" + :id :lava-swim-damage + :logfmt :lava-swim-damage + :event :other-damage + :args #(hash-map + :target-name %1 + :damage (->int %2) + :resisted (->int %3) + :absorbed (->int %4) + :damage-type :fire + :source "Swimming in lava")} + + {:regex #"^(.+) (?:fall|falls) and (?:lose|loses) (\d+) health\.$" + :id :fall-damage + :logfmt :fall-damage + :event :other-damage + :args #(hash-map + :damage (->int %2) + :target-name %1 + :source "Falling")} + + ;;; --------------------------------------------------------------------------------------------- + ;;; DEATH + + {:regex #"^(.+) (?:die|dies)\.$" + :id :death + :logfmt :death + :event :death + :args #(hash-map + :source-name %1)} + + ]) diff --git a/vwowrla.core/src/vwowrla/core/parser.clj b/vwowrla.core/src/vwowrla/core/parser.clj new file mode 100644 index 0000000..ef71dba --- /dev/null +++ b/vwowrla.core/src/vwowrla/core/parser.clj @@ -0,0 +1,86 @@ +(ns vwowrla.core.parser + (:import (java.util TimeZone)) + (:require + [clojure.tools.logging :refer [info error warn]] + [clojure.java.io :as io] + [vwowrla.core.encounters :as encounters] + [vwowrla.core.handlers :refer [handle-event]] + [vwowrla.core.matchers :refer [regex-matchers]]) + (:use + vwowrla.core.preparsing + vwowrla.core.utils)) + +(defn active-encounter? + [data] + (not (nil? (:active-encounter data)))) + +(defn parse-line + [^String line {:keys [log-owner-char-name] :as options}] + (let [[timestamp stripped-line] (split-log-timestamp-and-content line) + sanitized-line (-> stripped-line + (undo-swstats-fixlogstring) + (sanitize-entity-names)) + line-metadata {:timestamp (parse-log-timestamp timestamp options) + :line line}] + (if-let [matcher (->> regex-matchers + (filter #(re-matches (:regex %) sanitized-line)) + (first))] + (let [regex-matches (rest (re-matches (:regex matcher) sanitized-line)) + args-fn (or (:args matcher) (fn [& _])) + parsed-line (merge + line-metadata + (select-keys matcher [:logfmt :event :id]) + (apply args-fn regex-matches))] + (process-parsed-line parsed-line log-owner-char-name)) + line-metadata))) + +(defn handle-line + [parsed-line data] + (handle-event parsed-line data)) + +(defn- active-encounter-processing + [parsed-line data] + (let [data (handle-line parsed-line data)] + (if-let [encounter-end (encounters/detect-encounter-end parsed-line data)] + (encounters/end-encounter parsed-line encounter-end data) + data))) + +(defn- out-of-encounter-processing + [parsed-line data] + (if-let [encounter-name (encounters/detect-encounter-triggered parsed-line data)] + (->> data + (encounters/begin-encounter encounter-name parsed-line) + (handle-line parsed-line)) + data)) + +(defn- parse-log* + [f options] + (with-open [rdr (io/reader f)] + (try + (reduce + (fn [data ^String line] + (try + (let [parsed (parse-line line options)] + (if (active-encounter? data) + (active-encounter-processing parsed data) + (out-of-encounter-processing parsed data))) + (catch Exception ex + (throw (ex-info "Parser error" {:line line} ex))))) + {:encounters [] + :active-encounter nil} + (line-seq rdr)) + (catch Exception ex + (flush) + (error ex "Parser error."))))) + +(defn parse-log + [f options] + (let [line-ending-type (detect-file-line-ending-type f)] + (if-not line-ending-type + (warn "Could not detect line-ending type in log file. Assuming" :windows) + (info "Detected" line-ending-type "line-ending type in log file.")) + (parse-log* f (merge + {:windows? (= :windows (or line-ending-type :windows)) + :timezone (TimeZone/getDefault) + :year (current-year)} + options)))) diff --git a/vwowrla.core/src/vwowrla/core/preparsing.clj b/vwowrla.core/src/vwowrla/core/preparsing.clj new file mode 100644 index 0000000..7b14f1a --- /dev/null +++ b/vwowrla.core/src/vwowrla/core/preparsing.clj @@ -0,0 +1,73 @@ +(ns vwowrla.core.preparsing + (:import + (java.util Calendar GregorianCalendar TimeZone)) + (:require + [clojure.string :as string] + [clojure.java.io :as io]) + (:use + vwowrla.core.utils)) + +(def problem-entity-names (get-text-resource-as-lines "problem_entity_names.txt")) + +(def problem-entity-name-to-fixed-name + (reduce + (fn [m problem-name] + (let [fixed-name (.replace problem-name "'s" "s")] + (-> m + (assoc-in [:problem-to-fixed problem-name] fixed-name) + (assoc-in [:fixed-to-problem fixed-name] problem-name)))) + {:problem-to-fixed {} + :fixed-to-problem {}} + problem-entity-names)) + +(defn sanitize-entity-name + [^String entity-name] + (get-in problem-entity-name-to-fixed-name [:problem-to-fixed entity-name] entity-name)) + +(defn get-original-entity-name + [^String potentially-sanitized-entity-name] + (get-in problem-entity-name-to-fixed-name [:fixed-to-problem potentially-sanitized-entity-name] potentially-sanitized-entity-name)) + +(defn sanitize-entity-names + [^String line] + (reduce + (fn [^String line [^String problem-name ^String fixed-name]] + (.replace line problem-name fixed-name)) + line + (:problem-to-fixed problem-entity-name-to-fixed-name))) + +(defn undo-swstats-fixlogstring + [^String line] + (.replace line " 's" "'s")) + +(defn parse-log-timestamp + [^String timestamp {:keys [^long year ^TimeZone timezone windows?] :as options}] + (if-let [matches (re-matches #"^(\d{1,2})\/(\d{1,2}) (\d{1,2}):(\d{2}):(\d{2})\.(\d{3})$" timestamp)] + (let [c (GregorianCalendar.) + [month day hour minute second millis] (rest matches)] + (.clear c) + (.setTimeZone c timezone) + (.set c year (if windows? (dec (->int month)) (->int month)) (->int day) (->int hour) (->int minute) (->int second)) + (.set c Calendar/MILLISECOND (->int millis)) + (.getTime c)))) + +(defn split-log-timestamp-and-content + [^String line] + (clojure.string/split line #" " 2)) + +(defn process-parsed-line + [{:keys [source-name target-name source] :as parsed-line} ^String log-owner-char-name] + (merge + parsed-line + (if source-name + {:source-name (if (= "you" (string/lower-case source-name)) + log-owner-char-name + (get-original-entity-name source-name))}) + (if target-name + {:target-name (if (= "you" (string/lower-case target-name)) + log-owner-char-name + (get-original-entity-name target-name))}) + (if source + {:source (if (= "you" (string/lower-case source)) + log-owner-char-name + (get-original-entity-name source))}))) \ No newline at end of file diff --git a/vwowrla.core/src/vwowrla/core/utils.clj b/vwowrla.core/src/vwowrla/core/utils.clj new file mode 100644 index 0000000..526ad25 --- /dev/null +++ b/vwowrla.core/src/vwowrla/core/utils.clj @@ -0,0 +1,75 @@ +(ns vwowrla.core.utils + (:import + (java.io Reader) + (java.util Date)) + (:require + [clojure.string :as string] + [clojure.edn :as edn] + [clojure.java.io :as io] + [cheshire.core :as json])) + + +(defn ->kw + [^String s] + (keyword (string/lower-case s))) + +(defn ->int + [^String s] + (if s + (Integer/parseInt s))) + +(defn one-of? + [x & more] + (boolean + (some #{x} more))) + +(defn contained-in? + [x coll] + (boolean + (some #{x} coll))) + +(defn detect-file-line-ending-type + [f] + (if-let [^Reader reader (io/reader f)] + (loop [ch (.read reader)] + (cond + (= -1 ch) + nil + + (= 10 ch) + :linux + + (= 13 ch) + (let [ch (.read reader)] + (if (= 10 ch) + :windows + :unix)) + + :default + (recur (.read reader)))))) + +(defn current-year + [] + (+ 1900 (.getYear (Date.)))) + +(defn time-between + [^Date a ^Date b] + (- (.getTime b) + (.getTime a))) + +(defn get-text-resource-as-lines + [f] + (with-open [rdr (io/reader (io/resource f))] + (doall (line-seq rdr)))) + +(defn get-json-resource + [f] + (-> (io/resource f) + (io/reader) + (json/parse-stream true))) + +(defn get-edn-resource + [f] + (-> (io/resource f) + (slurp) + (edn/read-string))) \ No newline at end of file diff --git a/vwowrla.core/test/vwowrla/core/matchers/aura_gained_test.clj b/vwowrla.core/test/vwowrla/core/matchers/aura_gained_test.clj new file mode 100644 index 0000000..1bb8368 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/aura_gained_test.clj @@ -0,0 +1,102 @@ +(ns vwowrla.core.matchers.aura-gained-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest aura-buff-gained + (is (valid-matcher? (get-matcher regex-matchers :aura-buff-gained))) + + (is (= (parse-line "5/25 21:21:16.385 Vasling gains Blink." options) + {:id :aura-buff-gained + :logfmt :aura-buff-gained + :event :aura-gained + :line "5/25 21:21:16.385 Vasling gains Blink." + :timestamp (parse-log-timestamp "5/25 21:21:16.385" options) + :target-name "Vasling" + :aura-name "Blink" + :type :buff + :stacks nil})) + + (is (= (parse-line "5/25 21:16:27.257 Eggs gains Renew." options) + {:id :aura-buff-gained + :logfmt :aura-buff-gained + :event :aura-gained + :line "5/25 21:16:27.257 Eggs gains Renew." + :timestamp (parse-log-timestamp "5/25 21:16:27.257" options) + :target-name "Eggs" + :aura-name "Renew" + :type :buff + :stacks nil})) + + (is (= (parse-line "5/25 23:26:03.093 Victore gains Bonereaver's Edge (2)." options) + {:id :aura-buff-gained + :logfmt :aura-buff-gained + :event :aura-gained + :line "5/25 23:26:03.093 Victore gains Bonereaver's Edge (2)." + :timestamp (parse-log-timestamp "5/25 23:26:03.093" options) + :target-name "Victore" + :aura-name "Bonereaver's Edge" + :type :buff + :stacks 2})) + + (is (= (parse-line "5/25 21:42:59.537 You gain Regrowth." options) + {:id :aura-buff-gained + :logfmt :aura-buff-gained + :event :aura-gained + :line "5/25 21:42:59.537 You gain Regrowth." + :timestamp (parse-log-timestamp "5/25 21:42:59.537" options) + :target-name owner-char-name + :aura-name "Regrowth" + :type :buff + :stacks nil}))) + +(deftest aura-debuff-gained + (is (valid-matcher? (get-matcher regex-matchers :aura-debuff-gained))) + + (is (= (parse-line "5/25 21:16:46.564 Vasling is afflicted by Gnomish Death Ray." options) + {:id :aura-debuff-gained + :logfmt :aura-debuff-gained + :event :aura-gained + :line "5/25 21:16:46.564 Vasling is afflicted by Gnomish Death Ray." + :timestamp (parse-log-timestamp "5/25 21:16:46.564" options) + :target-name "Vasling" + :aura-name "Gnomish Death Ray" + :type :debuff + :stacks nil})) + + (is (= (parse-line "5/25 21:16:43.064 Onyxia is afflicted by Shadow Vulnerability (5)." options) + {:id :aura-debuff-gained + :logfmt :aura-debuff-gained + :event :aura-gained + :line "5/25 21:16:43.064 Onyxia is afflicted by Shadow Vulnerability (5)." + :timestamp (parse-log-timestamp "5/25 21:16:43.064" options) + :target-name "Onyxia" + :aura-name "Shadow Vulnerability" + :type :debuff + :stacks 5})) + + (is (= (parse-line "5/25 21:43:05.511 You are afflicted by Weakened Soul." options) + {:id :aura-debuff-gained + :logfmt :aura-debuff-gained + :event :aura-gained + :line "5/25 21:43:05.511 You are afflicted by Weakened Soul." + :timestamp (parse-log-timestamp "5/25 21:43:05.511" options) + :target-name owner-char-name + :aura-name "Weakened Soul" + :type :debuff + :stacks nil}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/aura_lost_test.clj b/vwowrla.core/test/vwowrla/core/matchers/aura_lost_test.clj new file mode 100644 index 0000000..86c9881 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/aura_lost_test.clj @@ -0,0 +1,90 @@ +(ns vwowrla.core.matchers.aura-lost-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest aura-fades + (is (valid-matcher? (get-matcher regex-matchers :aura-fades))) + + (is (= (parse-line "5/25 21:16:39.701 Defensive Stance fades from Eggs." options) + {:id :aura-fades + :logfmt :aura-fades + :event :aura-lost + :line "5/25 21:16:39.701 Defensive Stance fades from Eggs." + :timestamp (parse-log-timestamp "5/25 21:16:39.701" options) + :target-name "Eggs" + :aura-name "Defensive Stance" + :faded? true})) + + (is (= (parse-line "5/25 21:17:53.550 Fire Shield fades from you." options) + {:id :aura-fades + :logfmt :aura-fades + :event :aura-lost + :line "5/25 21:17:53.550 Fire Shield fades from you." + :timestamp (parse-log-timestamp "5/25 21:17:53.550" options) + :target-name owner-char-name + :aura-name "Fire Shield" + :faded? true}))) + +(deftest aura-removed-self + (is (valid-matcher? (get-matcher regex-matchers :aura-removed-self))) + + (is (= (parse-line "5/25 23:09:26.614 Your Winter's Chill is removed." options) + {:id :aura-removed-self + :logfmt :aura-removed + :event :aura-lost + :line "5/25 23:09:26.614 Your Winter's Chill is removed." + :timestamp (parse-log-timestamp "5/25 23:09:26.614" options) + :target-name owner-char-name + :aura-name "Winter's Chill" + :faded? false}))) + +(deftest aura-removed + (is (valid-matcher? (get-matcher regex-matchers :aura-removed))) + + (is (= (parse-line "5/25 22:41:56.108 Magnomage's Shazzrah's Curse is removed." options) + {:id :aura-removed + :logfmt :aura-removed + :event :aura-lost + :line "5/25 22:41:56.108 Magnomage's Shazzrah's Curse is removed." + :timestamp (parse-log-timestamp "5/25 22:41:56.108" options) + :target-name "Magnomage" + :aura-name "Shazzrah's Curse" + :faded? false})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Twilight's Hammer Ambassador's Winter's Chill is removed." options) + {:id :aura-removed + :logfmt :aura-removed + :event :aura-lost + :line "1/2 3:45:00.123 Twilight's Hammer Ambassador's Winter's Chill is removed." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Twilight's Hammer Ambassador" + :aura-name "Winter's Chill" + :faded? false})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Twilight's Hammer Ambassador's Mana Shield is removed." options) + {:id :aura-removed + :logfmt :aura-removed + :event :aura-lost + :line "1/2 3:45:00.123 Twilight's Hammer Ambassador's Mana Shield is removed." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Twilight's Hammer Ambassador" + :aura-name "Mana Shield" + :faded? false}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/cast_test.clj b/vwowrla.core/test/vwowrla/core/matchers/cast_test.clj new file mode 100644 index 0000000..0ae0833 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/cast_test.clj @@ -0,0 +1,96 @@ +(ns vwowrla.core.matchers.cast-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest cast-begins + (is (valid-matcher? (get-matcher regex-matchers :cast-begins))) + + (is (= (parse-line "5/25 23:27:34.377 Fei begins to cast Lesser Healing Wave." options) + {:id :cast-begins + :logfmt :cast-begins + :event :cast-begins + :line "5/25 23:27:34.377 Fei begins to cast Lesser Healing Wave." + :timestamp (parse-log-timestamp "5/25 23:27:34.377" options) + :source-name "Fei" + :skill "Lesser Healing Wave" + :spell? true})) + + (is (= (parse-line "5/25 21:42:31.485 Bahamatt begins to perform Auto Shot." options) + {:id :cast-begins + :logfmt :cast-begins + :event :cast-begins + :line "5/25 21:42:31.485 Bahamatt begins to perform Auto Shot." + :timestamp (parse-log-timestamp "5/25 21:42:31.485" options) + :source-name "Bahamatt" + :skill "Auto Shot" + :spell? false})) + + (is (= (parse-line "5/25 23:31:26.154 Laurent begins to perform Pick Lock." options) + {:id :cast-begins + :logfmt :cast-begins + :event :cast-begins + :line "5/25 23:31:26.154 Laurent begins to perform Pick Lock." + :timestamp (parse-log-timestamp "5/25 23:31:26.154" options) + :source-name "Laurent" + :skill "Pick Lock" + :spell? false})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You begin to perform Pick Lock." options) + {:id :cast-begins + :logfmt :cast-begins + :event :cast-begins + :line "1/2 3:45:00.123 You begin to perform Pick Lock." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :source-name owner-char-name + :skill "Pick Lock" + :spell? false}))) + +(deftest cast-instant + (is (valid-matcher? (get-matcher regex-matchers :cast))) + + (is (= (parse-line "5/25 21:14:54.539 Eggs casts Place Unfired Blade." options) + {:id :cast + :logfmt :cast + :event :cast + :line "5/25 21:14:54.539 Eggs casts Place Unfired Blade." + :timestamp (parse-log-timestamp "5/25 21:14:54.539" options) + :source-name "Eggs" + :skill "Place Unfired Blade" + :spell? true})) + + (is (= (parse-line "5/25 21:15:09.436 Pwnstar performs Berserking." options) + {:id :cast + :logfmt :cast + :event :cast + :line "5/25 21:15:09.436 Pwnstar performs Berserking." + :timestamp (parse-log-timestamp "5/25 21:15:09.436" options) + :source-name "Pwnstar" + :skill "Berserking" + :spell? false})) + + (is (= (parse-line "5/25 21:46:25.894 You cast Remove Lesser Curse." options) + {:id :cast + :logfmt :cast + :event :cast + :line "5/25 21:46:25.894 You cast Remove Lesser Curse." + :timestamp (parse-log-timestamp "5/25 21:46:25.894" options) + :source-name owner-char-name + :skill "Remove Lesser Curse" + :spell? true}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/damage_reflected_test.clj b/vwowrla.core/test/vwowrla/core/matchers/damage_reflected_test.clj new file mode 100644 index 0000000..7398eae --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/damage_reflected_test.clj @@ -0,0 +1,56 @@ +(ns vwowrla.core.matchers.damage-reflected-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest target-reflects-elemental-damage + (is (valid-matcher? (get-matcher regex-matchers :target-reflects-elemental-damage))) + + (is (= (parse-line "5/25 21:13:30.482 Futilian reflects 16 Fire damage to Onyxia." options) + {:id :target-reflects-elemental-damage + :logfmt :target-reflects-elemental-damage + :event :damage-reflected + :line "5/25 21:13:30.482 Futilian reflects 16 Fire damage to Onyxia." + :timestamp (parse-log-timestamp "5/25 21:13:30.482" options) + :target-name "Onyxia" + :source-name "Futilian" + :damage 16 + :damage-type :fire})) + + (is (= (parse-line "5/25 23:26:09.552 Son of Flame reflects 50 Fire damage to Deadasfuk." options) + {:id :target-reflects-elemental-damage + :logfmt :target-reflects-elemental-damage + :event :damage-reflected + :line "5/25 23:26:09.552 Son of Flame reflects 50 Fire damage to Deadasfuk." + :timestamp (parse-log-timestamp "5/25 23:26:09.552" options) + :target-name "Deadasfuk" + :source-name "Son of Flame" + :damage 50 + :damage-type :fire})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You reflect 42 Fire damage to Winna's Kitten." options) + {:id :target-reflects-elemental-damage + :logfmt :target-reflects-elemental-damage + :event :damage-reflected + :line "1/2 3:45:00.123 You reflect 42 Fire damage to Winna's Kitten." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Winna's Kitten" + :source-name owner-char-name + :damage 42 + :damage-type :fire}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/death_test.clj b/vwowrla.core/test/vwowrla/core/matchers/death_test.clj new file mode 100644 index 0000000..b568d06 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/death_test.clj @@ -0,0 +1,38 @@ +(ns vwowrla.core.matchers.death-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest death + (is (valid-matcher? (get-matcher regex-matchers :death))) + + (is (= (parse-line "5/25 21:17:18.944 Onyxia dies." options) + {:id :death + :logfmt :death + :event :death + :line "5/25 21:17:18.944 Onyxia dies." + :timestamp (parse-log-timestamp "5/25 21:17:18.944" options) + :source-name "Onyxia"})) + + (is (= (parse-line "5/25 22:24:54.829 You die." options) + {:id :death + :logfmt :death + :event :death + :line "5/25 22:24:54.829 You die." + :timestamp (parse-log-timestamp "5/25 22:24:54.829" options) + :source-name owner-char-name}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/dot_damages_target_test.clj b/vwowrla.core/test/vwowrla/core/matchers/dot_damages_target_test.clj new file mode 100644 index 0000000..26ab44f --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/dot_damages_target_test.clj @@ -0,0 +1,97 @@ +(ns vwowrla.core.matchers.dot-damages-target-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest dot-damages-target + (is (valid-matcher? (get-matcher regex-matchers :dot-damages-target))) + + (is (= (parse-line "5/25 21:16:56.786 Onyxia suffers 261 Shadow damage from Smooth's Corruption." options) + {:id :dot-damages-target + :logfmt :dot-damages-target + :event :dot-damages-target + :line "5/25 21:16:56.786 Onyxia suffers 261 Shadow damage from Smooth's Corruption." + :timestamp (parse-log-timestamp "5/25 21:16:56.786" options) + :target-name "Onyxia" + :source-name "Smooth" + :skill "Corruption" + :damage 261 + :damage-type :shadow + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 22:01:54.974 You suffer 1010 Fire damage from Gehennas's Rain of Fire. (60 absorbed)" options) + {:id :dot-damages-target + :logfmt :dot-damages-target + :event :dot-damages-target + :line "5/25 22:01:54.974 You suffer 1010 Fire damage from Gehennas's Rain of Fire. (60 absorbed)" + :timestamp (parse-log-timestamp "5/25 22:01:54.974" options) + :target-name owner-char-name + :source-name "Gehennas" + :skill "Rain of Fire" + :damage 1010 + :damage-type :fire + :absorbed 60 + :resisted nil})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia suffers 1337 Shadow damage from Sri'skulk's Arugal's Curse." options) + {:id :dot-damages-target + :logfmt :dot-damages-target + :event :dot-damages-target + :line "1/2 3:45:00.123 Onyxia suffers 1337 Shadow damage from Sri'skulk's Arugal's Curse." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Onyxia" + :source-name "Sri'skulk" + :skill "Arugal's Curse" + :damage 1337 + :damage-type :shadow + :absorbed nil + :resisted nil})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia suffers 1337 Shadow damage from Sri'skulk's Corruption." options) + {:id :dot-damages-target + :logfmt :dot-damages-target + :event :dot-damages-target + :line "1/2 3:45:00.123 Onyxia suffers 1337 Shadow damage from Sri'skulk's Corruption." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Onyxia" + :source-name "Sri'skulk" + :skill "Corruption" + :damage 1337 + :damage-type :shadow + :absorbed nil + :resisted nil}))) + +(deftest dot-damages-target-self + (is (valid-matcher? (get-matcher regex-matchers :dot-damages-target-self))) + + (is (= (parse-line "5/25 21:15:18.909 Onyxian Whelp suffers 179 Frost damage from your Blizzard." options) + {:id :dot-damages-target-self + :logfmt :dot-damages-target + :event :dot-damages-target + :line "5/25 21:15:18.909 Onyxian Whelp suffers 179 Frost damage from your Blizzard." + :timestamp (parse-log-timestamp "5/25 21:15:18.909" options) + :target-name "Onyxian Whelp" + :source-name owner-char-name + :skill "Blizzard" + :damage 179 + :damage-type :frost + :absorbed nil + :resisted nil}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/matchers_test_utils.clj b/vwowrla.core/test/vwowrla/core/matchers/matchers_test_utils.clj new file mode 100644 index 0000000..6deccaf --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/matchers_test_utils.clj @@ -0,0 +1,13 @@ +(ns vwowrla.core.matchers.matchers-test-utils) + +(defn get-matcher [matchers id] + (->> matchers + (filter #(= (:id %) id)) + (first))) + +(defn valid-matcher? [matcher] + (and (map? matcher) + (:regex matcher) + (:logfmt matcher) + (:event matcher) + (:args matcher))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/melee_avoided_by_target_test.clj b/vwowrla.core/test/vwowrla/core/matchers/melee_avoided_by_target_test.clj new file mode 100644 index 0000000..579028e --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/melee_avoided_by_target_test.clj @@ -0,0 +1,124 @@ +(ns vwowrla.core.matchers.melee-avoided-by-target-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest melee-full-absorb + (is (valid-matcher? (get-matcher regex-matchers :melee-full-absorb))) + + (is (= (parse-line "5/25 21:51:35.315 Magmadar attacks. Eggs absorbs all the damage." options) + {:id :melee-full-absorb + :logfmt :melee-full-absorb + :event :melee-avoided-by-target + :line "5/25 21:51:35.315 Magmadar attacks. Eggs absorbs all the damage." + :timestamp (parse-log-timestamp "5/25 21:51:35.315" options) + :target-name "Eggs" + :source-name "Magmadar" + :avoidance-method :absorb}))) + +(deftest melee-full-resist + (is (valid-matcher? (get-matcher regex-matchers :melee-full-resist))) + + (is (= (parse-line "5/25 22:33:14.279 Baron Geddon attacks. Futilian resists all the damage." options) + {:id :melee-full-resist + :logfmt :melee-full-resist + :event :melee-avoided-by-target + :line "5/25 22:33:14.279 Baron Geddon attacks. Futilian resists all the damage." + :timestamp (parse-log-timestamp "5/25 22:33:14.279" options) + :target-name "Futilian" + :source-name "Baron Geddon" + :avoidance-method :resist}))) + +(deftest melee-miss + (is (valid-matcher? (get-matcher regex-matchers :melee-miss))) + + (is (= (parse-line "5/25 23:27:21.695 Architrex misses Ragnaros." options) + {:id :melee-miss + :logfmt :melee-miss + :event :melee-avoided-by-target + :line "5/25 23:27:21.695 Architrex misses Ragnaros." + :timestamp (parse-log-timestamp "5/25 23:27:21.695" options) + :target-name "Ragnaros" + :source-name "Architrex" + :avoidance-method :miss}))) + +(deftest melee-parry + (is (valid-matcher? (get-matcher regex-matchers :melee-parry))) + + (is (= (parse-line "5/25 23:07:34.127 Flamewaker Elite attacks. Boompow parries." options) + {:id :melee-parry + :logfmt :melee-parry + :event :melee-avoided-by-target + :line "5/25 23:07:34.127 Flamewaker Elite attacks. Boompow parries." + :timestamp (parse-log-timestamp "5/25 23:07:34.127" options) + :target-name "Boompow" + :source-name "Flamewaker Elite" + :avoidance-method :parry}))) + +(deftest melee-dodge + (is (valid-matcher? (get-matcher regex-matchers :melee-dodge))) + + (is (= (parse-line "5/25 23:03:44.619 Laurent attacks. Ancient Core Hound dodges." options) + {:id :melee-dodge + :logfmt :melee-dodge + :event :melee-avoided-by-target + :line "5/25 23:03:44.619 Laurent attacks. Ancient Core Hound dodges." + :timestamp (parse-log-timestamp "5/25 23:03:44.619" options) + :target-name "Ancient Core Hound" + :source-name "Laurent" + :avoidance-method :dodge}))) + +(deftest melee-block + (is (valid-matcher? (get-matcher regex-matchers :melee-block))) + + (is (= (parse-line "5/25 22:10:51.639 Molten Destroyer attacks. Futilian blocks." options) + {:id :melee-block + :logfmt :melee-block + :event :melee-avoided-by-target + :line "5/25 22:10:51.639 Molten Destroyer attacks. Futilian blocks." + :timestamp (parse-log-timestamp "5/25 22:10:51.639" options) + :target-name "Futilian" + :source-name "Molten Destroyer" + :avoidance-method :block}))) + +(deftest melee-evade + (is (valid-matcher? (get-matcher regex-matchers :melee-evade))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You attack. Onyxia evades." options) + {:id :melee-evade + :logfmt :melee-evade + :event :melee-avoided-by-target + :line "1/2 3:45:00.123 You attack. Onyxia evades." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Onyxia" + :source-name owner-char-name + :avoidance-method :evade}))) + +(deftest melee-immune + (is (valid-matcher? (get-matcher regex-matchers :melee-immune))) + + (is (= (parse-line "5/25 22:38:40.210 Agusto attacks but Lava Elemental is immune." options) + {:id :melee-immune + :logfmt :melee-immune + :event :melee-avoided-by-target + :line "5/25 22:38:40.210 Agusto attacks but Lava Elemental is immune." + :timestamp (parse-log-timestamp "5/25 22:38:40.210" options) + :target-name "Lava Elemental" + :source-name "Agusto" + :avoidance-method :immune}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/melee_damage_to_target_test.clj b/vwowrla.core/test/vwowrla/core/matchers/melee_damage_to_target_test.clj new file mode 100644 index 0000000..857cf1a --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/melee_damage_to_target_test.clj @@ -0,0 +1,303 @@ +(ns vwowrla.core.matchers.melee-damage-to-target-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest melee-damages-target + (is (valid-matcher? (get-matcher regex-matchers :melee-damages-target))) + + (is (= (parse-line "5/25 21:42:23.038 Eggs hits Lava Surger for 175." options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "5/25 21:42:23.038 Eggs hits Lava Surger for 175." + :timestamp (parse-log-timestamp "5/25 21:42:23.038" options) + :target-name "Lava Surger" + :source-name "Eggs" + :damage 175 + :hit-type nil + :crit? false + :absorbed nil + :blocked nil})) + + (is (= (parse-line "6/16 21:29:44.927 You hit Lava Annihilator for 187." options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "6/16 21:29:44.927 You hit Lava Annihilator for 187." + :timestamp (parse-log-timestamp "6/16 21:29:44.927" options) + :target-name "Lava Annihilator" + :source-name owner-char-name + :damage 187 + :hit-type nil + :crit? false + :absorbed nil + :blocked nil})) + + (is (= (parse-line "5/25 21:42:22.322 Lava Surger crits Futilian for 1382." options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "5/25 21:42:22.322 Lava Surger crits Futilian for 1382." + :timestamp (parse-log-timestamp "5/25 21:42:22.322" options) + :target-name "Futilian" + :source-name "Lava Surger" + :damage 1382 + :hit-type nil + :crit? true + :absorbed nil + :blocked nil})) + + (is (= (parse-line "5/25 21:42:24.924 Lava Surger hits Eggs for 752. (81 blocked)" options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "5/25 21:42:24.924 Lava Surger hits Eggs for 752. (81 blocked)" + :timestamp (parse-log-timestamp "5/25 21:42:24.924" options) + :target-name "Eggs" + :source-name "Lava Surger" + :damage 752 + :hit-type nil + :crit? false + :absorbed nil + :blocked 81})) + + (is (= (parse-line "5/25 21:42:26.578 Laurent hits Lava Surger for 114. (glancing)" options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "5/25 21:42:26.578 Laurent hits Lava Surger for 114. (glancing)" + :timestamp (parse-log-timestamp "5/25 21:42:26.578" options) + :target-name "Lava Surger" + :source-name "Laurent" + :damage 114 + :hit-type :glancing + :crit? false + :absorbed nil + :blocked nil})) + + (is (= (parse-line "5/25 21:45:32.294 Lucifron hits Mightystroon for 1365. (crushing)" options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "5/25 21:45:32.294 Lucifron hits Mightystroon for 1365. (crushing)" + :timestamp (parse-log-timestamp "5/25 21:45:32.294" options) + :target-name "Mightystroon" + :source-name "Lucifron" + :damage 1365 + :hit-type :crushing + :crit? false + :absorbed nil + :blocked nil})) + + (is (= (parse-line "5/25 21:56:56.337 Flame Imp hits Aesthetera for 147. (395 absorbed)" options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "5/25 21:56:56.337 Flame Imp hits Aesthetera for 147. (395 absorbed)" + :timestamp (parse-log-timestamp "5/25 21:56:56.337" options) + :target-name "Aesthetera" + :source-name "Flame Imp" + :damage 147 + :hit-type nil + :crit? false + :absorbed 395 + :blocked nil})) + + (is (= (parse-line "5/25 21:52:56.175 Magmadar hits Eggs for 160. (82 blocked) (607 absorbed)" options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "5/25 21:52:56.175 Magmadar hits Eggs for 160. (82 blocked) (607 absorbed)" + :timestamp (parse-log-timestamp "5/25 21:52:56.175" options) + :target-name "Eggs" + :source-name "Magmadar" + :damage 160 + :hit-type nil + :crit? false + :absorbed 607 + :blocked 82})) + + (is (= (parse-line "6/9 21:50:40.122 Gehennas hits Mightystroon for 1530. (crushing) (96 absorbed)" options) + {:id :melee-damages-target + :logfmt :melee-damages-target + :event :melee-damage-to-target + :line "6/9 21:50:40.122 Gehennas hits Mightystroon for 1530. (crushing) (96 absorbed)" + :timestamp (parse-log-timestamp "6/9 21:50:40.122" options) + :target-name "Mightystroon" + :source-name "Gehennas" + :damage 1530 + :hit-type :crushing + :crit? false + :absorbed 96 + :blocked nil}))) + +(deftest melee-damages-target-elemental + (is (valid-matcher? (get-matcher regex-matchers :melee-damages-target-elemental))) + + (is (= (parse-line "5/25 21:58:30.162 Firelord hits Crayson for 792 Fire damage." options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "5/25 21:58:30.162 Firelord hits Crayson for 792 Fire damage." + :timestamp (parse-log-timestamp "5/25 21:58:30.162" options) + :target-name "Crayson" + :source-name "Firelord" + :damage 792 + :damage-type :fire + :hit-type nil + :crit? false + :absorbed nil + :resisted nil})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Twilight's Hammer Ambassador hits you for 42 Fire damage." options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "1/2 3:45:00.123 Twilight's Hammer Ambassador hits you for 42 Fire damage." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Twilight's Hammer Ambassador" + :damage 42 + :damage-type :fire + :hit-type nil + :crit? false + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 22:03:50.816 Lava Spawn crits Strength of Earth Totem IV for 1172 Fire damage." options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "5/25 22:03:50.816 Lava Spawn crits Strength of Earth Totem IV for 1172 Fire damage." + :timestamp (parse-log-timestamp "5/25 22:03:50.816" options) + :target-name "Strength of Earth Totem IV" + :source-name "Lava Spawn" + :damage 1172 + :damage-type :fire + :hit-type nil + :crit? true + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 21:58:42.215 Firelord hits Eggs for 480 Fire damage. (160 resisted)" options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "5/25 21:58:42.215 Firelord hits Eggs for 480 Fire damage. (160 resisted)" + :timestamp (parse-log-timestamp "5/25 21:58:42.215" options) + :target-name "Eggs" + :source-name "Firelord" + :damage 480 + :damage-type :fire + :hit-type nil + :crit? false + :absorbed nil + :resisted 160})) + + (is (= (parse-line "5/25 22:24:22.577 Baron Geddon hits Soma for 1592 Fire damage. (967 absorbed)" options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "5/25 22:24:22.577 Baron Geddon hits Soma for 1592 Fire damage. (967 absorbed)" + :timestamp (parse-log-timestamp "5/25 22:24:22.577" options) + :target-name "Soma" + :source-name "Baron Geddon" + :damage 1592 + :damage-type :fire + :hit-type nil + :crit? false + :absorbed 967 + :resisted nil})) + + (is (= (parse-line "5/25 23:25:53.596 Son of Flame hits Agusto for 269 Fire damage. (135 resisted) (138 absorbed)" options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "5/25 23:25:53.596 Son of Flame hits Agusto for 269 Fire damage. (135 resisted) (138 absorbed)" + :timestamp (parse-log-timestamp "5/25 23:25:53.596" options) + :target-name "Agusto" + :source-name "Son of Flame" + :damage 269 + :damage-type :fire + :hit-type nil + :crit? false + :absorbed 138 + :resisted 135})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Baron Geddon hits Rrahg for 3609 Fire damage. (glancing)" options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "1/2 3:45:00.123 Baron Geddon hits Rrahg for 3609 Fire damage. (glancing)" + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Rrahg" + :source-name "Baron Geddon" + :damage 3609 + :damage-type :fire + :hit-type :glancing + :crit? false + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 22:24:09.229 Baron Geddon hits Rrahg for 3609 Fire damage. (crushing)" options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "5/25 22:24:09.229 Baron Geddon hits Rrahg for 3609 Fire damage. (crushing)" + :timestamp (parse-log-timestamp "5/25 22:24:09.229" options) + :target-name "Rrahg" + :source-name "Baron Geddon" + :damage 3609 + :damage-type :fire + :hit-type :crushing + :crit? false + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 22:24:11.225 Baron Geddon hits Hsaru for 2911 Fire damage. (crushing) (646 resisted)" options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "5/25 22:24:11.225 Baron Geddon hits Hsaru for 2911 Fire damage. (crushing) (646 resisted)" + :timestamp (parse-log-timestamp "5/25 22:24:11.225" options) + :target-name "Hsaru" + :source-name "Baron Geddon" + :damage 2911 + :damage-type :fire + :hit-type :crushing + :crit? false + :absorbed nil + :resisted 646})) + + (is (= (parse-line "5/25 22:33:22.440 Baron Geddon hits Futilian for 1509 Fire damage. (crushing) (1376 resisted) (371 absorbed)" options) + {:id :melee-damages-target-elemental + :logfmt :melee-damages-target-elemental + :event :melee-damage-to-target + :line "5/25 22:33:22.440 Baron Geddon hits Futilian for 1509 Fire damage. (crushing) (1376 resisted) (371 absorbed)" + :timestamp (parse-log-timestamp "5/25 22:33:22.440" options) + :target-name "Futilian" + :source-name "Baron Geddon" + :damage 1509 + :damage-type :fire + :hit-type :crushing + :crit? false + :absorbed 371 + :resisted 1376}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/other_damage_test.clj b/vwowrla.core/test/vwowrla/core/matchers/other_damage_test.clj new file mode 100644 index 0000000..c935bfc --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/other_damage_test.clj @@ -0,0 +1,126 @@ +(ns vwowrla.core.matchers.other-damage-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest environmental-damage + (is (valid-matcher? (get-matcher regex-matchers :environmental-damage))) + + (is (= (parse-line "5/25 23:08:30.798 Hsaru suffers 713 points of fire damage." options) + {:id :environmental-damage + :logfmt :environmental-damage + :event :other-damage + :line "5/25 23:08:30.798 Hsaru suffers 713 points of fire damage." + :timestamp (parse-log-timestamp "5/25 23:08:30.798" options) + :target-name "Hsaru" + :damage 713 + :damage-type :fire + :absorbed nil + :resisted nil})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You suffer 42 points of fire damage." options) + {:id :environmental-damage + :logfmt :environmental-damage + :event :other-damage + :line "1/2 3:45:00.123 You suffer 42 points of fire damage." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :damage 42 + :damage-type :fire + :absorbed nil + :resisted nil}))) + +(deftest lava-swim-damage + (is (valid-matcher? (get-matcher regex-matchers :lava-swim-damage))) + + (is (= (parse-line "5/25 21:58:13.460 Agusto loses 583 health for swimming in lava." options) + {:id :lava-swim-damage + :logfmt :lava-swim-damage + :event :other-damage + :line "5/25 21:58:13.460 Agusto loses 583 health for swimming in lava." + :timestamp (parse-log-timestamp "5/25 21:58:13.460" options) + :target-name "Agusto" + :source "Swimming in lava" + :damage 583 + :damage-type :fire + :absorbed nil + :resisted nil})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You lose 42 health for swimming in lava." options) + {:id :lava-swim-damage + :logfmt :lava-swim-damage + :event :other-damage + :line "1/2 3:45:00.123 You lose 42 health for swimming in lava." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source "Swimming in lava" + :damage 42 + :damage-type :fire + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 23:16:47.108 Agusto loses 0 health for swimming in lava. (632 absorbed)" options) + {:id :lava-swim-damage + :logfmt :lava-swim-damage + :event :other-damage + :line "5/25 23:16:47.108 Agusto loses 0 health for swimming in lava. (632 absorbed)" + :timestamp (parse-log-timestamp "5/25 23:16:47.108" options) + :target-name "Agusto" + :source "Swimming in lava" + :damage 0 + :damage-type :fire + :absorbed 632 + :resisted nil})) + + (is (= (parse-line "6/9 21:32:31.616 Hsaru loses 205 health for swimming in lava. (380 absorbed)" options) + {:id :lava-swim-damage + :logfmt :lava-swim-damage + :event :other-damage + :line "6/9 21:32:31.616 Hsaru loses 205 health for swimming in lava. (380 absorbed)" + :timestamp (parse-log-timestamp "6/9 21:32:31.616" options) + :target-name "Hsaru" + :source "Swimming in lava" + :damage 205 + :damage-type :fire + :absorbed 380 + :resisted nil}))) + +(deftest fall-damage + (is (valid-matcher? (get-matcher regex-matchers :fall-damage))) + + (is (= (parse-line "5/25 23:11:05.498 Crowquill falls and loses 167 health." options) + {:id :fall-damage + :logfmt :fall-damage + :event :other-damage + :line "5/25 23:11:05.498 Crowquill falls and loses 167 health." + :timestamp (parse-log-timestamp "5/25 23:11:05.498" options) + :target-name "Crowquill" + :source "Falling" + :damage 167})) + + (is (= (parse-line "5/25 23:10:40.994 You fall and lose 939 health." options) + {:id :fall-damage + :logfmt :fall-damage + :event :other-damage + :line "5/25 23:10:40.994 You fall and lose 939 health." + :timestamp (parse-log-timestamp "5/25 23:10:40.994" options) + :target-name owner-char-name + :source "Falling" + :damage 939}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/resource_gained_test.clj b/vwowrla.core/test/vwowrla/core/matchers/resource_gained_test.clj new file mode 100644 index 0000000..d08f645 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/resource_gained_test.clj @@ -0,0 +1,299 @@ +(ns vwowrla.core.matchers.resource-gained-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest resource-gained-from-skill + (is (valid-matcher? (get-matcher regex-matchers :resource-gained-from-skill))) + + (is (= (parse-line "5/25 21:21:32.915 Pwnstar gains 8 Mana from Pwnstar's Mana Regeneration." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "5/25 21:21:32.915 Pwnstar gains 8 Mana from Pwnstar's Mana Regeneration." + :timestamp (parse-log-timestamp "5/25 21:21:32.915" options) + :target-name "Pwnstar" + :source-name "Pwnstar" + :skill "Mana Regeneration" + :amount 8 + :resource-type :mana})) + + (is (= (parse-line "5/25 21:46:01.660 Vasling gains 375 Mana from Vasling's Replenish Mana." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "5/25 21:46:01.660 Vasling gains 375 Mana from Vasling's Replenish Mana." + :timestamp (parse-log-timestamp "5/25 21:46:01.660" options) + :target-name "Vasling" + :source-name "Vasling" + :skill "Replenish Mana" + :amount 375 + :resource-type :mana})) + + (is (= (parse-line "5/25 21:16:53.591 Architrex gains 25 Energy from Architrex's Relentless Strikes Effect." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "5/25 21:16:53.591 Architrex gains 25 Energy from Architrex's Relentless Strikes Effect." + :timestamp (parse-log-timestamp "5/25 21:16:53.591" options) + :target-name "Architrex" + :source-name "Architrex" + :skill "Relentless Strikes Effect" + :amount 25 + :resource-type :energy})) + + (is (= (parse-line "5/25 21:16:46.844 Boompow gains 1 Rage from Boompow's Bloodrage." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "5/25 21:16:46.844 Boompow gains 1 Rage from Boompow's Bloodrage." + :timestamp (parse-log-timestamp "5/25 21:16:46.844" options) + :target-name "Boompow" + :source-name "Boompow" + :skill "Bloodrage" + :amount 1 + :resource-type :rage})) + + (is (= (parse-line "5/25 21:13:17.531 Futilian gains 330 health from Leaf's Rejuvenation." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "5/25 21:13:17.531 Futilian gains 330 health from Leaf's Rejuvenation." + :timestamp (parse-log-timestamp "5/25 21:13:17.531" options) + :target-name "Futilian" + :source-name "Leaf" + :skill "Rejuvenation" + :amount 330 + :resource-type :health})) + + (is (= (parse-line "5/25 21:21:16.303 Shivaara gains 35 Happiness from Pwnstar's Feed Pet Effect." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "5/25 21:21:16.303 Shivaara gains 35 Happiness from Pwnstar's Feed Pet Effect." + :timestamp (parse-log-timestamp "5/25 21:21:16.303" options) + :target-name "Shivaara" + :source-name "Pwnstar" + :skill "Feed Pet Effect" + :amount 35 + :resource-type :happiness})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Somebody gains 100 health from Somebody's Druid's Potion." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "1/2 3:45:00.123 Somebody gains 100 health from Somebody's Druid's Potion." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Somebody" + :source-name "Somebody" + :skill "Druid's Potion" + :amount 100 + :resource-type :health})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Chok'sul gains 100 health from Chok'sul's Druid's Potion." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "1/2 3:45:00.123 Chok'sul gains 100 health from Chok'sul's Druid's Potion." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Chok'sul" + :source-name "Chok'sul" + :skill "Druid's Potion" + :amount 100 + :resource-type :health})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You gain 100 health from Somebody's Rejuvenation." options) + {:id :resource-gained-from-skill + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "1/2 3:45:00.123 You gain 100 health from Somebody's Rejuvenation." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Somebody" + :skill "Rejuvenation" + :amount 100 + :resource-type :health}))) + +(deftest resource-gained-from-skill-self + (is (valid-matcher? (get-matcher regex-matchers :resource-gained-from-skill-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Futilian gains 1337 health from your Rejuvenation." options) + {:id :resource-gained-from-skill-self + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "1/2 3:45:00.123 Futilian gains 1337 health from your Rejuvenation." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Futilian" + :source-name owner-char-name + :skill "Rejuvenation" + :amount 1337 + :resource-type :health})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You gain 1 Rage from your Bloodrage." options) + {:id :resource-gained-from-skill-self + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "1/2 3:45:00.123 You gain 1 Rage from your Bloodrage." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Bloodrage" + :amount 1 + :resource-type :rage})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You gain 25 Energy from your Relentless Strikes Effect." options) + {:id :resource-gained-from-skill-self + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "1/2 3:45:00.123 You gain 25 Energy from your Relentless Strikes Effect." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Relentless Strikes Effect" + :amount 25 + :resource-type :energy})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You gain 1000 Mana from your Replenish Mana." options) + {:id :resource-gained-from-skill-self + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "1/2 3:45:00.123 You gain 1000 Mana from your Replenish Mana." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Replenish Mana" + :amount 1000 + :resource-type :mana})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 SomePet gains 35 Happiness from your Feed Pet Effect." options) + {:id :resource-gained-from-skill-self + :logfmt :resource-gained-from-skill + :event :resource-gained + :line "1/2 3:45:00.123 SomePet gains 35 Happiness from your Feed Pet Effect." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "SomePet" + :source-name owner-char-name + :skill "Feed Pet Effect" + :amount 35 + :resource-type :happiness}))) + +(deftest resource-gained + (is (valid-matcher? (get-matcher regex-matchers :resource-gained))) + + (is (= (parse-line "5/25 22:15:35.674 You gain 1144 Mana from Replenish Mana." options) + {:id :resource-gained + :logfmt :resource-gained + :event :resource-gained + :line "5/25 22:15:35.674 You gain 1144 Mana from Replenish Mana." + :timestamp (parse-log-timestamp "5/25 22:15:35.674" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Replenish Mana" + :amount 1144 + :resource-type :mana})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You gain 1000 health from Rejuvenation." options) + {:id :resource-gained + :logfmt :resource-gained + :event :resource-gained + :line "1/2 3:45:00.123 You gain 1000 health from Rejuvenation." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Rejuvenation" + :amount 1000 + :resource-type :health})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You gain 1 Rage from Bloodrage." options) + {:id :resource-gained + :logfmt :resource-gained + :event :resource-gained + :line "1/2 3:45:00.123 You gain 1 Rage from Bloodrage." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Bloodrage" + :amount 1 + :resource-type :rage})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You gain 25 Energy from Relentless Strikes Effect." options) + {:id :resource-gained + :logfmt :resource-gained + :event :resource-gained + :line "1/2 3:45:00.123 You gain 25 Energy from Relentless Strikes Effect." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Relentless Strikes Effect" + :amount 25 + :resource-type :energy}))) + +(deftest resource-drained-from-skill + (is (valid-matcher? (get-matcher regex-matchers :resource-drained-from-skill))) + + (is (= (parse-line "6/16 22:36:47.916 Lazyspawn's Flee drains 225 Mana from Lazyspawn." options) + {:id :resource-drained-from-skill + :logfmt :resource-drained-from-skill + :event :resource-lost + :line "6/16 22:36:47.916 Lazyspawn's Flee drains 225 Mana from Lazyspawn." + :timestamp (parse-log-timestamp "6/16 22:36:47.916" options) + :target-name "Lazyspawn" + :source-name "Lazyspawn" + :skill "Flee" + :amount 225 + :resource-type :mana})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Dreka'Sur's Unit Test's Curse drains 100 health from Greatfather Winter's Helper." options) + {:id :resource-drained-from-skill + :logfmt :resource-drained-from-skill + :event :resource-lost + :line "1/2 3:45:00.123 Dreka'Sur's Unit Test's Curse drains 100 health from Greatfather Winter's Helper." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Greatfather Winter's Helper" + :source-name "Dreka'Sur" + :skill "Unit Test's Curse" + :amount 100 + :resource-type :health}))) + +(deftest resource-drained-from-skill-self + (is (valid-matcher? (get-matcher regex-matchers :resource-drained-from-skill-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Your Flee drains 225 Mana from you." options) + {:id :resource-drained-from-skill-self + :logfmt :resource-drained-from-skill + :event :resource-lost + :line "1/2 3:45:00.123 Your Flee drains 225 Mana from you." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Flee" + :amount 225 + :resource-type :mana}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/skill_avoided_by_target_test.clj b/vwowrla.core/test/vwowrla/core/matchers/skill_avoided_by_target_test.clj new file mode 100644 index 0000000..dfe1560 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/skill_avoided_by_target_test.clj @@ -0,0 +1,659 @@ +(ns vwowrla.core.matchers.skill-avoided-by-target-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest skill-miss-self + (is (valid-matcher? (get-matcher regex-matchers :skill-miss-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Your Sunder Armor missed Firelord." options) + {:id :skill-miss-self + :logfmt :skill-miss + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Your Sunder Armor missed Firelord." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Firelord" + :source-name owner-char-name + :skill "Sunder Armor" + :avoidance-method :miss}))) + +(deftest skill-miss + (is (valid-matcher? (get-matcher regex-matchers :skill-miss))) + + (is (= (parse-line "5/25 23:04:08.426 Eggs's Sunder Armor missed Firelord." options) + {:id :skill-miss + :logfmt :skill-miss + :event :skill-avoided-by-target + :line "5/25 23:04:08.426 Eggs's Sunder Armor missed Firelord." + :timestamp (parse-log-timestamp "5/25 23:04:08.426" options) + :target-name "Firelord" + :source-name "Eggs" + :skill "Sunder Armor" + :avoidance-method :miss})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack misses you." options) + {:id :skill-miss + :logfmt :skill-miss + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack misses you." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :miss})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack misses you." options) + {:id :skill-miss + :logfmt :skill-miss + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack misses you." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :miss}))) + +(deftest skill-parry-self + (is (valid-matcher? (get-matcher regex-matchers :skill-parry-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Your Test Attack was parried by Firelord." options) + {:id :skill-parry-self + :logfmt :skill-parry + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Your Test Attack was parried by Firelord." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Firelord" + :source-name owner-char-name + :skill "Test Attack" + :avoidance-method :parry}))) + +(deftest skill-parry + (is (valid-matcher? (get-matcher regex-matchers :skill-parry))) + + (is (= (parse-line "5/25 23:02:30.319 Futilian's Heroic Strike was parried by Golemagg the Incinerator." options) + {:id :skill-parry + :logfmt :skill-parry + :event :skill-avoided-by-target + :line "5/25 23:02:30.319 Futilian's Heroic Strike was parried by Golemagg the Incinerator." + :timestamp (parse-log-timestamp "5/25 23:02:30.319" options) + :target-name "Golemagg the Incinerator" + :source-name "Futilian" + :skill "Heroic Strike" + :avoidance-method :parry})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was parried by Tester." options) + {:id :skill-parry + :logfmt :skill-parry + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was parried by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :parry})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was parried by Tester." options) + {:id :skill-parry + :logfmt :skill-parry + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was parried by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :parry}))) + +(deftest skill-parry-implied-self + (is (valid-matcher? (get-matcher regex-matchers :skill-parry-implied-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was parried." options) + {:id :skill-parry-implied-self + :logfmt :skill-parry + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was parried." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :parry})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was parried." options) + {:id :skill-parry-implied-self + :logfmt :skill-parry + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was parried." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :parry}))) + +(deftest skill-block-self + (is (valid-matcher? (get-matcher regex-matchers :skill-block-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Your Test Attack was blocked by Firelord." options) + {:id :skill-block-self + :logfmt :skill-block + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Your Test Attack was blocked by Firelord." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Firelord" + :source-name owner-char-name + :skill "Test Attack" + :avoidance-method :block}))) + +(deftest skill-block + (is (valid-matcher? (get-matcher regex-matchers :skill-block))) + + (is (= (parse-line "5/25 23:08:34.885 Boompow's Shield Bash was blocked by Flamewaker Healer." options) + {:id :skill-block + :logfmt :skill-block + :event :skill-avoided-by-target + :line "5/25 23:08:34.885 Boompow's Shield Bash was blocked by Flamewaker Healer." + :timestamp (parse-log-timestamp "5/25 23:08:34.885" options) + :target-name "Flamewaker Healer" + :source-name "Boompow" + :skill "Shield Bash" + :avoidance-method :block})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was blocked by Tester." options) + {:id :skill-block + :logfmt :skill-block + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was blocked by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :block})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was blocked by Tester." options) + {:id :skill-block + :logfmt :skill-block + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was blocked by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :block}))) + +(deftest skill-dodge-self + (is (valid-matcher? (get-matcher regex-matchers :skill-dodge-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Your Test Attack was dodged by Firelord." options) + {:id :skill-dodge-self + :logfmt :skill-dodge + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Your Test Attack was dodged by Firelord." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Firelord" + :source-name owner-char-name + :skill "Test Attack" + :avoidance-method :dodge}))) + +(deftest skill-dodge + (is (valid-matcher? (get-matcher regex-matchers :skill-dodge))) + + (is (= (parse-line "5/25 23:24:42.147 Victore's Mortal Strike was dodged by Ragnaros." options) + {:id :skill-dodge + :logfmt :skill-dodge + :event :skill-avoided-by-target + :line "5/25 23:24:42.147 Victore's Mortal Strike was dodged by Ragnaros." + :timestamp (parse-log-timestamp "5/25 23:24:42.147" options) + :target-name "Ragnaros" + :source-name "Victore" + :skill "Mortal Strike" + :avoidance-method :dodge})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was dodged by Tester." options) + {:id :skill-dodge + :logfmt :skill-dodge + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was dodged by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :dodge})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was dodged by Tester." options) + {:id :skill-dodge + :logfmt :skill-dodge + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was dodged by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :dodge}))) + +(deftest skill-dodge-implied-self + (is (valid-matcher? (get-matcher regex-matchers :skill-dodge-implied-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was dodged." options) + {:id :skill-dodge-implied-self + :logfmt :skill-dodge + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was dodged." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :dodge})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was dodged." options) + {:id :skill-dodge-implied-self + :logfmt :skill-dodge + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was dodged." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :dodge}))) + +(deftest skill-evade-self + (is (valid-matcher? (get-matcher regex-matchers :skill-evade-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Your Test Attack was evaded by Firelord." options) + {:id :skill-evade-self + :logfmt :skill-evade + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Your Test Attack was evaded by Firelord." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Firelord" + :source-name owner-char-name + :skill "Test Attack" + :avoidance-method :evade}))) + +(deftest skill-evade + (is (valid-matcher? (get-matcher regex-matchers :skill-evade))) + + (is (= (parse-line "5/25 23:10:18.706 Futilian's Heroic Strike was evaded by Majordomo Executus." options) + {:id :skill-evade + :logfmt :skill-evade + :event :skill-avoided-by-target + :line "5/25 23:10:18.706 Futilian's Heroic Strike was evaded by Majordomo Executus." + :timestamp (parse-log-timestamp "5/25 23:10:18.706" options) + :target-name "Majordomo Executus" + :source-name "Futilian" + :skill "Heroic Strike" + :avoidance-method :evade})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was evaded by you." options) + {:id :skill-evade + :logfmt :skill-evade + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was evaded by you." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :evade})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was evaded by you." options) + {:id :skill-evade + :logfmt :skill-evade + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was evaded by you." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :evade}))) + +(deftest skill-resist-self + (is (valid-matcher? (get-matcher regex-matchers :skill-resist-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Firelord resists your Test Attack." options) + {:id :skill-resist-self + :logfmt :skill-resist + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Firelord resists your Test Attack." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Firelord" + :source-name owner-char-name + :skill "Test Attack" + :avoidance-method :resist}))) + +(deftest skill-resist + (is (valid-matcher? (get-matcher regex-matchers :skill-resist))) + + (is (= (parse-line "6/9 21:37:10.291 Vamp resists Acal's Sap." options) + {:id :skill-resist + :logfmt :skill-resist + :event :skill-avoided-by-target + :line "6/9 21:37:10.291 Vamp resists Acal's Sap." + :timestamp (parse-log-timestamp "6/9 21:37:10.291" options) + :target-name "Vamp" + :source-name "Acal" + :skill "Sap" + :avoidance-method :resist})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard resists Umi's Mechanical Yeti's Unit Test's Attack." options) + {:id :skill-resist + :logfmt :skill-resist + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard resists Umi's Mechanical Yeti's Unit Test's Attack." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Onyxia's Elite Guard" + :source-name "Umi's Mechanical Yeti" + :skill "Unit Test's Attack" + :avoidance-method :resist})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard resists Umi's Mechanical Yeti's Test Attack." options) + {:id :skill-resist + :logfmt :skill-resist + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard resists Umi's Mechanical Yeti's Test Attack." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Onyxia's Elite Guard" + :source-name "Umi's Mechanical Yeti" + :skill "Test Attack" + :avoidance-method :resist}))) + +(deftest skill-absorb + (is (valid-matcher? (get-matcher regex-matchers :skill-absorb))) + + (is (= (parse-line "5/25 21:15:43.435 Onyxia's Eruption is absorbed by Leaf." options) + {:id :skill-absorb + :logfmt :skill-absorb + :event :skill-avoided-by-target + :line "5/25 21:15:43.435 Onyxia's Eruption is absorbed by Leaf." + :timestamp (parse-log-timestamp "5/25 21:15:43.435" options) + :target-name "Leaf" + :source-name "Onyxia" + :skill "Eruption" + :avoidance-method :absorb})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack is absorbed by Tester." options) + {:id :skill-absorb + :logfmt :skill-absorb + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack is absorbed by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :absorb})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack is absorbed by Tester." options) + {:id :skill-absorb + :logfmt :skill-absorb + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack is absorbed by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :absorb}))) + +(deftest skill-absorb-self + (is (valid-matcher? (get-matcher regex-matchers :skill-absorb-self))) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Your Test Attack is absorbed by Firelord." options) + {:id :skill-absorb-self + :logfmt :skill-absorb + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Your Test Attack is absorbed by Firelord." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Firelord" + :source-name owner-char-name + :skill "Test Attack" + :avoidance-method :absorb}))) + +(deftest skill-absorb-2 + (is (valid-matcher? (get-matcher regex-matchers :skill-absorb-2))) + + (is (= (parse-line "5/25 21:43:08.285 You absorb Flame Imp's Fire Nova." options) + {:id :skill-absorb-2 + :logfmt :skill-absorb-2 + :event :skill-avoided-by-target + :line "5/25 21:43:08.285 You absorb Flame Imp's Fire Nova." + :timestamp (parse-log-timestamp "5/25 21:43:08.285" options) + :target-name owner-char-name + :source-name "Flame Imp" + :skill "Fire Nova" + :avoidance-method :absorb})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Umi's Mechanical Yeti absorbs Onyxia's Elite Guard's Unit Test's Attack." options) + {:id :skill-absorb-2 + :logfmt :skill-absorb-2 + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Umi's Mechanical Yeti absorbs Onyxia's Elite Guard's Unit Test's Attack." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Umi's Mechanical Yeti" + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :absorb})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Umi's Mechanical Yeti absorbs Onyxia's Elite Guard's Test Attack." options) + {:id :skill-absorb-2 + :logfmt :skill-absorb-2 + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Umi's Mechanical Yeti absorbs Onyxia's Elite Guard's Test Attack." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Umi's Mechanical Yeti" + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :absorb})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You absorb Onyxia's Elite Guard's Test Attack." options) + {:id :skill-absorb-2 + :logfmt :skill-absorb-2 + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 You absorb Onyxia's Elite Guard's Test Attack." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :absorb})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You absorb Onyxia's Elite Guard's Unit Test's Attack." options) + {:id :skill-absorb-2 + :logfmt :skill-absorb-2 + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 You absorb Onyxia's Elite Guard's Unit Test's Attack." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :absorb}))) + +(deftest skill-resist-2 + (is (valid-matcher? (get-matcher regex-matchers :skill-resist-2))) + + (is (= (parse-line "5/25 21:16:31.690 Ruktuku's Mind Flay was resisted by Onyxia." options) + {:id :skill-resist-2 + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :line "5/25 21:16:31.690 Ruktuku's Mind Flay was resisted by Onyxia." + :timestamp (parse-log-timestamp "5/25 21:16:31.690" options) + :target-name "Onyxia" + :source-name "Ruktuku" + :skill "Mind Flay" + :avoidance-method :resist})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was resisted by Tester." options) + {:id :skill-resist-2 + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Unit Test's Attack was resisted by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Unit Test's Attack" + :avoidance-method :resist})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was resisted by Tester." options) + {:id :skill-resist-2 + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Attack was resisted by Tester." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Tester" + :source-name "Onyxia's Elite Guard" + :skill "Test Attack" + :avoidance-method :resist}))) + +(deftest skill-resist-2-self + (is (valid-matcher? (get-matcher regex-matchers :skill-resist-2-self))) + + (is (= (parse-line "5/25 21:14:15.457 Your Frostbolt was resisted by Onyxia." options) + {:id :skill-resist-2-self + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :line "5/25 21:14:15.457 Your Frostbolt was resisted by Onyxia." + :timestamp (parse-log-timestamp "5/25 21:14:15.457" options) + :target-name "Onyxia" + :source-name owner-char-name + :skill "Frostbolt" + :avoidance-method :resist}))) + +(deftest skill-resist-no-source + (is (valid-matcher? (get-matcher regex-matchers :skill-resist-implied-self))) + + (is (= (parse-line "5/25 22:40:54.804 Shazzrah's Shazzrah's Curse was resisted." options) + {:id :skill-resist-implied-self + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :line "5/25 22:40:54.804 Shazzrah's Shazzrah's Curse was resisted." + :timestamp (parse-log-timestamp "5/25 22:40:54.804" options) + :target-name owner-char-name + :source-name "Shazzrah" + :skill "Shazzrah's Curse" + :avoidance-method :resist})) + + (is (= (parse-line "5/25 23:08:26.297 Flamewaker Healer's Shadow Shock was resisted." options) + {:id :skill-resist-implied-self + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :line "5/25 23:08:26.297 Flamewaker Healer's Shadow Shock was resisted." + :timestamp (parse-log-timestamp "5/25 23:08:26.297" options) + :target-name owner-char-name + :source-name "Flamewaker Healer" + :skill "Shadow Shock" + :avoidance-method :resist})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Tester's Curse was resisted." options) + {:id :skill-resist-implied-self + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Tester's Curse was resisted." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Tester's Curse" + :avoidance-method :resist})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Curse was resisted." options) + {:id :skill-resist-implied-self + :logfmt :skill-resist-2 + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Curse was resisted." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Onyxia's Elite Guard" + :skill "Test Curse" + :avoidance-method :resist}))) + +(deftest skill-immune-self + (is (valid-matcher? (get-matcher regex-matchers :skill-immune-self))) + + (is (= (parse-line "5/25 22:02:47.619 Your Fire Blast failed. Gehennas is immune." options) + {:id :skill-immune-self + :logfmt :skill-immune + :event :skill-avoided-by-target + :line "5/25 22:02:47.619 Your Fire Blast failed. Gehennas is immune." + :timestamp (parse-log-timestamp "5/25 22:02:47.619" options) + :target-name "Gehennas" + :source-name owner-char-name + :skill "Fire Blast" + :avoidance-method :immune}))) + +(deftest skill-immune + (is (valid-matcher? (get-matcher regex-matchers :skill-immune))) + + (is (= (parse-line "5/25 21:16:35.991 Onyxia's Bellowing Roar fails. Slater is immune." options) + {:id :skill-immune + :logfmt :skill-immune + :event :skill-avoided-by-target + :line "5/25 21:16:35.991 Onyxia's Bellowing Roar fails. Slater is immune." + :timestamp (parse-log-timestamp "5/25 21:16:35.991" options) + :target-name "Slater" + :source-name "Onyxia" + :skill "Bellowing Roar" + :avoidance-method :immune})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Tester's Curse fails. Slater is immune." options) + {:id :skill-immune + :logfmt :skill-immune + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Tester's Curse fails. Slater is immune." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Slater" + :source-name "Onyxia's Elite Guard" + :skill "Tester's Curse" + :avoidance-method :immune})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Curse fails. Slater is immune." options) + {:id :skill-immune + :logfmt :skill-immune + :event :skill-avoided-by-target + :line "1/2 3:45:00.123 Onyxia's Elite Guard's Test Curse fails. Slater is immune." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Slater" + :source-name "Onyxia's Elite Guard" + :skill "Test Curse" + :avoidance-method :immune}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/skill_damage_to_target_test.clj b/vwowrla.core/test/vwowrla/core/matchers/skill_damage_to_target_test.clj new file mode 100644 index 0000000..3c5d9c0 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/skill_damage_to_target_test.clj @@ -0,0 +1,235 @@ +(ns vwowrla.core.matchers.skill-damage-to-target-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest skill-damages-target-elemental-self + (is (valid-matcher? (get-matcher regex-matchers :skill-damages-target-elemental-self))) + + (is (= (parse-line "5/25 21:42:29.230 Your Frostbolt hits Lava Surger for 881 Frost damage." options) + {:id :skill-damages-target-elemental-self + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 21:42:29.230 Your Frostbolt hits Lava Surger for 881 Frost damage." + :timestamp (parse-log-timestamp "5/25 21:42:29.230" options) + :source-name owner-char-name + :target-name "Lava Surger" + :skill "Frostbolt" + :damage 881 + :damage-type :frost + :crit? false + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 21:45:50.850 Your Frostbolt crits Flamewaker Protector for 1784 Frost damage." options) + {:id :skill-damages-target-elemental-self + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 21:45:50.850 Your Frostbolt crits Flamewaker Protector for 1784 Frost damage." + :timestamp (parse-log-timestamp "5/25 21:45:50.850" options) + :source-name owner-char-name + :target-name "Flamewaker Protector" + :skill "Frostbolt" + :damage 1784 + :damage-type :frost + :crit? true + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 21:46:03.864 Your Fire Blast hits Flamewaker Protector for 503 Fire damage. (167 resisted)" options) + {:id :skill-damages-target-elemental-self + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 21:46:03.864 Your Fire Blast hits Flamewaker Protector for 503 Fire damage. (167 resisted)" + :timestamp (parse-log-timestamp "5/25 21:46:03.864" options) + :source-name owner-char-name + :target-name "Flamewaker Protector" + :skill "Fire Blast" + :damage 503 + :damage-type :fire + :crit? false + :absorbed nil + :resisted 167}))) + +(deftest skill-damages-target-self + (is (valid-matcher? (get-matcher regex-matchers :skill-damages-target-self))) + ; TODO + ) + +(deftest skill-damages-target-short-self + (is (valid-matcher? (get-matcher regex-matchers :skill-damages-target-short-self))) + ; TODO + ) + +(deftest skill-damages-target-elemental + (is (valid-matcher? (get-matcher regex-matchers :skill-damages-target-elemental))) + + (is (= (parse-line "5/25 21:42:31.082 Tomaka's Shadow Bolt hits Lava Surger for 1119 Shadow damage." options) + {:id :skill-damages-target-elemental + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 21:42:31.082 Tomaka's Shadow Bolt hits Lava Surger for 1119 Shadow damage." + :timestamp (parse-log-timestamp "5/25 21:42:31.082" options) + :source-name "Tomaka" + :target-name "Lava Surger" + :skill "Shadow Bolt" + :damage 1119 + :damage-type :shadow + :crit? false + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 21:42:33.583 Fervens's Frostbolt crits Lava Surger for 1825 Frost damage." options) + {:id :skill-damages-target-elemental + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 21:42:33.583 Fervens's Frostbolt crits Lava Surger for 1825 Frost damage." + :timestamp (parse-log-timestamp "5/25 21:42:33.583" options) + :source-name "Fervens" + :target-name "Lava Surger" + :skill "Frostbolt" + :damage 1825 + :damage-type :frost + :crit? true + :absorbed nil + :resisted nil})) + + (is (= (parse-line "5/25 21:42:42.644 Magnomage's Fire Blast hits Lava Surger for 171 Fire damage. (513 resisted)" options) + {:id :skill-damages-target-elemental + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 21:42:42.644 Magnomage's Fire Blast hits Lava Surger for 171 Fire damage. (513 resisted)" + :timestamp (parse-log-timestamp "5/25 21:42:42.644" options) + :source-name "Magnomage" + :target-name "Lava Surger" + :skill "Fire Blast" + :damage 171 + :damage-type :fire + :crit? false + :absorbed nil + :resisted 513})) + + (is (= (parse-line "5/25 21:51:35.316 Magmadar's Magma Spit hits Eggs for 3 Fire damage. (91 absorbed)" options) + {:id :skill-damages-target-elemental + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 21:51:35.316 Magmadar's Magma Spit hits Eggs for 3 Fire damage. (91 absorbed)" + :timestamp (parse-log-timestamp "5/25 21:51:35.316" options) + :source-name "Magmadar" + :target-name "Eggs" + :skill "Magma Spit" + :damage 3 + :damage-type :fire + :crit? false + :absorbed 91 + :resisted nil})) + + (is (= (parse-line "5/25 21:56:49.465 Flame Imp's Fire Nova hits you for 89 Fire damage. (409 resisted) (320 absorbed)" options) + {:id :skill-damages-target-elemental + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 21:56:49.465 Flame Imp's Fire Nova hits you for 89 Fire damage. (409 resisted) (320 absorbed)" + :timestamp (parse-log-timestamp "5/25 21:56:49.465" options) + :source-name "Flame Imp" + :target-name owner-char-name + :skill "Fire Nova" + :damage 89 + :damage-type :fire + :crit? false + :absorbed 320 + :resisted 409})) + + (is (= (parse-line "5/25 22:21:33.172 Firewalker's Fire Blossom hits Ruktuku for 1430 Fire damage. (798 resisted) (964 absorbed)" options) + {:id :skill-damages-target-elemental + :logfmt :skill-damages-target-elemental + :event :skill-damage-to-target + :line "5/25 22:21:33.172 Firewalker's Fire Blossom hits Ruktuku for 1430 Fire damage. (798 resisted) (964 absorbed)" + :timestamp (parse-log-timestamp "5/25 22:21:33.172" options) + :source-name "Firewalker" + :target-name "Ruktuku" + :skill "Fire Blossom" + :damage 1430 + :damage-type :fire + :crit? false + :absorbed 964 + :resisted 798}))) + +; TODO: might not need this one... see comments for the associated matcher +(deftest skill-damages-target + (is (valid-matcher? (get-matcher regex-matchers :skill-damages-target))) + ; TODO + ) + +(deftest skill-damages-target-short + (is (valid-matcher? (get-matcher regex-matchers :skill-damages-target-short))) + + (is (= (parse-line "5/25 21:42:27.247 Peasemold's Ambush crits Lava Surger for 1398." options) + {:id :skill-damages-target-short + :logfmt :skill-damages-target-short + :event :skill-damage-to-target + :line "5/25 21:42:27.247 Peasemold's Ambush crits Lava Surger for 1398." + :timestamp (parse-log-timestamp "5/25 21:42:27.247" options) + :source-name "Peasemold" + :target-name "Lava Surger" + :skill "Ambush" + :damage 1398 + :crit? true + :absorbed nil + :blocked nil})) + + (is (= (parse-line "5/25 21:42:28.379 Eggs's Shield Slam hits Lava Surger for 240." options) + {:id :skill-damages-target-short + :logfmt :skill-damages-target-short + :event :skill-damage-to-target + :line "5/25 21:42:28.379 Eggs's Shield Slam hits Lava Surger for 240." + :timestamp (parse-log-timestamp "5/25 21:42:28.379" options) + :source-name "Eggs" + :target-name "Lava Surger" + :skill "Shield Slam" + :damage 240 + :crit? false + :absorbed nil + :blocked nil})) + + (is (= (parse-line "5/25 21:42:32.488 Victore's Whirlwind crits Lava Surger for 902. (31 blocked)" options) + {:id :skill-damages-target-short + :logfmt :skill-damages-target-short + :event :skill-damage-to-target + :line "5/25 21:42:32.488 Victore's Whirlwind crits Lava Surger for 902. (31 blocked)" + :timestamp (parse-log-timestamp "5/25 21:42:32.488" options) + :source-name "Victore" + :target-name "Lava Surger" + :skill "Whirlwind" + :damage 902 + :crit? true + :absorbed nil + :blocked 31})) + + (is (= (parse-line "6/9 22:31:18.633 Golemagg the Incinerator's Earthquake hits Architrex for 1034. (460 absorbed)" options) + {:id :skill-damages-target-short + :logfmt :skill-damages-target-short + :event :skill-damage-to-target + :line "6/9 22:31:18.633 Golemagg the Incinerator's Earthquake hits Architrex for 1034. (460 absorbed)" + :timestamp (parse-log-timestamp "6/9 22:31:18.633" options) + :source-name "Golemagg the Incinerator" + :target-name "Architrex" + :skill "Earthquake" + :damage 1034 + :crit? false + :absorbed 460 + :blocked nil}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/skill_heals_target_test.clj b/vwowrla.core/test/vwowrla/core/matchers/skill_heals_target_test.clj new file mode 100644 index 0000000..52c91ac --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/skill_heals_target_test.clj @@ -0,0 +1,100 @@ +(ns vwowrla.core.matchers.skill-heals-target-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest skill-heals-target-self + (is (valid-matcher? (get-matcher regex-matchers :skill-heals-target-self))) + + (is (= (parse-line "6/9 22:16:35.330 Your Healing Potion heals you for 1627." options) + {:id :skill-heals-target-self + :logfmt :skill-heals-target + :event :skill-heals-target + :line "6/9 22:16:35.330 Your Healing Potion heals you for 1627." + :timestamp (parse-log-timestamp "6/9 22:16:35.330" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Healing Potion" + :amount 1627 + :crit? false})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Your Healing Touch critically heals you for 3000." options) + {:id :skill-heals-target-self + :logfmt :skill-heals-target + :event :skill-heals-target + :line "1/2 3:45:00.123 Your Healing Touch critically heals you for 3000." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name owner-char-name + :skill "Healing Touch" + :amount 3000 + :crit? true}))) + +(deftest skill-heals-target + (is (valid-matcher? (get-matcher regex-matchers :skill-heals-target))) + + (is (= (parse-line "5/25 21:16:40.096 Fei's Lesser Healing Wave heals Futilian for 1012." options) + {:id :skill-heals-target + :logfmt :skill-heals-target + :event :skill-heals-target + :line "5/25 21:16:40.096 Fei's Lesser Healing Wave heals Futilian for 1012." + :timestamp (parse-log-timestamp "5/25 21:16:40.096" options) + :target-name "Futilian" + :source-name "Fei" + :skill "Lesser Healing Wave" + :amount 1012 + :crit? false})) + + (is (= (parse-line "5/25 21:16:40.686 Leaf's Regrowth critically heals Futilian for 1885." options) + {:id :skill-heals-target + :logfmt :skill-heals-target + :event :skill-heals-target + :line "5/25 21:16:40.686 Leaf's Regrowth critically heals Futilian for 1885." + :timestamp (parse-log-timestamp "5/25 21:16:40.686" options) + :target-name "Futilian" + :source-name "Leaf" + :skill "Regrowth" + :amount 1885 + :crit? true})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Ribbly's Crony's Unit Tester's Heal heals you for 2000." options) + {:id :skill-heals-target + :logfmt :skill-heals-target + :event :skill-heals-target + :line "1/2 3:45:00.123 Ribbly's Crony's Unit Tester's Heal heals you for 2000." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source-name "Ribbly's Crony" + :skill "Unit Tester's Heal" + :amount 2000 + :crit? false})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Ribbly's Crony's Test Heal critically heals Futilian for 3000." options) + {:id :skill-heals-target + :logfmt :skill-heals-target + :event :skill-heals-target + :line "1/2 3:45:00.123 Ribbly's Crony's Test Heal critically heals Futilian for 3000." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Futilian" + :source-name "Ribbly's Crony" + :skill "Test Heal" + :amount 3000 + :crit? true}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/skill_interrupted_by_target_test.clj b/vwowrla.core/test/vwowrla/core/matchers/skill_interrupted_by_target_test.clj new file mode 100644 index 0000000..0480f29 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/skill_interrupted_by_target_test.clj @@ -0,0 +1,89 @@ +(ns vwowrla.core.matchers.skill-interrupted-by-target-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest skill-interrupt + (is (valid-matcher? (get-matcher regex-matchers :skill-interrupt))) + + (is (= (parse-line "5/25 22:42:15.079 Shazzrah interrupts Oprawindfury's Lesser Healing Wave." options) + {:id :skill-interrupt + :logfmt :skill-interrupt + :event :skill-interrupted-by-target + :line "5/25 22:42:15.079 Shazzrah interrupts Oprawindfury's Lesser Healing Wave." + :timestamp (parse-log-timestamp "5/25 22:42:15.079" options) + :target-name "Oprawindfury" + :source-name "Shazzrah" + :skill "Lesser Healing Wave"})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Warug's Target Dummy interrupts Jen'shan's Unit Tester's Heal." options) + {:id :skill-interrupt + :logfmt :skill-interrupt + :event :skill-interrupted-by-target + :line "1/2 3:45:00.123 Warug's Target Dummy interrupts Jen'shan's Unit Tester's Heal." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Jen'shan" + :source-name "Warug's Target Dummy" + :skill "Unit Tester's Heal"})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 Warug's Target Dummy interrupts Jen'shan's Test Heal." options) + {:id :skill-interrupt + :logfmt :skill-interrupt + :event :skill-interrupted-by-target + :line "1/2 3:45:00.123 Warug's Target Dummy interrupts Jen'shan's Test Heal." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Jen'shan" + :source-name "Warug's Target Dummy" + :skill "Test Heal"})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You interrupt Jen'shan's Unit Tester's Heal." options) + {:id :skill-interrupt + :logfmt :skill-interrupt + :event :skill-interrupted-by-target + :line "1/2 3:45:00.123 You interrupt Jen'shan's Unit Tester's Heal." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Jen'shan" + :source-name owner-char-name + :skill "Unit Tester's Heal"})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You interrupt Jen'shan's Test Heal." options) + {:id :skill-interrupt + :logfmt :skill-interrupt + :event :skill-interrupted-by-target + :line "1/2 3:45:00.123 You interrupt Jen'shan's Test Heal." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Jen'shan" + :source-name owner-char-name + :skill "Test Heal"}))) + +(deftest skill-interrupt-self + (is (valid-matcher? (get-matcher regex-matchers :skill-interrupt-self))) + + (is (= (parse-line "6/9 22:16:52.007 Shazzrah interrupts your Frostbolt." options) + {:id :skill-interrupt-self + :logfmt :skill-interrupt + :event :skill-interrupted-by-target + :line "6/9 22:16:52.007 Shazzrah interrupts your Frostbolt." + :timestamp (parse-log-timestamp "6/9 22:16:52.007" options) + :target-name owner-char-name + :source-name "Shazzrah" + :skill "Frostbolt"}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/skill_performed_on_target_test.clj b/vwowrla.core/test/vwowrla/core/matchers/skill_performed_on_target_test.clj new file mode 100644 index 0000000..b31d542 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/skill_performed_on_target_test.clj @@ -0,0 +1,79 @@ +(ns vwowrla.core.matchers.skill-performed-on-target-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest skill-performed-on-target + (is (valid-matcher? (get-matcher regex-matchers :skill-performed-on-target))) + + (is (= (parse-line "6/16 21:48:26.263 Acal performs Feint on Lava Surger." options) + {:id :skill-performed-on-target + :logfmt :skill-performed-on-target + :event :skill-performed-on-target + :line "6/16 21:48:26.263 Acal performs Feint on Lava Surger." + :timestamp (parse-log-timestamp "6/16 21:48:26.263" options) + :target-name "Lava Surger" + :source-name "Acal" + :skill "Feint" + :spell? false})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You perform Feint on Onyxia." options) + {:id :skill-performed-on-target + :logfmt :skill-performed-on-target + :event :skill-performed-on-target + :line "1/2 3:45:00.123 You perform Feint on Onyxia." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name "Onyxia" + :source-name owner-char-name + :skill "Feint" + :spell? false})) + + (is (= (parse-line "5/25 22:27:13.538 Crayson casts Ancestral Spirit on Acal." options) + {:id :skill-performed-on-target + :logfmt :skill-performed-on-target + :event :skill-performed-on-target + :line "5/25 22:27:13.538 Crayson casts Ancestral Spirit on Acal." + :timestamp (parse-log-timestamp "5/25 22:27:13.538" options) + :skill "Ancestral Spirit" + :target-name "Acal" + :source-name "Crayson" + :spell? true})) + + (is (= (parse-line "5/25 23:23:44.343 Acal casts Melt Weapon on Acal: Gutgore Ripper damaged." options) + {:id :skill-performed-on-target + :logfmt :skill-performed-on-target + :event :skill-performed-on-target + :line "5/25 23:23:44.343 Acal casts Melt Weapon on Acal: Gutgore Ripper damaged." + :timestamp (parse-log-timestamp "5/25 23:23:44.343" options) + :target-name "Acal" + :source-name "Acal" + :skill "Melt Weapon" + :spell? true + :extra "Gutgore Ripper damaged"})) + + (is (= (parse-line "5/25 23:07:22.282 Aesthetera casts Polymorph: Pig on Flamewaker Healer." options) + {:id :skill-performed-on-target + :logfmt :skill-performed-on-target + :event :skill-performed-on-target + :line "5/25 23:07:22.282 Aesthetera casts Polymorph: Pig on Flamewaker Healer." + :timestamp (parse-log-timestamp "5/25 23:07:22.282" options) + :target-name "Flamewaker Healer" + :source-name "Aesthetera" + :skill "Polymorph: Pig" + :spell? true}))) diff --git a/vwowrla.core/test/vwowrla/core/matchers/special_gained_test.clj b/vwowrla.core/test/vwowrla/core/matchers/special_gained_test.clj new file mode 100644 index 0000000..4273442 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/matchers/special_gained_test.clj @@ -0,0 +1,53 @@ +(ns vwowrla.core.matchers.special-gained-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.matchers.matchers-test-utils) + (:require + [vwowrla.core.parser :refer [parse-line]] + [vwowrla.core.preparsing :refer [parse-log-timestamp]] + [vwowrla.core.matchers :refer [regex-matchers]])) + +(def options {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false}) + +(def owner-char-name (:log-owner-char-name options)) +(def year (:year options)) +(def timezone (:timezone options)) + +(deftest special-gained + (is (valid-matcher? (get-matcher regex-matchers :special-gained))) + + (is (= (parse-line "5/25 21:13:34.098 Futilian gains 2 extra attacks through Fury of Forgewright." options) + {:id :special-gained + :logfmt :special-gained + :event :special-gained + :line "5/25 21:13:34.098 Futilian gains 2 extra attacks through Fury of Forgewright." + :timestamp (parse-log-timestamp "5/25 21:13:34.098" options) + :target-name "Futilian" + :source "Fury of Forgewright" + :special "2 extra attacks"})) + + (is (= (parse-line "5/25 21:16:16.199 Bigjuicy gains 1 extra attack through Hand of Justice." options) + {:id :special-gained + :logfmt :special-gained + :event :special-gained + :line "5/25 21:16:16.199 Bigjuicy gains 1 extra attack through Hand of Justice." + :timestamp (parse-log-timestamp "5/25 21:16:16.199" options) + :target-name "Bigjuicy" + :source "Hand of Justice" + :special "1 extra attack"})) + + ; NOTE: this combat log entry was not generated by the WoW client, it was hand-written for this test + (is (= (parse-line "1/2 3:45:00.123 You gains 1 extra attack through Sword Specialization." options) + {:id :special-gained + :logfmt :special-gained + :event :special-gained + :line "1/2 3:45:00.123 You gains 1 extra attack through Sword Specialization." + :timestamp (parse-log-timestamp "1/2 3:45:00.123" options) + :target-name owner-char-name + :source "Sword Specialization" + :special "1 extra attack"}))) diff --git a/vwowrla.core/test/vwowrla/core/parser_test.clj b/vwowrla.core/test/vwowrla/core/parser_test.clj new file mode 100644 index 0000000..d3ca894 --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/parser_test.clj @@ -0,0 +1,16 @@ +(ns vwowrla.core.parser-test + (:import + (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.parser)) + +(deftest barebones-line-parse-results + (let [parsed (parse-line "1/2 3:45:00.000 Test combat log line with 2 spaces in content part." + {:log-owner-char-name "Blasticus" + :year 2015 + :timezone (TimeZone/getDefault) + :windows? false})] + (is (and (map? parsed) + (contains? parsed :timestamp) + (contains? parsed :line))))) diff --git a/vwowrla.core/test/vwowrla/core/preparsing_test.clj b/vwowrla.core/test/vwowrla/core/preparsing_test.clj new file mode 100644 index 0000000..e64fbdb --- /dev/null +++ b/vwowrla.core/test/vwowrla/core/preparsing_test.clj @@ -0,0 +1,55 @@ +(ns vwowrla.core.preparsing-test + (:import (java.util TimeZone)) + (:use + clojure.test + vwowrla.core.preparsing)) + +(deftest entity-name-sanitizing + (is (= (sanitize-entity-name "Twilight's Hammer Ambassador") + "Twilights Hammer Ambassador")) + (is (= (sanitize-entity-name "Ragnaros") + "Ragnaros")) + (is (= (sanitize-entity-name "C'Thun") + "C'Thun"))) + +(deftest entity-name-unsanitizing + (is (= (get-original-entity-name "Twilights Hammer Ambassador") + "Twilight's Hammer Ambassador")) + (is (= (get-original-entity-name "Ragnaros") + "Ragnaros")) + (is (= (get-original-entity-name "C'Thun") + "C'Thun"))) + +(deftest combat-log-line-entity-name-sanitizing + (is (= (sanitize-entity-names "Twilight's Hammer Ambassador's Flame Shock hits you for 1234 Fire damage.") + "Twilights Hammer Ambassador's Flame Shock hits you for 1234 Fire damage.")) + (is (= (sanitize-entity-names "Magnomage's Shazzrah's Curse is removed.") + "Magnomage's Shazzrah's Curse is removed."))) + +(deftest combat-log-line-undo-swstats-fixlogstring + (is (= (undo-swstats-fixlogstring "Twilight's Hammer Ambassador 's Flame Shock hits you for 1234 Fire damage.") + "Twilight's Hammer Ambassador's Flame Shock hits you for 1234 Fire damage.")) + (is (= (undo-swstats-fixlogstring "Magnomage 's Shazzrah's Curse is removed.") + "Magnomage's Shazzrah's Curse is removed."))) + +(deftest date-parsing + (let [options {:year 2015 + :timezone (TimeZone/getTimeZone "America/Toronto") + :windows? false}] + (is (= (parse-log-timestamp "6/9 21:36:18.227" options) + #inst "2015-07-10T01:36:18.227-00:00")) + (is (= (parse-log-timestamp "11/31 13:37:42.123" options) + #inst "2015-12-31T18:37:42.123-00:00")) + (is (= (parse-log-timestamp "0/1 00:00:00.000" options) + #inst "2015-01-01T05:00:00.000-00:00"))) + (let [options {:year 2015 + :timezone (TimeZone/getTimeZone "America/Toronto") + :windows? true}] + (is (= (parse-log-timestamp "7/24 10:25:50.444" options) + #inst "2015-07-24T14:25:50.444-00:00")))) + +(deftest raw-log-line-splitting + (is (= (split-log-timestamp-and-content "6/9 22:50:49.199 Ragnaros dies.") + ["6/9 22:50:49.199" "Ragnaros dies."])) + (is (= (split-log-timestamp-and-content "1/2 3:45:00.000 Test combat log line with 2 spaces in content part.") + ["1/2 3:45:00.000" "Test combat log line with 2 spaces in content part."])))