add lua addon from old sources

This commit is contained in:
Gered 2016-03-06 17:42:18 -05:00
parent 9068d39139
commit b18176b85e
7 changed files with 1286 additions and 0 deletions

View file

@ -0,0 +1,7 @@
## Interface:11200
## Title: Vanilla World of Warcraft Raid Log Analysis Helper Addon
## Notes: Provides support for taking buff/debuff snapshots of the entire raid at the beginning of raid encounters, to supplement the data analysis done on raid combat logs.
## OptionalDeps:
## Dependencies:
## SavedVariables: vwowrla_raid_snapshots
VWoWRLAHelper.xml

View file

@ -0,0 +1,20 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ C:\Program%20Files\World%20of%20Warcraft\UI.xsd">
<Script file="data_lists.lua"/>
<Script file="utils.lua"/>
<Script file="matchers.lua"/>
<Script file="event_handler.lua"/>
<Script file="core.lua"/>
<GameTooltip name="VWoWRLA_tooltip" frameStrata="TOOLTIP" hidden="true" parent="UIParent" inherits="GameTooltipTemplate"/>
<Frame name="VWoWRLA_events">
<Scripts>
<OnLoad>
VWoWRLA_on_load()
</OnLoad>
<OnEvent>
VWoWRLA_on_event()
</OnEvent>
</Scripts>
</Frame>
</Ui>

122
VWoWRLAHelper/core.lua Normal file
View file

@ -0,0 +1,122 @@
vwowrla = {}
vwowrla_raid_snapshots = {} -- saved data
function get_buff_name(target, idx)
VWoWRLA_tooltip:ClearLines()
VWoWRLA_tooltip:SetOwner(UIParent,"ANCHOR_NONE")
VWoWRLA_tooltip:SetUnitBuff(target, idx)
return VWoWRLA_tooltipTextLeft1:GetText()
end
function get_debuff_name(target, idx)
VWoWRLA_tooltip:ClearLines()
VWoWRLA_tooltip:SetOwner(UIParent,"ANCHOR_NONE")
VWoWRLA_tooltip:SetUnitDebuff(target, idx)
return VWoWRLA_tooltipTextLeft1:GetText()
end
function get_all_unit_buffs(target)
local buffs = {}
local i = 1
local buff = UnitBuff(target, i)
while buff do
table.insert(buffs, get_buff_name(target, i))
i = i + 1
buff = UnitBuff(target, i)
end
return buffs;
end
function get_all_unit_debuffs(target)
local debuffs = {}
local i = 1
local debuff = UnitDebuff(target, i)
while debuff do
table.insert(debuffs, get_debuff_name(target, i))
i = i + 1
debuff = UnitDebuff(target, i)
end
return debuffs;
end
function get_party_member_info(unit)
local buffs = get_all_unit_buffs(unit)
local debuffs = get_all_unit_debuffs(unit)
local localizedClass, englishClass = UnitClass(unit);
local level = UnitLevel(unit);
local online = UnitIsConnected(unit);
return {buffs = buffs, debuffs = debuffs, class = englishClass, level = level, online = online}
end
function get_all_raid_member_status()
local status = {}
local unit, name, buffs, debuffs, localizedClass, englishClass, level
for i = 1, GetNumRaidMembers() do
unit = "raid" .. i
name = UnitName(unit)
if name then
status[name] = get_party_member_info(unit)
end
end
for i = 1, GetNumPartyMembers() do
unit = "party" .. i
name = UnitName(unit)
if name then
status[name] = get_party_member_info(unit)
end
unit = "player"
name = UnitName(unit)
status[name] = get_party_member_info(unit)
end
return status
end
function take_raid_buff_snapshot(encounter_name)
print("Taking snapshot of all raid member buffs/debuffs.")
local snapshot = get_all_raid_member_status()
if not vwowrla_raid_snapshots then
vwowrla_raid_snapshots = {}
end
table.insert(vwowrla_raid_snapshots, {
date = date(),
encounter = encounter_name,
snapshot = snapshot
})
end
function VWoWRLA_on_load()
this:RegisterEvent("VARIABLES_LOADED")
this:RegisterEvent("PLAYER_ENTERING_WORLD")
this:RegisterEvent("ZONE_CHANGED_NEW_AREA")
print("Combat Log Parser Helper -- Loaded")
if not vwowrla_raid_snapshots then
print("vwowrla_raid_snapshots is nil")
vwowrla_raid_snapshots = {}
else
print("vwowrla_raid_snapshots is not nil")
end
if not vwowrla.Loaded then
print("Combat Log Parser Helper -- Registering for events.")
VWoWRLA_register_events()
vwowrla.Loaded = true
end
end
function VWoWRLA_on_event()
if not arg1 then return end
--print("event:", event, arg1, arg2, arg3, arg4, arg5, arg6)
vwowrla_process_combat_event(arg1)
end
function VWoWRLA_register_events()
for i, name in ipairs(vwowrla_events_to_register) do
VWoWRLA_events:RegisterEvent(name)
end
end

View file

@ -0,0 +1,230 @@
defined_encounters = {
["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},
["Flamewaker Healer"] = {count = 4},
["Flamewaker Elite"] = {count = 4}
}
},
["Ragnaros"] = {
entities = {
["Ragnaros"] = {count = 1}
},
trigger_on_attack = true
},
["Onyxia"] = {
entities = {
["Onyxia"] = {count = 1}
}
}
}
non_combat_starting_auras = {
"Hunter's Mark",
"Detect Magic",
"Mind Soothe",
"Distract"
}
vwowrla_events_to_register = {
"CHAT_MSG_SPELL_AURA_GONE_OTHER",
"CHAT_MSG_SPELL_AURA_GONE_PARTY",
"CHAT_MSG_SPELL_AURA_GONE_SELF",
"CHAT_MSG_SPELL_BREAK_AURA",
"CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF",
"CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE",
"CHAT_MSG_SPELL_CREATURE_VS_PARTY_BUFF",
"CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE",
"CHAT_MSG_SPELL_CREATURE_VS_SELF_BUFF",
"CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE",
"CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS",
"CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF",
"CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF",
"CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE",
"CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF",
"CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE",
"CHAT_MSG_SPELL_ITEM_ENCHANTMENTS",
"CHAT_MSG_SPELL_PARTY_BUFF",
"CHAT_MSG_SPELL_PARTY_DAMAGE",
"CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS",
"CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE",
"CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS",
"CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE",
"CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS",
"CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE",
"CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS",
"CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE",
"CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS",
"CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE",
"CHAT_MSG_SPELL_PET_BUFF",
"CHAT_MSG_SPELL_PET_DAMAGE",
"CHAT_MSG_SPELL_SELF_BUFF",
"CHAT_MSG_SPELL_SELF_DAMAGE",
"CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_HITS",
"CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_MISSES",
"CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS",
"CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES",
"CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS",
"CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES",
"CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS",
"CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES",
"CHAT_MSG_COMBAT_FRIENDLY_DEATH",
"CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS",
"CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES",
"CHAT_MSG_COMBAT_HOSTILE_DEATH",
"CHAT_MSG_COMBAT_MISC_INFO",
"CHAT_MSG_COMBAT_PARTY_HITS",
"CHAT_MSG_COMBAT_PARTY_MISSES",
"CHAT_MSG_COMBAT_PET_HITS",
"CHAT_MSG_COMBAT_PET_MISSES",
"CHAT_MSG_COMBAT_SELF_HITS",
"CHAT_MSG_COMBAT_SELF_MISSES"
};
problem_entity_names = {
"\"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"
}
problem_entity_name_to_fixed_name = {
problem_to_fixed = {},
fixed_to_problem = {}
}
for i, problem_name in ipairs(problem_entity_names) do
local fixed_name = string.gsub(problem_name, "'s", "s")
problem_entity_name_to_fixed_name.problem_to_fixed[problem_name] = fixed_name
problem_entity_name_to_fixed_name.fixed_to_problem[fixed_name] = problem_name
end

View file

@ -0,0 +1,260 @@
wipe_or_timeout_period = 60
active_encounter = {}
previous_successful_encounters = {}
function undo_swstats_fixlogstring(combat_log_line)
return string.gsub(combat_log_line, " 's", "'s")
end
function sanitize_entity_name(entity_name)
local fixed_name = problem_entity_name_to_fixed_name.problem_to_fixed[entity_name]
if fixed_name then
return fixed_name
else
return entity_name
end
end
function get_original_entity_name(fixed_entity_name)
local original_name = problem_entity_name_to_fixed_name.fixed_to_problem[fixed_entity_name]
if original_name then
return original_name
else
return fixed_entity_name
end
end
function sanitize_entity_names(combat_log_line)
local fixed_line = combat_log_line
for problem_name, fixed_name in pairs(problem_entity_name_to_fixed_name.problem_to_fixed) do
fixed_line = string.gsub(fixed_line, problem_name, fixed_name)
end
return fixed_line
end
function parse_combat_log_line(combat_log_line)
local found_matcher, match
for i, matcher in ipairs(vwowrla_combat_log_patterns) do
for j, pattern in ipairs(matcher.pattern) do
local _, _, m1, m2, m3, m4, m5, m6 = string.find(combat_log_line, pattern)
if m1 then
-- found a matching line. dont need to test anymore patterns.
-- if fn does not exist, don't worry, just return nil (it's probably an ignored pattern)
if matcher.fn then
local result = matcher.fn({m1, m2, m3, m4, m5, m6})
result.event = matcher.event
result.line = combat_log_line
result.timestamp = time()
return result
else
return nil
end
end
end
end
-- no match found
print("*** UNRECOGNIZED LINE***")
print(" >", event, combat_log_line)
return nil
end
function process_parsed_combat_log_line(parsed_line)
if parsed_line.source_name then
parsed_line.source_name = get_original_entity_name(parsed_line.source_name)
end
if parsed_line.source then
parsed_line.source = get_original_entity_name(parsed_line.source)
end
if parsed_line.target_name then
parsed_line.target_name = get_original_entity_name(parsed_line.target_name)
end
end
function in_active_encounter()
return not (active_encounter.name == nil)
end
function touch_entity(entity_name, timestamp)
if not active_encounter.entities[entity_name] then
active_encounter.entities[entity_name] = {
last_activity_at = timestamp,
deaths = {},
resurrections = {}
}
else
active_encounter.entities[entity_name].last_activity_at = timestamp
end
end
function kill_entity(entity_name, timestamp)
active_encounter.entities[entity_name].last_activity_at = timestamp
table.insert(active_encounter.entities[entity_name].deaths, timestamp)
end
function handle_line(parsed_line)
local ev = parsed_line.event
if ev == "ignored" or ev == "other-damage" then
-- nop
elseif ev == "death" then
kill_entity(parsed_line.source_name, parsed_line.timestamp)
else
if parsed_line.source_name then touch_entity(parsed_line.source_name, parsed_line.timestamp) end
if parsed_line.target_name then touch_entity(parsed_line.target_name, parsed_line.timestamp) end
end
end
function count_dead(entity_name)
local entity = active_encounter.entities[entity_name]
if entity then
local num_deaths = table.getn(entity.deaths)
local num_resurrects = table.getn(entity.resurrections)
return num_deaths - num_resurrects
else
return 0
end
end
function find_defined_encounter(entity_name)
for encounter_name, encounter in pairs(defined_encounters) do
for encounter_entity, entity_props in pairs(encounter.entities) do
if entity_name == encounter_entity then return encounter_name end
end
end
return nil
end
function determine_encounter_from_combat_event(parsed_line)
local encounter_name
encounter_name = find_defined_encounter(parsed_line.source_name)
if not encounter_name then encounter_name = find_defined_encounter(parsed_line.target_name) end
return encounter_name
end
function has_previous_successful_encounter(encounter_name)
if not encounter_name then return false end
for i, name in ipairs(previous_successful_encounters) do
if name == encounter_name then return true end
end
return false
end
function aura_is_non_combat_starting(aura_name)
if not aura_name then return false end
for i, name in ipairs(non_combat_starting_auras) do
if name == aura_name then return true end
end
return false
end
function detect_encounter_triggered(parsed_line)
local encounter_name = determine_encounter_from_combat_event(parsed_line)
if not encounter_name then return nil end
if (not has_previous_successful_encounter(encounter_name)) and (not aura_is_non_combat_starting(parsed_line.aura_name)) then
local encounter = defined_encounters[encounter_name]
if encounter.trigger_on_attack then
if parsed_line.attack then return encounter_name end
else
return encounter_name
end
end
return nil
end
function are_all_encounter_mobs_dead()
local encounter = defined_encounters[active_encounter.name]
for entity_name, props in pairs(encounter.entities) do
local num_dead = count_dead(entity_name)
if props.must_kill_count then
if num_dead < props.must_kill_count then return false end
else
if num_dead < props.count then return false end
end
end
return true
end
function has_active_encounter_wiped_or_timed_out(parsed_line)
local timestamp = parsed_line.timestamp
local encounter = defined_encounters[active_encounter.name]
for entity_name, props in pairs(encounter.entities) do
local entity = active_encounter.entities[entity_name]
if entity then
if (timestamp - entity.last_activity_at) < wipe_or_timeout_period then return false end
end
end
return true
end
function detect_encounter_end(parsed_line)
if are_all_encounter_mobs_dead() then
return "killed"
elseif has_active_encounter_wiped_or_timed_out(parsed_line) then
return "wipe-or-timeout"
else
return nil
end
end
function begin_encounter(encounter_name, parsed_line)
local datetime = date()
active_encounter = {
name = encounter_name,
entities = {},
started_at = datetime,
started_at_timestamp = parsed_line.timestamp
}
print("Beginning encounter \"" .. encounter_name .. "\" detected on line: " .. parsed_line.line)
take_raid_buff_snapshot(encounter_name)
end
function end_encounter(parsed_line, end_reason)
local wipe_or_timeout = (end_reason == "wipe-or-timeout")
print("Ending encounter \"" .. active_encounter.name .. "\" detected on line: " .. parsed_line.line)
if wipe_or_timeout then
print("Encounter ending due to wipe or trigger entity activity timeout (unsuccessful encounter kill attempt).")
end
if not wipe_or_timeout then
table.insert(previous_successful_encounters, active_encounter.name);
end
active_encounter = {}
end
function active_encounter_processing(parsed_line)
handle_line(parsed_line)
local end_reason = detect_encounter_end(parsed_line)
if end_reason then
end_encounter(parsed_line, end_reason)
end
end
function out_of_encounter_processing(parsed_line)
local encounter_name = detect_encounter_triggered(parsed_line)
if encounter_name then
begin_encounter(encounter_name, parsed_line)
handle_line(parsed_line)
end
end
function vwowrla_process_combat_event(combat_log_line)
if not combat_log_line then return end
combat_log_line = undo_swstats_fixlogstring(sanitize_entity_names(combat_log_line))
local parsed = parse_combat_log_line(combat_log_line)
if parsed then
process_parsed_combat_log_line(parsed)
if in_active_encounter() then
active_encounter_processing(parsed)
else
out_of_encounter_processing(parsed)
end
end
end

581
VWoWRLAHelper/matchers.lua Normal file
View file

@ -0,0 +1,581 @@
-- these patterns copied from the full set of combat log regex patterns located in
-- the server app code under vwowrla.core.events.matchers/regex-matchers and
-- "dumbed-down" / converted to lua patterns. lua patterns are not as complex as regular
-- expressions, so there's a bit more repetition to get around things like missing support
-- for optional substring matching (e.g. "(hits|crits)", "(?:missed|misses)", etc ...).
-- event types have been "compacted" somewhat. we only care about types of events in a
-- fairly broad sense and don't need to be really, really specific.
-- additionally, for the purposes of this addon, there are some bits of info in certain
-- more complicated combat log strings that we don't care about and so support for matching
-- them has just been dropped out of the appropriate patterns. things such as glancing/crushing
-- blows and partial absorbs/resists... all we are using these matchers for is to correctly
-- parse out 1) the type of event, 2) the names of the entities involved, and finally 3) the
-- name of any skill being used by the source on a target.
vwowrla_combat_log_patterns = {
------------------------------------------------------------------------------------------------
-- IGNORED EVENTS
-- these are only here to prevent "unrecognized" errors for combat log events that we
-- do not care about. we list them first so they are matched first (and not perhaps mistaken
-- for another type of event) and then ignored by the event handler.
{
event = "ignored",
pattern = {
"^You fail to cast (.+): (.+)%.$",
"^You fail to perform (.+): (.+)%.$",
"^You have slain (.+)!$",
"^(.+) is slain by (.+)!$",
"^(.+) create(%a?) (.+)%.$",
"^Your pet begins eating a (.+)%.$",
"^(.-)'s pet begins eating a (.+)%.$",
"^Your (.+) is reflected back by (.+)%.$",
"^(.-)'s (.+) is reflected back by (.+)%.$",
"^(.+) is destroyed%.$",
"^Your (.+) reputation has increased by (%d+)%.$",
"^Your equipped items suffer a (.+) durability loss%.$",
"^(.+) dies, honorable kill (.+)$",
"^(.+) is killed by (.+)%.$"
}
},
------------------------------------------------------------------------------------------------
-- SKILL/SPELL DAMAGE
{
event = "skill-damage-to-target",
pattern = {
"^Your (.+) hits (.+) for (%d+) (.+) damage%.(.*)$",
"^Your (.+) crits (.+) for (%d+) (.+) damage%.(.*)$",
"^Your (.+) hits (.+) for (%d+) damage%.(.*)$",
"^Your (.+) crits (.+) for (%d+) damage%.(.*)$",
"^Your (.+) hits (.+) for (%d+) (.+)%.(.*)$",
"^Your (.+) crits (.+) for (%d+) (.+)%.(.*)$"
},
fn = function(matches)
return {
skill = matches[1],
target_name = matches[2],
source_name = "you",
attack = true
}
end
},
{
event = "skill-damage-to-target",
pattern = {
"^(.-)'s (.+) hits (.+) for (%d+) (.+) damage%.(.*)$",
"^(.-)'s (.+) crits (.+) for (%d+) (.+) damage%.(.*)$",
"^(.-)'s (.+) hits (.+) for (%d+) damage%.(.*)$",
"^(.-)'s (.+) crits (.+) for (%d+) damage%.(.*)$",
"^(.-)'s (.+) hits (.+) for (%d+)%.(.*)$",
"^(.-)'s (.+) crits (.+) for (%d+)%.(.*)$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2],
target_name = matches[3],
attack = true
}
end
},
------------------------------------------------------------------------------------------------
-- SKILL/SPELL MISSES / FULL ABSORBS/RESISTS
{
event = "skill-avoided-by-target",
pattern = {
"^Your (.+) missed (.+)%.$",
"^Your (.+) was parried by (.+)%.$",
"^Your (.+) was blocked by (.+)%.$",
"^Your (.+) was dodged by (.+)%.$",
"^Your (.+) was evaded by (.+)%.$",
"^Your (.+) is absorbed by (.+)%.$",
"^Your (.+) was resisted by (.+)%.$",
"^Your (.+) failed%. (.+) is immune%.$"
},
fn = function(matches)
return {
skill = matches[1],
target_name = matches[2],
source_name = "you",
attack = true
}
end
},
{
event = "skill-avoided-by-target",
pattern = {
"^(.+) resists your (.+)%.$"
},
fn = function(matches)
return {
target_name = matches[1],
skill = matches[2],
source_name = "you",
attack = true
}
end
},
{
event = "skill-avoided-by-target",
pattern = {
"^(.-)'s (.+) was parried%.$",
"^(.-)'s (.+) was dodged%.$",
"^(.-)'s (.+) was resisted%.$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2],
target_name = "you",
attack = true
}
end
},
{
event = "skill-avoided-by-target",
pattern = {
"^(.-)'s (.+) misses (.+)%.$",
"^(.-)'s (.+) missed (.+)%.$",
"^(.-)'s (.+) was parried by (.+)%.$",
"^(.-)'s (.+) was blocked by (.+)%.$",
"^(.-)'s (.+) was dodged by (.+)%.$",
"^(.-)'s (.+) was evaded by (.+)%.$",
"^(.-)'s (.+) is absorbed by (.+)%.$",
"^(.-)'s (.+) was resisted by (.+)%.$",
"^(.-)'s (.+) fails%. (.+) is immune%.$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2],
target_name = matches[3],
attack = true
}
end
},
{
event = "skill-avoided-by-target",
pattern = {
"^(.+) resists? (.-)'s (.+)%.$",
"^(.+) absorbs? (.-)'s (.+)%.$"
},
fn = function(matches)
return {
target_name = matches[1],
source_name = matches[2],
skill = matches[3],
attack = true
}
end
},
------------------------------------------------------------------------------------------------
-- REFLECTED DAMAGE
{
event = "damage-reflected",
pattern = {
"^(.+) reflects? (%d+) (.+) damage to (.+)%.$"
},
fn = function(matches)
return {
source_name = matches[1],
target_name = matches[4],
attack = true
}
end
},
------------------------------------------------------------------------------------------------
-- MELEE DAMAGE
{
event = "melee-damage-to-target",
pattern = {
"^(.+) hits? (.+) for (%d+)%.(.*)$",
"^(.+) crits? (.+) for (%d+)%.(.*)$",
"^(.+) hits? (.+) for (%d+) (.+) damage%.(.*)$",
"^(.+) crits? (.+) for (%d+) (.+) damage%.(.*)$"
},
fn = function(matches)
return {
source_name = matches[1],
target_name = matches[2],
attack = true
}
end
},
------------------------------------------------------------------------------------------------
-- MELEE DAMAGE AVOIDANCE (ABSORB/RESIST/MISS/BLOCK/DODGE/PARRY/EVADE)
{
event = "melee-avoided-by-target",
pattern = {
"^(.+) attacks?%. (.+) absorbs? all the damage%.$",
"^(.+) attacks?%. (.+) resists? all the damage%.$",
"^(.+) misses (.+)%.$",
"^(.+) miss (.+)%.$",
"^(.+) attacks?%. (.+) parry%.$",
"^(.+) attacks?%. (.+) parries%.$",
"^(.+) attacks?%. (.+) dodges?%.$",
"^(.+) attacks?%. (.+) blocks?%.$",
"^(.+) attacks?%. (.+) evades?%.$",
"^(.+) attacks? but (.+) is immune%.$"
},
fn = function(matches)
return {
source_name = matches[1],
target_name = matches[2],
attack = true
}
end
},
------------------------------------------------------------------------------------------------
-- SKILL INTERRUPTION
{
event = "skill-interrupted-by-target",
pattern = {
"^(.+) interrupts? your (.+)%.$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2],
target_name = "you",
attack = true
}
end
},
{
event = "skill-interrupted-by-target",
pattern = {
"^(.+) interrupts? (.-)'s (.+)%.$"
},
fn = function(matches)
return {
source_name = matches[1],
target_name = matches[2],
skill = matches[3],
attack = true
}
end
},
------------------------------------------------------------------------------------------------
-- DAMAGE OVER TIME (DOT)
{
event = "dot-damages-target",
pattern = {
"^(.+) suffers? (%d+) (.+) damage from your (.+)%.(.*)$"
},
fn = function(matches)
return {
target_name = matches[1],
skill = matches[4],
source_name = "you",
attack = true
}
end
},
{
event = "dot-damages-target",
pattern = {
"^(.+) suffers? (%d+) (.+) damage from (.-)'s (.+)%.(.*)$"
},
fn = function(matches)
return {
target_name = matches[1],
source_name = matches[4],
skill = matches[5],
attack = true
}
end
},
------------------------------------------------------------------------------------------------
-- CAST NOTIFICATION / INSTANT CAST ABILITIES PERFORMED
{
event = "cast-begins",
pattern = {
"^(.+) begins? to perform (.+)%.$",
"^(.+) begins? to cast (.+)%.$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2]
}
end
},
{
event = "skill-performed-on-target",
pattern = {
"^(.+) casts? (.+) on (.+): (.+)%.$",
"^(.+) performs? (.+) on (.+): (.+)%.$",
"^(.+) casts? (.+) on (.+)%.$",
"^(.+) performs? (.+) on (.+)%.$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2],
target_name = matches[3]
}
end
},
{
event = "cast",
pattern = {
"^(.+) casts? (.+)%.$",
"^(.+) performs? (.+)%.$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2]
}
end
},
------------------------------------------------------------------------------------------------
-- DIRECT HEALING FROM SKILLS
{
event = "skill-heals-target",
pattern = {
"^Your (.+) critically heals (.+) for (%d+)%.$",
"^Your (.+) heals (.+) for (%d+)%.$"
},
fn = function(matches)
return {
skill = matches[1],
target_name = matches[2],
source_name = "you"
}
end
},
{
event = "skill-heals-target",
pattern = {
"^(.-)'s (.+) critically heals (.+) for (%d+)%.$",
"^(.-)'s (.+) heals (.+) for (%d+)%.$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2],
target_name = matches[3]
}
end
},
------------------------------------------------------------------------------------------------
-- RESOURCE (HEALTH/MANA/RAGE/ENERGY/HAPPINESS) GAIN / LOSS
{
event = "resource-gained",
pattern = {
"^(.+) gains? (%d+) (.+) from your (.+)%.$"
},
fn = function(matches)
return {
target_name = matches[1],
resource_type = string.lower(matches[3]),
skill = matches[4],
source_name = "you"
}
end
},
{
event = "resource-gained",
pattern = {
"^(.+) gains? (%d+) (.+) from (.-)'s (.+)%.$"
},
fn = function(matches)
return {
target_name = matches[1],
resource_type = string.lower(matches[3]),
source_name = matches[4],
skill = matches[5]
}
end
},
{
event = "resource-gained",
pattern = {
"^(.+) gains? (%d+) (.+) from (.+)%.$"
},
fn = function(matches)
return {
target_name = matches[1],
resource_type = string.lower(matches[3]),
skill = matches[4],
source_name = matches[1]
}
end
},
{
event = "resource-lost",
pattern = {
"^Your (.+) drains (%d+) (.+) from (.+)%.$"
},
fn = function(matches)
return {
skill = matches[1],
resource_type = string.lower(matches[3]),
target_name = matches[4],
source_name = "you"
}
end
},
{
event = "resource-lost",
pattern = {
"^(.-)'s (.+) drains (%d+) (.+) from (.+)%.$"
},
fn = function(matches)
return {
source_name = matches[1],
skill = matches[2],
resource_type = string.lower(matches[4]),
target_name = matches[5]
}
end
},
------------------------------------------------------------------------------------------------
-- OTHER SPECIAL ABILITY GAINS
{
event = "special-gained",
pattern = {
"^(.+) gains? (.+) through (.+)%.$"
},
fn = function(matches)
return {
target_name = matches[1],
special = matches[2],
source = matches[3] -- not an entity name
}
end
},
------------------------------------------------------------------------------------------------
-- BUFF/DEBUFF
{
event = "aura-gained",
pattern = {
"^(.+) gains? (.+) %((%d+)%)%.$",
"^(.+) gains? (.+)%.$"
},
fn = function(matches)
return {
target_name = matches[1],
aura_name = matches[2],
aura_type = "buff"
}
end
},
{
event = "aura-gained",
pattern = {
"^(.+) is afflicted by (.+) %((%d+)%)%.$",
"^(.+) are afflicted by (.+) %((%d+)%)%.$",
"^(.+) is afflicted by (.+)%.$",
"^(.+) are afflicted by (.+)%.$",
},
fn = function(matches)
return {
target_name = matches[1],
aura_name = matches[2],
aura_type = "debuff",
attack = true
}
end
},
{
event = "aura-lost",
pattern = {
"^(.+) fades from (.+)%.$"
},
fn = function(matches)
return {
aura_name = matches[1],
target_name = matches[2],
}
end
},
{
event = "aura-lost",
pattern = {
"^Your (.+) is removed%.$"
},
fn = function(matches)
return {
aura_name = matches[1],
target_name = "you",
}
end
},
{
event = "aura-lost",
pattern = {
"^(.-)'s (.+) is removed%.$"
},
fn = function(matches)
return {
target_name = matches[1],
aura_name = matches[2],
}
end
},
------------------------------------------------------------------------------------------------
-- ENVIRONMENTAL / OTHER DAMAGE
{
event = "other-damage",
pattern = {
"^(.+) suffers? (%d+) points of (.+) damage%.(.*)$"
},
fn = function(matches)
return {
target_name = matches[1],
}
end
},
{
event = "other-damage",
pattern = {
"^(.+) loses? (%d+) health for swimming in lava%.(.*)$"
},
fn = function(matches)
return {
target_name = matches[1],
}
end
},
{
event = "other-damage",
pattern = {
"^(.+) falls? and loses? (%d+) health%.$"
},
fn = function(matches)
return {
target_name = matches[1],
}
end
},
------------------------------------------------------------------------------------------------
-- DEATH
{
event = "death",
pattern = {
"^(.+) dies?%.$"
},
fn = function(matches)
return {
source_name = matches[1],
}
end
}
};

66
VWoWRLAHelper/utils.lua Normal file
View file

@ -0,0 +1,66 @@
function obj_copy(obj, seen)
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do res[obj_copy(k, s)] = obj_copy(v, s) end
return res
end
function dump_var(var, indent)
print("<", type(var), ">")
if not indent then indent = 0 end
if type(var) == "table" then
for k, v in pairs(var) do
formatting = string.rep(" ", indent) .. k .. ": "
if type(v) == "table" then
print(formatting)
dump_var(v, indent+1)
elseif type(v) == 'boolean' then
print(formatting .. tostring(v))
else
print(formatting .. v)
end
end
elseif type(var) == "boolean" then
formatting = string.rep(" ", indent)
print(formatting .. tostring(var))
else
formatting = string.rep(" ", indent)
print(formatting .. tostring(var))
end
end
--function explode(sep, str, limit)
-- if not sep or sep == "" then return false end
-- if not str then return false end
-- limit = limit or mhuge
-- if limit == 0 or limit == 1 then return {str},1 end
--
-- local r = {}
-- local n, init = 0, 1
--
-- while true do
-- local s,e = strfind(str, sep, init, true)
-- if not s then break end
-- r[#r+1] = strsub(str, init, s - 1)
-- init = e + 1
-- n = n + 1
-- if n == limit - 1 then break end
-- end
--
-- if init <= strlen(str) then
-- r[#r+1] = strsub(str, init)
-- else
-- r[#r+1] = ""
-- end
-- n = n + 1
--
-- if limit < 0 then
-- for i=n, n + limit + 1, -1 do r[i] = nil end
-- n = n + limit
-- end
--
-- return r, n
--end