|
|
|
@ -12,7 +12,6 @@ const Response = union(enum) {
|
|
|
|
|
transition: struct {
|
|
|
|
|
game_state: GameState,
|
|
|
|
|
can_undo: bool,
|
|
|
|
|
reset_selection: bool = false,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -128,36 +127,6 @@ pub fn main() !void {
|
|
|
|
|
}
|
|
|
|
|
defer seizer.backend.glfw.c.glfwTerminate();
|
|
|
|
|
|
|
|
|
|
// update joystick to gamepad mappings
|
|
|
|
|
update_gamepad_mappings_file: {
|
|
|
|
|
const sdl_controller_config_filepath = std.process.getEnvVarOwned(gpa.allocator(), "SDL_GAMECONTROLLERCONFIG_FILE") catch break :update_gamepad_mappings_file;
|
|
|
|
|
defer gpa.allocator().free(sdl_controller_config_filepath);
|
|
|
|
|
|
|
|
|
|
const controller_config_data = std.fs.cwd().readFileAllocOptions(
|
|
|
|
|
gpa.allocator(),
|
|
|
|
|
sdl_controller_config_filepath,
|
|
|
|
|
512 * 1024 * 1024,
|
|
|
|
|
null,
|
|
|
|
|
@alignOf(u8),
|
|
|
|
|
0,
|
|
|
|
|
) catch break :update_gamepad_mappings_file;
|
|
|
|
|
defer gpa.allocator().free(controller_config_data);
|
|
|
|
|
|
|
|
|
|
if (seizer.backend.glfw.c.glfwUpdateGamepadMappings(controller_config_data) == 0) {
|
|
|
|
|
std.log.warn("Failed to update gamepad mappings from file", .{});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
update_gamepad_mappings: {
|
|
|
|
|
const sdl_controller_config = std.process.getEnvVarOwned(gpa.allocator(), "SDL_GAMECONTROLLERCONFIG") catch break :update_gamepad_mappings;
|
|
|
|
|
defer gpa.allocator().free(sdl_controller_config);
|
|
|
|
|
|
|
|
|
|
const sdl_controller_configz = gpa.allocator().dupeZ(u8, sdl_controller_config) catch break :update_gamepad_mappings;
|
|
|
|
|
|
|
|
|
|
if (seizer.backend.glfw.c.glfwUpdateGamepadMappings(sdl_controller_configz) == 0) {
|
|
|
|
|
std.log.warn("Failed to update gamepad mappings from environment variable", .{});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seizer.backend.glfw.c.glfwWindowHint(seizer.backend.glfw.c.GLFW_OPENGL_DEBUG_CONTEXT, seizer.backend.glfw.c.GLFW_TRUE);
|
|
|
|
|
seizer.backend.glfw.c.glfwWindowHint(seizer.backend.glfw.c.GLFW_CLIENT_API, seizer.backend.glfw.c.GLFW_OPENGL_ES_API);
|
|
|
|
|
seizer.backend.glfw.c.glfwWindowHint(seizer.backend.glfw.c.GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
@ -215,21 +184,6 @@ pub fn main() !void {
|
|
|
|
|
// TODO: Restore hovered_action when undoing
|
|
|
|
|
var hovered_action: ?Action = null;
|
|
|
|
|
|
|
|
|
|
if (seizer.backend.glfw.c.glfwJoystickIsGamepad(seizer.backend.glfw.c.GLFW_JOYSTICK_1) != 0) {
|
|
|
|
|
std.log.info("detected gamepad = \"{?s}\" {?s}", .{ seizer.backend.glfw.c.glfwGetGamepadName(seizer.backend.glfw.c.GLFW_JOYSTICK_1), seizer.backend.glfw.c.glfwGetJoystickGUID(seizer.backend.glfw.c.GLFW_JOYSTICK_1) });
|
|
|
|
|
} else if (seizer.backend.glfw.c.glfwJoystickPresent(seizer.backend.glfw.c.GLFW_JOYSTICK_1) != 0) {
|
|
|
|
|
std.log.info("detected joystick = \"{?s}\" {?s}", .{ seizer.backend.glfw.c.glfwGetJoystickName(seizer.backend.glfw.c.GLFW_JOYSTICK_1), seizer.backend.glfw.c.glfwGetJoystickGUID(seizer.backend.glfw.c.GLFW_JOYSTICK_1) });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var prev_controller_input_state = InputState{
|
|
|
|
|
.left = false,
|
|
|
|
|
.right = false,
|
|
|
|
|
.up = false,
|
|
|
|
|
.down = false,
|
|
|
|
|
.action = false,
|
|
|
|
|
.undo = false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while (seizer.backend.glfw.c.glfwWindowShouldClose(window) != seizer.backend.glfw.c.GLFW_TRUE) {
|
|
|
|
|
input_state = .{
|
|
|
|
|
.left = false,
|
|
|
|
@ -241,55 +195,6 @@ pub fn main() !void {
|
|
|
|
|
};
|
|
|
|
|
seizer.backend.glfw.c.glfwPollEvents();
|
|
|
|
|
|
|
|
|
|
if (seizer.backend.glfw.c.glfwJoystickIsGamepad(seizer.backend.glfw.c.GLFW_JOYSTICK_1) != 0) {
|
|
|
|
|
var gamepad_state: seizer.backend.glfw.c.GLFWgamepadstate = undefined;
|
|
|
|
|
if (seizer.backend.glfw.c.glfwGetGamepadState(seizer.backend.glfw.c.GLFW_JOYSTICK_1, &gamepad_state) != 0) {
|
|
|
|
|
input_state.left = input_state.left or gamepad_state.buttons[seizer.backend.glfw.c.GLFW_GAMEPAD_BUTTON_DPAD_LEFT] != 0;
|
|
|
|
|
input_state.right = input_state.right or gamepad_state.buttons[seizer.backend.glfw.c.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] != 0;
|
|
|
|
|
input_state.up = input_state.up or gamepad_state.buttons[seizer.backend.glfw.c.GLFW_GAMEPAD_BUTTON_DPAD_UP] != 0;
|
|
|
|
|
input_state.down = input_state.down or gamepad_state.buttons[seizer.backend.glfw.c.GLFW_GAMEPAD_BUTTON_DPAD_DOWN] != 0;
|
|
|
|
|
}
|
|
|
|
|
} else if (seizer.backend.glfw.c.glfwJoystickPresent(seizer.backend.glfw.c.GLFW_JOYSTICK_1) != 0) {
|
|
|
|
|
var controller_input_state = InputState{
|
|
|
|
|
.left = false,
|
|
|
|
|
.right = false,
|
|
|
|
|
.up = false,
|
|
|
|
|
.down = false,
|
|
|
|
|
.action = false,
|
|
|
|
|
.undo = false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var hats_count: c_int = undefined;
|
|
|
|
|
const hats_ptr = seizer.backend.glfw.c.glfwGetJoystickHats(seizer.backend.glfw.c.GLFW_JOYSTICK_1, &hats_count);
|
|
|
|
|
const hats = hats_ptr[0..@intCast(hats_count)];
|
|
|
|
|
|
|
|
|
|
const HAT_UP = seizer.backend.glfw.c.GLFW_HAT_UP;
|
|
|
|
|
const HAT_RIGHT = seizer.backend.glfw.c.GLFW_HAT_RIGHT;
|
|
|
|
|
const HAT_DOWN = seizer.backend.glfw.c.GLFW_HAT_DOWN;
|
|
|
|
|
const HAT_LEFT = seizer.backend.glfw.c.GLFW_HAT_LEFT;
|
|
|
|
|
|
|
|
|
|
controller_input_state.left = hats[0] & HAT_LEFT != 0;
|
|
|
|
|
controller_input_state.right = hats[0] & HAT_RIGHT != 0;
|
|
|
|
|
controller_input_state.up = hats[0] & HAT_UP != 0;
|
|
|
|
|
controller_input_state.down = hats[0] & HAT_DOWN != 0;
|
|
|
|
|
|
|
|
|
|
var buttons_count: c_int = undefined;
|
|
|
|
|
const buttons_ptr = seizer.backend.glfw.c.glfwGetJoystickButtons(seizer.backend.glfw.c.GLFW_JOYSTICK_1, &buttons_count);
|
|
|
|
|
const buttons = buttons_ptr[0..@intCast(buttons_count)];
|
|
|
|
|
controller_input_state.action = buttons[0] != 0;
|
|
|
|
|
controller_input_state.undo = buttons[1] != 0;
|
|
|
|
|
|
|
|
|
|
// detect rising for controller input
|
|
|
|
|
input_state.left = input_state.left or (!prev_controller_input_state.left and controller_input_state.left);
|
|
|
|
|
input_state.right = input_state.right or (!prev_controller_input_state.right and controller_input_state.right);
|
|
|
|
|
input_state.up = input_state.up or (!prev_controller_input_state.up and controller_input_state.up);
|
|
|
|
|
input_state.down = input_state.down or (!prev_controller_input_state.down and controller_input_state.down);
|
|
|
|
|
input_state.action = input_state.action or (!prev_controller_input_state.action and controller_input_state.action);
|
|
|
|
|
input_state.undo = input_state.undo or (!prev_controller_input_state.undo and controller_input_state.undo);
|
|
|
|
|
|
|
|
|
|
prev_controller_input_state = controller_input_state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var request_command: ?[]const u8 = null;
|
|
|
|
|
defer if (request_command) |command| gpa.allocator().free(command);
|
|
|
|
|
|
|
|
|
@ -297,6 +202,7 @@ pub fn main() !void {
|
|
|
|
|
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];
|
|
|
|
@ -358,9 +264,6 @@ pub fn main() !void {
|
|
|
|
|
switch (response) {
|
|
|
|
|
.page => |page_root_element| root_element = page_root_element,
|
|
|
|
|
.transition => |transition| {
|
|
|
|
|
if (transition.reset_selection) {
|
|
|
|
|
hovered_action = null;
|
|
|
|
|
}
|
|
|
|
|
if (!transition.can_undo) {
|
|
|
|
|
for (history.items) |*state| {
|
|
|
|
|
state.deinit();
|
|
|
|
@ -908,7 +811,7 @@ pub const CardElement = struct {
|
|
|
|
|
.card => |card| render_resources.deck.tilesheet.renderTile(
|
|
|
|
|
canvas,
|
|
|
|
|
render_resources.deck.getTileForCard(card),
|
|
|
|
|
.{ @floor(min[0] + mark_offset[0]), @floor(min[1] + mark_offset[1]) },
|
|
|
|
|
.{ min[0] + mark_offset[0], min[1] + mark_offset[1] },
|
|
|
|
|
.{},
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
@ -1078,33 +981,7 @@ fn playerTurnHandler(arena: std.mem.Allocator, request: Request) HandlerError!Re
|
|
|
|
|
return Response{ .transition = .{
|
|
|
|
|
.game_state = new_game_state,
|
|
|
|
|
.can_undo = true,
|
|
|
|
|
.reset_selection = false,
|
|
|
|
|
} };
|
|
|
|
|
} else if (std.mem.eql(u8, command, "discard")) {
|
|
|
|
|
if (request.game_state.marked_cards.count() == 1) {
|
|
|
|
|
var new_game_state = try request.game_state.clone(arena);
|
|
|
|
|
|
|
|
|
|
try new_game_state.discard_pile.ensureUnusedCapacity(arena, 1);
|
|
|
|
|
|
|
|
|
|
new_game_state.hands[0].clearRetainingCapacity();
|
|
|
|
|
new_game_state.marked_cards.clearRetainingCapacity();
|
|
|
|
|
|
|
|
|
|
for (request.game_state.hands[0].items) |card| {
|
|
|
|
|
if (request.game_state.marked_cards.contains(card)) {
|
|
|
|
|
new_game_state.discard_pile.appendAssumeCapacity(card);
|
|
|
|
|
} else {
|
|
|
|
|
new_game_state.hands[0].appendAssumeCapacity(card);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_game_state.handler = &endOfTurnConfirmHandler;
|
|
|
|
|
|
|
|
|
|
return Response{ .transition = .{
|
|
|
|
|
.game_state = new_game_state,
|
|
|
|
|
.can_undo = true,
|
|
|
|
|
.reset_selection = false,
|
|
|
|
|
} };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1123,9 +1000,6 @@ fn playerTurnHandler(arena: std.mem.Allocator, request: Request) HandlerError!Re
|
|
|
|
|
draw_pile.hidden = true;
|
|
|
|
|
|
|
|
|
|
var discard_pile = try Pile.create(arena, request.game_state.discard_pile.items);
|
|
|
|
|
if (request.game_state.marked_cards.count() == 1) {
|
|
|
|
|
discard_pile.command = "discard";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hand = try HBox.create(arena);
|
|
|
|
|
for (request.game_state.hands[0].items) |card| {
|
|
|
|
@ -1149,60 +1023,6 @@ fn playerTurnHandler(arena: std.mem.Allocator, request: Request) HandlerError!Re
|
|
|
|
|
return Response{ .page = page.element() };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn endOfTurnConfirmHandler(arena: std.mem.Allocator, request: Request) HandlerError!Response {
|
|
|
|
|
if (request.command) |command| {
|
|
|
|
|
if (std.mem.eql(u8, command, "end_turn")) {
|
|
|
|
|
var new_game_state = try request.game_state.clone(arena);
|
|
|
|
|
new_game_state.handler = &drawCardHandler;
|
|
|
|
|
|
|
|
|
|
return Response{ .transition = .{
|
|
|
|
|
.game_state = new_game_state,
|
|
|
|
|
.can_undo = false,
|
|
|
|
|
} };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var new_meld_text = try TextElement.create(arena, .{
|
|
|
|
|
.text = "New Meld",
|
|
|
|
|
.command = null,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var melds_hbox = try HBox.create(arena);
|
|
|
|
|
try melds_hbox.addElement(new_meld_text.element());
|
|
|
|
|
|
|
|
|
|
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 = null,
|
|
|
|
|
.marked = false,
|
|
|
|
|
});
|
|
|
|
|
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 end_turn_text = try TextElement.create(arena, .{
|
|
|
|
|
.text = "End Turn",
|
|
|
|
|
.command = "end_turn",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
try page.addElement(.{ 0.5, 0 }, .{ 0.5, 0 }, melds_hbox.element());
|
|
|
|
|
try page.addElement(.{ 0.5, 0.5 }, .{ 0.5, 0.5 }, end_turn_text.element());
|
|
|
|
|
|
|
|
|
|
return Response{ .page = page.element() };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn isValidRummyMeld(cards: []const Card) bool {
|
|
|
|
|
std.debug.assert(std.sort.isSorted(Card, cards, {}, rummyHandSort));
|
|
|
|
|
if (cards.len < 3) {
|
|
|
|
@ -1282,10 +1102,6 @@ fn glfw_key_callback(window: ?*seizer.backend.glfw.c.GLFWwindow, key: c_int, sca
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test {
|
|
|
|
|
_ = @import("./assets.zig");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const DeckSprites = assets.DeckSprites;
|
|
|
|
|
const Card = assets.Card;
|
|
|
|
|
|
|
|
|
|