Mercurial > wow > breuesk
comparison Lists.lua @ 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 | 6c0a8acccbc8 |
| children | b155a875de42 |
comparison
equal
deleted
inserted
replaced
| 48:b3679847e292 | 49:f52d472f0b0a |
|---|---|
| 83 local assert=assert | 83 local assert=assert |
| 84 local getmetatable=getmetatable | 84 local getmetatable=getmetatable |
| 85 local setmetatable=setmetatable | 85 local setmetatable=setmetatable |
| 86 setfenv(1,bsk) | 86 setfenv(1,bsk) |
| 87 | 87 |
| 88 lists = {} | 88 ListEntry = |
| 89 persons = {} | 89 { |
| 90 | 90 index = 0.0, |
| 91 raidNameP = {} -- "name" is present in raid | 91 id = 0, |
| 92 raidIdP = {} -- "id" is present in raid | 92 } |
| 93 reserveIdP = {} -- "reserve id present" | 93 ListEntry.__index = ListEntry |
| 94 local personName2id = {} -- given "name" get that person's id | 94 function ListEntry:new(arg1,arg2) |
| 95 local n = {} | |
| 96 setmetatable(n,ListEntry) | |
| 97 if type(arg1) == "number" and type(arg2) == "string" then | |
| 98 n.index, n.id = index,id | |
| 99 elseif type(arg1) == "table" and type(arg2) == "nil" then | |
| 100 n.index, n.id = arg1.roll, arg1.id | |
| 101 else | |
| 102 _G.error("Do not know how to construct a ListEntry from " .. type(arg1) .. " and " .. type(arg2)) | |
| 103 end | |
| 104 assert(n.index ~= nil) | |
| 105 assert(n.id ~= nil) | |
| 106 return n | |
| 107 end | |
| 108 function ListEntry:GetId() | |
| 109 return self.id | |
| 110 end | |
| 111 function ListEntry:GetIndex() | |
| 112 return self.index | |
| 113 end | |
| 114 function ListEntry:ReplaceId(newId) -- returns the old Id | |
| 115 local temp = self.id | |
| 116 self.id = newId | |
| 117 return temp | |
| 118 end | |
| 95 | 119 |
| 96 List = | 120 List = |
| 97 { | 121 { |
| 98 id = 0, | |
| 99 name = "", | 122 name = "", |
| 100 Sort = function() | 123 time = 0, |
| 101 end | 124 |
| 125 -- "private" functions. only private thing about them is that my | |
| 126 -- autocomplete won't pick them up when defined this way | |
| 127 Sort = function(self) | |
| 128 table.sort(self.data,function(a,b) return a:GetIndex() < b:GetIndex() end) | |
| 129 end, | |
| 130 GetLastIndex = function(self) | |
| 131 if not self.data or getn(self.data) == 0 then return 0.0 | |
| 132 else return self.data[#self.data]:GetIndex() end | |
| 133 end, | |
| 134 SetTime = function(self,time) | |
| 135 if time == nil or time == 0 then | |
| 136 assert("Dangerous things are afoot") | |
| 137 else | |
| 138 self.time = time | |
| 139 end | |
| 140 end | |
| 141 | |
| 102 } | 142 } |
| 103 function List:new() -- TODO: take args, build list | 143 List.__index = List |
| 104 n = {} | 144 function List:new(arg1, arg2, arg3) |
| 105 setmetatable(n,self) | 145 local n = {data={}} |
| 106 self.__index = self | 146 setmetatable(n,List) |
| 147 if type(arg1) == "string" and type(arg2) == "number" and type(arg3) == "nil" then | |
| 148 n.name = arg1 | |
| 149 n.time = arg2 | |
| 150 elseif type(arg1) == "table" and type(arg2) == "nil" and type(arg3) == "nil" then | |
| 151 n.name = arg1.name | |
| 152 if arg1.data then | |
| 153 for _,v in pairs(arg1.data) do | |
| 154 local le = ListEntry:new(v) | |
| 155 table.insert(n.data,entry) | |
| 156 end | |
| 157 n:Sort() | |
| 158 end | |
| 159 n:SetTime(arg1.time) | |
| 160 else | |
| 161 _G.error(sformat("Do not know how to construct list from: %s and %s and %s", type(arg1), type(arg2), type(arg3))) | |
| 162 end | |
| 107 return n | 163 return n |
| 108 end | 164 end |
| 109 function List:HasID(id) | 165 function List:HasId(id) |
| 110 for i = 1,#self do | 166 for le in self:OrderedIdIter() do |
| 111 if id == self[i].id then | 167 if id == le then |
| 112 return true | 168 return true |
| 113 end | 169 end |
| 114 end | 170 end |
| 115 return false | 171 return false |
| 116 end | 172 end |
| 117 function List:GetID() | 173 function List:GetTime() |
| 174 return self.time | |
| 175 end | |
| 176 function List:GetName() | |
| 177 return self.name | |
| 178 end | |
| 179 function List:GetId() | |
| 180 local listIndex = LootLists:IdOf(self) -- TODO: undo circular dep somehow | |
| 181 return listIndex | |
| 182 end | |
| 183 function List:Rename(packet,time) | |
| 184 self.name = packet.name | |
| 185 self:SetTime(time) | |
| 186 return packet | |
| 187 end | |
| 188 function List:RenameList(newName) | |
| 189 local listIndex = self:GetId() | |
| 190 return InitiateChange("Rename",self,{listIndex=listIndex,name=newName}) | |
| 191 end | |
| 192 function List:RemoveEntry(entry,time) | |
| 193 local pos = self:Select(entry.id) | |
| 194 if pos then | |
| 195 --print("Removing id " .. entry.id .. " pos " .. pos) | |
| 196 table.remove(self.data,pos) | |
| 197 self:Sort() | |
| 198 self:SetTime(time) | |
| 199 return pos | |
| 200 end | |
| 201 --print("Failed removal of ...") | |
| 202 --PrintTable(entry) | |
| 203 end | |
| 204 function List:Remove(id) | |
| 205 -- TODO: check id | |
| 206 local listIndex = self:GetId() | |
| 207 return InitiateChange("RemoveEntry",self,{listIndex=listIndex,id=id}) | |
| 208 end | |
| 209 function List:InsertEndEntry(packet,time) | |
| 210 if self:InsertEntry(packet,time) then | |
| 211 self.closedRandom = true | |
| 212 return packet | |
| 213 end | |
| 214 end | |
| 215 function List:InsertEntry(packet,time) | |
| 216 local le = ListEntry:new(packet) | |
| 217 table.insert(self.data,le) | |
| 218 self:Sort() | |
| 219 self:SetTime(time) | |
| 220 --printf("Inserting %s to %s", packet.id, packet.listIndex) | |
| 221 return le | |
| 222 end | |
| 223 function List:InsertEnd(id) -- returns the LE created | |
| 224 if self:Select(id) then | |
| 225 printf("Person %s is already on the reqeuested list",id) -- todo: lookup name | |
| 226 return false | |
| 227 end | |
| 228 local index = self:GetLastIndex() + 0.1 | |
| 229 local le = ListEntry:new(index,id) | |
| 230 local listIndex = self:GetId() | |
| 231 return InitiateChange("InsertEndEntry",self,{listIndex=listIndex,id=id,roll=index}) | |
| 232 end | |
| 233 function List:InsertRandom(id) -- returns the LE created | |
| 234 if self.closedRandom then | |
| 235 print("Cannot add person to list by random roll because an add-to-end operation has already occurred") | |
| 236 return false | |
| 237 end | |
| 238 if self:Select(id) then | |
| 239 printf("Person %s is already on the reqeuested list",id) -- todo: lookup name | |
| 240 return false | |
| 241 end | |
| 242 local index = math.random() | |
| 243 local listIndex = self:GetId() | |
| 244 return InitiateChange("InsertEntry",self,{listIndex=listIndex,id=id,roll=index}) | |
| 245 end | |
| 246 function List:OrderedListEntryIter() | |
| 247 local i = 0 | |
| 248 local n = #self.data | |
| 249 | |
| 250 return function() | |
| 251 i = i+1 | |
| 252 if i<=n then return self.data[i] end | |
| 253 end | |
| 254 end | |
| 255 function List:OrderedIdIter() | |
| 256 local i = 0 | |
| 257 local n = #self.data | |
| 258 return function() | |
| 259 i = i+1 | |
| 260 if i<=n then return self.data[i]:GetId() end | |
| 261 end | |
| 262 end | |
| 263 function List:GetAllIds() | |
| 264 local t = {} | |
| 265 for id in self:OrderedIdIter() do | |
| 266 table.insert(t,id) | |
| 267 end | |
| 268 return t | |
| 269 end | |
| 270 function List:Select(entry) -- returns the position and the entry | |
| 271 if type(entry) == "string" then -- search by id | |
| 272 local ids = self:GetAllIds() | |
| 273 for i,v in ipairs(ids) do | |
| 274 if v == entry then | |
| 275 return i, self.data[i] | |
| 276 end | |
| 277 end | |
| 278 return false, nil | |
| 279 else | |
| 280 assert("undone") | |
| 281 return false, nil | |
| 282 end | |
| 283 end | |
| 284 function List:Suicide(packet,time) | |
| 285 -- the goal here is to rotate the suicide list by 1 | |
| 286 -- then we can just mash it on top of the intersection between the original | |
| 287 -- list and the working copy | |
| 288 local affected = shallowCopy(packet.affect) | |
| 289 local replacement = shallowCopy(packet.affect) | |
| 290 local temp = table.remove(replacement,1) -- pop | |
| 291 tinsert(replacement,temp) -- push_back | |
| 292 --rintf(("Before suicide of %s on list %s",slist[1],list.name) | |
| 293 --PrintTable(list) | |
| 294 for le in self:OrderedListEntryIter() do | |
| 295 if le:GetId() == affected[1] then | |
| 296 le:ReplaceId(replacement[1]) | |
| 297 table.remove(affected,1) | |
| 298 table.remove(replacement,1) | |
| 299 end | |
| 300 end | |
| 301 -- TODO: flag error if affected and replacement aren't both empty now | |
| 302 self:SetTime(time) | |
| 303 return packet | |
| 304 end | |
| 305 function List:SuicidePerson(id) | |
| 306 -- first calculate the effect, then initiate the change | |
| 307 PersonList:RefreshRaidList() | |
| 308 local slist = {} | |
| 309 local pushing = false | |
| 310 for le in self:OrderedListEntryIter() do -- get all ids | |
| 311 local lid = le:GetId() | |
| 312 if lid == id then | |
| 313 pushing = true | |
| 314 end | |
| 315 if pushing and PersonList:IsActive(lid) then -- TODO: decouple | |
| 316 tinsert(slist,lid) | |
| 317 end | |
| 318 end | |
| 319 local listIndex = self:GetId() | |
| 320 return InitiateChange("Suicide",self,{listIndex=listIndex,affect=slist}) | |
| 321 end | |
| 322 | |
| 323 LootLists = | |
| 324 { | |
| 325 --l = {} -- list of List objects, keyed by id | |
| 326 } | |
| 327 | |
| 328 -- generate self, then generate sublists | |
| 329 function LootLists:ConstructFromDB(db) | |
| 330 self:Reset() | |
| 331 local saved = db.profile.lists | |
| 332 for i,v in pairs(saved) do -- upconvert saved to true list objects | |
| 333 self.l[i] = List:new(v) | |
| 334 end | |
| 335 end | |
| 336 function LootLists:SaveToDB(db) | |
| 337 db.profile.lists = self.l | |
| 338 end | |
| 339 function LootLists:CreateList(packet,time) | |
| 340 local le = List:new(packet.name,time) | |
| 341 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 | |
| 342 self.l[packet.id] = le | |
| 343 return le | |
| 344 end | |
| 345 function LootLists:Create(name) | |
| 346 return InitiateChange("CreateList",self,{name=name}) | |
| 347 end | |
| 348 function LootLists:DeleteList(packet,time) | |
| 349 self.l[packet.listIndex] = nil | |
| 350 return id | |
| 351 end | |
| 352 function LootLists:Delete(index) | |
| 353 -- TODO: is there anything to check first, or just fire away? | |
| 354 return InitiateChange("DeleteList",self,{listIndex=index}) | |
| 355 end | |
| 356 function LootLists:Select(id) | |
| 357 if type(id) == "number" then -- by id | |
| 358 return self.l[id] | |
| 359 elseif type(id) == "string" then -- name | |
| 360 for i,v in pairs(self.l) do | |
| 361 if v:GetName() == id then | |
| 362 return v | |
| 363 end | |
| 364 end | |
| 365 end | |
| 366 return nil | |
| 367 end | |
| 368 function LootLists:Reset() | |
| 369 self.l = {} | |
| 370 end | |
| 371 function LootLists:GetAllIds() | |
| 372 local t = {} | |
| 373 for i,v in pairs(self.l) do | |
| 374 table.insert(t,i) | |
| 375 end | |
| 376 return t | |
| 377 end | |
| 378 function LootLists:IdOf(list) | |
| 379 for i,v in pairs(self.l) do | |
| 380 if v == list then | |
| 381 return i | |
| 382 end | |
| 383 end | |
| 384 end | |
| 385 | |
| 386 Toon = | |
| 387 { | |
| 388 id=0, | |
| 389 name="", | |
| 390 class="" | |
| 391 } | |
| 392 Toon.__index = Toon | |
| 393 function Toon:new(arg1,arg2,arg3) | |
| 394 local t = {} | |
| 395 setmetatable(t,Toon) | |
| 396 if type(arg1) == "number" and type(arg2) == "string" and type(arg3) == "string" then | |
| 397 t.id, t.name, t.class = arg1, arg2, arg3 | |
| 398 elseif type(arg1) == "table" and arg2 == nil and arg3 == nil then | |
| 399 t.id, t.name, t.class = arg1.id, arg1.name, arg1.class | |
| 400 else | |
| 401 error("Cannot construct a toon object from types " .. type(arg1) .. ", " .. type(arg2) .. ", " .. type(arg3)) | |
| 402 end | |
| 403 return t | |
| 404 end | |
| 405 function Toon:GetName() | |
| 406 return self.name | |
| 407 end | |
| 408 function Toon:GetId() | |
| 118 return self.id | 409 return self.id |
| 119 end | 410 end |
| 120 function List:RemoveEntry() | 411 function Toon:GetClass() |
| 121 end | 412 return self.class |
| 122 function List:InsertEntry() | 413 end |
| 123 end | 414 |
| 124 function List:OrderedIter() | 415 PersonList = |
| 125 end | 416 { |
| 126 | 417 toons = {}, |
| 127 | 418 time = 0, |
| 128 | 419 active = { raid={}, reserve={} } |
| 129 | 420 } |
| 130 | 421 |
| 131 | 422 function PersonList:ConstructFromDB(db) |
| 132 | 423 self:Reset() |
| 133 | 424 local dbp = db.profile.persons |
| 134 | 425 self.time = dbp.time |
| 135 | 426 if dbp.toons == nil then return end |
| 136 | 427 for i,v in pairs(dbp.toons) do |
| 428 local te = Toon:new(v) | |
| 429 table.insert(self.toons,te) | |
| 430 end | |
| 431 end | |
| 432 function PersonList:SaveToDB(db) | |
| 433 db.profile.persons = { toons=self.toons, time=self.time } | |
| 434 end | |
| 435 function PersonList:Reset() | |
| 436 self.toons = {} | |
| 437 self.time = 0 | |
| 438 self.active = { raid={}, reserve={}, raidExtras={} } | |
| 439 end | |
| 440 function PersonList:Select(id) | |
| 441 -- both id and name are strings, but there won't be clashes | |
| 442 -- because an ID will contain either a number or all caps letters | |
| 443 -- and names must be long enough to ensure that one of those is true | |
| 444 if type(id) == "string" then | |
| 445 for i,v in pairs(self.toons) do | |
| 446 --print(i) | |
| 447 --PrintTable(v) | |
| 448 if v:GetName() == id or v:GetId() == id then | |
| 449 return v | |
| 450 end | |
| 451 end | |
| 452 end | |
| 453 end | |
| 454 function PersonList:AddToon(packet,time) | |
| 455 local te = Toon:new(packet) | |
| 456 table.insert(self.toons,te) | |
| 457 return te | |
| 458 end | |
| 459 function PersonList:Add(name) | |
| 460 local guid = _G.UnitGUID(name) | |
| 461 -- TODO: check guid to be sure it's a player | |
| 462 if not guid then | |
| 463 printf("Could not add player %s - they must be in range or group",name) | |
| 464 return | |
| 465 end | |
| 466 local _,englishClass = _G.UnitClass(name) | |
| 467 --print("Person " .. name .. " is class " .. englishClass) | |
| 468 local id = string.sub(guid,6) -- skip at least 0x0580 ... | |
| 469 id = id:gsub("^0*(.*)","%1") -- nom all leading zeroes remaining | |
| 470 | |
| 471 local pe = self:Select(id) | |
| 472 if pe and pe:GetName() ~= name then | |
| 473 printf("Namechange detected for %s - new is %s, please rename the existing entry", pe:GetName(), name) | |
| 474 return | |
| 475 end | |
| 476 if pe then | |
| 477 printf("%s is already in the persons list; disregarding", name) | |
| 478 return | |
| 479 end | |
| 480 | |
| 481 return InitiateChange("AddToon", self, {name=name, id=id, class=englishClass}) | |
| 482 end | |
| 483 function PersonList:RemoveToon(packet,time) | |
| 484 local id = packet.id | |
| 485 for i,v in pairs(self.toons) do | |
| 486 if v:GetId() == id then | |
| 487 table.remove(self.toons,i) | |
| 488 return v | |
| 489 end | |
| 490 end | |
| 491 end | |
| 492 function PersonList:Remove(ident) | |
| 493 local le = PersonList:Select(ident) | |
| 494 if not le then | |
| 495 printf("%s is not in the persons list, please check your spelling", ident) | |
| 496 return false | |
| 497 end | |
| 498 local id = le:GetId() | |
| 499 local listsTheyreOn = {} | |
| 500 | |
| 501 -- check if they're active on any loot list | |
| 502 local allListIds = LootLists:GetAllIds() | |
| 503 for _,v in pairs(allListIds) do | |
| 504 if LootLists:Select(v):HasId(id) then -- TODO: this is ineloquent | |
| 505 tinsert(listsTheyreOn,LootLists:Select(v):GetName()) | |
| 506 break | |
| 507 end | |
| 508 end | |
| 509 if getn(listsTheyreOn) > 0 then | |
| 510 printf("Cannot remove person %s because they are on one or more lists (%s)",ident,table.concat(listsTheyreOn,", ")) | |
| 511 return false | |
| 512 end | |
| 513 return InitiateChange("RemoveToon", self, {id=id}) | |
| 514 end | |
| 515 function PersonList:IsRegistered(id) | |
| 516 if self:Select(id) ~= nil then return true end | |
| 517 end | |
| 518 function PersonList:AddReserve(id) | |
| 519 local le = self:Select(id) | |
| 520 if le then | |
| 521 -- todo: check that they're not already reserved | |
| 522 self.active.reserve[le:GetId()] = true | |
| 523 end | |
| 524 end | |
| 525 -- todo: remove reserve | |
| 526 function PersonList:IsActive(id) | |
| 527 return self.active.raid[id] or self.active.reserve[id] | |
| 528 end | |
| 529 function PersonList:AddMissing() | |
| 530 self:RefreshRaidList() | |
| 531 for _,name in pairs(self.active.raidExtras) do | |
| 532 printf("Person %s is missing from the persons list - adding",name) | |
| 533 self:Add(name) | |
| 534 end | |
| 535 -- TODO: batch into a single op - no need to spam 25 messages in a row | |
| 536 end | |
| 537 function PersonList:GetAllActiveIds() | |
| 538 self:RefreshRaidList() | |
| 539 local t = {} | |
| 540 for i,v in pairs(self.active.raid) do | |
| 541 if v then table.insert(t,i) end | |
| 542 end | |
| 543 for i,v in pairs(self.active.reserve) do | |
| 544 if v then table.insert(t,i) end | |
| 545 end | |
| 546 return t | |
| 547 end | |
| 548 | |
| 549 -- The following (adapted) code is from Xinhuan (wowace forum member) | |
| 550 -- Pre-create the unitId strings we will use | |
| 551 local pId = {} | |
| 552 local rId = {} | |
| 553 for i = 1, 4 do | |
| 554 pId[i] = sformat("party%d", i) | |
| 555 end | |
| 556 for i = 1, 40 do | |
| 557 rId[i] = sformat("raid%d", i) | |
| 558 end | |
| 559 function PersonList:RefreshRaidList() | |
| 560 local inParty = _G.GetNumPartyMembers() | |
| 561 local inRaid = _G.GetNumRaidMembers() | |
| 562 local add = function(unitNameArg) | |
| 563 local name = _G.UnitName(unitNameArg) | |
| 564 local te = self:Select(name) | |
| 565 if te then | |
| 566 self.active.raid[te:GetId()]=true | |
| 567 else | |
| 568 table.insert(self.active.raidExtras,name) | |
| 569 end | |
| 570 --if personName2id[name] ~= nil then | |
| 571 -- raidIdP[personName2id[name]]=true | |
| 572 --end | |
| 573 end | |
| 574 | |
| 575 self.active.raid = {} | |
| 576 self.active.raidExtras = {} | |
| 577 if inRaid > 0 then | |
| 578 for i = 1, inRaid do | |
| 579 add(rId[i]) | |
| 580 end | |
| 581 elseif inParty > 0 then | |
| 582 for i = 1, inParty do | |
| 583 add(pId[i]) | |
| 584 end | |
| 585 -- Now add yourself as the last party member | |
| 586 add("player") | |
| 587 else | |
| 588 -- You're alone | |
| 589 add("player") | |
| 590 end | |
| 591 end | |
| 592 | |
| 593 | |
| 594 function GetSafeTimestamp() | |
| 595 local changes = db.profile.changes | |
| 596 local ctime = time() | |
| 597 local n = getn(changes) | |
| 598 if n > 0 then | |
| 599 if changes[n].time >= ctime then | |
| 600 ctime = changes[n].time + 1 | |
| 601 end | |
| 602 end | |
| 603 return ctime | |
| 604 end | |
| 605 | |
| 606 function InitiateChange(finalizeAction,acceptor,arg) | |
| 607 local change = {} | |
| 608 change.time = GetSafeTimestamp() | |
| 609 change.action = finalizeAction | |
| 610 change.arg = arg | |
| 611 | |
| 612 if acceptor[finalizeAction](acceptor,arg,change.time) then | |
| 613 table.insert(db.profile.changes,change) | |
| 614 -- TODO: broadcast | |
| 615 return arg | |
| 616 else | |
| 617 return nil | |
| 618 end | |
| 619 end | |
| 620 function ProcessChange(change) | |
| 621 -- try list-o-lists and persons - if has matching function, call it | |
| 622 local action = change.action | |
| 623 if PersonList[action] then | |
| 624 PersonList[action](PersonList,change.arg,change.time) | |
| 625 return | |
| 626 elseif LootLists[action] then | |
| 627 LootLists[action](LootLists,change.arg,change.time) | |
| 628 return | |
| 629 else | |
| 630 -- pray that the change has a listIndex in it ... | |
| 631 if change.arg.listIndex then | |
| 632 local l = LootLists:Select(change.arg.listIndex) | |
| 633 if l and l[action] then | |
| 634 l[action](l,change.arg,change.time) | |
| 635 return | |
| 636 end | |
| 637 end | |
| 638 end | |
| 639 _G.error("Could not process change: " .. change.action) | |
| 640 end | |
| 137 | 641 |
| 138 function SelfDestruct() | 642 function SelfDestruct() |
| 139 lists = {} | 643 LootLists:Reset() |
| 140 persons = {} | 644 PersonList:Reset() |
| 141 db.profile.persons = {} | 645 db.profile.persons = {} |
| 142 db.profile.changes = {} | 646 db.profile.changes = {} |
| 143 db.profile.lists = {} | 647 db.profile.lists = {} |
| 144 raidNameP = {} | |
| 145 raidIdP = {} | |
| 146 reserveIdP = {} | |
| 147 personName2id = {} | |
| 148 end | 648 end |
| 149 | 649 |
| 150 -- Debugging {{{ | 650 -- Debugging {{{ |
| 151 function PrettyPrintList(listIndex) | 651 function PrettyPrintList(listIndex) |
| 152 local list = lists[listIndex] | 652 PersonList:RefreshRaidList() |
| 153 print("List: " .. list.name .. " (" .. listIndex .. ") - last modified " .. date("%m/%d/%y %H:%M:%S", list.time) .. " (",list.time,")" ) | 653 local le = LootLists:Select(listIndex) |
| 154 for i = 1,#list do | 654 print("List: " .. le:GetName() .. " (" .. le:GetId() .. ") - last modified " .. date("%m/%d/%y %H:%M:%S", le:GetTime()) .. " ("..le:GetTime()..")" ) |
| 155 print(" " .. i .. " - " .. persons[list[i].id].main) | 655 local pos = 1 |
| 656 for i in le:OrderedIdIter() do -- ordered iterator | |
| 657 local s = "" | |
| 658 if PersonList:IsActive(i) then | |
| 659 s = "*" | |
| 660 end | |
| 661 | |
| 662 print(" " .. pos .. " - " .. PersonList:Select(i):GetName() .. " ("..i..")",s) | |
| 663 pos = pos + 1 | |
| 156 end | 664 end |
| 157 end | 665 end |
| 158 function PrettyPrintLists() | 666 function PrettyPrintLists() |
| 159 for i,_ in pairs(lists) do | 667 for _,i in pairs(LootLists:GetAllIds()) do |
| 160 PrettyPrintList(i) | 668 PrettyPrintList(i) |
| 161 end | 669 end |
| 162 end | 670 end |
| 163 function PrintLists() | 671 function PrintLists() |
| 164 PrintTable(lists) | 672 PrintTable(LootLists) |
| 165 end | 673 end |
| 166 function PrintChanges() | 674 function PrintChanges() |
| 167 PrintTable(db.profile.changes) | 675 PrintTable(db.profile.changes) |
| 168 end | 676 end |
| 169 function PrintPersons() | 677 function PrintPersons() |
| 170 PrintTable(persons) | 678 PrintTable(PersonList) |
| 171 end | 679 end |
| 172 function PrintAPI(object) | 680 function PrintAPI(object) |
| 173 for i,v in pairs(object) do | 681 for i,v in pairs(object) do |
| 174 if type(v) == "function" then | 682 if type(v) == "function" then |
| 175 print("function "..i.."()") | 683 print("function "..i.."()") |
| 176 end | 684 end |
| 177 end | 685 end |
| 178 end | 686 end |
| 179 function PrintRaidAndReserve() | |
| 180 print("RaidNameP") | |
| 181 PrintTable(raidNameP) | |
| 182 print("RaidIdP") | |
| 183 PrintTable(raidIdP) | |
| 184 print("ReserveP") | |
| 185 PrintTable(reserveIdP) | |
| 186 print("personName2id") | |
| 187 PrintTable(personName2id) | |
| 188 end | |
| 189 --}}} | 687 --}}} |
| 190 | |
| 191 function UpdatePersonsReverse() | |
| 192 for i,v in pairs(persons) do | |
| 193 if i ~= "time" then | |
| 194 personName2id[v.main] = i | |
| 195 end | |
| 196 end | |
| 197 end | |
| 198 | 688 |
| 199 -- Change processing {{{ | 689 -- Change processing {{{ |
| 200 function CreateWorkingStateFromChanges(changes) | 690 function CreateWorkingStateFromChanges(changes) |
| 201 local personsBase = db.profile.persons | |
| 202 local lists = db.profile.lists | |
| 203 | |
| 204 -- copy the base to the working state | 691 -- copy the base to the working state |
| 205 wipe(lists) | 692 LootLists:ConstructFromDB(db) |
| 206 wipe(persons) | 693 PersonList:ConstructFromDB(db) |
| 207 wipe(personName2id) | |
| 208 | |
| 209 tcopy(lists,lists) | |
| 210 tcopy(persons,personsBase) | |
| 211 | 694 |
| 212 -- now just go through the changes list applying each | 695 -- now just go through the changes list applying each |
| 213 for i,v in ipairs(changes) do | 696 for i,v in ipairs(changes) do |
| 214 ProcessChange(v) | 697 ProcessChange(v) |
| 215 end | 698 end |
| 216 | |
| 217 -- update the persons reverse list | |
| 218 UpdatePersonsReverse() | |
| 219 end | |
| 220 | |
| 221 function CreateChange(change) | |
| 222 -- sanity | |
| 223 assert(change) | |
| 224 assert(change.action) | |
| 225 assert(change.arg) | |
| 226 | |
| 227 StartChange(change) | |
| 228 CommitChange(change) | |
| 229 end | |
| 230 | |
| 231 function StartChange(change) | |
| 232 local changes = db.profile.changes | |
| 233 change.time = time() | |
| 234 local n = getn(changes) | |
| 235 if n > 0 then | |
| 236 if changes[n].time >= change.time then | |
| 237 change.time = changes[n].time + 1 | |
| 238 end | |
| 239 end | |
| 240 end | |
| 241 | |
| 242 function CommitChange(change) | |
| 243 local changes = db.profile.changes | |
| 244 tinsert(changes,change) | |
| 245 -- TODO: broadcast change | |
| 246 end | |
| 247 | |
| 248 function ProcessChange(change) | |
| 249 if change.action == "AddPerson" then | |
| 250 DoAddPerson(change) | |
| 251 elseif change.action == "RenameList" then | |
| 252 DoRenameList(change) | |
| 253 elseif change.action == "CreateList" then | |
| 254 DoCreateList(change) | |
| 255 elseif change.action == "DeleteList" then | |
| 256 DoDeleteList(change) | |
| 257 elseif change.action == "AddToListEnd" then | |
| 258 DoAddPersonToListEnd(change) | |
| 259 elseif change.action == "AddToListRand" then | |
| 260 DoAddPersonToListRandom(change) | |
| 261 elseif change.action == "RemovePerson" then | |
| 262 DoRemovePerson(change) | |
| 263 elseif change.action == "RemovePersonFromList" then | |
| 264 DoRemovePersonFromList(change) | |
| 265 elseif change.action == "SuicidePerson" then | |
| 266 DoSuicidePerson(change) | |
| 267 else | |
| 268 print("Unknown message encountered") | |
| 269 PrintTable(change) | |
| 270 assert(false) | |
| 271 end | |
| 272 end | 699 end |
| 273 | 700 |
| 274 --}}} | 701 --}}} |
| 275 -- | 702 |
| 276 -- holy crap long winded {{{ | 703 -- holy crap long winded {{{ |
| 277 -- timestamp logic: | 704 -- timestamp logic: |
| 278 -- use time() for comparisons - local clients use date() to make it pretty. only | 705 -- use time() for comparisons - local clients use date() to make it pretty. only |
| 279 -- dowisde - we can't have a server timestamp. Which kind of sucks, but it turns | 706 -- dowisde - we can't have a server timestamp. Which kind of sucks, but it turns |
| 280 -- out you can change timezones when you enter an instance server, so you really | 707 -- out you can change timezones when you enter an instance server, so you really |
| 319 -- it's assumed that the change has been vetted elsewhere. These are very blunt | 746 -- it's assumed that the change has been vetted elsewhere. These are very blunt |
| 320 -- routines. | 747 -- routines. |
| 321 -- | 748 -- |
| 322 -- Note that "undo" has no special voodoo to it. It's basically a change that | 749 -- Note that "undo" has no special voodoo to it. It's basically a change that |
| 323 -- reverses the prior change on the stack.--}}} | 750 -- reverses the prior change on the stack.--}}} |
| 324 function DoAddPerson(change)--{{{ | |
| 325 assert(change) | |
| 326 assert(change.arg.id) | |
| 327 -- require admin | |
| 328 local persons = persons | |
| 329 local name = change.arg.name | |
| 330 local id = change.arg.id | |
| 331 assert(persons[id]==nil) | |
| 332 persons[id] = {main=name,class=change.arg.class} | |
| 333 persons.time=change.time | |
| 334 personName2id[name] = id | |
| 335 return true | |
| 336 end--}}} | |
| 337 function AddPerson(name)--{{{ | 751 function AddPerson(name)--{{{ |
| 338 local persons = persons | 752 print("Adding ... " .. name) |
| 339 local guid = _G.UnitGUID(name) | 753 PersonList:Add(name) |
| 340 -- TODO: check guid to be sure it's a player | |
| 341 if not guid then | |
| 342 printf("Could not add player %s - they must be in range or group",name) | |
| 343 return | |
| 344 end | |
| 345 local _,englishClass = _G.UnitClass(name) | |
| 346 --print("Person " .. name .. " is class " .. englishClass) | |
| 347 local id = string.sub(guid,6) -- skip at least 0x0580 ... | |
| 348 id = id:gsub("^0*(.*)","%1") -- nom all leading zeroes remaining | |
| 349 | |
| 350 if persons[id] and persons[id] ~= name then | |
| 351 printf("Namechange detected for %s - new is %s, please rename the existing entry", persons[id].main, name) | |
| 352 return | |
| 353 end | |
| 354 if persons[id] ~= nil then | |
| 355 printf("%s is already in the persons list; disregarding", name) | |
| 356 return | |
| 357 end | |
| 358 local change = {action="AddPerson",arg={name=name,id=id,class=englishClass}} | |
| 359 if DoAddPerson(change) then | |
| 360 CreateChange(change) | |
| 361 end | |
| 362 end--}}} | |
| 363 function DoCreateList(change)--{{{ | |
| 364 --if GetListIndex(change.arg.name) then | |
| 365 -- rintf(("List %s already exists",v.name) | |
| 366 -- return false | |
| 367 --end | |
| 368 lists[change.arg.id]={name=change.arg.name,time=change.time} | |
| 369 return true | |
| 370 end--}}} | 754 end--}}} |
| 371 function CreateList(name)--{{{ | 755 function CreateList(name)--{{{ |
| 372 -- require admin | 756 -- require admin |
| 373 local change={action="CreateList",arg={name=name}} | |
| 374 StartChange(change) | |
| 375 change.arg.id=change.time -- use the creation timestamp as the list's index. it's as unique as anything... | |
| 376 print("Creating ... " .. name) | 757 print("Creating ... " .. name) |
| 377 if DoCreateList(change) then | 758 return LootLists:Create(name) |
| 378 CommitChange(change) | |
| 379 end | |
| 380 end--}}} | |
| 381 function DoAddPersonToListEnd(change)--{{{ | |
| 382 local list = lists[change.arg.listIndex] | |
| 383 local index | |
| 384 if getn(list) > 0 then | |
| 385 index = list[#list].index + 0.1 | |
| 386 else | |
| 387 index = 0.1 | |
| 388 end | |
| 389 local entry = {index=index, id=change.arg.id} | |
| 390 | |
| 391 tinsert(list,entry) | |
| 392 list.time = change.time | |
| 393 list.closedRandom = true | |
| 394 | |
| 395 return true | |
| 396 end--}}} | 759 end--}}} |
| 397 function AddPersonToListEnd(name,listName)--{{{ | 760 function AddPersonToListEnd(name,listName)--{{{ |
| 398 -- require admin | 761 -- require admin |
| 399 local listIndex = GetListIndex(listName) | 762 local l = LootLists:Select(listName) |
| 400 local id = personName2id[name] | 763 local te = PersonList:Select(name) |
| 401 if IdIsInList(id,lists[listIndex]) then | 764 -- TODO: if not te ... |
| 402 printf("Person %s is already on the reqeuested list",name) | 765 printf("Adding %s (%s) to list %s", name, te:GetId(), listName) |
| 403 return false | 766 return l:InsertEnd(te:GetId()) |
| 404 end | |
| 405 printf("Adding %s (%s) to list %s (%s)", name, id, listName, listIndex) | |
| 406 local change = {action="AddToListEnd",arg={id=id,listIndex=listIndex}} | |
| 407 StartChange(change) | |
| 408 if DoAddPersonToListEnd(change) then | |
| 409 CommitChange(change) | |
| 410 end | |
| 411 end--}}} | |
| 412 function DoAddPersonToListRandom(change)--{{{ | |
| 413 local list = lists[change.arg.listIndex] | |
| 414 local entry = {index=change.arg.roll, id=change.arg.id} | |
| 415 | |
| 416 tinsert(list,entry) | |
| 417 table.sort(list,function(a,b) return a.index < b.index end) | |
| 418 list.time = change.time | |
| 419 | |
| 420 return true | |
| 421 end--}}} | 767 end--}}} |
| 422 function AddPersonToListRandom(name,listName)--{{{ | 768 function AddPersonToListRandom(name,listName)--{{{ |
| 423 -- require admin | 769 -- require admin |
| 424 local listIndex = GetListIndex(listName) | 770 local l = LootLists:Select(listName) |
| 425 if lists[listIndex].closedRandom then | 771 local te = PersonList:Select(name) |
| 426 print("Cannot add person to list by random roll because an add-to-end operation has already occurred") | 772 -- TODO: if not te ... |
| 427 return false | 773 printf("Adding %s (%s) to list %s - randomly!", name, te:GetId(), listName) |
| 428 end | 774 return l:InsertRandom(te:GetId()) |
| 429 local id = personName2id[name] | |
| 430 if IdIsInList(id,lists[listIndex]) then | |
| 431 printf("Person %s is already on the reqeuested list",name) | |
| 432 return false | |
| 433 end | |
| 434 local roll = math.random() | |
| 435 printf("Adding %s (%s) to list %s (%s) with roll (%f)", name, id, listName, listIndex, roll) | |
| 436 local change = {action="AddToListRand",arg={id=id,listIndex=listIndex,roll=roll}} | |
| 437 StartChange(change) | |
| 438 if DoAddPersonToListRandom(change) then | |
| 439 CommitChange(change) | |
| 440 end | |
| 441 end--}}} | |
| 442 function DoRemovePerson(change)--{{{ | |
| 443 local person = persons[change.arg.id] | |
| 444 personName2id[person.main] = nil | |
| 445 persons[change.arg.id] = nil | |
| 446 persons.time = change.time | |
| 447 return true | |
| 448 end--}}} | |
| 449 function RemovePerson(name)--{{{ | |
| 450 local id = personName2id[name] | |
| 451 if not id then | |
| 452 printf("%s is not in the persons list, please check your spelling", name) | |
| 453 return false | |
| 454 end | |
| 455 local listsTheyreOn = {} | |
| 456 -- check if they're active on any loot list | |
| 457 for i,v in pairs(lists) do | |
| 458 if IdIsInList(id,v) then | |
| 459 tinsert(listsTheyreOn,v.name) | |
| 460 break | |
| 461 end | |
| 462 end | |
| 463 if getn(listsTheyreOn) > 0 then | |
| 464 printf("Cannot remove person %s because they are on one or more lists (%s)",name,table.concat(listsTheyreOn,", ")) | |
| 465 return false | |
| 466 end | |
| 467 local change = {action="RemovePerson",arg={id=id}} | |
| 468 StartChange(change) | |
| 469 if DoRemovePerson(change) then | |
| 470 CommitChange(change) | |
| 471 end | |
| 472 end--}}} | |
| 473 function DoSuicidePerson(change)--{{{ | |
| 474 local list = lists[change.arg.listIndex] | |
| 475 local affected = shallowCopy(change.arg.affect) | |
| 476 -- the goal here is to rotate the suicide list by 1 | |
| 477 -- then we can just mash it on top of the intersection between the original | |
| 478 -- list and the working copy | |
| 479 | |
| 480 local replacement = shallowCopy(change.arg.affect) | |
| 481 local temp = table.remove(replacement,1) -- pop | |
| 482 tinsert(replacement,temp) -- push_back | |
| 483 --rintf(("Before suicide of %s on list %s",slist[1],list.name) | |
| 484 --PrintTable(list) | |
| 485 for i = 1, #list do | |
| 486 if list[i].id == affected[1] then | |
| 487 table.remove(affected,1) | |
| 488 list[i].id = replacement[1] | |
| 489 table.remove(replacement,1) | |
| 490 end | |
| 491 end | |
| 492 list.time=change.time | |
| 493 return true | |
| 494 end--}}} | 775 end--}}} |
| 495 function SuicidePerson(name,listName)--{{{ | 776 function SuicidePerson(name,listName)--{{{ |
| 496 -- require admin | 777 -- require admin |
| 497 PopulateRaidList() | 778 PersonList:RefreshRaidList() |
| 498 local listIndex = GetListIndex(listName) | 779 local le = LootLists:Select(listName) |
| 499 local id = personName2id[name] | 780 local te = PersonList:Select(name) |
| 500 local affect=GetSuicideList(id,lists[listIndex]) | 781 return le:SuicidePerson(te:GetId()) |
| 501 local change = {action="SuicidePerson",arg={affect=affect,listIndex=listIndex}} | |
| 502 StartChange(change) | |
| 503 if DoSuicidePerson(change) then | |
| 504 CommitChange(change) | |
| 505 end | |
| 506 end--}}} | |
| 507 function DoRenameList(change)--{{{ | |
| 508 lists[change.arg.listIndex].name = change.arg.name | |
| 509 lists[change.arg.listIndex].time = change.time | |
| 510 return true | |
| 511 end--}}} | 782 end--}}} |
| 512 function RenameList(listName,newListName)--{{{ | 783 function RenameList(listName,newListName)--{{{ |
| 513 -- require admin | 784 -- require admin |
| 514 local listIndex = GetListIndex(listName) | 785 local le = LootLists:Select(listName) |
| 515 local change = {action="RenameList",arg={listIndex=listIndex,name=newListName}} | 786 return le:RenameList(newListName) |
| 516 StartChange(change) | |
| 517 if DoRenameList(change) then | |
| 518 CommitChange(change) | |
| 519 end | |
| 520 end--}}} | |
| 521 function DoDeleteList(change)--{{{ | |
| 522 lists[change.arg.listIndex] = nil | |
| 523 return true | |
| 524 end--}}} | 787 end--}}} |
| 525 function DeleteList(listName)--{{{ | 788 function DeleteList(listName)--{{{ |
| 526 local listIndex = GetListIndex(listName) | 789 return LootLists:DeleteList(LootLists:Select(listName):GetId()) |
| 527 local change = {action="DeleteList",arg={listIndex=listIndex}} | |
| 528 StartChange(change) | |
| 529 if DoDeleteList(change) then | |
| 530 CommitChange(change) | |
| 531 end | |
| 532 end--}}} | |
| 533 function DoRemovePersonFromList(change)--{{{ | |
| 534 local list = lists[change.arg.listIndex] | |
| 535 | |
| 536 for i,v in ipairs(list) do | |
| 537 if v.id == change.arg.id then | |
| 538 table.remove(list,i) | |
| 539 break | |
| 540 end | |
| 541 end | |
| 542 table.sort(list,function(a,b) return a.index < b.index end) | |
| 543 list.time = change.time | |
| 544 return true | |
| 545 end--}}} | 790 end--}}} |
| 546 function RemovePersonFromList(name,listName)--{{{ | 791 function RemovePersonFromList(name,listName)--{{{ |
| 547 local listIndex = GetListIndex(listName) | 792 local le = LootLists:Select(listName) |
| 548 local pid = personName2id[name] | 793 local te = PersonList:Select(name) |
| 549 -- todo: check that they're on the list in the first place | 794 return le:Remove(te:GetId()) |
| 550 local change = {action="RemovePersonFromList",arg={id=pid,listIndex=listIndex}} | |
| 551 StartChange(change) | |
| 552 if DoRemovePersonFromList(change) then | |
| 553 CommitChange(change) | |
| 554 end | |
| 555 end | 795 end |
| 556 --}}} | 796 --}}} |
| 797 function RemovePerson(person) | |
| 798 print("Removing " .. person) | |
| 799 PersonList:Remove(person) | |
| 800 end | |
| 801 function ReservePerson(person) | |
| 802 print("Reserving " .. person) | |
| 803 PersonList:AddReserve(person) | |
| 804 end | |
| 557 --}}} | 805 --}}} |
| 558 -- Higher order actions (ie calls other standard actions){{{ | 806 -- Higher order actions (ie calls other standard actions){{{ |
| 559 | 807 |
| 560 function TrimLists(time) | 808 function TrimLists(time) |
| 561 if not CheckListCausality() then | 809 if not CheckListCausality() then |
| 579 | 827 |
| 580 -- apply first half | 828 -- apply first half |
| 581 CreateWorkingStateFromChanges(before) | 829 CreateWorkingStateFromChanges(before) |
| 582 | 830 |
| 583 -- save this state permanently; trim the changes permanently | 831 -- save this state permanently; trim the changes permanently |
| 584 tcopy(db.profile.persons,persons) | 832 LootLists:SaveToDB(db) |
| 585 tcopy(db.profile.lists,lists) | 833 PersonList:SaveToDB(db) |
| 586 while db.profile.changes ~= nil and db.profile.changes[1] ~= nil and db.profile.changes[1].time <= time do | 834 while db.profile.changes ~= nil and db.profile.changes[1] ~= nil and db.profile.changes[1].time <= time do |
| 587 table.remove(db.profile.changes,1) | 835 table.remove(db.profile.changes,1) |
| 588 end | 836 end |
| 589 | 837 |
| 590 -- using the trimmed list and the new bases, recreate the working state | 838 -- using the trimmed list and the new bases, recreate the working state |
| 591 CreateWorkingStateFromChanges(db.profile.changes) | 839 CreateWorkingStateFromChanges(db.profile.changes) |
| 592 end | 840 end |
| 593 | 841 |
| 594 function AddMissingPersons() | |
| 595 PopulateRaidList() | |
| 596 local t = {} | |
| 597 for id,_ in pairs(persons) do | |
| 598 t[id] = true | |
| 599 end | |
| 600 for name,_ in pairs(raidNameP) do | |
| 601 if personName2id[name] == nil then | |
| 602 printf("Person %s is missing from the persons list - adding",name) | |
| 603 AddPerson(name) | |
| 604 end | |
| 605 end | |
| 606 -- TODO: batch into a single op - no need to spam 25 messages in a row | |
| 607 end | |
| 608 | 842 |
| 609 function PopulateListRandom(listIndex) | 843 function PopulateListRandom(listIndex) |
| 610 -- difference (raid+reserve)-list, then random shuffle that, then add | 844 -- difference (raid+reserve)-list, then random shuffle that, then add |
| 611 PopulateRaidList() | 845 local actives = PersonList:GetAllActiveIds() |
| 612 local list = lists[listIndex] | 846 local list = LootLists:Select(listIndex) |
| 613 | 847 |
| 614 local t = {} -- after loops, contains intersection of IDs present between raid and reserve | 848 --swap keys on actives |
| 615 for i,v in pairs(raidIdP) do | 849 local t = {} |
| 616 if v then t[i] = true end | 850 for _,v in pairs(actives) do t[v] = true end |
| 617 end | |
| 618 for i,v in pairs(reserveIdP) do | |
| 619 if v then t[i] = true end | |
| 620 end | |
| 621 | 851 |
| 622 -- now remove from t all of the people already present on the list | 852 -- now remove from t all of the people already present on the list |
| 623 if list then | 853 if t then |
| 624 for i = 1,#list do | 854 for id in list:OrderedIdIter() do -- id iterator |
| 625 if t[list[i].id] then | 855 if t[id] then |
| 626 t[list[i].id] = false | 856 t[id] = false |
| 627 end | 857 end |
| 628 end | 858 end |
| 629 end | 859 end |
| 630 | 860 |
| 631 -- add all remaining | 861 -- add all remaining |
| 632 for i,v in pairs(t) do | 862 for i,v in pairs(t) do |
| 633 if v then | 863 if v then |
| 634 AddPersonToListRandom(persons[i].main,list.name) -- TODO: APTLR keys off of string names. probably need to change this. | 864 AddPersonToListRandom(i,list:GetId()) |
| 635 end | 865 end |
| 636 end | 866 end |
| 637 end | 867 end |
| 638 | |
| 639 function NukePerson(name) -- delete from all lists and then from persons | 868 function NukePerson(name) -- delete from all lists and then from persons |
| 640 local pid = personName2id[name] | 869 for _,id in pairs(LootLists:GetAllIds()) do |
| 641 for i,v in pairs(lists) do | 870 RemovePersonFromList(name,id) |
| 642 RemovePersonFromList(name,v.name) | |
| 643 end | 871 end |
| 644 RemovePerson(name) | 872 RemovePerson(name) |
| 645 end | 873 end |
| 646 --}}} | 874 --}}} |
| 647 -- "Soft" actions- ie things that cause nonpermanent state {{{ | |
| 648 | |
| 649 -- reserves | |
| 650 function AddReserve(name) | |
| 651 print("Reserving" .. name) | |
| 652 reserveIdP[personName2id[name]]=true | |
| 653 -- TODO: communicate to others. don't store this in any way. | |
| 654 end | |
| 655 | |
| 656 function RemoveReserve(name) | |
| 657 reserveIdP[personName2id[name]]=false | |
| 658 -- TODO: communicate to others. don't store this in any way. | |
| 659 end | |
| 660 | |
| 661 | |
| 662 --function GetActiveList() | |
| 663 -- return lists[1] -- todo! | |
| 664 --end | |
| 665 | |
| 666 --}}} | |
| 667 | |
| 668 -- The following (adapted) code is from Xinhuan (wowace forum member) | |
| 669 -- Pre-create the unitID strings we will use | |
| 670 local pID = {} | |
| 671 local rID = {} | |
| 672 for i = 1, 4 do | |
| 673 pID[i] = sformat("party%d", i) | |
| 674 end | |
| 675 for i = 1, 40 do | |
| 676 rID[i] = sformat("raid%d", i) | |
| 677 end | |
| 678 function PopulateRaidList() | |
| 679 local inParty = _G.GetNumPartyMembers() | |
| 680 local inRaid = _G.GetNumRaidMembers() | |
| 681 local add = function(unitNameArg) | |
| 682 local name = _G.UnitName(unitNameArg) | |
| 683 raidNameP[name]=true | |
| 684 if personName2id[name] ~= nil then | |
| 685 raidIdP[personName2id[name]]=true | |
| 686 end | |
| 687 end | |
| 688 | |
| 689 wipe(raidNameP) | |
| 690 wipe(raidIdP) | |
| 691 if inRaid > 0 then | |
| 692 for i = 1, inRaid do | |
| 693 add(rID[i]) | |
| 694 end | |
| 695 elseif inParty > 0 then | |
| 696 for i = 1, inParty do | |
| 697 add(pID[i]) | |
| 698 end | |
| 699 -- Now add yourself as the last party member | |
| 700 add("player") | |
| 701 else | |
| 702 -- You're alone | |
| 703 add("player") | |
| 704 end | |
| 705 --PrintTable(raidNameP) | |
| 706 end | |
| 707 | 875 |
| 708 -- undo rules! | 876 -- undo rules! |
| 709 -- only the most recent event can be undone | 877 -- only the most recent event can be undone |
| 710 -- ^^^ on a given list? | 878 -- ^^^ on a given list? |
| 711 -- algorithm is easy, given "Suicide A B C" | 879 -- algorithm is easy, given "Suicide A B C" |
| 712 -- just find A,B,C in the list and replace in order from the s message | 880 -- just find A,B,C in the list and replace in order from the s message |
| 713 -- while undo is allowed *per-list*, certain events in the stream will | 881 -- while undo is allowed *per-list*, certain events in the stream will |
| 714 -- prevent proper undo, such as add/delete player or add/delete list | 882 -- prevent proper undo, such as add/delete player or add/delete list |
| 715 | |
| 716 | |
| 717 function GetSuicideList(id,list) | |
| 718 --print("Calculating changeset for "..name.." from list -") | |
| 719 --PrintTable(list) | |
| 720 local t = {} | |
| 721 local ret = {} | |
| 722 local pushing = false | |
| 723 for i = 1, #list do | |
| 724 if list[i].id == id then | |
| 725 pushing = true | |
| 726 end | |
| 727 if pushing and (raidIdP[list[i].id] or reserveIdP[list[i].id]) then | |
| 728 tinsert(ret,list[i].id) | |
| 729 end | |
| 730 end | |
| 731 --print("GSL") | |
| 732 --PrintTable(ret) | |
| 733 --print("GSL") | |
| 734 return ret | |
| 735 end | |
| 736 | |
| 737 function IdIsInList(id,listRef) | |
| 738 for i = 1,#listRef do | |
| 739 if id == listRef[i].id then | |
| 740 return true | |
| 741 end | |
| 742 end | |
| 743 return false | |
| 744 end | |
| 745 | 883 |
| 746 -- returns true if the events in the list are in time order | 884 -- returns true if the events in the list are in time order |
| 747 function CheckListCausality() | 885 function CheckListCausality() |
| 748 local t = nil | 886 local t = nil |
| 749 for i,v in ipairs(db.profile.changes) do | 887 for i,v in ipairs(db.profile.changes) do |
| 755 t = v.time | 893 t = v.time |
| 756 end | 894 end |
| 757 return true | 895 return true |
| 758 end | 896 end |
| 759 | 897 |
| 760 -- Support functions | |
| 761 | |
| 762 function GetListIndex(name) | |
| 763 for i,v in pairs(lists) do | |
| 764 if v.name == name then | |
| 765 return i | |
| 766 end | |
| 767 end | |
| 768 return nil | |
| 769 end | |
| 770 | |
| 771 |
