diff --git a/OverwriteFTC.lua b/OverwriteFTC.lua new file mode 100644 index 0000000..703855a --- /dev/null +++ b/OverwriteFTC.lua @@ -0,0 +1,175 @@ + +-- grab ftc from global +FTC = _G['FTC'] + +--[[ + * Handles Combat Events + * -------------------------------- + * Called by EVENT_COMBAT_EVENT + * -------------------------------- + ]]-- +function FTC.OnCombatEvent( eventCode , result , isError , abilityName , abilityGraphic , abilityActionSlotType , sourceName , sourceType , targetName , targetType , hitValue , powerType , damageType , log , sourceUnitId , targetUnitId , abilityId ) + + -- Ignore errors + if ( isError ) then return end + + -- Pass damage event to handler + FTC.Damage:New( result , abilityName , abilityGraphic , abilityActionSlotType , sourceName , sourceType , targetName , targetType , hitValue , powerType , damageType, abilityId ) +end + + + +--[[ + * Validate and Process New Damages + * -------------------------------- + * Called by FTC:OnCombatEvent() + * -------------------------------- + ]]-- +function FTC.Damage:New( result , abilityName , abilityGraphic , abilityActionSlotType , sourceName , sourceType , targetName , targetType , hitValue , powerType , damageType, abilityId ) + + -- Determine context + local target = zo_strformat("<<!aC:1>>",targetName) + local player = zo_strformat("<<!aC:1>>",FTC.Player.name) + local damageOut = false + if ( sourceType == COMBAT_UNIT_TYPE_PLAYER or sourceType == COMBAT_UNIT_TYPE_PLAYER_PET ) then damageOut = true + elseif ( target == player ) then damageOut = false + else return end + + -- Debugging + --d( result .. " || " .. sourceType .. " || " .. sourceName .. " || " .. targetName .. " || " .. abilityName .. " || " .. hitValue ) + + -- Reflag self-targetted as incoming + if ( damageOut and ( target == player ) ) then damageOut = false end + + -- Ignore certain results + if ( FTC.Damage:Filter( result , abilityName ) ) then return end + + -- Compute some flags + local isCrit = result == ACTION_RESULT_CRITICAL_DAMAGE or result == ACTION_RESULT_CRITICAL_HEAL or result == ACTION_RESULT_DOT_TICK_CRITICAL or result == ACTION_RESULT_HOT_TICK_CRITICAL + local isHeal = result == ACTION_RESULT_HEAL or result == ACTION_RESULT_CRITICAL_HEAL or result == ACTION_RESULT_HOT_TICK or result == ACTION_RESULT_HOT_TICK_CRITICAL + + -- Get the icon + local icon = FTC.UI.Textures[abilityName] or '/esoui/art/icons/death_recap_ranged_basic.dds' + if ( abilityName == "" and ( not damageOut ) and isHeal ) then icon = '/esoui/art/icons/ability_healer_017.dds' + elseif ( abilityName == "" and not damageOut ) then icon = '/esoui/art/icons/death_recap_ranged_heavy.dds' end + + -- Setup the damage object + local damage = { + ["out"] = damageOut, + ["result"] = result, + ["target"] = targetName, + ["source"] = sourceName, + ["ability"] = abilityName, + ["type"] = damageType, + ["value"] = hitValue, + ["power"] = powerType, + ["ms"] = GetGameTimeMilliseconds(), + ["crit"] = isCrit, + ["heal"] = isHeal, + ["icon"] = icon, + ["mult"] = 1, + ["weapon"] = FTC.Damage:IsWeaponAttack(abilityName), + -- added by PanicModeCombatAnalyzer for better usage + ["abilityId"] = abilityId + } + + -- ACTION_RESULT_IMMUNE + -- ACTION_RESULT_BLOCKED + -- ACTION_RESULT_POWER_DRAIN + -- ACTION_RESULT_POWER_ENERGIZE + + -- Damage Dealt + if ( hitValue > 0 and ( result == ACTION_RESULT_DAMAGE or result == ACTION_RESULT_CRITICAL_DAMAGE or result == ACTION_RESULT_BLOCKED_DAMAGE or result == ACTION_RESULT_DOT_TICK or result == ACTION_RESULT_DOT_TICK_CRITICAL ) ) then + + -- Flag timestamps + if ( damageOut ) then FTC.Damage.lastOut = GetGameTimeMilliseconds() + else FTC.Damage.lastIn = GetGameTimeMilliseconds() end + + -- Log and SCT + if ( FTC.init.Log ) then FTC.Log:CombatEvent(damage) end + if ( FTC.init.SCT ) then FTC.SCT:Damage(damage) end + + -- Statistics + if ( FTC.init.Stats and damageOut ) then FTC.Stats:RegisterDamage(damage) end + + -- Trigger Ulti Buff + if ( FTC.init.Hotbar ) then FTC.Hotbar:UltimateBuff(damage) end + + -- Falling damage + elseif ( result == ACTION_RESULT_FALL_DAMAGE ) then + damage.ability = GetString(FTC_Falling) + damage.icon = '/esoui/art/icons/death_recap_fall_damage.dds' + + -- Log and SCT + if ( FTC.init.Log ) then FTC.Log:CombatEvent(damage) end + if ( FTC.init.SCT ) then FTC.SCT:Damage(damage) end + + -- Shielded Damage + elseif ( result == ACTION_RESULT_DAMAGE_SHIELDED ) then + + -- Log and SCT + if ( FTC.init.Log ) then FTC.Log:CombatEvent(damage) end + if ( FTC.init.SCT ) then FTC.SCT:Damage(damage) end + + -- Statistics + if ( FTC.init.Stats and damageOut ) then FTC.Stats:RegisterDamage(damage) end + + -- Misses and Dodges + elseif ( result == ACTION_RESULT_DODGED or result == ACTION_RESULT_MISS ) then + + -- Log and SCT + if ( FTC.init.Log ) then FTC.Log:CombatEvent(damage) end + if ( FTC.init.SCT ) then FTC.SCT:Damage(damage) end + + -- Crowd Controls + elseif ( result == ACTION_RESULT_INTERRUPT or result == ACTION_RESULT_STUNNED or result == ACTION_RESULT_OFFBALANCE or result == ACTION_RESULT_DISORIENTED or result == ACTION_RESULT_STAGGERED or result == ACTION_RESULT_FEARED or result == ACTION_RESULT_SILENCED or result == ACTION_RESULT_ROOTED ) then + + -- Trigger Break Free buff + if ( FTC.init.Buffs and damage.ability == GetAbilityName(16565) ) then + local ability = { + ["owner"] = FTC.Player.name, + ["id"] = 16565, + ["name"] = GetString(FTC_BreakFree), + ["dur"] = 8000, + ["icon"] = FTC.UI.Textures[GetAbilityName(16565)], + ["ground"] = false, + ["area"] = false, + ["debuff"] = false, + ["toggle"] = nil, + } + FTC.Buffs:NewEffect( ability , "Player" ) + end + + -- Fire SCT Alert + if ( FTC.init.SCT ) then FTC.SCT:NewCC( result , abilityName , damageOut ) end + + -- Healing Dealt + elseif ( hitValue > 0 and ( result == ACTION_RESULT_HEAL or result == ACTION_RESULT_CRITICAL_HEAL or result == ACTION_RESULT_HOT_TICK or result == ACTION_RESULT_HOT_TICK_CRITICAL ) ) then + + -- Log and SCT + if ( FTC.init.Log ) then FTC.Log:CombatEvent(damage) end + if ( FTC.init.SCT ) then FTC.SCT:Damage(damage) end + + -- Statistics + if ( FTC.init.Stats and sourceType == COMBAT_UNIT_TYPE_PLAYER ) then FTC.Stats:RegisterDamage(damage) end + + -- Target Death + elseif ( result == ACTION_RESULD_DIED or result == ACTION_RESULT_DIED_XP ) then + + -- Wipe Buffs + if ( FTC.init.Buffs ) then FTC.Buffs:WipeBuffs(targetName) end + + -- Log and SCT + if ( FTC.init.Log ) then FTC.Log:CombatEvent(damage) end + + -- DEBUG NEW EVENTS + elseif ( hitValue > 0 ) then + + -- Prompt other unrecognized + --local direction = damageIn and "Incoming" or "Outgoing" + -- FTC.Log:Print( direction .. " result " .. result .. " not recognized! Target: " .. targetName .. " Value: " .. hitValue , {1,1,0} ) + end + + -- Fire a callback for extensions to use + CALLBACK_MANAGER:FireCallbacks( "FTC_NewDamage" , damage ) +end \ No newline at end of file diff --git a/PanicModeCombatAnalyzer.lua b/PanicModeCombatAnalyzer.lua index ad279c3..ff5ef8e 100644 --- a/PanicModeCombatAnalyzer.lua +++ b/PanicModeCombatAnalyzer.lua @@ -2,19 +2,22 @@ PanicModeCombatAnalyzer = PanicModeCombatAnalyzer or {} PanicModeCombatAnalyzer.name = 'PanicModeCombatAnalyzer' -PanicModeCombatAnalyzer.version = '1.0.2' -PanicModeCombatAnalyzer.versionDB = 1 +PanicModeCombatAnalyzer.version = '1.0.3' +PanicModeCombatAnalyzer.versionDB = 2 PanicModeCombatAnalyzer.loaded = false PanicModeCombatAnalyzer.author = 'silentgecko, deevilius' PanicModeCombatAnalyzer.savedVarsName = 'PMCAVars' PanicModeCombatAnalyzer.variables = { - fights = {}, - groups = {}, - trials = {}, + data = {}, + metadata = { + server = 'EU', + language = 'de', + } } PanicModeCombatAnalyzer.tempVars = { lastSave = 0, + lastSaveTimeStamp = 0, inFight = false, lastDps = 0, lastDpsCount = 0, @@ -22,9 +25,6 @@ PanicModeCombatAnalyzer.tempVars = { trialId = 0, } --- grab ftc from global -FTC = _G['FTC'] - ---------Passing saved variables to the labels at initialize------- function PanicModeCombatAnalyzer.Initialize(_, addonName) local self = PanicModeCombatAnalyzer @@ -35,62 +35,97 @@ function PanicModeCombatAnalyzer.Initialize(_, addonName) self.savedVariables = ZO_SavedVars:New(self.savedVarsName, self.versionDB, nil, self.variables) - EVENT_MANAGER:RegisterForEvent(self.name, EVENT_PLAYER_COMBAT_STATE, self.OnPlayerCombatState) - - --trials - EVENT_MANAGER:RegisterForEvent(self.name .. "_TRIAL_START", EVENT_RAID_TRIAL_STARTED, self.saveTrials) - EVENT_MANAGER:RegisterForEvent(self.name .. "_TRIAL_STOP", EVENT_RAID_TRIAL_COMPLETE, self.saveTrials) - EVENT_MANAGER:RegisterForEvent(self.name .. "_TRIAL_STOP", EVENT_RAID_TRIAL_FAILED, self.saveTrials) - EVENT_MANAGER:RegisterForEvent(self.name .. "_TRIAL_STOP_GROUP", EVENT_GROUP_MEMBER_LEFT, self.saveTrialsFromGroup) + -- reset previously saved data +-- if self.savedVariables.fights ~= nil then +-- self.savedVariables.fights = nil +-- end +-- if self.savedVariables.groups ~= nil then +-- self.savedVariables.groups = nil +-- end +-- if self.savedVariables.trials ~= nil then +-- self.savedVariables.trials = nil +-- end + + -- todo cleanup old data + + --save current server and language + self.savedVariables.metadata = { + server = GetWorldName(), + language = GetCVar('language.2') + } - --groups - EVENT_MANAGER:RegisterForEvent(self.name .. "_GROUP_CHANGE", EVENT_GROUP_MEMBER_JOINED, self.saveGroup) - EVENT_MANAGER:RegisterForEvent(self.name .. "_GROUP_CHANGE", EVENT_GROUP_MEMBER_LEFT, self.saveGroup) - EVENT_MANAGER:RegisterForEvent(self.name .. "_GROUP_CHANGE", EVENT_GROUP_UPDATE, self.saveGroup) + EVENT_MANAGER:RegisterForEvent(self.name, EVENT_PLAYER_COMBAT_STATE, self.OnPlayerCombatState) + CALLBACK_MANAGER:RegisterCallback("FTC_NewDamage", self.addDamage) end function PanicModeCombatAnalyzer.OnPlayerCombatState(_, inCombat) PanicModeCombatAnalyzer.tempVars.inFight = inCombat end -function PanicModeCombatAnalyzer.saveTrials(event) +function PanicModeCombatAnalyzer.getTrial() local self = PanicModeCombatAnalyzer - local timeStamp = GetTimeStamp() - local currentRaid = GetCurrentParticipatingRaidId() - local eventStr = 'start' - if event == 131134 or event == '131134' then - self.tempVars.trialRunning = true - self.tempVars.trialId = currentRaid - else - eventStr = 'stop' - self.tempVars.trialRunning = false - end - self.savedVariables.trials[currentRaid] = self.savedVariables.trials[currentRaid] or {} - self.savedVariables.trials[currentRaid][timeStamp] = eventStr + local currentRaid = GetCurrentParticipatingRaidId() or 0 + + return currentRaid end -function PanicModeCombatAnalyzer.saveTrialsFromGroup(_, _, reason) - local self = PanicModeCombatAnalyzer - local timeStamp = GetTimeStamp() - local currentRaid = self.tempVars.trialId -- we use our tempvar, because when you zone out of trial, and still in grp, raidid from api is 0 - local eventStr = 'stop' - - if (reason == GROUP_LEAVE_REASON_DISBAND or reason == GROUP_LEAVE_REASON_DESTROYED) and currentRaid > 0 and self.tempVars.trialRunning then - self.savedVariables.trials[currentRaid] = self.savedVariables.trials[currentRaid] or {} - self.savedVariables.trials[currentRaid][timeStamp] = eventStr - self.tempVars.trialRunning = false +-- get dmg from FTC +function PanicModeCombatAnalyzer.addDamage(damage) + local self = PanicModeCombatAnalyzer + + --only log outgoing stuff and greater than zero + if damage['out'] and damage['value'] > 0 then + local data = self.savedVariables.data or {} + local lastSave = self.tempVars.lastSave + local lastSaveTS = self.tempVars.lastSaveTimeStamp + local currentTime = GetGameTimeMilliseconds() + local timeStamp = GetTimeStamp() + local lastSaveDiff = currentTime - lastSave + + -- if the last saving data is 1 sek ago, make a new table + if lastSaveDiff >= 1000 then + lastSaveTS = timeStamp + self.tempVars.lastSaveTimeStamp = timeStamp + self.tempVars.lastSave = currentTime + end + data[lastSaveTS] = data[lastSaveTS] or {} + data[lastSaveTS]['damage'] = data[lastSaveTS]['damage'] or {} + data[lastSaveTS]['healing'] = data[lastSaveTS]['healing'] or {} + + local damageData = { + abilityId = damage['abilityId'], + damage = damage['value'], + crit = damage['crit'], + ms = damage['ms'], + ability = damage['ability'], + } + + if damage['heal'] then + local currentData = data[lastSaveTS]['healing'][damage['target']] or {} + table.insert(currentData, damageData) + data[lastSaveTS]['healing'][damage['target']] = currentData + else + local currentData = data[lastSaveTS]['damage'][damage['target']] or {} + table.insert(currentData, damageData) + data[lastSaveTS]['damage'][damage['target']] = currentData + end + + data[lastSaveTS]['group'] = self.getGroup() + data[lastSaveTS]['trial'] = self.getTrial() + + self.savedVariables.data[lastSaveTS] = data[lastSaveTS] + table.sort(self.savedVariables.data[lastSaveTS]) + end end -function PanicModeCombatAnalyzer.saveGroup() +function PanicModeCombatAnalyzer.getGroup() local self = PanicModeCombatAnalyzer - local timeStamp = GetTimeStamp() local groupSize = GetGroupSize() local group = {} -- iterate over group if groupSize > 0 then - self.savedVariables.groups[timeStamp] = self.savedVariables.groups[timeStamp] or {} for i = 1, groupSize do local unitTag = GetGroupUnitTagByIndex(i) if (DoesUnitExist(unitTag)) then @@ -98,65 +133,9 @@ function PanicModeCombatAnalyzer.saveGroup() table.insert(group, charName) end end - self.savedVariables.groups[timeStamp] = group end -end - ---[[ - * Overwrite from origin FTC - * Update the mini DPS meter - * -------------------------------- - * Called by FTC.Stats:Initialize() - * -------------------------------- - ]]-- -function FTC.Stats:Update() - local self = PanicModeCombatAnalyzer - - -- Bail out if there is no damage to report - if ( FTC.Stats.damage == 0 and FTC.Stats.healing == 0 ) then return end - - -- Retrieve data - local mini = _G["FTC_MiniMeter"] - - -- Compute the fight time - local time = ( FTC.Stats.endTime - FTC.Stats.startTime ) / 1000 - - -- Determine the correct time label - local dtime = (( not IsUnitInCombat('player') ) or ((( GetGameTimeMilliseconds() - FTC.Stats.endTime ) / 1000 ) >= FTC.Vars.DamageTimeout )) and time or ( GetGameTimeMilliseconds() - FTC.Stats.startTime ) / 1000 - local secs = ZO_FormatTime( dtime , SI_TIME_FORMAT_TIMESTAMP) - - -- Compute player statistics - local dps = FTC.DisplayNumber( FTC.Stats.damage / math.max(time, 1) ) - local hps = FTC.DisplayNumber( FTC.Stats.healing / math.max(time, 1) ) - - -- added saving vars - local lastSave = self.tempVars.lastSave - local currentTime = GetGameTimeMilliseconds() - local timeStamp = GetTimeStamp() - local lastSaveDiff = currentTime - lastSave - local currenTarget = GetUnitName('reticleover') - local dpsForSaving = (FTC.Stats.damage / math.max(time,1)) - - if self.tempVars.lastDps == dpsForSaving then - self.tempVars.lastDpsCount = self.tempVars.lastDpsCount + 1 - else - self.tempVars.lastDpsCount = 0 - end - self.tempVars.lastDps = dpsForSaving - - -- only save if we don't get three times the same amount of dps, and lastSavediff is more than one second ago and we're in fight - if self.tempVars.lastDpsCount <= 2 and lastSaveDiff >= 1000 and self.tempVars.inFight then - self.tempVars.lastSave = currentTime - self.savedVariables.fights[currenTarget] = self.savedVariables.fights[currenTarget] or {} - self.savedVariables.fights[currenTarget][timeStamp] = dpsForSaving - end - - - -- Update the labels - mini.damage.label:SetText( dps ) - mini.healing.label:SetText( hps ) - mini.time.label:SetText( secs ) + return group end -- debug func @@ -165,5 +144,24 @@ function PanicModeCombatAnalyzer.debug(message, data) d(message, data) end +-- deep copy of table +function PanicModeCombatAnalyzer.copyTableAndClean(orig) + local self = PanicModeCombatAnalyzer + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + if orig_key ~= 'Total' and orig_key ~= 'icon' then + copy[self.copyTableAndClean(orig_key)] = self.copyTableAndClean(orig_value) + end + end + setmetatable(copy, self.copyTableAndClean(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy +end + ---------Events------- EVENT_MANAGER:RegisterForEvent(PanicModeCombatAnalyzer.name, EVENT_ADD_ON_LOADED, PanicModeCombatAnalyzer.Initialize) diff --git a/PanicModeCombatAnalyzer.txt b/PanicModeCombatAnalyzer.txt index aa36cbe..f1e1c33 100644 --- a/PanicModeCombatAnalyzer.txt +++ b/PanicModeCombatAnalyzer.txt @@ -1,9 +1,10 @@ ## Title: |cEFEBBEPanic Mode Combat Analyzer|r ## Description: Saves your dps during fight, based on FTC DPS Meter. ## Author: |c009ad6silentgecko|r, deevilius -## Version: 1.0.2 +## Version: 1.0.3 ## APIVersion: 100013 ## SavedVariables: PMCAVars ## OptionalDependsOn: FoundryTacticalCombat +OverwriteFTC.lua PanicModeCombatAnalyzer.lua \ No newline at end of file