add basic gui
This commit is contained in:
parent
45e32424cb
commit
f9dc023b90
@ -44,7 +44,7 @@ air: CombatStats,
|
|||||||
|
|
||||||
equipment: Equipment,
|
equipment: Equipment,
|
||||||
|
|
||||||
inventory_max_items: i64,
|
inventory_max_items: u64,
|
||||||
inventory: Inventory,
|
inventory: Inventory,
|
||||||
|
|
||||||
pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character {
|
pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character {
|
||||||
@ -54,7 +54,14 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character
|
|||||||
const x = try json_utils.getIntegerRequired(obj, "x");
|
const x = try json_utils.getIntegerRequired(obj, "x");
|
||||||
const y = try json_utils.getIntegerRequired(obj, "y");
|
const y = try json_utils.getIntegerRequired(obj, "y");
|
||||||
const name = (try json_utils.dupeString(allocator, obj, "name")) orelse return error.MissingProperty;
|
const name = (try json_utils.dupeString(allocator, obj, "name")) orelse return error.MissingProperty;
|
||||||
assert(name.len > 0);
|
if (name.len == 0) {
|
||||||
|
return error.InvalidName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inventory_max_items = json_utils.getInteger(obj, "inventory_max_items") orelse return error.MissingProperty;
|
||||||
|
if (inventory_max_items < 0) {
|
||||||
|
return error.InvalidInventoryMaxItems;
|
||||||
|
}
|
||||||
|
|
||||||
return Character{
|
return Character{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
@ -84,7 +91,7 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character
|
|||||||
|
|
||||||
.equipment = try Equipment.parse(api, obj),
|
.equipment = try Equipment.parse(api, obj),
|
||||||
|
|
||||||
.inventory_max_items = json_utils.getInteger(obj, "inventory_max_items") orelse return error.MissingProperty,
|
.inventory_max_items = @intCast(inventory_max_items),
|
||||||
.inventory = try Inventory.parse(api, inventory)
|
.inventory = try Inventory.parse(api, inventory)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
const Artificer = @import("artificer");
|
const Artificer = @import("artificer");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
@ -29,6 +30,11 @@ pub fn main() !void {
|
|||||||
var artificer = try Artificer.init(allocator, token);
|
var artificer = try Artificer.init(allocator, token);
|
||||||
defer artificer.deinit();
|
defer artificer.deinit();
|
||||||
|
|
||||||
|
if (builtin.mode != .Debug) {
|
||||||
|
std.log.info("Prefetching server data", .{});
|
||||||
|
try artificer.server.prefetch();
|
||||||
|
}
|
||||||
|
|
||||||
std.log.info("Starting main loop", .{});
|
std.log.info("Starting main loop", .{});
|
||||||
while (true) {
|
while (true) {
|
||||||
const waitUntil = artificer.nextStepAt();
|
const waitUntil = artificer.nextStepAt();
|
||||||
|
62
gui/main.zig
62
gui/main.zig
@ -1,18 +1,76 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Artificer = @import("artificer");
|
||||||
const rl = @import("raylib");
|
const rl = @import("raylib");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const srcery = @import("./srcery.zig");
|
||||||
|
|
||||||
|
const UI = @import("./ui.zig");
|
||||||
|
const UIStack = @import("./ui_stack.zig");
|
||||||
|
const RectUtils = @import("./rect_utils.zig");
|
||||||
|
|
||||||
|
fn getAPITokenFromArgs(allocator: Allocator) !?[]u8 {
|
||||||
|
const args = try std.process.argsAlloc(allocator);
|
||||||
|
defer std.process.argsFree(allocator, args);
|
||||||
|
|
||||||
|
if (args.len < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = args[1];
|
||||||
|
const cwd = std.fs.cwd();
|
||||||
|
var token_buffer: [256]u8 = undefined;
|
||||||
|
const token = try cwd.readFile(filename, &token_buffer);
|
||||||
|
|
||||||
|
return try allocator.dupe(u8, std.mem.trim(u8,token,"\n\t "));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drawCharacterInfo(ui: *UI, rect: rl.Rectangle, brain: Artificer.Brain) void {
|
||||||
|
const name_height = 20;
|
||||||
|
UI.drawTextCentered(ui.font, brain.name, .{
|
||||||
|
.x = RectUtils.center(rect).x,
|
||||||
|
.y = rect.y + name_height/2
|
||||||
|
}, 20, 2, srcery.bright_white);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
const token = (try getAPITokenFromArgs(allocator)) orelse return error.MissingToken;
|
||||||
|
defer allocator.free(token);
|
||||||
|
|
||||||
|
var artificer = try Artificer.init(allocator, token);
|
||||||
|
defer artificer.deinit();
|
||||||
|
|
||||||
rl.initWindow(800, 450, "Artificer");
|
rl.initWindow(800, 450, "Artificer");
|
||||||
defer rl.closeWindow();
|
defer rl.closeWindow();
|
||||||
|
|
||||||
rl.setTargetFPS(60);
|
rl.setTargetFPS(60);
|
||||||
|
|
||||||
|
var ui = UI.init();
|
||||||
|
defer ui.deinit();
|
||||||
|
|
||||||
while (!rl.windowShouldClose()) {
|
while (!rl.windowShouldClose()) {
|
||||||
|
if (std.time.timestamp() > artificer.nextStepAt()) {
|
||||||
|
try artificer.step();
|
||||||
|
}
|
||||||
|
|
||||||
|
const screen_size = rl.Vector2.init(
|
||||||
|
@floatFromInt(rl.getScreenWidth()),
|
||||||
|
@floatFromInt(rl.getScreenHeight())
|
||||||
|
);
|
||||||
|
|
||||||
rl.beginDrawing();
|
rl.beginDrawing();
|
||||||
defer rl.endDrawing();
|
defer rl.endDrawing();
|
||||||
|
|
||||||
rl.clearBackground(rl.Color.white);
|
rl.clearBackground(srcery.black);
|
||||||
|
|
||||||
rl.drawText("Congrats! You created your first window!", 190, 200, 20, rl.Color.light_gray);
|
var info_stack = UIStack.init(rl.Rectangle.init(0, 0, screen_size.x, screen_size.y), .left_to_right);
|
||||||
|
for (artificer.characters.items) |brain| {
|
||||||
|
const info_width = screen_size.x / @as(f32, @floatFromInt(artificer.characters.items.len));
|
||||||
|
drawCharacterInfo(&ui, info_stack.next(info_width), brain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
142
gui/rect_utils.zig
Normal file
142
gui/rect_utils.zig
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
const rl = @import("raylib");
|
||||||
|
const Rect = rl.Rectangle;
|
||||||
|
|
||||||
|
pub const AlignX = enum { left, center, right };
|
||||||
|
pub const AlignY = enum { top, center, bottom };
|
||||||
|
|
||||||
|
pub fn initCentered(rect: Rect, width: f32, height: f32) Rect {
|
||||||
|
const unused_width = rect.width - width;
|
||||||
|
const unused_height = rect.height - height;
|
||||||
|
return Rect.init(rect.x + unused_width / 2, rect.y + unused_height / 2, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn center(rect: Rect) rl.Vector2 {
|
||||||
|
return rl.Vector2{
|
||||||
|
.x = rect.x + rect.width / 2,
|
||||||
|
.y = rect.y + rect.height / 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bottomLeft(rect: Rect) rl.Vector2 {
|
||||||
|
return rl.Vector2{
|
||||||
|
.x = rect.x,
|
||||||
|
.y = rect.y + rect.height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bottomRight(rect: Rect) rl.Vector2 {
|
||||||
|
return rl.Vector2{
|
||||||
|
.x = rect.x + rect.width,
|
||||||
|
.y = rect.y + rect.height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn topLeft(rect: Rect) rl.Vector2 {
|
||||||
|
return rl.Vector2{
|
||||||
|
.x = rect.x,
|
||||||
|
.y = rect.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn topRight(rect: Rect) rl.Vector2 {
|
||||||
|
return rl.Vector2{
|
||||||
|
.x = rect.x + rect.width,
|
||||||
|
.y = rect.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn aligned(rect: Rect, align_x: AlignX, align_y: AlignY) rl.Vector2 {
|
||||||
|
const x = switch(align_x) {
|
||||||
|
.left => rect.x,
|
||||||
|
.center => rect.x + rect.width/2,
|
||||||
|
.right => rect.x + rect.width,
|
||||||
|
};
|
||||||
|
|
||||||
|
const y = switch(align_y) {
|
||||||
|
.top => rect.y,
|
||||||
|
.center => rect.y + rect.height/2,
|
||||||
|
.bottom => rect.y + rect.height,
|
||||||
|
};
|
||||||
|
|
||||||
|
return rl.Vector2.init(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrink(rect: Rect, x: f32, y: f32) rl.Rectangle {
|
||||||
|
return Rect.init(rect.x + x, rect.y + y, rect.width - 2 * x, rect.height - 2 * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrinkX(rect: rl.Rectangle, offset: f32) rl.Rectangle {
|
||||||
|
return shrink(rect, offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrinkY(rect: rl.Rectangle, offset: f32) rl.Rectangle {
|
||||||
|
return shrink(rect, 0, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrinkTop(rect: rl.Rectangle, offset: f32) rl.Rectangle {
|
||||||
|
return Rect.init(rect.x, rect.y + offset, rect.width, rect.height - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow(rect: Rect, x: f32, y: f32) rl.Rectangle {
|
||||||
|
return shrink(rect, -x, -y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn growY(rect: Rect, offset: f32) rl.Rectangle {
|
||||||
|
return grow(rect, 0, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(rect: rl.Rectangle) rl.Vector2 {
|
||||||
|
return rl.Vector2.init(rect.x, rect.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isInside(rect: rl.Rectangle, x: f32, y: f32) bool {
|
||||||
|
return (rect.x <= x and x < rect.x + rect.width) and (rect.y < y and y < rect.y + rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isInsideVec2(rect: rl.Rectangle, vec2: rl.Vector2) bool {
|
||||||
|
return isInside(rect, vec2.x, vec2.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn top(rect: rl.Rectangle) f32 {
|
||||||
|
return rect.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bottom(rect: rl.Rectangle) f32 {
|
||||||
|
return rect.y + rect.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn left(rect: rl.Rectangle) f32 {
|
||||||
|
return rect.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right(rect: rl.Rectangle) f32 {
|
||||||
|
return rect.x + rect.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verticalSplit(rect: rl.Rectangle, left_side_width: f32) [2]rl.Rectangle {
|
||||||
|
var left_side = rect;
|
||||||
|
left_side.width = left_side_width;
|
||||||
|
|
||||||
|
var right_side = rect;
|
||||||
|
right_side.x += left_side_width;
|
||||||
|
right_side.width -= left_side_width;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
left_side,
|
||||||
|
right_side
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn horizontalSplit(rect: rl.Rectangle, top_side_height: f32) [2]rl.Rectangle {
|
||||||
|
var top_side = rect;
|
||||||
|
top_side.height = top_side_height;
|
||||||
|
|
||||||
|
var bottom_side = rect;
|
||||||
|
bottom_side.y += top_side_height;
|
||||||
|
bottom_side.height -= top_side_height;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
top_side,
|
||||||
|
bottom_side
|
||||||
|
};
|
||||||
|
}
|
43
gui/srcery.zig
Normal file
43
gui/srcery.zig
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const rl = @import("raylib");
|
||||||
|
|
||||||
|
fn rgb(r: u8, g: u8, b: u8) rl.Color {
|
||||||
|
return rl.Color.init(r, g, b, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Primary
|
||||||
|
pub const black = rgb(28 , 27 , 25 );
|
||||||
|
pub const red = rgb(239, 47 , 39 );
|
||||||
|
pub const green = rgb(81 , 159, 80 );
|
||||||
|
pub const yellow = rgb(251, 184, 41 );
|
||||||
|
pub const blue = rgb(44 , 120, 191);
|
||||||
|
pub const magenta = rgb(224, 44 , 109);
|
||||||
|
pub const cyan = rgb(10 , 174, 179);
|
||||||
|
pub const white = rgb(186, 166, 127);
|
||||||
|
pub const bright_black = rgb(145, 129, 117);
|
||||||
|
pub const bright_red = rgb(247, 83 , 65 );
|
||||||
|
pub const bright_green = rgb(152, 188, 55 );
|
||||||
|
pub const bright_yellow = rgb(254, 208, 110);
|
||||||
|
pub const bright_blue = rgb(104, 168, 228);
|
||||||
|
pub const bright_magenta = rgb(255, 92 , 143);
|
||||||
|
pub const bright_cyan = rgb(43 , 228, 208);
|
||||||
|
pub const bright_white = rgb(252, 232, 195);
|
||||||
|
|
||||||
|
// Secondary
|
||||||
|
pub const orange = rgb(255, 95, 0);
|
||||||
|
pub const bright_orange = rgb(255, 135, 0);
|
||||||
|
pub const hard_black = rgb(18, 18, 18);
|
||||||
|
pub const teal = rgb(0, 128, 128);
|
||||||
|
|
||||||
|
// Grays
|
||||||
|
pub const xgray1 = rgb(38 , 38 , 38 );
|
||||||
|
pub const xgray2 = rgb(48 , 48 , 48 );
|
||||||
|
pub const xgray3 = rgb(58 , 58 , 58 );
|
||||||
|
pub const xgray4 = rgb(68 , 68 , 68 );
|
||||||
|
pub const xgray5 = rgb(78 , 78 , 78 );
|
||||||
|
pub const xgray6 = rgb(88 , 88 , 88 );
|
||||||
|
pub const xgray7 = rgb(98 , 98 , 98 );
|
||||||
|
pub const xgray8 = rgb(108, 108, 108);
|
||||||
|
pub const xgray9 = rgb(118, 118, 118);
|
||||||
|
pub const xgray10 = rgb(128, 128, 128);
|
||||||
|
pub const xgray11 = rgb(138, 138, 138);
|
||||||
|
pub const xgray12 = rgb(148, 148, 148);
|
159
gui/ui.zig
Normal file
159
gui/ui.zig
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
const rl = @import("raylib");
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const UI = @This();
|
||||||
|
|
||||||
|
font: rl.Font,
|
||||||
|
|
||||||
|
pub fn init() UI {
|
||||||
|
return UI{
|
||||||
|
.font = rl.getFontDefault()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: UI) void {
|
||||||
|
rl.unloadFont(self.font);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reimplementation of `GetGlyphIndex` from raylib in src/rtext.c
|
||||||
|
fn GetGlyphIndex(font: rl.Font, codepoint: i32) usize {
|
||||||
|
var index: usize = 0;
|
||||||
|
|
||||||
|
var fallbackIndex: usize = 0; // Get index of fallback glyph '?'
|
||||||
|
|
||||||
|
for (0..@intCast(font.glyphCount), font.glyphs) |i, glyph| {
|
||||||
|
if (glyph.value == '?') fallbackIndex = i;
|
||||||
|
|
||||||
|
if (glyph.value == codepoint)
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((index == 0) and (font.glyphs[0].value != codepoint)) index = fallbackIndex;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn GetCodePointNext(text: []const u8, next: *usize) i32 {
|
||||||
|
var letter: i32 = '?';
|
||||||
|
|
||||||
|
if (std.unicode.utf8ByteSequenceLength(text[0])) |codepointSize| {
|
||||||
|
next.* = codepointSize;
|
||||||
|
if (std.unicode.utf8Decode(text[0..codepointSize])) |codepoint| {
|
||||||
|
letter = @intCast(codepoint);
|
||||||
|
} else |_| {}
|
||||||
|
} else |_| {}
|
||||||
|
|
||||||
|
return letter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
|
||||||
|
const textLineSpacing = 2; // TODO: Assume that line spacing is not changed.
|
||||||
|
|
||||||
|
// Reimplementation of `rl.drawTextEx`, so a null terminated would not be required
|
||||||
|
pub fn drawTextEx(font: rl.Font, text: []const u8, position: rl.Vector2, font_size: f32, spacing: f32, tint: rl.Color) void {
|
||||||
|
var used_font = font;
|
||||||
|
if (font.texture.id == 0) {
|
||||||
|
used_font = rl.getFontDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
var text_offset_y: f32 = 0;
|
||||||
|
var text_offset_x: f32 = 0;
|
||||||
|
|
||||||
|
const scale_factor = font_size / @as(f32, @floatFromInt(used_font.baseSize));
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < text.len) {
|
||||||
|
var next: usize = 0;
|
||||||
|
|
||||||
|
const letter = GetCodePointNext(text[i..], &next);
|
||||||
|
const index = GetGlyphIndex(font, letter);
|
||||||
|
|
||||||
|
i += next;
|
||||||
|
|
||||||
|
if (letter == '\n') {
|
||||||
|
text_offset_x = 0;
|
||||||
|
text_offset_y += (font_size + textLineSpacing);
|
||||||
|
} else {
|
||||||
|
if (letter != ' ' and letter != '\t') {
|
||||||
|
rl.drawTextCodepoint(font, letter, .{
|
||||||
|
.x = position.x + text_offset_x,
|
||||||
|
.y = position.y + text_offset_y,
|
||||||
|
}, font_size, tint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font.glyphs[index].advanceX == 0) {
|
||||||
|
text_offset_x += font.recs[index].width*scale_factor + spacing;
|
||||||
|
} else {
|
||||||
|
text_offset_x += @as(f32, @floatFromInt(font.glyphs[index].advanceX))*scale_factor + spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reimplementation of `rl.measureTextEx`, so a null terminated would not be required
|
||||||
|
pub fn measureTextEx(font: rl.Font, text: []const u8, fontSize: f32, spacing: f32) rl.Vector2 {
|
||||||
|
var textSize = rl.Vector2.init(0, 0);
|
||||||
|
|
||||||
|
if (font.texture.id == 0) return textSize; // Security check
|
||||||
|
|
||||||
|
var tempByteCounter: i32 = 0; // Used to count longer text line num chars
|
||||||
|
var byteCounter: i32 = 0;
|
||||||
|
|
||||||
|
var textWidth: f32 = 0;
|
||||||
|
var tempTextWidth: f32 = 0; // Used to count longer text line width
|
||||||
|
|
||||||
|
var textHeight: f32 = fontSize;
|
||||||
|
const scaleFactor: f32 = fontSize/@as(f32, @floatFromInt(font.baseSize));
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < text.len)
|
||||||
|
{
|
||||||
|
byteCounter += 1;
|
||||||
|
|
||||||
|
var next: usize = 0;
|
||||||
|
|
||||||
|
const letter = GetCodePointNext(text[i..], &next);
|
||||||
|
const index = GetGlyphIndex(font, letter);
|
||||||
|
|
||||||
|
i += next;
|
||||||
|
|
||||||
|
if (letter != '\n')
|
||||||
|
{
|
||||||
|
if (font.glyphs[index].advanceX != 0) {
|
||||||
|
textWidth += @floatFromInt(font.glyphs[index].advanceX);
|
||||||
|
} else {
|
||||||
|
textWidth += font.recs[index].width;
|
||||||
|
textWidth += @floatFromInt(font.glyphs[index].offsetX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
|
||||||
|
byteCounter = 0;
|
||||||
|
textWidth = 0;
|
||||||
|
|
||||||
|
textHeight += (fontSize + textLineSpacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempByteCounter < byteCounter) tempByteCounter = byteCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
|
||||||
|
|
||||||
|
textSize.x = tempTextWidth*scaleFactor + @as(f32, @floatFromInt(tempByteCounter - 1)) * spacing;
|
||||||
|
textSize.y = textHeight;
|
||||||
|
|
||||||
|
return textSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTextCentered(font: rl.Font, text: []const u8, position: rl.Vector2, font_size: f32, spacing: f32, color: rl.Color) void {
|
||||||
|
const text_size = measureTextEx(font, text, font_size, spacing);
|
||||||
|
const adjusted_position = rl.Vector2{
|
||||||
|
.x = position.x - text_size.x/2,
|
||||||
|
.y = position.y - text_size.y/2,
|
||||||
|
};
|
||||||
|
drawTextEx(font, text, adjusted_position, font_size, spacing, color);
|
||||||
|
}
|
42
gui/ui_stack.zig
Normal file
42
gui/ui_stack.zig
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const rl = @import("raylib");
|
||||||
|
const Stack = @This();
|
||||||
|
|
||||||
|
pub const Direction = enum {
|
||||||
|
top_to_bottom,
|
||||||
|
bottom_to_top,
|
||||||
|
left_to_right
|
||||||
|
};
|
||||||
|
|
||||||
|
unused_box: rl.Rectangle,
|
||||||
|
dir: Direction,
|
||||||
|
gap: f32 = 0,
|
||||||
|
|
||||||
|
pub fn init(box: rl.Rectangle, dir: Direction) Stack {
|
||||||
|
return Stack{
|
||||||
|
.unused_box = box,
|
||||||
|
.dir = dir
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *Stack, size: f32) rl.Rectangle {
|
||||||
|
return switch (self.dir) {
|
||||||
|
.top_to_bottom => {
|
||||||
|
const next_box = rl.Rectangle.init(self.unused_box.x, self.unused_box.y, self.unused_box.width, size);
|
||||||
|
self.unused_box.y += size;
|
||||||
|
self.unused_box.y += self.gap;
|
||||||
|
return next_box;
|
||||||
|
},
|
||||||
|
.bottom_to_top => {
|
||||||
|
const next_box = rl.Rectangle.init(self.unused_box.x, self.unused_box.y + self.unused_box.height - size, self.unused_box.width, size);
|
||||||
|
self.unused_box.height -= size;
|
||||||
|
self.unused_box.height -= self.gap;
|
||||||
|
return next_box;
|
||||||
|
},
|
||||||
|
.left_to_right => {
|
||||||
|
const next_box = rl.Rectangle.init(self.unused_box.x, self.unused_box.y, size, self.unused_box.height);
|
||||||
|
self.unused_box.x += size;
|
||||||
|
self.unused_box.x += self.gap;
|
||||||
|
return next_box;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -400,7 +400,7 @@ pub fn isTaskFinished(self: *CharacterBrain) bool {
|
|||||||
|
|
||||||
pub fn performTask(self: *CharacterBrain, api: *Server) !void {
|
pub fn performTask(self: *CharacterBrain, api: *Server) !void {
|
||||||
if (self.task == null) {
|
if (self.task == null) {
|
||||||
std.log.debug("[{s}] idle", .{self.name});
|
// TODO: std.log.debug("[{s}] idle", .{self.name});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
lib/root.zig
26
lib/root.zig
@ -2,7 +2,7 @@ const std = @import("std");
|
|||||||
const Api = @import("artifacts-api");
|
const Api = @import("artifacts-api");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const CharacterBrain = @import("./brain.zig");
|
pub const Brain = @import("./brain.zig");
|
||||||
|
|
||||||
const Artificer = @This();
|
const Artificer = @This();
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ const expiration_margin: u64 = 100 * std.time.ns_per_ms; // 100ms
|
|||||||
const server_down_retry_interval = 5; // minutes
|
const server_down_retry_interval = 5; // minutes
|
||||||
|
|
||||||
server: Api.Server,
|
server: Api.Server,
|
||||||
characters: std.ArrayList(CharacterBrain),
|
characters: std.ArrayList(Brain),
|
||||||
|
|
||||||
paused_until: ?i64 = null,
|
paused_until: ?i64 = null,
|
||||||
|
|
||||||
@ -20,16 +20,15 @@ pub fn init(allocator: Allocator, token: []const u8) !Artificer {
|
|||||||
|
|
||||||
try server.setToken(token);
|
try server.setToken(token);
|
||||||
|
|
||||||
var characters = std.ArrayList(CharacterBrain).init(allocator);
|
var characters = std.ArrayList(Brain).init(allocator);
|
||||||
defer characters.deinit();
|
errdefer characters.deinit(); // TODO: Add character deinit
|
||||||
// TODO: Add character deinit
|
|
||||||
|
|
||||||
// const chars = try api.listMyCharacters();
|
const chars = try server.listMyCharacters();
|
||||||
// defer chars.deinit();
|
defer chars.deinit();
|
||||||
|
|
||||||
// for (chars.items) |char| {
|
for (chars.items) |char| {
|
||||||
// try scheduler.addCharacter(char.name);
|
try characters.append(try Brain.init(allocator, char.name));
|
||||||
// }
|
}
|
||||||
|
|
||||||
return Artificer{
|
return Artificer{
|
||||||
.server = server,
|
.server = server,
|
||||||
@ -37,11 +36,12 @@ pub fn init(allocator: Allocator, token: []const u8) !Artificer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Artificer) void {
|
pub fn deinit(self: *Artificer) void {
|
||||||
for (self.characters.items) |brain| {
|
for (self.characters.items) |brain| {
|
||||||
brain.deinit();
|
brain.deinit();
|
||||||
}
|
}
|
||||||
self.characters.deinit();
|
self.characters.deinit();
|
||||||
|
self.server.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(self: *Artificer) !void {
|
pub fn step(self: *Artificer) !void {
|
||||||
@ -93,7 +93,7 @@ pub fn nextStepAt(self: *Artificer) i64 {
|
|||||||
return earliestCooldown(self.characters.items, &self.server) orelse 0;
|
return earliestCooldown(self.characters.items, &self.server) orelse 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn earliestCooldown(characters: []CharacterBrain, api: *Api.Server) ?i64 {
|
fn earliestCooldown(characters: []Brain, api: *Api.Server) ?i64 {
|
||||||
var earliest_cooldown: ?i64 = null;
|
var earliest_cooldown: ?i64 = null;
|
||||||
for (characters) |*brain| {
|
for (characters) |*brain| {
|
||||||
if (brain.action_queue.items.len == 0) continue;
|
if (brain.action_queue.items.len == 0) continue;
|
||||||
@ -109,7 +109,7 @@ fn earliestCooldown(characters: []CharacterBrain, api: *Api.Server) ?i64 {
|
|||||||
return earliest_cooldown;
|
return earliest_cooldown;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runNextActions(characters: []CharacterBrain, api: *Api.Server) !void {
|
fn runNextActions(characters: []Brain, api: *Api.Server) !void {
|
||||||
const maybe_earliest_cooldown = earliestCooldown(characters, api);
|
const maybe_earliest_cooldown = earliestCooldown(characters, api);
|
||||||
if (maybe_earliest_cooldown == null) return;
|
if (maybe_earliest_cooldown == null) return;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user