Compare commits

..

No commits in common. "main" and "ed031c9b8c21e87ecb7797a0c2d2118d3b9a9cc9" have entirely different histories.

26 changed files with 524 additions and 773 deletions

3
.gitignore vendored
View File

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

9
.gitmodules vendored Normal file
View File

@ -0,0 +1,9 @@
[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,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const Sdk = @import("lib/SDL.zig/build.zig");
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose // Standard target options allows the person running `zig build` to choose
@ -18,8 +19,17 @@ pub fn build(b: *std.Build) void {
.optimize = optimize, .optimize = optimize,
}); });
exe.linkSystemLibrary("raylib"); const sdk = Sdk.init(b, null);
exe.linkSystemLibrary("c"); 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());
b.installArtifact(exe); b.installArtifact(exe);

1
lib/SDL.zig Submodule

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

1
lib/zgl Submodule

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

1
lib/zlm Submodule

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

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

View File

@ -1,86 +0,0 @@
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;
}

View File

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

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const SDL = @import("sdl2");
const Grid = @import("Game/Grid.zig"); const Grid = @import("Game/Grid.zig");
const Piece = @import("Game/Piece.zig"); const Piece = @import("Game/Piece.zig");
@ -6,13 +7,10 @@ const Bag = @import("Game/Bag.zig");
const Timer = @import("Timer.zig"); const Timer = @import("Timer.zig");
const State = @import("flow.zig").State; const State = @import("flow.zig").State;
const Action = @import("Action.zig"); const Action = @import("Action.zig");
const input = @import("input.zig");
const Renderer = @import("Game/Renderer.zig"); const Renderer = @import("Game/Renderer.zig");
const movement = @import("Game/movement.zig"); const movement = @import("Game/movement.zig");
pub const Score = u64;
const Self = @This(); const Self = @This();
// Codes to access action list // Codes to access action list
@ -38,16 +36,14 @@ held: ?Piece.Type,
piece: Piece, piece: Piece,
shadow: Piece, shadow: Piece,
score: Score = 0,
action_list: ActionList = .{ action_list: ActionList = .{
.right = Action.init(&input.game_right, actionRight), // Right .right = Action.init(SDL.Scancode.d, actionRight), // Right
.left = Action.init(&input.game_left, actionLeft), // Left .left = Action.init(SDL.Scancode.a, actionLeft), // Left
.down = Action.init(&input.game_down, actionDown), // Down .down = Action.init(SDL.Scancode.s, actionDown), // Down
.hard = Action.init(&input.game_drop, actionHard), // Instant Drop .hard = Action.init(SDL.Scancode.w, actionHard), // Instant Drop
.swap = Action.init(&input.game_swap_piece, actionSwap), // Swap Piece .swap = Action.init(SDL.Scancode.space, actionSwap), // Swap Piece
.rot_r = Action.init(&input.game_rotate_r, actionRotR), // Rotate .rot_r = Action.init(SDL.Scancode.right, actionRotR), // Rotate
.rot_l = Action.init(&input.game_rotate_l, actionRotL), // Rotate .rot_l = Action.init(SDL.Scancode.left, actionRotL), // Rotate
}, },
timer_down: Timer, timer_down: Timer,
@ -56,7 +52,6 @@ timer_left_das: Timer,
timer_right_arr: Timer, timer_right_arr: Timer,
timer_right_das: Timer, timer_right_das: Timer,
timer_gravity: Timer, timer_gravity: Timer,
timer_line_clear: Timer,
needs_reinit: bool = false, needs_reinit: bool = false,
@ -70,13 +65,12 @@ pub fn init(renderer: *Renderer.Renderer) Self {
.held = null, .held = null,
.renderer = Renderer.init(renderer), .renderer = Renderer.init(renderer),
.timer_down = Timer.init(0.05), .timer_down = Timer.init(50),
.timer_left_arr = Timer.init(0.05), .timer_left_arr = Timer.init(50),
.timer_left_das = Timer.init(0.2), .timer_left_das = Timer.init(200),
.timer_right_arr = Timer.init(0.05), .timer_right_arr = Timer.init(50),
.timer_right_das = Timer.init(0.2), .timer_right_das = Timer.init(200),
.timer_gravity = Timer.init(0.3), .timer_gravity = Timer.init(200),
.timer_line_clear = Timer.init(1),
}; };
// Get a piece from the bag // Get a piece from the bag
@ -87,10 +81,6 @@ pub fn init(renderer: *Renderer.Renderer) Self {
// Main Game loop // Main Game loop
pub fn tick(self: *Self) State { 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 // TIMERS
// Dropping a piece // Dropping a piece
@ -178,16 +168,17 @@ pub fn tick(self: *Self) State {
} }
// KEY EVENTS // KEY EVENTS
var key_state = SDL.getKeyboardState();
const action_fields = std.meta.fields(ActionList); const action_fields = std.meta.fields(ActionList);
inline for (action_fields) |field| { inline for (action_fields) |field| {
// REVIEW: Is this necessary? std.debug.print("\n{any}", .{field.name});
const action = &@field(self.action_list, field.name); 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.*.holding = true;
action.*.activate = true; action.*.activate = true;
} }
if (!action.input.isDown()) { if (!key_state.isPressed(action.*.scancode)) {
action.*.holding = false; action.*.holding = false;
} }
@ -201,7 +192,7 @@ pub fn tick(self: *Self) State {
// Update Shadow // Update Shadow
{ {
self.shadow = movement.shadow(self.grid, self.piece); self.shadow = movement.shadow(self.grid, self.piece);
self.shadow.color[3] /= 4; self.shadow.color.a /= 4;
} }
// Check for a top out // Check for a top out
@ -215,25 +206,7 @@ pub fn tick(self: *Self) State {
} }
} }
// Check for empty lines. If there are, then start the clearing sequence self.grid.clearLines();
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.*); self.renderer.render(self.*);

View File

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

View File

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

View File

@ -15,7 +15,6 @@ pub const buffer = 10;
cells: [nrows][ncolumns]Cell, cells: [nrows][ncolumns]Cell,
topped: bool = false, topped: bool = false,
lines_to_clear: [nrows]bool = .{false} ** nrows,
pub fn init() Self { pub fn init() Self {
var self = Self{ var self = Self{
@ -31,44 +30,20 @@ pub fn init() Self {
return self; return self;
} }
pub fn updateLinesToClear(self: *Self) bool { pub fn clearLines(self: *Self) void {
var ret: bool = false;
// Look at each row
for (self.cells, 0..) |_, y| { for (self.cells, 0..) |_, y| {
// Look at each cell in the line
for (self.cells[y], 0..) |cell_x, x| { for (self.cells[y], 0..) |cell_x, x| {
if (cell_x.free) { if (cell_x.free) {
// It is free, this line isn't to be cleared
break; break;
} else { } else {
// Reached the end of the column? // Reached the end of the column?
if (x == self.cells[y].len - 1) { if (x == self.cells[y].len - 1) {
self.lines_to_clear[y] = true; // Delete current row and bring the others down
ret = true; for (self.cells[1..y], 0..) |_, i| {
self.cells[y - i] = self.cells[y - i - 1];
}
} }
} }
} }
} }
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,3 +1,4 @@
const SDL = @import("sdl2");
const std = @import("std"); const std = @import("std");
const Grid = @import("Grid.zig"); const Grid = @import("Grid.zig");
@ -50,7 +51,7 @@ dropped: bool = false,
swapped: bool = false, swapped: bool = false,
piece_type: Type, piece_type: Type,
color: color.Color, color: SDL.Color,
pub fn init(piece_type: Type) Self { pub fn init(piece_type: Type) Self {
return Self{ return Self{
@ -58,7 +59,7 @@ pub fn init(piece_type: Type) Self {
.col = (Grid.ncolumns / 2) - (ncols / 2), .col = (Grid.ncolumns / 2) - (ncols / 2),
.piece_type = piece_type, .piece_type = piece_type,
.timer_dropped = Timer.init(0.5), .timer_dropped = Timer.init(500),
.structure = switch (piece_type) { .structure = switch (piece_type) {
Type.o => .{ Type.o => .{
@ -132,21 +133,21 @@ pub fn rotate(self: Self, dir: Rot) Self {
if (dir == Rot.right) { if (dir == Rot.right) {
// Cicle through rotation stage // Cicle through rotation stage
new_piece.rot_stage = @as(RotStage, @enumFromInt(@mod((@intFromEnum(new_piece.rot_stage) + 1), 4))); new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) + 1), 4));
// Rotate structure CW // Rotate structure CW
inline for (sequences) |seq| { inline for (sequences) |seq| {
inline for (seq, 0..) |_, i| { inline for (seq, 0..) |_, i| {
const refi = @mod((@as(i32, @intCast(i)) - 1), 4); const refi = @mod((@intCast(i32, i) - 1), 4);
new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]]; new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
} }
} }
} else { } else {
// Cicle through rotation stage // Cicle through rotation stage
new_piece.rot_stage = @as(RotStage, @enumFromInt(@mod((@intFromEnum(new_piece.rot_stage) - 1), 4))); new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) - 1), 4));
// Rotate structure CCW // Rotate structure CCW
inline for (sequences) |seq| { inline for (sequences) |seq| {
inline for (seq, 0..) |_, i| { inline for (seq, 0..) |_, i| {
const refi = @mod((@as(i32, @intCast(i)) + 1), 4); const refi = @mod((@intCast(i32, i) + 1), 4);
new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]]; new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
} }
} }
@ -156,21 +157,21 @@ pub fn rotate(self: Self, dir: Rot) Self {
if (dir == Rot.right) { if (dir == Rot.right) {
// Cicle through rotation stage // Cicle through rotation stage
new_piece.rot_stage = @as(RotStage, @enumFromInt(@mod((@intFromEnum(new_piece.rot_stage) + 1), 4))); new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) + 1), 4));
// Rotate structure CW // Rotate structure CW
inline for (sequences) |seq| { inline for (sequences) |seq| {
inline for (seq, 0..) |_, i| { inline for (seq, 0..) |_, i| {
const refi = @mod((@as(i32, @intCast(i)) - 1), 4); const refi = @mod((@intCast(i32, i) - 1), 4);
new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]]; new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
} }
} }
} else { } else {
// Cicle through rotation stage // Cicle through rotation stage
new_piece.rot_stage = @as(RotStage, @enumFromInt(@mod((@intFromEnum(new_piece.rot_stage) - 1), 4))); new_piece.rot_stage = @intToEnum(RotStage, @mod((@enumToInt(new_piece.rot_stage) - 1), 4));
// Rotate structure CCW // Rotate structure CCW
inline for (sequences) |seq| { inline for (sequences) |seq| {
inline for (seq, 0..) |_, i| { inline for (seq, 0..) |_, i| {
const refi = @mod((@as(i32, @intCast(i)) + 1), 4); const refi = @mod((@intCast(i32, i) + 1), 4);
new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]]; new_piece.structure[seq[i][0]][seq[i][1]] = self.structure[seq[refi][0]][seq[refi][1]];
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,35 +1,177 @@
const std = @import("std"); const std = @import("std");
const rl = @import("raylib.zig"); const sdl = @import("sdl2");
const zlm = @import("zlm");
const gl = @import("zgl");
const m = zlm.SpecializeOn(f32);
const Self = @This(); const Self = @This();
const colors = @import("color.zig");
color: colors.Color = .{ 0, 0, 0, 0 }, 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,
const max_objects: usize = 16384; const max_objects: usize = 16384;
const quadSize: usize = 9 * 6; const quadSize: usize = 9 * 6;
pub fn init() !Self { fn glGetProcAddress(p: []const u8, proc: [:0]const u8) ?*const anyopaque {
rl.ConfigFlag.msaa_4x_hint.set(); _ = p;
rl.initWindow(1280, 720, "USG", 120); return sdl.c.SDL_GL_GetProcAddress(@ptrCast([*c]const u8, proc));
}
return Self{}; pub fn init() !Self {
try sdl.init(.{ .video = true, .audio = true, .events = true });
try sdl.ttf.init();
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;
} }
pub fn render(self: *Self) void { pub fn render(self: *Self) void {
_ = self; // TODO: Submit with SubData instead to not pass the whole buffer
rl.drawFPS(10, 10); self.buffer.data(f32, &self.vbo, .static_draw);
rl.endDrawing(); var i: usize = 0;
rl.beginDrawing(); 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.clearBackground(.{ 232, 216, 166, 255 }); // 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 });
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
_ = self; 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);
}
rl.closeWindow(); 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;
} }
pub fn fillRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void { pub fn fillRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32) void {
@ -37,53 +179,108 @@ 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 { pub fn fillRectangleEx(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32) void {
var xf: f32 = @floatFromInt(x); renderRectangle(self, x, y, w, h, skew_x, 0);
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 drawText(self: Self, text: [:0]const u8, x: i32, y: i32, size: i32) void { pub fn renderTexture(self: *Self, texture: Texture, x: i32, y: i32) void {
rl.drawText(text, x, y, size, self.color); self.textures[self.vbo_index / quadSize] = texture;
renderRectangle(self, x, y, @intCast(i32, texture.width), @intCast(i32, texture.height), 0, 1);
} }
pub fn setColor(self: *Self, color: colors.Color) void { fn renderRectangle(self: *Self, x: i32, y: i32, w: i32, h: i32, skew_x: i32, unit: f32) void {
self.color = color; 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 const OutputSize = struct { width: i32, height: i32 }; pub const OutputSize = struct { width: c_int, height: c_int };
pub fn getOutputSize(self: Self) OutputSize { pub fn getOutputSize(self: Self) OutputSize {
_ = self; var wsize = self.window.getSize();
return OutputSize{ return OutputSize{
.width = rl.getScreenWidth(), .width = wsize.width,
.height = rl.getScreenHeight(), .height = wsize.height,
}; };
} }
pub const Texture = struct { pub const Texture = struct {
texture: rl.Texture, texture: gl.Texture,
width: usize,
height: usize,
pub fn init(path: [*]const u8) Texture { pub fn init(data: [*]const u8, width: usize, height: usize) Texture {
return .{ var tex = gl.genTexture();
.texture = rl.Texture.load(path), //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 drawTo(self: Texture, x: i32, y: i32, w: i32, h: i32, tint: [4]u8) void { pub fn fromText(text: [:0]const u8, size: c_int) Texture {
self.texture.drawTo(x, y, w, h, tint); 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 deinit(self: *Texture) void { pub fn deinit(self: *Texture) void {
self.texture.unload(); gl.Texture.delete(self.texture);
} }
}; };

View File

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

View File

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

View File

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

View File

@ -1,279 +0,0 @@
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);
}