Compare commits

...

31 Commits

Author SHA1 Message Date
Dendy e281938578 Add timer to line clearing 2023-10-07 21:26:09 +02:00
Dendy 1b0ab7c602 Add .ini files to .gitignore 2023-10-07 20:38:08 +02:00
Dusk 200d96470b Use msaa antialiasing 2023-10-02 16:25:22 +02:00
Dusk fcbdca4cf7 Change yellow color 2023-10-02 16:25:05 +02:00
Dusk 32795ef27f Make input config use code strings instead of i32 2023-10-02 16:24:30 +02:00
Dusk 3477be28e8 Implement input configuration loading from i32 2023-10-02 15:38:07 +02:00
Dusk 174c247e43 Merge remote-tracking branch 'refs/remotes/origin/main' 2023-10-01 15:30:46 +02:00
Dusk 9420ca82fe Temporal changes 2023-10-01 15:28:18 +02:00
Dendy e370e2bb66 Implement getting all the input names 2023-09-30 18:49:14 +02:00
Dendy 2b4dbc2875 Remove test cell in main menu 2023-09-30 15:47:39 +02:00
Dendy c2786fdceb Make inputs mutable 2023-09-29 11:27:02 +02:00
Dusk 6b32f45534 implment config file parsing via iterator 2023-09-28 18:22:44 +02:00
Dusk be50d94f78 Load config test 2023-09-27 21:08:04 +02:00
Dusk e72ab56197 New cell texture 2023-09-25 15:10:45 +02:00
Dusk 4f459e5125 Score rendering 2023-09-25 15:09:32 +02:00
Dusk 8fbcc7e915 Fix texture rendering for free cells 2023-09-24 22:53:52 +02:00
Dusk 0a94c68195 Add texture 2023-09-24 19:15:33 +02:00
Dusk 3e552932ad Texture support WIP 2023-09-24 19:11:25 +02:00
Dusk b666e83a0c fix | Render FPS last 2023-09-24 17:53:07 +02:00
Dusk 74cd3f5127 Show FPS 2023-09-22 20:59:55 +02:00
Dusk fb9968c19f Implement timers 2023-09-22 20:30:19 +02:00
Dusk 4f3599fa42 Reimplement game actions 2023-09-22 20:15:13 +02:00
Dusk a86fda392d Reimplement game rendering 2023-09-22 19:58:47 +02:00
Dusk e21e184c58 Implement actions with the new input system 2023-09-22 19:23:40 +02:00
Dusk 82e8cfdbe1 Implement input handler 2023-09-22 14:03:33 +02:00
Dusk bdf4e0b920 Proper rendering of MainMenu 2023-09-22 12:16:29 +02:00
Dusk 2ed65aa6ae Implement text rendering 2023-09-22 12:16:16 +02:00
Dusk f6007b90c8 Add color rendering 2023-09-22 12:04:16 +02:00
Dusk d88ef81a02 Implement rectangle rendering & more improvements 2023-09-21 18:17:20 +02:00
Dusk 914e1127db Add basic raylib functionality: open window 2023-09-21 16:40:53 +02:00
Dusk 821162015a Remove SDL & OpenGL dependency
Make the code compile without the need for SDL and OpenGL with
the intention of migrating towards Raylib. What is going to be needed in
the future has been commented, and the other stuff removed. It runs but
apparently does nothing.
2023-09-21 13:53:52 +02:00
26 changed files with 773 additions and 524 deletions

3
.gitignore vendored
View File

@ -90,3 +90,6 @@ zig-out
zig-cache
zig-cache/h
zig-cache/z
*.kra
*.ini

9
.gitmodules vendored
View File

@ -1,9 +0,0 @@
[submodule "lib/SDL.zig"]
path = lib/SDL.zig
url = ../SDL.zig
[submodule "lib/zgl"]
path = lib/zgl
url = https://github.com/ziglibs/zgl
[submodule "lib/zlm"]
path = lib/zlm
url = https://github.com/ziglibs/zlm

View File

@ -1,5 +1,4 @@
const std = @import("std");
const Sdk = @import("lib/SDL.zig/build.zig");
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
@ -19,17 +18,8 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
});
const sdk = Sdk.init(b, null);
sdk.link(exe, .dynamic);
exe.linkSystemLibrary("sdl2_ttf");
exe.linkSystemLibrary("sdl2_image");
exe.linkSystemLibrary("gl");
exe.linkSystemLibrary("epoxy");
const relative = std.Build.FileSource.relative;
exe.addModule("zgl", b.createModule(.{ .source_file = relative("lib/zgl/zgl.zig") }));
exe.addModule("zlm", b.createModule(.{ .source_file = relative("lib/zlm/zlm.zig") }));
exe.addModule("sdl2", sdk.getWrapperModule());
exe.linkSystemLibrary("raylib");
exe.linkSystemLibrary("c");
b.installArtifact(exe);

@ -1 +0,0 @@
Subproject commit 36e192a74f3be878766e1aa3e6f13ba5f92650cb

@ -1 +0,0 @@
Subproject commit b40eff53f384ced9481d1c8754651976de22588c

@ -1 +0,0 @@
Subproject commit 2dc8c0db2e92529bb618ca639bb891b6187fa579

BIN
res/cell.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,17 +1,19 @@
const Self = @This();
const SDL = @import("sdl2");
const input = @import("input.zig");
activate: bool = false,
holding: bool = false,
scancode: SDL.Scancode, // SDL Keycode
input: *const input.Input,
callback: *const anyopaque, // MUST be a function with void return
pub fn init(scancode: SDL.Scancode, callback: *const anyopaque) Self {
pub fn init(tmp_input: *const input.Input, callback: *const anyopaque) Self {
return Self{
.scancode = scancode,
.input = tmp_input,
.callback = callback,
};
}
pub fn call(self: Self, state: anytype) void {
@call(.auto, @ptrCast(*const fn (@TypeOf(state)) void, self.callback), .{state});
@call(.auto, @as(*const fn (@TypeOf(state)) void, @ptrCast(self.callback)), .{state});
}

86
src/Config/config.zig Normal file
View File

@ -0,0 +1,86 @@
const std = @import("std");
const fs = std.fs;
const io = std.io;
pub const Option = struct {
key: []const u8,
value: []const u8,
};
pub const ConfigIterator = struct {
filename: []const u8,
file: fs.File,
buf: [1024]u8,
i: i32 = 0,
pub fn next(self: *ConfigIterator) ?Option {
if (self.i >= 1024) {
std.debug.print("WARNING: Maximum number of lines in config reached, ignoring the rest.\n", .{});
return null;
}
var reader = self.file.reader();
var fbs = io.fixedBufferStream(&self.buf);
var writer = fbs.writer();
reader.streamUntilDelimiter(writer, '\n', self.buf.len) catch |err| {
if (err == error.EndOfStream) {
return null;
} else {
std.debug.print("ConfigIterator Error: {any}\n", .{err});
unreachable;
}
};
var line = fbs.getWritten();
var keyval = std.mem.split(u8, line, "=");
var result = Option{
.key = keyval.next() orelse {
std.debug.print("Config {s}: line {} with contents '{s}' is invalid. Skipping.\n", .{
self.filename,
self.i,
line,
});
self.i += 1;
return self.next();
},
.value = keyval.next() orelse {
std.debug.print("Config {s}: line {} with contents '{s}' is invalid. Skipping.\n", .{
self.filename,
self.i,
line,
});
self.i += 1;
return self.next();
},
};
if (keyval.next() != null) {
std.debug.print("Config {s}: line {} with contents '{s}' is invalid. Skipping.\n", .{
self.filename,
self.i,
line,
});
self.i += 1;
return self.next();
}
self.i += 1;
return result;
}
pub fn deinit(self: *ConfigIterator) void {
self.file.close();
}
};
pub fn getConfigIterator(path: []const u8) !ConfigIterator {
var iterator = ConfigIterator{
.filename = path,
.file = try fs.cwd().createFile(path, .{ .read = true, .truncate = false }),
.buf = undefined,
};
return iterator;
}

11
src/Config/controls.zig Normal file
View File

@ -0,0 +1,11 @@
@import("../input.zig");
fn loadControls() void {
}
fn createControls() void {
}
pub const controls = config.init("coso.ini")I

View File

@ -1,5 +1,4 @@
const std = @import("std");
const SDL = @import("sdl2");
const Grid = @import("Game/Grid.zig");
const Piece = @import("Game/Piece.zig");
@ -7,10 +6,13 @@ const Bag = @import("Game/Bag.zig");
const Timer = @import("Timer.zig");
const State = @import("flow.zig").State;
const Action = @import("Action.zig");
const input = @import("input.zig");
const Renderer = @import("Game/Renderer.zig");
const movement = @import("Game/movement.zig");
pub const Score = u64;
const Self = @This();
// Codes to access action list
@ -36,14 +38,16 @@ held: ?Piece.Type,
piece: Piece,
shadow: Piece,
score: Score = 0,
action_list: ActionList = .{
.right = Action.init(SDL.Scancode.d, actionRight), // Right
.left = Action.init(SDL.Scancode.a, actionLeft), // Left
.down = Action.init(SDL.Scancode.s, actionDown), // Down
.hard = Action.init(SDL.Scancode.w, actionHard), // Instant Drop
.swap = Action.init(SDL.Scancode.space, actionSwap), // Swap Piece
.rot_r = Action.init(SDL.Scancode.right, actionRotR), // Rotate
.rot_l = Action.init(SDL.Scancode.left, actionRotL), // Rotate
.right = Action.init(&input.game_right, actionRight), // Right
.left = Action.init(&input.game_left, actionLeft), // Left
.down = Action.init(&input.game_down, actionDown), // Down
.hard = Action.init(&input.game_drop, actionHard), // Instant Drop
.swap = Action.init(&input.game_swap_piece, actionSwap), // Swap Piece
.rot_r = Action.init(&input.game_rotate_r, actionRotR), // Rotate
.rot_l = Action.init(&input.game_rotate_l, actionRotL), // Rotate
},
timer_down: Timer,
@ -52,6 +56,7 @@ timer_left_das: Timer,
timer_right_arr: Timer,
timer_right_das: Timer,
timer_gravity: Timer,
timer_line_clear: Timer,
needs_reinit: bool = false,
@ -65,12 +70,13 @@ pub fn init(renderer: *Renderer.Renderer) Self {
.held = null,
.renderer = Renderer.init(renderer),
.timer_down = Timer.init(50),
.timer_left_arr = Timer.init(50),
.timer_left_das = Timer.init(200),
.timer_right_arr = Timer.init(50),
.timer_right_das = Timer.init(200),
.timer_gravity = Timer.init(200),
.timer_down = Timer.init(0.05),
.timer_left_arr = Timer.init(0.05),
.timer_left_das = Timer.init(0.2),
.timer_right_arr = Timer.init(0.05),
.timer_right_das = Timer.init(0.2),
.timer_gravity = Timer.init(0.3),
.timer_line_clear = Timer.init(1),
};
// Get a piece from the bag
@ -81,6 +87,10 @@ pub fn init(renderer: *Renderer.Renderer) Self {
// Main Game loop
pub fn tick(self: *Self) State {
if (self.timer_line_clear.started and !self.timer_line_clear.finished()) {
self.renderer.render(self.*);
return self.state;
}
// TIMERS
// Dropping a piece
@ -168,17 +178,16 @@ pub fn tick(self: *Self) State {
}
// KEY EVENTS
var key_state = SDL.getKeyboardState();
const action_fields = std.meta.fields(ActionList);
inline for (action_fields) |field| {
std.debug.print("\n{any}", .{field.name});
// REVIEW: Is this necessary?
const action = &@field(self.action_list, field.name);
if (key_state.isPressed(action.*.scancode) and !action.*.holding) {
if (action.input.isDown() and !action.*.holding) {
action.*.holding = true;
action.*.activate = true;
}
if (!key_state.isPressed(action.*.scancode)) {
if (!action.input.isDown()) {
action.*.holding = false;
}
@ -192,7 +201,7 @@ pub fn tick(self: *Self) State {
// Update Shadow
{
self.shadow = movement.shadow(self.grid, self.piece);
self.shadow.color.a /= 4;
self.shadow.color[3] /= 4;
}
// Check for a top out
@ -206,7 +215,25 @@ pub fn tick(self: *Self) State {
}
}
self.grid.clearLines();
// Check for empty lines. If there are, then start the clearing sequence
if (!self.timer_line_clear.started) {
if (self.grid.updateLinesToClear()) {
self.timer_line_clear.start();
}
}
// Don't go and clear them until the timer has finished
else if (self.timer_line_clear.finished()) {
const clear_score = self.grid.clearLines();
if (clear_score > 0) {
self.timer_line_clear.stop();
self.score += clear_score;
} else {
std.debug.print("WARNING: Clear line timer stopped but there was nothign to clear. This situation isn't valid\n", .{});
}
}
// NOTE: If for some reason game doesn't stop and another line has been cleared
// before the timer reached the end, that line will be processed right after
// the timer stops. Behaviour shouldn't get any funkier than that.
self.renderer.render(self.*);

View File

@ -10,17 +10,17 @@ prng: std.rand.DefaultPrng,
pub fn init() Self {
var ret = Self{
.contents = undefined,
.prng = std.rand.DefaultPrng.init(@intCast(u64, std.time.milliTimestamp())),
.prng = std.rand.DefaultPrng.init(@as(u64, @intCast(std.time.milliTimestamp()))),
};
// We must fill all of the array
for (ret.contents[0..7], 0..) |_, i| {
ret.contents[i] = @intToEnum(Piece.Type, i);
ret.contents[i] = @as(Piece.Type, @enumFromInt(i));
}
// Then we shuffle
ret.prng.random().shuffle(?Piece.Type, ret.contents[0..7]);
for (ret.contents[7..14], 0..) |_, i| {
ret.contents[i + 7] = @intToEnum(Piece.Type, i);
ret.contents[i + 7] = @as(Piece.Type, @enumFromInt(i));
}
// Then we shuffle
ret.updateSeed();
@ -46,7 +46,7 @@ pub fn pop(self: *Self) Piece {
// Get more pieces if needed
if (self.contents[7] == null) {
for (self.contents[7..14], 0..) |_, i| {
self.contents[i + 7] = @intToEnum(Piece.Type, i);
self.contents[i + 7] = @as(Piece.Type, @enumFromInt(i));
}
self.updateSeed();
self.prng.random().shuffle(?Piece.Type, self.contents[7..14]);
@ -56,5 +56,5 @@ pub fn pop(self: *Self) Piece {
}
fn updateSeed(self: *Self) void {
self.prng.seed(@intCast(u64, std.time.milliTimestamp()));
self.prng.seed(@as(u64, @intCast(std.time.milliTimestamp())));
}

View File

@ -1,16 +1,15 @@
const std = @import("std");
const SDL = @import("sdl2");
const Color = @import("../color.zig");
const color = @import("../color.zig");
const Self = @This();
free: bool,
color: SDL.Color,
color: color.Color,
pub fn init() Self {
return Self{
.free = true,
.color = Color.dark_grey,
.color = color.dark_grey,
};
}

View File

@ -15,6 +15,7 @@ pub const buffer = 10;
cells: [nrows][ncolumns]Cell,
topped: bool = false,
lines_to_clear: [nrows]bool = .{false} ** nrows,
pub fn init() Self {
var self = Self{
@ -30,20 +31,44 @@ pub fn init() Self {
return self;
}
pub fn clearLines(self: *Self) void {
pub fn updateLinesToClear(self: *Self) bool {
var ret: bool = false;
// Look at each row
for (self.cells, 0..) |_, y| {
// Look at each cell in the line
for (self.cells[y], 0..) |cell_x, x| {
if (cell_x.free) {
// It is free, this line isn't to be cleared
break;
} else {
// Reached the end of the column?
if (x == self.cells[y].len - 1) {
// Delete current row and bring the others down
for (self.cells[1..y], 0..) |_, i| {
self.cells[y - i] = self.cells[y - i - 1];
}
self.lines_to_clear[y] = true;
ret = true;
}
}
}
}
return ret;
}
pub fn clearLines(self: *Self) u8 {
var ncleared: u8 = 0;
// Go line by line, checking if any of them needs to be cleared
for (self.lines_to_clear, 0..) |_, y| {
if (self.lines_to_clear[y]) {
// Delete current row and bring the others down
for (self.cells[1..y], 0..) |_, i| {
self.cells[y - i] = self.cells[y - i - 1];
}
ncleared += 1;
}
}
self.lines_to_clear = .{false} ** nrows;
return ncleared;
}

View File

@ -1,4 +1,3 @@
const SDL = @import("sdl2");
const std = @import("std");
const Grid = @import("Grid.zig");
@ -51,7 +50,7 @@ dropped: bool = false,
swapped: bool = false,
piece_type: Type,
color: SDL.Color,
color: color.Color,
pub fn init(piece_type: Type) Self {
return Self{
@ -59,7 +58,7 @@ pub fn init(piece_type: Type) Self {
.col = (Grid.ncolumns / 2) - (ncols / 2),
.piece_type = piece_type,
.timer_dropped = Timer.init(500),
.timer_dropped = Timer.init(0.5),
.structure = switch (piece_type) {
Type.o => .{
@ -133,21 +132,21 @@ pub fn rotate(self: Self, dir: Rot) Self {
if (dir == Rot.right) {
// Cicle through rotation stage
new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) + 1), 4));
new_piece.rot_stage = @as(RotStage, @enumFromInt(@mod((@intFromEnum(new_piece.rot_stage) + 1), 4)));
// Rotate structure CW
inline for (sequences) |seq| {
inline for (seq, 0..) |_, i| {
const refi = @mod((@intCast(i32, i) - 1), 4);
const refi = @mod((@as(i32, @intCast(i)) - 1), 4);
new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
}
}
} else {
// Cicle through rotation stage
new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) - 1), 4));
new_piece.rot_stage = @as(RotStage, @enumFromInt(@mod((@intFromEnum(new_piece.rot_stage) - 1), 4)));
// Rotate structure CCW
inline for (sequences) |seq| {
inline for (seq, 0..) |_, i| {
const refi = @mod((@intCast(i32, i) + 1), 4);
const refi = @mod((@as(i32, @intCast(i)) + 1), 4);
new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
}
}
@ -157,21 +156,21 @@ pub fn rotate(self: Self, dir: Rot) Self {
if (dir == Rot.right) {
// Cicle through rotation stage
new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) + 1), 4));
new_piece.rot_stage = @as(RotStage, @enumFromInt(@mod((@intFromEnum(new_piece.rot_stage) + 1), 4)));
// Rotate structure CW
inline for (sequences) |seq| {
inline for (seq, 0..) |_, i| {
const refi = @mod((@intCast(i32, i) - 1), 4);
const refi = @mod((@as(i32, @intCast(i)) - 1), 4);
new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
}
}
} else {
// Cicle through rotation stage
new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) - 1), 4));
new_piece.rot_stage = @as(RotStage, @enumFromInt(@mod((@intFromEnum(new_piece.rot_stage) - 1), 4)));
// Rotate structure CCW
inline for (sequences) |seq| {
inline for (seq, 0..) |_, i| {
const refi = @mod((@intCast(i32, i) + 1), 4);
const refi = @mod((@as(i32, @intCast(i)) + 1), 4);
new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
}
}

View File

@ -1,5 +1,4 @@
const SDL = @import("sdl2");
const std = @import("std");
const Game = @import("../Game.zig");
const Bag = @import("Bag.zig");
const Grid = @import("Grid.zig");
@ -14,6 +13,7 @@ renderer: *Renderer,
grid_cell_size: i32,
grid_pos_x: i32,
grid_pos_y: i32,
cell_texture: Renderer.Texture,
pub fn init(renderer: *Renderer) Self {
var wsize = renderer.getOutputSize();
@ -26,6 +26,7 @@ pub fn init(renderer: *Renderer) Self {
.grid_pos_x = grid_pos_x,
.grid_pos_y = grid_pos_y,
.grid_cell_size = grid_cell_size,
.cell_texture = Renderer.Texture.init("res/cell.png"),
};
}
@ -33,36 +34,31 @@ pub fn renderBag(self: *Self, game: Game) void {
const pos_x = self.grid_pos_x + ((Grid.ncolumns + 1) * self.grid_cell_size);
const pos_y = self.grid_pos_y + (Grid.buffer * self.grid_cell_size);
var r: u8 = 0;
var g: u8 = 0;
var b: u8 = 0;
var a: u8 = 255;
for (game.bag.contents[0..5], 0..) |_, i| {
if (game.bag.contents[i]) |piece_type| {
var piece = Piece.init(piece_type);
for (piece.structure, 0..) |_, y| {
for (piece.structure[y], 0..) |_, x| {
const new_x = pos_x + @as(i32, @intCast(x)) * self.grid_cell_size;
const new_y = pos_y + (@as(i32, @intCast(y)) + @as(i32, @intCast(i * piece.structure.len))) * self.grid_cell_size;
if (piece.structure[y][x]) {
r = piece.color.r;
g = piece.color.g;
b = piece.color.b;
a = piece.color.a;
self.cell_texture.drawTo(new_x, new_y, self.grid_cell_size, self.grid_cell_size, piece.color);
} else {
r = piece.color.r;
g = piece.color.g;
b = piece.color.b;
a = 50;
const alpha = 50;
self.renderer.setColor([_]u8{
piece.color[0] -| 30, // r
piece.color[1] -| 30, // g
piece.color[2] -| 30, // b
alpha,
});
self.renderer.fillRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
self.renderer.setColor(.{ piece.color[0], piece.color[1], piece.color[2], alpha });
self.renderer.fillRectangle(new_x + 1, new_y + 1, self.grid_cell_size - 2, self.grid_cell_size - 2);
}
const new_x = pos_x + @intCast(i32, x) * self.grid_cell_size;
const new_y = pos_y + (@intCast(i32, y) + @intCast(i32, i * piece.structure.len)) * self.grid_cell_size;
self.renderer.setColor(r, g, b, a);
self.renderer.fillRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
self.renderer.setColor(r -| 30, g -| 30, b -| 30, a);
self.renderer.drawRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
}
}
}
@ -73,35 +69,30 @@ pub fn renderHeld(self: *Self, game: Game) void {
const pos_x = self.grid_pos_x - (5 * self.grid_cell_size);
const pos_y = self.grid_pos_y + (Grid.buffer * self.grid_cell_size);
var r: u8 = 0;
var g: u8 = 0;
var b: u8 = 0;
var a: u8 = 255;
if (game.held) |held_type| {
var held = Piece.init(held_type);
for (held.structure, 0..) |_, y| {
for (held.structure[y], 0..) |_, x| {
const new_x = pos_x + @as(i32, @intCast(x)) * self.grid_cell_size;
const new_y = pos_y + @as(i32, @intCast(y)) * self.grid_cell_size;
if (held.structure[y][x]) {
r = held.color.r;
g = held.color.g;
b = held.color.b;
a = held.color.a;
self.cell_texture.drawTo(new_x, new_y, self.grid_cell_size, self.grid_cell_size, held.color);
} else {
r = held.color.r;
g = held.color.g;
b = held.color.b;
a = 50;
const alpha = 50;
self.renderer.setColor([_]u8{
held.color[0] -| 30, // r
held.color[1] -| 30, // g
held.color[2] -| 30, // b
alpha,
});
self.renderer.fillRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
self.renderer.setColor(.{ held.color[0], held.color[1], held.color[2], alpha });
self.renderer.fillRectangle(new_x + 1, new_y + 1, self.grid_cell_size - 2, self.grid_cell_size - 2);
}
const new_x = pos_x + @intCast(i32, x) * self.grid_cell_size;
const new_y = pos_y + @intCast(i32, y) * self.grid_cell_size;
self.renderer.setColor(r, g, b, a);
self.renderer.fillRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
self.renderer.setColor(r -| 30, g -| 30, b -| 30, a);
self.renderer.drawRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
}
}
}
@ -113,6 +104,7 @@ pub fn render(self: *Self, game: Game) void {
renderPiece(self, game.shadow);
renderBag(self, game);
renderHeld(self, game);
renderScore(self, game.score);
}
pub fn renderPiece(self: *Self, piece: Piece) void {
@ -126,18 +118,10 @@ pub fn renderPiece(self: *Self, piece: Piece) void {
continue;
}
var r = piece.color.r;
var g = piece.color.g;
var b = piece.color.b;
var a = piece.color.a;
const new_x = pos_x + (@as(i32, @intCast(x)) + piece.col) * self.grid_cell_size;
const new_y = pos_y + (@as(i32, @intCast(y)) + piece.row) * self.grid_cell_size;
const new_x = pos_x + (@intCast(i32, x) + piece.col) * self.grid_cell_size;
const new_y = pos_y + (@intCast(i32, y) + piece.row) * self.grid_cell_size;
self.renderer.setColor(r, g, b, a);
self.renderer.fillRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
self.renderer.setColor(r -| 30, g -| 30, b -| 30, a);
self.renderer.drawRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
self.cell_texture.drawTo(new_x, new_y, self.grid_cell_size, self.grid_cell_size, piece.color);
}
}
}
@ -145,23 +129,37 @@ pub fn renderPiece(self: *Self, piece: Piece) void {
pub fn renderGrid(self: *Self, grid: Grid) void {
const pos_x = self.grid_pos_x;
const pos_y = self.grid_pos_y;
const lg = color.light_grey;
var visible = grid.cells[Grid.buffer..];
for (visible, 0..) |_, y| {
for (visible[y], 0..) |_, x| {
var r = visible[y][x].color.r;
var g = visible[y][x].color.g;
var b = visible[y][x].color.b;
var a = visible[y][x].color.a;
const new_x = pos_x + @as(i32, @intCast(x)) * self.grid_cell_size;
const new_y = pos_y + (@as(i32, @intCast(y)) + Grid.buffer) * self.grid_cell_size;
const new_x = pos_x + @intCast(i32, x) * self.grid_cell_size;
const new_y = pos_y + (@intCast(i32, y) + Grid.buffer) * self.grid_cell_size;
self.renderer.setColor(r, g, b, a);
// Background
self.renderer.setColor(color.light_grey);
self.renderer.fillRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
self.renderer.setColor(lg.r, lg.g, lg.b, lg.a);
self.renderer.drawRectangle(new_x, new_y, self.grid_cell_size, self.grid_cell_size);
if (visible[y][x].free) {
// Not dropped
self.renderer.setColor(visible[y][x].color);
self.renderer.fillRectangle(new_x + 1, new_y + 1, self.grid_cell_size - 2, self.grid_cell_size - 2);
} else {
// Dropped
self.cell_texture.drawTo(new_x, new_y, self.grid_cell_size, self.grid_cell_size, visible[y][x].color);
}
}
}
}
pub fn renderScore(self: *Self, score: Game.Score) void {
const s_size = self.renderer.getOutputSize();
var buffer: [32]u8 = undefined;
var score_string = std.fmt.bufPrintZ(&buffer, "Score: {d:0>16}", .{score}) catch "Score: " ++ "9" ** 16;
self.renderer.setColor(.{0,0,0,255});
self.renderer.drawText(score_string, s_size.width - 400, 50, 20);
}

1
src/Game/Score.zig Normal file
View File

@ -0,0 +1 @@
const Score = @Type(u64);

View File

@ -68,8 +68,8 @@ pub fn drop(grid: Grid, piece: Piece) Grid {
for (piece.structure, 0..) |_, y| {
for (piece.structure[y], 0..) |_, x| {
if (piece.structure[y][x]) {
new_grid.cells[@intCast(usize, piece.row + @intCast(i32, y))][@intCast(usize, piece.col + @intCast(i32, x))].free = false;
new_grid.cells[@intCast(usize, piece.row + @intCast(i32, y))][@intCast(usize, piece.col + @intCast(i32, x))].color = piece.color;
new_grid.cells[@as(usize, @intCast(piece.row + @as(i32, @intCast(y))))][@as(usize, @intCast(piece.col + @as(i32, @intCast(x))))].free = false;
new_grid.cells[@as(usize, @intCast(piece.row + @as(i32, @intCast(y))))][@as(usize, @intCast(piece.col + @as(i32, @intCast(x))))].color = piece.color;
}
}
}
@ -86,11 +86,11 @@ fn checkCollision(grid: Grid, piece: Piece) bool {
for (piece.structure, 0..) |_, y| {
for (piece.structure[y], 0..) |_, x| {
if (piece.structure[y][x] and
((@intCast(i32, x) + piece.col > Grid.ncolumns - 1) or
(@intCast(i32, x) + piece.col < 0) or
(@intCast(i32, y) + piece.row > Grid.nrows - 1) or
(@intCast(i32, y) + piece.row < 0) or
(!grid.cells[@intCast(usize, piece.row + @intCast(i32, y))][@intCast(usize, piece.col + @intCast(i32, x))].free)))
((@as(i32, @intCast(x)) + piece.col > Grid.ncolumns - 1) or
(@as(i32, @intCast(x)) + piece.col < 0) or
(@as(i32, @intCast(y)) + piece.row > Grid.nrows - 1) or
(@as(i32, @intCast(y)) + piece.row < 0) or
(!grid.cells[@as(usize, @intCast(piece.row + @as(i32, @intCast(y))))][@as(usize, @intCast(piece.col + @as(i32, @intCast(x))))].free)))
{
return true;
}
@ -219,7 +219,7 @@ fn checkTTwist(grid: Grid, piece: Piece) bool {
var row = piece.row + rows[i];
var col = piece.col + cols[i];
if ((row > 0 and row < Grid.nrows and col > 0 and col < Grid.ncolumns) and
!grid.cells[@intCast(usize, row)][@intCast(usize, col)].free)
!grid.cells[@as(usize, @intCast(row))][@as(usize, @intCast(col))].free)
{
std.debug.print("Hit\n", .{});
diagonals += 1;

View File

@ -1,5 +1,5 @@
const std = @import("std");
const SDL = @import("sdl2");
//const SDL = @import("sdl2");
const range = @import("utils.zig").range;
const MenuSelection = @import("MainMenu/MenuSelection.zig");
@ -9,6 +9,8 @@ const Renderer = @import("MainMenu/Renderer.zig");
const State = @import("flow.zig").State;
const Action = @import("Action.zig");
const input = @import("input.zig");
const Self = @This();
const ActionList = struct {
@ -20,11 +22,11 @@ const ActionList = struct {
};
action_list: ActionList = .{
.right = Action.init(SDL.Scancode.right, actionTabRight), // Tab Right
.left = Action.init(SDL.Scancode.left, actionTabLeft), // Tab left
.down = Action.init(SDL.Scancode.down, actionSelDown), // Go down
.up = Action.init(SDL.Scancode.up, actionSelUp), // Go up
.select = Action.init(SDL.Scancode.@"return", actionSelect), // Select
.right = Action.init(&input.menu_right, actionTabRight), // Tab Right
.left = Action.init(&input.menu_left, actionTabLeft), // Tab left
.down = Action.init(&input.menu_down, actionSelDown), // Go down
.up = Action.init(&input.menu_up, actionSelUp), // Go up
.select = Action.init(&input.menu_accept, actionSelect), // Select
},
tab_list: [3]MenuTab = .{
@ -39,8 +41,9 @@ tab_list: [3]MenuTab = .{
MenuTab.init("Online", .{ 105, 179, 86 }, &.{
MenuSelection.init("Play", play),
MenuSelection.init("Print", print),
MenuSelection.init("Print", print),
MenuSelection.init("Print", print),
// FIXME: Crashes upon trying to render them
//MenuSelection.init("Print", print),
//MenuSelection.init("Print", print),
}),
},
@ -65,14 +68,14 @@ pub fn getSel(self: Self, offset: i32) MenuSelection {
}
pub fn getSelIdx(self: Self, offset: i32) usize {
const len = @intCast(i32, self.getTab().contents.len);
const sel = @intCast(i32, self.getTab().sel);
const len = @as(i32, @intCast(self.getTab().contents.len));
const sel = @as(i32, @intCast(self.getTab().sel));
var ret: i32 = undefined;
if (offset < 0) {
ret = sel;
for (range(@intCast(usize, -offset))) |_| {
for (range(@as(usize, @intCast(-offset)))) |_| {
ret -= 1;
if (ret < 0) {
ret = len - 1;
@ -86,7 +89,7 @@ pub fn getSelIdx(self: Self, offset: i32) usize {
//std.debug.print("len {},offset {},ret {}, sel {}\n", .{ len, offset, ret, sel });
}
return @intCast(usize, ret);
return @as(usize, @intCast(ret));
}
pub fn getTab(self: Self) MenuTab {
@ -101,16 +104,17 @@ pub fn tick(self: *Self) State {
//const sel = self.getSel();
//const tab = self.getTab();
var key_state = SDL.getKeyboardState();
const action_fields = std.meta.fields(ActionList);
inline for (action_fields) |field| {
// REVIEW: Is this necessary?
const action = &@field(self.action_list, field.name);
if (key_state.isPressed(action.*.scancode) and !action.*.holding) {
if (action.input.isDown() and !action.*.holding) {
action.*.holding = true;
action.*.activate = true;
}
if (!key_state.isPressed(action.*.scancode)) {
if (!action.input.isDown()) {
action.*.holding = false;
}
@ -120,6 +124,7 @@ pub fn tick(self: *Self) State {
action.*.call(self);
}
}
//std.debug.print(
//\\Tab: {s}
//\\Selection: {s}

View File

@ -32,12 +32,10 @@ pub fn render(self: Self, main_menu: MainMenu) void {
};
const wsize = self.renderer.getOutputSize();
const screen_width = @intCast(i32, wsize.width);
const screen_height = @intCast(i32, wsize.height);
for (tabs, 0..) |u_tab, u_tab_i| {
//const tab = @intCast(i32, u_tab);
const tab_i = @intCast(i32, u_tab_i);
const tab_i = @as(i32, @intCast(u_tab_i));
const curr_tab = main_menu.tab_list[u_tab];
// Auxiliary variables to claculate the center of the screen
@ -47,12 +45,12 @@ pub fn render(self: Self, main_menu: MainMenu) void {
// Current selection vertical offset
const sel_y_offset = y_spacing * 12;
// Number of items below selection
const n_sel_below = @intCast(usize, @divExact((screen_height - sel_y_offset), (height + y_spacing)));
const n_sel_below = @as(usize, @intCast(@divExact((wsize.height - sel_y_offset), (height + y_spacing))));
// Number of items below selection
const n_sel_above = @intCast(usize, @divExact((sel_y_offset), (height + y_spacing)));
const n_sel_above = @as(usize, @intCast(@divExact((sel_y_offset), (height + y_spacing))));
// Move it from the left to the center
const centering = @divExact((screen_width - (total_width + total_spacing)), 2);
const centering = @divExact((wsize.width - (total_width + total_spacing)), 2);
const x = tab_i * (width + x_spacing) + centering;
@ -64,13 +62,13 @@ pub fn render(self: Self, main_menu: MainMenu) void {
// Current selection
{
const y = @intCast(i32, sel_y_offset);
const y = @as(i32, @intCast(sel_y_offset));
// TODO: The shadow should be static, it is easier like this rn tho
if (tab_i == 1) {
// Shadow
self.renderer.setColor(0, 0, 0, 30);
self.renderer.setColor(.{ 0, 0, 0, 30 });
self.renderer.fillRectangleEx(x + 10, y + 10, width, height, skew);
}
@ -78,51 +76,53 @@ pub fn render(self: Self, main_menu: MainMenu) void {
}
for (range(n_sel_below), 0..) |_, i| {
const aux_sel: i32 = @intCast(i32, i + 1);
const aux_sel: i32 = @as(i32, @intCast(i + 1));
const curr_sel: usize = main_menu.getSelIdx(aux_sel);
const y = @intCast(i32, sel_y_offset + ((y_spacing + height) * (aux_sel)));
const y = @as(i32, @intCast(sel_y_offset + ((y_spacing + height) * (aux_sel))));
self.renderMenu(x - ((skew + 8) * aux_sel), y, curr_tab, curr_sel, alpha, false);
}
for (range(n_sel_above), 0..) |_, i| {
const aux_sel: i32 = -@intCast(i32, i + 1);
const aux_sel: i32 = -@as(i32, @intCast(i + 1));
const curr_sel: usize = main_menu.getSelIdx(aux_sel);
const y = @intCast(i32, sel_y_offset + ((y_spacing + height) * (aux_sel)));
const y = @as(i32, @intCast(sel_y_offset + ((y_spacing + height) * (aux_sel))));
self.renderMenu(x - ((skew + 8) * aux_sel), y, curr_tab, curr_sel, alpha, false);
}
// Tab header
self.renderer.setColor(curr_tab.color[0], curr_tab.color[1], curr_tab.color[2], alpha);
self.renderer.setColor(.{ curr_tab.color[0], curr_tab.color[1], curr_tab.color[2], alpha });
self.renderer.fillRectangleEx(x - 25, 10, width + 50, height + 50, skew);
}
// TODO: Set the Color depending on the Main Menu color
self.renderer.setColorF(0.91, 0.85, 0.65, 0.50);
self.renderer.setColor(.{ 232, 217, 166, 127 });
self.renderer.fillRectangleEx(-150, 0, @divTrunc(wsize.width, 3), wsize.height, skew);
self.renderer.fillRectangleEx(@intCast(i32, screen_width) - 300, 0, @divTrunc(wsize.width, 3), wsize.height, skew);
self.renderer.fillRectangleEx(wsize.width - 300, 0, @divTrunc(wsize.width, 3), wsize.height, skew);
}
fn renderMenu(self: Self, x: i32, y: i32, tab: MenuTab, sel: usize, a: u8, selected: bool) void {
// White background
self.renderer.setColor(255, 255, 255, a);
self.renderer.setColor(.{ 255, 255, 255, a });
self.renderer.fillRectangleEx(x, y, width, height, skew);
//_ = sel;
if (selected) {
// Set color if selected
self.renderer.setColor(tab.color[0], tab.color[1], tab.color[2], a);
self.renderer.setColor(.{ tab.color[0], tab.color[1], tab.color[2], a });
} else {
// Set black color, not selected
self.renderer.setColor(0, 0, 0, a);
self.renderer.setColor(.{ 0, 0, 0, a });
}
const margin = 20;
//self.renderer.setColorF(1, 1, 1, 1);
self.renderer.drawText(
tab.contents[sel].name,
x + margin,
y + margin,
height - margin * 2,
);
var text = Renderer.Texture.fromText(tab.contents[sel].name, height - margin * 2);
self.renderer.renderTexture(text, x + margin, y + margin);
//self.renderer.fillRectangleEx(x + margin, y + margin, width - margin * 2, height - margin * 2, skew);
//self.renderer.fillRectangleEx(x + margin, y + margin, width - margin * 2 - 12, height - margin * 2, skew);
self.renderer.fillRectangleEx(x + width - 6, y, 6, height, skew);
self.renderer.fillRectangleEx(x + width - 12, y, 3, height, skew);
}

View File

@ -1,177 +1,35 @@
const std = @import("std");
const sdl = @import("sdl2");
const zlm = @import("zlm");
const gl = @import("zgl");
const m = zlm.SpecializeOn(f32);
const rl = @import("raylib.zig");
const Self = @This();
const colors = @import("color.zig");
window: sdl.Window,
context: sdl.gl.Context,
buffer: gl.Buffer,
color: [4]f32 = .{ 0, 0, 0, 0 },
// There's a vbo for each shader program
vbo: [max_objects]f32 = .{0.0} ** max_objects,
vbo_index: usize = 0,
// The index of the program is the enum converted to int
textures: [max_objects]Texture = .{undefined} ** max_objects,
color: colors.Color = .{ 0, 0, 0, 0 },
const max_objects: usize = 16384;
const quadSize: usize = 9 * 6;
fn glGetProcAddress(p: []const u8, proc: [:0]const u8) ?*const anyopaque {
_ = p;
return sdl.c.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc));
}
pub fn init() !Self {
try sdl.init(.{ .video = true, .audio = true, .events = true });
try sdl.ttf.init();
rl.ConfigFlag.msaa_4x_hint.set();
rl.initWindow(1280, 720, "USG", 120);
try sdl.gl.setAttribute(.{ .context_major_version = 3 });
try sdl.gl.setAttribute(.{ .context_minor_version = 3 });
try sdl.gl.setAttribute(.{ .multisamplebuffers = true });
try sdl.gl.setAttribute(.{ .multisamplesamples = 4 });
const window = try sdl.createWindow(
"USG",
.{ .centered = {} },
.{ .centered = {} },
1280,
720,
.{ .context = .opengl, .vis = .shown },
);
const proc: []const u8 = undefined;
try gl.loadExtensions(proc, glGetProcAddress);
const ctx = try sdl.gl.createContext(window);
sdl.gl.setSwapInterval(.immediate) catch {
std.debug.print("WARNING: Unable to configure the swap interval.\n", .{});
};
// Activate blending and configure it
gl.enable(.blend);
gl.blendFunc(.src_alpha, .one_minus_src_alpha);
var buf = gl.Buffer.gen();
buf.bind(.array_buffer);
var renderer = Self{ .window = window, .context = ctx, .buffer = buf };
// Load the shader programs according to the names in the enum
_ = renderer.loadProgram("texture").use();
var vertex_array = gl.VertexArray.gen();
vertex_array.bind();
gl.vertexAttribPointer(0, 2, .float, false, @sizeOf(f32) * 9, @sizeOf(f32) * 0);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(1, 4, .float, false, @sizeOf(f32) * 9, @sizeOf(f32) * 2);
gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(2, 2, .float, false, @sizeOf(f32) * 9, @sizeOf(f32) * 6);
gl.enableVertexAttribArray(2);
gl.vertexAttribPointer(3, 1, .float, false, @sizeOf(f32) * 9, @sizeOf(f32) * 8);
gl.enableVertexAttribArray(3);
gl.clearColor(0.91, 0.85, 0.65, 1.00);
return renderer;
}
fn loadProgram(self: Self, comptime name: []const u8) gl.Program {
var program = gl.Program.create();
const vs = gl.Shader.create(.vertex);
defer vs.delete();
const fs = gl.Shader.create(.fragment);
defer fs.delete();
vs.source(1, &.{@embedFile("shaders/" ++ name ++ ".vert")});
fs.source(1, &.{@embedFile("shaders/" ++ name ++ ".frag")});
vs.compile();
fs.compile();
program.attach(vs);
defer program.detach(vs);
program.attach(fs);
defer program.detach(fs);
program.link();
// Get the matrix that converts coordinates to 1:1 on the screen from the OpenGL default
if (program.uniformLocation("mvp")) |mvpLocation| {
const wsize = self.window.getSize();
const xunit = @intToFloat(f32, wsize.width);
const yunit = @intToFloat(f32, wsize.height);
const ortho = m.Mat4.createOrthogonal(0.0, xunit, yunit, 0.0, 0.0, 100.0);
program.use();
gl.uniformMatrix4fv(mvpLocation, false, &.{ortho.fields});
}
return program;
return Self{};
}
pub fn render(self: *Self) void {
// TODO: Submit with SubData instead to not pass the whole buffer
self.buffer.data(f32, &self.vbo, .static_draw);
_ = self;
rl.drawFPS(10, 10);
var i: usize = 0;
const amount = self.vbo_index / quadSize;
while (i < amount) : (i += 1) {
gl.bindTexture(self.textures[i].texture, .@"2d");
gl.drawArrays(.triangles, i * 6, 6);
}
self.vbo_index = 0;
rl.endDrawing();
rl.beginDrawing();
// Clear the vbo, this really shouldn't be necessary as the index is set to zero,
// but otherwise it leads to bugs
self.vbo = .{0.0} ** max_objects;
sdl.gl.swapWindow(self.window);
gl.clear(.{ .color = true });
rl.clearBackground(.{ 232, 216, 166, 255 });
}
pub fn deinit(self: *Self) void {
gl.disableVertexAttribArray(0);
gl.disableVertexAttribArray(1);
gl.disableVertexAttribArray(2);
gl.disableVertexAttribArray(3);
sdl.quit();
sdl.ttf.quit();
self.window.destroy();
sdl.gl.deleteContext(self.context);
}
pub fn setColorF(self: *Self, r: f32, g: f32, b: f32, a: f32) void {
self.*.color = .{ r, g, b, a };
}
pub fn setColor(self: *Self, r: i32, g: i32, b: i32, a: i32) void {
self.*.color = .{
@intToFloat(f32, r) / 255.0,
@intToFloat(f32, g) / 255.0,
@intToFloat(f32, b) / 255.0,
@intToFloat(f32, a) / 255.0,
};
}
pub fn drawRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void {
//const indices = [_]u8{ 0, 1, 1, 2, 2, 3, 3, 0 };
//self.renderRectangle(x, y, w, h, .lines, &indices);
_ = x;
_ = y;
_ = self;
_ = w;
_ = h;
rl.closeWindow();
}
pub fn fillRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void {
@ -179,108 +37,53 @@ pub fn fillRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void {
}
pub fn fillRectangleEx(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32) void {
renderRectangle(self, x, y, w, h, skew_x, 0);
var xf: f32 = @floatFromInt(x);
var yf: f32 = @floatFromInt(y);
var wf: f32 = @floatFromInt(w);
var hf: f32 = @floatFromInt(h);
const skew_x_offset = @as(f32, @floatFromInt(skew_x)) * hf / 100;
const upLeft = [_]f32{ xf + skew_x_offset, yf };
const upRight = [_]f32{ xf + wf + skew_x_offset, yf };
const downLeft = [_]f32{ xf - skew_x_offset, yf + hf };
const downRight = [_]f32{ xf + wf - skew_x_offset, yf + hf };
rl.drawTriangle(upLeft, downLeft, upRight, self.color);
rl.drawTriangle(downLeft, downRight, upRight, self.color);
}
pub fn renderTexture(self: *Self, texture: Texture, x: i32, y: i32) void {
self.textures[self.vbo_index / quadSize] = texture;
renderRectangle(self, x, y, @intCast(i32, texture.width), @intCast(i32, texture.height), 0, 1);
pub fn drawText(self: Self, text: [:0]const u8, x: i32, y: i32, size: i32) void {
rl.drawText(text, x, y, size, self.color);
}
fn renderRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32, unit: f32) void {
var xf = @intToFloat(f32, x);
var yf = @intToFloat(f32, y);
var wf = @intToFloat(f32, w);
var hf = @intToFloat(f32, h);
const skew_x_offset = @intToFloat(f32, skew_x) * hf / 100;
const i = self.vbo_index;
const vertex_data = [_]f32{
xf + skew_x_offset, yf, // up-left
self.color[0], self.color[1],
self.color[2], self.color[3],
0, 0,
unit,
xf + wf + skew_x_offset, yf, // up-right
self.color[0], self.color[1],
self.color[2], self.color[3],
1, 0,
unit,
xf - skew_x_offset, yf + hf, // down-left
self.color[0], self.color[1],
self.color[2], self.color[3],
0, 1,
unit,
xf + wf - skew_x_offset, yf + hf, // down-right
self.color[0], self.color[1],
self.color[2], self.color[3],
1, 1,
unit,
xf + wf + skew_x_offset, yf, // up-right
self.color[0], self.color[1],
self.color[2], self.color[3],
1, 0,
unit,
xf - skew_x_offset, yf + hf, // down-left
self.color[0], self.color[1],
self.color[2], self.color[3],
0, 1,
unit,
};
std.mem.copy(f32, self.vbo[i..], &vertex_data);
self.vbo_index += vertex_data.len;
pub fn setColor(self: *Self, color: colors.Color) void {
self.color = color;
}
pub const OutputSize = struct { width: c_int, height: c_int };
pub const OutputSize = struct { width: i32, height: i32 };
pub fn getOutputSize(self: Self) OutputSize {
var wsize = self.window.getSize();
_ = self;
return OutputSize{
.width = wsize.width,
.height = wsize.height,
.width = rl.getScreenWidth(),
.height = rl.getScreenHeight(),
};
}
pub const Texture = struct {
texture: gl.Texture,
width: usize,
height: usize,
texture: rl.Texture,
pub fn init(data: [*]const u8, width: usize, height: usize) Texture {
var tex = gl.genTexture();
//defer gl.Texture.delete(tex);
gl.bindTexture(tex, .@"2d");
gl.textureImage2D(.@"2d", 0, .rgba, width, height, .rgba, .unsigned_byte, data);
gl.texParameter(.@"2d", .wrap_s, .clamp_to_edge);
gl.texParameter(.@"2d", .wrap_r, .clamp_to_edge);
gl.texParameter(.@"2d", .min_filter, .linear);
gl.texParameter(.@"2d", .mag_filter, .linear);
return Texture{
.texture = tex,
.width = width,
.height = height,
pub fn init(path: [*]const u8) Texture {
return .{
.texture = rl.Texture.load(path),
};
}
pub fn fromText(text: [:0]const u8, size: c_int) Texture {
var font = sdl.ttf.openFont("res/fonts/MarginaliaRegular-8XlZ.ttf", size) catch unreachable;
defer font.close();
var surface = font.renderTextBlended(text, sdl.Color.white) catch unreachable;
defer surface.destroy();
const width = @intCast(usize, surface.ptr.w);
const height = @intCast(usize, surface.ptr.h);
var newsurf = sdl.c.SDL_ConvertSurfaceFormat(surface.ptr, @enumToInt(sdl.PixelFormatEnum.argb8888), 0) orelse unreachable;
const data = @ptrCast([*]const u8, newsurf.pixels);
return Texture.init(data, width, height);
pub fn drawTo(self: Texture, x: i32, y: i32, w: i32, h: i32, tint: [4]u8) void {
self.texture.drawTo(x, y, w, h, tint);
}
pub fn deinit(self: *Texture) void {
gl.Texture.delete(self.texture);
self.texture.unload();
}
};

View File

@ -1,13 +1,13 @@
const SDL = @import("sdl2");
const Self = @This();
started: bool,
initial: u64,
progress: u64,
target: u64,
const getTime = @import("raylib.zig").getTime;
pub fn init(target: u64) Self {
started: bool,
initial: f64,
progress: f64,
target: f64,
pub fn init(target: f64) Self {
return Self{
.started = false,
.initial = 0,
@ -19,12 +19,12 @@ pub fn init(target: u64) Self {
pub fn start(self: *Self) void {
self.started = true;
self.progress = 0;
self.initial = SDL.getTicks64();
self.initial = getTime();
}
pub fn finished(self: *Self) bool {
if (self.started) {
self.progress = SDL.getTicks64() - self.initial;
self.progress = getTime() - self.initial;
if (self.progress >= self.target) {
return true;
} else return false;
@ -35,7 +35,7 @@ pub fn finished(self: *Self) bool {
pub fn reset(self: *Self) void {
self.progress = 0;
self.initial = SDL.getTicks64();
self.initial = getTime();
}
pub fn stop(self: *Self) void {

View File

@ -1,68 +1,13 @@
const SDL = @import("sdl2");
pub const Color = [4]u8;
pub const yellow = SDL.Color{
.r = 232,
.g = 216,
.b = 165,
.a = 255,
};
pub const brown = SDL.Color{
.r = 180,
.g = 130,
.b = 90,
.a = 255,
};
pub const cyan = SDL.Color{
.r = 138,
.g = 167,
.b = 172,
.a = 255,
};
pub const orange = SDL.Color{
.r = 222,
.g = 154,
.b = 40,
.a = 255,
};
pub const blue = SDL.Color{
.r = 112,
.g = 123,
.b = 136,
.a = 255,
};
pub const green = SDL.Color{
.r = 142,
.g = 146,
.b = 87,
.a = 255,
};
pub const red = SDL.Color{
.r = 229,
.g = 93,
.b = 77,
.a = 255,
};
pub const purple = SDL.Color{
.r = 180,
.g = 171,
.b = 189,
.a = 255,
};
pub const pink = SDL.Color{
.r = 230,
.g = 115,
.b = 170,
.a = 255,
};
pub const dark_grey = SDL.Color{
.r = 40,
.g = 40,
.b = 40,
.a = 255,
};
pub const light_grey = SDL.Color{
.r = 80,
.g = 80,
.b = 80,
.a = 255,
};
pub const yellow = Color{ 255, 208, 95, 255 };
pub const brown = Color{ 180, 130, 90, 255 };
pub const cyan = Color{ 138, 167, 172, 255 };
pub const orange = Color{ 222, 154, 40, 255 };
pub const blue = Color{ 112, 123, 136, 255 };
pub const green = Color{ 142, 146, 87, 255 };
pub const red = Color{ 229, 93, 77, 255 };
pub const purple = Color{ 180, 171, 189, 255 };
pub const pink = Color{ 230, 115, 170, 255 };
pub const dark_grey = Color{ 40, 40, 40, 255 };
pub const light_grey = Color{ 80, 80, 80, 255 };

101
src/input.zig Normal file
View File

@ -0,0 +1,101 @@
const Self = @This();
const rl = @import("raylib.zig");
const std = @import("std");
const config = @import("Config/config.zig");
pub const Input = struct {
name: []const u8,
code: i32,
default: i32,
pub fn isDown(self: Input) bool {
return rl.isKeyDown(self.code);
}
pub fn isJustPressed(self: Input) bool {
return rl.isKeyPressed(self.code);
}
pub fn isJustReleased(self: Input) bool {
return rl.isKeyReleased(self.code);
}
};
pub fn loadConfig() void {
var iter = config.getConfigIterator("input.ini") catch {
std.debug.print("Error loading input config, using defaults...\n", .{});
return;
};
while (iter.next()) |option| {
inline for (comptime getInputNames()) |name| {
if (std.mem.eql(u8, option.key, name)) {
if (rl.getValueFromInputName(option.value)) |value| {
@field(Self, name).code = value;
} else {
std.debug.print("Invalid input value '{s}' in config, ignoring.\n", .{option.value});
}
}
}
}
}
fn init(comptime name: []const u8, comptime key_code: []const u8) Input {
return Input{
.name = name,
.code = rl.getKeyCode(key_code),
.default = rl.getKeyCode(key_code),
};
}
pub var menu_right = init("Menu Right", "RIGHT");
pub var menu_left = init("Menu Left", "LEFT");
pub var menu_down = init("Menu Down", "DOWN");
pub var menu_up = init("Menu Up", "UP");
pub var menu_accept = init("Menu Accept", "ENTER");
pub var menu_cancel = init("Menu Cancel", "BACKSPACE");
pub var game_right = init("Move Right", "D");
pub var game_left = init("Move Left", "A");
pub var game_down = init("Move Down", "S");
pub var game_drop = init("Drop Piece", "W");
pub var game_swap_piece = init("Swap Piece", "SPACE");
pub var game_rotate_r = init("Rotate Piece Clockwise", "RIGHT");
pub var game_rotate_l = init("Rotate Piece Counterclockwise", "LEFT");
// Get the names of the Input(s) defined in this file with introspection
fn getInputNames() [getInputCount()][]const u8 {
comptime {
const decls = std.meta.declarations(Self);
var ret: [getInputCount()][]const u8 = undefined;
var i: usize = 0;
inline for (decls) |i_decl| {
const field = @field(Self, i_decl.name);
if (@TypeOf(field) == Input) {
ret[i] = i_decl.name;
i += 1;
}
}
return ret;
}
}
// With reflection count the amount of declarations of type "Input"
fn getInputCount() usize {
comptime {
const decls = std.meta.declarations(Self);
var i: usize = 0;
inline for (decls) |i_decl| {
const field = @field(Self, i_decl.name);
if (@TypeOf(field) == Input) i += 1;
}
return i;
}
}

View File

@ -1,13 +1,18 @@
const std = @import("std");
const SDL = @import("sdl2");
// TODO: Get this from a input class, not from rl directly
const shouldClose = @import("raylib.zig").windowShouldClose;
const Renderer = @import("Renderer.zig");
const Game = @import("Game.zig");
const MainMenu = @import("MainMenu.zig");
const config = @import("Config/config.zig");
const State = @import("flow.zig").State;
const input = @import("input.zig");
pub fn main() !void {
input.loadConfig();
var renderer = try Renderer.init();
defer renderer.deinit();
@ -16,18 +21,7 @@ pub fn main() !void {
var current_state: State = main_menu.state;
try SDL.image.init(.{ .jpg = true });
mainLoop: while (true) {
const start = SDL.getTicks64();
while (SDL.pollEvent()) |ev| {
switch (ev) {
.quit => break :mainLoop,
else => {},
}
}
while (!shouldClose()) {
if (game.needs_reinit) {
game = Game.init(&renderer);
}
@ -42,12 +36,5 @@ pub fn main() !void {
};
renderer.render();
const delay = SDL.getTicks64() - start;
//std.debug.print("{} ms\n", .{delay});
if (delay < 17) {
SDL.delay(17 - @intCast(u32, delay));
}
}
}

279
src/raylib.zig Normal file
View File

@ -0,0 +1,279 @@
pub const c = @cImport({
@cInclude("raylib.h");
});
const std = @import("std");
const InputCode = struct {
name: []const u8 = "",
value: i32 = 0,
};
// zig fmt: off
const input_codes = [_]InputCode{
.{ .name = "NULL" , .value = 0, },
.{ .name = "APOSTROPHE" , .value = 39, },
.{ .name = "COMMA" , .value = 44, },
.{ .name = "MINUS" , .value = 45, },
.{ .name = "PERIOD" , .value = 46, },
.{ .name = "SLASH" , .value = 47, },
.{ .name = "ZERO" , .value = 48, },
.{ .name = "ONE" , .value = 49, },
.{ .name = "TWO" , .value = 50, },
.{ .name = "THREE" , .value = 51, },
.{ .name = "FOUR" , .value = 52, },
.{ .name = "FIVE" , .value = 53, },
.{ .name = "SIX" , .value = 54, },
.{ .name = "SEVEN" , .value = 55, },
.{ .name = "EIGHT" , .value = 56, },
.{ .name = "NINE" , .value = 57, },
.{ .name = "SEMICOLON" , .value = 59, },
.{ .name = "EQUAL" , .value = 61, },
.{ .name = "A" , .value = 65, },
.{ .name = "B" , .value = 66, },
.{ .name = "C" , .value = 67, },
.{ .name = "D" , .value = 68, },
.{ .name = "E" , .value = 69, },
.{ .name = "F" , .value = 70, },
.{ .name = "G" , .value = 71, },
.{ .name = "H" , .value = 72, },
.{ .name = "I" , .value = 73, },
.{ .name = "J" , .value = 74, },
.{ .name = "K" , .value = 75, },
.{ .name = "L" , .value = 76, },
.{ .name = "M" , .value = 77, },
.{ .name = "N" , .value = 78, },
.{ .name = "O" , .value = 79, },
.{ .name = "P" , .value = 80, },
.{ .name = "Q" , .value = 81, },
.{ .name = "R" , .value = 82, },
.{ .name = "S" , .value = 83, },
.{ .name = "T" , .value = 84, },
.{ .name = "U" , .value = 85, },
.{ .name = "V" , .value = 86, },
.{ .name = "W" , .value = 87, },
.{ .name = "X" , .value = 88, },
.{ .name = "Y" , .value = 89, },
.{ .name = "Z" , .value = 90, },
.{ .name = "LEFT_BRACKET" , .value = 91, },
.{ .name = "BACKSLASH" , .value = 92, },
.{ .name = "RIGHT_BRACKET" , .value = 93, },
.{ .name = "GRAVE" , .value = 96, },
.{ .name = "SPACE" , .value = 32, },
.{ .name = "ESCAPE" , .value = 256, },
.{ .name = "ENTER" , .value = 257, },
.{ .name = "TAB" , .value = 258, },
.{ .name = "BACKSPACE" , .value = 259, },
.{ .name = "INSERT" , .value = 260, },
.{ .name = "DELETE" , .value = 261, },
.{ .name = "RIGHT" , .value = 262, },
.{ .name = "LEFT" , .value = 263, },
.{ .name = "DOWN" , .value = 264, },
.{ .name = "UP" , .value = 265, },
.{ .name = "PAGE_UP" , .value = 266, },
.{ .name = "PAGE_DOWN" , .value = 267, },
.{ .name = "HOME" , .value = 268, },
.{ .name = "END" , .value = 269, },
.{ .name = "CAPS_LOCK" , .value = 280, },
.{ .name = "SCROLL_LOCK" , .value = 281, },
.{ .name = "NUM_LOCK" , .value = 282, },
.{ .name = "PRINT_SCREEN" , .value = 283, },
.{ .name = "PAUSE" , .value = 284, },
.{ .name = "F1" , .value = 290, },
.{ .name = "F2" , .value = 291, },
.{ .name = "F3" , .value = 292, },
.{ .name = "F4" , .value = 293, },
.{ .name = "F5" , .value = 294, },
.{ .name = "F6" , .value = 295, },
.{ .name = "F7" , .value = 296, },
.{ .name = "F8" , .value = 297, },
.{ .name = "F9" , .value = 298, },
.{ .name = "F10" , .value = 299, },
.{ .name = "F11" , .value = 300, },
.{ .name = "F12" , .value = 301, },
.{ .name = "LEFT_SHIFT" , .value = 340, },
.{ .name = "LEFT_CONTROL" , .value = 341, },
.{ .name = "LEFT_ALT" , .value = 342, },
.{ .name = "LEFT_SUPER" , .value = 343, },
.{ .name = "RIGHT_SHIFT" , .value = 344, },
.{ .name = "RIGHT_CONTROL" , .value = 345, },
.{ .name = "RIGHT_ALT" , .value = 346, },
.{ .name = "RIGHT_SUPER" , .value = 347, },
.{ .name = "KB_MENU" , .value = 348, },
.{ .name = "KP_0" , .value = 320, },
.{ .name = "KP_1" , .value = 321, },
.{ .name = "KP_2" , .value = 322, },
.{ .name = "KP_3" , .value = 323, },
.{ .name = "KP_4" , .value = 324, },
.{ .name = "KP_5" , .value = 325, },
.{ .name = "KP_6" , .value = 326, },
.{ .name = "KP_7" , .value = 327, },
.{ .name = "KP_8" , .value = 328, },
.{ .name = "KP_9" , .value = 329, },
.{ .name = "KP_DECIMAL" , .value = 330, },
.{ .name = "KP_DIVIDE" , .value = 331, },
.{ .name = "KP_MULTIPLY" , .value = 332, },
.{ .name = "KP_SUBTRACT" , .value = 333, },
.{ .name = "KP_ADD" , .value = 334, },
.{ .name = "KP_ENTER" , .value = 335, },
.{ .name = "KP_EQUAL" , .value = 336, },
.{ .name = "BACK" , .value = 4, },
.{ .name = "MENU" , .value = 82, },
.{ .name = "VOLUME_UP" , .value = 24, },
.{ .name = "VOLUME_DOWN" , .value = 25, },
};
// zig fmt: on
pub fn getValueFromInputName(name: []const u8) ?i32 {
for (input_codes) |ic| {
if (std.mem.eql(u8, ic.name, name)) {
return ic.value;
}
}
return null;
}
pub fn initWindow(width: i32, height: i32, title: [*c]const u8, fps: i32) void {
c.InitWindow(width, height, title);
c.SetTargetFPS(fps);
}
pub fn windowShouldClose() bool {
return c.WindowShouldClose();
}
pub fn closeWindow() void {
c.CloseWindow();
}
pub fn beginDrawing() void {
c.BeginDrawing();
}
pub fn endDrawing() void {
c.EndDrawing();
}
pub fn getKeyCode(comptime name: []const u8) i32 {
const full_name = "KEY_" ++ name;
if (!@hasDecl(c, full_name)) {
@compileError(full_name ++ " keycode does not exist.");
}
return @field(c, full_name);
}
pub fn isKeyPressed(key: i32) bool {
return c.IsKeyPressed(key);
}
pub fn isKeyDown(key: i32) bool {
return c.IsKeyDown(key);
}
pub fn isKeyReleased(key: i32) bool {
return c.IsKeyReleased(key);
}
pub fn drawText(text: [*c]const u8, x: i32, y: i32, fontSize: i32, color: [4]u8) void {
c.DrawText(text, x, y, fontSize, .{ .r = color[0], .g = color[1], .b = color[2], .a = color[3] });
}
pub fn setConfigFlags(flag: u32) void {
c.SetConfigFlags(flag);
}
// Call before initializing the window
pub const ConfigFlag = enum(u32) {
vsync_hint = 0x00000040, // set to try enabling v-sync on gpu
fullscreen_mode = 0x00000002, // set to run program in fullscreen
window_resizable = 0x00000004, // set to allow resizable window
window_undecorated = 0x00000008, // set to disable window decoration (frame and buttons)
window_hidden = 0x00000080, // set to hide window
window_minimized = 0x00000200, // set to minimize window (iconify)
window_maximized = 0x00000400, // set to maximize window (expanded to monitor)
window_unfocused = 0x00000800, // set to window non focused
window_topmost = 0x00001000, // set to window always on top
window_always_run = 0x00000100, // set to allow windows running while minimized
window_transparent = 0x00000010, // set to allow transparent framebuffer
window_highdpi = 0x00002000, // set to support highdpi
window_mouse_passthrough = 0x00004000, // set to support mouse passthrough, only supported when flag_window_undecorated
borderless_windowed_mode = 0x00008000, // set to run program in borderless windowed mode
msaa_4x_hint = 0x00000020, // set to try enabling msaa 4x
interlaced_hint = 0x00010000, // set to try enabling interlaced video format (for v3d)
pub fn set(self: ConfigFlag) void {
setConfigFlags(@intFromEnum(self));
}
};
pub fn clearBackground(color: [4]u8) void {
c.ClearBackground(.{ .r = color[0], .g = color[1], .b = color[2], .a = color[3] });
}
pub fn drawTriangle(v1: [2]f32, v2: [2]f32, v3: [2]f32, color: [4]u8) void {
c.DrawTriangle(
.{ .x = v1[0], .y = v1[1] },
.{ .x = v2[0], .y = v2[1] },
.{ .x = v3[0], .y = v3[1] },
.{ .r = color[0], .g = color[1], .b = color[2], .a = color[3] },
);
}
pub const Texture = struct {
texture: c.Texture2D,
pub fn load(file_name: [*c]const u8) Texture {
const aux = .{
.texture = c.LoadTexture(file_name),
};
c.SetTextureFilter(aux.texture, c.TEXTURE_FILTER_BILINEAR);
return aux;
}
pub fn drawTo(self: Texture, x: i32, y: i32, w: i32, h: i32, tint: [4]u8) void {
const srcRec: c.Rectangle = .{
.x = 0,
.y = 0,
.width = @floatFromInt(self.texture.width),
.height = @floatFromInt(self.texture.height),
};
const dstRec: c.Rectangle = .{
.x = @floatFromInt(x),
.y = @floatFromInt(y),
.width = @floatFromInt(w),
.height = @floatFromInt(h),
};
c.DrawTexturePro(
self.texture,
srcRec,
dstRec,
.{ .x = 0, .y = 0 },
0,
.{ .r = tint[0], .g = tint[1], .b = tint[2], .a = tint[3] },
);
}
pub fn unload(self: Texture) void {
c.UnloadTexture(self.texture);
}
};
pub fn getScreenWidth() i32 {
return c.GetScreenWidth();
}
pub fn getScreenHeight() i32 {
return c.GetScreenHeight();
}
pub fn getTime() f64 {
return c.GetTime();
}
pub fn drawFPS(x: i32, y: i32) void {
c.DrawFPS(x, y);
}