Advertisement
FeynmanTech

Multiplayer Mod

Mar 17th, 2014
590
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 56.99 KB | None | 0 0
  1. --Cracker64's Powder Toy Multiplayer
  2. --I highly recommend to use my Autorun Script Manager
  3. --VER 0.6 UPDATE http://pastebin.com/raw.php?i=Dk5Kx4JV
  4.  
  5. --Version 0.6
  6.  
  7. --TODO's
  8. --Support replace mode
  9. --FIGH,STKM,STK2,LIGH need a few more creation adjustments
  10. --Some more server functions
  11. --Force the russian to remake the ui
  12. --A few more helpful api functions (save ID get and load it)
  13. -------------------------------------------------------
  14.  
  15. --CHANGES:
  16. --More colors!
  17. --ESC key will unfocus, then minimize chat
  18. --Changes from jacob, including: Support jacobsMod, keyrepeat
  19.  
  20. local issocket,socket = pcall(require,"socket")
  21. if not sim.loadStamp then error"Tpt version not supported" end
  22. if MANAGER_EXISTS then using_manager=true else MANAGER_PRINT=print end
  23. local hooks_enabled = false --hooks only enabled once you maximize the button
  24.  
  25. local PORT = 34403 --Change 34403 to your desired port
  26. local KEYBOARD = 1 --only change if you have issues. Only other option right now is 2(finnish).
  27. --Local player vars we need to keep
  28. local L = {mousex=0, mousey=0, brushx=0, brushy=0, sell=1, sela=296, selr=0, mButt=0, mEvent=0, dcolour=0, stick2=false, chatHidden=true, flashChat=false,
  29. shift=false, alt=false, ctrl=false, tabs = false, z=false, downInside=nil, skipClick=false, pauseNextFrame=false, copying=false, stamp=false, placeStamp=false, lastStamp=nil, lastCopy=nil, smoved=false, rotate=false, sendScreen=false}
  30.  
  31. local tptversion = tpt.version.build
  32. local jacobsmod = tpt.version.jacob1s_mod~=nil
  33. --#####JACOBSMOD#####
  34. if not jacobsmod then
  35. --#####JACOBSMOD#####
  36. multi = multi or {}
  37. math.randomseed(os.time())
  38. local username = tpt.get_name()
  39. if username=="" then
  40.     username = "Guest"..math.random(10000,99999)
  41. end
  42. function multi.setName(name)
  43.     username = name
  44. end
  45. local con = {connected = false,
  46.          socket = nil,
  47.          members = nil,
  48.          pingTime = os.time()+60}
  49. local function conSend(cmd,msg,endNull)
  50.     if not con.connected then return false,"Not connected" end
  51.     msg = msg or ""
  52.     if endNull then msg = msg.."\0" end
  53.     if cmd then msg = string.char(cmd)..msg end
  54.     --print("sent "..msg)
  55.     con.socket:settimeout(10)
  56.     con.socket:send(msg)
  57.     con.socket:settimeout(0)
  58. end
  59. local function joinChannel(chan)
  60.     conSend(16,chan,true)
  61.     --send some things to new channel
  62.     conSend(34,string.char(L.brushx,L.brushy))
  63.     conSend(37,string.char(math.floor(L.sell/256),L.sell%256))
  64.     conSend(37,string.char(math.floor(64 + L.sela/256),L.sela%256))
  65.     conSend(37,string.char(math.floor(128 + L.selr/256),L.selr%256))
  66.     conSend(65,string.char(math.floor(L.dcolour/16777216),math.floor(L.dcolour/65536)%256,math.floor(L.dcolour/256)%256,L.dcolour%256))
  67. end
  68. local function connectToMniip(ip,port)
  69.     if con.connected then return false,"Already connected" end
  70.     --local newusername = tpt.get_name()
  71.     --if newusername~="" then username = newusername end
  72.     ip = ip or "pwc-servers.com"
  73.     port = port or PORT
  74.     local sock = socket.tcp()
  75.     sock:settimeout(10)
  76.     local s,r = sock:connect(ip,port)
  77.     if not s then return false,r end
  78.     sock:settimeout(0)
  79.     sock:setoption("keepalive",true)
  80.     sock:send(string.char(tpt.version.major)..string.char(tpt.version.minor)..username.."\0")
  81.     local c,r
  82.     while not c do
  83.     c,r = sock:receive(1)
  84.     if not c and r~="timeout" then break end
  85.     end
  86.     if not c and r~="timeout" then return false,r end
  87.  
  88.     if c~= "\1" then
  89.     if c=="\0" then
  90.         local err=""
  91.         c,r = sock:receive(1)
  92.         while c~="\0" do
  93.         err = err..c
  94.         c,r = sock:receive(1)
  95.         end
  96.         return false,err
  97.     end
  98.     return false,"Bad Connect"
  99.     end
  100.  
  101.     con.socket = sock
  102.     con.connected = true
  103.     return true
  104. end
  105. --get up to a null (\0)
  106. local function conGetNull()
  107.     con.socket:settimeout(nil)
  108.     local c,r = con.socket:receive(1)
  109.     local rstring=""
  110.     while c~="\0" do
  111.     rstring = rstring..c
  112.     c,r = con.socket:receive(1)
  113.     end
  114.     con.socket:settimeout(0)
  115.     return rstring
  116. end
  117. --get next char/byte
  118. local function cChar()
  119.     con.socket:settimeout(nil)
  120.     local c,r = con.socket:receive(1)
  121.     con.socket:settimeout(0)
  122.     return c or error(r)
  123. end
  124. local function cByte()
  125.     return cChar():byte()
  126. end
  127. --return table of arguments
  128. local function getArgs(msg)
  129.     if not msg then return {} end
  130.     local args = {}
  131.     for word in msg:gmatch("([^%s%c]+)") do
  132.     table.insert(args,word)
  133.     end
  134.     return args
  135. end
  136.  
  137. --get different lists for other language keyboards
  138. local keyboardshift = { {before=" qwertyuiopasdfghjklzxcvbnm1234567890-=.,/`|;'[]\\",after=" QWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_+><?~\\:\"{}|",},{before=" qwertyuiopasdfghjklzxcvbnm1234567890+,.-'???????<",after=" QWERTYUIOPASDFGHJKLZXCVBNM!\"#????&/()=?;:_*`^>",}  }
  139. local keyboardaltrg = { {nil},{before=" qwertyuiopasdfghjklzxcvbnm1234567890+,.-'????",after=" qwertyuiopasdfghjklzxcvbnm1@?????{[]}\\,.-'~|",},}
  140.  
  141. local function shift(s)
  142.     if keyboardshift[KEYBOARD]~=nil then
  143.         return (s:gsub("(.)",function(c)return keyboardshift[KEYBOARD]["after"]:sub(keyboardshift[KEYBOARD]["before"]:find(c,1,true))end))
  144.     else return s end
  145. end
  146. local function altgr(s)
  147.     if keyboardaltgr[KEYBOARD]~=nil then
  148.         return (s:gsub("(.)",function(c)return keyboardaltgr[KEYBOARD]["after"]:sub(keyboardaltgr[KEYBOARD]["before"]:find(c,1,true))end))
  149.     else return s end
  150. end
  151.  
  152. local ui_base local ui_box local ui_text local ui_button local ui_scrollbar local ui_inputbox local ui_chatbox
  153. ui_base = {
  154. new = function()
  155.     local b={}
  156.     b.drawlist = {}
  157.     function b:drawadd(f)
  158.         table.insert(self.drawlist,f)
  159.     end
  160.     function b:draw(...)
  161.         for _,f in ipairs(self.drawlist) do
  162.             if type(f)=="function" then
  163.                 f(self,unpack(arg))
  164.             end
  165.         end
  166.     end
  167.     b.movelist = {}
  168.     function b:moveadd(f)
  169.         table.insert(self.movelist,f)
  170.     end
  171.     function b:onmove(x,y)
  172.         for _,f in ipairs(self.movelist) do
  173.             if type(f)=="function" then
  174.                 f(self,x,y)
  175.             end
  176.         end
  177.     end
  178.     return b
  179. end
  180. }
  181. ui_box = {
  182. new = function(x,y,w,h,r,g,b)
  183.     local box=ui_base.new()
  184.     box.x=x box.y=y box.w=w box.h=h box.x2=x+w box.y2=y+h
  185.     box.r=r or 255 box.g=g or 255 box.b=b or 255
  186.     function box:setcolor(r,g,b) self.r=r self.g=g self.b=b end
  187.     function box:setbackground(r,g,b,a) self.br=r self.bg=g self.bb=b self.ba=a end
  188.     box.drawbox=true
  189.     box.drawbackground=false
  190.     box:drawadd(function(self) if self.drawbackground then tpt.fillrect(self.x,self.y,self.w,self.h,self.br,self.bg,self.bb,self.ba) end
  191.                                 if self.drawbox then tpt.drawrect(self.x,self.y,self.w,self.h,self.r,self.g,self.b) end end)
  192.     box:moveadd(function(self,x,y)
  193.         if x then self.x=self.x+x self.x2=self.x2+x end
  194.         if y then self.y=self.y+y self.y2=self.y2+y end
  195.     end)
  196.     return box
  197. end
  198. }
  199. ui_text = {
  200. new = function(text,x,y,r,g,b)
  201.     local txt = ui_base.new()
  202.     txt.text = text
  203.     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
  204.     function txt:setcolor(r,g,b) self.r=r self.g=g self.b=b end
  205.     txt:drawadd(function(self,x,y) tpt.drawtext(x or self.x,y or self.y,self.text,self.r,self.g,self.b) end)
  206.     txt:moveadd(function(self,x,y)
  207.         if x then self.x=self.x+x end
  208.         if y then self.y=self.y+y end
  209.     end)
  210.     function txt:process() return false end
  211.     return txt
  212. end,
  213. --Scrolls while holding mouse over
  214. newscroll = function(text,x,y,vis,force,r,g,b)
  215.     local txt = ui_text.new(text,x,y,r,g,b)
  216.     if not force and tpt.textwidth(text)<vis then return txt end
  217.     txt.visible=vis
  218.     txt.length=string.len(text)
  219.     txt.start=1
  220.     local last=2
  221.     while tpt.textwidth(text:sub(1,last))<vis and last<=txt.length do
  222.         last=last+1
  223.     end
  224.     txt.last=last-1
  225.     txt.minlast=last-1
  226.     txt.ppl=((txt.visible-6)/(txt.length-txt.minlast+1))
  227.     function txt:update(text,pos)
  228.         if text then
  229.             self.text=text
  230.             self.length=string.len(text)
  231.             local last=2
  232.             while tpt.textwidth(text:sub(1,last))<self.visible and last<=self.length do
  233.                 last=last+1
  234.             end
  235.             self.minlast=last-1
  236.             self.ppl=((self.visible-6)/(self.length-self.minlast+1))
  237.             if not pos then self.last=self.minlast end
  238.         end
  239.         if pos then
  240.             if pos>=self.last and pos<=self.length then --more than current visible
  241.                 local newlast = pos
  242.                 local newstart=1
  243.                 while tpt.textwidth(self.text:sub(newstart,newlast))>= self.visible do
  244.                     newstart=newstart+1
  245.                 end
  246.                 self.start=newstart self.last=newlast
  247.             elseif pos<self.start and pos>0 then --position less than current visible
  248.                 local newstart=pos
  249.                 local newlast=pos+1
  250.                 while tpt.textwidth(self.text:sub(newstart,newlast))<self.visible and newlast<self.length do
  251.                         newlast=newlast+1
  252.                 end
  253.                 self.start=newstart self.last=newlast-1
  254.             end
  255.             --keep strings as long as possible (pulls from left)
  256.             local newlast=self.last
  257.             if newlast<self.minlast then newlast=self.minlast end
  258.             local newstart=1
  259.             while tpt.textwidth(self.text:sub(newstart,newlast))>= self.visible do
  260.                     newstart=newstart+1
  261.             end
  262.             self.start=newstart self.last=newlast
  263.         end
  264.     end
  265.     txt.drawlist={} --reset draw
  266.     txt:drawadd(function(self,x,y)
  267.         tpt.drawtext(x or self.x,y or self.y, self.text:sub(self.start,self.last) ,self.r,self.g,self.b)
  268.     end)
  269.     function txt:process(mx,my,button,event,wheel)
  270.         if event==3 then
  271.             local newlast = math.floor((mx-self.x)/self.ppl)+self.minlast
  272.             if newlast<self.minlast then newlast=self.minlast end
  273.             if newlast>0 and newlast~=self.last then
  274.                 local newstart=1
  275.                 while tpt.textwidth(self.text:sub(newstart,newlast))>= self.visible do
  276.                     newstart=newstart+1
  277.                 end
  278.                 self.start=newstart self.last=newlast
  279.             end
  280.         end
  281.     end
  282.     return txt
  283. end
  284. }
  285. ui_inputbox = {
  286. new=function(x,y,w,h)
  287.     local intext=ui_box.new(x,y,w,h)
  288.     intext.cursor=0
  289.     intext.focus=false
  290.     intext.t=ui_text.newscroll("",x+2,y+2,w-2,true)
  291.     intext:drawadd(function(self)
  292.         local cursoradjust=tpt.textwidth(self.t.text:sub(self.t.start,self.cursor))+2
  293.         tpt.drawline(self.x+cursoradjust,self.y,self.x+cursoradjust,self.y+10,255,255,255)
  294.         self.t:draw()
  295.     end)
  296.     intext:moveadd(function(self,x,y) self.t:onmove(x,y) end)
  297.     function intext:setfocus(focus)
  298.         self.focus=focus
  299.         if focus then tpt.set_shortcuts(0) self:setcolor(255,255,0)
  300.         else tpt.set_shortcuts(1) self:setcolor(255,255,255) end
  301.     end
  302.     function intext:movecursor(amt)
  303.         self.cursor = self.cursor+amt
  304.         if self.cursor>self.t.length then self.cursor = self.t.length end
  305.         if self.cursor<0 then self.cursor = 0 return end
  306.     end
  307.     function intext:textprocess(key,nkey,modifier,event)
  308.         if not self.focus then return end
  309.         if event~=1 then return end
  310.         if nkey==27 then self:setfocus(false) return true end
  311.         if nkey==13 then local text=self.t.text self.cursor=0 self.t.text="" return text end --enter
  312.         if nkey==275 then self:movecursor(1) self.t:update(nil,self.cursor) return true end --right
  313.         if nkey==276 then self:movecursor(-1) self.t:update(nil,self.cursor) return true end --left
  314.         local modi = (modifier%1024)
  315.         local newstr
  316.         if nkey==8 and self.cursor > 0 then newstr=self.t.text:sub(1,self.cursor-1) .. self.t.text:sub(self.cursor+1) self:movecursor(-1) --back
  317.         elseif nkey==127 then newstr=self.t.text:sub(1,self.cursor) .. self.t.text:sub(self.cursor+2) --delete
  318.         else
  319.             if nkey<32 or nkey>=127 then return true end --normal key
  320.             local addkey = (modi==1 or modi==2) and shift(key) or key
  321.             if (math.floor(modi/512))==1 then addkey=altgr(key) end
  322.             newstr = self.t.text:sub(1,self.cursor) .. addkey .. self.t.text:sub(self.cursor+1)
  323.             self.t:update(newstr,self.cursor+1)
  324.             self:movecursor(1)
  325.             return true
  326.         end
  327.         if newstr then
  328.             self.t:update(newstr,self.cursor)
  329.         end
  330.         --some actual text processing, lol
  331.     end
  332.     return intext
  333. end
  334. }
  335. ui_scrollbar = {
  336. new = function(x,y,h,t,m)
  337.     local bar = ui_base.new() --use line object as base?
  338.     bar.x=x bar.y=y bar.h=h
  339.     bar.total=t
  340.     bar.numshown=m
  341.     bar.pos=0
  342.     bar.length=math.floor((1/math.ceil(bar.total-bar.numshown+1))*bar.h)
  343.     bar.soffset=math.floor(bar.pos*((bar.h-bar.length)/(bar.total-bar.numshown)))
  344.     function bar:update(total,shown,pos)
  345.         self.pos=pos or 0
  346.         if self.pos<0 then self.pos=0 end
  347.         self.total=total
  348.         self.numshown=shown
  349.         self.length= math.floor((1/math.ceil(self.total-self.numshown+1))*self.h)
  350.         self.soffset= math.floor(self.pos*((self.h-self.length)/(self.total-self.numshown)))
  351.     end
  352.     function bar:move(wheel)
  353.         self.pos = self.pos-wheel
  354.         if self.pos < 0 then self.pos=0 end
  355.         if self.pos > (self.total-self.numshown) then self.pos=(self.total-self.numshown) end
  356.         self.soffset= math.floor(self.pos*((self.h-self.length)/(self.total-self.numshown)))
  357.     end
  358.     bar:drawadd(function(self)
  359.         if self.total > self.numshown then
  360.             tpt.drawline(self.x,self.y+self.soffset,self.x,self.y+self.soffset+self.length)
  361.         end
  362.     end)
  363.     bar:moveadd(function(self,x,y)
  364.         if x then self.x=self.x+x end
  365.         if y then self.y=self.y+y end
  366.     end)
  367.     function bar:process(mx,my,button,event,wheel)
  368.         if wheel~=0 and not hidden_mode then
  369.             if self.total > self.numshown then
  370.                 local previous = self.pos
  371.                 self:move(wheel)
  372.                 if self.pos~=previous then
  373.                     return wheel
  374.                 end
  375.             end
  376.         end
  377.         --possibly click the bar and drag?
  378.         return false
  379.     end
  380.     return bar
  381. end
  382. }
  383. ui_button = {
  384. new = function(x,y,w,h,f,text)
  385.     local b = ui_box.new(x,y,w,h)
  386.     b.f=f
  387.     b.t=ui_text.new(text,x+2,y+2)
  388.     b.drawbox=false
  389.     b.almostselected=false
  390.     b.invert=true
  391.     b:drawadd(function(self)
  392.         if self.invert and self.almostselected then
  393.             self.almostselected=false
  394.             tpt.fillrect(self.x,self.y,self.w,self.h)
  395.             local tr=self.t.r local tg=self.t.g local tb=self.t.b
  396.             b.t:setcolor(0,0,0)
  397.             b.t:draw()
  398.             b.t:setcolor(tr,tg,tb)
  399.         else
  400.             b.t:draw()
  401.         end
  402.     end)
  403.     b:moveadd(function(self,x,y)
  404.         self.t:onmove(x,y)
  405.     end)
  406.     function b:process(mx,my,button,event,wheel)
  407.         if mx<self.x or mx>self.x2 or my<self.y or my>self.y2 then return false end
  408.         if event==3 then self.almostselected=true end
  409.         if event==2 then self:f() end
  410.         return true
  411.     end
  412.     return b
  413. end
  414. }
  415. ui_chatbox = {
  416. new=function(x,y,w,h)
  417.     local chat=ui_box.new(x,y,w,h)
  418.     chat.moving=false
  419.     chat.lastx=0
  420.     chat.lasty=0
  421.     chat.relx=0
  422.     chat.rely=0
  423.     chat.shown_lines=math.floor(chat.h/10)-2 --one line for top, one for chat
  424.     chat.max_width=chat.w-4
  425.     chat.max_lines=200
  426.     chat.lines = {}
  427.     chat.scrollbar = ui_scrollbar.new(chat.x2-2,chat.y+11,chat.h-22,0,chat.shown_lines)
  428.     chat.inputbox = ui_inputbox.new(x,chat.y2-10,w,10)
  429.     chat.minimize = ui_button.new(chat.x2-15,chat.y,15,10,function() chat.moving=false chat.inputbox:setfocus(false) L.chatHidden=true end,">>")
  430.     chat:drawadd(function(self)
  431.         tpt.drawtext(self.x+85,self.y+2,"TPT Multiplayer")
  432.         tpt.drawline(self.x+1,self.y+10,self.x2-1,self.y+10,120,120,120)
  433.         self.scrollbar:draw()
  434.         local count=0
  435.         for i,line in ipairs(self.lines) do
  436.             if i>self.scrollbar.pos and i<= self.scrollbar.pos+self.shown_lines then
  437.                 line:draw(self.x+3,self.y+12+(count*10))
  438.                 count = count+1
  439.             end
  440.         end
  441.         self.inputbox:draw()
  442.         self.minimize:draw()
  443.     end)
  444.     chat:moveadd(function(self,x,y)
  445.         for i,line in ipairs(self.lines) do
  446.             line:onmove(x,y)
  447.         end
  448.         self.scrollbar:onmove(x,y)
  449.         self.inputbox:onmove(x,y)
  450.         self.minimize:onmove(x,y)
  451.     end)
  452.     function chat:addline(line,r,g,b)
  453.         if not line or line=="" then return end --No blank lines
  454.         local linebreak=0
  455.         for i=0,#line do
  456.             if tpt.textwidth(line:sub(linebreak,i+1))>self.max_width or i==#line then
  457.                 table.insert(self.lines,ui_text.new(line:sub(linebreak,i),self.x,0,r,g,b))
  458.                 linebreak=i+1
  459.             end
  460.         end
  461.         while #self.lines>self.max_lines do table.remove(self.lines,1) end
  462.         self.scrollbar:update(#self.lines,self.shown_lines,#self.lines-self.shown_lines)
  463.         if L.chatHidden then L.flashChat=true end
  464.     end
  465.     function chat:process(mx,my,button,event,wheel)
  466.         if L.chatHidden then return false end
  467.         self.minimize:process(mx,my,button,event,wheel)
  468.         if self.moving and event==3 then
  469.             local newx,newy = mx-self.relx,my-self.rely
  470.             local ax,ay = 0,0
  471.             if newx<0 then ax = newx end
  472.             if newy<0 then ay = newy end
  473.             if (newx+self.w)>=612 then ax = newx+self.w-612 end
  474.             if (newy+self.h)>=384 then ay = newy+self.h-384 end
  475.             self:onmove(mx-self.lastx-ax,my-self.lasty-ay)
  476.             self.lastx=mx-ax
  477.             self.lasty=my-ay
  478.             return true
  479.         end
  480.         if self.moving and event==2 then self.moving=false return true end
  481.         if mx<self.x or mx>self.x2 or my<self.y or my>self.y2 then self.inputbox:setfocus(false) return false end
  482.         self.scrollbar:process(mx,my,button,event,wheel)
  483.         local which = math.floor((my-self.y)/10)
  484.         if event==1 and which==0 then self.moving=true self.lastx=mx self.lasty=my self.relx=mx-self.x self.rely=my-self.y return true end
  485.         if event==1 and which==self.shown_lines+1 then self.inputbox:setfocus(true) return true elseif self.inputbox.focus then return true end --trigger input_box
  486.         if which>0 and which<self.shown_lines+1 and self.lines[which+self.scrollbar.pos] then self.lines[which+self.scrollbar.pos]:process(mx,my,button,event,wheel) end
  487.         return event==1
  488.     end
  489.     --commands for chat window
  490.     chatcommands = {
  491.     connect = function(self,msg,args)
  492.         if not issocket then self:addline("No luasockets found") return end
  493.         tpt.version.minor = 0
  494.         local s,r = connectToMniip(args[1],tonumber(args[2]))
  495.         if not s then self:addline(r,255,50,50) end
  496.     end,
  497.     send = function(self,msg,args)
  498.         if tonumber(args[1]) and args[2] then
  499.         local withNull=false
  500.         if args[2]=="true" then withNull=true end
  501.         rest = rest or ""
  502.         conSend(tonumber(args[1]),rest:sub(#args[1]+#args[2]+2),withNull)
  503.         end
  504.     end,
  505.     quit = function(self,msg,args)
  506.         if con.connected and args[1] then
  507.             conSend(19,username.." has quit (Reason: " .. args[1] .. ")",true)
  508.         elseif con.connected then
  509.             conSend(19,username.."has quit",true)
  510.         end
  511.         con.socket:close()
  512.         if args[1] then
  513.             self:addline(username.." has quit (Reason: " .. args[1] .. ")",255,50,50)
  514.         end
  515.         con.connected = false
  516.         con.members = {}
  517.     end,
  518.     join = function(self,msg,args)
  519.         if args[1] then
  520.             joinChannel(args[1])
  521.             self:addline("joined channel "..args[1],50,255,50)
  522.         end
  523.     end,
  524.     nick = function(self,msg,args)
  525.         if args[1] then
  526.             con.socket:close()
  527.             con.connected = false
  528.             con.members = {}
  529.             local oldName = args[1]
  530.             multi.setName(args[1])
  531.             self:addline("New name is: "..args[1],50,255,50)
  532.             local s,r = connectToMniip()
  533.             if con.connected then
  534.                 --conSend(19,oldName.." changed name to "..args[1],true)
  535.             end
  536.         end
  537.     end,
  538.     sync = function(self,msg,args)
  539.         if con.connected then L.sendScreen=true end --need to send 67 clear screen
  540.         self:addline("Synced screen to server",255,255,50)
  541.     end,
  542.     help = function(self,msg,args)
  543.         if not args[1] then self:addline("/help <command>, type /list for a list of commands") end
  544.         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")
  545.         elseif args[1] == "send" then self:addline("(/send <something> <somethingelse>") -- send a raw command
  546.         elseif args[1] == "quit" then self:addline("(/quit, no arguments) -- quit the game")
  547.         elseif args[1] == "join" then self:addline("(/join <channel> -- joins a room on the server")
  548.         elseif args[1] == "sync" then self:addline("(/sync, no arguments) -- syncs your screen to everyone else in the room")
  549.         end
  550.     end,
  551.     list = function(self,msg,args)
  552.         local list = ""
  553.         for name in pairs(chatcommands) do
  554.             list=list..name..", "
  555.         end
  556.         self:addline("Commands: "..list:sub(1,#list-2))
  557.     end,
  558.     }
  559.     function chat:textprocess(key,nkey,modifier,event)
  560.         if L.chatHidden then return false end
  561.         local text = self.inputbox:textprocess(key,nkey,modifier,event)
  562.         if type(text)=="boolean" then return text end
  563.         if text and text~="" then
  564.             local cmd = text:match("^/([^%s]+)")
  565.             if cmd then
  566.                 local rest=text:sub(#cmd+3)
  567.                 local args = getArgs(rest)
  568.                 if chatcommands[cmd] then
  569.                     chatcommands[cmd](self,msg,args)
  570.                     --self:addline("Executed "..cmd.." "..rest)
  571.                 else
  572.                     self:addline("No such command: "..cmd.." "..rest,255,50,50)
  573.                 end
  574.             else
  575.                 --normal chat
  576.                 if con.connected then
  577.                     conSend(19,text,true)
  578.                     self:addline(username .. ": ".. text,255,100,100)
  579.                 else
  580.                     self:addline("Not connected to server!",255,50,50)
  581.                 end
  582.             end
  583.         end
  584.     end
  585.     return chat
  586. end
  587. }
  588. local fadeText = {}
  589. --A little text that fades away, (align text (left/center/right)?)
  590. local function newFadeText(text,frames,x,y,r,g,b,noremove)
  591.     local t = {ticks=frames,max=frames,text=text,x=x,y=y,r=r,g=g,b=b,keep=noremove}
  592.     t.reset = function(self,text) self.ticks=self.max if text then self.text=text end end
  593.     table.insert(fadeText,t)
  594.     return t
  595. end
  596. --Some text locations for repeated usage
  597. local infoText = newFadeText("",150,245,370,255,255,255,true)
  598. local cmodeText = newFadeText("",120,250,180,255,255,255,true)
  599.  
  600. local showbutton = ui_button.new(613,using_manager and 119 or 136,14,14,function() if not hooks_enabled then enableMultiplayer() end L.chatHidden=false L.flashChat=false end,"<<")
  601. local flashCount=0
  602. 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)
  603. local chatwindow = ui_chatbox.new(100,100,250,200)
  604. chatwindow:setbackground(10,10,10,235) chatwindow.drawbackground=true
  605.  
  606. local eleNameTable = {
  607. ["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,
  608. ["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,
  609. ["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,
  610. ["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,
  611. ["DEFAULT_PT_LIFE_FRG2"] = 276,["DEFAULT_PT_LIFE_STAR"] = 277,["DEFAULT_PT_LIFE_FROG"] = 278,["DEFAULT_PT_LIFE_BRAN"] = 279,
  612. ["DEFAULT_WL_0"] = 280,["DEFAULT_WL_1"] = 281,["DEFAULT_WL_2"] = 282,["DEFAULT_WL_3"] = 283,["DEFAULT_WL_4"] = 284,
  613. ["DEFAULT_WL_5"] = 285,["DEFAULT_WL_6"] = 286,["DEFAULT_WL_7"] = 287,["DEFAULT_WL_8"] = 288,["DEFAULT_WL_9"] = 289,["DEFAULT_WL_10"] = 290,
  614. ["DEFAULT_WL_11"] = 291,["DEFAULT_WL_12"] = 292,["DEFAULT_WL_13"] = 293,["DEFAULT_WL_14"] = 294,["DEFAULT_WL_15"] = 295,
  615. ["DEFAULT_UI_SAMPLE"] = 296,["DEFAULT_UI_SIGN"] = 297,["DEFAULT_UI_PROPERTY"] = 298,["DEFAULT_UI_WIND"] = 299,
  616. ["DEFAULT_TOOL_HEAT"] = 300,["DEFAULT_TOOL_COOL"] = 301,["DEFAULT_TOOL_VAC"] = 302,["DEFAULT_TOOL_AIR"] = 303,["DEFAULT_TOOL_GRAV"] = 304,["DEFAULT_TOOL_NGRV"] = 305,
  617. ["DEFAULT_DECOR_SET"] = 306,["DEFAULT_DECOR_ADD"] = 307,["DEFAULT_DECOR_SUB"] = 308,["DEFAULT_DECOR_MUL"] = 309,["DEFAULT_DECOR_DIV"] = 310,["DEFAULT_DECOR_SMDG"] = 311,["DEFAULT_DECOR_CLR"] = 312,["DEFAULT_DECOR_LIGH"] = 313, ["DEFAULT_DECOR_DARK"] = 314
  618. }
  619. local function convertDecoTool(c)
  620.     return c
  621. end
  622. if jacobsmod then
  623.     function convertDecoTool(c)
  624.         if c >= 307 and c <= 311 then
  625.             c = c + 1
  626.         elseif c == 312 then
  627.             c = 307
  628.         end
  629.         return c
  630.     end
  631.     local modNameTable = {
  632.     ["DEFAULT_WL_ERASE"] = 280,["DEFAULT_WL_CNDTW"] = 281,["DEFAULT_WL_EWALL"] = 282,["DEFAULT_WL_DTECT"] = 283,["DEFAULT_WL_STRM"] = 284,
  633.     ["DEFAULT_WL_FAN"] = 285,["DEFAULT_WL_LIQD"] = 286,["DEFAULT_WL_ABSRB"] = 287,["DEFAULT_WL_WALL"] = 288,["DEFAULT_WL_AIR"] = 289,["DEFAULT_WL_POWDR"] = 290,
  634.     ["DEFAULT_WL_CNDTR"] = 291,["DEFAULT_WL_EHOLE"] = 292,["DEFAULT_WL_GAS"] = 293,["DEFAULT_WL_GRVTY"] = 294,["DEFAULT_WL_ENRGY"] = 295,["DEFAULT_WL_ERASEA"] = 280
  635.     }
  636.     for k,v in pairs(modNameTable) do
  637.         eleNameTable[k] = v
  638.     end
  639. end
  640. local gravList= {[0]="Vertical",[1]="Off",[2]="Radial"}
  641. local airList= {[0]="On",[1]="Pressure Off",[2]="Velocity Off",[3]="Off",[4]="No Update"}
  642. local noFlood = {[15]=true,[55]=true,[87]=true,[128]=true,[158]=true}
  643. local noShape = {[55]=true,[87]=true,[128]=true,[158]=true}
  644. local createOverride = {[55]=function(x,y,rx,ry,c,brush) sim.createParts(x,y,0,0,c,brush) end ,[87]=function(x,y,rx,ry,c,brush) sim.createParts(x,y,0,0,c,brush) end,[128]=function(x,y,rx,ry,c,brush) sim.createParts(x,y,0,0,c,brush) end,[158]=function(x,y,rx,ry,c,brush) sim.createParts(x,y,0,0,c,brush) end}
  645. local golStart,golEnd=256,279
  646. local wallStart,wallEnd=280,295
  647. local toolStart,toolEnd=300,305
  648. local decoStart,decoEnd=306,314
  649.  
  650. --Functions that do stuff in powdertoy
  651. local function createBoxAny(x1,y1,x2,y2,c,user)
  652.     if noShape[c] then return end
  653.     if c>=wallStart then
  654.         if c<= wallEnd then
  655.             sim.createWallBox(x1,y1,x2,y2,c-wallStart)
  656.         elseif c<=toolEnd then
  657.             if c>=toolStart then sim.toolBox(x1,y1,x2,y2,c-toolStart) end
  658.         elseif c<= decoEnd then
  659.             sim.decoBox(x1,y1,x2,y2,user.dcolour[2],user.dcolour[3],user.dcolour[4],user.dcolour[1],convertDecoTool(c)-decoStart)
  660.         end
  661.         return
  662.     elseif c>=golStart then
  663.         c = 78+(c-golStart)*256
  664.     end
  665.     sim.createBox(x1,y1,x2,y2,c)
  666. end
  667. local function createPartsAny(x,y,rx,ry,c,brush,user)
  668.     if c>=wallStart then
  669.         if c<= wallEnd then
  670.             if c == 284 then rx,ry = 0,0 end
  671.             sim.createWalls(x,y,rx,ry,c-wallStart,brush)
  672.         elseif c<=toolEnd then
  673.             if c>=toolStart then sim.toolBrush(x,y,rx,ry,c-toolStart,brush) end
  674.         elseif c<= decoEnd then
  675.             sim.decoBrush(x,y,rx,ry,user.dcolour[2],user.dcolour[3],user.dcolour[4],user.dcolour[1],convertDecoTool(c)-decoStart,brush)
  676.         end
  677.         return
  678.     elseif c>=golStart then
  679.         c = 78+(c-golStart)*256
  680.     end
  681.     if createOverride[c] then createOverride[c](x,y,rx,ry,c,brush) return end
  682.     sim.createParts(x,y,rx,ry,c,brush)
  683. end
  684.  
  685. function tpt.create(x,y,elem)
  686.     local user = con.members[id]
  687.     createPartsAny(x,y,0,0,tpt.element(elem),0,username)
  688. end
  689. local function createLineAny(x1,y1,x2,y2,rx,ry,c,brush,user)
  690.     if noShape[c] then return end
  691.     if c>=wallStart then
  692.         if c<= wallEnd then
  693.             if c == 284 then rx,ry = 0,0 end
  694.             sim.createWallLine(x1,y1,x2,y2,rx,ry,c-wallStart,brush)
  695.         elseif c<=toolEnd then
  696.             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
  697.         elseif c<= decoEnd then
  698.             sim.decoLine(x1,y1,x2,y2,rx,ry,user.dcolour[2],user.dcolour[3],user.dcolour[4],user.dcolour[1],convertDecoTool(c)-decoStart,brush)
  699.         end
  700.         return
  701.     elseif c>=golStart then
  702.         c = 78+(c-golStart)*256
  703.     end
  704.     sim.createLine(x1,y1,x2,y2,rx,ry,c,brush)
  705. end
  706. local function floodAny(x,y,c,cm,bm)
  707.     if noFlood[c] then return end
  708.     if c>=wallStart then
  709.         if c<= wallEnd then
  710.             sim.floodWalls(x,y,c-wallStart,cm,bm)
  711.         end
  712.         --other tools shouldn't flood
  713.         return
  714.     elseif c>=golStart then --GoL adjust
  715.         c = 78+(c-golStart)*256
  716.     end
  717.     sim.floodParts(x,y,c,cm,bm)
  718. end
  719. local function lineSnapCoords(x1,y1,x2,y2)
  720.     local nx,ny
  721.     local snapAngle = math.floor(math.atan2(y2-y1, x2-x1)/(math.pi*0.25)+0.5)*math.pi*0.25;
  722.     local lineMag = math.sqrt(math.pow(x2-x1,2)+math.pow(y2-y1,2));
  723.     nx = math.floor(lineMag*math.cos(snapAngle)+x1+0.5);
  724.     ny = math.floor(lineMag*math.sin(snapAngle)+y1+0.5);
  725.     return nx,ny
  726. end
  727.  
  728. local function rectSnapCoords(x1,y1,x2,y2)
  729.     local nx,ny
  730.     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;
  731.     local lineMag = math.sqrt(math.pow(x2-x1,2)+math.pow(y2-y1,2));
  732.     nx = math.floor(lineMag*math.cos(snapAngle)+x1+0.5);
  733.     ny = math.floor(lineMag*math.sin(snapAngle)+y1+0.5);
  734.     return nx,ny
  735. end
  736. 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}
  737. local function getViewModes()
  738.     local t={0,0,0}
  739.     for k,v in pairs(ren.displayModes()) do
  740.         t[1] = t[1]+v
  741.     end
  742.     for k,v in pairs(ren.renderModes()) do
  743.         t[2] = t[2]+(renModes[v] or 0)
  744.     end
  745.     t[3] = ren.colorMode()
  746.     return t
  747. end
  748.  
  749. --clicky click
  750. local function playerMouseClick(id,btn,ev)
  751.     local user = con.members[id]
  752.     local createE, checkBut
  753.  
  754.     --MANAGER_PRINT(tostring(btn)..tostring(ev))
  755.     if ev==0 then return end
  756.     if btn==1 then
  757.         user.rbtn,user.abtn = false,false
  758.         createE,checkBut=user.selectedl,user.lbtn
  759.     elseif btn==2 then
  760.         user.rbtn,user.lbtn = false,false
  761.         createE,checkBut=user.selecteda,user.abtn
  762.     elseif btn==4 then
  763.         user.lbtn,user.abtn = false,false
  764.         createE,checkBut=user.selectedr,user.rbtn
  765.     else return end
  766.  
  767.     if user.mousex>=612 or user.mousey>=384 then user.drawtype=false return end
  768.  
  769.     if ev==1 then
  770.         user.pmx,user.pmy = user.mousex,user.mousey
  771.         if not user.drawtype then
  772.             --left box
  773.             if user.ctrl and not user.shift then user.drawtype = 2 return end
  774.             --left line
  775.             if user.shift and not user.ctrl then user.drawtype = 1 return end
  776.             --floodfill
  777.             if user.ctrl and user.shift then floodAny(user.mousex,user.mousey,createE,-1) user.drawtype = 3 return end
  778.             --an alt click
  779.             if user.alt then return end
  780.             user.drawtype=4 --normal hold
  781.         end
  782.         createPartsAny(user.mousex,user.mousey,user.brushx,user.brushy,createE,user.brush,user)
  783.     elseif ev==2 and checkBut and user.drawtype then
  784.         if user.drawtype==2 then
  785.             if user.alt then user.mousex,user.mousey = rectSnapCoords(user.pmx,user.pmy,user.mousex,user.mousey) end
  786.             createBoxAny(user.mousex,user.mousey,user.pmx,user.pmy,createE,user)
  787.         else
  788.             if user.alt then user.mousex,user.mousey = lineSnapCoords(user.pmx,user.pmy,user.mousex,user.mousey) end
  789.             createLineAny(user.mousex,user.mousey,user.pmx,user.pmy,user.brushx,user.brushy,createE,user.brush,user)
  790.         end
  791.         user.drawtype=false
  792.         user.pmx,user.pmy = user.mousex,user.mousey
  793.     end
  794. end
  795. --To draw continued lines
  796. local function playerMouseMove(id)
  797.     local user = con.members[id]
  798.     local createE, checkBut
  799.     if user.lbtn then
  800.         createE,checkBut=user.selectedl,user.lbtn
  801.     elseif user.rbtn then
  802.         createE,checkBut=user.selectedr,user.rbtn
  803.     elseif user.abtn then
  804.         createE,checkBut=user.selecteda,user.abtn
  805.     else return end
  806.     if user.drawtype~=4 then if user.drawtype==3 then floodAny(user.mousex,user.mousey,createE,-1) end return end
  807.     if checkBut==3 then
  808.         if user.mousex>=612 then user.mousex=611 end
  809.         if user.mousey>=384 then user.mousey=383 end
  810.         createLineAny(user.mousex,user.mousey,user.pmx,user.pmy,user.brushx,user.brushy,createE,user.brush,user)
  811.         user.pmx,user.pmy = user.mousex,user.mousey
  812.     end
  813. end
  814. local function loadStamp(size,x,y,reset)
  815.     con.socket:settimeout(10)
  816.     local s = con.socket:receive(size)
  817.     con.socket:settimeout(0)
  818.     if s then
  819.         local f = io.open(".tmp.stm","wb")
  820.         f:write(s)
  821.         f:close()
  822.         if reset then sim.clearSim() end
  823.         if not sim.loadStamp(".tmp.stm",x,y) then
  824.             infoText:reset("Error loading stamp")
  825.         end
  826.         os.remove".tmp.stm"
  827.     else
  828.         infoText:reset("Error loading empty stamp")
  829.     end
  830. end
  831. local function saveStamp(x, y, w, h)
  832.     local stampName = sim.saveStamp(x, y, w, h)
  833.     if jacobsmod and not stampName then
  834.         stampName = sim.saveStamp(x, y, w, h, 3)
  835.     end
  836.     local fullName = "stamps/"..stampName..".stm"
  837.     return stampName, fullName
  838. end
  839. local function deleteStamp(name)
  840.     if sim.deleteStamp then
  841.         sim.deleteStamp(name)
  842.     else
  843.         os.remove("stamps/"..name..".stm")
  844.     end
  845. end
  846.  
  847. local dataCmds = {
  848.     [16] = function()
  849.     --room members
  850.         con.members = {}
  851.         local amount = cByte()
  852.         local peeps = {}
  853.         for i=1,amount do
  854.             local id = cByte()
  855.             con.members[id]={name=conGetNull(),mousex=0,mousey=0,brushx=4,brushy=4,brush=0,selectedl=1,selectedr=0,selecteda=296,dcolour={0,0,0,0},lbtn=false,abtn=false,rbtn=false,ctrl=false,shift=false,alt=false}
  856.             local name = con.members[id].name
  857.             table.insert(peeps,name)
  858.         end
  859.         chatwindow:addline("Online: "..table.concat(peeps," "),255,255,50)
  860.     end,
  861.     [17]= function()
  862.         local id = cByte()
  863.         con.members[id] ={name=conGetNull(),mousex=0,mousey=0,brushx=4,brushy=4,brush=0,selectedl=1,selectedr=0,selecteda=296,dcolour={0,0,0,0},lbtn=false,abtn=false,rbtn=false,ctrl=false,shift=false,alt=false}
  864.         chatwindow:addline(con.members[id].name.." has joined",100,255,100)
  865.     end,
  866.     [18] = function()
  867.         local id = cByte()
  868.         chatwindow:addline(con.members[id].name.." has left",255,255,100)
  869.         con.members[id]=nil
  870.     end,
  871.     [19] = function()
  872.         chatwindow:addline(con.members[cByte()].name .. ": " .. conGetNull())
  873.     end,
  874.     --Mouse Position
  875.     [32] = function()
  876.         local id = cByte()
  877.         local b1,b2,b3=cByte(),cByte(),cByte()
  878.         con.members[id].mousex,con.members[id].mousey=((b1*16)+math.floor(b2/16)),((b2%16)*256)+b3
  879.         playerMouseMove(id)
  880.     end,
  881.     --Mouse Click
  882.     [33] = function()
  883.         local id = cByte()
  884.         local d=cByte()
  885.         local btn,ev=math.floor(d/16),d%16
  886.         playerMouseClick(id,btn,ev)
  887.         if ev==0 then return end
  888.         if btn==1 then
  889.             con.members[id].lbtn=ev
  890.         elseif btn==2 then
  891.             con.members[id].abtn=ev
  892.         elseif btn==4 then
  893.             con.members[id].rbtn=ev
  894.         end
  895.     end,
  896.     --Brush size
  897.     [34] = function()
  898.         local id = cByte()
  899.         con.members[id].brushx,con.members[id].brushy=cByte(),cByte()
  900.     end,
  901.     --Brush Shape change, no args
  902.     [35] = function()
  903.         local id = cByte()
  904.         con.members[id].brush=(con.members[id].brush+1)%3
  905.     end,
  906.     --Modifier (mod and state)
  907.     [36] = function()
  908.         local id = cByte()
  909.         local d=cByte()
  910.         local mod,state=math.floor(d/16),d%16~=0
  911.         if mod==0 then
  912.             con.members[id].ctrl=state
  913.         elseif mod==1 then
  914.             con.members[id].shift=state
  915.         elseif mod==2 then
  916.             con.members[id].alt=state
  917.         end
  918.     end,
  919.     --selected elements (2 bits button, 14-element)
  920.     [37] = function()
  921.         local id = cByte()
  922.         local b1,b2=cByte(),cByte()
  923.         local btn,el=math.floor(b1/64),(b1%64)*256+b2
  924.         if btn==0 then
  925.             con.members[id].selectedl=el
  926.         elseif btn==1 then
  927.             con.members[id].selecteda=el
  928.         elseif btn==2 then
  929.             con.members[id].selectedr=el
  930.         end
  931.     end,
  932.     --cmode defaults (1 byte mode)
  933.     [48] = function()
  934.         local id = cByte()
  935.         tpt.display_mode(cByte())
  936.         cmodeText:reset(con.members[id].name.." set:")
  937.     end,
  938.     --pause set (1 byte state)
  939.     [49] = function()
  940.         local id = cByte()
  941.         local p,str = cByte(),"Pause"
  942.         tpt.set_pause(p)
  943.         if p==0 then str="Unpause" end
  944.         infoText:reset(str.." from "..con.members[id].name)
  945.     end,
  946.     --step frame, no args
  947.     [50] = function()
  948.         local id = cByte()
  949.         tpt.set_pause(0)
  950.         L.pauseNextFrame=true
  951.     end,
  952.  
  953.     --deco mode, (1 byte state)
  954.     [51] = function()
  955.         local id = cByte()
  956.         tpt.decorations_enable(cByte())
  957.         cmodeText:reset(con.members[id].name.." set:")
  958.     end,
  959.     --[[HUD mode, (1 byte state), deprecated
  960.     [52] = function()
  961.         local id = cByte()
  962.         local hstate = cByte()
  963.         tpt.hud(hstate)
  964.     end,
  965.     --]]
  966.     --amb heat mode, (1 byte state)
  967.     [53] = function()
  968.         local id = cByte()
  969.         tpt.ambient_heat(cByte())
  970.     end,
  971.     --newt_grav mode, (1 byte state)
  972.     [54] = function()
  973.         local id = cByte()
  974.         tpt.newtonian_gravity(cByte())
  975.     end,
  976.  
  977.     --[[
  978.     --debug mode (1 byte state?) can't implement
  979.     [55] = function()
  980.         local id = cByte()
  981.         --local dstate = cByte()
  982.         tpt.setdebug()
  983.     end,
  984.     --]]
  985.     --legacy heat mode, (1 byte state)
  986.     [56] = function()
  987.         local id = cByte()
  988.         tpt.heat(cByte()==1 and 0 or 1)--tpt.heat in v88 beta is reversed, remove switch later
  989.     end,
  990.     --water equal, (1 byte state)
  991.     [57] = function()
  992.         local id = cByte()
  993.         sim.waterEqualisation(cByte())
  994.     end,
  995.  
  996.     --grav mode, (1 byte state)
  997.     [58] = function()
  998.         local id = cByte()
  999.         local mode = cByte()
  1000.         sim.gravityMode(mode)
  1001.         cmodeText:reset(con.members[id].name.." set: Gravity: "..gravList[mode])
  1002.     end,
  1003.     --air mode, (1 byte state)
  1004.     [59] = function()
  1005.         local id = cByte()
  1006.         local mode=cByte()
  1007.         sim.airMode(mode)
  1008.         cmodeText:reset(con.members[id].name.." set: Air: "..airList[mode])
  1009.     end,
  1010.  
  1011.     --clear sparks (no args)
  1012.     [60] = function()
  1013.         local id = cByte()
  1014.         tpt.reset_spark()
  1015.     end,
  1016.     --clear pressure/vel (no args)
  1017.     [61] = function()
  1018.         local id = cByte()
  1019.         tpt.reset_velocity()
  1020.         tpt.set_pressure()
  1021.     end,
  1022.     --invert pressure (no args)
  1023.     [62] = function()
  1024.         local id = cByte()
  1025.         for x=0,152 do
  1026.             for y=0,95 do
  1027.                 sim.pressure(x,y,-sim.pressure(x,y))
  1028.             end
  1029.         end
  1030.     end,
  1031.     --Clearsim button (no args)
  1032.     [63] = function()
  1033.         local id = cByte()
  1034.         sim.clearSim()
  1035.         L.lastSave=nil
  1036.         infoText:reset(con.members[id].name.." cleared the screen")
  1037.     end,
  1038.     --Full graphics view mode (for manual changes in display menu) (3 bytes)
  1039.     [64] = function()
  1040.         local id = cByte()
  1041.         local disM,renM,colM = cByte(),cByte(),cByte()
  1042.         ren.displayModes({disM})
  1043.         local t,i={},1
  1044.         while i<=32 do
  1045.             if bit.band(renM,i)>0 then table.insert(t,renModes[i]) end
  1046.             i=i*2
  1047.         end
  1048.         ren.renderModes(t)
  1049.         ren.colorMode(colM)
  1050.     end,
  1051.     --Selected deco colour (4 bytes)
  1052.     [65] = function()
  1053.         local id = cByte()
  1054.         con.members[id].dcolour = {cByte(),cByte(),cByte(),cByte()}
  1055.     end,
  1056.     --Recieve a stamp, with location (6 bytes location(3),size(3))
  1057.     [66] = function()
  1058.         local id = cByte()
  1059.         local b1,b2,b3=cByte(),cByte(),cByte()
  1060.         local x,y =((b1*16)+math.floor(b2/16)),((b2%16)*256)+b3
  1061.         local d = cByte()*65536+cByte()*256+cByte()
  1062.         loadStamp(d,x,y,false)
  1063.         infoText:reset("Stamp from "..con.members[id].name)
  1064.     end,
  1065.     --Clear an area, helper for cut (6 bytes, start(3), end(3))
  1066.     [67] = function()
  1067.         local id = cByte()
  1068.         local b1,b2,b3,b4,b5,b6=cByte(),cByte(),cByte(),cByte(),cByte(),cByte()
  1069.         local x1,y1 =((b1*16)+math.floor(b2/16)),((b2%16)*256)+b3
  1070.         local x2,y2 =((b4*16)+math.floor(b5/16)),((b5%16)*256)+b6
  1071.         --clear walls and parts
  1072.         createBoxAny(x1,y1,x2,y2,280)
  1073.         createBoxAny(x1,y1,x2,y2,0)
  1074.     end,
  1075.     --Edge mode (1 byte state)
  1076.     [68] = function()
  1077.         local id = cByte()
  1078.         sim.edgeMode(cByte())
  1079.     end,
  1080.     --Load a save ID (3 bytes ID)
  1081.     [69] = function()
  1082.         local id = cByte()
  1083.         local saveID = cByte()*65536+cByte()*256+cByte()
  1084.         L.lastSave=saveID
  1085.         sim.loadSave(saveID,1)
  1086.         L.browseMode=3
  1087.     end,
  1088.     --Reload sim(from a stamp right now, no args)
  1089.     [70] = function()
  1090.         local id = cByte()
  1091.         sim.clearSim()
  1092.         if not sim.loadStamp("stamps/tmp.stm",0,0) then
  1093.             infoText:reset("Error reloading save from "..con.members[id].name)
  1094.         end
  1095.     end,
  1096.     --A request to sync a player, from server, send screen, and various settings
  1097.     [128] = function()
  1098.         local id = cByte()
  1099.         conSend(130,string.char(id,49,tpt.set_pause()))
  1100.         local stampName,fullName = saveStamp(0,0,611,383)
  1101.         local f = assert(io.open(fullName,"rb"))
  1102.         local s = f:read"*a"
  1103.         f:close()
  1104.         deleteStamp(stampName)
  1105.         local d = #s
  1106.         conSend(128,string.char(id,math.floor(d/65536),math.floor(d/256)%256,d%256)..s)
  1107.         conSend(130,string.char(id,53,tpt.ambient_heat()))
  1108.         conSend(130,string.char(id,54,tpt.newtonian_gravity()))
  1109.         conSend(130,string.char(id,56,tpt.heat()))
  1110.         conSend(130,string.char(id,57,sim.waterEqualisation()))
  1111.         conSend(130,string.char(id,58,sim.gravityMode()))
  1112.         conSend(130,string.char(id,59,sim.airMode()))
  1113.         conSend(130,string.char(id,68,sim.edgeMode()))
  1114.         conSend(64,string.char(unpack(getViewModes())))
  1115.         conSend(34,string.char(tpt.brushx,tpt.brushy))
  1116.     end,
  1117.     --Recieve sync stamp
  1118.     [129] = function()
  1119.         local d = cByte()*65536+cByte()*256+cByte()
  1120.         loadStamp(d,0,0,true)
  1121.     end,
  1122. }
  1123.  
  1124. local function connectThink()
  1125.     if not con.connected then return end
  1126.     if not con.socket then chatwindow:addline("Disconnected") con.connected=false return end
  1127.     --read all messages
  1128.     while 1 do
  1129.         local s,r = con.socket:receive(1)
  1130.         if s then
  1131.             local cmd = string.byte(s)
  1132.             --MANAGER_PRINT("GOT "..tostring(cmd))
  1133.             if dataCmds[cmd] then dataCmds[cmd]() else MANAGER_PRINT("TPTMP: Unknown protocol "..tostring(cmd),255,20,20) end
  1134.         else break end
  1135.     end
  1136.  
  1137.     --ping every minute
  1138.     if os.time()>con.pingTime then conSend(2) con.pingTime=os.time()+60 end
  1139. end
  1140. --Track if we have STKM2 out, for WASD key changes
  1141. elements.property(128,"Update",function() L.stick2=true end)
  1142.  
  1143. local function drawStuff()
  1144.     if con.members then
  1145.         for i,user in pairs(con.members) do
  1146.             local x,y = user.mousex,user.mousey
  1147.             local brx,bry=user.brushx,user.brushy
  1148.             local brush,drawBrush=user.brush,true
  1149.             gfx.drawText(x,y,("%s %dx%d"):format(user.name,brx,bry),0,255,0,192)
  1150.             if user.drawtype then
  1151.                 if user.drawtype==1 then
  1152.                     if user.alt then x,y = lineSnapCoords(user.pmx,user.pmy,x,y) end
  1153.                     tpt.drawline(user.pmx,user.pmy,x,y,0,255,0,128)
  1154.                 elseif user.drawtype==2 then
  1155.                     if user.alt then x,y = rectSnapCoords(user.pmx,user.pmy,x,y) end
  1156.                     local tpmx,tpmy = user.pmx,user.pmy
  1157.                     if tpmx>x then tpmx,x=x,tpmx end
  1158.                     if tpmy>y then tpmy,y=y,tpmy end
  1159.                     tpt.drawrect(tpmx,tpmy,x-tpmx,y-tpmy,0,255,0,128)
  1160.                     drawBrush=false
  1161.                 elseif user.drawtype==3 then
  1162.                     tpt.drawline(x,y,x+5,y,0,255,0,128)
  1163.                     tpt.drawline(x,y,x-5,y,0,255,0,128)
  1164.                     tpt.drawline(x,y,x,y+5,0,255,0,128)
  1165.                     tpt.drawline(x,y,x,y-5,0,255,0,128)
  1166.                     drawBrush=false
  1167.                 end
  1168.             end
  1169.             if drawBrush then
  1170.                 if brush==0 then
  1171.                     gfx.drawCircle(x,y,brx,bry,0,255,0,128)
  1172.                 elseif brush==1 then
  1173.                     gfx.drawRect(x-brx,y-bry,brx*2+1,bry*2+1,0,255,0,128)
  1174.                 elseif brush==2 then
  1175.                     gfx.drawLine(x-brx,y+bry,x,y-bry,0,255,0,128)
  1176.                     gfx.drawLine(x-brx,y+bry,x+brx,y+bry,0,255,0,128)
  1177.                     gfx.drawLine(x,y-bry,x+brx,y+bry,0,255,0,128)
  1178.                 end
  1179.             end
  1180.         end
  1181.     end
  1182.     for k,v in pairs(fadeText) do
  1183.         if v.ticks > 0 then
  1184.             local a = math.floor(255*(v.ticks/v.max))
  1185.             tpt.drawtext(v.x,v.y,v.text,v.r,v.g,v.b,a)
  1186.             v.ticks = v.ticks-1
  1187.         else if not v.keep then table.remove(fadeText,k) end
  1188.         end
  1189.     end
  1190. end
  1191.  
  1192. local function sendStuff()
  1193.     if not con.connected then return end
  1194.     --mouse position every frame, not exactly needed, might be better/more accurate from clicks
  1195.     local nmx,nmy = tpt.mousex,tpt.mousey
  1196.     if nmx<612 and nmy<384 then nmx,nmy = sim.adjustCoords(nmx,nmy) end
  1197.     if L.mousex~= nmx or L.mousey~= nmy then
  1198.         L.mousex,L.mousey = nmx,nmy
  1199.         local b1,b2,b3 = math.floor(L.mousex/16),((L.mousex%16)*16)+math.floor(L.mousey/256),(L.mousey%256)
  1200.         conSend(32,string.char(b1,b2,b3))
  1201.     end
  1202.     local nbx,nby = tpt.brushx,tpt.brushy
  1203.     if L.brushx~=nbx or L.brushy~=nby then
  1204.         L.brushx,L.brushy = nbx,nby
  1205.         conSend(34,string.char(L.brushx,L.brushy))
  1206.     end
  1207.     --check selected elements
  1208.     local nsell,nsela,nselr = elements[tpt.selectedl] or eleNameTable[tpt.selectedl],elements[tpt.selecteda] or eleNameTable[tpt.selecteda],elements[tpt.selectedr] or eleNameTable[tpt.selectedr]
  1209.     if L.sell~=nsell then
  1210.         L.sell=nsell
  1211.         conSend(37,string.char(math.floor(L.sell/256),L.sell%256))
  1212.     elseif L.sela~=nsela then
  1213.         L.sela=nsela
  1214.         conSend(37,string.char(math.floor(64 + L.sela/256),L.sela%256))
  1215.     elseif L.selr~=nselr then
  1216.         L.selr=nselr
  1217.         conSend(37,string.char(math.floor(128 + L.selr/256),L.selr%256))
  1218.     end
  1219.     local ncol = sim.decoColour()
  1220.     if L.dcolour~=ncol then
  1221.         L.dcolour=ncol
  1222.         conSend(65,string.char(math.floor(ncol/16777216),math.floor(ncol/65536)%256,math.floor(ncol/256)%256,ncol%256))
  1223.     end
  1224.  
  1225.     --Tell others to open this save ID, or send screen if opened local browser
  1226.     if jacobsmod and L.browseMode and L.browseMode > 3 then
  1227.         --hacky hack
  1228.         L.browseMode = L.browseMode - 3
  1229.     elseif L.browseMode==1 then
  1230.         --loaded online save
  1231.         local id=sim.getSaveID()
  1232.         if L.lastSave~=id then
  1233.             L.lastSave=id
  1234.             --save a backup for the reload button
  1235.             local stampName,fullName = saveStamp(0,0,611,383)
  1236.             os.remove("stamps/tmp.stm") os.rename(fullName,"stamps/tmp.stm")
  1237.             conSend(69,string.char(math.floor(id/65536),math.floor(id/256)%256,id%256))
  1238.             deleteStamp(stampName)
  1239.         end
  1240.         L.browseMode=nil
  1241.     elseif L.browseMode==2 then
  1242.         --loaded local save (should probably clear sim first instead?)
  1243.         L.sendScreen=true
  1244.         L.browseMode=nil
  1245.     elseif L.browseMode==3 and L.lastSave==sim.getSaveID() then
  1246.         L.browseMode=nil
  1247.         --save this as a stamp for reloading (unless an api function exists to do this)
  1248.         local stampName,fullName = saveStamp(0,0,611,383)
  1249.         os.remove("stamps/tmp.stm") os.rename(fullName,"stamps/tmp.stm")
  1250.         deleteStamp(stampName)
  1251.     end
  1252.  
  1253.     --Send screen (or an area for known size) for stamps
  1254.     if jacobsmod and L.sendScreen == 2 then
  1255.         L.sendScreen = true
  1256.     elseif L.sendScreen then
  1257.         local x,y,w,h = 0,0,611,383
  1258.         if L.smoved then
  1259.             local stm
  1260.             if L.copying then stm=L.lastCopy else stm=L.lastStamp end
  1261.             if L.rotate then stm.w,stm.h=stm.h,stm.w end
  1262.             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
  1263.             L.smoved=false
  1264.             L.copying=false
  1265.         end
  1266.         L.sendScreen=false
  1267.         local stampName,fullName = saveStamp(x,y,w,h)
  1268.         local f = assert(io.open(fullName,"rb"))
  1269.         local s = f:read"*a"
  1270.         f:close()
  1271.         deleteStamp(stampName)
  1272.         local d = #s
  1273.         local b1,b2,b3 = math.floor(x/16),((x%16)*16)+math.floor(y/256),(y%256)
  1274.         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)))
  1275.         conSend(66,string.char(b1,b2,b3,math.floor(d/65536),math.floor(d/256)%256,d%256)..s)
  1276.     end
  1277.  
  1278.     --Check if custom modes were changed
  1279.     if jacobsmod and L.checkRen == 2 then
  1280.         L.checkRen = true
  1281.     elseif L.checkRen then
  1282.         L.checkRen=false
  1283.         local t,send=getViewModes(),false
  1284.         for k,v in pairs(t) do
  1285.             if v~=L.pModes[k] then
  1286.                 send=true break
  1287.             end
  1288.         end
  1289.         if send then conSend(64,string.char(t[1],t[2],t[3])) end
  1290.     end
  1291.  
  1292.     --Send option menu settings
  1293.     if L.checkOpt then
  1294.         L.checkOpt=false
  1295.         conSend(56,string.char(tpt.heat()))
  1296.         conSend(53,string.char(tpt.ambient_heat()))
  1297.         conSend(54,string.char(tpt.newtonian_gravity()))
  1298.         conSend(57,string.char(sim.waterEqualisation()))
  1299.         conSend(58,string.char(sim.gravityMode()))
  1300.         conSend(59,string.char(sim.airMode()))
  1301.         conSend(68,string.char(sim.edgeMode()))
  1302.     end
  1303.  
  1304. end
  1305. local function updatePlayers()
  1306.     if con.members then
  1307.         for k,v in pairs(con.members) do
  1308.             playerMouseMove(k)
  1309.         end
  1310.     end
  1311.     --Keep last frame of stick2
  1312.     L.lastStick2=L.stick2
  1313.     L.stick2=false
  1314. end
  1315.  
  1316. local pressedKeys
  1317. local function step()
  1318.     if not L.chatHidden then chatwindow:draw() else showbutton:draw() end
  1319.     if hooks_enabled then
  1320.         if pressedKeys and pressedKeys["repeat"] < socket.gettime() then
  1321.             chatwindow:textprocess(pressedKeys["key"],pressedKeys["nkey"],pressedKeys["modifier"],pressedKeys["event"])
  1322.             pressedKeys["repeat"] = socket.gettime()+.05
  1323.         end
  1324.         drawStuff()
  1325.         sendStuff()
  1326.         if L.pauseNextFrame then L.pauseNextFrame=false tpt.set_pause(1) end
  1327.         connectThink()
  1328.         updatePlayers()
  1329.     end
  1330. end
  1331.  
  1332. --some button locations that emulate tpt, return false will disable button
  1333. local tpt_buttons = {
  1334.     ["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},
  1335.     ["rload"] = {x1=19, y1=408, x2=355, y2=422, firstClick = true, f=function() if L.lastSave then if L.ctrl then infoText:reset("If you re-opened the save, please type /sync") else conSend(70) end else infoText:reset("Reloading local saves is not synced currently. Type /sync") end end},
  1336.     ["clear"] = {x1=470, y1=408, x2=486, y2=422, f=function() conSend(63) L.lastSave=nil end},
  1337.     ["opts"] = {x1=581, y1=408, x2=595, y2=422, f=function() L.checkOpt=true end},
  1338.     ["disp"] = {x1=597, y1=408, x2=611, y2=422, f=function() L.checkRen=true L.pModes=getViewModes() end},
  1339.     ["pause"] = {x1=613, y1=408, x2=627, y2=422, firstClick = true, f=function() conSend(49,tpt.set_pause()==0 and "\1" or "\0") end},
  1340.     ["deco"] = {x1=613, y1=33, x2=627, y2=47, f=function() conSend(51,tpt.decorations_enable()==0 and "\1" or "\0") end},
  1341.     ["newt"] = {x1=613, y1=49, x2=627, y2=63, f=function() conSend(54,tpt.newtonian_gravity()==0 and "\1" or "\0") end},
  1342.     ["ambh"] = {x1=613, y1=65, x2=627, y2=79, f=function() conSend(53,tpt.ambient_heat()==0 and "\1" or "\0") end},
  1343. }
  1344. if jacobsmod then
  1345.     tpt_buttons["tab"] = {x1=613, y1=1, x2=627, y2=15, firstClick = true, f=function() L.tabs = not L.tabs end}
  1346.     tpt_buttons["opts"] = {x1=470, y1=408, x2=484, y2=422, f=function() L.checkOpt=true end}
  1347.     tpt_buttons["clear"] = {x1=486, y1=408, x2=502, y2=422, firstClick = true, f=function() conSend(63) L.lastSave=nil end}
  1348.     tpt_buttons["disp"] = {x1=597, y1=408, x2=611, y2=422, firstClick = true, f=function() L.checkRen=2 L.pModes=getViewModes() end}
  1349.     tpt_buttons["open"] = {x1=1, y1=408, x2=17, y2=422, firstClick = true, f=function() if not L.ctrl then L.browseMode=4 else L.browseMode=5 end L.lastSave=sim.getSaveID() end}
  1350. end
  1351.  
  1352. local function mouseclicky(mousex,mousey,button,event,wheel)
  1353.     if L.chatHidden then showbutton:process(mousex,mousey,button,event,wheel) if not hooks_enabled then return true end
  1354.     elseif chatwindow:process(mousex,mousey,button,event,wheel) then return false end
  1355.     if L.skipClick then L.skipClick=false return true end
  1356.     if mousex<612 and mousey<384 then mousex,mousey = sim.adjustCoords(mousex,mousey) end
  1357.     if L.stamp and button>0 and button~=2 then
  1358.         if event==1 and button==1 then
  1359.             --initial stamp coords
  1360.             L.stampx,L.stampy = mousex,mousey
  1361.         elseif event==2 then
  1362.             --stamp has been saved, make our own copy
  1363.             if button==1 then
  1364.                 --save stamp ourself for data, delete it
  1365.                 local sx,sy = mousex,mousey
  1366.                 if sx<L.stampx then L.stampx,sx=sx,L.stampx end
  1367.                 if sy<L.stampy then L.stampy,sy=sy,L.stampy end
  1368.                 --cheap cut hook to send a clear
  1369.                 if L.copying==1 then
  1370.                     --maybe this is ctrl+x? 67 is clear area
  1371.                     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)))
  1372.                 end
  1373.                 local w,h = sx-L.stampx,sy-L.stampy
  1374.                 local stampName,fullName = saveStamp(L.stampx,L.stampy,w,h)
  1375.                 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
  1376.                 w,h = sx-L.stampx, sy-L.stampy
  1377.                 local f = assert(io.open(fullName,"rb"))
  1378.                 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
  1379.                 f:close()
  1380.                 deleteStamp(stampName)
  1381.             end
  1382.             L.stamp=false
  1383.             L.copying=false
  1384.         end
  1385.         return true
  1386.     elseif L.placeStamp and button>0 and button~=2 then
  1387.         if event==2 then
  1388.             if button==1 then
  1389.                 local stm
  1390.                 if L.copying then stm=L.lastCopy else stm=L.lastStamp end
  1391.                 if stm then
  1392.                     if not stm.data then
  1393.                         --unknown stamp, send full screen on next step, how can we read last created stamp, timestamps on files?
  1394.                         L.sendScreen = (jacobsmod and 2 or true)
  1395.                     else
  1396.                         --send the stamp
  1397.                         if L.smoved then
  1398.                             --moved from arrows or rotate, send area next frame
  1399.                             L.placeStamp=false
  1400.                             L.sendScreen=true
  1401.                             return true
  1402.                         end
  1403.                         local sx,sy = mousex-math.floor(stm.w/2),mousey-math.floor((stm.h)/2)
  1404.                         if sx<0 then sx=0 end
  1405.                         if sy<0 then sy=0 end
  1406.                         if sx+stm.w>611 then sx=612-stm.w end
  1407.                         if sy+stm.h>383 then sy=384-stm.h end
  1408.                         local b1,b2,b3 = math.floor(sx/16),((sx%16)*16)+math.floor(sy/256),(sy%256)
  1409.                         local d = #stm.data
  1410.                         conSend(66,string.char(b1,b2,b3,math.floor(d/65536),math.floor(d/256)%256,d%256)..stm.data)
  1411.                     end
  1412.                 end
  1413.             end
  1414.             L.placeStamp=false
  1415.             L.copying=false
  1416.         end
  1417.         return true
  1418.     end
  1419.  
  1420.     local obut,oevnt = L.mButt,L.mEvent
  1421.     if button~=obut or event~=oevnt then
  1422.         L.mButt,L.mEvent = button,event
  1423.         --More accurate mouse from here (because this runs BEFORE step function, it would draw old coords)
  1424.         local b1,b2,b3 = math.floor(mousex/16),((mousex%16)*16)+math.floor(mousey/256),(mousey%256)
  1425.         conSend(32,string.char(b1,b2,b3))
  1426.         L.mousex,L.mousey = mousex,mousey
  1427.         conSend(33,string.char(L.mButt*16+L.mEvent))
  1428.     elseif L.mEvent==3 and (L.mousex~=mousex or L.mousey~=mousey) then
  1429.         local b1,b2,b3 = math.floor(mousex/16),((mousex%16)*16)+math.floor(mousey/256),(mousey%256)
  1430.         conSend(32,string.char(b1,b2,b3))
  1431.         L.mousex,L.mousey = mousex,mousey
  1432.     end
  1433.     --Temporary SPRK floodfill crash block
  1434.     if L.shift and L.ctrl and event == 1 and ((button == 1 and L.sell==15) or (button == 4 and L.selr==15) or (button == 2 and L.sela==15)) then
  1435.         return false
  1436.     end
  1437.  
  1438.     --Click inside button first
  1439.     if button==1 then
  1440.         if event==1 then
  1441.             if jacobsmod and (L.tabs or L.ctrl) and mousex>=613 and mousex<=627 and mousey >=17 and mousey<=143 then
  1442.                 L.sendScreen = 2
  1443.             end
  1444.             for k,v in pairs(tpt_buttons) do
  1445.                 if mousex>=v.x1 and mousex<=v.x2 and mousey>=v.y1 and mousey<=v.y2 then
  1446.                     if jacobsmod and tpt_buttons[k].firstClick then
  1447.                         return tpt_buttons[k].f()~=false
  1448.                     else
  1449.                         L.downInside = k
  1450.                     end
  1451.                     break
  1452.                 end
  1453.             end
  1454.         --Up inside the button we started with
  1455.         elseif event==2 and L.downInside then
  1456.             local butt = tpt_buttons[L.downInside]
  1457.             if (jacobsmod and not butt.firstClick) or (mousex>=butt.x1 and mousex<=butt.x2 and mousey>=butt.y1 and mousey<=butt.y2) then
  1458.                 L.downInside = nil
  1459.                 return butt.f()~=false
  1460.             end
  1461.         --Mouse hold, we MUST stay inside button or don't trigger on up
  1462.         elseif event==3 and L.downInside then
  1463.             local butt = tpt_buttons[L.downInside]
  1464.             if mousex<butt.x1 or mousex>butt.x2 or mousey<butt.y1 or mousey>butt.y2 then
  1465.                 L.downInside = nil
  1466.             end
  1467.         end
  1468.     end
  1469. end
  1470.  
  1471. local keypressfuncs = {
  1472.     --TAB
  1473.     [9] = function() conSend(35) end,
  1474.  
  1475.     --ESC
  1476.     [27] = function() if not L.chatHidden then L.chatHidden = true return false end end,
  1477.  
  1478.     --space, pause toggle
  1479.     [32] = function() conSend(49,tpt.set_pause()==0 and "\1" or "\0") end,
  1480.  
  1481.     --View modes 0-9
  1482.     [48] = function() conSend(48,"\10") end,
  1483.     [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,
  1484.     [50] = function() conSend(48,"\1") end,
  1485.     [51] = function() conSend(48,"\2") end,
  1486.     [52] = function() conSend(48,"\3") end,
  1487.     [53] = function() conSend(48,"\4") end,
  1488.     [54] = function() conSend(48,"\5") end,
  1489.     [55] = function() conSend(48,"\6") end,
  1490.     [56] = function() conSend(48,"\7") end,
  1491.     [57] = function() conSend(48,"\8") end,
  1492.  
  1493.     --replace mode, TODO: implement
  1494.     [59] = function() if con.connected then infoText:reset("Replace mode not supported currently") return false end end,
  1495.     [277] = function() if con.connected then infoText:reset("Replace mode not supported currently") return false end end,
  1496.  
  1497.     --= key, pressure/spark reset
  1498.     [61] = function() if L.ctrl then conSend(60) else conSend(61) end end,
  1499.  
  1500.     --b , deco, pauses sim
  1501.     [98] = function() if L.ctrl then conSend(51,tpt.decorations_enable()==0 and "\1" or "\0") else conSend(49,"\1") conSend(51,"\1") end end,
  1502.  
  1503.     --c , copy
  1504.     [99] = function() if L.ctrl then L.stamp=true L.copying=true end end,
  1505.  
  1506.     --d key, debug, api broken right now
  1507.     --[100] = function() conSend(55) end,
  1508.  
  1509.     --F , frame step
  1510.     [102] = function() if not jacobsmod or not L.ctrl then conSend(50) end end,
  1511.  
  1512.     --I , invert pressure
  1513.     [105] = function() conSend(62) end,
  1514.  
  1515.     --K , stamp menu, abort our known stamp, who knows what they picked, send full screen?
  1516.     [107] = function() L.lastStamp={data=nil} L.placeStamp=true end,
  1517.  
  1518.     --L , last Stamp
  1519.     [108] = function () if L.lastStamp then L.placeStamp=true end end,
  1520.  
  1521.     --N , newtonian gravity or new save
  1522.     [110] = 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,
  1523.  
  1524.     --R , for stamp rotate
  1525.     [114] = function() if L.placeStamp then L.smoved=true if L.shift then return end L.rotate=not L.rotate elseif jacobsmod and L.ctl then conSend(70) end end,
  1526.  
  1527.     --S, stamp
  1528.     [115] = function() if L.lastStick2 and not L.ctrl then return end L.stamp=true end,
  1529.  
  1530.     --T, tabs
  1531.     [116] = function() if jacobsmod then L.tabs = not L.tabs end end,
  1532.  
  1533.     --U, ambient heat toggle
  1534.     [117] = function() conSend(53,tpt.ambient_heat()==0 and "\1" or "\0") end,
  1535.  
  1536.     --V, paste the copystamp
  1537.     [118] = function() if L.ctrl and L.lastCopy then L.placeStamp=true L.copying=true end end,
  1538.  
  1539.     --X, cut a copystamp and clear
  1540.     [120] = function() if L.ctrl then L.stamp=true L.copying=1 end end,
  1541.  
  1542.     --W,Y (grav mode, air mode)
  1543.     [119] = function() if L.lastStick2 and not L.ctrl then return end conSend(58,string.char((sim.gravityMode()+1)%3)) return true end,
  1544.     [121] = function() conSend(59,string.char((sim.airMode()+1)%5)) return true end,
  1545.     --Z
  1546.     [122] = function() myZ=true L.skipClick=true end,
  1547.  
  1548.     --Arrows for stamp adjust
  1549.     [273] = function() if L.placeStamp then L.smoved=true end end,
  1550.     [274] = function() if L.placeStamp then L.smoved=true end end,
  1551.     [275] = function() if L.placeStamp then L.smoved=true end end,
  1552.     [276] = function() if L.placeStamp then L.smoved=true end end,
  1553.  
  1554.     --F5 , save reload
  1555.     [286] = function() if jacobs_mod then conSend(70) end end,
  1556.  
  1557.     --SHIFT,CTRL,ALT
  1558.     [304] = function() L.shift=true conSend(36,string.char(17)) end,
  1559.     [306] = function() L.ctrl=true conSend(36,string.char(1)) end,
  1560.     [308] = function() L.alt=true conSend(36,string.char(33)) end,
  1561. }
  1562. local keyunpressfuncs = {
  1563.     --Z
  1564.     [122] = function() myZ=false L.skipClick=false if L.alt then L.skipClick=true end end,
  1565.     --SHIFT,CTRL,ALT
  1566.     [304] = function() L.shift=false conSend(36,string.char(16)) end,
  1567.     [306] = function() L.ctrl=false conSend(36,string.char(0)) end,
  1568.     [308] = function() L.alt=false conSend(36,string.char(32)) end,
  1569. }
  1570. local function keyclicky(key,nkey,modifier,event)
  1571.     if chatwindow.inputbox.focus then
  1572.         if event == 1 and nkey~=27 then
  1573.             pressedKeys = {["repeat"] = socket.gettime()+.6, ["key"] = key, ["nkey"] = nkey, ["modifier"] = modifier, ["event"] = event}
  1574.         elseif event == 2 and pressedKeys and nkey == pressedKeys["nkey"] then
  1575.             pressedKeys = nil
  1576.         end
  1577.     end
  1578.     local check = chatwindow:textprocess(key,nkey,modifier,event)
  1579.     if type(check)=="boolean" then return not check end
  1580.     --MANAGER_PRINT(nkey)
  1581.     local ret
  1582.     if event==1 then
  1583.         if keypressfuncs[nkey] then
  1584.             ret = keypressfuncs[nkey]()
  1585.         end
  1586.     elseif event==2 then
  1587.         if keyunpressfuncs[nkey] then
  1588.             ret = keyunpressfuncs[nkey]()
  1589.         end
  1590.     end
  1591.     if ret~= nil then return ret end
  1592. end
  1593.  
  1594. function enableMultiplayer()
  1595.     tpt.register_keypress(keyclicky)
  1596.     chatwindow:addline("TPTMP/Feynman's Mod v0.6: Type '/connect' to join server.",200,200,200)
  1597.     hooks_enabled = true
  1598.     enableMultiplayer = nil
  1599.     debug.sethook(nil,"",0)
  1600. end
  1601. tpt.register_step(step)
  1602. tpt.register_mouseclick(mouseclicky)
  1603. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement