SequenceEditor = class(Turbine.UI.Lotro.Window);
-- Class variable keeping track of all SequenceEditor instances:
SequenceEditor.instances = {};
function SequenceEditor:Constructor(bar, settings)
Turbine.UI.Lotro.Window.Constructor(self);
SequenceEditor.instances[self] = true;
self.bar = bar;
self.manager = self.bar.manager;
self.settings = settings;
self.globals = self.manager.settings;
self:SetText(bar:GetName());
self:SetZOrder(2);
local minWidth = L:GetText("/SequenceEditor/MinWidth");
if (not self.settings.sequenceEditor) then
self.settings.sequenceEditor = {};
self.settings.sequenceEditor.defaultIcon = resources.BlankIcons[3];
end
if (self.settings.sequenceEditor.position) then
self:SetPosition(unpack(self.settings.sequenceEditor.position));
else
self:SetPosition(self.bar:GetPosition());
end
if (self.settings.sequenceEditor.size) then
self:SetSize(unpack(self.settings.sequenceEditor.size));
else
self:SetSize(minWidth, 187);
end
self.relatedSlots = {};
-- Enforce minimum size
self:SetMinimumWidth(minWidth);
if (self:GetWidth() < minWidth) then
self:SetWidth(minWidth);
end
self:SetMinimumHeight(142);
self:SetResizable(true);
self:Redraw();
self.horizontalLines = {};
AddCallback(self, "SizeChanged", function()
self.skipUpdates = 2;
self:SetWantsUpdates(true); --redraw during update cycle
end);
self:SelectSlot(nil);
self:SetVisible(true);
end
function SequenceEditor:UpdateBar(updateEventDirectory)
self.bar:ShortcutChanged();
self.manager:NotifyIncluders(self.bar:GetID());
if (updateEventDirectory) then
self.manager:SaveSettings(false, true);
end
end
function SequenceEditor:Activated()
-- Find the maximum Z order among all other Sequence Editors.
local maxZ = self:GetZOrder() - 1;
for editor in keys(SequenceEditor.instances) do
if (editor ~= self) then
local z = editor:GetZOrder();
if (z > maxZ) then
maxZ = z;
end
end
end
if (maxZ == 2147483647) then
-- Move all other SequenceEditors back to make room at the front.
for editor in keys(SequenceEditor.instances) do
editor:SetZOrder(editor:GetZOrder() - 1);
end
maxZ = maxZ - 1;
end
-- Move the newly-activated Sequence Editor to the front of the stack.
self:SetZOrder(maxZ + 1);
end
function SequenceEditor:SetText(barName)
local title = L:GetText("/SequenceEditor/Title");
title = string.gsub(title, "<name>", barName);
Turbine.UI.Lotro.Window.SetText(self, title);
end
function SequenceEditor:SaveSettings()
self.bar:SaveSettings();
end
function SequenceEditor:Redraw()
if (self.noRedraw) then
return;
end
local width, height = self:GetSize();
local xMargin = 16;
local topMargin = 35;
local bottomMargin = 42;
local minSlotTabCardWidth = L:GetNumber("/SequenceEditor/SlotTabCardWidth");
if (not self.slotTabCard) then
self.slotTabCard = Thurallor.UI.TabCard();
self.slotTabCard:SetParent(self);
self.slotTabCard:SetTabText(L:GetText("/SequenceEditor/SlotTab"));
-- When mouse reenters the area, redisplay the current slot in case options have changed.
self.slotTabCard.MouseEnter = function(ctl)
if (not ctl.mouseInside) then
ctl.mouseInside = true;
self:DisplaySlot(self.selectedSlot);
end
end
self.MouseEnter = function()
if (self.slotTabCard.mouseInside) then
local left, top = self.slotTabCard:GetMousePosition();
local width, height = self.slotTabCard:GetSize();
if ((left < 0) or (left >= width) or (top < 0) or (top >= height)) then
self.slotTabCard.mouseInside = false;
end
end
end
-- When slot properties card size changes, redisplay the current slot
self.slotTabCard.SizeChanged = function()
self:DisplaySlot(self.selectedSlot);
end
end
if (not self.sequenceTabCard) then
self.sequenceTabCard = Thurallor.UI.TabCard();
self.sequenceTabCard:SetTabText(L:GetText("/SequenceEditor/SequenceTab"));
self.slotPanel = Turbine.UI.Control();
self.sequenceTabCard:SetInteriorControl(self.slotPanel);
self.sequenceTabCard:SetInteriorAlignment(Turbine.UI.ContentAlignment.TopLeft);
-- When sequence card size changes, update the slot grid layout
self.sequenceTabCard.SizeChanged = function()
self:DeleteEmptyTrailingSlots();
local slotPanelWidth = self.sequenceTabCard:GetWidth() - 6;
self.slotSize = self.settings.slotSize + self.settings.slotSpacing;
self.slotsWide = math.floor((slotPanelWidth - 3) / self.slotSize);
self.slotsHigh = math.max(math.ceil(#self.settings.sequenceItemInfo / self.slotsWide) + 1, 2);
self:ExtendSequenceTo(self.slotsHigh * self.slotsWide);
self.slotPanel:SetSize(self.slotsWide * self.slotSize + 3, self.slotsHigh * self.slotSize + 3);
self.slotPanel:SetBackColor(self.bar.color);
-- Draw some quickslots
if (not self.sequenceItems) then
self.sequenceItems = {};
end
local s = 1;
for y = 1, self.slotsHigh, 1 do
for x = 1, self.slotsWide, 1 do
self:RebuildSlot(s, x, y);
s = s + 1;
end
end
end
end
if (not self.splitControl) then
self.splitControl = Thurallor.UI.SplitControl(Turbine.UI.Orientation.Horizontal, self.sequenceTabCard, self.slotTabCard);
self.splitControl:SetParent(self);
self.splitControl:SetPosition(xMargin, topMargin);
self.splitControl:SetSashSize(5);
self.splitControl:SetSpacing(3);
if (self.settings.sequenceEditor.sashPosition == nil) then
self.splitControl:SetSashPosition(1);
self.settings.sequenceEditor.sashPosition = self.splitControl:GetSashPosition();
else
self.splitControl:SetSashPosition(self.settings.sequenceEditor.sashPosition);
end
AddCallback(self.splitControl, "SashPositionChanged", function()
self.settings.sequenceEditor.sashPosition = self.splitControl:GetSashPosition();
self:SaveSettings();
end);
end
local splitControlWidth = width - (2 * xMargin);
self.splitControl:SetSize(splitControlWidth, height - (topMargin + bottomMargin));
self.sequenceTabCard:SizeChanged();
-- Enforce minimum width for slot properties card
self.splitControl:SetMaximumSashPosition((splitControlWidth - minSlotTabCardWidth - 5.5) / splitControlWidth);
-- Enforce minimum width for sequence card
self.splitControl:SetMinimumSashPosition(124 / splitControlWidth);
if (not self.instructions) then
self.instructions = Turbine.UI.Label();
self.instructions:SetParent(self);
self.instructions:SetMouseVisible(false);
self.instructions:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
self.instructions:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.instructions:SetMultiline(true);
self.instructions:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
self.instructions:SetText(L:GetText("/SequenceEditor/Instructions"));
end
self.instructions:SetSize(self:GetWidth() - 2 * xMargin, 30);
self.instructions:SetPosition(xMargin, self:GetHeight() - 40);
if (self.selectedSlot) then
self:SelectSlot(self.selectedSlot);
end
end
function SequenceEditor:SetMinimumHeight(height)
Turbine.UI.Window.SetMinimumHeight(self, height);
if (self:GetHeight() < height) then
self:SetHeight(height);
self.skipUpdates = 0;
self:SetWantsUpdates(true);
end
end
function SequenceEditor:Update()
if (self.skipUpdates > 0) then
self.skipUpdates = self.skipUpdates - 1;
else
self:Redraw();
self.settings.sequenceEditor.size = {self:GetSize()};
self:SaveSettings();
self:SetWantsUpdates(false);
end
end
function SequenceEditor:PositionChanged()
self.settings.sequenceEditor.position = {self:GetPosition()};
self:SaveSettings();
end
function SequenceEditor:MoveOntoScreen()
local screenWidth, screenHeight = Turbine.UI.Display:GetWidth(), Turbine.UI.Display:GetHeight();
local left, top = self:GetPosition();
local right, bottom = left + self:GetWidth(), top + self:GetHeight();
local deltaX, deltaY = 0, 0;
if (left < 0) then
deltaX = -left;
elseif (right > screenWidth) then
deltaX = screenWidth - right;
end
if (top < 0) then
deltaY = -top;
elseif (bottom > screenHeight) then
deltaY = screenHeight - bottom;
end
self:SetPosition(left + deltaX, top + deltaY);
end
function SequenceEditor:SetVisible(visible)
Turbine.UI.Lotro.Window.SetVisible(self, visible);
if (visible) then
self:MoveOntoScreen();
end
end
function SequenceEditor:DisplaySlot(s)
if (self.ItemMovedCallback) then
RemoveCallback(Thurallor.Utils.Watcher, "ItemMoved", self.ItemMovedCallback);
self.ItemMovedCallback = nil;
end
self.slotTabCard:SetInteriorControl(nil);
for _, control in pairs(self.horizontalLines) do
control:SetParent(nil);
end
self.horizontalLines = {};
self.slotTabCard:SetTabText(L:GetText("/SequenceEditor/SlotTab"));
if ((s == nil) or (s > #self.settings.sequenceItemInfo)) then
self.slotTabCard:SetVisible(false);
return;
else
self.slotTabCard:SetVisible(true);
end
self:ExtendSequenceTo(s);
local info = self.settings.sequenceItemInfo[s];
local slotTabCardWidth = self.slotTabCard:GetWidth();
-- Update slot tab caption and create properties container
self.slotTabCard:SetTabText(L:GetText("/SequenceEditor/SlotTab") .. " " .. tostring(s));
self.slotProperties = Turbine.UI.Control();
self.slotTabCard:SetInteriorControl(self.slotProperties);
self.slotProperties:SetWidth(slotTabCardWidth - 17);
local function UpdateIcon()
local center = math.floor((slotTabCardWidth - 20) / 2);
self.selectedIcon = self:BuildSlot(s, self.slotProperties, self.selectedIcon, center - 16, 10);
self.selectedIcon.numLabel:SetText(nil); -- don't display slot number overlay
self.selectedIcon.numLabel.MouseClick = nil; -- disable right-click menu
self.selectedIcon.numLabel.MouseDown = nil; -- disable dragging
self.selectedIcon:SetActionEnabled(true);
self.selectedIcon:SetAllowDrop(true);
self:RebuildSlot(s, nil, nil, true);
end
UpdateIcon();
-- Create a function for adding a centered line of text to the properties control
local AddLabel = function(top, text, color)
local label = Turbine.UI.Label();
label:SetParent(self.slotProperties);
label:SetPosition(0, top);
label:SetSize(slotTabCardWidth - 20, 16);
top = top + 16;
label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
label:SetFont(Turbine.UI.Lotro.Font.TrajanPro14);
label:SetForeColor(color);
label:SetMarkupEnabled(true);
label:SetText(text);
return top, label;
end
-- Create a function for adding a centered button to the properties control
local AddButton = function(top, width, text, font)
local button = Turbine.UI.Lotro.Button();
button:SetParent(self.slotProperties);
local left = math.floor((self.slotProperties:GetWidth() - width) / 2);
button:SetPosition(left, top + 2);
button:SetWidth(width);
if (font == nil) then
font = Turbine.UI.Lotro.Font.TrajanPro14;
end
button:SetFont(font);
button:SetText(text);
top = top + 2 + button:GetHeight();
return top, button;
end
-- Create a function for adding a centered text box to the properties control
local AddTextBox = function(top, text, color, resizable, textBoxSize)
local textBox = Turbine.UI.Lotro.TextBox();
textBox:SetParent(self.slotProperties);
textBox:SetPosition(0, top);
textBox:SetSize(slotTabCardWidth - 20, 20);
if (textBoxSize) then
textBox:SetHeight(textBoxSize[2]);
end
top = top + textBox:GetHeight();
textBox:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
textBox:SetMultiline(false);
textBox:SetFont(Turbine.UI.Lotro.Font.TrajanPro14);
textBox:SetForeColor(color);
textBox:SetText(text);
if (resizable) then
textBox.resizer = Thurallor.UI.Resizer(textBox);
textBox.resizer:SetMinimumHeight(20);
textBox.resizer:SetMinimumWidth(slotTabCardWidth - 20);
textBox.resizer:SetMaximumWidth(slotTabCardWidth - 20);
end
return top, textBox;
end
-- Create a function for adding a left-justified checkbox to the properties control
local AddCheckBox = function(top, text, color, checked)
local checkBox = Turbine.UI.Lotro.CheckBox();
checkBox:SetParent(self.slotProperties);
checkBox:SetPosition(0, top);
checkBox:SetSize(slotTabCardWidth - 20, 20);
top = top + 20;
checkBox:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
checkBox:SetFont(Turbine.UI.Lotro.Font.TrajanPro14);
checkBox:SetForeColor(color);
checkBox:SetText(text);
if (checked) then
checkBox:SetChecked(true);
end
return top, checkBox;
end
-- Create a function for adding a left-justified radio button to the properties control
local AddRadioButton = function(top, text, color, checked)
local button = Thurallor.UI.RadioButton(self.slotProperties, text, checked);
button:SetPosition(0, top);
button:SetSize(slotTabCardWidth - 20, 16);
top = top + 16;
button:SetFont(Turbine.UI.Lotro.Font.TrajanPro14);
button:SetForeColor(color);
return top, button;
end
-- Create a function for adding a drop-down listbox to the properties control
local AddDropDown = function(top, items, default)
local dropDown = Thurallor.UI.DropDown(items, default or items[1]);
dropDown:SetParent(self.slotProperties);
dropDown:SetPosition(0, top);
dropDown:SetWidth(slotTabCardWidth - 20);
top = top + dropDown:GetHeight();
return top, dropDown;
end
-- Create a function for adding a pull-down hierarchical menu to the properties control
local AddPullDown = function(top, items, default)
local pullDown = Thurallor.UI.PullDownMenu(items, default);
pullDown:SetParent(self.slotProperties);
pullDown:SetPosition(0, top);
pullDown:SetWidth(slotTabCardWidth - 20);
top = top + pullDown:GetHeight();
return top, pullDown;
end
-- Create a function for adding a horizontal scrollbar to the properties control
local AddSlider = function(top, minimum, maximum, value)
local scrollBar = Turbine.UI.Lotro.ScrollBar();
scrollBar:SetParent(self.slotProperties);
scrollBar:SetPosition(0, top);
scrollBar:SetSize(slotTabCardWidth - 20, 10);
scrollBar:SetMinimum(minimum);
scrollBar:SetMaximum(maximum);
scrollBar:SetSmallChange(1);
scrollBar:SetValue(value);
top = top + 10;
return top, scrollBar;
end
local AddHorizontalLine = function(top)
top = top + 32;
local control = Turbine.UI.Control()
control:SetBackColor(Turbine.UI.Color(1, 0.161, 0.188, 0.286));
control:SetParent(self.slotTabCard);
control:SetPosition(0, top + 8);
control:SetSize(slotTabCardWidth, 1);
table.insert(self.horizontalLines, control);
return top + 1, control;
end
-- Create a function for adding an expand/collapse section header to the properties control
local AddSection = function(top, text, color, expanded)
top = AddHorizontalLine(top);
local button = Turbine.UI.Control();
button:SetParent(self.slotProperties);
button:SetPosition(0, top);
button:SetSize(16, 16);
button:SetBlendMode(Turbine.UI.BlendMode.Overlay);
local label = Turbine.UI.Label();
label:SetParent(self.slotProperties);
label:SetPosition(18, top);
label:SetSize(slotTabCardWidth - 20 - 18, 16);
top = top + 16;
label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
label:SetFont(Turbine.UI.Lotro.Font.TrajanPro14);
label:SetForeColor(color);
label:SetMarkupEnabled(true);
label:SetText(text);
function button.MouseClick()
if (button.expanded) then
button:SetBackground(resources.Icon.Expand);
button.expanded = false;
DoCallbacks(button, "ExpandedChanged", false);
else
button:SetBackground(resources.Icon.Collapse);
button.expanded = true;
DoCallbacks(button, "ExpandedChanged", true);
end
end
label.MouseClick = button.MouseClick;
button.expanded = not expanded;
button:MouseClick();
return top, button;
end
-- Draw the caption
local top = 54;
if (info.class == "Turbine.UI.Lotro.Quickslot") then
top = AddLabel(top, L:GetText("/SequenceEditor/StandardQuickslot"), Turbine.UI.Color.PaleGoldenrod);
elseif ((info.class == "Turbine.UI.Control") or (info.class == "Turbine.UI.Lotro.EntityControl")) then
top = AddLabel(top, L:GetText("/SequenceEditor/SpecialSlot") .. ":", Turbine.UI.Color.PaleGoldenrod);
local label;
top, label = AddLabel(top, info.toolTip, Turbine.UI.Color.PaleGoldenrod);
-- Some German strings need line wrapping here.
label:SetHeight(32);
label:SetTextAlignment(Turbine.UI.ContentAlignment.TopCenter);
end
-- Draw the arguments
if (info.class == "Turbine.UI.Lotro.Quickslot") then
for key, value in pairs(Turbine.UI.Lotro.ShortcutType) do
if (info.type == value) then
top = AddLabel(top, L:GetText("/SequenceEditor/ShortcutType") .. ": " .. L:GetText("/SequenceEditor/ShortcutTypes/" .. key), Turbine.UI.Color.Goldenrod);
break;
end
end
if (info.type == Turbine.UI.Lotro.ShortcutType.Alias) then
-- Allow the user to specify the command
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/ChatCommand") .. ":", Turbine.UI.Color.Goldenrod);
local textBox;
top, textBox = AddTextBox(top, info.Data, Turbine.UI.Color.White);
textBox.TextChanged = function(sender, args)
local text = sender:GetText();
-- Remove carriage returns
local stripped = string.gsub(text, "\n", "");
if (stripped ~= text) then
sender:SetText(stripped);
text = stripped;
end
info.Data = text;
self:UpdateBar();
end
elseif (info.type == Turbine.UI.Lotro.ShortcutType.Item) then
local shortcut = Turbine.UI.Lotro.Shortcut(info.type, info.Data);
local item = shortcut:GetItem();
if (pcall(item.GetMaxStackSize, item)) then
if (item:GetMaxStackSize() == 1) then -- equippable
-- Allow the user to specify immediate advance, or wait-for-equip
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/AdvanceToNextSlot") .. ":", Turbine.UI.Color.Goldenrod);
local button1, button2;
top, button1 = AddRadioButton(top, L:GetText("/SequenceEditor/WithLeftClick"), Turbine.UI.Color.DarkGoldenrod, (not info.advanceEvent));
top, button2 = AddRadioButton(top, L:GetText("/SequenceEditor/WhenItemEquipped"), Turbine.UI.Color.DarkGoldenrod, info.advanceEvent);
Thurallor.UI.RadioButton.LinkPeers({button1, button2});
button1.Clicked = function()
info.advanceEvent = nil;
self:UpdateBar();
end
button2.Clicked = function()
info.advanceEvent = "ItemEquipped";
self:UpdateBar();
end
end
-- else "invalid entity" -- probably an item that no longer exists in your inventory
end
elseif (info.type == Turbine.UI.Lotro.ShortcutType.Skill) then
local skillName = self.selectedIcon:GetSkillName();
if (skillName) then
-- Allow the user to specify immediate advance, or wait-for-execute
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/AdvanceToNextSlot") .. ":", Turbine.UI.Color.Goldenrod);
local button1, button2;
top, button1 = AddRadioButton(top, L:GetText("/SequenceEditor/WithLeftClick"), Turbine.UI.Color.DarkGoldenrod, (not info.advanceEvent));
top, button2 = AddRadioButton(top, L:GetText("/SequenceEditor/WhenSkillExecuted"), Turbine.UI.Color.DarkGoldenrod, info.advanceEvent);
Thurallor.UI.RadioButton.LinkPeers({button1, button2});
if (button2:IsChecked()) then
top, checkBox = AddCheckBox(top, L:GetText("/SequenceEditor/UseAlternateDetectMethod"), Turbine.UI.Color.DarkGoldenrod, info.executeDetectByChat);
checkBox:SetLeft(16); -- indent
checkBox:SetSize(checkBox:GetWidth() - 16, 28);
top = top + 8;
checkBox.CheckedChanged = function(cb)
info.executeDetectByChat = cb:IsChecked();
self:UpdateBar();
end
end
button1.Clicked = function()
info.advanceEvent = nil;
self:DisplaySlot(self.selectedSlot);
self:UpdateBar();
end
button2.Clicked = function()
info.advanceEvent = "SkillExecuted";
self:DisplaySlot(self.selectedSlot);
self:UpdateBar();
end
-- else this skill isn't in moebius92's SkillData database
end
end
elseif (info.class == "Turbine.UI.Lotro.EntityControl") then
if (info.type == "SelectTarget") then
local getTargetFuncs = {
-- args are: watcher, localPlayer, info.raidMemberName, info.number
["Self"] = "local _, player = ...; return player;";
["CurrentTarget"] = "local watcher = ...; return watcher:GetPlayerTarget();";
["CurrentTargetsTarget"] = "local watcher = ...; return watcher:GetTargetsTarget();";
["Pet"] = "local _, player = ...; return player:GetPet();";
["RaidLeader"] = "local watcher = ...; local party = watcher.GetParty(); return (party and party:GetLeader());";
["RaidMemberByName"] = "local watcher, _, name = ...; return watcher.GetPartyMemberByName(name, true);";
["NextRaidMember"] = "local watcher = ...; return watcher.GetNextPartyMember();";
["FirstAssistee"] = "local watcher = ...; return watcher.GetPartyAssistTarget(1);";
["SecondAssistee"] = "local watcher = ...; return watcher.GetPartyAssistTarget(2);";
["ThirdAssistee"] = "local watcher = ...; return watcher.GetPartyAssistTarget(3);";
["FourthAssistee"] = "local watcher = ...; return watcher.GetPartyAssistTarget(4);";
["NextAssistee"] = "local watcher = ...; return watcher.GetNextAssistTarget();";
["FirstAssisteesTarget"] = "local watcher = ...; local target = watcher.GetPartyAssistTarget(1); return (target and target:GetTarget());";
["SecondAssisteesTarget"] = "local watcher = ...; local target = watcher.GetPartyAssistTarget(2); return (target and target:GetTarget());";
["ThirdAssisteesTarget"] = "local watcher = ...; local target = watcher.GetPartyAssistTarget(3); return (target and target:GetTarget());";
["FourthAssisteesTarget"] = "local watcher = ...; local target = watcher.GetPartyAssistTarget(4); return (target and target:GetTarget());";
["NextAssisteesTarget"] = "local watcher = ...; local target = watcher.GetNextAssistTarget(); return (target and target:GetTarget());";
["SavedTarget"] = "local watcher, _, _, number = ...; return watcher.GetSavedTarget(number);";
};
-- Allow the user to choose the target category
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/Target") .. ":", Turbine.UI.Color.Goldenrod);
local currentItem = info.target;
info.getTargetFunc = getTargetFuncs[currentItem];
local prevContext;
prevContext = L:SetContext("/SequenceEditor/TargetMenu");
local targs, localTargs = L:GetItems(), {};
for targ in values(targs) do
table.insert(localTargs, L:GetText(targ));
end
table.sort(localTargs);
local dropDown;
top, dropDown = AddDropDown(top, localTargs, L:GetText(currentItem));
dropDown:SetAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
dropDown:SetExpandedWidth(math.max(220, slotTabCardWidth - 20));
dropDown.ItemChanged = function(sender, args)
for targ in values(targs) do
if (L:GetText(targ) == args.Text) then
info.target = targ;
self:DisplaySlot(self.selectedSlot);
self:UpdateBar();
break;
end
end
end
-- If "Raid Member" is chosen, allow the user to select raid member by name
if (info.target == "RaidMemberByName") then
info.raidMemberName = info.raidMemberName or "";
local textBox;
top, textBox = AddTextBox(top, info.raidMemberName, Turbine.UI.Color.White);
textBox.TextChanged = function(sender, args)
local text = sender:GetText();
-- Remove carriage returns
local stripped = string.gsub(text, "\n", "");
if (stripped ~= text) then
sender:SetText(stripped);
text = stripped;
end
info.raidMemberName = text;
self:UpdateBar();
end
-- Allow the user to easily select the currently-targeted raid member, if not already selected
local selectedMember = Thurallor.Utils.Watcher.TargetIsPartyMember();
local selectedMemberName = selectedMember and selectedMember:GetName();
if (selectedMemberName and (selectedMemberName:lower() ~= info.raidMemberName:lower())) then
top = textBox:GetTop();
local button1, button2;
top, button1 = AddRadioButton(top, selectedMemberName, Turbine.UI.Color.DarkGoldenrod);
_, button2 = AddRadioButton(top, "", Turbine.UI.Color.DarkGoldenrod, true);
function button1.Clicked()
info.raidMemberName = selectedMemberName;
self:UpdateBar();
end
Thurallor.UI.RadioButton.LinkPeers({button1, button2});
textBox:SetZOrder(1);
textBox:SetTop(button2:GetTop());
textBox:SetLeft(12);
textBox:SetWidth(textBox:GetWidth() - 12);
function textBox.FocusGained()
button2:MouseClick();
textBox:TextChanged();
end
top = top + textBox:GetHeight();
end
-- If "Saved target" is chosen, allow the user to select saved target number
elseif (info.target == "SavedTarget") then
info.number = info.number or 1;
local numbers = {};
for n = 1, 10 do
table.insert(numbers, tostring(n));
end
local dropDown;
top, dropDown = AddDropDown(top, numbers, tostring(info.number));
dropDown:SetWidth(40);
dropDown:SetLeft(math.floor((slotTabCardWidth - 20) / 2) - 20);
dropDown.ItemChanged = function(sender, args)
info.number = tonumber(args.Text);
self:UpdateBar();
end
end
-- Evaluate the target selection and display the name of the targeted entity
top, self.targetLabel = AddLabel(top, "", Turbine.UI.Color.Goldenrod);
self.targetLabel.Update = function(obj)
local entity = loadstring(info.getTargetFunc)(self.manager.watcher, self.manager.player, info.raidMemberName, info.number);
if (entity) then
obj:SetText("(" .. entity:GetName() .. ")");
obj:SetForeColor(Turbine.UI.Color.Lime);
else
obj:SetText(L:GetText("/SequenceEditor/NoTarget"));
obj:SetForeColor(Turbine.UI.Color.Red);
end
end
self.targetLabel:SetWantsUpdates(true);
end
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/AdvanceToNextSlot") .. ":", Turbine.UI.Color.Goldenrod);
elseif (info.class == "Turbine.UI.Control") then
if (info.type == "Delay") then
-- Allow the user to specify the delay period
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/DelayPeriod") .. ":", Turbine.UI.Color.Goldenrod);
local textBox;
_, textBox = AddTextBox(top, info.delay, Turbine.UI.Color.White);
textBox:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleRight);
local center = math.floor((slotTabCardWidth - 20) / 2);
textBox:SetWidth(50);
textBox:SetLeft(center - 2 - 50);
top, label = AddLabel(top + 2, L:GetText("/SequenceEditor/Seconds"), Turbine.UI.Color.DarkGoldenrod);
label:SetLeft(center + 2);
label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
textBox.TextChanged = function(sender, args)
info.delay = tonumber(sender:GetText());
info.action = "local item, s = ...; item.bar:StartSlotCooldown(s, " .. tostring(info.delay):gsub(",", ".") .. ");";
self:UpdateBar();
end
elseif (info.type == "SaveTarget") then
-- Allow the user to specify the target number
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/TargetNumber") .. ":", Turbine.UI.Color.Goldenrod);
local numbers = {};
for n = 1, 10 do
table.insert(numbers, tostring(n));
end
local dropDown;
top, dropDown = AddDropDown(top, numbers, tostring(info.number));
dropDown:SetWidth(40);
dropDown:SetLeft(math.floor((slotTabCardWidth - 20) / 2) - 20);
dropDown.ItemChanged = function(sender, args)
info.number = tonumber(args.Text);
info.action = "Thurallor.Utils.Watcher.SaveTarget(" .. info.number .. ");";
self:UpdateBar();
end
top = top + 16; -- skip a line
_, label = AddLabel(top, L:GetText("/SequenceEditor/SaveTargetWarning"), Turbine.UI.Color.Silver);
label:SetTextAlignment(Turbine.UI.ContentAlignment.LeftCenter);
label:SetHeight(14 * L:GetText("/SequenceEditor/SaveTargetWarningHeight"));
top = top + label:GetHeight();
elseif (info.type == "GenerateEvent") then
-- Allow the user to specify the event name
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/EventName") .. ":", Turbine.UI.Color.Goldenrod);
local textBox;
top, textBox = AddTextBox(top, info.eventName, Turbine.UI.Color.White);
textBox.TextChanged = function(sender, args)
local text = sender:GetText();
-- Remove carriage returns
local stripped = string.gsub(text, "\n", "");
if (stripped ~= text) then
sender:SetText(stripped);
text = stripped;
end
info.eventName = text;
self:UpdateBar(true);
end
elseif (info.type == "SetUnequipDestination") then
-- Temporary method to allow users to discover bag slot numbers.
self.ItemMovedCallback = function(sender, args)
local str = L:GetText("/ItemMoved");
str = string.gsub(str, "<source>", tostring(args.OldIndex));
str = string.gsub(str, "<destination>", tostring(args.NewIndex));
Puts(str);
end
AddCallback(Thurallor.Utils.Watcher, "ItemMoved", self.ItemMovedCallback);
-- Allow the user to specify the desired bag slot
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/PreferredBagSlot") .. ":", Turbine.UI.Color.Goldenrod);
top, textBox = AddTextBox(top, tostring(info.bagSlot), Turbine.UI.Color.White);
textBox:SetFont(Turbine.UI.Lotro.Font.Verdana14);
textBox.TextChanged = function(sender, args)
local series = Thurallor.Utils.Series(sender:GetText())
info.bagSlot = series:tostring();
self:UpdateBar();
end
elseif (string.match(info.type, "^Remove ")) then
-- Allow the user to edit choose the item to be unequipped
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/EquipmentType") .. ":", Turbine.UI.Color.Goldenrod);
local currentItem = string.sub(info.type, 8);
local prevContext = L:SetContext("/SequenceEditor/EquipmentSlots");
local eqSlots, localEqSlots = L:GetItems(), {};
for slot in values(eqSlots) do
table.insert(localEqSlots, L:GetText(slot));
end
table.sort(localEqSlots);
local dropDown;
top, dropDown = AddDropDown(top, localEqSlots, L:GetText(currentItem));
dropDown:SetExpandedWidth(math.max(220, slotTabCardWidth - 20));
dropDown.ItemChanged = function(sender, args)
for slot in values(eqSlots) do
if (L:GetText(slot) == args.Text) then
self:DeleteSlot(s);
self:CreateRemovalSlot(s, slot, info.bagSlot);
break;
end
end
end
elseif (info.type == "If") then
-- Allow the user to specify the conditional expression
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/Condition") .. ":", Turbine.UI.Color.Goldenrod);
local prevContext = L:SetContext("/SequenceEditor/ConditionTypes");
local condInfo = {
["Always"] = { expr = "return true;" };
["Never"] = { expr = "return false;" };
["Random"] = { arg = "x";
expr = "return (math.random() < (<x> / 100));" };
["SkillReady"] = { arg = "skillName";
expr = "return (Thurallor.Utils.Watcher.SkillReady(<skillName>));" };
["SkillNotReady"] = { arg = "skillName";
expr = "return (not Thurallor.Utils.Watcher.SkillReady(<skillName>));" };
["SkillUsable"] = { arg = "skillName";
expr = "return (Thurallor.Utils.Watcher.SkillUsable(<skillName>));" };
["SkillNotUsable"] = { arg = "skillName";
expr = "return (not Thurallor.Utils.Watcher.SkillUsable(<skillName>));" };
["SkillTrained"] = { arg = "skillName";
expr = "return (Thurallor.Utils.Watcher.SkillTrained(<skillName>));" };
["SkillNotTrained"] = { arg = "skillName";
expr = "return (not Thurallor.Utils.Watcher.SkillTrained(<skillName>));" };
["PlayerEffectDisease"] = { expr = "return Thurallor.Utils.Watcher.PlayerHasEffectCategory(Turbine.Gameplay.EffectCategory.Disease);" };
["PlayerEffectDiseaseCurable"] = { expr = "return Thurallor.Utils.Watcher.PlayerHasEffectCategory(Turbine.Gameplay.EffectCategory.Disease, true);" };
["PlayerEffectFear"] = { expr = "return Thurallor.Utils.Watcher.PlayerHasEffectCategory(Turbine.Gameplay.EffectCategory.Fear);" };
["PlayerEffectFearCurable"] = { expr = "return Thurallor.Utils.Watcher.PlayerHasEffectCategory(Turbine.Gameplay.EffectCategory.Fear, true);" };
["PlayerEffectPoison"] = { expr = "return Thurallor.Utils.Watcher.PlayerHasEffectCategory(Turbine.Gameplay.EffectCategory.Poison);" };
["PlayerEffectPoisonCurable"] = { expr = "return Thurallor.Utils.Watcher.PlayerHasEffectCategory(Turbine.Gameplay.EffectCategory.Poison, true);" };
["PlayerEffectWound"] = { expr = "return Thurallor.Utils.Watcher.PlayerHasEffectCategory(Turbine.Gameplay.EffectCategory.Wound);" };
["PlayerEffectWoundCurable"] = { expr = "return Thurallor.Utils.Watcher.PlayerHasEffectCategory(Turbine.Gameplay.EffectCategory.Wound, true);" };
["PlayerEffectOther"] = { arg = "effect";
expr = "return Thurallor.Utils.Watcher.PlayerHasEffect(<effect>);" };
["NotPlayerEffectOther"] = { arg = "effect";
expr = "return (not Thurallor.Utils.Watcher.PlayerHasEffect(<effect>));" };
["TargetEffectDisease"] = { expr = "return Thurallor.Utils.Watcher.TargetHasEffectCategory(Turbine.Gameplay.EffectCategory.Disease);" };
["TargetEffectFear"] = { expr = "return Thurallor.Utils.Watcher.TargetHasEffectCategory(Turbine.Gameplay.EffectCategory.Fear);" };
["TargetEffectPoison"] = { expr = "return Thurallor.Utils.Watcher.TargetHasEffectCategory(Turbine.Gameplay.EffectCategory.Poison);" };
["TargetEffectWound"] = { expr = "return Thurallor.Utils.Watcher.TargetHasEffectCategory(Turbine.Gameplay.EffectCategory.Wound);" };
["TargetEffectOther"] = { arg = "effect";
expr = "return Thurallor.Utils.Watcher.TargetHasEffect(<effect>);" };
["NotTargetEffectOther"] = { arg = "effect";
expr = "return (not Thurallor.Utils.Watcher.TargetHasEffect(<effect>));" };
["PlayerInCombat"] = { expr = "local _, player = ...; return player:IsInCombat();" };
["PlayerOutOfCombat"] = { expr = "local _, player = ...; return not player:IsInCombat();" };
["PlayerMorale < x"] = { arg = "x";
expr = "local _, player = ...; return (player:GetMorale() < <x>)" };
["PlayerMorale < x%"] = { arg = "x";
expr = "local _, player = ...; return (player:GetMorale() < player:GetBaseMaxMorale() * <x> / 100)" };
["PlayerMorale > x"] = { arg = "x";
expr = "local _, player = ...; return (player:GetMorale() > <x>)" };
["PlayerMorale > x%"] = { arg = "x";
expr = "local _, player = ...; return (player:GetMorale() > player:GetBaseMaxMorale() * <x> / 100)" };
["TargetIsFellow"] = { expr = "local _, player = ...; return Thurallor.Utils.Watcher.TargetIsPartyMember();" };
["TargetNotFellow"] = { expr = "local _, player = ...; return not Thurallor.Utils.Watcher.TargetIsPartyMember();" };
["TargetMorale < x"] = { arg = "x";
expr = "local _, player = ...; local target = player:GetTarget(); return (target and target.GetMorale and (target:GetMorale() < <x>))" };
["TargetMorale < x%"] = { arg = "x";
expr = "local _, player = ...; local target = player:GetTarget(); return (target and target.GetMorale and (target:GetMorale() < target:GetBaseMaxMorale() * <x> / 100))" };
["TargetMorale > x"] = { arg = "x";
expr = "local _, player = ...; local target = player:GetTarget(); return (target and target.GetMorale and (target:GetMorale() > <x>))" };
["TargetMorale > x%"] = { arg = "x";
expr = "local _, player = ...; local target = player:GetTarget(); return (target and target.GetMorale and (target:GetMorale() > target:GetBaseMaxMorale() * <x> / 100))" };
["PlayerPower < x"] = { arg = "x";
expr = "local _, player = ...; return (player:GetPower() < <x>)" };
["PlayerPower < x%"] = { arg = "x";
expr = "local _, player = ...; return (player:GetPower() < player:GetBaseMaxPower() * <x> / 100)" };
["PlayerPower > x"] = { arg = "x";
expr = "local _, player = ...; return (player:GetPower() > <x>)" };
["PlayerPower > x%"] = { arg = "x";
expr = "local _, player = ...; return (player:GetPower() > player:GetBaseMaxPower() * <x> / 100)" };
["TargetPower < x"] = { arg = "x";
expr = "local _, player = ...; local target = player:GetTarget(); return (target and target.GetPower and (target:GetPower() < <x>))" };
["TargetPower < x%"] = { arg = "x";
expr = "local _, player = ...; local target = player:GetTarget(); return (target and target.GetPower and (target:GetPower() < target:GetBaseMaxPower() * <x> / 100))" };
["TargetPower > x"] = { arg = "x";
expr = "local _, player = ...; local target = player:GetTarget(); return (target and target.GetPower and (target:GetPower() > <x>))" };
["TargetPower > x%"] = { arg = "x";
expr = "local _, player = ...; local target = player:GetTarget(); return (target and target.GetPower and (target:GetPower() > target:GetBaseMaxPower() * <x> / 100))" };
["ChampionPlayerFervor < x"] = { arg = "x";
expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.GetFervor and (attribs:GetFervor() < <x>);" };
["ChampionPlayerFervor > x"] = { arg = "x";
expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.GetFervor and (attribs:GetFervor() > <x>);" };
["BeorningPlayerWrath < x%"] = { arg = "x";
expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.GetWrath and (attribs:GetWrath() < <x>);" };
["BeorningPlayerWrath > x%"] = { arg = "x";
expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.GetWrath and (attribs:GetWrath() > <x>);" };
["BeorningInBearForm"] = { expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.IsInBearForm and attribs:IsInBearForm();" };
["BeorningNotInBearForm"] = { expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.IsInBearForm and (not attribs:IsInBearForm());" };
["HunterFocus < x"] = { arg = "x";
expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.GetFocus and (attribs:GetFocus() < <x>);" };
["HunterFocus > x"] = { arg = "x";
expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.GetFocus and (attribs:GetFocus() > <x>);" };
["BrawlerMettle < x"] = { arg = "x";
expr = "return (Thurallor.Utils.Watcher.GetPlayerMettle() < <x>);" };
["BrawlerMettle > x"] = { arg = "x";
expr = "return (Thurallor.Utils.Watcher.GetPlayerMettle() > <x>);" };
["RunekeeperAttunement < x"] = { arg = "x";
expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.GetAttunement and (attribs:GetAttunement() < <x>);" };
["RunekeeperAttunement > x"] = { arg = "x";
expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.GetAttunement and (attribs:GetAttunement() > <x>);" };
["RunekeeperCharged"] = { expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.IsCharged and attribs:IsCharged();" };
["RunekeeperNotCharged"] = { expr = "local _, player = ...; local attribs = player:GetClassAttributes(); return attribs.IsCharged and (not attribs:IsCharged());" };
["ItemEquipped"] = { arg = { "eqItemName", "eqSlot" };
expr = "return IsEquipped(<eqItemName>, <eqSlot>);" };
["ItemNotEquipped"] = { arg = { "eqItemName", "eqSlot" };
expr = "return (not IsEquipped(<eqItemName>, <eqSlot>));" };
["ItemQuantity > x"] = { arg = { "stackItemName", "x" };
expr = "return (Thurallor.Utils.Watcher.GetItemQuantity(<stackItemName>) > <x>);" };
["ItemQuantity < x"] = { arg = { "stackItemName", "x" };
expr = "return (Thurallor.Utils.Watcher.GetItemQuantity(<stackItemName>) < <x>);" };
["StanceSelected"] = { arg = "stance";
expr = "return (Thurallor.Utils.Watcher.GetStance() == <stance>);" };
["LuaScript"] = { arg = "script";
expr = "<script>" };
};
local condNames = {};
for k in keys(condInfo) do
table.insert(condNames, L:GetText(k));
end
table.sort(condNames);
local function SwitchArgs(arg)
if (not info.condArgs) then
info.condArgs = {};
end
if (not arg) then
info.condArgs = nil;
elseif ((arg == "x") and not info.condArgs.x) then
info.condArgs = { x = 0 };
elseif ((arg == "skillName") and not info.condArgs.skillName) then
info.condArgs = { skillName = "nil" };
elseif ((arg == "skill") and not info.condArgs.skill) then
info.condArgs = { skill = 0 };
elseif ((arg == "effect") and not info.condArgs.effect) then
info.condArgs = { effect = "nil" };
elseif ((arg == "stance") and not info.condArgs.stance) then
info.condArgs = { stance = 0 };
elseif ((arg == "script") and not info.condArgs.script) then
if (info.condExpr) then
local script = info.condExpr;
if (info.condArgs) then
for name, value in pairs(info.condArgs) do
script = string.gsub(script, "<" .. name .. ">", tostring(value));
end
end
info.condArgs = { script = script };
else
info.condArgs = { script = "return true;" };
end
elseif (type(arg) == "table") then
-- Multiple args
local prevArgs = info.condArgs;
info.condArgs = {};
for argName in values(arg) do
if (prevArgs[argName]) then
info.condArgs[argName] = prevArgs[argName];
end
if ((argName == "eqItemName") and not info.condArgs.eqItemName) then
info.condArgs.eqItemName = "nil";
elseif ((argName == "stackItemName") and not info.condArgs.stackItemName) then
info.condArgs.stackItemName = "nil";
elseif ((argName == "eqSlot") and not info.condArgs.eqSlot) then
info.condArgs.eqSlot = "nil";
elseif ((argName == "x") and not info.condArgs.x) then
info.condArgs.x = 0;
end
end
end
info.condExpr = condInfo[info.condName].expr;
end
local dropDown;
top, dropDown = AddDropDown(top, condNames, L:GetText(info.condName));
dropDown:SetExpandedWidth(math.max(300, slotTabCardWidth - 20));
dropDown:SetAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
dropDown.ItemChanged = function(sender, args)
local prevContext = L:SetContext("/SequenceEditor/ConditionTypes");
info.condName = L:GetItem(args.Text);
L:SetContext(prevContext);
local arg = condInfo[info.condName].arg;
SwitchArgs(arg);
self:UpdateBar();
self:DisplaySlot(self.selectedSlot);
end
SwitchArgs(condInfo[info.condName].arg);
L:SetContext(prevContext);
-- Display additional arguments depending on the current condition type
if (info.condArgs) then
if (info.condArgs.stackItemName) then
local function GetStackableItems()
local hash = {};
local backpack = self.manager.player:GetBackpack();
for i = 1, backpack:GetSize(), 1 do
local item = backpack:GetItem(i);
if (item and (item:GetMaxStackSize() > 1)) then
hash[item:GetName()] = true;
end
end
local items = {};
for key in sorted_keys(hash) do
table.insert(items, key);
end
return items;
end
local stackItemNames = GetStackableItems();
local dropDown;
top, dropDown = AddDropDown(top + 2, stackItemNames);
dropDown:SetExpandedWidth(math.max(400, slotTabCardWidth - 20));
dropDown.ItemChanged = function(sender, args)
info.condArgs.stackItemName = "\"" .. args.Text .. "\"";
self:UpdateBar();
end
dropDown:SetParent(self.slotProperties);
local name = info.condArgs.stackItemName;
if (name == "nil") then
name = stackItemNames[1];
if (name) then
dropDown:ItemChanged({Text = name});
end
else
dropDown:SetText(string.sub(name, 2, -2));
end
end
if (info.condArgs.x) then
local _, xLabel = AddLabel(top + 3, "", Turbine.UI.Color.DarkGoldenrod);
xLabel:SetWidth(30);
xLabel:SetFont(Turbine.UI.Lotro.Font.Verdana16);
xLabel:SetText("x =");
local xField;
top, xField = AddTextBox(top + 2, "", Turbine.UI.Color.White);
xField:SetLeft(30);
xField:SetWidth(slotTabCardWidth - 50);
xField.TextChanged = function(sender)
info.condArgs.x = tonumber(sender:GetText());
if (not info.condArgs.x) then
info.condArgs.x = 0;
end
self:UpdateBar();
end
xField:SetText(info.condArgs.x);
xLabel:SetParent(self.slotProperties);
xField:SetText(tostring(info.condArgs.x));
xField:SetParent(self.slotProperties);
end
if (info.condArgs.skillName) then
local trainedSkills = Thurallor.Utils.Watcher.GetTrainedSkillsInfo();
local skills = Thurallor.Utils.Watcher.GetSkillsInfo(true);
local displayNames = {};
local untrained = L:GetText("/SequenceEditor/Untrained");
for n = 1, #skills.names, 1 do
local name = skills.names[n];
local displayName = name;
local altNames = Turbine.Gameplay.Skill.GetAltNames(name);
if (altNames) then
displayName = displayName .. " / " .. altNames;
end
if (not trainedSkills.byName[name]) then
displayName = displayName .. " (" .. untrained .. ")";
end
table.insert(displayNames, displayName);
end
local dropDown;
top, dropDown = AddDropDown(top + 2, displayNames);
dropDown:SetExpandedWidth(math.max(300, slotTabCardWidth - 20));
dropDown.ItemChanged = function(sender, args)
local text = args.Text:gsub(" %(" .. L:GetText("/SequenceEditor/Untrained") .. "%)$", "");
text = text:gsub(" / .*$", "");
dropDown:SetText(text);
info.condArgs.skillName = "\"" .. text .. "\"";
self:UpdateBar();
end
dropDown:SetParent(self.slotProperties);
local skillName = info.condArgs.skillName;
if (skillName == "nil") then
skillName = skills.names[1]; -- first one always will be a trained skill
dropDown:ItemChanged({Text = skillName});
else
skillName = string.gsub(skillName, "\"", "")
dropDown:SetText(skillName);
end
end
if (info.condArgs.effect) then
local effectNames = Thurallor.Utils.Watcher.GetKnownEffectNames();
if (#effectNames >= 1) then
table.insert(effectNames, 1, L:GetText("/SequenceEditor/ConditionTypes/OtherEffect"));
local dropDown;
top, dropDown = AddDropDown(top + 2, effectNames);
dropDown:SetExpandedWidth(math.max(300, slotTabCardWidth - 20));
function dropDown.ItemChanged(ctl, args)
local effectNames = Thurallor.Utils.Watcher.GetKnownEffectNames();
local other = L:GetText("/SequenceEditor/ConditionTypes/OtherEffect");
table.insert(effectNames, 1, other);
if (args.Text == other) then
local ok = L:GetText("/ExportWindow/OK");
local window = Alert(other,
L:GetText("/SequenceEditor/ConditionTypes/OtherEffectHelp"),
ok);
CenterWindow(window);
window.label:SetFont(Turbine.UI.Lotro.Font.Verdana14);
window.label:SetText(window.label:GetText());
window.buttons[ok].Click = function()
window:Close();
end
dropDown:SetText(string.gsub(info.condArgs.effect, "\"", ""));
elseif (not args.Text) then
return;
else
info.condArgs.effect = "\"" .. args.Text .. "\"";
self:UpdateBar();
end
end
dropDown:SetParent(self.slotProperties);
local effect = info.condArgs.effect;
if (effect == "nil") then
effect = effectNames[2];
dropDown:SetText(effect);
dropDown:ItemChanged({Text = effect});
else
effect = string.gsub(effect, "\"", "")
dropDown:SetText(effect);
end
end
end
if (info.condArgs.stance) then
local stanceNames = L:GetSortedTexts("/SequenceEditor/Stance");
local dropDown;
top, dropDown = AddDropDown(top + 2, stanceNames);
dropDown:SetExpandedWidth(math.max(500, slotTabCardWidth - 20));
function dropDown.ItemChanged(ctl, args)
local prevContext = L:SetContext("/SequenceEditor/Stance");
local stance = L:GetItem(args.Text);
info.condArgs.stance = tonumber(stance);
self:UpdateBar();
end
dropDown:SetParent(self.slotProperties);
local stance = info.condArgs.stance;
dropDown:SetText(L:GetText("/SequenceEditor/Stance/" .. tostring(stance)));
end
if (info.condArgs.script) then
top, self.scriptBox = AddTextBox(top + 2, info.condArgs.script, Turbine.UI.Color.White, true, self.settings.sequenceEditor.scriptBoxSize);
self.scriptBox:SetZOrder(1);
self.scriptBox:SetMultiline(true);
self.scriptBox:SetTextAlignment(Turbine.UI.ContentAlignment.TopLeft);
self.scriptBox:SetFont(Turbine.UI.Lotro.Font.Verdana12);
self.scriptBox:SetText(info.condArgs.script);
self.scriptBox:SetWantsUpdates(true);
function self.scriptBox.Resized()
self.settings.sequenceEditor.scriptBoxSize = {self.scriptBox:GetSize()};
self:SaveSettings();
self:DisplaySlot(self.selectedSlot);
end
self.scriptBox.Update = function(sender)
local script = sender:GetText();
if (script ~= sender.prevText) then
local success, errorMsg = loadstring(script, "Slot " .. tostring(s));
if (success) then
self.scriptBox:SetBackColor(Turbine.UI.Color.Black);
info.condArgs.script = script;
self:UpdateBar();
else
self.scriptBox:SetBackColor(Turbine.UI.Color.Red);
end
end
sender.prevText = script;
end
end
if (info.condArgs.eqItemName) then
local function GetEquippableItems()
local hash = { [L:GetText("/SequenceEditor/AnyEqItem")] = true };
for container in values({ self.manager.player:GetBackpack(), self.manager.player:GetEquipment() }) do
for i = 1, container:GetSize(), 1 do
local item = container:GetItem(i);
if (item and (item:GetMaxStackSize() == 1)) then
hash[item:GetName()] = true;
end
end
end
local items = {};
for key in sorted_keys(hash) do
table.insert(items, key);
end
return items;
end
local eqItemNames = GetEquippableItems();
local anyItem = L:GetText("/SequenceEditor/AnyEqItem");
local dropDown;
top, dropDown = AddDropDown(top + 2, eqItemNames);
dropDown:SetExpandedWidth(math.max(400, slotTabCardWidth - 20));
dropDown.ItemChanged = function(sender, args)
local anyItem = L:GetText("/SequenceEditor/AnyEqItem");
if (args.Text == anyItem) then
info.condArgs.eqItemName = "nil";
else
info.condArgs.eqItemName = "\"" .. args.Text .. "\"";
end
self:UpdateBar();
end
dropDown:SetParent(self.slotProperties);
if (info.condArgs.eqItemName == "nil") then
dropDown:SetText(anyItem);
else
local name = string.sub(info.condArgs.eqItemName, 2, -2);
dropDown:SetText(name);
end
end
if (info.condArgs.eqSlot) then
local prevContext = L:SetContext("/SequenceEditor/EquipmentSlots");
local eqSlotNames, localEqSlotNames, eqSlotValues = L:GetItems(), {}, {};
for slot in values(eqSlotNames) do
local text = L:GetText(slot);
table.insert(localEqSlotNames, text);
eqSlotValues[text] = tostring(Turbine.Gameplay.Equipment[slot]);
end
local anySlot = L:GetText("/SequenceEditor/AnyEqSlot");
eqSlotValues[anySlot] = "nil";
table.insert(localEqSlotNames, anySlot);
table.sort(localEqSlotNames);
local dropDown;
top, dropDown = AddDropDown(top + 2, localEqSlotNames);
dropDown.ItemChanged = function(sender, args)
info.condArgs.eqSlot = eqSlotValues[args.Text];
self:UpdateBar();
end
dropDown:SetParent(self.slotProperties);
if (info.condArgs.eqSlot == "nil") then
dropDown:SetText(anySlot);
else
local slotName = Search(Turbine.Gameplay.Equipment, tonumber(info.condArgs.eqSlot));
dropDown:SetText(L:GetText(slotName));
end
L:SetContext(prevContext);
end
end
-- Evaluate the condition and display the current value
local function GetValueStr()
local sequence = self.bar.sequence;
local slot = sequence:GetSlot(sequence:GetNewIndex(s));
if (slot and slot.condFunc) then
local text;
local success, value = pcall(slot.condFunc, slot, self.manager.player);
if (success) then
if (value) then
text = L:GetText("/SequenceEditor/True");
else
text = L:GetText("/SequenceEditor/False");
end
else
text = "Error: " .. value;
end
return "(" .. text .. ")";
end
end
if (info.condArgs and info.condArgs.script) then
top, self.playButton = AddButton(top, 40, "âº", Turbine.UI.Lotro.Font.Arial12);
top, label = AddLabel(top, "", Turbine.UI.Color.Goldenrod);
AddCallback(self.playButton, "Click", function()
label:SetText(GetValueStr());
end);
AddCallback(self.playButton, "MouseLeave", function()
label:SetText("");
end);
else
top, self.resultLabel = AddLabel(top, "", Turbine.UI.Color.Goldenrod);
self.resultLabel.Update = function(obj)
obj:SetText(GetValueStr());
end
self.resultLabel:SetWantsUpdates(true);
end
local isTargetEffectCondition = {
["TargetEffectDisease"] = true;
["TargetEffectFear"] = true;
["TargetEffectPoison"] = true;
["TargetEffectWound"] = true;
["TargetEffectOther"] = true;
["NotTargetEffectOther"] = true;
};
if (isTargetEffectCondition[info.condName]) then
top = top + 16; -- skip a line
_, label = AddLabel(top, L:GetText("/SequenceEditor/TargetEffectWarning"), Turbine.UI.Color.Silver);
label:SetTextAlignment(Turbine.UI.ContentAlignment.LeftCenter);
label:SetHeight(14 * L:GetNumber("/SequenceEditor/TargetEffectWarningHeight"));
top = top + label:GetHeight();
end
elseif (info.type == "LuaScript") then
-- Display script editor box
top, self.scriptBox = AddTextBox(top + 2, info.action, Turbine.UI.Color.White, true, self.settings.sequenceEditor.scriptBoxSize);
self.scriptBox:SetZOrder(1);
self.scriptBox:SetMultiline(true);
self.scriptBox:SetTextAlignment(Turbine.UI.ContentAlignment.TopLeft);
self.scriptBox:SetFont(Turbine.UI.Lotro.Font.Verdana12);
self.scriptBox:SetText(info.action);
self.scriptBox:SetWantsUpdates(true);
function self.scriptBox.Resized()
self.settings.sequenceEditor.scriptBoxSize = {self.scriptBox:GetSize()};
self:SaveSettings();
self:DisplaySlot(self.selectedSlot);
end
self.scriptBox.Update = function(sender)
local script = sender:GetText();
if (script ~= sender.prevText) then
local success, errorMsg = loadstring(script, "Slot " .. tostring(s));
if (success) then
self.scriptBox:SetBackColor(Turbine.UI.Color.Black);
self.playButton:SetEnabled(true);
info.action = script;
self:UpdateBar();
else
self.scriptBox:SetBackColor(Turbine.UI.Color.Red);
self.playButton:SetEnabled(false);
end
end
sender.prevText = script;
end
-- Create a button for executing the script
top, self.playButton = AddButton(top, 40, "âº", Turbine.UI.Lotro.Font.Arial12);
top, label = AddLabel(top, "", Turbine.UI.Color.Goldenrod);
AddCallback(self.playButton, "Click", function()
local script = self.scriptBox:GetText();
local success, result = pcall(loadstring("setfenv(1, _G);" .. script, "Slot " .. tostring(s)));
if (success) then
if (type(result) == "string") then
result = "\"" .. result .. "\"";
else
result = tostring(result); -- or "nil";
end
label:SetText("returned: " .. result);
else
label:SetText("error: " .. result);
end
end);
AddCallback(self.playButton, "MouseLeave", function()
label:SetText("");
end);
elseif (info.type == "Include") then
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/OtherSequence") .. ":", Turbine.UI.Color.Goldenrod);
local function GetObjectTree()
-- Get a list of all bar IDs.
local objectIDs = self.manager:FindAllDescendentIDs(nil, true, false);
-- Including self doesn't make sense, so prevent that.
local foundSelf;
for o = 1, #objectIDs, 1 do
local object = self.manager:GetObject(objectIDs[o]);
if (object == self.bar) then
foundSelf = o;
end
end
if (foundSelf) then
table.remove(objectIDs, foundSelf);
end
-- Add nonempty groups.
local groupIDs = {};
for o = 1, #objectIDs, 1 do
local objectID = objectIDs[o];
local object = self.manager:GetObject(objectID);
while (object.parent) do
object = object.parent;
objectID = object:GetID();
if (objectID) then
groupIDs[objectID] = 1;
end
end
end
for groupID in keys(groupIDs) do
table.insert(objectIDs, groupID);
end
-- Create item list for PullDownMenu.
local items = {};
for o = 1, #objectIDs, 1 do
local objectID = objectIDs[o];
local object = self.manager:GetObject(objectID);
local parentID = nil;
if (object.parent) then
parentID = object.parent:GetID();
end
table.insert(items, { objectID, object:GetName(), parentID });
end
return items;
end
local pullDown;
top, pullDown = AddPullDown(top, GetObjectTree(), info.include);
pullDown.SelectionChanged = function(sender, id)
info.include = id;
self:UpdateBar();
self:SelectSlot(s);
end
AddCallback(pullDown, "MouseClick", function(sender, args)
-- Right-click displays the bar menu.
if (args.Button == Turbine.UI.MouseButton.Right) then
if (info.include) then
local bar = self.manager.objects[info.include];
if (bar) then
bar:ShowSettingsMenu();
end
end
end
end);
-- Add the "unlink" button if a sequence is selected.
if (info.include) then
local button;
local text, width = L:GetText("/SequenceEditor/Unlink"), L:GetNumber("/SequenceEditor/UnlinkButtonWidth");
top, button = AddButton(top, width, text);
AddCallback(button, "Click", function()
self:UnlinkIncludedSequence(s);
end);
end
end
-- Allow the user to specify whether activation is click-based or automatic.
if (info.type ~= "Include") then
top = AddHorizontalLine(top);
top = AddLabel(top, L:GetText("/SequenceEditor/Activation") .. ":", Turbine.UI.Color.Goldenrod);
local button1, button2;
top, button1 = AddRadioButton(top, L:GetText("/SequenceEditor/WithLeftClick"), Turbine.UI.Color.DarkGoldenrod, (not info.automatic));
top, button2 = AddRadioButton(top, L:GetText("/SequenceEditor/Automatic"), Turbine.UI.Color.DarkGoldenrod, info.automatic);
Thurallor.UI.RadioButton.LinkPeers({button1, button2});
button1.Clicked = function()
info.automatic = nil;
self:UpdateBar();
end
button2.Clicked = function()
info.automatic = true;
self:UpdateBar();
end
end
end
-- Allow the user to specify that advancement should not occur until there is a target
if ((info.type == Turbine.UI.Lotro.ShortcutType.Skill) or (info.type == "SelectTarget")) then
local checkbox;
top, checkbox = AddCheckBox(top, L:GetText("/SequenceEditor/RequireTarget"), Turbine.UI.Color.DarkGoldenrod, info.requireTarget);
checkbox.CheckedChanged = function(chkbx)
if (chkbx:IsChecked()) then
info.requireTarget = true;
else
info.requireTarget = nil;
end
self:UpdateBar();
end
end
-- Allow the user to modify slot appearance (text, icon)
local button;
top, button = AddSection(top, L:GetText("/SequenceEditor/Appearance"), Turbine.UI.Color.Goldenrod, self.appearanceExpanded);
function button.ExpandedChanged(sender, expanded)
self.appearanceExpanded = expanded;
self:DisplaySlot(self.selectedSlot);
end
if (self.appearanceExpanded) then
-- Allow the user to optionally overlay some text
top = top + 16; -- skip a line
top = AddLabel(top, L:GetText("/SequenceEditor/TextOverlay") .. ":", Turbine.UI.Color.Goldenrod);
local textBox;
top, textBox = AddTextBox(top, info.textOverlay, Turbine.UI.Color.White);
textBox:SetFont(Turbine.UI.Lotro.Font.Verdana12);
textBox.TextChanged = function(sender, args)
local text = sender:GetText();
if (text == "") then
text = nil;
end
info.textOverlay = text;
UpdateIcon();
self:UpdateBar();
end
-- Allow the user to optionally change the icon
top = top + 16; -- skip a line
top = AddLabel(top, L:GetText("/SequenceEditor/ChangeIcon") .. ":", Turbine.UI.Color.Goldenrod);
local button1, button2, slider2, button3, slider3, button4, textbox;
top, button1 = AddRadioButton(top, L:GetText("/SequenceEditor/DefaultIcon"), Turbine.UI.Color.DarkGoldenrod);
top, button2 = AddRadioButton(top, L:GetText("/SequenceEditor/BlankIcon"), Turbine.UI.Color.DarkGoldenrod);
top, slider2 = AddSlider(top, 1, #resources.BlankIcons, 1);
slider2:SetLeft(12);
slider2:SetWidth(slider2:GetWidth() - 12);
top, button3 = AddRadioButton(top, L:GetText("/SequenceEditor/SkillIcon"), Turbine.UI.Color.DarkGoldenrod);
local skillsInfo = Thurallor.Utils.Watcher.GetSkillsInfo();
local gambitsInfo = Thurallor.Utils.Watcher.GetGambitsInfo();
self.skillIcons = {};
for id in sorted_keys(skillsInfo.byIcon) do
table.insert(self.skillIcons, id);
end
for id in sorted_keys(gambitsInfo.byIcon) do
table.insert(self.skillIcons, id);
end
top, slider3 = AddSlider(top, 1, #self.skillIcons, 1);
slider3:SetLeft(12);
slider3:SetWidth(slider3:GetWidth() - 12);
top, button4 = AddRadioButton(top, L:GetText("/SequenceEditor/SpecificIconID"), Turbine.UI.Color.DarkGoldenrod);
top, textBox = AddTextBox(top, "", Turbine.UI.Color.White);
textBox:SetFont(Turbine.UI.Lotro.Font.Verdana12);
textBox:SetLeft(12);
textBox:SetWidth(textBox:GetWidth() - 12);
if (info.altIcon == nil) then
button1:SetChecked(true);
else
local blankIcon = Search(resources.BlankIcons, info.altIcon);
if (blankIcon) then
button2:SetChecked(true);
slider2:SetValue(blankIcon);
else
local skillIcon = Search(self.skillIcons, info.altIcon);
if (skillIcon) then
button3:SetChecked(true);
slider3:SetValue(skillIcon);
else
button4:SetChecked(true);
end
end
end
local function UpdateIconID()
local id = info.altIcon;
if (id == nil) then
id = info.background;
end
if (type(id) == "number") then
textBox:SetText(string.format("0x%8.8X", id));
elseif (type(id) == "string") then
textBox:SetText(id);
else
-- Get icon ID from quickslot
end
end
UpdateIconID();
Thurallor.UI.RadioButton.LinkPeers({button1, button2, button3, button4});
function button1.Clicked()
info.altIcon = nil;
UpdateIcon();
UpdateIconID();
self:UpdateBar();
end
function button2.Clicked()
slider2:ValueChanged();
end
function button3.Clicked()
slider3:ValueChanged();
end
function button4.Clicked()
textBox:TextChanged();
end
slider2.ValueChanged = function(sender)
if (not button2:IsChecked()) then
button2:MouseClick();
return;
end
info.altIcon = resources.BlankIcons[sender:GetValue()];
UpdateIcon();
UpdateIconID();
self:UpdateBar();
self.settings.sequenceEditor.defaultIcon = info.altIcon;
end
slider3.ValueChanged = function(sender)
if (not button3:IsChecked()) then
button3:MouseClick();
return;
end
info.altIcon = self.skillIcons[sender:GetValue()];
UpdateIcon();
UpdateIconID();
self:UpdateBar();
self.settings.sequenceEditor.defaultIcon = info.altIcon;
end
function textBox.FocusGained()
button4:MouseClick();
end
textBox.TextChanged = function(sender)
if (not button4:IsChecked()) then
return;
end
local id = sender:GetText();
if (tonumber(id) ~= nil) then
id = tonumber(id);
end
local valid = pcall(GetAssetSize, id);
if (valid) then
textBox:SetForeColor(Turbine.UI.Color.White);
info.altIcon = id;
UpdateIcon();
self:UpdateBar();
else
textBox:SetForeColor(Turbine.UI.Color.Red);
end
end
end
self.slotProperties:SetHeight(top);
self:SetMinimumHeight(top + 110);
end
function SequenceEditor:FindSlotAtPosition(x, y, drawCaret)
local originX, originY = self.slotPanel:GetParent():PointToScreen(self.slotPanel:GetPosition());
local relX, relY = x - originX, y - originY + 3;
local posX, posY = 1 + math.floor(relX / self.slotSize + 0.5), 1 + math.floor(relY / self.slotSize);
local s = nil;
if ((posX >= 1) and (posX <= self.slotsWide + 1) and (posY >= 1) and (posY <= self.slotsHigh)) then
s = ((posY - 1) * self.slotsWide) + posX;
end
if ((posX == self.slotsWide + 1) and (posY == self.slotsHigh)) then
s = nil;
end
-- Update caret position.
if ((not drawCaret) or (not s)) then
-- Hide carets in all Sequence Editors
for editor in keys(SequenceEditor.instances) do
if (editor.caret) then
editor.caret:SetParent(nil);
editor.caret = nil;
end
end
else
if (not self.caret) then
self.caret = Turbine.UI.Control();
self.caret:SetParent(self.slotPanel);
self.caret:SetBackground(resources.Icon.Caret);
self.caret:SetBlendMode(Turbine.UI.BlendMode.AlphaBlend);
self.caret:SetSize(3, 36)
end
self.caret:SetPosition((posX - 1) * self.slotSize, (posY - 1) * self.slotSize);
end
return s;
end
function SequenceEditor:FindForeignSlotAtPosition(x, y, drawCaret)
-- Find the frontmost SequenceEditor under the mouse cursor
local frontEditor = self;
local maxZ = -2147483648;
for editor in keys(SequenceEditor.instances) do
if (editor ~= self) then
local left, top = editor:GetPosition();
local width, height = editor:GetSize();
if ((x >= left) and (x <= left + width) and (y >= top) and (y <= top + height)) then
if (editor:GetZOrder() >= maxZ) then
frontEditor = editor;
maxZ = editor:GetZOrder();
end
end
end
end
return frontEditor, frontEditor:FindSlotAtPosition(x, y, drawCaret);
end
function SequenceEditor:BuildSlot(slot, parent, object, left, top)
--Puts("BuildSlot(" .. tostring(slot) .. ", " .. tostring(parent) .. ", " .. tostring(object) .. ", " .. tostring(left) .. ", " .. tostring(top));
-- Get slot info, or initialize to default
local info = self.settings.sequenceItemInfo[slot];
if (object) then
-- Reuse the existing object
object:SetParent(parent);
object:SetInfo(info);
object:SetPosition(left, top);
object:SetNumLabel(slot);
return object;
end
-- Need to create a new object
object = Slot(info);
object:SetPosition(left, top);
object:SetNumLabel(slot);
object:SetParent(parent);
object:SetActionEnabled(false);
object:SetAllowDrop(true);
object:SetDraggable(true);
-- Desired mouse behaviors:
-- Left-click selects the slot and shows its properties in the "Slot" tab.
-- Right-click displays the settings menu.
-- Left-button-drag initiates a drag-and-drop (move) operation.
object.ShortcutChanged = function(sender, shortcutType, shortcutData)
local s = sender:GetNumLabel();
self:ExtendSequenceTo(s);
local info = sender:GetInfo();
self.settings.sequenceItemInfo[s] = info;
if (shortcutType == Turbine.UI.Lotro.ShortcutType.Alias) then
info.textOverlay = string.gsub(info.Data, "^/", "");
info.textOverlay = string.gsub(info.textOverlay, "%s.*$", "");
info.background = self.settings.sequenceEditor.defaultIcon;
sender:SetInfo(info);
end
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
object.MouseClick = function(sender, args)
-- Left-click selects the slot and shows its properties in the "Slot" tab.
if (args.Button == Turbine.UI.MouseButton.Left) then
self:SelectSlot(sender:GetNumLabel());
-- Right-click displays the settings menu.
elseif (args.Button == Turbine.UI.MouseButton.Right) then
self.clickedItem = sender:GetNumLabel();
self:ShowContextMenu();
end
end
object.DropPositionValid = function(sender, left, top)
local s = sender:GetNumLabel();
-- Draw a "caret" showing where the slot will be inserted.
local editor = self;
local newSlot = self:FindSlotAtPosition(left, top, true);
if (not newSlot) then
editor, newSlot = self:FindForeignSlotAtPosition(left, top, true);
end
if ((editor == self) and ((newSlot == s) or (newSlot == s + 1))) then
self:FindSlotAtPosition(0, 0, false); -- hide caret if returning to the same position
end
return newSlot; -- if newSlot is nil, a red "X" will be shown
end
object.DragDropComplete = function(sender, left, top)
local s = sender:GetNumLabel();
-- Select destination slot. Hide the caret.
local editor = self;
local newSlot = self:FindSlotAtPosition(left, top, false);
if (not newSlot) then
editor, newSlot = self:FindForeignSlotAtPosition(left, top, false);
end
-- Do the move operation, and redraw.
if (newSlot and (editor ~= self)) then
editor:InsertSlot(newSlot, self.settings.sequenceItemInfo[s]);
self:DeleteSlot(s);
elseif (newSlot and (newSlot ~= s) and (newSlot ~= s + 1)) then
self:MoveSlot(s, newSlot);
else
self:Redraw();
end
end
return object;
end
function SequenceEditor:RebuildSlot(s, x, y, alreadyDisplaying)
local oldObject = self.sequenceItems[s];
-- If x and y aren't specified, then presumably the object has already been positioned previously
if (x == nil) then
x = oldObject.x;
y = oldObject.y;
end
local left, top = self:GetSlotPosition(x, y);
local newObject = self:BuildSlot(s, self.slotPanel, oldObject, left, top);
newObject.x = x;
newObject.y = y;
self.sequenceItems[s] = newObject;
if (not alreadyDisplaying and (self.selectedSlot == s)) then
self:DisplaySlot(s);
end
end
function SequenceEditor:SelectSlot(s)
--Puts("SelectSlot(" .. tostring(s) .. "): self.selectedSlot = " .. tostring(self.selectedSlot));
if (self.selectedSlot) then
local object = self.sequenceItems[self.selectedSlot];
object:SetSelected(false);
local info = self.settings.sequenceItemInfo[self.selectedSlot];
if (info and (info.class == "Turbine.UI.Lotro.Quickslot") and (not info.type)) then
-- Delete empty shortcut
info.class = nil;
end
end
for o = 1, #self.relatedSlots, 1 do
local otherSlot, otherObject = unpack(self.relatedSlots[o]);
otherObject:SetSelected(nil);
self.relatedSlots[o] = nil;
end
if (s ~= nil) then
local info = self.settings.sequenceItemInfo[s];
if ((not info) or (not info.class)) then
s = nil;
else
local object = self.sequenceItems[s];
object:SetSelected(true);
local sequence = self.bar:GetSequence();
local barObject = sequence:GetSlot(sequence:GetNewIndex(s));
if (barObject) then
for otherSlot, _ in pairs({ifSlot = 1; elseSlot = 1; endIfSlot = 1}) do
if (barObject[otherSlot]) then
local otherObject = self.sequenceItems[sequence:GetOldIndex(barObject[otherSlot])];
otherObject:SetSelected(true, Turbine.UI.Color.Yellow);
table.insert(self.relatedSlots, {otherSlot, otherObject});
end
end
end
end
end
self.selectedSlot = s;
self:DisplaySlot(s);
end
function SequenceEditor:GetSlotPosition(x, y)
local left = (x - 1) * (self.settings.slotSize + self.settings.slotSpacing) - self.settings.slotSpacing;
local top = (y - 1) * (self.settings.slotSize + self.settings.slotSpacing) - self.settings.slotSpacing;
return left, top;
end
function SequenceEditor:Closing()
SequenceEditor.instances[self] = nil;
self:DeleteEmptyTrailingSlots();
self:UpdateBar();
if (self.ItemMovedCallback) then
RemoveCallback(Thurallor.Utils.Watcher, "ItemMoved", self.ItemMovedCallback);
self.ItemMovedCallback = nil;
end
end
function SequenceEditor:Close()
Turbine.UI.Lotro.Window.Close(self);
end
function SequenceEditor:ShowContextMenu()
-- Build the settings menu.
self.settingsMenu = Turbine.UI.ContextMenu();
local prevContext = L:SetContext("/SequenceEditor/RightClickMenu");
self:AddSettingsMenuItem(self.settingsMenu, "Root", false);
L:SetContext(prevContext);
self.settingsMenu:ShowMenu();
end
function SequenceEditor:AddSettingsMenuItem(parent, itemName)
local item = Turbine.UI.MenuItem(L:GetText(itemName), true, false);
parent:GetItems():Add(item);
if (itemName == "Root") then
parent:GetItems():Clear();
self:AddSettingsMenuItem(parent, "CreateSpecialSlot");
self:AddSettingsMenuItem(parent, "InsertEmptySlot");
self:AddSettingsMenuItem(parent, "CloneSlot");
self:AddSettingsMenuItem(parent, "DeleteSlot");
elseif (itemName == "CloneSlot") then
item.Click = function()
self:CloneSlot(self.clickedItem);
end
elseif (itemName == "InsertEmptySlot") then
item.Click = function()
self:InsertEmptySlot(self.clickedItem);
end
elseif (itemName == ("DeleteSlot")) then
item.Click = function()
self:DeleteSlot(self.clickedItem);
end
elseif (itemName == "CreateSpecialSlot") then
local prevContext = L:SetContext("SpecialSlotMenu");
local texts = L:GetSortedTexts();
for i = 1, #texts, 1 do
self:AddSettingsMenuItem(item, L:GetItem(texts[i]));
end
L:SetContext(prevContext);
elseif (string.find("StopAnimating|GenerateEvent|SetUnequipDestination", itemName)) then
item.Click = function()
self:CreateCommandSlot(self.clickedItem, itemName, item:GetText());
end
elseif (itemName == "ChatCommand") then
local commands = Turbine.Shell.GetCommands();
local index = math.random(#commands);
item.Click = function()
self:CreateAliasSlot(self.clickedItem, "/" .. commands[index]);
end
elseif (itemName == "RemoveEquipment") then
item.Click = function()
self:CreateRemovalSlot(self.clickedItem, "Gloves");
end
elseif ((itemName == "IfThen") or (itemName == "IfThenElse")) then
item.Click = function()
self:CreateConditional(self.clickedItem, itemName);
end
elseif (itemName == "Include") then
item.Click = function()
self:CreateIncludeSlot(self.clickedItem);
end
elseif (itemName == "Delay") then
item.Click = function()
self:CreateDelaySlot(self.clickedItem);
end
elseif (itemName == "SelectTarget") then
item.Click = function()
self:CreateSelectTargetSlot(self.clickedItem);
end
elseif (itemName == "SaveTarget") then
item.Click = function()
self:CreateSaveTargetSlot(self.clickedItem);
end
elseif (itemName == "LuaScript") then
item.Click = function()
self:CreateLuaScriptSlot(self.clickedItem);
end
end
end
function SequenceEditor:CreateAliasSlot(s, command)
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Lotro.Quickslot";
info.background = nil;
info.type = Turbine.UI.Lotro.ShortcutType.Alias;
info.Data = command;
info.altIcon = self.settings.sequenceEditor.defaultIcon;
if (info.background) then
info.textOverlay = string.gsub(info.Data, "^/", "");
info.textOverlay = string.gsub(info.textOverlay, "%s.*$", "");
end
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:CreateDelaySlot(s)
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon.Clock;
info.altIcon = nil;
info.type = "Delay";
info.toolTip = L:GetText("/SequenceEditor/RightClickMenu/SpecialSlotMenu/Delay");
info.delay = 0.25;
info.action = "local item, s = ...; item.bar:StartSlotCooldown(s, " .. tostring(info.delay):gsub(",", ".") .. ");";
info.advanceEvent = "DelayComplete";
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:CreateSelectTargetSlot(s)
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Lotro.EntityControl";
info.altIcon = nil;
info.type = "SelectTarget";
info.toolTip = L:GetText("/SequenceEditor/RightClickMenu/SpecialSlotMenu/SelectTarget");
info.target = "Self";
info.getTargetFunc = "local _, player = ...; return player;";
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:CreateSaveTargetSlot(s)
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon.SaveTarget;
info.altIcon = nil;
info.type = "SaveTarget";
info.toolTip = L:GetText("/SequenceEditor/RightClickMenu/SpecialSlotMenu/SaveTarget");
info.number = 1;
info.action = "Thurallor.Utils.Watcher.SaveTarget(1);";
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:CreateIncludeSlot(s)
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon.Include;
info.altIcon = nil;
info.type = "Include";
info.toolTip = L:GetText("/SequenceEditor/RightClickMenu/SpecialSlotMenu/Include");
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:CreateLuaScriptSlot(s)
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon.LuaScript;
info.altIcon = nil;
info.type = "LuaScript";
info.toolTip = L:GetText("/SequenceEditor/RightClickMenu/SpecialSlotMenu/LuaScript");
info.action = "Turbine.Shell.WriteLine(\"Hello, world!\");";
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:UnlinkIncludedSequence(s)
local info = self.settings.sequenceItemInfo[s];
if (not info.include) then
return;
end
local otherSequenceSettings = self.globals.bars[info.include];
if (not otherSequenceSettings) then
return;
end
local otherItemInfo = otherSequenceSettings.sequenceItemInfo;
self:DeleteSlot(s);
for i = 1, #otherItemInfo, 1 do
if (otherItemInfo[i].class) then
table.insert(self.settings.sequenceItemInfo, s + i - 1, {});
DeepTableCopy(otherItemInfo[i], self.settings.sequenceItemInfo[s + i - 1]);
end
end
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:CreateConditional(s, condType)
local prevContext = L:SetContext("/SequenceEditor");
local ifSlot = s;
self.noRedraw = true;
self:InsertEmptySlot(s);
if (condType == "IfThenElse") then
self:InsertEmptySlot(s);
end
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon["If"];
info.altIcon = nil;
info.type = "If";
info.condName = "Always";
info.condExpr = "return true;";
info.toolTip = L:GetText("If");
info.automatic = true;
if (condType == "IfThenElse") then
s = s + 1;
info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon["Else"];
info.altIcon = nil;
info.type = "Else";
info.toolTip = L:GetText("Else");
info.automatic = true;
end
s = s + 1;
info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon["EndIf"];
info.altIcon = nil;
info.type = "EndIf";
info.toolTip = L:GetText("EndIf");
info.automatic = true;
self.noRedraw = false;
self:Redraw();
self:UpdateBar();
self:SelectSlot(ifSlot);
L:SetContext(prevContext);
end
function SequenceEditor:CreateCommandSlot(s, slotName, toolTip)
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon[slotName];
info.altIcon = nil;
if (slotName == "StopAnimating") then
info.action = "local item = ...; item.bar.animationStopped = true; ";
elseif (slotName == "GenerateEvent") then
info.eventName = L:GetText("/SequenceEditor/NewEvent");
info.action = "local item = ...; item.bar.manager:PropagateEvent(item.info.eventName);";
elseif (slotName == "SetUnequipDestination") then
info.bagSlot = 1;
info.action = "local item = ...; item.bar.manager:SetUnequipDestination(item.info.bagSlot);";
end
info.type = slotName;
info.toolTip = toolTip;
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:CreateRemovalSlot(s, eqSlotName, bagSlot)
self:InsertEmptySlot(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon["Unequip" .. eqSlotName];
info.altIcon = nil;
info.action = "local item = ...; item.bar.manager:Unequip(Turbine.Gameplay.Equipment." .. eqSlotName .. ");";
info.type = "Remove " .. eqSlotName;
local prevContext = L:SetContext("/SequenceEditor");
local toolTip = L:GetText("RemoveItem");
L:SetContext("EquipmentSlots");
local localEqSlotName = L:GetText(eqSlotName);
info.toolTip = string.gsub(toolTip, "<item>", localEqSlotName);
L:SetContext(prevContext);
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:ExtendSequenceTo(s)
if (s <= #self.settings.sequenceItemInfo) then
return false;
end
while (s > #self.settings.sequenceItemInfo) do
table.insert(self.settings.sequenceItemInfo, {});
end
return true;
end
function SequenceEditor:DeleteEmptyTrailingSlots()
local sequenceItemInfo = self.settings.sequenceItemInfo;
for s = #sequenceItemInfo, 1, -1 do
local info = sequenceItemInfo[s];
if (not info.class) then
table.remove(sequenceItemInfo, s);
-- Remove empty slot displays
if (self.sequenceItems) then
local object = self.sequenceItems[s];
if (object) then
if (object.numLabel) then
object.numLabel:SetParent(nil);
end
object:SetParent(nil);
self.sequenceItems[s] = nil;
end
end
else
break;
end
end
end
function SequenceEditor:CloneSlot(s)
self:InsertEmptySlot(s);
DeepTableCopy(self.settings.sequenceItemInfo[s + 1], self.settings.sequenceItemInfo[s]);
self:RebuildSlot(s);
self:UpdateBar();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:MoveSlot(sourceSlot, destSlot)
self:InsertEmptySlot(destSlot);
if (sourceSlot >= destSlot) then
sourceSlot = sourceSlot + 1;
end
DeepTableCopy(self.settings.sequenceItemInfo[sourceSlot], self.settings.sequenceItemInfo[destSlot]);
self:RebuildSlot(destSlot);
self:DeleteSlot(sourceSlot);
if (sourceSlot <= destSlot) then
destSlot = destSlot - 1;
end
self:UpdateBar();
self:Redraw();
self:SelectSlot(destSlot);
end
function SequenceEditor:InsertSlot(s, info)
if (s > #self.settings.sequenceItemInfo) then
self:ExtendSequenceTo(s);
else
table.insert(self.settings.sequenceItemInfo, s, {});
end
if (info) then
DeepTableCopy(info, self.settings.sequenceItemInfo[s]);
end
self:Redraw();
self:UpdateBar();
self:SelectSlot(s);
end
function SequenceEditor:InsertEmptySlot(s)
self:InsertSlot(s, nil);
end
function SequenceEditor:DeleteSlot(s)
table.remove(self.settings.sequenceItemInfo, s);
self:Redraw();
self:UpdateBar();
self:ExtendSequenceTo(s);
self:SelectSlot(nil);
end