319 lines
8.6 KiB
Zig
319 lines
8.6 KiB
Zig
//! By convention, root.zig is the root source file when making a library.
|
|
const std = @import("std");
|
|
|
|
pub const platform = @import("platform.zig");
|
|
|
|
const TILE_SIZE = 32;
|
|
|
|
const V2 = struct {
|
|
x: f32,
|
|
y: f32,
|
|
|
|
fn add(self: V2, other: V2) V2 {
|
|
return .{
|
|
.x = self.x + other.x,
|
|
.y = self.y + other.y,
|
|
};
|
|
}
|
|
|
|
fn sub(self: V2, other: V2) V2 {
|
|
return .{
|
|
.x = self.x - other.x,
|
|
.y = self.y - other.y,
|
|
};
|
|
}
|
|
};
|
|
|
|
const Camera = struct {
|
|
pos: V2,
|
|
offset: V2,
|
|
};
|
|
|
|
const Brick = struct {
|
|
rotating: bool,
|
|
pos: V2,
|
|
color: u32,
|
|
tiles: [4][4]u32,
|
|
|
|
fn rotate(self: Brick) [4][4]u32 {
|
|
var result = [4][4]u32{
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
};
|
|
|
|
for (0..self.tiles.len) |i| {
|
|
const row = self.tiles[i];
|
|
for (0..row.len) |j| {
|
|
result[j][row.len - i - 1] = self.tiles[i][j];
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
const State = struct {
|
|
frame_count: i32,
|
|
mouse_pos: V2,
|
|
camera: Camera,
|
|
bricks: [8]Brick,
|
|
};
|
|
|
|
fn pointInsideRect(p: V2, rect_start: V2, rect_size: V2) bool {
|
|
var result: bool = false;
|
|
|
|
const rect_end: V2 = rect_start.add(rect_size);
|
|
|
|
const inside_x = rect_start.x < p.x and p.x < rect_end.x;
|
|
const inside_y = rect_start.y < p.y and p.y < rect_end.y;
|
|
|
|
result = inside_x and inside_y;
|
|
|
|
return result;
|
|
}
|
|
|
|
fn fillRect(
|
|
buffer: platform.OffscreenBuffer,
|
|
pos_x: i32,
|
|
pos_y: i32,
|
|
width: i32,
|
|
height: i32,
|
|
color: u32,
|
|
) void {
|
|
var pixels: []u32 = @ptrCast(@alignCast(buffer.data));
|
|
|
|
var start_x: i32 = pos_x;
|
|
var start_y: i32 = pos_y;
|
|
var end_x: i32 = pos_x + width;
|
|
var end_y: i32 = pos_y + height;
|
|
|
|
if (start_x < 0) start_x = 0;
|
|
if (start_y < 0) start_y = 0;
|
|
if (buffer.width < end_x) end_x = buffer.width;
|
|
if (buffer.height < end_y) end_y = buffer.height;
|
|
|
|
for (@intCast(start_y)..@intCast(end_y)) |y| {
|
|
for (@intCast(start_x)..@intCast(end_x)) |x| {
|
|
const idx: usize = y * @as(usize, @intCast(buffer.width)) + x;
|
|
|
|
pixels[idx] = color;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn fillSquare(
|
|
buffer: platform.OffscreenBuffer,
|
|
x: i32,
|
|
y: i32,
|
|
size: i32,
|
|
color: u32,
|
|
) void {
|
|
fillRect(buffer, x, y, size, size, color);
|
|
}
|
|
|
|
pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBuffer, input: platform.AppInput) void {
|
|
var state: *State = @ptrCast(@alignCast(memory.permanent_storage));
|
|
|
|
const mid = V2{
|
|
.x = @floatFromInt(@divTrunc(buffer.width, 2)),
|
|
.y = @floatFromInt(@divTrunc(buffer.height, 2)),
|
|
};
|
|
if (!memory.initialized) {
|
|
state.camera.offset = mid;
|
|
|
|
state.bricks[0] = Brick{
|
|
.rotating = false,
|
|
.pos = V2{ .x = 0, .y = 50 },
|
|
.color = 0xFFFF0000,
|
|
.tiles = [4][4]u32{
|
|
.{ 1, 1, 1, 1 },
|
|
.{ 1, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
},
|
|
};
|
|
|
|
state.bricks[1] = Brick{
|
|
.rotating = false,
|
|
.pos = V2{ .x = 32, .y = 50 },
|
|
.color = 0xFFEF0000,
|
|
.tiles = [4][4]u32{
|
|
.{ 2, 2, 2, 0 },
|
|
.{ 2, 2, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
},
|
|
};
|
|
|
|
state.bricks[2] = Brick{
|
|
.rotating = false,
|
|
.pos = V2{ .x = 64, .y = 50 },
|
|
.color = 0xFFDF0000,
|
|
.tiles = [4][4]u32{
|
|
.{ 3, 3, 3, 0 },
|
|
.{ 0, 0, 3, 3 },
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
},
|
|
};
|
|
|
|
state.bricks[3] = Brick{
|
|
.rotating = false,
|
|
.pos = V2{ .x = 96, .y = 50 },
|
|
.color = 0xFFCF0000,
|
|
.tiles = [4][4]u32{
|
|
.{ 4, 0, 0, 0 },
|
|
.{ 4, 4, 4, 0 },
|
|
.{ 0, 0, 4, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
},
|
|
};
|
|
|
|
state.bricks[4] = Brick{
|
|
.rotating = false,
|
|
.pos = V2{ .x = 128, .y = 150 },
|
|
.color = 0xFFBF0000,
|
|
.tiles = [4][4]u32{
|
|
.{ 5, 5, 5, 5 },
|
|
.{ 0, 5, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
},
|
|
};
|
|
|
|
state.bricks[5] = Brick{
|
|
.rotating = false,
|
|
.pos = V2{ .x = 160, .y = 250 },
|
|
.color = 0xFFAF0000,
|
|
.tiles = [4][4]u32{
|
|
.{ 6, 6, 6, 0 },
|
|
.{ 6, 0, 0, 0 },
|
|
.{ 6, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
},
|
|
};
|
|
|
|
state.bricks[6] = Brick{
|
|
.rotating = false,
|
|
.pos = V2{ .x = 192, .y = 350 },
|
|
.color = 0xFF9F0000,
|
|
.tiles = [4][4]u32{
|
|
.{ 7, 7, 7, 0 },
|
|
.{ 7, 7, 7, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
},
|
|
};
|
|
|
|
state.bricks[7] = Brick{
|
|
.rotating = false,
|
|
.pos = V2{ .x = 224, .y = 450 },
|
|
.color = 0xFF8F0000,
|
|
.tiles = [4][4]u32{
|
|
.{ 8, 8, 8, 0 },
|
|
.{ 8, 0, 8, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
.{ 0, 0, 0, 0 },
|
|
},
|
|
};
|
|
|
|
memory.initialized = true;
|
|
}
|
|
|
|
// Update
|
|
const mouse_pos = V2{ .x = @floatCast(input.mouse_x), .y = @floatCast(input.mouse_y) };
|
|
|
|
const tile_size = V2{
|
|
.x = TILE_SIZE,
|
|
.y = TILE_SIZE,
|
|
};
|
|
|
|
var mouse_on_brick = false;
|
|
for (&state.bricks) |*brick| {
|
|
brick.rotating = false;
|
|
|
|
for (brick.tiles, 0..) |row, i| {
|
|
for (row, 0..) |tile, j| {
|
|
if (tile > 0) {
|
|
const tile_pos = V2{
|
|
.x = @floatFromInt(j * TILE_SIZE),
|
|
.y = @floatFromInt(i * TILE_SIZE),
|
|
};
|
|
const pos = tile_pos.add(brick.pos);
|
|
|
|
const camera_pos = pos.add(state.camera.pos);
|
|
if (pointInsideRect(state.mouse_pos, camera_pos, tile_size)) {
|
|
if (input.mouse_left_down) {
|
|
const diff = mouse_pos.sub(state.mouse_pos);
|
|
brick.pos = brick.pos.add(diff);
|
|
|
|
if (input.key.space.down and !brick.rotating) {
|
|
// TODO: Rotate
|
|
brick.rotating = true;
|
|
const rotated = brick.rotate();
|
|
brick.tiles = rotated;
|
|
}
|
|
|
|
mouse_on_brick = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (input.mouse_left_down and !mouse_on_brick) {
|
|
const diff = mouse_pos.sub(state.mouse_pos);
|
|
state.camera.pos = state.camera.pos.add(diff);
|
|
}
|
|
|
|
state.mouse_pos = mouse_pos;
|
|
|
|
// Render
|
|
fillRect(buffer, 0, 0, buffer.width, buffer.height, 0xFFFFFFFF);
|
|
|
|
const board_pos = mid;
|
|
for (0..7) |i| {
|
|
for (0..7) |j| {
|
|
if (i == 0 and j == 6) continue;
|
|
if (i == 1 and j == 6) continue;
|
|
if (i == 6 and j == 3) continue;
|
|
if (i == 6 and j == 4) continue;
|
|
if (i == 6 and j == 5) continue;
|
|
if (i == 6 and j == 6) continue;
|
|
|
|
const tile_pos = V2{
|
|
.x = @floatFromInt(j * TILE_SIZE),
|
|
.y = @floatFromInt(i * TILE_SIZE),
|
|
};
|
|
const camera_pos = board_pos.add(tile_pos).add(state.camera.pos);
|
|
fillSquare(buffer, @intFromFloat(camera_pos.x), @intFromFloat(camera_pos.y), 30, 0xFF000000);
|
|
}
|
|
}
|
|
|
|
for (state.bricks) |brick| {
|
|
for (brick.tiles, 0..) |row, i| {
|
|
for (row, 0..) |tile, j| {
|
|
if (tile > 0) {
|
|
const pos = V2{
|
|
.x = @floatFromInt(j * TILE_SIZE),
|
|
.y = @floatFromInt(i * TILE_SIZE),
|
|
};
|
|
const camera_pos = brick.pos.add(pos).add(state.camera.pos);
|
|
fillSquare(
|
|
buffer,
|
|
@intFromFloat(camera_pos.x),
|
|
@intFromFloat(camera_pos.y),
|
|
30,
|
|
if (brick.rotating) 0xFF00FF00 else brick.color ,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
state.frame_count += 1;
|
|
}
|