Add tile snapping
This commit is contained in:
+155
-13
@@ -70,6 +70,11 @@ const V2 = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Rect = struct {
|
||||||
|
pos: V2,
|
||||||
|
size: V2,
|
||||||
|
};
|
||||||
|
|
||||||
const BBox = struct {
|
const BBox = struct {
|
||||||
min: V2,
|
min: V2,
|
||||||
max: V2,
|
max: V2,
|
||||||
@@ -103,7 +108,42 @@ const Board = struct {
|
|||||||
pos: V2,
|
pos: V2,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
tile_size: V2,
|
||||||
tiles: [7][7]BoardTile,
|
tiles: [7][7]BoardTile,
|
||||||
|
|
||||||
|
fn getTilePos(self: Board, i: usize, j: usize) V2 {
|
||||||
|
const tile_pos = V2{
|
||||||
|
.x = @as(f32, @floatFromInt(j)) * self.tile_size.x,
|
||||||
|
.y = @as(f32, @floatFromInt(i)) * self.tile_size.y,
|
||||||
|
};
|
||||||
|
const result = tile_pos.add(self.pos);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const BoardIndex = struct {
|
||||||
|
i: usize,
|
||||||
|
j: usize,
|
||||||
|
|
||||||
|
fn init(i: usize, j: usize) BoardIndex {
|
||||||
|
return .{
|
||||||
|
.i = i,
|
||||||
|
.j = j,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max() BoardIndex {
|
||||||
|
return .{
|
||||||
|
.i = std.math.maxInt(usize),
|
||||||
|
.j = std.math.maxInt(usize),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const BrickTile = struct {
|
||||||
|
id: u32,
|
||||||
|
board_index: ?BoardIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Brick = struct {
|
const Brick = struct {
|
||||||
@@ -111,6 +151,7 @@ const Brick = struct {
|
|||||||
pos: V2,
|
pos: V2,
|
||||||
color: u32,
|
color: u32,
|
||||||
rotating_time: f64,
|
rotating_time: f64,
|
||||||
|
board_indices: [4][4]?BoardIndex,
|
||||||
tiles: [4][4]u32,
|
tiles: [4][4]u32,
|
||||||
|
|
||||||
fn transpose(self: Brick) [4][4]u32 {
|
fn transpose(self: Brick) [4][4]u32 {
|
||||||
@@ -203,6 +244,43 @@ fn tilesIntersects(r1: V2, r2: V2) bool {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rectsIntersects(r1: V2, r1_size: V2, r2: V2, r2_size: V2) bool {
|
||||||
|
var result = false;
|
||||||
|
|
||||||
|
const r2a: V2 = V2.init(r2.x, r2.y);
|
||||||
|
const r2b: V2 = V2.init(r2.x, r2.y + r2_size.y);
|
||||||
|
const r2c: V2 = V2.init(r2.x + r2_size.x, r2.y);
|
||||||
|
const r2d: V2 = V2.init(r2.x + r2_size.x, r2.y + r2_size.y);
|
||||||
|
|
||||||
|
const a = pointInsideRect(r2a, r1, r1_size);
|
||||||
|
const b = pointInsideRect(r2b, r1, r1_size);
|
||||||
|
const c = pointInsideRect(r2c, r1, r1_size);
|
||||||
|
const d = pointInsideRect(r2d, r1, r1_size);
|
||||||
|
|
||||||
|
result = a or b or c or d;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn brickHasValidPos(brick: *const Brick) bool {
|
||||||
|
var valid_pos = true;
|
||||||
|
|
||||||
|
for (brick.board_indices, 0..) |row, i| {
|
||||||
|
for (row, 0..) |opt, j| {
|
||||||
|
const tile = brick.tiles[i][j];
|
||||||
|
if (tile > 0) {
|
||||||
|
if (opt != null) {
|
||||||
|
valid_pos = valid_pos and true;
|
||||||
|
} else {
|
||||||
|
valid_pos = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid_pos;
|
||||||
|
}
|
||||||
|
|
||||||
fn findBrickBoundingBox(brick: *const Brick) BBox {
|
fn findBrickBoundingBox(brick: *const Brick) BBox {
|
||||||
var result = BBox{ .min = V2.max, .max = V2.zero, .center = V2.zero };
|
var result = BBox{ .min = V2.max, .max = V2.zero, .center = V2.zero };
|
||||||
|
|
||||||
@@ -231,6 +309,24 @@ fn findBrickBoundingBox(brick: *const Brick) BBox {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn findBrickMinBoardIdx(brick: *const Brick) BoardIndex {
|
||||||
|
var result = BoardIndex.max();
|
||||||
|
|
||||||
|
for (brick.board_indices, 0..) |row, i| {
|
||||||
|
for (row, 0..) |opt, j| {
|
||||||
|
const tile = brick.tiles[i][j];
|
||||||
|
if (tile > 0) {
|
||||||
|
if (opt) |index| {
|
||||||
|
result.i = if (index.i < result.i) index.i else result.i;
|
||||||
|
result.j = if (index.j < result.j) index.j else result.j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
fn drawRect(buffer: platform.OffscreenBuffer, pos: V2, size: V2, color: u32, line_width: i32) void {
|
fn drawRect(buffer: platform.OffscreenBuffer, pos: V2, size: V2, color: u32, line_width: i32) void {
|
||||||
var pixels: []u32 = @ptrCast(@alignCast(buffer.data));
|
var pixels: []u32 = @ptrCast(@alignCast(buffer.data));
|
||||||
|
|
||||||
@@ -332,11 +428,19 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
|
|
||||||
state.grabbed_brick_index = null;
|
state.grabbed_brick_index = null;
|
||||||
|
|
||||||
|
const indices = [4][4]?BoardIndex{
|
||||||
|
.{ null, null, null, null },
|
||||||
|
.{ null, null, null, null },
|
||||||
|
.{ null, null, null, null },
|
||||||
|
.{ null, null, null, null },
|
||||||
|
};
|
||||||
|
|
||||||
state.bricks[0] = Brick{
|
state.bricks[0] = Brick{
|
||||||
.rotating = false,
|
.rotating = false,
|
||||||
.rotating_time = 0,
|
.rotating_time = 0,
|
||||||
.pos = V2{ .x = 0, .y = 50 },
|
.pos = V2{ .x = 0, .y = 50 },
|
||||||
.color = 0xFF586F52,
|
.color = 0xFF586F52,
|
||||||
|
.board_indices = indices,
|
||||||
.tiles = [4][4]u32{
|
.tiles = [4][4]u32{
|
||||||
.{ 1, 1, 1, 1 },
|
.{ 1, 1, 1, 1 },
|
||||||
.{ 1, 0, 0, 0 },
|
.{ 1, 0, 0, 0 },
|
||||||
@@ -350,6 +454,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
.rotating_time = 0,
|
.rotating_time = 0,
|
||||||
.pos = V2{ .x = 256, .y = 50 },
|
.pos = V2{ .x = 256, .y = 50 },
|
||||||
.color = 0xFF8299B2,
|
.color = 0xFF8299B2,
|
||||||
|
.board_indices = indices,
|
||||||
.tiles = [4][4]u32{
|
.tiles = [4][4]u32{
|
||||||
.{ 2, 2, 2, 0 },
|
.{ 2, 2, 2, 0 },
|
||||||
.{ 2, 2, 0, 0 },
|
.{ 2, 2, 0, 0 },
|
||||||
@@ -363,6 +468,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
.rotating_time = 0,
|
.rotating_time = 0,
|
||||||
.pos = V2{ .x = 384, .y = 50 },
|
.pos = V2{ .x = 384, .y = 50 },
|
||||||
.color = 0xFF602925,
|
.color = 0xFF602925,
|
||||||
|
.board_indices = indices,
|
||||||
.tiles = [4][4]u32{
|
.tiles = [4][4]u32{
|
||||||
.{ 3, 3, 3, 0 },
|
.{ 3, 3, 3, 0 },
|
||||||
.{ 0, 0, 3, 3 },
|
.{ 0, 0, 3, 3 },
|
||||||
@@ -376,6 +482,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
.rotating_time = 0,
|
.rotating_time = 0,
|
||||||
.pos = V2{ .x = 512, .y = 50 },
|
.pos = V2{ .x = 512, .y = 50 },
|
||||||
.color = 0xFFDA983C,
|
.color = 0xFFDA983C,
|
||||||
|
.board_indices = indices,
|
||||||
.tiles = [4][4]u32{
|
.tiles = [4][4]u32{
|
||||||
.{ 4, 0, 0, 0 },
|
.{ 4, 0, 0, 0 },
|
||||||
.{ 4, 4, 4, 0 },
|
.{ 4, 4, 4, 0 },
|
||||||
@@ -389,6 +496,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
.rotating_time = 0,
|
.rotating_time = 0,
|
||||||
.pos = V2{ .x = 384, .y = 150 },
|
.pos = V2{ .x = 384, .y = 150 },
|
||||||
.color = 0xFF3A5E7A,
|
.color = 0xFF3A5E7A,
|
||||||
|
.board_indices = indices,
|
||||||
.tiles = [4][4]u32{
|
.tiles = [4][4]u32{
|
||||||
.{ 5, 5, 5, 5 },
|
.{ 5, 5, 5, 5 },
|
||||||
.{ 0, 5, 0, 0 },
|
.{ 0, 5, 0, 0 },
|
||||||
@@ -402,6 +510,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
.rotating_time = 0,
|
.rotating_time = 0,
|
||||||
.pos = V2{ .x = 160, .y = 250 },
|
.pos = V2{ .x = 160, .y = 250 },
|
||||||
.color = 0xFFD69EB0,
|
.color = 0xFFD69EB0,
|
||||||
|
.board_indices = indices,
|
||||||
.tiles = [4][4]u32{
|
.tiles = [4][4]u32{
|
||||||
.{ 6, 6, 6, 0 },
|
.{ 6, 6, 6, 0 },
|
||||||
.{ 6, 0, 0, 0 },
|
.{ 6, 0, 0, 0 },
|
||||||
@@ -415,6 +524,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
.rotating_time = 0,
|
.rotating_time = 0,
|
||||||
.pos = V2{ .x = 192, .y = 350 },
|
.pos = V2{ .x = 192, .y = 350 },
|
||||||
.color = 0xFF9AAB89,
|
.color = 0xFF9AAB89,
|
||||||
|
.board_indices = indices,
|
||||||
.tiles = [4][4]u32{
|
.tiles = [4][4]u32{
|
||||||
.{ 7, 7, 7, 0 },
|
.{ 7, 7, 7, 0 },
|
||||||
.{ 7, 7, 7, 0 },
|
.{ 7, 7, 7, 0 },
|
||||||
@@ -428,6 +538,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
.rotating_time = 0,
|
.rotating_time = 0,
|
||||||
.pos = V2{ .x = 224, .y = 450 },
|
.pos = V2{ .x = 224, .y = 450 },
|
||||||
.color = 0xFFBA4816,
|
.color = 0xFFBA4816,
|
||||||
|
.board_indices = indices,
|
||||||
.tiles = [4][4]u32{
|
.tiles = [4][4]u32{
|
||||||
.{ 8, 8, 8, 0 },
|
.{ 8, 8, 8, 0 },
|
||||||
.{ 8, 0, 8, 0 },
|
.{ 8, 0, 8, 0 },
|
||||||
@@ -462,6 +573,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
.pos = mid,
|
.pos = mid,
|
||||||
.width = 7,
|
.width = 7,
|
||||||
.height = 7,
|
.height = 7,
|
||||||
|
.tile_size = V2.init(32, 32),
|
||||||
.tiles = tiles,
|
.tiles = tiles,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -490,7 +602,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
for (&state.bricks, 0..) |*brick, brick_index| {
|
for (&state.bricks, 0..) |*brick, brick_index| {
|
||||||
if (brick.rotating) {
|
if (brick.rotating) {
|
||||||
brick.rotating_time += input.delta_time;
|
brick.rotating_time += input.delta_time;
|
||||||
if (brick.rotating_time > 0.25) {
|
if (brick.rotating_time > 0.05) {
|
||||||
brick.rotating = false;
|
brick.rotating = false;
|
||||||
brick.rotating_time = 0.0;
|
brick.rotating_time = 0.0;
|
||||||
}
|
}
|
||||||
@@ -500,6 +612,10 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
for (row, 0..) |tile, j| {
|
for (row, 0..) |tile, j| {
|
||||||
if (tile > 0) {
|
if (tile > 0) {
|
||||||
const pos = getTilePos(i, j).add(brick.pos);
|
const pos = getTilePos(i, j).add(brick.pos);
|
||||||
|
const tile_mid = pos.add(tile_size.mul(0.5));
|
||||||
|
|
||||||
|
const tile_board_index = &brick.board_indices[i][j];
|
||||||
|
tile_board_index.* = null;
|
||||||
|
|
||||||
if (input.mouse_left_down) {
|
if (input.mouse_left_down) {
|
||||||
if (pointInsideRect(mouse_world_pos, pos, tile_size)) {
|
if (pointInsideRect(mouse_world_pos, pos, tile_size)) {
|
||||||
@@ -511,10 +627,16 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
|
|
||||||
for (&state.board.tiles, 0..) |*board_row, k| {
|
for (&state.board.tiles, 0..) |*board_row, k| {
|
||||||
for (board_row, 0..) |*board_tile, l| {
|
for (board_row, 0..) |*board_tile, l| {
|
||||||
const board_tile_pos = getTilePos(k, l).add(state.board.pos);
|
const board_tile_pos = state.board.getTilePos(k, l);
|
||||||
|
|
||||||
|
const in_tile = pointInsideRect(tile_mid, board_tile_pos, state.board.tile_size);
|
||||||
|
if (in_tile and !board_tile.blocked and !board_tile.taken) {
|
||||||
|
tile_board_index.* = BoardIndex.init(k, l);
|
||||||
|
// board_tile.taken = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!board_tile.hovering) {
|
if (!board_tile.hovering) {
|
||||||
board_tile.hovering = tilesIntersects(board_tile_pos, pos);
|
board_tile.hovering = in_tile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,6 +669,19 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
brick.pos = mouse_world_pos.add(center);
|
brick.pos = mouse_world_pos.add(center);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!input.mouse_left_down) {
|
||||||
|
const valid_pos = brickHasValidPos(brick);
|
||||||
|
|
||||||
|
if (valid_pos) {
|
||||||
|
// TODO: Snap to board
|
||||||
|
const min = findBrickMinBoardIdx(brick);
|
||||||
|
const board_tile_pos = state.board.getTilePos(min.i, min.j);
|
||||||
|
const bbox = findBrickBoundingBox(brick);
|
||||||
|
const diff = brick.pos.sub(bbox.min);
|
||||||
|
brick.pos = board_tile_pos.add(diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.mouse_left_down and state.grabbed_brick_index == null) {
|
if (input.mouse_left_down and state.grabbed_brick_index == null) {
|
||||||
@@ -559,6 +694,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
// Render
|
// Render
|
||||||
fillRect(buffer, 0, 0, buffer.width, buffer.height, 0xFFFFFFFF);
|
fillRect(buffer, 0, 0, buffer.width, buffer.height, 0xFFFFFFFF);
|
||||||
|
|
||||||
|
// Draw board
|
||||||
{
|
{
|
||||||
const board = state.board;
|
const board = state.board;
|
||||||
// const board_size = V2.init_int(
|
// const board_size = V2.init_int(
|
||||||
@@ -567,17 +703,14 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
// );
|
// );
|
||||||
// const half = board_size.mul(0.5);
|
// const half = board_size.mul(0.5);
|
||||||
|
|
||||||
const board_camera_pos = board.pos.add(state.camera.pos);
|
|
||||||
|
|
||||||
for (board.tiles, 0..) |row, i| {
|
for (board.tiles, 0..) |row, i| {
|
||||||
for (row, 0..) |tile, j| {
|
for (row, 0..) |tile, j| {
|
||||||
const tile_pos = V2{
|
const tile_pos = board.getTilePos(i, j);
|
||||||
.x = @floatFromInt(j * TILE_SIZE),
|
const camera_pos = tile_pos.add(state.camera.pos);
|
||||||
.y = @floatFromInt(i * TILE_SIZE),
|
|
||||||
};
|
|
||||||
const camera_pos = board_camera_pos.add(tile_pos);
|
|
||||||
const pos_x: i32 = @intFromFloat(camera_pos.x);
|
const pos_x: i32 = @intFromFloat(camera_pos.x);
|
||||||
const pos_y: i32 = @intFromFloat(camera_pos.y);
|
const pos_y: i32 = @intFromFloat(camera_pos.y);
|
||||||
|
const width: i32 = @intFromFloat(board.tile_size.x);
|
||||||
|
const height: i32 = @intFromFloat(board.tile_size.y);
|
||||||
|
|
||||||
var color: u32 = 0xFF000000;
|
var color: u32 = 0xFF000000;
|
||||||
if (tile.blocked) {
|
if (tile.blocked) {
|
||||||
@@ -586,8 +719,8 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
color = 0xFF00FF00;
|
color = 0xFF00FF00;
|
||||||
}
|
}
|
||||||
|
|
||||||
fillSquare(buffer, pos_x, pos_y, TILE_SIZE, color);
|
fillRect(buffer, pos_x, pos_y, width, height, color);
|
||||||
drawRect(buffer, camera_pos, tile_size, 0xFFFFFFFF, 2);
|
drawRect(buffer, camera_pos, board.tile_size, 0xFFFFFFFF, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -600,12 +733,21 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
|
|||||||
if (tile > 0) {
|
if (tile > 0) {
|
||||||
const pos = getTilePos(i, j).add(brick.pos);
|
const pos = getTilePos(i, j).add(brick.pos);
|
||||||
const camera_pos = pos.add(state.camera.pos);
|
const camera_pos = pos.add(state.camera.pos);
|
||||||
|
var color: u32 = if (brick.rotating) 0xFF007800 else brick.color;
|
||||||
|
|
||||||
|
if (comptime builtin.mode == std.builtin.OptimizeMode.Debug) {
|
||||||
|
const board_index = brick.board_indices[i][j];
|
||||||
|
if (board_index != null) {
|
||||||
|
color = 0xFFFF0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fillSquare(
|
fillSquare(
|
||||||
buffer,
|
buffer,
|
||||||
@intFromFloat(camera_pos.x),
|
@intFromFloat(camera_pos.x),
|
||||||
@intFromFloat(camera_pos.y),
|
@intFromFloat(camera_pos.y),
|
||||||
30,
|
30,
|
||||||
if (brick.rotating) 0xFF00FF00 else brick.color,
|
color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user