Sequence = class();
function Sequence:Constructor(bar, info)
self.bar = bar;
self.manager = bar.manager;
self.visibleItems = {};
-- Callbacks
self.ItemChanged = nil;
if (not info) then
info = {};
end
self:SetInfo(info);
end
function Sequence:SetInfo(newInfo)
self.slots = {};
self.events = {};
self.slotIndexMap = {};
self.includes = {};
for i = 1, #newInfo, 1 do
self.i = i;
local info = newInfo[i];
self:ProcessInfoItem(info);
end
self:ProcessConditionals();
end
function Sequence:ProcessInfoItem(info)
local slot = Slot(info);
slot.i = self.i;
slot.info = info;
if ((type(info) ~= "table") or not info.class) then
-- Empty slot; discard
elseif ((info.class == "Turbine.UI.Control") and (info.type == "Include")) then
if (info.include ~= self.bar:GetID()) then
if (not self:IncludeSequence(info.include)) then
info.include = nil;
self.bar:SaveSettings(false);
end
end
else
table.insert(self.slots, slot);
self.slotIndexMap[self.i] = #self.slots;
if (info.class ~= "Turbine.UI.Control") then
-- Standard quickslot; no further handling necessary
else
if (info.type == "GenerateEvent") then
self.events[info.eventName] = 0;
end
if (info.action) then
slot.actionFunc = loadstring(info.action);
end
end
end
end
function Sequence:IncludeSequence(barID)
local otherSettings = self.manager.settings.bars[barID];
if (otherSettings and not otherSettings.deleted) then
table.insert(self.includes, barID);
local otherInfo = otherSettings.sequenceItemInfo;
for i = 1, #otherInfo, 1 do
local info = otherInfo[i];
self:ProcessInfoItem(info);
end
return true;
else
return false;
end
end
function Sequence:GetIncludes()
return self.includes;
end
-- Returns the new index of a slot after empty slots are removed
function Sequence:GetNewIndex(oldIndex)
return self.slotIndexMap[oldIndex];
end
function Sequence:GetOldIndex(newIndex)
return self.slots[newIndex].i;
end
function Sequence:GetSlots()
return self.slots;
end
function Sequence:GetSlot(index)
return self.slots[index];
end
function Sequence:GetEvents()
return self.events;
end
-- Populate the 'ifSlot', 'elseSlot', and 'endIfSlot' members of slots.
function Sequence:ProcessConditionals()
local stack = { {} };
local parentIfSlot, parentElseSlot = nil, nil;
for s = 1, #self.slots, 1 do
local slot = self.slots[s];
local info = slot.info;
slot.parentIfSlot, slot.parentElseSlot = parentIfSlot, parentElseSlot;
if (info.type == "If") then
slot.ifSlot, slot.elseSlot, slot.endIfSlot = nil, nil, nil;
-- Do argument substitutions
slot.condExpr = info.condExpr;
if (info.condArgs) then
for name, value in pairs(info.condArgs) do
slot.condExpr = string.gsub(slot.condExpr, "<" .. name .. ">", tostring(value));
end
end
slot.condFunc = loadstring(slot.condExpr);
table.insert(stack, { info.type, s });
parentIfSlot = s;
parentElseSlot = nil;
elseif (info.type == "Else") then
local lastType, lastLoc = unpack(table.remove(stack));
if (lastType == "If") then
local ifItem = self.slots[lastLoc];
ifItem.elseSlot = s;
slot.ifSlot, slot.elseSlot, slot.endIfSlot = lastLoc, nil, nil;
table.insert(stack, { info.type, s });
slot.parentIfSlot, slot.parentElseSlot = ifItem.parentIfSlot, ifItem.parentElseSlot;
parentIfSlot = nil;
parentElseSlot = s;
else -- Syntax error: Unmatched braces
Puts("Error: \"Else\" at slot " .. tostring(slot.i) .. " has no matching \"If\".");
table.insert(stack, { lastType, lastLoc });
end
elseif (info.type == "EndIf") then
local lastType, lastLoc = unpack(table.remove(stack));
if (lastType == "If") then
local ifItem = self.slots[lastLoc];
ifItem.endIfSlot = s;
slot.ifSlot, slot.elseSlot, slot.endIfSlot = lastLoc, nil, nil;
slot.parentIfSlot, slot.parentElseSlot = ifItem.parentIfSlot, ifItem.parentElseSlot;
parentIfSlot, parentElseSlot = ifItem.parentIfSlot, ifItem.parentElseSlot;
elseif (lastType == "Else") then
local elseItem = self.slots[lastLoc];
local ifItem = self.slots[elseItem.ifSlot];
ifItem.endIfSlot = s;
elseItem.endIfSlot = s;
slot.ifSlot, slot.elseSlot, slot.endIfSlot = elseItem.ifSlot, lastLoc, nil;
slot.parentIfSlot, slot.parentElseSlot = ifItem.parentIfSlot, ifItem.parentElseSlot;
parentIfSlot, parentElseSlot = ifItem.parentIfSlot, ifItem.parentElseSlot;
else -- Syntax error: Unmatched braces
Puts("Error: \"EndIf\" at slot " .. tostring(slot.i) .. " has no matching \"If\".");
table.insert(stack, { lastType, lastLoc });
end
end
end
while (#stack > 1) do
local t, s = unpack(table.remove(stack));
local slot = self.slots[s];
Puts("Error: \"" .. t .. "\" at slot " .. tostring(slot.i) .. " has no matching \"EndIf\".");
end
--for s = 1, #self.slots, 1 do
-- local slot = self.slots[s];
-- Puts(tostring(s) .. ". parentIfSlot/parentElseSlot/ifSlot/elseSlot/endIfSlot = " .. tostring(slot.parentIfSlot) .. "/" .. tostring(slot.parentElseSlot) .. "/" .. tostring(slot.ifSlot) .. "/" .. tostring(slot.elseSlot) .. "/" .. tostring(slot.endIfSlot) .. "; condExpr = " .. tostring(slot.info.condExpr) .. "; condFunc = " .. tostring(slot.condFunc));
--end
end
function Sequence:UnfoldAllSlots()
for s = 1, #self.slots, 1 do
self.slots[s].condResult = nil;
self.slots[s].foldTime = nil;
end
end
-- There is currently no reason to evaluate folding more than once at any given game time;
-- hence the gameTime argument equals the current game time, a cached folding value is returned.
function Sequence:SlotIsFolded(s, gameTime)
local slot = self.slots[s];
if (slot.foldTime == gameTime) then
return slot.isFolded;
end
if (slot.parentIfSlot) then
local ifSlot = self.slots[slot.parentIfSlot];
slot.isFolded = self:SlotIsFolded(slot.parentIfSlot, gameTime) or (not ifSlot.condResult);
elseif (slot.parentElseSlot) then
local elseSlot = self.slots[slot.parentElseSlot];
local ifSlot = self.slots[elseSlot.ifSlot];
slot.isFolded = self:SlotIsFolded(slot.parentElseSlot, gameTime) or ifSlot.condResult;
else
slot.isFolded = false;
end
slot.foldTime = gameTime;
return slot.isFolded;
end