2024-04-14 14:31:52 -06:00
|
|
|
pub const main = seizer.main;
|
|
|
|
|
2024-05-25 04:23:28 -06:00
|
|
|
const APPNAME = "seizer-solitaire";
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
var prng: std.rand.DefaultPrng = undefined;
|
2024-05-03 19:58:39 -06:00
|
|
|
var window_global: seizer.Window = undefined;
|
2024-04-14 14:31:52 -06:00
|
|
|
var canvas: seizer.Canvas = undefined;
|
|
|
|
var card_tilemap: ?DeckSprites = null;
|
|
|
|
|
|
|
|
var draw_pile: std.ArrayListUnmanaged(Card) = .{};
|
|
|
|
var draw_pile_exhausted = false;
|
|
|
|
var drawn_cards: std.ArrayListUnmanaged(Card) = .{};
|
|
|
|
var stacks: [7]std.ArrayListUnmanaged(Card) = [_]std.ArrayListUnmanaged(Card){.{}} ** 7;
|
|
|
|
var foundations: [4]std.ArrayListUnmanaged(Card) = [_]std.ArrayListUnmanaged(Card){.{}} ** 4;
|
|
|
|
|
2024-04-21 02:56:58 -06:00
|
|
|
var history: std.ArrayListUnmanaged(Snapshot) = .{};
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
var last_hovered_stack: usize = 0;
|
|
|
|
var last_hovered_foundation: usize = 0;
|
|
|
|
var hovered_deck: ?*std.ArrayListUnmanaged(Card) = null;
|
|
|
|
var hovered_card: usize = 0;
|
|
|
|
|
|
|
|
var selected_deck: ?*std.ArrayListUnmanaged(Card) = null;
|
|
|
|
var selected_card: usize = 0;
|
|
|
|
|
2024-04-21 01:04:51 -06:00
|
|
|
var show_menu: bool = false;
|
|
|
|
var menu_item_selected: u32 = 0;
|
|
|
|
const MENU_ITEMS = [_][]const u8{
|
|
|
|
"Continue",
|
|
|
|
"New Game",
|
|
|
|
"Quit",
|
|
|
|
};
|
|
|
|
|
2024-05-25 14:55:31 -06:00
|
|
|
var win_count: ?u32 = null;
|
2024-04-21 01:56:45 -06:00
|
|
|
var win_triggered: bool = false;
|
2024-05-25 14:55:31 -06:00
|
|
|
var frame_count: u64 = 0;
|
2024-04-21 01:56:45 -06:00
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
pub fn init() !void {
|
2024-04-14 14:31:52 -06:00
|
|
|
prng = std.rand.DefaultPrng.init(@bitCast(std.time.timestamp()));
|
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
window_global = try seizer.platform.createWindow(.{
|
2024-04-14 14:31:52 -06:00
|
|
|
.title = "Seizer Solitaire",
|
|
|
|
.on_render = render,
|
|
|
|
.on_destroy = destroy,
|
|
|
|
});
|
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
canvas = try seizer.Canvas.init(seizer.platform.allocator(), .{});
|
2024-04-14 14:31:52 -06:00
|
|
|
errdefer canvas.deinit();
|
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
card_tilemap = try assets.loadSolitaireCards(seizer.platform.allocator());
|
2024-04-14 14:31:52 -06:00
|
|
|
|
2024-04-21 01:04:51 -06:00
|
|
|
try resetGame();
|
2024-04-14 14:31:52 -06:00
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
try seizer.platform.addButtonInput(.{
|
2024-04-20 23:51:56 -06:00
|
|
|
.title = "move_up",
|
|
|
|
.on_event = moveUp,
|
2024-05-23 16:25:01 -06:00
|
|
|
.default_bindings = &.{
|
|
|
|
.{ .gamepad = .dpup },
|
|
|
|
.{ .keyboard = .up },
|
|
|
|
},
|
2024-04-20 23:51:56 -06:00
|
|
|
});
|
2024-05-25 22:14:16 -06:00
|
|
|
try seizer.platform.addButtonInput(.{
|
2024-04-20 23:51:56 -06:00
|
|
|
.title = "move_down",
|
|
|
|
.on_event = moveDown,
|
2024-05-23 16:25:01 -06:00
|
|
|
.default_bindings = &.{
|
|
|
|
.{ .gamepad = .dpdown },
|
|
|
|
.{ .keyboard = .down },
|
|
|
|
},
|
2024-04-20 23:51:56 -06:00
|
|
|
});
|
2024-05-25 22:14:16 -06:00
|
|
|
try seizer.platform.addButtonInput(.{
|
2024-04-20 23:51:56 -06:00
|
|
|
.title = "move_left",
|
|
|
|
.on_event = moveLeft,
|
2024-05-23 16:25:01 -06:00
|
|
|
.default_bindings = &.{
|
|
|
|
.{ .gamepad = .dpleft },
|
|
|
|
.{ .keyboard = .left },
|
|
|
|
},
|
2024-04-20 23:51:56 -06:00
|
|
|
});
|
2024-05-25 22:14:16 -06:00
|
|
|
try seizer.platform.addButtonInput(.{
|
2024-04-20 23:51:56 -06:00
|
|
|
.title = "move_right",
|
|
|
|
.on_event = moveRight,
|
2024-05-23 16:25:01 -06:00
|
|
|
.default_bindings = &.{
|
|
|
|
.{ .gamepad = .dpright },
|
|
|
|
.{ .keyboard = .right },
|
|
|
|
},
|
2024-04-20 23:51:56 -06:00
|
|
|
});
|
2024-05-25 22:14:16 -06:00
|
|
|
try seizer.platform.addButtonInput(.{
|
2024-04-20 23:51:56 -06:00
|
|
|
.title = "select_or_place",
|
|
|
|
.on_event = doSelectOrPlace,
|
2024-05-23 16:25:01 -06:00
|
|
|
.default_bindings = &.{
|
|
|
|
.{ .gamepad = .a },
|
|
|
|
.{ .keyboard = .z },
|
|
|
|
},
|
2024-04-20 23:51:56 -06:00
|
|
|
});
|
2024-05-25 22:14:16 -06:00
|
|
|
try seizer.platform.addButtonInput(.{
|
2024-04-20 23:51:56 -06:00
|
|
|
.title = "deselect",
|
|
|
|
.on_event = deselect,
|
2024-05-23 16:25:01 -06:00
|
|
|
.default_bindings = &.{
|
|
|
|
.{ .gamepad = .b },
|
|
|
|
.{ .keyboard = .x },
|
|
|
|
},
|
2024-04-20 23:51:56 -06:00
|
|
|
});
|
2024-05-25 22:14:16 -06:00
|
|
|
try seizer.platform.addButtonInput(.{
|
2024-04-21 01:04:51 -06:00
|
|
|
.title = "toggle_menu",
|
|
|
|
.on_event = toggleMenu,
|
2024-05-23 16:25:01 -06:00
|
|
|
.default_bindings = &.{
|
|
|
|
.{ .gamepad = .start },
|
2024-05-25 04:23:28 -06:00
|
|
|
.{ .keyboard = .esc },
|
2024-05-23 16:25:01 -06:00
|
|
|
},
|
2024-04-21 01:04:51 -06:00
|
|
|
});
|
2024-05-25 22:14:16 -06:00
|
|
|
try seizer.platform.addButtonInput(.{
|
2024-04-21 02:56:58 -06:00
|
|
|
.title = "undo",
|
|
|
|
.on_event = undo,
|
2024-05-23 16:25:01 -06:00
|
|
|
.default_bindings = &.{
|
|
|
|
.{ .gamepad = .x },
|
2024-05-25 04:23:28 -06:00
|
|
|
.{ .keyboard = .a },
|
2024-05-23 16:25:01 -06:00
|
|
|
},
|
2024-04-21 02:56:58 -06:00
|
|
|
});
|
2024-04-21 02:19:52 -06:00
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
const read_buffer = try seizer.platform.allocator().alloc(u8, @sizeOf(u32));
|
|
|
|
seizer.platform.readFile(.{
|
2024-05-25 04:23:28 -06:00
|
|
|
.appname = APPNAME,
|
|
|
|
.path = "win_count.bin",
|
|
|
|
.buffer = read_buffer,
|
|
|
|
.callback = onWinCountRead,
|
|
|
|
.userdata = read_buffer.ptr,
|
|
|
|
});
|
2024-05-23 16:25:01 -06:00
|
|
|
}
|
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
fn onWinCountRead(userdata: ?*anyopaque, result: seizer.Platform.FileError![]const u8) void {
|
2024-05-25 04:23:28 -06:00
|
|
|
const buffer: *[4]u8 = @ptrCast(userdata);
|
2024-05-25 22:14:16 -06:00
|
|
|
defer seizer.platform.allocator().free(buffer);
|
2024-05-23 16:25:01 -06:00
|
|
|
|
2024-05-25 04:23:28 -06:00
|
|
|
if (result) |bytes| {
|
2024-05-25 14:55:31 -06:00
|
|
|
if (bytes.len >= @sizeOf(u32)) {
|
2024-05-25 04:23:28 -06:00
|
|
|
win_count = std.mem.readInt(u32, bytes[0..4], .little);
|
|
|
|
}
|
|
|
|
} else |_| {}
|
2024-04-21 01:04:51 -06:00
|
|
|
}
|
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
fn onWinCountWritten(userdata: ?*anyopaque, result: seizer.Platform.FileError!void) void {
|
2024-05-25 04:23:28 -06:00
|
|
|
const buffer: *u32 = @ptrCast(@alignCast(userdata));
|
2024-05-25 22:14:16 -06:00
|
|
|
defer seizer.platform.allocator().destroy(buffer);
|
2024-05-25 04:23:28 -06:00
|
|
|
_ = result catch {};
|
2024-05-23 16:25:01 -06:00
|
|
|
}
|
|
|
|
|
2024-04-21 01:04:51 -06:00
|
|
|
fn resetGame() !void {
|
2024-04-23 22:17:35 -06:00
|
|
|
resetHistory();
|
2024-05-25 22:14:16 -06:00
|
|
|
draw_pile.deinit(seizer.platform.allocator());
|
2024-04-21 01:04:51 -06:00
|
|
|
draw_pile_exhausted = false;
|
|
|
|
drawn_cards.shrinkRetainingCapacity(0);
|
|
|
|
for (&stacks) |*stack| {
|
|
|
|
stack.shrinkRetainingCapacity(0);
|
|
|
|
}
|
|
|
|
for (&foundations) |*foundation| {
|
|
|
|
foundation.shrinkRetainingCapacity(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
last_hovered_stack = 0;
|
|
|
|
last_hovered_foundation = 0;
|
|
|
|
hovered_deck = null;
|
|
|
|
hovered_card = 0;
|
|
|
|
|
|
|
|
selected_deck = null;
|
|
|
|
selected_card = 0;
|
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
draw_pile = std.ArrayListUnmanaged(Card).fromOwnedSlice(try makeStandardDeck(seizer.platform.allocator()));
|
2024-04-21 01:04:51 -06:00
|
|
|
prng.random().shuffle(Card, draw_pile.items);
|
|
|
|
|
|
|
|
for (&stacks, 0..) |*stack, i| {
|
|
|
|
for (0..i + 1) |_| {
|
|
|
|
const drawn_card = draw_pile.pop();
|
2024-05-25 22:14:16 -06:00
|
|
|
try stack.append(seizer.platform.allocator(), drawn_card);
|
2024-04-21 01:04:51 -06:00
|
|
|
}
|
|
|
|
}
|
2024-04-21 01:56:45 -06:00
|
|
|
|
|
|
|
win_triggered = false;
|
2024-04-14 14:31:52 -06:00
|
|
|
}
|
|
|
|
|
2024-04-21 02:56:58 -06:00
|
|
|
fn resetHistory() void {
|
|
|
|
for (history.items) |*snapshot| {
|
|
|
|
snapshot.deinit();
|
|
|
|
}
|
|
|
|
history.shrinkRetainingCapacity(0);
|
|
|
|
}
|
|
|
|
|
2024-05-03 19:58:39 -06:00
|
|
|
fn destroy(window: seizer.Window) void {
|
2024-04-14 14:31:52 -06:00
|
|
|
_ = window;
|
2024-04-23 22:17:35 -06:00
|
|
|
for (history.items) |*snapshot| {
|
|
|
|
snapshot.deinit();
|
|
|
|
}
|
2024-05-25 22:14:16 -06:00
|
|
|
history.deinit(seizer.platform.allocator());
|
2024-04-23 22:17:35 -06:00
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
draw_pile.deinit(seizer.platform.allocator());
|
|
|
|
drawn_cards.deinit(seizer.platform.allocator());
|
2024-04-14 14:31:52 -06:00
|
|
|
for (&stacks) |*stack| {
|
2024-05-25 22:14:16 -06:00
|
|
|
stack.deinit(seizer.platform.allocator());
|
2024-04-14 14:31:52 -06:00
|
|
|
}
|
|
|
|
for (&foundations) |*foundation| {
|
2024-05-25 22:14:16 -06:00
|
|
|
foundation.deinit(seizer.platform.allocator());
|
2024-04-14 14:31:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (card_tilemap) |*ct| {
|
2024-05-25 22:14:16 -06:00
|
|
|
ct.deinit(seizer.platform.allocator());
|
2024-04-14 14:31:52 -06:00
|
|
|
card_tilemap = null;
|
|
|
|
}
|
|
|
|
canvas.deinit();
|
|
|
|
}
|
|
|
|
|
2024-05-03 19:58:39 -06:00
|
|
|
fn render(window: seizer.Window) !void {
|
2024-05-25 14:55:31 -06:00
|
|
|
if (haveWon() and !win_triggered and win_count != null) {
|
|
|
|
win_count.? += 1;
|
2024-04-21 02:19:52 -06:00
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
const win_count_write_buffer = try seizer.platform.allocator().create(u32);
|
2024-05-25 14:55:31 -06:00
|
|
|
std.mem.writeInt(u32, std.mem.asBytes(win_count_write_buffer), win_count.?, .little);
|
2024-05-25 22:14:16 -06:00
|
|
|
seizer.platform.writeFile(.{
|
2024-05-25 04:23:28 -06:00
|
|
|
.appname = APPNAME,
|
|
|
|
.path = "win_count.bin",
|
|
|
|
.data = std.mem.asBytes(win_count_write_buffer),
|
|
|
|
.callback = onWinCountWritten,
|
|
|
|
.userdata = win_count_write_buffer,
|
|
|
|
});
|
2024-04-21 02:19:52 -06:00
|
|
|
|
2024-04-21 02:56:58 -06:00
|
|
|
resetHistory();
|
2024-04-21 01:56:45 -06:00
|
|
|
win_triggered = true;
|
|
|
|
}
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
gl.clearColor(0.2, 0.4, 0.2, 1.0);
|
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
|
|
|
|
|
|
canvas.begin(.{
|
|
|
|
.window_size = window.getSize(),
|
|
|
|
.framebuffer_size = window.getFramebufferSize(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const scale: u32 = if (canvas.window_size[1] <= 400) 1 else 2;
|
|
|
|
const scalef: f32 = @floatFromInt(scale);
|
|
|
|
const tile_size = [2]u32{
|
|
|
|
card_tilemap.?.tilesheet.tile_size[0] * scale,
|
|
|
|
card_tilemap.?.tilesheet.tile_size[1] * scale,
|
|
|
|
};
|
|
|
|
const margin = card_tilemap.?.margin * scale;
|
2024-04-21 01:56:45 -06:00
|
|
|
const marginf: f32 = @floatFromInt(card_tilemap.?.margin * scale);
|
2024-04-14 14:31:52 -06:00
|
|
|
const hand_offset = [2]u32{
|
|
|
|
card_tilemap.?.hand_offset[0] * scale,
|
|
|
|
card_tilemap.?.hand_offset[1] * scale,
|
|
|
|
};
|
2024-04-21 01:56:45 -06:00
|
|
|
const hand_offsetf = [2]f32{
|
|
|
|
@floatFromInt(hand_offset[0]),
|
|
|
|
@floatFromInt(hand_offset[1]),
|
|
|
|
};
|
2024-04-14 14:31:52 -06:00
|
|
|
const tile_sizef = [2]f32{ @floatFromInt(tile_size[0]), @floatFromInt(tile_size[1]) };
|
|
|
|
|
2024-04-21 01:56:45 -06:00
|
|
|
const space_taken_by_board = [2]f32{
|
|
|
|
@floatFromInt(2 * margin + 8 * (margin + tile_size[0])),
|
|
|
|
@floatFromInt(2 * margin + 5 * (margin + tile_size[1])),
|
|
|
|
};
|
|
|
|
const board_offset = [2]f32{
|
2024-05-29 18:03:16 -06:00
|
|
|
@floor((canvas.window_size[0] - space_taken_by_board[0]) / 2),
|
|
|
|
@floor((canvas.window_size[1] - space_taken_by_board[1]) / 2),
|
2024-04-21 01:56:45 -06:00
|
|
|
};
|
|
|
|
|
2024-05-25 14:55:31 -06:00
|
|
|
if (win_count) |w| {
|
2024-04-21 01:56:45 -06:00
|
|
|
_ = canvas.printText(
|
|
|
|
.{
|
|
|
|
canvas.window_size[0] - board_offset[0] - marginf - hand_offsetf[0],
|
|
|
|
board_offset[1] + marginf + hand_offsetf[1],
|
|
|
|
},
|
|
|
|
"Win Count: {}",
|
2024-05-25 14:55:31 -06:00
|
|
|
.{w},
|
|
|
|
.{ .@"align" = .right, .scale = scalef },
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
const throbber_character: u8 = switch ((frame_count / 5) % 8) {
|
|
|
|
0 => '-',
|
|
|
|
1 => '\\',
|
|
|
|
2 => '|',
|
|
|
|
3 => '/',
|
|
|
|
4 => '-',
|
|
|
|
5 => '\\',
|
|
|
|
6 => '|',
|
|
|
|
7 => '/',
|
|
|
|
else => unreachable,
|
|
|
|
};
|
|
|
|
_ = canvas.printText(
|
|
|
|
.{
|
|
|
|
canvas.window_size[0] - board_offset[0] - marginf - hand_offsetf[0],
|
|
|
|
board_offset[1] + marginf + hand_offsetf[1],
|
|
|
|
},
|
|
|
|
"Win Count: [{c}]",
|
|
|
|
.{throbber_character},
|
2024-04-21 01:56:45 -06:00
|
|
|
.{ .@"align" = .right, .scale = scalef },
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
for (&stacks, 0..) |*stack, i| {
|
|
|
|
const deck_is_selected = selected_deck != null and selected_deck.? == stack;
|
|
|
|
const deck_is_hovered = hovered_deck != null and hovered_deck.? == stack;
|
|
|
|
const deck_color = if (deck_is_selected)
|
|
|
|
[4]u8{ 0xFF, 0xA0, 0xA0, 0xFF }
|
|
|
|
else if (deck_is_hovered)
|
|
|
|
[4]u8{ 0xA0, 0xFF, 0xA0, 0xFF }
|
|
|
|
else
|
|
|
|
[4]u8{ 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
|
|
|
|
var pos = [2]f32{
|
2024-04-21 01:56:45 -06:00
|
|
|
@floor(@as(f32, @floatFromInt(margin + ((tile_size[0] + margin) * (i + 1))))),
|
|
|
|
@floor(@as(f32, @floatFromInt(margin + margin + tile_size[1]))),
|
2024-04-14 14:31:52 -06:00
|
|
|
};
|
2024-04-21 01:56:45 -06:00
|
|
|
pos[0] += board_offset[0];
|
|
|
|
pos[1] += board_offset[1];
|
2024-04-14 14:31:52 -06:00
|
|
|
canvas.rect(pos, tile_sizef, .{ .color = deck_color });
|
|
|
|
|
|
|
|
for (stack.items, 0..) |card, j| {
|
|
|
|
const is_selected = deck_is_selected and j >= selected_card;
|
|
|
|
const is_hovered = deck_is_hovered and j >= hovered_card;
|
|
|
|
const color = if (is_selected)
|
|
|
|
[4]u8{ 0xFF, 0xA0, 0xA0, 0xFF }
|
|
|
|
else if (is_hovered)
|
|
|
|
[4]u8{ 0xA0, 0xFF, 0xA0, 0xFF }
|
|
|
|
else
|
|
|
|
[4]u8{ 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
|
|
|
|
const tile_id = card_tilemap.?.getTileForCard(card);
|
|
|
|
card_tilemap.?.tilesheet.renderTile(&canvas, tile_id, pos, .{
|
|
|
|
.size = tile_sizef,
|
|
|
|
.color = color,
|
|
|
|
});
|
|
|
|
pos[1] += @floatFromInt(hand_offset[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-21 01:56:45 -06:00
|
|
|
if (haveWon()) {
|
|
|
|
const offset =
|
|
|
|
[2]f32{
|
|
|
|
2 * marginf + tile_sizef[0],
|
|
|
|
2 * marginf + tile_sizef[1],
|
|
|
|
};
|
|
|
|
_ = canvas.writeText(
|
|
|
|
.{
|
|
|
|
board_offset[0] + offset[0] + (space_taken_by_board[0] - offset[0]) / 2,
|
|
|
|
board_offset[1] + offset[1] + (space_taken_by_board[1] - offset[0]) / 2,
|
|
|
|
},
|
|
|
|
"You Win!",
|
|
|
|
.{
|
|
|
|
.baseline = .middle,
|
|
|
|
.@"align" = .center,
|
|
|
|
.scale = 2 * scalef,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
for (&foundations, 0..) |*foundation, i| {
|
|
|
|
const is_hovered = hovered_deck != null and hovered_deck.? == foundation;
|
|
|
|
const color = if (is_hovered) [4]u8{ 0xA0, 0xFF, 0xA0, 0xFF } else [4]u8{ 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
|
2024-04-21 01:15:00 -06:00
|
|
|
var pos = [2]f32{
|
2024-04-14 14:31:52 -06:00
|
|
|
@floatFromInt(margin),
|
|
|
|
@floatFromInt(margin + ((margin + tile_size[1]) * (i + 1))),
|
|
|
|
};
|
2024-04-21 01:56:45 -06:00
|
|
|
pos[0] += board_offset[0];
|
|
|
|
pos[1] += board_offset[1];
|
2024-04-14 14:31:52 -06:00
|
|
|
canvas.rect(pos, tile_sizef, .{ .color = color });
|
|
|
|
|
|
|
|
for (foundation.items) |card| {
|
|
|
|
const tile_id = card_tilemap.?.getTileForCard(card);
|
|
|
|
card_tilemap.?.tilesheet.renderTile(&canvas, tile_id, pos, .{
|
|
|
|
.size = tile_sizef,
|
|
|
|
.color = color,
|
|
|
|
});
|
2024-04-21 01:15:00 -06:00
|
|
|
pos[1] -= 0.25 * scalef;
|
2024-04-14 14:31:52 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const deck_is_selected = selected_deck != null and selected_deck.? == &drawn_cards;
|
|
|
|
const deck_is_hovered = hovered_deck != null and hovered_deck.? == &drawn_cards;
|
|
|
|
|
|
|
|
var pos = [2]f32{
|
|
|
|
@floatFromInt(margin + margin + tile_size[0]),
|
|
|
|
@floatFromInt(margin),
|
|
|
|
};
|
2024-04-21 01:56:45 -06:00
|
|
|
pos[0] += board_offset[0];
|
|
|
|
pos[1] += board_offset[1];
|
2024-04-14 14:31:52 -06:00
|
|
|
for (drawn_cards.items, 0..) |card, j| {
|
|
|
|
const is_selected = deck_is_selected and j >= selected_card;
|
|
|
|
const is_hovered = deck_is_hovered and j >= hovered_card;
|
|
|
|
const color = if (is_selected)
|
|
|
|
[4]u8{ 0xFF, 0xA0, 0xA0, 0xFF }
|
|
|
|
else if (is_hovered)
|
|
|
|
[4]u8{ 0xA0, 0xFF, 0xA0, 0xFF }
|
|
|
|
else
|
|
|
|
[4]u8{ 0xFF, 0xFF, 0xFF, 0xFF };
|
|
|
|
|
|
|
|
const tile_id = card_tilemap.?.getTileForCard(card);
|
|
|
|
card_tilemap.?.tilesheet.renderTile(&canvas, tile_id, pos, .{
|
|
|
|
.size = tile_sizef,
|
|
|
|
.color = color,
|
|
|
|
});
|
|
|
|
pos[0] += @floatFromInt(hand_offset[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2024-04-21 11:46:52 -06:00
|
|
|
const is_selected = selected_deck != null and selected_deck.? == &draw_pile;
|
2024-04-14 14:31:52 -06:00
|
|
|
const is_hovered = hovered_deck != null and hovered_deck.? == &draw_pile;
|
2024-04-21 11:46:52 -06:00
|
|
|
const color = if (is_selected)
|
|
|
|
[4]u8{ 0xFF, 0xA0, 0xA0, 0xFF }
|
|
|
|
else if (is_hovered)
|
|
|
|
[4]u8{ 0xA0, 0xFF, 0xA0, 0xFF }
|
|
|
|
else
|
|
|
|
[4]u8{ 0xFF, 0xFF, 0xFF, 0xFF };
|
2024-04-14 14:31:52 -06:00
|
|
|
|
|
|
|
var pos = [2]f32{ @floatFromInt(margin), @floatFromInt(margin) };
|
2024-04-21 01:56:45 -06:00
|
|
|
pos[0] += board_offset[0];
|
|
|
|
pos[1] += board_offset[1];
|
2024-04-21 01:15:00 -06:00
|
|
|
canvas.rect(pos, tile_sizef, .{ .color = color });
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
for (draw_pile.items) |card| {
|
|
|
|
const tile_id = if (!draw_pile_exhausted) card_tilemap.?.back else card_tilemap.?.getTileForCard(card);
|
|
|
|
card_tilemap.?.tilesheet.renderTile(&canvas, tile_id, pos, .{
|
|
|
|
.size = tile_sizef,
|
|
|
|
.color = color,
|
|
|
|
});
|
|
|
|
pos[1] -= 0.25 * scalef;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-21 01:04:51 -06:00
|
|
|
if (show_menu) {
|
|
|
|
canvas.rect(
|
|
|
|
.{ canvas.window_size[0] / 3, canvas.window_size[1] / 3 },
|
|
|
|
.{ canvas.window_size[0] / 3, canvas.window_size[1] / 3 },
|
|
|
|
.{ .color = .{ 0, 0, 0, 0xFF } },
|
|
|
|
);
|
|
|
|
const x: f32 = canvas.window_size[0] / 2;
|
|
|
|
var y: f32 = canvas.window_size[1] / 3 + (canvas.font.lineHeight * scalef);
|
|
|
|
for (MENU_ITEMS, 0..) |menu_item, i| {
|
|
|
|
if (i == menu_item_selected) {
|
|
|
|
const size = canvas.printText(.{ x, y }, "> {s} <", .{menu_item}, .{ .@"align" = .center, .scale = scalef });
|
|
|
|
y += size[1];
|
|
|
|
} else {
|
|
|
|
const size = canvas.writeText(.{ x, y }, menu_item, .{ .@"align" = .center, .scale = scalef });
|
|
|
|
y += size[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
canvas.end();
|
2024-05-25 14:55:31 -06:00
|
|
|
|
|
|
|
frame_count +%= 1;
|
2024-04-14 14:31:52 -06:00
|
|
|
}
|
|
|
|
|
2024-04-21 01:56:45 -06:00
|
|
|
pub fn haveWon() bool {
|
|
|
|
if (draw_pile.items.len > 0) return false;
|
|
|
|
if (drawn_cards.items.len > 0) return false;
|
|
|
|
for (stacks) |stack| {
|
|
|
|
if (stack.items.len > 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
pub fn makeStandardDeck(allocator: std.mem.Allocator) ![]Card {
|
|
|
|
var deck = try allocator.alloc(Card, 52);
|
|
|
|
errdefer allocator.free(deck);
|
|
|
|
|
|
|
|
var next_index: usize = 0;
|
|
|
|
for (0..4) |suit| {
|
|
|
|
for (1..14) |rank| {
|
|
|
|
deck[next_index] = Card{
|
|
|
|
.suit = @enumFromInt(suit),
|
|
|
|
.rank = @intCast(rank),
|
|
|
|
};
|
|
|
|
next_index += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return deck;
|
|
|
|
}
|
|
|
|
|
2024-04-20 23:51:56 -06:00
|
|
|
pub fn deselect(pressed: bool) !void {
|
|
|
|
if (!pressed) return;
|
2024-04-21 01:04:51 -06:00
|
|
|
if (show_menu) return;
|
2024-04-20 23:51:56 -06:00
|
|
|
selected_deck = null;
|
|
|
|
selected_card = 0;
|
2024-04-14 14:31:52 -06:00
|
|
|
}
|
|
|
|
|
2024-04-20 23:51:56 -06:00
|
|
|
pub fn doSelectOrPlace(pressed: bool) !void {
|
|
|
|
if (!pressed) return;
|
2024-04-21 01:04:51 -06:00
|
|
|
if (show_menu) {
|
|
|
|
const menu_item = MENU_ITEMS[menu_item_selected];
|
|
|
|
if (std.mem.eql(u8, menu_item, "Continue")) {
|
|
|
|
show_menu = false;
|
|
|
|
} else if (std.mem.eql(u8, menu_item, "New Game")) {
|
|
|
|
try resetGame();
|
|
|
|
show_menu = false;
|
|
|
|
} else if (std.mem.eql(u8, menu_item, "Quit")) {
|
|
|
|
window_global.setShouldClose(true);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2024-04-14 14:31:52 -06:00
|
|
|
if (selected_deck == null) {
|
|
|
|
if (hovered_deck == &draw_pile and !draw_pile_exhausted) {
|
2024-04-21 02:56:58 -06:00
|
|
|
resetHistory();
|
2024-05-25 22:14:16 -06:00
|
|
|
drawn_cards.ensureUnusedCapacity(seizer.platform.allocator(), 3) catch return;
|
2024-04-14 14:31:52 -06:00
|
|
|
for (0..3) |_| {
|
|
|
|
if (draw_pile.popOrNull()) |card| drawn_cards.appendAssumeCapacity(card);
|
|
|
|
}
|
|
|
|
if (draw_pile.items.len == 0) {
|
|
|
|
draw_pile_exhausted = true;
|
|
|
|
}
|
|
|
|
} else if (hovered_deck == &draw_pile and draw_pile_exhausted) {
|
|
|
|
selected_deck = hovered_deck;
|
|
|
|
selected_card = hovered_card;
|
|
|
|
} else if (hovered_deck == &drawn_cards) {
|
|
|
|
selected_deck = hovered_deck;
|
|
|
|
selected_card = hovered_card;
|
|
|
|
} else {
|
|
|
|
for (stacks[0..]) |*stack| {
|
|
|
|
if (hovered_deck != stack) continue;
|
|
|
|
selected_deck = hovered_deck;
|
|
|
|
selected_card = hovered_card;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (hovered_deck == selected_deck) {
|
2024-04-23 22:17:35 -06:00
|
|
|
var snapshot = try Snapshot.initFromCurrentGlobalState();
|
2024-04-14 14:31:52 -06:00
|
|
|
const selected_substack = selected_deck.?.items[selected_card..];
|
|
|
|
|
|
|
|
// try to move all selected cards into the foundations
|
2024-04-21 02:56:58 -06:00
|
|
|
var cards_moved = false;
|
2024-04-14 14:31:52 -06:00
|
|
|
move_cards_to_foundations: for (0..selected_substack.len) |_| {
|
|
|
|
for (foundations[0..]) |*foundation| {
|
|
|
|
if (hovered_deck == foundation) break :move_cards_to_foundations;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (foundations[0..]) |*foundation| {
|
|
|
|
const empty = foundation.items.len == 0;
|
|
|
|
const ace = selected_deck.?.items[selected_card].rank == 1;
|
|
|
|
const suit_matches = !empty and foundation.items[foundation.items.len - 1].suit == selected_deck.?.items[selected_deck.?.items.len - 1].suit;
|
|
|
|
const is_next_rank = !empty and foundation.items[foundation.items.len - 1].rank + 1 == selected_deck.?.items[selected_deck.?.items.len - 1].rank;
|
|
|
|
if ((empty and ace) or (suit_matches and is_next_rank)) {
|
2024-05-25 22:14:16 -06:00
|
|
|
foundation.ensureUnusedCapacity(seizer.platform.allocator(), 1) catch continue;
|
2024-04-14 14:31:52 -06:00
|
|
|
foundation.appendAssumeCapacity(selected_deck.?.pop());
|
2024-04-21 11:56:36 -06:00
|
|
|
|
|
|
|
hovered_card = if (hovered_deck.? == &drawn_cards)
|
|
|
|
hovered_deck.?.items.len -| 1
|
|
|
|
else
|
|
|
|
indexOfTopOfStack(hovered_deck.?.items);
|
|
|
|
|
2024-04-21 02:56:58 -06:00
|
|
|
cards_moved = true;
|
2024-04-14 14:31:52 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break :move_cards_to_foundations;
|
|
|
|
}
|
|
|
|
}
|
2024-04-21 02:56:58 -06:00
|
|
|
|
2024-04-21 11:40:13 -06:00
|
|
|
if (cards_moved) {
|
2024-05-25 22:14:16 -06:00
|
|
|
try history.append(seizer.platform.allocator(), snapshot);
|
2024-04-23 22:17:35 -06:00
|
|
|
} else {
|
|
|
|
snapshot.deinit();
|
2024-04-21 11:40:13 -06:00
|
|
|
}
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
selected_deck = null;
|
|
|
|
} else {
|
2024-04-21 11:40:13 -06:00
|
|
|
var cards_moved = false;
|
2024-04-23 22:17:35 -06:00
|
|
|
var snapshot = try Snapshot.initFromCurrentGlobalState();
|
2024-04-14 14:31:52 -06:00
|
|
|
if (selected_deck) |selected| move_from_selected_to_hovered: {
|
|
|
|
const selected_substack = selected.items[selected_card..];
|
|
|
|
if (selected_substack.len == 0) break :move_from_selected_to_hovered;
|
|
|
|
|
|
|
|
const hovered = hovered_deck orelse break :move_from_selected_to_hovered;
|
|
|
|
|
|
|
|
if (hovered.items.len == 0) {
|
|
|
|
for (stacks[0..]) |*stack| {
|
|
|
|
if (hovered != stack) continue;
|
2024-05-25 22:14:16 -06:00
|
|
|
hovered.ensureUnusedCapacity(seizer.platform.allocator(), selected_substack.len) catch break :move_from_selected_to_hovered;
|
2024-04-14 14:31:52 -06:00
|
|
|
hovered.appendSliceAssumeCapacity(selected_substack);
|
|
|
|
selected.shrinkRetainingCapacity(selected.items.len - selected_substack.len);
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
2024-04-21 11:40:13 -06:00
|
|
|
cards_moved = true;
|
2024-04-14 14:31:52 -06:00
|
|
|
break :move_from_selected_to_hovered;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selected_substack.len == 1) {
|
|
|
|
if (draw_pile_exhausted and hovered == &draw_pile and draw_pile.items.len == 0) {
|
2024-05-25 22:14:16 -06:00
|
|
|
hovered.ensureUnusedCapacity(seizer.platform.allocator(), 1) catch break :move_from_selected_to_hovered;
|
2024-04-14 14:31:52 -06:00
|
|
|
hovered.appendAssumeCapacity(selected_deck.?.pop());
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
2024-04-23 22:17:35 -06:00
|
|
|
cards_moved = true;
|
2024-04-14 14:31:52 -06:00
|
|
|
break :move_from_selected_to_hovered;
|
|
|
|
} else for (foundations[0..]) |*foundation| {
|
|
|
|
if (foundation != selected) continue;
|
|
|
|
const empty = foundation.items.len == 0;
|
|
|
|
const ace = selected_deck.?.items[selected_card].rank == 1;
|
|
|
|
const suit_matches = !empty and foundation.items[foundation.items.len - 1].suit == selected_deck.?.items[selected_deck.?.items.len - 1].suit;
|
|
|
|
const is_next_rank = !empty and foundation.items[foundation.items.len - 1].rank + 1 == selected_deck.?.items[selected_deck.?.items.len - 1].rank;
|
|
|
|
if ((empty and ace) or (suit_matches and is_next_rank)) {
|
2024-05-25 22:14:16 -06:00
|
|
|
foundation.ensureUnusedCapacity(seizer.platform.allocator(), 1) catch continue;
|
2024-04-14 14:31:52 -06:00
|
|
|
foundation.appendAssumeCapacity(selected_deck.?.pop());
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
2024-04-21 11:40:13 -06:00
|
|
|
cards_moved = true;
|
2024-04-14 14:31:52 -06:00
|
|
|
break :move_from_selected_to_hovered;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break :move_from_selected_to_hovered;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hovered.items[hovered.items.len - 1].rank - 1 != selected_substack[0].rank or
|
2024-04-21 11:51:53 -06:00
|
|
|
hovered.items[hovered.items.len - 1].suit.color() == selected_substack[0].suit.color())
|
2024-04-14 14:31:52 -06:00
|
|
|
{
|
|
|
|
break :move_from_selected_to_hovered;
|
|
|
|
}
|
2024-05-25 22:14:16 -06:00
|
|
|
hovered.ensureUnusedCapacity(seizer.platform.allocator(), selected_substack.len) catch break :move_from_selected_to_hovered;
|
2024-04-14 14:31:52 -06:00
|
|
|
hovered.appendSliceAssumeCapacity(selected_substack);
|
|
|
|
selected.shrinkRetainingCapacity(selected.items.len - selected_substack.len);
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
2024-04-21 11:51:53 -06:00
|
|
|
cards_moved = true;
|
2024-04-14 14:31:52 -06:00
|
|
|
}
|
2024-04-21 11:40:13 -06:00
|
|
|
if (cards_moved) {
|
2024-05-25 22:14:16 -06:00
|
|
|
try history.append(seizer.platform.allocator(), snapshot);
|
2024-04-23 22:17:35 -06:00
|
|
|
} else {
|
|
|
|
snapshot.deinit();
|
2024-04-21 11:40:13 -06:00
|
|
|
}
|
2024-04-14 14:31:52 -06:00
|
|
|
selected_deck = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-20 23:51:56 -06:00
|
|
|
pub fn moveLeft(pressed: bool) !void {
|
|
|
|
if (!pressed) return;
|
2024-04-21 01:04:51 -06:00
|
|
|
if (show_menu) return;
|
2024-04-14 14:31:52 -06:00
|
|
|
if (hovered_deck == null) {
|
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
} else if (hovered_deck == &draw_pile) {
|
|
|
|
hovered_deck = &drawn_cards;
|
|
|
|
hovered_card = drawn_cards.items.len -| 1;
|
|
|
|
} else if (hovered_deck == &drawn_cards) {
|
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
} else if (hovered_deck == &stacks[0]) {
|
|
|
|
hovered_deck = &foundations[last_hovered_foundation];
|
|
|
|
} else {
|
|
|
|
for (stacks[1..], 1..) |*stack, i| {
|
|
|
|
if (hovered_deck == stack) {
|
|
|
|
hovered_deck = &stacks[i - 1];
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
|
|
|
last_hovered_stack = i - 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (&foundations) |*foundation| {
|
|
|
|
if (hovered_deck == foundation) {
|
|
|
|
hovered_deck = &stacks[stacks.len - 1];
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-20 23:51:56 -06:00
|
|
|
pub fn moveRight(pressed: bool) !void {
|
|
|
|
if (!pressed) return;
|
2024-04-21 01:04:51 -06:00
|
|
|
if (show_menu) return;
|
2024-04-14 14:31:52 -06:00
|
|
|
if (hovered_deck == null) {
|
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
} else if (hovered_deck == &draw_pile) {
|
|
|
|
hovered_deck = &drawn_cards;
|
|
|
|
hovered_card = drawn_cards.items.len -| 1;
|
|
|
|
} else if (hovered_deck == &drawn_cards) {
|
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
} else if (hovered_deck == &stacks[stacks.len - 1]) {
|
|
|
|
hovered_deck = &foundations[last_hovered_foundation];
|
|
|
|
} else {
|
|
|
|
for (stacks[0 .. stacks.len - 1], 0..) |*stack, i| {
|
|
|
|
if (hovered_deck == stack) {
|
|
|
|
hovered_deck = &stacks[i + 1];
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
|
|
|
last_hovered_stack = i + 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (&foundations) |*foundation| {
|
|
|
|
if (hovered_deck == foundation) {
|
|
|
|
hovered_deck = &stacks[0];
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-20 23:51:56 -06:00
|
|
|
pub fn moveUp(pressed: bool) !void {
|
|
|
|
if (!pressed) return;
|
|
|
|
|
2024-04-21 01:04:51 -06:00
|
|
|
if (show_menu) {
|
|
|
|
if (menu_item_selected == 0) menu_item_selected = MENU_ITEMS.len;
|
|
|
|
menu_item_selected -= 1;
|
|
|
|
} else if (hovered_deck == null) {
|
2024-04-14 14:31:52 -06:00
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
} else if (hovered_deck == &draw_pile) {
|
|
|
|
hovered_deck = &foundations[foundations.len - 1];
|
|
|
|
last_hovered_foundation = foundations.len - 1;
|
|
|
|
} else if (hovered_deck == &drawn_cards) {
|
|
|
|
hovered_deck = &stacks[last_hovered_stack];
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
|
|
|
} else if (hovered_deck == &foundations[0]) {
|
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
} else {
|
|
|
|
for (stacks[0..]) |*stack| {
|
|
|
|
if (hovered_deck != stack) continue;
|
|
|
|
const top_of_stack = indexOfTopOfStack(stack.items);
|
|
|
|
if (hovered_card > top_of_stack) {
|
|
|
|
hovered_card -= 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (drawn_cards.items.len > 0) {
|
|
|
|
hovered_deck = &drawn_cards;
|
|
|
|
hovered_card = drawn_cards.items.len -| 1;
|
|
|
|
} else {
|
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (foundations[1..], 1..) |*foundation, i| {
|
|
|
|
if (hovered_deck == foundation) {
|
|
|
|
hovered_deck = &foundations[i - 1];
|
|
|
|
last_hovered_foundation = i - 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-20 23:51:56 -06:00
|
|
|
pub fn moveDown(pressed: bool) !void {
|
|
|
|
if (!pressed) return;
|
2024-04-21 01:04:51 -06:00
|
|
|
if (show_menu) {
|
|
|
|
menu_item_selected += 1;
|
2024-05-03 19:58:39 -06:00
|
|
|
menu_item_selected %= @intCast(MENU_ITEMS.len);
|
2024-04-21 01:04:51 -06:00
|
|
|
} else if (hovered_deck == null) {
|
2024-04-14 14:31:52 -06:00
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
} else if (hovered_deck == &draw_pile) {
|
|
|
|
hovered_deck = &foundations[0];
|
|
|
|
last_hovered_foundation = 0;
|
|
|
|
} else if (hovered_deck == &drawn_cards) {
|
|
|
|
hovered_deck = &stacks[last_hovered_stack];
|
|
|
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
|
|
|
} else if (hovered_deck == &foundations[foundations.len - 1]) {
|
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
} else {
|
|
|
|
for (stacks[0..]) |*stack| {
|
|
|
|
if (hovered_deck != stack) continue;
|
|
|
|
if (hovered_card < stack.items.len -| 1) {
|
|
|
|
hovered_card += 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (drawn_cards.items.len > 0) {
|
|
|
|
hovered_deck = &drawn_cards;
|
|
|
|
hovered_card = drawn_cards.items.len -| 1;
|
|
|
|
} else {
|
|
|
|
hovered_deck = &draw_pile;
|
|
|
|
hovered_card = 0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (foundations[0 .. foundations.len - 1], 0..) |*foundation, i| {
|
|
|
|
if (hovered_deck == foundation) {
|
|
|
|
hovered_deck = &foundations[i + 1];
|
|
|
|
last_hovered_foundation = i + 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-21 02:56:58 -06:00
|
|
|
pub fn undo(pressed: bool) !void {
|
|
|
|
if (!pressed) return;
|
|
|
|
if (history.popOrNull()) |snapshot_const| {
|
|
|
|
var snapshot = snapshot_const;
|
|
|
|
defer snapshot.deinit();
|
|
|
|
try snapshot.restore();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-21 01:04:51 -06:00
|
|
|
pub fn toggleMenu(pressed: bool) !void {
|
|
|
|
if (!pressed) return;
|
|
|
|
show_menu = !show_menu;
|
|
|
|
}
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
pub fn indexOfTopOfStack(cards: []Card) usize {
|
|
|
|
if (cards.len < 2) return 0;
|
|
|
|
|
|
|
|
var index = cards.len - 1;
|
|
|
|
while (index > 0) : (index -= 1) {
|
|
|
|
const prev_card = cards[index];
|
|
|
|
const card = cards[index - 1];
|
|
|
|
if (card.suit.color() == prev_card.suit.color()) break;
|
|
|
|
if (card.rank != prev_card.rank + 1) break;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
2024-04-21 02:56:58 -06:00
|
|
|
const Snapshot = struct {
|
|
|
|
draw_pile: []Card,
|
|
|
|
draw_pile_exhausted: bool,
|
|
|
|
drawn_cards: []Card,
|
|
|
|
stacks: [7][]Card,
|
|
|
|
foundations: [4][]Card,
|
|
|
|
selected_deck: *std.ArrayListUnmanaged(Card),
|
|
|
|
selected_card: usize,
|
|
|
|
|
|
|
|
pub fn initFromCurrentGlobalState() !Snapshot {
|
|
|
|
const snapshot_selected_deck = selected_deck orelse return error.InvalidSelectionForSnapshot;
|
|
|
|
|
2024-05-25 22:14:16 -06:00
|
|
|
const snapshot_draw_pile = try seizer.platform.allocator().dupe(Card, draw_pile.items);
|
|
|
|
errdefer seizer.platform.allocator().free(snapshot_draw_pile);
|
|
|
|
const snapshot_drawn_cards = try seizer.platform.allocator().dupe(Card, drawn_cards.items);
|
|
|
|
errdefer seizer.platform.allocator().free(snapshot_drawn_cards);
|
2024-04-21 02:56:58 -06:00
|
|
|
|
|
|
|
var snapshot_stacks: [7][]Card = undefined;
|
|
|
|
for (&snapshot_stacks, &stacks) |*snapshot_stack, stack| {
|
2024-05-25 22:14:16 -06:00
|
|
|
snapshot_stack.* = try seizer.platform.allocator().dupe(Card, stack.items);
|
2024-04-21 02:56:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
var snapshot_foundations: [4][]Card = undefined;
|
|
|
|
for (&snapshot_foundations, &foundations) |*snapshot_foundation, foundation| {
|
2024-05-25 22:14:16 -06:00
|
|
|
snapshot_foundation.* = try seizer.platform.allocator().dupe(Card, foundation.items);
|
2024-04-21 02:56:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return Snapshot{
|
|
|
|
.draw_pile = snapshot_draw_pile,
|
|
|
|
.draw_pile_exhausted = draw_pile_exhausted,
|
|
|
|
.drawn_cards = snapshot_drawn_cards,
|
|
|
|
.stacks = snapshot_stacks,
|
|
|
|
.foundations = snapshot_foundations,
|
|
|
|
.selected_deck = snapshot_selected_deck,
|
|
|
|
.selected_card = selected_card,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn deinit(snapshot: *@This()) void {
|
2024-05-25 22:14:16 -06:00
|
|
|
seizer.platform.allocator().free(snapshot.draw_pile);
|
|
|
|
seizer.platform.allocator().free(snapshot.drawn_cards);
|
2024-04-23 22:17:35 -06:00
|
|
|
for (snapshot.stacks) |stack| {
|
2024-05-25 22:14:16 -06:00
|
|
|
seizer.platform.allocator().free(stack);
|
2024-04-23 22:17:35 -06:00
|
|
|
}
|
|
|
|
for (snapshot.foundations) |foundation| {
|
2024-05-25 22:14:16 -06:00
|
|
|
seizer.platform.allocator().free(foundation);
|
2024-04-23 22:17:35 -06:00
|
|
|
}
|
2024-04-21 02:56:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn restore(snapshot: @This()) !void {
|
|
|
|
// ensure there is enough space for all the cards
|
2024-05-25 22:14:16 -06:00
|
|
|
try draw_pile.ensureTotalCapacity(seizer.platform.allocator(), snapshot.draw_pile.len);
|
|
|
|
try drawn_cards.ensureTotalCapacity(seizer.platform.allocator(), snapshot.drawn_cards.len);
|
2024-04-21 02:56:58 -06:00
|
|
|
for (&snapshot.stacks, &stacks) |snapshot_stack, *stack| {
|
2024-05-25 22:14:16 -06:00
|
|
|
try stack.ensureTotalCapacity(seizer.platform.allocator(), snapshot_stack.len);
|
2024-04-21 02:56:58 -06:00
|
|
|
}
|
|
|
|
for (&snapshot.foundations, &foundations) |snapshot_foundation, *foundation| {
|
2024-05-25 22:14:16 -06:00
|
|
|
try foundation.ensureTotalCapacity(seizer.platform.allocator(), snapshot_foundation.len);
|
2024-04-21 02:56:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// reset the state
|
|
|
|
draw_pile.shrinkRetainingCapacity(0);
|
|
|
|
draw_pile.appendSliceAssumeCapacity(snapshot.draw_pile);
|
|
|
|
|
|
|
|
draw_pile_exhausted = snapshot.draw_pile_exhausted;
|
|
|
|
|
|
|
|
drawn_cards.shrinkRetainingCapacity(0);
|
|
|
|
drawn_cards.appendSliceAssumeCapacity(snapshot.drawn_cards);
|
|
|
|
|
|
|
|
for (&snapshot.stacks, &stacks) |snapshot_stack, *stack| {
|
|
|
|
stack.shrinkRetainingCapacity(0);
|
|
|
|
stack.appendSliceAssumeCapacity(snapshot_stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (&snapshot.foundations, &foundations) |snapshot_foundation, *foundation| {
|
|
|
|
foundation.shrinkRetainingCapacity(0);
|
|
|
|
foundation.appendSliceAssumeCapacity(snapshot_foundation);
|
|
|
|
}
|
|
|
|
|
|
|
|
selected_deck = null;
|
|
|
|
selected_card = 0;
|
|
|
|
hovered_deck = snapshot.selected_deck;
|
|
|
|
hovered_card = snapshot.selected_card;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-04-14 14:31:52 -06:00
|
|
|
const DeckSprites = assets.DeckSprites;
|
|
|
|
const Card = assets.Card;
|
|
|
|
|
|
|
|
const assets = @import("./assets.zig");
|
2024-05-23 16:25:01 -06:00
|
|
|
const builtin = @import("builtin");
|
2024-04-14 14:31:52 -06:00
|
|
|
const seizer = @import("seizer");
|
|
|
|
const gl = seizer.gl;
|
|
|
|
const ecs = seizer.flecs;
|
|
|
|
const std = @import("std");
|