· 6 years ago · May 03, 2020, 12:58 AM
1local sentInfos = 0
2
3local scr_x, scr_y = term.getSize()
4local keysDown = {}
5local game = {
6 p = {}, -- stores player information
7 pp = {}, -- stores other player information that doesn't need to be sent during netplay
8 you = 1, -- current player slot
9 instanceID = math.random(1, 2^31-1), -- random per-instance value to ensure skynet doesn't poopie
10 amountOfPlayers = 2, -- amount of players for the current game
11 running = true, -- if set to false, will quit the game
12 moveHoldDelay = 0.1, -- amount of time to hold left or right for it to keep moving that way
13 boardOverflow = 12, -- amount of space above the board that it can overflow
14 paused = false, -- whether or not game is paused
15 canPause = true, -- if false, cannot pause game (such as in online multiplayer)
16 inputDelay = 0.05, -- amount of time between each input
17 gameDelay = 0.05, -- amount of time between game ticks
18 minimumLockTimer = 0.4, -- shortest amount of time before a piece locks upon touching the ground
19 appearanceDelay = 0.15, -- amount of time to wait after placing a piece
20 config = {
21 TGMlock = false, -- replicate the piece locking from Tetris: The Grand Master
22 scrubMode = false, -- gives you nothing but I-pieces
23 },
24 control = { -- client's control scheme
25 moveLeft = keys.left, -- shift left
26 moveRight = keys.right, -- shift right
27 moveDown = keys.down, -- shift downwards
28 rotateLeft = keys.z, -- rotate counter-clockwise
29 rotateRight = keys.x, -- rotate clockwise
30 fastDrop = keys.up, -- instantly drop and place piece
31 hold = keys.leftShift, -- slot piece into hold buffer
32 quit = keys.q, -- fuck off
33 pause = keys.p
34 },
35 revControl = {}, -- a mirror of "control", but with the keys and values inverted
36 net = { -- all network-related values
37 isHost = true, -- the host holds all the cards
38 gameID = math.random(1, 2^31-1), -- per-game ID to prevent interference from other games
39 channel = 1294, -- modem or skynet channel
40 active = true, -- whether or not you're using modems at all
41 waitingForGame = true, -- if you're waiting for another player to start the game
42 modem = peripheral.find("modem"), -- modem transmit object
43 useSkynet = false, -- if true, uses Skynet instead of modems
44 skynet = nil, -- skynet transmit object
45 skynetURL = "https://github.com/LDDestroier/CC/raw/master/API/skynet.lua", -- exactly what it looks like
46 skynetPath = "/skynet.lua" -- location for Skynet API
47 },
48 timers = {},
49 timerNo = 1,
50}
51
52for k,v in pairs(game.control) do
53 game.revControl[v] = k
54end
55
56game.setPaletteColor = function(...)
57 if game.amountOfPlayers <= 1 then
58 term.setPaletteColor(...)
59 end
60end
61
62local getTime = function()
63 if os.epoch then
64 return os.epoch("utc")
65 else
66 return 24 * os.day() + os.time()
67 end
68end
69
70game.startTimer = function(duration)
71 game.timers[game.timerNo] = duration
72 game.timerNo = game.timerNo + 1
73 return game.timerNo - 1
74end
75
76game.cancelTimer = function(tID)
77 game.timers[tID or 0] = nil
78end
79
80game.alterTimer = function(tID, mod)
81 if game.timers[tID] then
82 game.timers[tID] = game.timers[tID] + mod
83 end
84end
85
86local tableCopy
87tableCopy = function(tbl)
88 local output = {}
89 for k,v in pairs(tbl) do
90 if type(v) == "table" then
91 output[k] = tableCopy(v)
92 else
93 output[k] = v
94 end
95 end
96 return output
97end
98
99-- sets up brown as colors.special, for palette swapping magic(k)
100local tColors = tableCopy(colors)
101
102tColors.white = 1
103tColors.brown = nil -- brown is now white
104tColors.special = 4096
105term.setPaletteColor(tColors.special, 0x353535)
106term.setPaletteColor(tColors.white, 0xf0f0f0)
107
108-- initializes and fixes up a board
109-- boards are 2D objects that can display perfectly square graphics
110local clearBoard = function(board, xpos, ypos, newXsize, newYsize, newBGcolor, topCull, clearContent)
111 board = board or {}
112 board.x = board.x or xpos or 1
113 board.y = board.y or ypos or 1
114 board.xSize = board.xSize or newXsize or 10
115 board.ySize = board.ySize or newYsize or 24 + game.boardOverflow
116 board.topCull = board.topCull or topCull or game.boardOverflow
117 board.BGcolor = board.BGcolor or newBGcolor or "f"
118 for y = 1, board.ySize do
119 board[y] = board[y] or {}
120 for x = 1, board.xSize do
121 -- explanation on each space:
122 -- {
123 -- boolean; if true, the space is solid
124 -- string; the hex color of the space
125 -- number; the countdown until the space is made non-solid (inactive if 0)
126 -- number; the countdown until the space is colored to board.BGcolor (inactive if 0)
127 -- }
128 if clearContent then
129 board[y][x] = {false, board.BGcolor, 0, 0}
130 else
131 board[y][x] = board[y][x] or {false, board.BGcolor, 0, 0}
132 end
133 end
134 end
135 return board
136end
137
138-- tetramino information
139-- don't tamper with this or I'll beat your ass so hard that war veterans would blush
140local minos = {
141 [1] = { -- I-piece
142 canRotate = true,
143 canTspin = false,
144 mainColor = "3",
145 shape = {
146 " ",
147 "3333",
148 " ",
149 " ",
150 }
151 },
152 [2] = { -- L-piece
153 canRotate = true,
154 canTspin = false,
155 mainColor = "1",
156 shape = {
157 " 1",
158 "111",
159 " ",
160 }
161 },
162 [3] = { -- J-piece
163 canRotate = true,
164 canTspin = false,
165 mainColor = "b",
166 shape = {
167 "b ",
168 "bbb",
169 " ",
170 }
171 },
172 [4] = { -- O-piece
173 canRotate = true,
174 canTspin = false,
175 mainColor = "4",
176 shape = {
177 "44",
178 "44",
179 }
180 },
181 [5] = { -- T-piece
182 canRotate = true,
183 canTspin = true,
184 mainColor = "a",
185 shape = {
186 " a ",
187 "aaa",
188 " ",
189 }
190 },
191 [6] = { -- Z-piece
192 canRotate = true,
193 canTspin = false,
194 mainColor = "e",
195 shape = {
196 "ee ",
197 " ee",
198 " ",
199 }
200 },
201 [7] = { -- S-piece
202 canRotate = true,
203 canTspin = false,
204 mainColor = "5",
205 shape = {
206 " 55",
207 "55 ",
208 " ",
209 }
210 },
211 ["gameover"] = { -- special "mino" for game over
212 canRotate = false,
213 shape = {
214 " ccc ccc c c ccccc ccc c c ccccc cccc",
215 "c c c cc cc c c c c c c c c",
216 "c c c cc cc c c c c c c c c",
217 "c cc ccccc c c c cccc c c c c cccc cccc",
218 "c c c c c c c c c c c c c c",
219 "c c c c c c c c c c c c c c",
220 "c c c c c c c c c c c c c c",
221 " ccc c c c c ccccc ccc c ccccc c c",
222 }
223 },
224 ["yousuck"] = {
225 canRotate = false,
226 shape = {
227 "c c ccc c c ccc c c ccc c c",
228 "c c c c c c c c c c c c c c ",
229 "c c c c c c c c c c c c ",
230 " c c c c c c ccc c c c cc ",
231 " c c c c c c c c c c c ",
232 " c c c c c c c c c c c ",
233 " c c c c c c c c c c c c c",
234 " c ccc ccc ccc ccc ccc c c",
235 }
236 },
237 ["eatmyass"] = {
238 canRotate = false,
239 shape = {
240 "ccccc ccc ccccc c c c c ccc ccc ccc ",
241 "c c c c cc cc c c c c c c c c",
242 "c c c c c c c c c c c c c c ",
243 "cccc ccccc c c c c c ccccc ccc ccc ",
244 "c c c c c c c c c c c",
245 "c c c c c c c c c c c",
246 "c c c c c c c c c c c c c",
247 "ccccc c c c c c c c c ccc ccc ",
248 }
249 },
250 ["nice"] = { -- nice
251 canRotate = false,
252 shape = {
253 " c ",
254 " ",
255 "c ccc c cccc cccc ",
256 "c c c c c c c c ",
257 "cc c c c c c ",
258 "c c c c cccccc ",
259 "c c c c c ",
260 "c c c c c ",
261 "c c c c c c c ",
262 "c c c cccc cccc c",
263 }
264 }
265}
266
267local images = {
268 -- to do...add images...
269}
270
271-- converts blit colors to colors api, and back
272local to_colors, to_blit = {
273 [' '] = 0,
274 ['0'] = 1,
275 ['1'] = 2,
276 ['2'] = 4,
277 ['3'] = 8,
278 ['4'] = 16,
279 ['5'] = 32,
280 ['6'] = 64,
281 ['7'] = 128,
282 ['8'] = 256,
283 ['9'] = 512,
284 ['a'] = 1024,
285 ['b'] = 2048,
286 ['c'] = 4096,
287 ['d'] = 8192,
288 ['e'] = 16384,
289 ['f'] = 32768,
290}, {}
291for k,v in pairs(to_colors) do
292 to_blit[v] = k
293end
294
295-- checks if (x, y) is a valid space on the board
296local doesSpaceExist = function(board, x, y)
297 return (x >= 1 and x <= board.xSize) and (y >= 1 and y <= board.ySize)
298end
299
300-- checks if (x, y) is being occupied by a tetramino (or if it's off-board)
301local isSpaceSolid = function(board, _x, _y)
302 local x, y = math.floor(_x), math.floor(_y)
303 if doesSpaceExist(board, x, y) then
304 return board[y][x][1]
305 else
306 return true
307 end
308end
309
310-- ticks down a space's timers, which can cause it to become non-solid or background-colored
311local ageSpace = function(board, _x, _y)
312 local x, y = math.floor(_x), math.floor(_y)
313 if doesSpaceExist(board, x, y) then
314 -- make space non-solid if timer elapses
315 if board[y][x][3] ~= 0 then
316 board[y][x][3] = board[y][x][3] - 1
317 if board[y][x][3] == 0 then
318 board[y][x][1] = false
319 end
320 end
321 -- color space board.BGcolor if timer elapses
322 if board[y][x][4] ~= 0 then
323 board[y][x][4] = board[y][x][4] - 1
324 if board[y][x][4] == 0 then
325 board[y][x][2] = board.BGcolor
326 end
327 end
328 end
329end
330
331local transmit = function(msg)
332 if game.net.active then
333 if game.net.useSkynet then
334 game.net.skynet.send(game.net.channel, msg)
335 else
336 game.net.modem.transmit(game.net.channel, game.net.channel, msg)
337 end
338 end
339end
340
341local sendInfo = function(command, doSendTime, playerNumber)
342 sentInfos = sentInfos + 1
343 if game.net.isHost then
344 transmit({
345 command = command,
346 gameID = game.net.gameID,
347 time = doSendTime and getTime(),
348 p = playerNumber and game.p[playerNumber] or game.p,
349 pNum = playerNumber,
350 you = game.you,
351 specialColor = {term.getPaletteColor(tColors.special)}
352 })
353 else
354 transmit({
355 command = command,
356 gameID = game.net.gameID,
357 time = doSendTime and getTime(),
358 pNum = playerNumber,
359 you = game.you,
360 control = game.p[game.you].control
361 })
362 end
363end
364
365-- generates a "mino" object, which can be drawn and manipulated on a board
366local makeNewMino = function(minoType, board, x, y, replaceColor)
367 local mino = tableCopy(minos[minoType])
368 if replaceColor then
369 for yy = 1, #mino.shape do
370 mino.shape[yy] = mino.shape[yy]:gsub("[^ ]", replaceColor)
371 end
372 end
373 -- what color the ghost mino will be
374 mino.ghostColor = 0x353535
375
376 mino.x = x
377 mino.y = y
378 mino.color = minos[minoType].mainColor or "c"
379 mino.didTspin = false -- if the player has done a T-spin with this piece
380 mino.lockBreaks = 16 -- anti-infinite measure
381 mino.waitingForLock = false
382 mino.board = board
383 mino.minoType = minoType
384 -- checks to see if the mino is currently clipping with a solid board space (with the offset values)
385 mino.checkCollision = function(xOffset, yOffset)
386 local cx, cy
387 for y = 1, #mino.shape do
388 for x = 1, #mino.shape[y] do
389 cx = mino.x + x + (xOffset or 0)
390 cy = mino.y + y + (yOffset or 0)
391 if mino.shape[y]:sub(x,x) ~= " " then
392 if isSpaceSolid(mino.board, cx, cy) then
393 return true
394 end
395 end
396 end
397 end
398 return false
399 end
400 -- rotates a mino, and kicks it off a wall if need be
401 mino.rotate = function(direction)
402 local output = {}
403 local oldShape = tableCopy(mino.shape)
404 local origX, origY = mino.x, mino.y
405 for y = 1, #mino.shape do
406 output[y] = {}
407 for x = 1, #mino.shape[y] do
408 if direction == 1 then
409 output[y][x] = mino.shape[#mino.shape - (x - 1)]:sub(y,y)
410 elseif direction == -1 then
411 output[y][x] = mino.shape[x]:sub(-y, -y)
412 else
413 error("invalid rotation direction (must be 1 or -1)")
414 end
415 end
416 output[y] = table.concat(output[y])
417 end
418 mino.shape = output
419 -- check T-spin
420 local checkTspin = function(mino)
421 if (mino.checkCollision(-1, 0) and mino.checkCollision(1, 0) and mino.checkCollision(0, -1)) then
422 mino.didTspin = true
423 return true
424 else
425 return false
426 end
427 end
428 -- try to kick off wall/floor
429 if mino.checkCollision(0, 0) then
430 -- try T-spin triple rotation
431 if not mino.checkCollision(-direction, 2) then
432 mino.y = mino.y + 2
433 mino.x = mino.x - direction
434 checkTspin(mino)
435 return true
436 end
437 -- kick off ceiling
438 for y = 1, #mino.shape do
439 for x = -1, 1 do
440 if not mino.checkCollision(x, y) then
441 mino.x = mino.x + x
442 mino.y = mino.y + y
443 --checkTspin(mino)
444 --return true
445 end
446 end
447 end
448 -- kick off floor
449 for y = 1, math.floor(#mino.shape / 2) do
450 if not mino.checkCollision(0, -y) then
451 mino.y = mino.y - y
452 checkTspin(mino)
453 return true
454 elseif not mino.checkCollision(-1, -y) then
455 mino.y = mino.y - y
456 mino.x = mino.x - 1
457 checkTspin(mino)
458 return true
459 elseif not mino.checkCollision(1, -y) then
460 mino.y = mino.y - y
461 mino.x = mino.x + 1
462 checkTspin(mino)
463 return true
464 end
465 end
466 -- kick off right wall
467 for x = 0, -math.floor(#mino.shape[1] / 2), -1 do
468 if not mino.checkCollision(x, 0) then
469 mino.x = mino.x + x
470 checkTspin(mino)
471 return true
472 end
473 -- try diagonal-down
474 if not mino.checkCollision(x, 1) then
475 mino.x = mino.x + x
476 mino.y = mino.y + 1
477 sendInfo("send_info", false)
478 checkTspin(mino)
479 return true
480 end
481 end
482 -- kick off left wall
483 for x = 0, math.floor(#mino.shape[1] / 2) do
484 if not mino.checkCollision(x, 0) then
485 mino.x = mino.x + x
486 checkTspin(mino)
487 return true
488 end
489 -- try diagonal-down
490 if not mino.checkCollision(x, 1) then
491 mino.x = mino.x + x
492 mino.y = mino.y + 1
493 sendInfo("send_info", false)
494 checkTspin(mino)
495 return true
496 end
497 end
498 mino.shape = oldShape
499 return false
500 else
501 return true
502 end
503 end
504 -- draws a mino onto a board; you'll still need to render the board, though
505 mino.draw = function(isSolid, colorReplace)
506 for y = 1, #mino.shape do
507 for x = 1, #mino.shape[y] do
508 if mino.shape[y]:sub(x,x) ~= " " then
509 if doesSpaceExist(mino.board, x + math.floor(mino.x), y + math.floor(mino.y)) then
510 mino.board[y + math.floor(mino.y)][x + math.floor(mino.x)] = {
511 isSolid or false,
512 colorReplace or mino.shape[y]:sub(x,x),
513 isSolid and 0 or 0,
514 isSolid and 0 or 1
515 }
516 end
517 end
518 end
519 end
520 end
521 -- moves a mino, making sure not to clip with solid board spaces
522 mino.move = function(x, y, doSlam)
523 if not mino.checkCollision(x, y) then
524 mino.x = mino.x + x
525 mino.y = mino.y + y
526 mino.didTspin = false
527 return true
528 elseif doSlam then
529 for sx = 0, x, math.abs(x) / x do
530 if mino.checkCollision(sx, 0) then
531 mino.x = mino.x + sx - math.abs(x) / x
532 break
533 end
534 if sx ~= 0 then
535 mino.didTspin = false
536 end
537 end
538 for sy = 0, math.ceil(y), math.abs(y) / y do
539 if mino.checkCollision(0, sy) then
540 mino.y = mino.y + sy - math.abs(y) / y
541 break
542 end
543 if sy ~= 0 then
544 mino.didTspin = false
545 end
546 end
547 else
548 return false
549 end
550 end
551
552 return mino
553end
554
555-- generates a random number, excluding those listed in the _psExclude table
556local pseudoRandom = function(randomPieces)
557 if game.config.scrubMode then
558 return 1
559 else
560 if #randomPieces == 0 then
561 for i = 1, #minos do
562 randomPieces[i] = i
563 end
564 end
565 local rand = math.random(1, #randomPieces)
566 local num = randomPieces[rand]
567 table.remove(randomPieces, rand)
568 return num
569 end
570end
571
572-- initialize players
573local initializePlayers = function(amountOfPlayers)
574 local newPlayer = function(xmod, ymod)
575 return {
576 xmod = xmod,
577 ymod = ymod,
578 control = {},
579 board = clearBoard({}, 2 + xmod, 2 + ymod, 10, nil, "f"),
580 holdBoard = clearBoard({}, 13 + xmod, 14 + ymod, 4, 3, "f", 0),
581 queueBoard = clearBoard({}, 13 + xmod, 2 + ymod, 4, 14, "f", 0),
582 randomPieces = {}, -- list of all minos for pseudo-random selection
583 flashingSpecial = false, -- if true, then this player is flashing the special color
584 }, {
585 frozen = false, -- if true, literally cannot move or act
586 hold = 0, -- current piece being held
587 canHold = true, -- whether or not player can hold (can't hold twice in a row)
588 queue = {}, -- current queue of minos to use
589 garbage = 0, -- amount of garbage you'll get after the next drop
590 lines = 0, -- amount of lines cleared, "points"
591 combo = 0, -- amount of consequative line clears
592 drawCombo = false, -- draw the combo message
593 lastLinesClear = 0, -- previous amount of simultaneous line clears (does not reset if miss)
594 level = 1, -- level determines speed of mino drop
595 fallSteps = 0.1, -- amount of spaces the mino will draw each drop
596 }
597 end
598
599 game.p = {}
600 game.pp = {}
601
602 for i = 1, (amountOfPlayers or 1) do
603 game.p[i], game.pp[i] = newPlayer((scr_x/2 - 8 * amountOfPlayers) + (i - 1) * 18, 0)
604 end
605
606 -- generates the initial queue of minos per player
607 for p = 1, #game.pp do
608 for i = 1, #minos do
609 game.pp[p].queue[i] = pseudoRandom(game.p[p].randomPieces)
610 end
611 end
612end
613
614-- actually renders a board to the screen
615local renderBoard = function(board, bx, by, doAgeSpaces, blankColor)
616 local char, line
617 local tY = board.y + (by or 0)
618 for y = (board.topCull or 0) + 1, board.ySize, 3 do
619 line = {("\143"):rep(board.xSize),"",""}
620 term.setCursorPos(board.x + (bx or 0), tY)
621 for x = 1, board.xSize do
622 line[2] = line[2] .. (blankColor or board[y][x][2])
623 if board[y + 1] then
624 line[3] = line[3] .. (blankColor or board[y + 1][x][2])
625 else
626 line[3] = line[3] .. board.BGcolor
627 end
628 end
629 term.blit(line[1], line[2], line[3])
630 line = {("\131"):rep(board.xSize),"",""}
631 term.setCursorPos(board.x + (bx or 0), tY + 1)
632 for x = 1, board.xSize do
633 if board[y + 2] then
634 line[2] = line[2] .. (blankColor or board[y + 1][x][2])
635 line[3] = line[3] .. (blankColor or board[y + 2][x][2])
636 elseif board[y + 1] then
637 line[2] = line[2] .. (blankColor or board[y + 1][x][2])
638 line[3] = line[3] .. board.BGcolor
639 else
640 line[2] = line[2] .. board.BGcolor
641 line[3] = line[3] .. board.BGcolor
642 end
643 end
644 term.blit(line[1], line[2], line[3])
645 tY = tY + 2
646 end
647 if doAgeSpaces then
648 for y = 1, board.ySize do
649 for x = 1, board.xSize do
650 ageSpace(board, x, y)
651 end
652 end
653 end
654end
655
656-- checks if you've done the one thing in tetris that you need to be doing
657local checkIfLineCleared = function(board, y)
658 for x = 1, board.xSize do
659 if not board[y][x][1] then
660 return false
661 end
662 end
663 return true
664end
665
666-- draws the score of a player, and clears the space where the combo text is drawn
667local drawScore = function(player, cPlayer)
668 if not cPlayer.drawCombo then
669 term.setCursorPos(2 + player.xmod, 18 + player.ymod)
670 term.setTextColor(tColors.white)
671 term.write((" "):rep(14))
672 term.setCursorPos(2 + player.xmod, 18 + player.ymod)
673 term.write("Lines: " .. cPlayer.lines)
674 term.setCursorPos(2 + player.xmod, 19 + player.ymod)
675 term.write((" "):rep(14))
676 end
677end
678
679local drawLevel = function(player, cPlayer)
680 term.setCursorPos(13 + player.xmod, 17 + player.ymod)
681 term.write("Lv" .. cPlayer.level .. " ")
682end
683
684-- draws the player's simultaneous line clear after clearing one or more lines
685-- also tells the player's combo, which is nice
686local drawComboMessage = function(player, cPlayer, lines, didTspin)
687 local msgs = {
688 "SINGLE",
689 "DOUBLE",
690 "TRIPLE",
691 "TETRIS"
692 }
693 if not msgs[lines] then
694 return
695 end
696 term.setCursorPos(player.xmod + 2, player.ymod + 18)
697 term.setTextColor(tColors.white)
698 term.write((" "):rep(16))
699 term.setCursorPos(player.xmod + 2, player.ymod + 18)
700 if didTspin then
701 term.write("T-SPIN ")
702 else
703 if lines == cPlayer.lastLinesCleared then
704 if lines == 3 then
705 term.write("OH BABY A ")
706 else
707 term.write("ANOTHER ")
708 end
709 end
710 end
711 term.write(msgs[lines])
712 if cPlayer.combo >= 2 then
713 term.setCursorPos(player.xmod + 2, player.ymod + 19)
714 term.setTextColor(tColors.white)
715 term.write((" "):rep(16))
716 term.setCursorPos(player.xmod + 2, player.ymod + 19)
717 if lines == 4 and cPlayer.combo == 3 then
718 term.write("HOLY SHIT!")
719 elseif lines == 4 and cPlayer.combo > 3 then
720 term.write("ALRIGHT JACKASS")
721 else
722 term.write(cPlayer.combo .. "x COMBO")
723 end
724 end
725
726end
727
728-- god damn it you've fucked up
729local gameOver = function(player, cPlayer)
730 local mino
731 local waitTime
732 if cPlayer.lines == 0 then
733 mino = makeNewMino("eatmyass", player.board, 12, 3 + game.boardOverflow)
734 waitTime = 70
735 elseif cPlayer.lines <= 5 then
736 mino = makeNewMino("yousuck", player.board, 12, 3 + game.boardOverflow)
737 waitTime = 60
738 elseif cPlayer.lines == 69 or cPlayer.lines == 690 then
739 mino = makeNewMino("nice", player.board, 12, 3 + game.boardOverflow)
740 waitTime = 50
741 else
742 mino = makeNewMino("gameover", player.board, 12, 3 + game.boardOverflow)
743 waitTime = 70
744 end
745 local color = 0
746 for i = 1, waitTime do
747 mino.x = mino.x - 1
748 mino.draw()
749 sendInfo("send_info", false)
750 renderBoard(player.board, 0, 0, true)
751 for i = 1, 20 do
752 color = color + 0.01
753 game.setPaletteColor(4096, math.sin(color) / 2 + 0.5, math.sin(color) / 2, math.sin(color) / 2)
754 end
755 sleep(0.1)
756 end
757 return
758end
759
760-- used when darkening just-placed pieces
761local normalPalettes = {}
762local darkerPalettes = {}
763for i = 1, 16 do
764 normalPalettes[("0123456789abcdef"):sub(i,i)] = {term.getPaletteColor(2 ^ (i - 1))}
765end
766local genDarkerPalettes = function(mult)
767 for i = 1, 16 do
768 darkerPalettes[("0123456789abcdef"):sub(i,i)] = {
769 normalPalettes[("0123456789abcdef"):sub(i,i)][1] * (mult or 0.6),
770 normalPalettes[("0123456789abcdef"):sub(i,i)][2] * (mult or 0.6),
771 normalPalettes[("0123456789abcdef"):sub(i,i)][3] * (mult or 0.6),
772 }
773 end
774end
775genDarkerPalettes()
776
777-- calculates the amount of garbage to send
778local calculateGarbage = function(lines, combo, backToBack, didTspin)
779 local output = 0
780 local clearTbl = {}
781 if didTspin then
782 clearTbl = {
783 2,
784 4,
785 6,
786 8,
787 10,
788 12,
789 14,
790 }
791 else
792 clearTbl = {
793 0,
794 1,
795 2,
796 4,
797 6,
798 8,
799 10
800 }
801 end
802 return (clearTbl[lines] or 0) + backToBack + math.max(0, combo - 2)
803end
804
805-- actually give a player some garbage
806local doleOutGarbage = function(player, cPlayer, amount)
807 local board = player.board
808 local gx = math.random(1, board.xSize)
809 local repeatProbability = 75 -- percent probability that garbage will leave the same hole open
810 for i = 1, amount do
811 table.remove(player.board, 1)
812 player.board[board.ySize] = {}
813 for x = 1, board.xSize do
814 if x ~= gx then
815 player.board[board.ySize][x] = {true, "8", 0, 0}
816 else
817 player.board[board.ySize][x] = {false, board.BGcolor, 0, 0}
818 end
819 end
820 if math.random(0, 100) > repeatProbability then
821 gx = math.random(1, board.xSize)
822 end
823 end
824 cPlayer.garbage = 0
825end
826
827local ldPalettes = function(light)
828 if light then
829 for k,v in pairs(normalPalettes) do
830 if k == "c" then
831 --term.setPaletteColor(to_colors[k], 0xf0f0f0)
832 else
833 game.setPaletteColor(to_colors[k], table.unpack(v))
834 end
835 end
836 else
837 for i = 1, 0.3, -0.1 do
838 genDarkerPalettes(i)
839 for k,v in pairs(darkerPalettes) do
840 if k == "c" then
841 --game.setPaletteColor(to_colors[k], 0xf0f0f0)
842 else
843 game.setPaletteColor(to_colors[k], table.unpack(v))
844 end
845 end
846 sleep(0.05)
847 end
848 end
849 genDarkerPalettes()
850end
851
852-- initiates a game as a specific player (takes a number)
853local startGame = function(playerNumber)
854
855 local mino, ghostMino
856 local dropTimer, inputTimer, lockTimer, tickTimer, comboTimer
857 local evt, board, player, cPlayer, control
858 local finished -- whether or not a mino is done being placed
859 local clearedLines = {} -- used when calculating cleared lines
860
861 if game.net.isHost then
862
863 player = game.p[playerNumber]
864 cPlayer = game.pp[playerNumber]
865 board = player.board
866 control = player.control
867
868 local draw = function(isSolid, doSendInfo)
869 local canChangeSpecial = true
870 for k,v in pairs(game.p) do
871 if v.flashingSpecial then
872 canChangeSpecial = false
873 break
874 end
875 end
876 if canChangeSpecial then
877 game.setPaletteColor(4096, mino.ghostColor)
878 end
879 ghostMino.x = mino.x
880 ghostMino.y = mino.y
881 ghostMino.move(0, board.ySize, true)
882 ghostMino.draw(false)
883 mino.draw(isSolid, ageSpaces)
884 if doSendInfo then
885 sendInfo("send_info", false, playerNumber)
886 end
887 renderBoard(board, 0, 0, true)
888 renderBoard(player.queueBoard, 0, 0, false)
889 end
890
891 local currentMinoType
892 local takeFromQueue = true
893
894 local interpretInput = function()
895 finished = false
896 game.cancelTimer(inputTimer)
897 inputTimer = game.startTimer(game.inputDelay)
898
899 if control.quit == 1 then
900 finished = true
901 game.running = false
902 game.paused = false
903 ldPalettes(true)
904 sendInfo("quit_game", false)
905 return
906 end
907
908 if game.paused then
909 if control.pause == 1 then
910 ldPalettes(true)
911 game.paused = false
912 end
913 else
914 if control.pause == 1 then
915 ldPalettes(false)
916 game.paused = true
917 end
918 if not cPlayer.frozen then
919 if control.moveLeft == 1 or (control.moveLeft or 0) > 1 + game.moveHoldDelay then
920 if mino.move(-1, 0) then
921 game.cancelTimer(lockTimer or 0)
922 mino.waitingForLock = false
923 draw(nil, true)
924 end
925 end
926 if control.moveRight == 1 or (control.moveRight or 0) >= 1 + game.moveHoldDelay then
927 if mino.move(1, 0) then
928 game.cancelTimer(lockTimer or 0)
929 mino.waitingForLock = false
930 draw(nil, true)
931 end
932 end
933 if control.moveDown then
934 game.cancelTimer(lockTimer or 0)
935 mino.waitingForLock = false
936 if mino.move(0, 1) then
937 draw(nil, true)
938 else
939 if game.config.TGMlock then
940 draw(true, true)
941 cPlayer.canHold = true
942 finished = true
943 else
944 if mino.waitingForLock then
945 game.alterTimer(lockTimer, -0.1)
946 else
947 mino.lockBreaks = mino.lockBreaks - 1
948 lockTimer = game.startTimer(math.max(0.2 / cPlayer.fallSteps, game.minimumLockTimer))
949 mino.waitingForLock = true
950 end
951 end
952 end
953 end
954 if control.rotateLeft == 1 then
955 if mino.rotate(-1) then
956 ghostMino.y = mino.y
957 ghostMino.rotate(-1)
958 game.cancelTimer(lockTimer or 0)
959 mino.waitingForLock = false
960 draw(nil, true)
961 end
962 end
963 if control.rotateRight == 1 then
964 if mino.rotate(1) then
965 ghostMino.y = mino.y
966 ghostMino.rotate(1)
967 game.cancelTimer(lockTimer or 0)
968 mino.waitingForLock = false
969 draw(nil, true)
970 end
971 end
972 if control.hold == 1 then
973 if cPlayer.canHold then
974 if cPlayer.hold == 0 then
975 takeFromQueue = true
976 else
977 takeFromQueue = false
978 end
979 cPlayer.hold, currentMinoType = currentMinoType, cPlayer.hold
980 cPlayer.canHold = false
981 player.holdBoard = clearBoard(player.holdBoard, nil, nil, nil, nil, nil, nil, true)
982 makeNewMino(
983 cPlayer.hold,
984 player.holdBoard,
985 #minos[cPlayer.hold].shape[1] == 2 and 1 or 0,
986 0
987 ).draw(false)
988 sendInfo("send_info", false, playerNumber)
989 renderBoard(player.holdBoard, 0, 0, false)
990 finished = true
991 cPlayer.didHold = true
992 end
993 end
994 if control.fastDrop == 1 then
995 mino.move(0, board.ySize, true)
996 if game.config.TGMlock then
997 draw(false)
998 else
999 draw(true, true)
1000 cPlayer.canHold = true
1001 finished = true
1002 end
1003 end
1004 end
1005 end
1006 for k,v in pairs(player.control) do
1007 player.control[k] = v + game.inputDelay
1008 end
1009 end
1010
1011 renderBoard(player.holdBoard, 0, 0, true)
1012
1013 while game.running do
1014
1015 cPlayer.level = math.ceil((1 + cPlayer.lines) / 10)
1016 cPlayer.fallSteps = 0.075 * (1.33 ^ cPlayer.level)
1017 cPlayer.didHold = false
1018
1019 if takeFromQueue then
1020 currentMinoType = cPlayer.queue[1]
1021 end
1022
1023 mino = makeNewMino(
1024 currentMinoType,
1025 board,
1026 math.floor(board.xSize / 2) - 2,
1027 game.boardOverflow
1028 )
1029 cPlayer.mino = mino
1030
1031 ghostMino = makeNewMino(
1032 currentMinoType,
1033 board,
1034 math.floor(board.xSize / 2) - 2,
1035 game.boardOverflow,
1036 "c"
1037 )
1038 cPlayer.ghostMino = ghostMino
1039
1040 if takeFromQueue then
1041 table.remove(cPlayer.queue, 1)
1042 table.insert(cPlayer.queue, pseudoRandom(player.randomPieces))
1043 end
1044
1045 -- draw queue
1046 player.queueBoard = clearBoard(player.queueBoard, nil, nil, nil, nil, nil, nil, true)
1047 for i = 1, math.min(#cPlayer.queue, 4) do
1048 local m = makeNewMino(
1049 cPlayer.queue[i],
1050 player.queueBoard,
1051 #minos[cPlayer.queue[i]].shape[1] == 2 and 1 or 0,
1052 1 + (3 * (i - 1)) + (i > 1 and 2 or 0)
1053 )
1054 m.draw()
1055 end
1056
1057 -- draw held piece
1058 if cPlayer.hold ~= 0 then
1059 local m = makeNewMino(
1060 cPlayer.hold,
1061 player.holdBoard,
1062 #minos[cPlayer.hold].shape[1] == 2 and 1 or 0,
1063 0
1064 )
1065 end
1066
1067 takeFromQueue = true
1068
1069 drawScore(player, cPlayer)
1070 drawLevel(player, cPlayer)
1071
1072 term.setCursorPos(13 + player.xmod, 13 + player.ymod)
1073 term.write("HOLD")
1074
1075 -- check to see if you've topped out
1076 if mino.checkCollision() then
1077 for k,v in pairs(game.pp) do
1078 game.pp[k].frozen = true
1079 end
1080 sendInfo("game_over", false)
1081 gameOver(player, cPlayer)
1082 sendInfo("quit_game", false)
1083 return
1084 end
1085
1086 mino.draw()
1087 ghostMino.move(0, board.ySize, true)
1088 ghostMino.draw()
1089 sendInfo("send_info", false, playerNumber)
1090
1091 draw()
1092
1093
1094 game.cancelTimer(dropTimer)
1095 game.cancelTimer(lockTimer)
1096
1097 dropTimer = game.startTimer(0)
1098 inputTimer = game.startTimer(game.inputDelay)
1099 game.cancelTimer(lockTimer or 0)
1100
1101 tickTimer = os.startTimer(game.inputDelay)
1102
1103 -- drop a piece
1104 while game.running do
1105
1106 evt = {os.pullEvent()}
1107
1108 control = game.p[playerNumber].control
1109
1110 -- tick down internal game timer system
1111 if evt[1] == "timer" then
1112 if evt[2] == tickTimer then
1113 --local delKeys = {}
1114 for k,v in pairs(game.timers) do
1115 game.timers[k] = v - 0.05
1116 if v <= 0 then
1117 os.queueEvent("gameTimer", k)
1118 game.timers[k] = nil
1119 end
1120 end
1121 tickTimer = os.startTimer(game.inputDelay)
1122 elseif evt[2] == comboTimer then
1123 cPlayer.drawCombo = false
1124 drawScore(player, cPlayer)
1125 end
1126 end
1127
1128 if player.paused then
1129 if evt[1] == "gameTimer" then
1130 if control.pause == 1 then
1131 game.paused = false
1132 end
1133 end
1134 else
1135 if evt[1] == "key" and evt[3] == false then
1136
1137 interpretInput()
1138 if finished then
1139 break
1140 end
1141
1142 elseif evt[1] == "gameTimer" then
1143
1144 if evt[2] == inputTimer then
1145
1146 interpretInput()
1147 if finished then
1148 break
1149 end
1150
1151 elseif evt[2] == dropTimer then
1152 dropTimer = game.startTimer(0)
1153 if not game.paused then
1154 if not cPlayer.frozen then
1155 mino.oldY = mino.y
1156 if mino.checkCollision(0, 1) then
1157 if mino.lockBreaks == 0 then
1158 draw(true)
1159 cPlayer.canHold = true
1160 break
1161 elseif not mino.waitingForLock then
1162 mino.lockBreaks = mino.lockBreaks - 1
1163 lockTimer = game.startTimer(math.max(0.2 / cPlayer.fallSteps, game.minimumLockTimer))
1164 mino.waitingForLock = true
1165 end
1166 else
1167 mino.move(0, cPlayer.fallSteps, true)
1168 if math.floor(mino.oldY) ~= math.floor(mino.y) then
1169 draw(false, true)
1170 --sendInfo("send_info", false)
1171 end
1172 draw()
1173 end
1174 end
1175 end
1176 elseif evt[2] == lockTimer then
1177 if not game.paused then
1178 cPlayer.canHold = true
1179 draw(true)
1180 break
1181 end
1182 end
1183 end
1184 end
1185 end
1186
1187 clearedLines = {}
1188 for y = 1, board.ySize do
1189 if checkIfLineCleared(board, y) then
1190 table.insert(clearedLines, y)
1191 end
1192 end
1193 if #clearedLines == 0 then
1194 if cPlayer.canHold then
1195 cPlayer.combo = 0
1196 end
1197 else
1198 cPlayer.combo = cPlayer.combo + 1
1199 cPlayer.lines = cPlayer.lines + #clearedLines
1200 cPlayer.drawCombo = true
1201 os.cancelTimer(comboTimer or 0)
1202 comboTimer = os.startTimer(2)
1203 if cPlayer.lastLinesCleared == #clearedLines and #clearedLines >= 3 then
1204 player.backToBack = player.backToBack + 1
1205 else
1206 player.backToBack = 0
1207 end
1208
1209 drawComboMessage(player, cPlayer, #clearedLines, mino.didTspin)
1210
1211 cPlayer.lastLinesCleared = #clearedLines
1212
1213 -- give the other fucktard(s) some garbage
1214 cPlayer.garbage = cPlayer.garbage - calculateGarbage(#clearedLines, cPlayer.combo, player.backToBack, mino.didTspin) -- calculate T-spin later
1215 if cPlayer.garbage < 0 then
1216 for e, enemy in pairs(game.pp) do
1217 if e ~= playerNumber then
1218 enemy.garbage = enemy.garbage - cPlayer.garbage
1219 end
1220 end
1221 end
1222 cPlayer.garbage = math.max(0, cPlayer.garbage)
1223
1224 for l = 1, #clearedLines do
1225 for x = 1, board.xSize do
1226 board[clearedLines[l]][x][2] = "c"
1227 end
1228 end
1229 -- make the other network player see the flash
1230 sendInfo("flash_special", false)
1231 player.flashingSpecial = true
1232 renderBoard(board, 0, 0, true)
1233 for i = 1, 0, -0.12 do
1234 game.setPaletteColor(4096, i,i,i)
1235 sleep(0.05)
1236 end
1237 for i = #clearedLines, 1, -1 do
1238 table.remove(board, clearedLines[i])
1239 end
1240 for i = 1, #clearedLines do
1241 table.insert(board, 1, false)
1242 end
1243 board = clearBoard(board)
1244 player.flashingSpecial = false
1245 end
1246
1247 -- take some garbage for yourself
1248
1249 if cPlayer.garbage > 0 then
1250 doleOutGarbage(player, cPlayer, cPlayer.garbage)
1251 end
1252
1253 if #clearedLines == 0 and game.running and not cPlayer.didHold then
1254 mino.draw(false, "c")
1255 game.setPaletteColor(colors.brown, table.unpack(darkerPalettes[mino.color]))
1256 renderBoard(board, 0, 0, true)
1257 sleep(game.appearanceDelay)
1258 mino.draw(true)
1259 renderBoard(board, 0, 0, false)
1260 end
1261 end
1262 else
1263 -- if you're a client, take in all that board info and just fukkin draw it
1264
1265 inputTimer = os.startTimer(game.inputDelay)
1266
1267 local timeoutTimer = os.startTimer(3)
1268
1269 while game.running do
1270 evt = {os.pullEvent()}
1271 if evt[1] == "new_player_info" then
1272 player = game.p[game.you]
1273 for k,v in pairs(game.p) do
1274 renderBoard(v.board, 0, 0, false)
1275 renderBoard(v.holdBoard, 0, 0, false)
1276 renderBoard(v.queueBoard, 0, 0, false)
1277 drawScore(v, game.pp[k])
1278 drawLevel(v, game.pp[k])
1279 term.setCursorPos(13 + v.xmod, 13 + v.ymod)
1280 term.write("HOLD")
1281 end
1282 os.cancelTimer(timeoutTimer or 0)
1283 timeoutTimer = os.startTimer(3)
1284 elseif evt[1] == "timer" then
1285 if evt[2] == inputTimer then
1286 os.cancelTimer(inputTimer or 0)
1287 inputTimer = os.startTimer(game.inputDelay)
1288 if player then
1289 for k,v in pairs(player.control) do
1290 player.control[k] = v + game.inputDelay
1291 end
1292 end
1293 elseif evt[2] == timeoutTimer then
1294 return
1295 end
1296 end
1297 end
1298
1299 end
1300end
1301
1302-- records all key input
1303local getInput = function()
1304 local evt
1305 while true do
1306 evt = {os.pullEvent()}
1307 if evt[1] == "key" and evt[3] == false then
1308 keysDown[evt[2]] = 1
1309 elseif evt[1] == "key_up" then
1310 keysDown[evt[2]] = nil
1311 end
1312 if (evt[1] == "key" and evt[3] == false) or (evt[1] == "key_up") then
1313 if game.revControl[evt[2]] then
1314 game.p[game.you].control[game.revControl[evt[2]]] = keysDown[evt[2]]
1315 if not game.net.isHost then
1316 sendInfo("send_info", false)
1317 end
1318 end
1319 end
1320 end
1321end
1322
1323local cTime
1324local networking = function()
1325 local evt, side, channel, repchannel, msg, distance
1326 local currentPlayers = 1
1327 while true do
1328 if game.net.useSkynet then
1329 evt, channel, msg = os.pullEvent("skynet_message")
1330 else
1331 evt, side, channel, repchannel, msg, distance = os.pullEvent("modem_message")
1332 end
1333 if channel == game.net.channel and type(msg) == "table" then
1334 if game.net.waitingForGame then
1335 if type(msg.time) == "number" and msg.command == "find_game" then
1336 if msg.instanceID ~= game.instanceID then
1337 if msg.time < cTime then
1338 game.net.isHost = false
1339 game.you = 2
1340 game.net.gameID = msg.gameID
1341 else
1342 game.net.isHost = true
1343 end
1344
1345 transmit({
1346 gameID = game.net.gameID,
1347 time = cTime,
1348 command = "find_game",
1349 instanceID = game.instanceID
1350 })
1351 game.net.waitingForGame = false
1352 os.queueEvent("new_game", game.net.gameID)
1353 return game.net.gameID
1354 end
1355 end
1356 else
1357 if msg.gameID == game.net.gameID then
1358
1359 if game.net.isHost then
1360 if type(msg.control) == "table" then
1361 if type(msg.you) == "number" and msg.you ~= game.you then
1362 game.p[msg.you].control = msg.control
1363 for y = 1, game.p[msg.you].board.ySize do
1364 for x = 1, game.p[msg.you].board.xSize do
1365 ageSpace(game.p[msg.you].board, x, y)
1366 end
1367 end
1368 end
1369 end
1370 else
1371 if type(msg.you) == "number" and msg.you ~= game.you then
1372 if type(msg.p) == "table" then
1373 if msg.pNum then
1374 for k,v in pairs(msg.p) do
1375 if k ~= "control" then
1376 game.p[msg.pNum][k] = v
1377 end
1378 end
1379 else
1380 game.p = msg.p
1381 end
1382 if msg.specialColor then
1383 game.setPaletteColor(tColors.special, table.unpack(msg.specialColor))
1384 end
1385 os.queueEvent("new_player_info", msg.p)
1386 end
1387 if msg.command == "quit_game" then
1388 return
1389 end
1390 if msg.command == "flash_special" then
1391 for i = 1, 0, -0.12 do
1392 game.setPaletteColor(4096, i,i,i)
1393 renderBoard(game.p[msg.you].board, 0, 0, true)
1394 sleep(0.05)
1395 end
1396 end
1397 end
1398 end
1399
1400 end
1401 end
1402 end
1403 end
1404end
1405
1406local cwrite = function(text, y, xdiff, wordPosCheck)
1407 wordPosCheck = wordPosCheck or #text
1408 term.setCursorPos(math.floor(scr_x / 2 - math.floor(0.5 + #text + (xdiff or 0)) / 2), y or (scr_y - 2))
1409 term.write(text)
1410 return (scr_x / 2) - (#text / 2) + wordPosCheck
1411end
1412
1413local setUpModem = function()
1414 if game.net.useSkynet then
1415 if fs.exists(game.net.skynetPath) then
1416 game.net.skynet = dofile(game.net.skynetPath)
1417 term.clear()
1418 cwrite("Connecting to Skynet...", scr_y / 2)
1419 game.net.skynet.open(game.net.channel)
1420 return true
1421 else
1422 term.clear()
1423 cwrite("Downloading Skynet...", scr_y / 2)
1424 local prog = http.get(game.net.skynetURL)
1425 if prog then
1426 local file = fs.open(game.net.skynetPath, "w")
1427 file.write(prog.readAll())
1428 file.close()
1429 skynet = dofile(game.net.skynetPath)
1430 cwrite("Connecting to Skynet...", 1 + scr_y / 2)
1431 skynet.open(game.net.channel)
1432 return true
1433 else
1434 return false, "Could not download Skynet."
1435 end
1436 end
1437 else
1438 -- unload / close skynet
1439 if game.net.skynet then
1440 if game.net.skynet.socket then
1441 game.net.skynet.socket.close()
1442 end
1443 game.net.skynet = nil
1444 end
1445 game.net.modem = peripheral.find("modem")
1446 if (not game.net.modem) and ccemux then
1447 ccemux.attach("top", "wireless_modem")
1448 game.net.modem = peripheral.find("modem")
1449 end
1450 if game.net.modem then
1451 game.net.modem.open(game.net.channel)
1452 return true
1453 else
1454 return false, "No modem was found."
1455 end
1456 end
1457end
1458
1459local pleaseWait = function()
1460 local periods = 1
1461 local maxPeriods = 5
1462 term.setBackgroundColor(tColors.black)
1463 term.setTextColor(tColors.gray)
1464 term.clear()
1465
1466 local tID = os.startTimer(0.2)
1467 local evt, txt
1468 if game.net.useSkynet then
1469 txt = "Waiting for Skynet game"
1470 else
1471 txt = "Waiting for modem game"
1472 end
1473
1474 while true do
1475 cwrite("(Press 'Q' to cancel)", 2)
1476 cwrite(txt, scr_y - 2, maxPeriods)
1477 term.write(("."):rep(periods))
1478 evt = {os.pullEvent()}
1479 if evt[1] == "timer" and evt[2] == tID then
1480 tID = os.startTimer(0.5)
1481 periods = (periods % maxPeriods) + 1
1482 term.clearLine()
1483 elseif evt[1] == "key" and evt[2] == keys.q then
1484 return
1485 end
1486 end
1487end
1488
1489local titleScreen = function() -- mondo placeholder
1490 term.setTextColor(tColors.white)
1491 term.setBackgroundColor(tColors.black)
1492 term.clear()
1493 cwrite("LDris", 3)
1494 cwrite("by LDDestroier", 5)
1495 cwrite("Press 1 to play a game.", 7)
1496 if game.net.useSkynet then
1497 cwrite("Press 2 to play an HTTP game.", 8)
1498 cwrite("Press S to disable Skynet.", 9)
1499 else
1500 cwrite("Press 2 to play a modem game.", 8)
1501 cwrite("Press S to enable Skynet.", 9)
1502 end
1503 cwrite("Press H to see controls.", 10)
1504 cwrite("Press Q to quit.", 11)
1505 local evt
1506 while true do
1507 evt = {os.pullEvent()}
1508 if evt[1] == "key" then
1509 if evt[2] == keys.one then
1510 return "1P"
1511 elseif evt[2] == keys.two then
1512 return "2P"
1513 elseif evt[2] == keys.s then
1514 return "skynet"
1515 elseif evt[2] == keys.h then
1516 return "help"
1517 elseif evt[2] == keys.q then
1518 return "quit"
1519 end
1520 end
1521 end
1522end
1523
1524local screenError = function(...)
1525 local lines = {...}
1526 term.setBackgroundColor(tColors.black)
1527 term.setTextColor(tColors.white)
1528 term.clear()
1529 for i = 1, #lines do
1530 cwrite(lines[i], 2 + i)
1531 end
1532 cwrite("Press any key to continue.", #lines + 4)
1533 sleep(0)
1534 repeat until os.pullEvent("key")
1535end
1536
1537-- a lot of these menus and whatnot are very primitive. I can improve that later
1538local showHelp = function()
1539 term.setBackgroundColor(tColors.black)
1540 term.setTextColor(tColors.white)
1541 term.clear()
1542 cwrite("CONTROLS (defaults):", 2)
1543 term.setCursorPos(1, 4)
1544 print(" Move Piece: LEFT and RIGHT")
1545 print(" Hard Drop: UP")
1546 print(" Fast Drop: DOWN")
1547 print(" Rotate Piece: Z and X")
1548 print(" Hold Piece: L.SHIFT")
1549 print(" Quit: Q")
1550 print("\n Press any key to continue.")
1551 sleep(0)
1552 repeat until os.pullEvent("key")
1553end
1554
1555local main = function()
1556
1557 local rVal -- please wait result
1558 local modeVal -- title screen mode result
1559 local funcs
1560
1561 setUpModem()
1562
1563 while true do
1564
1565 game.you = 1
1566 game.net.isHost = true
1567 finished = false
1568 game.running = true
1569
1570 while true do
1571 modeVal = titleScreen()
1572
1573 if modeVal == "1P" then
1574 game.net.active = false
1575 game.amountOfPlayers = 1
1576 game.gameDelay = 0.05
1577 break
1578 elseif modeVal == "2P" then
1579 if setUpModem() then
1580 if (game.net.skynet and game.net.useSkynet) then
1581 game.gameDelay = 0.1
1582 else
1583 game.gameDelay = 0.05
1584 end
1585 game.net.active = true
1586 game.amountOfPlayers = 2
1587 break
1588 else
1589 screenError("A modem is required for multiplayer.")
1590 finished = true
1591 end
1592 elseif modeVal == "skynet" then
1593 if http.websocket then
1594 game.net.useSkynet = not game.net.useSkynet
1595 setUpModem()
1596 else
1597 screenError(
1598 "Skynet requires websocket support.",
1599 "Use CCEmuX, CC:Tweaked,",
1600 "or CraftOS-PC 2.2 or higher to play."
1601 )
1602 finished = true
1603 end
1604 elseif modeVal == "help" then
1605 showHelp()
1606 elseif modeVal == "quit" then
1607 return false
1608 end
1609 end
1610
1611 if game.net.active then
1612
1613 game.net.waitingForGame = true
1614
1615 cTime = getTime()
1616 transmit({
1617 gameID = game.net.gameID,
1618 time = cTime,
1619 command = "find_game",
1620 instanceID = game.instanceID
1621 })
1622 if game.net.useSkynet then
1623 rVal = parallel.waitForAny( networking, pleaseWait, game.net.skynet.listen )
1624 else
1625 rVal = parallel.waitForAny( networking, pleaseWait )
1626 end
1627 sleep(0.1)
1628
1629 if rVal == 1 then
1630
1631 funcs = {
1632 getInput,
1633 }
1634
1635 if game.net.active then
1636 table.insert(funcs, networking)
1637 if game.net.useSkynet and game.net.skynet then
1638 table.insert(funcs, game.net.skynet.listen)
1639 end
1640 end
1641
1642 initializePlayers(game.amountOfPlayers or 1)
1643
1644 if game.net.isHost then
1645 for k,v in pairs(game.p) do
1646 funcs[#funcs + 1] = function()
1647 return startGame(k)
1648 end
1649 end
1650 else
1651 funcs[#funcs + 1] = startGame
1652 end
1653
1654 else
1655 finished = true
1656 end
1657
1658 else
1659
1660 funcs = {getInput}
1661 initializePlayers(game.amountOfPlayers or 1)
1662
1663 for k,v in pairs(game.p) do
1664 funcs[#funcs + 1] = function()
1665 return startGame(k)
1666 end
1667 end
1668
1669 end
1670
1671 if not finished then
1672
1673 term.setBackgroundColor(tColors.gray)
1674 term.clear()
1675
1676 parallel.waitForAny(table.unpack(funcs))
1677
1678 end
1679
1680 end
1681
1682end
1683
1684main()
1685
1686-- reset palette to back from whence it came
1687for k,v in pairs(colors) do
1688 if type(v) == "number" then
1689 term.setPaletteColor(v, term.nativePaletteColor(v))
1690 end
1691end
1692
1693term.setBackgroundColor(colors.black)
1694term.setTextColor(colors.white)
1695
1696if game.net.skynet then
1697 if game.net.skynet.socket then
1698 game.net.skynet.socket.close()
1699 end
1700end
1701
1702for i = 1, 5 do
1703 term.scroll(1)
1704 if i == 3 then
1705 term.setCursorPos(1, scr_y)
1706 term.write("Thanks for playing!")
1707 end
1708 sleep(0.05)
1709end
1710term.setCursorPos(1, scr_y)