# Game 2048
--By Rocket on Wheelz 18349047
-- init APIs, I guess - Khởi tạo API
-- p/s: WTH is API? i have no idea
local Block = class.Block.new()
--local Area = class.Area.new()
local Chat = class.Chat.new()
local Game = class.Game.new()
local Player = class.Player.new()
local Actor = class.Actor.new()
local eventHandler = {}
local game2048 = {}
Chat:sendSystemMsg([=[I am fine, thank you. And you?]=], 0)
-- define constants - định nghĩa các hằng số
local locations = {
boardCorner = {x = 35, y = 14, z = 26},
materials = {x = 37, y = 7, z = 7},
xSpot = {x = 35, y = 10, z = 17},
undoSpot = {x = 35, y = 8, z = 17},
notiSound = {x = 35, y = 17, z = 26}
}
local items = {
upButton = 2003,
downButton = 2004,
leftButton = 2002,
rightButton = 2001,
startButton = 4097
}
local blocks = {
x = 2009,
undo = {[0] = 2008, [1] = 2007},
lithium = 415
}
-- end constant def
-- Game data - Dữ liệu game
local game_board = {
cur = {
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}
},
undo = {
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}
},
temp = {
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}
}
}
local data2048 = {
isGameRunning = 0,
isMoveSuccessful = 0,
undoLeft = 0,
score = 0,
scoreUndo = 0,
currentPlayerID = 0,
spamUndoCount = 0
}
-- end data
-- miscellaneous functions - các lệnh bổ sung
local function boardCopy(board1,board2)
for i = 1,4 do
for j = 1,4 do
board1[i][j] = board2[i][j]
end
end
end
local function locationOffset(loca, dx, dy, dz)
return {loca.x + dx, loca.y + dy, loca.z + dz}
end
local function occupiedCellCount(board)
local count = 0
for i, v in ipairs(board) do
for j, u in ipairs(v) do
if u>0 then
count = count + 1
end
end
end
return count
end
local function cellSpawn()
local pos = 0
math.randomseed(os.time())
pos = math.random(1, 16 - occupiedCellCount(game_board.cur))
--Chat:sendSystemMsg(pos, 0)
--Game:msgBox(pos)
for i, v in ipairs(game_board.cur) do
if pos == 0 then break end
for j, u in ipairs(v) do
if u == 0 then
pos = pos - 1
if pos == 0 then game_board.cur[i][j] = math.floor(math.random(1,5)/5 + 1) end
end
end
end
end
local function showMeTheBoard()
--cellSpawn()
local str = ""
for i, v in ipairs(game_board.temp) do
for j, u in ipairs(v) do
str = str.." "..u
end
str = str.."\n"
end
Game:msgBox(str)
end
-- game processor - bộ điều hành game
game2048 = {
initGame = function(playerId)
data2048.isGameRunning = 1
data2048.score = 0
data2048.undoLeft = 0
data2048.undoSpamCount = 0
-- give player control buttons
Player:gainItems(playerId, items.upButton, 1, 1)
Player:gainItems(playerId, items.downButton, 1, 1)
Player:gainItems(playerId, items.leftButton, 1, 1)
Player:gainItems(playerId, items.rightButton, 1, 1)
-- display HUD
game2048.displayScore(data2048.score)
local temp = locations.xSpot
Block:placeBlock(blocks.x, temp.x, temp.y, temp.z, 4)
local t, v = locations.materials, locations.boardCorner
for i = 0, 7 do
for j = 0, 7 do
Block:placeBlock(0, v.x, v.y - j, v.z - i, 1)
end
end
for i = 0, 4 do
for j = 0,1 do
local err, b = Block:getBlockID(t.x, t.y + j, t.z - i)
Block:placeBlock(b, v.x, v.y + j - 1, v.z - i - 9, 4)
end
end
cellSpawn()
game2048.updateBoard()
end,
displayScore = function(score)
--Chat:sendSystemMsg('disp',0)
local s, d = locations.materials, locations.xSpot
local n = 0 -- number of digits
if score > 0 then n = math.floor(math.log10(score)) end
for i = 0,n do
local v = math.floor((score % (10 ^ (i + 1))) / 10 ^ i)
--Chat:sendSystemMsg(v,0)
for j = 0, 1 do
local err, b = Block:getBlockID(s.x + 1, s.y + j, s.z - v)
Block:placeBlock(b, d.x, d.y + j + 1, d.z + i - n, 4)
end
end
end,
updateBoard = function()
local t = locations.undoSpot
Block:placeBlock(blocks.undo[data2048.undoLeft], t.x, t.y, t.z, 4)
local d, s = locations.boardCorner, locations.materials
for i = 1,4 do
for j = 1,4 do
local value = game_board.cur[i][j]
for k = 0,1 do
for l = 0,1 do
local err, b = Block:getBlockID(s.x + 4 - l, s.y + 1 - k, s.z - value)
Block:placeBlock(b, d.x, d.y - k - 2*i + 2, d.z - l -2*j + 2, 4)
end
end
end
end
end,
playNotiSound = function()
local loca = locations.notiSound
Block:placeBlock(blocks.lithium, loca.x, loca.y, loca.z, 1)
Block:placeBlock(0, loca.x, loca.y, loca.z, 1)
end,
moveUp = function()
if data2048.isGameRunning == 0 then return end
local tempScore = data2048.score
local b = game_board
boardCopy(b.temp, b.cur)
data2048.undoLeft = 1
for j = 1,4 do
local fuseLeft = 1
for i = 1,3 do
for k = i,1,-1 do
if b.cur[k+1][j] == 0 then break end
if b.cur[k][j] == 0 then
b.cur[k][j] = b.cur[k+1][j]
b.cur[k+1][j] = 0
elseif b.cur[k][j] == b.cur[k+1][j] then
if fuseLeft == 1 then
b.cur[k][j] = b.cur[k][j]+1
data2048.score = data2048.score + 2 ^ b.cur[k][j]
b.cur[k+1][j] = 0
fuseLeft = 0
break
else fuseLeft = 1
end
else
fuseLeft = 1
break
end
end
end
end
if game2048.checkMoveSuccessful() ~= 0 then
cellSpawn()
game2048.updateBoard()
data2048.undoScore = tempScore
boardCopy(b.undo, b.temp)
game2048.displayScore(data2048.score)
else game2048.playNotiSound()
end
end,
moveDown = function()
if data2048.isGameRunning == 0 then return end
local tempScore = data2048.score
local b = game_board
boardCopy(b.temp, b.cur)
data2048.undoLeft = 1
for j = 1,4 do
local fuseLeft = 1
for i = 4,2,-1 do
for k = i,4 do
if b.cur[k-1][j] == 0 then break end
if b.cur[k][j] == 0 then
b.cur[k][j] = b.cur[k-1][j]
b.cur[k-1][j] = 0
elseif b.cur[k][j] == b.cur[k-1][j] then
if fuseLeft == 1 then
b.cur[k][j] = b.cur[k][j]+1
data2048.score = data2048.score + 2 ^ b.cur[k][j]
b.cur[k-1][j] = 0
fuseLeft = 0
break
else fuseLeft = 1
end
else
fuseLeft = 1
break
end
end
end
end
if game2048.checkMoveSuccessful() ~= 0 then
cellSpawn()
game2048.updateBoard()
data2048.undoScore = tempScore
boardCopy(b.undo, b.temp)
game2048.displayScore(data2048.score)
else game2048.playNotiSound()
end
end,
moveLeft = function()
if data2048.isGameRunning == 0 then return end
local tempScore = data2048.score
local b = game_board
boardCopy(b.temp, b.cur)
data2048.undoLeft = 1
for i = 1,4 do
local fuseLeft = 1
for j = 1,3 do
for k = j,1,-1 do
if b.cur[i][k+1] == 0 then break end
if b.cur[i][k] == 0 then
b.cur[i][k] = b.cur[i][k+1]
b.cur[i][k+1] = 0
elseif b.cur[i][k] == b.cur[i][k+1] then
if fuseLeft == 1 then
b.cur[i][k] = b.cur[i][k]+1
data2048.score = data2048.score + 2 ^ b.cur[i][k]
b.cur[i][k+1] = 0
fuseLeft = 0
break
else fuseLeft = 1
end
else
fuseLeft = 1
break
end
end
end
end
if game2048.checkMoveSuccessful() ~= 0 then
cellSpawn()
game2048.updateBoard()
data2048.undoScore = tempScore
boardCopy(b.undo, b.temp)
game2048.displayScore(data2048.score)
else game2048.playNotiSound()
end
end,
moveRight = function()
if data2048.isGameRunning == 0 then return end
local tempScore = data2048.score
local b = game_board
boardCopy(b.temp, b.cur)
data2048.undoLeft = 1
for i = 1,4 do
local fuseLeft = 1
for j = 4,2,-1 do
for k = j,4 do
if b.cur[i][k-1] == 0 then break end
if b.cur[i][k] == 0 then
b.cur[i][k] = b.cur[i][k-1]
b.cur[i][k-1] = 0
elseif b.cur[i][k] == b.cur[i][k-1] then
if fuseLeft == 1 then
b.cur[i][k] = b.cur[i][k]+1
data2048.score = data2048.score + 2 ^ b.cur[i][k]
b.cur[i][k-1] = 0
fuseLeft = 0
break
else fuseLeft = 1
end
else
fuseLeft = 1
break
end
end
end
end
if game2048.checkMoveSuccessful() ~= 0 then
cellSpawn()
game2048.updateBoard()
data2048.undoScore = tempScore
boardCopy(b.undo, b.temp)
game2048.displayScore(data2048.score)
else game2048.playNotiSound()
end
end,
checkMoveSuccessful = function()
local answer = 0
local b = game_board
for i = 1,4 do
for j = 1,4 do
if b.cur[i][j] ~= b.temp[i][j] then
data2048.undoSpamCount = 0
answer = 1
break
end
end
end
return answer
end,
cancelGame = function()
if data2048.isGameRunning == 0 then return end
data2048.isGameRunning = 0
for i, v in pairs(game_board) do
for j = 1,4 do
for k = 1,4 do
v[j][k] = 0
end
end
end
for i, v in pairs(items) do
if i ~= 'startButton' then
Player:removeBackpackItem(data2048.currentPlayerID, v, 1)
end
end
local t = locations.boardCorner
for i = 0, 7 do
for j = 0, 14 do
Block:placeBlock(0, t.x, t.y - i, t.z - j, 1)
end
end
local v = locations.materials
for i = 0, 3 do
for j = 0, 1 do
local err, b = Block:getBlockID(v.x - 1, v.y + j, v.z - i)
Block:placeBlock(b, t.x, t.y + j - 4, t.z - i - 2, 4)
end
end
end,
undo = function()
if data2048.undoLeft == 0 then return end
data2048.score = data2048.scoreUndo
local b = game_board
game2048.displayScore(data2048.score)
boardCopy(b.cur, b.undo)
data2048.undoLeft = 0
game2048.updateBoard()
end
}
-- yes, it handles triggered events as its name suggests
-- bộ giao tiếp với các sự kiện được kích hoạt
eventHandler = {
pressedButtonDetect = function(params)
local id = params.itemid
if id==items.startButton then
data2048.currentPlayerID = params.eventobjid
if data2048.isGameRunning==0 then game2048.initGame(params.eventobjid)
else Chat:sendSystemMsg('Hey, the game is not finished! \nChơi hết ván trước đi đã!', 0)
end
elseif id==items.upButton then
game2048.moveUp()
elseif id==items.downButton then
game2048.moveDown()
elseif id==items.leftButton then
game2048.moveLeft()
elseif id==items.rightButton then
game2048.moveRight()
end
end,
pressedBlockDetect = function(params)
local id = params.blockid
local d = data2048
if id == blocks.x then
game2048.cancelGame()
elseif id == blocks.undo[0] then
game2048.playNotiSound()
d.undoSpamCount = d.undoSpamCount + 1
if d.undoSpamCount == 3 then
Chat:sendSystemMsg([[Hey, don't spam hitting me! Này, đừng có ấn vào tôi nhiều thế chứ !]], 0)
end
if d.undoSpamCount == 6 then
d.undoSpamCount = 0
Chat:sendSystemMsg([[Hey, you want a punch, don't you?]], 0)
Chat:sendSystemMsg([[Thích đánh nhau phải không? :((]], 0)
end
elseif id == blocks.undo[1] then
d.undoSpamCount = 0
game2048.undo()
end
end
}
--ScriptSupportEvent:registerEvent('Player.ClickBlock', showMeTheBoard)
ScriptSupportEvent:registerEvent('Player.SelectShortcut', eventHandler.pressedButtonDetect)
ScriptSupportEvent:registerEvent('Player.ClickBlock', eventHandler.pressedBlockDetect)