~sircmpwn/tetrominoes

ddb5cf68fae193e803c80a996e43e9b717e8b0a3 — Drew DeVault 2 years ago 1f7cd7a
Implement line clearing
5 files changed, 132 insertions(+), 71 deletions(-)

M board.ha
A draw.ha
M main.ha
M notes.txt
M update.ha
M board.ha => board.ha +61 -0
@@ 1,4 1,5 @@
use sdl2;
use fmt; // XXX: TEMP

def BOARD_WIDTH: int = 10;
def BOARD_HEIGHT: int = 20;


@@ 54,6 55,12 @@ fn draw_board(state: *state, board: *board) (void | sdl2::error) = {
			h = tex.height * BOARD_SCALE,
		})?;
	};
	switch (state.state) {
	case gamestate::CLEAR =>
		return;
	case => void;
	};

	const active = (board.active_x, board.active_y);
	for (let y = 0; y < TETROMINO_HEIGHT; y += 1)
	for (let x = 0; x < TETROMINO_WIDTH; x += 1) {


@@ 75,3 82,57 @@ fn draw_board(state: *state, board: *board) (void | sdl2::error) = {
		})?;
	};
};

fn commit(state: *state) void = {
	let board = &state.board.cells;
	let active = &state.board.active[state.board.active_i];
	const active_x = state.board.active_x;
	const active_y = state.board.active_y;
	for (let y = 0; y < TETROMINO_HEIGHT; y += 1)
	for (let x = 0; x < TETROMINO_WIDTH; x += 1) {
		const board_ix = (y + active_y) * BOARD_WIDTH + (x + active_x);
		const active_ix = y * TETROMINO_WIDTH + x;
		if (board_ix: size >= len(board)) {
			continue;
		};
		if (active[active_ix] != 0) {
			board[board_ix] = active[active_ix];
		};
	};

	static let clearbuf: [TETROMINO_HEIGHT]int = [0...];
	let clear = clearbuf[..0];
	state.state = gamestate::SPAWN;
	for (let y = active_y; y < BOARD_HEIGHT; y += 1)
	for (let x = 0, sum = 0; x < BOARD_WIDTH; x += 1) {
		const board_ix = y * BOARD_WIDTH + x;
		const cell = state.board.cells[board_ix];
		if (cell != 0) {
			sum += 1;
		};
		if (sum == BOARD_WIDTH) {
			state.state = gamestate::CLEAR;
			static append(clear, y);
		};
	};
	state.clearlines = clear;
	state.clearframe = -1;
};

fn shiftrows(state: *state) void = {
	let j = 0;
	for (let i = len(state.clearlines): int - 1; i >= 0; i -= 1) {
		shiftrow(state, state.clearlines[i] + j);
		j += 1;
	};
};

fn shiftrow(state: *state, row: int) void = {
	const cells = &state.board.cells;
	for (let y = row; y > 0; y -= 1) {
		for (let x = 0; x < BOARD_WIDTH; x += 1) {
			const src = &cells[(y - 1) * BOARD_WIDTH + x];
			cells[y * BOARD_WIDTH + x] = *src;
		};
	};
};

A draw.ha => draw.ha +52 -0
@@ 0,0 1,52 @@
use sdl2;

fn draw(state: *state) (void | sdl2::error) = {
	sdl2::set_render_draw_color(state.render, 0, 0, 0, 255)?;
	sdl2::render_clear(state.render)?;
	sdl2::render_copy(state.render, state.scene.tex, null, &sdl2::rect{
		x = 0,
		y = 0,
		w = 640,
		h = 480,
	})?;

	draw_board(state, &state.board)?;

	switch (state.state) {
	case gamestate::CLEAR =>
		draw_clear(state)?;
	case => void; // No special drawing required
	};

	sdl2::render_present(state.render);
};

fn draw_clear(state: *state) (void | sdl2::error) = {
	if (state.clearframe < 0) {
		return;
	};
	for (let i = 0z; i < len(state.clearlines); i += 1) {
		draw_clearline(state, state.clearlines[i])?;
	};
};

fn draw_clearline(state: *state, y: int) (void | sdl2::error) = {
	const inrect = &sdl2::rect {
		x = state.clearframe * 4,
		y = 0,
		w = 4,
		h = 4,
	};
	const tex = state.clear;
	const board = &state.board;
	const offs = (board.offs_x * SCENE_SCALE, board.offs_y * SCENE_SCALE);
	for (let x = 0; x < BOARD_WIDTH; x += 1) {
		const outrect = &sdl2::rect {
			x = offs.0 + x * BOARD_SCALE * TETROMINO_WIDTH,
			y = offs.1 + y * BOARD_SCALE * TETROMINO_HEIGHT,
			w = TETROMINO_WIDTH * BOARD_SCALE,
			h = TETROMINO_HEIGHT * BOARD_SCALE,
		};
		sdl2::render_copy(state.render, tex.tex, inrect, outrect)?;
	};
};

M main.ha => main.ha +1 -36
@@ 35,6 35,7 @@ type state = struct {
	faster: bool,
	lastcolor: color,
	clearframe: int,
	clearlines: []int,
};

export fn main() void = {


@@ 106,42 107,6 @@ fn run() (void | sdl2::error) = {
	};
};

fn draw(state: *state) (void | sdl2::error) = {
	sdl2::set_render_draw_color(state.render, 0, 0, 0, 255)?;
	sdl2::render_clear(state.render)?;
	sdl2::render_copy(state.render, state.scene.tex, null, &sdl2::rect{
		x = 0,
		y = 0,
		w = 640,
		h = 480,
	})?;

	draw_board(state, &state.board)?;

	switch (state.state) {
	case gamestate::CLEAR =>
		draw_clear(state);
	case => void; // No special drawing required
	};

	sdl2::render_present(state.render);
};

fn draw_clear(state: *state) void = {
	// TODO: Expand me
	sdl2::render_copy(state.render, state.clear.tex, &sdl2::rect{
		x = state.clearframe * 4,
		y = 0,
		w = 4,
		h = 4,
	}, &sdl2::rect{
		x = 0,
		y = 0,
		w = 8,
		h = 8,
	})?;
};

fn load_texture(render: *sdl2::renderer, path: str) (texture | sdl2::error) = {
	const tex = image::load_texture(render, path)?;
	let width = 0, height = 0;

M notes.txt => notes.txt +1 -1
@@ 2,7 2,6 @@
- instant place
- fix thumbsticks
- scorekeeping/display
- clear completed rows (+animation)
- try to accomodate rotation by moving pieces left/right
- piece swapping
- keyboard input


@@ 10,6 9,7 @@
- victory (+animation)
- music
- sound effects
- alternate clear animations

later:
- menu system

M update.ha => update.ha +17 -34
@@ 116,6 116,15 @@ fn do_clear(state: *state, now: u32) void = {
	state.clearframe += 1;
	state.clearframe %= 12;
	state.next = now + (state.speed / 6);

	if (state.clearframe == 4) {
		clear_lines(state);
	};
	if (state.clearframe == 9) {
		state.state = gamestate::SPAWN;
		state.board.active_y = -4;
		shiftrows(state);
	};
};

fn schedule_tick(state: *state, now: u32) void = {


@@ 126,40 135,6 @@ fn schedule_tick(state: *state, now: u32) void = {
	};
};

fn commit(state: *state) void = {
	let board = &state.board.cells;
	let active = &state.board.active[state.board.active_i];
	const active_x = state.board.active_x;
	const active_y = state.board.active_y;
	for (let y = 0; y < TETROMINO_HEIGHT; y += 1)
	for (let x = 0; x < TETROMINO_WIDTH; x += 1) {
		const board_ix = (y + active_y) * BOARD_WIDTH + (x + active_x);
		const active_ix = y * TETROMINO_WIDTH + x;
		if (board_ix: size >= len(board)) {
			continue;
		};
		if (active[active_ix] != 0) {
			board[board_ix] = active[active_ix];
		};
	};

	static let clearbuf: [TETROMINO_HEIGHT]int = [0...];
	let clear = clearbuf[..0];
	state.state = gamestate::SPAWN;
	for (let y = active_y; y < BOARD_HEIGHT; y += 1)
	for (let x = 0, sum = 0; x < BOARD_WIDTH; x += 1) {
		const board_ix = y * BOARD_WIDTH + x;
		const cell = state.board.cells[board_ix];
		if (cell != 0) {
			sum += 1;
		};
		if (sum == BOARD_WIDTH) {
			state.state = gamestate::CLEAR;
			static append(clear, y);
		};
	};
};

fn move_left(state: *state) void = {
	if (state.board.active_x > 0) {
		state.board.active_x -= 1;


@@ 243,3 218,11 @@ fn canmove(
	};
	return true;
};

fn clear_lines(state: *state) void = {
	for (let i = 0z; i < len(state.clearlines); i += 1)
	for (let x = 0; x < BOARD_WIDTH; x += 1) {
		const y = state.clearlines[i];
		state.board.cells[y * BOARD_WIDTH + x] = 0;
	};
};