lotrointerface.com
Search Downloads

LoTROInterface SVN SequenceBars

[/] [tags/] [3.07/] [SequenceBars/] [Manager.lua] - Rev 110

Compare with Previous | Blame | View Log

Manager = class(Group);

function Manager:Constructor()
    Turbine.UI.Window.Constructor(self);
    self.constructing = true;

    self.manager = self;
    self.watcher = Thurallor.Utils.Watcher;
    self.groupID = "0";

    self.objects = {};
    self.eventGenerators = {};
    self.eventCallbacks = {};
    self.propagatingEvent = {};
    self.lastSaveTime = 0;
    self.player = Turbine.Gameplay.LocalPlayer:GetInstance();
    self.username = self.player:GetName();
    self.preferredBagSlot = 1;
    self.hudVisible = true;
    self.includees = {};
    self.includers = {};
    self.idleTime = Turbine.Engine.GetGameTime();
    
    -- Default settings
    self.settings = {};
    self.settings.type = "group";
    self.settings.caption = {};
    self.settings.snapToGrid = false;
    self.settings.gridSize = 35;
    self.settings.language = Turbine.Engine:GetLanguage();
    local name = L:GetText("/Default/GlobalName");
    self.settings.caption.text = string.gsub(name, "<name>", self.username);
    self.settings.caption.width = 80;
    self.settings.caption.height = 20;
    self.settings.caption.font = Turbine.UI.Lotro.Font.TrajanPro15;
    self.settings.color = "FFFFFF";
    self.settings.barIDs = {};
    self.settings.groupIDs = { "new" };
    self.settings.deletedBarIDs = {};
    self.settings.deletedGroupIDs = {};
    self.settings.eventHandlers = {};
    self.settings.eventsEnabled = false;
    self.settings.groups = {};
    self.settings.bars = {};
    self.settings.nextObjectID = 1;
    self.settings.pluginVersion = plugin:GetVersion();
    self.settings.uiScale = 32; -- zoom level will be applied to make quickslots this size
    self.settings.uiOpacity = 1.0;

    self.settings.displaySize = { Turbine.UI.Display.GetSize() };
    self.globals = self.settings;
    self:LoadSettings();
end

function Manager:LoadSettings()
    Turbine.PluginData.Load(Turbine.DataScope.Character, "SequenceBars", function(loadStr)
        if (loadStr) then
            -- Workaround for Turbine localization bug -- Thanks, Lynx3d!
            local settings = ImportTable(loadStr);
            if (not settings) then
                Turbine.Shell.WriteLine("Failed to parse SequenceBars.plugindata file!");
                return;
            end
        
            -- Previously-saved settings override the defaults
            DeepTableCopy(settings, self.settings);
        end

        -- Make color object from hex string
        self.color = Thurallor.Utils.Color(1, 0, 0, 0);
        self.color:SetHex(self.settings.color);
        
        -- Select language
        self:SetLanguage(self.settings.language);

        _G.manualUpdatesNeeded = "";
        self:LoadBars();
        self:LoadGroups();
        self:UpdateOptionsPanel();
        self.settings.pluginVersion = tonumber(Plugins.SequenceBars:GetVersion());
        self.constructing = nil;
        
        if (#_G.manualUpdatesNeeded > 0) then
            _G.manualUpdatesNeeded = "Since you have upgraded to a new version of SequenceBars, your settings file needs to be updated.  The following item(s) cannot be updated automatically, so you will have to do it yourself:\n\n" .. _G.manualUpdatesNeeded;
            Alert("Settings File Updated", _G.manualUpdatesNeeded, nil, Turbine.UI.Lotro.Font.Verdana14);
            _G.manualUpdatesNeeded = "";
        end

        
        self:SetWantsKeyEvents(true);
        self:SetWantsEvents(self.settings.eventsEnabled);
        
        self.displaySizeCallback = function(display)
            self.anchorCoords = nil;
        end
        AddCallback(Turbine.UI.Display, "SizeChanged", self.displaySizeCallback);
        
        -- In case the display size changed since the last time the plugin was used:
        self.anchorCoords = nil;
        self:SetWantsUpdates(true);
        
        self:PropagateEvent("Startup\n");
    end);
end

function Manager:Unload()
    self.lastSaveTime = 0;
    self.constructing = false;
    self.editingCaption = true;
    self:DoSave(); -- force save

    -- The following may appear pointless, but failure to clean up (e.g. not unregistering event handlers) can cause crashes.
    for objectID, object in pairs(self.objects) do
--Puts("Destroying object " .. tostring(objectID));
        if (object) then
            object:Destroy();
        end
    end
end

function Manager:SaveSettings(updateOptionsPanel)
    if (updateOptionsPanel) then
        self.updateOptionsPanel = true;
    end
    if (not self.constructing) then
        -- Start the idle timer.
        self.idleTime = Turbine.Engine.GetGameTime() + 0.25;
        self.AfterIdle = self.DoSave;
    end
end 

function Manager:Update()

    -- Propagate queued events
    if (next(self.propagatingEvent)) then
        local events = self.propagatingEvent;
        self.propagatingEvent = {};
        for eventName, _ in pairs(events) do
            DoCallbacks(self.eventCallbacks, eventName, eventName);
        end
    end

    -- Adjust bar layout based on new screen size
    if (not self.anchorCoords) then
        local speed = 0.2;
        local prevWidth, prevHeight = unpack(self.settings.displaySize);
        local width, height = Turbine.UI.Display:GetSize();
        local newWidth = prevWidth + (width - prevWidth) * speed;
        local newHeight = prevHeight + (height - prevHeight) * speed;
        if (math.abs(newWidth - width) < 1) then
            newWidth = width;
        end
        if (math.abs(newHeight - height) < 1) then
            newHeight = height;
        end
        self.settings.displaySize = { newWidth, newHeight };
        self:SaveSettings(false);
        self:Redraw(true);
        if ((newWidth ~= width) or (newHeight ~= height)) then
            self.anchorCoords = nil;
        end
    end

    if (self.AfterIdle) then
        -- Save will only occur when idle time reaches 0.25 s, and never in combat.
        local currentTime = Turbine.Engine.GetGameTime();
        if ((currentTime >= self.idleTime) and not self.player:IsInCombat()) then
            self:AfterIdle();
            self.AfterIdle = nil;
        end
    end
end
    
function Manager:DoSave()
--Puts("Saving...");
    -- Workaround for Turbine localization bug -- Thanks, Lynx3d!
    local saveData = ExportTable(self.settings);
    Turbine.PluginData.Save(Turbine.DataScope.Character, "SequenceBars", saveData, function()
--Puts("Save complete.");
    end);

    if (self.updateOptionsPanel and (not self.editingCaption)) then
        self:UpdateOptionsPanel();
        self.updateOptionsPanel = false;
    end
end

function Manager:UpdateOptionsPanel()
    if (not self.optionsPanel) then
        self.optionsPanel = OptionsPanel(self);
    end
    self.optionsPanel:RefreshDirectory();
end

function Manager:KeyDown(args)
    if (args.Action == Turbine.UI.Lotro.Action.ToggleHUD) then
        self.hudVisible = not self.hudVisible;
        self:ApplyHiddenness();
    end
end

function Manager:IsParentHidden()
    return (not self.hudVisible);
end

function Manager:ShellCommand(cmd, args)
    if (string.match(args, "^" .. L:GetText("/ShellCommand/Options"))) then
        self.optionsPanel:ShowGlobalSettings();
    elseif (string.match(args, "^" .. L:GetText("/ShellCommand/Events"))) then
        local eventNames = self:GetEventNames();
        if (#eventNames > 0) then
            Puts(table.concat(eventNames, "\n"));
        end
    elseif (string.match(args, "^" .. L:GetText("/ShellCommand/Event"))) then
        local eventName = string.sub(args, string.find(args, " ") + 1);
        Puts(" -> \"" .. eventName .. "\"");
        self:PropagateEvent(eventName);
    end
end

function Manager:FindNewObjectID()
    local id = self.settings.nextObjectID;
    self.settings.nextObjectID = id + 1;
    id = self.username .. "." .. tostring(id);
    self.objects[id] = { "reserved" };
    return id;
end

function Manager:GetObject(objectID)
    return self.objects[objectID];
end

function Manager:TransferBar(barID, oldGroup, newGroup)
    local foundPos = Search(oldGroup.settings.barIDs, barID);
    if (foundPos) then
        table.remove(oldGroup.settings.barIDs, foundPos);
        table.insert(newGroup.settings.barIDs, barID);
        local bar = self.objects[barID];
        bar.parent = newGroup;
        self:SaveSettings(true);
        newGroup:ApplyHiddenness();
    end
end

function Manager:TransferGroup(groupID, oldGroup, newGroup)
    local foundPos = Search(oldGroup.settings.groupIDs, groupID);
    if (foundPos) then
        table.remove(oldGroup.settings.groupIDs, foundPos);
        table.insert(newGroup.settings.groupIDs, groupID);
        local group = self.objects[groupID];
        group.parent = newGroup;
        self:SaveSettings(true);
        newGroup:ApplyHiddenness();
    end
end

function Manager:EmptyTrash()
    self.settings.deletedGroupIDs = {};
    for groupID, settings in pairs(self.settings.groups) do
        settings.deletedGroupIDs = {};
        settings.deletedBarIDs = {};
        if (not self.objects[groupID]) then
            self.settings.groups[groupID] = nil;
        end
    end
    self.settings.deletedBarIDs = {};
    for barID, bar in pairs(self.settings.bars) do
        if (not self.objects[barID]) then
            self.settings.bars[barID] = nil;
        end
    end
    self:SaveSettings(false);
end

function Manager:ShowSettingsMenu(fromOptionsPanel)
    -- Build the settings menu.
    self.settingsMenu = Turbine.UI.ContextMenu();
    Group.AddSettingsMenuItem(self, self.settingsMenu, "Root", fromOptionsPanel);
    self.amSubMenu = false;
    self:AddSettingsMenuItem(self.settingsMenu, "Root");
    self.settingsMenu:ShowMenu();
end

function Manager:AddSettingsMenuItem(parent, itemName, amSubMenu)
    if (amSubMenu ~= nil) then
        self.amSubMenu = amSubMenu;
    end
    local item = Turbine.UI.MenuItem(L:GetText(itemName), true, false);
    parent:GetItems():Add(item);

    if (itemName == "Root") then
        local prevContext = L:SetContext("/GlobalMenu");
        parent:GetItems():Clear();
        self:AddSettingsMenuItem(parent, "HideAll");
        if (not self.amSubMenu) then
            L:SetContext("/GroupMenu");
            Group.AddSettingsMenuItem(self, parent, "CreateNewBar");
            Group.AddSettingsMenuItem(self, parent, "CreateNewSubgroup");
            Group.AddSettingsMenuItem(self, parent, "Import");
        end
        if (#self.settings.deletedBarIDs + #self.settings.deletedGroupIDs > 0) then
            self:AddSettingsMenuItem(parent, "Undelete");
        end
        L:SetContext("/GroupMenu");
        Group.AddSettingsMenuItem(self, parent, "ArrangeBars");
        Group.AddSettingsMenuItem(self, parent, "GlobalEventBehaviors");
        L:SetContext("/GlobalMenu");
        self:AddSettingsMenuItem(parent, "GlobalSettings");
        if (self:HaveTrash()) then
            self:AddSettingsMenuItem(parent, "EmptyTrash");
        end
        L:SetContext(prevContext);
    elseif (itemName == "HideAll") then
        item:SetChecked(self.settings.hidden);
        item.Click = function(sender, args)
            self:SetHidden(not self.settings.hidden);
        end
    elseif (itemName == "GlobalSettings") then
        item.Click = function(sender, args)
            self.optionsPanel:ShowGlobalSettings();
        end
    elseif (itemName == "EmptyTrash") then
        item.Click = function(sender, args)
            self:EmptyTrash();
        end
    else
        -- Item is not handled here; send it to Group:AddSettingsMenuItem().
        parent:GetItems():RemoveAt(parent:GetItems():GetCount());
        Group.AddSettingsMenuItem(self, parent, itemName, true);
    end
end

function Manager:GetNearestGridPos(left, top)
    local gridSize = math.floor(0.5 + self.settings.gridSize * (self.settings.uiScale / 32));
    local displayBottom = Turbine.UI.Display:GetHeight() - 1;
    local centerLeft = math.floor(0.5 + Turbine.UI.Display:GetWidth() / 2 + gridSize / 2) + 3;
    local offsetLeft, offsetTop = left - centerLeft, displayBottom - top;
    local newLeft = centerLeft + math.floor(0.5 + offsetLeft / gridSize) * gridSize;
    local newTop = displayBottom - math.floor(0.5 + offsetTop / gridSize) * gridSize;
    return newLeft, newTop, gridSize;
end

function Manager:GetAnchorPosition(anchor)
    if (not self.anchorCoords) then
        local width, height = unpack (self.settings.displaySize);
        local center = math.floor(0.5 + width / 2);
        local middle = math.floor(0.5 + height / 2);
        self.anchorCoords = {
            TopLeft    = { 0, 0      }; TopCenter    = { center, 0      }; TopRight    = { width, 0      };
            MiddleLeft = { 0, middle }; MiddleCenter = { center, middle }; MiddleRight = { width, middle };
            BottomLeft = { 0, height }; BottomCenter = { center, height }; BottomRight = { width, height };
        }
    end
    return unpack(self.anchorCoords[anchor]);
end

function Manager:SetUnequipDestination(bagSlot)
    self.preferredBagSlot = bagSlot;
end

function Manager:Unequip(itemSlot)
    local lp = Turbine.Gameplay.LocalPlayer:GetInstance();
    local equippedItems = lp:GetEquipment();
    local item = equippedItems:GetItem(itemSlot);
    if (item ~= nil) then
        _G.Unequip(itemSlot, "*", self.preferredBagSlot);
    end
end

-- Some trivial wrappers retained for backward compatibility
function Manager:GetSkills()
    return self.watcher.GetSkillsInfo();
end
function Manager:SkillReady(iconID)
    return self.watcher.SkillReady(iconID);
end
function Manager:PlayerHasEffectCategory(category)
    return self.watcher.PlayerHasEffectCategory(category);
end
function Manager:TargetHasEffectCategory(category)
    return self.watcher.TargetHasEffectCategory(category);
end

function Manager:SetLanguage(language)
    self.settings.language = language;
    L:SetLanguage(language);
    if (language == Turbine.Language.Russian) then
        SetCyrillicEnabled(true);
        Turbine.UI.ContextMenu = Turbine.UI.ContextMenu2;
        Turbine.UI.MenuItem = Turbine.UI.MenuItem2;
    else
        Turbine.UI.ContextMenu = Turbine.UI.ContextMenu1;
        Turbine.UI.MenuItem = Turbine.UI.MenuItem1;
        SetCyrillicEnabled(false);
    end
    if (self.optionsPanel) then
        self.optionsPanel:Localize();
    end
    self:SaveSettings(false);
end

function Manager:SetSnapToGrid(snapToGrid)
    self.settings.snapToGrid = snapToGrid;
    if (snapToGrid) then
        self:SnapToGrid();
    end
    self:SaveSettings(false);
end

function Manager:ShowGrid(left, top, slotsRight, slotsDown)
    if (not self.gridDisplay) then
        self.gridDisplay = Turbine.UI.Window();
        self.gridDisplay:SetVisible(true);
        self.gridDisplay:SetMouseVisible(false);
        self.gridDisplay:SetBackground(resources.Icon.Grid);
        self.gridDisplay:SetStretchMode(0);
        self.gridDisplay:SetSize(slotsRight * self.settings.gridSize, slotsDown * self.settings.gridSize);
        self.gridDisplay:SetStretchMode(1);
        self.gridDisplay.slotsRight, self.gridDisplay.slotsDown = slotsRight, slotsDown;
    end
    left, top, scaledGridSize = self:GetNearestGridPos(left, top);
    self.gridDisplay.prevLeft, self.gridDisplay.prevTop = left, top;
    self.gridDisplay:SetSize(slotsRight * scaledGridSize, slotsDown * scaledGridSize);
    self.gridDisplay:SetPosition(left, top);
end

function Manager:HideGrid()
    if (self.gridDisplay) then
        self.gridDisplay:Close();
        self.gridDisplay = nil;
    end
end

function Manager:SetUIOpacity(opacity)
    self.settings.uiOpacity = opacity;
    self:Redraw(true);
    self:SaveSettings(false);
end

function Manager:SetUIScale(scale)
    self.settings.uiScale = scale;
    self:Redraw(true);
    self:SaveSettings(false);
end

function Manager:SetIncludees(includer, includees)
--Puts(tostring(includer) .. " is including " .. Serialize(includees));
    if (not includees) then
        -- Destroying bar.  Notify includers.
        self:NotifyIncluders(includer);
    end
    
    -- Delete the previous includes mapping (if any)
    local prevIncludes = self.includees[includer];
    if (prevIncludes) then
        for i = 1, #prevIncludes do
            local includee = prevIncludes[i];
            self.includers[includee][includer] = nil;
--Puts(tostring(includee) .. " will no longer notify " .. tostring(includer));
        end
    end
    
    -- Create the new includes mapping (if any)
    self.includees[includer] = includees;
    if (includees) then
        for i = 1, #includees, 1 do
            local includee = includees[i];
            if (not self.includers[includee]) then
                self.includers[includee] = {};
            end
            self.includers[includee][includer] = true;
--Puts(tostring(includee) .. " will notify " .. tostring(includer));
        end
    end
end

-- This function gets called when a sequence changes, so any bars that include that sequence can be notified to update.
function Manager:NotifyIncluders(includee)
--Puts(tostring(includee) .. " changed");
    if (not self.includers[includee]) then
        return;
    end
    self.notifyList = {};
    self:FindDependencies(includee);
    for barID in keys(self.notifyList) do
        if (barID ~= includee) then
            local bar = self.objects[barID];
            if (bar) then
--Puts("Notifying " .. tostring(barID));
                bar:ShortcutChanged();
            end
        end
    end
    self.notifyList = nil;
end

function Manager:FindDependencies(includee)
    if (self.includers[includee]) then
        for includer in keys(self.includers[includee]) do
            if (not self.notifyList[includer]) then
                self.notifyList[includer] = true;
                self:FindDependencies(includer);
            end
        end
    end
end

function Manager:AddEventGenerator(object, eventName)
    if (not self.eventGenerators[eventName]) then
        self.eventGenerators[eventName] = {};
    end
    table.insert(self.eventGenerators[eventName], object);
    local generatorID = #self.eventGenerators[eventName];
--Puts("Adding event generator " .. tostring(generatorID) .. " for event " .. Serialize(eventName) .. " from object " .. Serialize(object.settings.caption.text));
    return generatorID;
end

function Manager:RemoveEventGenerator(generatorID, eventName)
--Puts("Removing event generator " .. tostring(generatorID) .. " for event " .. Serialize(eventName));
    local eventGenerators = self.eventGenerators[eventName];
    if (eventGenerators) then
        local object = eventGenerators[generatorID];
--Puts("  (was from object " .. Serialize(object.settings.caption.text) .. ")");
        table.remove(eventGenerators, generatorID);
        if (#eventGenerators == 0) then
            self.eventGenerators[eventName] = nil;
        end
    else
--Puts("Not registered!");
    end
end

-- Returns a list of bars that generates the specified event
function Manager:GetEventGenerators(eventName)
    return self.eventGenerators[eventName];
end

-- Returns the names of all of the events generated by all bars
function Manager:GetEventNames()
    local eventNames = {};
    for k in keys(self.eventGenerators) do
        table.insert(eventNames, k);
    end
    return eventNames;
end

function Manager:GetEventCallbacks()
    return self.eventCallbacks;
end

function Manager:PropagateEvent(eventName)
    -- Enqueue event for processing at next Update cycle.  We use a hash table to avoid
    -- generating the same event multiple times in the same tick, which would be useless
    -- and could cause stack overflow.
    self.propagatingEvent[eventName] = true;
end

function Manager:Destroy()
    self:SetWantsPlayerEvents(false);
    Node.Destroy(self);
end

Compare with Previous | Blame


All times are GMT -5. The time now is 05:11 PM.


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