Advertisement
cracker64

TPTMP

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