//! 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; }