John@38
|
1 local AceGUI = LibStub("AceGUI-3.0")
|
John@42
|
2 local bsk=bsk
|
John@42
|
3 local _G=_G
|
John@42
|
4 local table=table
|
John@42
|
5 local pairs=pairs
|
John@65
|
6 local setmetatable=setmetatable
|
John@42
|
7 local ipairs=ipairs
|
John@42
|
8 local string=string
|
John@42
|
9 local tostring=tostring
|
John@42
|
10 local type=type
|
John@42
|
11 local getn=getn
|
John@42
|
12 setfenv(1,bsk)
|
John@1
|
13
|
John@56
|
14 local copy = function(t)
|
John@56
|
15 local c = {}
|
John@56
|
16 if not t then return c end
|
John@56
|
17 for i,v in pairs(t) do c[i] = v end
|
John@56
|
18 return c
|
John@56
|
19 end
|
John@56
|
20 local colorize = function(str,color)
|
John@56
|
21 if str==nil or str=="" then return "" end
|
John@56
|
22 if type(color) == "table" then
|
John@56
|
23 color = string.format("%02x%02x%02x",255*color.r,255*color.g,255*color.b)
|
John@56
|
24 end
|
John@56
|
25 if color == nil then return str end
|
John@56
|
26 return "|cff"..tostring(color or "ffffff")..str.."|r"
|
John@56
|
27 end
|
John@1
|
28
|
John@63
|
29 local CreateListSelector = function(SListPopulator)
|
John@56
|
30 PersonList:RefreshRaidList()
|
John@56
|
31 local pulldown = AceGUI:Create("Dropdown")
|
John@56
|
32 local pull = {}
|
John@56
|
33 local ltemp = 0
|
John@56
|
34 local lids = LootLists:GetAllIds()
|
John@56
|
35 for _,v in pairs(lids) do
|
John@56
|
36 local l = LootLists:Select(v)
|
John@56
|
37 pull[l:GetId()] = l:GetName()
|
John@56
|
38 --local entry = {value=i,text=v.name}
|
John@56
|
39 if l:GetLength() > 0 then
|
John@56
|
40 pulldown:SetItemDisabled(i,true)
|
John@56
|
41 if ltemp == 0 then
|
John@56
|
42 ltemp = l:GetId()
|
John@51
|
43 end
|
John@51
|
44 end
|
John@1
|
45 end
|
John@56
|
46 pulldown:SetWidth(175)
|
John@56
|
47 pulldown:SetList(pull)
|
John@63
|
48 pulldown:SetCallback("OnValueChanged", function(_,_,value) SListPopulator:SetList(value) end)
|
John@63
|
49 if ltemp > 0 then pulldown:SetValue(ltemp); SListPopulator:SetList(ltemp) end -- sadly, SetValue doesn't fire a OnValueChanged
|
John@56
|
50 return pulldown
|
John@1
|
51 end
|
John@1
|
52
|
John@65
|
53 local f, right
|
John@59
|
54 local escapeButton =
|
John@59
|
55 {
|
John@59
|
56 shown = false,
|
John@59
|
57 ["IsShown"] = function(self) return self.shown end,
|
John@60
|
58 ["Hide"] = function(self) if f then AceGUI:Release(f); self.shown=false end end
|
John@59
|
59 }
|
John@38
|
60
|
John@63
|
61
|
John@65
|
62 SelectorListEventDispatcher =
|
John@63
|
63 {
|
John@65
|
64 listeners = nil,
|
John@63
|
65 target = nil,
|
John@63
|
66 ["SetTarget"] = function(self,other)
|
John@63
|
67 self.target = other
|
John@65
|
68 self.target:SetCallback("OnSelectionCleared",function(...) self:OnSelectionCleared(...) end)
|
John@65
|
69 self.target:SetCallback("OnSelection",function(...) self:OnSelection(...) end)
|
John@63
|
70 end,
|
John@63
|
71 ["RegisterListener"] = function(self,other) table.insert(self.listeners,other) end,
|
John@63
|
72 ["OnSelectionCleared"] = function(self,_)
|
John@63
|
73 self:Event("OnSelectionCleared")
|
John@63
|
74 end,
|
John@65
|
75 ["OnSelection"] = function(self,_,_,line)
|
John@63
|
76 self:Event("OnSelection",line)
|
John@63
|
77 end,
|
John@63
|
78 ["Event"] = function(self,event,arg)
|
John@63
|
79 if not self.target then error("Event called with no listener...") end
|
John@63
|
80 if not self.listeners then return end
|
John@63
|
81 for i,v in pairs(self.listeners) do
|
John@65
|
82 bsk.print("listener")
|
John@63
|
83 if type(v) == "table" then
|
John@63
|
84 if v[event] then
|
John@63
|
85 v[event](v,arg)
|
John@63
|
86 end
|
John@63
|
87 elseif type(v) == "function" then
|
John@63
|
88 v(event,arg)
|
John@63
|
89 elseif type(v) ~= "nil" then -- allow nils to pass quietly
|
John@63
|
90 _G.error("Bad listener - "..type(v))
|
John@63
|
91 end
|
John@63
|
92 end
|
John@63
|
93 end,
|
John@65
|
94 ["Release"] = function(self) self.listeners = {}; self.target=nil end
|
John@63
|
95 }
|
John@65
|
96 function SelectorListEventDispatcher:new()
|
John@65
|
97 local t = {}
|
John@65
|
98 setmetatable(t,SelectorListEventDispatcher)
|
John@65
|
99 self.__index = self
|
John@65
|
100 t.listeners = {}
|
John@65
|
101 return t
|
John@63
|
102 end
|
John@65
|
103
|
John@65
|
104 SListEventDispatch = SelectorListEventDispatcher:new()
|
John@65
|
105 LListEventDispatch = SelectorListEventDispatcher:new()
|
John@65
|
106
|
John@65
|
107 local AdminLootEventCollector =
|
John@65
|
108 {
|
John@65
|
109 listeners = {},
|
John@65
|
110 target1 =
|
John@65
|
111 {
|
John@65
|
112 },
|
John@65
|
113 target2 =
|
John@65
|
114 {
|
John@65
|
115 },
|
John@65
|
116
|
John@65
|
117
|
John@65
|
118 }
|
John@65
|
119
|
John@63
|
120 local SListPopulator =
|
John@63
|
121 {
|
John@63
|
122 filtered = false,
|
John@63
|
123 widget = nil,
|
John@63
|
124 data = nil,
|
John@63
|
125 lref = nil,
|
John@63
|
126
|
John@63
|
127 ["Release"] = function(self) self.filtered, self.widget, self.data, self.lref = false, nil, nil, nil end,
|
John@63
|
128 ["Redraw"] = function(self)
|
John@63
|
129 if self.lref == nil or self.widget == nil then return end -- don't do work if not fully initialized
|
John@63
|
130 self.data = {}
|
John@63
|
131 for le in self.lref:OrderedListEntryIter() do
|
John@63
|
132 local disabled = not PersonList:IsActive(le:GetId())
|
John@63
|
133 if not self.filtered or not disabled then
|
John@63
|
134 local line = {value=le:GetId(),text=le:GetName(),icon=[[Interface\Glues\CharacterCreate\UI-CharacterCreate-Classes]]}
|
John@63
|
135 line.iconCoords=_G.CLASS_ICON_TCOORDS[le:GetClass()]
|
John@63
|
136 line.text=colorize(line.text,_G.RAID_CLASS_COLORS[le:GetClass()])
|
John@63
|
137 line.disabled = disabled
|
John@63
|
138 table.insert(self.data,line)
|
John@63
|
139 end
|
John@63
|
140 end
|
John@63
|
141 self.widget:SetList(self.data)
|
John@65
|
142 SListEventDispatch:Event("Redraw")
|
John@63
|
143 end,
|
John@63
|
144 ["SetWidget"] = function(self,w)
|
John@65
|
145 if type(w) ~= "table" or type(w.SetList) ~= "function" then
|
John@63
|
146 _G.error("Bad SetWidget")
|
John@63
|
147 end
|
John@63
|
148 self.widget = w
|
John@63
|
149 end,
|
John@63
|
150 ["SetFiltered"] = function(self,value)
|
John@63
|
151 self.filtered = value
|
John@63
|
152 self:Redraw()
|
John@63
|
153 end,
|
John@63
|
154 ["SetList"] = function(self,value)
|
John@63
|
155 self.lref = LootLists:Select(value)
|
John@63
|
156 self:Redraw()
|
John@63
|
157 end
|
John@63
|
158 }
|
John@63
|
159
|
John@42
|
160 function CreateGUI()
|
John@59
|
161 -- special registration procedure to be closable with the escape button
|
John@59
|
162 --escapeButton.shown = true
|
John@59
|
163 --_G["BSK_ESCAPEBUTTON"] = escapeButton
|
John@59
|
164 --table.insert(_G.UISpecialFrames, "BSK_ESCAPEBUTTON")
|
John@59
|
165
|
John@60
|
166 if f then return end -- no second gui please
|
John@60
|
167 local admin = bsk.admin or true
|
John@59
|
168 f = AceGUI:Create("Frame")
|
John@1
|
169
|
John@65
|
170 f:SetCallback("OnClose",function(widget) escapeButton.shown = false; AceGUI:Release(widget); f=nil; right=nil; SListEventDispatch:Release(); LListEventDispatch:Release(); SListPopulator:Release() end)
|
John@56
|
171 f:SetTitle("BSK")
|
John@56
|
172 f:SetLayout("Flow")
|
John@56
|
173 f:SetHeight(680)
|
John@56
|
174 f:SetWidth(580)
|
John@1
|
175
|
John@56
|
176 local left = AceGUI:Create("InlineGroup")
|
John@56
|
177 left:SetLayout("List")
|
John@56
|
178 left:SetWidth(175)
|
John@56
|
179 left:SetFullHeight(true)
|
John@56
|
180 left.alignoffset=0.25 -- hack, as per http://forums.wowace.com/showthread.php?t=17114
|
John@1
|
181
|
John@65
|
182 right = AceGUI:Create("InlineGroup")
|
John@56
|
183 right:SetLayout("Flow")
|
John@56
|
184 right:SetWidth(700-175-160)
|
John@56
|
185 right:SetFullHeight(true)
|
John@56
|
186 right.alignoffset=0.25
|
John@38
|
187
|
John@56
|
188 local t1 = AceGUI:Create("SelectorList")
|
John@56
|
189 t1:SetNumLines(25)
|
John@56
|
190 t1:SetFullWidth(true)
|
John@56
|
191 t1:SetInteractive(admin)
|
John@63
|
192 SListPopulator:SetWidget(t1)
|
John@63
|
193 SListEventDispatch:SetTarget(t1)
|
John@38
|
194
|
John@63
|
195 local p1 = CreateListSelector(SListPopulator)
|
John@56
|
196 p1:SetFullWidth(true)
|
John@38
|
197
|
John@56
|
198 left:AddChild(p1)
|
John@56
|
199 left:AddChild(t1)
|
John@38
|
200
|
John@56
|
201 local t2 = AceGUI:Create("SelectorList")
|
John@56
|
202 t2:SetNumLines(7)
|
John@56
|
203 t2:SetFullWidth(true)
|
John@56
|
204 t2:EnableButtonTooltips(true)
|
John@56
|
205 t2:SetList({
|
John@56
|
206 {
|
John@56
|
207 value=1,
|
John@56
|
208 text = "|cffa335ee|Hitem:77109:4080:4009:0:0:0:0:0:85:0|h[Band of Reconstruction]|h|r",
|
John@56
|
209 link = "|cffa335ee|Hitem:77109:4080:4009:0:0:0:0:0:85:0|h[Band of Reconstruction]|h|r",
|
John@56
|
210 },
|
John@56
|
211 {
|
John@56
|
212 value=2,
|
John@56
|
213 text = "|cffa335ee|Hitem:19351:0:0:0:0:0:0:0:85:0|h[Maladath, Runed Blade of the Black Flight]|h|r",
|
John@56
|
214 link = "|cffa335ee|Hitem:19351:0:0:0:0:0:0:0:85:0|h[Maladath, Runed Blade of the Black Flight]|h|r"
|
John@56
|
215 },
|
John@56
|
216 {
|
John@56
|
217 value=3,
|
John@56
|
218 text = "|cffa335ee|Hitem:31986:0:0:0:0:0:0:0:85:0|h[Merciless Gladiator's Crossbow of the Phoenix]|h|r",
|
John@56
|
219 link = "|cffa335ee|Hitem:31986:0:0:0:0:0:0:0:85:0|h[Merciless Gladiator's Crossbow of the Phoenix]|h|r"
|
John@56
|
220 },
|
John@56
|
221 {
|
John@56
|
222 value=4,
|
John@56
|
223 text = "|cffa335ee|Hitem:65003:0:0:0:0:0:0:0:85:0|h[Reclaimed Ashkandi, Greatsword of the Brotherhood]|h|r",
|
John@56
|
224 link = "|cffa335ee|Hitem:65003:0:0:0:0:0:0:0:85:0|h[Reclaimed Ashkandi, Greatsword of the Brotherhood]|h|r"
|
John@56
|
225 },
|
John@56
|
226 {
|
John@56
|
227 value=5,
|
John@56
|
228 text = "|cffff8000|Hitem:19019:0:0:0:0:0:0:0:85:0|h[Thunderfury, Blessed Blade of the Windseeker]|h|r",
|
John@56
|
229 link = "|cffff8000|Hitem:19019:0:0:0:0:0:0:0:85:0|h[Thunderfury, Blessed Blade of the Windseeker]|h|r"
|
John@56
|
230 },
|
John@56
|
231 })
|
John@65
|
232 LListEventDispatch:SetTarget(t2)
|
John@65
|
233
|
John@65
|
234 local biddingZone = AceGUI:Create("SimpleGroup")
|
John@65
|
235 biddingZone:SetLayout("Flow")
|
John@65
|
236 biddingZone:SetFullWidth(true)
|
John@38
|
237
|
John@60
|
238 local alb1, alb2, alb3
|
John@60
|
239 if admin then
|
John@60
|
240 alb1 = AceGUI:Create("Button")
|
John@60
|
241 alb1:SetWidth(100)
|
John@60
|
242 alb1:SetText("Open Bids")
|
John@65
|
243 alb1.userdata =
|
John@65
|
244 {
|
John@65
|
245 state = false,
|
John@65
|
246 widget = alb1,
|
John@65
|
247 ["Redraw"] = function(self,_) self.widget:SetDisabled(true); self.item = nil end,
|
John@65
|
248 ["OnSelection"] = function(self,value) self.widget:SetDisabled(false); self.item = value end,
|
John@65
|
249 ["OnSelectionCleared"] = function(self) self.widget:SetDisabled(true); self.item = nil end
|
John@65
|
250 }
|
John@65
|
251 alb1:SetDisabled(true)
|
John@65
|
252 LListEventDispatch:RegisterListener(alb1.userdata)
|
John@65
|
253 alb1:SetCallback("OnClick",
|
John@65
|
254 function(widget)
|
John@65
|
255 if widget.userdata.state then -- we were bidding when the button was pressed
|
John@65
|
256 biddingZone:ReleaseChildren()
|
John@65
|
257 else
|
John@65
|
258 local spacer = AceGUI:Create("Label")
|
John@65
|
259 spacer:SetText(" ")
|
John@65
|
260 spacer:SetFullWidth(true)
|
John@65
|
261 local spacer2 = AceGUI:Create("Label")
|
John@65
|
262 spacer2:SetText(" ")
|
John@65
|
263 spacer2:SetFullWidth(true)
|
John@65
|
264
|
John@65
|
265 local label = AceGUI:Create("Label")
|
John@65
|
266 label:SetText("Bidding now open for ...")
|
John@65
|
267 local biddingOn = AceGUI:Create("InteractiveLabel")
|
John@65
|
268 biddingOn.userdata = { "|cffa335ee|Hitem:65003:0:0:0:0:0:0:0:85:0|h[Reclaimed Ashkandi, Greatsword of the Brotherhood]|h|r" }
|
John@65
|
269 biddingOn:SetText(biddingOn.userdata[1])
|
John@65
|
270 biddingOn:SetFullWidth(true)
|
John@65
|
271 biddingOn:SetCallback("OnEnter", function(widget) _G.GameTooltip:SetOwner(widget.frame,"ANCHOR_RIGHT"); _G.GameTooltip:SetHyperlink(widget.userdata[1]); _G.GameTooltip:Show() end )
|
John@65
|
272 biddingOn:SetCallback("OnLeave", function(widget) _G.GameTooltip:Hide() end )
|
John@65
|
273 local b1 = AceGUI:Create("SelectorList")
|
John@65
|
274 b1:SetNumLines(6)
|
John@65
|
275 b1:SetInteractive(admin)
|
John@65
|
276 local dummydata= {}
|
John@65
|
277 local tree =SListPopulator.data
|
John@65
|
278 for i,v in pairs(tree) do dummydata[i] = copy(v); dummydata[i].disabled = false end
|
John@65
|
279 if dummydata[2] then dummydata[2].text = dummydata[2].text .. " (roll 73)" end
|
John@65
|
280 b1:SetList(dummydata)
|
John@65
|
281 local bidTitle = AceGUI:Create("Label")
|
John@65
|
282 bidTitle:SetText("Current bids")
|
John@65
|
283 bidTitle:SetFullWidth(true)
|
John@65
|
284
|
John@65
|
285 local bidRetractButton = AceGUI:Create("Button")
|
John@65
|
286 bidRetractButton:SetText("Place Bid")
|
John@65
|
287 bidRetractButton:SetWidth(100)
|
John@65
|
288 local rollButton = AceGUI:Create("Button")
|
John@65
|
289 rollButton:SetText("Offset Roll")
|
John@65
|
290 rollButton:SetWidth(100)
|
John@65
|
291
|
John@65
|
292 local g1
|
John@65
|
293 if admin then
|
John@65
|
294 b1.alignoffset = 0.25 -- or else g1 won't align well
|
John@65
|
295 g1 = AceGUI:Create("SimpleGroup")
|
John@65
|
296 g1.alignoffset = 0.25
|
John@65
|
297 g1:SetWidth(120)
|
John@65
|
298 g1:SetLayout("List")
|
John@65
|
299
|
John@65
|
300 adminForce = AceGUI:Create("Button")
|
John@65
|
301 adminForce:SetText("Force bid")
|
John@65
|
302 adminForce:SetWidth(100)
|
John@65
|
303
|
John@65
|
304 adminRetract = AceGUI:Create("Button")
|
John@65
|
305 adminRetract:SetText("Retract bid")
|
John@65
|
306 adminRetract:SetWidth(100)
|
John@65
|
307
|
John@65
|
308 g1:AddChildren(adminForce,adminRetract)
|
John@65
|
309 end
|
John@65
|
310
|
John@65
|
311 biddingZone:AddChildren(spacer,label,biddingOn,bidRetractButton,rollButton,spacer2,bidTitle,b1)
|
John@65
|
312 if admin then biddingZone:AddChildren(g1) end
|
John@65
|
313
|
John@65
|
314
|
John@65
|
315 end
|
John@65
|
316 widget.userdata.state = not widget.userdata.state
|
John@65
|
317 end
|
John@65
|
318 )
|
John@60
|
319
|
John@60
|
320 alb2 = AceGUI:Create("Button")
|
John@60
|
321 alb2:SetWidth(100)
|
John@60
|
322 alb2:SetText("Assign")
|
John@65
|
323 alb2.userdata =
|
John@65
|
324 {
|
John@65
|
325 widget = alb2,
|
John@65
|
326 ["OnUpdate"] = function(self) self.widget:SetDisabled(not (self.slist.checked and self.llist.checked)) end,
|
John@65
|
327 }
|
John@65
|
328 alb2.userdata.slist =
|
John@65
|
329 {
|
John@65
|
330 parent = alb2.userdata,
|
John@65
|
331 checked = false,
|
John@65
|
332 ["Redraw"] = function(self) self.checked = false; self.parent:OnUpdate() end,
|
John@65
|
333 ["OnSelection"] = function(self) self.checked = true; self.parent:OnUpdate() end,
|
John@65
|
334 ["OnSelectionCleared"] = function(self) self.checked = false; self.parent:OnUpdate() end,
|
John@65
|
335 }
|
John@65
|
336 alb2.userdata.llist =
|
John@65
|
337 {
|
John@65
|
338 parent = alb2.userdata,
|
John@65
|
339 checked = false,
|
John@65
|
340 ["Redraw"] = function(self) self.checked = false; self.parent:OnUpdate() end,
|
John@65
|
341 ["OnSelection"] = function(self) self.checked = true; self.parent:OnUpdate() end,
|
John@65
|
342 ["OnSelectionCleared"] = function(self) self.checked = false; self.parent:OnUpdate() end,
|
John@65
|
343 }
|
John@65
|
344 SListEventDispatch:RegisterListener(alb2.userdata.slist)
|
John@65
|
345 LListEventDispatch:RegisterListener(alb2.userdata.llist)
|
John@60
|
346
|
John@60
|
347 alb3 = AceGUI:Create("Button")
|
John@60
|
348 alb3:SetWidth(100)
|
John@60
|
349 alb3:SetText("Suicide")
|
John@65
|
350 alb3.userdata = -- TODO: holy hell, come up with a pattern or something for this ....
|
John@65
|
351 {
|
John@65
|
352 widget = alb3,
|
John@65
|
353 ["OnUpdate"] = function(self) self.widget:SetDisabled(not (self.slist.checked and self.llist.checked)) end,
|
John@65
|
354 }
|
John@65
|
355 alb3.userdata.slist =
|
John@65
|
356 {
|
John@65
|
357 parent = alb3.userdata,
|
John@65
|
358 checked = false,
|
John@65
|
359 ["Redraw"] = function(self) self.checked = false; self.parent:OnUpdate() end,
|
John@65
|
360 ["OnSelection"] = function(self) self.checked = true; self.parent:OnUpdate() end,
|
John@65
|
361 ["OnSelectionCleared"] = function(self) self.checked = false; self.parent:OnUpdate() end,
|
John@65
|
362 }
|
John@65
|
363 alb3.userdata.llist =
|
John@65
|
364 {
|
John@65
|
365 parent = alb3.userdata,
|
John@65
|
366 checked = false,
|
John@65
|
367 ["Redraw"] = function(self) self.checked = false; self.parent:OnUpdate() end,
|
John@65
|
368 ["OnSelection"] = function(self) self.checked = true; self.parent:OnUpdate() end,
|
John@65
|
369 ["OnSelectionCleared"] = function(self) self.checked = false; self.parent:OnUpdate() end,
|
John@65
|
370 }
|
John@65
|
371 SListEventDispatch:RegisterListener(alb3.userdata.slist)
|
John@65
|
372 LListEventDispatch:RegisterListener(alb3.userdata.llist)
|
John@60
|
373 end
|
John@38
|
374
|
John@65
|
375 local suicideSelected, undo
|
John@60
|
376 if admin then
|
John@60
|
377
|
John@60
|
378 suicideSelected = AceGUI:Create("Button")
|
John@60
|
379 suicideSelected:SetFullWidth(true)
|
John@60
|
380 suicideSelected:SetText("Suicide")
|
John@63
|
381 -- use userdata + SListEventDispatch to toggle state
|
John@63
|
382 suicideSelected.userdata =
|
John@63
|
383 {
|
John@63
|
384 widget = suicideSelected,
|
John@65
|
385 ["Redraw"] = function(self,_) self.widget:SetDisabled(true) end,
|
John@63
|
386 ["OnSelection"] = function(self,_) self.widget:SetDisabled(false) end,
|
John@63
|
387 ["OnSelectionCleared"] = function(self) self.widget:SetDisabled(true) end
|
John@63
|
388 }
|
John@63
|
389 SListEventDispatch:RegisterListener(suicideSelected.userdata)
|
John@60
|
390
|
John@60
|
391 undo = AceGUI:Create("Button")
|
John@63
|
392 undo:SetText("Undo / out of order")
|
John@60
|
393 undo:SetFullWidth(true)
|
John@63
|
394 undo:SetDisabled(true)
|
John@60
|
395 end
|
John@60
|
396
|
John@56
|
397 local filter = AceGUI:Create("CheckBox")
|
John@56
|
398 filter:SetLabel("Only show active")
|
John@56
|
399 filter:SetFullWidth(true)
|
John@63
|
400 filter:SetValue(false)
|
John@63
|
401 SListPopulator:SetFiltered(false)
|
John@63
|
402 filter:SetCallback("OnValueChanged",function(widget,_,value) SListPopulator:SetFiltered(value) end)
|
John@56
|
403
|
John@56
|
404 left:AddChildren(filter)
|
John@56
|
405 if admin then left:AddChildren(suicideSelected,undo) end
|
John@56
|
406 right:AddChildren(t2)
|
John@56
|
407 if admin then right:AddChildren(alb1,alb2,alb3) end
|
John@56
|
408 right:AddChildren(biddingZone)
|
John@56
|
409 f:AddChildren(left,right)
|
John@58
|
410
|
John@58
|
411
|
John@1
|
412 end
|