Advertisement
my_hat_stinks

TTS - Trader

Apr 8th, 2020
1,643
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 26.74 KB | None | 0 0
  1. ------------------------
  2. -- Collectable Trader --
  3. ------------------------
  4. --- by my_hat_stinks ---
  5. ------------------------
  6.  
  7. -- If you're looking in this file, you probably want to know
  8. -- all the possible formats for collectable item descriptions.
  9. --
  10. --
  11. -- Details
  12. -- Collectable <append> - This is REQUIRED on the first line of every collectable.
  13. --                        `Collectible` is also acceptable.
  14. --                        If you use the <append> value, it will be added to the end
  15. --                        of the collectable's name in the trade menu. This allows you
  16. --                        two have two separate listings for one item.
  17. --
  18. -- Cost <quantity> <name> - The cost for the item. If <quantity> is excluded, it will default
  19. --                       to one of the listed item. <name> must match the required item
  20. --                       name exactly, including formatting.
  21. --                       Multiple Cost lines are allowed, all items will be required.
  22. --                       If <name> matches a set, all items in that set are added to cost.
  23. --
  24. -- CostType <type> - Cost type, defaults to "all" if excluded. Only one CostType can be listed.
  25. --                   All - All listed Cost items (including sets) are required to purchase
  26. --                         this item.
  27. --                   Any - Any listed Cost items (matching <quantity>) can be used to purchase
  28. --                         this item.
  29. --                         If a set is listed in Cost, <quantity> is the number of items from the
  30. --                         set required to purchase this item.
  31. --
  32. -- Requires <name> - Items that are required, but NOT consumed, when purchasing this listing.
  33. --                   Completely optional.  <name> must match the required item name exactly,
  34. --                   including formatting.
  35. --                   `Require` is also acceptable.
  36. --
  37. -- Limit <quantity> - The user can't purchase another of this item if they already have <quantity>.
  38. --                    If the item is part of a set, the limit counts for the entire set.
  39. --                    Limits will not be reliable for non-Blackjack tables.
  40. --                    
  41. --
  42. -- SpawnAs <name> - Optional. Renames this item when it is spawned. Can include additional variables
  43. --                  for dynamic names, case sensitive:
  44. --                   {name} - Changes to the Steam name of the user.
  45. --                   {id} - Changes to the Steam ID of the user.
  46. --                   {color} - Changes to the color of the user at time of purchase.
  47. --                  os.date() formatting is also available, eg "%Y-%b-%d" to mark the purchase date.
  48. --
  49. -- Category <Directory> - The menu this item should appear under. `>` indicates a sub-menu.
  50. --                        Items without a Category will be listed on the main menu.
  51. --                        Only one Category can be listed per item. Use Collectable <append>
  52. --                        for additional listings.
  53. --
  54. -- Set <set name> - Optional. Marks this item as part of a set. Items do not need to be purchasable
  55. --                  to be part of a set.
  56. --
  57. --
  58. -- Complete Example:
  59. --
  60. -- Collectable
  61. -- CostType: All
  62. -- Cost: 20 Collectable token
  63. -- Cost: Reward token
  64. -- Requires: [DEB444]Prestige 8[-]
  65. -- Require: Bankruptcy token
  66. -- Category: Main Directory > Sub Directory > Bottom Directory
  67. --
  68.  
  69.  
  70. TradeItems = {}
  71.  
  72. DirectoryPath = {}
  73.  
  74. ListData = {}
  75. ListReference = {}
  76. ListPage = 0
  77. ListTitle = ""
  78.  
  79. AdminMode = false
  80.  
  81. SpawnPos = { 1.168, 3.01, 0.02 }
  82.  
  83. COST_ALL = 0
  84. COST_ANY = 1
  85.  
  86. function onLoad()
  87.     refreshAllItems()
  88.    
  89.     mainMenu()
  90. end
  91. function doNull() end
  92.  
  93. function getObjectName( obj )
  94.     return obj.getVar("__trader_ObjectName") or obj.getName():match("^%s*(.-)%s*$") or obj.getName()
  95. end
  96.  
  97.  
  98. -- Buy Item --
  99. --------------
  100.  
  101. function traderSpawnObject( item, c, spawnAs )
  102.     local ourColor = c and self.getName():lower():find(c:lower())
  103.    
  104.     local params = {}
  105.     params.position = self.positionToWorld(SpawnPos)
  106.    
  107.     local clone
  108.     if item.tag=="Infinite" then
  109.         clone = item.takeObject(params)
  110.     else
  111.         clone = item.clone(params)
  112.     end
  113.     clone.interactable = true
  114.     clone.setLock(false)
  115.     clone.setPosition(params.position)
  116.     clone.setDescription( ourColor and ("%s - %s"):format( Player[c].steam_id, Player[c].steam_name) or "" )
  117.    
  118.     local shouldReset = (clone.tag=="Bag" and item.tag~="Infinite")
  119.     Wait.frames(function()
  120.         if (not clone) or clone==nil then return end
  121.        
  122.         if shouldReset then
  123.             clone.reset()
  124.         end
  125.        
  126.         local formattedObjectName = (clone.getName():match("^%s*(.-)%s*$") or clone.getName()):gsub("[\\\"]", "\\%1")
  127.         if spawnAs and ourColor and Player[c].seated then
  128.             local newStr = os.date(spawnAs, os.time()):gsub("{name}", Player[c].steam_name):gsub("{id}", Player[c].steam_id):gsub("{color}", c)
  129.            
  130.             clone.setName( newStr )
  131.         end
  132.        
  133.         clone.setLuaScript( clone.getLuaScript() .. ("\n\n__trader_ObjectName = \"%s\""):format(formattedObjectName) )
  134.         clone.reload()
  135.     end, 0)
  136. end
  137. function buyItem( data, c )
  138.     if CooldownTime and CooldownTime>os.time() then return end
  139.    
  140.     if not data.item then
  141.         broadcastToColor( "Something went wrong! (Item no longer exists)", c, {1,0.2,0.2} )
  142.         return
  143.     end
  144.    
  145.     -- Cooldown to give objects a chance to be destroyed
  146.     CooldownTime = os.time() + 0.25
  147.     Wait.frames(function() CooldownTime = nil end, 3) -- Clear cooldown after 3 frames, time value is a failsafe
  148.    
  149.     local ourColor = c and self.getName():lower():find(c:lower())
  150.    
  151.     if AdminMode then
  152.         -- Spawn, no additional checks
  153.         traderSpawnObject( data.item, c, data.spawnAs )
  154.     elseif ourColor then
  155.         if Global.getVar("forwardFunction") and Global.getVar("findObjectSetFromColor") then -- Blackjack tables
  156.             local set = Global.call( "forwardFunction", {function_name="findObjectSetFromColor", data={c}} )
  157.            
  158.             if not set then
  159.                 broadcastToColor( "Something went wrong! (Your zones do not exist)", c, {1,0.2,0.2} )
  160.                 return
  161.             end
  162.            
  163.             local zoneObjects = {set.zone.getObjects(), set.tbl.getObjects(), set.prestige.getObjects()}
  164.            
  165.             -- Requirements
  166.             if data.req then
  167.                 if not processRequirements(c, data, zoneObjects) then return end
  168.             end
  169.            
  170.             -- Costs
  171.             if data.cost then
  172.                 if data.costType==COST_ANY then
  173.                     if not processCostAny(c, data, zoneObjects) then return end
  174.                 else
  175.                     if not processCostAll(c, data, zoneObjects) then return end
  176.                 end
  177.             end
  178.            
  179.             -- Spawn
  180.             traderSpawnObject( data.item, c, data.spawnAs )
  181.         else -- Other tables
  182.             local traderScale = self.getScale()
  183.            
  184.             -- Create search zone
  185.             local zone = spawnObject({
  186.                 type = "ScriptingTrigger",
  187.                 position = self.positionToWorld({1.2, 1.6, 0}), rotation = self.getRotation(), scale = {2.7*traderScale[1],3,3*traderScale[3]},
  188.                 sound = false,
  189.             })
  190.            
  191.             -- Add self-destruct script
  192.             zone.setLuaScript([[
  193.                 function onLoad()
  194.                     Wait.frames(function()
  195.                         destroyObject(self)
  196.                     end, 2)
  197.                 end
  198.             ]])
  199.            
  200.             -- Wait for object initialisation
  201.             Wait.frames(function()
  202.                 local zoneObjects = {zone.getObjects()}
  203.                
  204.                 -- Requirements
  205.                 if data.req then
  206.                     if not processRequirements(c, data, zoneObjects) then return end
  207.                 end
  208.                
  209.                 -- Cost
  210.                 if data.cost then
  211.                     if data.costType==COST_ANY then
  212.                         if not processCostAny(c, data, zoneObjects) then return end
  213.                     else
  214.                         if not processCostAll(c, data, zoneObjects) then return end
  215.                     end
  216.                 end
  217.                
  218.                 -- Spawn
  219.                 traderSpawnObject( data.item, c, data.spawnAs )
  220.             end, 1)
  221.         end
  222.     else
  223.         broadcastToColor( "You can't spawn items from other people's Traders. Toggle Admin Mode first.", c, {1,0.2,0.2} )
  224.         return
  225.     end
  226.    
  227.     -- traderSpawnObject( data.item, c, data.spawnAs )
  228. end
  229.  
  230.  
  231. -- Process Requirements --
  232. --------------------------
  233. function processRequirements( c, data, zoneObjects )
  234.     if not Player[c].seated then return false end -- Player gone
  235.    
  236.     local req = {}
  237.     for i=1,#data.req do -- Copy table
  238.         req[data.req[i]] = true
  239.     end
  240.    
  241.     for _,zone in pairs(zoneObjects) do
  242.         for _, obj in ipairs(zone) do
  243.             req[ getObjectName(obj) ] = nil -- Remove entry from table
  244.         end
  245.     end
  246.    
  247.     for missing in pairs(req) do -- If at least one requirement isn't met
  248.         broadcastToColor( ("You need a %s on your table to buy this item."):format(tostring(missing)), c, {1,0.2,0.2} )
  249.         return false
  250.     end
  251.    
  252.     return true
  253. end
  254.  
  255.  
  256. -- Process Costs --
  257. -------------------
  258. function processCostAll( c, data, zoneObjects )
  259.     if not Player[c].seated then return false end -- Player gone
  260.    
  261.     local costData = data.cost
  262.    
  263.     local missingCost = TranslateSetsToItems( CopyTable(costData) )
  264.     local foundStacks = {}
  265.    
  266.     local sort = function(a,b)
  267.         return self.positionToLocal(a.getPosition()).z > self.positionToLocal(b.getPosition()).z
  268.     end
  269.     for i=1,#zoneObjects do
  270.         table.sort(zoneObjects[i], sort)
  271.     end
  272.    
  273.     for _,zone in pairs(zoneObjects) do
  274.         -- for j, item in ipairs(zone) do
  275.         for i=1,#zone do
  276.             local item = zone[i]
  277.             -- local name = item.getName():match("^%s*(.-)%s*$") or item.getName()
  278.             local name = getObjectName(item)
  279.             if item and not (item==nil) and missingCost[name] and missingCost[name]>0 and item.interactable and not (item.getLock()) then
  280.                 local count = item.getQuantity()
  281.                 if count==-1 then count = 1 end
  282.                
  283.                 if item.tag=="Bag" then
  284.                     count = 1
  285.                 end
  286.                
  287.                 if count>missingCost[name] then
  288.                     table.insert(foundStacks, {item, missingCost[name]})
  289.                    
  290.                     missingCost[name] = nil
  291.                 elseif count==missingCost[name] then
  292.                     table.insert(foundStacks, {item})
  293.                    
  294.                     missingCost[name] = nil
  295.                 else
  296.                     table.insert(foundStacks, {item})
  297.                    
  298.                     missingCost[name] = (missingCost[name] or 0) - count
  299.                 end
  300.             end
  301.         end
  302.     end
  303.    
  304.     for missing,cost in pairs(missingCost) do -- Should only run if there's values left
  305.         broadcastToColor( ("You need %i more %s on your table to buy this item."):format(tonumber(cost) or 0, tostring(missing)), c, {1,0.2,0.2} )
  306.         return false
  307.     end
  308.    
  309.     if exeedsLimits( data, zoneObjects, foundStacks ) then
  310.         broadcastToColor( "You have already reached your limit for this item.", c, {1,0.2,0.2} )
  311.         return false
  312.     end
  313.    
  314.     local pos = self.getPosition()
  315.     pos.y = pos.y + 5
  316.    
  317.     for i=1,#foundStacks do
  318.         local tbl = foundStacks[i]
  319.        
  320.         if tbl[2] then
  321.             for i=1,tbl[2] do
  322.                 local taken = tbl[1].takeObject( {position=pos, rotation={0,0,0}} )
  323.                 destroyObject(taken)
  324.             end
  325.         else
  326.             destroyObject(tbl[1])
  327.         end
  328.     end
  329.    
  330.     return true
  331. end
  332. function processCostAny( c, data, zoneObjects )
  333.     if not Player[c].seated then return false end -- Player gone
  334.    
  335.     local tblCost = data.cost
  336.    
  337.     local missingFromSets = {}
  338.     local foundInSets = {}
  339.    
  340.     for i=1,#tblCost do
  341.         local setName = tblCost[i][1]
  342.         local setCount = tblCost[i][2] or 1
  343.        
  344.         if TradeSets[setName] then
  345.             missingFromSets[i] = {
  346.                 Required = setCount,
  347.                 Objects = {}
  348.             }
  349.            
  350.             local tbl = missingFromSets[i].Objects
  351.             for objName,objCount in pairs(TradeSets[setName]) do
  352.                 tbl[objName] = true
  353.             end
  354.         else
  355.             missingFromSets[i] = {
  356.                 Required = setCount,
  357.                 Objects = {
  358.                     [setName] = true,
  359.                 }
  360.             }
  361.         end
  362.     end
  363.    
  364.     local foundStacks
  365.    
  366.     local sort = function(a,b)
  367.         return self.positionToLocal(a.getPosition()).z > self.positionToLocal(b.getPosition()).z
  368.     end
  369.     for i=1,#zoneObjects do
  370.         table.sort(zoneObjects[i], sort)
  371.     end
  372.     local done = false
  373.    
  374.     for _,zone in pairs(zoneObjects) do
  375.         -- for j, item in ipairs(zone) do
  376.         for i=1,#zone do
  377.             local item = zone[i]
  378.             -- local name = item.getName():match("^%s*(.-)%s*$") or item.getName()
  379.             local name = getObjectName( item )
  380.            
  381.             for i=1,#missingFromSets do
  382.                 local missingCost = missingFromSets[i]
  383.                 foundInSets[i] = foundInSets[i] or {}
  384.                
  385.                 if missingCost.Objects[name] and missingCost.Required>0 and item.interactable and not (item.getLock()) then
  386.                     local count = item.getQuantity()
  387.                     if count==-1 then count = 1 end
  388.                    
  389.                     if count>missingCost.Required then
  390.                         table.insert(foundInSets[i], {item, missingCost.Required})
  391.                        
  392.                         missingCost.Required = nil
  393.                     elseif count==missingCost.Required then
  394.                         table.insert(foundInSets[i], {item})
  395.                        
  396.                         missingCost.Required = nil
  397.                     else
  398.                         table.insert(foundInSets[i], {item})
  399.                        
  400.                         missingCost.Required = (missingCost.Required or 0) - count
  401.                     end
  402.                    
  403.                     if (not missingCost.Required) or missingCost.Required<=0 then
  404.                         done = true
  405.                        
  406.                         foundStacks = foundInSets[i] or {}
  407.                        
  408.                         break
  409.                     end
  410.                 end
  411.             end
  412.             if done then break end
  413.         end
  414.         if done then break end
  415.     end
  416.    
  417.     if not foundStacks then
  418.         broadcastToColor( "You don't have the necessary items on your table to buy this item.", c, {1,0.2,0.2} )
  419.         return false
  420.     end
  421.     if exeedsLimits( data, zoneObjects, foundStacks ) then
  422.         broadcastToColor( "You have already reached your limit for this item.", c, {1,0.2,0.2} )
  423.         return false
  424.     end
  425.    
  426.     local pos = self.getPosition()
  427.     pos.y = pos.y + 5
  428.    
  429.     for i=1,#foundStacks do
  430.         local tbl = foundStacks[i]
  431.        
  432.         if tbl[2] then
  433.             for i=1,tbl[2] do
  434.                 local taken = tbl[1].takeObject( {position=pos, rotation={0,0,0}} )
  435.                 destroyObject(taken)
  436.             end
  437.         else
  438.             destroyObject(tbl[1])
  439.         end
  440.     end
  441.    
  442.     return true
  443. end
  444.  
  445.  
  446. -- Process Limits --
  447. --------------------
  448.  
  449. function exeedsLimits( data, zones, skipObjects )
  450.     if not data.limit then return false end
  451.    
  452.     local restrictedObjects = {}
  453.    
  454.     if data.item.tag=="Infinite" then
  455.         local clone = data.item.takeObject(params)
  456.         if clone then
  457.             restrictedObjects[clone.getName() or "[ITEM]"] = true
  458.         end
  459.         destroyObject(clone)
  460.     else
  461.         restrictedObjects[data.item.getName() or "[ITEM]"] = true
  462.     end
  463.    
  464.     local limitLeft = data.limit
  465.     if data.sets then
  466.         for i=1,#data.sets do
  467.             for name in pairs(TradeSets[data.sets[i]] or {}) do
  468.                 restrictedObjects[name] = true
  469.             end
  470.         end
  471.     end
  472.    
  473.     for _,zone in pairs(zones) do
  474.         for j, item in ipairs(zone) do
  475.             local name = item.getName()
  476.            
  477.             if restrictedObjects[name] then
  478.                 local count = item.getQuantity()
  479.                 if count==-1 then count = 1 end
  480.                
  481.                 if item.tag=="Bag" then
  482.                     count = 1
  483.                 end
  484.                
  485.                 for i=1,#skipObjects do
  486.                     local tbl = skipObjects[i]
  487.                    
  488.                     if tbl[1]==item then
  489.                         if tbl[2] then
  490.                             count = count - tbl[2]
  491.                         else
  492.                             count = count - 1
  493.                         end
  494.                     end
  495.                 end
  496.                
  497.                 if count>0 then
  498.                     limitLeft = limitLeft - count
  499.                    
  500.                     if limitLeft<=0 then return true end
  501.                 end
  502.             end
  503.         end
  504.     end
  505.     if limitLeft<= 0 then return true end
  506.    
  507.     return false
  508. end
  509.  
  510.  
  511. -- Register Items --
  512. --------------------
  513.  
  514. function clickAdminMode(o,c)
  515.     if not Player[c].admin then
  516.         printToColor( "You can't do this.", c, {1,0,0} )
  517.         return
  518.     end
  519.    
  520.     AdminMode = not AdminMode
  521.     doMenu( ListPage )
  522. end
  523. function clickRefresh(o,c)
  524.     if not Player[c].admin then
  525.         printToColor( "You can't do this.", c, {1,0,0} )
  526.         return
  527.     end
  528.    
  529.     refreshAllItems()
  530. end
  531. function refreshAllItems()
  532.     TradeItems = {}
  533.     TradeSets = {}
  534.    
  535.     DirectoryPath = {}
  536.    
  537.     ListData = {}
  538.     ListReference = {}
  539.     ListPage = 0
  540.    
  541.     for _,obj in pairs(getAllObjects()) do
  542.         if obj.getDescription():match("^[Cc]ollect[ai]ble") then
  543.             registerItem( obj, obj.getDescription():match("^[Cc]ollect[ai]ble *([^\n]*)") )
  544.         end
  545.     end
  546.    
  547.     mainMenu()
  548. end
  549.  
  550. local TextToCostType = {
  551.     any = COST_ANY,
  552.     all = COST_ALL,
  553. }
  554. function registerItem( obj, appendName )
  555.     local desc = obj.getDescription()
  556.     local name = obj.getName() or "[ITEM]"
  557.    
  558.     if obj.tag=="Infinite" then
  559.         local clone = obj.takeObject(params)
  560.         if clone then
  561.             name = clone.getName() or name
  562.         end
  563.         destroyObject(clone)
  564.     end
  565.    
  566.     if appendName then
  567.         name = name .. " " .. tostring(appendName)
  568.     end
  569.    
  570.    
  571.     local CostType = COST_ALL
  572.     local hasCost = false
  573.     for foundType in desc:gmatch("[Cc]ost[Tt]ype:? *([^\n]+)") do
  574.         foundType = foundType:match("^%s*(.-)%s*$") or foundType
  575.        
  576.         CostType = TextToCostType[ foundType:lower() ] or CostType
  577.     end
  578.    
  579.     local cost = {}
  580.     local hasCost = false
  581.     for num,item in desc:gmatch("[Cc]ost:? +(%d*)x? *([^\n]+)") do
  582.         num = tonumber(num) or 1
  583.         if item then
  584.             hasCost = true
  585.             item = item:match("^%s*(.-)%s*$") or item
  586.            
  587.             if CostType==COST_ALL then
  588.                 cost[item] = (cost[item] or 0) + num
  589.             elseif CostType==COST_ANY then
  590.                 table.insert(cost, {item,num})
  591.             end
  592.         end
  593.     end
  594.     if not hasCost then -- No cost, can't buy
  595.         registerSet( obj )
  596.         return
  597.     end
  598.    
  599.     local req = {}
  600.     for foundReq in desc:gmatch("[Rr]equires?:? *([^\n]+)") do
  601.         foundReq = foundReq:match("^%s*(.-)%s*$") or foundReq
  602.         table.insert(req, foundReq)
  603.     end
  604.     if #req==0 then
  605.         req = nil
  606.     end
  607.    
  608.     local spawnAs = ""
  609.     for foundName in desc:gmatch("[Ss]pawn[Aa]s?:? *([^\n]+)") do
  610.         spawnAs = foundName:match("^%s*(.-)%s*$") or foundName
  611.         break
  612.     end
  613.     if #spawnAs==0 then
  614.         spawnAs = nil
  615.     end
  616.    
  617.     local limit = nil
  618.     for foundLimit in desc:gmatch("[Ll]imit:? *(%d+)") do
  619.         limit = tonumber(foundLimit) or 0
  620.         break
  621.     end
  622.    
  623.     local workingDir = TradeItems
  624.    
  625.     local cat = desc:match("[Cc]ategory:? *([^\n]+)")
  626.     if cat then
  627.         local exploded = {}
  628.        
  629.         local str = cat
  630.         local s,e,before,after = str:find("^([^>]*)>(.*)$")
  631.         while s and e do
  632.             before = before:match( "^%s*(.+)$" ) or before
  633.            
  634.             -- before = before:match( "^(.-)%s*$" ) or before -- "Too complex", apparently.
  635.             local posTrailingSpaces = before:find("%s*$")
  636.             if posTrailingSpaces then
  637.                 before = before:sub(1,posTrailingSpaces-1)
  638.             end
  639.             if #before>0 then table.insert(exploded, before) end
  640.            
  641.             str = after
  642.             s,e,before,after = str:find("^([^>]*)>(.*)$")
  643.         end
  644.        
  645.         str = str:match( "^%s*(.+)$" ) or str
  646.         local posTrailingSpaces = str:find("%s*$")
  647.         if posTrailingSpaces then
  648.             str = str:sub(1,posTrailingSpaces-1)
  649.         end
  650.         if #str>0 then table.insert(exploded, str) end
  651.        
  652.         for i=1,#exploded do
  653.             if not exploded[i] then break end
  654.            
  655.             if (not workingDir[exploded[i]]) or (workingDir[exploded[i]].IsTraderItem) then -- Empty or an item, replace with category
  656.                 workingDir[exploded[i]] = {}
  657.             end
  658.             workingDir = workingDir[exploded[i]]
  659.         end
  660.     end
  661.    
  662.     workingDir[ name ] = {
  663.         IsTraderItem = true,
  664.         item = obj,
  665.        
  666.         costType = CostType,
  667.         cost = cost,
  668.        
  669.         req = req,
  670.        
  671.         spawnAs = spawnAs,
  672.         limit = limit,
  673.         sets = registerSet( obj ),
  674.     }
  675. end
  676. function registerSet( obj )
  677.     local desc = obj.getDescription()
  678.     local name = obj.getName() or "[ITEM]"
  679.    
  680.     if obj.tag=="Infinite" then
  681.         local clone = obj.takeObject(params)
  682.         if clone then
  683.             name = clone.getName() or name
  684.         end
  685.         destroyObject(clone)
  686.     end
  687.    
  688.     local sets = {}
  689.     local hasSets = false
  690.     for num,setName in desc:gmatch("Set:? *(%d*)x? *([^\n]+)") do
  691.         num = tonumber(num) or 1
  692.         if setName then
  693.             sets[setName] = (sets[setName] or 0) + num
  694.             hasSets = true
  695.         end
  696.     end
  697.     if not hasSets then return end -- Not part of a set
  698.    
  699.     local inSets = {}
  700.     for setName,num in pairs(sets) do
  701.         TradeSets[setName] = TradeSets[setName] or {}
  702.        
  703.         TradeSets[setName][name] = num
  704.         table.insert(inSets, setName)
  705.     end
  706.    
  707.     return inSets
  708. end
  709.  
  710.  
  711. -- Cost To String --
  712. --------------------
  713.  
  714. function getCostString( costTable, costType )
  715.     if not costTable then return "" end
  716.    
  717.     local str = ""
  718.     local orderedCost = {}
  719.     if costType==COST_ANY then
  720.         orderedCost = CopyTable(costTable)
  721.     else
  722.         for item,num in pairs(costTable) do
  723.             table.insert( orderedCost, {item,num} )
  724.         end
  725.     end
  726.     table.sort( orderedCost, function(a,b)
  727.         if a[2]==b[2] then
  728.             return a[1]<b[1]
  729.         end
  730.         return a[2]<b[2]
  731.     end)
  732.    
  733.     for i=1,#orderedCost do
  734.         str = str .. ("%s%i %s[b]%s[/b]"):format(
  735.             (i==1 and "") or (i==#orderedCost and (costType==COST_ANY and ", or " or ", and ")) or ", ",
  736.             orderedCost[i][2],
  737.             costType==COST_ANY and TradeSets[ orderedCost[i][1] ] and "from " or "",
  738.             orderedCost[i][1]
  739.         )
  740.     end
  741.    
  742.     return str
  743. end
  744.  
  745.  
  746. -- Menu --
  747. ----------
  748.  
  749. function doListData()
  750.     for k in pairs(ListReference) do
  751.         table.insert(ListData, k)
  752.     end
  753.     table.sort(ListData, function(a,b)
  754.         if ListReference[a].IsTraderItem and not ListReference[b].IsTraderItem then return false end
  755.         if ListReference[b].IsTraderItem and not ListReference[a].IsTraderItem then return true end
  756.        
  757.         return a<b
  758.     end)
  759. end
  760.  
  761. function clickMainMenu(o,c)
  762.     local ourColor = c and self.getName():lower():find(c:lower())
  763.     if not (c and (Player[c].admin or ourColor)) then
  764.         broadcastToColor( "This does not belong to you.", c, {1,0.2,0.2} )
  765.         return
  766.     end
  767.    
  768.     mainMenu()
  769. end
  770. function mainMenu()
  771.     ListTitle = "Collectable Trader"
  772.     ListReference = TradeItems
  773.     ListData = {}
  774.    
  775.     DirectoryPath = {}
  776.    
  777.     doListData()
  778.    
  779.     doMenu( 1 )
  780. end
  781.  
  782. function doMenu(page)
  783.     self.clearButtons()
  784.     self.clearInputs()
  785.    
  786.     ListPage = page or 1
  787.    
  788.     -- Title
  789.     self.createButton({
  790.         label=ListTitle, click_function="doNull", function_owner=self, scale = {0.5,0.5,0.5},
  791.         position={1.2, 0.25, -1.23}, rotation={0,0,0}, width=0, height=0, font_size=170,
  792.         font_color = {r=1,g=1,b=1},
  793.     })
  794.    
  795.     Targets = {}
  796.    
  797.     local displayFrom = (ListPage-1)*10
  798.     -- List Page
  799.     for i=1,10 do
  800.         local data = ListData[displayFrom + i]
  801.        
  802.         if not (data and ListReference[data]) then break end
  803.        
  804.         local zpos = -1.17 + (i * 0.21)
  805.         local col = AdminMode and {r=1,g=0.1,b=0.1} or {r=1,b=1,g=1}
  806.        
  807.         -- Button
  808.         if ListReference[data].IsTraderItem then
  809.             self.createButton({
  810.                 label= ("[b][u]%s[/u][/b]\n%s"):format( data, getCostString(ListReference[data].cost, ListReference[data].costType) ), click_function="doAction"..i, function_owner=self, scale = {0.5,0.5,0.5},
  811.                 position={1.2, 0.25, zpos}, rotation={0,0,0}, width=1800, height=220, font_size=80,
  812.                 color = col
  813.             })
  814.         else
  815.             self.createButton({
  816.                 label= ("[b]%s[b] >"):format( data ), click_function="doAction"..i, function_owner=self, scale = {0.5,0.5,0.5},
  817.                 position={1.2, 0.25, zpos}, rotation={0,0,0}, width=1800, height=220, font_size=80,
  818.                 color = col
  819.             })
  820.         end
  821.     end
  822.    
  823.    
  824.     -- Page Navigaton
  825.     local pageStr = ("Page %i of %i"):format( ListPage, math.ceil(#ListData/10) )
  826.     self.createButton({
  827.         label=pageStr, click_function="doNull", function_owner=self, scale = {0.5,0.5,0.5},
  828.         position={1.2, 0.25, 1.12}, rotation={0,0,0}, width=0, height=0, font_size=100,
  829.         font_color = {r=1,g=1,b=1},
  830.     })
  831.    
  832.     self.createButton({
  833.         label="<", click_function="PrevPage", function_owner=self, scale = {1,1,1}, scale = {0.5,0.5,0.5},
  834.         position={0.7, 0.25, 1.12}, rotation={0,0,0}, width=100, height=100, font_size=80,
  835.         color = ListPage==1 and {0.5,0.5,0.5} or {1,1,1}
  836.     })
  837.     self.createButton({
  838.         label=">", click_function="NextPage", function_owner=self, scale = {1,1,1}, scale = {0.5,0.5,0.5},
  839.         position={1.7, 0.25, 1.12}, rotation={0,0,0}, width=100, height=100, font_size=80,
  840.         color = ListPage>=math.ceil(#ListData/10) and {0.5,0.5,0.5} or {1,1,1}
  841.     })
  842.    
  843.    
  844.     if #DirectoryPath>0 then
  845.         -- Return
  846.         self.createButton({
  847.             label="Back", click_function="clickBack", function_owner=self, scale = {1,1,1}, scale = {0.5,0.5,0.5},
  848.             position={1.3, 0.25, 1.32}, rotation={0,0,0}, width=450, height=150, font_size=80,
  849.         })
  850.         self.createButton({
  851.             label="Main Menu", click_function="clickMainMenu", function_owner=self, scale = {1,1,1}, scale = {0.5,0.5,0.5},
  852.             position={1.8, 0.25, 1.32}, rotation={0,0,0}, width=450, height=150, font_size=80,
  853.         })
  854.     end
  855.    
  856.     -- Admin
  857.     self.createButton({
  858.         label="Admin Mode", click_function="clickAdminMode", function_owner=self, scale = {1,1,1}, scale = {0.3,0.3,0.3},
  859.         position={0.2, 0.25, -1.33}, rotation={0,0,0}, width=450, height=50, font_size=70,
  860.         color = AdminMode and {r=1,g=0,b=0} or {r=0.25,g=0.25,b=0.25},
  861.     })
  862.     self.createButton({
  863.         label="Reload", click_function="clickRefresh", function_owner=self, scale = {1,1,1}, scale = {0.3,0.3,0.3},
  864.         position={0.2, 0.25, -1.23}, rotation={0,0,0}, width=450, height=50, font_size=70,
  865.         color = {r=0.25,g=0.25,b=0.25},
  866.     })
  867. end
  868.  
  869.  
  870. -- Actions --
  871. -------------
  872. function doAction( index, c )
  873.     local ourColor = c and self.getName():lower():find(c:lower())
  874.     if not (c and (Player[c].admin or ourColor)) then
  875.         broadcastToColor( "This does not belong to you.", c, {1,0.2,0.2} )
  876.         return
  877.     end
  878.    
  879.     local trueIndex = index + (ListPage-1)*10
  880.     local data = ListData[trueIndex]
  881.     local ref = data and ListReference[data]
  882.    
  883.     if not ref then
  884.         broadcastToColor( "Something went wrong! (Button reference is missing)", c, {1,0.2,0.2} )
  885.         mainMenu()
  886.         return
  887.     end
  888.    
  889.     if ref.IsTraderItem then
  890.         buyItem( ref, c )
  891.        
  892.         return
  893.     end
  894.    
  895.     table.insert(DirectoryPath, data)
  896.     ListReference = ref
  897.     ListPage = 0
  898.     ListTitle = data
  899.     ListData = {}
  900.    
  901.     doListData()
  902.    
  903.     doMenu(1)
  904. end
  905. for i=1,10 do
  906.     _G["doAction"..i] = function(o,c) return doAction(i,c) end
  907. end
  908.  
  909. function clickBack(o,c)
  910.     local ourColor = c and self.getName():lower():find(c:lower())
  911.     if not (c and (Player[c].admin or ourColor)) then
  912.         broadcastToColor( "This does not belong to you.", c, {1,0.2,0.2} )
  913.         return
  914.     end
  915.    
  916.     table.remove(DirectoryPath)
  917.     if #DirectoryPath==0 then
  918.         mainMenu()
  919.         return
  920.     end
  921.    
  922.     local workingDir = TradeItems
  923.     for i=1,#DirectoryPath do
  924.         local newDir = workingDir[DirectoryPath[i]]
  925.         if newDir and not newDir.IsTraderItem then
  926.             workingDir = newDir
  927.         else
  928.             while #DirectoryPath>=i do
  929.                 table.remove(DirectoryPath)
  930.             end
  931.             break
  932.         end
  933.     end
  934.     if #DirectoryPath==0 then
  935.         mainMenu()
  936.         return
  937.     end
  938.    
  939.     ListReference = workingDir
  940.     ListPage = 0
  941.     ListTitle = DirectoryPath[#DirectoryPath]
  942.     ListData = {}
  943.    
  944.     doListData()
  945.    
  946.     doMenu(1)
  947. end
  948.  
  949. function NextPage(o,c)
  950.     local ourColor = c and self.getName():lower():find(c:lower())
  951.     if not (c and (Player[c].admin or ourColor)) then
  952.         broadcastToColor( "This does not belong to you.", c, {1,0.2,0.2} )
  953.         return
  954.     end
  955.    
  956.     local maxPage = math.max( math.ceil(#ListData/10), 1 )
  957.    
  958.     doMenu( math.min(ListPage+1, maxPage) )
  959. end
  960. function PrevPage(o,c)
  961.     local ourColor = c and self.getName():lower():find(c:lower())
  962.     if not (c and (Player[c].admin or ourColor)) then
  963.         broadcastToColor( "This does not belong to you.", c, {1,0.2,0.2} )
  964.         return
  965.     end
  966.    
  967.     doMenu( math.max(ListPage-1, 1) )
  968. end
  969.  
  970. -- Util --
  971. ----------
  972.  
  973. function CopyTable(from)
  974.     local to = {}
  975.     for k,v in pairs(from) do
  976.         to[k]=v
  977.     end
  978.     return to
  979. end
  980. function TranslateSetsToItems( tbl )
  981.     for setName,setCount in pairs(tbl) do
  982.         if TradeSets[setName] then
  983.             for objName,objCount in pairs(TradeSets[setName]) do
  984.                 tbl[objName] = (tbl[objName] or 0) + ((objCount or 1) * (setCount or 1))
  985.             end
  986.             tbl[setName] = nil
  987.         end
  988.     end
  989.    
  990.     return tbl
  991. end
  992.  
  993. function PrintTable( tbl, indent, skipTables )
  994.     indent = indent or 0
  995.     skipTables = skipTables or {[tbl]=true}
  996.    
  997.     for k,v in pairs(tbl) do
  998.         if type(v)=="table" and not skipTables[v] then
  999.             skipTables[v]=true
  1000.             print( (" "):rep(indent+1), "\"",k,"\" = {" )
  1001.             PrintTable(v, indent+1, skipTables)
  1002.             print( (" "):rep(indent+1), "}" )
  1003.         else
  1004.             print( (" "):rep(indent+1), "\"", k, "\" = ", v )
  1005.         end
  1006.     end
  1007. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement