version 1.0.3

René Welbers [01-30-16 - 17:14]
version 1.0.3
- refactored storing of dps and other data completely
Filename
OverwriteFTC.lua
PanicModeCombatAnalyzer.lua
PanicModeCombatAnalyzer.txt
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