Advertisement
Guest User

Untitled

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