· 5 years ago · Apr 08, 2020, 03:30 PM
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
70TradeItems = {}
71
72DirectoryPath = {}
73
74ListData = {}
75ListReference = {}
76ListPage = 0
77ListTitle = ""
78
79AdminMode = false
80
81SpawnPos = { 1.168, 3.01, 0.02 }
82
83COST_ALL = 0
84COST_ANY = 1
85
86function onLoad()
87 refreshAllItems()
88
89 mainMenu()
90end
91function doNull() end
92
93function getObjectName( obj )
94 return obj.getVar("__trader_ObjectName") or obj.getName():match("^%s*(.-)%s*$") or obj.getName()
95end
96
97
98-- Buy Item --
99--------------
100
101function 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)
136end
137function 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("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 )
228end
229
230
231-- Process Requirements --
232--------------------------
233function 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
253end
254
255
256-- Process Costs --
257-------------------
258function 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
331end
332function 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
443end
444
445
446-- Process Limits --
447--------------------
448
449function 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
508end
509
510
511-- Register Items --
512--------------------
513
514function 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 )
522end
523function 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()
530end
531function 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()
548end
549
550local TextToCostType = {
551 any = COST_ANY,
552 all = COST_ALL,
553}
554function 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 }
675end
676function 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
708end
709
710
711-- Cost To String --
712--------------------
713
714function 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
743end
744
745
746-- Menu --
747----------
748
749function 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)
759end
760
761function 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()
769end
770function mainMenu()
771 ListTitle = "Collectable Trader"
772 ListReference = TradeItems
773 ListData = {}
774
775 DirectoryPath = {}
776
777 doListData()
778
779 doMenu( 1 )
780end
781
782function 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 })
867end
868
869
870-- Actions --
871-------------
872function 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)
904end
905for i=1,10 do
906 _G["doAction"..i] = function(o,c) return doAction(i,c) end
907end
908
909function 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)
947end
948
949function 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) )
959end
960function 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) )
968end
969
970-- Util --
971----------
972
973function CopyTable(from)
974 local to = {}
975 for k,v in pairs(from) do
976 to[k]=v
977 end
978 return to
979end
980function 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
991end
992
993function 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
1007end