Advertisement
Geniusz1

EssentialChat by Geniusz1

Dec 21st, 2019
423
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 88.71 KB | None | 0 0
  1. --------------------------------------------
  2. --             Essential Chat             --
  3. --               by Geniusz1              --
  4. --                                        --
  5. --               version 1.2              --
  6. --------------------------------------------
  7.  
  8. --SETTINGS----------
  9. visualsync=false
  10. serverversion=4
  11. --------------------
  12. --Essential Chat      
  13. laistchan = ''
  14. --mod version:         
  15. ECdate="26.12.2019"
  16. ECversion="1.2"
  17.  
  18. if MANAGER.getsetting('tptmp','autoconnect') == 'true' then
  19.     temp = 1
  20. end
  21.  
  22. local socket = require "socket"
  23.  
  24. function sleep(sec)
  25.     if sec then
  26.         socket.select(nil, nil, sec)
  27.     end
  28. end
  29.  
  30. temp2 = 1
  31.  
  32. scrollrage = true
  33.  
  34. isTyping = false
  35.  
  36. cursoradjust = 0
  37.  
  38. local text
  39.  
  40. local inputBx
  41.  
  42. local windowy = 0
  43. local windowx = 0
  44. local acON = false
  45. local acSuggest = ''
  46. local selected = 1
  47.  
  48.  
  49. function isIn(tab, val)
  50.     for index, value in ipairs(tab) do
  51.         if value == val then
  52.             return true
  53.         end
  54.     end
  55.  
  56.     return false
  57. end
  58.  
  59. if MANAGER.getsetting('tptmp', 'autocomplete') then
  60.     autocompleteV = MANAGER.getsetting('tptmp', 'autocomplete')
  61. else
  62.     autocompleteV = false
  63. end
  64.  
  65. local versionstring = "EssentialChat v1.2"
  66. local trueversion="1.2"
  67. local intversion= tostring(trueversion)
  68. --version check was here
  69.  
  70. TPTMP = {["version"] = serverversion, ["versionStr"] = trueversion} -- script version sent on connect to ensure server protocol is the same
  71. local issocket,socket = pcall(require,"socket")
  72. if not sim.clearRect then error"Tpt version not supported" end
  73. local using_manager = false
  74. local type = type -- people like to overwrite this function with a global a lot
  75. local _print = print
  76. if MANAGER ~= nil or MANAGER_EXISTS then
  77.     using_manager = true
  78.     _print = MANAGER and MANAGER.print or MANAGER_PRINT
  79. else
  80.     _print = print
  81. end
  82. local hooks_enabled = true --hooks only enabled once you maximize the button
  83. --WTF Why it was turned OFF
  84.  
  85. local PORT = 34403 --Change 34403 to your desired port
  86. local KEYBOARD = 1 --only change if you have issues. Only other option right now is 2(finnish).
  87. --Local player vars we need to keep
  88. local L = {mousex=0, mousey=0, realMouseX=0, realMouseY=0, brushx=0, brushy=0, sell=1, sela=296, selr=0, selrep=0, replacemode = 0, mButt=0, mEvent=0, isDrawing=false, dcolour=0, stick2=false, chatHidden=true, flashChat=false,
  89. shift=false, alt=false, ctrl=false, tabs = false, z=false, skipClick=false, pauseNextFrame=false, copying=false, stamp=false, placeStamp=false, lastStamp=nil, lastCopy=nil, smoved=false, rotate=false, sendScreen=false}
  90.  
  91. tptversion = 999 --disable version check
  92. local jacobsmod = nil --disable jacobsmod support (bruh)
  93. math.randomseed(os.time())
  94.  
  95. function string:split(sep)
  96.     local sep, fields = sep or ":", {}
  97.     local pattern = string.format("([^%s]+)", sep)
  98.     self:gsub(pattern, function(c) fields[#fields+1] = c end)
  99.     return fields
  100. end
  101.  
  102. function reverse(arr)
  103.     temparr = {}
  104.     for i = #arr, 1, -1 do
  105.         table.insert(temparr, arr[i])
  106.     end
  107.     return temparr
  108. end
  109. -------------------------------------------------------------------------------------------------------------------------------
  110. function EC_loop()
  111.  
  112. end
  113.  
  114. function EC_info()
  115.     tpt.message_box("EssentialChat INFO",versionstring.."\n".."VERSION "..ECversion.." ("..ECdate..")".."\n\nIt is a modification of Cracker64's Powder Toy Multiplayer.\n\nCreated by Geniusz1")
  116. end
  117.  
  118. local chatwindow
  119. --EC vars
  120. lastchan=''
  121. lastip=''
  122. lastport=''
  123.  
  124. function toboolean(input1)
  125.     if (input1==("1" or "true")) then
  126.         return true
  127.     else
  128.         return false
  129.     end
  130. end
  131.  
  132. function printbool(input2,onoff)
  133.     if onoff then
  134.         if input2 then
  135.             return "ON"
  136.         else
  137.             return "OFF"
  138.         end
  139.     else
  140.         if input2 then
  141.             return "TRUE"
  142.         else
  143.             return "FALSE"
  144.         end
  145.     end
  146. end
  147.  
  148. username = tpt.get_name()
  149.  
  150. if username == "" then
  151.     username="Guest"..math.random(10000,99999)
  152. end
  153.  
  154. local con = {connected = false,
  155.          socket = nil,
  156.          members = nil,
  157.          pingTime = os.time()+60}
  158.  
  159. local function disconnected(reason)
  160.     if con.socket then
  161.         con.socket:close()
  162.     end
  163.     if reason then
  164.         chatwindow:addline(reason,255,50,50)
  165.     else
  166.         chatwindow:addline("Connection was closed",255,50,50)
  167.     end
  168.     con.connected = false
  169.     con.members = {}
  170.     lastchan = ''
  171. end
  172.  
  173. local function conSend(cmd,msg,endNull)
  174.     if not con.connected then return false,"Not connected" end
  175.     msg = msg or ""
  176.     if endNull then msg = msg.."\0" end
  177.     if cmd then msg = string.char(cmd)..msg end
  178.     con.socket:settimeout(10)
  179.     con.socket:send(msg)
  180.     con.socket:settimeout(0)
  181. end
  182. local function joinChannel(chan)
  183.     lastchan=chan --EC
  184.     laistchan=chan
  185.     conSend(16,chan,true)
  186.     --send some things to new channel
  187.     conSend(34,string.char(L.brushx,L.brushy))
  188.     conSend(37,string.char(math.floor(L.sell/256),L.sell%256))
  189.     conSend(37,string.char(math.floor(64 + L.sela/256),L.sela%256))
  190.     conSend(37,string.char(math.floor(128 + L.selr/256),L.selr%256))
  191.     conSend(37,string.char(math.floor(192 + L.selrep/256),L.selrep%256))
  192.     conSend(38,L.replacemode)
  193.     conSend(65,string.char(math.floor(L.dcolour/16777216),math.floor(L.dcolour/65536)%256,math.floor(L.dcolour/256)%256,L.dcolour%256))
  194. end
  195. function connectToServer(ip,port,nick)
  196.     if con.connected then return false,"Already connected" end
  197.     ip = ip or "tptmp.starcatcher.us"
  198.     port = port or PORT
  199.     if tpt.get_name() ~= '' then username = tpt.get_name() end
  200.     nick = nick or username
  201.     last=''--EC
  202.     lastip=ip
  203.     lastport=port
  204.  
  205.  
  206.     local sock = socket.tcp()
  207.     sock:settimeout(10)
  208.     local s,r = sock:connect(ip,port)
  209.     if not s then return false,r end
  210.     sock:settimeout(0)
  211.     sock:setoption("keepalive",true)
  212.     sock:send(string.char(tpt.version.major)..string.char(tpt.version.minor)..string.char(TPTMP.version)..nick.."\0")
  213.     local c,r
  214.     while not c do
  215.     c,r = sock:receive(1)
  216.     if not c and r~="timeout" then break end
  217.     end
  218.     if not c and r~="timeout" then return false,r end
  219.  
  220.     if c~= "\1" then
  221.     if c=="\0" then
  222.         local err=""
  223.         c,r = sock:receive(1)
  224.         while c~="\0" do
  225.         err = err..c
  226.         c,r = sock:receive(1)
  227.         end
  228.         if err=="This nick is already on the server" then
  229.             nick = nick:gsub("(.)$",function(s) local n=tonumber(s) if n and n+1 <= 9 then return n+1 else return nick:sub(-1)..'0' end end)
  230.             return connectToServer(ip,port,nick)
  231.         end
  232.         return false,err
  233.     end
  234.     return false,"Bad Connect"
  235.     end
  236.  
  237.     con.socket = sock
  238.     con.connected = true
  239.     username = nick
  240.     conSend(34,string.char(L.brushx,L.brushy))
  241.     conSend(37,string.char(math.floor(L.sell/256),L.sell%256))
  242.     conSend(37,string.char(math.floor(64 + L.sela/256),L.sela%256))
  243.     conSend(37,string.char(math.floor(128 + L.selr/256),L.selr%256))
  244.     conSend(37,string.char(math.floor(192 + L.selrep/256),L.selrep%256))
  245.     conSend(38,L.replacemode)
  246.     conSend(65,string.char(math.floor(L.dcolour/16777216),math.floor(L.dcolour/65536)%256,math.floor(L.dcolour/256)%256,L.dcolour%256))
  247.     return true
  248. end
  249. --get up to a null (\0)
  250. local function conGetNull()
  251.     con.socket:settimeout(nil)
  252.     local c,r = con.socket:receive(1)
  253.     if not c and r ~= "timeout" then disconnected() return nil end
  254.     local rstring=""
  255.     while c~="\0" do
  256.     rstring = rstring..c
  257.     c,r = con.socket:receive(1)
  258.     if not c and r ~= "timeout" then disconnected() return nil end
  259.     end
  260.     con.socket:settimeout(0)
  261.     return rstring
  262. end
  263. --get next char/byte
  264. local function cChar()
  265.     con.socket:settimeout(nil)
  266.     local c,r = con.socket:receive(1)
  267.     con.socket:settimeout(0)
  268.     if not c then disconnected() end
  269.     return c
  270. end
  271. local function cByte()
  272.     local byte = cChar()
  273.     return byte and byte:byte() or nil
  274. end
  275. --return table of arguments
  276. local function getArgs(msg)
  277.     if not msg then return {} end
  278.     local args = {}
  279.     for word in msg:gmatch("([^%s%c]+)") do
  280.     table.insert(args,word)
  281.     end
  282.     return args
  283. end
  284.  
  285. local ui_base local ui_box local ui_text local ui_button local ui_scrollbar local ui_inputbox local ui_chatbox
  286. ui_base = {
  287. new = function()
  288.     local b={}
  289.     b.drawlist = {}
  290.     function b:drawadd(f)
  291.         table.insert(self.drawlist,f)
  292.     end
  293.     function b:draw(...)
  294.         for _,f in ipairs(self.drawlist) do
  295.             if type(f)=="function" then
  296.                 f(self,...)
  297.             end
  298.         end
  299.     end
  300.     b.movelist = {}
  301.     function b:moveadd(f)
  302.         table.insert(self.movelist,f)
  303.     end
  304.     function b:onmove(x,y)
  305.         for _,f in ipairs(self.movelist) do
  306.             if type(f)=="function" then
  307.                 f(self,x,y)
  308.             end
  309.         end
  310.     end
  311.     return b
  312. end
  313. }
  314. ui_box = {
  315. new = function(x,y,w,h,r,g,b)
  316.     local box=ui_base.new()
  317.     box.x=x box.y=y box.w=w box.h=h box.x2=x+w box.y2=y+h
  318.     box.r=r or 255 box.g=g or 255 box.b=b or 255
  319.     function box:setcolor(r,g,b) self.r=r self.g=g self.b=b end
  320.     function box:setbackground(r,g,b,a) self.br=r self.bg=g self.bb=b self.ba=a end
  321.     box.drawbox=true
  322.     box.drawbackground=false
  323.     box:drawadd(function(self) if self.drawbackground then gfx.fillRect(self.x,self.y,self.w+1,self.h+1,self.br,self.bg,self.bb,self.ba) end
  324.                                 if self.drawbox then gfx.drawRect(self.x,self.y,self.w+1,self.h+1,self.r,self.g,self.b) end end)
  325.     box:moveadd(function(self,x,y)
  326.         if x then self.x=self.x+x self.x2=self.x2+x end
  327.         if y then self.y=self.y+y self.y2=self.y2+y end
  328.     end)
  329.     return box
  330. end
  331. }
  332. ui_text = {
  333. new = function(text,x,y,r,g,b)
  334.     local txt = ui_base.new()
  335.     txt.text = text
  336.     txt.x=x or 0 txt.y=y or 0 txt.r=r or 255 txt.g=g or 255 txt.b=b or 255
  337.     function txt:setcolor(r,g,b) self.r=r self.g=g self.b=b end
  338.     txt:drawadd(function(self,x,y)
  339.         gfx.drawText(x or self.x,y or self.y,self.text,self.r,self.g,self.b)
  340.     end)
  341.     txt:moveadd(function(self,x,y)
  342.         if x then self.x=self.x+x end
  343.         if y then self.y=self.y+y end
  344.     end)
  345.     return txt
  346. end,
  347. --Scrolls while holding mouse over
  348. newscroll = function(text,x,y,vis,force,r,g,b)
  349.     local txt = ui_text.new(text,x,y,r,g,b)
  350.     if not force and tpt.textwidth(text)<vis then return txt end
  351.     txt.visible=vis
  352.     txt.length=string.len(text)
  353.     txt.start=1
  354.     local last=2
  355.     while tpt.textwidth(text:sub(1,last))<vis and last<=txt.length do
  356.         last=last+1
  357.     end
  358.     txt.last=last-1
  359.     txt.minlast=last-1
  360.     txt.ppl=((txt.visible-6)/(txt.length-txt.minlast+1))
  361.     function txt:update(text,pos)
  362.         if text then
  363.             self.text=text
  364.             self.length=string.len(text)
  365.             local last=2
  366.             while tpt.textwidth(text:sub(1,last))<self.visible and last<=self.length do
  367.                 last=last+1
  368.             end
  369.             self.minlast=last-1
  370.             self.ppl=((self.visible-6)/(self.length-self.minlast+1))
  371.             if not pos then self.last=self.minlast end
  372.         end
  373.         if pos then
  374.             if pos>=self.last and pos<=self.length then --more than current visible
  375.                 local newlast = pos
  376.                 local newstart=1
  377.                 while tpt.textwidth(self.text:sub(newstart,newlast))>= self.visible do
  378.                     newstart=newstart+1
  379.                 end
  380.                 self.start=newstart self.last=newlast
  381.             elseif pos<self.start and pos>0 then --position less than current visible
  382.                 local newstart=pos
  383.                 local newlast=pos+1
  384.                 while tpt.textwidth(self.text:sub(newstart,newlast))<self.visible and newlast<self.length do
  385.                         newlast=newlast+1
  386.                 end
  387.                 self.start=newstart self.last=newlast-1
  388.             end
  389.             --keep strings as long as possible (pulls from left)
  390.             local newlast=self.last
  391.             if newlast<self.minlast then newlast=self.minlast end
  392.             local newstart=1
  393.             while tpt.textwidth(self.text:sub(newstart,newlast))>= self.visible do
  394.                     newstart=newstart+1
  395.             end
  396.             self.start=newstart self.last=newlast
  397.         end
  398.     end
  399.     txt.drawlist={} --reset draw
  400.     txt:drawadd(function(self,x,y)
  401.         gfx.drawText(x or self.x,y or self.y, self.text:sub(self.start,self.last) ,self.r,self.g,self.b)
  402.     end)
  403.     function txt:mouseMove(mx,my,dX,dY)
  404.         local newlast = math.floor((mx-self.x)/self.ppl)+self.minlast
  405.         if newlast<self.minlast then newlast=self.minlast end
  406.         if newlast>0 and newlast~=self.last then
  407.             local newstart=1
  408.             while tpt.textwidth(self.text:sub(newstart,newlast))>= self.visible do
  409.                 newstart=newstart+1
  410.             end
  411.             self.start=newstart self.last=newlast
  412.         end
  413.     end
  414.     return txt
  415. end
  416. }
  417.  
  418. ui_inputbox = {
  419. new=function(x,y,w,h)
  420.     local intext=ui_box.new(x,y,w,h)
  421.     intext.x = x
  422.     intext.y = y
  423.     intext.x2 = x+w
  424.     intext.y2 = y+h
  425.     intext.cursor=0
  426.     intext.line=1
  427.     intext.currentline = ""
  428.     intext.focus=false
  429.     intext.t=ui_text.newscroll("",x+2,y+2,w-2,true)
  430.     intext.history={}
  431.     intext.max_history=200
  432.     intext.ratelimit = 0
  433.     intext:drawadd(function(self)
  434.         cursoradjust=tpt.textwidth(self.t.text:sub(self.t.start,self.cursor))+2
  435.         self.t:draw()
  436.         cursor = self.cursor
  437.         if #self.t.text ~= 0 and self.focus then
  438.             if (math.floor(os.clock()*2.5))%2==0 and not isTyping then
  439.                 gfx.drawLine(
  440.                     self.x+cursoradjust,
  441.                     self.y,
  442.                     self.x+cursoradjust,
  443.                     self.y2
  444.                 )
  445.             elseif isTyping then
  446.                 gfx.drawLine(
  447.                     self.x+cursoradjust,
  448.                     self.y,
  449.                     self.x+cursoradjust,
  450.                     self.y2
  451.                 )
  452.             end
  453.         end
  454.         if (math.floor(os.clock()*5))%2~=0 then
  455.             isTyping = false
  456.         end
  457.     end)
  458.     intext:moveadd(function(self,x,y) self.t:onmove(x,y) end)
  459.     function intext:setfocus(focus)
  460.         self.focus=focus
  461.         if focus then self:setcolor(255,255,0)
  462.         else self:setcolor(255,255,255) end
  463.     end
  464.     function intext:movecursor(amt)
  465.         self.cursor = self.cursor+amt
  466.         isTyping = true
  467.         if self.cursor>self.t.length then self.cursor = self.t.length end
  468.         if self.cursor<0 then self.cursor = 0 return end
  469.     end
  470.     function intext:addhistory(str)
  471.         self.history[#self.history+1] = str
  472.         if #self.history >= self.max_history then
  473.             table.remove(self.history, 1)
  474.         end
  475.     end
  476.     function intext:moveline(amt)
  477.         self.line = self.line+amt
  478.         local max = #self.currentline and #self.history+2 or #self.history+1
  479.         if self.line>max then self.line=max
  480.         elseif self.line<1 then self.line=1 end
  481.         local history = self.history[self.line] or ""
  482.         if self.line == #self.history+1 then history = self.currentline end
  483.         self.cursor = string.len(history)
  484.         self.t:update(history, self.cursor)
  485.     end
  486.  
  487.     function intext:keypress(key, scan, rep, shift, ctrl, alt)
  488.         if not self.focus then
  489.             if key == 13 then
  490.                 self:setfocus(true)
  491.             end
  492.             return
  493.         end
  494.         -- Esc
  495.         if key == 27 then
  496.             self:setfocus(false)
  497.         -- Enter
  498.         elseif key == 13 and not rep then
  499.             if acON == true then
  500.                 self.t:update(acSuggest)
  501.                 self.cursor = #acSuggest
  502.             else
  503.                 local text = self.t.text
  504.                 if text == "" then
  505.                     self:setfocus(false)
  506.                 else
  507.                     self.cursor = 0
  508.                     self.t.text = ""
  509.                     self:addhistory(text)
  510.                     self.line = #self.history + 1
  511.                     self.currentline = ""
  512.                     self.ratelimit = socket.gettime() + 1
  513.                     return text --now the text is transfered to the server
  514.                 end
  515.             end
  516.         -- Up
  517.         elseif key == 1073741906 then
  518.             if acON == true then
  519.                 selected = selected+1
  520.             else
  521.                 self:moveline(-1)
  522.             end
  523.         -- Down
  524.         elseif key == 1073741905 then
  525.             if acON == true then
  526.                 selected = selected-1
  527.             else
  528.                 self:moveline(1)
  529.             end
  530.         -- Right
  531.         elseif key == 1073741903 then
  532.             self:movecursor(1)
  533.             self.t:update(nil, self.cursor)
  534.         -- Left
  535.         elseif key == 1073741904 then
  536.             self:movecursor(-1)
  537.             self.t:update(nil, self.cursor)
  538.         end
  539.  
  540.         local newstr
  541.         -- Backspace
  542.         if key == 8 then
  543.             if self.cursor > 0 then
  544.                 newstr = self.t.text:sub(1,self.cursor-1) .. self.t.text:sub(self.cursor+1)
  545.                 self:movecursor(-1)
  546.             end
  547.         -- Delete
  548.         elseif key == 127 then
  549.             newstr=self.t.text:sub(1,self.cursor) .. self.t.text:sub(self.cursor+2)
  550.         -- Tab
  551.         elseif key == 9 then
  552.             local nickstart, nickend, nick = self.t.text:sub(1,self.cursor+1):find("([^%s%c]+)"..(self.cursor == #self.t.text and "" or " ").."$")
  553.             if con.members and nick then
  554.                 for k, v in pairs(con.members) do
  555.                     if v.name:sub(1, #nick) == nick then
  556.                         nick = v.name
  557.                         if nickstart == 1 then
  558.                             nick = nick .. ":"
  559.                         end
  560.                         newstr = self.t.text:sub(1,nickstart-1)..nick.." "..self.t.text:sub(nickend+1, #self.t.text)
  561.                         self.cursor = nickstart + #nick
  562.                     end
  563.                 end
  564.             end
  565.         end
  566.         if newstr then
  567.             self.t:update(newstr,self.cursor)
  568.         end
  569.     end
  570.     function intext:textinput(text)
  571.         if not self.focus then
  572.             return
  573.         end
  574.         -- TPT font has no unicode characters at the moment, nor does it have a good enough api to support them
  575.         if #text > 1 or string.byte(text) < 20 or string.byte(text) > 126 then return end
  576.         newstr = self.t.text:sub(1, self.cursor) .. text .. self.t.text:sub(self.cursor + 1)
  577.         self.currentline = newstr
  578.         if #self.t.text > 200 then
  579.             tpt.log('Message too long!')
  580.         else
  581.             self.t:update(newstr, self.cursor + 1)
  582.             self:movecursor(1)
  583.         end
  584.     end
  585.     return intext
  586. end
  587. }
  588. ui_scrollbar = {
  589. new = function(x,y,h,t,m)
  590.     local bar = ui_base.new() --use line object as base?
  591.     bar.x=x bar.y=y bar.h=h
  592.     bar.total=t
  593.     bar.numshown=m
  594.     bar.pos=0
  595.     bar.length=math.floor((1/math.ceil(bar.total-bar.numshown+1))*bar.h)
  596.     bar.soffset=math.floor(bar.pos*((bar.h-bar.length)/(bar.total-bar.numshown)))
  597.     function bar:update(total,shown,pos)
  598.         self.pos=pos or 0
  599.         if self.pos<0 then self.pos=0 end
  600.         self.total=total
  601.         self.numshown=shown
  602.         self.length= math.floor((1/math.ceil(self.total-self.numshown+1))*self.h)
  603.         self.soffset= math.floor(self.pos*((self.h-self.length)/(self.total-self.numshown)))
  604.     end
  605.     function bar:move(wheel)
  606.         self.pos = self.pos-wheel
  607.         if self.pos < 0 then self.pos=0 end
  608.         if self.pos > (self.total-self.numshown) then self.pos=(self.total-self.numshown) end
  609.         self.soffset= math.floor(self.pos*((self.h-self.length)/(self.total-self.numshown)))
  610.     end
  611.     bar:drawadd(function(self)
  612.         if self.total > self.numshown then
  613.             gfx.drawLine(self.x,self.y+self.soffset + 2,self.x,self.y+self.soffset+self.length)
  614.         end
  615.     end)
  616.     bar:moveadd(function(self,x,y)
  617.         if x then self.x=self.x+x end
  618.         if y then self.y=self.y+y end
  619.     end)
  620.     function bar:mouseWheel(mx,my,wheel)
  621.         if wheel~=0 and not hidden_mode then
  622.             if self.total > self.numshown then
  623.                 local previous = self.pos
  624.                 self:move(wheel)
  625.                 if self.pos~=previous then
  626.                     return wheel
  627.                 end
  628.             end
  629.         end
  630.     end
  631.     return bar
  632. end
  633. }
  634. ui_button = {
  635. new = function(x,y,w,h,f,text,justify)
  636.     local b = ui_box.new(x,y,w,h)
  637.     if justify == true then justify = 1 else justify = 0 end
  638.     b.f=f
  639.     b.t=ui_text.new(text,x+2,y+2-justify)
  640.     b.drawbox=false
  641.     b.clicked = false
  642.     b.invert = false
  643.     b:drawadd(function(self)
  644.         if self.clicked or self.invert then
  645.             gfx.fillRect(self.x,self.y,self.w+1,self.h)
  646.             local tr=self.t.r local tg=self.t.g local tb=self.t.b
  647.             b.t:setcolor(0,0,0)
  648.             b.t:draw()
  649.             b.t:setcolor(tr,tg,tb)
  650.         else
  651.             b.t:draw()
  652.         end
  653.     end)
  654.     b:moveadd(function(self,x,y)
  655.         self.t:onmove(x,y)
  656.     end)
  657.     function b:mouseDown(mouseX, mouseY, button, reason)
  658.         if mouseX >= self.x and mouseX <= self.x2 and mouseY >= self.y and mouseY <= self.y2 then
  659.             self.clicked = true
  660.             return true
  661.         end
  662.     end
  663.     function b:mouseMove(mouseX, mouseY, dX, dY)
  664.         if not (mouseX >= self.x and mouseX <= self.x2 and mouseY >= self.y and mouseY <= self.y2) then
  665.             self.clicked = false
  666.         end
  667.     end
  668.     function b:mouseUp(mouseX, mouseY, button, reason)
  669.         if self.clicked and mouseX >= self.x and mouseX <= self.x2 and mouseY >= self.y and mouseY <= self.y2 then
  670.             self:f()
  671.             return true
  672.         end
  673.     end
  674.     return b
  675. end
  676. }
  677. ------------------------------------
  678.  
  679. ui_checkbox = {
  680.     new = function(x,y,text,prop,fT,fF,r,g,b)
  681.         local cb = ui_box.new(x,y,8,8)
  682.         if MANAGER.getsetting('tptmp', prop) == 'true' then
  683.             cb.selected = true
  684.         else
  685.             cb.selected = false
  686.         end
  687.         cb.x=x cb.y=y cb.r=r cb.g=g cb.b=b
  688.         cb.check=ui_button.new(x+1, y+1, 11+tpt.textwidth(text), 7,
  689.         function()
  690.             if cb.selected then
  691.                 cb.selected = false
  692.                 fT()
  693.             else
  694.                 cb.selected = true
  695.                 fF()
  696.             end
  697.         end, '')
  698.         cb:drawadd(
  699.         function(self)
  700.             if cb.selected then
  701.                 gfx.fillRect(cb.x+2,cb.y+2,5,5,255,255,255)
  702.             end
  703.             gfx.drawText(cb.x+12, cb.y+1, text, r or 255, g or 255, b or 255)
  704.         end)
  705.         return cb
  706.     end
  707. }
  708.  
  709. local results = {}
  710. local longestS = ''
  711.  
  712. ui_autocompleteBox = {
  713.     new = function(x,y)
  714.         local ac = ui_base.new()
  715.         ac.x=x ac.y=y-10 ac.w=10 ac.h=10 ac.x2=x+10 ac.y2=y+10
  716.         ac.jumper = ui_box.new(x, y-10, 10, 10)
  717.         ac:drawadd(
  718.         function(self)
  719.             if MANAGER.getsetting('tptmp', 'autocomplete') == 'true' then
  720.                 if text:find(' ') then
  721.                     sample = text:reverse():find(' ')
  722.                 else
  723.                     sample = #text+1
  724.                 end
  725.                 word = text:reverse():sub(1, sample-1):reverse()
  726.                 pattern=''
  727.                 for i=1, #word do
  728.                     pattern = pattern..'['..word:sub(i,i):upper()..','..word:sub(i,i):lower()..']'
  729.                 end
  730.                 pattern = pattern..'.*'
  731.                 pattern1 = '^'..pattern..':'
  732.                 pattern2 = '^'..pattern..' has joined'
  733.                 pattern3 = '^'..pattern..' has left'
  734.                 --pattern4 = 'Online:%(.%)* '..pattern..'%( %)+'
  735.                 results = {}
  736.                 if #word > 2 then
  737.                     for i, v in ipairs(theLines) do
  738.                         if v:match(pattern1) ~= nil then
  739.                             found = v:match(pattern1)
  740.                             found = found:gsub(':', '')
  741.                             if found:find(' ') then
  742.                                 found = found:sub(1,found:find(' ')-1)
  743.                             end
  744.                             if found ~= word then
  745.                                 table.insert(results, found)
  746.                             end
  747.                         elseif v:match(pattern2) ~= nil then
  748.                             found = v:match(pattern2)
  749.                             found = found:gsub(' has joined', '')
  750.                             if found ~= word then
  751.                                 table.insert(results, found)
  752.                             end
  753.                         elseif v:match(pattern3) ~= nil then
  754.                             found = v:match(pattern3)
  755.                             found = found:gsub(' has left', '')
  756.                             if found ~= word then
  757.                                 table.insert(results, found)
  758.                             end
  759.                         -- elseif v:match(pattern4) ~= nil then
  760.                         --  found = v:match(pattern4)
  761.                         --  found = found:gsub('Online: ', '')
  762.                         --  tpt.log('pat4: '..found)
  763.                         end
  764.                     end
  765.                 end
  766.                 --
  767.                 if #word > 2 then
  768.                     for i,v in ipairs(theLines) do
  769.                         if v:match('^Online: ') then
  770.                             onlines = v:split(' ')
  771.                             table.remove(onlines, 1)
  772.                             for i,n in ipairs(onlines) do
  773.                                 if n:match(pattern) then
  774.                                     if n:match(pattern) ~= word then
  775.                                         table.insert(results, n)
  776.                                     end
  777.                                 end
  778.                             end
  779.                         end
  780.                     end
  781.                 end
  782.                 temptable = {}
  783.                 for i,v in ipairs(results) do
  784.                     if not isIn(temptable, v) then table.insert(temptable, v) end
  785.                 end
  786.                 results = temptable
  787.                 for i,v in ipairs(results) do
  788.                     if v == 'Online' then table.remove(results, i) end
  789.                 end
  790.                
  791.                 --
  792.                 acON = #results > 0 and cursor > #text-#word-1
  793.                 if acON then self.jumper:draw() end
  794.                 self.jumper.y = windowy - 10*#results - 12
  795.                 self.jumper.h = 10*#results+2
  796.                 self.jumper:setbackground(10,10,10,235)
  797.                 self.jumper.drawbackground=true
  798.                 self.jumper.x = windowx + cursoradjust
  799.                 --
  800.                 longestN = 0
  801.                 longestS = ''
  802.                 for i,v in ipairs(results) do
  803.                     if #v > longestN then
  804.                         longestN = #v
  805.                         longestS = v
  806.                     end
  807.                 end
  808.  
  809.                 if selected > #results then selected = #results end
  810.                 if selected < 1 then selected = 1 end
  811.  
  812.  
  813.                 self.jumper.w = tpt.textwidth(longestS)+2
  814.                 --
  815.                 if acON then
  816.                     if #results > 0 then
  817.                         gfx.fillRect(windowx+cursoradjust+1, windowy-10*selected-11, tpt.textwidth(longestS)+1, 11, 100,100,100)
  818.                     end
  819.                     for i,v in ipairs(results) do
  820.                         gfx.drawText(windowx+cursoradjust+2,windowy-10*i-10, v)
  821.                     end
  822.                     acSuggest = text:sub(1,-#word-1)..results[selected]
  823.                 end
  824.             end --if end
  825.         end)
  826.         return ac
  827.     end
  828. }
  829.  
  830. ------------------------------------
  831. settingsOpened = false
  832.  
  833. theLines = {}
  834. lineNum = 1
  835.  
  836. ui_settings = {
  837.     new = function(x,y,w,h)
  838.         local set=ui_box.new(x,y,w,h)
  839.         set.x=x set.y=y set.w=w set.h=h set.x2=x+w set.y2=y+h
  840.         set.cLine = ui_box.new(set.x+tpt.textwidth('Copy to clipboard line no.  '), set.y+4, 20, 10)
  841.         set.downbutt = ui_button.new(
  842.             set.x+tpt.textwidth('Copy to clipboard line no.')+31,
  843.             set.y+4,
  844.             8,
  845.             11,
  846.             function()
  847.                 if lineNum > 1 then
  848.                     lineNum = lineNum - 1
  849.                     if lineNum > 1 then
  850.                         if string.match(chatwindow.lines[lineNum-1].text, '^(>> )(.*)') then
  851.                             chatwindow.lines[lineNum-1].text = chatwindow.lines[lineNum-1].text:sub(4)
  852.                         end
  853.                     end
  854.                     if string.match(chatwindow.lines[lineNum+1].text, '^(>> )(.*)') then
  855.                         chatwindow.lines[lineNum+1].text = chatwindow.lines[lineNum+1].text:sub(4)
  856.                     end
  857.                     if not string.match(chatwindow.lines[lineNum].text, '^(>> )(.*)') then
  858.                         chatwindow.lines[lineNum].text = '>> '..chatwindow.lines[lineNum].text
  859.                     end
  860.                 end
  861.             end,
  862.             '<', true)
  863.         set.defbutt = ui_button.new(
  864.             set.x+tpt.textwidth('Copy to clipboard line no.')+41,
  865.             set.y+4,
  866.             tpt.textwidth('last')+3,
  867.             11,
  868.             function()
  869.                 lineNum = #chatwindow.lines
  870.                 for i, v in ipairs(chatwindow.lines) do
  871.                     if string.match(v.text, '^(>> )(.*)') then
  872.                         chatwindow.lines[i].text = v.text:sub(4)
  873.                     end
  874.                 end
  875.                 chatwindow.lines[lineNum].text = '>> '..chatwindow.lines[lineNum].text
  876.             end,
  877.             'last')
  878.         set.upbutt = ui_button.new(
  879.             set.x+tpt.textwidth('Copy to clipboard line no.')+63,
  880.             set.y+4,
  881.             8,
  882.             11,
  883.             function()
  884.                 if lineNum < #chatwindow.lines then
  885.                     lineNum = lineNum + 1
  886.                     if string.match(chatwindow.lines[lineNum-1].text, '^(>> )(.*)') then
  887.                         chatwindow.lines[lineNum-1].text = chatwindow.lines[lineNum-1].text:sub(4)
  888.                     end
  889.                     if lineNum < #chatwindow.lines then
  890.                         if string.match(chatwindow.lines[lineNum+1].text, '^(>> )(.*)') then
  891.                             chatwindow.lines[lineNum+1].text = chatwindow.lines[lineNum+1].text:sub(4)
  892.                         end
  893.                     end
  894.                     if not string.match(chatwindow.lines[lineNum].text, '^(>> )(.*)') then
  895.                         chatwindow.lines[lineNum].text = '>> '..chatwindow.lines[lineNum].text
  896.                     end
  897.                 elseif lineNum == #chatwindow.lines then
  898.                     if string.match(chatwindow.lines[lineNum-1].text, '^(>> )(.*)') then
  899.                         chatwindow.lines[lineNum-1].text = chatwindow.lines[lineNum-1].text:sub(4)
  900.                     end
  901.                     if not string.match(chatwindow.lines[lineNum].text, '^(>> )(.*)') then
  902.                         chatwindow.lines[lineNum].text = '>> '..chatwindow.lines[lineNum].text
  903.                     end
  904.                 end
  905.             end,
  906.             '>', true)
  907.         set.copy = ui_button.new(
  908.             set.x+4,
  909.             set.y+4,
  910.             tpt.textwidth('Copy to clipboard line no.')+3,
  911.             11,
  912.             function()
  913.                 tpt.set_clipboard(theLines[lineNum])
  914.                 tpt.log('Copied to clipboard')
  915.                 for i, v in ipairs(chatwindow.lines) do
  916.                     if string.match(v.text, '^(>> )(.*)') then
  917.                         chatwindow.lines[i].text = v.text:sub(4)
  918.                     end
  919.                 end
  920.             end,
  921.             'Copy to clipboard line no.')
  922.         set.paste = ui_button.new(
  923.             set.x+4,
  924.             set.y+16,
  925.             tpt.textwidth('Paste text from clipboard')+3,
  926.             11,
  927.             function()
  928.                 local txt = tpt.get_clipboard()
  929.                 txt:gsub('\n', ' ')
  930.                 chatwindow.inputbox.t:update(chatwindow.inputbox.t.text..txt, chatwindow.inputbox.cursor + #txt)
  931.                 chatwindow.inputbox.cursor = #chatwindow.inputbox.t.text
  932.             end,
  933.             'Paste text from clipboard')
  934.         set.info = ui_button.new(
  935.             set.x+set.w/2-tpt.textwidth('INFO')/2,
  936.             set.y+67,
  937.             tpt.textwidth('INFO')+3,
  938.             10,
  939.             function()
  940.                 EC_info()
  941.             end,
  942.             'INFO')
  943.         set.autocomplete = ui_checkbox.new(set.x+6, set.y+42,'Autocomplete usernames','autocomplete',
  944.             function()
  945.                 --whenUNSelected
  946.                 autocompleteV = false
  947.                 MANAGER.savesetting('tptmp', 'autocomplete', 'false')
  948.             end,
  949.             function()
  950.                 --whenSelected
  951.                 autocompleteV = true
  952.                 MANAGER.savesetting('tptmp', 'autocomplete', 'true')
  953.             end, 200,200,200)
  954.         set.downtick = ui_checkbox.new(set.x+6, set.y+30,'Freeze the chat',nil,
  955.         function()
  956.             --whenUNSelected
  957.             scrollrage = true
  958.             chatwindow.scrollbar:update(#chatwindow.lines,chatwindow.shown_lines,#chatwindow.lines-chatwindow.shown_lines)
  959.         end,
  960.         function()
  961.             --whenSelected
  962.             scrollrage = false
  963.         end, 200,200,200)
  964.         set.connectonstart = ui_checkbox.new(set.x+6, set.y+54,'Auto-connect on start', 'autoconnect',
  965.         function()
  966.             --whenUNSelected
  967.             MANAGER.savesetting('tptmp', 'autoconnect', 'false')
  968.         end,
  969.         function()
  970.             --whenSelected
  971.             MANAGER.savesetting('tptmp', 'autoconnect', 'true')
  972.         end, 200,200,200)
  973.         set:drawadd(
  974.         function(self)
  975.             gfx.fillRect(self.x+1,self.y+1,self.w-1,self.h-1, 10,10,10,235)
  976.             gfx.drawRect(self.x+tpt.textwidth('Copy to clipboard line no.')+30, self.y+4,43, 11, 100,100,100)
  977.             gfx.drawLine(self.x+tpt.textwidth('Copy to clipboard line no.')+40, self.y+4,self.x+tpt.textwidth('Copy to clipboard line no.')+40, self.y+14, 100,100,100)
  978.             gfx.drawLine(self.x+tpt.textwidth('Copy to clipboard line no.')+62, self.y+4,self.x+tpt.textwidth('Copy to clipboard line no.')+62, self.y+14, 100,100,100)
  979.             actualLineNum = tostring(lineNum)
  980.             if lineNum < 10 then actualLineNum = '00'..lineNum
  981.             elseif lineNum < 100 then actualLineNum = '0'..lineNum end
  982.             gfx.drawText(set.x+tpt.textwidth('Copy to clipboard line no.  ')+2, set.y+6,actualLineNum)
  983.             self.autocomplete:draw()
  984.             self.copy:draw()
  985.             self.paste:draw()
  986.             self.connectonstart:draw()
  987.             self.info.t:setcolor(127,255,255)
  988.             self.info:draw()
  989.             self.cLine:draw()
  990.             self.upbutt:draw()
  991.             self.downbutt:draw()
  992.             self.defbutt:draw()
  993.             self.downtick:draw()
  994.         end)
  995.         return set
  996.     end
  997. }
  998.  
  999. ui_chatbox = {
  1000. new=function(x,y,w,h)
  1001.     local chat=ui_box.new(x,y,w,h)
  1002.     chat.moving=false
  1003.     chat.lastx=0
  1004.     chat.lasty=0
  1005.     chat.relx=0
  1006.     chat.rely=0
  1007.     chat.shown_lines=math.floor(chat.h/10)-2--one line for top, one for chat
  1008.     chat.max_width=chat.w-4
  1009.     chat.max_lines=500
  1010.     chat.lines = {}
  1011.     chat.inputbox = ui_inputbox.new(x+15, chat.y2-10, w-42, 10)
  1012.     chat.acBox = ui_autocompleteBox.new(x+15, chat.y2-10)
  1013.     chat.settings = ui_settings.new(chat.x, chat.y2, chat.w, 82)
  1014.     chat.scrollbar = ui_scrollbar.new(chat.x2-2,chat.y+11,chat.h-22,0,chat.shown_lines)
  1015.     chat.send = ui_button.new(chat.x2-27,chat.y2-10,27,10,
  1016.     function()
  1017.         chat:keypress(13,40,false,false,false,false)
  1018.     end, 'SEND')
  1019.     chat.show = ui_button.new(x,chat.y2-10,15,10,
  1020.     function()
  1021.         if settingsOpened then
  1022.             settingsOpened = false
  1023.         else
  1024.             settingsOpened = true
  1025.         end
  1026.     end,'++')
  1027.     chat.minimize = ui_button.new(chat.x2-15,chat.y,15,12,function() chat.moving=false chat.inputbox:setfocus(false) L.chatHidden=true TPTMP.chatHidden=true end,">>")
  1028.     chat:drawadd(function(self)
  1029.         --gfx.fillRect(self.x,self.y,self.w, 13, 180, 180, 180)
  1030.         -- local var = self.inputbox.cursor*(tpt.textwidth(self.inputbox.t.text)/#self.inputbox.t.text)
  1031.         -- if self.inputbox.t.text == nil then var = 0 end
  1032.         -- if var+2 > self.inputbox.w then var = self.inputbox.w-2 end
  1033.         -- gfx.drawRect(self.inputbox.x+var+2,self.inputbox.y,1,self.inputbox.h)
  1034.         gfx.drawLine(self.x, self.y2-10, self.x+15, self.y2-10)
  1035.         gfx.drawLine(self.x2-27, self.y2-10, self.x2, self.y2-10)
  1036.         gfx.drawText(self.x+self.w-tpt.textwidth("tahClaitnessE")-20,self.y+3,"E",255, 92, 13)
  1037.         gfx.drawText(self.x+self.w-tpt.textwidth("tahClaitness")-20,self.y+3,"s",255, 188, 1)
  1038.         gfx.drawText(self.x+self.w-tpt.textwidth("tahClaitnes")-20,self.y+3,"s",255, 254, 13)
  1039.         gfx.drawText(self.x+self.w-tpt.textwidth("tahClaitne")-20,self.y+3,"e",188, 255, 0)
  1040.         gfx.drawText(self.x+self.w-tpt.textwidth("tahClaitn")-20,self.y+3,"n",13, 255, 255)
  1041.         gfx.drawText(self.x+self.w-tpt.textwidth("tahClait")-20,self.y+3,"t",0, 150, 203)
  1042.         gfx.drawText(self.x+self.w-tpt.textwidth("tahClai")-20,self.y+3,"i",207, 143, 254)
  1043.         gfx.drawText(self.x+self.w-tpt.textwidth("tahCla")-20,self.y+3,"a",255, 92, 13)
  1044.         gfx.drawText(self.x+self.w-tpt.textwidth("tahCl")-20,self.y+3,"l",255, 188, 1)
  1045.         gfx.drawText(self.x+self.w-tpt.textwidth("tahC")-20,self.y+3,"C",255, 254, 13)
  1046.         gfx.drawText(self.x+self.w-tpt.textwidth("tah")-20,self.y+3,"h",188, 255, 0)
  1047.         gfx.drawText(self.x+self.w-tpt.textwidth("ta")-20,self.y+3,"a",13, 255, 255)
  1048.         gfx.drawText(self.x+self.w-tpt.textwidth("t")-20,self.y+3,"t",0, 150, 203)
  1049.  
  1050.         text = self.inputbox.t.text
  1051.         inputBx = self.inputbox.x2
  1052.  
  1053.         if MANAGER.getsetting('tptmp','autoconnect') == 'true' and temp == 1 then
  1054.             connectToServer()
  1055.             lastchan = 'null'
  1056.             temp = 0
  1057.         end
  1058.  
  1059.         local chaname = lastchan
  1060.  
  1061.         if tpt.textwidth(lastchan) > self.w - tpt.textwidth("EssentialChat") - 30 then
  1062.             chaname = lastchan:sub(1, (self.w - tpt.textwidth("EssentialChat") - 30) / 4 - 6)..'...'
  1063.         end
  1064.         if lastchan == 'null' then
  1065.             chaname = 'lobby'
  1066.             gfx.drawText(self.x + 4,self.y+3, chaname, 200, 200, 50)
  1067.         else
  1068.             gfx.drawText(self.x + 4,self.y+3, chaname, 23, 161, 190)
  1069.         end
  1070.         gfx.drawLine(self.x+1,self.y+12,self.x2-1,self.y+12,120,120,120)
  1071.         self.scrollbar:draw()
  1072.         local count=0
  1073.         for i,line in ipairs(self.lines) do
  1074.             if i>self.scrollbar.pos and i<= self.scrollbar.pos+self.shown_lines then
  1075.                 line:draw(self.x+3,self.y+15+(count*9.5))
  1076.                 count = count+1
  1077.             end
  1078.         end
  1079.         if settingsOpened then
  1080.             self.settings:draw()
  1081.         end
  1082.         self.show:draw()
  1083.         self.send:draw()
  1084.         self.inputbox:draw()
  1085.         self.minimize:draw()
  1086.         self.acBox:draw()
  1087.         windowy = self.y2
  1088.         windowx = self.x
  1089.     end)
  1090.     chat:moveadd(function(self,x,y)
  1091.         for i,line in ipairs(self.lines) do
  1092.             line:onmove(x,y)
  1093.         end
  1094.         self.scrollbar:onmove(x,y)
  1095.         self.settings:onmove(x,y)
  1096.         self.settings.connectonstart:onmove(x,y)
  1097.         self.settings.connectonstart.check:onmove(x,y)
  1098.         self.settings.autocomplete:onmove(x,y)
  1099.         self.settings.autocomplete.check:onmove(x,y)
  1100.         self.settings.downtick:onmove(x,y)
  1101.         self.settings.downtick.check:onmove(x,y)
  1102.         self.settings.info:onmove(x,y)
  1103.         self.inputbox:onmove(x,y)
  1104.         self.minimize:onmove(x,y)
  1105.         self.show:onmove(x,y)
  1106.         self.acBox:onmove(x,y)
  1107.         self.acBox.jumper:onmove(x,y)
  1108.         self.send:onmove(x,y)
  1109.         self.settings.copy:onmove(x,y)
  1110.         self.settings.paste:onmove(x,y)
  1111.         self.settings.cLine:onmove(x,y)
  1112.         self.settings.upbutt:onmove(x,y)
  1113.         self.settings.downbutt:onmove(x,y)
  1114.         self.settings.defbutt:onmove(x,y)
  1115.     end)
  1116.     function chat:addline(line,r,g,b,noflash)
  1117.         if not line or line=="" then return end --No blank lines
  1118.         local linebreak,lastspace = 0,nil
  1119.         for i=0,#line do
  1120.             local width = tpt.textwidth(line:sub(linebreak,i+1))
  1121.             if width > self.max_width/2 and line:sub(i,i):match("[%s,_%.%-?!]") then
  1122.                 lastspace = i
  1123.             end
  1124.             if width > self.max_width or i==#line then
  1125.                 local pos = (i==#line or not lastspace) and i or lastspace
  1126.                 table.insert(self.lines,ui_text.new(line:sub(linebreak,pos),self.x,0,r,g,b))
  1127.                 table.insert(theLines, line:sub(linebreak,pos))
  1128.                 linebreak = pos+1
  1129.                 lastspace = nil
  1130.             end
  1131.         end
  1132.         while #self.lines>self.max_lines do table.remove(self.lines,1) end
  1133.         if scrollrage then
  1134.             self.scrollbar:update(#self.lines,self.shown_lines,#self.lines-self.shown_lines)
  1135.         end
  1136.         if L.chatHidden and not noflash then L.flashChat=true end
  1137.     end
  1138.     chat:addline(versionstring,127,255,255,true)
  1139.     chat:addline("Main commands:",200,200,200,true)
  1140.     chat:addline("/connect - connect to the server",200,200,200,true)
  1141.     chat:addline("/join - join a channel",200,200,200,true)
  1142.     chat:addline("/info - information about the script",200,200,200,true)
  1143.     chat:addline("/reconnect - rejoin server or channel",200,200,200,true)
  1144.     chat:addline("/lobby - go to the lobby",200,200,200,true)
  1145.     chat:addline("/list - list of all commands",200,200,200,true)
  1146.     chat:addline("\n",200,200,200,true)
  1147.     local date = os.date('*t')
  1148.     if date.month == 12 and date.day >= 24 and date.day <= 26 then
  1149.         chat:addline("***************",255,0,0,true)   
  1150.         chat:addline(" Merry Christmas!",0,255,0,true)
  1151.         chat:addline("***************",255,0,0,true)
  1152.         chat:addline("\n",255,0,0,true)
  1153.     elseif date.month == 12 and date.day == 31 then
  1154.         chat:addline("***************",0,0,255,true)   
  1155.         chat:addline(" Happy New Year's Eve!",255,0,0,true)
  1156.         chat:addline("***************",0,0,255,true)
  1157.         chat:addline("\n",255,0,0,true)
  1158.     elseif date.month == 1 and date.day == 1 then
  1159.         chat:addline("***************",0,0,255,true)   
  1160.         chat:addline(" Happy New Year!",255,0,0,true)
  1161.         chat:addline("***************",0,0,255,true)
  1162.         chat:addline("\n",255,0,0,true)
  1163.     end
  1164.     function chat:mouseDown(mouseX, mouseY, button)
  1165.         if L.chatHidden then return false end
  1166.         self.minimize:mouseDown(mouseX, mouseY, button)
  1167.         self.show:mouseDown(mouseX, mouseY, button)
  1168.         self.send:mouseDown(mouseX, mouseY, button)
  1169.         if settingsOpened then
  1170.             self.settings.connectonstart.check:mouseDown(mouseX, mouseY, button)
  1171.             self.settings.autocomplete.check:mouseDown(mouseX, mouseY, button)
  1172.             self.settings.downtick.check:mouseDown(mouseX, mouseY, button)
  1173.             self.settings.info:mouseDown(mouseX, mouseY, button)
  1174.             self.settings.copy:mouseDown(mouseX, mouseY, button)
  1175.             self.settings.paste:mouseDown(mouseX, mouseY, button)
  1176.             self.settings.upbutt:mouseDown(mouseX, mouseY, button)
  1177.             self.settings.downbutt:mouseDown(mouseX, mouseY, button)
  1178.             self.settings.defbutt:mouseDown(mouseX, mouseY, button)
  1179.         end
  1180.         local selectedLine = math.floor((mouseY - self.y) / 10)
  1181.         -- Mouse outside chat window, defocus it
  1182.         if not settingsOpened then
  1183.             if mouseX < self.x or mouseX > self.x2 or mouseY < self.y or mouseY > self.y2 then
  1184.                 for i, v in ipairs(self.lines) do
  1185.                     if string.match(v.text, '^(>> )(.*)') then
  1186.                         self.lines[i].text = v.text:sub(4)
  1187.                     end
  1188.                 end
  1189.                 self.inputbox:setfocus(false)
  1190.                 return false
  1191.             end
  1192.         end
  1193.  
  1194.         if settingsOpened then
  1195.             if mouseX < self.x or mouseX > self.x2 or mouseY < self.y or mouseY > self.y2+self.settings.h then
  1196.                 for i, v in ipairs(self.lines) do
  1197.                     if string.match(v.text, '^(>> )(.*)') then
  1198.                         self.lines[i].text = v.text:sub(4)
  1199.                     end
  1200.                 end
  1201.                 self.inputbox:setfocus(false)
  1202.                 return false
  1203.             end
  1204.         end
  1205.  
  1206.         -- header was grabbed, enable window movement
  1207.         if selectedLine == 0 and mouseX < self.minimize.x then
  1208.             self.moving = true
  1209.             self.lastx = mx
  1210.             self.lasty = my
  1211.             self.relx = mouseX - self.x
  1212.             self.rely = mouseY - self.y
  1213.             return true
  1214.         -- Textbox clicked
  1215.         elseif selectedLine == self.shown_lines + 1 then
  1216.             self.inputbox:setfocus(true)
  1217.             return true
  1218.         end
  1219.  
  1220.         -- At this point we know chatbox is selected, ensure window is focused then block mouse events
  1221.         if not self.inputbox.focus then
  1222.             self.inputbox:setfocus(true)
  1223.         end
  1224.         return true
  1225.     end
  1226.     function chat:mouseMove(mouseX, mouseY, dX, dY)
  1227.         if self.moving then
  1228.             local newx, newy = self.x + dX, self.y + dY
  1229.             if newx < 0 then dX = dX - newx end
  1230.             if newy < 0 then dY = dY - newy end
  1231.             if (newx + self.w) >= sim.XRES then dX = dX - (newx + self.w - sim.XRES) end
  1232.             if (newy + self.h) >= sim.YRES then dY = dY - (newy + self.h - sim.YRES) end
  1233.  
  1234.             if dX < 0 and mouseX > self.relx + self.x then dX = 0 end
  1235.             if dX > 0 and mouseX < self.relx + self.x then dX = 0 end
  1236.             if dY < 0 and mouseY > self.rely + self.y then dY = 0 end
  1237.             if dY > 0 and mouseY < self.rely + self.y then dY = 0 end
  1238.            
  1239.             self:onmove(dX, dY)
  1240.         end
  1241.         self.minimize:mouseMove(mouseX, mouseY, dX, dY)
  1242.         self.show:mouseMove(mouseX, mouseY, dX, dY)
  1243.         self.send:mouseMove(mouseX, mouseY, dX, dY)
  1244.         self.settings.connectonstart.check:mouseMove(mouseX, mouseY, dX, dY)
  1245.         self.settings.autocomplete.check:mouseMove(mouseX, mouseY, dX, dY)
  1246.         self.settings.downtick.check:mouseMove(mouseX, mouseY, dX, dY)
  1247.         self.settings.info:mouseMove(mouseX, mouseY, dX, dY)
  1248.         self.settings.info:mouseMove(mouseX, mouseY, dX, dY)
  1249.         self.settings.copy:mouseMove(mouseX, mouseY, dX, dY)
  1250.         self.settings.paste:mouseMove(mouseX, mouseY, dX, dY)
  1251.         self.settings.upbutt:mouseMove(mouseX, mouseY, dX, dY)
  1252.         self.settings.downbutt:mouseMove(mouseX, mouseY, dX, dY)
  1253.         self.settings.defbutt:mouseMove(mouseX, mouseY, dX, dY)
  1254.     end
  1255.     function chat:mouseUp(mouseX, mouseY, button, reason)
  1256.         self.minimize:mouseUp(mouseX, mouseY, button, reason)
  1257.         self.show:mouseUp(mouseX, mouseY, button, reason)
  1258.         self.send:mouseUp(mouseX, mouseY, button, reason)
  1259.         if settingsOpened then
  1260.             self.settings.connectonstart.check:mouseUp(mouseX, mouseY, button, reason)
  1261.             self.settings.autocomplete.check:mouseUp(mouseX, mouseY, button, reason)
  1262.             self.settings.downtick.check:mouseUp(mouseX, mouseY, button, reason)
  1263.             self.settings.info:mouseUp(mouseX, mouseY, button, reason)
  1264.             self.settings.copy:mouseUp(mouseX, mouseY, button, reason)
  1265.             self.settings.paste:mouseUp(mouseX, mouseY, button, reason)
  1266.             self.settings.upbutt:mouseUp(mouseX, mouseY, button, reason)
  1267.             self.settings.downbutt:mouseUp(mouseX, mouseY, button, reason)
  1268.             self.settings.defbutt:mouseUp(mouseX, mouseY, button, reason)
  1269.         end
  1270.         if self.moving then
  1271.             self.moving = false
  1272.             return true
  1273.         end
  1274.     end
  1275.     function chat:mouseWheel(mouseX, mouseY, wheel)
  1276.         self.scrollbar:mouseWheel(mouseX, mouseY, wheel)
  1277.     end
  1278.     --commands for chat window
  1279.     chatcommands = {
  1280.     reconnect= function(self,msg,args)
  1281.     chat:addline("Reconnecting",100,255,10,true)
  1282.         reconnect()
  1283.     end,
  1284.     clear= function(self,msg,args)
  1285.         chatwindow.lines = {}
  1286.     end,
  1287.     info= function(self,msg,args)
  1288.         EC_info()
  1289.     end,
  1290.     connect= function(self,msg,args)
  1291.         if not issocket then self:addline("No luasockets found") return end
  1292.         --local newname = pcall(string.dump, get_name) and "Gue".."st"..math["random"](1111,9888) or tpt.get_name()
  1293.         if tpt.get_name() ~= '' then username = tpt.get_name() end
  1294.         local s,r = connectToServer(args[1],tonumber(args[2]),username)
  1295.         laistchan = lastchan
  1296.         lastchan = 'null'
  1297.         if not s then self:addline(r,255,50,50) end
  1298.         pressedKeys = nil
  1299.     end,
  1300.     lobby= function(self,msg,args)
  1301.         joinChannel('null')
  1302.     end,
  1303.     send= function(self,msg,args)
  1304.         if tonumber(args[1]) and args[2] then
  1305.         local withNull=false
  1306.         if args[2]=="true" then withNull=true end
  1307.         msg = msg:sub(#args[1]+1+(withNull and #args[2]+2 or 0))
  1308.         conSend(tonumber(args[1]),msg,withNull)
  1309.         end
  1310.     end,
  1311.     quit= function(self,msg,args)
  1312.         disconnected("Disconnected")
  1313.     end,
  1314.     disconnect= function(self,msg,args)
  1315.         disconnected("Disconnected")
  1316.     end,
  1317.     join= function(self,msg,args)
  1318.         if con.connected then
  1319.             if args[1] then
  1320.                 joinChannel(args[1])
  1321.                 self:addline("joined channel "..args[1],50,255,50)
  1322.             end
  1323.         else
  1324.             self:addline("Not connected to server!",255,50,50)
  1325.         end
  1326.     end,
  1327.     sync= function(self,msg,args)
  1328.         if con.connected then L.sendScreen=true end --need to send 67 clear screen
  1329.         self:addline("Synced screen to server",255,255,50)
  1330.     end,
  1331.     help= function(self,msg,args)
  1332.         if not args[1] then self:addline("/help <command>, type /list for a list of commands") end
  1333.         if args[1] == "connect" then self:addline("(/connect [ip] [port]) -- connect to a TPT multiplayer server, or no args to connect to the default one")
  1334.         --elseif args[1] == "send" then self:addline("(/send <something> <somethingelse>) -- send raw data to the server") -- send a raw command
  1335.         elseif args[1] == "quit" or args[1] == "disconnect" then self:addline("(/quit, no arguments) -- quit the game")
  1336.         elseif args[1] == "join" then self:addline("(/join <channel> -- joins a room on the server")
  1337.         elseif args[1] == "sync" then self:addline("(/sync, no arguments) -- syncs your screen to everyone else in the room")
  1338.         elseif args[1] == "me" then self:addline("(/me <message>) -- say something in 3rd person") -- send a raw command
  1339.         elseif args[1] == "kick" then self:addline("(/kick <nick> <reason>) -- kick a user, only works if you have been in a channel the longest")
  1340.         elseif args[1] == "size" then self:addline("(/size <width> <height>) -- sets the size of the chat window")
  1341.         elseif args[1] == "clear" then self:addline("(/clear, no arguments) -- clears whole chat")
  1342.         end
  1343.     end,
  1344.     list= function(self,msg,args)
  1345.         self:addline("Commands: \n")
  1346.         for name in pairs(chatcommands) do
  1347.             self:addline(name.."\n")
  1348.         end
  1349.        
  1350.     end,
  1351.     me= function(self, msg, args)
  1352.         if not con.connected then return end
  1353.         self:addline("* " .. username .. " ".. table.concat(args, " "),200,200,200)
  1354.         conSend(20,table.concat(args, " "),true)
  1355.     end,
  1356.     kick= function(self, msg, args)
  1357.         if not con.connected then return end
  1358.         if not args[1] then self:addline("Need a nick! '/kick <nick> [reason]'") return end
  1359.         conSend(21, args[1].."\0"..table.concat(args, " ", 2),true)
  1360.     end,
  1361.     size= function(self, msg, args)
  1362.         if not args[1] then self:addline("Need values! '/size <width> <height>'") return end
  1363.         if args[2] then
  1364.             local w, h = tonumber(args[1]), tonumber(args[2])
  1365.             if w < 200 and not h < 150 then self:addline("Width too small") return
  1366.             elseif h < 150 and not w < 200 then self:addline('Height too small') return
  1367.             elseif h < 150 and w < 200 then self:addline('Width and height too small') return
  1368.             elseif w > gfx.WIDTH and not h > gfx.HEIGHT then self:addline("Width too big") return
  1369.             elseif h > gfx.HEIGHT and not w > gfx.WIDTH then self:addline('Height too big') return
  1370.             elseif h > gfx.HEIGHT and w > gfx.WIDTH then self:addline('Width and height too big') return
  1371.             end
  1372.             chatwindow = ui_chatbox.new(100,100,w,h)
  1373.             chatwidth = w
  1374.             chatheight = h
  1375.             chatwindow:setbackground(10,10,10,235) chatwindow.drawbackground=true
  1376.             if using_manager then
  1377.                 MANAGER.savesetting("tptmp", "width", w)
  1378.                 MANAGER.savesetting("tptmp", "height", h)
  1379.             end
  1380.         end
  1381.     end
  1382.     }
  1383.     function chat:keypress(key, scan, rep, shift, ctrl, alt)
  1384.         if L.chatHidden then return nil end
  1385.         local text = self.inputbox:keypress(key, scan, rep, shift, ctrl, alt)
  1386.         if text and text~="" then
  1387.             local cmd = text:match("^/([^%s]+)")
  1388.             if cmd then
  1389.                 local msg=text:sub(#cmd+3)
  1390.                 local args = getArgs(msg)
  1391.                 if chatcommands[cmd] then
  1392.                     chatcommands[cmd](self,msg,args)
  1393.                     --self:addline("Executed "..cmd.." "..rest)
  1394.                     return
  1395.                 end
  1396.             end
  1397.             --normal chat
  1398.             if con.connected then
  1399.                 conSend(19,text,true)
  1400.                 self:addline(username .. ": ".. text,200,200,200)
  1401.             else
  1402.                 self:addline("Not connected to server!",255,50,50)
  1403.             end
  1404.         end
  1405.         if self.inputbox.focus then
  1406.             return true
  1407.         end
  1408.     end
  1409.     function chat:textinput(text)
  1410.         if L.chatHidden then return end
  1411.         self.inputbox:textinput(text)
  1412.     end
  1413.     return chat
  1414. end
  1415. }
  1416. local fadeText = {}
  1417. --A little text that fades away, (align text (left/center/right)?)
  1418. local function newFadeText(text,frames,x,y,r,g,b,noremove)
  1419.     local t = {ticks=frames,max=frames,text=text,x=x,y=y,r=r,g=g,b=b,keep=noremove}
  1420.     t.reset = function(self,text) self.ticks=self.max if text then self.text=text end end
  1421.     table.insert(fadeText,t)
  1422.     return t
  1423. end
  1424. --Some text locations for repeated usage
  1425. local infoText = newFadeText("",150,245,370,255,255,255,true)
  1426. local cmodeText = newFadeText("",120,250,180,255,255,255,true)
  1427.  
  1428. local function getypos()
  1429.     local ypos = 136
  1430.     if jacobsmod and tpt.oldmenu and tpt.oldmenu()==1 then
  1431.         ypos = 392
  1432.     elseif tpt.num_menus then
  1433.         ypos = 392-16*tpt.num_menus()-(not jacobsmod and 16 or 0)
  1434.     end
  1435.     if using_manager then ypos = ypos - 17 end
  1436.     return ypos
  1437. end
  1438. local jacobsmod_old_menu_check = false
  1439. local showbutton = ui_button.new(613,getypos(),14,14,function() if using_manager and not MANAGER.hidden then _print("minimize the manager before opening TPTMP") return end if not hooks_enabled then TPTMP.enableMultiplayer() end L.chatHidden=false TPTMP.chatHidden=false L.flashChat=false end,"<<")
  1440. local flashCount=0
  1441. showbutton.drawbox = true showbutton:drawadd(function(self) if L.flashChat then self.almostselected=true flashCount=flashCount+1 if flashCount%25==0 then self.invert=not self.invert end end end)
  1442. if using_manager then
  1443.     local loadsettings = function()
  1444.         chatwindow = ui_chatbox.new(100, 100, tonumber(MANAGER.getsetting("tptmp", "width")), tonumber(MANAGER.getsetting("tptmp", "height")))
  1445.         chatwidth = w
  1446.         chatheight = h
  1447.     end
  1448.     if not pcall(loadsettings) then
  1449.         chatwindow = ui_chatbox.new(100, 100, 225, 150)
  1450.         chatwidth = w
  1451.         chatheight = h
  1452.     end
  1453. else
  1454.     chatwindow = ui_chatbox.new(100, 100, 225, 150)
  1455.     chatwidth = w
  1456.     chatheight = h
  1457. end
  1458. chatwindow:setbackground(10,10,10,235) chatwindow.drawbackground=true
  1459.  
  1460. local eleNameTable = {
  1461. ["DEFAULT_PT_LIFE_GOL"] = 256,["DEFAULT_PT_LIFE_HLIF"] = 257,["DEFAULT_PT_LIFE_ASIM"] = 258,["DEFAULT_PT_LIFE_2x2"] = 259,["DEFAULT_PT_LIFE_DANI"] = 260,
  1462. ["DEFAULT_PT_LIFE_AMOE"] = 261,["DEFAULT_PT_LIFE_MOVE"] = 262,["DEFAULT_PT_LIFE_PGOL"] = 263,["DEFAULT_PT_LIFE_DMOE"] = 264,["DEFAULT_PT_LIFE_34"] = 265,
  1463. ["DEFAULT_PT_LIFE_LLIF"] = 276,["DEFAULT_PT_LIFE_STAN"] = 267,["DEFAULT_PT_LIFE_SEED"] = 268,["DEFAULT_PT_LIFE_MAZE"] = 269,["DEFAULT_PT_LIFE_COAG"] = 270,
  1464. ["DEFAULT_PT_LIFE_WALL"] = 271,["DEFAULT_PT_LIFE_GNAR"] = 272,["DEFAULT_PT_LIFE_REPL"] = 273,["DEFAULT_PT_LIFE_MYST"] = 274,["DEFAULT_PT_LIFE_LOTE"] = 275,
  1465. ["DEFAULT_PT_LIFE_FRG2"] = 276,["DEFAULT_PT_LIFE_STAR"] = 277,["DEFAULT_PT_LIFE_FROG"] = 278,["DEFAULT_PT_LIFE_BRAN"] = 279,
  1466. --walls
  1467. ["DEFAULT_WL_ERASE"] = 280,["DEFAULT_WL_CNDTW"] = 281,["DEFAULT_WL_EWALL"] = 282,["DEFAULT_WL_DTECT"] = 283,["DEFAULT_WL_STRM"] = 284,
  1468. ["DEFAULT_WL_FAN"] = 285,["DEFAULT_WL_LIQD"] = 286,["DEFAULT_WL_ABSRB"] = 287,["DEFAULT_WL_WALL"] = 288,["DEFAULT_WL_AIR"] = 289,["DEFAULT_WL_POWDR"] = 290,
  1469. ["DEFAULT_WL_CNDTR"] = 291,["DEFAULT_WL_EHOLE"] = 292,["DEFAULT_WL_GAS"] = 293,["DEFAULT_WL_GRVTY"] = 294,["DEFAULT_WL_ENRGY"] = 295,
  1470. ["DEFAULT_WL_NOAIR"] = 296,["DEFAULT_WL_ERASEA"] = 297,["DEFAULT_WL_STASIS"] = 298,
  1471. --special tools
  1472. ["DEFAULT_UI_SAMPLE"] = 299,["DEFAULT_UI_SIGN"] = 300,["DEFAULT_UI_PROPERTY"] = 301,["DEFAULT_UI_WIND"] = 302,
  1473. --tools
  1474. ["DEFAULT_TOOL_HEAT"] = 303,["DEFAULT_TOOL_COOL"] = 304,["DEFAULT_TOOL_AIR"] = 305,["DEFAULT_TOOL_VAC"] = 306,["DEFAULT_TOOL_PGRV"] = 307,["DEFAULT_TOOL_NGRV"] = 308, ["DEFAULT_TOOL_MIX"] = 309,
  1475. --decoration tools
  1476. ["DEFAULT_DECOR_SET"] = 310,["DEFAULT_DECOR_CLR"] = 311,["DEFAULT_DECOR_ADD"] = 312,["DEFAULT_DECOR_SUB"] = 313,["DEFAULT_DECOR_MUL"] = 314,["DEFAULT_DECOR_DIV"] = 315,["DEFAULT_DECOR_SMDG"] = 316,
  1477. ["DEFAULT_DECOR_LIGH"] = 317, ["DEFAULT_DECOR_DARK"] = 318
  1478. }
  1479. local gravList= {[0]="Vertical",[1]="Off",[2]="Radial"}
  1480. local airList= {[0]="On",[1]="Pressure Off",[2]="Velocity Off",[3]="Off",[4]="No Update"}
  1481. local noFlood = {[15]=true,[55]=true,[87]=true,[128]=true,[158]=true}
  1482. local noShape = {[55]=true,[87]=true,[128]=true,[158]=true}
  1483. local createOverride = {
  1484.     [55] = function(rx,ry,c) return 0,0,c end,
  1485.     [87] = function(rx,ry,c) local tmp=rx+ry if tmp>55 then tmp=55 end return 0,0,c+bit.lshift(tmp,8) end,
  1486.     [88] = function(rx,ry,c) local tmp=rx*4+ry*4+7 if tmp>300 then tmp=300 end return rx,ry,c+bit.lshift(tmp,8) end,
  1487.     [128] = function(rx,ry,c) return 0,0,c end,
  1488.     [158] = function(rx,ry,c) return 0,0,c end}
  1489. local golStart,golEnd=256,279
  1490. local wallStart,wallEnd=280,298
  1491. local toolStart,toolEnd=303,309
  1492. local decoStart,decoEnd=310,318
  1493.  
  1494. --Functions that do stuff in powdertoy
  1495. local function createPartsAny(x,y,rx,ry,c,brush,user)
  1496.     if c>=wallStart then
  1497.         if c<= wallEnd then
  1498.             if c == 284 then rx,ry = 0,0 end
  1499.             sim.createWalls(x,y,rx,ry,c-wallStart,brush)
  1500.         elseif c<=toolEnd then
  1501.             if c>=toolStart then sim.toolBrush(x,y,rx,ry,c-toolStart,brush) end
  1502.         elseif c<= decoEnd then
  1503.             sim.decoBrush(x,y,rx,ry,user.dcolour[2],user.dcolour[3],user.dcolour[4],user.dcolour[1],c-decoStart,brush)
  1504.         end
  1505.         return
  1506.     elseif c>=golStart then
  1507.         c = 78+(c-golStart)*bit.lshift(1, sim.PMAPBITS)
  1508.     end
  1509.     if createOverride[c] then
  1510.         rx,ry,c = createOverride[c](rx,ry,c)
  1511.     end
  1512.     sim.createParts(x,y,rx,ry,c,brush,user.replacemode)
  1513. end
  1514. local function createLineAny(x1,y1,x2,y2,rx,ry,c,brush,user)
  1515.     if noShape[c] then return end
  1516.     if jacobsmod and c == tpt.element("ball") and not user.shift then return end
  1517.     if c>=wallStart then
  1518.         if c<= wallEnd then
  1519.             if c == 284 then rx,ry = 0,0 end
  1520.             sim.createWallLine(x1,y1,x2,y2,rx,ry,c-wallStart,brush)
  1521.         elseif c<=toolEnd then
  1522.             if c>=toolStart then local str=1.0 if user.drawtype==4 then if user.shift then str=10.0 elseif user.alt then str=0.1 end end sim.toolLine(x1,y1,x2,y2,rx,ry,c-toolStart,brush,str) end
  1523.         elseif c<= decoEnd then
  1524.             sim.decoLine(x1,y1,x2,y2,rx,ry,user.dcolour[2],user.dcolour[3],user.dcolour[4],user.dcolour[1],c-decoStart,brush)
  1525.         end
  1526.         return
  1527.     elseif c>=golStart then
  1528.         c = 78+(c-golStart)*bit.lshift(1, sim.PMAPBITS)
  1529.     end
  1530.     if createOverride[c] then
  1531.         rx,ry,c = createOverride[c](rx,ry,c)
  1532.     end
  1533.     sim.createLine(x1,y1,x2,y2,rx,ry,c,brush,user.replacemode)
  1534. end
  1535. local function createBoxAny(x1,y1,x2,y2,c,user)
  1536.     if noShape[c] then return end
  1537.     if c>=wallStart then
  1538.         if c<= wallEnd then
  1539.             sim.createWallBox(x1,y1,x2,y2,c-wallStart)
  1540.         elseif c<=toolEnd then
  1541.             if c>=toolStart then sim.toolBox(x1,y1,x2,y2,c-toolStart) end
  1542.         elseif c<= decoEnd then
  1543.             sim.decoBox(x1,y1,x2,y2,user.dcolour[2],user.dcolour[3],user.dcolour[4],user.dcolour[1],c-decoStart)
  1544.         end
  1545.         return
  1546.     elseif c>=golStart then
  1547.         c = 78+(c-golStart)*bit.lshift(1, sim.PMAPBITS)
  1548.     end
  1549.     if createOverride[c] then
  1550.         _,_,c = createOverride[c](user.brushx,user.brushy,c)
  1551.     end
  1552.     sim.createBox(x1,y1,x2,y2,c,user and user.replacemode)
  1553. end
  1554. local function floodAny(x,y,c,cm,bm,user)
  1555.     if noFlood[c] then return end
  1556.     if c>=wallStart then
  1557.         if c<= wallEnd then
  1558.             sim.floodWalls(x,y,c-wallStart,bm)
  1559.         end
  1560.         --other tools shouldn't flood
  1561.         return
  1562.     elseif c>=golStart then --GoL adjust
  1563.         c = 78+(c-golStart)*bit.lshift(1, sim.PMAPBITS)
  1564.     end
  1565.     if createOverride[c] then
  1566.         _,_,c = createOverride[c](user.brushx,user.brushy,c)
  1567.     end
  1568.     sim.floodParts(x,y,c,cm,user.replacemode)
  1569. end
  1570. local function lineSnapCoords(x1,y1,x2,y2)
  1571.     local nx,ny
  1572.     local snapAngle = math.floor(math.atan2(y2-y1, x2-x1)/(math.pi*0.25)+0.5)*math.pi*0.25;
  1573.     local lineMag = math.sqrt(math.pow(x2-x1,2)+math.pow(y2-y1,2));
  1574.     nx = math.floor(lineMag*math.cos(snapAngle)+x1+0.5);
  1575.     ny = math.floor(lineMag*math.sin(snapAngle)+y1+0.5);
  1576.     return nx,ny
  1577. end
  1578.  
  1579. local function rectSnapCoords(x1,y1,x2,y2)
  1580.     local nx,ny
  1581.     local snapAngle = math.floor((math.atan2(y2-y1, x2-x1)+math.pi*0.25)/(math.pi*0.5)+0.5)*math.pi*0.5 - math.pi*0.25;
  1582.     local lineMag = math.sqrt(math.pow(x2-x1,2)+math.pow(y2-y1,2));
  1583.     nx = math.floor(lineMag*math.cos(snapAngle)+x1+0.5);
  1584.     ny = math.floor(lineMag*math.sin(snapAngle)+y1+0.5);
  1585.     return nx,ny
  1586. end
  1587. local function wallSnapCoords(x, y)
  1588.     return math.floor(x / 4) * 4, math.floor(y / 4) * 4
  1589. end
  1590. local renModes = {[0xff00f270]=1,[-16715152]=1,[0x0400f381]=2,[0xf382]=4,[0xf388]=8,[0xf384]=16,[0xfff380]=32,[1]=0xff00f270,[2]=0x0400f381,[4]=0xf382,[8]=0xf388,[16]=0xf384,[32]=0xfff380}
  1591. local function getViewModes()
  1592.     local t={0,0,0}
  1593.     for k,v in pairs(ren.displayModes()) do
  1594.         t[1] = t[1]+v
  1595.     end
  1596.     for k,v in pairs(ren.renderModes()) do
  1597.         t[2] = t[2]+(renModes[v] or 0)
  1598.     end
  1599.     t[3] = ren.colorMode()
  1600.     return t
  1601. end
  1602.  
  1603. --clicky click
  1604. local function playerMouseClick(id,btn,ev)
  1605.     local user = con.members[id]
  1606.     local createE, checkBut
  1607.  
  1608.     --_print(tostring(btn)..tostring(ev))
  1609.     if ev==0 then return end
  1610.     -- Mouse up event, TPT will "draw" whatever element was last clicked, even if we are releasing a different button
  1611.     -- This covers the case where we start drawing a line with rmb, switch to lmb, but then release rmb. The lmb element is drawn
  1612.     if ev == 2 then
  1613.         if user.lbtn then
  1614.             createE,checkBut=user.selectedl,user.lbtn
  1615.         elseif user.abtn then
  1616.             createE,checkBut=user.selecteda,user.abtn
  1617.         elseif user.rbtn then
  1618.             createE,checkBut=user.selectedr,user.rbtn
  1619.         else
  1620.             return
  1621.         end
  1622.     else
  1623.         if btn==1 then
  1624.             user.rbtn,user.abtn = false,false
  1625.             createE,checkBut=user.selectedl,user.lbtn
  1626.         elseif btn==2 then
  1627.             user.rbtn,user.lbtn = false,false
  1628.             createE,checkBut=user.selecteda,user.abtn
  1629.         elseif btn==3 then
  1630.             user.lbtn,user.abtn = false,false
  1631.             createE,checkBut=user.selectedr,user.rbtn
  1632.         else
  1633.             return
  1634.         end
  1635.     end
  1636.  
  1637.     --if user.mousex>=sim.XRES or user.mousey>=sim.YRES then user.drawtype=false return end
  1638.  
  1639.     if ev==1 then
  1640.         if user.mousex >= 0 and user.mousey >= 0 and user.mousex < sim.XRES and user.mousey < sim.YRES then
  1641.             user.pmx,user.pmy = user.mousex,user.mousey
  1642.             --left box
  1643.             if user.ctrl and not user.shift then user.drawtype = 2 return end
  1644.             --left line
  1645.             if user.shift and not user.ctrl then user.drawtype = 1 return end
  1646.             --floodfill
  1647.             if user.ctrl and user.shift then floodAny(user.mousex,user.mousey,createE,-1,-1,user) user.drawtype = 3 return end
  1648.             --an alt click
  1649.             if user.alt then return end
  1650.             user.drawtype=4 --normal hold
  1651.             createPartsAny(user.mousex,user.mousey,user.brushx,user.brushy,createE,user.brush,user)
  1652.         end
  1653.     elseif ev==2 and checkBut and user.drawtype then
  1654.         local releaseX, releaseY = user.mousex, user.mousey
  1655.        
  1656.         if user.drawtype==2 then
  1657.             if user.alt then user.mousex,user.mousey = rectSnapCoords(user.pmx,user.pmy,releaseX,releaseY) end
  1658.             createBoxAny(releaseX,releaseY,user.pmx,user.pmy,createE,user)
  1659.         elseif user.drawtype ~= 3 then
  1660.             if user.alt then user.mousex,user.mousey = lineSnapCoords(user.pmx,user.pmy,user.mousex,user.mousey) end
  1661.             createLineAny(releaseX,releaseY,user.pmx,user.pmy,user.brushx,user.brushy,createE,user.brush,user)
  1662.         end
  1663.         user.drawtype=false
  1664.         user.pmx,user.pmy = user.mousex,user.mousey
  1665.     end
  1666. end
  1667. --To draw continued lines
  1668. local function playerMouseMove(id)
  1669.     local user = con.members[id]
  1670.     local createE, checkBut
  1671.     if user.lbtn then
  1672.         createE,checkBut=user.selectedl,user.lbtn
  1673.     elseif user.rbtn then
  1674.         createE,checkBut=user.selectedr,user.rbtn
  1675.     elseif user.abtn then
  1676.         createE,checkBut=user.selecteda,user.abtn
  1677.     else return end
  1678.     if user.drawtype~=4 then if user.drawtype==3 then floodAny(user.mousex,user.mousey,createE,-1,-1,user) end return end
  1679.    
  1680.     if user.mousex>=sim.XRES then user.mousex=sim.XRES-1 end
  1681.     if user.mousey>=sim.YRES then user.mousey=sim.YRES-1 end
  1682.     createLineAny(user.mousex,user.mousey,user.pmx,user.pmy,user.brushx,user.brushy,createE,user.brush,user)
  1683.     user.pmx,user.pmy = user.mousex,user.mousey
  1684. end
  1685. local function loadStamp(size,x,y,reset)
  1686.     con.socket:settimeout(10)
  1687.     local s = con.socket:receive(size)
  1688.     con.socket:settimeout(0)
  1689.     if s then
  1690.         local f = io.open(".tmp.stm","wb")
  1691.         f:write(s)
  1692.         f:close()
  1693.         if reset then sim.clearSim() end
  1694.         if not sim.loadStamp(".tmp.stm",x,y) then
  1695.             infoText:reset("Error loading stamp")
  1696.         end
  1697.         os.remove".tmp.stm"
  1698.     else
  1699.         infoText:reset("Error loading empty stamp")
  1700.     end
  1701. end
  1702. local function saveStamp(x, y, w, h)
  1703.     local stampName = sim.saveStamp(x, y, w, h) or "errorsavingstamp"
  1704.     local fullName = "stamps/"..stampName..".stm"
  1705.     return stampName, fullName
  1706. end
  1707. local function deleteStamp(name)
  1708.     if sim.deleteStamp then
  1709.         sim.deleteStamp(name)
  1710.     else
  1711.         os.remove("stamps/"..name..".stm")
  1712.     end
  1713. end
  1714.  
  1715. local dataCmds = {
  1716.     [16] = function()
  1717.     --room members
  1718.         con.members = {}
  1719.         local amount = cByte()
  1720.         local peeps = {}
  1721.         for i=1,amount do
  1722.             local id = cByte()
  1723.             con.members[id]={name=conGetNull(),mousex=0,mousey=0,brushx=4,brushy=4,brush=0,selectedl=1,selectedr=0,selecteda=296,replacemode=0,dcolour={0,0,0,0},lbtn=false,abtn=false,rbtn=false,ctrl=false,shift=false,alt=false}
  1724.             local name = con.members[id].name
  1725.             table.insert(peeps,name)
  1726.         end
  1727.         chatwindow:addline("Online: "..table.concat(peeps," "),255,255,50)
  1728.     end,
  1729.     [17]= function()
  1730.         local id = cByte()
  1731.         con.members[id] ={name=conGetNull(),mousex=0,mousey=0,brushx=4,brushy=4,brush=0,selectedl=1,selectedr=0,selecteda=296,replacemode=0,dcolour={0,0,0,0},lbtn=false,abtn=false,rbtn=false,ctrl=false,shift=false,alt=false}
  1732.         chatwindow:addline(con.members[id].name.." has joined",100,255,100)
  1733.     end,
  1734.     [18] = function()
  1735.         local id = cByte()
  1736.         chatwindow:addline(con.members[id].name.." has left",255,255,100)
  1737.         con.members[id]=nil
  1738.     end,
  1739.     [19] = function()
  1740.         chatwindow:addline(con.members[cByte()].name .. ": " .. conGetNull())
  1741.     end,
  1742.     [20] = function()
  1743.         chatwindow:addline("* "..con.members[cByte()].name .. " " .. conGetNull())
  1744.     end,
  1745.     [22] = function()
  1746.         chatwindow:addline("[SERVER] "..conGetNull(), cByte(), cByte(), cByte())
  1747.     end,
  1748.     --Mouse Position
  1749.     [32] = function()
  1750.         local id = cByte()
  1751.         local b1,b2,b3=cByte(),cByte(),cByte()
  1752.         con.members[id].mousex,con.members[id].mousey=((b1*16)+math.floor(b2/16)),((b2%16)*256)+b3
  1753.         playerMouseMove(id)
  1754.     end,
  1755.     --Mouse Click
  1756.     [33] = function()
  1757.         local id = cByte()
  1758.         local d=cByte()
  1759.         local btn,ev=math.floor(d/16),d%16
  1760.         -- Fake mouseup due to either blur or zoom window
  1761.         if btn == 0 then
  1762.             local user = con.members[id]
  1763.             user.lbtn, user.rbtn, user.abtn, user.drawtype = nil, nil, nil, nil
  1764.             return
  1765.         end
  1766.         playerMouseClick(id,btn,ev)
  1767.         if ev==0 then return end
  1768.         if ev==2 then ev = nil end
  1769.         if btn==1 then
  1770.             con.members[id].lbtn=ev
  1771.         elseif btn==2 then
  1772.             con.members[id].abtn=ev
  1773.         elseif btn==3 then
  1774.             con.members[id].rbtn=ev
  1775.         end
  1776.     end,
  1777.     --Brush size
  1778.     [34] = function()
  1779.         local id = cByte()
  1780.         con.members[id].brushx,con.members[id].brushy=cByte(),cByte()
  1781.     end,
  1782.     --Brush Shape change, no args
  1783.     [35] = function()
  1784.         local id = cByte()
  1785.         con.members[id].brush=(con.members[id].brush+1)%3
  1786.     end,
  1787.     --Modifier (mod and state)
  1788.     [36] = function()
  1789.         local id = cByte()
  1790.         local d=cByte()
  1791.         local mod,state=math.floor(d/16),d%16~=0
  1792.         if mod==0 then
  1793.             con.members[id].ctrl=state
  1794.         elseif mod==1 then
  1795.             con.members[id].shift=state
  1796.         elseif mod==2 then
  1797.             con.members[id].alt=state
  1798.         end
  1799.     end,
  1800.     --selected elements (2 bits button, 14-element)
  1801.     [37] = function()
  1802.         local id = cByte()
  1803.         local b1,b2=cByte(),cByte()
  1804.         local btn,el=math.floor(b1/64),(b1%64)*256+b2
  1805.         if btn==0 then
  1806.             con.members[id].selectedl=el
  1807.         elseif btn==1 then
  1808.             con.members[id].selecteda=el
  1809.         elseif btn==2 then
  1810.             con.members[id].selectedr=el
  1811.         elseif btn==3 then
  1812.             --sync replace mode element between all players since apparently you have to set tpt.selectedreplace to use replace mode ...
  1813.             tpt.selectedreplace = elem.property(el, "Identifier")
  1814.         end
  1815.     end,
  1816.     --replace mode / specific delete
  1817.     [38] = function()
  1818.         local id = cByte()
  1819.         local mod = cByte()
  1820.         con.members[id].replacemode = mod
  1821.     end,
  1822.     --cmode defaults (1 byte mode)
  1823.     [48] = function()
  1824.         local id = cByte()
  1825.         if (visualsync) then
  1826.             tpt.display_mode(cByte())
  1827.         end
  1828.         cmodeText:reset(con.members[id].name.." set:")
  1829.     end,
  1830.     --pause set (1 byte state)
  1831.     [49] = function()
  1832.         local id = cByte()
  1833.         local p,str = cByte(),"Pause"
  1834.         tpt.set_pause(p)
  1835.         if p==0 then str="Unpause" end
  1836.         infoText:reset(str.." from "..con.members[id].name)
  1837.     end,
  1838.     --step frame, no args
  1839.     [50] = function()
  1840.         local id = cByte()
  1841.         tpt.set_pause(0)
  1842.         L.pauseNextFrame=true
  1843.     end,
  1844.     --deco mode, (1 byte state)
  1845.     [51] = function()
  1846.         local id = cByte()
  1847.         if (visualsync) then  --EC---------------------------------------------
  1848.             tpt.decorations_enable(cByte())
  1849.         end
  1850.         cmodeText:reset(con.members[id].name.." set:")
  1851.     end,
  1852.     --amb heat mode, (1 byte state)
  1853.     [53] = function()
  1854.         local id = cByte()
  1855.         tpt.ambient_heat(cByte())
  1856.     end,
  1857.     --newt_grav mode, (1 byte state)
  1858.     [54] = function()
  1859.         local id = cByte()
  1860.         tpt.newtonian_gravity(cByte())
  1861.     end,
  1862.  
  1863.     --legacy heat mode, (1 byte state)
  1864.     [56] = function()
  1865.         local id = cByte()
  1866.         tpt.heat(cByte())
  1867.     end,
  1868.     --water equal, (1 byte state)
  1869.     [57] = function()
  1870.         local id = cByte()
  1871.         sim.waterEqualisation(cByte())
  1872.     end,
  1873.  
  1874.     --grav mode, (1 byte state)
  1875.     [58] = function()
  1876.         local id = cByte()
  1877.         local mode = cByte()
  1878.         sim.gravityMode(mode)
  1879.         cmodeText:reset(con.members[id].name.." set: Gravity: "..gravList[mode])
  1880.     end,
  1881.     --air mode, (1 byte state)
  1882.     [59] = function()
  1883.         local id = cByte()
  1884.         local mode=cByte()
  1885.         sim.airMode(mode)
  1886.         cmodeText:reset(con.members[id].name.." set: Air: "..airList[mode])
  1887.     end,
  1888.  
  1889.     --clear sparks (no args)
  1890.     [60] = function()
  1891.         local id = cByte()
  1892.         tpt.reset_spark()
  1893.     end,
  1894.     --clear pressure/vel (no args)
  1895.     [61] = function()
  1896.         local id = cByte()
  1897.         tpt.reset_velocity()
  1898.         tpt.set_pressure()
  1899.     end,
  1900.     --invert pressure (no args)
  1901.     [62] = function()
  1902.         local id = cByte()
  1903.         for x=0,152 do
  1904.             for y=0,95 do
  1905.                 sim.pressure(x,y,-sim.pressure(x,y))
  1906.             end
  1907.         end
  1908.     end,
  1909.     --Clearsim button (no args)
  1910.     [63] = function()
  1911.         local id = cByte()
  1912.         sim.clearSim()
  1913.         L.lastSave=nil
  1914.         infoText:reset(con.members[id].name.." cleared the screen")
  1915.     end,
  1916.     --Full graphics view mode (for manual changes in display menu) (3 bytes)
  1917.     [64] = function()
  1918.         local id = cByte()
  1919.         local disM,renM,colM = cByte(),cByte(),cByte()
  1920.         if(visualsync) then
  1921.             ren.displayModes({disM})
  1922.             local t,i={},1
  1923.             while i<=32 do
  1924.                 if bit.band(renM,i)>0 then table.insert(t,renModes[i]) end
  1925.                 i=i*2
  1926.             end
  1927.             ren.renderModes(t)
  1928.             ren.colorMode(colM)
  1929.         end
  1930.     end,
  1931.     --Selected deco colour (4 bytes)
  1932.     [65] = function()
  1933.         local id = cByte()
  1934.         con.members[id].dcolour = {cByte(),cByte(),cByte(),cByte()}
  1935.     end,
  1936.     --Recieve a stamp, with location (6 bytes location(3),size(3))
  1937.     [66] = function()
  1938.         local id = cByte()
  1939.         local b1,b2,b3=cByte(),cByte(),cByte()
  1940.         local x,y =((b1*16)+math.floor(b2/16)),((b2%16)*256)+b3
  1941.         local d = cByte()*65536+cByte()*256+cByte()
  1942.         loadStamp(d,x,y,false)
  1943.         infoText:reset("Stamp from "..con.members[id].name)
  1944.     end,
  1945.     --Clear an area, helper for cut (6 bytes, start(3), end(3))
  1946.     [67] = function()
  1947.         local id = cByte()
  1948.         local b1,b2,b3,b4,b5,b6=cByte(),cByte(),cByte(),cByte(),cByte(),cByte()
  1949.         local x1,y1 =((b1*16)+math.floor(b2/16)),((b2%16)*256)+b3
  1950.         local x2,y2 =((b4*16)+math.floor(b5/16)),((b5%16)*256)+b6
  1951.         sim.clearRect(x1,y1,x2-x1+1,y2-y1+1)
  1952.     end,
  1953.     --Edge mode (1 byte state)
  1954.     [68] = function()
  1955.         local id = cByte()
  1956.         sim.edgeMode(cByte())
  1957.     end,
  1958.     --Load a save ID (3 bytes ID)
  1959.     [69] = function()
  1960.         local id = cByte()
  1961.         local saveID = cByte()*65536+cByte()*256+cByte()
  1962.         L.lastSave=saveID
  1963.         sim.loadSave(saveID,1)
  1964.     end,
  1965.     --Reload sim(from a stamp right now, no args)
  1966.     [70] = function()
  1967.         local id = cByte()
  1968.         sim.reloadSave()
  1969.         infoText:reset(con.members[id].name.." reloaded the save")
  1970.     end,
  1971.     --A request to sync a player, from server, send screen, and various settings
  1972.     [128] = function()
  1973.         local id = cByte()
  1974.         conSend(130,string.char(id,49,tpt.set_pause()))
  1975.         local stampName,fullName = saveStamp(0,0,sim.XRES-1,sim.YRES-1)
  1976.         local f = assert(io.open(fullName,"rb"))
  1977.         local s = f:read"*a"
  1978.         f:close()
  1979.         deleteStamp(stampName)
  1980.         local d = #s
  1981.         conSend(128,string.char(id,math.floor(d/65536),math.floor(d/256)%256,d%256)..s)
  1982.         conSend(130,string.char(id,53,tpt.ambient_heat()))
  1983.         conSend(130,string.char(id,54,tpt.newtonian_gravity()))
  1984.         conSend(130,string.char(id,56,tpt.heat()))
  1985.         conSend(130,string.char(id,57,sim.waterEqualisation()))
  1986.         conSend(130,string.char(id,58,sim.gravityMode()))
  1987.         conSend(130,string.char(id,59,sim.airMode()))
  1988.         conSend(130,string.char(id,68,sim.edgeMode()))
  1989.         conSend(64,string.char(unpack(getViewModes())))
  1990.         conSend(34,string.char(tpt.brushx,tpt.brushy))
  1991.     end,
  1992.     --Recieve sync stamp
  1993.     [129] = function()
  1994.         local d = cByte()*65536+cByte()*256+cByte()
  1995.         loadStamp(d,0,0,true)
  1996.     end,
  1997. }
  1998.  
  1999. local function connectThink()
  2000.     if not con.connected then return end
  2001.     if not con.socket then disconnected() return end
  2002.     --read all messages
  2003.     while 1 do
  2004.         local s,r = con.socket:receive(1)
  2005.         if s then
  2006.             local cmd = string.byte(s)
  2007.             --_print("GOT "..tostring(cmd))
  2008.             if dataCmds[cmd] then dataCmds[cmd]() else _print("TPTMP: Unknown protocol "..tostring(cmd),255,20,20) end
  2009.         else
  2010.             if r ~= "timeout" then disconnected() end
  2011.             break
  2012.         end
  2013.     end
  2014.  
  2015.     --ping every minute
  2016.     if os.time()>con.pingTime then conSend(2) con.pingTime=os.time()+60 end
  2017. end
  2018. --Track if we have STKM2 out, for WASD key changes
  2019. elements.property(128,"Update",function() L.stick2=true end)
  2020.  
  2021. local function drawStuff()
  2022.     if con.members then
  2023.         for i,user in pairs(con.members) do
  2024.             local x,y = user.mousex,user.mousey
  2025.             local brx,bry=user.brushx,user.brushy
  2026.             local brush,drawBrush=user.brush,true
  2027.             gfx.drawText(x,y,("%s %dx%d"):format(user.name,brx,bry),0,255,0,192)
  2028.  
  2029.             -- Draw player cursors
  2030.             if user.drawtype==1 then
  2031.                 if user.alt then x,y = lineSnapCoords(user.pmx,user.pmy,x,y) end
  2032.                 gfx.drawLine(user.pmx,user.pmy,x,y,0,255,0,128)
  2033.             elseif user.drawtype==2 then
  2034.                 if user.alt then x,y = rectSnapCoords(user.pmx,user.pmy,x,y) end
  2035.                 local tpmx,tpmy = user.pmx,user.pmy
  2036.                 if tpmx>x then tpmx,x=x,tpmx end
  2037.                 if tpmy>y then tpmy,y=y,tpmy end
  2038.                 gfx.drawRect(tpmx,tpmy,x-tpmx+1,y-tpmy+1,0,255,0,128)
  2039.                 drawBrush=false
  2040.             elseif user.drawtype==3 or (user.shift and user.ctrl) then
  2041.                 gfx.drawLine(x,y,x+5,y,0,255,0,128)
  2042.                 gfx.drawLine(x,y,x-5,y,0,255,0,128)
  2043.                 gfx.drawLine(x,y,x,y+5,0,255,0,128)
  2044.                 gfx.drawLine(x,y,x,y-5,0,255,0,128)
  2045.                 drawBrush=false
  2046.             end
  2047.  
  2048.             if drawBrush then
  2049.                 if user.selectedl >= wallStart and user.selectedl <= wallEnd then
  2050.                     local blockX, blockY = wallSnapCoords(x, y)
  2051.                     local blockRadX, blockRadY = wallSnapCoords(brx, bry)
  2052.                    
  2053.                     local x1, y1 = blockX - blockRadX, blockY - blockRadY
  2054.                     local x2, y2 = blockX + blockRadX + 3, blockY + blockRadY + 3
  2055.                     gfx.drawRect(x1, y1, x2 - x1, y2 - y1)
  2056.                 elseif brush==0 then
  2057.                     gfx.drawCircle(x,y,brx,bry,0,255,0,128)
  2058.                 elseif brush==1 then
  2059.                     gfx.drawRect(x-brx,y-bry,brx*2+1,bry*2+1,0,255,0,128)
  2060.                 elseif brush==2 then
  2061.                     gfx.drawLine(x-brx,y+bry,x,y-bry,0,255,0,128)
  2062.                     gfx.drawLine(x-brx,y+bry,x+brx,y+bry,0,255,0,128)
  2063.                     gfx.drawLine(x,y-bry,x+brx,y+bry,0,255,0,128)
  2064.                 end
  2065.             end
  2066.         end
  2067.     end
  2068.     for k,v in pairs(fadeText) do
  2069.         if v.ticks > 0 then
  2070.             local a = math.floor(255*(v.ticks/v.max))
  2071.             gfx.drawText(v.x,v.y,v.text,v.r,v.g,v.b,a)
  2072.             v.ticks = v.ticks-1
  2073.         else if not v.keep then table.remove(fadeText,k) end
  2074.         end
  2075.     end
  2076. end
  2077.  
  2078. local function sendStuff()
  2079.     if not con.connected then return end
  2080.     if tpt.brushx > 255 then tpt.brushx = 255 end
  2081.     if tpt.brushy > 255 then tpt.brushy = 255 end
  2082.     local nbx,nby = tpt.brushx,tpt.brushy
  2083.     if L.brushx~=nbx or L.brushy~=nby then
  2084.         L.brushx,L.brushy = nbx,nby
  2085.         conSend(34,string.char(L.brushx,L.brushy))
  2086.     end
  2087.     --check selected elements
  2088.     local nsell,nsela,nselr,nselrep = elements[tpt.selectedl] or eleNameTable[tpt.selectedl],elements[tpt.selecteda] or eleNameTable[tpt.selecteda],elements[tpt.selectedr] or eleNameTable[tpt.selectedr],elements[tpt.selectedreplace] or eleNameTable[tpt.selectedreplace]
  2089.     if L.sell~=nsell then
  2090.         L.sell=nsell
  2091.         if nsell == nil then
  2092.             _print("Unsupported wall/tool "..tpt.selectedl)
  2093.         else
  2094.             conSend(37,string.char(math.floor(L.sell/256),L.sell%256))
  2095.         end
  2096.     elseif L.sela~=nsela then
  2097.         L.sela=nsela
  2098.         if nsela == nil then
  2099.             _print("Unsupported wall/tool "..tpt.selecteda)
  2100.         else
  2101.             conSend(37,string.char(math.floor(64 + L.sela/256),L.sela%256))
  2102.         end
  2103.     elseif L.selr~=nselr then
  2104.         L.selr=nselr
  2105.         if nselr == nil then
  2106.             _print("Unsupported wall/tool "..tpt.selectedr)
  2107.         else
  2108.             conSend(37,string.char(math.floor(128 + L.selr/256),L.selr%256))
  2109.         end
  2110.     elseif L.selrep~=nselrep then
  2111.         L.selrep=nselrep
  2112.         if nselrep == nil then
  2113.             _print("Unsupported wall/tool "..tpt.selectedreplace)
  2114.         else
  2115.             conSend(37,string.char(math.floor(192 + L.selrep/256),L.selrep%256))
  2116.         end
  2117.     end
  2118.     local ncol = sim.decoColour()
  2119.     if L.dcolour~=ncol then
  2120.         L.dcolour=ncol
  2121.         conSend(65,string.char(math.floor(ncol/16777216),math.floor(ncol/65536)%256,math.floor(ncol/256)%256,ncol%256))
  2122.     end
  2123.  
  2124.     --Tell others to open this save ID, or send screen if opened local browser
  2125.     if L.browseMode==1 then
  2126.         --loaded online save
  2127.         local id=sim.getSaveID()
  2128.         if L.lastSave~=id then
  2129.             L.lastSave=id
  2130.             conSend(69,string.char(math.floor(id/65536),math.floor(id/256)%256,id%256))
  2131.         end
  2132.         L.browseMode=nil
  2133.     elseif L.browseMode==2 then
  2134.         --loaded local save (should probably clear sim first instead?)
  2135.         L.sendScreen=true
  2136.         L.browseMode=nil
  2137.     end
  2138.  
  2139.     --Send screen (or an area for known size) for stamps
  2140.     if jacobsmod and L.sendScreen == 2 then
  2141.         L.sendScreen = true
  2142.     elseif L.sendScreen then
  2143.         local x,y,w,h = 0,0,sim.XRES-1,sim.YRES-1
  2144.         if L.smoved then
  2145.             local stm
  2146.             if L.copying then stm=L.lastCopy else stm=L.lastStamp end
  2147.             if L.rotate then stm.w,stm.h=stm.h,stm.w end
  2148.             x,y,w,h = math.floor((L.mousex-stm.w/2)/4)*4,math.floor((L.mousey-stm.h/2)/4)*4,stm.w,stm.h
  2149.             L.smoved=false
  2150.             L.copying=false
  2151.         end
  2152.         L.sendScreen=false
  2153.         local stampName,fullName = saveStamp(x,y,w,h)
  2154.         local f = assert(io.open(fullName,"rb"))
  2155.         local s = f:read"*a"
  2156.         f:close()
  2157.         deleteStamp(stampName)
  2158.         local d = #s
  2159.         local b1,b2,b3 = math.floor(x/16),((x%16)*16)+math.floor(y/256),(y%256)
  2160.         conSend(67,string.char(math.floor(x/16),((x%16)*16)+math.floor(y/256),(y%256),math.floor((x+w)/16),(((x+w)%16)*16)+math.floor((y+h)/256),((y+h)%256)))
  2161.         conSend(66,string.char(b1,b2,b3,math.floor(d/65536),math.floor(d/256)%256,d%256)..s)
  2162.         conSend(49,string.char(tpt.set_pause()))
  2163.     end
  2164.  
  2165.     --Check if custom modes were changed
  2166.     if jacobsmod and L.checkRen == 2 then
  2167.         L.checkRen = true
  2168.     elseif L.checkRen then
  2169.         L.checkRen=false
  2170.         local t,send=getViewModes(),false
  2171.         for k,v in pairs(t) do
  2172.             if v~=L.pModes[k] then
  2173.                 send=true break
  2174.             end
  2175.         end
  2176.         if send then conSend(64,string.char(t[1],t[2],t[3])) end
  2177.     end
  2178.  
  2179.     --Send option menu settings
  2180.     if L.checkOpt then
  2181.         L.checkOpt=false
  2182.         conSend(49,string.char(tpt.set_pause()))
  2183.         conSend(56,string.char(tpt.heat()))
  2184.         conSend(53,string.char(tpt.ambient_heat()))
  2185.         conSend(54,string.char(tpt.newtonian_gravity()))
  2186.         conSend(57,string.char(sim.waterEqualisation()))
  2187.         conSend(58,string.char(sim.gravityMode()))
  2188.         conSend(59,string.char(sim.airMode()))
  2189.         conSend(68,string.char(sim.edgeMode()))
  2190.     end
  2191.  
  2192. end
  2193. local function updatePlayers()
  2194.     if con.members then
  2195.         for k,v in pairs(con.members) do
  2196.             playerMouseMove(k)
  2197.         end
  2198.     end
  2199.     --Keep last frame of stick2
  2200.     L.lastStick2=L.stick2
  2201.     L.stick2=false
  2202. end
  2203.  
  2204. local pressedKeys
  2205. local function step()
  2206.     if jacobsmod_old_menu_check then showbutton:onmove(0, getypos()-showbutton.y) end
  2207.     if not L.chatHidden then chatwindow:draw() else showbutton:draw() end
  2208.     if hooks_enabled then
  2209.         drawStuff()
  2210.         sendStuff()
  2211.         if L.pauseNextFrame then L.pauseNextFrame=false tpt.set_pause(1) end
  2212.         connectThink()
  2213.         updatePlayers()
  2214.     EC_loop()
  2215.     end
  2216. end
  2217.  
  2218. --some button locations that emulate tpt, return false will disable button
  2219. local tpt_buttons = {
  2220.     ["open"] = {x1=1, y1=408, x2=17, y2=422, f=function() if not L.ctrl then L.browseMode=1 else L.browseMode=2 end L.lastSave=sim.getSaveID() end},
  2221.     ["rload"] = {x1=19, y1=408, x2=35, y2=422, f=function()
  2222.         if L.lastSave then
  2223.             if L.ctrl then
  2224.                 infoText:reset("If you re-opened the save, please type /sync")
  2225.             else
  2226.                 conSend(70)
  2227.                 infoText:reset("Sent sync")
  2228.             end
  2229.         else
  2230.             --[[infoText:reset("Reloading local saves is not synced currently. Type /sync")]]
  2231.             L.checkOpt = true
  2232.             L.sendScreen = true
  2233.         end
  2234.     end},
  2235.     ["clear"] = {x1=470, y1=408, x2=486, y2=422, f=function() conSend(63) L.lastSave=nil end},
  2236.     ["opts"] = {x1=581, y1=408, x2=595, y2=422, f=function() L.checkOpt=true end},
  2237.     ["disp"] = {x1=597, y1=408, x2=611, y2=422, f=function() L.checkRen=true L.pModes=getViewModes() end},
  2238.     ["pause"] = {x1=gfx.WIDTH-16, y1=408, x2=gfx.WIDTH-2, y2=422, f=function() conSend(49,tpt.set_pause()==0 and "\1" or "\0") end},
  2239.     ["deco"] = {x1=gfx.WIDTH-16, y1=33, x2=gfx.WIDTH-2, y2=47, f=function() if jacobsmod and (L.tabs or L.ctrl) then return end conSend(51,tpt.decorations_enable()==0 and "\1" or "\0") end},
  2240.     ["newt"] = {x1=gfx.WIDTH-16, y1=49, x2=gfx.WIDTH-2, y2=63, f=function() if jacobsmod and (L.tabs or L.ctrl) then return end conSend(54,tpt.newtonian_gravity()==0 and "\1" or "\0") end},
  2241.     ["ambh"] = {x1=gfx.WIDTH-16, y1=65, x2=gfx.WIDTH-2, y2=79, f=function() if jacobsmod and (L.tabs or L.ctrl) then return end conSend(53,tpt.ambient_heat()==0 and "\1" or "\0") end},
  2242. }
  2243. if jacobsmod then
  2244.     tpt_buttons["tab"] = {x1=gfx.WIDTH-16, y1=1, x2=gfx.WIDTH-2, y2=15, f=function() L.tabs = not L.tabs end}
  2245.     tpt_buttons["tabs"] = {x1=gfx.WIDTH-16, y1=17, x2=gfx.WIDTH-2, y2=147, f=function() if L.tabs or L.ctrl then L.sendScreen = true end end}
  2246.     tpt_buttons["opts"] = {x1=465, y1=408, x2=479, y2=422, f=function() L.checkOpt=true end}
  2247.     tpt_buttons["clear"] = {x1=481, y1=408, x2=497, y2=422, f=function() conSend(63) L.lastSave=nil end}
  2248.     tpt_buttons["disp"] = {x1=595, y1=408, x2=611, y2=422,f=function() L.checkRen=2 L.pModes=getViewModes() end}
  2249.     tpt_buttons["open"] = {x1=1, y1=408, x2=17, y2=422, f=function() if not L.ctrl then L.browseMode=1 else L.browseMode=2 end L.lastSave=sim.getSaveID() end}
  2250. end
  2251.  
  2252. local function inZoomWindow(x, y)
  2253.     if not L.isDrawing and (x < 0 or x >= sim.XRES or y < 0 or y >= sim.YRES) then
  2254.         return false
  2255.     end
  2256.     local snappedX, snappedY = x, y
  2257.     -- When the mouse is outside the window, TPT will snap coords to simulation area then check for zoom window
  2258.     if snappedX < 0 then snappedX = 0 elseif snappedX >= sim.XRES then snappedX = sim.XRES end
  2259.     if snappedY < 0 then snappedY = 0 elseif snappedY >= sim.YRES then snappedY = sim.YRES end
  2260.     local zoomX, zoomY = sim.adjustCoords(snappedX, snappedY)
  2261.     return zoomX ~= x or zoomY ~= y
  2262. end
  2263.  
  2264. local function sendMouseUpdate(mouseX, mouseY)
  2265.     L.realMouseX, L.realMouseY = mouseX, mouseY
  2266.     if inZoomWindow(mouseX, mouseY) then
  2267.         mouseX, mouseY = sim.adjustCoords(mouseX, mouseY)  
  2268.     else
  2269.         if mouseX < 0 then mouseX = 0 end
  2270.         if mouseY < 0 then mouseY = 0 end
  2271.         if mouseX > gfx.WIDTH then mouseX = gfx.WIDTH end
  2272.         if mouseY > gfx.HEIGHT then mouseY = gfx.HEIGHT end
  2273.     end
  2274.  
  2275.     if L.mousex ~= mouseX or L.mousey ~= mouseY then
  2276.         local b1, b2, b3 = math.floor(mouseX / 16), ((mouseX % 16) * 16) + math.floor(mouseY / 256), (mouseY % 256)
  2277.         conSend(32, string.char(b1, b2, b3))
  2278.         L.mousex, L.mousey = mouseX, mouseY
  2279.     end
  2280. end
  2281.  
  2282. local function mouseDown(mouseX, mouseY, button)
  2283.     if L.chatHidden then
  2284.         showbutton:mouseDown(mouseX, mouseY, button)
  2285.         if not hooks_enabled then
  2286.             return true
  2287.         end
  2288.     end
  2289.     if L.stamp and button == 1 then
  2290.         L.stampx, L.stampy = mouseX, mouseY
  2291.         return true
  2292.     end
  2293.     if L.stamp or L.placeStamp then
  2294.         return true
  2295.     end
  2296.     if L.skipClick then
  2297.         L.skipClick = false
  2298.         return true
  2299.     end
  2300.     if chatwindow:mouseDown(mouseX, mouseY, button) then
  2301.         return false
  2302.     end
  2303.  
  2304.     sendMouseUpdate(mouseX, mouseY)
  2305.     local obut, oevnt = L.mButt, L.mEvent
  2306.     if button ~= obut or 1 ~= oevnt then
  2307.         L.mButt, L.mEvent = button, 1
  2308.         if mouseX >= 0 and mouseY >= 0 and mouseX < sim.XRES and mouseY < sim.YRES then
  2309.             L.isDrawing = true
  2310.         end
  2311.         conSend(33, string.char(L.mButt * 16 + L.mEvent))
  2312.     end
  2313.    
  2314.     -- Click inside button first
  2315.     if button==1 then
  2316.         for k, v in pairs(tpt_buttons) do
  2317.             if mouseX >= v.x1 and mouseX <= v.x2 and mouseY >= v.y1 and mouseY <= v.y2 then
  2318.                 v.downInside = true
  2319.             end
  2320.         end
  2321.     end
  2322. end
  2323.  
  2324. local function mouseUp(mouseX, mouseY, button, reason)
  2325.     if L.chatHidden then
  2326.         showbutton:mouseUp(mouseX, mouseY, button)
  2327.         if not hooks_enabled then
  2328.             return true
  2329.         end
  2330.     end
  2331.     if L.stamp then
  2332.         if L.skipClick then
  2333.             L.skipClick = false
  2334.             return true
  2335.         end
  2336.         --stamp has been saved, make our own copy
  2337.         if button==1 then
  2338.             --save stamp ourself for data, delete it
  2339.             local sx,sy = mouseX, mouseY
  2340.             if sx<L.stampx then L.stampx,sx=sx,L.stampx end
  2341.             if sy<L.stampy then L.stampy,sy=sy,L.stampy end
  2342.             --cheap cut hook to send a clear
  2343.             if L.copying==1 then
  2344.                 --maybe this is ctrl+x? 67 is clear area
  2345.                 conSend(67,string.char(math.floor(L.stampx/16),((L.stampx%16)*16)+math.floor(L.stampy/256),(L.stampy%256),math.floor(sx/16),((sx%16)*16)+math.floor(sy/256),(sy%256)))
  2346.             end
  2347.             local w,h = sx-L.stampx,sy-L.stampy
  2348.             local stampName,fullName = saveStamp(L.stampx,L.stampy,w,h)
  2349.             sx,sy,L.stampx,L.stampy = math.ceil((sx+1)/4)*4,math.ceil((sy+1)/4)*4,math.floor(L.stampx/4)*4,math.floor(L.stampy/4)*4
  2350.             w,h = sx-L.stampx, sy-L.stampy
  2351.             local f = assert(io.open(fullName,"rb"))
  2352.             if L.copying then L.lastCopy = {data=f:read"*a",w=w,h=h} else L.lastStamp = {data=f:read"*a",w=w,h=h} end
  2353.             f:close()
  2354.             deleteStamp(stampName)
  2355.         end
  2356.         L.stamp=false
  2357.         L.copying=false
  2358.     end
  2359.     if L.placeStamp and reason == 0 then
  2360.         if L.skipClick then
  2361.             L.skipClick=false
  2362.             return true
  2363.         end
  2364.         if button==1 then
  2365.             local stm
  2366.             if L.copying then stm=L.lastCopy else stm=L.lastStamp end
  2367.             if stm then
  2368.                 if not stm.data then
  2369.                     --unknown stamp, send full screen on next step, how can we read last created stamp, timestamps on files?
  2370.                     L.sendScreen = (jacobsmod and 2 or true)
  2371.                 else
  2372.                     --send the stamp
  2373.                     if L.smoved then
  2374.                         --moved from arrows or rotate, send area next frame
  2375.                         L.placeStamp=false
  2376.                         L.sendScreen=true
  2377.                         return true
  2378.                     end
  2379.                     local sx,sy = mouseX-math.floor(stm.w/2),mouseY-math.floor((stm.h)/2)
  2380.                     if sx<0 then sx=0 end
  2381.                     if sy<0 then sy=0 end
  2382.                     if sx+stm.w>sim.XRES-1 then sx=sim.XRES-stm.w end
  2383.                     if sy+stm.h>sim.YRES-1 then sy=sim.YRES-stm.h end
  2384.                     local b1,b2,b3 = math.floor(sx/16),((sx%16)*16)+math.floor(sy/256),(sy%256)
  2385.                     local d = #stm.data
  2386.                     conSend(66,string.char(b1,b2,b3,math.floor(d/65536),math.floor(d/256)%256,d%256)..stm.data)
  2387.                 end
  2388.             end
  2389.         end
  2390.         L.placeStamp=false
  2391.         L.copying=false
  2392.     end
  2393.    
  2394.     if L.skipClick then
  2395.         L.skipClick = false
  2396.         return true
  2397.     end
  2398.     if chatwindow:mouseUp(mouseX, mouseY, button, reason) then
  2399.         return false
  2400.     end
  2401.  
  2402.     -- Ignore fake mouseups due to blur (don't send 0, 0 coordinate)
  2403.     -- Also ignore fake mouseups due to going into / outside of zoom window
  2404.     -- In both cases, tell other clients we're no longer holding the mouse, without causing it to draw the line
  2405.     if reason == 1 or reason == 2 then
  2406.         L.mButt, L.mEvent = button, 2
  2407.         L.isDrawing = false
  2408.         conSend(33, string.char(0 * 16 + L.mEvent))
  2409.         return
  2410.     end
  2411.  
  2412.     sendMouseUpdate(mouseX, mouseY)
  2413.     local obut, oevnt = L.mButt, L.mEvent
  2414.     if button ~= obut or 2 ~= oevnt then
  2415.         L.isDrawing = false
  2416.         L.mButt, L.mEvent = button, 2
  2417.         conSend(33, string.char(L.mButt * 16 + L.mEvent))
  2418.     end
  2419.  
  2420.     -- Up inside the button we started with
  2421.     if button == 1 then
  2422.         local ret = true
  2423.         for k,v in pairs(tpt_buttons) do
  2424.             if v.downInside and (mouseX>=v.x1 and mouseX<=v.x2 and mouseY>=v.y1 and mouseY<=v.y2) then
  2425.                 if v.f() == false then ret = false end
  2426.             end
  2427.             v.downInside = nil
  2428.         end
  2429.         return ret
  2430.     end
  2431. end
  2432.  
  2433. local function mouseMove(mouseX, mouseY, dX, dY)
  2434.     if L.chatHidden then
  2435.         showbutton:mouseMove(mouseX, mouseY, dX, dY)
  2436.         if not hooks_enabled then
  2437.             return true
  2438.         end
  2439.     end
  2440.     if chatwindow:mouseMove(mouseX, mouseY, dX, dY) then
  2441.         return false
  2442.     end
  2443.  
  2444.     -- Always send mouse update, unless we're currently drawing and entered/exited the zoom window
  2445.     -- TPT sends a fake mouseup event in this case, but the mouse move happens first so we need to stop it ourselves
  2446.     local shouldBlockMove = L.isDrawing and not inZoomWindow(mouseX, mouseY) ~= inZoomWindow(L.realMouseX, L.realMouseY)
  2447.     if shouldBlockMove then
  2448.         L.isDrawing = false
  2449.     else
  2450.         sendMouseUpdate(mouseX, mouseY)
  2451.     end
  2452.  
  2453.     --Mouse hold, we MUST stay inside button or don't trigger on up
  2454.     if button == 1 then
  2455.         for k, v in pairs(tpt_buttons) do
  2456.             if v.downInside and (mouseX < v.x1 or mouseX > v.x2 or mouseY < v.y1 or mouseY > v.y2) then
  2457.                 v.downInside = nil
  2458.             end
  2459.         end
  2460.     end
  2461. end
  2462.  
  2463. local function mouseWheel(mouseX, mouseY, wheel)
  2464.     if chatwindow.inputbox.focus and chatwindow:mouseWheel(mousex, mousey, wheel) then
  2465.         return false
  2466.     end
  2467. end
  2468.  
  2469. local keyFuncs = {
  2470.     --TAB
  2471.     [9] = function() if not jacobsmod or not L.ctrl then conSend(35) end end,
  2472.  
  2473.     --View modes 0-9
  2474.     [48] = function() conSend(48,"\10") end,
  2475.     [49] = function() if L.shift then conSend(48,"\9") tpt.display_mode(9)--[[force local display mode, screw debug check for now]] return false end conSend(48,"\0") end,
  2476.     [50] = function() conSend(48,"\1") end,
  2477.     [51] = function() conSend(48,"\2") end,
  2478.     [52] = function() conSend(48,"\3") end,
  2479.     [53] = function() conSend(48,"\4") end,
  2480.     [54] = function() conSend(48,"\5") end,
  2481.     [55] = function() conSend(48,"\6") end,
  2482.     [56] = function() conSend(48,"\7") end,
  2483.     [57] = function() conSend(48,"\8") end,
  2484.  
  2485.     -- ins / del for replace mode
  2486.     [1073741897] = function() L.replacemode = bit.bxor(L.replacemode, 1) conSend(38, L.replacemode) end,
  2487.     [127] = function() L.replacemode = bit.bxor(L.replacemode, 2) conSend(38, L.replacemode) end,
  2488.  
  2489.     --Arrows for stamp adjust
  2490.     [273] = function() if L.placeStamp then L.smoved=true end end,
  2491.     [274] = function() if L.placeStamp then L.smoved=true end end,
  2492.     [275] = function() if L.placeStamp then L.smoved=true end end,
  2493.     [276] = function() if L.placeStamp then L.smoved=true end end
  2494. }
  2495.  
  2496. local scanFuncs = {
  2497.     --`, console
  2498.     [53] = function() if not L.shift and con.connected then infoText:reset("Console does not sync, use shift+` to open instead") return false else jacobsmod_old_menu_check = true end end,
  2499.  
  2500.     --b, deco, pauses sim
  2501.     [5] = function() if L.ctrl then conSend(51,tpt.decorations_enable()==0 and "\1" or "\0") else conSend(49,"\1") conSend(51,"\1") end end,
  2502.  
  2503.     --c, copy
  2504.     [6] = function() if L.ctrl then L.stamp=true L.copying=true L.stampx = -1 L.stampy = -1 end end,
  2505.  
  2506.     --d key, debug, api broken right now
  2507.     --[7] = function() conSend(55) end,
  2508.  
  2509.     --F, frame step
  2510.     [9] = function() if not jacobsmod or not L.ctrl then conSend(50) end end,
  2511.  
  2512.     --H, HUD and intro text
  2513.     [11] = function() if L.ctrl and jacobsmod then return false end end,
  2514.  
  2515.     --I, invert pressure
  2516.     [12] = function() conSend(62) end,
  2517.  
  2518.     --K, stamp menu, abort our known stamp, who knows what they picked, send full screen?
  2519.     [14] = function() L.lastStamp={data=nil,w=0,h=0} L.placeStamp=true end,
  2520.  
  2521.     --L, last Stamp
  2522.     [15] = function() if L.lastStamp then L.placeStamp=true end end,
  2523.  
  2524.     --N, newtonian gravity or new save
  2525.     [17] = function() if jacobsmod and L.ctrl then L.sendScreen=2 L.lastSave=nil else conSend(54,tpt.newtonian_gravity()==0 and "\1" or "\0") end end,
  2526.  
  2527.     --O, old menu in jacobs mod
  2528.     [18] = function() if jacobsmod and not L.ctrl then jacobsmod_old_menu_check = true end end,
  2529.  
  2530.     --R, for stamp rotate or save reload
  2531.     [21] = function()
  2532.         if L.placeStamp then
  2533.             L.smoved = true
  2534.             if L.shift then
  2535.                 return
  2536.             end
  2537.             L.rotate = not L.rotate
  2538.         elseif L.ctrl then
  2539.             if L.lastSave then
  2540.                 conSend(70)
  2541.                 infoText:reset("Sent reload command")
  2542.             else
  2543.                 L.sendScreen = true
  2544.                 infoText:reset("Send sync")
  2545.             end
  2546.         end
  2547.     end,
  2548.  
  2549.     --S, stamp
  2550.     [22] = function() if (L.lastStick2 and not L.ctrl) or (jacobsmod and L.ctrl) then return end L.stamp=true L.stampx = -1 L.stampy = -1 end,
  2551.  
  2552.     --T, tabs
  2553.     [23] = function() if jacobsmod then L.tabs = not L.tabs end end,
  2554.  
  2555.     --U, ambient heat toggle
  2556.     [24] = function() conSend(53,tpt.ambient_heat()==0 and "\1" or "\0") end,
  2557.  
  2558.     --V, paste the copystamp
  2559.     [25] = function() if L.ctrl and L.lastCopy then L.placeStamp=true L.copying=true end end,
  2560.  
  2561.     --W, gravity mode
  2562.     [26] = function() if L.lastStick2 and not L.ctrl then return end conSend(58,string.char((sim.gravityMode()+1)%3)) return true end,
  2563.  
  2564.     --X, cut a copystamp and clear
  2565.     [27] = function() if L.ctrl then L.stamp=true L.copying=1 L.stampx = -1 L.stampy = -1 end end,
  2566.  
  2567.     --Y, air mode
  2568.     [28] = function() if L.ctrl then L.sendScreen = true else conSend(59,string.char((sim.airMode()+1)%5)) return true end end,
  2569.  
  2570.     --Z
  2571.     [29] = function() if L.ctrl then L.sendScreen = true else L.skipClick=true end end,
  2572.  
  2573.     --ESC
  2574.     [41] = function() if not L.chatHidden then L.chatHidden = true TPTMP.chatHidden = true return false end end,
  2575.  
  2576.     --space, pause toggle
  2577.     [44] = function() conSend(49,tpt.set_pause()==0 and "\1" or "\0") end,
  2578.  
  2579.     --= key, pressure/spark reset
  2580.     [46] = function() if L.ctrl then conSend(60) else conSend(61) end end,
  2581.  
  2582.     --;, replace mode or specific delete
  2583.     [59] = function() if L.ctrl then  L.replacemode = bit.bxor(L.replacemode, 2) else  L.replacemode = bit.bxor(L.replacemode, 1) end conSend(38, L.replacemode) end,
  2584.  
  2585.     --F1 , intro text
  2586.     [58] = function() if jacobsmod then return false end end,
  2587.  
  2588.     --F5 , save reload
  2589.     [62] = function()
  2590.         if L.lastSave then
  2591.             conSend(70)
  2592.             infoText:reset("Sent reload command")
  2593.         else
  2594.             L.sendScreen = true
  2595.             infoText:reset("Sent sync")
  2596.         end
  2597.     end,
  2598. }
  2599. local scanUnpressFuncs = {
  2600.     --Z
  2601.     [29] = function() L.skipClick=false if L.alt then L.skipClick=true end end,
  2602. }
  2603.  
  2604. local function keypress(key, scan, rep, shift, ctrl, alt)
  2605.     if shift and not L.shift then
  2606.         L.shift = true
  2607.         conSend(36, string.char(17))
  2608.     end
  2609.     if ctrl and not L.ctrl then
  2610.         L.ctrl = true
  2611.         conSend(36, string.char(1))
  2612.     end
  2613.     if alt and not L.alt then
  2614.         L.alt = true
  2615.         conSend(36, string.char(33))
  2616.     end
  2617.  
  2618.     local check = chatwindow:keypress(key, scan, rep, shift, ctrl, alt)
  2619.     if type(check) == "boolean" then
  2620.         return not check
  2621.     end
  2622.    
  2623.     if rep then return end
  2624.  
  2625.     if scanFuncs[scan] then
  2626.         ret = scanFuncs[scan]()
  2627.         if ret ~= nil then
  2628.             return ret
  2629.         end
  2630.     elseif keyFuncs[key] then
  2631.         ret = keyFuncs[key]()
  2632.         if ret ~= nil then
  2633.             return ret
  2634.         end
  2635.     end
  2636. end
  2637. local function keyrelease(key, scan, rep, shift, ctrl, alt)
  2638.     if not shift and L.shift then
  2639.         L.shift = false
  2640.         conSend(36, string.char(16))
  2641.     end
  2642.     if not ctrl and L.ctrl then
  2643.         L.ctrl = false
  2644.         conSend(36, string.char(0))
  2645.     end
  2646.     if not alt and L.alt then
  2647.         L.alt = false
  2648.         conSend(36, string.char(32))
  2649.     end
  2650.  
  2651.     if rep then return end
  2652.  
  2653.     if scanUnpressFuncs[scan] then
  2654.         ret = scanUnpressFuncs[scan]()
  2655.         if ret~= nil then
  2656.             return ret
  2657.         end
  2658.     end
  2659. end
  2660. local function textinput(text)
  2661.     chatwindow:textinput(text)
  2662. end
  2663. local function blur()
  2664.     if L.shift then
  2665.         L.shift = false
  2666.         conSend(36, string.char(16))
  2667.     end
  2668.     if L.ctrl then
  2669.         L.ctrl = false
  2670.         conSend(36, string.char(0))
  2671.     end
  2672.     if L.alt then
  2673.         L.alt = false
  2674.         conSend(36, string.char(32))
  2675.     end
  2676. end
  2677. function TPTMP.disableMultiplayer()
  2678.     evt.unregister(evt.tick, step)
  2679.     evt.unregister(evt.mousedown, mouseDown)
  2680.     evt.unregister(evt.mouseup, mouseUp)
  2681.     evt.unregister(evt.mousemove, mouseMove)
  2682.     evt.unregister(evt.mousewheel, mouseQheel)
  2683.     evt.unregister(evt.keypress, keypress)
  2684.     evt.unregister(evt.keyrelease, keyrelease)
  2685.     evt.unregister(evt.textinput, textinput)
  2686.     evt.unregister(evt.blur, blur)
  2687.     TPTMP = nil
  2688.     disconnected("TPTMP unloaded")
  2689. end
  2690.  
  2691. function TPTMP.enableMultiplayer()
  2692.     hooks_enabled = true
  2693.     TPTMP.enableMultiplayer = nil
  2694.     debug.sethook(nil,"",0)
  2695.     if jacobsmod then
  2696.         --clear intro text tooltip
  2697.         gfx.toolTip("", 0, 0, 0, 4)
  2698.     end
  2699. end
  2700.  
  2701. function reconnect()
  2702.     disconnected()
  2703.     if tpt.get_name() ~= '' then username = tpt.get_name() end
  2704.     connectToServer(lastip,lastport,username)
  2705.     if laistchan~='' then
  2706.         joinChannel(laistchan)
  2707.     end
  2708. end
  2709.  
  2710. TPTMP.con = con
  2711. TPTMP.chatHidden = true
  2712. evt.register(evt.tick, step)
  2713. evt.register(evt.mousedown, mouseDown)
  2714. evt.register(evt.mouseup, mouseUp)
  2715. evt.register(evt.mousemove, mouseMove)
  2716. evt.register(evt.mousewheel, mouseWheel)
  2717. evt.register(evt.keypress, keypress)
  2718. evt.register(evt.keyrelease, keyrelease)
  2719. evt.register(evt.textinput, textinput)
  2720. evt.register(evt.blur, blur)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement