Skip to content

Commit

Permalink
Input handling; update active piece; piece lock
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Nov 2, 2024
1 parent 3cc99ff commit 6d9c601
Showing 1 changed file with 149 additions and 33 deletions.
182 changes: 149 additions & 33 deletions src/component/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { type Component, onCleanup, onMount } from 'solid-js'
import './App.module.scss'

export const createOrientations = (desc: PieceDescription): PieceOrientationState => {
const orientation = { blocs: desc.blocs }
const orientation = { blocks: desc.blocks }
if (desc.rotationMode === 'off') return { orientations: [orientation, orientation, orientation, orientation] }
const offset = desc.rotationMode === 'between' ? 1 : 0
const cw = { blocs: orientation.blocs.map(b => rotateCw(b)) }
const cw2 = { blocs: cw.blocs.map(b => rotateCw(b)) }
const cw3 = { blocs: cw2.blocs.map(b => rotateCw(b)) }
const cw = { blocks: orientation.blocks.map(b => rotateCw(b)) }
const cw2 = { blocks: cw.blocks.map(b => rotateCw(b)) }
const cw3 = { blocks: cw2.blocks.map(b => rotateCw(b)) }

switch (desc.rotationMode) {
case 'normal':
Expand All @@ -19,9 +19,9 @@ export const createOrientations = (desc: PieceDescription): PieceOrientationStat
return {
orientations: [
orientation,
{ blocs: cw.blocs.map(b => b.add(vec(1, 0).scale(offset))) },
{ blocs: cw2.blocs.map(b => b.add(vec(1, -1).scale(offset))) },
{ blocs: cw3.blocs.map(b => b.add(vec(0, -1).scale(offset))) }
{ blocks: cw.blocks.map(b => b.add(vec(1, 0).scale(offset))) },
{ blocks: cw2.blocks.map(b => b.add(vec(1, -1).scale(offset))) },
{ blocks: cw3.blocks.map(b => b.add(vec(0, -1).scale(offset))) }
]
}
}
Expand All @@ -40,6 +40,8 @@ export const createOrientations = (desc: PieceDescription): PieceOrientationStat
*/
export const rotateCw = (position: Vector): Vector => vec(position.y, -position.x)

export const createButton = () => ({ held: false, down: false, pressed: false, released: false })

export type Board = Color[][]

/**
Expand All @@ -50,12 +52,12 @@ export type Board = Color[][]
export type Color = number

export type PieceDescription = {
blocs: Vector[]
blocks: Vector[]
rotationMode: 'normal' | 'between' | 'off'
}

export type Piece = {
blocs: Vector[]
blocks: Vector[]
}

export type PieceOrientationState = {
Expand All @@ -81,50 +83,62 @@ export type Input = {

export type Button = {
held: boolean
down: boolean
pressed: boolean
released: boolean
}

export const piecesDescription: PieceDescription[] = [
{
// I piece
blocs: [vec(0, 0), vec(-1, 0), vec(1, 0), vec(2, 0)],
blocks: [vec(0, 0), vec(-1, 0), vec(1, 0), vec(2, 0)],
rotationMode: 'between'
},
{
// O piece
blocs: [vec(0, 0), vec(1, 0), vec(1, 1), vec(0, 1)],
blocks: [vec(0, 0), vec(1, 0), vec(1, 1), vec(0, 1)],
rotationMode: 'off'
},
{
// T piece
blocs: [vec(0, 0), vec(0, 1), vec(1, 0), vec(-1, 0)],
blocks: [vec(0, 0), vec(0, 1), vec(1, 0), vec(-1, 0)],
rotationMode: 'normal'
},
{
// S piece
blocs: [vec(0, 0), vec(-1, 0), vec(0, 1), vec(1, 1)],
blocks: [vec(0, 0), vec(-1, 0), vec(0, 1), vec(1, 1)],
rotationMode: 'normal'
},
{
// Z piece
blocs: [vec(0, 0), vec(1, 0), vec(0, 1), vec(-1, 1)],
blocks: [vec(0, 0), vec(1, 0), vec(0, 1), vec(-1, 1)],
rotationMode: 'normal'
},
{
// J piece
blocs: [vec(0, 0), vec(-1, 1), vec(-1, 0), vec(1, 0)],
blocks: [vec(0, 0), vec(-1, 1), vec(-1, 0), vec(1, 0)],
rotationMode: 'normal'
},
{
// L piece
blocs: [vec(0, 0), vec(1, 1), vec(1, 0), vec(-1, 0)],
blocks: [vec(0, 0), vec(1, 1), vec(1, 0), vec(-1, 0)],
rotationMode: 'normal'
}
]

export const pieces: PieceOrientationState[] = piecesDescription.map(createOrientations)

export const input: Input = {
left: createButton(),
right: createButton(),
ccw: createButton(),
cw: createButton(),
r180: createButton(),
soft: createButton(),
hard: createButton(),
hold: createButton()
}

export const gameConfig = {
boardSize: vec(10, 20),
blockScreenSize: vec(40, 40),
Expand All @@ -139,7 +153,17 @@ export const gameConfig = {
'#db2d20',
'#0160f4',
'#ee6522'
]
],
keyMap: {
left: 'KeyA',
right: 'KeyD',
ccw: 'KeyJ',
cw: 'KeyL',
r180: 'KeyK',
soft: 'KeyS',
hard: 'Space',
hold: 'KeyI'
}
}

export const App: Component = () => {
Expand Down Expand Up @@ -167,53 +191,145 @@ export const App: Component = () => {
const drawBoard = (board: Board): void => {
const gridOpts = { fill: gameConfig.colors[0], stroke: gameConfig.colors[1] }

for (let i = 0; i < gameConfig.boardSize.x; i++) {
for (let j = 0; j < gameConfig.boardSize.y; j++) {
const pos = boardToScreen(vec(i, j))
for (let j = 0; j < gameConfig.boardSize.x; j++) {
for (let i = 0; i < Math.max(gameConfig.boardSize.y, board.length); i++) {
const piecePos = vec(j, i)
const pos = boardToScreen(piecePos)
if (board.length > i) {
ctx.rect(pos, gameConfig.blockScreenSize, {
fill: gameConfig.colors[board[i][j]],
stroke: gameConfig.colors[1]
})
}
ctx.rect(pos, gameConfig.blockScreenSize, gridOpts)
}
}

// TODO: draw board
}

const drawActivePiece = (activePiece: ActivePiece): void => {
const opts = { fill: gameConfig.colors[activePiece.pieceId + 3], stroke: gameConfig.colors[1] }

pieces[activePiece.pieceId].orientations[activePiece.orientation].blocs.forEach(block => {
const pos = activePiece.position.add(block)
activePieceBoardPos(activePiece).blocks.forEach(pos => {
ctx.rect(boardToScreen(pos), gameConfig.blockScreenSize, opts)
})
}

const activePieceBoardPos = (activePiece: ActivePiece): Piece => {
return {
blocks: pieces[activePiece.pieceId].orientations[activePiece.orientation].blocks.map(b =>
activePiece.position.add(b)
)
}
}

const insertPiece = (board: Board, activePiece: ActivePiece): void => {
activePieceBoardPos(activePiece).blocks.forEach(pos => {
const missingLines = 1 + pos.y - board.length
for (let i = 0; i < missingLines; i++) {
board.push(new Array(gameConfig.boardSize.x).fill(0))
}
board[pos.y][pos.x] = activePiece.pieceId + 3
})
}

const collides = (board: Board, activePiece: ActivePiece): boolean => {
const activeBlocks = activePieceBoardPos(activePiece)
return activeBlocks.blocks.some(pos => {
if (pos.x < 0 || pos.x >= gameConfig.boardSize.x || pos.y < 0) return true
if (pos.y >= board.length) return false
const boardBlock = board[pos.y][pos.x]
return boardBlock > 0
})
}

const handleKeyboard = (): void => {
const handleKey = (e: KeyboardEvent): void => {
if (e.repeat) return
const result = Object.entries(gameConfig.keyMap).find(([, code]) => code === e.code)
if (!result) return
const [action] = result
const button = input[action as keyof Input]
button.down = e.type === 'keydown'
}
window.addEventListener('keydown', handleKey)
window.addEventListener('keyup', handleKey)
}

const updateInput = (): void => {
Object.values(input).forEach(button => {
button.pressed = button.down && !button.held
button.released = !button.down && button.held
button.held = button.down
})
}

const updateActivePiece = (): void => {
if (!activePiece) throw Error()
const originalPos = activePiece.position

if (input.right.pressed) {
activePiece.position = activePiece.position.add(vec(1, 0))
}
if (input.left.pressed) {
activePiece.position = activePiece.position.add(vec(-1, 0))
}
if (collides(board, activePiece)) {
activePiece.position = originalPos
}

const originalOrient = activePiece.orientation
if (input.cw.pressed) {
activePiece.orientation = (activePiece.orientation + 1) % 4
}
if (input.ccw.pressed) {
activePiece.orientation = (4 + activePiece.orientation - 1) % 4
}

// TODO: wall kicks
if (collides(board, activePiece)) {
activePiece.orientation = originalOrient
}

if (input.hard.pressed) {
while (!collides(board, activePiece)) {
activePiece.position = activePiece.position.add(vec(0, -1))
}
// TODO: will ascend if not checked for game over
activePiece.position = activePiece.position.add(vec(0, 1))
insertPiece(board, activePiece)
activePiece = undefined
}
}

onMount(() => {
ctx = new Context(canvas)
resizeWindow()
window.addEventListener('resize', resizeWindow)
handleKeyboard()

engine = new Engine()
engine.start()
subs.push(
engine.eventDispatcher.beforeUpdate.subscribe(() => {
updateInput()

if (!activePiece) {
const spawnPos = vec(Math.floor(gameConfig.boardSize.x / 2) - 1, gameConfig.boardSize.y)
const pieceId = Math.floor(Math.random() * piecesDescription.length)
// TODO: piece selection
activePiece = { pieceId: 0, position: spawnPos, orientation: 0 }
}
if (engine.frameInfo.id % 10 === 0) {
activePiece.orientation = (activePiece.orientation + 1) % 4
}
if (engine.frameInfo.id % 40 === 0) {
activePiece.pieceId = (activePiece.pieceId + 1) % 7
activePiece.orientation = 0
activePiece = { pieceId, position: spawnPos, orientation: 0 }
}

updateActivePiece()
})
)
subs.push(
engine.eventDispatcher.beforeDraw.subscribe(() => {
ctx.clear()
drawBoard(board)
drawActivePiece(activePiece!)
if (activePiece) {
drawActivePiece(activePiece!)
}
})
)
})
Expand Down

0 comments on commit 6d9c601

Please sign in to comment.