699 lines
24 KiB
Zig
699 lines
24 KiB
Zig
// zig fmt: off
|
|
const std = @import("std");
|
|
const Api = @import("artifacts-api");
|
|
const Artificer = @import("artificer");
|
|
const UI = @import("./ui.zig");
|
|
const RectUtils = @import("./rect-utils.zig");
|
|
const rl = @import("raylib");
|
|
const FontFace = @import("./font-face.zig");
|
|
const srcery = @import("./srcery.zig");
|
|
const rlgl_h = @cImport({
|
|
@cInclude("rlgl.h");
|
|
});
|
|
|
|
const assert = std.debug.assert;
|
|
|
|
const App = @This();
|
|
|
|
const MapTexture = struct {
|
|
name: Api.Map.Skin,
|
|
texture: rl.Texture2D,
|
|
};
|
|
|
|
ui: UI,
|
|
server: *Api.Server,
|
|
store: *Api.Store,
|
|
map_textures: std.ArrayList(MapTexture),
|
|
map_texture_indexes: std.ArrayList(usize),
|
|
map_position_min: Api.Position,
|
|
map_position_max: Api.Position,
|
|
camera: rl.Camera2D,
|
|
font_face: FontFace,
|
|
|
|
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, store: *Api.Store, server: *Api.Server) !App {
|
|
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;
|
|
}
|
|
|
|
var fontChars: [95]i32 = undefined;
|
|
for (0..fontChars.len) |i| {
|
|
fontChars[i] = 32 + @as(i32, @intCast(i));
|
|
}
|
|
var font = rl.loadFontFromMemory(".ttf", @embedFile("./roboto-font/Roboto-Medium.ttf"), 16, &fontChars);
|
|
if (!font.isReady()) {
|
|
return error.LoadFontFromMemory;
|
|
}
|
|
|
|
return App{
|
|
.store = store,
|
|
.server = server,
|
|
.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,
|
|
.font_face = .{ .font = font },
|
|
.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.map_textures.deinit();
|
|
self.map_texture_indexes.deinit();
|
|
self.font_face.font.unload();
|
|
|
|
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.*.?;
|
|
}
|
|
|
|
// Modified version of `DrawRectangleRounded` where the UV texture coordiantes are consistent and align
|
|
fn drawRectangleRoundedUV(rec: rl.Rectangle, roundness: f32, color: rl.Color) void {
|
|
assert(roundness < 1);
|
|
|
|
if (roundness <= 0 or rec.width <= 1 or rec.height <= 1) {
|
|
rl.drawRectangleRec(rec, color);
|
|
return;
|
|
}
|
|
|
|
const radius: f32 = @min(rec.width, rec.height) * roundness / 2;
|
|
if (radius <= 0.0) return;
|
|
|
|
// Calculate the maximum angle between segments based on the error rate (usually 0.5f)
|
|
const smooth_circle_error_rate = 0.5;
|
|
const th: f32 = std.math.acos(2 * std.math.pow(f32, 1 - smooth_circle_error_rate / radius, 2) - 1);
|
|
var segments: i32 = @intFromFloat(@ceil(2 * std.math.pi / th) / 4.0);
|
|
if (segments <= 0) segments = 4;
|
|
|
|
const step_length = 90.0 / @as(f32, @floatFromInt(segments));
|
|
|
|
// Quick sketch to make sense of all of this,
|
|
// there are 9 parts to draw, also mark the 12 points we'll use
|
|
//
|
|
// P0____________________P1
|
|
// /| |\
|
|
// /1| 2 |3\
|
|
// P7 /__|____________________|__\ P2
|
|
// | |P8 P9| |
|
|
// | 8 | 9 | 4 |
|
|
// | __|____________________|__ |
|
|
// P6 \ |P11 P10| / P3
|
|
// \7| 6 |5/
|
|
// \|____________________|/
|
|
// P5 P4
|
|
|
|
// Coordinates of the 12 points that define the rounded rect
|
|
const radius_u = radius / rec.width;
|
|
const radius_v = radius / rec.height;
|
|
const points = [_]rl.Vector2{
|
|
.{ .x = radius_u , .y = 0 }, // P0
|
|
.{ .x = 1 - radius_u , .y = 0 }, // P1
|
|
.{ .x = 1 , .y = radius_v }, // P2
|
|
.{ .x = 1 , .y = 1 - radius_v }, // P3
|
|
.{ .x = 1 - radius_u , .y = 1 }, // P4
|
|
.{ .x = radius_u , .y = 1 }, // P5
|
|
.{ .x = 0 , .y = 1 - radius_v }, // P6
|
|
.{ .x = 0 , .y = radius_v }, // P7
|
|
.{ .x = radius_u , .y = radius_v }, // P8
|
|
.{ .x = 1 - radius_u , .y = radius_v }, // P9
|
|
.{ .x = 1 - radius_u , .y = 1 - radius_v }, // P10
|
|
.{ .x = radius_u , .y = 1 - radius_v }, // P11
|
|
};
|
|
|
|
const texture = rl.getShapesTexture();
|
|
const shape_rect = rl.getShapesTextureRectangle();
|
|
|
|
const texture_width: f32 = @floatFromInt(texture.width);
|
|
const texture_height: f32 = @floatFromInt(texture.height);
|
|
|
|
rl.gl.rlBegin(rlgl_h.RL_TRIANGLES);
|
|
defer rl.gl.rlEnd();
|
|
|
|
rl.gl.rlSetTexture(texture.id);
|
|
defer rl.gl.rlSetTexture(0);
|
|
|
|
// Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
|
|
const centers = [_]rl.Vector2{ points[8], points[9], points[10], points[11] };
|
|
const angles = [_]f32{ 180.0, 270.0, 0.0, 90.0 };
|
|
for (0..4) |k| {
|
|
var angle = angles[k];
|
|
const center = centers[k];
|
|
for (0..@intCast(segments)) |_| {
|
|
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
|
|
|
|
const rad_per_deg = std.math.rad_per_deg;
|
|
|
|
const triangle = .{
|
|
center,
|
|
.{
|
|
.x = center.x + @cos(rad_per_deg*(angle + step_length))*radius_u,
|
|
.y = center.y + @sin(rad_per_deg*(angle + step_length))*radius_v
|
|
},
|
|
.{
|
|
.x = center.x + @cos(rad_per_deg * angle)*radius_u,
|
|
.y = center.y + @sin(rad_per_deg * angle)*radius_v
|
|
}
|
|
};
|
|
|
|
inline for (triangle) |point| {
|
|
rl.gl.rlTexCoord2f(
|
|
(shape_rect.x + shape_rect.width * point.x) / texture_width,
|
|
(shape_rect.y + shape_rect.height * point.y) / texture_height
|
|
);
|
|
rl.gl.rlVertex2f(
|
|
rec.x + rec.width * point.x,
|
|
rec.y + rec.height * point.y
|
|
);
|
|
}
|
|
|
|
angle += step_length;
|
|
}
|
|
}
|
|
|
|
// [2] Upper Rectangle
|
|
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
|
|
inline for (.{ 0, 8, 9, 1, 0, 9 }) |index| {
|
|
const point = points[index];
|
|
rl.gl.rlTexCoord2f(
|
|
(shape_rect.x + shape_rect.width * point.x) / texture_width,
|
|
(shape_rect.y + shape_rect.height * point.y) / texture_height
|
|
);
|
|
rl.gl.rlVertex2f(
|
|
rec.x + rec.width * point.x,
|
|
rec.y + rec.height * point.y
|
|
);
|
|
}
|
|
|
|
// [4] Right Rectangle
|
|
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
|
|
inline for (.{ 9, 10, 3, 2, 9, 3 }) |index| {
|
|
const point = points[index];
|
|
rl.gl.rlTexCoord2f(
|
|
(shape_rect.x + shape_rect.width * point.x) / texture_width,
|
|
(shape_rect.y + shape_rect.height * point.y) / texture_height
|
|
);
|
|
rl.gl.rlVertex2f(
|
|
rec.x + rec.width * point.x,
|
|
rec.y + rec.height * point.y
|
|
);
|
|
}
|
|
|
|
// [6] Bottom Rectangle
|
|
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
|
|
inline for (.{ 11, 5, 4, 10, 11, 4 }) |index| {
|
|
const point = points[index];
|
|
rl.gl.rlTexCoord2f(
|
|
(shape_rect.x + shape_rect.width * point.x) / texture_width,
|
|
(shape_rect.y + shape_rect.height * point.y) / texture_height
|
|
);
|
|
rl.gl.rlVertex2f(
|
|
rec.x + rec.width * point.x,
|
|
rec.y + rec.height * point.y
|
|
);
|
|
}
|
|
|
|
// [8] Left Rectangle
|
|
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
|
|
inline for (.{ 7, 6, 11, 8, 7, 11 }) |index| {
|
|
const point = points[index];
|
|
rl.gl.rlTexCoord2f(
|
|
(shape_rect.x + shape_rect.width * point.x) / texture_width,
|
|
(shape_rect.y + shape_rect.height * point.y) / texture_height
|
|
);
|
|
rl.gl.rlVertex2f(
|
|
rec.x + rec.width * point.x,
|
|
rec.y + rec.height * point.y
|
|
);
|
|
}
|
|
|
|
// [9] Middle Rectangle
|
|
rl.gl.rlColor4ub(color.r, color.g, color.b, color.a);
|
|
inline for (.{ 8, 11, 10, 9, 8, 10 }) |index| {
|
|
const point = points[index];
|
|
rl.gl.rlTexCoord2f(
|
|
(shape_rect.x + shape_rect.width * point.x) / texture_width,
|
|
(shape_rect.y + shape_rect.height * point.y) / texture_height
|
|
);
|
|
rl.gl.rlVertex2f(
|
|
rec.x + rec.width * point.x,
|
|
rec.y + rec.height * point.y
|
|
);
|
|
}
|
|
}
|
|
|
|
fn drawBlurredWorld(self: *App, rect: rl.Rectangle, color: rl.Color) void {
|
|
const blur_both = self.blur_texture_both.?.texture;
|
|
|
|
const previous_texture = rl.getShapesTexture();
|
|
const previous_rect = rl.getShapesTextureRectangle();
|
|
defer rl.setShapesTexture(previous_texture, previous_rect);
|
|
|
|
const texture_height: f32 = @floatFromInt(blur_both.height);
|
|
const shape_rect = rl.Rectangle{
|
|
.x = rect.x,
|
|
.y = texture_height - rect.y,
|
|
.width = rect.width,
|
|
.height = -rect.height,
|
|
};
|
|
rl.setShapesTexture(blur_both, shape_rect);
|
|
|
|
const border = 2;
|
|
const roundness = 0.2;
|
|
drawRectangleRoundedUV(rect, roundness, color);
|
|
rl.drawRectangleRoundedLinesEx(RectUtils.shrink(rect, border - 1, border - 1), roundness, 0, border, srcery.bright_white.alpha(0.3));
|
|
}
|
|
|
|
pub fn drawWorld(self: *App) void {
|
|
rl.clearBackground(srcery.black);
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn drawWorldAndBlur(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();
|
|
|
|
self.camera.begin();
|
|
defer self.camera.end();
|
|
|
|
self.drawWorld();
|
|
}
|
|
|
|
// 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
|
|
);
|
|
}
|
|
|
|
fn drawRatelimits(self: *App, box: rl.Rectangle) void {
|
|
const Category = Api.RateLimit.Category;
|
|
const ratelimits = self.server.ratelimits;
|
|
|
|
self.drawBlurredWorld(
|
|
box,
|
|
srcery.xgray10
|
|
);
|
|
|
|
const padding = 16;
|
|
var stack = UI.Stack.init(RectUtils.shrink(box, padding, padding), .top_to_bottom);
|
|
stack.gap = 8;
|
|
|
|
inline for (.{
|
|
.{ "Account creation", Category.account_creation },
|
|
.{ "Token", Category.token },
|
|
.{ "Data", Category.data },
|
|
.{ "Actions", Category.actions },
|
|
}) |ratelimit_bar| {
|
|
const title = ratelimit_bar[0];
|
|
const category = ratelimit_bar[1];
|
|
const ratelimit = ratelimits.get(category);
|
|
|
|
const ratelimit_box = stack.next(24);
|
|
rl.drawRectangleRec(ratelimit_box, rl.Color.white);
|
|
|
|
inline for (.{
|
|
.{ ratelimit.hours , std.time.ms_per_hour, srcery.red },
|
|
.{ ratelimit.minutes, std.time.ms_per_min , srcery.blue },
|
|
.{ ratelimit.seconds, std.time.ms_per_s , srcery.green },
|
|
}) |limit_spec| {
|
|
const maybe_timespan = limit_spec[0];
|
|
const timespan_size = limit_spec[1];
|
|
const color = limit_spec[2];
|
|
|
|
if (maybe_timespan) |timespan| {
|
|
const limit_f32: f32 = @floatFromInt(timespan.limit);
|
|
const counter_f32: f32 = @floatFromInt(timespan.counter);
|
|
const timer_f32: f32 = @floatFromInt(timespan.timer_ms);
|
|
|
|
const ms_per_request = timespan_size / limit_f32;
|
|
const progress = std.math.clamp((counter_f32 - timer_f32 / ms_per_request) / limit_f32, 0, 1);
|
|
|
|
var progress_bar = ratelimit_box;
|
|
progress_bar.width *= progress;
|
|
rl.drawRectangleRec(progress_bar, color);
|
|
}
|
|
}
|
|
|
|
if (self.ui.isMouseInside(ratelimit_box)) {
|
|
// TODO: Draw more detailed info about rate limits.
|
|
// Show how many requests have occured
|
|
} else {
|
|
const title_size = self.font_face.measureText(title);
|
|
self.font_face.drawText(
|
|
title,
|
|
.{
|
|
.x = ratelimit_box.x + 8,
|
|
.y = ratelimit_box.y + title_size.y/2,
|
|
},
|
|
srcery.white
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn tick(self: *App) !void {
|
|
for (&self.server.ratelimits.values) |*ratelimit| {
|
|
ratelimit.update_timers(std.time.milliTimestamp());
|
|
}
|
|
|
|
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.drawWorldAndBlur();
|
|
|
|
self.drawRatelimits(
|
|
.{ .x = 20, .y = 20, .width = 200, .height = 200 },
|
|
);
|
|
|
|
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);
|
|
// }
|
|
}
|