add background blur
This commit is contained in:
parent
bbdf0511b9
commit
b80a356739
442
gui/app.zig
Normal file
442
gui/app.zig
Normal file
@ -0,0 +1,442 @@
|
||||
// zig fmt: off
|
||||
const std = @import("std");
|
||||
const Api = @import("artifacts-api");
|
||||
const Artificer = @import("artificer");
|
||||
const UI = @import("./ui.zig");
|
||||
const UIStack = @import("./ui_stack.zig");
|
||||
const RectUtils = @import("./rect_utils.zig");
|
||||
const rl = @import("raylib");
|
||||
const srcery = @import("./srcery.zig");
|
||||
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const App = @This();
|
||||
|
||||
const MapTexture = struct {
|
||||
name: Api.Map.Skin,
|
||||
texture: rl.Texture2D,
|
||||
};
|
||||
|
||||
ui: UI,
|
||||
artificer: *Artificer,
|
||||
map_textures: std.ArrayList(MapTexture),
|
||||
map_texture_indexes: std.ArrayList(usize),
|
||||
map_position_min: Api.Position,
|
||||
map_position_max: Api.Position,
|
||||
camera: rl.Camera2D,
|
||||
|
||||
blur_texture_original: ?rl.RenderTexture = null,
|
||||
blur_texture_horizontal: ?rl.RenderTexture = null,
|
||||
blur_texture_both: ?rl.RenderTexture = null,
|
||||
blur_shader: rl.Shader,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, artificer: *Artificer) !App {
|
||||
const store = artificer.server.store;
|
||||
|
||||
var map_textures = std.ArrayList(MapTexture).init(allocator);
|
||||
errdefer map_textures.deinit();
|
||||
errdefer {
|
||||
for (map_textures.items) |map_texture| {
|
||||
map_texture.texture.unload();
|
||||
}
|
||||
}
|
||||
|
||||
var map_texture_indexes = std.ArrayList(usize).init(allocator);
|
||||
errdefer map_texture_indexes.deinit();
|
||||
|
||||
// Load all map textures from api store
|
||||
{
|
||||
const map_image_ids = store.images.category_mapping.get(.map).items;
|
||||
try map_textures.ensureTotalCapacity(map_image_ids.len);
|
||||
|
||||
for (map_image_ids) |image_id| {
|
||||
const image = store.images.get(image_id).?;
|
||||
const texture = rl.loadTextureFromImage(rl.Image{
|
||||
.width = @intCast(image.width),
|
||||
.height = @intCast(image.height),
|
||||
.data = store.images.getRGBA(image_id).?.ptr,
|
||||
.mipmaps = 1,
|
||||
.format = rl.PixelFormat.pixelformat_uncompressed_r8g8b8a8
|
||||
});
|
||||
if (!rl.isTextureReady(texture)) {
|
||||
return error.LoadMapTextureFromImage;
|
||||
}
|
||||
|
||||
map_textures.appendAssumeCapacity(MapTexture{
|
||||
.name = try Api.Map.Skin.fromSlice(image.code.slice()),
|
||||
.texture = texture
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var map_position_max = Api.Position.zero();
|
||||
var map_position_min = Api.Position.zero();
|
||||
for (store.maps.items) |map| {
|
||||
map_position_min.x = @min(map_position_min.x, map.position.x);
|
||||
map_position_min.y = @min(map_position_min.y, map.position.y);
|
||||
|
||||
map_position_max.x = @max(map_position_max.x, map.position.x);
|
||||
map_position_max.y = @max(map_position_max.y, map.position.y);
|
||||
}
|
||||
const map_size = map_position_max.subtract(map_position_min);
|
||||
|
||||
try map_texture_indexes.ensureTotalCapacity(@intCast(map_size.x * map_size.y));
|
||||
|
||||
for (0..@intCast(map_size.y)) |oy| {
|
||||
for (0..@intCast(map_size.x)) |ox| {
|
||||
const x = map_position_min.x + @as(i64, @intCast(ox));
|
||||
const y = map_position_min.y + @as(i64, @intCast(oy));
|
||||
const map = store.getMap(.{ .x = x, .y = y }).?;
|
||||
|
||||
var found_texture = false;
|
||||
const map_skin = map.skin.slice();
|
||||
for (0.., map_textures.items) |i, map_texture| {
|
||||
if (std.mem.eql(u8, map_skin, map_texture.name.slice())) {
|
||||
map_texture_indexes.appendAssumeCapacity(i);
|
||||
found_texture = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_texture) {
|
||||
return error.MapImageNotFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const blur_shader = rl.loadShaderFromMemory(
|
||||
@embedFile("./base.vsh"),
|
||||
@embedFile("./blur.fsh"),
|
||||
);
|
||||
if (!rl.isShaderReady(blur_shader)) {
|
||||
return error.LoadShaderFromMemory;
|
||||
}
|
||||
|
||||
return App{
|
||||
.artificer = artificer,
|
||||
.ui = UI.init(),
|
||||
.map_textures = map_textures,
|
||||
.map_texture_indexes = map_texture_indexes,
|
||||
.map_position_max = map_position_max,
|
||||
.map_position_min = map_position_min,
|
||||
.blur_shader = blur_shader,
|
||||
.camera = rl.Camera2D{
|
||||
.offset = rl.Vector2.zero(),
|
||||
.target = rl.Vector2.zero(),
|
||||
.rotation = 0,
|
||||
.zoom = 1,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *App) void {
|
||||
for (self.map_textures.items) |map_texture| {
|
||||
map_texture.texture.unload();
|
||||
}
|
||||
|
||||
self.ui.deinit();
|
||||
self.map_textures.deinit();
|
||||
self.map_texture_indexes.deinit();
|
||||
|
||||
if (self.blur_texture_horizontal) |render_texture| {
|
||||
render_texture.unload();
|
||||
}
|
||||
if (self.blur_texture_both) |render_texture| {
|
||||
render_texture.unload();
|
||||
}
|
||||
if (self.blur_texture_original) |render_texture| {
|
||||
render_texture.unload();
|
||||
}
|
||||
}
|
||||
|
||||
fn cameraControls(camera: *rl.Camera2D) void {
|
||||
if (rl.isMouseButtonDown(.mouse_button_left)) {
|
||||
const mouse_delta = rl.getMouseDelta();
|
||||
camera.target.x -= mouse_delta.x / camera.zoom;
|
||||
camera.target.y -= mouse_delta.y / camera.zoom;
|
||||
}
|
||||
|
||||
const zoom_speed = 0.2;
|
||||
const min_zoom = 0.1;
|
||||
const max_zoom = 2;
|
||||
const zoom_delta = rl.getMouseWheelMove();
|
||||
if (zoom_delta != 0) {
|
||||
const mouse_screen = rl.getMousePosition();
|
||||
|
||||
// Get the world point that is under the mouse
|
||||
const mouse_world = rl.getScreenToWorld2D(mouse_screen, camera.*);
|
||||
|
||||
// Set the offset to where the mouse is
|
||||
camera.offset = mouse_screen;
|
||||
|
||||
// Set the target to match, so that the camera maps the world space point
|
||||
// under the cursor to the screen space point under the cursor at any zoom
|
||||
camera.target = mouse_world;
|
||||
|
||||
// Zoom increment
|
||||
camera.zoom *= (1 + zoom_delta * zoom_speed);
|
||||
camera.zoom = std.math.clamp(camera.zoom, min_zoom, max_zoom);
|
||||
}
|
||||
}
|
||||
|
||||
// fn drawCharacterInfo(ui: *UI, rect: rl.Rectangle, artificer: *Artificer, brain: *Artificer.Brain) !void {
|
||||
// var buffer: [256]u8 = undefined;
|
||||
//
|
||||
// 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);
|
||||
//
|
||||
// var label_stack = UIStack.init(RectUtils.shrinkTop(rect, name_height + 4), .top_to_bottom);
|
||||
// label_stack.gap = 4;
|
||||
//
|
||||
// const now = std.time.milliTimestamp();
|
||||
// const cooldown = brain.cooldown(&artificer.server);
|
||||
// const seconds_left: f32 = @as(f32, @floatFromInt(@max(cooldown - now, 0))) / std.time.ms_per_s;
|
||||
// const cooldown_label = try std.fmt.bufPrint(&buffer, "Cooldown: {d:.3}", .{seconds_left});
|
||||
// UI.drawTextEx(ui.font, cooldown_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white);
|
||||
//
|
||||
// var task_label: []u8 = undefined;
|
||||
// if (brain.task) |task| {
|
||||
// task_label = try std.fmt.bufPrint(&buffer, "Task: {s}", .{@tagName(task)});
|
||||
// } else {
|
||||
// task_label = try std.fmt.bufPrint(&buffer, "Task: -", .{});
|
||||
// }
|
||||
// UI.drawTextEx(ui.font, task_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white);
|
||||
//
|
||||
// const actions_label = try std.fmt.bufPrint(&buffer, "Actions: {}", .{brain.action_queue.items.len});
|
||||
// UI.drawTextEx(ui.font, actions_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white);
|
||||
// }
|
||||
|
||||
fn createOrGetRenderTexture(maybe_render_texture: *?rl.RenderTexture) !rl.RenderTexture {
|
||||
const screen_width = rl.getScreenWidth();
|
||||
const screen_height = rl.getScreenHeight();
|
||||
|
||||
if (maybe_render_texture.*) |render_texture| {
|
||||
if (render_texture.texture.width != screen_width or render_texture.texture.height != screen_height) {
|
||||
render_texture.unload();
|
||||
maybe_render_texture.* = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (maybe_render_texture.* == null) {
|
||||
const render_texture = rl.loadRenderTexture(screen_width, screen_height);
|
||||
if (!rl.isRenderTextureReady(render_texture)) {
|
||||
return error.LoadRenderTexture;
|
||||
}
|
||||
|
||||
maybe_render_texture.* = render_texture;
|
||||
}
|
||||
|
||||
return maybe_render_texture.*.?;
|
||||
}
|
||||
|
||||
fn drawRectangleRoundedUV(rect: rl.Rectangle, roundness: f32, color: rl.Color) void {
|
||||
if (roundness == 0) {
|
||||
rl.drawRectangleRec(rect, color);
|
||||
}
|
||||
}
|
||||
|
||||
fn drawBlurredWorld(self: *App, rect: rl.Rectangle, roundness: f32, color: rl.Color) !void {
|
||||
const blur_both = try createOrGetRenderTexture(&self.blur_texture_both);
|
||||
|
||||
const previous_texture = rl.getShapesTexture();
|
||||
const previous_rect = rl.getShapesTextureRectangle();
|
||||
defer rl.setShapesTexture(previous_texture, previous_rect);
|
||||
|
||||
const texture_height: f32 = @floatFromInt(blur_both.texture.height);
|
||||
const shape_rect = rl.Rectangle{
|
||||
.x = rect.x,
|
||||
.y = texture_height - rect.y,
|
||||
.width = rect.width,
|
||||
.height = -rect.height,
|
||||
};
|
||||
rl.setShapesTexture(blur_both.texture, shape_rect);
|
||||
drawRectangleRoundedUV(rect, roundness, 0, color);
|
||||
}
|
||||
|
||||
pub fn drawWorld(self: *App) !void {
|
||||
const screen_width = rl.getScreenWidth();
|
||||
const screen_height = rl.getScreenHeight();
|
||||
const screen_size = rl.Vector2.init(@floatFromInt(screen_width), @floatFromInt(screen_height));
|
||||
|
||||
const blur_original = try createOrGetRenderTexture(&self.blur_texture_original);
|
||||
const blur_horizontal = try createOrGetRenderTexture(&self.blur_texture_horizontal);
|
||||
const blur_both = try createOrGetRenderTexture(&self.blur_texture_both);
|
||||
|
||||
// 1 pass. Draw the all of the sprites
|
||||
{
|
||||
blur_original.begin();
|
||||
defer blur_original.end();
|
||||
|
||||
rl.clearBackground(rl.Color.black.alpha(0));
|
||||
|
||||
self.camera.begin();
|
||||
defer self.camera.end();
|
||||
|
||||
rl.drawCircleV(rl.Vector2.zero(), 5, rl.Color.red);
|
||||
|
||||
const map_size = self.map_position_max.subtract(self.map_position_min);
|
||||
for (0..@intCast(map_size.y)) |oy| {
|
||||
for (0..@intCast(map_size.x)) |ox| {
|
||||
const map_index = @as(usize, @intCast(map_size.x)) * oy + ox;
|
||||
const x = self.map_position_min.x + @as(i64, @intCast(ox));
|
||||
const y = self.map_position_min.y + @as(i64, @intCast(oy));
|
||||
const texture_index = self.map_texture_indexes.items[map_index];
|
||||
const texture = self.map_textures.items[texture_index].texture;
|
||||
|
||||
const tile_size = rl.Vector2.init(224, 224);
|
||||
const position = rl.Vector2.init(@floatFromInt(x), @floatFromInt(y)).multiply(tile_size);
|
||||
rl.drawTextureV(texture, position, rl.Color.white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2 pass. Apply horizontal blur
|
||||
const kernel_radius: i32 = 16;
|
||||
var kernel_coeffs: [kernel_radius * 2 + 1]f32 = undefined;
|
||||
{
|
||||
const sigma = 10;
|
||||
|
||||
for (0..kernel_coeffs.len) |i| {
|
||||
const i_i32: i32 = @intCast(i);
|
||||
const io: f32 = @floatFromInt(i_i32 - kernel_radius);
|
||||
kernel_coeffs[i] = @exp(-(io * io) / (sigma * sigma));
|
||||
// kernel_coeffs[i] /= @floatFromInt(kernel_coeffs.len);
|
||||
}
|
||||
|
||||
var kernel_sum: f32 = 0;
|
||||
for (kernel_coeffs) |coeff| {
|
||||
kernel_sum += coeff;
|
||||
}
|
||||
|
||||
for (&kernel_coeffs) |*coeff| {
|
||||
coeff.* /= kernel_sum;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const texture_size_loc = rl.getShaderLocation(self.blur_shader, "textureSize");
|
||||
assert(texture_size_loc != -1);
|
||||
|
||||
rl.setShaderValue(self.blur_shader, texture_size_loc, &screen_size, .shader_uniform_vec2);
|
||||
}
|
||||
|
||||
{
|
||||
const kernel_radius_loc = rl.getShaderLocation(self.blur_shader, "kernelRadius");
|
||||
assert(kernel_radius_loc != -1);
|
||||
|
||||
rl.setShaderValue(self.blur_shader, kernel_radius_loc, &kernel_radius, .shader_uniform_int);
|
||||
}
|
||||
|
||||
{
|
||||
const coeffs_loc = rl.getShaderLocation(self.blur_shader, "coeffs");
|
||||
assert(coeffs_loc != -1);
|
||||
|
||||
rl.setShaderValueV(self.blur_shader, coeffs_loc, &kernel_coeffs, .shader_uniform_float, kernel_coeffs.len);
|
||||
}
|
||||
|
||||
{
|
||||
const kernel_direction_loc = rl.getShaderLocation(self.blur_shader, "kernelDirection");
|
||||
assert(kernel_direction_loc != -1);
|
||||
|
||||
const kernel_direction = rl.Vector2.init(1, 0);
|
||||
rl.setShaderValue(self.blur_shader, kernel_direction_loc, &kernel_direction, .shader_uniform_vec2);
|
||||
}
|
||||
|
||||
{
|
||||
blur_horizontal.begin();
|
||||
defer blur_horizontal.end();
|
||||
|
||||
rl.clearBackground(rl.Color.black.alpha(0));
|
||||
|
||||
self.blur_shader.activate();
|
||||
defer self.blur_shader.deactivate();
|
||||
|
||||
rl.drawTextureRec(
|
||||
blur_original.texture,
|
||||
.{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = screen_size.x,
|
||||
.height = -screen_size.y,
|
||||
},
|
||||
rl.Vector2.zero(),
|
||||
rl.Color.white
|
||||
);
|
||||
}
|
||||
|
||||
// 3 pass. Apply vertical blur
|
||||
{
|
||||
const kernel_direction_loc = rl.getShaderLocation(self.blur_shader, "kernelDirection");
|
||||
assert(kernel_direction_loc != -1);
|
||||
|
||||
const kernel_direction = rl.Vector2.init(0, 1);
|
||||
rl.setShaderValue(self.blur_shader, kernel_direction_loc, &kernel_direction, .shader_uniform_vec2);
|
||||
}
|
||||
|
||||
{
|
||||
blur_both.begin();
|
||||
defer blur_both.end();
|
||||
|
||||
self.blur_shader.activate();
|
||||
defer self.blur_shader.deactivate();
|
||||
|
||||
rl.drawTextureRec(
|
||||
blur_horizontal.texture,
|
||||
.{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = screen_size.x,
|
||||
.height = -screen_size.y,
|
||||
},
|
||||
rl.Vector2.zero(),
|
||||
rl.Color.white
|
||||
);
|
||||
}
|
||||
|
||||
// Last thing, draw world without blur
|
||||
rl.drawTextureRec(
|
||||
blur_original.texture,
|
||||
.{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = @floatFromInt(blur_original.texture.width),
|
||||
.height = @floatFromInt(-blur_original.texture.height),
|
||||
},
|
||||
rl.Vector2.zero(),
|
||||
rl.Color.white
|
||||
);
|
||||
}
|
||||
|
||||
pub fn tick(self: *App) !void {
|
||||
try self.artificer.tick();
|
||||
|
||||
const screen_width = rl.getScreenWidth();
|
||||
const screen_height = rl.getScreenHeight();
|
||||
const screen_size = rl.Vector2.init(@floatFromInt(screen_width), @floatFromInt(screen_height));
|
||||
|
||||
cameraControls(&self.camera);
|
||||
|
||||
rl.beginDrawing();
|
||||
defer rl.endDrawing();
|
||||
|
||||
rl.clearBackground(srcery.black);
|
||||
|
||||
try self.drawWorld();
|
||||
|
||||
try self.drawBlurredWorld(
|
||||
.{ .x = 20, .y = 20, .width = 200, .height = 200 },
|
||||
0.5,
|
||||
rl.Color.gray
|
||||
);
|
||||
|
||||
rl.drawFPS(
|
||||
@as(i32, @intFromFloat(screen_size.x)) - 100,
|
||||
@as(i32, @intFromFloat(screen_size.y)) - 24
|
||||
);
|
||||
|
||||
// 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));
|
||||
// try drawCharacterInfo(&ui, info_stack.next(info_width), &artificer, brain);
|
||||
// }
|
||||
}
|
24
gui/base.vsh
Normal file
24
gui/base.vsh
Normal file
@ -0,0 +1,24 @@
|
||||
#version 330
|
||||
|
||||
// Input vertex attributes
|
||||
in vec3 vertexPosition;
|
||||
in vec2 vertexTexCoord;
|
||||
in vec3 vertexNormal;
|
||||
in vec4 vertexColor;
|
||||
|
||||
// Input uniform values
|
||||
uniform mat4 mvp;
|
||||
|
||||
// Output vertex attributes (to fragment shader)
|
||||
out vec2 fragTexCoord;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
// Send vertex attributes to fragment shader
|
||||
fragTexCoord = vertexTexCoord;
|
||||
fragColor = vertexColor;
|
||||
|
||||
// Calculate final vertex position
|
||||
gl_Position = mvp * vec4(vertexPosition, 1.0);
|
||||
}
|
45
gui/blur.fsh
Normal file
45
gui/blur.fsh
Normal file
@ -0,0 +1,45 @@
|
||||
#version 330
|
||||
|
||||
#define MAX_KERNEL_RADIUS 16
|
||||
#define MAX_KERNEL_COEFFS 2*MAX_KERNEL_RADIUS + 1
|
||||
|
||||
// Input vertex attributes (from vertex shader)
|
||||
in vec2 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
|
||||
// Input uniform values
|
||||
uniform sampler2D texture0;
|
||||
uniform vec4 colDiffuse;
|
||||
|
||||
uniform vec2 textureSize;
|
||||
|
||||
uniform float coeffs[MAX_KERNEL_COEFFS];
|
||||
uniform int kernelRadius;
|
||||
uniform vec2 kernelDirection;
|
||||
|
||||
// Output fragment color
|
||||
out vec4 finalColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 texel = 1.0 / textureSize;
|
||||
|
||||
vec4 texelColor = vec4(0);
|
||||
float alphaCorrection = 0;
|
||||
|
||||
for (int i = 0; i < 2*kernelRadius + 1; i++)
|
||||
{
|
||||
vec2 offset = kernelDirection * vec2(i - kernelRadius, i - kernelRadius) * texel;
|
||||
vec2 sampleCoord = fragTexCoord + offset;
|
||||
|
||||
if ((0 <= sampleCoord.x && sampleCoord.x <= 1) && (0 <= sampleCoord.y && sampleCoord.y <= 1)) {
|
||||
vec4 sample = texture(texture0, sampleCoord);
|
||||
texelColor += sample * coeffs[i];
|
||||
alphaCorrection += sample.a * coeffs[i];
|
||||
}
|
||||
}
|
||||
|
||||
texelColor /= alphaCorrection;
|
||||
|
||||
finalColor = texelColor * colDiffuse * fragColor;
|
||||
}
|
150
gui/main.zig
150
gui/main.zig
@ -1,15 +1,65 @@
|
||||
// zig fmt: off
|
||||
const std = @import("std");
|
||||
const Artificer = @import("artificer");
|
||||
const Api = @import("artifacts-api");
|
||||
const rl = @import("raylib");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const raylib_h = @cImport({
|
||||
@cInclude("stdio.h");
|
||||
@cInclude("raylib.h");
|
||||
});
|
||||
const App = @import("./app.zig");
|
||||
|
||||
const srcery = @import("./srcery.zig");
|
||||
pub const std_options = .{
|
||||
.log_scope_levels = &[_]std.log.ScopeLevel{
|
||||
.{ .scope = .api, .level = .info },
|
||||
.{ .scope = .raylib, .level = .warn },
|
||||
}
|
||||
};
|
||||
|
||||
const UI = @import("./ui.zig");
|
||||
const UIStack = @import("./ui_stack.zig");
|
||||
const RectUtils = @import("./rect_utils.zig");
|
||||
fn toRaylibTraceLogLevel(log_level: std.log.Level) rl.TraceLogLevel {
|
||||
return switch (log_level) {
|
||||
.err => rl.TraceLogLevel.log_error,
|
||||
.warn => rl.TraceLogLevel.log_warning,
|
||||
.info => rl.TraceLogLevel.log_info,
|
||||
.debug => rl.TraceLogLevel.log_trace,
|
||||
};
|
||||
}
|
||||
|
||||
fn getAPITokenFromArgs(allocator: Allocator) !?[]u8 {
|
||||
fn toZigLogLevel(log_type: c_int) ?std.log.Level {
|
||||
return switch (log_type) {
|
||||
@intFromEnum(rl.TraceLogLevel.log_trace) => std.log.Level.debug,
|
||||
@intFromEnum(rl.TraceLogLevel.log_debug) => std.log.Level.debug,
|
||||
@intFromEnum(rl.TraceLogLevel.log_info) => std.log.Level.info,
|
||||
@intFromEnum(rl.TraceLogLevel.log_warning) => std.log.Level.warn,
|
||||
@intFromEnum(rl.TraceLogLevel.log_error) => std.log.Level.err,
|
||||
@intFromEnum(rl.TraceLogLevel.log_fatal) => std.log.Level.err,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
fn raylibTraceLogCallback(logType: c_int, text: [*c]const u8, args: [*c]raylib_h.struct___va_list_tag_1) callconv(.C) void {
|
||||
const log_level = toZigLogLevel(logType) orelse return;
|
||||
|
||||
const scope = .raylib;
|
||||
const raylib_log = std.log.scoped(scope);
|
||||
|
||||
const max_tracelog_msg_length = 256; // from utils.c in raylib
|
||||
var buffer: [max_tracelog_msg_length:0]u8 = undefined;
|
||||
|
||||
inline for (std.meta.fields(std.log.Level)) |field| {
|
||||
const message_level: std.log.Level = @enumFromInt(field.value);
|
||||
if (std.log.logEnabled(message_level, scope) and log_level == message_level) {
|
||||
@memset(&buffer, 0);
|
||||
const text_length = raylib_h.vsnprintf(&buffer, buffer.len, text, args);
|
||||
const formatted_text = buffer[0..@intCast(text_length)];
|
||||
|
||||
const log_function = @field(raylib_log, field.name);
|
||||
@call(.auto, log_function, .{ "{s}", .{formatted_text} });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn getAPITokenFromArgs(allocator: std.mem.Allocator) !?[]u8 {
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
defer std.process.argsFree(allocator, args);
|
||||
|
||||
@ -22,37 +72,7 @@ fn getAPITokenFromArgs(allocator: Allocator) !?[]u8 {
|
||||
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, artificer: *Artificer, brain: *Artificer.Brain) !void {
|
||||
var buffer: [256]u8 = undefined;
|
||||
|
||||
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);
|
||||
|
||||
var label_stack = UIStack.init(RectUtils.shrinkTop(rect, name_height + 4), .top_to_bottom);
|
||||
label_stack.gap = 4;
|
||||
|
||||
const now = std.time.milliTimestamp();
|
||||
const cooldown = brain.cooldown(&artificer.server);
|
||||
const seconds_left: f32 = @as(f32, @floatFromInt(@max(cooldown - now, 0))) / std.time.ms_per_s;
|
||||
const cooldown_label = try std.fmt.bufPrint(&buffer, "Cooldown: {d:.3}", .{ seconds_left });
|
||||
UI.drawTextEx(ui.font, cooldown_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white);
|
||||
|
||||
var task_label: []u8 = undefined;
|
||||
if (brain.task) |task| {
|
||||
task_label = try std.fmt.bufPrint(&buffer, "Task: {s}", .{ @tagName(task) });
|
||||
} else {
|
||||
task_label = try std.fmt.bufPrint(&buffer, "Task: -", .{ });
|
||||
}
|
||||
UI.drawTextEx(ui.font, task_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white);
|
||||
|
||||
const actions_label = try std.fmt.bufPrint(&buffer, "Actions: {}", .{ brain.action_queue.items.len });
|
||||
UI.drawTextEx(ui.font, actions_label, RectUtils.topLeft(label_stack.next(16)), 16, 2, srcery.bright_white);
|
||||
return try allocator.dupe(u8, std.mem.trim(u8, token, "\n\t "));
|
||||
}
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
@ -60,45 +80,47 @@ pub fn main() anyerror!void {
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
raylib_h.SetTraceLogCallback(raylibTraceLogCallback);
|
||||
rl.setTraceLogLevel(toRaylibTraceLogLevel(std.log.default_level));
|
||||
|
||||
const token = (try getAPITokenFromArgs(allocator)) orelse return error.MissingToken;
|
||||
defer allocator.free(token);
|
||||
|
||||
var artificer = try Artificer.init(allocator, token);
|
||||
var store = try Api.Store.init(allocator);
|
||||
defer store.deinit(allocator);
|
||||
|
||||
var server = try Api.Server.init(allocator, &store);
|
||||
defer server.deinit();
|
||||
|
||||
try server.setToken(token);
|
||||
|
||||
var artificer = try Artificer.init(allocator, &server);
|
||||
defer artificer.deinit();
|
||||
|
||||
const cache_path = try std.fs.cwd().realpathAlloc(allocator, "api-store.bin");
|
||||
defer allocator.free(cache_path);
|
||||
|
||||
std.log.info("Prefetching server data", .{});
|
||||
try artificer.server.prefetchCached(cache_path);
|
||||
{
|
||||
const cwd_path = try std.fs.cwd().realpathAlloc(allocator, ".");
|
||||
defer allocator.free(cwd_path);
|
||||
|
||||
const cache_path = try std.fs.path.resolve(allocator, &.{ cwd_path, "./api-store.bin" });
|
||||
defer allocator.free(cache_path);
|
||||
|
||||
try server.prefetchCached(allocator, cache_path);
|
||||
}
|
||||
|
||||
rl.initWindow(800, 450, "Artificer");
|
||||
defer rl.closeWindow();
|
||||
|
||||
rl.setTargetFPS(60);
|
||||
rl.setWindowMinSize(200, 200);
|
||||
rl.setWindowState(.{
|
||||
.vsync_hint = true,
|
||||
// .window_resizable = true
|
||||
});
|
||||
|
||||
var ui = UI.init();
|
||||
defer ui.deinit();
|
||||
var app = try App.init(allocator, &artificer);
|
||||
defer app.deinit();
|
||||
|
||||
while (!rl.windowShouldClose()) {
|
||||
if (std.time.milliTimestamp() > artificer.nextStepAt()) {
|
||||
try artificer.step();
|
||||
}
|
||||
|
||||
const screen_size = rl.Vector2.init(
|
||||
@floatFromInt(rl.getScreenWidth()),
|
||||
@floatFromInt(rl.getScreenHeight())
|
||||
);
|
||||
|
||||
rl.beginDrawing();
|
||||
defer rl.endDrawing();
|
||||
|
||||
rl.clearBackground(srcery.black);
|
||||
|
||||
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));
|
||||
try drawCharacterInfo(&ui, info_stack.next(info_width), &artificer, brain);
|
||||
}
|
||||
try app.tick();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user