Mercurial > wow > breuesk
changeset 49:f52d472f0b0a
Sweeping refactor to compartmentalize the various lists and to give them nice interfaces. Disentangled dependency web significantly
author | John@Yosemite-PC |
---|---|
date | Wed, 21 Mar 2012 22:52:42 -0400 |
parents | b3679847e292 |
children | b155a875de42 |
files | Core.lua Lists.lua |
diffstat | 2 files changed, 652 insertions(+), 527 deletions(-) [+] |
line wrap: on
line diff
--- a/Core.lua Wed Mar 21 22:51:34 2012 -0400 +++ b/Core.lua Wed Mar 21 22:52:42 2012 -0400 @@ -63,121 +63,114 @@ local MULTIBYTE_FIRST_CHAR = "^([\192-\255]?%a?[\128-\191]*)" return string.gsub(p, MULTIBYTE_FIRST_CHAR, string.upper, 1) end + action,arg1,arg2,arg3 = bsk:GetArgs(paramIn,4) - if param[1] == nil or param[1] == "" then + if not action then print("need args") return end - if param[1] == "persons" then + if action == "persons" then PrintPersons() - elseif param[1] == "changes" then + elseif action == "changes" then PrintChanges() - elseif param[1] == "delete" then - if param[2] == nil or param[2] == "" or param[3] == nil or param[3] == "" then + elseif action == "selfdestruct" then + SelfDestruct() + elseif action == "delete" or action == "remove" then + if not arg1 or not arg2 then PrintTable(param) return end - if param[2] == "list" then - DeleteList(param[3]) - elseif param[2] == "personfromlist" then - if param[4] == nil or param[4] == "" then + if arg1 == "list" then + DeleteList(arg2) + elseif arg1 == "personfromlist" or "fromlist" then + if not arg3 then PrintTable(param) return end - local person = FixPersonName(param[3]) - RemovePersonFromList(person, param[4]) - elseif param[2] == "person" then - local person = FixPersonName(param[3]) + local person = FixPersonName(arg2) + RemovePersonFromList(person, arg3) + elseif arg1 == "person" then + local person = FixPersonName(arg2) RemovePerson(person) else - printf("Deleting anything of type %s is not supported",param[2]) + printf("Deleting anything of type %s is not supported",arg1) end - elseif param[1] == "nuke" then - if param[2] == nil or param[2] == "" then + elseif action == "nuke" then + if not arg1 then PrintTable(param) return end - local person = FixPersonName(param[2]) + local person = FixPersonName(arg1) NukePerson(person) - elseif param[1] == "add" then - if param[2] == nil or param[2] == "" then + elseif action == "add" or action == "create" then + if not arg1 or not arg2 then PrintTable(param) return end - if param[3] == nil or param[3] == "" then - PrintTable(param) - return - end - if param[2] == "person" then - if param[3] == "all" then - AddMissingPersons() + if arg1 == "person" then + if arg2 == "all" or arg2 == "missing" then + PersonList:AddMissing() else - local person = FixPersonName(param[3]) + local person = FixPersonName(arg2) AddPerson(person) end - elseif param[2] == "list" then - CreateList(param[3]) - elseif param[2] == "tolist" then - if param[4] == nil or param[4] == "" then + elseif arg1 == "list" then + CreateList(arg2) + elseif arg1 == "tolist" then + if not arg3 then PrintTable(param) return end - local person = FixPersonName(param[3]) - AddPersonToListEnd(person,param[4]) - elseif param[2] == "tolistrandom" then - if param[4] == nil or param[4] == "" then + local person = FixPersonName(arg2) + AddPersonToListEnd(person,arg3) + elseif arg1 == "tolistrandom" then + if not arg3 then PrintTable(param) return end - local person = FixPersonName(param[3]) - AddPersonToListRandom(person,param[4]) + local person = FixPersonName(arg2) + AddPersonToListRandom(person,arg3) end - elseif param[1] == "populate" then - if param[2] == nil or param[2] == "" or param[3] == nil or param[3] == "" then + elseif action == "populate" then + if not arg1 then PrintTable(param) return end -- list = p2 - local index = GetListIndex(param[2]) - if param[3] == "random" then - PopulateListRandom(index) - end - elseif param[1] == "suicide" then - if param[2] == nil or param[2] == "" or param[3] == nil or param[3] == "" then + PopulateListRandom(arg1) + elseif action == "suicide" then + if not arg1 or not arg2 then PrintTable(param) return end - local person = FixPersonName(param[2]) - SuicidePerson(person,param[3]) - elseif param[1] == "lists" then - if param[2] == nil or param[2] == "" then + local person = FixPersonName(arg1) + SuicidePerson(person,arg2) + elseif action == "lists" then + if not arg1 then PrettyPrintLists() return else - local listIndex = GetListIndex(param[2]) - PrettyPrintList(listIndex) + PrettyPrintList(arg1) end - elseif param[1] == "reserve" then - if param[2] == nil or param[2] == "" then + elseif action == "reserve" then + if not arg1 then + PrintTable(param) + return + end + local person = FixPersonName(arg1) + ReservePerson(person) + elseif action == "trim" then + if not arg1 then printtable(param) return end - local person = FixPersonName(param[2]) - AddReserve(person) - elseif param[1] == "trim" then - if param[2] == nil or param[2] == "" then - printtable(param) + TrimLists(arg1) + elseif action == "rename" then + if not arg1 or not arg2 then + PrintTable(param) return end - TrimLists(param[2]) - elseif param[1] == "rename" then - if param[2] == nil or param[2] == "" or param[3] == nil or param[3] == "" then - printtable(param) - return - end - RenameList(param[2],param[3]) - elseif param[1] == "selfdestruct" then - SelfDestruct() + RenameList(arg1,arg2) else CreateGUI() end
--- a/Lists.lua Wed Mar 21 22:51:34 2012 -0400 +++ b/Lists.lua Wed Mar 21 22:52:42 2012 -0400 @@ -85,89 +85,597 @@ local setmetatable=setmetatable setfenv(1,bsk) -lists = {} -persons = {} - -raidNameP = {} -- "name" is present in raid -raidIdP = {} -- "id" is present in raid -reserveIdP = {} -- "reserve id present" -local personName2id = {} -- given "name" get that person's id +ListEntry = +{ + index = 0.0, + id = 0, +} +ListEntry.__index = ListEntry +function ListEntry:new(arg1,arg2) + local n = {} + setmetatable(n,ListEntry) + if type(arg1) == "number" and type(arg2) == "string" then + n.index, n.id = index,id + elseif type(arg1) == "table" and type(arg2) == "nil" then + n.index, n.id = arg1.roll, arg1.id + else + _G.error("Do not know how to construct a ListEntry from " .. type(arg1) .. " and " .. type(arg2)) + end + assert(n.index ~= nil) + assert(n.id ~= nil) + return n +end +function ListEntry:GetId() + return self.id +end +function ListEntry:GetIndex() + return self.index +end +function ListEntry:ReplaceId(newId) -- returns the old Id + local temp = self.id + self.id = newId + return temp +end List = { - id = 0, name = "", - Sort = function() + time = 0, + + -- "private" functions. only private thing about them is that my + -- autocomplete won't pick them up when defined this way + Sort = function(self) + table.sort(self.data,function(a,b) return a:GetIndex() < b:GetIndex() end) + end, + GetLastIndex = function(self) + if not self.data or getn(self.data) == 0 then return 0.0 + else return self.data[#self.data]:GetIndex() end + end, + SetTime = function(self,time) + if time == nil or time == 0 then + assert("Dangerous things are afoot") + else + self.time = time + end end + } -function List:new() -- TODO: take args, build list - n = {} - setmetatable(n,self) - self.__index = self +List.__index = List +function List:new(arg1, arg2, arg3) + local n = {data={}} + setmetatable(n,List) + if type(arg1) == "string" and type(arg2) == "number" and type(arg3) == "nil" then + n.name = arg1 + n.time = arg2 + elseif type(arg1) == "table" and type(arg2) == "nil" and type(arg3) == "nil" then + n.name = arg1.name + if arg1.data then + for _,v in pairs(arg1.data) do + local le = ListEntry:new(v) + table.insert(n.data,entry) + end + n:Sort() + end + n:SetTime(arg1.time) + else + _G.error(sformat("Do not know how to construct list from: %s and %s and %s", type(arg1), type(arg2), type(arg3))) + end return n end -function List:HasID(id) - for i = 1,#self do - if id == self[i].id then +function List:HasId(id) + for le in self:OrderedIdIter() do + if id == le then return true end end return false end -function List:GetID() +function List:GetTime() + return self.time +end +function List:GetName() + return self.name +end +function List:GetId() + local listIndex = LootLists:IdOf(self) -- TODO: undo circular dep somehow + return listIndex +end +function List:Rename(packet,time) + self.name = packet.name + self:SetTime(time) + return packet +end +function List:RenameList(newName) + local listIndex = self:GetId() + return InitiateChange("Rename",self,{listIndex=listIndex,name=newName}) +end +function List:RemoveEntry(entry,time) + local pos = self:Select(entry.id) + if pos then + --print("Removing id " .. entry.id .. " pos " .. pos) + table.remove(self.data,pos) + self:Sort() + self:SetTime(time) + return pos + end + --print("Failed removal of ...") + --PrintTable(entry) +end +function List:Remove(id) + -- TODO: check id + local listIndex = self:GetId() + return InitiateChange("RemoveEntry",self,{listIndex=listIndex,id=id}) +end +function List:InsertEndEntry(packet,time) + if self:InsertEntry(packet,time) then + self.closedRandom = true + return packet + end +end +function List:InsertEntry(packet,time) + local le = ListEntry:new(packet) + table.insert(self.data,le) + self:Sort() + self:SetTime(time) + --printf("Inserting %s to %s", packet.id, packet.listIndex) + return le +end +function List:InsertEnd(id) -- returns the LE created + if self:Select(id) then + printf("Person %s is already on the reqeuested list",id) -- todo: lookup name + return false + end + local index = self:GetLastIndex() + 0.1 + local le = ListEntry:new(index,id) + local listIndex = self:GetId() + return InitiateChange("InsertEndEntry",self,{listIndex=listIndex,id=id,roll=index}) +end +function List:InsertRandom(id) -- returns the LE created + if self.closedRandom then + print("Cannot add person to list by random roll because an add-to-end operation has already occurred") + return false + end + if self:Select(id) then + printf("Person %s is already on the reqeuested list",id) -- todo: lookup name + return false + end + local index = math.random() + local listIndex = self:GetId() + return InitiateChange("InsertEntry",self,{listIndex=listIndex,id=id,roll=index}) +end +function List:OrderedListEntryIter() + local i = 0 + local n = #self.data + + return function() + i = i+1 + if i<=n then return self.data[i] end + end +end +function List:OrderedIdIter() + local i = 0 + local n = #self.data + return function() + i = i+1 + if i<=n then return self.data[i]:GetId() end + end +end +function List:GetAllIds() + local t = {} + for id in self:OrderedIdIter() do + table.insert(t,id) + end + return t +end +function List:Select(entry) -- returns the position and the entry + if type(entry) == "string" then -- search by id + local ids = self:GetAllIds() + for i,v in ipairs(ids) do + if v == entry then + return i, self.data[i] + end + end + return false, nil + else + assert("undone") + return false, nil + end +end +function List:Suicide(packet,time) + -- the goal here is to rotate the suicide list by 1 + -- then we can just mash it on top of the intersection between the original + -- list and the working copy + local affected = shallowCopy(packet.affect) + local replacement = shallowCopy(packet.affect) + local temp = table.remove(replacement,1) -- pop + tinsert(replacement,temp) -- push_back + --rintf(("Before suicide of %s on list %s",slist[1],list.name) + --PrintTable(list) + for le in self:OrderedListEntryIter() do + if le:GetId() == affected[1] then + le:ReplaceId(replacement[1]) + table.remove(affected,1) + table.remove(replacement,1) + end + end + -- TODO: flag error if affected and replacement aren't both empty now + self:SetTime(time) + return packet +end +function List:SuicidePerson(id) + -- first calculate the effect, then initiate the change + PersonList:RefreshRaidList() + local slist = {} + local pushing = false + for le in self:OrderedListEntryIter() do -- get all ids + local lid = le:GetId() + if lid == id then + pushing = true + end + if pushing and PersonList:IsActive(lid) then -- TODO: decouple + tinsert(slist,lid) + end + end + local listIndex = self:GetId() + return InitiateChange("Suicide",self,{listIndex=listIndex,affect=slist}) +end + +LootLists = +{ + --l = {} -- list of List objects, keyed by id +} + +-- generate self, then generate sublists +function LootLists:ConstructFromDB(db) + self:Reset() + local saved = db.profile.lists + for i,v in pairs(saved) do -- upconvert saved to true list objects + self.l[i] = List:new(v) + end +end +function LootLists:SaveToDB(db) + db.profile.lists = self.l +end +function LootLists:CreateList(packet,time) + local le = List:new(packet.name,time) + if not packet.id then packet.id = time end -- uses the timestamp for the index - it's unique, unlike anything else I can think of + self.l[packet.id] = le + return le +end +function LootLists:Create(name) + return InitiateChange("CreateList",self,{name=name}) +end +function LootLists:DeleteList(packet,time) + self.l[packet.listIndex] = nil + return id +end +function LootLists:Delete(index) + -- TODO: is there anything to check first, or just fire away? + return InitiateChange("DeleteList",self,{listIndex=index}) +end +function LootLists:Select(id) + if type(id) == "number" then -- by id + return self.l[id] + elseif type(id) == "string" then -- name + for i,v in pairs(self.l) do + if v:GetName() == id then + return v + end + end + end + return nil +end +function LootLists:Reset() + self.l = {} +end +function LootLists:GetAllIds() + local t = {} + for i,v in pairs(self.l) do + table.insert(t,i) + end + return t +end +function LootLists:IdOf(list) + for i,v in pairs(self.l) do + if v == list then + return i + end + end +end + +Toon = +{ + id=0, + name="", + class="" +} +Toon.__index = Toon +function Toon:new(arg1,arg2,arg3) + local t = {} + setmetatable(t,Toon) + if type(arg1) == "number" and type(arg2) == "string" and type(arg3) == "string" then + t.id, t.name, t.class = arg1, arg2, arg3 + elseif type(arg1) == "table" and arg2 == nil and arg3 == nil then + t.id, t.name, t.class = arg1.id, arg1.name, arg1.class + else + error("Cannot construct a toon object from types " .. type(arg1) .. ", " .. type(arg2) .. ", " .. type(arg3)) + end + return t +end +function Toon:GetName() + return self.name +end +function Toon:GetId() return self.id end -function List:RemoveEntry() -end -function List:InsertEntry() -end -function List:OrderedIter() +function Toon:GetClass() + return self.class end +PersonList = +{ + toons = {}, + time = 0, + active = { raid={}, reserve={} } +} +function PersonList:ConstructFromDB(db) + self:Reset() + local dbp = db.profile.persons + self.time = dbp.time + if dbp.toons == nil then return end + for i,v in pairs(dbp.toons) do + local te = Toon:new(v) + table.insert(self.toons,te) + end +end +function PersonList:SaveToDB(db) + db.profile.persons = { toons=self.toons, time=self.time } +end +function PersonList:Reset() + self.toons = {} + self.time = 0 + self.active = { raid={}, reserve={}, raidExtras={} } +end +function PersonList:Select(id) + -- both id and name are strings, but there won't be clashes + -- because an ID will contain either a number or all caps letters + -- and names must be long enough to ensure that one of those is true + if type(id) == "string" then + for i,v in pairs(self.toons) do + --print(i) + --PrintTable(v) + if v:GetName() == id or v:GetId() == id then + return v + end + end + end +end +function PersonList:AddToon(packet,time) + local te = Toon:new(packet) + table.insert(self.toons,te) + return te +end +function PersonList:Add(name) + local guid = _G.UnitGUID(name) + -- TODO: check guid to be sure it's a player + if not guid then + printf("Could not add player %s - they must be in range or group",name) + return + end + local _,englishClass = _G.UnitClass(name) + --print("Person " .. name .. " is class " .. englishClass) + local id = string.sub(guid,6) -- skip at least 0x0580 ... + id = id:gsub("^0*(.*)","%1") -- nom all leading zeroes remaining + + local pe = self:Select(id) + if pe and pe:GetName() ~= name then + printf("Namechange detected for %s - new is %s, please rename the existing entry", pe:GetName(), name) + return + end + if pe then + printf("%s is already in the persons list; disregarding", name) + return + end + return InitiateChange("AddToon", self, {name=name, id=id, class=englishClass}) +end +function PersonList:RemoveToon(packet,time) + local id = packet.id + for i,v in pairs(self.toons) do + if v:GetId() == id then + table.remove(self.toons,i) + return v + end + end +end +function PersonList:Remove(ident) + local le = PersonList:Select(ident) + if not le then + printf("%s is not in the persons list, please check your spelling", ident) + return false + end + local id = le:GetId() + local listsTheyreOn = {} + -- check if they're active on any loot list + local allListIds = LootLists:GetAllIds() + for _,v in pairs(allListIds) do + if LootLists:Select(v):HasId(id) then -- TODO: this is ineloquent + tinsert(listsTheyreOn,LootLists:Select(v):GetName()) + break + end + end + if getn(listsTheyreOn) > 0 then + printf("Cannot remove person %s because they are on one or more lists (%s)",ident,table.concat(listsTheyreOn,", ")) + return false + end + return InitiateChange("RemoveToon", self, {id=id}) +end +function PersonList:IsRegistered(id) + if self:Select(id) ~= nil then return true end +end +function PersonList:AddReserve(id) + local le = self:Select(id) + if le then + -- todo: check that they're not already reserved + self.active.reserve[le:GetId()] = true + end +end +-- todo: remove reserve +function PersonList:IsActive(id) + return self.active.raid[id] or self.active.reserve[id] +end +function PersonList:AddMissing() + self:RefreshRaidList() + for _,name in pairs(self.active.raidExtras) do + printf("Person %s is missing from the persons list - adding",name) + self:Add(name) + end + -- TODO: batch into a single op - no need to spam 25 messages in a row +end +function PersonList:GetAllActiveIds() + self:RefreshRaidList() + local t = {} + for i,v in pairs(self.active.raid) do + if v then table.insert(t,i) end + end + for i,v in pairs(self.active.reserve) do + if v then table.insert(t,i) end + end + return t +end +-- The following (adapted) code is from Xinhuan (wowace forum member) +-- Pre-create the unitId strings we will use +local pId = {} +local rId = {} +for i = 1, 4 do + pId[i] = sformat("party%d", i) +end +for i = 1, 40 do + rId[i] = sformat("raid%d", i) +end +function PersonList:RefreshRaidList() + local inParty = _G.GetNumPartyMembers() + local inRaid = _G.GetNumRaidMembers() + local add = function(unitNameArg) + local name = _G.UnitName(unitNameArg) + local te = self:Select(name) + if te then + self.active.raid[te:GetId()]=true + else + table.insert(self.active.raidExtras,name) + end + --if personName2id[name] ~= nil then + -- raidIdP[personName2id[name]]=true + --end + end + self.active.raid = {} + self.active.raidExtras = {} + if inRaid > 0 then + for i = 1, inRaid do + add(rId[i]) + end + elseif inParty > 0 then + for i = 1, inParty do + add(pId[i]) + end + -- Now add yourself as the last party member + add("player") + else + -- You're alone + add("player") + end +end +function GetSafeTimestamp() + local changes = db.profile.changes + local ctime = time() + local n = getn(changes) + if n > 0 then + if changes[n].time >= ctime then + ctime = changes[n].time + 1 + end + end + return ctime +end +function InitiateChange(finalizeAction,acceptor,arg) + local change = {} + change.time = GetSafeTimestamp() + change.action = finalizeAction + change.arg = arg - + if acceptor[finalizeAction](acceptor,arg,change.time) then + table.insert(db.profile.changes,change) + -- TODO: broadcast + return arg + else + return nil + end +end +function ProcessChange(change) + -- try list-o-lists and persons - if has matching function, call it + local action = change.action + if PersonList[action] then + PersonList[action](PersonList,change.arg,change.time) + return + elseif LootLists[action] then + LootLists[action](LootLists,change.arg,change.time) + return + else + -- pray that the change has a listIndex in it ... + if change.arg.listIndex then + local l = LootLists:Select(change.arg.listIndex) + if l and l[action] then + l[action](l,change.arg,change.time) + return + end + end + end + _G.error("Could not process change: " .. change.action) +end function SelfDestruct() - lists = {} - persons = {} + LootLists:Reset() + PersonList:Reset() db.profile.persons = {} db.profile.changes = {} db.profile.lists = {} - raidNameP = {} - raidIdP = {} - reserveIdP = {} - personName2id = {} end -- Debugging {{{ function PrettyPrintList(listIndex) - local list = lists[listIndex] - print("List: " .. list.name .. " (" .. listIndex .. ") - last modified " .. date("%m/%d/%y %H:%M:%S", list.time) .. " (",list.time,")" ) - for i = 1,#list do - print(" " .. i .. " - " .. persons[list[i].id].main) + PersonList:RefreshRaidList() + local le = LootLists:Select(listIndex) + print("List: " .. le:GetName() .. " (" .. le:GetId() .. ") - last modified " .. date("%m/%d/%y %H:%M:%S", le:GetTime()) .. " ("..le:GetTime()..")" ) + local pos = 1 + for i in le:OrderedIdIter() do -- ordered iterator + local s = "" + if PersonList:IsActive(i) then + s = "*" + end + + print(" " .. pos .. " - " .. PersonList:Select(i):GetName() .. " ("..i..")",s) + pos = pos + 1 end end function PrettyPrintLists() - for i,_ in pairs(lists) do + for _,i in pairs(LootLists:GetAllIds()) do PrettyPrintList(i) end end function PrintLists() - PrintTable(lists) + PrintTable(LootLists) end function PrintChanges() PrintTable(db.profile.changes) end function PrintPersons() - PrintTable(persons) + PrintTable(PersonList) end function PrintAPI(object) for i,v in pairs(object) do @@ -176,103 +684,22 @@ end end end -function PrintRaidAndReserve() - print("RaidNameP") - PrintTable(raidNameP) - print("RaidIdP") - PrintTable(raidIdP) - print("ReserveP") - PrintTable(reserveIdP) - print("personName2id") - PrintTable(personName2id) -end --}}} -function UpdatePersonsReverse() - for i,v in pairs(persons) do - if i ~= "time" then - personName2id[v.main] = i - end - end -end - -- Change processing {{{ function CreateWorkingStateFromChanges(changes) - local personsBase = db.profile.persons - local lists = db.profile.lists - -- copy the base to the working state - wipe(lists) - wipe(persons) - wipe(personName2id) - - tcopy(lists,lists) - tcopy(persons,personsBase) + LootLists:ConstructFromDB(db) + PersonList:ConstructFromDB(db) -- now just go through the changes list applying each for i,v in ipairs(changes) do ProcessChange(v) end - - -- update the persons reverse list - UpdatePersonsReverse() -end - -function CreateChange(change) - -- sanity - assert(change) - assert(change.action) - assert(change.arg) - - StartChange(change) - CommitChange(change) -end - -function StartChange(change) - local changes = db.profile.changes - change.time = time() - local n = getn(changes) - if n > 0 then - if changes[n].time >= change.time then - change.time = changes[n].time + 1 - end - end -end - -function CommitChange(change) - local changes = db.profile.changes - tinsert(changes,change) - -- TODO: broadcast change -end - -function ProcessChange(change) - if change.action == "AddPerson" then - DoAddPerson(change) - elseif change.action == "RenameList" then - DoRenameList(change) - elseif change.action == "CreateList" then - DoCreateList(change) - elseif change.action == "DeleteList" then - DoDeleteList(change) - elseif change.action == "AddToListEnd" then - DoAddPersonToListEnd(change) - elseif change.action == "AddToListRand" then - DoAddPersonToListRandom(change) - elseif change.action == "RemovePerson" then - DoRemovePerson(change) - elseif change.action == "RemovePersonFromList" then - DoRemovePersonFromList(change) - elseif change.action == "SuicidePerson" then - DoSuicidePerson(change) - else - print("Unknown message encountered") - PrintTable(change) - assert(false) - end end --}}} --- + -- holy crap long winded {{{ -- timestamp logic: -- use time() for comparisons - local clients use date() to make it pretty. only @@ -321,239 +748,60 @@ -- -- Note that "undo" has no special voodoo to it. It's basically a change that -- reverses the prior change on the stack.--}}} -function DoAddPerson(change)--{{{ - assert(change) - assert(change.arg.id) - -- require admin - local persons = persons - local name = change.arg.name - local id = change.arg.id - assert(persons[id]==nil) - persons[id] = {main=name,class=change.arg.class} - persons.time=change.time - personName2id[name] = id - return true -end--}}} function AddPerson(name)--{{{ - local persons = persons - local guid = _G.UnitGUID(name) - -- TODO: check guid to be sure it's a player - if not guid then - printf("Could not add player %s - they must be in range or group",name) - return - end - local _,englishClass = _G.UnitClass(name) - --print("Person " .. name .. " is class " .. englishClass) - local id = string.sub(guid,6) -- skip at least 0x0580 ... - id = id:gsub("^0*(.*)","%1") -- nom all leading zeroes remaining - - if persons[id] and persons[id] ~= name then - printf("Namechange detected for %s - new is %s, please rename the existing entry", persons[id].main, name) - return - end - if persons[id] ~= nil then - printf("%s is already in the persons list; disregarding", name) - return - end - local change = {action="AddPerson",arg={name=name,id=id,class=englishClass}} - if DoAddPerson(change) then - CreateChange(change) - end -end--}}} -function DoCreateList(change)--{{{ - --if GetListIndex(change.arg.name) then - -- rintf(("List %s already exists",v.name) - -- return false - --end - lists[change.arg.id]={name=change.arg.name,time=change.time} - return true + print("Adding ... " .. name) + PersonList:Add(name) end--}}} function CreateList(name)--{{{ -- require admin - local change={action="CreateList",arg={name=name}} - StartChange(change) - change.arg.id=change.time -- use the creation timestamp as the list's index. it's as unique as anything... print("Creating ... " .. name) - if DoCreateList(change) then - CommitChange(change) - end -end--}}} -function DoAddPersonToListEnd(change)--{{{ - local list = lists[change.arg.listIndex] - local index - if getn(list) > 0 then - index = list[#list].index + 0.1 - else - index = 0.1 - end - local entry = {index=index, id=change.arg.id} - - tinsert(list,entry) - list.time = change.time - list.closedRandom = true - - return true + return LootLists:Create(name) end--}}} function AddPersonToListEnd(name,listName)--{{{ -- require admin - local listIndex = GetListIndex(listName) - local id = personName2id[name] - if IdIsInList(id,lists[listIndex]) then - printf("Person %s is already on the reqeuested list",name) - return false - end - printf("Adding %s (%s) to list %s (%s)", name, id, listName, listIndex) - local change = {action="AddToListEnd",arg={id=id,listIndex=listIndex}} - StartChange(change) - if DoAddPersonToListEnd(change) then - CommitChange(change) - end -end--}}} -function DoAddPersonToListRandom(change)--{{{ - local list = lists[change.arg.listIndex] - local entry = {index=change.arg.roll, id=change.arg.id} - - tinsert(list,entry) - table.sort(list,function(a,b) return a.index < b.index end) - list.time = change.time - - return true + local l = LootLists:Select(listName) + local te = PersonList:Select(name) + -- TODO: if not te ... + printf("Adding %s (%s) to list %s", name, te:GetId(), listName) + return l:InsertEnd(te:GetId()) end--}}} function AddPersonToListRandom(name,listName)--{{{ -- require admin - local listIndex = GetListIndex(listName) - if lists[listIndex].closedRandom then - print("Cannot add person to list by random roll because an add-to-end operation has already occurred") - return false - end - local id = personName2id[name] - if IdIsInList(id,lists[listIndex]) then - printf("Person %s is already on the reqeuested list",name) - return false - end - local roll = math.random() - printf("Adding %s (%s) to list %s (%s) with roll (%f)", name, id, listName, listIndex, roll) - local change = {action="AddToListRand",arg={id=id,listIndex=listIndex,roll=roll}} - StartChange(change) - if DoAddPersonToListRandom(change) then - CommitChange(change) - end -end--}}} -function DoRemovePerson(change)--{{{ - local person = persons[change.arg.id] - personName2id[person.main] = nil - persons[change.arg.id] = nil - persons.time = change.time - return true -end--}}} -function RemovePerson(name)--{{{ - local id = personName2id[name] - if not id then - printf("%s is not in the persons list, please check your spelling", name) - return false - end - local listsTheyreOn = {} - -- check if they're active on any loot list - for i,v in pairs(lists) do - if IdIsInList(id,v) then - tinsert(listsTheyreOn,v.name) - break - end - end - if getn(listsTheyreOn) > 0 then - printf("Cannot remove person %s because they are on one or more lists (%s)",name,table.concat(listsTheyreOn,", ")) - return false - end - local change = {action="RemovePerson",arg={id=id}} - StartChange(change) - if DoRemovePerson(change) then - CommitChange(change) - end -end--}}} -function DoSuicidePerson(change)--{{{ - local list = lists[change.arg.listIndex] - local affected = shallowCopy(change.arg.affect) - -- the goal here is to rotate the suicide list by 1 - -- then we can just mash it on top of the intersection between the original - -- list and the working copy - - local replacement = shallowCopy(change.arg.affect) - local temp = table.remove(replacement,1) -- pop - tinsert(replacement,temp) -- push_back - --rintf(("Before suicide of %s on list %s",slist[1],list.name) - --PrintTable(list) - for i = 1, #list do - if list[i].id == affected[1] then - table.remove(affected,1) - list[i].id = replacement[1] - table.remove(replacement,1) - end - end - list.time=change.time - return true + local l = LootLists:Select(listName) + local te = PersonList:Select(name) + -- TODO: if not te ... + printf("Adding %s (%s) to list %s - randomly!", name, te:GetId(), listName) + return l:InsertRandom(te:GetId()) end--}}} function SuicidePerson(name,listName)--{{{ -- require admin - PopulateRaidList() - local listIndex = GetListIndex(listName) - local id = personName2id[name] - local affect=GetSuicideList(id,lists[listIndex]) - local change = {action="SuicidePerson",arg={affect=affect,listIndex=listIndex}} - StartChange(change) - if DoSuicidePerson(change) then - CommitChange(change) - end -end--}}} -function DoRenameList(change)--{{{ - lists[change.arg.listIndex].name = change.arg.name - lists[change.arg.listIndex].time = change.time - return true + PersonList:RefreshRaidList() + local le = LootLists:Select(listName) + local te = PersonList:Select(name) + return le:SuicidePerson(te:GetId()) end--}}} function RenameList(listName,newListName)--{{{ -- require admin - local listIndex = GetListIndex(listName) - local change = {action="RenameList",arg={listIndex=listIndex,name=newListName}} - StartChange(change) - if DoRenameList(change) then - CommitChange(change) - end -end--}}} -function DoDeleteList(change)--{{{ - lists[change.arg.listIndex] = nil - return true + local le = LootLists:Select(listName) + return le:RenameList(newListName) end--}}} function DeleteList(listName)--{{{ - local listIndex = GetListIndex(listName) - local change = {action="DeleteList",arg={listIndex=listIndex}} - StartChange(change) - if DoDeleteList(change) then - CommitChange(change) - end -end--}}} -function DoRemovePersonFromList(change)--{{{ - local list = lists[change.arg.listIndex] - - for i,v in ipairs(list) do - if v.id == change.arg.id then - table.remove(list,i) - break - end - end - table.sort(list,function(a,b) return a.index < b.index end) - list.time = change.time - return true + return LootLists:DeleteList(LootLists:Select(listName):GetId()) end--}}} function RemovePersonFromList(name,listName)--{{{ - local listIndex = GetListIndex(listName) - local pid = personName2id[name] - -- todo: check that they're on the list in the first place - local change = {action="RemovePersonFromList",arg={id=pid,listIndex=listIndex}} - StartChange(change) - if DoRemovePersonFromList(change) then - CommitChange(change) - end + local le = LootLists:Select(listName) + local te = PersonList:Select(name) + return le:Remove(te:GetId()) end --}}} +function RemovePerson(person) + print("Removing " .. person) + PersonList:Remove(person) +end +function ReservePerson(person) + print("Reserving " .. person) + PersonList:AddReserve(person) +end --}}} -- Higher order actions (ie calls other standard actions){{{ @@ -581,8 +829,8 @@ CreateWorkingStateFromChanges(before) -- save this state permanently; trim the changes permanently - tcopy(db.profile.persons,persons) - tcopy(db.profile.lists,lists) + LootLists:SaveToDB(db) + PersonList:SaveToDB(db) while db.profile.changes ~= nil and db.profile.changes[1] ~= nil and db.profile.changes[1].time <= time do table.remove(db.profile.changes,1) end @@ -591,39 +839,21 @@ CreateWorkingStateFromChanges(db.profile.changes) end -function AddMissingPersons() - PopulateRaidList() - local t = {} - for id,_ in pairs(persons) do - t[id] = true - end - for name,_ in pairs(raidNameP) do - if personName2id[name] == nil then - printf("Person %s is missing from the persons list - adding",name) - AddPerson(name) - end - end - -- TODO: batch into a single op - no need to spam 25 messages in a row -end function PopulateListRandom(listIndex) -- difference (raid+reserve)-list, then random shuffle that, then add - PopulateRaidList() - local list = lists[listIndex] + local actives = PersonList:GetAllActiveIds() + local list = LootLists:Select(listIndex) - local t = {} -- after loops, contains intersection of IDs present between raid and reserve - for i,v in pairs(raidIdP) do - if v then t[i] = true end - end - for i,v in pairs(reserveIdP) do - if v then t[i] = true end - end + --swap keys on actives + local t = {} + for _,v in pairs(actives) do t[v] = true end -- now remove from t all of the people already present on the list - if list then - for i = 1,#list do - if t[list[i].id] then - t[list[i].id] = false + if t then + for id in list:OrderedIdIter() do -- id iterator + if t[id] then + t[id] = false end end end @@ -631,79 +861,17 @@ -- add all remaining for i,v in pairs(t) do if v then - AddPersonToListRandom(persons[i].main,list.name) -- TODO: APTLR keys off of string names. probably need to change this. + AddPersonToListRandom(i,list:GetId()) end end end - function NukePerson(name) -- delete from all lists and then from persons - local pid = personName2id[name] - for i,v in pairs(lists) do - RemovePersonFromList(name,v.name) + for _,id in pairs(LootLists:GetAllIds()) do + RemovePersonFromList(name,id) end RemovePerson(name) end --}}} --- "Soft" actions- ie things that cause nonpermanent state {{{ - --- reserves -function AddReserve(name) - print("Reserving" .. name) - reserveIdP[personName2id[name]]=true - -- TODO: communicate to others. don't store this in any way. -end - -function RemoveReserve(name) - reserveIdP[personName2id[name]]=false - -- TODO: communicate to others. don't store this in any way. -end - - ---function GetActiveList() --- return lists[1] -- todo! ---end - ---}}} - --- The following (adapted) code is from Xinhuan (wowace forum member) --- Pre-create the unitID strings we will use -local pID = {} -local rID = {} -for i = 1, 4 do - pID[i] = sformat("party%d", i) -end -for i = 1, 40 do - rID[i] = sformat("raid%d", i) -end -function PopulateRaidList() - local inParty = _G.GetNumPartyMembers() - local inRaid = _G.GetNumRaidMembers() - local add = function(unitNameArg) - local name = _G.UnitName(unitNameArg) - raidNameP[name]=true - if personName2id[name] ~= nil then - raidIdP[personName2id[name]]=true - end - end - - wipe(raidNameP) - wipe(raidIdP) - if inRaid > 0 then - for i = 1, inRaid do - add(rID[i]) - end - elseif inParty > 0 then - for i = 1, inParty do - add(pID[i]) - end - -- Now add yourself as the last party member - add("player") - else - -- You're alone - add("player") - end - --PrintTable(raidNameP) -end -- undo rules! -- only the most recent event can be undone @@ -713,36 +881,6 @@ -- while undo is allowed *per-list*, certain events in the stream will -- prevent proper undo, such as add/delete player or add/delete list - -function GetSuicideList(id,list) - --print("Calculating changeset for "..name.." from list -") - --PrintTable(list) - local t = {} - local ret = {} - local pushing = false - for i = 1, #list do - if list[i].id == id then - pushing = true - end - if pushing and (raidIdP[list[i].id] or reserveIdP[list[i].id]) then - tinsert(ret,list[i].id) - end - end - --print("GSL") - --PrintTable(ret) - --print("GSL") - return ret -end - -function IdIsInList(id,listRef) - for i = 1,#listRef do - if id == listRef[i].id then - return true - end - end - return false -end - -- returns true if the events in the list are in time order function CheckListCausality() local t = nil @@ -757,15 +895,3 @@ return true end --- Support functions - -function GetListIndex(name) - for i,v in pairs(lists) do - if v.name == name then - return i - end - end - return nil -end - -