~sircmpwn/tetrominoes

a85a92141ac53d358c1bcdb3400ccff8f69a5f76 — Eyal Sawady 5 months ago 307d926
Add keyboard input

Including rebinding
4 files changed, 130 insertions(+), 5 deletions(-)

A config.ha
M main.ha
M notes.txt
M update.ha
A config.ha => config.ha +46 -0
@@ 0,0 1,46 @@
use format::ini;
use fs;
use io;
use os;
use sdl2;

fn config(
	keybindings: *[](sdl2::keycode, action),
	cfg: io::handle,
) (void | ini::error | sdl2::error) = {
	let sc = ini::scan(cfg);
	defer ini::finish(&sc);
	for (true) match (ini::next(&sc)?) {
	case io::EOF =>
		break;
	case let e: ini::entry =>
		switch (e.0) {
		case "keyboard" =>
			const action = switch (e.1) {
			case "commit" =>
				yield action::COMMIT;
			case "down" =>
				yield action::DOWN;
			case "left" =>
				yield action::LEFT;
			case "right" =>
				yield action::RIGHT;
			case "rotleft" =>
				yield action::ROTLEFT;
			case "rotright" =>
				yield action::ROTRIGHT;
			case =>
				continue;
			};
			let keycode = sdl2::get_key_from_name(e.2)?;
			for (let i = 0z; i < len(keybindings); i += 1) {
				if (keybindings[i].0 == keycode) {
					delete(keybindings[i]);
					i -= 1;
				};
			};
			append(keybindings, (keycode, action));
		case => void;
		};
	};
};

M main.ha => main.ha +51 -3
@@ 1,10 1,15 @@
use dirs;
use fmt;
use format::ini;
use fs;
use getopt;
use io;
use math::random;
use sdl2;
use sdl2::{renderer_flags, window_flags};
use os;
use sdl2::image;
use sdl2::mixer;
use sdl2::{renderer_flags, window_flags};
use sdl2;
use time;
use types;



@@ 22,6 27,15 @@ type gamestate = enum {
	GAMEOVER,
};

type action = enum {
	COMMIT,
	DOWN,
	LEFT,
	RIGHT,
	ROTLEFT,
	ROTRIGHT,
};

type state = struct {
	run: bool,
	window: *sdl2::window,


@@ 48,19 62,52 @@ type state = struct {
	// Intro
	fadein_start: u32,
	fadein_finish: u32,

	keybindings: [](sdl2::keycode, action),
};

export fn main() void = {
	match (run()) {
	case let err: sdl2::error =>
		fmt::fatal("SDL error: {}", sdl2::strerror(err));
	case let err: ini::error =>
		fmt::fatal("Error loading config file: {}", ini::strerror(err));
	case let err: fs::error =>
		fmt::fatal("Error: {}", fs::strerror(err));
	case void => void;
	};
};

fn run() (void | fs::error | sdl2::error) = {
fn run() (void | fs::error | sdl2::error | ini::error) = {
	let keybindings: [](sdl2::keycode, action) = alloc([
		(sdl2::keycode::UP, action::ROTLEFT),
		(sdl2::keycode::x, action::ROTLEFT),
		(sdl2::keycode::SPACE, action::COMMIT),
		(sdl2::keycode::LCTRL, action::ROTRIGHT),
		(sdl2::keycode::RCTRL, action::ROTRIGHT),
		(sdl2::keycode::z, action::ROTRIGHT),
		(sdl2::keycode::LEFT, action::LEFT),
		(sdl2::keycode::RIGHT, action::RIGHT),
		(sdl2::keycode::DOWN, action::DOWN),
		(sdl2::keycode::KP_8, action::COMMIT),
		(sdl2::keycode::KP_4, action::LEFT),
		(sdl2::keycode::KP_6, action::RIGHT),
		(sdl2::keycode::KP_2, action::COMMIT),
		(sdl2::keycode::KP_1, action::ROTLEFT),
		(sdl2::keycode::KP_5, action::ROTLEFT),
		(sdl2::keycode::KP_9, action::ROTLEFT),
		(sdl2::keycode::KP_3, action::ROTRIGHT),
		(sdl2::keycode::KP_7, action::ROTRIGHT),
	]);
	defer free(keybindings);
	const cfg = dirs::configfs("tetrominoes");
	defer fs::close(cfg);
	match (fs::open(cfg, "config")) {
	case let cfg: io::handle =>
		config(&keybindings, cfg)?;
	case fs::error => void;
	};

	sdl2::init(sdl2::init_flags::VIDEO
		| sdl2::init_flags::AUDIO
		| sdl2::init_flags::GAMECONTROLLER)?;


@@ 101,6 148,7 @@ fn run() (void | fs::error | sdl2::error) = {
		},
		rand = random::init(seed),
		speed = 60 * 1000 / 84, // TODO: Set this to BPM properly
		keybindings = keybindings,
		...
	};
	defer sdl2::destroy_texture(state.piece.tex);

M notes.txt => notes.txt +1 -2
@@ 3,14 3,13 @@
- level files & music timings
- fix thumbsticks
- scorekeeping/display
- keyboard input
- victory (+animation)
- alternate clear animations

later:
- menu system
	- options (music/sfx volume)
- key/controller re-bindings
- controller re-binding
- custom piece designs
- local multiplayer
- online multiplayer

M update.ha => update.ha +32 -0
@@ 60,6 60,38 @@ fn update(state: *state) (void | sdl2::error) = {
			state.faster = false;
		case => void;
		};
	case event_type::KEYDOWN =>
		for (let i = 0z; i < len(state.keybindings); i += 1) {
			if (state.keybindings[i].0 == ev.key.keysym.sym) {
				switch (state.keybindings[i].1) {
				case action::COMMIT =>
					if (state.state != gamestate::INTRO) {
						state.board.active_y =
							state.board.ghost_y;
						commit(state);
						state.next = now;
					};
				case action::DOWN =>
					state.faster = true;
				case action::LEFT =>
					move_left(state);
				case action::RIGHT =>
					move_right(state);
				case action::ROTLEFT =>
					rotate(state, 1);
				case action::ROTRIGHT =>
					rotate(state, -1);
				};
			};
		};
	case event_type::KEYUP =>
		for (let i = 0z; i < len(state.keybindings); i += 1) {
			if (state.keybindings[i].0 == ev.key.keysym.sym) {
				if (state.keybindings[i].1 == action::DOWN) {
					state.faster = false;
				};
			};
		};
	case => void;
	};