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;
|
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 {
|
const GameState = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
prng: std.rand.DefaultPrng,
|
prng: std.rand.DefaultPrng,
|
||||||
|
handler: Handler,
|
||||||
draw_pile: std.ArrayListUnmanaged(Card),
|
draw_pile: std.ArrayListUnmanaged(Card),
|
||||||
discard_pile: std.ArrayListUnmanaged(Card),
|
discard_pile: std.ArrayListUnmanaged(Card),
|
||||||
hands: []std.ArrayListUnmanaged(Card),
|
hands: []std.ArrayListUnmanaged(Card),
|
||||||
|
@ -39,6 +55,7 @@ const GameState = struct {
|
||||||
return @This(){
|
return @This(){
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.prng = prng,
|
.prng = prng,
|
||||||
|
.handler = &drawCardHandler,
|
||||||
.draw_pile = draw_pile,
|
.draw_pile = draw_pile,
|
||||||
.discard_pile = discard_pile,
|
.discard_pile = discard_pile,
|
||||||
.hands = hands,
|
.hands = hands,
|
||||||
|
@ -53,6 +70,22 @@ const GameState = struct {
|
||||||
}
|
}
|
||||||
this.allocator.free(this.hands);
|
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 {
|
pub fn makeStandardDeck(allocator: std.mem.Allocator) ![]Card {
|
||||||
|
@ -123,18 +156,23 @@ pub fn main() !void {
|
||||||
defer canvas.deinit(gpa.allocator());
|
defer canvas.deinit(gpa.allocator());
|
||||||
|
|
||||||
// game state
|
// game state
|
||||||
var game_state = try GameState.init(gpa.allocator(), std.crypto.random.int(u64), 1);
|
var history = std.ArrayList(GameState).init(gpa.allocator());
|
||||||
defer game_state.deinit();
|
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 root_element: ?Element = null;
|
||||||
var response_arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
var response_arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
||||||
defer response_arena.deinit();
|
defer response_arena.deinit();
|
||||||
|
|
||||||
const handler = &drawCardHandler;
|
|
||||||
|
|
||||||
var actions = std.ArrayList(Action).init(gpa.allocator());
|
var actions = std.ArrayList(Action).init(gpa.allocator());
|
||||||
defer actions.deinit();
|
defer actions.deinit();
|
||||||
|
|
||||||
|
// TODO: Restore hovered_action when undoing
|
||||||
var hovered_action: ?Action = null;
|
var hovered_action: ?Action = null;
|
||||||
|
|
||||||
while (seizer.backend.glfw.c.glfwWindowShouldClose(window) != seizer.backend.glfw.c.GLFW_TRUE) {
|
while (seizer.backend.glfw.c.glfwWindowShouldClose(window) != seizer.backend.glfw.c.GLFW_TRUE) {
|
||||||
|
@ -143,13 +181,31 @@ pub fn main() !void {
|
||||||
.right = false,
|
.right = false,
|
||||||
.up = false,
|
.up = false,
|
||||||
.down = false,
|
.down = false,
|
||||||
|
.action = false,
|
||||||
|
.undo = false,
|
||||||
};
|
};
|
||||||
seizer.backend.glfw.c.glfwPollEvents();
|
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];
|
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| {
|
if (hovered_action) |hovered| {
|
||||||
var direction = [2]f32{ 0, 0 };
|
var direction = [2]f32{ 0, 0 };
|
||||||
if (input_state.left) {
|
if (input_state.left) {
|
||||||
|
@ -188,14 +244,25 @@ pub fn main() !void {
|
||||||
hovered_action = new_action;
|
hovered_action = new_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root_element == null) {
|
while (root_element == null) {
|
||||||
_ = response_arena.reset(.retain_capacity);
|
_ = response_arena.reset(.retain_capacity);
|
||||||
const response = try handler(response_arena.allocator(), Request{
|
const current_state = history.items[history.items.len - 1];
|
||||||
.game_state = game_state,
|
const response = try current_state.handler(response_arena.allocator(), Request{
|
||||||
|
.game_state = current_state,
|
||||||
|
.command = request_command,
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (response) {
|
switch (response) {
|
||||||
.page => |page_root_element| root_element = page_root_element,
|
.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]) {
|
switch (framebuffer_size[1]) {
|
||||||
0...300 => if (card_tilemap_small == null) {
|
0...300 => if (card_tilemap_small == null) {
|
||||||
card_tilemap_small = try assets.loadSmallCards(gpa.allocator());
|
card_tilemap_small = try assets.loadSmallCards(gpa.allocator());
|
||||||
|
@ -307,16 +376,6 @@ const RenderResources = struct {
|
||||||
deck: DeckSprites,
|
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 {
|
pub const Element = struct {
|
||||||
pointer: ?*anyopaque,
|
pointer: ?*anyopaque,
|
||||||
interface: *const Interface,
|
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.
|
/// Handler at the start of a turn, while the player is drawing a card.
|
||||||
fn drawCardHandler(arena: std.mem.Allocator, request: Request) HandlerError!Response {
|
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);
|
var draw_pile = try Pile.create(arena, request.game_state.draw_pile.items);
|
||||||
draw_pile.hidden = true;
|
draw_pile.hidden = true;
|
||||||
draw_pile.command = "draw draw_pile";
|
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| {
|
for (request.game_state.hands[0].items) |card| {
|
||||||
var card_element = try CardElement.create(arena, .{
|
var card_element = try CardElement.create(arena, .{
|
||||||
.visual = .{ .card = card },
|
.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());
|
try hand.addElement(card_element.element());
|
||||||
}
|
}
|
||||||
|
@ -802,6 +907,8 @@ const InputState = struct {
|
||||||
right: bool,
|
right: bool,
|
||||||
up: bool,
|
up: bool,
|
||||||
down: 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 {
|
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_RIGHT => input_state.right = true,
|
||||||
seizer.backend.glfw.c.GLFW_KEY_UP => input_state.up = 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_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 => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue