feat: activating and undoing actions
parent
1134f0ee08
commit
c5526ba229
147
src/main.zig
147
src/main.zig
|
@ -1,8 +1,24 @@
|
|||
var gl_binding: gl.Binding = undefined;
|
||||
|
||||
const HandlerError = error{OutOfMemory};
|
||||
const Handler = *const fn (std.mem.Allocator, Request) HandlerError!Response;
|
||||
|
||||
const Request = struct {
|
||||
game_state: GameState,
|
||||
command: ?[]const u8,
|
||||
};
|
||||
const Response = union(enum) {
|
||||
page: Element,
|
||||
transition: struct {
|
||||
game_state: GameState,
|
||||
can_undo: bool,
|
||||
},
|
||||
};
|
||||
|
||||
const GameState = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
prng: std.rand.DefaultPrng,
|
||||
handler: Handler,
|
||||
draw_pile: std.ArrayListUnmanaged(Card),
|
||||
discard_pile: std.ArrayListUnmanaged(Card),
|
||||
hands: []std.ArrayListUnmanaged(Card),
|
||||
|
@ -39,6 +55,7 @@ const GameState = struct {
|
|||
return @This(){
|
||||
.allocator = allocator,
|
||||
.prng = prng,
|
||||
.handler = &drawCardHandler,
|
||||
.draw_pile = draw_pile,
|
||||
.discard_pile = discard_pile,
|
||||
.hands = hands,
|
||||
|
@ -53,6 +70,22 @@ const GameState = struct {
|
|||
}
|
||||
this.allocator.free(this.hands);
|
||||
}
|
||||
|
||||
pub fn clone(this: @This(), allocator: std.mem.Allocator) !@This() {
|
||||
const hands = try allocator.alloc(std.ArrayListUnmanaged(Card), this.hands.len);
|
||||
for (hands, this.hands) |*hand_clone, hand| {
|
||||
hand_clone.* = try hand.clone(allocator);
|
||||
}
|
||||
|
||||
return @This(){
|
||||
.allocator = allocator,
|
||||
.prng = this.prng,
|
||||
.handler = this.handler,
|
||||
.draw_pile = try this.draw_pile.clone(allocator),
|
||||
.discard_pile = try this.discard_pile.clone(allocator),
|
||||
.hands = hands,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn makeStandardDeck(allocator: std.mem.Allocator) ![]Card {
|
||||
|
@ -123,18 +156,23 @@ pub fn main() !void {
|
|||
defer canvas.deinit(gpa.allocator());
|
||||
|
||||
// game state
|
||||
var game_state = try GameState.init(gpa.allocator(), std.crypto.random.int(u64), 1);
|
||||
defer game_state.deinit();
|
||||
var history = std.ArrayList(GameState).init(gpa.allocator());
|
||||
defer {
|
||||
for (history.items) |*state| {
|
||||
state.deinit();
|
||||
}
|
||||
history.deinit();
|
||||
}
|
||||
try history.append(try GameState.init(gpa.allocator(), std.crypto.random.int(u64), 1));
|
||||
|
||||
var root_element: ?Element = null;
|
||||
var response_arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer response_arena.deinit();
|
||||
|
||||
const handler = &drawCardHandler;
|
||||
|
||||
var actions = std.ArrayList(Action).init(gpa.allocator());
|
||||
defer actions.deinit();
|
||||
|
||||
// TODO: Restore hovered_action when undoing
|
||||
var hovered_action: ?Action = null;
|
||||
|
||||
while (seizer.backend.glfw.c.glfwWindowShouldClose(window) != seizer.backend.glfw.c.GLFW_TRUE) {
|
||||
|
@ -143,13 +181,31 @@ pub fn main() !void {
|
|||
.right = false,
|
||||
.up = false,
|
||||
.down = false,
|
||||
.action = false,
|
||||
.undo = false,
|
||||
};
|
||||
seizer.backend.glfw.c.glfwPollEvents();
|
||||
|
||||
if (hovered_action == null and actions.items.len > 0) {
|
||||
var request_command: ?[]const u8 = null;
|
||||
defer if (request_command) |command| gpa.allocator().free(command);
|
||||
|
||||
if (hovered_action) |hovered| {
|
||||
if (input_state.action) {
|
||||
request_command = try gpa.allocator().dupe(u8, hovered.command);
|
||||
root_element = null;
|
||||
hovered_action = null;
|
||||
}
|
||||
} else if (actions.items.len > 0) {
|
||||
hovered_action = actions.items[0];
|
||||
}
|
||||
|
||||
if (input_state.undo and history.items.len > 1) {
|
||||
var discarded_state = history.pop();
|
||||
discarded_state.deinit();
|
||||
root_element = null;
|
||||
hovered_action = null;
|
||||
}
|
||||
|
||||
if (hovered_action) |hovered| {
|
||||
var direction = [2]f32{ 0, 0 };
|
||||
if (input_state.left) {
|
||||
|
@ -188,14 +244,25 @@ pub fn main() !void {
|
|||
hovered_action = new_action;
|
||||
}
|
||||
|
||||
if (root_element == null) {
|
||||
while (root_element == null) {
|
||||
_ = response_arena.reset(.retain_capacity);
|
||||
const response = try handler(response_arena.allocator(), Request{
|
||||
.game_state = game_state,
|
||||
const current_state = history.items[history.items.len - 1];
|
||||
const response = try current_state.handler(response_arena.allocator(), Request{
|
||||
.game_state = current_state,
|
||||
.command = request_command,
|
||||
});
|
||||
|
||||
switch (response) {
|
||||
.page => |page_root_element| root_element = page_root_element,
|
||||
.transition => |transition| {
|
||||
if (!transition.can_undo) {
|
||||
for (history.items) |*state| {
|
||||
state.deinit();
|
||||
}
|
||||
history.clearRetainingCapacity();
|
||||
}
|
||||
try history.append(try transition.game_state.clone(gpa.allocator()));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,6 +286,8 @@ pub fn main() !void {
|
|||
},
|
||||
});
|
||||
|
||||
_ = canvas.printText(.{ 0, 0 }, "History len: {}", .{history.items.len}, .{});
|
||||
|
||||
switch (framebuffer_size[1]) {
|
||||
0...300 => if (card_tilemap_small == null) {
|
||||
card_tilemap_small = try assets.loadSmallCards(gpa.allocator());
|
||||
|
@ -307,16 +376,6 @@ const RenderResources = struct {
|
|||
deck: DeckSprites,
|
||||
};
|
||||
|
||||
const HandlerError = error{OutOfMemory};
|
||||
const Handler = *const fn (std.mem.Allocator, Request) HandlerError!Response;
|
||||
|
||||
const Request = struct {
|
||||
game_state: GameState,
|
||||
};
|
||||
const Response = union(enum) {
|
||||
page: Element,
|
||||
};
|
||||
|
||||
pub const Element = struct {
|
||||
pointer: ?*anyopaque,
|
||||
interface: *const Interface,
|
||||
|
@ -760,6 +819,26 @@ pub const CardElement = struct {
|
|||
|
||||
/// Handler at the start of a turn, while the player is drawing a card.
|
||||
fn drawCardHandler(arena: std.mem.Allocator, request: Request) HandlerError!Response {
|
||||
if (request.command) |command| {
|
||||
if (std.mem.eql(u8, command, "draw draw_pile")) {
|
||||
var new_game_state = try request.game_state.clone(arena);
|
||||
try new_game_state.hands[0].append(arena, new_game_state.draw_pile.pop());
|
||||
new_game_state.handler = &playerTurnHandler;
|
||||
return Response{ .transition = .{
|
||||
.game_state = new_game_state,
|
||||
.can_undo = false,
|
||||
} };
|
||||
} else if (std.mem.eql(u8, command, "draw discard_pile")) {
|
||||
var new_game_state = try request.game_state.clone(arena);
|
||||
try new_game_state.hands[0].append(arena, new_game_state.discard_pile.pop());
|
||||
new_game_state.handler = &playerTurnHandler;
|
||||
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;
|
||||
draw_pile.command = "draw draw_pile";
|
||||
|
@ -771,7 +850,33 @@ fn drawCardHandler(arena: std.mem.Allocator, request: Request) HandlerError!Resp
|
|||
for (request.game_state.hands[0].items) |card| {
|
||||
var card_element = try CardElement.create(arena, .{
|
||||
.visual = .{ .card = card },
|
||||
.command = try std.fmt.allocPrint(arena, "mark {} of {s}", .{ card.rank, @tagName(card.suit) }),
|
||||
.command = null,
|
||||
});
|
||||
try hand.addElement(card_element.element());
|
||||
}
|
||||
|
||||
var draw_discard_hbox = try HBox.create(arena);
|
||||
try draw_discard_hbox.addElement(draw_pile.element());
|
||||
try draw_discard_hbox.addElement(discard_pile.element());
|
||||
|
||||
var page = try Page.create(arena);
|
||||
try page.addElement(.{ 0.5, 0.5 }, .{ 0.5, 0.5 }, draw_discard_hbox.element());
|
||||
try page.addElement(.{ 0.5, 1 }, .{ 0.5, 1 }, hand.element());
|
||||
|
||||
return Response{ .page = page.element() };
|
||||
}
|
||||
|
||||
fn playerTurnHandler(arena: std.mem.Allocator, request: Request) HandlerError!Response {
|
||||
var draw_pile = try Pile.create(arena, request.game_state.draw_pile.items);
|
||||
draw_pile.hidden = true;
|
||||
|
||||
var discard_pile = try Pile.create(arena, request.game_state.discard_pile.items);
|
||||
|
||||
var hand = try HBox.create(arena);
|
||||
for (request.game_state.hands[0].items) |card| {
|
||||
var card_element = try CardElement.create(arena, .{
|
||||
.visual = .{ .card = card },
|
||||
.command = "mark",
|
||||
});
|
||||
try hand.addElement(card_element.element());
|
||||
}
|
||||
|
@ -802,6 +907,8 @@ const InputState = struct {
|
|||
right: bool,
|
||||
up: bool,
|
||||
down: bool,
|
||||
action: bool,
|
||||
undo: bool,
|
||||
};
|
||||
|
||||
fn glfw_key_callback(window: ?*seizer.backend.glfw.c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, mods: c_int) callconv(.C) void {
|
||||
|
@ -815,6 +922,8 @@ fn glfw_key_callback(window: ?*seizer.backend.glfw.c.GLFWwindow, key: c_int, sca
|
|||
seizer.backend.glfw.c.GLFW_KEY_RIGHT => input_state.right = true,
|
||||
seizer.backend.glfw.c.GLFW_KEY_UP => input_state.up = true,
|
||||
seizer.backend.glfw.c.GLFW_KEY_DOWN => input_state.down = true,
|
||||
seizer.backend.glfw.c.GLFW_KEY_Z => input_state.action = true,
|
||||
seizer.backend.glfw.c.GLFW_KEY_BACKSPACE => input_state.undo = true,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue