mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 11:17:05 -04:00
Game controller support (#717)
* Refactor getting X positions of buttons in preparation for not moving the window when clicking the buttons * Initial support for controllers * Basic controller support * Allow runtime download of gamecontrollerdb.txt * Add new gamepad cursor asset, and use it. * Various improvements Allow saving and configuring gamepad mappings Add gamepad sensitivity, for camera only Adjust multipliers in usage of gamepad axes Prompt for downloading controller mappings, and allow configuration * Refactor startup window handling so that controller settings does not duplicate code, or have to detect if startup has finished * Clamp cursor position to window size when changing via gamepad * Add deadzone * Implement changes requested * Follow more suggestions * Use atomics * Use orelse for oldState in Gamepads.update * Implement requested changes * Fix accidental removal of whitespace on two empty lines * Fix incorrect formatting on blank line due to Vim expanding tabs to spaces * Fix build errors. * Change unusual orelse usage with better if expression * Add resettable keys * Revert "Add resettable keys" This reverts commit 902f032ec834161dcaac97cbe596788e0110e870. * Update controller support * Fix things I forgot about * Fix more things * Implement most requested changes and change button text to be more clear * Update controller support * Hopefully fix format without breaking it. * Refactor getting X positions of buttons in preparation for not moving the window when clicking the buttons * Initial support for controllers * Basic controller support * Allow runtime download of gamecontrollerdb.txt * Add new gamepad cursor asset, and use it. * Various improvements Allow saving and configuring gamepad mappings Add gamepad sensitivity, for camera only Adjust multipliers in usage of gamepad axes Prompt for downloading controller mappings, and allow configuration * Refactor startup window handling so that controller settings does not duplicate code, or have to detect if startup has finished * Clamp cursor position to window size when changing via gamepad * Add deadzone * Implement changes requested * Follow more suggestions * Use atomics * Use orelse for oldState in Gamepads.update * Implement requested changes * Fix build errors. * Change unusual orelse usage with better if expression * Add resettable keys * Revert "Add resettable keys" This reverts commit 902f032ec834161dcaac97cbe596788e0110e870. * Update controller support * Fix things I forgot about * Fix more things * Implement most requested changes and change button text to be more clear * Update controller support * Hopefully fix format without breaking it. * Implement suggestions * Update controller mapping downloading to be more automatic * Fix parameter in Window.Gamepad.ControllerMappingDownloadTask.isStillNeeded * Revert order of window opening and opening next startup window in openStartupWindow * Only download controller mappings the first moment a controller is plugged in * Open controller mapping download UI when actually downloading mappings, or at startup if they have started before GUI initialization but haven't finished yet. * Don't unnecessarily try to download controller mappings at init when it will be done once the first update happens anyways. * Implement requested changes * Remove debugging log output * Implement changes to download_controller_mappings.zig
This commit is contained in:
parent
507dc35e20
commit
e6fa7498c6
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,6 +9,8 @@ settings.json
|
||||
gui_layout.json
|
||||
settings.zig.zon
|
||||
gui_layout.zig.zon
|
||||
gamecontrollerdb.txt
|
||||
gamecontrollerdb.stamp
|
||||
|
||||
test.png
|
||||
|
||||
|
BIN
assets/cubyz/ui/gamepad_cursor.png
Normal file
BIN
assets/cubyz/ui/gamepad_cursor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 B |
44
src/game.zig
44
src/game.zig
@ -14,6 +14,7 @@ const network = @import("network.zig");
|
||||
const Connection = network.Connection;
|
||||
const ConnectionManager = network.ConnectionManager;
|
||||
const vec = @import("vec.zig");
|
||||
const Vec2f = vec.Vec2f;
|
||||
const Vec3f = vec.Vec3f;
|
||||
const Vec4f = vec.Vec4f;
|
||||
const Vec3d = vec.Vec3d;
|
||||
@ -681,7 +682,7 @@ pub fn update(deltaTime: f64) void { // MARK: update()
|
||||
const terminalVelocity = 90.0;
|
||||
const airFrictionCoefficient = gravity/terminalVelocity; // λ = a/v in equillibrium
|
||||
var move: Vec3d = .{0, 0, 0};
|
||||
if (main.renderer.mesh_storage.getBlock(@intFromFloat(@floor(Player.super.pos[0])), @intFromFloat(@floor(Player.super.pos[1])), @intFromFloat(@floor(Player.super.pos[2]))) != null) {
|
||||
if (main.renderer.mesh_storage.getBlock(@intFromFloat(@floor(Player.super.pos[0])), @intFromFloat(@floor(Player.super.pos[1])), @intFromFloat(@floor(Player.super.pos[2]))) != null) {
|
||||
var acc = Vec3d{0, 0, 0};
|
||||
if (!Player.isFlying.load(.monotonic)) {
|
||||
acc[2] = -gravity;
|
||||
@ -705,34 +706,34 @@ pub fn update(deltaTime: f64) void { // MARK: update()
|
||||
var movementDir: Vec3d = .{0, 0, 0};
|
||||
var movementSpeed: f64 = 0;
|
||||
if(main.Window.grabbed) {
|
||||
if(KeyBoard.key("forward").pressed) {
|
||||
if(KeyBoard.key("forward").value > 0.0) {
|
||||
if(KeyBoard.key("sprint").pressed) {
|
||||
if(Player.isGhost.load(.monotonic)) {
|
||||
movementSpeed = @max(movementSpeed, 128);
|
||||
movementDir += forward*@as(Vec3d, @splat(128));
|
||||
movementSpeed = @max(movementSpeed, 128)*KeyBoard.key("forward").value;
|
||||
movementDir += forward*@as(Vec3d, @splat(128*KeyBoard.key("forward").value));
|
||||
} else if(Player.isFlying.load(.monotonic)) {
|
||||
movementSpeed = @max(movementSpeed, 32);
|
||||
movementDir += forward*@as(Vec3d, @splat(32));
|
||||
movementSpeed = @max(movementSpeed, 32)*KeyBoard.key("forward").value;
|
||||
movementDir += forward*@as(Vec3d, @splat(32*KeyBoard.key("forward").value));
|
||||
} else {
|
||||
movementSpeed = @max(movementSpeed, 8);
|
||||
movementDir += forward*@as(Vec3d, @splat(8));
|
||||
movementSpeed = @max(movementSpeed, 8)*KeyBoard.key("forward").value;
|
||||
movementDir += forward*@as(Vec3d, @splat(8*KeyBoard.key("forward").value));
|
||||
}
|
||||
} else {
|
||||
movementSpeed = @max(movementSpeed, 4);
|
||||
movementDir += forward*@as(Vec3d, @splat(4));
|
||||
movementSpeed = @max(movementSpeed, 4)*KeyBoard.key("forward").value;
|
||||
movementDir += forward*@as(Vec3d, @splat(4*KeyBoard.key("forward").value));
|
||||
}
|
||||
}
|
||||
if(KeyBoard.key("backward").pressed) {
|
||||
movementSpeed = @max(movementSpeed, 4);
|
||||
movementDir += forward*@as(Vec3d, @splat(-4));
|
||||
if(KeyBoard.key("backward").value > 0.0) {
|
||||
movementSpeed = @max(movementSpeed, 4)*KeyBoard.key("backward").value;
|
||||
movementDir += forward*@as(Vec3d, @splat(-4*KeyBoard.key("backward").value));
|
||||
}
|
||||
if(KeyBoard.key("left").pressed) {
|
||||
movementSpeed = @max(movementSpeed, 4);
|
||||
movementDir += right*@as(Vec3d, @splat(4));
|
||||
if(KeyBoard.key("left").value > 0.0) {
|
||||
movementSpeed = @max(movementSpeed, 4*KeyBoard.key("left").value);
|
||||
movementDir += right*@as(Vec3d, @splat(4*KeyBoard.key("left").value));
|
||||
}
|
||||
if(KeyBoard.key("right").pressed) {
|
||||
movementSpeed = @max(movementSpeed, 4);
|
||||
movementDir += right*@as(Vec3d, @splat(-4));
|
||||
if(KeyBoard.key("right").value > 0.0) {
|
||||
movementSpeed = @max(movementSpeed, 4*KeyBoard.key("right").value);
|
||||
movementDir += right*@as(Vec3d, @splat(-4*KeyBoard.key("right").value));
|
||||
}
|
||||
if(KeyBoard.key("jump").pressed) {
|
||||
if(Player.isFlying.load(.monotonic)) {
|
||||
@ -779,6 +780,11 @@ pub fn update(deltaTime: f64) void { // MARK: update()
|
||||
const newSlot: i32 = @as(i32, @intCast(Player.selectedSlot)) -% @as(i32, @intFromFloat(main.Window.scrollOffset));
|
||||
Player.selectedSlot = @intCast(@mod(newSlot, 12));
|
||||
main.Window.scrollOffset = 0;
|
||||
const newPos = Vec2f {
|
||||
@floatCast(main.KeyBoard.key("cameraRight").value - main.KeyBoard.key("cameraLeft").value),
|
||||
@floatCast(main.KeyBoard.key("cameraDown").value - main.KeyBoard.key("cameraUp").value),
|
||||
} * @as(Vec2f, @splat(3.14 * settings.controllerSensitivity));
|
||||
main.game.camera.moveRotation(newPos[0] / 64.0, newPos[1] / 64.0);
|
||||
}
|
||||
|
||||
// This our model for movement on a single frame:
|
||||
|
@ -1,6 +1,8 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("root");
|
||||
const settings = main.settings;
|
||||
const files = main.files;
|
||||
const vec = main.vec;
|
||||
const Vec2f = vec.Vec2f;
|
||||
|
||||
@ -10,16 +12,282 @@ pub const c = @cImport ({
|
||||
});
|
||||
|
||||
var isFullscreen: bool = false;
|
||||
pub var lastUsedMouse: bool = true;
|
||||
pub var width: u31 = 1280;
|
||||
pub var height: u31 = 720;
|
||||
pub var window: *c.GLFWwindow = undefined;
|
||||
pub var grabbed: bool = false;
|
||||
pub var scrollOffset: f32 = 0;
|
||||
|
||||
pub const Gamepad = struct {
|
||||
pub var gamepadState: std.AutoHashMap(c_int, *c.GLFWgamepadstate) = undefined;
|
||||
pub var controllerMappingsDownloaded: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);
|
||||
var controllerConnectedPreviously: bool = false;
|
||||
fn applyDeadzone(value: f32) f32 {
|
||||
const minValue = settings.controllerAxisDeadzone;
|
||||
const maxRange = 1.0 - minValue;
|
||||
return (value * maxRange) + minValue;
|
||||
}
|
||||
pub fn update(delta: f64) void {
|
||||
if (!controllerConnectedPreviously and isControllerConnected()) {
|
||||
controllerConnectedPreviously = true;
|
||||
downloadControllerMappings();
|
||||
}
|
||||
var jid: c_int = 0;
|
||||
while (jid < c.GLFW_JOYSTICK_LAST) : (jid += 1) {
|
||||
// Can't initialize with the state, or it will become a reference.
|
||||
var oldState: c.GLFWgamepadstate = std.mem.zeroes(c.GLFWgamepadstate);
|
||||
if (gamepadState.get(jid)) |v| {
|
||||
oldState = v.*;
|
||||
}
|
||||
const joystickFound = c.glfwJoystickPresent(jid) != 0 and c.glfwJoystickIsGamepad(jid) != 0;
|
||||
if (joystickFound) {
|
||||
if (!gamepadState.contains(jid)) {
|
||||
gamepadState.put(jid, main.globalAllocator.create(c.GLFWgamepadstate)) catch unreachable;
|
||||
}
|
||||
_ = c.glfwGetGamepadState(jid, gamepadState.get(jid).?);
|
||||
} else {
|
||||
if (gamepadState.contains(jid)) {
|
||||
main.globalAllocator.destroy(gamepadState.get(jid).?);
|
||||
_ = gamepadState.remove(jid);
|
||||
}
|
||||
}
|
||||
const newState: c.GLFWgamepadstate = if (gamepadState.get(jid)) |v| v.* else std.mem.zeroes(c.GLFWgamepadstate);
|
||||
if (nextGamepadListener != null) {
|
||||
for (0..c.GLFW_GAMEPAD_BUTTON_LAST) |btn| {
|
||||
if ((newState.buttons[btn] == 0) and (oldState.buttons[btn] != 0)) {
|
||||
nextGamepadListener.?(null, @intCast(btn));
|
||||
nextGamepadListener = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextGamepadListener != null) {
|
||||
for (0..c.GLFW_GAMEPAD_AXIS_LAST) |axis| {
|
||||
const newAxis = applyDeadzone(newState.axes[axis]);
|
||||
const oldAxis = applyDeadzone(oldState.axes[axis]);
|
||||
if (newAxis != 0 and oldAxis == 0) {
|
||||
nextGamepadListener.?(.{.axis = @intCast(axis), .positive = newState.axes[axis] > 0}, -1);
|
||||
nextGamepadListener = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(&main.KeyBoard.keys) |*key| {
|
||||
if(key.gamepadAxis == null) {
|
||||
if(key.gamepadButton >= 0) {
|
||||
const oldPressed = oldState.buttons[@intCast(key.gamepadButton)] != 0;
|
||||
const newPressed = newState.buttons[@intCast(key.gamepadButton)] != 0;
|
||||
if(oldPressed != newPressed) {
|
||||
key.pressed = newPressed;
|
||||
key.value = if(newPressed) 1.0 else 0.0;
|
||||
if(key.pressed) {
|
||||
if(key.pressAction) |pressAction| {
|
||||
pressAction();
|
||||
}
|
||||
} else {
|
||||
if(key.releaseAction) |releaseAction| {
|
||||
releaseAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const axis = key.gamepadAxis.?.axis;
|
||||
const positive = key.gamepadAxis.?.positive;
|
||||
var newAxis = applyDeadzone(newState.axes[@intCast(axis)]);
|
||||
var oldAxis = applyDeadzone(oldState.axes[@intCast(axis)]);
|
||||
if(!positive) {
|
||||
newAxis *= -1.0;
|
||||
oldAxis *= -1.0;
|
||||
}
|
||||
newAxis = @max(newAxis, 0.0);
|
||||
oldAxis = @max(oldAxis, 0.0);
|
||||
const oldPressed = oldAxis > 0.5;
|
||||
const newPressed = newAxis > 0.5;
|
||||
if (oldPressed != newPressed) {
|
||||
key.pressed = newPressed;
|
||||
if (newPressed) {
|
||||
if (key.pressAction) |pressAction| {
|
||||
pressAction();
|
||||
}
|
||||
} else {
|
||||
if (key.releaseAction) |releaseAction| {
|
||||
releaseAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newAxis != oldAxis) {
|
||||
key.value = newAxis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!grabbed) {
|
||||
const x = main.KeyBoard.key("uiRight").value - main.KeyBoard.key("uiLeft").value;
|
||||
const y = main.KeyBoard.key("uiDown").value - main.KeyBoard.key("uiUp").value;
|
||||
if (x != 0 or y != 0) {
|
||||
lastUsedMouse = false;
|
||||
GLFWCallbacks.currentPos[0] += @floatCast(x * delta * 256);
|
||||
GLFWCallbacks.currentPos[1] += @floatCast(y * delta * 256);
|
||||
const winSize = getWindowSize();
|
||||
GLFWCallbacks.currentPos[0] = std.math.clamp(GLFWCallbacks.currentPos[0], 0, winSize[0]);
|
||||
GLFWCallbacks.currentPos[1] = std.math.clamp(GLFWCallbacks.currentPos[1], 0, winSize[1]);
|
||||
}
|
||||
}
|
||||
scrollOffset += @floatCast((main.KeyBoard.key("scrollUp").value - main.KeyBoard.key("scrollDown").value) * delta * 4);
|
||||
setCursorVisible(!grabbed and lastUsedMouse);
|
||||
}
|
||||
pub fn isControllerConnected() bool {
|
||||
return gamepadState.count() > 0;
|
||||
}
|
||||
pub fn wereControllerMappingsDownloaded() bool {
|
||||
return controllerMappingsDownloaded.load(std.builtin.AtomicOrder.acquire);
|
||||
}
|
||||
const ControllerMappingDownloadTask = struct { // MARK: ControllerMappingDownloadTask
|
||||
curTimestamp: i128,
|
||||
var running = std.atomic.Value(bool).init(false);
|
||||
const vtable = main.utils.ThreadPool.VTable{
|
||||
.getPriority = @ptrCast(&getPriority),
|
||||
.isStillNeeded = @ptrCast(&isStillNeeded),
|
||||
.run = @ptrCast(&run),
|
||||
.clean = @ptrCast(&clean),
|
||||
};
|
||||
|
||||
pub fn schedule(curTimestamp: i128) void {
|
||||
|
||||
if (running.swap(true, .monotonic)) {
|
||||
std.log.warn("Attempt to schedule a duplicate controller mapping download task!", .{});
|
||||
return; // Controller mappings are already downloading.
|
||||
}
|
||||
controllerMappingsDownloaded.store(false, .monotonic);
|
||||
const task = main.globalAllocator.create(ControllerMappingDownloadTask);
|
||||
task.* = ControllerMappingDownloadTask {
|
||||
.curTimestamp = curTimestamp,
|
||||
};
|
||||
main.threadPool.addTask(task, &vtable);
|
||||
// Don't attempt to open the window before the GUI is initialized.
|
||||
main.gui.openWindow("download_controller_mappings");
|
||||
}
|
||||
|
||||
pub fn getPriority(_: *ControllerMappingDownloadTask) f32 {
|
||||
return std.math.inf(f32);
|
||||
}
|
||||
|
||||
pub fn isStillNeeded(_: *ControllerMappingDownloadTask) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn run(self: *ControllerMappingDownloadTask) void {
|
||||
std.log.info("Starting controller mapping download...", .{});
|
||||
defer self.clean();
|
||||
var client: std.http.Client = .{.allocator = main.stackAllocator.allocator};
|
||||
defer client.deinit();
|
||||
var list = std.ArrayList(u8).init(main.stackAllocator.allocator);
|
||||
defer list.deinit();
|
||||
defer controllerMappingsDownloaded.store(true, std.builtin.AtomicOrder.release);
|
||||
const fetchResult = client.fetch(.{
|
||||
.method = .GET,
|
||||
.location = .{.url = "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/master/gamecontrollerdb.txt"},
|
||||
.response_storage = .{ .dynamic = &list }
|
||||
}) catch |err| {
|
||||
std.log.err("Failed to download controller mappings: {s}", .{@errorName(err)});
|
||||
return;
|
||||
};
|
||||
if (fetchResult.status != .ok) {
|
||||
std.log.err("Failed to download controller mappings: HTTP error {d}", .{@intFromEnum(fetchResult.status)});
|
||||
return;
|
||||
}
|
||||
files.write("./gamecontrollerdb.txt", list.items) catch |err| {
|
||||
std.log.err("Failed to write controller mappings: {s}", .{@errorName(err)});
|
||||
return;
|
||||
};
|
||||
const timeStampStr = std.fmt.allocPrint(main.stackAllocator.allocator, "{x}", .{self.*.curTimestamp}) catch unreachable;
|
||||
defer main.stackAllocator.free(timeStampStr);
|
||||
files.write("gamecontrollerdb.stamp", timeStampStr) catch |err| {
|
||||
std.log.err("Failed to write controller mappings: {s}", .{@errorName(err)});
|
||||
return;
|
||||
};
|
||||
std.log.info("Controller mappings downloaded succesfully!", .{});
|
||||
}
|
||||
|
||||
pub fn clean(self: *ControllerMappingDownloadTask) void {
|
||||
main.globalAllocator.destroy(self);
|
||||
updateControllerMappings();
|
||||
running.store(false, .monotonic);
|
||||
}
|
||||
};
|
||||
pub fn downloadControllerMappings() void {
|
||||
var needsDownload: bool = false;
|
||||
const curTimestamp = std.time.nanoTimestamp();
|
||||
const timestamp: i128 = blk: {
|
||||
const stamp = files.read(main.stackAllocator, "./gamecontrollerdb.stamp") catch break :blk 0;
|
||||
defer main.stackAllocator.free(stamp);
|
||||
break :blk std.fmt.parseInt(i128, stamp, 16) catch 0;
|
||||
};
|
||||
const delta = curTimestamp-%timestamp;
|
||||
needsDownload = delta >= 7*std.time.ns_per_day;
|
||||
|
||||
for (0..c.GLFW_JOYSTICK_LAST) |jsid| {
|
||||
if ((c.glfwJoystickPresent(@intCast(jsid)) != 0) and (c.glfwJoystickIsGamepad(@intCast(jsid)) == 0)) {
|
||||
needsDownload = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std.log.info("Game controller mappings {s}need downloading.", .{if (needsDownload) "" else "do not "});
|
||||
if (needsDownload) {
|
||||
ControllerMappingDownloadTask.schedule(curTimestamp);
|
||||
} else {
|
||||
controllerMappingsDownloaded.store(true, .monotonic);
|
||||
updateControllerMappings();
|
||||
}
|
||||
}
|
||||
pub fn updateControllerMappings() void {
|
||||
std.log.info("Updating controller mappings in-memory...", .{});
|
||||
var _envMap = std.process.getEnvMap(main.stackAllocator.allocator) catch null;
|
||||
if (_envMap) |*envMap| {
|
||||
defer envMap.deinit();
|
||||
if (envMap.get("SDL_GAMECONTROLLERCONFIG")) |controller_config_env| {
|
||||
_ = c.glfwUpdateGamepadMappings(@ptrCast(controller_config_env));
|
||||
return;
|
||||
}
|
||||
}
|
||||
const data = main.files.read(main.stackAllocator, "./gamecontrollerdb.txt") catch |err| {
|
||||
if (@TypeOf(err) == std.fs.File.OpenError and err == std.fs.File.OpenError.FileNotFound) {
|
||||
return; // Ignore not finding mappings.
|
||||
}
|
||||
std.log.err("Error opening gamepad mappings file: {s}", .{@errorName(err)});
|
||||
return;
|
||||
};
|
||||
var newData = main.stackAllocator.realloc(data, data.len + 1);
|
||||
defer main.stackAllocator.free(newData);
|
||||
newData[data.len - 1] = 0;
|
||||
_ = c.glfwUpdateGamepadMappings(newData.ptr);
|
||||
std.log.info("Controller mappings updated!", .{});
|
||||
}
|
||||
|
||||
pub fn init() void {
|
||||
gamepadState = .init(main.globalAllocator.allocator);
|
||||
}
|
||||
pub fn deinit() void {
|
||||
var iter = gamepadState.valueIterator();
|
||||
while (iter.next()) |value| {
|
||||
main.globalAllocator.destroy(value.*);
|
||||
}
|
||||
gamepadState.deinit();
|
||||
}
|
||||
};
|
||||
pub const GamepadAxis = struct {
|
||||
axis: c_int,
|
||||
positive: bool = true
|
||||
};
|
||||
pub const Key = struct { // MARK: Key
|
||||
name: []const u8,
|
||||
pressed: bool = false,
|
||||
value: f32 = 0.0,
|
||||
key: c_int = c.GLFW_KEY_UNKNOWN,
|
||||
gamepadAxis: ?GamepadAxis = null,
|
||||
gamepadButton: c_int = -1,
|
||||
mouseButton: c_int = -1,
|
||||
scancode: c_int = 0,
|
||||
releaseAction: ?*const fn() void = null,
|
||||
@ -34,6 +302,40 @@ pub const Key = struct { // MARK: Key
|
||||
capsLock: bool = false,
|
||||
numLock: bool = false,
|
||||
};
|
||||
pub fn getGamepadName(self: Key) []const u8 {
|
||||
if(self.gamepadAxis != null) {
|
||||
const positive = self.gamepadAxis.?.positive;
|
||||
return switch(self.gamepadAxis.?.axis) {
|
||||
c.GLFW_GAMEPAD_AXIS_LEFT_X => if(positive) "Left stick right" else "Left stick left",
|
||||
c.GLFW_GAMEPAD_AXIS_RIGHT_X => if(positive) "Right stick right" else "Right stick left",
|
||||
c.GLFW_GAMEPAD_AXIS_LEFT_Y => if(positive) "Left stick down" else "Left stick up",
|
||||
c.GLFW_GAMEPAD_AXIS_RIGHT_Y => if(positive) "Right stick down" else "Right stick up",
|
||||
c.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER => if(positive) "Left trigger" else "Left trigger (Negative)",
|
||||
c.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER => if(positive) "Right trigger" else "Right trigger (Negative)",
|
||||
else => "(Invalid axis)"
|
||||
};
|
||||
} else {
|
||||
return switch(self.gamepadButton) {
|
||||
c.GLFW_GAMEPAD_BUTTON_A => "A",
|
||||
c.GLFW_GAMEPAD_BUTTON_B => "B",
|
||||
c.GLFW_GAMEPAD_BUTTON_X => "X",
|
||||
c.GLFW_GAMEPAD_BUTTON_Y => "Y",
|
||||
c.GLFW_GAMEPAD_BUTTON_BACK => "Back",
|
||||
c.GLFW_GAMEPAD_BUTTON_DPAD_DOWN => "Down",
|
||||
c.GLFW_GAMEPAD_BUTTON_DPAD_LEFT => "Left",
|
||||
c.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT => "Right",
|
||||
c.GLFW_GAMEPAD_BUTTON_DPAD_UP => "Up",
|
||||
c.GLFW_GAMEPAD_BUTTON_GUIDE => "Guide",
|
||||
c.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER => "Left bumper",
|
||||
c.GLFW_GAMEPAD_BUTTON_LEFT_THUMB => "Left stick press",
|
||||
c.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER => "Right bumper",
|
||||
c.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB => "Right stick press",
|
||||
c.GLFW_GAMEPAD_BUTTON_START => "Start",
|
||||
-1 => "(Unbound)",
|
||||
else => "(Unrecognized button)"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getName(self: Key) []const u8 {
|
||||
if(self.mouseButton == -1) {
|
||||
@ -96,6 +398,7 @@ pub const Key = struct { // MARK: Key
|
||||
c.GLFW_KEY_RIGHT_ALT => "Right Alt",
|
||||
c.GLFW_KEY_RIGHT_SUPER => "Right Super",
|
||||
c.GLFW_KEY_MENU => "Menu",
|
||||
c.GLFW_KEY_UNKNOWN => "(Unbound)",
|
||||
else => "Unknown Key",
|
||||
};
|
||||
} else {
|
||||
@ -121,6 +424,7 @@ pub const GLFWCallbacks = struct { // MARK: GLFWCallbacks
|
||||
if(glfw_key == key.key) {
|
||||
if(glfw_key != c.GLFW_KEY_UNKNOWN or scancode == key.scancode) {
|
||||
key.pressed = true;
|
||||
key.value = 1.0;
|
||||
if(key.pressAction) |pressAction| {
|
||||
pressAction();
|
||||
}
|
||||
@ -139,6 +443,7 @@ pub const GLFWCallbacks = struct { // MARK: GLFWCallbacks
|
||||
if(glfw_key == key.key) {
|
||||
if(glfw_key != c.GLFW_KEY_UNKNOWN or scancode == key.scancode) {
|
||||
key.pressed = false;
|
||||
key.value = 0.0;
|
||||
if(key.releaseAction) |releaseAction| {
|
||||
releaseAction();
|
||||
}
|
||||
@ -195,6 +500,7 @@ pub const GLFWCallbacks = struct { // MARK: GLFWCallbacks
|
||||
}
|
||||
ignoreDataAfterRecentGrab = false;
|
||||
currentPos = newPos;
|
||||
lastUsedMouse = true;
|
||||
}
|
||||
fn mouseButton(_: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void {
|
||||
_ = mods;
|
||||
@ -202,6 +508,7 @@ pub const GLFWCallbacks = struct { // MARK: GLFWCallbacks
|
||||
for(&main.KeyBoard.keys) |*key| {
|
||||
if(button == key.mouseButton) {
|
||||
key.pressed = true;
|
||||
key.value = 1.0;
|
||||
if(key.pressAction) |pressAction| {
|
||||
pressAction();
|
||||
}
|
||||
@ -215,6 +522,7 @@ pub const GLFWCallbacks = struct { // MARK: GLFWCallbacks
|
||||
for(&main.KeyBoard.keys) |*key| {
|
||||
if(button == key.mouseButton) {
|
||||
key.pressed = false;
|
||||
key.value = 0.0;
|
||||
if(key.releaseAction) |releaseAction| {
|
||||
releaseAction();
|
||||
}
|
||||
@ -264,22 +572,35 @@ pub fn setNextKeypressListener(listener: ?*const fn(c_int, c_int, c_int) void) !
|
||||
if(nextKeypressListener != null) return error.AlreadyUsed;
|
||||
nextKeypressListener = listener;
|
||||
}
|
||||
var nextGamepadListener: ?*const fn(?GamepadAxis, c_int) void = null;
|
||||
pub fn setNextGamepadListener(listener: ?*const fn(?GamepadAxis, c_int) void) !void {
|
||||
if (nextGamepadListener != null) return error.AlreadyUsed;
|
||||
nextGamepadListener = listener;
|
||||
}
|
||||
|
||||
fn updateCursor() void {
|
||||
if(grabbed) {
|
||||
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_DISABLED);
|
||||
// Behavior seems much more intended without this line on MacOS.
|
||||
// Perhaps this is an inconsistency in GLFW due to its fresh XQuartz support?
|
||||
if(@import("builtin").target.os.tag != .macos) {
|
||||
if (c.glfwRawMouseMotionSupported() != 0)
|
||||
c.glfwSetInputMode(window, c.GLFW_RAW_MOUSE_MOTION, c.GLFW_TRUE);
|
||||
}
|
||||
GLFWCallbacks.ignoreDataAfterRecentGrab = true;
|
||||
} else {
|
||||
if (cursorVisible) {
|
||||
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_NORMAL);
|
||||
} else {
|
||||
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_HIDDEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setMouseGrabbed(grab: bool) void {
|
||||
if(grabbed != grab) {
|
||||
if(grab) {
|
||||
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_DISABLED);
|
||||
// Behavior seems much more intended without this line on MacOS.
|
||||
// Perhaps this is an inconsistency in GLFW due to its fresh XQuartz support?
|
||||
if(@import("builtin").target.os.tag != .macos) {
|
||||
if (c.glfwRawMouseMotionSupported() != 0)
|
||||
c.glfwSetInputMode(window, c.GLFW_RAW_MOUSE_MOTION, c.GLFW_TRUE);
|
||||
}
|
||||
GLFWCallbacks.ignoreDataAfterRecentGrab = true;
|
||||
} else {
|
||||
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_NORMAL);
|
||||
}
|
||||
grabbed = grab;
|
||||
updateCursor();
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,16 +670,26 @@ pub fn init() void { // MARK: init()
|
||||
c.glEnable(c.GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
c.glDebugMessageCallback(GLFWCallbacks.glDebugOutput, null);
|
||||
c.glDebugMessageControl(c.GL_DONT_CARE, c.GL_DONT_CARE, c.GL_DONT_CARE, 0, null, c.GL_TRUE);
|
||||
Gamepad.init();
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
Gamepad.deinit();
|
||||
c.glfwDestroyWindow(window);
|
||||
c.glfwTerminate();
|
||||
}
|
||||
var cursorVisible: bool = true;
|
||||
fn setCursorVisible(visible: bool) void {
|
||||
if (cursorVisible != visible) {
|
||||
cursorVisible = visible;
|
||||
updateCursor();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handleEvents() void {
|
||||
pub fn handleEvents(deltaTime: f64) void {
|
||||
scrollOffset = 0;
|
||||
c.glfwPollEvents();
|
||||
Gamepad.update(deltaTime);
|
||||
}
|
||||
|
||||
var oldX: c_int = 0;
|
||||
|
28
src/gui/gamepad_cursor.zig
Normal file
28
src/gui/gamepad_cursor.zig
Normal file
@ -0,0 +1,28 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("root");
|
||||
const graphics = main.graphics;
|
||||
const Texture = graphics.Texture;
|
||||
const Vec2f = main.vec.Vec2f;
|
||||
|
||||
const gui = @import("gui.zig");
|
||||
|
||||
const size: f32 = 16;
|
||||
|
||||
var texture: Texture = undefined;
|
||||
|
||||
pub fn init() void {
|
||||
texture = Texture.initFromFile("assets/cubyz/ui/gamepad_cursor.png");
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
texture.deinit();
|
||||
}
|
||||
|
||||
pub fn render() void {
|
||||
if (main.Window.lastUsedMouse or main.Window.grabbed) return;
|
||||
texture.bindTo(0);
|
||||
graphics.draw.setColor(0xffffffff);
|
||||
const mousePos = main.Window.getMousePosition();
|
||||
graphics.draw.boundImage(@as(Vec2f, @splat(-size/2.0)) + (mousePos/@as(Vec2f, @splat(gui.scale))), .{size, size});
|
||||
}
|
@ -22,6 +22,7 @@ pub const GuiComponent = @import("gui_component.zig").GuiComponent;
|
||||
pub const GuiWindow = @import("GuiWindow.zig");
|
||||
|
||||
pub const windowlist = @import("windows/_windowlist.zig");
|
||||
const GamepadCursor = @import("gamepad_cursor.zig");
|
||||
|
||||
var windowList: List(*GuiWindow) = undefined;
|
||||
var hudWindows: List(*GuiWindow) = undefined;
|
||||
@ -158,10 +159,12 @@ pub fn init() void { // MARK: init()
|
||||
TextInput.__init();
|
||||
load();
|
||||
inventory.init();
|
||||
GamepadCursor.init();
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
save();
|
||||
GamepadCursor.deinit();
|
||||
windowList.deinit();
|
||||
hudWindows.deinit();
|
||||
for(openWindows.items) |window| {
|
||||
@ -220,7 +223,7 @@ pub fn save() void { // MARK: save()
|
||||
windowZon.put("scale", window.scale);
|
||||
guiZon.put(window.id, windowZon);
|
||||
}
|
||||
|
||||
|
||||
main.files.writeZon("gui_layout.zig.zon", guiZon) catch |err| {
|
||||
std.log.err("Could not write gui_layout.zig.zon: {s}", .{@errorName(err)});
|
||||
};
|
||||
@ -561,6 +564,9 @@ pub fn updateAndRenderGui() void {
|
||||
}
|
||||
inventory.render(mousePos);
|
||||
}
|
||||
const oldScale = draw.setScale(scale);
|
||||
defer draw.restoreScale(oldScale);
|
||||
GamepadCursor.render();
|
||||
}
|
||||
|
||||
pub fn toggleGameMenu() void {
|
||||
@ -691,4 +697,4 @@ pub const inventory = struct { // MARK: inventory
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ pub const debug_network = @import("debug_network.zig");
|
||||
pub const debug_network_advanced = @import("debug_network_advanced.zig");
|
||||
pub const debug = @import("debug.zig");
|
||||
pub const delete_world_confirmation = @import("delete_world_confirmation.zig");
|
||||
pub const download_controller_mappings = @import("download_controller_mappings.zig");
|
||||
pub const gpu_performance_measuring = @import("gpu_performance_measuring.zig");
|
||||
pub const graphics = @import("graphics.zig");
|
||||
pub const healthbar = @import("healthbar.zig");
|
||||
|
@ -2,7 +2,7 @@ const std = @import("std");
|
||||
|
||||
const main = @import("root");
|
||||
const Vec2f = main.vec.Vec2f;
|
||||
|
||||
const c = main.Window.c;
|
||||
const gui = @import("../gui.zig");
|
||||
const GuiComponent = gui.GuiComponent;
|
||||
const GuiWindow = gui.GuiWindow;
|
||||
@ -13,19 +13,18 @@ const VerticalList = @import("../components/VerticalList.zig");
|
||||
const ContinuousSlider = @import("../components/ContinuousSlider.zig");
|
||||
|
||||
pub var window = GuiWindow {
|
||||
.contentSize = Vec2f{128, 256},
|
||||
.contentSize = Vec2f{128, 192},
|
||||
};
|
||||
|
||||
const padding: f32 = 8;
|
||||
var selectedKey: ?*main.Window.Key = null;
|
||||
var editingKeyboard: bool = true;
|
||||
var needsUpdate: bool = false;
|
||||
|
||||
fn function(keyPtr: usize) void {
|
||||
fn keyFunction(keyPtr: usize) void {
|
||||
main.Window.setNextKeypressListener(&keypressListener) catch return;
|
||||
selectedKey = @ptrFromInt(keyPtr);
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
fn keypressListener(key: c_int, mouseButton: c_int, scancode: c_int) void {
|
||||
selectedKey.?.key = key;
|
||||
selectedKey.?.mouseButton = mouseButton;
|
||||
@ -35,28 +34,75 @@ fn keypressListener(key: c_int, mouseButton: c_int, scancode: c_int) void {
|
||||
main.settings.save();
|
||||
}
|
||||
|
||||
fn gamepadFunction(keyPtr: usize) void {
|
||||
main.Window.setNextGamepadListener(&gamepadListener) catch return;
|
||||
selectedKey = @ptrFromInt(keyPtr);
|
||||
needsUpdate = true;
|
||||
}
|
||||
fn gamepadListener(axis: ?main.Window.GamepadAxis, btn: c_int) void {
|
||||
selectedKey.?.gamepadAxis = axis;
|
||||
selectedKey.?.gamepadButton = btn;
|
||||
selectedKey = null;
|
||||
needsUpdate = true;
|
||||
main.settings.save();
|
||||
}
|
||||
fn updateSensitivity(sensitivity: f32) void {
|
||||
main.settings.mouseSensitivity = sensitivity;
|
||||
if (editingKeyboard) {
|
||||
main.settings.mouseSensitivity = sensitivity;
|
||||
} else {
|
||||
main.settings.controllerSensitivity = sensitivity;
|
||||
}
|
||||
main.settings.save();
|
||||
}
|
||||
|
||||
fn updateDeadzone(deadzone: f32) void {
|
||||
main.settings.controllerAxisDeadzone = deadzone;
|
||||
}
|
||||
|
||||
fn deadzoneFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
|
||||
return std.fmt.allocPrint(allocator.allocator, "Deadzone: {d:.0}%", .{value*100}) catch unreachable;
|
||||
}
|
||||
|
||||
fn sensitivityFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
|
||||
return std.fmt.allocPrint(allocator.allocator, "Mouse Sensitivity: {d:.0}%", .{value*100}) catch unreachable;
|
||||
return std.fmt.allocPrint(allocator.allocator, "{s} Sensitivity: {d:.0}%", .{if (editingKeyboard) "Mouse" else "Controller", value*100}) catch unreachable;
|
||||
}
|
||||
|
||||
fn toggleKeyboard(_: usize) void {
|
||||
editingKeyboard = !editingKeyboard;
|
||||
needsUpdate = true;
|
||||
}
|
||||
fn unbindKey(keyPtr: usize) void {
|
||||
var key: ?*main.Window.Key = @ptrFromInt(keyPtr);
|
||||
if (editingKeyboard) {
|
||||
key.?.key = c.GLFW_KEY_UNKNOWN;
|
||||
key.?.mouseButton = -1;
|
||||
key.?.scancode = 0;
|
||||
} else {
|
||||
key.?.gamepadAxis = null;
|
||||
key.?.gamepadButton = -1;
|
||||
}
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
pub fn onOpen() void {
|
||||
const list = VerticalList.init(.{padding, 16 + padding}, 300, 8);
|
||||
list.add(ContinuousSlider.init(.{0, 0}, 256, 0, 5, main.settings.mouseSensitivity, &updateSensitivity, &sensitivityFormatter));
|
||||
const list = VerticalList.init(.{padding, 16 + padding}, 364, 8);
|
||||
list.add(Button.initText(.{0, 0}, 128, if (editingKeyboard) "Gamepad" else "Keyboard", .{.callback = &toggleKeyboard}));
|
||||
list.add(ContinuousSlider.init(.{0, 0}, 256, 0, 5, if (editingKeyboard) main.settings.mouseSensitivity else main.settings.controllerSensitivity, &updateSensitivity, &sensitivityFormatter));
|
||||
if (!editingKeyboard) {
|
||||
list.add(ContinuousSlider.init(.{0, 0}, 256, 0, 5, main.settings.controllerAxisDeadzone, &updateDeadzone, &deadzoneFormatter));
|
||||
}
|
||||
for(&main.KeyBoard.keys) |*key| {
|
||||
const label = Label.init(.{0, 0}, 128, key.name, .left);
|
||||
const button = if(key == selectedKey) (
|
||||
Button.initText(.{16, 0}, 128, "...", .{})
|
||||
) else (
|
||||
Button.initText(.{16, 0}, 128, key.getName(), .{.callback = &function, .arg = @intFromPtr(key)})
|
||||
Button.initText(.{16, 0}, 128, if (editingKeyboard) key.getName() else key.getGamepadName(), .{.callback = if (editingKeyboard) &keyFunction else &gamepadFunction, .arg = @intFromPtr(key)})
|
||||
);
|
||||
const unbindBtn = Button.initText(.{16, 0}, 64, "Unbind", .{.callback = &unbindKey, .arg = @intFromPtr(key)});
|
||||
const row = HorizontalList.init();
|
||||
row.add(label);
|
||||
row.add(button);
|
||||
row.add(unbindBtn);
|
||||
row.finish(.{0, 0}, .center);
|
||||
list.add(row);
|
||||
}
|
||||
@ -80,4 +126,4 @@ pub fn render() void {
|
||||
onOpen();
|
||||
window.rootComponent.?.verticalList.scrollBar.currentState = oldScroll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
src/gui/windows/download_controller_mappings.zig
Normal file
44
src/gui/windows/download_controller_mappings.zig
Normal file
@ -0,0 +1,44 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("root");
|
||||
const files = main.files;
|
||||
const settings = main.settings;
|
||||
const Vec2f = main.vec.Vec2f;
|
||||
|
||||
const gui = @import("../gui.zig");
|
||||
const GuiComponent = gui.GuiComponent;
|
||||
const GuiWindow = gui.GuiWindow;
|
||||
const Button = @import("../components/Button.zig");
|
||||
const CheckBox = @import("../components/CheckBox.zig");
|
||||
const Label = @import("../components/Label.zig");
|
||||
const VerticalList = @import("../components/VerticalList.zig");
|
||||
const HorizontalList = @import("../components/HorizontalList.zig");
|
||||
|
||||
pub var window = GuiWindow {
|
||||
.contentSize = Vec2f{128, 64},
|
||||
.hasBackground = true,
|
||||
.closeable = false,
|
||||
.relativePosition = .{
|
||||
.{ .attachedToFrame = .{.selfAttachmentPoint = .upper, .otherAttachmentPoint = .upper} },
|
||||
.{ .attachedToFrame = .{.selfAttachmentPoint = .upper, .otherAttachmentPoint = .upper} },
|
||||
},
|
||||
};
|
||||
|
||||
const padding: f32 = 8;
|
||||
pub fn update() void {
|
||||
if (main.Window.Gamepad.wereControllerMappingsDownloaded()) {
|
||||
gui.closeWindowFromRef(&window);
|
||||
}
|
||||
}
|
||||
pub fn onOpen() void {
|
||||
const label = Label.init(.{padding, 16 + padding}, 128, "Downloading controller mappings...", .center);
|
||||
window.rootComponent = label.toComponent();
|
||||
window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding));
|
||||
gui.updateWindowPositions();
|
||||
}
|
||||
|
||||
pub fn onClose() void {
|
||||
if(window.rootComponent) |*comp| {
|
||||
comp.deinit();
|
||||
}
|
||||
}
|
@ -32,4 +32,4 @@ pub fn onClose() void {
|
||||
if(window.rootComponent) |*comp| {
|
||||
comp.deinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
60
src/main.zig
60
src/main.zig
@ -298,6 +298,13 @@ fn toggleNetworkDebugOverlay() void {
|
||||
fn toggleAdvancedNetworkDebugOverlay() void {
|
||||
gui.toggleWindow("debug_network_advanced");
|
||||
}
|
||||
fn cycleHotbarSlot(i: comptime_int) *const fn() void {
|
||||
return &struct {
|
||||
fn set() void {
|
||||
game.Player.selectedSlot = @intCast(@mod(@as(i33, game.Player.selectedSlot) + i, 12));
|
||||
}
|
||||
}.set;
|
||||
}
|
||||
fn setHotbarSlot(i: comptime_int) *const fn() void {
|
||||
return &struct {
|
||||
fn set() void {
|
||||
@ -310,32 +317,39 @@ pub const KeyBoard = struct { // MARK: KeyBoard
|
||||
const c = Window.c;
|
||||
pub var keys = [_]Window.Key {
|
||||
// Gameplay:
|
||||
.{.name = "forward", .key = c.GLFW_KEY_W},
|
||||
.{.name = "left", .key = c.GLFW_KEY_A},
|
||||
.{.name = "backward", .key = c.GLFW_KEY_S},
|
||||
.{.name = "right", .key = c.GLFW_KEY_D},
|
||||
.{.name = "sprint", .key = c.GLFW_KEY_LEFT_CONTROL},
|
||||
.{.name = "jump", .key = c.GLFW_KEY_SPACE},
|
||||
.{.name = "fly", .key = c.GLFW_KEY_F, .pressAction = &game.flyToggle},
|
||||
.{.name = "forward", .key = c.GLFW_KEY_W, .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_Y, .positive = false}},
|
||||
.{.name = "left", .key = c.GLFW_KEY_A, .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_X, .positive = false}},
|
||||
.{.name = "backward", .key = c.GLFW_KEY_S, .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_Y, .positive = true}},
|
||||
.{.name = "right", .key = c.GLFW_KEY_D, .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_X, .positive = true}},
|
||||
.{.name = "sprint", .key = c.GLFW_KEY_LEFT_CONTROL, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_LEFT_THUMB},
|
||||
.{.name = "jump", .key = c.GLFW_KEY_SPACE, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_A},
|
||||
.{.name = "fly", .key = c.GLFW_KEY_F, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_DPAD_DOWN, .pressAction = &game.flyToggle},
|
||||
.{.name = "ghost", .key = c.GLFW_KEY_G, .pressAction = &game.ghostToggle},
|
||||
.{.name = "hyperSpeed", .key = c.GLFW_KEY_H, .pressAction = &game.hyperSpeedToggle},
|
||||
.{.name = "gamemode", .key = c.GLFW_KEY_M, .releaseAction = &game.gamemodeToggle},
|
||||
.{.name = "fall", .key = c.GLFW_KEY_LEFT_SHIFT},
|
||||
.{.name = "shift", .key = c.GLFW_KEY_LEFT_SHIFT},
|
||||
.{.name = "fall", .key = c.GLFW_KEY_LEFT_SHIFT, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB},
|
||||
.{.name = "shift", .key = c.GLFW_KEY_LEFT_SHIFT, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB},
|
||||
.{.name = "fullscreen", .key = c.GLFW_KEY_F11, .releaseAction = &Window.toggleFullscreen},
|
||||
.{.name = "placeBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT, .pressAction = &game.pressPlace, .releaseAction = &game.releasePlace},
|
||||
.{.name = "breakBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .pressAction = &game.pressBreak, .releaseAction = &game.releaseBreak},
|
||||
.{.name = "acquireSelectedBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_MIDDLE, .pressAction = &game.pressAcquireSelectedBlock},
|
||||
.{.name = "placeBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT, .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER}, .pressAction = &game.pressPlace, .releaseAction = &game.releasePlace},
|
||||
.{.name = "breakBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER}, .pressAction = &game.pressBreak, .releaseAction = &game.releaseBreak},
|
||||
.{.name = "acquireSelectedBlock", .mouseButton = c.GLFW_MOUSE_BUTTON_MIDDLE, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_DPAD_LEFT, .pressAction = &game.pressAcquireSelectedBlock},
|
||||
|
||||
.{.name = "takeBackgroundImage", .key = c.GLFW_KEY_PRINT_SCREEN, .releaseAction = &takeBackgroundImageFn},
|
||||
|
||||
// Gui:
|
||||
.{.name = "escape", .key = c.GLFW_KEY_ESCAPE, .releaseAction = &escape},
|
||||
.{.name = "openInventory", .key = c.GLFW_KEY_E, .releaseAction = &openInventory},
|
||||
.{.name = "openCreativeInventory(aka cheat inventory)", .key = c.GLFW_KEY_C, .releaseAction = &openCreativeInventory},
|
||||
.{.name = "escape", .key = c.GLFW_KEY_ESCAPE, .releaseAction = &escape, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_B},
|
||||
.{.name = "openInventory", .key = c.GLFW_KEY_E, .releaseAction = &openInventory, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_X},
|
||||
.{.name = "openCreativeInventory(aka cheat inventory)", .key = c.GLFW_KEY_C, .releaseAction = &openCreativeInventory, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_Y},
|
||||
.{.name = "openChat", .key = c.GLFW_KEY_T, .releaseAction = &openChat},
|
||||
.{.name = "mainGuiButton", .mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .pressAction = &gui.mainButtonPressed, .releaseAction = &gui.mainButtonReleased},
|
||||
.{.name = "secondaryGuiButton", .mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT, .pressAction = &gui.secondaryButtonPressed, .releaseAction = &gui.secondaryButtonReleased},
|
||||
.{.name = "mainGuiButton", .mouseButton = c.GLFW_MOUSE_BUTTON_LEFT, .pressAction = &gui.mainButtonPressed, .releaseAction = &gui.mainButtonReleased, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_A},
|
||||
.{.name = "secondaryGuiButton", .mouseButton = c.GLFW_MOUSE_BUTTON_RIGHT, .pressAction = &gui.secondaryButtonPressed, .releaseAction = &gui.secondaryButtonReleased, .gamepadButton = c.GLFW_GAMEPAD_BUTTON_Y},
|
||||
// gamepad gui.
|
||||
.{.name = "scrollUp", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_RIGHT_Y, .positive = false}},
|
||||
.{.name = "scrollDown", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_RIGHT_Y, .positive = true}},
|
||||
.{.name = "uiUp", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_Y, .positive = false}},
|
||||
.{.name = "uiLeft", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_X, .positive = false}},
|
||||
.{.name = "uiDown", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_Y, .positive = true}},
|
||||
.{.name = "uiRight", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_LEFT_X, .positive = true}},
|
||||
// text:
|
||||
.{.name = "textCursorLeft", .key = c.GLFW_KEY_LEFT, .repeatAction = &gui.textCallbacks.left},
|
||||
.{.name = "textCursorRight", .key = c.GLFW_KEY_RIGHT, .repeatAction = &gui.textCallbacks.right},
|
||||
@ -364,7 +378,12 @@ pub const KeyBoard = struct { // MARK: KeyBoard
|
||||
.{.name = "Hotbar 10", .key = c.GLFW_KEY_0, .releaseAction = setHotbarSlot(10)},
|
||||
.{.name = "Hotbar 11", .key = c.GLFW_KEY_MINUS, .releaseAction = setHotbarSlot(11)},
|
||||
.{.name = "Hotbar 12", .key = c.GLFW_KEY_EQUAL, .releaseAction = setHotbarSlot(12)},
|
||||
|
||||
.{.name = "Hotbar left", .gamepadButton = c.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, .releaseAction = cycleHotbarSlot(-1)},
|
||||
.{.name = "Hotbar right", .gamepadButton = c.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, .releaseAction = cycleHotbarSlot(1)},
|
||||
.{.name = "cameraLeft", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_RIGHT_X, .positive = false}},
|
||||
.{.name = "cameraRight", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_RIGHT_X, .positive = true}},
|
||||
.{.name = "cameraUp", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_RIGHT_Y, .positive = false}},
|
||||
.{.name = "cameraDown", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_RIGHT_Y, .positive = true}},
|
||||
// debug:
|
||||
.{.name = "hideMenu", .key = c.GLFW_KEY_F1, .releaseAction = &toggleHideGui},
|
||||
.{.name = "debugOverlay", .key = c.GLFW_KEY_F3, .releaseAction = &toggleDebugOverlay},
|
||||
@ -434,7 +453,7 @@ pub fn convertJsonToZon(jsonPath: []const u8) void { // TODO: Remove after #480
|
||||
var zonString = List(u8).init(stackAllocator);
|
||||
defer zonString.deinit();
|
||||
std.log.debug("{s}", .{jsonString});
|
||||
|
||||
|
||||
var i: usize = 0;
|
||||
while(i < jsonString.len) : (i += 1) {
|
||||
switch(jsonString[i]) {
|
||||
@ -622,7 +641,8 @@ pub fn main() void { // MARK: main()
|
||||
lastDeltaTime.store(deltaTime, .monotonic);
|
||||
lastBeginRendering = begin;
|
||||
|
||||
Window.handleEvents();
|
||||
Window.handleEvents(deltaTime);
|
||||
|
||||
file_monitor.handleEvents();
|
||||
|
||||
if(game.world != null) { // Update the game
|
||||
|
@ -25,6 +25,7 @@ pub var fpsCap: ?u32 = null;
|
||||
pub var fov: f32 = 70;
|
||||
|
||||
pub var mouseSensitivity: f32 = 1;
|
||||
pub var controllerSensitivity: f32 = 1;
|
||||
|
||||
pub var renderDistance: u16 = 7;
|
||||
|
||||
@ -56,6 +57,7 @@ pub var developerAutoEnterWorld: []const u8 = "";
|
||||
|
||||
pub var developerGPUInfiniteLoopDetection: bool = false;
|
||||
|
||||
pub var controllerAxisDeadzone: f32 = 0.0;
|
||||
|
||||
pub fn init() void {
|
||||
const zon: ZonElement = main.files.readToZon(main.stackAllocator, "settings.zig.zon") catch |err| blk: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user