refactor: handler that returns a tree of elements
parent
d63a6d06fb
commit
54d04f4a2b
|
@ -0,0 +1,301 @@
|
||||||
|
pub const Suit = enum(u2) {
|
||||||
|
clubs = 0b00,
|
||||||
|
spades = 0b01,
|
||||||
|
hearts = 0b10,
|
||||||
|
diamonds = 0b11,
|
||||||
|
|
||||||
|
pub fn color(this: @This()) u1 {
|
||||||
|
return (@intFromEnum(this) & 0b10) >> 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Card = packed struct(u6) {
|
||||||
|
suit: Suit,
|
||||||
|
rank: u4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A texture with a regular grid of sprites
|
||||||
|
pub const TileSheet = struct {
|
||||||
|
texture: seizer.Texture,
|
||||||
|
tile_offset: [2]u32,
|
||||||
|
tile_size: [2]u32,
|
||||||
|
tile_stride: [2]u32,
|
||||||
|
|
||||||
|
pub const InitOptions = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
image_file_contents: []const u8,
|
||||||
|
tile_offset: [2]u32,
|
||||||
|
tile_size: [2]u32,
|
||||||
|
tile_stride: [2]u32,
|
||||||
|
texture_options: seizer.Texture.InitFromFileOptions = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(options: InitOptions) !@This() {
|
||||||
|
const texture = try seizer.Texture.initFromFileContents(
|
||||||
|
options.allocator,
|
||||||
|
options.image_file_contents,
|
||||||
|
options.texture_options,
|
||||||
|
);
|
||||||
|
return @This(){
|
||||||
|
.texture = texture,
|
||||||
|
.tile_offset = options.tile_offset,
|
||||||
|
.tile_size = options.tile_size,
|
||||||
|
.tile_stride = options.tile_stride,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(this: *@This()) void {
|
||||||
|
this.texture.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn renderTile(this: @This(), canvas: *seizer.Canvas, tile_id: u32, pos: [2]f32, options: struct {
|
||||||
|
size: ?[2]f32 = null,
|
||||||
|
}) void {
|
||||||
|
const texture_sizef = [2]f32{
|
||||||
|
@floatFromInt(this.texture.size[0]),
|
||||||
|
@floatFromInt(this.texture.size[1]),
|
||||||
|
};
|
||||||
|
const tile_offsetf = [2]f32{
|
||||||
|
@floatFromInt(this.tile_offset[0]),
|
||||||
|
@floatFromInt(this.tile_offset[1]),
|
||||||
|
};
|
||||||
|
const tile_stridef = [2]f32{
|
||||||
|
@floatFromInt(this.tile_stride[0]),
|
||||||
|
@floatFromInt(this.tile_stride[1]),
|
||||||
|
};
|
||||||
|
const tile_sizef = [2]f32{
|
||||||
|
@floatFromInt(this.tile_size[0]),
|
||||||
|
@floatFromInt(this.tile_size[1]),
|
||||||
|
};
|
||||||
|
// add a half to round up
|
||||||
|
const size_in_tiles = [2]u32{
|
||||||
|
(@as(u32, @intCast(this.texture.size[0])) + (this.tile_stride[0] / 2)) / this.tile_stride[0],
|
||||||
|
(@as(u32, @intCast(this.texture.size[1])) + (this.tile_stride[1] / 2)) / this.tile_stride[1],
|
||||||
|
};
|
||||||
|
const pos_in_tiles = [2]u32{
|
||||||
|
tile_id % size_in_tiles[0],
|
||||||
|
tile_id / size_in_tiles[0],
|
||||||
|
};
|
||||||
|
const pos_in_tilesf = [2]f32{
|
||||||
|
@floatFromInt(pos_in_tiles[0]),
|
||||||
|
@floatFromInt(pos_in_tiles[1]),
|
||||||
|
};
|
||||||
|
|
||||||
|
const pixel_pos = [2]f32{
|
||||||
|
pos_in_tilesf[0] * tile_stridef[0] + tile_offsetf[0],
|
||||||
|
pos_in_tilesf[1] * tile_stridef[1] + tile_offsetf[1],
|
||||||
|
};
|
||||||
|
const uv = seizer.geometry.AABB(f32){
|
||||||
|
.min = .{
|
||||||
|
pixel_pos[0] / texture_sizef[0],
|
||||||
|
pixel_pos[1] / texture_sizef[1],
|
||||||
|
},
|
||||||
|
.max = .{
|
||||||
|
(pixel_pos[0] + tile_sizef[0]) / texture_sizef[0],
|
||||||
|
(pixel_pos[1] + tile_sizef[1]) / texture_sizef[1],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
canvas.rect(pos, options.size orelse tile_sizef, .{
|
||||||
|
.texture = this.texture.glTexture,
|
||||||
|
.uv = uv,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A texture with a regular grid of sprites
|
||||||
|
pub const DeckSprites = struct {
|
||||||
|
tilesheet: TileSheet,
|
||||||
|
/// Return the tile index for a given card,
|
||||||
|
mapping: std.AutoHashMapUnmanaged(Card, u32),
|
||||||
|
blank: u32,
|
||||||
|
back: u32,
|
||||||
|
|
||||||
|
pub fn deinit(this: *@This(), gpa: std.mem.Allocator) void {
|
||||||
|
this.tilesheet.deinit();
|
||||||
|
this.mapping.deinit(gpa);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getTileForCard(this: @This(), card: Card) u32 {
|
||||||
|
return this.mapping.get(card) orelse 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn loadSmallCards(gpa: std.mem.Allocator) !DeckSprites {
|
||||||
|
var tilesheet = try TileSheet.init(.{
|
||||||
|
.allocator = gpa,
|
||||||
|
.image_file_contents = @embedFile("./cardsSmall_tilemap.png"),
|
||||||
|
.tile_offset = .{ 0, 0 },
|
||||||
|
.tile_size = .{ 16, 16 },
|
||||||
|
.tile_stride = .{ 17, 17 },
|
||||||
|
.texture_options = .{
|
||||||
|
.min_filter = .nearest,
|
||||||
|
.mag_filter = .nearest,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
errdefer tilesheet.deinit();
|
||||||
|
|
||||||
|
var mapping = std.AutoHashMap(Card, u32).init(gpa);
|
||||||
|
errdefer mapping.deinit();
|
||||||
|
try mapping.ensureTotalCapacity(52);
|
||||||
|
|
||||||
|
const hearts_start_index: u32 = 0;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .hearts, .rank = @intCast(rank + 1) },
|
||||||
|
hearts_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const diamonds_start_index: u32 = 14;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .diamonds, .rank = @intCast(rank + 1) },
|
||||||
|
diamonds_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clubs_start_index: u32 = 28;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .clubs, .rank = @intCast(rank + 1) },
|
||||||
|
clubs_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const spades_start_index: u32 = 42;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .spades, .rank = @intCast(rank + 1) },
|
||||||
|
spades_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeckSprites{
|
||||||
|
.tilesheet = tilesheet,
|
||||||
|
.mapping = mapping.unmanaged,
|
||||||
|
// TODO: add better graphic for blank card
|
||||||
|
.blank = 59,
|
||||||
|
.back = 59,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadMediumCards(gpa: std.mem.Allocator) !DeckSprites {
|
||||||
|
var tilesheet = try TileSheet.init(.{
|
||||||
|
.allocator = gpa,
|
||||||
|
.image_file_contents = @embedFile("./cardsMedium_tilemap.png"),
|
||||||
|
.tile_offset = .{ 6, 2 },
|
||||||
|
.tile_size = .{ 20, 29 },
|
||||||
|
.tile_stride = .{ 33, 33 },
|
||||||
|
.texture_options = .{
|
||||||
|
.min_filter = .nearest,
|
||||||
|
.mag_filter = .nearest,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
errdefer tilesheet.deinit();
|
||||||
|
|
||||||
|
var mapping = std.AutoHashMap(Card, u32).init(gpa);
|
||||||
|
errdefer mapping.deinit();
|
||||||
|
try mapping.ensureTotalCapacity(52);
|
||||||
|
|
||||||
|
const hearts_start_index: u32 = 0;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .hearts, .rank = @intCast(rank + 1) },
|
||||||
|
hearts_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const diamonds_start_index: u32 = 15;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .diamonds, .rank = @intCast(rank + 1) },
|
||||||
|
diamonds_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clubs_start_index: u32 = 30;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .clubs, .rank = @intCast(rank + 1) },
|
||||||
|
clubs_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const spades_start_index: u32 = 45;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .spades, .rank = @intCast(rank + 1) },
|
||||||
|
spades_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeckSprites{
|
||||||
|
.tilesheet = tilesheet,
|
||||||
|
.mapping = mapping.unmanaged,
|
||||||
|
.blank = 14,
|
||||||
|
.back = 29,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadLargeCards(gpa: std.mem.Allocator) !DeckSprites {
|
||||||
|
var tilesheet = try TileSheet.init(.{
|
||||||
|
.allocator = gpa,
|
||||||
|
.image_file_contents = @embedFile("./cardsLarge_tilemap.png"),
|
||||||
|
.tile_offset = .{ 11, 2 },
|
||||||
|
.tile_size = .{ 41, 60 },
|
||||||
|
.tile_stride = .{ 65, 65 },
|
||||||
|
.texture_options = .{
|
||||||
|
.min_filter = .nearest,
|
||||||
|
.mag_filter = .nearest,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
errdefer tilesheet.deinit();
|
||||||
|
|
||||||
|
var mapping = std.AutoHashMap(Card, u32).init(gpa);
|
||||||
|
errdefer mapping.deinit();
|
||||||
|
try mapping.ensureTotalCapacity(52);
|
||||||
|
|
||||||
|
const hearts_start_index: u32 = 0;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .hearts, .rank = @intCast(rank + 1) },
|
||||||
|
hearts_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const diamonds_start_index: u32 = 14;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .diamonds, .rank = @intCast(rank + 1) },
|
||||||
|
diamonds_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clubs_start_index: u32 = 28;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .clubs, .rank = @intCast(rank + 1) },
|
||||||
|
clubs_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const spades_start_index: u32 = 42;
|
||||||
|
for (0..13) |rank| {
|
||||||
|
mapping.putAssumeCapacityNoClobber(
|
||||||
|
Card{ .suit = .spades, .rank = @intCast(rank + 1) },
|
||||||
|
spades_start_index + @as(u32, @intCast(rank)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeckSprites{
|
||||||
|
.tilesheet = tilesheet,
|
||||||
|
.mapping = mapping.unmanaged,
|
||||||
|
.blank = 13,
|
||||||
|
.back = 27,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const seizer = @import("seizer");
|
||||||
|
const gl = seizer.gl;
|
||||||
|
const std = @import("std");
|
688
src/main.zig
688
src/main.zig
|
@ -1,20 +1,5 @@
|
||||||
var gl_binding: gl.Binding = undefined;
|
var gl_binding: gl.Binding = undefined;
|
||||||
|
|
||||||
const Suit = enum(u2) {
|
|
||||||
clubs = 0b00,
|
|
||||||
spades = 0b01,
|
|
||||||
hearts = 0b10,
|
|
||||||
diamonds = 0b11,
|
|
||||||
|
|
||||||
pub fn color(this: @This()) u1 {
|
|
||||||
return (@intFromEnum(this) & 0b10) >> 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const Card = packed struct(u6) {
|
|
||||||
suit: Suit,
|
|
||||||
rank: u4,
|
|
||||||
};
|
|
||||||
|
|
||||||
const GameState = struct {
|
const GameState = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
prng: std.rand.DefaultPrng,
|
prng: std.rand.DefaultPrng,
|
||||||
|
@ -88,113 +73,6 @@ pub fn makeStandardDeck(allocator: std.mem.Allocator) ![]Card {
|
||||||
return deck;
|
return deck;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A texture with a regular grid of sprites
|
|
||||||
const TileSheet = struct {
|
|
||||||
texture: seizer.Texture,
|
|
||||||
tile_offset: [2]u32,
|
|
||||||
tile_size: [2]u32,
|
|
||||||
tile_stride: [2]u32,
|
|
||||||
|
|
||||||
pub const InitOptions = struct {
|
|
||||||
allocator: std.mem.Allocator,
|
|
||||||
image_file_contents: []const u8,
|
|
||||||
tile_offset: [2]u32,
|
|
||||||
tile_size: [2]u32,
|
|
||||||
tile_stride: [2]u32,
|
|
||||||
texture_options: seizer.Texture.InitFromFileOptions = .{},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init(options: InitOptions) !@This() {
|
|
||||||
const texture = try seizer.Texture.initFromFileContents(
|
|
||||||
options.allocator,
|
|
||||||
options.image_file_contents,
|
|
||||||
options.texture_options,
|
|
||||||
);
|
|
||||||
return @This(){
|
|
||||||
.texture = texture,
|
|
||||||
.tile_offset = options.tile_offset,
|
|
||||||
.tile_size = options.tile_size,
|
|
||||||
.tile_stride = options.tile_stride,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(this: *@This()) void {
|
|
||||||
this.texture.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn renderTile(this: @This(), canvas: *seizer.Canvas, tile_id: u32, pos: [2]f32, options: struct {
|
|
||||||
size: ?[2]f32 = null,
|
|
||||||
}) void {
|
|
||||||
const texture_sizef = [2]f32{
|
|
||||||
@floatFromInt(this.texture.size[0]),
|
|
||||||
@floatFromInt(this.texture.size[1]),
|
|
||||||
};
|
|
||||||
const tile_offsetf = [2]f32{
|
|
||||||
@floatFromInt(this.tile_offset[0]),
|
|
||||||
@floatFromInt(this.tile_offset[1]),
|
|
||||||
};
|
|
||||||
const tile_stridef = [2]f32{
|
|
||||||
@floatFromInt(this.tile_stride[0]),
|
|
||||||
@floatFromInt(this.tile_stride[1]),
|
|
||||||
};
|
|
||||||
const tile_sizef = [2]f32{
|
|
||||||
@floatFromInt(this.tile_size[0]),
|
|
||||||
@floatFromInt(this.tile_size[1]),
|
|
||||||
};
|
|
||||||
// add a half to round up
|
|
||||||
const size_in_tiles = [2]u32{
|
|
||||||
(@as(u32, @intCast(this.texture.size[0])) + (this.tile_stride[0] / 2)) / this.tile_stride[0],
|
|
||||||
(@as(u32, @intCast(this.texture.size[1])) + (this.tile_stride[1] / 2)) / this.tile_stride[1],
|
|
||||||
};
|
|
||||||
const pos_in_tiles = [2]u32{
|
|
||||||
tile_id % size_in_tiles[0],
|
|
||||||
tile_id / size_in_tiles[0],
|
|
||||||
};
|
|
||||||
const pos_in_tilesf = [2]f32{
|
|
||||||
@floatFromInt(pos_in_tiles[0]),
|
|
||||||
@floatFromInt(pos_in_tiles[1]),
|
|
||||||
};
|
|
||||||
|
|
||||||
const pixel_pos = [2]f32{
|
|
||||||
pos_in_tilesf[0] * tile_stridef[0] + tile_offsetf[0],
|
|
||||||
pos_in_tilesf[1] * tile_stridef[1] + tile_offsetf[1],
|
|
||||||
};
|
|
||||||
const uv = seizer.geometry.AABB(f32){
|
|
||||||
.min = .{
|
|
||||||
pixel_pos[0] / texture_sizef[0],
|
|
||||||
pixel_pos[1] / texture_sizef[1],
|
|
||||||
},
|
|
||||||
.max = .{
|
|
||||||
(pixel_pos[0] + tile_sizef[0]) / texture_sizef[0],
|
|
||||||
(pixel_pos[1] + tile_sizef[1]) / texture_sizef[1],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
canvas.rect(pos, options.size orelse tile_sizef, .{
|
|
||||||
.texture = this.texture.glTexture,
|
|
||||||
.uv = uv,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A texture with a regular grid of sprites
|
|
||||||
const DeckSprites = struct {
|
|
||||||
tilesheet: TileSheet,
|
|
||||||
/// Return the tile index for a given card,
|
|
||||||
mapping: std.AutoHashMapUnmanaged(Card, u32),
|
|
||||||
blank: u32,
|
|
||||||
back: u32,
|
|
||||||
|
|
||||||
pub fn deinit(this: *@This(), gpa: std.mem.Allocator) void {
|
|
||||||
this.tilesheet.deinit();
|
|
||||||
this.mapping.deinit(gpa);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getTileForCard(this: @This(), card: Card) u32 {
|
|
||||||
return this.mapping.get(card) orelse 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
@ -248,6 +126,12 @@ pub fn main() !void {
|
||||||
var game_state = try GameState.init(gpa.allocator(), std.crypto.random.int(u64), 1);
|
var game_state = try GameState.init(gpa.allocator(), std.crypto.random.int(u64), 1);
|
||||||
defer game_state.deinit();
|
defer game_state.deinit();
|
||||||
|
|
||||||
|
var root_element: ?Element = null;
|
||||||
|
var response_arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
||||||
|
defer response_arena.deinit();
|
||||||
|
|
||||||
|
const handler = &drawCardHandler;
|
||||||
|
|
||||||
var selection: usize = 0;
|
var selection: usize = 0;
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -259,10 +143,24 @@ pub fn main() !void {
|
||||||
};
|
};
|
||||||
seizer.backend.glfw.c.glfwPollEvents();
|
seizer.backend.glfw.c.glfwPollEvents();
|
||||||
|
|
||||||
if (input_state.right) {
|
if (input_state.left) {
|
||||||
|
if (selection == 0) selection = game_state.hands[0].items.len;
|
||||||
|
selection = selection - 1;
|
||||||
|
} else if (input_state.right) {
|
||||||
selection = (selection + 1) % game_state.hands[0].items.len;
|
selection = (selection + 1) % game_state.hands[0].items.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (root_element == null) {
|
||||||
|
_ = response_arena.reset(.retain_capacity);
|
||||||
|
const response = try handler(response_arena.allocator(), Request{
|
||||||
|
.game_state = game_state,
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (response) {
|
||||||
|
.page => |page_root_element| root_element = page_root_element,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gl.clearColor(0.7, 0.5, 0.5, 1.0);
|
gl.clearColor(0.7, 0.5, 0.5, 1.0);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
@ -285,13 +183,13 @@ pub fn main() !void {
|
||||||
|
|
||||||
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 loadSmallCards(gpa.allocator());
|
card_tilemap_small = try assets.loadSmallCards(gpa.allocator());
|
||||||
},
|
},
|
||||||
301...1000 => if (card_tilemap_medium == null) {
|
301...1000 => if (card_tilemap_medium == null) {
|
||||||
card_tilemap_medium = try loadMediumCards(gpa.allocator());
|
card_tilemap_medium = try assets.loadMediumCards(gpa.allocator());
|
||||||
},
|
},
|
||||||
1001...std.math.maxInt(c_int) => if (card_tilemap_large == null) {
|
1001...std.math.maxInt(c_int) => if (card_tilemap_large == null) {
|
||||||
card_tilemap_large = try loadLargeCards(gpa.allocator());
|
card_tilemap_large = try assets.loadLargeCards(gpa.allocator());
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
@ -303,47 +201,19 @@ pub fn main() !void {
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
const draw_pile_pos = [2]f32{
|
if (root_element) |root| {
|
||||||
canvas.window_size[0] / 2 - @as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[0])),
|
root.interface.render(
|
||||||
canvas.window_size[1] / 2,
|
root.pointer,
|
||||||
};
|
&canvas,
|
||||||
const discard_pile_pos = [2]f32{
|
.{
|
||||||
canvas.window_size[0] / 2 + @as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[0])),
|
.deck = deck_sprites,
|
||||||
canvas.window_size[1] / 2,
|
},
|
||||||
};
|
.{ 0, 0 },
|
||||||
const hand_pos = [2]f32{
|
.{
|
||||||
(canvas.window_size[0] - @as(f32, @floatFromInt(game_state.hands[0].items.len * deck_sprites.tilesheet.tile_size[0]))) / 2,
|
@floatFromInt(window_size[0]),
|
||||||
canvas.window_size[1] - @as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[1])),
|
@floatFromInt(window_size[1]),
|
||||||
};
|
},
|
||||||
|
);
|
||||||
for (game_state.draw_pile.items, 0..) |_, i| {
|
|
||||||
const oy = -@as(f32, @floatFromInt(i)) * (@as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[1])) / (52.0 * 4));
|
|
||||||
deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.back, .{
|
|
||||||
draw_pile_pos[0],
|
|
||||||
draw_pile_pos[1] + oy,
|
|
||||||
}, .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (game_state.discard_pile.items, 0..) |card, i| {
|
|
||||||
const oy = -@as(f32, @floatFromInt(i)) * (@as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[1])) / (52.0 * 4));
|
|
||||||
deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.getTileForCard(card), .{
|
|
||||||
discard_pile_pos[0],
|
|
||||||
discard_pile_pos[1] + oy,
|
|
||||||
}, .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (game_state.hands[0].items, 0..) |card, i| {
|
|
||||||
const pos = [2]f32{
|
|
||||||
hand_pos[0] + @as(f32, @floatFromInt(i)) * @as(f32, @floatFromInt(deck_sprites.tilesheet.tile_size[0])),
|
|
||||||
hand_pos[1],
|
|
||||||
};
|
|
||||||
deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.getTileForCard(card), pos, .{});
|
|
||||||
if (i == selection) {
|
|
||||||
canvas.rect(pos, [2]f32{
|
|
||||||
@floatFromInt(deck_sprites.tilesheet.tile_size[0]),
|
|
||||||
@floatFromInt(deck_sprites.tilesheet.tile_size[1]),
|
|
||||||
}, .{ .color = .{ 0xAA, 0xFF, 0xAA, 0x60 } });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.end();
|
canvas.end();
|
||||||
|
@ -352,179 +222,347 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadSmallCards(gpa: std.mem.Allocator) !DeckSprites {
|
const RenderResources = struct {
|
||||||
var tilesheet = try TileSheet.init(.{
|
deck: DeckSprites,
|
||||||
.allocator = gpa,
|
};
|
||||||
.image_file_contents = @embedFile("./cardsSmall_tilemap.png"),
|
|
||||||
.tile_offset = .{ 0, 0 },
|
|
||||||
.tile_size = .{ 16, 16 },
|
|
||||||
.tile_stride = .{ 17, 17 },
|
|
||||||
.texture_options = .{
|
|
||||||
.min_filter = .nearest,
|
|
||||||
.mag_filter = .nearest,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
errdefer tilesheet.deinit();
|
|
||||||
|
|
||||||
var mapping = std.AutoHashMap(Card, u32).init(gpa);
|
const HandlerError = error{OutOfMemory};
|
||||||
errdefer mapping.deinit();
|
const Handler = *const fn (std.mem.Allocator, Request) HandlerError!Response;
|
||||||
try mapping.ensureTotalCapacity(52);
|
|
||||||
|
|
||||||
const hearts_start_index: u32 = 0;
|
const Request = struct {
|
||||||
for (0..13) |rank| {
|
game_state: GameState,
|
||||||
mapping.putAssumeCapacityNoClobber(
|
};
|
||||||
Card{ .suit = .hearts, .rank = @intCast(rank + 1) },
|
const Response = union(enum) {
|
||||||
hearts_start_index + @as(u32, @intCast(rank)),
|
page: Element,
|
||||||
);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const diamonds_start_index: u32 = 14;
|
pub const Element = struct {
|
||||||
for (0..13) |rank| {
|
pointer: ?*anyopaque,
|
||||||
mapping.putAssumeCapacityNoClobber(
|
interface: *const Interface,
|
||||||
Card{ .suit = .diamonds, .rank = @intCast(rank + 1) },
|
|
||||||
diamonds_start_index + @as(u32, @intCast(rank)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const clubs_start_index: u32 = 28;
|
pub const Interface = struct {
|
||||||
for (0..13) |rank| {
|
minimum_size: *const fn (?*anyopaque, RenderResources) [2]f32,
|
||||||
mapping.putAssumeCapacityNoClobber(
|
render: *const fn (?*anyopaque, *seizer.Canvas, RenderResources, [2]f32, [2]f32) void,
|
||||||
Card{ .suit = .clubs, .rank = @intCast(rank + 1) },
|
|
||||||
clubs_start_index + @as(u32, @intCast(rank)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const spades_start_index: u32 = 42;
|
|
||||||
for (0..13) |rank| {
|
|
||||||
mapping.putAssumeCapacityNoClobber(
|
|
||||||
Card{ .suit = .spades, .rank = @intCast(rank + 1) },
|
|
||||||
spades_start_index + @as(u32, @intCast(rank)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DeckSprites{
|
|
||||||
.tilesheet = tilesheet,
|
|
||||||
.mapping = mapping.unmanaged,
|
|
||||||
// TODO: add better graphic for blank card
|
|
||||||
.blank = 59,
|
|
||||||
.back = 59,
|
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
fn loadMediumCards(gpa: std.mem.Allocator) !DeckSprites {
|
pub const Page = struct {
|
||||||
var tilesheet = try TileSheet.init(.{
|
allocator: std.mem.Allocator,
|
||||||
.allocator = gpa,
|
children: std.ArrayListUnmanaged(Child),
|
||||||
.image_file_contents = @embedFile("./cardsMedium_tilemap.png"),
|
|
||||||
.tile_offset = .{ 6, 2 },
|
|
||||||
.tile_size = .{ 20, 29 },
|
|
||||||
.tile_stride = .{ 33, 33 },
|
|
||||||
.texture_options = .{
|
|
||||||
.min_filter = .nearest,
|
|
||||||
.mag_filter = .nearest,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
errdefer tilesheet.deinit();
|
|
||||||
|
|
||||||
var mapping = std.AutoHashMap(Card, u32).init(gpa);
|
pub const Child = struct {
|
||||||
errdefer mapping.deinit();
|
/// Where is the child attached on the parent? Imagine it as a pin going through
|
||||||
try mapping.ensureTotalCapacity(52);
|
/// both the parent and child element. This defines where on the parent that pin
|
||||||
|
/// passes through.
|
||||||
const hearts_start_index: u32 = 0;
|
///
|
||||||
for (0..13) |rank| {
|
/// In a virtual coordinate where <0,0> = top-left, <1,1> = bottom-right, unless
|
||||||
mapping.putAssumeCapacityNoClobber(
|
/// the numbers are negative.
|
||||||
Card{ .suit = .hearts, .rank = @intCast(rank + 1) },
|
anchor_in_parent: [2]f32,
|
||||||
hearts_start_index + @as(u32, @intCast(rank)),
|
/// Where is the child attached on the parent? Imagine it as a pin going through
|
||||||
);
|
/// both the parent and child element. This defines where on the child that pin
|
||||||
}
|
/// passes through.
|
||||||
|
///
|
||||||
const diamonds_start_index: u32 = 15;
|
/// In a virtual coordinate where <0,0> = top-left, <1,1> = bottom-right, unless
|
||||||
for (0..13) |rank| {
|
/// the numbers are negative.
|
||||||
mapping.putAssumeCapacityNoClobber(
|
anchor_in_child: [2]f32,
|
||||||
Card{ .suit = .diamonds, .rank = @intCast(rank + 1) },
|
element: Element,
|
||||||
diamonds_start_index + @as(u32, @intCast(rank)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const clubs_start_index: u32 = 30;
|
|
||||||
for (0..13) |rank| {
|
|
||||||
mapping.putAssumeCapacityNoClobber(
|
|
||||||
Card{ .suit = .clubs, .rank = @intCast(rank + 1) },
|
|
||||||
clubs_start_index + @as(u32, @intCast(rank)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const spades_start_index: u32 = 45;
|
|
||||||
for (0..13) |rank| {
|
|
||||||
mapping.putAssumeCapacityNoClobber(
|
|
||||||
Card{ .suit = .spades, .rank = @intCast(rank + 1) },
|
|
||||||
spades_start_index + @as(u32, @intCast(rank)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DeckSprites{
|
|
||||||
.tilesheet = tilesheet,
|
|
||||||
.mapping = mapping.unmanaged,
|
|
||||||
.blank = 14,
|
|
||||||
.back = 29,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
fn loadLargeCards(gpa: std.mem.Allocator) !DeckSprites {
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||||
var tilesheet = try TileSheet.init(.{
|
const this = try allocator.create(@This());
|
||||||
.allocator = gpa,
|
errdefer allocator.destroy(this);
|
||||||
.image_file_contents = @embedFile("./cardsLarge_tilemap.png"),
|
this.* = .{
|
||||||
.tile_offset = .{ 11, 2 },
|
.allocator = allocator,
|
||||||
.tile_size = .{ 41, 60 },
|
.children = .{},
|
||||||
.tile_stride = .{ 65, 65 },
|
};
|
||||||
.texture_options = .{
|
return this;
|
||||||
.min_filter = .nearest,
|
|
||||||
.mag_filter = .nearest,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
errdefer tilesheet.deinit();
|
|
||||||
|
|
||||||
var mapping = std.AutoHashMap(Card, u32).init(gpa);
|
|
||||||
errdefer mapping.deinit();
|
|
||||||
try mapping.ensureTotalCapacity(52);
|
|
||||||
|
|
||||||
const hearts_start_index: u32 = 0;
|
|
||||||
for (0..13) |rank| {
|
|
||||||
mapping.putAssumeCapacityNoClobber(
|
|
||||||
Card{ .suit = .hearts, .rank = @intCast(rank + 1) },
|
|
||||||
hearts_start_index + @as(u32, @intCast(rank)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const diamonds_start_index: u32 = 14;
|
pub fn addElement(this: *@This(), anchor_in_parent: [2]f32, anchor_in_child: [2]f32, child_element: Element) !void {
|
||||||
for (0..13) |rank| {
|
try this.children.append(this.allocator, Child{
|
||||||
mapping.putAssumeCapacityNoClobber(
|
.anchor_in_parent = anchor_in_parent,
|
||||||
Card{ .suit = .diamonds, .rank = @intCast(rank + 1) },
|
.anchor_in_child = anchor_in_child,
|
||||||
diamonds_start_index + @as(u32, @intCast(rank)),
|
.element = child_element,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const clubs_start_index: u32 = 28;
|
pub fn element(this: *@This()) Element {
|
||||||
for (0..13) |rank| {
|
return Element{
|
||||||
mapping.putAssumeCapacityNoClobber(
|
.pointer = this,
|
||||||
Card{ .suit = .clubs, .rank = @intCast(rank + 1) },
|
.interface = &Element.Interface{
|
||||||
clubs_start_index + @as(u32, @intCast(rank)),
|
.minimum_size = &element_minimum_size,
|
||||||
);
|
.render = &element_render,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const spades_start_index: u32 = 42;
|
pub fn element_minimum_size(pointer: ?*anyopaque, render_resources: RenderResources) [2]f32 {
|
||||||
for (0..13) |rank| {
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
mapping.putAssumeCapacityNoClobber(
|
|
||||||
Card{ .suit = .spades, .rank = @intCast(rank + 1) },
|
var minimum_size = [2]f32{ 0, 0 };
|
||||||
spades_start_index + @as(u32, @intCast(rank)),
|
for (this.children.items) |child| {
|
||||||
);
|
const child_size = child.element.interface.minimum_size(child.element.pointer, render_resources);
|
||||||
|
|
||||||
|
minimum_size = .{
|
||||||
|
@max(minimum_size[0], child_size[0]),
|
||||||
|
@max(minimum_size[1], child_size[1]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return minimum_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DeckSprites{
|
pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resources: RenderResources, min: [2]f32, max: [2]f32) void {
|
||||||
.tilesheet = tilesheet,
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
.mapping = mapping.unmanaged,
|
|
||||||
.blank = 13,
|
const parent_size = [2]f32{
|
||||||
.back = 27,
|
max[0] - min[0],
|
||||||
};
|
max[1] - min[1],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (this.children.items) |child| {
|
||||||
|
const pos_in_parent = [2]f32{
|
||||||
|
child.anchor_in_parent[0] * parent_size[0],
|
||||||
|
child.anchor_in_parent[1] * parent_size[1],
|
||||||
|
};
|
||||||
|
|
||||||
|
const child_size = child.element.interface.minimum_size(child.element.pointer, render_resources);
|
||||||
|
|
||||||
|
const pos_in_child = [2]f32{
|
||||||
|
child.anchor_in_child[0] * child_size[0],
|
||||||
|
child.anchor_in_child[1] * child_size[1],
|
||||||
|
};
|
||||||
|
|
||||||
|
const child_min = [2]f32{
|
||||||
|
pos_in_parent[0] - pos_in_child[0],
|
||||||
|
pos_in_parent[1] - pos_in_child[1],
|
||||||
|
};
|
||||||
|
const child_max = [2]f32{
|
||||||
|
pos_in_parent[0] + (child_size[0] - pos_in_child[0]),
|
||||||
|
pos_in_parent[1] + (child_size[1] - pos_in_child[1]),
|
||||||
|
};
|
||||||
|
|
||||||
|
child.element.interface.render(child.element.pointer, canvas, render_resources, child_min, child_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const HBox = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
children: std.ArrayListUnmanaged(Element),
|
||||||
|
|
||||||
|
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||||
|
const this = try allocator.create(@This());
|
||||||
|
errdefer allocator.destroy(this);
|
||||||
|
this.* = .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.children = .{},
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addElement(this: *@This(), child_element: Element) !void {
|
||||||
|
try this.children.append(this.allocator, child_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element(this: *@This()) Element {
|
||||||
|
return Element{
|
||||||
|
.pointer = this,
|
||||||
|
.interface = &Element.Interface{
|
||||||
|
.minimum_size = &element_minimum_size,
|
||||||
|
.render = &element_render,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element_minimum_size(pointer: ?*anyopaque, render_resources: RenderResources) [2]f32 {
|
||||||
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
|
var minimum_size = [2]f32{ 0, 0 };
|
||||||
|
for (this.children.items) |child| {
|
||||||
|
const child_size = child.interface.minimum_size(child.pointer, render_resources);
|
||||||
|
|
||||||
|
minimum_size = .{
|
||||||
|
minimum_size[0] + child_size[0],
|
||||||
|
@max(minimum_size[1], child_size[1]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return minimum_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
if (this.children.items.len == 0) return;
|
||||||
|
|
||||||
|
const parent_size = [2]f32{
|
||||||
|
max[0] - min[0],
|
||||||
|
max[1] - min[1],
|
||||||
|
};
|
||||||
|
|
||||||
|
var filled_space: f32 = 0;
|
||||||
|
for (this.children.items) |child| {
|
||||||
|
const child_size = child.interface.minimum_size(child.pointer, render_resources);
|
||||||
|
|
||||||
|
filled_space += child_size[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const empty_space = parent_size[0] - filled_space;
|
||||||
|
|
||||||
|
const space_around = empty_space / @as(f32, @floatFromInt((this.children.items.len + 1)));
|
||||||
|
|
||||||
|
var x: f32 = min[0] + space_around;
|
||||||
|
for (this.children.items) |child| {
|
||||||
|
const child_size = child.interface.minimum_size(child.pointer, render_resources);
|
||||||
|
|
||||||
|
const child_min = [2]f32{ x, min[1] };
|
||||||
|
const child_max = [2]f32{ x + child_size[0], max[1] };
|
||||||
|
|
||||||
|
child.interface.render(child.pointer, canvas, render_resources, child_min, child_max);
|
||||||
|
|
||||||
|
x += child_size[0] + space_around;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Pile = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
cards: []Card,
|
||||||
|
hidden: bool = false,
|
||||||
|
|
||||||
|
pub fn create(allocator: std.mem.Allocator, cards: []const Card) !*@This() {
|
||||||
|
const this = try allocator.create(@This());
|
||||||
|
errdefer allocator.destroy(this);
|
||||||
|
|
||||||
|
const cards_owned = try allocator.dupe(Card, cards);
|
||||||
|
errdefer allocator.free(cards_owned);
|
||||||
|
|
||||||
|
this.* = .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.cards = cards_owned,
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element(this: *@This()) Element {
|
||||||
|
return Element{
|
||||||
|
.pointer = this,
|
||||||
|
.interface = &Element.Interface{
|
||||||
|
.minimum_size = &element_minimum_size,
|
||||||
|
.render = &element_render,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element_minimum_size(pointer: ?*anyopaque, render_resources: RenderResources) [2]f32 {
|
||||||
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
|
return .{
|
||||||
|
@floatFromInt(render_resources.deck.tilesheet.tile_size[0]),
|
||||||
|
@as(f32, (@floatFromInt(render_resources.deck.tilesheet.tile_size[1]))) + @as(f32, (@floatFromInt(render_resources.deck.tilesheet.tile_size[1]))) * @as(f32, @floatFromInt(this.cards.len)) / 52.0 * 0.25,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 start_y: f32 = max[1] - @as(f32, @floatFromInt(render_resources.deck.tilesheet.tile_size[1]));
|
||||||
|
|
||||||
|
for (this.cards, 0..) |card, i| {
|
||||||
|
const oy = -@as(f32, @floatFromInt(i)) * (@as(f32, @floatFromInt(render_resources.deck.tilesheet.tile_size[1])) / (52.0 * 4));
|
||||||
|
if (this.hidden) {
|
||||||
|
render_resources.deck.tilesheet.renderTile(canvas, render_resources.deck.back, .{
|
||||||
|
min[0],
|
||||||
|
start_y + oy,
|
||||||
|
}, .{});
|
||||||
|
} else {
|
||||||
|
render_resources.deck.tilesheet.renderTile(canvas, render_resources.deck.getTileForCard(card), .{
|
||||||
|
min[0],
|
||||||
|
start_y + oy,
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Spread = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
cards: []Card,
|
||||||
|
hidden: bool = false,
|
||||||
|
|
||||||
|
pub fn create(allocator: std.mem.Allocator, cards: []const Card) !*@This() {
|
||||||
|
const this = try allocator.create(@This());
|
||||||
|
errdefer allocator.destroy(this);
|
||||||
|
|
||||||
|
const cards_owned = try allocator.dupe(Card, cards);
|
||||||
|
errdefer allocator.free(cards_owned);
|
||||||
|
|
||||||
|
this.* = .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.cards = cards_owned,
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element(this: *@This()) Element {
|
||||||
|
return Element{
|
||||||
|
.pointer = this,
|
||||||
|
.interface = &Element.Interface{
|
||||||
|
.minimum_size = &element_minimum_size,
|
||||||
|
.render = &element_render,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element_minimum_size(pointer: ?*anyopaque, render_resources: RenderResources) [2]f32 {
|
||||||
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
|
return .{
|
||||||
|
@as(f32, @floatFromInt(render_resources.deck.tilesheet.tile_size[0])) * @as(f32, @floatFromInt(this.cards.len)),
|
||||||
|
@as(f32, @floatFromInt(render_resources.deck.tilesheet.tile_size[1])),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 start_y = max[1] - @as(f32, @floatFromInt(render_resources.deck.tilesheet.tile_size[1]));
|
||||||
|
|
||||||
|
for (this.cards, 0..) |card, i| {
|
||||||
|
const ox = @as(f32, @floatFromInt(i)) * @as(f32, @floatFromInt(render_resources.deck.tilesheet.tile_size[0]));
|
||||||
|
if (this.hidden) {
|
||||||
|
render_resources.deck.tilesheet.renderTile(canvas, render_resources.deck.back, .{
|
||||||
|
min[0] + ox,
|
||||||
|
start_y,
|
||||||
|
}, .{});
|
||||||
|
} else {
|
||||||
|
render_resources.deck.tilesheet.renderTile(canvas, render_resources.deck.getTileForCard(card), .{
|
||||||
|
min[0] + ox,
|
||||||
|
start_y,
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handler at the start of a turn, while the player is drawing a card.
|
||||||
|
fn drawCardHandler(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 Spread.create(arena, request.game_state.hands[0].items);
|
||||||
|
|
||||||
|
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 glfw_framebuffer_size_callback(window: ?*seizer.backend.glfw.c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
|
fn glfw_framebuffer_size_callback(window: ?*seizer.backend.glfw.c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
|
||||||
|
@ -560,6 +598,10 @@ fn glfw_key_callback(window: ?*seizer.backend.glfw.c.GLFWwindow, key: c_int, sca
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DeckSprites = assets.DeckSprites;
|
||||||
|
const Card = assets.Card;
|
||||||
|
|
||||||
|
const assets = @import("./assets.zig");
|
||||||
const seizer = @import("seizer");
|
const seizer = @import("seizer");
|
||||||
const gl = seizer.gl;
|
const gl = seizer.gl;
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
Loading…
Reference in New Issue