mirror of
https://gitea.com/SweetSea-ButImNotSweet/tromi_mobile.git
synced 2025-01-08 17:33:09 +08:00
V0 version
Add ``.gitignore``
Update ``.vscode\settings.json``
Main file changed a bit
Replace every single ``io.open`` into ``fs.read()``
Add ``input.waiting2trigger`` as buffer for too quick inputs
Replace ``binser`` with ``bitser``
Add the missing buffer logical code in training mode
Add a debug connector
Not a big update
Update VirtualControl.lua
Update in vctrl system
Trimming some unnecessary empty lines in classic library
Update virtual control stuff
Replace ``table.getn`` with ``#`` and ``scene`` with ``SCENE``
Renaming and moving some modules
Removing unnecessary ``local mino = {...}``
Add loading screen
Update loading screen
Apply replay patch
Not showing virtual control on computer
Adding touch screen configuration scene (placeholder)
Fix loading screen
update virtual control texture
Do some preparation for touch config screen
Quick patch
Compress background
Not important uodates
Small changes on how virtual key call action
Add ``SCENE:onInputMove``
Apply V2.2 patch
Clean up unnecessary imports
Test
.
Remove a redudant global variable
Small change
Split up alpha number
Sorting code
Update storeInput function
Optimize replay storing, saving and reading
Add VCTRL.export (for saving feature)
Remove unnecessary imports
Redesign loading screen
Replace loading screen
Make a simple BUTTON module
Update BUTTON module
Update button module
Add new callback
Add new callback
TEST
Update simple-button module
Update simple button module
Set default draw function for button
Add scene type notation
TEST
Not important updates
Design a error screen
Small update
Remove error key
Update
TEST
TEST
Test
TEST
TEST
Update button module
TEST
TEST
TEST
TEST
TEST
TEST
TEST
TEST
test
TEST
TEST
TEST
test
TEST
test
Fix a bug in VCTRL module that affect to SCENE:onInputRelease
Moving VCTRL related calls and adding buttons for name entry screen
Add type notation
Update modules
Final update for touch configuration scene
Fix 2 buttons can be highlighted at the same time in simple-button module
Narrow the safe border
Remove id = b (it was there for test)
Update of touch configuration scene
Add touch gesture for replay and input configuration scene
Add buttons for Replay, add MENU to go out after finishing game or in 20G Training mode
TEST
Fix some bugs (TEST)
Fix lỗi giữa đêm
Fix bug again
It should work imo
TEST
Fix SCENE:onInputMove{type="touch"} is not working
Fix bug once again (DONE!)
Temproraily allowing save
Fix settings module
Fix VCTRL.exportAll()
Fix VCTRL.exportAll returns userdata
Reverse a change
Fix forgetting to import virtual control settings
Fix grid drawing
Fix bugs related to the first time launching game
Add README file
Add README file
Update README and add LICENSE files
Update README
Add TV remote code
Disable debug code
Fix Android code
Small fix
Rename LICENSE to COPYING
Moving scene.lua to modules folder
Add new libraries
Make a new FILE API and add a simple error screen in case most thing went down
Change special code, add a way to skip keys
Update icon + README file
Rename screenshot file
Update README
Updating README file
Replace loading screen
Update README
Update virtual control texture
Fix virtual button method
Update README
Add icon font
Add importing and exporting replays
Update touch control
Update conf.lua
Replacing font, to avoid license issue
convert indents to spaces
Update font related stuff
Replace font
Updating README file
Update virtual control texture
This commit is contained in:
1120
game/gamemode.lua
Normal file
1120
game/gamemode.lua
Normal file
File diff suppressed because it is too large
Load Diff
211
game/grid.lua
Normal file
211
game/grid.lua
Normal file
@@ -0,0 +1,211 @@
|
||||
local Object = require 'libs.classic'
|
||||
|
||||
local Grid = Object:extend()
|
||||
|
||||
local empty = { skin = "", colour = "", oob=false }
|
||||
local oob = { skin = "", colour = "", oob=true }
|
||||
local block = { skin = "2tie", colour = "A", oob=false }
|
||||
|
||||
function Grid:new(width, height)
|
||||
self.grid = {}
|
||||
self.grid_age = {}
|
||||
self.width = width
|
||||
self.height = height
|
||||
for y = 1, self.height do
|
||||
self.grid[y] = {}
|
||||
self.grid_age[y] = {}
|
||||
for x = 1, self.width do
|
||||
self.grid[y][x] = empty
|
||||
self.grid_age[y][x] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:clear()
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
self.grid[y][x] = empty
|
||||
self.grid_age[y][x] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:getCell(x, y)
|
||||
if x < 1 or x > self.width or y > self.height then return oob
|
||||
elseif y < 1 then return oob
|
||||
else return self.grid[y][x]
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:isOccupied(x, y)
|
||||
return self:getCell(x+1, y+1) ~= empty
|
||||
end
|
||||
|
||||
function Grid:isRowFull(row)
|
||||
for index, square in pairs(self.grid[row]) do
|
||||
if square == empty then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:canPlacePiece(piece)
|
||||
local offsets = piece:getBlockOffsets()
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = piece.position.x + offset.x
|
||||
local y = piece.position.y + offset.y
|
||||
if self:isOccupied(x, y) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:canPlacePieceInVisibleGrid(piece)
|
||||
local offsets = piece:getBlockOffsets()
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = piece.position.x + offset.x
|
||||
local y = piece.position.y + offset.y
|
||||
if y < 1 or self:isOccupied(x, y) ~= empty then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:getClearedRowCount()
|
||||
local count = 0
|
||||
local cleared_row_table = {}
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
count = count + 1
|
||||
table.insert(cleared_row_table, row)
|
||||
end
|
||||
end
|
||||
return count, cleared_row_table
|
||||
end
|
||||
|
||||
function Grid:markClearedRows()
|
||||
local block_table = {}
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
block_table[row] = {}
|
||||
for x = 1, self.width do
|
||||
block_table[row][x] = {
|
||||
skin = self.grid[row][x].skin,
|
||||
colour = self.grid[row][x].colour,
|
||||
}
|
||||
self.grid[row][x] = {
|
||||
skin = self.grid[row][x].skin,
|
||||
colour = "X"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
return block_table
|
||||
end
|
||||
|
||||
function Grid:clearClearedRows()
|
||||
for row = 1, self.height do
|
||||
if self:isRowFull(row) then
|
||||
for above_row = row, 2, -1 do
|
||||
self.grid[above_row] = self.grid[above_row - 1]
|
||||
self.grid_age[above_row] = self.grid_age[above_row - 1]
|
||||
end
|
||||
self.grid[1] = {}
|
||||
self.grid_age[1] = {}
|
||||
for i = 1, self.width do
|
||||
self.grid[1][i] = empty
|
||||
self.grid_age[1][i] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Grid:clearSpecificRow(row)
|
||||
for col = 1, self.width do
|
||||
self.grid[row][col] = empty
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:applyPiece(piece)
|
||||
if piece.big then
|
||||
self:applyBigPiece(piece)
|
||||
return
|
||||
end
|
||||
for index, offset in pairs(piece:getBlockOffsets()) do
|
||||
local x = piece.position.x + offset.x
|
||||
local y = piece.position.y + offset.y
|
||||
if y + 1 > 0 and y < self.height then
|
||||
self.grid[y+1][x+1] = {
|
||||
skin = piece.skin,
|
||||
colour = piece.colour,
|
||||
flash = 5
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:checkStackHeight()
|
||||
for i = 0, self.height - 1 do
|
||||
for j = 0, self.width - 1 do
|
||||
if self:isOccupied(j, i) then return self.height - i end
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function Grid:applyMap(map)
|
||||
for y, row in pairs(map) do
|
||||
for x, block in pairs(row) do
|
||||
self.grid_age[y][x] = 0
|
||||
self.grid[y][x] = block
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:update()
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
if self.grid[y][x] ~= empty then
|
||||
self.grid_age[y][x] = self.grid_age[y][x] + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Grid:draw(greyscale, timer)
|
||||
love.graphics.setColor(0,0,0,1)
|
||||
love.graphics.rectangle("fill", 256, 31, 80, 45, 0, 0)
|
||||
love.graphics.setColor(0.3, 0.3, 0.3, 1)
|
||||
love.graphics.line(256,31,256,31+45)
|
||||
love.graphics.line(256,31,256+80,31)
|
||||
love.graphics.line(256+80,31,256+80,31+45)
|
||||
love.graphics.line(256,31+45,256+80,31+45)
|
||||
for y = 1, self.height do
|
||||
for x = 1, self.width do
|
||||
if BLOCKS[self.grid[y][x].skin] and
|
||||
BLOCKS[self.grid[y][x].skin][self.grid[y][x].colour] then
|
||||
if self.grid[y][x].flash ~= nil then
|
||||
if self.grid[y][x].flash > 0 then
|
||||
love.graphics.setColor(0.4+(self.grid[y][x].flash*0.1), 0.4+(self.grid[y][x].flash*0.1), 0.4+(self.grid[y][x].flash*0.1), 1)
|
||||
love.graphics.draw(BLOCKS[self.grid[y][x].skin]['W'], 200+x*16, 64+y*16)
|
||||
self.grid[y][x].flash = self.grid[y][x].flash - 1
|
||||
else
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.draw(BLOCKS[self.grid[y][x].skin][self.grid[y][x].colour..'_d'], 200+x*16, 64+y*16)
|
||||
end
|
||||
end
|
||||
if greyscale then
|
||||
if timer > 1 then timer = 1 end
|
||||
love.graphics.setColor(0.7, 0.7, 0.7, 0+timer)
|
||||
if self.grid[y][x] ~= empty and self.grid[y][x].colour ~= 'X' then
|
||||
love.graphics.draw(BLOCKS[self.grid[y][x].skin]["A"], 200+x*16, 64+y*16)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Grid
|
||||
174
game/piece.lua
Normal file
174
game/piece.lua
Normal file
@@ -0,0 +1,174 @@
|
||||
local Object = require 'libs.classic'
|
||||
|
||||
local Piece = Object:extend()
|
||||
|
||||
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, colour, big)
|
||||
self.shape = shape
|
||||
self.rotation = rotation
|
||||
self.position = position
|
||||
self.block_offsets = block_offsets
|
||||
self.gravity = gravity
|
||||
self.lock_delay = lock_delay
|
||||
self.skin = skin
|
||||
self.colour = colour
|
||||
self.lowest_point = -1
|
||||
-- self.ghost = false
|
||||
self.locked = false
|
||||
-- self.big = big
|
||||
end
|
||||
|
||||
-- Functions that return a new piece to test in rotation systems.
|
||||
|
||||
function Piece:withOffset(offset)
|
||||
return Piece(
|
||||
self.shape, self.rotation,
|
||||
{x = self.position.x + offset.x, y = self.position.y + offset.y},
|
||||
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour
|
||||
)
|
||||
end
|
||||
|
||||
function Piece:withRelativeRotation(rot)
|
||||
local new_rot = self.rotation + rot
|
||||
while new_rot < 0 do new_rot = new_rot + 4 end
|
||||
while new_rot >= 4 do new_rot = new_rot - 4 end
|
||||
return Piece(
|
||||
self.shape, new_rot, self.position,
|
||||
self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour
|
||||
)
|
||||
end
|
||||
|
||||
-- Functions that return predicates relative to a grid.
|
||||
|
||||
function Piece:getBlockOffsets()
|
||||
return self.block_offsets[self.shape][self.rotation + 1]
|
||||
end
|
||||
|
||||
function Piece:occupiesSquare(x, y)
|
||||
local offsets = self:getBlockOffsets()
|
||||
for index, offset in pairs(offsets) do
|
||||
local new_offset = {x = self.position.x + offset.x, y = self.position.y + offset.y}
|
||||
if new_offset.x == x and new_offset.y == y then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Piece:isMoveBlocked(grid, offset)
|
||||
local moved_piece = self:withOffset(offset)
|
||||
return not grid:canPlacePiece(moved_piece)
|
||||
end
|
||||
|
||||
function Piece:isDropBlocked(grid)
|
||||
return self:isMoveBlocked(grid, { x=0, y=1 })
|
||||
end
|
||||
|
||||
-- Procedures to actually do stuff to pieces.
|
||||
|
||||
function Piece:setOffset(offset)
|
||||
self.position.x = self.position.x + offset.x
|
||||
self.position.y = self.position.y + offset.y
|
||||
return self
|
||||
end
|
||||
|
||||
function Piece:setRelativeRotation(rot)
|
||||
new_rot = self.rotation + rot
|
||||
while new_rot < 0 do new_rot = new_rot + 4 end
|
||||
while new_rot >= 4 do new_rot = new_rot - 4 end
|
||||
self.rotation = new_rot
|
||||
return self
|
||||
end
|
||||
|
||||
function Piece:moveInGrid(step, squares, grid, instant)
|
||||
local moved = false
|
||||
for x = 1, squares do
|
||||
if grid:canPlacePiece(self:withOffset(step)) then
|
||||
moved = true
|
||||
self:setOffset(step)
|
||||
if instant then
|
||||
self:dropToBottom(grid)
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Piece:dropSquares(dropped_squares, grid)
|
||||
self:moveInGrid({ x = 0, y = 1 }, dropped_squares, grid)
|
||||
end
|
||||
|
||||
function Piece:dropToBottom(grid)
|
||||
local piece_y = self.position.y
|
||||
self:dropSquares(math.huge, grid)
|
||||
self.gravity = 0
|
||||
if self.position.y > piece_y then
|
||||
--self.lock_delay = 0
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Piece:lockIfBottomed(grid)
|
||||
if self:isDropBlocked(grid) then
|
||||
self.locked = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Piece:addGravity(gravity, grid, classic_lock)
|
||||
gravity = gravity / (self.big and 2 or 1)
|
||||
local new_gravity = self.gravity + gravity
|
||||
if self:isDropBlocked(grid) then
|
||||
if classic_lock then
|
||||
self.gravity = new_gravity
|
||||
else
|
||||
self.gravity = 0
|
||||
self.lock_delay = self.lock_delay + 1
|
||||
end
|
||||
elseif not (
|
||||
self:isMoveBlocked(grid, { x=0, y=-1 }) and gravity < 0
|
||||
) then
|
||||
local dropped_squares = math.floor(math.abs(new_gravity))
|
||||
if gravity >= 0 then
|
||||
local new_frac_gravity = new_gravity - dropped_squares
|
||||
self.gravity = new_frac_gravity
|
||||
self:dropSquares(dropped_squares, grid)
|
||||
if self:isDropBlocked(grid) then
|
||||
PlaySE("bottom")
|
||||
end
|
||||
else
|
||||
local new_frac_gravity = new_gravity + dropped_squares
|
||||
self.gravity = new_frac_gravity
|
||||
self:moveInGrid({ x=0, y=-1 }, dropped_squares, grid)
|
||||
if self:isMoveBlocked(grid, { x=0, y=-1 }) then
|
||||
PlaySE("bottom")
|
||||
end
|
||||
end
|
||||
else
|
||||
self.gravity = 0
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-- Procedures for drawing.
|
||||
|
||||
function Piece:draw(opacity, brightness, grid, partial_das)
|
||||
if opacity == nil then opacity = 1 end
|
||||
if brightness == nil then brightness = 1 end
|
||||
love.graphics.setColor(brightness, brightness, brightness, opacity)
|
||||
local offsets = self:getBlockOffsets()
|
||||
local gravity_offset = 0
|
||||
if partial_das == nil then partial_das = 0 end
|
||||
for index, offset in pairs(offsets) do
|
||||
local x = self.position.x + offset.x
|
||||
local y = self.position.y + offset.y
|
||||
love.graphics.draw(
|
||||
BLOCKS[self.skin][self.colour],
|
||||
216+x*16+partial_das, 80+y*16+gravity_offset
|
||||
)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return Piece
|
||||
148
game/randomizer.lua
Normal file
148
game/randomizer.lua
Normal file
@@ -0,0 +1,148 @@
|
||||
local Object = require 'libs.classic'
|
||||
|
||||
local Randomizer = Object:extend()
|
||||
|
||||
local highest_count = 0
|
||||
|
||||
function Randomizer:new(always, seed)
|
||||
self.always = always
|
||||
if seed ~= nil then
|
||||
self.seed = seed
|
||||
else
|
||||
self.seed = love.math.random(1, 9007199254740991)
|
||||
end
|
||||
self.drought = {
|
||||
I = 0,
|
||||
J = 0,
|
||||
L = 0,
|
||||
T = 0,
|
||||
S = 0,
|
||||
Z = 0,
|
||||
O = 0
|
||||
}
|
||||
self.droughted_deals = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
|
||||
self.piece_counts = {
|
||||
I = 0,
|
||||
J = 0,
|
||||
L = 0,
|
||||
T = 0,
|
||||
S = 0,
|
||||
Z = 0,
|
||||
O = 0
|
||||
}
|
||||
self.piece_round = 0
|
||||
self:initialize()
|
||||
end
|
||||
|
||||
function Randomizer:nextPiece()
|
||||
return self:generatePiece()
|
||||
end
|
||||
|
||||
function Randomizer:runTest()
|
||||
local i_distance = 0
|
||||
local highest_distance = 0
|
||||
local distance_total = 0
|
||||
local i_appearances = 0
|
||||
local dupe = 0
|
||||
local trip = 0
|
||||
local quad = 0
|
||||
local quin = 0
|
||||
local lastpiece = 0
|
||||
local lastpiece2 = 0
|
||||
local lastpiece3 = 0
|
||||
local lastpiece4 = 0
|
||||
local pieceseq_rep = 0
|
||||
local pieceseq = {}
|
||||
local last_pieceseq = {}
|
||||
local highest_discrepancy = 0
|
||||
local discrepancy = self.piece_counts[table.highest(self.piece_counts)] - self.piece_counts[table.lowest(self.piece_counts)]
|
||||
local total_chance = 0
|
||||
for i=0, 750000 do
|
||||
piece = self:generatePiece()
|
||||
if #pieceseq < 7 then
|
||||
table.insert(pieceseq, piece)
|
||||
else
|
||||
--print(table.concat(pieceseq))
|
||||
if table.concat(pieceseq) == table.concat(last_pieceseq) then
|
||||
pieceseq_rep = pieceseq_rep + 1
|
||||
end
|
||||
last_pieceseq = copy(pieceseq)
|
||||
pieceseq = {}
|
||||
end
|
||||
if piece == lastpiece then dupe = dupe + 1 end
|
||||
if piece == lastpiece and piece == lastpiece2 then trip = trip + 1 end
|
||||
if piece == lastpiece and piece == lastpiece2 and piece == lastpiece3 then quad = quad + 1 end
|
||||
if piece == lastpiece and piece == lastpiece2 and piece == lastpiece3 and piece == lastpiece4 then quin = quin + 1 end
|
||||
if piece == 'I' then
|
||||
distance_total = distance_total + i_distance
|
||||
i_appearances = i_appearances + 1
|
||||
i_distance = 0
|
||||
else i_distance = i_distance + 1 end
|
||||
if highest_distance < i_distance then highest_distance = i_distance end
|
||||
lastpiece4 = lastpiece3
|
||||
lastpiece3 = lastpiece2
|
||||
lastpiece2 = lastpiece
|
||||
lastpiece = piece
|
||||
--if self.piece_round <= 750 then print(discrepancy) end
|
||||
local new_discrepancy = self.piece_counts[table.highest(self.piece_counts)] - self.piece_counts[table.lowest(self.piece_counts)]
|
||||
if new_discrepancy > discrepancy then highest_discrepancy = new_discrepancy end
|
||||
discrepancy = new_discrepancy
|
||||
local chance = 0
|
||||
for piece,drought in pairs(self.drought) do
|
||||
local piece_chance = 0.14/( ((self.drought[table.highest(self.drought)]+1)-drought) / (self.drought[table.highest(self.drought)]) )
|
||||
chance = chance + piece_chance
|
||||
end
|
||||
chance = chance / 7
|
||||
total_chance = total_chance + chance
|
||||
end
|
||||
--something = something / 750000
|
||||
print(string.format('dupes: %d, trips: %s, quads: %s, quins: %s, highest i distance: %d, average i distance: %f, pieceseq reps: %d, highest discrepancy:%d, average chance:%f\ndrought lengths dealt: %s', dupe, trip, quad, quin, highest_distance, distance_total/i_appearances, pieceseq_rep, highest_discrepancy, total_chance/750000, table.concat(self.droughted_deals, '-')))
|
||||
for piece,count in pairs(self.piece_counts) do
|
||||
print(piece..' '..count)
|
||||
end
|
||||
end
|
||||
|
||||
function Randomizer:initialize()
|
||||
local start_pieces = {"I", "J", "L", "T"}
|
||||
local shapes = {"I", "J", "L", "T", "S", "Z", "O"}
|
||||
love.math.setRandomSeed(self.seed)
|
||||
self.drought[table.remove(shapes, love.math.random(1, 4))] = 10
|
||||
for i=4,9 do
|
||||
self.drought[table.remove(shapes, love.math.random(1, #shapes))] = i
|
||||
end
|
||||
|
||||
--self:runTest()
|
||||
end
|
||||
|
||||
function Randomizer:generatePiece(always)
|
||||
if self.always then
|
||||
return "I"
|
||||
end
|
||||
|
||||
local shapes = {"I", "J", "L", "T", "S", "Z", "O"}
|
||||
local new_piece = shapes[love.math.random(1,7)]
|
||||
local chance = love.math.random(0,self.drought[table.highest(self.drought)])
|
||||
while self.drought[new_piece] < chance do
|
||||
new_piece = shapes[love.math.random(1,7)]
|
||||
end
|
||||
for piece,drought in pairs(self.drought) do
|
||||
if drought >= 10 then
|
||||
new_piece = piece
|
||||
end
|
||||
end
|
||||
generated_piece = new_piece
|
||||
|
||||
for piece,drought in pairs(self.drought) do
|
||||
if new_piece ~= piece then self.drought[piece] = self.drought[piece] + 1
|
||||
else
|
||||
if drought < #self.droughted_deals then self.droughted_deals[drought+1] = self.droughted_deals[drought+1] + 1 end
|
||||
self.piece_counts[piece] = self.piece_counts[piece] + 1
|
||||
self.drought[piece] = 0
|
||||
end
|
||||
end
|
||||
self.piece_round = self.piece_round + 1
|
||||
|
||||
return generated_piece
|
||||
end
|
||||
|
||||
return Randomizer
|
||||
299
game/rotation.lua
Normal file
299
game/rotation.lua
Normal file
@@ -0,0 +1,299 @@
|
||||
local Object = require 'libs.classic'
|
||||
local Piece = require 'game.piece'
|
||||
|
||||
local Rotation = Object:extend()
|
||||
|
||||
Rotation.spawn_positions = {
|
||||
I = { x=3, y= -1 },
|
||||
J = { x=3, y= -1 },
|
||||
L = { x=3, y= -1 },
|
||||
O = { x=3, y= -1 },
|
||||
S = { x=3, y= -1 },
|
||||
T = { x=3, y= -1 },
|
||||
Z = { x=3, y= -1 },
|
||||
}
|
||||
|
||||
Rotation.colourscheme = {
|
||||
I = "R",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "M",
|
||||
Z = "G",
|
||||
O = "Y",
|
||||
T = "C",
|
||||
}
|
||||
|
||||
Rotation.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=1}, {x=1, y=1}, {x=2, y=1}, {x=3, y=1} },
|
||||
{ {x=2, y=0}, {x=2, y=1}, {x=2, y=2}, {x=2, y=3} },
|
||||
{ {x=0, y=1}, {x=1, y=1}, {x=2, y=1}, {x=3, y=1} },
|
||||
{ {x=2, y=0}, {x=2, y=1}, {x=2, y=2}, {x=2, y=3} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=1}, {x=1, y=1}, {x=2, y=1}, {x=2, y=2} },
|
||||
{ {x=1, y=0}, {x=1, y=1}, {x=1, y=2}, {x=0, y=2} },
|
||||
{ {x=0, y=1}, {x=0, y=2}, {x=1, y=2}, {x=2, y=2} },
|
||||
{ {x=1, y=0}, {x=2, y=0}, {x=1, y=1}, {x=1, y=2} },
|
||||
},
|
||||
L={
|
||||
{ {x=0, y=1}, {x=0, y=2}, {x=1, y=1}, {x=2, y=1} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=1, y=1}, {x=1, y=2} },
|
||||
{ {x=0, y=2}, {x=1, y=2}, {x=2, y=1}, {x=2, y=2} },
|
||||
{ {x=1, y=0}, {x=1, y=1}, {x=1, y=2}, {x=2, y=2} },
|
||||
},
|
||||
O={
|
||||
{ {x=1, y=1}, {x=2, y=1}, {x=1, y=2}, {x=2, y=2} },
|
||||
{ {x=1, y=1}, {x=2, y=1}, {x=1, y=2}, {x=2, y=2} },
|
||||
{ {x=1, y=1}, {x=2, y=1}, {x=1, y=2}, {x=2, y=2} },
|
||||
{ {x=1, y=1}, {x=2, y=1}, {x=1, y=2}, {x=2, y=2} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=1}, {x=2, y=1}, {x=0, y=2}, {x=1, y=2} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=1, y=1}, {x=1, y=2} },
|
||||
{ {x=1, y=1}, {x=2, y=1}, {x=0, y=2}, {x=1, y=2} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=1, y=1}, {x=1, y=2} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=1}, {x=1, y=1}, {x=1, y=2}, {x=2, y=1} },
|
||||
{ {x=0, y=1}, {x=1, y=0}, {x=1, y=1}, {x=1, y=2} },
|
||||
{ {x=0, y=2}, {x=1, y=1}, {x=1, y=2}, {x=2, y=2} },
|
||||
{ {x=1, y=0}, {x=1, y=1}, {x=2, y=1}, {x=1, y=2} },
|
||||
},
|
||||
Z={
|
||||
{ {x=0, y=1}, {x=1, y=1}, {x=1, y=2}, {x=2, y=2} },
|
||||
{ {x=2, y=0}, {x=1, y=1}, {x=2, y=1}, {x=1, y=2} },
|
||||
{ {x=0, y=1}, {x=1, y=1}, {x=1, y=2}, {x=2, y=2} },
|
||||
{ {x=2, y=0}, {x=1, y=1}, {x=2, y=1}, {x=1, y=2} },
|
||||
}
|
||||
}
|
||||
|
||||
Rotation.pieces = 7
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function Rotation:new(game_mode)
|
||||
self.game = require 'game.gamemode'
|
||||
end
|
||||
|
||||
function Rotation:rotatePiece(inputs, piece, grid, prev_inputs, initial, lastdir)
|
||||
local new_inputs = {}
|
||||
|
||||
for input, value in pairs(inputs) do
|
||||
if value and not prev_inputs[input] then
|
||||
new_inputs[input] = true
|
||||
end
|
||||
end
|
||||
local was_drop_blocked = piece:isDropBlocked(grid)
|
||||
|
||||
if self:canPieceRotate(piece, grid) then
|
||||
-- if not self.held_rotate then
|
||||
-- self:attemptRotate(inputs, piece, grid, initial)
|
||||
-- self.held_rotate = true
|
||||
-- else
|
||||
self:attemptRotate(new_inputs, piece, grid, initial, lastdir)
|
||||
-- end
|
||||
end
|
||||
|
||||
if not initial and not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
PlaySE("bottom")
|
||||
end
|
||||
|
||||
-- prev_inputs becomes the previous inputs
|
||||
for input, value in pairs(inputs) do
|
||||
prev_inputs[input] = inputs[input]
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:attemptRotate(new_inputs, piece, grid, initial, lastdir)
|
||||
local rot_dir = 0
|
||||
|
||||
if (new_inputs["rotate_left"] or new_inputs["rotate_left2"]) then
|
||||
rot_dir = 3
|
||||
if lastdir == 0 then lastdir = -1 end
|
||||
elseif (new_inputs["rotate_right"] or new_inputs["rotate_right2"]) then
|
||||
rot_dir = 1
|
||||
if lastdir == 0 then lastdir = 1 end
|
||||
end
|
||||
if rot_dir == 0 then return end
|
||||
|
||||
local new_piece = piece:withRelativeRotation(rot_dir)
|
||||
self:attemptWallkicks(piece, new_piece, rot_dir, grid, lastdir)
|
||||
end
|
||||
|
||||
function Rotation:attemptWallkicks(piece, new_piece, rot_dir, grid, lastdir)
|
||||
-- wallkick routine designed to maximize flexibility while minimizing teleports
|
||||
|
||||
-- O doesn't kick
|
||||
if (piece.shape == "O") then return end
|
||||
|
||||
-- assess precisely what rows/columns would be blocked given the desired rotation
|
||||
local sides = {top=false,uright=false,lright=false,uleft=false,lleft=false,center=false,bottom=false}
|
||||
local left_exists = false
|
||||
local right_exists = false
|
||||
local kick = {x=0,y=0}
|
||||
for _,offset in pairs(new_piece:getBlockOffsets()) do
|
||||
local x = piece.position.x + offset.x
|
||||
local y = piece.position.y + offset.y
|
||||
if offset.x == 0 then left_exists = true end
|
||||
if offset.x == 2 or offset.x == 3 then right_exists = true end
|
||||
|
||||
if grid:isOccupied(x,y) then
|
||||
if offset.y == 0 then sides.top = true end
|
||||
if offset.y == 3 then sides.bottom = true end
|
||||
if offset.y == 1 and offset.x == 0 then sides.uleft = true end
|
||||
if offset.y == 2 and offset.x == 0 then sides.lleft = true end
|
||||
if offset.y == 1 and (offset.x == 2 or offset.x == 3) then sides.uright = true end
|
||||
if offset.y == 2 and (offset.x == 2 or offset.x == 3) then sides.lright = true end
|
||||
if offset.x == 1 then sides.center = true end
|
||||
end
|
||||
end
|
||||
|
||||
if sides.top then kick = {x=0,y=1}
|
||||
elseif (sides.uleft and sides.lright) or (sides.uright and sides.lleft) or (sides.uright and sides.uleft) then kick = {x=0,y=1}
|
||||
elseif (sides.lleft and sides.lright) then kick = {x=0,y=-1}
|
||||
elseif (sides.lleft or sides.uleft) then kick = {x=1,y=0}
|
||||
elseif (sides.lright or sides.uright) then kick = {x=-1,y=0}
|
||||
elseif sides.center and left_exists then kick = {x=1,y=0}
|
||||
elseif sides.center and right_exists then kick = {x=-1,y=0}
|
||||
elseif sides.bottom then kick = {x=0,y=-1}
|
||||
end
|
||||
|
||||
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=0,y=0})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0,y=0})
|
||||
elseif grid:canPlacePiece(new_piece:withOffset(kick)) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset(kick)
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:movePiece(piece, grid, move, instant)
|
||||
local was_drop_blocked = piece:isDropBlocked(grid)
|
||||
local offset = ({x=0, y=0})
|
||||
local moves = 0
|
||||
local y = piece.position.y
|
||||
if move == "left" then
|
||||
offset.x = -1
|
||||
moves = 1
|
||||
elseif move == "right" then
|
||||
offset.x = 1
|
||||
moves = 1
|
||||
elseif move == "speedleft" then
|
||||
offset.x = -1
|
||||
moves = grid.width
|
||||
elseif move == "speedright" then
|
||||
offset.x = 1
|
||||
moves = grid.width
|
||||
end
|
||||
if not self:canPieceMove(piece, grid) then return end
|
||||
for i = 1, moves do
|
||||
local x = piece.position.x
|
||||
if moves ~= 1 then
|
||||
piece:moveInGrid(offset, 1, grid, instant)
|
||||
else
|
||||
piece:moveInGrid(offset, 1, grid, false)
|
||||
end
|
||||
if piece.position.x ~= x then
|
||||
self:onPieceMove(piece, grid)
|
||||
if piece.locked then break end
|
||||
end
|
||||
end
|
||||
if not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
PlaySE("bottom")
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
local y = piece.position.y
|
||||
if inputs["down"] == true and drop_locked == false then
|
||||
piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
|
||||
else
|
||||
piece:addGravity(gravity, grid, classic_lock)
|
||||
end
|
||||
if piece.position.y ~= y then
|
||||
self:onPieceDrop(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:lockPiece(piece, grid, lock_delay, classic_lock)
|
||||
if piece:isDropBlocked(grid) and piece.lock_delay >= lock_delay then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:get180RotationValue() return 3 end
|
||||
function Rotation:getDefaultOrientation() return 1 end
|
||||
function Rotation:getDrawOffset(shape, orientation) return { x=0, y=0 } end
|
||||
function Rotation:getAboveFieldOffset(shape, orientation)
|
||||
if shape == "I" then
|
||||
return 1
|
||||
else
|
||||
return 2
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:initializePiece(
|
||||
inputs, data, grid, gravity, prev_inputs,
|
||||
move, lock_delay, drop_speed,
|
||||
drop_locked, hard_drop_locked, big, irs, lastdir
|
||||
)
|
||||
local spawn_positions
|
||||
spawn_positions = self.spawn_positions
|
||||
|
||||
local colours
|
||||
colours = self.colourscheme
|
||||
self.last_dir = 0
|
||||
self.held_rotate = false
|
||||
if inputs['left'] then self.last_dir = -1
|
||||
elseif inputs['right'] then self.last_dir = 1 end
|
||||
local spawn_x = math.floor(spawn_positions[data.shape].x / 10 * grid.width)
|
||||
|
||||
local spawn_dy
|
||||
spawn_dy = 0
|
||||
|
||||
local piece = Piece(data.shape, data.orientation - 1, {
|
||||
x = spawn_x or spawn_positions[data.shape].x,
|
||||
y = spawn_positions[data.shape].y - spawn_dy
|
||||
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
|
||||
|
||||
self:onPieceCreate(piece)
|
||||
if irs then
|
||||
self:rotatePiece(inputs, piece, grid, {}, true, lastdir)
|
||||
end
|
||||
return piece
|
||||
end
|
||||
|
||||
function Rotation:onPieceCreate(piece) end
|
||||
|
||||
function Rotation:processPiece(
|
||||
inputs, piece, grid, gravity, prev_inputs,
|
||||
move, lock_delay, drop_speed,
|
||||
drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity, classic_lock, lastdir
|
||||
)
|
||||
self:rotatePiece(inputs, piece, grid, prev_inputs, false, lastdir)
|
||||
self:movePiece(piece, grid, move, gravity >= grid.height)
|
||||
self:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
self:lockPiece(piece, grid, lock_delay, classic_lock)
|
||||
end
|
||||
|
||||
function Rotation:canPieceMove(piece, grid) return true end
|
||||
function Rotation:canPieceRotate(piece, grid) return true end
|
||||
function Rotation:onPieceMove(piece) end
|
||||
function Rotation:onPieceRotate(piece) end
|
||||
function Rotation:onPieceDrop(piece)
|
||||
if piece.position.y > piece.lowest_point then
|
||||
piece.lock_delay = 0
|
||||
piece.lowest_point = piece.position.y
|
||||
end
|
||||
end
|
||||
|
||||
return Rotation
|
||||
287
game/rotation_pent.lua
Normal file
287
game/rotation_pent.lua
Normal file
@@ -0,0 +1,287 @@
|
||||
local Object = require 'libs.classic'
|
||||
local Piece = require 'game.piece'
|
||||
|
||||
local Rotation = Object:extend()
|
||||
|
||||
Rotation.spawn_positions = {
|
||||
I = { x=3, y= -2 },
|
||||
J = { x=3, y= -2 },
|
||||
L = { x=3, y= -2 },
|
||||
O = { x=3, y= -2 },
|
||||
S = { x=3, y= -2 },
|
||||
T = { x=3, y= -2 },
|
||||
Z = { x=3, y= -2 },
|
||||
}
|
||||
|
||||
Rotation.colourscheme = {
|
||||
I = "R",
|
||||
L = "O",
|
||||
J = "B",
|
||||
S = "M",
|
||||
Z = "G",
|
||||
O = "Y",
|
||||
T = "C",
|
||||
}
|
||||
|
||||
Rotation.block_offsets = {
|
||||
I={
|
||||
{ {x=0, y=2}, {x=1, y=2}, {x=2, y=2}, {x=3, y=2}, {x=4, y=2} },
|
||||
{ {x=2, y=0}, {x=2, y=1}, {x=2, y=2}, {x=2, y=3}, {x=2, y=4} },
|
||||
{ {x=0, y=2}, {x=1, y=2}, {x=2, y=2}, {x=3, y=2}, {x=4, y=2} },
|
||||
{ {x=2, y=0}, {x=2, y=1}, {x=2, y=2}, {x=2, y=3}, {x=2, y=4} },
|
||||
},
|
||||
J={
|
||||
{ {x=0, y=2}, {x=1, y=2}, {x=2, y=2}, {x=2, y=3}, {x=3, y=2} },
|
||||
{ {x=1, y=0}, {x=1, y=1}, {x=1, y=2}, {x=0, y=2}, {x=1, y=3} },
|
||||
{ {x=1, y=2}, {x=1, y=3}, {x=2, y=3}, {x=3, y=3}, {x=0, y=3} },
|
||||
{ {x=1, y=1}, {x=2, y=1}, {x=1, y=2}, {x=1, y=3}, {x=1, y=0} },
|
||||
},
|
||||
L={
|
||||
{ {x=0, y=2}, {x=0, y=3}, {x=1, y=2}, {x=2, y=2}, {x=3, y=2} },
|
||||
{ {x=0, y=0}, {x=1, y=0}, {x=1, y=1}, {x=1, y=2}, {x=1, y=3} },
|
||||
{ {x=1, y=3}, {x=2, y=3}, {x=3, y=2}, {x=3, y=3}, {x=0, y=3} },
|
||||
{ {x=1, y=1}, {x=1, y=2}, {x=1, y=3}, {x=2, y=3}, {x=1, y=0} },
|
||||
},
|
||||
O={
|
||||
{ {x=0, y=2}, {x=0, y=3}, {x=1, y=2}, {x=2, y=2}, {x=1, y=3} },
|
||||
{ {x=0, y=1}, {x=1, y=1}, {x=1, y=2}, {x=1, y=3}, {x=0, y=2} },
|
||||
{ {x=0, y=3}, {x=1, y=3}, {x=2, y=2}, {x=2, y=3}, {x=1, y=2} },
|
||||
{ {x=1, y=1}, {x=1, y=2}, {x=1, y=3}, {x=2, y=3}, {x=2, y=2} },
|
||||
},
|
||||
S={
|
||||
{ {x=1, y=2}, {x=2, y=2}, {x=0, y=3}, {x=1, y=3}, {x=3, y=2} },
|
||||
{ {x=0, y=0}, {x=0, y=1}, {x=1, y=1}, {x=1, y=2}, {x=1, y=3} },
|
||||
{ {x=2, y=2}, {x=3, y=2}, {x=1, y=3}, {x=2, y=3}, {x=0, y=3} },
|
||||
{ {x=1, y=1}, {x=1, y=2}, {x=2, y=2}, {x=2, y=3}, {x=1, y=0} },
|
||||
},
|
||||
T={
|
||||
{ {x=0, y=2}, {x=1, y=2}, {x=1, y=3}, {x=2, y=2}, {x=3, y=2} },
|
||||
{ {x=0, y=1}, {x=1, y=0}, {x=1, y=1}, {x=1, y=2}, {x=1, y=3} },
|
||||
{ {x=1, y=3}, {x=2, y=2}, {x=2, y=3}, {x=3, y=3}, {x=0, y=3} },
|
||||
{ {x=2, y=1}, {x=2, y=2}, {x=3, y=2}, {x=2, y=3}, {x=2, y=0} },
|
||||
},
|
||||
Z={
|
||||
{ {x=0, y=2}, {x=1, y=2}, {x=1, y=3}, {x=2, y=3}, {x=2, y=2} },
|
||||
{ {x=1, y=1}, {x=0, y=2}, {x=1, y=2}, {x=0, y=3}, {x=1, y=3} },
|
||||
{ {x=0, y=2}, {x=1, y=2}, {x=1, y=3}, {x=2, y=3}, {x=0, y=3} },
|
||||
{ {x=2, y=1}, {x=1, y=2}, {x=2, y=2}, {x=1, y=3}, {x=1, y=1} },
|
||||
}
|
||||
}
|
||||
|
||||
Rotation.pieces = 7
|
||||
|
||||
-- Component functions.
|
||||
|
||||
function Rotation:new(game_mode)
|
||||
self.game = require 'game.gamemode'
|
||||
end
|
||||
|
||||
function Rotation:rotatePiece(inputs, piece, grid, prev_inputs, initial, lastdir)
|
||||
local new_inputs = {}
|
||||
|
||||
for input, value in pairs(inputs) do
|
||||
if value and not prev_inputs[input] then
|
||||
new_inputs[input] = true
|
||||
end
|
||||
end
|
||||
local was_drop_blocked = piece:isDropBlocked(grid)
|
||||
|
||||
if self:canPieceRotate(piece, grid) then
|
||||
-- if not self.held_rotate then
|
||||
-- self:attemptRotate(inputs, piece, grid, initial)
|
||||
-- self.held_rotate = true
|
||||
-- else
|
||||
self:attemptRotate(new_inputs, piece, grid, initial, lastdir)
|
||||
-- end
|
||||
end
|
||||
|
||||
if not initial and not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
PlaySE("bottom")
|
||||
end
|
||||
|
||||
-- prev_inputs becomes the previous inputs
|
||||
for input, value in pairs(inputs) do
|
||||
prev_inputs[input] = inputs[input]
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:attemptRotate(new_inputs, piece, grid, initial, lastdir)
|
||||
local rot_dir = 0
|
||||
|
||||
if (new_inputs["rotate_left"] or new_inputs["rotate_left2"]) then
|
||||
rot_dir = 3
|
||||
if lastdir == 0 then lastdir = -1 end
|
||||
elseif (new_inputs["rotate_right"] or new_inputs["rotate_right2"]) then
|
||||
rot_dir = 1
|
||||
if lastdir == 0 then lastdir = 1 end
|
||||
end
|
||||
if rot_dir == 0 then return end
|
||||
|
||||
local new_piece = piece:withRelativeRotation(rot_dir)
|
||||
self:attemptWallkicks(piece, new_piece, rot_dir, grid, lastdir)
|
||||
end
|
||||
|
||||
function Rotation:attemptWallkicks(piece, new_piece, rot_dir, grid, lastdir)
|
||||
-- assess precisely what rows/columns would be blocked given the desired rotation
|
||||
local sides = {top=false,uright=false,lright=false,uleft=false,lleft=false,center=false,bottom=false}
|
||||
local left_exists = false
|
||||
local right_exists = false
|
||||
local kick = {x=0,y=0}
|
||||
for _,offset in pairs(new_piece:getBlockOffsets()) do
|
||||
local x = piece.position.x + offset.x
|
||||
local y = piece.position.y + offset.y
|
||||
|
||||
if grid:isOccupied(x,y) then
|
||||
if offset.y <= 1 then sides.top = true end
|
||||
if offset.y == 4 then sides.bottom = true end
|
||||
if offset.x <= 1 then sides.lleft = true end
|
||||
if offset.x >= 2 then sides.lright = true end
|
||||
end
|
||||
end
|
||||
|
||||
if sides.top then kick = {x=0,y=1}
|
||||
elseif sides.bottom then kick = {x=0,y=-1}
|
||||
elseif (sides.lleft or sides.uleft) then kick = {x=1,y=0}
|
||||
elseif (sides.lright or sides.uright) then kick = {x=-1,y=0}
|
||||
end
|
||||
|
||||
if grid:canPlacePiece(new_piece:withOffset({x=0,y=0})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=0,y=0})
|
||||
elseif grid:canPlacePiece(new_piece:withOffset(kick)) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset(kick)
|
||||
elseif grid:canPlacePiece(new_piece:withOffset({x=kick.x*2,y=kick.y*2})) then
|
||||
self:onPieceRotate(piece, grid)
|
||||
piece:setRelativeRotation(rot_dir):setOffset({x=kick.x*2,y=kick.y*2})
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:movePiece(piece, grid, move, instant)
|
||||
local was_drop_blocked = piece:isDropBlocked(grid)
|
||||
local offset = ({x=0, y=0})
|
||||
local moves = 0
|
||||
local y = piece.position.y
|
||||
if move == "left" then
|
||||
offset.x = -1
|
||||
moves = 1
|
||||
elseif move == "right" then
|
||||
offset.x = 1
|
||||
moves = 1
|
||||
elseif move == "speedleft" then
|
||||
offset.x = -1
|
||||
moves = grid.width
|
||||
elseif move == "speedright" then
|
||||
offset.x = 1
|
||||
moves = grid.width
|
||||
end
|
||||
if not self:canPieceMove(piece, grid) then return end
|
||||
for i = 1, moves do
|
||||
local x = piece.position.x
|
||||
if moves ~= 1 then
|
||||
piece:moveInGrid(offset, 1, grid, instant)
|
||||
else
|
||||
piece:moveInGrid(offset, 1, grid, false)
|
||||
end
|
||||
if piece.position.x ~= x then
|
||||
self:onPieceMove(piece, grid)
|
||||
if piece.locked then break end
|
||||
end
|
||||
end
|
||||
if not was_drop_blocked and piece:isDropBlocked(grid) then
|
||||
PlaySE("bottom")
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
local y = piece.position.y
|
||||
if inputs["down"] == true and drop_locked == false then
|
||||
piece:addGravity(math.max(gravity, drop_speed), grid, classic_lock)
|
||||
else
|
||||
piece:addGravity(gravity, grid, classic_lock)
|
||||
end
|
||||
if piece.position.y ~= y then
|
||||
self:onPieceDrop(piece, grid)
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:lockPiece(piece, grid, lock_delay, classic_lock)
|
||||
if piece:isDropBlocked(grid) and piece.lock_delay >= lock_delay then
|
||||
piece.locked = true
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:get180RotationValue() return 3 end
|
||||
function Rotation:getDefaultOrientation() return 1 end
|
||||
function Rotation:getDrawOffset(shape, orientation) return { x=0, y=0 } end
|
||||
function Rotation:getAboveFieldOffset(shape, orientation)
|
||||
if shape == "I" then
|
||||
return 1
|
||||
else
|
||||
return 2
|
||||
end
|
||||
end
|
||||
|
||||
function Rotation:initializePiece(
|
||||
inputs, data, grid, gravity, prev_inputs,
|
||||
move, lock_delay, drop_speed,
|
||||
drop_locked, hard_drop_locked, big, irs, lastdir
|
||||
)
|
||||
local spawn_positions
|
||||
spawn_positions = self.spawn_positions
|
||||
|
||||
local colours
|
||||
colours = self.colourscheme
|
||||
self.last_dir = 0
|
||||
self.held_rotate = false
|
||||
if inputs['left'] then self.last_dir = -1
|
||||
elseif inputs['right'] then self.last_dir = 1 end
|
||||
local spawn_x = math.floor(spawn_positions[data.shape].x / 10 * grid.width)
|
||||
|
||||
local spawn_dy
|
||||
spawn_dy = 0
|
||||
|
||||
local piece = Piece(data.shape, data.orientation - 1, {
|
||||
x = spawn_x or spawn_positions[data.shape].x,
|
||||
y = spawn_positions[data.shape].y - spawn_dy
|
||||
}, self.block_offsets, 0, 0, data.skin, colours[data.shape], big)
|
||||
|
||||
self:onPieceCreate(piece)
|
||||
if irs then
|
||||
self:rotatePiece(inputs, piece, grid, {}, true, lastdir)
|
||||
end
|
||||
return piece
|
||||
end
|
||||
|
||||
function Rotation:onPieceCreate(piece) end
|
||||
|
||||
function Rotation:processPiece(
|
||||
inputs, piece, grid, gravity, prev_inputs,
|
||||
move, lock_delay, drop_speed,
|
||||
drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity, classic_lock, lastdir
|
||||
)
|
||||
self:rotatePiece(inputs, piece, grid, prev_inputs, false, lastdir)
|
||||
self:movePiece(piece, grid, move, gravity >= grid.height)
|
||||
self:dropPiece(
|
||||
inputs, piece, grid, gravity, drop_speed, drop_locked, hard_drop_locked,
|
||||
hard_drop_enabled, additive_gravity, classic_lock
|
||||
)
|
||||
self:lockPiece(piece, grid, lock_delay, classic_lock)
|
||||
end
|
||||
|
||||
function Rotation:canPieceMove(piece, grid) return true end
|
||||
function Rotation:canPieceRotate(piece, grid) return true end
|
||||
function Rotation:onPieceMove(piece) end
|
||||
function Rotation:onPieceRotate(piece) end
|
||||
function Rotation:onPieceDrop(piece)
|
||||
if piece.position.y > piece.lowest_point then
|
||||
piece.lock_delay = 0
|
||||
piece.lowest_point = piece.position.y
|
||||
end
|
||||
end
|
||||
|
||||
return Rotation
|
||||
238
game/vctrl.lua
Normal file
238
game/vctrl.lua
Normal file
@@ -0,0 +1,238 @@
|
||||
local gc_newQuad=love.graphics.newQuad
|
||||
|
||||
---Get distance between two points
|
||||
---@param x1 number
|
||||
---@param y1 number
|
||||
---@param x2 number
|
||||
---@param y2 number
|
||||
---@return number
|
||||
local function math_distance(x1,y1,x2,y2)
|
||||
return ((x1-x2)^2+(y1-y2)^2)^.5
|
||||
end
|
||||
|
||||
local function mDrawQ(obj,quad,x,y,a,k)
|
||||
local _,_,w,h=quad:getViewport()
|
||||
love.graphics.draw(obj,quad,x,y,a,k,nil,w*.5,h*.5)
|
||||
end
|
||||
|
||||
ShowLoadingText('virtual key skin')
|
||||
local empty_quad
|
||||
-- A table containing quads used to draw icons for virtual control system.
|
||||
-- local virtual_quad=setmetatable((function()
|
||||
-- local t={}
|
||||
-- local w=180
|
||||
-- empty_quad=gc_newQuad(0,0,1,1,5*w,7*w)
|
||||
-- for i,name in next,{
|
||||
-- 'left','right','up','down','',
|
||||
-- 'rotate_right','rotate_left','','','',
|
||||
-- '','','','','',
|
||||
-- '','','','','',
|
||||
-- '','','menu_back','','',
|
||||
-- '','','','','',
|
||||
-- '','','','','menu_decide',
|
||||
-- } do if #name>0 then t[name]=gc_newQuad((i-1)%5*w,math.floor((i-1)/5)*w,w,w,5*w,7*w) end end
|
||||
-- t.rotate_right2, t.rotate_left2 = t.rotate_right, t.rotate_left
|
||||
-- return t
|
||||
-- end)(),{
|
||||
-- __index=function() return empty_quad end
|
||||
-- })
|
||||
local virtual_quad=setmetatable((function()
|
||||
local t={}
|
||||
local w=180
|
||||
empty_quad=gc_newQuad(0,0,1,1,5*w,2*w)
|
||||
for i,name in next,{
|
||||
'left','right','up','down','restart',
|
||||
'rotate_right','rotate_left','rotate_right2','rotate_left2','',
|
||||
} do if #name>0 then t[name]=gc_newQuad((i-1)%5*w,math.floor((i-1)/5)*w,w,w,5*w,2*w) end end
|
||||
return t
|
||||
end)(),{
|
||||
__index=function() return empty_quad end
|
||||
})
|
||||
local virtual_texture=love.graphics.newImage('game/vctrlTexture.png')
|
||||
|
||||
|
||||
local control_type={}
|
||||
|
||||
control_type.button={}
|
||||
control_type.button.__index=control_type.button
|
||||
function control_type.button:new(data)
|
||||
local data=data or {}
|
||||
return setmetatable({
|
||||
show=data.show==nil and true or data.show,
|
||||
x=data.x or 320,
|
||||
y=data.y or 240,
|
||||
r=data.r or 80, -- size
|
||||
shape=data.shape or 'circle',
|
||||
key=data.key or 'X',
|
||||
iconSize=data.iconSize or 80,
|
||||
alpha=data.alpha or 0.75,
|
||||
quad=virtual_quad[data.key]
|
||||
},self)
|
||||
end
|
||||
function control_type.button:export()
|
||||
return {
|
||||
type = 'button',
|
||||
show = self.show,
|
||||
x = self.x,
|
||||
y = self.y,
|
||||
r = self.r,
|
||||
shape = self.shape,
|
||||
key = self.key,
|
||||
iconSize = self.iconSize,
|
||||
alpha = self.alpha
|
||||
}
|
||||
end
|
||||
function control_type.button:reset()
|
||||
self.pressed=false
|
||||
self.lastPressTime=-1e99
|
||||
self.pressingID=false
|
||||
end
|
||||
function control_type.button:press(_,_,id)
|
||||
self.pressed=true
|
||||
self.lastPressTime=love.timer.getTime()
|
||||
self.pressing=id
|
||||
-- love.keypressed(self.key, love.keyboard.getScancodeFromKey(self.key))
|
||||
SCENE:onInputPress{input=self.key,type="virtual"}
|
||||
end
|
||||
function control_type.button:release()
|
||||
self.pressed=false
|
||||
self.pressingID=false
|
||||
-- love.keyreleased(self.key,love.keyboard.getScancodeFromKey(self.key))
|
||||
SCENE:onInputRelease{input=self.key,type="virtual"}
|
||||
end
|
||||
function control_type.button:drag(dx,dy)
|
||||
self.x,self.y=self.x+dx,self.y+dy
|
||||
end
|
||||
function control_type.button:draw(forceAlpha)
|
||||
local alpha = forceAlpha or self.alpha
|
||||
love.graphics.setLineWidth(4)
|
||||
if self.shape=='circle' then
|
||||
love.graphics.setColor(0,0,0,alpha)
|
||||
love.graphics.circle('fill',self.x,self.y,self.r-4)
|
||||
|
||||
love.graphics.setColor(1,1,1,self.pressed and .5 or 0)
|
||||
love.graphics.circle('fill',self.x,self.y,self.r-4)
|
||||
|
||||
love.graphics.setColor(1,1,1,alpha)
|
||||
love.graphics.circle('line',self.x,self.y,self.r-2)
|
||||
elseif self.shape=='square' then
|
||||
love.graphics.setColor(0,0,0,alpha)
|
||||
love.graphics.rectangle('fill',self.x-self.r-4,self.y-self.r-4,self.r*2+8,self.r*2+8)
|
||||
|
||||
love.graphics.setColor(1,1,1,self.pressed and .5 or 0)
|
||||
love.graphics.rectangle('fill',self.x-self.r-4,self.y-self.r-4,self.r*2+8,self.r*2+8)
|
||||
|
||||
love.graphics.setColor(1,1,1,alpha)
|
||||
love.graphics.rectangle('line',self.x-self.r-2,self.y-self.r-2,self.r*2+4,self.r*2+4)
|
||||
end
|
||||
if self.iconSize>0 and self.quad then
|
||||
love.graphics.setColor(1,1,1,alpha)
|
||||
local _,_,w,h=self.quad:getViewport()
|
||||
mDrawQ(
|
||||
virtual_texture,
|
||||
self.quad,
|
||||
self.x,self.y,0,
|
||||
self.iconSize/100*math.min(self.r*2/w,self.r*2/h)
|
||||
)
|
||||
end
|
||||
end
|
||||
function control_type.button:getDistance(x,y)
|
||||
if self.shape=='circle' then
|
||||
return math_distance(x,y,self.x,self.y)/self.r
|
||||
elseif self.shape=='square' then
|
||||
return math.max(math.abs(x-self.x),math.abs(y-self.y))/self.r
|
||||
end
|
||||
end
|
||||
|
||||
local touches={}
|
||||
local global_toggle=false
|
||||
VCTRL={}
|
||||
VCTRL.focus=nil -- Focusing buttons
|
||||
|
||||
---@class VCTRL.data
|
||||
---@field type 'button'
|
||||
---@field x number
|
||||
---@field y number
|
||||
---@field shape? string
|
||||
---@field key? string
|
||||
---@field iconSize? number
|
||||
---@field alpha? number
|
||||
---@field show? boolean
|
||||
|
||||
---@param ... VCTRL.data[]
|
||||
---Adding (multiple) virtual button(s)
|
||||
function VCTRL.new(...)
|
||||
for _,d in pairs(...) do table.insert(VCTRL,control_type[d.type]:new(d)) end
|
||||
end
|
||||
|
||||
---@param toggle boolean|false
|
||||
---Enabling virtual control or not
|
||||
function VCTRL.toggle(toggle)
|
||||
if not toggle then
|
||||
-- Release all buttons to prevent button ghost situation
|
||||
for id, b in pairs(touches) do
|
||||
b:release(id)
|
||||
touches[id]=nil
|
||||
end
|
||||
end
|
||||
global_toggle=toggle
|
||||
end
|
||||
|
||||
function VCTRL.clearAll()
|
||||
local toggle = global_toggle
|
||||
VCTRL.toggle(false)
|
||||
global_toggle = toggle
|
||||
|
||||
for i=#VCTRL,1,-1 do VCTRL[i] = nil end
|
||||
collectgarbage()
|
||||
end
|
||||
|
||||
---@param force? boolean Forcing click on hidden widgets?
|
||||
function VCTRL.press(x,y,id,force)
|
||||
if not (global_toggle and id) then return end
|
||||
local obj,closestDist=false,1e99
|
||||
for _, w in ipairs(VCTRL) do
|
||||
if w.show or force then
|
||||
local d=w:getDistance(x,y)
|
||||
if d<=1 and d<closestDist then
|
||||
obj,closestDist=w,d
|
||||
end
|
||||
end
|
||||
end
|
||||
if obj then
|
||||
touches[id]=obj
|
||||
obj:press(x,y,id)
|
||||
VCTRL.focus=obj
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function VCTRL.release(id)
|
||||
if not (global_toggle and id) then return end
|
||||
if touches[id] then
|
||||
touches[id]:release()
|
||||
touches[id]=nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function VCTRL.drag(dx,dy,id)
|
||||
if not global_toggle then return end
|
||||
if touches[id] then
|
||||
touches[id]:drag(dx,dy)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function VCTRL.draw(forceAlpha)
|
||||
if not global_toggle then return end
|
||||
for _, w in ipairs(VCTRL) do
|
||||
if w.show then w:draw(forceAlpha) end
|
||||
end
|
||||
end
|
||||
|
||||
function VCTRL.exportAll()
|
||||
local t = {}
|
||||
for o, k in ipairs(VCTRL) do t[o] = k:export() end
|
||||
return t
|
||||
end
|
||||
BIN
game/vctrlTexture.png
Normal file
BIN
game/vctrlTexture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
BIN
game/vctrlTexture_legacy.png
Normal file
BIN
game/vctrlTexture_legacy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Reference in New Issue
Block a user