const std = @import("std"); const SDL = @import("sdl2"); const Grid = @import("Grid.zig"); const Piece = @import("Piece.zig"); const Bag = @import("Bag.zig"); const Timer = @import("Timer.zig"); const State = @import("flow.zig").State; const renderer = @import("renderer.zig"); const movement = @import("movement.zig"); const Self = @This(); const Action = struct { activate: bool = false, holding: bool = false, code: u8, callback: fn (*Self) void, pub const Offset = enum(usize) { right = 0, left = 1, down = 2, hard = 3, swap = 4, rot_r = 5, rot_l = 6, }; }; grid: Grid, grid_cell_size: i32, grid_pos_x: i32, grid_pos_y: i32, state: State = State.game, bag: Bag, held: ?Piece.Type, piece: Piece, shadow: Piece, renderer: *SDL.SDL_Renderer, action_list: [7]Action = .{ .{ .code = SDL.SDL_SCANCODE_D, .callback = actionRight }, // Right .{ .code = SDL.SDL_SCANCODE_A, .callback = actionLeft }, // Left .{ .code = SDL.SDL_SCANCODE_S, .callback = actionDown }, // Down .{ .code = SDL.SDL_SCANCODE_W, .callback = actionHard }, // Instant Drop .{ .code = SDL.SDL_SCANCODE_SPACE, .callback = actionSwap }, // Swap Piece .{ .code = SDL.SDL_SCANCODE_RIGHT, .callback = actionRotR }, // Rotate .{ .code = SDL.SDL_SCANCODE_LEFT, .callback = actionRotL }, // Rotate }, timer_down: Timer, pub fn init(_renderer: *SDL.SDL_Renderer) Self { var ret = Self{ .grid = Grid.init(), .grid_cell_size = undefined, .grid_pos_x = undefined, .grid_pos_y = undefined, .bag = Bag.init(), .piece = undefined, .shadow = undefined, .held = null, .renderer = _renderer, .timer_down = Timer.init(33), }; // Calculate positions { var aux_w: i32 = undefined; var aux_h: i32 = undefined; _ = SDL.SDL_GetRendererOutputSize(_renderer, &aux_w, &aux_h); ret.grid_cell_size = @divFloor(@minimum(aux_w, aux_h), 32); ret.grid_pos_x = @divFloor(aux_w, 2) - (ret.grid_cell_size * @divFloor(Grid.ncolumns, 2)); ret.grid_pos_y = @divFloor(aux_h, 2) - (ret.grid_cell_size * @divFloor(Grid.nrows + Grid.buffer, 2)); ret.piece = ret.bag.pop(); } return ret; } pub fn tick(self: *Self) State { // TIMERS if (self.piece.timer_dropped.finished()) { self.piece.timer_dropped.stop(); self.piece.dropped = true; } if (self.action_list[@enumToInt(Action.Offset.down)].holding) { if (!self.timer_down.started) { self.timer_down.start(); } else if (self.timer_down.finished()) { self.timer_down.stop(); self.action_list[@enumToInt(Action.Offset.down)].holding = false; } } else { self.timer_down.stop(); } // DROP if (self.piece.dropped) { self.grid = movement.drop(self.grid, self.piece); // New Piece self.piece = self.bag.pop(); } // KEY EVENTS var key_state = SDL.SDL_GetKeyboardState(null); for (self.action_list) |*action| { if (key_state[action.*.code] == 1 and !action.*.holding) { action.*.holding = true; action.*.activate = true; } if (key_state[action.*.code] == 0) { action.*.holding = false; } // Action if (action.*.activate) { action.*.activate = false; @call(.{}, action.*.callback, .{self}); } } // Update Shadow { self.shadow = movement.shadow(self.grid, self.piece); self.shadow.color.a /= 4; } self.grid.clearLines(); renderer.render(self.*); return self.state; } // ACTION CALLBACKS fn actionDown(self: *Self) void { self.piece = movement.moveDown(self.grid, self.piece); } fn actionRight(self: *Self) void { self.piece = movement.moveRight(self.grid, self.piece); } fn actionLeft(self: *Self) void { self.piece = movement.moveLeft(self.grid, self.piece); } fn actionHard(self: *Self) void { self.piece = movement.hardDrop(self.grid, self.piece); } fn actionSwap(self: *Self) void { if (self.held) |held_piece| { if (!self.piece.swapped) { self.held = self.piece.piece_type; self.piece = Piece.init(held_piece); self.piece.swapped = true; } } else { self.held = self.piece.piece_type; self.piece = self.bag.pop(); self.piece.swapped = true; } } fn actionRotR(self: *Self) void { self.piece = movement.rotateRight(self.grid, self.piece); } fn actionRotL(self: *Self) void { self.piece = movement.rotateLeft(self.grid, self.piece); }