lotrointerface.com
Search Downloads

LoTROInterface SVN SequenceBars

[/] [trunk/] [Thurallor/] [Common/] [Utils/] [Watcher.lua] - Rev 20

Go to most recent revision | Compare with Previous | Blame | View Log

-- Singleton object for efficiently observing and keeping track of game state,
-- providing convenient functions and events.

local Watcher = {};
Thurallor.Utils.Watcher = Watcher;

local startupTime = Turbine.Engine.GetGameTime();
local function SaveSettings()
    if (not Watcher.settingsLoaded) then
        if (Turbine.Engine.GetGameTime() - startupTime > 5) then
            -- Settings file probably doesn't exist if it hasn't loaded in 5 seconds.
            Watcher.settingsLoaded = true;
        else
            return;
        end
    end
--Puts("Saving...");
    -- Workaround for Turbine localization bug.
    local saveData = ExportTable(Watcher.settings);
    Turbine.PluginData.Save(Turbine.DataScope.Account, "Thurallor_GameInfo", saveData, function()
--Puts("Save complete.");
    end);
end

local function LoadSettings()
--Puts("Loading...");
    Turbine.PluginData.Load(Turbine.DataScope.Account, "Thurallor_GameInfo", function(loadData, args)
        if (not loadData) then
            return;
        end
        
        -- Workaround for Turbine localization bug.
        local settings = ImportTable(loadData);
        if (not settings) then
            return;
        end
        
        -- Previously-saved settings override the defaults
        DeepTableCopy(settings, Watcher.settings);
        
        -- Compile loaded functions
        for categories, sourceCode in pairs(settings.funcs.AddCategories) do
            Watcher.AddCategories[categories] = loadstring(sourceCode);
        end
        for categories, sourceCode in pairs(settings.funcs.RemoveCategories) do
            Watcher.RemoveCategories[categories] = loadstring(sourceCode);
        end
        
        -- Allow save operations to proceed.
        Watcher.settingsLoaded = true;
        SaveSettings();
--Puts("Load complete.");
    end);
end

local function AddActorEffect(effectsObject, effect)
    local categories, name = effect:GetCategory(), effect:GetName();
    if (not effectsObject.cache[name]) then
        effectsObject.cache[name] = 0;
    end
    effectsObject.cache[name] = effectsObject.cache[name] + 1;

    if (not Watcher.settings.knownEffects[name]) then
        -- This is an effect we have never seen before.  Learn some info about it and save for future reference.
        categories = effect:GetCategory();
        Watcher.settings.knownEffects[name] = {
            categories, effect:GetIcon(), effect:IsCurable(), effect:IsDebuff()};

        local bits = categories;
        if (not Watcher.AddCategories[bits]) then
            -- This is a GetCategory value we have never seen before.  Create functions for adding/removing.
            local AddCategories = "local categories = ...; ";
            local RemoveCategories = "local categories = ...; ";
            if (bits >= 2048) then
                -- Unknown bits; discard them.
                bits = bits % 2048;
            end
            if (bits >= 1226) then
                -- Turbine.Gameplay.EffectCategory.Dispellable is the OR of several bits
                AddCategories = AddCategories .. "categories[1226] = categories[1226] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[1226] = categories[1226] - 1; ";
            end
            if (bits >= 1024) then
                AddCategories = AddCategories .. "categories[1024] = categories[1024] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[1024] = categories[1024] - 1; ";
                bits = bits - 1024;
            end
            if (bits >= 512) then
                AddCategories = AddCategories .. "categories[512] = categories[512] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[512] = categories[512] - 1; ";
                bits = bits - 512;
            end
            if (bits >= 256) then
                AddCategories = AddCategories .. "categories[256] = categories[256] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[256] = categories[256] - 1; ";
                bits = bits - 256;
            end
            if (bits >= 128) then
                AddCategories = AddCategories .. "categories[128] = categories[128] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[128] = categories[128] - 1; ";
                bits = bits - 128;
            end
            if (bits >= 64) then
                AddCategories = AddCategories .. "categories[64] = categories[64] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[64] = categories[64] - 1; ";
                bits = bits - 64;
            end
            if (bits >= 32) then
                AddCategories = AddCategories .. "categories[32] = categories[32] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[32] = categories[32] - 1; ";
                bits = bits - 32;
            end
            if (bits >= 16) then
                AddCategories = AddCategories .. "categories[16] = categories[16] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[16] = categories[16] - 1; ";
                bits = bits - 16;
            end
            if (bits >= 8) then
                AddCategories = AddCategories .. "categories[8] = categories[8] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[8] = categories[8] - 1; ";
                bits = bits - 8;
            end
            if (bits >= 4) then
                AddCategories = AddCategories .. "categories[4] = categories[4] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[4] = categories[4] - 1; ";
                bits = bits - 4;
            end
            if (bits >= 2) then
                AddCategories = AddCategories .. "categories[2] = categories[2] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[2] = categories[2] - 1; ";
                bits = bits - 2;
            end
            if (bits >= 1) then
                AddCategories = AddCategories .. "categories[1] = categories[1] + 1; ";
                RemoveCategories = RemoveCategories .. "categories[1] = categories[1] - 1; ";
            end
            
            Watcher.settings.funcs.AddCategories[categories] = AddCategories;
            Watcher.AddCategories[categories] = loadstring(AddCategories);
            Watcher.settings.funcs.RemoveCategories[categories] = RemoveCategories;
            Watcher.RemoveCategories[categories] = loadstring(RemoveCategories);
        end

        SaveSettings();
    else
        categories = Watcher.settings.knownEffects[name][1];
    end

    Watcher.AddCategories[categories](effectsObject.activeCategories);
end

local function RemoveActorEffect(effectsObject, effect)
    local categories, name = effect:GetCategory(), effect:GetName();
    Watcher.RemoveCategories[categories](effectsObject.activeCategories);
    effectsObject.cache[name] = effectsObject.cache[name] - 1;
    if (effectsObject.cache[name] == 0) then
        effectsObject.cache[name] = nil;
    end
end

local function GetActorEffects(effectsObject)
    effectsObject.cache = {};
    effectsObject.activeCategories = {
        [Turbine.Gameplay.EffectCategory.Dispellable] = 0;
        [Turbine.Gameplay.EffectCategory.Corruption] = 0;
        [Turbine.Gameplay.EffectCategory.Elemental] = 0;
        [Turbine.Gameplay.EffectCategory.Tactical] = 0;
        [Turbine.Gameplay.EffectCategory.Poison] = 0;
        [Turbine.Gameplay.EffectCategory.Fear] = 0;
        [Turbine.Gameplay.EffectCategory.Song] = 0;
        [Turbine.Gameplay.EffectCategory.Cry] = 0;
        [Turbine.Gameplay.EffectCategory.Wound] = 0;
        [Turbine.Gameplay.EffectCategory.Physical] = 0;
        [Turbine.Gameplay.EffectCategory.Disease] = 0;
        [1] = 0;
    };
    for e = 1, effectsObject:GetCount(), 1 do
        local effect = effectsObject:Get(e);
        AddActorEffect(effectsObject, effect);
    end
end

local function GetEquipment()
    local eq = Watcher.playerEquipmentObject;
    eq.cache = { byName = {}; bySlot = {}; };
    for slot = 1, eq:GetSize() do
        local item = eq:GetItem(slot);
        if (item) then
            local name = item:GetItemInfo():GetName();
            eq.cache.byName[name] = { item, slot };
            eq.cache.bySlot[slot] = { item, name };
        end
    end
end

local function GetBackpack()
    local bp = Watcher.playerBackpackObject;
    bp.cache = { byName = {}; bySlot = {}; };
    for slot = 1, bp:GetSize() do
        local item = bp:GetItem(slot);
        if (item) then
            local name = item:GetItemInfo():GetName();
            bp.cache.byName[name] = { item, slot };
            bp.cache.bySlot[slot] = { item, name };
        end
    end
end

local function ResetTimeChanged(skill, args)
    DoCallbacks(self, "ResetTimeChanged", args);
end

local function GetSkills()
    local skills = Watcher.playerTrainedSkills;
    skills.cache = { names = {}; byName = {}; byIcon = {} };
    for c = 1, skills:GetCount(), 1 do
        local skill = skills:GetItem(c);
--        AddCallback(skill, "ResetTimeChanged", ResetTimeChanged);
        local skillInfo = skill:GetSkillInfo();
        local icon = skillInfo:GetIconImageID();
        local name = skillInfo:GetName();
        skills.cache.byName[name] = skill;
        skills.cache.byIcon[icon] = skill;
    end
    return skills.cache;
end

local function EffectAdded(effectsObject, args)
    AddActorEffect(effectsObject, effectsObject:Get(args.Index));
end

local function EffectRemoved(effectsObject, args)
    RemoveActorEffect(effectsObject, args.Effect);
end

local function EffectsCleared(effectsObject, args)
    GetActorEffects(effectsObject);
end

local function PlayerTargetChanged()
    Watcher.targetObject = Watcher.playerObject:GetTarget();
    if (Watcher.targetObject and Watcher.targetObject.GetEffects) then
        Watcher.targetEffectsObject = Watcher.targetObject:GetEffects();
        AddCallback(Watcher.targetEffectsObject, "EffectAdded", EffectAdded);
        AddCallback(Watcher.targetEffectsObject, "EffectRemoved", EffectRemoved);
        AddCallback(Watcher.targetEffectsObject, "EffectsCleared", EffectsCleared);
        GetActorEffects(Watcher.targetEffectsObject);
    else
        Watcher.targetEffectsObject = nil;
    end
end

local function ItemEquipped(sender, args)
--Puts("ItemEquipped: args = " .. Serialize(args));    
    local slot, item, name = args.Index, args.Item, args.Item:GetItemInfo():GetName();
    Watcher.playerEquipmentObject.cache.byName[name] = { item, slot };
    Watcher.playerEquipmentObject.cache.bySlot[slot] = { item, name };
    DoCallbacks(Watcher, "ItemEquipped", args);
end

local function ItemUnequipped(sender, args)
--Puts("ItemUnequipped: args = " .. Serialize(args));    
    local slot, name = args.Index, args.Item:GetItemInfo():GetName();
    Watcher.playerEquipmentObject.cache.bySlot[slot] = nil;
    Watcher.playerEquipmentObject.cache.byName[name] = nil;
    DoCallbacks(Watcher, "ItemUnequipped", args);
end

local function ItemAdded(sender, args)
--Puts("ItemAdded: args = " .. Serialize(args));    
    local slot, item, name = args.Index, args.Item, args.Item:GetItemInfo():GetName();
--Puts("Item " .. name .. " added in slot " .. slot);
    Watcher.playerBackpackObject.cache.byName[name] = { item, slot };
    Watcher.playerBackpackObject.cache.bySlot[slot] = { item, name };
end

local function ItemRemoved(sender, args)
--Puts("ItemRemoved: args = " .. Serialize(args));    
    local cache = Watcher.playerBackpackObject.cache;
    local slot, name = args.Index, cache.bySlot[args.Index][2];
--Puts("Item " .. name .. " removed from slot " .. slot);
    cache.bySlot[slot] = nil;
    cache.byName[name] = nil;
end

local function ItemMoved(sender, args)
--Puts("ItemMoved: args = " .. Serialize(args));    
    local cache = Watcher.playerBackpackObject.cache;
    local oldSlot, newSlot = args.OldIndex, args.NewIndex;
    if (not args.Item) then
        -- Spurious ItemMoved event that sometimes occurs when unequipping an item
        return;
    end
--Puts("Item " .. cache.bySlot[oldIndex][2] .. " moved from slot " .. oldSlot .. " to slot " .. newSlot);
    local item, name = unpack(cache.bySlot[oldSlot]);
    cache.bySlot[newSlot] = { item, name };
    cache.bySlot[oldSlot] = nil;
    DoCallbacks(Watcher, "ItemMoved", args);
end

local function SkillAdded(sender, args)
    -- rare occurrence; efficiency not important
    GetSkills();
end

local function SkillRemoved(sender, args)
    -- rare occurrence; efficiency not important
    GetSkills();
end

function Watcher.GetSkillsInfo()
    local cache = Watcher.playerTrainedSkills.cache;
    if (not cache) then
        cache = GetSkills();
    end
    for name in sorted_keys(cache.byName) do
        table.insert(cache.names, name);
    end
    return cache;
end

function Watcher.GetKnownEffectNames()
    local names = {};
    for name in sorted_keys(Watcher.settings.knownEffects) do
        table.insert(names, name);
    end
    return names;
end

-- If itemName is specified, the equipped item (if any) with the specified name is returned.
-- If itemSlot is specified, the equipped item (if any) at the specified slot is returned.
-- If neither is specified, a random equipped item (if any) is returned.
-- Returns slot, item (or nil).
function Watcher.GetEquippedItem(itemName, itemSlot)
    if ((not itemSlot) and (not itemName)) then
        for name, byName in pairs(Watcher.playerEquipmentObject.cache.byName) do
            local item, slot = unpack(byName);
            return slot, item;
        end
    elseif (not itemSlot) then
        local byName = Watcher.playerEquipmentObject.cache.byName[itemName];
        if (byName) then
            local item, slot = unpack(byName);
            return slot, item;
        end
    elseif (not itemName) then
        local bySlot = Watcher.playerEquipmentObject.cache.bySlot[itemSlot];
        if (bySlot) then
            local item, name = unpack(bySlot);
            return itemSlot, item;
        end
    else -- itemName and itemSlot specified
        local bySlot = Watcher.playerEquipmentObject.cache.bySlot[itemSlot];
        if (bySlot) then
            local item, name = unpack(bySlot);
            if (name == itemName) then
                return itemSlot, item;
            end
        end
    end
end

function Watcher.SkillReady(iconID)
    local cache = Watcher.playerTrainedSkills.cache;
    if (not cache) then
        cache = GetSkills();
    end
    local skill = cache.byIcon[iconID];
    return (skill and skill:IsUsable() and (skill:GetResetTime() == -1));
end

function Watcher.PlayerHasEffectCategory(category)
    if (category) then
        return Watcher.playerEffectsObject.activeCategories[category] > 0;
    end
    return false;
end

function Watcher.PlayerHasEffect(name)
    return Watcher.playerEffectsObject.cache[name];
end

function Watcher.TargetHasEffectCategory(category)
    if (Watcher.targetEffectsObject and category) then
        return Watcher.targetEffectsObject.activeCategories[category] > 0;
    end
    return false;
end

function Watcher.TargetHasEffect(name)
    return (Watcher.targetEffectsObject and Watcher.targetEffectsObject.cache[name]);
end

local function Constructor()
    
    -- Default settings
    Watcher.settings = {};
    Watcher.settings.knownEffects = {};
    Watcher.settings.funcs = {};
    Watcher.settings.funcs.AddCategories = {};
    Watcher.settings.funcs.RemoveCategories = {};

    -- Optimized functions generated on-the-fly
    Watcher.AddCategories = {};
    Watcher.RemoveCategories = {};
    
    -- Object for monitoring display updates:
--    Watcher.window = Turbine.UI.Window();
--    Watcher.window:SetWantsUpdates(true);

    -- Object for monitoring player events
    Watcher.playerObject = Turbine.Gameplay.LocalPlayer:GetInstance();
    AddCallback(Watcher.playerObject, "TargetChanged", PlayerTargetChanged);
    PlayerTargetChanged();
    
    Watcher.playerEquipmentObject = Watcher.playerObject:GetEquipment();
    AddCallback(Watcher.playerEquipmentObject, "ItemEquipped", ItemEquipped);
    AddCallback(Watcher.playerEquipmentObject, "ItemUnequipped", ItemUnequipped);
    GetEquipment();

    Watcher.playerBackpackObject = Watcher.playerObject:GetBackpack();
    AddCallback(Watcher.playerBackpackObject, "ItemAdded", ItemAdded);
    AddCallback(Watcher.playerBackpackObject, "ItemRemoved", ItemRemoved);
    AddCallback(Watcher.playerBackpackObject, "ItemMoved", ItemMoved);
    AddCallback(Watcher.playerBackpackObject, "SizeChanged", GetBackpack);
    GetBackpack();
    
    Watcher.playerEffectsObject = Watcher.playerObject:GetEffects();
    AddCallback(Watcher.playerEffectsObject, "EffectAdded", EffectAdded);
    AddCallback(Watcher.playerEffectsObject, "EffectRemoved", EffectRemoved);
    AddCallback(Watcher.playerEffectsObject, "EffectsCleared", EffectsCleared);
    GetActorEffects(Watcher.playerEffectsObject);
    
    Watcher.playerTrainedSkills = Watcher.playerObject:GetTrainedSkills();
    AddCallback(Watcher.playerTrainedSkills, "SkillAdded", PlayerSkillAdded);
    AddCallback(Watcher.playerTrainedSkills, "SkillRemoved", PlayerSkillRemoved);
    GetSkills();
end

-- Create single instance and load saved settings (if any).
Constructor();
LoadSettings();

Go to most recent revision | Compare with Previous | Blame


All times are GMT -5. The time now is 10:22 PM.


Our Network
EQInterface | EQ2Interface | Minion | WoWInterface | ESOUI | LoTROInterface | MMOUI | Swtorui