view State.lua @ 98:0cd1d46e7b66

Admin detection from a string of the format "BSKADMIN: admin1, admin2, ..." in the guild info box
author John@Doomsday
date Fri, 27 Apr 2012 09:41:26 -0400
parents b89558d3e833
children
line wrap: on
line source
local bsk=bsk
local _G=_G
local table=table 
local pairs=pairs
local setmetatable=setmetatable
local ipairs=ipairs
local string=string
local sformat=string.format
local tostring=tostring
local type=type
local unpack=unpack
local getn=getn
setfenv(1,bsk)

-- simple state machine

--Begin loot
--Activate list ... only during looting?
--open bidding/rolling
--bid/roll occurred
--remove bid/roll
--close bidding
--remove item


-- we'll track state, but that may or may not result in a GUI change until down
-- the road

-- todo: transmit this all to only the raid, not the guild?

-- sample procedure
-- person B opens GUI.
-- person A begins looting, sets a list
-- person A begins bidding, transmists the state
-- person B goes into bidding state, their button activates
-- person B clicks the button. button changes state.
-- person B broadcasts their bid. if a bid, everyone just accepts it.
--              - if a roll, then the master does the roll and rebroadcasts

state = "neutral" -- states that are possible: neutral, looting, bidding
local looting = false
stateactive = nil
stateitem = nil
stateitemlist = {}
statebids = {}
staterolls = {}
staterollvalues = {}
stateactivelist = nil 

-- todo: protect initiators to admin only

local rollListeners = {}
function RegisterListenerRolls(listener)
    table.insert(rollListeners,listener)
end
function AlertRollListeners()
    for i,v in pairs(rollListeners) do
        print("roll out")
        v["RollEvent"](v)
    end
end

local listChangeListeners = {}
function RegisterListenerActiveListChanged(listener)
    table.insert(listChangeListeners,listener)
end
function AlertActiveListChangedListeners()
    for i,v in pairs(listChangeListeners) do
        print("list out")
        v["ActiveListEvent"](v)
    end
end

local stateChangeListeners = {}
function RegisterListenerStateChange(listener)
    table.insert(stateChangeListeners,listener)
end
function AlertStateChangeListeners()
    for i,v in pairs(stateChangeListeners) do
        print("state out")
        v["StateEvent"](v)
    end
end

local itemListListeners = {}
function RegisterItemListListener(listener)
    table.insert(itemListListeners,listener)
end
function AlertItemListListeners()
    for i,v in pairs(itemListListeners) do
        print("item change")
        v["ItemListEvent"](v)
    end
end

function BeginLoot(packet)
    local items, listValue = unpack(packet)
    if state == "neutral" then
        state = "looting"
        stateactivelist = listValue
        stateitemlist = items
        AlertItemListListeners()
        CreateGUI() -- todo: bad spot, but that's ok for now
    else
        _G.error("Bad state transition", state, "looting")
    end
end

function InitiateBeginLoot(items,listValue)
    if state == "neutral" then
        Comm:SendStateChange("BL",items,listValue)
    end
end

-- Activate List {{{
function ActivateList(packet) -- doesn't cause a transition, but we only enforce a list selection during certain times
    local list = unpack(packet)
    if state == "looting" then
        stateactivelist = list
        AlertActiveListChangedListeners()
    end
end

function InitiateActivateList(list)
    if state == "looting" then
        Comm:SendStateChange("AL",list)
    end
end
--}}}
-- Open Bidding {{{
function OpenBid(packet)
    local item = unpack(packet)
    if state == "looting" then
        state = "bidding"
        stateitem = item
        AlertStateChangeListeners()
        if admin and masterLooterIsMe then
            local chan
            if _G.GetNumRaidMembers() > 0 then chan = "RAID" else chan = "PARTY" end
            _G.SendChatMessage("Bidding is now open for "..item.link.."!",chan)
            _G.SendChatMessage("Whisper me \"bid\" to bid or \"roll\" to offset roll; \"retract\" will remove your bid",chan)
        end
    end
end

function InitiateOpenBid(item)
    if state == "looting" then
        Comm:SendStateChange("OB",item)
    end
end
--}}}
-- Bid {{{
local function SortByList(lref,unordered)
    local t = {}
    for le in lref:OrderedListEntryIter() do
        local lid = le:GetId()
        for i,v in pairs(unordered) do
            if v.value == lid then
                print("Sort time: insert", lid)
                table.insert(t,v)
                table.remove(unordered,i)
                break -- done with this inner iteration
            end
        end
    end
    if getn(t) > 0 then
        for i,v in pairs(unordered) do
            printf("Disregarding bid/roll from %s because they are not on the list", v.textPlain)
        end
    end
    return t
end
local function SortByRoll(values,unordered)
    local t = {}
    for r = 100,1,-1 do -- 100:1
        if values[r] then
            for i,v in pairs(unordered) do
                if v.value == values[r].value then
                    print("Sort time: insert", v.value, r)
                    table.insert(t,v)
                    table.remove(unordered,i)
                    break
                end
            end
        end
    end
    if getn(t) > 0 then
        for i,v in pairs(unordered) do
            printf("Disregarding bid/roll from %s because they are not on the list", v.textPlain)
        end
    end
    return t
end
function ReceivedBid(packet) -- no state transition, but only matters during one state
    local person, roll = unpack(packet)

    if state == "bidding" then
        if roll then
            table.insert(staterolls,person)
            staterollvalues[roll] = person
            staterolls = SortByRoll(staterollvalues,staterolls)
        else
            local lref = LootLists:Select(stateactivelist)
            table.insert(statebids,person)
            statebids = SortByList(lref,statebids)
        end
        AlertRollListeners()
        if admin and masterLooterIsMe then
            local leader
            if getn(statebids) > 0 then
                leader = statebids[1].textPlain
            elseif getn(staterolls) > 0 then
                leader = staterolls[1].textPlain
            end

            local chan -- todo: this idiom is wearing on me already
            if _G.GetNumRaidMembers() > 0 then chan = "RAID" else chan = "PARTY" end

            if roll then
                _G.SendChatMessage(sformat("Received roll of %d from %s; current leader is %s", roll, person.textPlain, leader),chan)
            else
                _G.SendChatMessage(sformat("Received bid from %s; current leader is %s", person.textPlain, leader),chan) -- todo: tell what spot they're bidding from 
            end
        end
    end

    -- else ignore ...
end

function InitiateBid(person,roll)
    if state == "bidding" then
        if not person then
            print("You cannot bid becuase you are not on the list")
            return
        end
        for i,v in pairs(statebids) do
            if person and person.value == v.value then
                print(person.value .. " is already on the list")
                return -- no double adds please
            end
        end
        for i,v in pairs(staterolls) do
            if person and person.value == v.value then
                print(person.value .. " is already on the list")
                return -- no double adds please
            end
        end
        Comm:SendStateChange("RB",person,roll)
    end
end
--}}}
-- Retration {{{
function ReceivedRetraction(packet)
    local person = unpack(packet)
    if state == "bidding" then
        for i,v in pairs(statebids) do
            if v.value == person.value then
                table.remove(statebids,i)
                AlertRollListeners()
                return
            end
        end
        for i,v in pairs(staterolls) do
            if v.value == person.value then
                table.remove(staterolls,i)
                AlertRollListeners()
                return
            end
        end
    end
end

function InitiateRetract(person)
    if state == "bidding" then
        Comm:SendStateChange("RR",person)
    end
end
--}}}
-- Close Bidding {{{
function CloseBidding(packet)
    local awardedTo = unpack(packet)  -- todo: unused
    if state == "bidding" then
        state = "looting"
        stateitem = nil
        statebids = {}
        staterolls = {}
        staterollvalues = {}
        AlertStateChangeListeners()
        AlertItemListListeners()
        -- todo: record history
        if admin then
            local chan -- todo: this idiom is wearing on me already
            if _G.GetNumRaidMembers() > 0 then chan = "RAID" else chan = "PARTY" end
            _G.SendChatMessage("Bidding is closed",chan)
        end
    end
end

function InitiateCloseBidding(awardedTo)
    if state == "bidding" then
        Comm:SendStateChange("CB",awardedTo)
    end
end
--}}}
-- Close Looting {{{
function CloseLooting(packet)
    state = "neutral"
    stateactive = nil
    stateitem = nil
    stateitemlist = {}
    statebids = {}
    staterolls = {}
    staterollvalues = {}
    AlertStateChangeListeners()
    AlertItemListListeners()
end

function InitiateCloseLooting()
    Comm:SendStateChange("CL")
end
--}}}
function RollRequest(packet)
    local person = unpack(packet)
    
    if state == "bidding" and admin and masterLooterIsMe then
        local roll
        for i,v in pairs(staterollvalues) do
            if v and v.value == person.value then
                roll = i
                break
            end
        end
        if not roll then -- roll isn't on cache
            repeat -- random until you find a good value
                roll = _G.random(100)
            until staterollvalues[roll] == nil
            print("rolling! ", roll)
            staterollvalues[roll] = person
        end
        InitiateBid(person,roll)
    end
end
function InitiateRollRequest(person)
    if state == "bidding" then
        if not person then
            print("You cannot bid becuase you are not on the list")
            return
        end
        for i,v in pairs(staterolls) do
            if person and person.value == v.value then
                print(person.value .. " is already on the list")
                return -- no double adds please
            end
        end
        for i,v in pairs(statebids) do
            if person and person.value == v.value then
                print(person.value .. " is already on the list")
                return -- no double adds please
            end
        end
        Comm:SendStateChange("IR",person,roll)
    end
end

local function LootSlotCleared(packet)
    local index = unpack(packet)
    if state == "looting" or state == "bidding" then
        for i,v in pairs(stateitemlist) do
            if v.mlid == index then
                v.disabled = true
                print("DISABLING")
                AlertItemListListeners()
                return
            end
        end
    end
end
function InitiateLSClear(index)
    if state == "looting" or state == "bidding" then
        Comm:SendStateChange("SC",index)
    end
end
function DispatchState(packet)
    local state = table.remove(packet,1)
    print("Dispatching", state)
    if state == "RB" then
        ReceivedBid(packet)
    elseif state == "BL" then
        BeginLoot(packet)
    elseif state == "RR" then
        ReceivedRetraction(packet)
    elseif state == "OB" then
        OpenBid(packet)
    elseif state == "CB" then
        CloseBidding(packet)
    elseif state == "CL" then
        CloseLooting(packet)
    elseif state == "AL" then
        ActivateList(packet)
    elseif state == "IR" then
        RollRequest(packet)
    elseif state == "SC" then
        LootSlotCleared(packet)
    else
        _G.error("Cannot dispatch message of type:",state)
    end
end

function InitializeState()
    -- basically, find a value for stateactivelist. it really doesn't matter
    -- which one, but I decided on trying to choose one that has entries on it
    -- so the whole thing isn't all empty. stateactivelist being anything
    -- besides a valid ID could trigger errors
    local ltemp = 0
    local lids = LootLists:GetAllIds()
    for _,v in pairs(lids) do
        local l = LootLists:Select(v)
        if l:GetLength() > 0 then
            if ltemp == 0 then
                ltemp = l:GetId()
            end
        end
    end
    stateactivelist = ltemp
end