diff --git a/src/main.zig b/src/main.zig index bea3bb7..7b64032 100644 --- a/src/main.zig +++ b/src/main.zig @@ -23,6 +23,8 @@ const GameState = struct { discard_pile: std.ArrayListUnmanaged(Card), hands: []std.ArrayListUnmanaged(Card), + marked_cards: std.AutoHashMapUnmanaged(Card, void), + pub fn init(allocator: std.mem.Allocator, seed: u64, num_players: usize) !@This() { var draw_pile = std.ArrayListUnmanaged(Card).fromOwnedSlice(try makeStandardDeck(allocator)); errdefer draw_pile.deinit(allocator); @@ -59,6 +61,7 @@ const GameState = struct { .draw_pile = draw_pile, .discard_pile = discard_pile, .hands = hands, + .marked_cards = .{}, }; } @@ -69,6 +72,7 @@ const GameState = struct { hand.deinit(this.allocator); } this.allocator.free(this.hands); + this.marked_cards.deinit(this.allocator); } pub fn clone(this: @This(), allocator: std.mem.Allocator) !@This() { @@ -84,6 +88,7 @@ const GameState = struct { .draw_pile = try this.draw_pile.clone(allocator), .discard_pile = try this.discard_pile.clone(allocator), .hands = hands, + .marked_cards = try this.marked_cards.clone(allocator), }; } }; @@ -262,6 +267,9 @@ pub fn main() !void { history.clearRetainingCapacity(); } try history.append(try transition.game_state.clone(gpa.allocator())); + + if (request_command) |command| gpa.allocator().free(command); + request_command = null; }, } } @@ -734,13 +742,14 @@ pub const CardElement = struct { allocator: std.mem.Allocator, visual: Visual, command: ?[]const u8, + marked: bool, const Visual = union(enum) { back, card: Card, }; - pub fn create(allocator: std.mem.Allocator, options: struct { visual: Visual, command: ?[]const u8 }) !*@This() { + pub fn create(allocator: std.mem.Allocator, options: struct { visual: Visual, command: ?[]const u8, marked: ?bool = null }) !*@This() { const this = try allocator.create(@This()); errdefer allocator.destroy(this); @@ -748,6 +757,7 @@ pub const CardElement = struct { .allocator = allocator, .visual = options.visual, .command = options.command, + .marked = options.marked orelse false, }; return this; } @@ -776,24 +786,32 @@ pub const CardElement = struct { pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resources: RenderResources, min: [2]f32, max: [2]f32) void { const this: *@This() = @ptrCast(@alignCast(pointer)); + const mark_offset = if (this.marked) + [2]f32{ + 0, + @as(f32, @floatFromInt(render_resources.deck.tilesheet.tile_size[1])) * -0.4, + } + else + [2]f32{ 0, 0 }; + switch (this.visual) { .back => render_resources.deck.tilesheet.renderTile( canvas, render_resources.deck.back, - .{ min[0], min[1] }, + .{ min[0] + mark_offset[0], min[1] + mark_offset[1] }, .{}, ), .card => |card| render_resources.deck.tilesheet.renderTile( canvas, render_resources.deck.getTileForCard(card), - .{ min[0], min[1] }, + .{ min[0] + mark_offset[0], min[1] + mark_offset[1] }, .{}, ), } if (render_resources.hovered != null and this.command != null and std.mem.eql(u8, render_resources.hovered.?, this.command.?)) { canvas.rect( - min, + .{ min[0] + mark_offset[0], min[1] + mark_offset[1] }, .{ max[0] - min[0], max[1] - min[1] }, .{ .color = .{ 0xAA, 0xFF, 0xAA, 0x60 } }, ); @@ -867,6 +885,33 @@ fn drawCardHandler(arena: std.mem.Allocator, request: Request) HandlerError!Resp } fn playerTurnHandler(arena: std.mem.Allocator, request: Request) HandlerError!Response { + if (request.command) |command| handle_command: { + if (std.mem.startsWith(u8, command, "mark ")) { + var iter = std.mem.tokenizeScalar(u8, command, ' '); + _ = iter.next() orelse break :handle_command; + const rank_str = iter.next() orelse break :handle_command; + _ = iter.next() orelse break :handle_command; + const suit_str = iter.next() orelse break :handle_command; + + const card = Card{ + .suit = std.meta.stringToEnum(assets.Suit, suit_str) orelse break :handle_command, + .rank = std.fmt.parseInt(u4, rank_str, 10) catch break :handle_command, + }; + + var new_game_state = try request.game_state.clone(arena); + if (new_game_state.marked_cards.contains(card)) { + _ = new_game_state.marked_cards.remove(card); + } else { + try new_game_state.marked_cards.put(arena, card, {}); + } + + return Response{ .transition = .{ + .game_state = new_game_state, + .can_undo = true, + } }; + } + } + var draw_pile = try Pile.create(arena, request.game_state.draw_pile.items); draw_pile.hidden = true; @@ -876,7 +921,8 @@ fn playerTurnHandler(arena: std.mem.Allocator, request: Request) HandlerError!Re for (request.game_state.hands[0].items) |card| { var card_element = try CardElement.create(arena, .{ .visual = .{ .card = card }, - .command = "mark", + .command = try std.fmt.allocPrint(arena, "mark {} of {s}", .{ card.rank, @tagName(card.suit) }), + .marked = request.game_state.marked_cards.contains(card), }); try hand.addElement(card_element.element()); }