Architectury build system & huge code cleanup

This commit is contained in:
Martin Prokoph
2024-07-17 14:26:17 +02:00
parent 9e3b2ae060
commit 27221b62cd
146 changed files with 1529 additions and 855 deletions

31
common/build.gradle Normal file
View File

@@ -0,0 +1,31 @@
architectury {
common(rootProject.enabled_platforms.split(","))
}
loom {
accessWidenerPath = file("src/main/resources/midnightcontrols.accesswidener")
}
dependencies {
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
// Using the Fabric version of midnightlib here to create a common config and get useful utilities
// Just make sure NOT to use classes from the .fabric classpath
modCompileOnlyApi "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric"
modCompileOnlyApi "maven.modrinth:obsidianui:${project.obsidianui_version}-fabric"
include 'org.aperlambda:lambdajcommon:1.8.1'
}
publishing {
publications {
mavenCommon(MavenPublication) {
artifactId = rootProject.archives_base_name
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols;
import org.jetbrains.annotations.NotNull;
import org.thinkingstudio.obsidianui.util.Nameable;
import java.util.Arrays;
import java.util.Optional;
/**
* Represents the controls mode.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.0.0
*/
public enum ControlsMode implements Nameable {
DEFAULT,
CONTROLLER,
TOUCHSCREEN;
/**
* Returns the next controls mode available.
*
* @return the next available controls mode
*/
public ControlsMode next() {
var v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
/**
* Gets the translation key of this controls mode.
*
* @return the translated key of this controls mode
* @since 1.1.0
*/
public String getTranslationKey() {
return "midnightcontrols.controls_mode." + this.getName();
}
@Override
public @NotNull String getName() {
return this.name().toLowerCase();
}
/**
* Gets the controls mode from its identifier.
*
* @param id the identifier of the controls mode
* @return the controls mode if found, else empty
*/
public static Optional<ControlsMode> byId(@NotNull String id) {
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols;
import eu.midnightdust.lib.util.PlatformFunctions;
import net.minecraft.network.packet.CustomPayload;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Represents the MidnightControls mod.
*
* @author LambdAurora & Motschen
* @version 1.8.0
* @since 1.0.0
*/
public class MidnightControls {
public static final CustomPayload.Id<CustomPayload> CONTROLS_MODE_CHANNEL = new CustomPayload.Id<>(MidnightControlsConstants.CONTROLS_MODE_CHANNEL);
public static final CustomPayload.Id<CustomPayload> FEATURE_CHANNEL = new CustomPayload.Id<>(MidnightControlsConstants.FEATURE_CHANNEL);
public static final CustomPayload.Id<CustomPayload> HELLO_CHANNEL = new CustomPayload.Id<>(MidnightControlsConstants.HELLO_CHANNEL);
public static boolean isExtrasLoaded;
public static final Logger logger = LogManager.getLogger("MidnightControls");
public static void init() {
isExtrasLoaded = PlatformFunctions.isModLoaded("midnightcontrols-extra");
log("Initializing MidnightControls...");
}
/**
* Prints a message to the terminal.
*
* @param info the message to print
*/
public static void log(String info) {
logger.info("[MidnightControls] {}", info);
}
/**
* Prints a warning to the terminal.
*
* @param warning the warning to print
*/
public static void warn(String warning) {
logger.warn("[MidnightControls] {}", warning);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols;
import net.minecraft.util.Identifier;
/**
* Represents the constants used by MidnightControls.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class MidnightControlsConstants {
public static final String NAMESPACE = "midnightcontrols";
public static final Identifier CONTROLS_MODE_CHANNEL = Identifier.of(NAMESPACE, "controls_mode");
public static final Identifier FEATURE_CHANNEL = Identifier.of(NAMESPACE, "feature");
public static final Identifier HELLO_CHANNEL = Identifier.of(NAMESPACE, "hello");
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols;
import org.thinkingstudio.obsidianui.util.Nameable;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* Represents a feature.
*
* @author LambdAurora
* @version 1.5.0
* @since 1.1.0
*/
public class MidnightControlsFeature implements Nameable {
private static final List<MidnightControlsFeature> FEATURES = new ArrayList<>();
public static final MidnightControlsFeature FAST_BLOCK_PLACING = new MidnightControlsFeature("fast_block_placing", true, MidnightControlsConfig.fastBlockPlacing);
public static final MidnightControlsFeature HORIZONTAL_REACHAROUND = new MidnightControlsFeature("horizontal_reacharound", true, MidnightControlsConfig.horizontalReacharound);
public static final MidnightControlsFeature VERTICAL_REACHAROUND = new MidnightControlsFeature("vertical_reacharound", true, MidnightControlsConfig.verticalReacharound);
private final String key;
private final boolean defaultAllowed;
private boolean allowed;
private final boolean defaultEnabled;
private boolean enabled;
public MidnightControlsFeature(@NotNull String key, boolean allowed, boolean enabled) {
Objects.requireNonNull(key, "Feature key cannot be null.");
this.key = key;
this.setAllowed(this.defaultAllowed = allowed);
this.setEnabled(this.defaultEnabled = enabled);
}
@Deprecated
public MidnightControlsFeature(@NotNull String key) {
this(key, false, false);
}
/**
* Allows the feature.
*/
@Deprecated
public void allow() {
this.setAllowed(true);
}
/**
* Returns whether this feature is allowed.
*
* @return {@code true} if this feature is allowed, else {@code false}
*/
public boolean isAllowed() {
return MidnightControls.isExtrasLoaded && this.allowed;
}
/**
* Sets whether this feature is allowed.
*
* @param allowed {@code true} if this feature is allowed, else {@code false}
*/
public void setAllowed(boolean allowed) {
this.allowed = allowed;
}
/**
* Resets allowed state to default.
*/
public void resetAllowed() {
this.setAllowed(this.defaultAllowed);
}
/**
* Returns whether this feature is enabled.
*
* @return {@code true} if this feature is enabled, else {@code false}
*/
public boolean isEnabled() {
return this.enabled;
}
/**
* Returns whether this feature is enabled.
*
* @param enabled {@code true} if this feature is enabled, else {@code false}
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* Refreshes the enabled values from the config.
*/
public static void refreshEnabled() {
MidnightControlsFeature.VERTICAL_REACHAROUND.setEnabled(MidnightControlsConfig.verticalReacharound);
MidnightControlsFeature.FAST_BLOCK_PLACING.setEnabled(MidnightControlsConfig.fastBlockPlacing);
MidnightControlsFeature.HORIZONTAL_REACHAROUND.setEnabled(MidnightControlsConfig.horizontalReacharound);
}
/**
* Returns whether this feature is available or not.
*
* @return {@code true} if this feature is available, else {@code false}
* @see #isAllowed()
* @see #isEnabled()
*/
public boolean isAvailable() {
return this.isAllowed() && this.isEnabled();
}
/**
* Resets the feature to its default values.
*/
public void reset() {
this.resetAllowed();
this.setEnabled(this.defaultEnabled);
}
@Override
public @NotNull String getName() {
return this.key;
}
public static @NotNull Optional<MidnightControlsFeature> fromName(@NotNull String key) {
Objects.requireNonNull(key, "Cannot find features with a null name.");
return FEATURES.parallelStream().filter(feature -> feature.getName().equals(key)).findFirst();
}
/**
* Resets all features to their default values.
*/
public static void resetAll() {
FEATURES.parallelStream().forEach(MidnightControlsFeature::reset);
}
/**
* Resets all features to allow state.
*/
public static void resetAllAllowed() {
FEATURES.parallelStream().forEach(MidnightControlsFeature::resetAllowed);
}
static {
FEATURES.add(FAST_BLOCK_PLACING);
FEATURES.add(HORIZONTAL_REACHAROUND);
FEATURES.add(VERTICAL_REACHAROUND);
}
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client;
import eu.midnightdust.lib.util.PlatformFunctions;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsHud;
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
import eu.midnightdust.midnightcontrols.client.mixin.KeyBindingIDAccessor;
import eu.midnightdust.midnightcontrols.client.ring.ButtonBindingRingAction;
import eu.midnightdust.midnightcontrols.client.ring.MidnightRing;
import eu.midnightdust.midnightcontrols.client.util.platform.NetworkUtil;
import org.thinkingstudio.obsidianui.hud.HudManager;
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
import eu.midnightdust.midnightcontrols.client.util.RainbowColor;
import eu.midnightdust.midnightcontrols.packet.ControlsModePayload;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.client.util.InputUtil;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* Represents the midnightcontrols client mod.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.1.0
*/
public class MidnightControlsClient extends MidnightControls {
public static boolean lateInitDone = false;
public static final KeyBinding BINDING_LOOK_UP = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "look_up"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_8, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_RIGHT = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "look_right"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_6, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_DOWN = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "look_down"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_2, "key.categories.movement");
public static final KeyBinding BINDING_LOOK_LEFT = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "look_left"),
InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_KP_4, "key.categories.movement");
public static final KeyBinding BINDING_RING = InputManager.makeKeyBinding(Identifier.of(MidnightControlsConstants.NAMESPACE, "ring"),
InputUtil.Type.KEYSYM, InputUtil.UNKNOWN_KEY.getCode(), "key.categories.misc");
public static final Identifier CONTROLLER_BUTTONS = Identifier.of(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_buttons.png");
public static final Identifier CONTROLLER_EXPANDED = Identifier.of(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_expanded.png");
public static final Identifier CONTROLLER_AXIS = Identifier.of(MidnightControlsConstants.NAMESPACE, "textures/gui/controller_axis.png");
public static final Identifier CURSOR_TEXTURE = Identifier.of(MidnightControlsConstants.NAMESPACE, "textures/gui/cursor.png");
public static final File MAPPINGS_FILE = new File("config/gamecontrollercustommappings.txt");
public static final MinecraftClient client = MinecraftClient.getInstance();
public static final MidnightInput input = new MidnightInput();
public static final MidnightRing ring = new MidnightRing();
public static final MidnightReacharound reacharound = new MidnightReacharound();
private static MidnightControlsHud hud;
private static ControlsMode previousControlsMode;
public static void initClient() {
ring.registerAction("buttonbinding", ButtonBindingRingAction.FACTORY);
final MinecraftClient client = MinecraftClient.getInstance();
int delay = 0; // delay for 0 sec.
int period = 1; // repeat every 0.001 sec. (100 times a second)
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
input.updateCamera(client);
}
}, delay, period);
HudManager.register(hud = new MidnightControlsHud());
}
/**
* This method is called when Minecraft is initializing.
*/
public static void onMcInit(@NotNull MinecraftClient client) {
ButtonBinding.init(client.options);
MidnightControlsConfig.load();
if (MidnightControlsConfig.configVersion < 2) {
MidnightControlsConfig.mouseScreens.remove("me.jellysquid.mods.sodium.client.gui");
MidnightControlsConfig.mouseScreens.remove("net.coderbot.iris.gui");
MidnightControlsConfig.mouseScreens.remove("net.minecraft.class_5375");
MidnightControlsConfig.mouseScreens.remove("net.minecraft.client.gui.screen.pack.PackScreen");
MidnightControlsConfig.configVersion = 2;
MidnightControlsConfig.write("midnightcontrols");
}
hud.setVisible(MidnightControlsConfig.hudEnable);
Controller.updateMappings();
try {
GLFW.glfwSetJoystickCallback((jid, event) -> {
if (event == GLFW.GLFW_CONNECTED) {
var controller = Controller.byId(jid);
client.getToastManager().add(new SystemToast(SystemToast.Type.PERIODIC_NOTIFICATION, Text.translatable("midnightcontrols.controller.connected", jid),
Text.literal(controller.getName())));
} else if (event == GLFW.GLFW_DISCONNECTED) {
client.getToastManager().add(new SystemToast(SystemToast.Type.PERIODIC_NOTIFICATION, Text.translatable("midnightcontrols.controller.disconnected", jid),
null));
}
switchControlsMode();
});
} catch (Exception e) {e.fillInStackTrace();}
MidnightControlsCompat.init();
}
/**
* This method is called to initialize keybindings
*/
public static void initKeybindings() {
if (lateInitDone) return;
if (KeyBindingIDAccessor.getKEYS_BY_ID() == null || KeyBindingIDAccessor.getKEYS_BY_ID().isEmpty()) return;
if (PlatformFunctions.isModLoaded("voxelmap") && !KeyBindingIDAccessor.getKEYS_BY_ID().containsKey("key.minimap.toggleingamewaypoints")) return;
if (PlatformFunctions.isModLoaded("wynntils") && KeyBindingIDAccessor.getKEYS_BY_ID().entrySet().stream().noneMatch(b -> Objects.equals(b.getValue().getCategory(), "Wynntils"))) return;
for (int i = 0; i < KeyBindingIDAccessor.getKEYS_BY_ID().size(); ++i) {
KeyBinding keyBinding = KeyBindingIDAccessor.getKEYS_BY_ID().entrySet().stream().toList().get(i).getValue();
if (MidnightControlsConfig.excludedKeybindings.stream().noneMatch(excluded -> keyBinding.getTranslationKey().startsWith(excluded))) {
if (!keyBinding.getTranslationKey().contains("midnightcontrols") && !keyBinding.getTranslationKey().contains("ok_zoomer") && !keyBinding.getTranslationKey().contains("okzoomer")) {
AtomicReference<ButtonCategory> category = new AtomicReference<>();
InputManager.streamCategories().forEach(buttonCategory -> {
if (buttonCategory.getIdentifier().equals(Identifier.of("minecraft", keyBinding.getCategory())))
category.set(buttonCategory);
});
if (category.get() == null) {
category.set(new ButtonCategory(Identifier.of("minecraft", keyBinding.getCategory())));
InputManager.registerCategory(category.get());
}
ButtonBinding buttonBinding = new ButtonBinding.Builder(keyBinding.getTranslationKey()).category(category.get()).linkKeybind(keyBinding).register();
if (MidnightControlsConfig.debug) {
MidnightControls.log(keyBinding.getTranslationKey());
MidnightControls.log(String.valueOf(buttonBinding));
}
}
}
}
InputManager.loadButtonBindings();
lateInitDone = true;
}
/**
* This method is called every Minecraft tick.
*
* @param client the client instance
*/
public static void onTick(@NotNull MinecraftClient client) {
initKeybindings();
input.tick(client);
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && (client.isWindowFocused() || MidnightControlsConfig.unfocusedInput))
input.tickController(client);
if (BINDING_RING.wasPressed()) {
ring.loadFromUnbound();
client.setScreen(new RingScreen());
}
if (client.world != null && MidnightControlsConfig.enableHints && !MidnightControlsConfig.autoSwitchMode && MidnightControlsConfig.controlsMode == ControlsMode.DEFAULT && MidnightControlsConfig.getController().isGamepad()) {
client.getToastManager().add(SystemToast.create(client, SystemToast.Type.PERIODIC_NOTIFICATION, Text.translatable("midnightcontrols.controller.tutorial.title"),
Text.translatable("midnightcontrols.controller.tutorial.description", Text.translatable("options.title"), Text.translatable("controls.title"),
Text.translatable("midnightcontrols.menu.title.controller"))));
MidnightControlsConfig.enableHints = false;
MidnightControlsConfig.save();
}
RainbowColor.tick();
TouchInput.tick();
}
/**
* Called when leaving a server.
*/
public static void onLeave() {
MidnightControlsFeature.resetAllAllowed();
}
/**
* Switches the controls mode if the auto switch is enabled.
*/
public static void switchControlsMode() {
if (MidnightControlsConfig.autoSwitchMode) {
if (MidnightControlsConfig.getController().isGamepad()) {
previousControlsMode = MidnightControlsConfig.controlsMode;
MidnightControlsConfig.controlsMode = ControlsMode.CONTROLLER;
} else {
if (previousControlsMode == null) {
previousControlsMode = ControlsMode.DEFAULT;
}
MidnightControlsConfig.controlsMode = previousControlsMode;
}
NetworkUtil.sendPayloadC2S(new ControlsModePayload(MidnightControlsConfig.controlsMode.getName()));
}
}
/**
* Sets whether the HUD is enabled or not.
*
* @param enabled true if the HUD is enabled, else false
*/
public static void setHudEnabled(boolean enabled) {
MidnightControlsConfig.hudEnable = enabled;
hud.setVisible(enabled);
}
private static final MidnightControlsClient INSTANCE = new MidnightControlsClient();
/**
* Gets the midnightcontrols client instance.
*
* @return the midnightcontrols client instance
*/
@Deprecated
public static MidnightControlsClient get() {
return INSTANCE;
}
}

View File

@@ -0,0 +1,411 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mojang.blaze3d.platform.GlDebugInfo;
import eu.midnightdust.lib.config.MidnightConfig;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import eu.midnightdust.midnightcontrols.client.enums.CameraMode;
import eu.midnightdust.midnightcontrols.client.enums.ControllerType;
import eu.midnightdust.midnightcontrols.client.enums.HudSide;
import eu.midnightdust.midnightcontrols.client.enums.VirtualMouseSkin;
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
import eu.midnightdust.midnightcontrols.client.enums.TouchMode;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import java.util.*;
import java.util.regex.Pattern;
import static org.lwjgl.glfw.GLFW.*;
/**
* Represents MidnightControls configuration.
*/
public class MidnightControlsConfig extends MidnightConfig {
public static final String CONTROLLER = "controller";
public static final String TOUCH = "touch";
public static final String GAMEPLAY = "gameplay";
public static final String SCREENS = "screens";
public static final String VISUAL = "visual";
public static final String MISC = "misc";
public static boolean isEditing = false;
@Hidden @Entry public static int configVersion = 2;
// General
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.controls_mode") public static ControlsMode controlsMode = ControlsMode.DEFAULT;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.auto_switch_mode") public static boolean autoSwitchMode = true;
@Entry(category = MISC, name = "Debug") public static boolean debug = false;
// HUD
@Entry(category = VISUAL, name = "midnightcontrols.menu.hud_enable") public static boolean hudEnable = true;
@Entry(category = VISUAL, name = "midnightcontrols.menu.hud_side") public static HudSide hudSide = HudSide.LEFT;
@Entry(category = SCREENS, name = "midnightcontrols.menu.move_chat") public static boolean moveChat = false;
// Gameplay
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.analog_movement") public static boolean analogMovement = true;
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.double_tap_to_sprint") public static boolean doubleTapToSprint = true;
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.controller_toggle_sneak") public static boolean controllerToggleSneak = MinecraftClient.getInstance().options.getSneakToggled().getValue();
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.controller_toggle_sprint") public static boolean controllerToggleSprint = MinecraftClient.getInstance().options.getSprintToggled().getValue();
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fast_block_placing") public static boolean fastBlockPlacing = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fly_drifting") public static boolean flyDrifting = true; // Enabled by default as disabling this behaviour can be considered cheating on multiplayer servers. It can also conflict with some other mods.
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.fly_drifting_vertical") public static boolean verticalFlyDrifting = true; // Enabled by default as disabling this behaviour can be considered cheating on multiplayer servers.
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.reacharound.horizontal") public static boolean horizontalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Entry(category = GAMEPLAY, name = "midnightcontrols.menu.reacharound.vertical") public static boolean verticalReacharound = false; // Disabled by default as this behaviour can be considered cheating on multiplayer servers.
@Entry(category = VISUAL, name = "Reacharound Outline") public static boolean shouldRenderReacharoundOutline = true;
@Entry(category = VISUAL, name = "Reacharound Outline Color", isColor = true) public static String reacharoundOutlineColorHex = "#ffffff";
@Entry(category = VISUAL, name = "Reacharound Outline Alpha", isSlider = true, min = 0, max = 255) public static int reacharoundOutlineColorAlpha = 102;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.right_dead_zone", isSlider = true, min = 0.05, max = 1) public static double rightDeadZone = 0.25;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.left_dead_zone", isSlider = true, min = 0.05, max = 1) public static double leftDeadZone = 0.25;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.invert_right_y_axis") public static boolean invertRightYAxis = false;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.invert_right_x_axis") public static boolean invertRightXAxis = false;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.rotation_speed", isSlider = true, min = 0, max = 100, precision = 10) public static double rotationSpeed = 35.0; //used for x-axis, name kept for compatibility
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.y_axis_rotation_speed", isSlider = true, min = 0, max = 100, precision = 10) public static double yAxisRotationSpeed = rotationSpeed;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.camera_mode") public static CameraMode cameraMode = CameraMode.FLAT;
@Entry(category = SCREENS, name = "midnightcontrols.menu.mouse_speed", isSlider = true, min = 0, max = 150, precision = 10) public static double mouseSpeed = 25.0;
@Entry(category = SCREENS, name = "midnightcontrols.menu.joystick_as_mouse") public static boolean joystickAsMouse = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.eye_tracker_as_mouse") public static boolean eyeTrackerAsMouse = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.eye_tracker_deadzone", isSlider = true, min = 0, max = 0.4) public static double eyeTrackerDeadzone = 0.05;
@Entry(category = CONTROLLER, name = "midnightcontrols.menu.unfocused_input") public static boolean unfocusedInput = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_mouse") public static boolean virtualMouse = false;
@Entry(category = SCREENS, name = "midnightcontrols.menu.virtual_mouse.skin") public static VirtualMouseSkin virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
@Entry(category = SCREENS, name = "midnightcontrols.menu.hide_cursor") public static boolean hideNormalMouse = false;
@Entry(category = CONTROLLER, name = "Controller ID") @Hidden public static Object controllerID = 0;
@Entry(category = CONTROLLER, name = "2nd Controller ID") @Hidden public static Object secondControllerID = -1;
@Entry(category = VISUAL, name = "midnightcontrols.menu.controller_type") public static ControllerType controllerType = ControllerType.DEFAULT;
@Entry(category = SCREENS, name = "Mouse screens") public static List<String> mouseScreens = Lists.newArrayList("net.minecraft.client.gui.screen.advancement",
"net.minecraft.class_457", "net.minecraft.class_408", "net.minecraft.class_3872", "me.flashyreese.mods.reeses_sodium_options.client.gui", "dev.emi.emi.screen",
"hardcorequesting.client.interfaces.GuiQuestBook", "hardcorequesting.client.interfaces.GuiReward", "hardcorequesting.client.interfaces.EditTrackerScreen",
"me.shedaniel.clothconfig2.gui.ClothConfigScreen", "com.mamiyaotaru.voxelmap.gui.GuiWaypoints", "com.mamiyaotaru.voxelmap.gui.GuiPersistentMap");
@Entry(category = SCREENS, name = "Arrow screens") public static List<String> arrowScreens = Lists.newArrayList(ChatScreen.class.getCanonicalName());
@Entry(category = SCREENS, name = "WASD screens") public static List<String> wasdScreens = Lists.newArrayList("com.ultreon.devices.core.Laptop");
@Entry(category = TOUCH, name = "Screens with close button") public static List<String> closeButtonScreens = Lists.newArrayList(ChatScreen.class.getCanonicalName(), AdvancementsScreen.class.getCanonicalName(), RingScreen.class.getCanonicalName());
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_with_controller") public static boolean touchInControllerMode = false;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_speed", isSlider = true, min = 0, max = 150, precision = 10) public static double touchSpeed = 50.0;
@Entry(category = TOUCH, name = "midnightcontrols.menu.invert_touch") public static boolean invertTouch = false;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_mode") public static TouchMode touchMode = TouchMode.CROSSHAIR;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_break_delay", isSlider = true, min = 50, max = 500) public static int touchBreakDelay = 120;
@Entry(category = TOUCH, name = "midnightcontrols.menu.touch_transparency", isSlider = true, min = 0, max = 100) public static int touchTransparency = 75;
@Entry(category = TOUCH, name = "Touch Outline Color", isColor = true) public static String touchOutlineColorHex = "#ffffff";
@Entry(category = TOUCH, name = "Touch Outline Alpha", isSlider = true, min = 0, max = 255) public static int touchOutlineColorAlpha = 150;
@Entry(category = TOUCH, name = "Left Touch button bindings") public static List<String> leftTouchBinds = Lists.newArrayList("debug_screen", "screenshot","toggle_perspective");
@Entry(category = TOUCH, name = "Right Touch button bindings") public static List<String> rightTouchBinds = Lists.newArrayList("screenshot","toggle_perspective", "use");
@Entry @Hidden public static Map<String, String> BINDING = new HashMap<>();
private static final Pattern BUTTON_BINDING_PATTERN = Pattern.compile("(-?\\d+)\\+?");
@Deprecated @Hidden @Entry public static double[] maxAnalogValues = new double[]{1, 1, 1, 1};
@Entry(category = CONTROLLER, name = "Max analog value: Left X", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueLeftX = maxAnalogValues[0];
@Entry(category = CONTROLLER, name = "Max analog value: Left Y", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueLeftY = maxAnalogValues[1];
@Entry(category = CONTROLLER, name = "Max analog value: Right X", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueRightX = maxAnalogValues[2];
@Entry(category = CONTROLLER, name = "Max analog value: Right Y", isSlider = true, min = .25f, max = 1.f) public static double maxAnalogValueRightY = maxAnalogValues[3];
@Entry(category = CONTROLLER, name = "Trigger button fix") public static boolean triggerFix = true;
@Entry(category = MISC, name = "Excluded Keybindings") public static List<String> excludedKeybindings = Lists.newArrayList("key.forward", "key.left", "key.back", "key.right", "key.jump", "key.sneak", "key.sprint", "key.inventory",
"key.swapOffhand", "key.drop", "key.use", "key.attack", "key.chat", "key.playerlist", "key.screenshot", "key.togglePerspective", "key.smoothCamera", "key.fullscreen", "key.saveToolbarActivator", "key.loadToolbarActivator",
"key.pickItem", "key.hotbar.1", "key.hotbar.2", "key.hotbar.3", "key.hotbar.4", "key.hotbar.5", "key.hotbar.6", "key.hotbar.7", "key.hotbar.8", "key.hotbar.9");
@Entry(category = GAMEPLAY, name = "Enable Hints") public static boolean enableHints = true;
@Entry(category = SCREENS, name = "Enable Shortcut in Controls Options") public static boolean shortcutInControls = true;
@Entry(category = MISC, name = "Ring Bindings (WIP)") public static List<String> ringBindings = new ArrayList<>();
@Entry(category = MISC, name = "Ignored Unbound Keys") public static List<String> ignoredUnboundKeys = Lists.newArrayList("inventorytabs.key.next_tab");
@Entry @Hidden public static Map<String, Map<String, String>> controllerBindingProfiles = new HashMap<>();
private static Map<String, String> currentBindingProfile = new HashMap<>();
private static Controller prevController;
/**
* Loads the configuration
*/
public static void load() {
MidnightControlsConfig.init("midnightcontrols", MidnightControlsConfig.class);
MidnightControls.log("Configuration loaded.");
// Controller controls.
InputManager.loadButtonBindings();
}
/**
* Saves the configuration.
*/
public static void save() {
MidnightControlsConfig.write("midnightcontrols");
MidnightControls.log("Configuration saved.");
MidnightControlsFeature.refreshEnabled();
}
public static void updateBindingsForController(Controller controller) {
if (controller.isConnected() && controller.isGamepad() && controllerBindingProfiles.containsKey(controller.getGuid()))
currentBindingProfile = controllerBindingProfiles.get(controller.getGuid());
else currentBindingProfile = Maps.newHashMap(BINDING);
InputManager.loadButtonBindings();
}
public static Map<String, String> getBindingsForController() {
return currentBindingProfile;
}
/**
* Gets the used controller.
*
* @return the controller
*/
public static Controller getController() {
var raw = MidnightControlsConfig.controllerID;
Controller controller = Controller.byId(GLFW.GLFW_JOYSTICK_1);
if (raw instanceof Number) {
controller = Controller.byId(((Number) raw).intValue());
} else if (raw instanceof String) {
controller = Controller.byGuid((String) raw).orElse(Controller.byId(GLFW.GLFW_JOYSTICK_1));
}
if ((!controller.isConnected() || !controller.isGamepad()) && MidnightControlsConfig.autoSwitchMode && !isEditing) {
for (int i = 0; i < GLFW.GLFW_JOYSTICK_LAST; ++i) {
Controller gamepad = Controller.byId(i);
if (gamepad.isConnected() && gamepad.isGamepad()) {
controller = gamepad;
i = GLFW_JOYSTICK_LAST;
}
}
}
if (controller.isConnected() && controller.isGamepad() && MidnightControlsConfig.autoSwitchMode && !isEditing) MidnightControlsConfig.controlsMode = ControlsMode.CONTROLLER;
if (prevController != controller) updateBindingsForController(controller);
prevController = controller;
return controller;
}
/**
* Sets the used controller.
*
* @param controller the controller
*/
public static void setController(Controller controller) {
MidnightControlsConfig.controllerID = controller.id();
MidnightControlsConfig.write("midnightcontrols");
}
/**
* Gets the second controller (for Joy-Con supports).
*
* @return the second controller
*/
public static Optional<Controller> getSecondController() {
var raw = MidnightControlsConfig.secondControllerID;
if (raw instanceof Number) {
if (((Number) raw).intValue() == -1)
return Optional.empty();
return Optional.of(Controller.byId(((Number) raw).intValue()));
} else if (raw instanceof String) {
return Optional.of(Controller.byGuid((String) raw).orElse(Controller.byId(GLFW.GLFW_JOYSTICK_1)));
}
return Optional.empty();
}
/**
* Sets the second controller.
*
* @param controller the second controller
*/
public static void setSecondController(@Nullable Controller controller) {
MidnightControlsConfig.secondControllerID = controller == null ? -1 : controller.id();
}
/**
* Gets the right X axis sign.
*
* @return the right X axis sign
*/
public static double getRightXAxisSign() {
return MidnightControlsConfig.invertRightXAxis ? -1.0 : 1.0;
}
/**
* Gets the right Y axis sign.
*
* @return the right Y axis sign
*/
public static double getRightYAxisSign() {
return MidnightControlsConfig.invertRightYAxis ? -1.0 : 1.0;
}
public static double getAxisMaxValue(int axis) {
return switch (axis) {
case GLFW_GAMEPAD_AXIS_LEFT_X -> MidnightControlsConfig.maxAnalogValueLeftX;
case GLFW_GAMEPAD_AXIS_LEFT_Y -> MidnightControlsConfig.maxAnalogValueLeftY;
case GLFW_GAMEPAD_AXIS_RIGHT_X -> MidnightControlsConfig.maxAnalogValueRightX;
default -> MidnightControlsConfig.maxAnalogValueRightY;
};
}
public static void setAxisMaxValue(int axis, double value) {
switch (axis) {
case GLFW_GAMEPAD_AXIS_LEFT_X -> MidnightControlsConfig.maxAnalogValueLeftX = value;
case GLFW_GAMEPAD_AXIS_LEFT_Y -> MidnightControlsConfig.maxAnalogValueLeftY = value;
case GLFW_GAMEPAD_AXIS_RIGHT_X -> MidnightControlsConfig.maxAnalogValueRightX = value;
default -> MidnightControlsConfig.maxAnalogValueRightY = value;
};
}
/**
* Loads the button binding from configuration.
*
* @param button the button binding
*/
public static void loadButtonBinding(@NotNull ButtonBinding button) {
button.setButton(button.getDefaultButton());
var code = getBindingsForController().getOrDefault("controller.controls." + button.getName(), button.getButtonCode());
var matcher = BUTTON_BINDING_PATTERN.matcher(code);
try {
var buttons = new int[1];
int count = 0;
while (matcher.find()) {
count++;
if (count > buttons.length)
buttons = Arrays.copyOf(buttons, count);
String current;
if (!MidnightControlsConfig.checkValidity(button, code, current = matcher.group(1)))
return;
buttons[count - 1] = Integer.parseInt(current);
}
if (count == 0) {
MidnightControls.warn("Malformed config value \"" + code + "\" for binding \"" + button.getName() + "\".");
MidnightControlsConfig.setButtonBinding(button, new int[]{-1});
}
button.setButton(buttons);
} catch (Exception e) {
MidnightControls.warn("Malformed config value \"" + code + "\" for binding \"" + button.getName() + "\".");
setButtonBinding(button, button.getButton());
}
}
private static boolean checkValidity(@NotNull ButtonBinding binding, @NotNull String input, String group) {
if (group == null) {
MidnightControls.warn("Malformed config value \"" + input + "\" for binding \"" + binding.getName() + "\".");
setButtonBinding(binding, binding.getButton());
return false;
}
return true;
}
/**
* Sets the button binding in configuration.
*
* @param binding the button binding
* @param button the button
*/
public static void setButtonBinding(@NotNull ButtonBinding binding, int[] button) {
binding.setButton(button);
getBindingsForController().put("controller.controls." + binding.getName(), binding.getButtonCode());
if (controllerBindingProfiles.containsKey(getController().getGuid())) controllerBindingProfiles.get(getController().getGuid()).put("controller.controls." + binding.getName(), binding.getButtonCode());
else BINDING.put("controller.controls." + binding.getName(), binding.getButtonCode());
}
public static boolean isBackButton(int btn, boolean isBtn, int state) {
if (!isBtn && state == 0)
return false;
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false) == ButtonBinding.axisAsButton(btn, state == 1);
}
public static boolean isForwardButton(int btn, boolean isBtn, int state) {
if (!isBtn && state == 0)
return false;
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true) == ButtonBinding.axisAsButton(btn, state == 1);
}
public static boolean isLeftButton(int btn, boolean isBtn, int state) {
if (!isBtn && state == 0)
return false;
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false) == ButtonBinding.axisAsButton(btn, state == 1);
}
public static boolean isRightButton(int btn, boolean isBtn, int state) {
if (!isBtn && state == 0)
return false;
return ButtonBinding.axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true) == ButtonBinding.axisAsButton(btn, state == 1);
}
/**
* Returns whether the specified axis is an axis used for movements.
*
* @param axis the axis index
* @return true if the axis is used for movements, else false
*/
public static boolean isMovementAxis(int axis) {
return axis == GLFW_GAMEPAD_AXIS_LEFT_Y || axis == GLFW_GAMEPAD_AXIS_LEFT_X;
}
public static void reset() {
controlsMode = ControlsMode.DEFAULT;
autoSwitchMode = true;
debug = false;
hudEnable = true;
hudSide = HudSide.LEFT;
analogMovement = true;
doubleTapToSprint = true;
controllerToggleSneak = MinecraftClient.getInstance().options.getSneakToggled().getValue();
controllerToggleSprint = MinecraftClient.getInstance().options.getSprintToggled().getValue();
fastBlockPlacing = false;
flyDrifting = true;
verticalFlyDrifting = true;
horizontalReacharound = false;
verticalReacharound = false;
shouldRenderReacharoundOutline = true;
reacharoundOutlineColorHex = "#ffffff";
reacharoundOutlineColorAlpha = 102;
rightDeadZone = 0.25;
leftDeadZone = 0.25;
invertRightYAxis = false;
invertRightXAxis = false;
rotationSpeed = 40.0;
yAxisRotationSpeed = rotationSpeed;
mouseSpeed = 25.0;
unfocusedInput = false;
virtualMouse = false;
virtualMouseSkin = VirtualMouseSkin.DEFAULT_LIGHT;
controllerID = 0;
secondControllerID = -1;
controllerType = ControllerType.DEFAULT;
mouseScreens = Lists.newArrayList("net.minecraft.client.gui.screen.advancement", "net.minecraft.class_457", "net.minecraft.class_408", "net.minecraft.class_3872", "me.flashyreese.mods.reeses_sodium_options.client.gui", "dev.emi.emi.screen", "me.shedaniel.clothconfig2.gui.ClothConfigScreen", "com.mamiyaotaru.voxelmap.gui.GuiWaypoints", "com.mamiyaotaru.voxelmap.gui.GuiPersistentMap");
BINDING = new HashMap<>();
maxAnalogValueLeftX = 1;
maxAnalogValueLeftY = 1;
maxAnalogValueRightX = 1;
maxAnalogValueRightY = 1;
triggerFix = true;
enableHints = true;
shortcutInControls = true;
ringBindings = new ArrayList<>();
ignoredUnboundKeys = Lists.newArrayList("inventorytabs.key.next_tab");
controllerBindingProfiles = new HashMap<>();
}
/**
* Gets the controller type from the controller's identifier.
*
* @return the controller name matches a type, else empty
*/
public static @NotNull ControllerType matchControllerToType() {
String controller = getController().getName().toLowerCase();
if (controller.contains("xbox 360")) return ControllerType.XBOX_360;
else if (controller.contains("xbox") || controller.contains("afterglow")) return ControllerType.XBOX;
else if (controller.contains("steam") && GlDebugInfo.getCpuInfo().contains("AMD Custom APU")) return ControllerType.STEAM_DECK;
else if (controller.contains("steam")) return ControllerType.STEAM_CONTROLLER;
else if (controller.contains("dualsense") || controller.contains("ps5")) return ControllerType.DUALSENSE;
else if (controller.contains("dualshock") || controller.contains("ps4") || controller.contains("sony")) return ControllerType.DUALSHOCK;
else if (controller.contains("switch") || controller.contains("joy-con") || controller.contains("wii") || controller.contains("nintendo")) return ControllerType.SWITCH;
else if (controller.contains("ouya")) return ControllerType.OUYA;
else return ControllerType.DEFAULT;
}
public static boolean doMixedInput() {
return touchInControllerMode && controlsMode == ControlsMode.CONTROLLER;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
/**
* Represents the API implementation of ModMenu for midnightcontrols.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.1.0
*/
public class MidnightControlsModMenu implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return parent -> new MidnightControlsSettingsScreen(parent, false);
}
}

View File

@@ -0,0 +1,941 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client;
import com.google.common.collect.ImmutableSet;
import net.minecraft.util.Pair;
import org.thinkingstudio.obsidianui.widget.AbstractSpruceWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceEntryListWidget;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.client.compat.*;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import eu.midnightdust.midnightcontrols.client.enums.CameraMode;
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
import eu.midnightdust.midnightcontrols.client.gui.TouchscreenOverlay;
import eu.midnightdust.midnightcontrols.client.gui.widget.ControllerControlsWidget;
import eu.midnightdust.midnightcontrols.client.mixin.*;
import eu.midnightdust.midnightcontrols.client.ring.RingPage;
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
import eu.midnightdust.midnightcontrols.client.util.MathUtil;
import org.thinkingstudio.obsidianui.navigation.NavigationDirection;
import org.thinkingstudio.obsidianui.screen.SpruceScreen;
import org.thinkingstudio.obsidianui.widget.AbstractSprucePressableButtonWidget;
import org.thinkingstudio.obsidianui.widget.SpruceElement;
import org.thinkingstudio.obsidianui.widget.SpruceLabelWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceParentWidget;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.ParentElement;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.advancement.AdvancementTab;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.screen.ingame.MerchantScreen;
import net.minecraft.client.gui.screen.ingame.StonecutterScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget;
import net.minecraft.client.gui.screen.world.WorldListWidget;
import net.minecraft.client.gui.widget.*;
import net.minecraft.screen.slot.Slot;
import net.minecraft.text.TranslatableTextContent;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWGamepadState;
import java.util.*;
import static org.lwjgl.glfw.GLFW.*;
/**
* Represents the midnightcontrols' input handler.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.0.0
*/
public class MidnightInput {
private static final Map<Integer, Integer> BUTTON_COOLDOWNS = new HashMap<>();
// Cooldowns
public int actionGuiCooldown = 0;
public int joystickCooldown = 0;
public boolean ignoreNextARelease = false;
public boolean ignoreNextXRelease = false;
private double targetYaw = 0.0;
private double targetPitch = 0.0;
private float prevXAxis = 0.f;
private float prevYAxis = 0.f;
private int targetMouseX = 0;
private int targetMouseY = 0;
private float mouseSpeedX = 0.f;
private float mouseSpeedY = 0.f;
public int inventoryInteractionCooldown = 0;
public int screenCloseCooldown = 0;
private ControllerControlsWidget controlsInput = null;
public MidnightInput() {}
/**
* This method is called every Minecraft tick.
*
* @param client the client instance
*/
public void tick(@NotNull MinecraftClient client) {
this.targetYaw = 0.F;
this.targetPitch = 0.F;
// Handles the key bindings.
if (MidnightControlsClient.BINDING_LOOK_UP.isPressed()) {
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 2);
} else if (MidnightControlsClient.BINDING_LOOK_DOWN.isPressed()) {
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_Y, 0.8F, 1);
}
if (MidnightControlsClient.BINDING_LOOK_LEFT.isPressed()) {
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 2);
} else if (MidnightControlsClient.BINDING_LOOK_RIGHT.isPressed()) {
this.handleLook(client, GLFW_GAMEPAD_AXIS_RIGHT_X, 0.8F, 1);
}
InputManager.INPUT_MANAGER.tick(client);
}
/**
* This method is called every Minecraft tick for controller input update.
*
* @param client the client instance
*/
public void tickController(@NotNull MinecraftClient client) {
BUTTON_COOLDOWNS.entrySet().stream().filter(entry -> entry.getValue() > 0)
.forEach(entry -> BUTTON_COOLDOWNS.put(entry.getKey(), entry.getValue() - 1));
// Decreases the cooldown for GUI actions.
if (this.actionGuiCooldown > 0)
--this.actionGuiCooldown;
if (this.screenCloseCooldown > 0)
--this.screenCloseCooldown;
if (this.joystickCooldown > 0)
--this.joystickCooldown;
InputManager.updateStates();
var controller = MidnightControlsConfig.getController();
if (controller.isConnected()) {
var state = controller.getState();
this.fetchButtonInput(client, state, false);
this.fetchAxeInput(client, state, false);
}
MidnightControlsConfig.getSecondController().filter(Controller::isConnected)
.ifPresent(joycon -> {
GLFWGamepadState state = joycon.getState();
this.fetchButtonInput(client, state, true);
this.fetchAxeInput(client, state, true);
});
boolean allowInput = this.controlsInput == null || this.controlsInput.focusedBinding == null;
if (allowInput)
InputManager.updateBindings(client);
if (this.controlsInput != null) {
InputManager.STATES.forEach((num, button) -> {
if (button.isPressed()) System.out.println(num);
});
}
if (this.controlsInput != null && InputManager.STATES.int2ObjectEntrySet().parallelStream().map(Map.Entry::getValue).allMatch(ButtonState::isUnpressed)) {
System.out.println("finished");
if (this.controlsInput.focusedBinding != null && !this.controlsInput.waiting) {
int[] buttons = new int[this.controlsInput.currentButtons.size()];
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
buttons[i] = this.controlsInput.currentButtons.get(i);
this.controlsInput.finishBindingEdit(buttons);
this.controlsInput = null;
}
}
if (this.inventoryInteractionCooldown > 0)
this.inventoryInteractionCooldown--;
}
/**
* This method is called before the screen is rendered.
*
* @param client the client instance
* @param screen the screen to render
*/
public void onPreRenderScreen(@NotNull MinecraftClient client, @NotNull Screen screen) {
if (!isScreenInteractive(screen)) {
InputManager.INPUT_MANAGER.updateMousePosition(client);
}
}
/**
* This method is called to update the camera
*
* @param client the client instance
*/
public void updateCamera(@NotNull MinecraftClient client) {
if (!(client.currentScreen == null || client.currentScreen instanceof TouchscreenOverlay))
return;
var player = client.player;
if (player == null)
return;
if (this.targetYaw != 0.f || this.targetPitch != 0.f) {
float rotationYaw = (float) (client.player.prevYaw + (this.targetYaw * 0.175));
float rotationPitch = (float) (client.player.prevPitch + (this.targetPitch * 0.175));
client.player.prevYaw = rotationYaw;
client.player.prevPitch = MathHelper.clamp(rotationPitch, -90.f, 90.f);
client.player.setYaw(rotationYaw);
client.player.setPitch(MathHelper.clamp(rotationPitch, -90.f, 90.f));
if (client.player.isRiding() && client.player.getVehicle() != null) {
client.player.getVehicle().onPassengerLookAround(client.player);
}
client.getTutorialManager().onUpdateMouse(this.targetPitch, this.targetYaw);
MidnightControlsCompat.handleCamera(client, targetYaw, targetPitch);
}
}
/**
* This method is called when a Screen is opened.
*
* @param client the client instance
* @param windowWidth the window width
* @param windowHeight the window height
*/
public void onScreenOpen(@NotNull MinecraftClient client, int windowWidth, int windowHeight) {
if (client.currentScreen == null) {
this.mouseSpeedX = this.mouseSpeedY = 0.0F;
InputManager.INPUT_MANAGER.resetMousePosition(windowWidth, windowHeight);
} else if (isScreenInteractive(client.currentScreen) && MidnightControlsConfig.virtualMouse) {
((MouseAccessor) client.mouse).midnightcontrols$onCursorPos(client.getWindow().getHandle(), 0, 0);
InputManager.INPUT_MANAGER.resetMouseTarget(client);
}
this.inventoryInteractionCooldown = 5;
}
public void beginControlsInput(ControllerControlsWidget widget) {
this.controlsInput = widget;
if (widget != null) {
this.controlsInput.currentButtons.clear();
this.controlsInput.waiting = true;
}
}
private void fetchButtonInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon) {
var buffer = gamepadState.buttons();
for (int i = 0; i < buffer.limit(); i++) {
int btn = leftJoycon ? ButtonBinding.controller2Button(i) : i;
boolean btnState = buffer.get() == (byte) 1;
var state = ButtonState.NONE;
var previousState = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
if (btnState != previousState.isPressed()) {
state = btnState ? ButtonState.PRESS : ButtonState.RELEASE;
this.handleButton(client, btn, btnState ? 0 : 1, btnState);
if (btnState)
BUTTON_COOLDOWNS.put(btn, 5);
} else if (btnState) {
state = ButtonState.REPEAT;
if (BUTTON_COOLDOWNS.getOrDefault(btn, 0) == 0) {
BUTTON_COOLDOWNS.put(btn, 5);
this.handleButton(client, btn, 2, true);
}
}
InputManager.STATES.put(btn, state);
}
}
MathUtil.PolarUtil polarLeft = new MathUtil.PolarUtil();
MathUtil.PolarUtil polarRight = new MathUtil.PolarUtil();
private void fetchAxeInput(@NotNull MinecraftClient client, @NotNull GLFWGamepadState gamepadState, boolean leftJoycon) {
var buffer = gamepadState.axes();
polarLeft.calculate(buffer.get(GLFW_GAMEPAD_AXIS_LEFT_X), buffer.get(GLFW_GAMEPAD_AXIS_LEFT_Y), 1, MidnightControlsConfig.leftDeadZone);
float leftX = polarLeft.polarX;
float leftY = polarLeft.polarY;
polarRight.calculate(buffer.get(GLFW_GAMEPAD_AXIS_RIGHT_X), buffer.get(GLFW_GAMEPAD_AXIS_RIGHT_Y), 1, MidnightControlsConfig.rightDeadZone);
float rightX = polarRight.polarX;
float rightY = polarRight.polarY;
for (int i = 0; i < buffer.limit(); i++) {
int axis = leftJoycon ? ButtonBinding.controller2Button(i) : i;
float value = buffer.get();
if (MidnightControlsConfig.analogMovement) {
switch (i) {
case GLFW_GAMEPAD_AXIS_LEFT_X -> value = leftX;
case GLFW_GAMEPAD_AXIS_LEFT_Y -> value = leftY;
}
}
switch (i) {
case GLFW_GAMEPAD_AXIS_RIGHT_X -> value = rightX;
case GLFW_GAMEPAD_AXIS_RIGHT_Y -> value = rightY;
}
float absValue = Math.abs(value);
if (i == GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y)
value *= -1.0F;
int state = value > MidnightControlsConfig.rightDeadZone ? 1 : (value < -MidnightControlsConfig.rightDeadZone ? 2 : 0);
if (!(client.currentScreen instanceof RingScreen || (MidnightControlsCompat.isEmotecraftPresent() && EmotecraftCompat.isEmotecraftScreen(client.currentScreen)))) this.handleAxe(client, axis, value, absValue, state);
}
if (client.currentScreen instanceof RingScreen || (MidnightControlsCompat.isEmotecraftPresent() && EmotecraftCompat.isEmotecraftScreen(client.currentScreen))) {
float x = leftX;
float y = leftY;
if (x == 0 && y == 0) {
x = rightX;
y = rightY;
}
int index = -1;
float border = 0.3f;
if (x < -border) {
index = 3;
if (y < -border) index = 0;
else if (y > border) index = 5;
} else if (x > border) {
index = 4;
if (y < -border) index = 2;
else if (y > border) index = 7;
} else {
if (y < -border) index = 1;
else if (y > border) index = 6;
}
if (client.currentScreen instanceof RingScreen && index > -1) RingPage.selected = index;
if (MidnightControlsCompat.isEmotecraftPresent() && EmotecraftCompat.isEmotecraftScreen(client.currentScreen)) EmotecraftCompat.handleEmoteSelector(index);
}
}
public void handleButton(@NotNull MinecraftClient client, int button, int action, boolean state) {
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
if (action == 0 && !this.controlsInput.currentButtons.contains(button)) {
this.controlsInput.currentButtons.add(button);
var buttons = new int[this.controlsInput.currentButtons.size()];
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
buttons[i] = this.controlsInput.currentButtons.get(i);
this.controlsInput.focusedBinding.setButton(buttons);
this.controlsInput.waiting = false;
}
return;
}
if (client.currentScreen != null && (action == 0 || action == 2) && button == GLFW_GAMEPAD_BUTTON_Y &&
MidnightControlsConfig.arrowScreens.contains(client.currentScreen.getClass().getCanonicalName())) {
pressKeyboardKey(client, GLFW.GLFW_KEY_ENTER);
this.screenCloseCooldown = 5;
}
else if (action == 0 || action == 2) {
if (client.currentScreen != null
&& (button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN
|| button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT || button == GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)) {
if (this.actionGuiCooldown == 0) {
switch (button) {
case GLFW_GAMEPAD_BUTTON_DPAD_UP -> this.changeFocus(client.currentScreen, NavigationDirection.UP);
case GLFW_GAMEPAD_BUTTON_DPAD_DOWN -> this.changeFocus(client.currentScreen, NavigationDirection.DOWN);
case GLFW_GAMEPAD_BUTTON_DPAD_LEFT -> this.handleLeftRight(client.currentScreen, false);
case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT -> this.handleLeftRight(client.currentScreen, true);
}
if (MidnightControlsConfig.wasdScreens.contains(client.currentScreen.getClass().getCanonicalName())) {
switch (button) {
case GLFW_GAMEPAD_BUTTON_DPAD_UP -> pressKeyboardKey(client, GLFW.GLFW_KEY_W);
case GLFW_GAMEPAD_BUTTON_DPAD_DOWN -> pressKeyboardKey(client, GLFW.GLFW_KEY_S);
case GLFW_GAMEPAD_BUTTON_DPAD_LEFT -> pressKeyboardKey(client, GLFW.GLFW_KEY_A);
case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT -> pressKeyboardKey(client, GLFW.GLFW_KEY_D);
}
}
}
return;
}
}
if (action == 1) {
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null) {
if (this.actionGuiCooldown == 0) {
var focused = client.currentScreen.getFocused();
if (focused != null && isScreenInteractive(client.currentScreen)) {
if (this.handleAButton(client.currentScreen, focused)) {
this.actionGuiCooldown = 5; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
return;
}
}
else if (FabricLoader.getInstance().isModLoaded("libgui")) LibGuiCompat.handlePress(client.currentScreen);
}
}
}
if (button == GLFW.GLFW_GAMEPAD_BUTTON_A && client.currentScreen != null && !isScreenInteractive(client.currentScreen)
&& this.actionGuiCooldown == 0) {
if (client.currentScreen instanceof HandledScreen<?> handledScreen && ((HandledScreenAccessor) handledScreen).midnightcontrols$getSlotAt(
client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth(),
client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight()) != null) return;
if (!this.ignoreNextARelease && client.currentScreen != null) {
double mouseX = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
if (action == 0) {
Screen.wrapScreenError(() -> {
((MouseAccessor) client.mouse).setLeftButtonClicked(false);
client.currentScreen.mouseClicked(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1);
},
"mouseClicked event handler", client.currentScreen.getClass().getCanonicalName());
} else if (action == 1) {
Screen.wrapScreenError(() -> {
((MouseAccessor) client.mouse).setLeftButtonClicked(false);
client.currentScreen.mouseReleased(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_1);
},
"mouseReleased event handler", client.currentScreen.getClass().getCanonicalName());
} else if (action == 2) {
Screen.wrapScreenError(() -> {
client.currentScreen.setDragging(true);
((MouseAccessor) client.mouse).setLeftButtonClicked(true);
((MouseAccessor) client.mouse).midnightcontrols$onCursorPos(client.getWindow().getHandle(), client.mouse.getX(), client.mouse.getY());
client.currentScreen.setDragging(false);
},
"mouseClicked event handler", client.currentScreen.getClass().getCanonicalName());
}
this.screenCloseCooldown = 5;
} else {
this.ignoreNextARelease = false;
}
}
else if (button == GLFW.GLFW_GAMEPAD_BUTTON_X && client.currentScreen != null && !isScreenInteractive(client.currentScreen)
&& this.actionGuiCooldown == 0) {
if (client.currentScreen instanceof HandledScreen<?> handledScreen && ((HandledScreenAccessor) handledScreen).midnightcontrols$getSlotAt(
client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth(),
client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight()) != null) return;
if (!this.ignoreNextXRelease && client.currentScreen != null) {
double mouseX = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
if (action == 0) {
Screen.wrapScreenError(() -> client.currentScreen.mouseClicked(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_2),
"mouseClicked event handler", client.currentScreen.getClass().getCanonicalName());
} else if (action == 1) {
Screen.wrapScreenError(() -> client.currentScreen.mouseReleased(mouseX, mouseY, GLFW.GLFW_MOUSE_BUTTON_2),
"mouseReleased event handler", client.currentScreen.getClass().getCanonicalName());
}
this.screenCloseCooldown = 5;
} else {
this.ignoreNextXRelease = false;
}
}
}
public void pressKeyboardKey(MinecraftClient client, int key) {
client.keyboard.onKey(client.getWindow().getHandle(), key, 0, 1, 0);
}
public void pressKeyboardKey(Screen screen, int key) {
screen.keyPressed(key, 0, 1);
}
/**
/**
* Tries to go back.
*
* @param screen the current screen
* @return true if successful, else false
*/
public boolean tryGoBack(@NotNull Screen screen) {
var set = ImmutableSet.of("gui.back", "gui.done", "gui.cancel", "gui.toTitle", "gui.toMenu");
return screen.children().stream().filter(element -> element instanceof PressableWidget)
.map(element -> (PressableWidget) element)
.filter(element -> element.getMessage() != null && element.getMessage().getContent() != null)
.anyMatch(element -> {
if (element.getMessage().getContent() instanceof TranslatableTextContent translatableText) {
if (set.stream().anyMatch(key -> translatableText.getKey().equals(key))) {
element.onPress();
return true;
}
}
return false;
});
}
private double getDeadZoneValue(int axis) {
return (axis == GLFW_GAMEPAD_AXIS_LEFT_X || axis == GLFW_GAMEPAD_AXIS_LEFT_Y) ? MidnightControlsConfig.leftDeadZone
: MidnightControlsConfig.rightDeadZone;
}
private void handleAxe(@NotNull MinecraftClient client, int axis, float value, float absValue, int state) {
int asButtonState = value > .5f ? 1 : (value < -.5f ? 2 : 0);
if (axis == GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER
|| axis == ButtonBinding.controller2Button(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER)
|| axis == ButtonBinding.controller2Button(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER)) {
if (asButtonState == 2) {
asButtonState = 0;
}
else {
// Fixes Triggers not working correctly on some controllers
if (MidnightControlsConfig.triggerFix) {
value = 1.0f;
absValue = 1.0f;
state = 1;
asButtonState = 1;
}
//if (MidnightControlsConfig.debug) System.out.println(axis + " "+ value + " " + absValue + " " + state);
}
}
{
boolean currentPlusState = value > getDeadZoneValue(axis);
boolean currentMinusState = value < -getDeadZoneValue(axis);
if (axis == GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || axis == GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER) currentMinusState = false;
if (!MidnightControlsConfig.analogMovement && (axis == GLFW_GAMEPAD_AXIS_LEFT_X || axis == GLFW_GAMEPAD_AXIS_LEFT_Y)) {
currentPlusState = asButtonState == 1;
currentMinusState = asButtonState == 2;
}
var previousPlusState = InputManager.STATES.getOrDefault(ButtonBinding.axisAsButton(axis, true), ButtonState.NONE);
var previousMinusState = InputManager.STATES.getOrDefault(ButtonBinding.axisAsButton(axis, false), ButtonState.NONE);
if (currentPlusState != previousPlusState.isPressed()) {
InputManager.STATES.put(ButtonBinding.axisAsButton(axis, true), currentPlusState ? ButtonState.PRESS : ButtonState.RELEASE);
if (currentPlusState)
BUTTON_COOLDOWNS.put(ButtonBinding.axisAsButton(axis, true), 5);
} else if (currentPlusState) {
InputManager.STATES.put(ButtonBinding.axisAsButton(axis, true), ButtonState.REPEAT);
if (BUTTON_COOLDOWNS.getOrDefault(ButtonBinding.axisAsButton(axis, true), 0) == 0) {
BUTTON_COOLDOWNS.put(ButtonBinding.axisAsButton(axis, true), 5);
}
}
if (currentMinusState != previousMinusState.isPressed()) {
InputManager.STATES.put(ButtonBinding.axisAsButton(axis, false), currentMinusState ? ButtonState.PRESS : ButtonState.RELEASE);
if (currentMinusState)
BUTTON_COOLDOWNS.put(ButtonBinding.axisAsButton(axis, false), 5);
} else if (currentMinusState) {
InputManager.STATES.put(ButtonBinding.axisAsButton(axis, false), ButtonState.REPEAT);
if (BUTTON_COOLDOWNS.getOrDefault(ButtonBinding.axisAsButton(axis, false), 0) == 0) {
BUTTON_COOLDOWNS.put(ButtonBinding.axisAsButton(axis, false), 5);
}
}
float axisValue = absValue;
if (!MidnightControlsConfig.analogMovement) {
double deadZone = this.getDeadZoneValue(axis);
axisValue = (float) (absValue - deadZone);
axisValue /= (float) (1.0 - deadZone);
axisValue *= (float) deadZone;
}
axisValue = (float) Math.min(axisValue / MidnightControlsConfig.getAxisMaxValue(axis), 1);
if (currentPlusState)
InputManager.BUTTON_VALUES.put(ButtonBinding.axisAsButton(axis, true), axisValue);
else
InputManager.BUTTON_VALUES.put(ButtonBinding.axisAsButton(axis, true), 0.f);
if (currentMinusState)
InputManager.BUTTON_VALUES.put(ButtonBinding.axisAsButton(axis, false), axisValue);
else
InputManager.BUTTON_VALUES.put(ButtonBinding.axisAsButton(axis, false), 0.f);
}
double deadZone = this.getDeadZoneValue(axis);
if (this.controlsInput != null && this.controlsInput.focusedBinding != null) {
if (asButtonState != 0 && !this.controlsInput.currentButtons.contains(ButtonBinding.axisAsButton(axis, asButtonState == 1))) {
this.controlsInput.currentButtons.add(ButtonBinding.axisAsButton(axis, asButtonState == 1));
int[] buttons = new int[this.controlsInput.currentButtons.size()];
for (int i = 0; i < this.controlsInput.currentButtons.size(); i++)
buttons[i] = this.controlsInput.currentButtons.get(i);
this.controlsInput.focusedBinding.setButton(buttons);
this.controlsInput.waiting = false;
}
return;
} else if (client.currentScreen instanceof CreativeInventoryScreen creativeInventoryScreen) {
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
var accessor = (CreativeInventoryScreenAccessor) creativeInventoryScreen;
// @TODO allow rebinding to left stick
if (accessor.midnightcontrols$hasScrollbar() && absValue >= deadZone) {
creativeInventoryScreen.mouseScrolled(0.0, 0.0, 0, -value);
}
return;
}
} else if (client.currentScreen instanceof MerchantScreen merchantScreen) {
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
// @TODO allow rebinding to left stick
if (absValue >= deadZone) {
merchantScreen.mouseScrolled(0.0, 0.0, 0, -(value * 1.5f));
}
return;
}
} else if (client.currentScreen instanceof StonecutterScreen stonecutterScreen) {
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
// @TODO allow rebinding to left stick
if (absValue >= deadZone) {
stonecutterScreen.mouseScrolled(0.0, 0.0, 0, -(value * 1.5f));
}
return;
}
} else if (client.currentScreen instanceof AdvancementsScreen advancementsScreen) {
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X || axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
var accessor = (AdvancementsScreenAccessor) advancementsScreen;
if (absValue >= deadZone) {
AdvancementTab tab = accessor.getSelectedTab();
tab.move(axis == GLFW_GAMEPAD_AXIS_RIGHT_X ? -value * 5.0 : 0.0, axis == GLFW_GAMEPAD_AXIS_RIGHT_Y ? -value * 5.0 : 0.0);
}
return;
}
} else if (client.currentScreen != null) {
float finalValue = value;
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y && absValue >= deadZone && client.currentScreen.children().stream().filter(element -> element instanceof SpruceEntryListWidget)
.map(element -> (SpruceEntryListWidget<?>) element)
.filter(AbstractSpruceWidget::isFocusedOrHovered)
.noneMatch(element -> {
element.mouseScrolled(0.0, 0.0, 0, -finalValue);
return true;
})
&&
client.currentScreen.children().stream().filter(element -> element instanceof EntryListWidget)
.map(element -> (EntryListWidget<?>) element)
.filter(element -> element.getType().isFocused())
.noneMatch(element -> {
element.mouseScrolled(0.0, 0.0, 0, -finalValue);
return true;
}))
{
client.currentScreen.mouseScrolled(0.0, 0.0, 0, -(value * 1.5f));
}
else if (isScreenInteractive(client.currentScreen) && absValue >= deadZone) {
if (value > 0 && joystickCooldown == 0) {
switch (axis) {
case GLFW_GAMEPAD_AXIS_LEFT_Y -> this.changeFocus(client.currentScreen, NavigationDirection.UP);
case GLFW_GAMEPAD_AXIS_LEFT_X -> this.handleLeftRight(client.currentScreen, true);
}
if (axis == GLFW_GAMEPAD_AXIS_LEFT_Y || axis == GLFW_GAMEPAD_AXIS_LEFT_X) joystickCooldown = 4;
} else if (value < 0 && joystickCooldown == 0) {
switch (axis) {
case GLFW_GAMEPAD_AXIS_LEFT_Y -> this.changeFocus(client.currentScreen, NavigationDirection.DOWN);
case GLFW_GAMEPAD_AXIS_LEFT_X -> this.handleLeftRight(client.currentScreen, false);
}
if (axis == GLFW_GAMEPAD_AXIS_LEFT_Y || axis == GLFW_GAMEPAD_AXIS_LEFT_X) joystickCooldown = 4;
}
return;
}
}
absValue = (float) MathHelper.clamp(absValue / MidnightControlsConfig.getAxisMaxValue(axis), 0.f, 1.f);
if (client.currentScreen == null) {
// Handles the look direction.
this.handleLook(client, axis, absValue, state);
} else {
boolean allowMouseControl = true;
if (this.actionGuiCooldown == 0 && MidnightControlsConfig.isMovementAxis(axis) && isScreenInteractive(client.currentScreen)) {
if (MidnightControlsConfig.isForwardButton(axis, false, asButtonState)) {
allowMouseControl = this.changeFocus(client.currentScreen, NavigationDirection.UP);
} else if (MidnightControlsConfig.isBackButton(axis, false, asButtonState)) {
allowMouseControl = this.changeFocus(client.currentScreen, NavigationDirection.DOWN);
} else if (MidnightControlsConfig.isLeftButton(axis, false, asButtonState)) {
allowMouseControl = this.handleLeftRight(client.currentScreen, false);
} else if (MidnightControlsConfig.isRightButton(axis, false, asButtonState)) {
allowMouseControl = this.handleLeftRight(client.currentScreen, true);
}
}
float movementX = 0.f;
float movementY = 0.f;
if (MidnightControlsConfig.isBackButton(axis, false, (value > 0 ? 1 : 2))) {
movementY = absValue;
} else if (MidnightControlsConfig.isForwardButton(axis, false, (value > 0 ? 1 : 2))) {
movementY = -absValue;
} else if (MidnightControlsConfig.isLeftButton(axis, false, (value > 0 ? 1 : 2))) {
movementX = -absValue;
} else if (MidnightControlsConfig.isRightButton(axis, false, (value > 0 ? 1 : 2))) {
movementX = absValue;
}
if (client.currentScreen != null && allowMouseControl) {
boolean moving = movementY != 0 || movementX != 0;
if (moving) {
/*
Updates the target mouse position when the initial movement stick movement is detected.
It prevents the cursor to jump to the old target mouse position if the user moves the cursor with the mouse.
*/
if (Math.abs(prevXAxis) < deadZone && Math.abs(prevYAxis) < deadZone) {
InputManager.INPUT_MANAGER.resetMouseTarget(client);
}
this.mouseSpeedX = movementX;
this.mouseSpeedY = movementY;
} else {
this.mouseSpeedX = 0.f;
this.mouseSpeedY = 0.f;
}
if (Math.abs(this.mouseSpeedX) >= .05f || Math.abs(this.mouseSpeedY) >= .05f) {
InputManager.queueMoveMousePosition(
this.mouseSpeedX * MidnightControlsConfig.mouseSpeed,
this.mouseSpeedY * MidnightControlsConfig.mouseSpeed
);
}
this.moveMouseToClosestSlot(client, client.currentScreen);
}
this.prevXAxis = movementX;
this.prevYAxis = movementY;
}
}
public boolean handleAButton(@NotNull Screen screen, @NotNull Element focused) {
if (focused instanceof PressableWidget widget) {
widget.playDownSound(MinecraftClient.getInstance().getSoundManager());
widget.onPress();
return true;
} else if (focused instanceof AbstractSprucePressableButtonWidget widget) {
widget.playDownSound();
widget.onPress();
return true;
} else if (focused instanceof SpruceLabelWidget labelWidget) {
labelWidget.onPress();
return true;
} else if (focused instanceof WorldListWidget list) {
list.getSelectedAsOptional().ifPresent(WorldListWidget.WorldEntry::play);
return true;
} else if (focused instanceof MultiplayerServerListWidget list) {
var entry = list.getSelectedOrNull();
if (entry instanceof MultiplayerServerListWidget.LanServerEntry || entry instanceof MultiplayerServerListWidget.ServerEntry) {
((MultiplayerScreen) screen).select(entry);
((MultiplayerScreen) screen).connect();
}
} else if (focused instanceof SpruceParentWidget) {
var childFocused = ((SpruceParentWidget<?>) focused).getFocused();
if (childFocused != null)
return this.handleAButton(screen, childFocused);
} else if (focused instanceof ParentElement widget) {
var childFocused = widget.getFocused();
if (childFocused != null)
return this.handleAButton(screen, childFocused);
} else if (FabricLoader.getInstance().isModLoaded("yet-another-config-lib") && YACLCompat.handleAButton(screen, focused)) {
return true;
}
else pressKeyboardKey(screen, GLFW_KEY_ENTER);
return false;
}
/**
* Handles the left and right buttons.
*
* @param screen the current screen
* @param right true if the right button is pressed, else false
*/
private boolean handleLeftRight(@NotNull Screen screen, boolean right) {
if (screen instanceof SpruceScreen spruceScreen) {
spruceScreen.onNavigation(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false);
this.actionGuiCooldown = 5;
return false;
}
if (FabricLoader.getInstance().isModLoaded("yet-another-config-lib") && YACLCompat.handleLeftRight(screen, right)) {
this.actionGuiCooldown = 5;
return false;
}
var focused = screen.getFocused();
if (focused != null)
if (this.handleRightLeftElement(focused, right))
return this.changeFocus(screen, right ? NavigationDirection.RIGHT : NavigationDirection.LEFT);
return true;
}
private boolean handleRightLeftElement(@NotNull Element element, boolean right) {
switch (element) {
case SpruceElement spruceElement -> {
if (spruceElement.requiresCursor())
return true;
return !spruceElement.onNavigation(right ? NavigationDirection.RIGHT : NavigationDirection.LEFT, false);
}
case SliderWidget slider -> {
if (slider.active) {
slider.keyPressed(right ? 262 : 263, 0, 0);
this.actionGuiCooldown = 2; // Prevent to press too quickly the focused element, so we have to skip 5 ticks.
return true;
}
}
case AlwaysSelectedEntryListWidget<?> alwaysSelectedEntryListWidget -> {
//TODO ((EntryListWidgetAccessor) element).midnightcontrols$moveSelection(right ? EntryListWidget.MoveDirection.DOWN : EntryListWidget.MoveDirection.UP);
return false;
}
case ParentElement entryList -> {
var focused = entryList.getFocused();
if (focused == null)
return true;
return this.handleRightLeftElement(focused, right);
}
default -> {
}
}
return true;
}
private double prevX = 0;
private double prevY = 0;
private double xValue;
private int xState;
/**
* Handles the look direction input.
*
* @param client the client instance
* @param axis the axis to change
* @param value the value of the look
* @param state the state
*/
public void handleLook(@NotNull MinecraftClient client, int axis, float value, int state) {
if (client.player == null) return;
// Handles the look direction.
if (MidnightControlsConfig.cameraMode == CameraMode.FLAT) {
double powValue = Math.pow(value, 2.0);
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
if (state == 2) {
this.targetPitch = -MidnightControlsConfig.getRightYAxisSign() * (MidnightControlsConfig.yAxisRotationSpeed * powValue) * 0.11D;
} else if (state == 1) {
this.targetPitch = MidnightControlsConfig.getRightYAxisSign() * (MidnightControlsConfig.yAxisRotationSpeed * powValue) * 0.11D;
}
}
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X) {
if (state == 2) {
this.targetYaw = -MidnightControlsConfig.getRightXAxisSign() * (MidnightControlsConfig.rotationSpeed * powValue) * 0.11D;
} else if (state == 1) {
this.targetYaw = MidnightControlsConfig.getRightXAxisSign() * (MidnightControlsConfig.rotationSpeed * powValue) * 0.11D;
}
}
return;
}
// Code below runs for adaptive camera mode
// Handles the look direction.
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_X) {
xValue = value;
xState = state;
return;
}
if (axis == GLFW_GAMEPAD_AXIS_RIGHT_Y) {
double yStep = (MidnightControlsConfig.yAxisRotationSpeed / 100) * 0.6000000238418579 + 0.20000000298023224;
double xStep = (MidnightControlsConfig.rotationSpeed / 100) * 0.6000000238418579 + 0.20000000298023224;
double cursorDeltaX = 2 * xValue - this.prevX;
double cursorDeltaY = 2 * value - this.prevY;
boolean slowdown = client.options.getPerspective().isFirstPerson() && client.player.isUsingSpyglass();
double x = cursorDeltaX * xStep * (slowdown ? xStep : 1);
double y = cursorDeltaY * yStep * (slowdown ? yStep : 1);
double powXValue = Math.pow(x, 2.0);
double powYValue = Math.pow(y, 2.0);
if (state == 2) {
this.targetPitch = -MidnightControlsConfig.getRightYAxisSign() * (MidnightControlsConfig.yAxisRotationSpeed * powYValue) * 0.11D;
} else if (state == 1) {
this.targetPitch = MidnightControlsConfig.getRightYAxisSign() * (MidnightControlsConfig.yAxisRotationSpeed * powYValue) * 0.11D;
}
if (xState == 2) {
this.targetYaw = -MidnightControlsConfig.getRightXAxisSign() * (MidnightControlsConfig.rotationSpeed * powXValue) * 0.11D;
} else if (xState == 1) {
this.targetYaw = MidnightControlsConfig.getRightXAxisSign() * (MidnightControlsConfig.rotationSpeed * powXValue) * 0.11D;
}
this.prevY = value;
this.prevX = xValue;
}
}
private boolean changeFocus(@NotNull Screen screen, NavigationDirection direction) {
if (!isScreenInteractive(screen) && !screen.getClass().getCanonicalName().contains("me.jellysquid.mods.sodium.client.gui")) return false;
try {
if (screen instanceof SpruceScreen spruceScreen) {
if (spruceScreen.onNavigation(direction, false)) {
this.actionGuiCooldown = 5;
}
return true;
}
switch (direction) {
case UP -> pressKeyboardKey(screen, GLFW.GLFW_KEY_UP);
case DOWN -> pressKeyboardKey(screen, GLFW.GLFW_KEY_DOWN);
case LEFT -> pressKeyboardKey(screen, GLFW.GLFW_KEY_LEFT);
case RIGHT -> pressKeyboardKey(screen, GLFW.GLFW_KEY_RIGHT);
}
this.actionGuiCooldown = 5;
return true;
} catch (Exception exception) {MidnightControls.warn("Unknown exception encountered while trying to change focus: "+exception);}
return false;
}
public static boolean isScreenInteractive(@NotNull Screen screen) {
return !(screen instanceof HandledScreen || MidnightControlsConfig.joystickAsMouse || MidnightControlsConfig.mouseScreens.stream().anyMatch(a -> screen.getClass().toString().contains(a))
|| (screen instanceof SpruceScreen && ((SpruceScreen) screen).requiresCursor())
|| MidnightControlsCompat.requireMouseOnScreen(screen));
}
// Inspired from https://github.com/MrCrayfish/Controllable/blob/1.14.X/src/main/java/com/mrcrayfish/controllable/client/ControllerInput.java#L686.
private void moveMouseToClosestSlot(@NotNull MinecraftClient client, @Nullable Screen screen) {
// Makes the mouse attracted to slots. This helps with selecting items when using a controller.
if (screen instanceof HandledScreen<?> inventoryScreen) {
var accessor = (HandledScreenAccessor) inventoryScreen;
int guiLeft = accessor.getX();
int guiTop = accessor.getY();
int mouseX = (int) (targetMouseX * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
int mouseY = (int) (targetMouseY * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight());
// Finds the closest slot in the GUI within 14 pixels.
Optional<Pair<Slot, Double>> closestSlot = inventoryScreen.getScreenHandler().slots.parallelStream()
.map(slot -> {
int x = guiLeft + slot.x + 8;
int y = guiTop + slot.y + 8;
// Distance between the slot and the cursor.
double distance = Math.sqrt(Math.pow(x - mouseX, 2) + Math.pow(y - mouseY, 2));
return new Pair<Slot, Double>(slot, distance);
}).filter(entry -> entry.getRight() <= 14.0)
.min(Comparator.comparingDouble(Pair::getRight));
if (closestSlot.isPresent() && client.player != null) {
var slot = closestSlot.get().getLeft();
if (slot.hasStack() || !client.player.getInventory().getMainHandStack().isEmpty()) {
int slotCenterXScaled = guiLeft + slot.x + 8;
int slotCenterYScaled = guiTop + slot.y + 8;
int slotCenterX = (int) (slotCenterXScaled / ((double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth()));
int slotCenterY = (int) (slotCenterYScaled / ((double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight()));
double deltaX = slotCenterX - targetMouseX;
double deltaY = slotCenterY - targetMouseY;
if (mouseX != slotCenterXScaled || mouseY != slotCenterYScaled) {
this.targetMouseX += (int) (deltaX * 0.75);
this.targetMouseY += (int) (deltaY * 0.75);
} else {
this.mouseSpeedX *= 0.3F;
this.mouseSpeedY *= 0.3F;
}
this.mouseSpeedX *= .75F;
this.mouseSpeedY *= .75F;
} else {
this.mouseSpeedX *= .1F;
this.mouseSpeedY *= .1F;
}
} else {
this.mouseSpeedX *= .3F;
this.mouseSpeedY *= .3F;
}
} else {
this.mouseSpeedX = 0.F;
this.mouseSpeedY = 0.F;
}
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client;
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.FluidBlock;
import net.minecraft.block.SlabBlock;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents the reach-around API of midnightcontrols.
*
* @version 1.7.0
* @since 1.3.2
*/
public class MidnightReacharound {
private BlockHitResult lastReacharoundResult = null;
private boolean lastReacharoundVertical = false;
private boolean onSlab = false;
public void tick(@NotNull MinecraftClient client) {
this.lastReacharoundResult = this.tryVerticalReachAround(client);
if (this.lastReacharoundResult == null) {
this.lastReacharoundResult = this.tryHorizontalReachAround(client);
this.lastReacharoundVertical = false;
} else this.lastReacharoundVertical = true;
}
/**
* Returns the last reach around result.
*
* @return the last reach around result
*/
public @Nullable BlockHitResult getLastReacharoundResult() {
return this.lastReacharoundResult;
}
/**
* Returns whether the last reach around is vertical.
*
* @return {@code true} if the reach around is vertical
*/
public boolean isLastReacharoundVertical() {
return this.lastReacharoundVertical;
}
/**
* Returns whether reacharound is available or not.
*
* @return {@code true} if reacharound is available, else {@code false}
*/
public boolean isReacharoundAvailable() {
return MidnightControlsFeature.HORIZONTAL_REACHAROUND.isAvailable() || MidnightControlsFeature.VERTICAL_REACHAROUND.isAvailable();
}
public static float getPlayerRange(@NotNull MinecraftClient client) {
return client.player != null ? Double.valueOf(client.player.getAttributeValue(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE)).floatValue() : 0.f;
}
/**
* Returns a nullable block hit result if vertical reach-around is possible.
*
* @param client the client instance
* @return a block hit result if vertical reach-around is possible, else {@code null}
*/
public @Nullable BlockHitResult tryVerticalReachAround(@NotNull MinecraftClient client) {
if (!MidnightControlsFeature.VERTICAL_REACHAROUND.isAvailable())
return null;
if (client.player == null || client.world == null || client.crosshairTarget == null || client.crosshairTarget.getType() != HitResult.Type.MISS
|| !client.player.isOnGround() || client.player.getPitch(0.f) < 80.0F
|| client.player.isRiding())
return null;
Vec3d pos = client.player.getCameraPosVec(1.0F);
Vec3d rotationVec = client.player.getRotationVec(1.0F);
float range = getPlayerRange(client);
var rayVec = pos.add(rotationVec.x * range, rotationVec.y * range, rotationVec.z * range).add(0, 0.75, 0);
var result = client.world.raycast(new RaycastContext(pos, rayVec, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, client.player));
if (result.getType() == HitResult.Type.BLOCK) {
BlockPos blockPos = result.getBlockPos().down();
BlockState state = client.world.getBlockState(blockPos);
if (client.player.getBlockPos().getY() - blockPos.getY() > 1 && (client.world.isAir(blockPos) || state.isReplaceable())) {
return new BlockHitResult(result.getPos(), Direction.DOWN, blockPos, false);
}
}
return null;
}
/**
* Returns a nullable block hit result if horizontal reach-around is possible.
*
* @param client the client instance
* @return a block hit result if horizontal reach-around is possible
*/
public @Nullable BlockHitResult tryHorizontalReachAround(@NotNull MinecraftClient client) {
if (!MidnightControlsFeature.HORIZONTAL_REACHAROUND.isAvailable())
return null;
if (client.world != null && client.player != null && client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.MISS
&& client.player.isOnGround() && client.player.getPitch(0.f) >= 35.f) {
if (client.player.isRiding())
return null;
// Temporary pos, do not use
Vec3d playerPosi = client.player.getPos();
// Imitates var playerPos = client.player.getBlockPos().down();
Vec3d playerPos = new Vec3d(playerPosi.getX(), playerPosi.getY() - 1.0, playerPosi.getZ());
if (client.player.getY() - playerPos.getY() - 1.0 >= 0.25) {
// Imitates playerPos = playerPos.up();
playerPos = playerPosi;
this.onSlab = true;
} else {
this.onSlab = false;
}
var targetPos = new Vec3d(client.crosshairTarget.getPos().getX(), client.crosshairTarget.getPos().getY(), client.crosshairTarget.getPos().getZ()).subtract(playerPos);
var vector = new Vec3d(MathHelper.clamp(targetPos.getX(), -1, 1), 0, MathHelper.clamp(targetPos.getZ(), -1, 1));
var blockPos = playerPos.add(vector);
// Some functions still need BlockPos, so this is here to let that happen
var blockyPos = BlockPos.ofFloored(blockPos);
var direction = client.player.getHorizontalFacing();
var state = client.world.getBlockState(blockyPos);
if (!state.isAir())
return null;
var adjacentBlockState = client.world.getBlockState(blockyPos.offset(direction.getOpposite()));
if (adjacentBlockState.isAir() || adjacentBlockState.getBlock() instanceof FluidBlock || (vector.getX() == 0 && vector.getZ() == 0)) {
return null;
}
return new BlockHitResult(blockPos, direction, blockyPos, false);
}
return null;
}
public @NotNull BlockHitResult withSideForReacharound(@NotNull BlockHitResult result, @Nullable ItemStack stack) {
if (stack == null || stack.isEmpty() || !(stack.getItem() instanceof BlockItem))
return result;
return withSideForReacharound(result, Block.getBlockFromItem(stack.getItem()));
}
public @NotNull BlockHitResult withSideForReacharound(@NotNull BlockHitResult result, @NotNull Block block) {
if (block instanceof SlabBlock) {
if (this.onSlab) result = result.withSide(Direction.UP);
else result = result.withSide(Direction.DOWN);
}
return result;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright © 2022 Motschen <motschen@midnightdust.eu>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.compat;
import me.juancarloscp52.bedrockify.client.BedrockifyClient;
import me.juancarloscp52.bedrockify.client.BedrockifyClientSettings;
/**
* Represents HQM compatibility handler.
*
* @author Motschen
* @version 1.7.0
* @since 1.7.0
*/
public class BedrockifyCompat implements CompatHandler {
@Override
public void handle() {
BedrockifyClient.getInstance().settings.disableFlyingMomentum = false;
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.compat;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.screen.slot.Slot;
import net.minecraft.util.hit.BlockHitResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a compatibility handler for a mod.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.1.0
*/
public interface CompatHandler {
/**
* Handles compatibility of a mod.
*/
default void handle() {
handle(MidnightControlsClient.get());
}
/**
* Handles compatibility of a mod.
*
* @param mod this mod instance
*/
@Deprecated
default void handle(@NotNull MidnightControlsClient mod) {}
/**
* Handles custom camera updates
*
* @param client the Minecraft client instance
*/
default void handleCamera(@NotNull MinecraftClient client, double targetYaw, double targetPitch) {};
/**
* Handles custom tab behavior
*
* @param forward whether the direction is forward or backward
*/
default boolean handleTabs(Screen screen, boolean forward) {
return false;
};
/**
* Returns whether the mouse is required on the specified screen.
*
* @param screen the screen
* @return true if the mouse is required on the specified screen, else false
*/
default boolean requireMouseOnScreen(Screen screen) {
return false;
}
/**
* Returns a slot at the specified location if possible.
*
* @param screen the screen
* @param mouseX the mouse X-coordinate
* @param mouseY the mouse Y-coordinate
* @return a slot if present, else null
* @since 1.5.0
*/
default @Nullable CompatHandler.SlotPos getSlotAt(@NotNull Screen screen, int mouseX, int mouseY) {
return null;
}
/**
* Returns whether the current slot is a creative slot or not.
*
* @param screen the screen
* @param slot the slot to check
* @return true if the slot is a creative slot, else false
*/
default boolean isCreativeSlot(@NotNull HandledScreen screen, @NotNull Slot slot) {
return false;
}
/**
* Returns a custom translation key to make custom attack action strings on the HUD.
*
* @param client the client instance
* @param placeResult the last place block result
* @return null if untouched, else a translation key
*/
default String getAttackActionAt(@NotNull MinecraftClient client, @Nullable BlockHitResult placeResult) {
return null;
}
/**
* Returns a custom translation key to make custom use action strings on the HUD.
*
* @param client the client instance
* @param placeResult the last place block result
* @return null if untouched, else a translation key
*/
default String getUseActionAt(@NotNull MinecraftClient client, @Nullable BlockHitResult placeResult) {
return null;
}
/**
* Handles the menu back button.
*
* @param client the client instance
* @param screen the screen
* @return true if the handle was fired and succeed, else false
*/
default boolean handleMenuBack(@NotNull MinecraftClient client, @NotNull Screen screen) {
return false;
}
record SlotPos(int x, int y) {
public static final SlotPos INVALID_SLOT = new SlotPos(-1, -1);
}
}

View File

@@ -0,0 +1,46 @@
package eu.midnightdust.midnightcontrols.client.compat;
import dev.emi.emi.api.EmiApi;
import dev.emi.emi.config.EmiConfig;
import dev.emi.emi.screen.EmiScreenManager;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import net.minecraft.util.Identifier;
import org.lwjgl.glfw.GLFW;
public class EMICompat implements CompatHandler {
public static boolean handleEmiPages(boolean direction) {
if (isEMIEnabled() && MidnightControlsClient.input.actionGuiCooldown == 0 && EmiScreenManager.getSearchPanel() != null && EmiScreenManager.getSearchPanel().pageLeft != null && EmiScreenManager.getSearchPanel().pageRight != null) {
if (direction) EmiScreenManager.getSearchPanel().pageRight.onPress();
else EmiScreenManager.getSearchPanel().pageLeft.onPress();
MidnightControlsClient.input.actionGuiCooldown = 5;
return true;
}
return false;
}
@Override
public void handle() {
ButtonCategory category = new ButtonCategory(Identifier.of("midnightcontrols","category.emi"));
InputManager.registerCategory(category);
new ButtonBinding.Builder("emi_page_left")
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true))
.category(category)
.action((client,action,value,buttonState)->handleEmiPages(false)).cooldown()
.filter(((client, buttonBinding) -> EmiApi.getHandledScreen() != null))
.register();
new ButtonBinding.Builder("emi_page_right")
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true))
.category(category)
.action((client,action,value,buttonState)->handleEmiPages(true)).cooldown()
.filter(((client, buttonBinding) -> EmiApi.getHandledScreen() != null))
.register();
}
public static boolean isEMIEnabled() {
return EmiConfig.enabled;
}
public static boolean isSearchBarCentered() {
return EmiConfig.centerSearchBar;
}
}

View File

@@ -0,0 +1,34 @@
package eu.midnightdust.midnightcontrols.client.compat;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import io.github.kosmx.emotes.arch.gui.EmoteMenuImpl;
import io.github.kosmx.emotes.arch.gui.screen.ingame.FastChosseScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
public class EmotecraftCompat {
private static final MinecraftClient client = MinecraftClient.getInstance();
public static void openEmotecraftScreen(Screen parent) {
client.setScreen(new EmoteMenuImpl(parent));
}
public static boolean isEmotecraftScreen(Screen screen) {
return screen instanceof FastChosseScreen;
}
public static void handleEmoteSelector(int index) {
if (client.currentScreen instanceof FastChosseScreen) {
int x = client.getWindow().getWidth() / 2;
int y = client.getWindow().getHeight() / 2;
if (index == 0) InputManager.queueMousePosition(x-200, y-200);
if (index == 1) InputManager.queueMousePosition(x, y-200);
if (index == 2) InputManager.queueMousePosition(x+200, y-200);
if (index == 3) InputManager.queueMousePosition(x-200, y);
if (index == 4) InputManager.queueMousePosition(x+200, y);
if (index == 5) InputManager.queueMousePosition(x-200, y+200);
if (index == 6) InputManager.queueMousePosition(x, y+200);
if (index == 7) InputManager.queueMousePosition(x+200, y+200);
InputManager.INPUT_MANAGER.updateMousePosition(client);
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.compat;
import net.minecraft.client.gui.screen.Screen;
import org.aperlambda.lambdacommon.utils.LambdaReflection;
import java.util.Optional;
/**
* Represents HQM compatibility handler.
* <p>
* This is bad.
*
* @author LambdAurora
* @version 1.3.2
* @since 1.3.2
*/
public class HQMCompat implements CompatHandler {
public static final String GUI_BASE_CLASS_PATH = "hardcorequesting.client.interfaces.GuiBase";
private Optional<Class<?>> guiBaseClass;
@Override
public void handle() {
this.guiBaseClass = LambdaReflection.getClass(GUI_BASE_CLASS_PATH);
}
@Override
public boolean requireMouseOnScreen(Screen screen) {
return this.guiBaseClass.map(clazz -> clazz.isInstance(screen)).orElse(false);
}
}

View File

@@ -0,0 +1,44 @@
package eu.midnightdust.midnightcontrols.client.compat;
import com.kqp.inventorytabs.tabs.TabManager;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
public class InventoryTabsCompat implements CompatHandler {
private static InventoryTabsCompat INSTANCE;
@Override
public void handle() {
INSTANCE = this;
}
@Override
public boolean handleTabs(Screen screen, boolean next) {
if (screen instanceof HandledScreen<?> && !(screen instanceof CreativeInventoryScreen)) {
TabManager tabManager = TabManager.getInstance();
int tabIndex = tabManager.tabs.indexOf(tabManager.currentTab);
if (next) {
if (tabIndex < tabManager.tabs.size() - 1) tabManager.onTabClick(tabManager.tabs.get(tabIndex + 1));
else tabManager.onTabClick(tabManager.tabs.get(0));
} else {
if (tabIndex > 0) tabManager.onTabClick(tabManager.tabs.get(tabIndex - 1));
else tabManager.onTabClick(tabManager.tabs.get(tabManager.tabs.size() - 1));
}
return true;
}
return false;
}
public static boolean handleInventoryTabs(Screen screen, boolean next) {
return INSTANCE.handleTabs(screen, next);
}
public static void handleInventoryPage(Screen screen, boolean next) {
if (screen instanceof HandledScreen<?> && !(screen instanceof CreativeInventoryScreen)) {
TabManager tabManager = TabManager.getInstance();
if (next) {
if (tabManager.canGoForwardAPage()) tabManager.setCurrentPage(tabManager.currentPage + 1);
} else {
if (tabManager.canGoBackAPage()) tabManager.setCurrentPage(tabManager.currentPage - 1);
}
}
}
}

View File

@@ -0,0 +1,25 @@
package eu.midnightdust.midnightcontrols.client.compat;
import io.github.cottonmc.cotton.gui.impl.client.CottonScreenImpl;
import io.github.cottonmc.cotton.gui.widget.WButton;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.sound.PositionedSoundInstance;
import net.minecraft.sound.SoundEvents;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("UnstableApiUsage")
public class LibGuiCompat {
public static boolean handlePress(@NotNull Screen screen) {
if (screen instanceof CottonScreenImpl cottonScreen) {
if (cottonScreen.getDescription() != null && cottonScreen.getDescription().getFocus() != null) {
if (cottonScreen.getDescription().getFocus() instanceof WButton button && button.getOnClick() != null) {
button.getOnClick().run();
MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.compat;
import eu.midnightdust.lib.util.PlatformFunctions;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.hit.BlockHitResult;
import org.aperlambda.lambdacommon.utils.LambdaReflection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import static eu.midnightdust.midnightcontrols.MidnightControls.log;
/**
* Represents a compatibility handler.
*
* @author LambdAurora, Motschen
* @version 1.10.0
* @since 1.1.0
*/
public class MidnightControlsCompat {
private static final List<CompatHandler> HANDLERS = new ArrayList<>();
/**
* Initializes compatibility with other mods if needed.
*/
public static void init() {
// "okzoomer" is the mod ID used by Fabric-compatible versions of Ok Zoomer. (5.0.0-beta.6 and below.)
// "ok_zoomer" is the mod ID used by Quilt-exclusive versions of Ok Zoomer. (5.0.0-beta.7 and above.)
if (FabricLoader.getInstance().isModLoaded("okzoomer") || FabricLoader.getInstance().isModLoaded("ok_zoomer")) {
log("Adding Ok Zoomer compatibility...");
registerCompatHandler(new OkZoomerCompat());
}
if (isEMIPresent()) {
log("Adding EMI compatibility...");
registerCompatHandler(new EMICompat());
}
if (FabricLoader.getInstance().isModLoaded("hardcorequesting") && LambdaReflection.doesClassExist(HQMCompat.GUI_BASE_CLASS_PATH)) {
log("Adding HQM compatibility...");
registerCompatHandler(new HQMCompat());
}
if (FabricLoader.getInstance().isModLoaded("bedrockify")) {
log("Adding Bedrockify compatibility...");
registerCompatHandler(new BedrockifyCompat());
}
if (PlatformFunctions.isModLoaded("yet-another-config-lib")) {
log("Adding YACL compatibility...");
registerCompatHandler(new YACLCompat());
}
if (PlatformFunctions.isModLoaded("sodium")) {
log("Adding Sodium compatibility...");
registerCompatHandler(new SodiumCompat());
}
if (PlatformFunctions.isModLoaded("inventorytabs")) {
log("Adding Inventory Tabs compatibility...");
registerCompatHandler(new InventoryTabsCompat());
}
HANDLERS.forEach(CompatHandler::handle);
InputManager.loadButtonBindings();
}
/**
* Registers a new compatibility handler.
*
* @param handler the compatibility handler to register
*/
public static void registerCompatHandler(@NotNull CompatHandler handler) {
HANDLERS.add(handler);
}
/**
* Streams through compatibility handlers.
*
* @return a stream of compatibility handlers
*/
public static Stream<CompatHandler> streamCompatHandlers() {
return HANDLERS.stream();
}
/**
* Returns whether the mouse is required on the specified screen.
*
* @param screen the screen
* @return true if the mouse is requried on the specified screen, else false
*/
public static boolean requireMouseOnScreen(Screen screen) {
return streamCompatHandlers().anyMatch(handler -> handler.requireMouseOnScreen(screen));
}
/**
* Handles custom tabs for modded screens
*
* @param screen the screen
* @return true if the handle was fired and succeed, else false
*/
public static boolean handleTabs(Screen screen, boolean forward) {
return streamCompatHandlers().anyMatch(handler -> handler.handleTabs(screen, forward));
}
/**
* Returns a slot at the specified location if possible.
*
* @param screen the screen
* @param mouseX the mouse X-coordinate
* @param mouseY the mouse Y-coordinate
* @return a slot if present, else null
*/
public static @Nullable CompatHandler.SlotPos getSlotAt(@NotNull Screen screen, int mouseX, int mouseY) {
for (var handler : HANDLERS) {
var slot = handler.getSlotAt(screen, mouseX, mouseY);
if (slot != null)
return slot;
}
return null;
}
/**
* Returns a custom translation key to make custom attack action strings on the HUD.
*
* @param client the client instance
* @param placeResult the last place block result
* @return null if untouched, else a translation key
*/
public static String getAttackActionAt(@NotNull MinecraftClient client, @Nullable BlockHitResult placeResult) {
for (CompatHandler handler : HANDLERS) {
String action = handler.getAttackActionAt(client, placeResult);
if (action != null) {
return action;
}
}
return null;
}
/**
* Returns a custom translation key to make custom use action strings on the HUD.
*
* @param client the client instance
* @param placeResult the last place block result
* @return null if untouched, else a translation key
*/
public static String getUseActionAt(@NotNull MinecraftClient client, @Nullable BlockHitResult placeResult) {
for (CompatHandler handler : HANDLERS) {
String action = handler.getUseActionAt(client, placeResult);
if (action != null) {
return action;
}
}
return null;
}
/**
* Handles the menu back button.
*
* @param client the client instance
* @param screen the screen
* @return true if the handle was fired and succeed, else false
*/
public static boolean handleMenuBack(@NotNull MinecraftClient client, @NotNull Screen screen) {
for (CompatHandler handler : HANDLERS) {
if (handler.handleMenuBack(client, screen))
return true;
}
return false;
}
/**
* Handles the camera movement.
*
* @param client the client instance
* @param targetYaw the target yaw
* @param targetPitch the target pitch
*/
public static void handleCamera(@NotNull MinecraftClient client, double targetYaw, double targetPitch) {
MidnightControlsCompat.HANDLERS.forEach(handler -> handler.handleCamera(client, targetYaw, targetPitch));
}
/**
* Returns whether EMI is present.
*
* @return true if EMI is present, else false
*/
public static boolean isEMIPresent() {
return PlatformFunctions.isModLoaded("emi");
}
/**
* Returns whether InventoryTabs is present.
*
* @return true if InventoryTabs is present, else false
*/
public static boolean isInventoryTabsPresent() {
return PlatformFunctions.isModLoaded("inventorytabs");
}
/**
* Returns whether Emotecraft is present.
*
* @return true if Emotecraft is present, else false
*/
public static boolean isEmotecraftPresent() {
return FabricLoader.getInstance().isModLoaded("emotecraft");
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.compat;
import eu.midnightdust.lib.util.PlatformFunctions;
import net.fabricmc.loader.api.FabricLoader;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
* This plugin is only present for the conditional mixins.
*
* @author LambdAurora & Motschen
* @version 1.6.0
* @since 1.2.0
*/
public class MidnightControlsMixinPlugin implements IMixinConfigPlugin {
private String mixinPackage;
@Override
public void onLoad(String mixinPackage) {
this.mixinPackage = mixinPackage + ".";
}
@Override
public String getRefMapperConfig() {
return null;
}
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
final String mixinName = mixinClassName.substring(this.mixinPackage.length());
final String packageName = mixinName.substring(0, mixinName.lastIndexOf('.'));
if (packageName.startsWith("sodium") && !PlatformFunctions.isModLoaded("sodium"))
return false;
return true;
}
@Override
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
}
@Override
public List<String> getMixins() {
return null;
}
@Override
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright © 2021-2022 Karen/あけみ <karen@akemi.ai>, LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of MidnightControls.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.compat;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import net.minecraft.client.option.KeyBinding;
import org.jetbrains.annotations.NotNull;
import org.aperlambda.lambdacommon.utils.LambdaReflection;
import org.lwjgl.glfw.GLFW;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Represents a compatibility handler for Ok Zoomer.
*
* @author Karen/あけみ, LambdAurora
* @version 1.4.3
* @since 1.1.0
*/
public class OkZoomerCompat implements CompatHandler {
private boolean didAllReflectionCallsSucceed = false;
private KeyBinding okZoomerZoomKey;
private KeyBinding okZoomerIncreaseZoomKey;
private KeyBinding okZoomerDecreaseZoomKey;
private KeyBinding okZoomerResetZoomKey;
private Method okZoomerAreExtraKeyBindsEnabledMethod;
public OkZoomerCompat() {
// These strings represent the names of the classes, fields, and methods we use from the Ok Zoomer API
String okZoomerZoomKeybindsClassString;
String okZoomerZoomKeyFieldString;
String okZoomerIncreaseZoomKeyFieldString;
String okZoomerDecreaseZoomKeyFieldString;
String okZoomerResetZoomKeyFieldString;
String okZoomerAreExtraKeyBindsEnabledMethodNameString;
// These variables represent the actual objects that we reflect to
Class<?> okZoomerZoomKeybindsClass;
Field okZoomerZoomKeyField;
Field okZoomerIncreaseZoomKeyField;
Field okZoomerDecreaseZoomKeyField;
Field okZoomerResetZoomKeyField;
// First, we need to determine which version of the Ok Zoomer API we're dealing with here.
if (LambdaReflection.doesClassExist("io.github.ennuil.okzoomer.keybinds.ZoomKeybinds")) {
// https://github.com/EnnuiL/OkZoomer/blob/5.0.0-beta.3+1.17.1/src/main/java/io/github/ennuil/okzoomer/keybinds/ZoomKeybinds.java
MidnightControls.log("Ok Zoomer version 5.0.0-beta.3 or below detected!");
okZoomerZoomKeybindsClassString = "io.github.ennuil.okzoomer.keybinds.ZoomKeybinds";
okZoomerZoomKeyFieldString = "zoomKey";
okZoomerIncreaseZoomKeyFieldString = "increaseZoomKey";
okZoomerDecreaseZoomKeyFieldString = "decreaseZoomKey";
okZoomerResetZoomKeyFieldString = "resetZoomKey";
okZoomerAreExtraKeyBindsEnabledMethodNameString = "areExtraKeybindsEnabled";
} else if (LambdaReflection.doesClassExist("io.github.ennuil.okzoomer.key_binds.ZoomKeyBinds")) {
// https://github.com/EnnuiL/OkZoomer/blob/5.0.0-beta.6+1.18.2/src/main/java/io/github/ennuil/okzoomer/key_binds/ZoomKeyBinds.java
MidnightControls.log("Ok Zoomer version 5.0.0-beta.6, 5.0.0-beta.5, or 5.0.0-beta.4 detected!");
okZoomerZoomKeybindsClassString = "io.github.ennuil.okzoomer.key_binds.ZoomKeyBinds";
okZoomerZoomKeyFieldString = "ZOOM_KEY";
okZoomerIncreaseZoomKeyFieldString = "INCREASE_ZOOM_KEY";
okZoomerDecreaseZoomKeyFieldString = "DECREASE_ZOOM_KEY";
okZoomerResetZoomKeyFieldString = "RESET_ZOOM_KEY";
okZoomerAreExtraKeyBindsEnabledMethodNameString = "areExtraKeyBindsEnabled";
} else if (LambdaReflection.doesClassExist("io.github.ennuil.ok_zoomer.key_binds.ZoomKeyBinds")) {
// https://github.com/EnnuiL/OkZoomer/blob/5.0.0-beta.7+1.18.2/src/main/java/io/github/ennuil/ok_zoomer/key_binds/ZoomKeyBinds.java
MidnightControls.log("Ok Zoomer version 5.0.0-beta.7 (Quilt) or above detected!");
okZoomerZoomKeybindsClassString = "io.github.ennuil.ok_zoomer.key_binds.ZoomKeyBinds";
okZoomerZoomKeyFieldString = "ZOOM_KEY";
okZoomerIncreaseZoomKeyFieldString = "INCREASE_ZOOM_KEY";
okZoomerDecreaseZoomKeyFieldString = "DECREASE_ZOOM_KEY";
okZoomerResetZoomKeyFieldString = "RESET_ZOOM_KEY";
okZoomerAreExtraKeyBindsEnabledMethodNameString = "areExtraKeyBindsEnabled";
} else {
// If all of the above checks fail, then the version of the Ok Zoomer API that the user is trying to use is too new.
MidnightControls.warn("The version of Ok Zoomer that you are currently using is too new, and is not yet supported by MidnightControls!");
return;
}
// Reflect to the ZoomKeyBinds (>= 5.0.0-beta.4) / ZoomKeybinds (<= 5.0.0-beta.3) class.
try {
okZoomerZoomKeybindsClass = Class.forName(okZoomerZoomKeybindsClassString);
} catch (ClassNotFoundException exception) {
// This theoretically should never happen.
MidnightControls.warn("MidnightControls failed to reflect to the Ok Zoomer keybinds class!");
exception.printStackTrace();
return;
}
// Reflect to all of the keybind fields.
try {
okZoomerZoomKeyField = okZoomerZoomKeybindsClass.getField(okZoomerZoomKeyFieldString);
okZoomerIncreaseZoomKeyField = okZoomerZoomKeybindsClass.getField(okZoomerIncreaseZoomKeyFieldString);
okZoomerDecreaseZoomKeyField = okZoomerZoomKeybindsClass.getField(okZoomerDecreaseZoomKeyFieldString);
okZoomerResetZoomKeyField = okZoomerZoomKeybindsClass.getField(okZoomerResetZoomKeyFieldString);
} catch (NoSuchFieldException exception) {
MidnightControls.warn("MidnightControls failed to reflect to the Ok Zoomer keybind fields!");
exception.printStackTrace();
return;
}
// Initialise KeyBinding objects
try {
okZoomerZoomKey = (KeyBinding) okZoomerZoomKeyField.get(null);
okZoomerIncreaseZoomKey = (KeyBinding) okZoomerIncreaseZoomKeyField.get(null);
okZoomerDecreaseZoomKey = (KeyBinding) okZoomerDecreaseZoomKeyField.get(null);
okZoomerResetZoomKey = (KeyBinding) okZoomerResetZoomKeyField.get(null);
} catch (IllegalAccessException exception) {
MidnightControls.warn("MidnightControls failed to reflect to the Ok Zoomer keybind objects!");
exception.printStackTrace();
return;
}
// Reflect to the areExtraKeyBindsEnabled (>= 5.0.0-beta.4) / areExtraKeybindsEnabled (<= 5.0.0-beta.3) method.
// TODO: Consider replacing this entirely with getExtraKeyBind (>= 5.0.0-beta.4) / getExtraKeybind (<= 5.0.0-beta.3) in the future.
try {
okZoomerAreExtraKeyBindsEnabledMethod = okZoomerZoomKeybindsClass.getDeclaredMethod(okZoomerAreExtraKeyBindsEnabledMethodNameString);
} catch (NoSuchMethodException exception) {
MidnightControls.warn("MidnightControls failed to reflect to an Ok Zoomer method (areExtraKeyBindsEnabled / areExtraKeybindsEnabled)!");
exception.printStackTrace();
return;
}
didAllReflectionCallsSucceed = true;
}
@Override
public void handle() {
if (didAllReflectionCallsSucceed) {
new ButtonBinding.Builder("zoom")
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW.GLFW_GAMEPAD_BUTTON_X)
.onlyInGame()
.cooldown(true)
.category(ButtonBinding.MISC_CATEGORY)
.linkKeybind(okZoomerZoomKey)
.register();
boolean okZoomerAreExtraKeyBindsEnabled = false;
try {
okZoomerAreExtraKeyBindsEnabled = (boolean) okZoomerAreExtraKeyBindsEnabledMethod.invoke(null);
} catch (IllegalAccessException exception) {
MidnightControls.warn("MidnightControls encountered an IllegalAccessException while attempting to invoke a reflected Ok Zoomer method (areExtraKeyBindsEnabled / areExtraKeybindsEnabled)!");
exception.printStackTrace();
} catch (InvocationTargetException exception) {
MidnightControls.warn("MidnightControls encountered an InvocationTargetException while attempting to invoke a reflected Ok Zoomer method (areExtraKeyBindsEnabled / areExtraKeybindsEnabled)!");
exception.printStackTrace();
}
if (okZoomerAreExtraKeyBindsEnabled) {
new ButtonBinding.Builder("zoom_in")
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true))
.onlyInGame()
.cooldown(true)
.category(ButtonBinding.MISC_CATEGORY)
.linkKeybind(okZoomerIncreaseZoomKey)
.register();
new ButtonBinding.Builder("zoom_out")
.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP, ButtonBinding.axisAsButton(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true))
.onlyInGame()
.cooldown(true)
.category(ButtonBinding.MISC_CATEGORY)
.linkKeybind(okZoomerDecreaseZoomKey)
.register();
new ButtonBinding.Builder("zoom_reset")
.onlyInGame()
.cooldown(true)
.category(ButtonBinding.MISC_CATEGORY)
.linkKeybind(okZoomerResetZoomKey)
.register();
}
}
}
}

View File

@@ -0,0 +1,20 @@
package eu.midnightdust.midnightcontrols.client.compat;
import eu.midnightdust.midnightcontrols.client.compat.mixin.sodium.SodiumOptionsGUIAccessor;
import me.jellysquid.mods.sodium.client.gui.SodiumOptionsGUI;
import net.minecraft.client.gui.screen.Screen;
public class SodiumCompat implements CompatHandler {
@Override
public boolean handleTabs(Screen screen, boolean direction) {
if (screen instanceof SodiumOptionsGUI optionsGUI) {
SodiumOptionsGUIAccessor accessor = (SodiumOptionsGUIAccessor) optionsGUI;
final int max = accessor.getPages().size()-1;
int i = accessor.getPages().indexOf(accessor.getCurrentPage());
i = (direction ? ((max > i) ? ++i : 0) : (i > 0 ? --i : max));
optionsGUI.setPage(accessor.getPages().get(i));
return true;
}
return false;
}
}

View File

@@ -0,0 +1,57 @@
package eu.midnightdust.midnightcontrols.client.compat;
import dev.isxander.yacl.gui.AbstractWidget;
import dev.isxander.yacl.gui.OptionListWidget;
import dev.isxander.yacl.gui.YACLScreen;
import dev.isxander.yacl.gui.controllers.ControllerWidget;
import dev.isxander.yacl.gui.controllers.slider.SliderControllerElement;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.screen.Screen;
import org.lwjgl.glfw.GLFW;
public class YACLCompat implements CompatHandler {
public static boolean handleAButton(Screen screen, Element element) {
if (element instanceof AbstractWidget abstractWidget) {
// imitate enter key press
return abstractWidget.keyPressed(GLFW.GLFW_KEY_ENTER, 0, 0);
}
return false;
}
public static boolean handleLeftRight(Screen screen, boolean direction) {
if (screen instanceof YACLScreen yaclScreen) {
SliderControllerElement focusedSlider = yaclScreen.optionList.children().stream()
.filter(OptionListWidget.OptionEntry.class::isInstance)
.map(entry -> ((OptionListWidget.OptionEntry) entry).widget)
.filter(ControllerWidget.class::isInstance)
.map(ControllerWidget.class::cast)
.filter(SliderControllerElement.class::isInstance)
.map(SliderControllerElement.class::cast)
.filter(ControllerWidget::isHovered)
.findFirst()
.orElse(null);
if (focusedSlider == null)
return false;
focusedSlider.incrementValue(direction ? 1 : -1);
return true;
}
return false;
}
@Override
public boolean handleTabs(Screen screen, boolean direction) {
if (screen instanceof YACLScreen yaclScreen) {
int categoryIdx = yaclScreen.getCurrentCategoryIdx();
if (direction) categoryIdx++; else categoryIdx--;
if (categoryIdx < 0) categoryIdx = yaclScreen.config.categories().size() - 1;
if (categoryIdx >= yaclScreen.config.categories().size()) categoryIdx = 0;
yaclScreen.changeCategory(categoryIdx);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,19 @@
package eu.midnightdust.midnightcontrols.client.compat.mixin.sodium;
import me.jellysquid.mods.sodium.client.gui.SodiumOptionsGUI;
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
import me.jellysquid.mods.sodium.client.gui.options.control.ControlElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
@Mixin(value = SodiumOptionsGUI.class, remap = false)
public interface SodiumOptionsGUIAccessor {
@Accessor
List<ControlElement<?>> getControls();
@Accessor
List<OptionPage> getPages();
@Accessor
OptionPage getCurrentPage();
}

View File

@@ -0,0 +1,656 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.controller;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.GameOptions;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.aperlambda.lambdacommon.utils.function.PairPredicate;
import org.aperlambda.lambdacommon.utils.function.Predicates;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import static org.lwjgl.glfw.GLFW.*;
/**
* Represents a button binding.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.0.0
*/
public class ButtonBinding {
public static final ButtonCategory MOVEMENT_CATEGORY;
public static final ButtonCategory GAMEPLAY_CATEGORY;
public static final ButtonCategory INVENTORY_CATEGORY;
public static final ButtonCategory MULTIPLAYER_CATEGORY;
public static final ButtonCategory MISC_CATEGORY;
public static final ButtonBinding ATTACK = new Builder("attack").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true)).onlyInGame().register();
public static final ButtonBinding BACK = new Builder("back").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, false))
.action(MovementHandler.HANDLER).onlyInGame().register();
public static final ButtonBinding CHAT = new Builder("chat").buttons(GLFW_GAMEPAD_BUTTON_DPAD_RIGHT).onlyInGame().cooldown().register();
public static final ButtonBinding CONTROLS_RING = new Builder("controls_ring").buttons(GLFW_GAMEPAD_BUTTON_GUIDE).onlyInGame().cooldown()
.action((client, button1, value, action) -> {
if (action.isPressed()) {
MidnightControlsClient.ring.loadFromUnbound();
client.setScreen(new RingScreen());
}
if (action.isUnpressed() && client.currentScreen != null) client.currentScreen.close();
return true;
}).register();
public static final ButtonBinding DROP_ITEM = new Builder("drop_item").buttons(GLFW_GAMEPAD_BUTTON_B).onlyInGame().cooldown().register();
public static final ButtonBinding FORWARD = new Builder("forward").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_Y, true))
.action(MovementHandler.HANDLER).onlyInGame().register();
public static final ButtonBinding HOTBAR_LEFT = new Builder("hotbar_left").buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
.action(InputHandlers.handleHotbar(false)).onlyInGame().cooldown().register();
public static final ButtonBinding HOTBAR_RIGHT = new Builder("hotbar_right").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
.action(InputHandlers.handleHotbar(true)).onlyInGame().cooldown().register();
public static final ButtonBinding INVENTORY = new Builder("inventory").buttons(GLFW_GAMEPAD_BUTTON_Y).onlyInGame().cooldown().register();
public static final ButtonBinding EXIT = new Builder("exit").buttons(GLFW_GAMEPAD_BUTTON_B).filter((client, buttonBinding) -> client.currentScreen != null && buttonBinding.cooldown == 0 && INVENTORY.cooldown == 0)
.action(InputHandlers.handleExit()).cooldown().register();
public static final ButtonBinding JUMP = new Builder("jump").buttons(GLFW_GAMEPAD_BUTTON_A).onlyInGame().register();
public static final ButtonBinding LEFT = new Builder("left").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, false))
.action(MovementHandler.HANDLER).onlyInGame().register();
public static final ButtonBinding PAUSE_GAME = new Builder("pause_game").buttons(GLFW_GAMEPAD_BUTTON_START).action(InputHandlers::handlePauseGame).cooldown().register();
public static final ButtonBinding PICK_BLOCK = new Builder("pick_block").buttons(GLFW_GAMEPAD_BUTTON_DPAD_LEFT).onlyInGame().cooldown().register();
public static final ButtonBinding PLAYER_LIST = new Builder("player_list").buttons(GLFW_GAMEPAD_BUTTON_BACK).onlyInGame().register();
public static final ButtonBinding RIGHT = new Builder("right").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_X, true))
.action(MovementHandler.HANDLER).onlyInGame().register();
public static final ButtonBinding SCREENSHOT = new Builder("screenshot").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_A)
.action(InputHandlers::handleScreenshot).cooldown().register();
public static final ButtonBinding DEBUG_SCREEN = new Builder("debug_screen").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_B)
.action((client,binding,value,action) -> {if (action == ButtonState.PRESS) client.inGameHud.getDebugHud().toggleDebugHud(); return true;}).cooldown().register();
public static final ButtonBinding SLOT_DOWN = new Builder("slot_down").buttons(GLFW_GAMEPAD_BUTTON_DPAD_DOWN)
.action(InputHandlers.handleInventorySlotPad(1)).onlyInInventory().cooldown().register();
public static final ButtonBinding SLOT_LEFT = new Builder("slot_left").buttons(GLFW_GAMEPAD_BUTTON_DPAD_LEFT)
.action(InputHandlers.handleInventorySlotPad(3)).onlyInInventory().cooldown().register();
public static final ButtonBinding SLOT_RIGHT = new Builder("slot_right").buttons(GLFW_GAMEPAD_BUTTON_DPAD_RIGHT)
.action(InputHandlers.handleInventorySlotPad(2)).onlyInInventory().cooldown().register();
public static final ButtonBinding SLOT_UP = new Builder("slot_up").buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP)
.action(InputHandlers.handleInventorySlotPad(0)).onlyInInventory().cooldown().register();
public static final ButtonBinding SNEAK = new Builder("sneak").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_THUMB)
.actions(InputHandlers::handleToggleSneak).onlyInGame().cooldown().register();
public static final ButtonBinding SPRINT = new Builder("sprint").buttons(GLFW_GAMEPAD_BUTTON_LEFT_THUMB)
.actions(InputHandlers::handleToggleSprint).onlyInGame().cooldown().register();
public static final ButtonBinding SWAP_HANDS = new Builder("swap_hands").buttons(GLFW_GAMEPAD_BUTTON_X).onlyInGame().cooldown().register();
public static final ButtonBinding TAB_LEFT = new Builder("tab_back").buttons(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER)
.action(InputHandlers.handleHotbar(false)).filter(Predicates.or(InputHandlers::inInventory, InputHandlers::inAdvancements).or((client, binding) -> client.currentScreen != null)).cooldown().register();
public static final ButtonBinding TAB_RIGHT = new Builder("tab_next").buttons(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER)
.action(InputHandlers.handleHotbar(true)).filter(Predicates.or(InputHandlers::inInventory, InputHandlers::inAdvancements).or((client, binding) -> client.currentScreen != null)).cooldown().register();
public static final ButtonBinding PAGE_LEFT = new Builder("page_back").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true))
.action(InputHandlers.handlePage(false)).filter(InputHandlers::inInventory).cooldown(30).register();
public static final ButtonBinding PAGE_RIGHT = new Builder("page_next").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, true))
.action(InputHandlers.handlePage(true)).filter(InputHandlers::inInventory).cooldown(30).register();
public static final ButtonBinding TAKE = new Builder("take").buttons(GLFW_GAMEPAD_BUTTON_X)
.action(InputHandlers.handleActions()).filter(InputHandlers::inInventory).cooldown().register();
public static final ButtonBinding TAKE_ALL = new Builder("take_all").buttons(GLFW_GAMEPAD_BUTTON_A)
.action(InputHandlers.handleActions()).filter(InputHandlers::inInventory).cooldown().register();
public static final ButtonBinding QUICK_MOVE = new Builder("quick_move").buttons(GLFW_GAMEPAD_BUTTON_Y)
.action(InputHandlers.handleActions()).filter(InputHandlers::inInventory).cooldown().register();
public static final ButtonBinding TOGGLE_PERSPECTIVE = new Builder("toggle_perspective").filter(InputHandlers::inGame).buttons(GLFW_GAMEPAD_BUTTON_DPAD_UP, GLFW_GAMEPAD_BUTTON_Y).cooldown().register();
public static final ButtonBinding USE = new Builder("use").buttons(axisAsButton(GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, true)).register();
private int[] button;
private final int[] defaultButton;
private final String key;
private final Text text;
private KeyBinding mcKeyBinding = null;
protected PairPredicate<MinecraftClient, ButtonBinding> filter;
private final List<PressAction> actions = new ArrayList<>(Collections.singletonList(PressAction.DEFAULT_ACTION));
private final boolean hasCooldown;
private int cooldownLength = 5;
private int cooldown = 0;
private boolean pressed = false;
public ButtonBinding(String key, int[] defaultButton, List<PressAction> actions, PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown) {
this.setButton(this.defaultButton = defaultButton);
this.key = key;
this.text = Text.translatable(this.key);
this.filter = filter;
this.actions.addAll(actions);
this.hasCooldown = hasCooldown;
}
public ButtonBinding(String key, int[] defaultButton, List<PressAction> actions, PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown, int cooldownLength) {
this.setButton(this.defaultButton = defaultButton);
this.key = key;
this.text = Text.translatable(this.key);
this.filter = filter;
this.actions.addAll(actions);
this.hasCooldown = hasCooldown;
this.cooldownLength = cooldownLength;
}
public ButtonBinding(String key, int[] defaultButton, boolean hasCooldown) {
this(key, defaultButton, Collections.emptyList(), Predicates.pairAlwaysTrue(), hasCooldown);
}
public ButtonBinding(String key, int[] defaultButton, boolean hasCooldown, int cooldownLength) {
this(key, defaultButton, Collections.emptyList(), Predicates.pairAlwaysTrue(), hasCooldown, cooldownLength);
}
/**
* Returns the button bound.
*
* @return the bound button
*/
public int[] getButton() {
return this.button;
}
/**
* Sets the bound button.
*
* @param button the bound button
*/
public void setButton(int[] button) {
this.button = button;
if (InputManager.hasBinding(this))
InputManager.sortBindings();
}
/**
* Sets the button press state.
*
* @param pressed whether the button is pressed
*/
public void setPressed(boolean pressed) {
this.pressed = pressed;
}
/**
* Returns whether the bound button is the specified button or not.
*
* @param button the button to check
* @return true if the bound button is the specified button, else false
*/
public boolean isButton(int[] button) {
return InputManager.areButtonsEquivalent(button, this.button);
}
/**
* Returns whether this button is down or not.
*
* @return true if the button is down, else false
* @deprecated Use {@link #isPressed()} instead
*/
@Deprecated
public boolean isButtonDown() {
return isPressed();
}
/**
* Returns whether this button is down or not.
*
* @return true if the button is down, else false
*/
public boolean isPressed() {
return this.pressed;
}
/**
* Returns whether this button binding is bound or not.
*
* @return true if this button binding is bound, else false
*/
public boolean isNotBound() {
return this.button.length == 0 || this.button[0] == -1;
}
/**
* Gets the default button assigned to this binding.
*
* @return the default button
*/
public int[] getDefaultButton() {
return this.defaultButton;
}
/**
* Returns whether the assigned button is the default button.
*
* @return true if the assigned button is the default button, else false
*/
public boolean isDefault() {
return this.button.length == this.defaultButton.length && InputManager.areButtonsEquivalent(this.button, this.defaultButton);
}
/**
* Returns the button code.
*
* @return the button code
*/
public String getButtonCode() {
return Arrays.stream(this.button)
.mapToObj(btn -> Integer.valueOf(btn).toString())
.collect(Collectors.joining("+"));
}
/**
* Sets the key binding to emulate with this button binding.
*
* @param keyBinding the optional key binding
*/
public void setKeyBinding(@Nullable KeyBinding keyBinding) {
this.mcKeyBinding = keyBinding;
}
/**
* Returns whether the button binding is available in the current context.
*
* @param client the client instance
* @return true if the button binding is available, else false
*/
public boolean isAvailable(@NotNull MinecraftClient client) {
return this.filter.test(client, this);
}
/**
* Updates the button binding cooldown.
*/
public void update() {
if (this.hasCooldown && this.cooldown > 0)
this.cooldown--;
}
/**
* Handles the button binding.
*
* @param client the client instance
* @param state the state
*/
public void handle(@NotNull MinecraftClient client, float value, @NotNull ButtonState state) {
if (state == ButtonState.REPEAT && this.hasCooldown && this.cooldown != 0)
return;
if (this.hasCooldown && state.isPressed()) {
this.cooldown = cooldownLength;
}
for (int i = this.actions.size() - 1; i >= 0; i--) {
if (this.actions.get(i).press(client, this, value, state))
break;
}
}
public @NotNull String getName() {
return this.key;
}
/**
* Returns the translation key of this button binding.
*
* @return the translation key
*/
public @NotNull String getTranslationKey() {
return I18n.hasTranslation("midnightcontrols.action." + this.getName()) ? "midnightcontrols.action." + this.getName() : this.getName();
}
public @NotNull Text getText() {
return this.text;
}
/**
* Returns the key binding equivalent of this button binding.
*
* @return the key binding equivalent
*/
public @NotNull Optional<KeyBinding> asKeyBinding() {
return Optional.ofNullable(this.mcKeyBinding);
}
@Override
public String toString() {
return "ButtonBinding{id=\"" + this.key + "\","
+ "hasCooldown=" + this.hasCooldown
+ "}";
}
/**
* Returns the specified axis as a button.
*
* @param axis the axis
* @param positive true if the axis part is positive, else false
* @return the axis as a button
*/
public static int axisAsButton(int axis, boolean positive) {
return positive ? 100 + axis : 200 + axis;
}
/**
* Returns whether the specified button is an axis or not.
*
* @param button the button
* @return true if the button is an axis, else false
*/
public static boolean isAxis(int button) {
button %= 500;
return button >= 100;
}
/**
* Returns the second Joycon's specified button code.
*
* @param button the raw button code
* @return the second Joycon's button code
*/
public static int controller2Button(int button) {
return 500 + button;
}
public static void init(@NotNull GameOptions options) {
ATTACK.mcKeyBinding = options.attackKey;
BACK.mcKeyBinding = options.backKey;
CHAT.mcKeyBinding = options.chatKey;
DROP_ITEM.mcKeyBinding = options.dropKey;
FORWARD.mcKeyBinding = options.forwardKey;
INVENTORY.mcKeyBinding = options.inventoryKey;
JUMP.mcKeyBinding = options.jumpKey;
LEFT.mcKeyBinding = options.leftKey;
PICK_BLOCK.mcKeyBinding = options.pickItemKey;
PLAYER_LIST.mcKeyBinding = options.playerListKey;
RIGHT.mcKeyBinding = options.rightKey;
SCREENSHOT.mcKeyBinding = options.screenshotKey;
SNEAK.mcKeyBinding = options.sneakKey;
SPRINT.mcKeyBinding = options.sprintKey;
SWAP_HANDS.mcKeyBinding = options.swapHandsKey;
TOGGLE_PERSPECTIVE.mcKeyBinding = options.togglePerspectiveKey;
USE.mcKeyBinding = options.useKey;
}
/**
* Returns the localized name of the specified button.
*
* @param button the button
* @return the localized name of the button
*/
public static @NotNull Text getLocalizedButtonName(int button) {
return switch (button % 500) {
case -1 -> Text.translatable("key.keyboard.unknown");
case GLFW_GAMEPAD_BUTTON_A -> Text.translatable("midnightcontrols.button.a");
case GLFW_GAMEPAD_BUTTON_B -> Text.translatable("midnightcontrols.button.b");
case GLFW_GAMEPAD_BUTTON_X -> Text.translatable("midnightcontrols.button.x");
case GLFW_GAMEPAD_BUTTON_Y -> Text.translatable("midnightcontrols.button.y");
case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER -> Text.translatable("midnightcontrols.button.left_bumper");
case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER -> Text.translatable("midnightcontrols.button.right_bumper");
case GLFW_GAMEPAD_BUTTON_BACK -> Text.translatable("midnightcontrols.button.back");
case GLFW_GAMEPAD_BUTTON_START -> Text.translatable("midnightcontrols.button.start");
case GLFW_GAMEPAD_BUTTON_GUIDE -> Text.translatable("midnightcontrols.button.guide");
case GLFW_GAMEPAD_BUTTON_LEFT_THUMB -> Text.translatable("midnightcontrols.button.left_thumb");
case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB -> Text.translatable("midnightcontrols.button.right_thumb");
case GLFW_GAMEPAD_BUTTON_DPAD_UP -> Text.translatable("midnightcontrols.button.dpad_up");
case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT -> Text.translatable("midnightcontrols.button.dpad_right");
case GLFW_GAMEPAD_BUTTON_DPAD_DOWN -> Text.translatable("midnightcontrols.button.dpad_down");
case GLFW_GAMEPAD_BUTTON_DPAD_LEFT -> Text.translatable("midnightcontrols.button.dpad_left");
case 100 -> Text.translatable("midnightcontrols.axis.left_x+");
case 101 -> Text.translatable("midnightcontrols.axis.left_y+");
case 102 -> Text.translatable("midnightcontrols.axis.right_x+");
case 103 -> Text.translatable("midnightcontrols.axis.right_y+");
case 104 -> Text.translatable("midnightcontrols.axis.left_trigger");
case 105 -> Text.translatable("midnightcontrols.axis.right_trigger");
case 200 -> Text.translatable("midnightcontrols.axis.left_x-");
case 201 -> Text.translatable("midnightcontrols.axis.left_y-");
case 202 -> Text.translatable("midnightcontrols.axis.right_x-");
case 203 -> Text.translatable("midnightcontrols.axis.right_y-");
case 15 -> Text.translatable("midnightcontrols.button.l4");
case 16 -> Text.translatable("midnightcontrols.button.l5");
case 17 -> Text.translatable("midnightcontrols.button.r4");
case 18 -> Text.translatable("midnightcontrols.button.r5");
default -> Text.translatable("midnightcontrols.button.unknown", button);
};
}
static {
MOVEMENT_CATEGORY = InputManager.registerDefaultCategory("key.categories.movement", category -> category.registerAllBindings(
ButtonBinding.FORWARD,
ButtonBinding.BACK,
ButtonBinding.LEFT,
ButtonBinding.RIGHT,
ButtonBinding.JUMP,
ButtonBinding.SNEAK,
ButtonBinding.SPRINT));
GAMEPLAY_CATEGORY = InputManager.registerDefaultCategory("key.categories.gameplay", category -> category.registerAllBindings(
ButtonBinding.ATTACK,
ButtonBinding.PICK_BLOCK,
ButtonBinding.USE
));
INVENTORY_CATEGORY = InputManager.registerDefaultCategory("key.categories.inventory", category -> category.registerAllBindings(
ButtonBinding.EXIT,
ButtonBinding.DROP_ITEM,
ButtonBinding.HOTBAR_LEFT,
ButtonBinding.HOTBAR_RIGHT,
ButtonBinding.INVENTORY,
ButtonBinding.SWAP_HANDS,
ButtonBinding.TAB_LEFT,
ButtonBinding.TAB_RIGHT,
ButtonBinding.PAGE_LEFT,
ButtonBinding.PAGE_RIGHT,
ButtonBinding.TAKE,
ButtonBinding.TAKE_ALL,
ButtonBinding.QUICK_MOVE,
ButtonBinding.SLOT_UP,
ButtonBinding.SLOT_DOWN,
ButtonBinding.SLOT_LEFT,
ButtonBinding.SLOT_RIGHT
));
MULTIPLAYER_CATEGORY = InputManager.registerDefaultCategory("key.categories.multiplayer",
category -> category.registerAllBindings(ButtonBinding.CHAT, ButtonBinding.PLAYER_LIST));
MISC_CATEGORY = InputManager.registerDefaultCategory("key.categories.misc", category -> category.registerAllBindings(
ButtonBinding.SCREENSHOT,
ButtonBinding.TOGGLE_PERSPECTIVE,
ButtonBinding.PAUSE_GAME,
//SMOOTH_CAMERA,
ButtonBinding.DEBUG_SCREEN,
ButtonBinding.CONTROLS_RING
));
}
/**
* Returns a builder instance.
*
* @param identifier the identifier of the button binding
* @return the builder instance
* @since 1.5.0
*/
public static Builder builder(@NotNull Identifier identifier) {
return new Builder(identifier);
}
/**
* Represents a quick {@link ButtonBinding} builder.
*
* @author LambdAurora
* @version 1.5.0
* @since 1.1.0
*/
public static class Builder {
private final String key;
private int[] buttons = new int[0];
private final List<PressAction> actions = new ArrayList<>();
private PairPredicate<MinecraftClient, ButtonBinding> filter = Predicates.pairAlwaysTrue();
private boolean cooldown = false;
private int cooldownLength = 5;
private ButtonCategory category = null;
private KeyBinding mcBinding = null;
/**
* This constructor shouldn't be used for other mods.
*
* @param key the key with format {@code "<namespace>.<name>"}
*/
public Builder(@NotNull String key) {
this.key = key;
this.unbound();
}
public Builder(@NotNull Identifier identifier) {
this(identifier.getNamespace() + "." + identifier.getPath());
}
/**
* Defines the default buttons of the {@link ButtonBinding}.
*
* @param buttons the default buttons
* @return the builder instance
*/
public Builder buttons(int... buttons) {
this.buttons = buttons;
return this;
}
/**
* Sets the {@link ButtonBinding} to unbound.
*
* @return the builder instance
*/
public Builder unbound() {
return this.buttons(-1);
}
/**
* Adds the actions to the {@link ButtonBinding}.
*
* @param actions the actions to add
* @return the builder instance
*/
public Builder actions(@NotNull PressAction... actions) {
this.actions.addAll(Arrays.asList(actions));
return this;
}
/**
* Adds an action to the {@link ButtonBinding}.
*
* @param action the action to add
* @return the builder instance
*/
public Builder action(@NotNull PressAction action) {
this.actions.add(action);
return this;
}
/**
* Sets a filter for the {@link ButtonBinding}.
*
* @param filter the filter
* @return the builder instance
*/
public Builder filter(@NotNull PairPredicate<MinecraftClient, ButtonBinding> filter) {
this.filter = filter;
return this;
}
/**
* Sets the filter of {@link ButtonBinding} to only in game.
*
* @return the builder instance
* @see #filter(PairPredicate)
* @see InputHandlers#inGame(MinecraftClient, ButtonBinding)
*/
public Builder onlyInGame() {
return this.filter(InputHandlers::inGame);
}
/**
* Sets the filter of {@link ButtonBinding} to only in inventory.
*
* @return the builder instance
* @see #filter(PairPredicate)
* @see InputHandlers#inInventory(MinecraftClient, ButtonBinding)
*/
public Builder onlyInInventory() {
return this.filter(InputHandlers::inInventory);
}
/**
* Sets whether the {@link ButtonBinding} has a cooldown or not.
*
* @param cooldown true if the {@link ButtonBinding} has a cooldown, else false
* @return the builder instance
*/
public Builder cooldown(boolean cooldown) {
this.cooldown = cooldown;
return this;
}
/**
* Sets the cooldown enabled with a custom duration for {@link ButtonBinding}.
*
* @param cooldownLength duration of {@link ButtonBinding} cooldown
* @return the builder instance
*/
public Builder cooldown(int cooldownLength) {
this.cooldownLength = cooldownLength;
this.cooldown = true;
return this;
}
/**
* Puts a cooldown on the {@link ButtonBinding}.
*
* @return the builder instance
* @since 1.5.0
*/
public Builder cooldown() {
return this.cooldown(true);
}
/**
* Sets the category of the {@link ButtonBinding}.
*
* @param category the category
* @return the builder instance
*/
public Builder category(@Nullable ButtonCategory category) {
this.category = category;
return this;
}
/**
* Sets the keybinding linked to the {@link ButtonBinding}.
*
* @param binding the keybinding to link
* @return the builder instance
*/
public Builder linkKeybind(@Nullable KeyBinding binding) {
this.mcBinding = binding;
return this;
}
/**
* Builds the {@link ButtonBinding}.
*
* @return the built {@link ButtonBinding}
*/
public ButtonBinding build() {
var binding = new ButtonBinding(this.key, this.buttons, this.actions, this.filter, this.cooldown, this.cooldownLength);
if (this.category != null)
this.category.registerBinding(binding);
if (this.mcBinding != null)
binding.setKeyBinding(this.mcBinding);
return binding;
}
/**
* Builds and registers the {@link ButtonBinding}.
*
* @return the built {@link ButtonBinding}
* @see #build()
*/
public ButtonBinding register() {
return InputManager.registerBinding(this.build());
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.controller;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Represents a button binding category
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public class ButtonCategory {
private final List<ButtonBinding> bindings = new ArrayList<>();
private final Identifier id;
private final int priority;
public ButtonCategory(@NotNull Identifier id, int priority) {
this.id = id;
this.priority = priority;
}
public ButtonCategory(@NotNull Identifier id) {
this(id, 100);
}
public void registerBinding(@NotNull ButtonBinding binding) {
if (this.bindings.contains(binding))
throw new IllegalStateException("Cannot register twice a button binding in the same category.");
this.bindings.add(binding);
}
public void registerAllBindings(@NotNull ButtonBinding... bindings) {
this.registerAllBindings(Arrays.asList(bindings));
}
public void registerAllBindings(@NotNull List<ButtonBinding> bindings) {
bindings.forEach(this::registerBinding);
}
/**
* Gets the bindings assigned to this category.
*
* @return the bindings assigned to this category
*/
public @NotNull List<ButtonBinding> getBindings() {
return Collections.unmodifiableList(this.bindings);
}
/**
* Gets the translated name of this category.
* <p>
* The translation key should be `modid.identifier_name`.
*
* @return the translated name
*/
public @NotNull String getTranslatedName() {
if (this.id.getNamespace().equals("minecraft"))
return I18n.translate(this.id.getPath());
else
return I18n.translate(this.id.getNamespace() + "." + this.id.getPath());
}
/**
* Gets the priority display of this category.
* It will defines in which order the categories will display on the controls screen.
*
* @return the priority of this category
*/
public int getPriority() {
return this.priority;
}
public @NotNull Identifier getIdentifier() {
return this.id;
}
}

View File

@@ -0,0 +1,214 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.controller;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWGamepadState;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import java.io.*;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import static org.lwjgl.BufferUtils.createByteBuffer;
/**
* Represents a controller.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.0.0
*/
public record Controller(int id) implements Nameable {
private static final Map<Integer, Controller> CONTROLLERS = new HashMap<>();
/**
* Gets the controller's globally unique identifier.
*
* @return the controller's GUID
*/
public String getGuid() {
String guid = GLFW.glfwGetJoystickGUID(this.id);
return guid == null ? "" : guid;
}
/**
* Returns whether this controller is connected or not.
*
* @return true if this controller is connected, else false
*/
public boolean isConnected() {
return GLFW.glfwJoystickPresent(this.id);
}
/**
* Returns whether this controller is a gamepad or not.
*
* @return true if this controller is a gamepad, else false
*/
public boolean isGamepad() {
return this.isConnected() && GLFW.glfwJoystickIsGamepad(this.id);
}
/**
* Gets the name of the controller.
*
* @return the controller's name
*/
@Override
public @NotNull String getName() {
var name = this.isGamepad() ? GLFW.glfwGetGamepadName(this.id) : GLFW.glfwGetJoystickName(this.id);
return name == null ? String.valueOf(this.id()) : name;
}
/**
* Gets the state of the controller.
*
* @return the state of the controller input
*/
public GLFWGamepadState getState() {
var state = GLFWGamepadState.create();
if (this.isGamepad())
GLFW.glfwGetGamepadState(this.id, state);
return state;
}
public static Controller byId(int id) {
if (id > GLFW.GLFW_JOYSTICK_LAST) {
MidnightControls.log("Controller '" + id + "' doesn't exist.");
id = GLFW.GLFW_JOYSTICK_LAST;
}
Controller controller;
if (CONTROLLERS.containsKey(id))
return CONTROLLERS.get(id);
else {
controller = new Controller(id);
CONTROLLERS.put(id, controller);
return controller;
}
}
public static Optional<Controller> byGuid(@NotNull String guid) {
return CONTROLLERS.values().stream().filter(Controller::isConnected)
.filter(controller -> controller.getGuid().equals(guid))
.max(Comparator.comparingInt(Controller::id));
}
/**
* Reads the specified resource and returns the raw data as a ByteBuffer.
*
* @param resource the resource to read
* @return the resource data
* @throws IOException If an IO error occurs.
*/
private static ByteBuffer ioResourceToBuffer(String resource) throws IOException {
ByteBuffer buffer = null;
var path = Paths.get(resource);
if (Files.isReadable(path)) {
try (var fc = Files.newByteChannel(path)) {
buffer = createByteBuffer((int) fc.size() + 2);
while (fc.read(buffer) != -1) ;
buffer.put((byte) 0);
}
}
if (buffer != null) buffer.flip(); // Force Java 8 >.<
return buffer;
}
/**
* Updates the controller mappings.
*/
public static void updateMappings() {
CompletableFuture.supplyAsync(Controller::updateMappingsSync);
}
private static boolean updateMappingsSync() {
try {
MidnightControls.log("Updating controller mappings...");
Optional<File> databaseFile = getDatabaseFile();
if (databaseFile.isPresent()) {
var database = ioResourceToBuffer(databaseFile.get().getPath());
if (database != null) GLFW.glfwUpdateGamepadMappings(database);
}
if (!MidnightControlsClient.MAPPINGS_FILE.exists())
return false;
var buffer = ioResourceToBuffer(MidnightControlsClient.MAPPINGS_FILE.getPath());
if (buffer != null) GLFW.glfwUpdateGamepadMappings(buffer);
} catch (IOException e) {
e.fillInStackTrace();
}
try (var memoryStack = MemoryStack.stackPush()) {
var pointerBuffer = memoryStack.mallocPointer(1);
int i = GLFW.glfwGetError(pointerBuffer);
if (i != 0) {
long l = pointerBuffer.get();
var string = l == 0L ? "" : MemoryUtil.memUTF8(l);
var client = MinecraftClient.getInstance();
if (client != null) {
client.getToastManager().add(SystemToast.create(client, SystemToast.Type.PERIODIC_NOTIFICATION,
Text.translatable("midnightcontrols.controller.mappings.error"), Text.literal(string)));
}
MidnightControls.log(I18n.translate("midnightcontrols.controller.mappings.error")+string);
}
} catch (Throwable e) {
/* Ignored :concern: */
}
if (MidnightControlsConfig.debug) {
for (int i = GLFW.GLFW_JOYSTICK_1; i <= GLFW.GLFW_JOYSTICK_16; i++) {
var controller = byId(i);
if (!controller.isConnected())
continue;
MidnightControls.log(String.format("Controller #%d name: \"%s\"\n GUID: %s\n Gamepad: %s",
controller.id,
controller.getName(),
controller.getGuid(),
controller.isGamepad()));
}
}
return true;
}
private static Optional<File> getDatabaseFile() {
File databaseFile = new File("config/gamecontrollerdatabase.txt");
try {
BufferedInputStream in = new BufferedInputStream(URI.create("https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt").toURL().openStream());
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(databaseFile));
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
out.write(dataBuffer, 0, bytesRead);
}
out.close();
} catch (Exception e) {return Optional.empty();}
return Optional.of(databaseFile);
}
}

View File

@@ -0,0 +1,324 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.controller;
import com.google.common.collect.Lists;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightInput;
import eu.midnightdust.midnightcontrols.client.compat.InventoryTabsCompat;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.gui.RingScreen;
import eu.midnightdust.midnightcontrols.client.gui.TouchscreenOverlay;
import eu.midnightdust.midnightcontrols.client.mixin.*;
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
import eu.midnightdust.midnightcontrols.client.util.InventoryUtil;
import eu.midnightdust.midnightcontrols.client.util.ToggleSneakSprintUtil;
import eu.midnightdust.midnightcontrols.client.util.platform.ItemGroupUtil;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.gui.screen.ingame.*;
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
import net.minecraft.client.gui.widget.TabNavigationWidget;
import net.minecraft.client.util.ScreenshotRecorder;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import java.util.Optional;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_2;
/**
* Represents some input handlers.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.1.0
*/
public class InputHandlers {
private InputHandlers() {
}
public static PressAction handleHotbar(boolean next) {
return (client, button, value, action) -> {
if (action == ButtonState.RELEASE)
return false;
// When in-game
if (client.currentScreen == null && client.player != null) {
if (!client.player.isSpectator()) {
if (next)
client.player.getInventory().scrollInHotbar(-1.0);
else
client.player.getInventory().scrollInHotbar(1.0);
}
else {
if (client.inGameHud.getSpectatorHud().isOpen()) {
client.inGameHud.getSpectatorHud().cycleSlot(next ? -1 : 1);
} else {
float g = MathHelper.clamp(client.player.getAbilities().getFlySpeed() + (next ? 1 : -1) * 0.005F, 0.0F, 0.2F);
client.player.getAbilities().setFlySpeed(g);
}
}
return true;
} else if (client.currentScreen instanceof RingScreen) {
MidnightControlsClient.ring.cyclePage(next);
} else if (client.currentScreen instanceof CreativeInventoryScreenAccessor inventory) {
inventory.midnightcontrols$setSelectedTab(ItemGroupUtil.cycleTab(next, client));
return true;
} else if (client.currentScreen instanceof InventoryScreen || client.currentScreen instanceof CraftingScreen || client.currentScreen instanceof AbstractFurnaceScreen<?>) {
RecipeBookWidget recipeBook;
if (client.currentScreen instanceof InventoryScreen inventoryScreen) recipeBook = inventoryScreen.getRecipeBookWidget();
else if (client.currentScreen instanceof CraftingScreen craftingScreen) recipeBook = craftingScreen.getRecipeBookWidget();
else recipeBook = ((AbstractFurnaceScreen<?>)client.currentScreen).getRecipeBookWidget();
var recipeBookAccessor = (RecipeBookWidgetAccessor) recipeBook;
var tabs = recipeBookAccessor.getTabButtons();
var currentTab = recipeBookAccessor.getCurrentTab();
if (currentTab == null || !recipeBook.isOpen()) {
if (MidnightControlsCompat.isInventoryTabsPresent()) InventoryTabsCompat.handleInventoryTabs(client.currentScreen, next);
return false;
}
int nextTab = tabs.indexOf(currentTab) + (next ? 1 : -1);
if (nextTab < 0)
nextTab = tabs.size() - 1;
else if (nextTab >= tabs.size())
nextTab = 0;
currentTab.setToggled(false);
recipeBookAccessor.setCurrentTab(currentTab = tabs.get(nextTab));
currentTab.setToggled(true);
recipeBookAccessor.midnightcontrols$refreshResults(true);
return true;
} else if (client.currentScreen instanceof AdvancementsScreenAccessor screen) {
var tabs = screen.getTabs().values().stream().distinct().toList();
var tab = screen.getSelectedTab();
if (tab == null)
return false;
for (int i = 0; i < tabs.size(); i++) {
if (tabs.get(i).equals(tab)) {
int nextTab = i + (next ? 1 : -1);
if (nextTab < 0)
nextTab = tabs.size() - 1;
else if (nextTab >= tabs.size())
nextTab = 0;
screen.getAdvancementManager().selectTab(tabs.get(nextTab).getRoot().getAdvancementEntry(), true);
break;
}
}
return true;
} else if (client.currentScreen != null && client.currentScreen.children().stream().anyMatch(e -> e instanceof TabNavigationWidget)) {
return Lists.newCopyOnWriteArrayList(client.currentScreen.children()).stream().anyMatch(e -> {
if (e instanceof TabNavigationWidget tabs) {
TabNavigationWidgetAccessor accessor = (TabNavigationWidgetAccessor) tabs;
int tabIndex = accessor.getTabs().indexOf(accessor.getTabManager().getCurrentTab());
if (next ? tabIndex+1 < accessor.getTabs().size() : tabIndex > 0) {
if (next) tabs.selectTab(tabIndex + 1, true);
else tabs.selectTab(tabIndex - 1, true);
return true;
}
}
return false;
});
} else return MidnightControlsCompat.handleTabs(client.currentScreen, next);
return false;
};
}
public static PressAction handlePage(boolean next) {
return (client, button, value, action) -> {
if (action == ButtonState.RELEASE)
return false;
if (client.currentScreen instanceof CreativeInventoryScreen creativeScreen) {
return ItemGroupUtil.cyclePage(next, creativeScreen);
}
if (MidnightControlsCompat.isInventoryTabsPresent()) InventoryTabsCompat.handleInventoryPage(client.currentScreen, next);
return false;
};
}
public static PressAction handleExit() {
return (client, button, value, action) -> {
if (client.currentScreen != null && client.currentScreen.getClass() != TitleScreen.class) {
if (!MidnightControlsCompat.handleMenuBack(client, client.currentScreen))
if (!MidnightControlsClient.input.tryGoBack(client.currentScreen))
client.currentScreen.close();
return true;
}
return false;
};
}
public static PressAction handleActions() {
return (client, button, value, action) -> {
if (!(client.currentScreen instanceof HandledScreen<?> screen)) return false;
if (client.interactionManager == null || client.player == null)
return false;
if (MidnightControlsClient.input.inventoryInteractionCooldown > 0)
return true;
double x = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double y = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
var accessor = (HandledScreenAccessor) screen;
Slot slot = accessor.midnightcontrols$getSlotAt(x, y);
int slotId;
if (slot == null) {
if (button.getName().equals("take_all")) {
((MouseAccessor) client.mouse).setLeftButtonClicked(true);
return false;
}
slotId = accessor.midnightcontrols$isClickOutsideBounds(x, y, accessor.getX(), accessor.getY(), GLFW_MOUSE_BUTTON_1) ? -999 : -1;
} else {
slotId = slot.id;
}
var actionType = SlotActionType.PICKUP;
int clickData = GLFW.GLFW_MOUSE_BUTTON_1;
MidnightControlsClient.input.inventoryInteractionCooldown = 5;
switch (button.getName()) {
case "take_all" -> {
if (screen instanceof CreativeInventoryScreen) {
if (slot != null && (((CreativeInventoryScreenAccessor) accessor).midnightcontrols$isCreativeInventorySlot(slot) || MidnightControlsCompat.streamCompatHandlers().anyMatch(handler -> handler.isCreativeSlot(screen, slot))))
actionType = SlotActionType.CLONE;
}
}
case "take" -> {
clickData = GLFW_MOUSE_BUTTON_2;
}
case "quick_move" -> {
actionType = SlotActionType.QUICK_MOVE;
}
default -> {
return false;
}
}
accessor.midnightcontrols$onMouseClick(slot, slotId, clickData, actionType);
return true;
};
}
public static boolean handlePauseGame(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, float value, @NotNull ButtonState action) {
if (action == ButtonState.PRESS) {
// If in game, then pause the game.
if (client.currentScreen == null || client.currentScreen instanceof RingScreen)
client.openGameMenu(false);
else if (client.currentScreen instanceof HandledScreen && client.player != null) // If the current screen is a container then close it.
client.player.closeHandledScreen();
else // Else just close the current screen.
client.currentScreen.close();
}
return true;
}
/**
* Handles the screenshot action.
*
* @param client the client instance
* @param binding the binding which fired the action
* @param action the action done on the binding
* @return true if handled, else false
*/
public static boolean handleScreenshot(@NotNull MinecraftClient client, @NotNull ButtonBinding binding, float value, @NotNull ButtonState action) {
if (action == ButtonState.RELEASE)
ScreenshotRecorder.saveScreenshot(client.runDirectory, client.getFramebuffer(),
text -> client.execute(() -> client.inGameHud.getChatHud().addMessage(text)));
return true;
}
public static boolean handleToggleSneak(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action) {
return ToggleSneakSprintUtil.toggleSneak(button);
}
public static boolean handleToggleSprint(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action) {
return ToggleSneakSprintUtil.toggleSprint(button);
}
public static PressAction handleInventorySlotPad(int direction) {
return (client, binding, value, action) -> {
if (!(client.currentScreen instanceof HandledScreen<?> inventory && action != ButtonState.RELEASE))
return false;
var accessor = (HandledScreenAccessor) inventory;
Optional<Slot> closestSlot = InventoryUtil.findClosestSlot(inventory, direction);
if (closestSlot.isPresent()) {
var slot = closestSlot.get();
int x = accessor.getX() + slot.x + 8;
int y = accessor.getY() + slot.y + 8;
InputManager.queueMousePosition(x * (double) client.getWindow().getWidth() / (double) client.getWindow().getScaledWidth(),
y * (double) client.getWindow().getHeight() / (double) client.getWindow().getScaledHeight());
return true;
}
return false;
};
}
/**
* Returns always true to the filter.
*
* @param client the client instance
* @param binding the affected binding
* @return true
*/
public static boolean always(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
return true;
}
/**
* Returns whether the client is in game or not.
*
* @param client the client instance
* @param binding the affected binding
* @return true if the client is in game, else false
*/
public static boolean inGame(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
return (client.currentScreen == null && MidnightControlsClient.input.screenCloseCooldown <= 0) || client.currentScreen instanceof TouchscreenOverlay || client.currentScreen instanceof RingScreen;
}
/**
* Returns whether the client is in a non-interactive screen (which means require mouse input) or not.
*
* @param client the client instance
* @param binding the affected binding
* @return true if the client is in a non-interactive screen, else false
*/
public static boolean inNonInteractiveScreens(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
if (client.currentScreen == null)
return false;
return !MidnightInput.isScreenInteractive(client.currentScreen);
}
/**
* Returns whether the client is in an inventory or not.
*
* @param client the client instance
* @param binding the affected binding
* @return true if the client is in an inventory, else false
*/
public static boolean inInventory(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
return client.currentScreen instanceof HandledScreen;
}
/**
* Returns whether the client is in the advancements screen or not.
*
* @param client the client instance
* @param binding the affected binding
* @return true if the client is in the advancements screen, else false
*/
public static boolean inAdvancements(@NotNull MinecraftClient client, @NotNull ButtonBinding binding) {
return client.currentScreen instanceof AdvancementsScreen;
}
}

View File

@@ -0,0 +1,421 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.controller;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.mixin.MouseAccessor;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.InputUtil;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import org.aperlambda.lambdacommon.utils.function.PairPredicate;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Represents an input manager for controllers.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.1.0
*/
public class InputManager {
public static final InputManager INPUT_MANAGER = new InputManager();
private static final List<ButtonBinding> BINDINGS = new ArrayList<>();
private static final List<ButtonCategory> CATEGORIES = new ArrayList<>();
public static final Int2ObjectMap<ButtonState> STATES = new Int2ObjectOpenHashMap<>();
public static final Int2FloatMap BUTTON_VALUES = new Int2FloatOpenHashMap();
public int prevTargetMouseX = 0;
public int prevTargetMouseY = 0;
public int targetMouseX = 0;
public int targetMouseY = 0;
protected InputManager() {
}
public void tick(@NotNull MinecraftClient client) {
if (MidnightControlsConfig.autoSwitchMode && !MidnightControlsConfig.isEditing && MidnightControlsConfig.controlsMode != ControlsMode.TOUCHSCREEN)
if (MidnightControlsConfig.getController().isConnected() && MidnightControlsConfig.getController().isGamepad())
MidnightControlsConfig.controlsMode = ControlsMode.CONTROLLER;
else MidnightControlsConfig.controlsMode = ControlsMode.DEFAULT;
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) {
this.controllerTick(client);
}
}
public void controllerTick(@NotNull MinecraftClient client) {
this.prevTargetMouseX = this.targetMouseX;
this.prevTargetMouseY = this.targetMouseY;
}
/**
* Updates the mouse position. Should only be called on pre render of a screen.
*
* @param client the client instance
*/
public void updateMousePosition(@NotNull MinecraftClient client) {
Objects.requireNonNull(client, "Client instance cannot be null.");
if (this.prevTargetMouseX != this.targetMouseX || this.prevTargetMouseY != this.targetMouseY) {
double mouseX = this.prevTargetMouseX + (this.targetMouseX - this.prevTargetMouseX) * client.getRenderTickCounter().getTickDelta(true) + 0.5;
double mouseY = this.prevTargetMouseY + (this.targetMouseY - this.prevTargetMouseY) * client.getRenderTickCounter().getTickDelta(true) + 0.5;
if (!MidnightControlsConfig.virtualMouse)
GLFW.glfwSetCursorPos(client.getWindow().getHandle(), mouseX, mouseY);
((MouseAccessor) client.mouse).midnightcontrols$onCursorPos(client.getWindow().getHandle(), mouseX, mouseY);
}
}
/**
* Resets the mouse position.
*
* @param windowWidth the window width
* @param windowHeight the window height
*/
public void resetMousePosition(int windowWidth, int windowHeight) {
this.targetMouseX = this.prevTargetMouseX = (int) (windowWidth / 2.F);
this.targetMouseY = this.prevTargetMouseY = (int) (windowHeight / 2.F);
}
public void resetMouseTarget(@NotNull MinecraftClient client) {
double mouseX = client.mouse.getX();
double mouseY = client.mouse.getY();
this.prevTargetMouseX = this.targetMouseX = (int) mouseX;
this.prevTargetMouseY = this.targetMouseY = (int) mouseY;
}
/**
* Returns whether the specified binding is registered or not.
*
* @param binding the binding to check
* @return true if the binding is registered, else false
*/
public static boolean hasBinding(@NotNull ButtonBinding binding) {
return BINDINGS.contains(binding);
}
/**
* Returns whether the specified binding is registered or not.
*
* @param name the name of the binding to check
* @return true if the binding is registered, else false
*/
public static boolean hasBinding(@NotNull String name) {
return BINDINGS.parallelStream().map(ButtonBinding::getName).anyMatch(binding -> binding.equalsIgnoreCase(name));
}
/**
* Returns whether the specified binding is registered or not.
*
* @param identifier the identifier of the binding to check
* @return true if the binding is registered, else false
*/
public static boolean hasBinding(@NotNull Identifier identifier) {
return hasBinding(identifier.getNamespace() + "." + identifier.getPath());
}
private static ButtonBinding tempBinding;
/**
* Returns the binding matching the given string.
*
* @param name the name of the binding to get
* @return true if the binding is registered, else false
*/
public static ButtonBinding getBinding(@NotNull String name) {
if (BINDINGS.parallelStream().map(ButtonBinding::getName).anyMatch(binding -> binding.equalsIgnoreCase(name)))
BINDINGS.forEach(binding -> {
if (binding.getName().equalsIgnoreCase(name)) InputManager.tempBinding = binding;
});
return tempBinding;
}
private static List<ButtonBinding> unboundBindings;
public static List<ButtonBinding> getUnboundBindings() {
unboundBindings = new ArrayList<>();
BINDINGS.forEach(binding -> {
if (binding.isNotBound() && !MidnightControlsConfig.ignoredUnboundKeys.contains(binding.getTranslationKey())) unboundBindings.add(binding);
});
unboundBindings.sort(Comparator.comparing(s -> I18n.translate(s.getTranslationKey())));
return unboundBindings;
}
/**
* Registers a button binding.
*
* @param binding the binding to register
* @return the registered binding
*/
public static @NotNull ButtonBinding registerBinding(@NotNull ButtonBinding binding) {
if (hasBinding(binding))
throw new IllegalStateException("Cannot register twice a button binding in the registry.");
BINDINGS.add(binding);
return binding;
}
@Deprecated
public static @NotNull ButtonBinding registerBinding(@NotNull org.aperlambda.lambdacommon.Identifier id, int[] defaultButton, @NotNull List<PressAction> actions, @NotNull PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown) {
return registerBinding(Identifier.of(id.getNamespace(), id.getName()), defaultButton, actions, filter, hasCooldown);
}
@Deprecated
public static @NotNull ButtonBinding registerBinding(@NotNull org.aperlambda.lambdacommon.Identifier id, int[] defaultButton, boolean hasCooldown) {
return registerBinding(id, defaultButton, Collections.emptyList(), InputHandlers::always, hasCooldown);
}
public static @NotNull ButtonBinding registerBinding(@NotNull Identifier id, int[] defaultButton, @NotNull List<PressAction> actions, @NotNull PairPredicate<MinecraftClient, ButtonBinding> filter, boolean hasCooldown) {
return registerBinding(new ButtonBinding(id.getNamespace() + "." + id.getPath(), defaultButton, actions, filter, hasCooldown));
}
public static @NotNull ButtonBinding registerBinding(@NotNull Identifier id, int[] defaultButton, boolean hasCooldown) {
return registerBinding(id, defaultButton, Collections.emptyList(), InputHandlers::always, hasCooldown);
}
/**
* Sorts bindings to get bindings with the higher button counts first.
*/
public static void sortBindings() {
synchronized (BINDINGS) {
var sorted = BINDINGS.stream()
.sorted(Collections.reverseOrder(Comparator.comparingInt(binding -> binding.getButton().length))).toList();
BINDINGS.clear();
BINDINGS.addAll(sorted);
}
}
/**
* Registers a category of button bindings.
*
* @param category the category to register
* @return the registered category
*/
public static ButtonCategory registerCategory(@NotNull ButtonCategory category) {
CATEGORIES.add(category);
return category;
}
public static ButtonCategory registerCategory(@NotNull org.aperlambda.lambdacommon.Identifier identifier, int priority) {
return registerCategory(Identifier.of(identifier.getNamespace(), identifier.getName()), priority);
}
public static ButtonCategory registerCategory(@NotNull org.aperlambda.lambdacommon.Identifier identifier) {
return registerCategory(Identifier.of(identifier.getNamespace(), identifier.getName()));
}
public static ButtonCategory registerCategory(@NotNull Identifier identifier, int priority) {
return registerCategory(new ButtonCategory(identifier, priority));
}
public static ButtonCategory registerCategory(@NotNull Identifier identifier) {
return registerCategory(new ButtonCategory(identifier));
}
protected static ButtonCategory registerDefaultCategory(@NotNull String key, @NotNull Consumer<ButtonCategory> keyAdder) {
var category = registerCategory(Identifier.of("minecraft", key), CATEGORIES.size());
keyAdder.accept(category);
return category;
}
/**
* Loads the button bindings from configuration.
*/
public static void loadButtonBindings() {
var queue = new ArrayList<>(BINDINGS);
queue.forEach(MidnightControlsConfig::loadButtonBinding);
}
/**
* Returns the binding state.
*
* @param binding the binding
* @return the current state of the binding
*/
public static @NotNull ButtonState getBindingState(@NotNull ButtonBinding binding) {
var state = ButtonState.REPEAT;
for (int btn : binding.getButton()) {
var btnState = InputManager.STATES.getOrDefault(btn, ButtonState.NONE);
if (btnState == ButtonState.PRESS)
state = ButtonState.PRESS;
else if (btnState == ButtonState.RELEASE) {
state = ButtonState.RELEASE;
break;
} else if (btnState == ButtonState.NONE) {
state = ButtonState.NONE;
break;
}
}
return state;
}
public static float getBindingValue(@NotNull ButtonBinding binding, @NotNull ButtonState state) {
if (state.isUnpressed())
return 0.f;
float value = 0.f;
for (int btn : binding.getButton()) {
if (ButtonBinding.isAxis(btn)) {
value = BUTTON_VALUES.getOrDefault(btn, 1.f);
break;
} else {
value = 1.f;
}
}
return value;
}
/**
* Returns whether the button has duplicated bindings.
*
* @param button the button to check
* @return true if the button has duplicated bindings, else false
*/
public static boolean hasDuplicatedBindings(int[] button) {
return BINDINGS.parallelStream().filter(binding -> areButtonsEquivalent(binding.getButton(), button)).count() > 1;
}
/**
* Returns whether the button has duplicated bindings.
*
* @param binding the binding to check
* @return true if the button has duplicated bindings, else false
*/
public static boolean hasDuplicatedBindings(ButtonBinding binding) {
return BINDINGS.parallelStream().filter(other -> areButtonsEquivalent(other.getButton(), binding.getButton()) && other.filter.equals(binding.filter)).count() > 1;
}
/**
* Returns whether the specified buttons are equivalent or not.
*
* @param buttons1 first set of buttons
* @param buttons2 second set of buttons
* @return true if the two sets of buttons are equivalent, else false
*/
public static boolean areButtonsEquivalent(int[] buttons1, int[] buttons2) {
if (buttons1.length != buttons2.length)
return false;
int count = 0;
for (int btn : buttons1) {
for (int btn2 : buttons2) {
if (btn == btn2) {
count++;
break;
}
}
}
return count == buttons1.length;
}
/**
* Returns whether the button set contains the specified button or not.
*
* @param buttons the button set
* @param button the button to check
* @return true if the button set contains the specified button, else false
*/
public static boolean containsButton(int[] buttons, int button) {
return Arrays.stream(buttons).anyMatch(btn -> btn == button);
}
/**
* Updates the button states.
*/
public static void updateStates() {
for (var entry : STATES.int2ObjectEntrySet()) {
if (entry.getValue() == ButtonState.PRESS)
STATES.put(entry.getIntKey(), ButtonState.REPEAT);
else if (entry.getValue() == ButtonState.RELEASE)
STATES.put(entry.getIntKey(), ButtonState.NONE);
}
}
public static void updateBindings(@NotNull MinecraftClient client) {
var skipButtons = new IntArrayList();
record ButtonStateValue(ButtonState state, float value) {
}
var states = new Object2ObjectOpenHashMap<ButtonBinding, ButtonStateValue>();
for (var binding : BINDINGS) {
var state = binding.isAvailable(client) ? getBindingState(binding) : ButtonState.NONE;
if (skipButtons.intStream().anyMatch(btn -> containsButton(binding.getButton(), btn))) {
if (binding.isPressed())
state = ButtonState.RELEASE;
else
state = ButtonState.NONE;
}
if (state == ButtonState.RELEASE && !binding.isPressed()) {
state = ButtonState.NONE;
}
binding.setPressed(state.isPressed());
binding.update();
if (binding.isPressed())
Arrays.stream(binding.getButton()).forEach(skipButtons::add);
float value = getBindingValue(binding, state);
states.put(binding, new ButtonStateValue(state, value));
}
states.forEach((binding, state) -> {
if (state.state() != ButtonState.NONE) {
binding.handle(client, state.value(), state.state());
}
});
}
public static void queueMousePosition(double x, double y) {
INPUT_MANAGER.targetMouseX = (int) MathHelper.clamp(x, 0, MinecraftClient.getInstance().getWindow().getWidth());
INPUT_MANAGER.targetMouseY = (int) MathHelper.clamp(y, 0, MinecraftClient.getInstance().getWindow().getHeight());
}
public static void queueMoveMousePosition(double x, double y) {
queueMousePosition(INPUT_MANAGER.targetMouseX + x, INPUT_MANAGER.targetMouseY + y);
}
public static @NotNull Stream<ButtonBinding> streamBindings() {
return BINDINGS.stream();
}
public static @NotNull Stream<ButtonCategory> streamCategories() {
return CATEGORIES.stream();
}
/**
* Returns a new key binding instance.
*
* @param id the identifier of the key binding
* @param type the type
* @param code the code
* @param category the category of the key binding
* @return the key binding
* @see #makeKeyBinding(org.aperlambda.lambdacommon.Identifier, InputUtil.Type, int, String)
*/
public static @NotNull KeyBinding makeKeyBinding(@NotNull org.aperlambda.lambdacommon.Identifier id, InputUtil.Type type, int code, @NotNull String category) {
return makeKeyBinding(Identifier.of(id.getNamespace(), id.getName()), type, code, category);
}
/**
* Returns a new key binding instance.
*
* @param id the identifier of the key binding
* @param type the type
* @param code the code
* @param category the category of the key binding
* @return the key binding
* @see #makeKeyBinding(Identifier, InputUtil.Type, int, String)
*/
public static @NotNull KeyBinding makeKeyBinding(@NotNull Identifier id, InputUtil.Type type, int code, @NotNull String category) {
return new KeyBinding(String.format("key.%s.%s", id.getNamespace(), id.getPath()), type, code, category);
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.controller;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.util.MathUtil;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.NotNull;
/**
* Represents the movement handler.
*
* @author LambdAurora
* @version 1.6.0
* @since 1.4.0
*/
public final class MovementHandler implements PressAction {
public static final MovementHandler HANDLER = new MovementHandler();
private boolean shouldOverrideMovement = false;
private boolean pressingForward = false;
private boolean pressingBack = false;
private boolean pressingLeft = false;
private boolean pressingRight = false;
private float slowdownFactor = 1.f;
private float movementForward = 0.f;
private float movementSideways = 0.f;
private final MathUtil.PolarUtil polarUtil = new MathUtil.PolarUtil();
private MovementHandler() {
}
/**
* Applies movement input of this handler to the player's input.
*
* @param player The client player.
*/
public void applyMovement(@NotNull ClientPlayerEntity player) {
if (!this.shouldOverrideMovement)
return;
player.input.pressingForward = this.pressingForward;
player.input.pressingBack = this.pressingBack;
player.input.pressingLeft = this.pressingLeft;
player.input.pressingRight = this.pressingRight;
polarUtil.calculate(this.movementSideways, this.movementForward, this.slowdownFactor);
player.input.movementForward = polarUtil.polarY;
player.input.movementSideways = polarUtil.polarX;
this.shouldOverrideMovement = false;
}
@Override
public boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action) {
if (client.currentScreen != null || client.player == null)
return this.shouldOverrideMovement = false;
int direction = 0;
if (button == ButtonBinding.FORWARD || button == ButtonBinding.LEFT)
direction = 1;
else if (button == ButtonBinding.BACK || button == ButtonBinding.RIGHT)
direction = -1;
if (action.isUnpressed())
direction = 0;
this.shouldOverrideMovement = direction != 0;
if (!MidnightControlsConfig.analogMovement) {
value = 1.f;
}
this.slowdownFactor = client.player.shouldSlowDown() ? (MathHelper.clamp(
0.3F + (float) client.player.getAttributeValue(EntityAttributes.PLAYER_SNEAKING_SPEED),
0.0F,
1.0F
)) : 1.f;
if (button == ButtonBinding.FORWARD || button == ButtonBinding.BACK) {
// Handle forward movement.
this.pressingForward = direction > 0;
this.pressingBack = direction < 0;
this.movementForward = direction * value;
} else {
// Handle sideways movement.
this.pressingLeft = direction > 0;
this.pressingRight = direction < 0;
this.movementSideways = direction * value;
}
return this.shouldOverrideMovement;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.controller;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.StickyKeyBinding;
import org.jetbrains.annotations.NotNull;
/**
* Represents a press action callback.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.0.0
*/
@FunctionalInterface
public interface PressAction {
PressAction DEFAULT_ACTION = (client, button, value, action) -> {
if (action == ButtonState.REPEAT || client.currentScreen != null)
return false;
button.asKeyBinding().ifPresent(binding -> {
if (binding instanceof StickyKeyBinding)
binding.setPressed(button.isPressed());
else
((KeyBindingAccessor) binding).midnightcontrols$handlePressState(button.isPressed());
});
return true;
};
/**
* Handles when there is a press action.
*
* @param client the client instance
* @param action the action done
*/
boolean press(@NotNull MinecraftClient client, @NotNull ButtonBinding button, float value, @NotNull ButtonState action);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.enums;
/**
* Represents a button state.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
public enum ButtonState {
NONE(0),
PRESS(1),
RELEASE(2),
REPEAT(3);
public final int id;
ButtonState(int id) {
this.id = id;
}
/**
* Returns whether this state is a pressed state.
*
* @return true if this state is a pressed state, else false
*/
public boolean isPressed() {
return this == PRESS || this == REPEAT;
}
/**
* Returns whether this state is an unpressed state.
*
* @return true if this state is an unpressed state, else false
*/
public boolean isUnpressed() {
return this == RELEASE || this == NONE;
}
}

View File

@@ -0,0 +1,17 @@
package eu.midnightdust.midnightcontrols.client.enums;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
public enum CameraMode {
FLAT, ADAPTIVE;
public Text getTranslatedText() {
return Text.translatable("midnightcontrols.midnightconfig.enum."+this.getClass().getSimpleName()+"."+this.name());
}
public @NotNull CameraMode next() {
var v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.enums;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Optional;
/**
* Represents a controller type.
*
* @author LambdAurora
* @version 1.4.3
* @since 1.0.0
*/
public enum ControllerType {
DEFAULT(0),
DUALSHOCK(1),
DUALSENSE(2),
SWITCH(3),
XBOX_360(4),
XBOX(5),
STEAM_DECK(6),
STEAM_CONTROLLER(7),
OUYA(8),
NUMBERED(9);
private final int id;
private final Text text;
ControllerType(int id) {
this.id = id;
this.text = Text.translatable("midnightcontrols.controller_type." + this.getName());
}
ControllerType(int id, @NotNull Text text) {
this.id = id;
this.text = text;
}
/**
* Returns the controller type's identifier.
*
* @return the controller type's identifier
*/
public int getId() {
return this.id;
}
/**
* Returns the next controller type available.
*
* @return the next available controller type
*/
public @NotNull ControllerType next() {
var v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
/**
* Gets the translated text of this controller type.
*
* @return the translated text of this controller type
*/
public @NotNull Text getTranslatedText() {
return this.text;
}
public @NotNull String getName() {
return this.name().toLowerCase();
}
/**
* Gets the controller type from its identifier.
*
* @param id the identifier of the controller type
* @return the controller type if found, else empty
*/
public static @NotNull Optional<ControllerType> byId(@NotNull String id) {
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.enums;
import net.minecraft.text.Text;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Optional;
/**
* Represents the hud side which is the side where the movements buttons are.
*
* @author LambdAurora
* @version 1.4.0
* @since 1.0.0
*/
public enum HudSide {
LEFT,
RIGHT;
private final Text text;
HudSide() {
this.text = Text.translatable(this.getTranslationKey());
}
/**
* Returns the next side available.
*
* @return the next available side
*/
public @NotNull HudSide next() {
var v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
/**
* Returns the translation key of this hud side.
*
* @return the translation key of this hude side
*/
public @NotNull String getTranslationKey() {
return "midnightcontrols.hud_side." + this.getName();
}
/**
* Gets the translated text of this hud side.
*
* @return the translated text of this hud side
*/
public @NotNull Text getTranslatedText() {
return this.text;
}
public @NotNull String getName() {
return this.name();
}
/**
* Gets the hud side from its identifier.
*
* @param id the identifier of the hud side
* @return the hud side if found, else empty
*/
@Deprecated
public static @NotNull Optional<HudSide> byId(@NotNull String id) {
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -0,0 +1,17 @@
package eu.midnightdust.midnightcontrols.client.enums;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
public enum TouchMode {
CROSSHAIR, FINGER_POS;
public Text getTranslatedText() {
return Text.translatable("midnightcontrols.midnightconfig.enum."+this.getClass().getSimpleName()+"."+this.name());
}
public @NotNull TouchMode next() {
var v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.enums;
import net.minecraft.text.Text;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Optional;
/**
* Represents the virtual mouse skins.
*
* @version 1.7.0
* @since 1.2.0
*/
public enum VirtualMouseSkin implements Nameable {
DEFAULT_LIGHT("default_light"),
DEFAULT_DARK("default_dark"),
SECOND_LIGHT("second_light"),
SECOND_DARK("second_dark");
private final String name;
private final Text text;
VirtualMouseSkin(String name) {
this.name = name;
this.text = Text.translatable(this.getTranslationKey());
}
/**
* Returns the next virtual mouse skin available.
*
* @return the next available virtual mouse skin
*/
public @NotNull VirtualMouseSkin next() {
var v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
/**
* Returns the translation key of this virtual mouse skin.
*
* @return the virtual mouse skin's translation key
*/
public @NotNull String getTranslationKey() {
return "midnightcontrols.virtual_mouse.skin." + this.getName();
}
/**
* Gets the translated text of this virtual mouse skin.
*
* @return the translated text of this virtual mouse skin
*/
public @NotNull Text getTranslatedText() {
return this.text;
}
@Override
public @NotNull String getName() {
return this.name;
}
/**
* Gets the virtual mouse skin from its identifier.
*
* @param id the identifier of the virtual mouse skin
* @return the virtual mouse skin if found, else empty
*/
@Deprecated
public static @NotNull Optional<VirtualMouseSkin> byId(@NotNull String id) {
return Arrays.stream(values()).filter(mode -> mode.getName().equalsIgnoreCase(id)).findFirst();
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import net.minecraft.client.util.math.MatrixStack;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.option.SpruceOption;
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
import org.thinkingstudio.obsidianui.widget.text.SpruceTextAreaWidget;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
/**
* Represents the controller mappings file editor screen.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.4.3
*/
public class MappingsStringInputWidget extends SpruceContainerWidget {
private final SpruceOption reloadMappingsOption;
private String mappings;
private SpruceTextAreaWidget textArea;
protected MappingsStringInputWidget(Position position, int width, int height) {
super(position, width, height);
//super(new TranslatableText("midnightcontrols.menu.title.mappings.string"));
this.reloadMappingsOption = ReloadControllerMappingsOption.newOption(btn -> {
this.writeMappings();
});
this.init();
}
public void removed() {
this.writeMappings();
Controller.updateMappings();
}
public void onClose() {
this.removed();
}
public void writeMappings() {
if (this.textArea != null) {
this.mappings = this.textArea.getText();
try {
var fw = new FileWriter(MidnightControlsClient.MAPPINGS_FILE, false);
fw.write(this.mappings);
fw.close();
} catch (IOException e) {
if (this.client != null)
this.client.getToastManager().add(SystemToast.create(this.client, SystemToast.Type.PERIODIC_NOTIFICATION,
Text.translatable("midnightcontrols.controller.mappings.error.write"), Text.empty()));
e.fillInStackTrace();
}
}
}
protected void init() {
if (this.textArea != null) {
this.mappings = this.textArea.getText();
}
var mappings = "";
if (this.mappings != null)
mappings = this.mappings;
else if (MidnightControlsClient.MAPPINGS_FILE.exists()) {
try {
mappings = String.join("\n", Files.readAllLines(MidnightControlsClient.MAPPINGS_FILE.toPath()));
this.mappings = mappings;
} catch (IOException e) {
/* Ignored */
}
}
int textFieldWidth = (int) (this.width * (5.0 / 6.0));
this.textArea = new SpruceTextAreaWidget(Position.of(this, this.width / 2 - textFieldWidth / 2, 0), textFieldWidth, this.height - 50, Text.literal(mappings));
this.textArea.setText(mappings);
// Display as many lines as possible
this.textArea.setDisplayedLines(this.textArea.getInnerHeight() / this.client.textRenderer.fontHeight);
this.addChild(this.textArea);
this.addChild(this.reloadMappingsOption.createWidget(Position.of(this.width / 2 - 155, this.height - 29), 310));
}
//public void renderTitle(MatrixStack matrices, int mouseX, int mouseY, float delta) {
//this.drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 8, 16777215);
//}
}

View File

@@ -0,0 +1,301 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.client.enums.HudSide;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import org.thinkingstudio.obsidianui.hud.Hud;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents the midnightcontrols HUD.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.0.0
*/
public class MidnightControlsHud extends Hud {
private final MinecraftClient client = MinecraftClient.getInstance();
private int attackWidth = 0;
private int attackButtonWidth = 0;
private int dropItemWidth = 0;
private int dropItemButtonWidth = 0;
private int inventoryWidth = 0;
private int inventoryButtonWidth = 0;
private int swapHandsWidth = 0;
private int swapHandsButtonWidth = 0;
private boolean showSwapHandsAction = false;
private int useWidth = 0;
private int useButtonWidth = 0;
private String attackAction = "";
private String placeAction = "";
private int ticksDisplayedCrosshair = 0;
private static boolean isCrammed = false;
public MidnightControlsHud() {
super(Identifier.of(MidnightControlsConstants.NAMESPACE, "hud/button_indicator"));
}
@Override
public void init(@NotNull MinecraftClient client, int screenWidth, int screenHeight) {
super.init(client, screenWidth, screenHeight);
this.inventoryWidth = this.width(ButtonBinding.INVENTORY);
this.inventoryButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.INVENTORY);
this.swapHandsWidth = this.width(ButtonBinding.SWAP_HANDS);
this.swapHandsButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.SWAP_HANDS);
this.dropItemWidth = this.width(ButtonBinding.DROP_ITEM);
this.dropItemButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.DROP_ITEM);
this.attackButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.ATTACK);
this.useButtonWidth = MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.USE);
}
/**
* Renders the MidnightControls HUD.
*/
@Override
public void render(DrawContext context, float tickDelta) {
if (this.client == null) return;
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && this.client.currentScreen == null) {
isCrammed = client.getWindow().getScaledWidth() < 520;
int y = bottom(2);
MatrixStack matrices = context.getMatrices();
matrices.push();
this.renderFirstIcons(context, MidnightControlsConfig.hudSide == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderSecondIcons(context, MidnightControlsConfig.hudSide == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderFirstSection(context, MidnightControlsConfig.hudSide == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
this.renderSecondSection(context, MidnightControlsConfig.hudSide == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
matrices.pop();
}
if (MidnightControlsClient.reacharound.isLastReacharoundVertical()) {
// Render crosshair indicator.
var window = this.client.getWindow();
var text = "[ ]";
float scale = Math.min(5, this.ticksDisplayedCrosshair + tickDelta) / 5F;
scale *= scale;
int opacity = ((int) (255 * scale)) << 24;
context.drawText(client.textRenderer, text, (int) (window.getScaledWidth() / 2.f - this.client.textRenderer.getWidth(text) / 2.f),
(int) (window.getScaledHeight() / 2.f - 4), 0xCCCCCC | opacity, false);
}
}
public void renderFirstIcons(DrawContext context, int x, int y) {
int offset = 2 + this.inventoryWidth + this.inventoryButtonWidth + 4;
int currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x : x - this.inventoryButtonWidth;
if (!ButtonBinding.INVENTORY.isNotBound()) this.drawButton(context, currentX, y, ButtonBinding.INVENTORY, true);
if (!ButtonBinding.SWAP_HANDS.isNotBound() && !isCrammed && showSwapHandsAction) this.drawButton(context, currentX += (MidnightControlsConfig.hudSide == HudSide.LEFT ? offset : -offset), y, ButtonBinding.SWAP_HANDS, true);
offset = 2 + this.swapHandsWidth + this.dropItemButtonWidth + 4;
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.RIGHT) {
currentX += -offset;
} else {
currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x : x - this.dropItemButtonWidth;
y -= 20;
}
if (!ButtonBinding.DROP_ITEM.isNotBound() && client.player != null)
this.drawButton(context, currentX, y, ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty());
}
public void renderSecondIcons(DrawContext context, int x, int y) {
int offset;
int currentX = x;
if (isCrammed && showSwapHandsAction && !this.client.options.getShowSubtitles().getValue() && !ButtonBinding.SWAP_HANDS.isNotBound()) {
if (MidnightControlsConfig.hudSide == HudSide.LEFT)
currentX -= this.useButtonWidth;
this.drawButton(context, currentX, y, ButtonBinding.SWAP_HANDS, true);
currentX = x;
y -= 20;
}
if (!this.placeAction.isEmpty() && (!ButtonBinding.USE.isNotBound()) ) {
if (MidnightControlsConfig.hudSide == HudSide.LEFT)
currentX -= this.useButtonWidth;
this.drawButton(context, currentX, y, ButtonBinding.USE, true);
offset = 2 + this.useWidth + 4;
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.LEFT) {
currentX -= offset;
} else {
currentX = x;
y -= 20;
}
}
if (MidnightControlsConfig.hudSide == HudSide.LEFT)
currentX -= this.attackButtonWidth;
if (!ButtonBinding.ATTACK.isNotBound()) this.drawButton(context, currentX, y, ButtonBinding.ATTACK, this.attackWidth != 0);
}
public void renderFirstSection(DrawContext context, int x, int y) {
int currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x + this.inventoryButtonWidth + 2 : x - this.inventoryButtonWidth - 2 - this.inventoryWidth;
if (!ButtonBinding.INVENTORY.isNotBound()) this.drawTip(context, currentX, y, ButtonBinding.INVENTORY, true);
currentX += MidnightControlsConfig.hudSide == HudSide.LEFT ? this.inventoryWidth + 4 + this.swapHandsButtonWidth + 2
: -this.swapHandsWidth - 2 - this.swapHandsButtonWidth - 4;
if (!ButtonBinding.SWAP_HANDS.isNotBound() && !isCrammed && showSwapHandsAction) this.drawTip(context, currentX, y, ButtonBinding.SWAP_HANDS, true);
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.RIGHT) {
currentX += -this.dropItemWidth - 2 - this.dropItemButtonWidth - 4;
} else {
y -= 20;
currentX = MidnightControlsConfig.hudSide == HudSide.LEFT ? x + this.dropItemButtonWidth + 2 : x - this.dropItemButtonWidth - 2 - this.dropItemWidth;
}
if (!ButtonBinding.DROP_ITEM.isNotBound() && client.player != null) this.drawTip(context, currentX, y, ButtonBinding.DROP_ITEM, !this.client.player.getMainHandStack().isEmpty());
}
public void renderSecondSection(DrawContext context, int x, int y) {
int currentX = x;
if (isCrammed && showSwapHandsAction && !this.client.options.getShowSubtitles().getValue() && !ButtonBinding.SWAP_HANDS.isNotBound()) {
currentX += MidnightControlsConfig.hudSide == HudSide.RIGHT ? this.swapHandsButtonWidth + 2 : -this.swapHandsButtonWidth - 2 - this.swapHandsWidth;
this.drawTip(context, currentX, y, ButtonBinding.SWAP_HANDS, true);
currentX = x;
y -= 20;
}
if (!this.placeAction.isEmpty()) {
currentX += MidnightControlsConfig.hudSide == HudSide.RIGHT ? this.useButtonWidth + 2 : -this.useButtonWidth - 2 - this.useWidth;
this.drawTip(context, currentX, y, this.placeAction, true);
if (this.client.options.getShowSubtitles().getValue() && MidnightControlsConfig.hudSide == HudSide.LEFT) {
currentX -= 4;
} else {
currentX = x;
y -= 20;
}
}
currentX += MidnightControlsConfig.hudSide == HudSide.RIGHT ? this.attackButtonWidth + 2 : -this.attackButtonWidth - 2 - this.attackWidth;
if (!ButtonBinding.ATTACK.isNotBound()) this.drawTip(context, currentX, y, this.attackAction, this.attackWidth != 0);
}
@Override
public void tick() {
if (this.client == null) return;
super.tick();
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER) {
if (this.client.crosshairTarget == null)
return;
String placeAction;
// Update "Use" tip status.
BlockHitResult placeHitResult;
if (this.client.crosshairTarget.getType() == HitResult.Type.MISS) {
placeHitResult = MidnightControlsClient.reacharound.getLastReacharoundResult();
this.attackAction = "";
this.attackWidth = 0;
} else {
if (this.client.crosshairTarget.getType() == HitResult.Type.BLOCK)
placeHitResult = (BlockHitResult) this.client.crosshairTarget;
else
placeHitResult = null;
this.attackAction = this.client.crosshairTarget.getType() == HitResult.Type.BLOCK ? "midnightcontrols.action.hit" : ButtonBinding.ATTACK.getTranslationKey();
this.attackWidth = this.width(attackAction);
}
if (MidnightControlsClient.reacharound.isLastReacharoundVertical()) {
if (this.ticksDisplayedCrosshair < 5)
this.ticksDisplayedCrosshair++;
} else {
this.ticksDisplayedCrosshair = 0;
}
var customAttackAction = MidnightControlsCompat.getAttackActionAt(this.client, placeHitResult);
if (customAttackAction != null) {
this.attackAction = customAttackAction;
this.attackWidth = this.width(customAttackAction);
}
ItemStack stack = null;
if (this.client.player != null) {
stack = this.client.player.getMainHandStack();
if (stack == null || stack.isEmpty())
stack = this.client.player.getOffHandStack();
}
if (stack == null || stack.isEmpty()) {
placeAction = "";
} else {
if (placeHitResult != null && stack.getItem() instanceof BlockItem) {
placeAction = "midnightcontrols.action.place";
} else {
placeAction = ButtonBinding.USE.getTranslationKey();
}
}
var customUseAction = MidnightControlsCompat.getUseActionAt(this.client, placeHitResult);
if (customUseAction != null)
placeAction = customUseAction;
this.placeAction = placeAction;
this.showSwapHandsAction = !this.client.player.getMainHandStack().isEmpty() || !this.client.player.getOffHandStack().isEmpty();
// Cache the "Use" tip width.
if (this.placeAction.isEmpty())
this.useWidth = 0;
else
this.useWidth = this.width(this.placeAction);
}
}
@Override
public boolean hasTicks() {
return true;
}
private int bottom(int y) {
return (this.client.getWindow().getScaledHeight() - y - MidnightControlsRenderer.ICON_SIZE);
}
private int width(@NotNull ButtonBinding binding) {
return this.width(binding.getTranslationKey());
}
private int width(@Nullable String text) {
if (text == null || text.isEmpty())
return 0;
return this.client.textRenderer.getWidth(I18n.translate(text));
}
private void drawButton(DrawContext context, int x, int y, @NotNull ButtonBinding button, boolean display) {
if (display)
MidnightControlsRenderer.drawButton(context, x, y, button, this.client);
}
private void drawTip(DrawContext context, int x, int y, @NotNull ButtonBinding button, boolean display) {
this.drawTip(context, x, y, button.getTranslationKey(), display);
}
private void drawTip(DrawContext context, int x, int y, @NotNull String action, boolean display) {
if (!display)
return;
var translatedAction = I18n.translate(action);
int textY = (MidnightControlsRenderer.ICON_SIZE / 2 - this.client.textRenderer.fontHeight / 2) + 1;
context.drawText(this.client.textRenderer, translatedAction, x, (y + textY), 14737632, false);
}
}

View File

@@ -0,0 +1,264 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import eu.midnightdust.midnightcontrols.client.enums.ControllerType;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.MidnightInput;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.screen.slot.Slot;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
/**
* Represents the midnightcontrols renderer.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.2.0
*/
public class MidnightControlsRenderer {
public static final int ICON_SIZE = 20;
private static final int BUTTON_SIZE = 15;
private static final int AXIS_SIZE = 18;
public static int getButtonSize(int button) {
return switch (button) {
case -1 -> 0;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 100, GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 200,
GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 100, GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 200,
GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 100, GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 200,
GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 100, GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 200 -> AXIS_SIZE;
default -> BUTTON_SIZE;
};
}
/**
* Gets the binding icon width.
*
* @param binding the binding
* @return the width
*/
public static int getBindingIconWidth(@NotNull ButtonBinding binding) {
return getBindingIconWidth(binding.getButton());
}
/**
* Gets the binding icon width.
*
* @param buttons the buttons
* @return the width
*/
public static int getBindingIconWidth(int[] buttons) {
int width = 0;
for (int i = 0; i < buttons.length; i++) {
width += ICON_SIZE;
if (i + 1 < buttons.length) {
width += 2;
}
}
return width;
}
public static ButtonSize drawButton(DrawContext context, int x, int y, @NotNull ButtonBinding button, @NotNull MinecraftClient client) {
return drawButton(context, x, y, button.getButton(), client);
}
public static ButtonSize drawButton(DrawContext context, int x, int y, int[] buttons, @NotNull MinecraftClient client) {
int height = 0;
int length = 0;
int currentX = x;
for (int i = 0; i < buttons.length; i++) {
int btn = buttons[i];
int size = drawButton(context, currentX, y, btn, client);
if (size > height)
height = size;
length += size;
if (i + 1 < buttons.length) {
length += 2;
currentX = x + length;
}
}
return new ButtonSize(length, height);
}
public static int drawButton(DrawContext context, int x, int y, int button, @NotNull MinecraftClient client) {
boolean second = false;
if (button == -1)
return 0;
else if (button >= 500) {
button -= 1000;
second = true;
}
int controllerType = MidnightControlsConfig.controllerType == ControllerType.DEFAULT ? MidnightControlsConfig.matchControllerToType().getId() : MidnightControlsConfig.controllerType.getId();
boolean axis = false;
int buttonOffset = button * 15;
switch (button) {
case 15 -> buttonOffset = 0;
case 16 -> buttonOffset = 18;
case 17 -> buttonOffset = 36;
case 18 -> buttonOffset = 54;
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER -> buttonOffset = 7 * 15;
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER -> buttonOffset = 8 * 15;
case GLFW.GLFW_GAMEPAD_BUTTON_BACK -> buttonOffset = 4 * 15;
case GLFW.GLFW_GAMEPAD_BUTTON_START -> buttonOffset = 6 * 15;
case GLFW.GLFW_GAMEPAD_BUTTON_GUIDE -> buttonOffset = 5 * 15;
case GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB -> buttonOffset = 15 * 15;
case GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB -> buttonOffset = 16 * 15;
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 100 -> {
buttonOffset = 0;
axis = true;
}
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 100 -> {
buttonOffset = 18;
axis = true;
}
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 100 -> {
buttonOffset = 2 * 18;
axis = true;
}
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 100 -> {
buttonOffset = 3 * 18;
axis = true;
}
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_X + 200 -> {
buttonOffset = 4 * 18;
axis = true;
}
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y + 200 -> {
buttonOffset = 5 * 18;
axis = true;
}
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X + 200 -> {
buttonOffset = 6 * 18;
axis = true;
}
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y + 200 -> {
buttonOffset = 7 * 18;
axis = true;
}
case GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 100, GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER + 200 -> buttonOffset = 9 * 15;
case GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 100, GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER + 200 -> buttonOffset = 10 * 15;
}
RenderSystem.disableDepthTest();
int assetSize = axis || (button >= 15 && button <= 18) ? AXIS_SIZE : BUTTON_SIZE;
RenderSystem.setShaderColor(1.f, second ? 0.f : 1.f, 1.f, 1.f);
context.drawTexture(axis ? MidnightControlsClient.CONTROLLER_AXIS : button >= 15 && button <= 19 ? MidnightControlsClient.CONTROLLER_EXPANDED :MidnightControlsClient.CONTROLLER_BUTTONS
, x + (ICON_SIZE / 2 - assetSize / 2), y + (ICON_SIZE / 2 - assetSize / 2),
(float) buttonOffset, (float) (controllerType * assetSize),
assetSize, assetSize,
256, 256);
RenderSystem.enableDepthTest();
return ICON_SIZE;
}
public static int drawButtonTip(DrawContext context, int x, int y, @NotNull ButtonBinding button, boolean display, @NotNull MinecraftClient client) {
return drawButtonTip(context, x, y, button.getButton(), button.getTranslationKey(), display, client);
}
public static int drawButtonTip(DrawContext context, int x, int y, int[] button, @NotNull String action, boolean display, @NotNull MinecraftClient client) {
if (display) {
int buttonWidth = drawButton(context, x, y, button, client).length();
var translatedAction = I18n.translate(action);
int textY = (MidnightControlsRenderer.ICON_SIZE / 2 - client.textRenderer.fontHeight / 2) + 1;
return context.drawTextWithShadow(client.textRenderer, translatedAction, (x + buttonWidth + 2), (y + textY), 14737632);
}
return -10;
}
private static int getButtonTipWidth(@NotNull String action, @NotNull TextRenderer textRenderer) {
return 15 + 5 + textRenderer.getWidth(action);
}
public static void renderVirtualCursor(@NotNull DrawContext context, @NotNull MinecraftClient client) {
if (!MidnightControlsConfig.virtualMouse || (client.currentScreen == null
|| MidnightInput.isScreenInteractive(client.currentScreen)))
return;
int mouseX = (int) (client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
int mouseY = (int) (client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight());
boolean hoverSlot = false;
if (client.currentScreen instanceof HandledScreenAccessor inventoryScreen) {
int guiLeft = inventoryScreen.getX();
int guiTop = inventoryScreen.getY();
Slot slot = inventoryScreen.midnightcontrols$getSlotAt(mouseX, mouseY);
if (slot != null) {
mouseX = guiLeft + slot.x;
mouseY = guiTop + slot.y;
hoverSlot = true;
}
}
if (!hoverSlot && client.currentScreen != null) {
var slot = MidnightControlsCompat.getSlotAt(client.currentScreen, mouseX, mouseY);
if (slot != null) {
mouseX = slot.x();
mouseY = slot.y();
hoverSlot = true;
}
}
if (!hoverSlot) {
mouseX -= 8;
mouseY -= 8;
}
//context.getMatrices().push();
context.getMatrices().translate(0f, 0f, 999f);
drawCursor(context, mouseX, mouseY, hoverSlot, client);
//context.getMatrices().pop();
}
/**
* Draws the virtual cursor.
*
* @param context the context
* @param x x coordinate
* @param y y coordinate
* @param hoverSlot true if hovering a slot, else false
* @param client the client instance
*/
public static void drawCursor(@NotNull DrawContext context, int x, int y, boolean hoverSlot, @NotNull MinecraftClient client) {
//RenderSystem.disableDepthTest();
//RenderSystem.setShaderColor(1.f, 1.f, 1.f, 1.f);
//RenderSystem.disableBlend();
//RenderSystem.setShaderTexture(0, MidnightControlsClient.CURSOR_TEXTURE);
context.drawTexture(MidnightControlsClient.CURSOR_TEXTURE, x, y,
hoverSlot ? 16.f : 0.f, MidnightControlsConfig.virtualMouseSkin.ordinal() * 16.f,
16, 16, 32, 64);
context.fill(1, 1, x, y, 0xFFFFFF);
context.draw();
//RenderSystem.enableDepthTest();
}
public record ButtonSize(int length, int height) {
}
}

View File

@@ -0,0 +1,524 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.util.platform.NetworkUtil;
import org.thinkingstudio.obsidianui.background.Background;
import org.thinkingstudio.obsidianui.widget.SpruceWidget;
import eu.midnightdust.lib.util.MidnightColorUtil;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import eu.midnightdust.midnightcontrols.client.gui.widget.ControllerControlsWidget;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.SpruceTexts;
import org.thinkingstudio.obsidianui.option.*;
import org.thinkingstudio.obsidianui.screen.SpruceScreen;
import org.thinkingstudio.obsidianui.widget.AbstractSpruceWidget;
import org.thinkingstudio.obsidianui.widget.SpruceLabelWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceOptionListWidget;
import org.thinkingstudio.obsidianui.widget.container.tabbed.SpruceTabbedWidget;
import eu.midnightdust.midnightcontrols.packet.ControlsModePayload;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.render.*;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Util;
import org.joml.Matrix4f;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
/**
* Represents the midnightcontrols settings screen.
*/
public class MidnightControlsSettingsScreen extends SpruceScreen {
private static final Text SDL2_GAMEPAD_TOOL = Text.literal("SDL2 Gamepad Tool").formatted(Formatting.GREEN);
public static final String GAMEPAD_TOOL_URL = "https://generalarcade.com/gamepadtool/";
private final Screen parent;
// General options
private final SpruceOption inputModeOption;
private final SpruceOption autoSwitchModeOption;
private final SpruceOption rotationSpeedOption;
private final SpruceOption yAxisRotationSpeedOption;
private final SpruceOption mouseSpeedOption;
private final SpruceOption joystickAsMouseOption;
private final SpruceOption eyeTrackingAsMouseOption;
private final SpruceOption eyeTrackingDeadzone;
private final SpruceOption virtualMouseOption;
private final SpruceOption hideCursorOption;
private final SpruceOption resetOption;
private final SpruceOption advancedConfigOption;
// Gameplay options
private final SpruceOption analogMovementOption;
private final SpruceOption doubleTapToSprintOption;
private final SpruceOption autoJumpOption;
private final SpruceOption controllerToggleSneakOption;
private final SpruceOption controllerToggleSprintOption;
private final SpruceOption fastBlockPlacingOption;
private final SpruceOption frontBlockPlacingOption;
private final SpruceOption verticalReacharoundOption;
private final SpruceOption flyDriftingOption;
private final SpruceOption flyVerticalDriftingOption;
// Appearance options
private final SpruceOption controllerTypeOption;
private final SpruceOption virtualMouseSkinOption;
private final SpruceOption hudEnableOption;
private final SpruceOption hudSideOption;
private final SpruceOption moveChatOption;
// Controller options
private final SpruceOption controllerOption =
new SpruceCyclingOption("midnightcontrols.menu.controller",
amount -> {
int id = MidnightControlsConfig.getController().id();
id += amount;
if (id > GLFW.GLFW_JOYSTICK_LAST)
id = GLFW.GLFW_JOYSTICK_1;
id = searchNextAvailableController(id, false);
MidnightControlsConfig.setController(Controller.byId(id));
if (MidnightControlsConfig.debug) System.out.println(Controller.byId(id).getName() + "'s Controller GUID: " + Controller.byId(id).getGuid());
},
option -> {
var controller = MidnightControlsConfig.getController();
var controllerName = controller.getName();
if (!controller.isConnected())
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.RED));
else if (!controller.isGamepad())
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.GOLD));
else
return option.getDisplayText(Text.literal(controllerName));
}, null);
private final SpruceOption secondControllerOption = new SpruceCyclingOption("midnightcontrols.menu.controller2",
amount -> {
int id = MidnightControlsConfig.getSecondController().map(Controller::id).orElse(-1);
id += amount;
if (id > GLFW.GLFW_JOYSTICK_LAST)
id = -1;
id = searchNextAvailableController(id, true);
MidnightControlsConfig.setSecondController(id == -1 ? null : Controller.byId(id));
},
option -> MidnightControlsConfig.getSecondController().map(controller -> {
var controllerName = controller.getName();
if (!controller.isConnected())
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.RED));
else if (!controller.isGamepad())
return option.getDisplayText(Text.literal(controllerName).formatted(Formatting.GOLD));
else
return option.getDisplayText(Text.literal(controllerName));
}).orElse(option.getDisplayText(SpruceTexts.OPTIONS_OFF.copyContentOnly().formatted(Formatting.RED))),
Text.translatable("midnightcontrols.menu.controller2.tooltip"));
private final SpruceOption unfocusedInputOption;
private final SpruceOption invertsRightXAxis;
private final SpruceOption invertsRightYAxis;
private final SpruceOption cameraModeOption;
private final SpruceOption toggleControllerProfileOption;
private final SpruceOption rightDeadZoneOption;
private final SpruceOption leftDeadZoneOption;
private final SpruceOption[] maxAnalogValueOptions = new SpruceOption[]{
maxAnalogValueOption("midnightcontrols.menu.max_left_x_value", GLFW.GLFW_GAMEPAD_AXIS_LEFT_X),
maxAnalogValueOption("midnightcontrols.menu.max_left_y_value", GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y),
maxAnalogValueOption("midnightcontrols.menu.max_right_x_value", GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X),
maxAnalogValueOption("midnightcontrols.menu.max_right_y_value", GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y)
};
private static SpruceOption maxAnalogValueOption(String key, int axis) {
return new SpruceDoubleOption(key, .25f, 1.f, 0.05f,
() -> MidnightControlsConfig.getAxisMaxValue(axis),
newValue -> MidnightControlsConfig.setAxisMaxValue(axis, newValue),
option -> option.getDisplayText(Text.literal(String.format("%.2f", option.get()))),
Text.translatable(key.concat(".tooltip"))
);
}
// Touch options
private final SpruceOption touchWithControllerOption;
private final SpruceOption touchSpeedOption;
private final SpruceOption touchBreakDelayOption;
private final SpruceOption invertTouchOption;
private final SpruceOption touchTransparencyOption;
private final SpruceOption touchModeOption;
private final MutableText controllerMappingsUrlText = Text.literal("(")
.append(Text.literal(GAMEPAD_TOOL_URL).formatted(Formatting.GOLD))
.append("),");
private static int searchNextAvailableController(int newId, boolean allowNone) {
if ((allowNone && newId == -1) || newId == 0) return newId;
boolean connected = Controller.byId(newId).isConnected();
if (!connected) {
newId++;
}
if (newId > GLFW.GLFW_JOYSTICK_LAST)
newId = allowNone ? -1 : GLFW.GLFW_JOYSTICK_1;
return connected ? newId : searchNextAvailableController(newId, allowNone);
}
public MidnightControlsSettingsScreen(Screen parent, boolean hideControls) {
super(Text.translatable("midnightcontrols.title.settings"));
MidnightControlsConfig.isEditing = true;
this.parent = parent;
// General options
this.inputModeOption = new SpruceCyclingOption("midnightcontrols.menu.controls_mode",
amount -> {
var next = MidnightControlsConfig.controlsMode.next();
MidnightControlsConfig.controlsMode = next;
MidnightControlsConfig.save();
if (this.client != null && this.client.player != null) {
NetworkUtil.sendPayloadC2S(new ControlsModePayload(next.getName()));
}
}, option -> option.getDisplayText(Text.translatable(MidnightControlsConfig.controlsMode.getTranslationKey())),
Text.translatable("midnightcontrols.menu.controls_mode.tooltip"));
this.autoSwitchModeOption = new SpruceToggleBooleanOption("midnightcontrols.menu.auto_switch_mode", () -> MidnightControlsConfig.autoSwitchMode,
value -> MidnightControlsConfig.autoSwitchMode = value, Text.translatable("midnightcontrols.menu.auto_switch_mode.tooltip"));
this.rotationSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.rotation_speed", 0.0, 100.0, .5f,
() -> MidnightControlsConfig.rotationSpeed,
value -> MidnightControlsConfig.rotationSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.rotation_speed.tooltip"));
this.yAxisRotationSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.y_axis_rotation_speed", 0.0, 100.0, .5f,
() -> MidnightControlsConfig.yAxisRotationSpeed,
value -> MidnightControlsConfig.yAxisRotationSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.y_axis_rotation_speed.tooltip"));
this.mouseSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.mouse_speed", 0.0, 150.0, .5f,
() -> MidnightControlsConfig.mouseSpeed,
value -> MidnightControlsConfig.mouseSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.mouse_speed.tooltip"));
this.joystickAsMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.joystick_as_mouse",
() -> MidnightControlsConfig.joystickAsMouse, value -> MidnightControlsConfig.joystickAsMouse = value,
Text.translatable("midnightcontrols.menu.joystick_as_mouse.tooltip"));
this.eyeTrackingAsMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.eye_tracker_as_mouse",
() -> MidnightControlsConfig.eyeTrackerAsMouse, value -> MidnightControlsConfig.eyeTrackerAsMouse = value,
Text.translatable("midnightcontrols.menu.eye_tracker_as_mouse.tooltip"));
this.eyeTrackingDeadzone = new SpruceDoubleInputOption("midnightcontrols.menu.eye_tracker_deadzone",
() -> MidnightControlsConfig.eyeTrackerDeadzone, value -> MidnightControlsConfig.eyeTrackerDeadzone = value,
Text.translatable("midnightcontrols.menu.eye_tracker_deadzone.tooltip"));
this.resetOption = SpruceSimpleActionOption.reset(btn -> {
MidnightControlsConfig.reset();
var client = MinecraftClient.getInstance();
this.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
});
this.advancedConfigOption = SpruceSimpleActionOption.of("midnightcontrols.midnightconfig.title", button -> client.setScreen(MidnightControlsConfig.getScreen(this, "midnightcontrols")));
// Gameplay options
this.analogMovementOption = new SpruceToggleBooleanOption("midnightcontrols.menu.analog_movement",
() -> MidnightControlsConfig.analogMovement, value -> MidnightControlsConfig.analogMovement = value,
Text.translatable("midnightcontrols.menu.analog_movement.tooltip"));
this.doubleTapToSprintOption = new SpruceToggleBooleanOption("midnightcontrols.menu.double_tap_to_sprint",
() -> MidnightControlsConfig.doubleTapToSprint, value -> MidnightControlsConfig.doubleTapToSprint = value,
Text.translatable("midnightcontrols.menu.double_tap_to_sprint.tooltip"));
this.autoJumpOption = new SpruceToggleBooleanOption("options.autoJump",
() -> this.client.options.getAutoJump().getValue(),
newValue -> this.client.options.getAutoJump().setValue(newValue),
null);
this.controllerToggleSneakOption = new SpruceToggleBooleanOption("midnightcontrols.menu.controller_toggle_sneak",
() -> MidnightControlsConfig.controllerToggleSneak, value -> MidnightControlsConfig.controllerToggleSneak = value,
null);
this.controllerToggleSprintOption = new SpruceToggleBooleanOption("midnightcontrols.menu.controller_toggle_sprint",
() -> MidnightControlsConfig.controllerToggleSprint, value -> MidnightControlsConfig.controllerToggleSprint = value,
null);
this.fastBlockPlacingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fast_block_placing", () -> MidnightControlsConfig.fastBlockPlacing,
value -> MidnightControlsConfig.fastBlockPlacing = value, Text.translatable("midnightcontrols.menu.fast_block_placing.tooltip"));
this.frontBlockPlacingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.reacharound.horizontal", () -> MidnightControlsConfig.horizontalReacharound,
value -> MidnightControlsConfig.horizontalReacharound = value, Text.translatable("midnightcontrols.menu.reacharound.horizontal.tooltip"));
this.verticalReacharoundOption = new SpruceToggleBooleanOption("midnightcontrols.menu.reacharound.vertical", () -> MidnightControlsConfig.verticalReacharound,
value -> MidnightControlsConfig.verticalReacharound = value, Text.translatable("midnightcontrols.menu.reacharound.vertical.tooltip"));
this.flyDriftingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fly_drifting", () -> MidnightControlsConfig.flyDrifting,
value -> MidnightControlsConfig.flyDrifting = value, Text.translatable("midnightcontrols.menu.fly_drifting.tooltip"));
this.flyVerticalDriftingOption = new SpruceToggleBooleanOption("midnightcontrols.menu.fly_drifting_vertical", () -> MidnightControlsConfig.verticalFlyDrifting,
value -> MidnightControlsConfig.verticalFlyDrifting = value, Text.translatable("midnightcontrols.menu.fly_drifting_vertical.tooltip"));
// Appearance options
this.controllerTypeOption = new SpruceCyclingOption("midnightcontrols.menu.controller_type",
amount -> MidnightControlsConfig.controllerType = MidnightControlsConfig.controllerType.next(),
option -> option.getDisplayText(MidnightControlsConfig.controllerType.getTranslatedText()),
Text.translatable("midnightcontrols.menu.controller_type.tooltip"));
this.virtualMouseSkinOption = new SpruceCyclingOption("midnightcontrols.menu.virtual_mouse.skin",
amount -> MidnightControlsConfig.virtualMouseSkin = MidnightControlsConfig.virtualMouseSkin.next(),
option -> option.getDisplayText(MidnightControlsConfig.virtualMouseSkin.getTranslatedText()),
null);
this.hudEnableOption = new SpruceToggleBooleanOption("midnightcontrols.menu.hud_enable", () -> MidnightControlsConfig.hudEnable,
MidnightControlsClient::setHudEnabled, Text.translatable("midnightcontrols.menu.hud_enable.tooltip"));
this.hudSideOption = new SpruceCyclingOption("midnightcontrols.menu.hud_side",
amount -> MidnightControlsConfig.hudSide = MidnightControlsConfig.hudSide.next(),
option -> option.getDisplayText(MidnightControlsConfig.hudSide.getTranslatedText()),
Text.translatable("midnightcontrols.menu.hud_side.tooltip"));
this.moveChatOption = new SpruceToggleBooleanOption("midnightcontrols.menu.move_chat", () -> MidnightControlsConfig.moveChat,
value -> MidnightControlsConfig.moveChat = value, Text.translatable("midnightcontrols.menu.move_chat.tooltip"));
// Controller options
this.toggleControllerProfileOption = new SpruceToggleBooleanOption("midnightcontrols.menu.separate_controller_profile", () -> MidnightControlsConfig.controllerBindingProfiles.containsKey(MidnightControlsConfig.getController().getGuid()), value -> {
if (value) {
MidnightControlsConfig.controllerBindingProfiles.put(MidnightControlsConfig.getController().getGuid(), MidnightControlsConfig.getBindingsForController());
MidnightControlsConfig.updateBindingsForController(MidnightControlsConfig.getController());
} else {
MidnightControlsConfig.controllerBindingProfiles.remove(MidnightControlsConfig.getController().getGuid());
MidnightControlsConfig.updateBindingsForController(MidnightControlsConfig.getController());
}
}, Text.empty());
this.cameraModeOption = new SpruceCyclingOption("midnightcontrols.menu.camera_mode",
amount -> MidnightControlsConfig.cameraMode = MidnightControlsConfig.cameraMode.next(),
option -> option.getDisplayText(MidnightControlsConfig.cameraMode.getTranslatedText()),
Text.translatable("midnightcontrols.menu.camera_mode.tooltip"));
this.rightDeadZoneOption = new SpruceDoubleOption("midnightcontrols.menu.right_dead_zone", 0.05, 1.0, .05f,
() -> MidnightControlsConfig.rightDeadZone,
value -> MidnightControlsConfig.rightDeadZone = value, option -> {
var value = String.valueOf(option.get());
return option.getDisplayText(Text.literal(value.substring(0, Math.min(value.length(), 5))));
}, Text.translatable("midnightcontrols.menu.right_dead_zone.tooltip"));
this.leftDeadZoneOption = new SpruceDoubleOption("midnightcontrols.menu.left_dead_zone", 0.05, 1.0, .05f,
() -> MidnightControlsConfig.leftDeadZone,
value -> MidnightControlsConfig.leftDeadZone = value, option -> {
var value = String.valueOf(option.get());
return option.getDisplayText(Text.literal(value.substring(0, Math.min(value.length(), 5))));
}, Text.translatable("midnightcontrols.menu.left_dead_zone.tooltip"));
this.invertsRightXAxis = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_right_x_axis", () -> MidnightControlsConfig.invertRightXAxis,
value -> MidnightControlsConfig.invertRightXAxis = value, null);
this.invertsRightYAxis = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_right_y_axis", () -> MidnightControlsConfig.invertRightYAxis,
value -> MidnightControlsConfig.invertRightYAxis = value, null);
this.unfocusedInputOption = new SpruceToggleBooleanOption("midnightcontrols.menu.unfocused_input", () -> MidnightControlsConfig.unfocusedInput,
value -> MidnightControlsConfig.unfocusedInput = value, Text.translatable("midnightcontrols.menu.unfocused_input.tooltip"));
this.virtualMouseOption = new SpruceToggleBooleanOption("midnightcontrols.menu.virtual_mouse", () -> MidnightControlsConfig.virtualMouse,
value -> MidnightControlsConfig.virtualMouse = value, Text.translatable("midnightcontrols.menu.virtual_mouse.tooltip"));
this.hideCursorOption = new SpruceToggleBooleanOption("midnightcontrols.menu.hide_cursor", () -> MidnightControlsConfig.hideNormalMouse,
value -> MidnightControlsConfig.hideNormalMouse = value, Text.translatable("midnightcontrols.menu.hide_cursor.tooltip"));
// Touch options
this.touchModeOption = new SpruceCyclingOption("midnightcontrols.menu.touch_mode",
amount -> MidnightControlsConfig.touchMode = MidnightControlsConfig.touchMode.next(),
option -> option.getDisplayText(MidnightControlsConfig.touchMode.getTranslatedText()),
Text.translatable("midnightcontrols.menu.touch_mode.tooltip"));
this.touchWithControllerOption = new SpruceToggleBooleanOption("midnightcontrols.menu.touch_with_controller", () -> MidnightControlsConfig.touchInControllerMode,
value -> MidnightControlsConfig.touchInControllerMode = value, Text.translatable("midnightcontrols.menu.touch_with_controller.tooltip"));
this.touchSpeedOption = new SpruceDoubleOption("midnightcontrols.menu.touch_speed", 0.0, 150.0, .5f,
() -> MidnightControlsConfig.touchSpeed,
value -> MidnightControlsConfig.touchSpeed = value, option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.touch_speed.tooltip"));
this.touchBreakDelayOption = new SpruceDoubleOption("midnightcontrols.menu.touch_break_delay", 50, 500, 1f,
() -> (double) MidnightControlsConfig.touchBreakDelay,
value -> MidnightControlsConfig.touchBreakDelay = value.intValue(), option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.touch_break_delay.tooltip"));
this.touchTransparencyOption = new SpruceDoubleOption("midnightcontrols.menu.touch_transparency", 0, 100, 1f,
() -> (double) MidnightControlsConfig.touchTransparency,
value -> MidnightControlsConfig.touchTransparency = value.intValue(), option -> option.getDisplayText(Text.literal(String.valueOf(option.get()))),
Text.translatable("midnightcontrols.menu.touch_break_delay.tooltip"));
this.invertTouchOption = new SpruceToggleBooleanOption("midnightcontrols.menu.invert_touch", () -> MidnightControlsConfig.invertTouch,
value -> MidnightControlsConfig.invertTouch = value, Text.translatable("midnightcontrols.menu.invert_touch.tooltip"));
}
@Override
public void removed() {
MidnightControlsConfig.isEditing = false;
MidnightControlsConfig.save();
super.removed();
}
@Override
public void close() {
MidnightControlsConfig.isEditing = false;
MidnightControlsConfig.save();
super.close();
}
private int getTextHeight() {
return (5 + this.textRenderer.fontHeight) * 3 + 5;
}
@Override
protected void init() {
super.init();
this.buildTabs();
this.addDrawableChild(this.resetOption.createWidget(Position.of(this.width / 2 - 155, this.height - 29), 150));
this.addDrawableChild(ButtonWidget.builder(SpruceTexts.GUI_DONE, btn -> this.client.setScreen(this.parent))
.dimensions(this.width / 2 - 155 + 160, this.height - 29, 150, 20).build());
}
public void buildTabs() {
var tabs = new SpruceTabbedWidget(Position.of(0, 24), this.width, this.height - 32 - 24,
null,
Math.max(116, this.width / 8), 0);
tabs.getList().setBackground(new MidnightControlsBackground());
this.addDrawableChild(tabs);
tabs.addSeparatorEntry(Text.translatable("midnightcontrols.menu.separator.general"));
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.general"), null,
this::buildGeneralTab);
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.gameplay"), null,
this::buildGameplayTab);
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.visual"), null,
this::buildVisualTab);
tabs.addSeparatorEntry(Text.translatable("options.controls"));
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.controller_controls"), null,
this::buildControllerControlsTab);
tabs.addSeparatorEntry(Text.translatable("midnightcontrols.menu.separator.controller"));
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.controller"), null,
this::buildControllerTab);
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.touch"), null,
this::buildTouchTab);
tabs.addTabEntry(Text.translatable("midnightcontrols.menu.title.mappings.string"), null,
this::buildMappingsStringEditorTab);
}
public SpruceOptionListWidget buildGeneralTab(int width, int height) {
var list = new SpruceOptionListWidget(Position.origin(), width, height);
list.setBackground(new MidnightControlsBackground(130));
list.addSingleOptionEntry(this.inputModeOption);
list.addSingleOptionEntry(this.autoSwitchModeOption);
list.addSingleOptionEntry(this.rotationSpeedOption);
list.addSingleOptionEntry(this.yAxisRotationSpeedOption);
list.addSingleOptionEntry(this.mouseSpeedOption);
list.addSingleOptionEntry(this.virtualMouseOption);
list.addSingleOptionEntry(this.hideCursorOption);
list.addSingleOptionEntry(this.joystickAsMouseOption);
list.addSingleOptionEntry(this.eyeTrackingAsMouseOption);
list.addSingleOptionEntry(this.advancedConfigOption);
return list;
}
public SpruceOptionListWidget buildGameplayTab(int width, int height) {
var list = new SpruceOptionListWidget(Position.origin(), width, height);
list.setBackground(new MidnightControlsBackground(130));
list.addSingleOptionEntry(this.analogMovementOption);
list.addSingleOptionEntry(this.doubleTapToSprintOption);
list.addSingleOptionEntry(this.controllerToggleSneakOption);
list.addSingleOptionEntry(this.controllerToggleSprintOption);
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.fastBlockPlacingOption);
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.frontBlockPlacingOption);
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.verticalReacharoundOption);
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.flyDriftingOption);
if (MidnightControls.isExtrasLoaded) list.addSingleOptionEntry(this.flyVerticalDriftingOption);
list.addSingleOptionEntry(this.autoJumpOption);
return list;
}
public SpruceOptionListWidget buildVisualTab(int width, int height) {
var list = new SpruceOptionListWidget(Position.origin(), width, height);
list.setBackground(new MidnightControlsBackground(130));
list.addSingleOptionEntry(this.controllerTypeOption);
list.addSingleOptionEntry(this.virtualMouseSkinOption);
list.addSingleOptionEntry(new SpruceSeparatorOption("midnightcontrols.menu.title.hud", true, null));
list.addSingleOptionEntry(this.hudEnableOption);
list.addSingleOptionEntry(this.hudSideOption);
list.addSingleOptionEntry(this.moveChatOption);
return list;
}
public ControllerControlsWidget buildControllerControlsTab(int width, int height) {
return new ControllerControlsWidget(Position.origin(), width, height);
}
public AbstractSpruceWidget buildControllerTab(int width, int height) {
var root = new SpruceContainerWidget(Position.origin(), width, height);
var aboutMappings1 = new SpruceLabelWidget(Position.of(0, 2),
Text.translatable("midnightcontrols.controller.mappings.1", SDL2_GAMEPAD_TOOL),
width, true);
var gamepadToolUrlLabel = new SpruceLabelWidget(Position.of(0, aboutMappings1.getHeight() + 4),
this.controllerMappingsUrlText, width,
label -> Util.getOperatingSystem().open(GAMEPAD_TOOL_URL), true);
gamepadToolUrlLabel.setTooltip(Text.translatable("chat.link.open"));
var aboutMappings3 = new SpruceLabelWidget(Position.of(0,
aboutMappings1.getHeight() + gamepadToolUrlLabel.getHeight() + 6),
Text.translatable("midnightcontrols.controller.mappings.3", Formatting.GREEN.toString(), Formatting.RESET.toString()),
width, true);
int listHeight = height - 8 - aboutMappings1.getHeight() - aboutMappings3.getHeight() - gamepadToolUrlLabel.getHeight();
var labels = new SpruceContainerWidget(Position.of(0,
listHeight),
width, height - listHeight);
labels.addChild(aboutMappings1);
labels.addChild(gamepadToolUrlLabel);
labels.addChild(aboutMappings3);
var list = new SpruceOptionListWidget(Position.origin(), width, listHeight);
list.setBackground(new MidnightControlsBackground(130));
list.addSingleOptionEntry(this.controllerOption);
list.addSingleOptionEntry(this.secondControllerOption);
list.addSingleOptionEntry(this.toggleControllerProfileOption);
list.addSingleOptionEntry(this.unfocusedInputOption);
list.addSingleOptionEntry(this.cameraModeOption);
list.addOptionEntry(this.invertsRightXAxis, this.invertsRightYAxis);
list.addSingleOptionEntry(this.rightDeadZoneOption);
list.addSingleOptionEntry(this.leftDeadZoneOption);
for (var option : this.maxAnalogValueOptions) {
list.addSingleOptionEntry(option);
}
root.addChild(list);
root.addChild(labels);
return root;
}
public SpruceOptionListWidget buildTouchTab(int width, int height) {
var list = new SpruceOptionListWidget(Position.origin(), width, height);
list.setBackground(new MidnightControlsBackground(130));
list.addSingleOptionEntry(this.touchSpeedOption);
list.addSingleOptionEntry(this.touchWithControllerOption);
list.addSingleOptionEntry(this.invertTouchOption);
list.addSingleOptionEntry(new SpruceSeparatorOption("midnightcontrols.menu.title.hud", true, null));
list.addSingleOptionEntry(this.touchModeOption);
list.addSingleOptionEntry(this.touchBreakDelayOption);
list.addSingleOptionEntry(this.touchTransparencyOption);
return list;
}
public SpruceContainerWidget buildMappingsStringEditorTab(int width, int height) {
return new MappingsStringInputWidget(Position.origin(), width, height);
}
@Override
public void renderTitle(DrawContext context, int mouseX, int mouseY, float delta) {
context.drawCenteredTextWithShadow(this.textRenderer, I18n.translate("midnightcontrols.menu.title"), this.width / 2, 8, 16777215);
}
public static class MidnightControlsBackground implements Background {
private static int transparency = 160;
public MidnightControlsBackground() {}
public MidnightControlsBackground(int transparency) {
MidnightControlsBackground.transparency = transparency;
}
@Override
public void render(DrawContext context, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) {
fill(context.getMatrices(), widget.getX(), widget.getY(), widget.getX() + widget.getWidth(), widget.getY() + widget.getHeight(), MidnightColorUtil.hex2Rgb("#000000"));
}
private static void fill(MatrixStack matrixStack, int x2, int y2, int x1, int y1, Color color) {
matrixStack.push();
Matrix4f matrix = matrixStack.peek().getPositionMatrix();
float r = (float)(color.getRed()) / 255.0F;
float g = (float)(color.getGreen()) / 255.0F;
float b = (float)(color.getBlue()) / 255.0F;
float t = (float)(transparency) / 255.0F;
BufferBuilder bufferBuilder = Tessellator.getInstance().begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionColorProgram);
bufferBuilder.vertex(matrix, (float)x1, (float)y2, 0.0F).color(r, g, b, t);
bufferBuilder.vertex(matrix, (float)x2, (float)y2, 0.0F).color(r, g, b, t);
bufferBuilder.vertex(matrix, (float)x2, (float)y1, 0.0F).color(r, g, b, t);
bufferBuilder.vertex(matrix, (float)x1, (float)y1, 0.0F).color(r, g, b, t);
BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
RenderSystem.disableBlend();
matrixStack.pop();
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui;
import eu.midnightdust.midnightcontrols.client.controller.Controller;
import org.thinkingstudio.obsidianui.option.SpruceSimpleActionOption;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
/**
* Represents the option to reload the controller mappings.
*/
public class ReloadControllerMappingsOption {
private static final String KEY = "midnightcontrols.menu.reload_controller_mappings";
public static SpruceSimpleActionOption newOption(@Nullable Consumer<SpruceButtonWidget> before) {
return SpruceSimpleActionOption.of(KEY, btn -> {
var client = MinecraftClient.getInstance();
if (before != null)
before.accept(btn);
Controller.updateMappings();
if (client.currentScreen instanceof MidnightControlsSettingsScreen)
client.currentScreen.init(client, client.getWindow().getScaledWidth(), client.getWindow().getScaledHeight());
client.getToastManager().add(SystemToast.create(client, SystemToast.Type.PERIODIC_NOTIFICATION,
Text.translatable("midnightcontrols.controller.mappings.updated"), Text.empty()));
}, Text.translatable("midnightcontrols.tooltip.reload_controller_mappings"));
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.ring.RingButtonMode;
import eu.midnightdust.midnightcontrols.client.ring.RingPage;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.ring;
/**
* Represents the controls ring screen.
*
* @author LambdAurora
* @version 1.4.3
* @since 1.4.3
*/
public class RingScreen extends Screen {
public RingScreen() {
super(Text.literal("midnightcontrols.menu.title.ring"));
}
@Override
protected void init() {
super.init();
if (ring.getMaxPages() > 1) {
this.addDrawableChild(ButtonWidget.builder(Text.of(""), button -> ring.cyclePage(false)).dimensions(5, 5, 20, 20).build());
this.addDrawableChild(ButtonWidget.builder(Text.of(""), button -> ring.cyclePage(true)).dimensions(width - 25, 5, 20, 20).build());
}
}
@Override
public boolean shouldPause() {
return false;
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
RingPage page = ring.getCurrentPage();
page.render(context, this.textRenderer, this.width, this.height, mouseX, mouseY, delta);
}
@Override
public void close() {
super.close();
assert client != null;
client.currentScreen = null;
RingPage page = ring.getCurrentPage();
if (RingPage.selected >= 0 && page.actions[RingPage.selected] != null)
page.actions[RingPage.selected].activate(RingButtonMode.PRESS);
RingPage.selected = -1;
this.removed();
}
// @Override
// public boolean changeFocus(boolean lookForwards) {
// if (lookForwards) {
// if (RingPage.selected < 7) ++RingPage.selected;
// else RingPage.selected = -1;
// }
// else {
// if (RingPage.selected > -1) --RingPage.selected;
// else RingPage.selected = 7;
// }
// return true;
// }
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
if (ring.getCurrentPage().onClick(width, height, (int) mouseX, (int) mouseY)) {
this.close();
return true;
}
return false;
}
}

View File

@@ -0,0 +1,374 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import eu.midnightdust.lib.util.PlatformFunctions;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.enums.HudSide;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.compat.EmotecraftCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import eu.midnightdust.midnightcontrols.client.touch.gui.ItemUseButtonWidget;
import eu.midnightdust.midnightcontrols.client.touch.gui.SilentTexturedButtonWidget;
import eu.midnightdust.midnightcontrols.client.touch.TouchUtils;
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.*;
import net.minecraft.client.gui.screen.ingame.InventoryScreen;
import net.minecraft.client.gui.widget.TextIconButtonWidget;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.texture.MissingSprite;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.InputUtil;
import net.minecraft.item.*;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.text.Text;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import org.lwjgl.glfw.GLFW;
import java.util.List;
import java.util.Objects;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.input;
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X;
import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y;
/**
* Represents the touchscreen overlay
*/
public class TouchscreenOverlay extends Screen {
public static final Identifier WIDGETS_LOCATION = Identifier.of("midnightcontrols", "textures/gui/widgets.png");
private SilentTexturedButtonWidget inventoryButton;
private SilentTexturedButtonWidget swapHandsButton;
private SilentTexturedButtonWidget dropButton;
private ItemUseButtonWidget useButton;
private SilentTexturedButtonWidget jumpButton;
private SilentTexturedButtonWidget flyButton;
private SilentTexturedButtonWidget flyUpButton;
private SilentTexturedButtonWidget flyDownButton;
private SilentTexturedButtonWidget forwardButton;
private SilentTexturedButtonWidget forwardLeftButton;
private SilentTexturedButtonWidget forwardRightButton;
private SilentTexturedButtonWidget leftButton;
private SilentTexturedButtonWidget rightButton;
private SilentTexturedButtonWidget backButton;
private SilentTexturedButtonWidget startSneakButton;
private SilentTexturedButtonWidget endSneakButton;
private int flyButtonEnableTicks = 0;
private int forwardButtonTick = 0;
public TouchscreenOverlay() {
super(Text.literal("Touchscreen Overlay"));
}
@Override
public boolean shouldPause() {
return false;
}
@Override
public void renderInGameBackground(DrawContext context) {}
@Override
protected void applyBlur(float delta) {}
private void pauseGame() {
assert this.client != null;
this.client.setScreen(new GameMenuScreen(true));
if (this.client.isIntegratedServerRunning() && !Objects.requireNonNull(this.client.getServer()).isRemote()) {
this.client.getSoundManager().pauseAll();
}
}
/**
* Updates the forward button ticks cooldown.
*
* @param state The button state.
*
*/
private void updateForwardButtonsState(boolean state) {
this.forwardButtonTick = state ? -1 : 20;
}
/**
* Updates the jump buttons.
*/
private void updateJumpButtons() {
assert this.client != null;
assert this.client.player != null;
float transparency = MidnightControlsConfig.touchTransparency / 100f;
if (this.client.player.getAbilities().flying) {
boolean oldStateFly = this.flyButton.isVisible();
this.jumpButton.setVisible(false);
this.flyButton.setVisible(true);
this.flyUpButton.setVisible(true);
this.flyDownButton.setVisible(true);
this.flyButton.setAlpha(transparency);
this.flyUpButton.setAlpha(transparency);
this.flyDownButton.setAlpha(transparency);
if (oldStateFly != this.flyButton.isVisible()) {
this.flyButtonEnableTicks = 5;
this.setJump(false);
} else if (this.flyButtonEnableTicks > 0)
this.flyButtonEnableTicks--;
} else {
this.jumpButton.setVisible(true);
this.flyButton.setVisible(false);
this.flyUpButton.setVisible(false);
this.flyDownButton.setVisible(false);
this.jumpButton.setAlpha(transparency);
}
}
/**
* Handles the jump button.
*
* @param btn The pressed button.
*/
private void handleJump(SpruceButtonWidget btn) {
assert this.client != null;
((KeyBindingAccessor) this.client.options.jumpKey).midnightcontrols$handlePressState(btn.isActive());
}
/**
* Handles the jump button.
*
* @param state The state.
*/
private void setJump(boolean state) {
assert this.client != null;
((KeyBindingAccessor) this.client.options.jumpKey).midnightcontrols$handlePressState(state);
}
@Override
protected void init() {
super.init();
assert this.client != null;
assert this.client.player != null;
assert this.client.interactionManager != null;
int scaledWidth = this.client.getWindow().getScaledWidth();
int scaledHeight = this.client.getWindow().getScaledHeight();
int emoteOffset = 0;
if (PlatformFunctions.isModLoaded("emotecraft")) {
emoteOffset = 10;
TextIconButtonWidget emoteButton = TextIconButtonWidget.builder(Text.empty(), btn -> EmotecraftCompat.openEmotecraftScreen(this), true).width(20).texture(Identifier.of(MidnightControlsConstants.NAMESPACE, "touch/emote"), 20, 20).build();
emoteButton.setPosition(scaledWidth / 2 - 30, 0);
this.addDrawableChild(emoteButton);
}
TextIconButtonWidget chatButton = TextIconButtonWidget.builder(Text.empty(), btn -> this.client.setScreen(new ChatScreen("")), true).width(20).texture(Identifier.of(MidnightControlsConstants.NAMESPACE, "touch/chat"), 20, 20).build();
chatButton.setPosition(scaledWidth / 2 - 20 + emoteOffset, 0);
this.addDrawableChild(chatButton);
TextIconButtonWidget pauseButton = TextIconButtonWidget.builder(Text.empty(), btn -> this.pauseGame(), true).width(20).texture(Identifier.of(MidnightControlsConstants.NAMESPACE, "touch/pause"), 20, 20).build();
pauseButton.setPosition(scaledWidth / 2 + emoteOffset, 0);
this.addDrawableChild(pauseButton);
// Inventory buttons.
int inventoryButtonX = scaledWidth / 2;
int inventoryButtonY = scaledHeight - 16 - 5;
if (this.client.options.getMainArm().getValue() == Arm.LEFT) {
inventoryButtonX = inventoryButtonX - 91 - 24;
} else {
inventoryButtonX = inventoryButtonX + 91 + 4;
}
this.addDrawableChild(this.inventoryButton = new SilentTexturedButtonWidget(Position.of(inventoryButtonX, inventoryButtonY), 20, 20, Text.empty(), btn -> {
if (this.client.interactionManager.hasRidingInventory()) {
this.client.player.openRidingInventory();
} else {
this.client.getTutorialManager().onInventoryOpened();
this.client.setScreen(new InventoryScreen(this.client.player));
}
}, 20, 0, 20, WIDGETS_LOCATION, 256, 256));
;
int jumpButtonX, swapHandsX, sneakButtonX;
int sneakButtonY = scaledHeight - 10 - 40 - 5;
if (MidnightControlsConfig.hudSide == HudSide.LEFT) {
jumpButtonX = scaledWidth - 20 - 20;
swapHandsX = jumpButtonX - 5 - 40;
sneakButtonX = 10 + 20 + 5;
} else {
jumpButtonX = 20;
swapHandsX = jumpButtonX + 5 + 40;
sneakButtonX = scaledWidth - 10 - 40 - 5;
}
// Swap items hand.
this.addDrawableChild(this.swapHandsButton = new SilentTexturedButtonWidget(Position.of(swapHandsX, sneakButtonY), 20, 20, Text.empty(),
button -> {
if (button.isActive()) {
if (!this.client.player.isSpectator()) {
Objects.requireNonNull(this.client.getNetworkHandler()).sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.DOWN));
}
}
},0, 160, 20, WIDGETS_LOCATION));
// Drop
this.addDrawableChild(this.dropButton = new SilentTexturedButtonWidget(Position.of(swapHandsX, sneakButtonY + 5 + 20), 20, 20, Text.empty(), btn -> {
if (btn.isActive() && !client.player.isSpectator() && client.player.dropSelectedItem(false)) {
client.player.swingHand(Hand.MAIN_HAND);
}
}, 20, 160, 20, WIDGETS_LOCATION));
// Use
this.addDrawableChild(this.useButton = new ItemUseButtonWidget(Position.of(width/2-25, height - 70), 50, 17, Text.translatable(MidnightControlsConstants.NAMESPACE+".action.eat"), btn ->
client.interactionManager.interactItem(client.player, client.player.getActiveHand())));
// Jump keys
this.addDrawableChild(this.jumpButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY), 20, 20, Text.empty(), this::handleJump, 0, 40, 20, WIDGETS_LOCATION));
this.addDrawableChild(this.flyButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY), 20, 20, Text.empty(),btn -> {
if (this.flyButtonEnableTicks == 0) this.client.player.getAbilities().flying = false;
}, 20, 40, 20, WIDGETS_LOCATION)
);
this.addDrawableChild(this.flyUpButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY - 5 - 20), 20, 20,Text.empty(),
this::handleJump, 40, 40, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.flyDownButton = new SilentTexturedButtonWidget(Position.of(jumpButtonX, sneakButtonY + 20 + 5), 20, 20, Text.empty(),
btn -> ((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(btn.isActive()), 60, 40, 20, WIDGETS_LOCATION
));
this.updateJumpButtons();
// Movements keys
this.addDrawableChild((this.startSneakButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY), 20, 20, Text.empty(), btn -> {
if (btn.isActive()) {
((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(true);
this.startSneakButton.setVisible(false);
this.endSneakButton.setVisible(true);
}
}, 0, 120, 20, WIDGETS_LOCATION))
);
this.addDrawableChild((this.endSneakButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY), 20, 20, Text.empty(), btn -> {
if (btn.isActive()) {
((KeyBindingAccessor) this.client.options.sneakKey).midnightcontrols$handlePressState(false);
this.endSneakButton.setVisible(false);
this.startSneakButton.setVisible(true);
}
}, 20, 120, 20, WIDGETS_LOCATION)));
this.addDrawableChild(this.forwardLeftButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX - 20 - 5, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
((KeyBindingAccessor) this.client.options.leftKey).midnightcontrols$handlePressState(btn.isActive());
this.updateForwardButtonsState(btn.isActive());
}, 80, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.forwardButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
this.updateForwardButtonsState(btn.isActive());
this.forwardLeftButton.setVisible(true);
this.forwardRightButton.setVisible(true);
}, 0, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.forwardRightButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX + 20 + 5, sneakButtonY - 5 - 20), 20, 20, Text.empty(), btn -> {
((KeyBindingAccessor) this.client.options.forwardKey).midnightcontrols$handlePressState(btn.isActive());
((KeyBindingAccessor) this.client.options.rightKey).midnightcontrols$handlePressState(btn.isActive());
this.updateForwardButtonsState(btn.isActive());
}, 100, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.rightButton =new SilentTexturedButtonWidget(Position.of(sneakButtonX + 20 + 5, sneakButtonY), 20, 20, Text.empty(),
btn -> ((KeyBindingAccessor) this.client.options.rightKey).midnightcontrols$handlePressState(btn.isActive()), 20, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.backButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX, sneakButtonY + 20 + 5), 20, 20, Text.empty(),
btn -> ((KeyBindingAccessor) this.client.options.backKey).midnightcontrols$handlePressState(btn.isActive()), 40, 80, 20, WIDGETS_LOCATION
));
this.addDrawableChild(this.leftButton = new SilentTexturedButtonWidget(Position.of(sneakButtonX - 20 - 5, sneakButtonY), 20, 20, Text.empty(),
btn -> ((KeyBindingAccessor) this.client.options.leftKey).midnightcontrols$handlePressState(btn.isActive()), 60, 80, 20, WIDGETS_LOCATION
));
initCustomButtons(true);
initCustomButtons(false);
this.setButtonProperties(MidnightControlsConfig.touchTransparency / 100f);
}
private void initCustomButtons(boolean left) {
assert client != null;
Identifier emptySprite = Identifier.of(MidnightControlsConstants.NAMESPACE, "touch/empty");
List<String> list = left ? MidnightControlsConfig.leftTouchBinds : MidnightControlsConfig.rightTouchBinds;
Sprite missingSprite = client.getGuiAtlasManager().getSprite(MissingSprite.getMissingSpriteId());
for (int i = 0; i < list.size(); i++) {
String bindName = list.get(i);
ButtonBinding binding = InputManager.getBinding(bindName);
if (binding == null) continue;
boolean hasTexture = client.getGuiAtlasManager().getSprite(Identifier.of(MidnightControlsConstants.NAMESPACE, "binding/"+bindName)) != missingSprite;
if (MidnightControlsConfig.debug) System.out.println(left +" "+Identifier.of(MidnightControlsConstants.NAMESPACE, "binding/"+bindName)+" "+ hasTexture);
var button = TextIconButtonWidget.builder(Text.translatable(binding.getTranslationKey()), b -> binding.handle(client, 1, ButtonState.PRESS), hasTexture)
.texture(hasTexture ? Identifier.of(MidnightControlsConstants.NAMESPACE, "binding/"+bindName) : emptySprite, 20, 20).dimension(20, 20).build();
button.setPosition(left ? (3+(i*23)) : this.width-(23+(i*23)), 3);
button.setAlpha(MidnightControlsConfig.touchTransparency / 100f);
this.addDrawableChild(button);
}
}
private void setButtonProperties(float transparency) {
this.inventoryButton.setAlpha(transparency);
this.dropButton.setAlpha(transparency);
this.swapHandsButton.setAlpha(transparency);
this.jumpButton.setAlpha(transparency);
this.flyButton.setAlpha(transparency);
this.flyUpButton.setAlpha(transparency);
this.useButton.setAlpha(Math.min(transparency+0.1f, 1.0f));
this.flyDownButton.setAlpha(transparency);
this.startSneakButton.setAlpha(transparency);
this.endSneakButton.setAlpha(transparency);
this.forwardButton.setAlpha(transparency);
this.forwardLeftButton.setAlpha(Math.max(0.05f, transparency-0.1f));
this.forwardRightButton.setAlpha(Math.max(0.05f, transparency-0.1f));
this.leftButton.setAlpha(transparency);
this.rightButton.setAlpha(transparency);
this.backButton.setAlpha(transparency);
this.useButton.setAlpha(Math.min(transparency+0.1f, 1.0f));
this.endSneakButton.setVisible(false);
this.forwardLeftButton.setVisible(false);
this.forwardRightButton.setVisible(false);
}
@Override
public void tick() {
assert this.client != null;
assert this.client.interactionManager != null;
assert this.client.player != null;
if (this.forwardButtonTick > 0) {
--this.forwardButtonTick;
} else {
this.forwardLeftButton.setVisible(false);
this.forwardRightButton.setVisible(false);
}
this.useButton.setVisible(client.player.getMainHandStack() != null && (client.player.getMainHandStack().getUseAction() != UseAction.NONE || client.player.getMainHandStack().getItem() instanceof ArmorItem) && !TouchUtils.hasInWorldUseAction(client.player.getMainHandStack()));
this.updateJumpButtons();
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
if (button == GLFW.GLFW_MOUSE_BUTTON_1 && this.client != null) {
if (!MidnightControlsConfig.invertTouch) {
deltaX = -deltaX;
deltaY = -deltaY;
}
if (deltaY > 0.01)
input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) Math.abs((deltaY / 3.0)*MidnightControlsConfig.touchSpeed/100), 2);
else input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_Y, (float) Math.abs((deltaY / 3.0)*MidnightControlsConfig.touchSpeed/100), 1);
if (deltaX > 0.01)
input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_X, (float) Math.abs((deltaX / 3.0)*MidnightControlsConfig.touchSpeed/100), 2);
else input.handleLook(this.client, GLFW_GAMEPAD_AXIS_RIGHT_X, (float) Math.abs((deltaX / 3.0)*MidnightControlsConfig.touchSpeed/100), 1);
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
KeyBinding.onKeyPressed(InputUtil.fromKeyCode(keyCode, scanCode));
super.keyPressed(keyCode,scanCode,modifiers);
return true;
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
context.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
context.fill(mouseX-10, mouseY-10, mouseX+10, mouseY+10, 0xFFFFFF);
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui.widget;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.SpruceTexts;
import org.thinkingstudio.obsidianui.widget.AbstractSpruceIconButtonWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
/**
* Represents a controller button widget.
*/
public class ControllerButtonWidget extends AbstractSpruceIconButtonWidget {
private ButtonBinding binding;
private int iconWidth;
public ControllerButtonWidget(Position position, int width, @NotNull ButtonBinding binding, @NotNull PressAction action) {
super(position, width, 20, ButtonBinding.getLocalizedButtonName(binding.getButton()[0]), action);
this.binding = binding;
}
public void update() {
int length = binding.getButton().length;
this.setMessage(this.binding.isNotBound() ? SpruceTexts.NOT_BOUND.copy() :
(length > 0 ? ButtonBinding.getLocalizedButtonName(binding.getButton()[0]) : Text.literal("<>")));
}
@Override
public Text getMessage() {
if (this.binding.getButton().length > 1)
return Text.empty();
return super.getMessage();
}
@Override
protected int renderIcon(DrawContext context, int mouseX, int mouseY, float delta) {
int x = this.getX();
if (this.binding.getButton().length > 1) {
x += (this.width / 2 - this.iconWidth / 2) - 4;
}
var size = MidnightControlsRenderer.drawButton(context, x, this.getY(), this.binding, MinecraftClient.getInstance());
this.iconWidth = size.length();
return size.height();
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui.widget;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.SpruceTexts;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceContainerWidget;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.option.ControlsOptionsScreen;
import net.minecraft.text.Text;
import org.aperlambda.lambdacommon.utils.function.Predicates;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Represents the controls screen.
*/
public class ControllerControlsWidget extends SpruceContainerWidget {
private SpruceButtonWidget resetButton;
public ButtonBinding focusedBinding;
public boolean waiting = false;
public List<Integer> currentButtons = new ArrayList<>();
public ControllerControlsWidget(Position position, int width, int height) {
super(position, width, height);
this.init();
}
protected void init() {
this.addChild(new SpruceButtonWidget(Position.of(this, this.width / 2 - 155, 18), 310, 20,
Text.translatable("midnightcontrols.menu.keyboard_controls"),
btn -> this.client.setScreen(new ControlsOptionsScreen(null, this.client.options))));
ControlsListWidget bindingsListWidget = new ControlsListWidget(Position.of(this, 0, 43), this.width, this.height - 43 - 35, this);
bindingsListWidget.setBackground(new MidnightControlsSettingsScreen.MidnightControlsBackground(130));
this.addChild(bindingsListWidget);
this.addChild(this.resetButton = new SpruceButtonWidget(Position.of(this, this.width / 2 - 155, this.height - 29), 150, 20,
SpruceTexts.CONTROLS_RESET_ALL,
btn -> InputManager.streamBindings().collect(Collectors.toSet()).forEach(binding -> MidnightControlsConfig.setButtonBinding(binding, binding.getDefaultButton()))));
}
@Override
public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
context.drawCenteredTextWithShadow(this.client.textRenderer, Text.translatable("midnightcontrols.menu.title.controller_controls"),
this.getX() + this.width / 2, this.getY() + 4, 16777215);
this.resetButton.setActive(InputManager.streamBindings().anyMatch(Predicates.not(ButtonBinding::isDefault)));
super.renderWidget(context, mouseX, mouseY, delta);
}
public void finishBindingEdit(int... buttons) {
if (this.focusedBinding == null) return;
MidnightControlsConfig.setButtonBinding(this.focusedBinding, buttons);
this.focusedBinding = null;
}
}

View File

@@ -0,0 +1,319 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.gui.widget;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.ButtonCategory;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.SpruceTexts;
import org.thinkingstudio.obsidianui.navigation.NavigationDirection;
import org.thinkingstudio.obsidianui.navigation.NavigationUtils;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import org.thinkingstudio.obsidianui.widget.SpruceSeparatorWidget;
import org.thinkingstudio.obsidianui.widget.SpruceWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceEntryListWidget;
import org.thinkingstudio.obsidianui.widget.container.SpruceParentWidget;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* Represents a control list widget.
*/
public class ControlsListWidget extends SpruceEntryListWidget<ControlsListWidget.Entry> {
private static final int[] UNBOUND = new int[]{-1};
private final ControllerControlsWidget gui;
protected int lastIndex = 0;
private final int maxTextLength;
public ControlsListWidget(Position position, int width, int height, ControllerControlsWidget gui) {
super(position, width, height, 4, ControlsListWidget.Entry.class);
this.gui = gui;
this.maxTextLength = InputManager.streamBindings().mapToInt(binding -> this.client.textRenderer.getWidth(binding.getText())).max().orElse(0);
InputManager.streamCategories()
.sorted(Comparator.comparingInt(ButtonCategory::getPriority))
.forEach(category -> {
this.addEntry(new CategoryEntry(this, category));
category.getBindings().forEach(binding -> {
this.addEntry(new ControlsListWidget.ButtonBindingEntry(this, binding));
});
});
this.setAllowOutsideHorizontalNavigation(true);
}
private int getRowWidth() {
return this.getWidth() - 6 - this.getRowLeft() * 2;
}
public int getRowLeft() {
int baseWidth = 220 + 32;
return this.getWidth() / 2 - baseWidth / 2 + 72 - this.maxTextLength;
}
public class ButtonBindingEntry extends Entry implements SpruceParentWidget<SpruceWidget> {
private final List<SpruceWidget> children = new ArrayList<>();
private @Nullable SpruceWidget focused;
private final ButtonBinding binding;
private final String bindingName;
private final ControllerButtonWidget editButton;
private final SpruceButtonWidget resetButton;
private final SpruceButtonWidget unbindButton;
ButtonBindingEntry(@NotNull ControlsListWidget parent, @NotNull ButtonBinding binding) {
super(parent);
this.binding = binding;
this.bindingName = I18n.translate(this.binding.getTranslationKey());
this.editButton = new ControllerButtonWidget(Position.of(this, parent.getWidth() / 2 - 8, 0), 110, this.binding, btn -> {
gui.focusedBinding = binding;
MidnightControlsClient.input.beginControlsInput(gui);
}) {
protected Text getNarrationMessage() {
return binding.isNotBound() ? Text.translatable("narrator.controls.unbound", bindingName)
: Text.translatable("narrator.controls.bound", bindingName, super.getNarrationMessage());
}
};
this.children.add(editButton);
this.resetButton = new SpruceButtonWidget(Position.of(this,
this.editButton.getPosition().getRelativeX() + this.editButton.getWidth() + 2, 0),
44, 20, Text.translatable("controls.reset"),
btn -> MidnightControlsConfig.setButtonBinding(binding, binding.getDefaultButton())) {
protected Text getNarrationMessage() {
return Text.translatable("narrator.controls.reset", bindingName);
}
};
this.children.add(this.resetButton);
this.unbindButton = new SpruceButtonWidget(Position.of(this,
this.editButton.getPosition().getRelativeX() + this.editButton.getWidth() + 2, 0),
this.resetButton.getWidth(), this.resetButton.getHeight(), SpruceTexts.GUI_UNBIND,
btn -> {
MidnightControlsConfig.setButtonBinding(binding, UNBOUND);
gui.focusedBinding = null;
MidnightControlsClient.input.beginControlsInput(null);
}) {
protected Text getNarrationMessage() {
return Text.translatable("midnightcontrols.narrator.unbound", bindingName);
}
};
this.children.add(this.unbindButton);
this.position.setRelativeX(4);
this.width -= 10;
}
@Override
public List<SpruceWidget> children() {
return this.children;
}
@Override
public @Nullable SpruceWidget getFocused() {
return this.focused;
}
@Override
public void setFocused(@Nullable SpruceWidget focused) {
if (this.focused == focused)
return;
if (this.focused != null)
this.focused.setFocused(false);
this.focused = focused;
}
@Override
public int getHeight() {
return this.children.stream().mapToInt(SpruceWidget::getHeight).reduce(Integer::max).orElse(0) + 4;
}
/* Input */
@Override
protected boolean onMouseClick(double mouseX, double mouseY, int button) {
var it = this.children().iterator();
SpruceWidget element;
do {
if (!it.hasNext()) {
return false;
}
element = it.next();
} while (!element.mouseClicked(mouseX, mouseY, button));
this.setFocused(element);
if (button == GLFW.GLFW_MOUSE_BUTTON_1)
this.dragging = true;
return true;
}
@Override
protected boolean onMouseRelease(double mouseX, double mouseY, int button) {
this.dragging = false;
return this.hoveredElement(mouseX, mouseY).filter(element -> element.mouseReleased(mouseX, mouseY, button)).isPresent();
}
@Override
protected boolean onMouseDrag(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
return this.getFocused() != null && this.dragging && button == GLFW.GLFW_MOUSE_BUTTON_1
&& this.getFocused().mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
@Override
protected boolean onKeyPress(int keyCode, int scanCode, int modifiers) {
return this.focused != null && this.focused.keyPressed(keyCode, scanCode, modifiers);
}
/* Navigation */
@Override
public void setFocused(boolean focused) {
super.setFocused(focused);
if (!focused) {
this.setFocused(null);
}
}
@Override
public boolean onNavigation(@NotNull NavigationDirection direction, boolean tab) {
if (this.requiresCursor()) return false;
if (!tab && direction.isVertical()) {
if (this.isFocused()) {
this.setFocused(null);
return false;
}
int lastIndex = this.parent.lastIndex;
if (lastIndex >= this.children.size())
lastIndex = this.children.size() - 1;
if (!this.children.get(lastIndex).onNavigation(direction, tab))
return false;
this.setFocused(this.children.get(lastIndex));
return true;
}
boolean result = NavigationUtils.tryNavigate(direction, tab, this.children, this.focused, this::setFocused, true);
if (result) {
this.setFocused(true);
if (direction.isHorizontal() && this.getFocused() != null) {
this.parent.lastIndex = this.children.indexOf(this.getFocused());
}
}
return result;
}
/* Rendering */
@Override
protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
boolean focused = gui.focusedBinding == this.binding;
var textRenderer = ControlsListWidget.this.client.textRenderer;
int height = this.getHeight();
//float textX = (float) (this.getX() + 70 - ControlsListWidget.this.maxTextLength);
int textY = this.getY() + height / 2;
context.drawText(textRenderer, this.bindingName, this.getX(), (textY - 9 / 2), 16777215, true);
this.resetButton.setVisible(!focused);
this.unbindButton.setVisible(focused);
this.resetButton.setActive(!this.binding.isDefault());
this.editButton.update();
if (focused) {
var text = Text.literal("> ").formatted(Formatting.WHITE);
text.append(this.editButton.getMessage().copy().formatted(Formatting.YELLOW));
this.editButton.setMessage(text.append(Text.literal(" <").formatted(Formatting.WHITE)));
} else if (!this.binding.isNotBound() && InputManager.hasDuplicatedBindings(this.binding)) {
var text = this.editButton.getMessage().copy();
this.editButton.setMessage(text.formatted(Formatting.RED));
} else if (this.binding.isNotBound()) {
var text = this.editButton.getMessage().copy();
this.editButton.setMessage(text.formatted(Formatting.GOLD));
}
this.children.forEach(widget -> widget.render(context, mouseX, mouseY, delta));
}
}
public static class CategoryEntry extends Entry {
private final SpruceSeparatorWidget separatorWidget;
protected CategoryEntry(ControlsListWidget parent, ButtonCategory category) {
super(parent);
this.separatorWidget = new SpruceSeparatorWidget(Position.of(this, 2, 0), this.getWidth() - 4,
Text.literal(category.getTranslatedName())) {
@Override
public int getWidth() {
return CategoryEntry.this.getWidth() - 4;
}
};
}
public SpruceSeparatorWidget getSeparatorWidget() {
return this.separatorWidget;
}
@Override
public int getHeight() {
return this.separatorWidget.getHeight() + 4;
}
/* Navigation */
@Override
public boolean onNavigation(@NotNull NavigationDirection direction, boolean tab) {
return this.separatorWidget.onNavigation(direction, tab);
}
/* Rendering */
@Override
protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
this.separatorWidget.render(context, mouseX, mouseY, delta);
}
@Override
public String toString() {
return "SpruceTabbedWidget$SeparatorEntry{" +
"position=" + this.getPosition() +
", width=" + this.getWidth() +
", height=" + this.getHeight() +
'}';
}
}
@Environment(EnvType.CLIENT)
public abstract static class Entry extends SpruceEntryListWidget.Entry {
protected final ControlsListWidget parent;
protected Entry(ControlsListWidget parent) {
this.parent = parent;
}
@Override
public int getWidth() {
return this.parent.getInnerWidth();
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.advancement.Advancement;
import net.minecraft.client.gui.screen.advancement.AdvancementTab;
import net.minecraft.client.gui.screen.advancement.AdvancementsScreen;
import net.minecraft.client.network.ClientAdvancementManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
/**
* Represents an accessor of {@link AdvancementsScreen}.
*/
@Mixin(AdvancementsScreen.class)
public interface AdvancementsScreenAccessor {
@Accessor("advancementHandler")
ClientAdvancementManager getAdvancementManager();
@Accessor("tabs")
Map<Advancement, AdvancementTab> getTabs();
@Accessor("selectedTab")
AdvancementTab getSelectedTab();
}

View File

@@ -0,0 +1,35 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ChatScreen.class)
public abstract class ChatScreenMixin extends Screen {
@Shadow protected TextFieldWidget chatField;
protected ChatScreenMixin(Text title) {
super(title);
}
@Inject(at = @At("TAIL"), method = "init")
private void midnightcontrols$moveInputField(CallbackInfo ci) {
if (MidnightControlsConfig.moveChat) chatField.setY(4);
}
@Inject(method = "render", at = @At("HEAD"))
private void midnightcontrols$moveInputFieldBackground(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, -this.height + 16, 0f);
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/TextFieldWidget;render(Lnet/minecraft/client/gui/DrawContext;IIF)V", shift = At.Shift.BEFORE))
private void midnightcontrols$dontMoveOtherStuff(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (MidnightControlsConfig.moveChat) context.getMatrices().translate(0f, this.height - 16, 0f);
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.gui.widget.ClickableWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ClickableWidget.class)
public interface ClickableWidgetAccessor {
@Accessor("height")
int getHeight();
@Accessor("focused")
void setFocused(boolean value);
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import com.mojang.authlib.GameProfile;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.MovementHandler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.input.Input;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.MovementType;
import net.minecraft.network.encryption.PlayerPublicKey;
import net.minecraft.util.math.Vec3d;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Injects the anti fly drifting feature.
*/
@Mixin(ClientPlayerEntity.class)
public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity {
@Unique private boolean midnightcontrols$driftingPrevented = false;
public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile) {
super(world, profile);
}
@Shadow
protected abstract boolean hasMovementInput();
@Shadow
@Final
protected MinecraftClient client;
@Shadow
public Input input;
@Shadow
protected abstract boolean isCamera();
@Shadow protected int ticksLeftToDoubleTapSprint;
@Inject(method = "move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V"))
public void onMove(MovementType type, Vec3d movement, CallbackInfo ci) {
if (!MidnightControlsConfig.doubleTapToSprint) ticksLeftToDoubleTapSprint = 0;
if (!MidnightControls.isExtrasLoaded) return;
if (type == MovementType.SELF) {
if (this.getAbilities().flying && (!MidnightControlsConfig.flyDrifting || !MidnightControlsConfig.verticalFlyDrifting)) {
if (!this.hasMovementInput()) {
if (!this.midnightcontrols$driftingPrevented) {
if (!MidnightControlsConfig.flyDrifting)
this.setVelocity(this.getVelocity().multiply(0, 1.0, 0));
}
this.midnightcontrols$driftingPrevented = true;
} else
this.midnightcontrols$driftingPrevented = false;
}
}
}
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick(ZF)V", shift = At.Shift.AFTER))
public void onInputUpdate(CallbackInfo ci) {
MovementHandler.HANDLER.applyMovement((ClientPlayerEntity) (Object) this);
}
@Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isCamera()Z"))
public void onTickMovement(CallbackInfo ci) {
if (this.getAbilities().flying && this.isCamera()) {
if (MidnightControlsConfig.verticalFlyDrifting || !MidnightControls.isExtrasLoaded)
return;
int moving = 0;
if (this.input.sneaking) {
--moving;
}
if (this.input.jumping) {
++moving;
}
if (moving == 0) {
this.setVelocity(this.getVelocity().multiply(1.0, 0.0, 1.0));
}
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsSettingsScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.option.ControlsOptionsScreen;
import net.minecraft.client.gui.screen.option.GameOptionsScreen;
import net.minecraft.client.gui.widget.TextIconButtonWidget;
import net.minecraft.client.option.GameOptions;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
/**
* Injects the new controls settings button.
*/
@Mixin(ControlsOptionsScreen.class)
public abstract class ControlsOptionsScreenMixin extends GameOptionsScreen {
public ControlsOptionsScreenMixin(Screen parent, GameOptions gameOptions, Text title) {
super(parent, gameOptions, title);
}
@Unique TextIconButtonWidget midnightcontrols$button = TextIconButtonWidget.builder(Text.translatable("midnightcontrols.menu.title.controller"), (button -> this.client.setScreen(new MidnightControlsSettingsScreen(this, false))), true)
.dimension(20,20).texture(Identifier.of("midnightcontrols", "icon/controller"), 20, 20).build();
@Override
public void init() {
super.init();
this.midnightcontrols$setupButton();
this.addDrawableChild(midnightcontrols$button);
}
@Override
public void resize(MinecraftClient client, int width, int height) {
super.resize(client, width, height);
this.midnightcontrols$setupButton();
}
@Unique
public void midnightcontrols$setupButton() {
assert body != null;
midnightcontrols$button.setPosition(body.getWidth() / 2 + 158, body.getY() + 4);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemGroups;
import net.minecraft.screen.slot.Slot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
/**
* Represents an accessor to CreativeInventoryScreen.
*/
@Mixin(CreativeInventoryScreen.class)
public interface CreativeInventoryScreenAccessor {
/**
* Gets the selected tab.
*
* @return the selected tab index
*/
@Accessor("selectedTab")
static ItemGroup getSelectedTab() {
return ItemGroups.getDefaultTab();
}
/**
* Sets the selected tab.
*
* @param group the tab's item group
*/
@Invoker("setSelectedTab")
void midnightcontrols$setSelectedTab(@NotNull ItemGroup group);
/**
* Returns whether the slot belongs to the creative inventory or not.
*
* @param slot the slot to check
* @return true if the slot is from the creative inventory, else false
*/
@Invoker("isCreativeInventorySlot")
boolean midnightcontrols$isCreativeInventorySlot(@Nullable Slot slot);
/**
* Returns whether the current tab has a scrollbar or not.
*
* @return true if the current tab has a scrollbar, else false
*/
@Invoker("hasScrollbar")
boolean midnightcontrols$hasScrollbar();
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.systems.RenderSystem;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
import eu.midnightdust.midnightcontrols.client.touch.TouchUtils;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.RenderTickCounter;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
public abstract class GameRendererMixin {
@Shadow
@Final
MinecraftClient client;
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Mouse;getX()D", shift = At.Shift.BEFORE))
private void onRender(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci) {
if (this.client.currentScreen != null && MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER)
MidnightControlsClient.input.onPreRenderScreen(this.client, this.client.currentScreen);
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;draw()V", shift = At.Shift.BEFORE))
private void renderVirtualCursor(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci, @Local DrawContext drawContext) {
MidnightControlsRenderer.renderVirtualCursor(drawContext, client);
drawContext.draw();
}
@Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/GameRenderer;renderHand:Z"), method = "renderWorld")
private void captureProjAndModMatrix(RenderTickCounter tickCounter, CallbackInfo ci, @Local(ordinal = 1) Matrix4f matrices) {
TouchUtils.lastProjMat.set(RenderSystem.getProjectionMatrix());
TouchUtils.lastModMat.set(RenderSystem.getModelViewMatrix());
TouchUtils.lastWorldSpaceMatrix.set(matrices);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.lib.util.PlatformFunctions;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.compat.EMICompat;
import eu.midnightdust.midnightcontrols.client.compat.MidnightControlsCompat;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.gui.MidnightControlsRenderer;
import eu.midnightdust.midnightcontrols.client.util.HandledScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Represents the mixin for the class ContainerScreen.
*/
@Mixin(HandledScreen.class)
public abstract class HandledScreenMixin implements HandledScreenAccessor {
@Accessor("x")
public abstract int getX();
@Accessor("y")
public abstract int getY();
@Invoker("getSlotAt")
public abstract Slot midnightcontrols$getSlotAt(double posX, double posY);
@Invoker("isClickOutsideBounds")
public abstract boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int x, int y, int button);
@Invoker("onMouseClick")
public abstract void midnightcontrols$onMouseClick(@Nullable Slot slot, int slotId, int clickData, SlotActionType actionType);
@Inject(method = "render", at = @At("RETURN"))
public void onRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.hudEnable) {
var client = MinecraftClient.getInstance();
int x = 2, y = client.getWindow().getScaledHeight() - 2 - MidnightControlsRenderer.ICON_SIZE;
if (MidnightControlsCompat.isEMIPresent() && EMICompat.isEMIEnabled()) {
x += 42;
}
if (!ButtonBinding.TAKE_ALL.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.TAKE_ALL,true, client) + 2;
if (!ButtonBinding.EXIT.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.EXIT, true, client) + 2;
if (PlatformFunctions.isModLoaded("roughlyenoughitems")) {
x = 2;
y -= 24;
}
if (MidnightControlsCompat.isEMIPresent() && EMICompat.isEMIEnabled() && EMICompat.isSearchBarCentered()) {
x = client.getWindow().getScaledWidth() - 55 - client.textRenderer.getWidth(Text.translatable("midnightcontrols.action.pickup"))
- client.textRenderer.getWidth(Text.translatable("midnightcontrols.action.quick_move"))
- MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.TAKE) - MidnightControlsRenderer.getBindingIconWidth(ButtonBinding.QUICK_MOVE);
}
if (!ButtonBinding.TAKE.isNotBound()) x = MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.TAKE, true, client);
if (!ButtonBinding.QUICK_MOVE.isNotBound()) MidnightControlsRenderer.drawButtonTip(context, x, y, ButtonBinding.QUICK_MOVE, true, client);
}
}
}

View File

@@ -0,0 +1,24 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.util.InputUtil;
import org.spongepowered.asm.mixin.Mixin;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(InputUtil.class)
public abstract class InputUtilMixin {
/**
* @author kabliz
* @reason This method is static, and there is a terrible UX issue if raw input is turned on at the same time as
* eye tracking. Raw input only tracks literal mice and not other devices, leading to the game appearing to be
* unresponsive and the player not understanding why. This overwrite preserves the user's mouse preferences,
* while not interfering with eye tracking, and the two modes can be switched between during a play session.
*/
@Inject(method = "isRawMouseMotionSupported", at = @At("HEAD"), cancellable = true)
private static void setRawMouseMotionSupported(CallbackInfoReturnable<Boolean> cir) {
if (MidnightControlsConfig.eyeTrackerAsMouse) cir.setReturnValue(false);
}
}

View File

@@ -0,0 +1,14 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.option.KeyBinding;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
@Mixin(KeyBinding.class)
public interface KeyBindingIDAccessor {
@Accessor @Final
static Map<String, KeyBinding> getKEYS_BY_ID() {return null;};
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.option.KeyBinding;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(KeyBinding.class)
public abstract class KeyBindingMixin implements KeyBindingAccessor {
@Shadow
private int timesPressed;
@Shadow
private boolean pressed;
@Override
public boolean midnightcontrols$press() {
boolean oldPressed = this.pressed;
if (!this.pressed)
this.pressed = true;
++this.timesPressed;
return !oldPressed;
}
@Override
public boolean midnightcontrols$unpress() {
if (this.pressed) {
this.pressed = false;
return true;
}
return false;
}
}

View File

@@ -0,0 +1,18 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.client.gui.TouchscreenOverlay;
import net.minecraft.client.Keyboard;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(Keyboard.class)
public class KeyboardMixin {
@Redirect(method = "onKey", at = @At(value = "FIELD", target = "Lnet/minecraft/client/MinecraftClient;currentScreen:Lnet/minecraft/client/gui/screen/Screen;"))
private Screen midnightcontrols$ignoreTouchOverlay(MinecraftClient instance) {
if (instance.currentScreen instanceof TouchscreenOverlay) return null;
return instance.currentScreen;
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.reacharound;
@Mixin(MinecraftClient.class)
public abstract class MinecraftClientMixin {
@Shadow @Nullable public HitResult crosshairTarget;
@Shadow @Nullable public ClientPlayerEntity player;
@Shadow @Nullable public ClientPlayerInteractionManager interactionManager;
@Shadow @Final public GameRenderer gameRenderer;
@Shadow private int itemUseCooldown;
@Unique private BlockPos midnightcontrols$lastTargetPos;
@Unique private Vec3d midnightcontrols$lastPos;
@Unique private Direction midnightcontrols$lastTargetSide;
@Inject(method = "<init>", at = @At("RETURN"))
private void onInit(CallbackInfo ci) {
MidnightControlsClient.onMcInit((MinecraftClient) (Object) this);
}
@Inject(method = "tick", at = @At("HEAD"))
private void onStartTick(CallbackInfo ci) {
if (this.player == null)
return;
if (!MidnightControlsFeature.FAST_BLOCK_PLACING.isAvailable())
return;
if (this.midnightcontrols$lastPos == null)
this.midnightcontrols$lastPos = this.player.getPos();
int cooldown = this.itemUseCooldown;
BlockHitResult hitResult;
if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.BLOCK && this.player.getAbilities().flying) {
hitResult = (BlockHitResult) this.crosshairTarget;
var targetPos = hitResult.getBlockPos();
var side = hitResult.getSide();
boolean sidewaysBlockPlacing = this.midnightcontrols$lastTargetPos == null || !targetPos.equals(this.midnightcontrols$lastTargetPos.offset(this.midnightcontrols$lastTargetSide));
boolean backwardsBlockPlacing = this.player.input.movementForward < 0.0f && (this.midnightcontrols$lastTargetPos == null || targetPos.equals(this.midnightcontrols$lastTargetPos.offset(this.midnightcontrols$lastTargetSide)));
if (cooldown > 1
&& !targetPos.equals(this.midnightcontrols$lastTargetPos)
&& (sidewaysBlockPlacing || backwardsBlockPlacing)) {
this.itemUseCooldown = 1;
}
this.midnightcontrols$lastTargetPos = targetPos.toImmutable();
this.midnightcontrols$lastTargetSide = side;
}
// Removed front placing sprinting as way too cheaty.
// else if (this.player.isSprinting()) {
// hitResult = MidnightControlsClient.get().reacharound.getLastReacharoundResult();
// if (hitResult != null) {
// if (cooldown > 0)
// this.itemUseCooldown = 0;
// }
// }
this.midnightcontrols$lastPos = this.player.getPos();
}
@Inject(at = @At("TAIL"), method = "setScreen")
private void setScreen(Screen screen, CallbackInfo info) {
if (MidnightControlsConfig.hideNormalMouse){
if (screen != null) GLFW.glfwSetInputMode(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_HIDDEN);
else GLFW.glfwSetInputMode(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
}
}
@Inject(method = "doItemUse()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/hit/HitResult;getType()Lnet/minecraft/util/hit/HitResult$Type;"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true)
private void onItemUse(CallbackInfo ci, Hand[] hands, int handCount, int handIndex, Hand hand, ItemStack stackInHand) {
if (player != null && !stackInHand.isEmpty() && this.player.getPitch(0.f) > 35.0F && reacharound.isReacharoundAvailable()) {
if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.MISS && this.player.isOnGround()) {
if (!stackInHand.isEmpty() && stackInHand.getItem() instanceof BlockItem) {
var hitResult = reacharound.getLastReacharoundResult();
if (hitResult == null || this.interactionManager == null)
return;
hitResult = reacharound.withSideForReacharound(hitResult, stackInHand);
int previousStackCount = stackInHand.getCount();
var result = this.interactionManager.interactBlock(this.player, hand, hitResult);
if (result.isAccepted()) {
if (result.shouldSwingHand()) {
this.player.swingHand(hand);
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || this.interactionManager.hasCreativeInventory())) {
this.gameRenderer.firstPersonRenderer.resetEquipProgress(hand);
}
}
ci.cancel();
}
if (result == ActionResult.FAIL) {
ci.cancel();
}
}
}
}
}
// This is always supposed to be located at before the line 'this.profiler.swap("Keybindings");'
// @Redirect(method = "tick", at = @At(value = "FIELD",target = "Lnet/minecraft/client/MinecraftClient;currentScreen:Lnet/minecraft/client/gui/screen/Screen;", ordinal = 6))
// private Screen midnightcontrols$ignoreTouchOverlay(MinecraftClient instance) {
// if (instance.currentScreen instanceof TouchscreenOverlay) return null;
// return instance.currentScreen;
// }
}

View File

@@ -0,0 +1,15 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.Mouse;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(Mouse.class)
public interface MouseAccessor {
@Invoker("onCursorPos")
void midnightcontrols$onCursorPos(long window, double x, double y);
@Accessor
void setLeftButtonClicked(boolean value);
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.gui.TouchscreenOverlay;
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
import eu.midnightdust.midnightcontrols.client.touch.TouchUtils;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.Mouse;
import net.minecraft.client.util.GlfwUtil;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ThrowablePotionItem;
import net.minecraft.util.UseAction;
import net.minecraft.util.math.Smoother;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import eu.midnightdust.midnightcontrols.client.mouse.EyeTrackerHandler;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsConfig.doMixedInput;
import static org.lwjgl.glfw.GLFW.*;
/**
* Adds extra access to the mouse.
*/
@Mixin(Mouse.class)
public abstract class MouseMixin implements MouseAccessor {
@Shadow @Final private MinecraftClient client;
@Shadow private double y;
@Shadow private double cursorDeltaX;
@Shadow private double cursorDeltaY;
@Shadow private double x;
@Shadow private boolean cursorLocked;
@Shadow private boolean hasResolutionChanged;
@Shadow private double glfwTime;
@Shadow @Final private Smoother cursorXSmoother;
@Shadow @Final private Smoother cursorYSmoother;
@Shadow private boolean leftButtonClicked;
@Inject(method = "onMouseButton", at = @At(value = "HEAD"), cancellable = true)
private void midnightcontrols$onMouseButton(long window, int button, int action, int mods, CallbackInfo ci) {
if (window != this.client.getWindow().getHandle()) return;
if (action == 1 && button == GLFW.GLFW_MOUSE_BUTTON_4 && client.currentScreen != null) {
MidnightControlsClient.input.tryGoBack(client.currentScreen);
}
else if ((client.currentScreen == null && doMixedInput() || client.currentScreen instanceof TouchscreenOverlay) && client.player != null && button == GLFW_MOUSE_BUTTON_1) {
double mouseX = x / client.getWindow().getScaleFactor();
double mouseY = y / client.getWindow().getScaleFactor();
int centerX = client.getWindow().getScaledWidth() / 2;
if (action == 1 && mouseY >= (double) (client.getWindow().getScaledHeight() - 22) && mouseX >= (double) (centerX - 90) && mouseX <= (double) (centerX + 90)) {
for (int slot = 0; slot < 9; ++slot) {
int slotX = centerX - 90 + slot * 20 + 2;
if (mouseX >= (double) slotX && mouseX <= (double) (slotX + 20)) {
client.player.getInventory().selectedSlot = slot;
ci.cancel();
return;
}
}
}
if (action == 1) {
TouchInput.clickStartTime = System.currentTimeMillis();
boolean bl = false;
if (client.currentScreen instanceof TouchscreenOverlay overlay) bl = overlay.mouseClicked(mouseX, mouseY, button);
if (!bl) TouchInput.firstHitResult = TouchUtils.getTargetedObject(mouseX, mouseY);
if (client.currentScreen == null) ci.cancel();
}
else if (TouchInput.mouseReleased(mouseX, mouseY, button)) ci.cancel();
}
}
@Inject(method = "isCursorLocked", at = @At("HEAD"), cancellable = true)
private void midnightcontrols$isCursorLocked(CallbackInfoReturnable<Boolean> ci) {
if (this.client.currentScreen == null) {
if (MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.virtualMouse) {
ci.setReturnValue(true);
ci.cancel();
}
}
}
@Inject(method = "lockCursor", at = @At("HEAD"), cancellable = true)
private void midnightcontrols$onCursorLocked(CallbackInfo ci) {
if ((MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.virtualMouse) ||
MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN || doMixedInput())
ci.cancel();
}
@Inject(method = "updateMouse", at = @At("HEAD"), cancellable = true)
private void midnightcontrols$updateMouse(CallbackInfo ci) {
if (MidnightControlsConfig.eyeTrackerAsMouse && cursorLocked && client.isWindowFocused()) {
// Eye Tracking is only for the camera controlling cursor, we need the normal cursor everywhere else.
if (!client.options.smoothCameraEnabled) {
cursorXSmoother.clear();
cursorYSmoother.clear();
}
EyeTrackerHandler.updateMouseWithEyeTracking(x + cursorDeltaX, y + cursorDeltaY, client,
glfwTime, leftButtonClicked, midnightcontrols$isUsingLongRangedTool(), cursorXSmoother, cursorYSmoother);
glfwTime = GlfwUtil.getTime();
cursorDeltaX = 0.0;
cursorDeltaY = 0.0;
ci.cancel();
}
if (doMixedInput() && client.isWindowFocused()) {
ci.cancel();
}
}
@Unique
private boolean midnightcontrols$isUsingLongRangedTool() {
if (client.player == null) return false;
ItemStack stack = client.player.getActiveItem();
return (leftButtonClicked && (stack.getUseAction() == UseAction.BOW || stack.getUseAction() == UseAction.CROSSBOW ||
stack.getUseAction() == UseAction.SPEAR || stack.getItem() instanceof ThrowablePotionItem));
}
@Inject(method = "lockCursor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/InputUtil;setCursorParameters(JIDD)V",shift = At.Shift.BEFORE), cancellable = true)
private void midnightcontrols$lockCursor(CallbackInfo ci) {
if ((doMixedInput() || MidnightControlsConfig.eyeTrackerAsMouse)) {
//In eye tracking mode, we cannot have the cursor locked to the center.
GLFW.glfwSetInputMode(client.getWindow().getHandle(), GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
client.setScreen(null);
hasResolutionChanged = true;
ci.cancel();
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.List;
@Mixin(RecipeBookWidget.class)
public interface RecipeBookWidgetAccessor {
@Accessor("tabButtons")
List<RecipeGroupButtonWidget> getTabButtons();
@Accessor("currentTab")
RecipeGroupButtonWidget getCurrentTab();
@Accessor("currentTab")
void setCurrentTab(RecipeGroupButtonWidget currentTab);
@Invoker("refreshResults")
void midnightcontrols$refreshResults(boolean resetCurrentPage);
}

View File

@@ -0,0 +1,38 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import org.thinkingstudio.obsidianui.Position;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.InputHandlers;
import eu.midnightdust.midnightcontrols.client.touch.gui.SilentTexturedButtonWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.Selectable;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static eu.midnightdust.midnightcontrols.client.gui.TouchscreenOverlay.WIDGETS_LOCATION;
@Mixin(Screen.class)
public abstract class ScreenMixin {
@Shadow protected abstract <T extends Element & Drawable & Selectable> T addDrawableChild(T drawableElement);
@Shadow public int width;
@Inject(method = "init(Lnet/minecraft/client/MinecraftClient;II)V", at = @At("TAIL"))
public void midnightcontrols$addCloseButton(MinecraftClient client, int width, int height, CallbackInfo ci) {
if (MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN && (MidnightControlsConfig.closeButtonScreens.stream().anyMatch(s -> this.getClass().getName().startsWith(s) || ((Object)this) instanceof HandledScreen<?>))) {
this.addDrawableChild(new SilentTexturedButtonWidget(Position.of(this.width - 30, 10), 20, 20, Text.empty(), btn ->
InputHandlers.handleExit().press(client, ButtonBinding.BACK, 0f, ButtonState.PRESS), 20, 160, 20, WIDGETS_LOCATION));
}
}
}

View File

@@ -0,0 +1,16 @@
package eu.midnightdust.midnightcontrols.client.mixin;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.gui.tab.Tab;
import net.minecraft.client.gui.tab.TabManager;
import net.minecraft.client.gui.widget.TabNavigationWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(TabNavigationWidget.class)
public interface TabNavigationWidgetAccessor {
@Accessor
TabManager getTabManager();
@Accessor
ImmutableList<Tab> getTabs();
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import eu.midnightdust.lib.util.MidnightColorUtil;
import eu.midnightdust.midnightcontrols.ControlsMode;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.touch.TouchInput;
import eu.midnightdust.midnightcontrols.client.enums.TouchMode;
import eu.midnightdust.midnightcontrols.client.util.RainbowColor;
import net.minecraft.block.ShapeContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.shape.VoxelShape;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.awt.*;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.reacharound;
/**
* Represents a mixin to WorldRenderer.
* <p>
* Handles the rendering of the block outline of the reach-around features.
*/
@Mixin(WorldRenderer.class)
public abstract class WorldRendererMixin {
@Shadow
@Final
private MinecraftClient client;
@Shadow
private ClientWorld world;
@Shadow
@Final
private BufferBuilderStorage bufferBuilders;
@Shadow
private static void drawCuboidShapeOutline(MatrixStack matrices, VertexConsumer vertexConsumer, VoxelShape shape, double offsetX, double offsetY, double offsetZ, float red, float green, float blue, float alpha) {
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/hit/HitResult;getType()Lnet/minecraft/util/hit/HitResult$Type;"))
private HitResult.Type dontRenderOutline(HitResult instance) {
if (MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN && MidnightControlsConfig.touchMode == TouchMode.FINGER_POS) {
return HitResult.Type.MISS;
}
return instance.getType();
}
@Inject(
method = "render",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/client/MinecraftClient;crosshairTarget:Lnet/minecraft/util/hit/HitResult;",
ordinal = 1,
shift = At.Shift.AFTER
)
)
private void onOutlineRender(RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, Matrix4f matrix4f2, CallbackInfo ci, @Local MatrixStack matrices) {
if (((MidnightControlsConfig.controlsMode == ControlsMode.CONTROLLER && MidnightControlsConfig.touchInControllerMode) || MidnightControlsConfig.controlsMode == ControlsMode.TOUCHSCREEN)
&& MidnightControlsConfig.touchMode == TouchMode.FINGER_POS) {
this.midnightcontrols$renderFingerOutline(matrices, camera);
}
this.midnightcontrols$renderReacharoundOutline(matrices, camera);
}
@Unique
private void midnightcontrols$renderFingerOutline(MatrixStack matrices, Camera camera) {
if (TouchInput.firstHitResult == null || TouchInput.firstHitResult.getType() != HitResult.Type.BLOCK)
return;
BlockHitResult result = (BlockHitResult) TouchInput.firstHitResult;
var blockPos = result.getBlockPos();
if (this.world.getWorldBorder().contains(blockPos) && this.client.player != null) {
var outlineShape = this.world.getBlockState(blockPos).getOutlineShape(this.client.world, blockPos, ShapeContext.of(camera.getFocusedEntity()));
Color rgb = MidnightColorUtil.hex2Rgb(MidnightControlsConfig.touchOutlineColorHex);
if (MidnightControlsConfig.touchOutlineColorHex.isEmpty()) rgb = RainbowColor.radialRainbow(1,1);
var pos = camera.getPos();
matrices.push();
var vertexConsumer = this.bufferBuilders.getEntityVertexConsumers().getBuffer(RenderLayer.getLines());
drawCuboidShapeOutline(matrices, vertexConsumer, outlineShape, blockPos.getX() - pos.getX(), blockPos.getY() - pos.getY(), blockPos.getZ() - pos.getZ(),
rgb.getRed() / 255.f, rgb.getGreen() / 255.f, rgb.getBlue() / 255.f, MidnightControlsConfig.touchOutlineColorAlpha / 255.f);
matrices.pop();
}
}
@Unique
private void midnightcontrols$renderReacharoundOutline(MatrixStack matrices, Camera camera) {
if (this.client.crosshairTarget == null || this.client.crosshairTarget.getType() != HitResult.Type.MISS || !MidnightControlsConfig.shouldRenderReacharoundOutline)
return;
var result = reacharound.getLastReacharoundResult();
if (result == null)
return;
var blockPos = result.getBlockPos();
if (this.world.getWorldBorder().contains(blockPos) && this.client.player != null) {
var stack = this.client.player.getStackInHand(Hand.MAIN_HAND);
if (stack == null || !(stack.getItem() instanceof BlockItem))
return;
var block = ((BlockItem) stack.getItem()).getBlock();
result = reacharound.withSideForReacharound(result, block);
var context = new ItemPlacementContext(new ItemUsageContext(this.client.player, Hand.MAIN_HAND, result));
var placementState = block.getPlacementState(context);
if (placementState == null)
return;
var pos = camera.getPos();
var outlineShape = placementState.getOutlineShape(this.client.world, blockPos, ShapeContext.of(camera.getFocusedEntity()));
Color rgb = MidnightColorUtil.hex2Rgb(MidnightControlsConfig.reacharoundOutlineColorHex);
if (MidnightControlsConfig.reacharoundOutlineColorHex.isEmpty()) rgb = RainbowColor.radialRainbow(1,1);
matrices.push();
var vertexConsumer = this.bufferBuilders.getEntityVertexConsumers().getBuffer(RenderLayer.getLines());
drawCuboidShapeOutline(matrices, vertexConsumer, outlineShape,
(double) blockPos.getX() - pos.getX(), (double) blockPos.getY() - pos.getY(), (double) blockPos.getZ() - pos.getZ(),
rgb.getRed() / 255.f, rgb.getGreen() / 255.f, rgb.getBlue() / 255.f, MidnightControlsConfig.reacharoundOutlineColorAlpha / 255.f);
matrices.pop();
}
}
}

View File

@@ -0,0 +1,72 @@
package eu.midnightdust.midnightcontrols.client.mouse;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.util.GlfwUtil;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import net.minecraft.util.math.Smoother;
public class EyeTrackerHandler {
/**
* Based on the updateMouse method in the Mouse.class, this changes the mouse algorithm to suit eye tracking.
* This requires the cursor to not be locked, and the raw input setting to be turned off.
*/
public static void updateMouseWithEyeTracking(double mouseX, double mouseY, MinecraftClient client, double lastMouseUpdateTime, boolean holdingLeftMouseButton, boolean usingLongRangedTool, Smoother smoothX, Smoother smoothY) {
if (client.player == null) return;
/* The player wants objects of interest to be moved under the crosshair that is always center of screen.
* Normal mouse controls operate with the delta values from the direction of mouse movement,
* but in eye tracking we want to use the cursor's actual x,y values (their point of gaze), relative to
* the screen center (where the crosshair is). This new eye tracking delta creates a vector that points
* from the crosshair to the gaze point. As the player keeps their eyes on the object of interest, we pull
* that object into the center until the object is underneath the crosshair.
*/
double deltaTime = GlfwUtil.getTime() - lastMouseUpdateTime;
// The center of screen is the new (0,0)
double centerX = client.getWindow().getWidth() / 2.0;
double centerY = client.getWindow().getHeight() / 2.0;
double gazeRawX = mouseX - centerX;
double gazeRawY = mouseY - centerY;
//This part follows the original mouse.java somewhat closely, with different constants
double feeling = 2.5;
double sensitivity = client.options.getMouseSensitivity().getValue() * feeling;
double spyglass = sensitivity * sensitivity * sensitivity;
double moveScalar = spyglass * 8.0;
double frameScalar;
if(client.options.getPerspective().isFirstPerson() && client.player.isUsingSpyglass()) {
frameScalar = spyglass;
} else {
frameScalar = moveScalar;
}
if(holdingLeftMouseButton && !usingLongRangedTool) {
frameScalar *= 0.5; //Don't move the camera so much while mining. It's annoying.
}
// The longest vector connects the center to the corner of the screen, so that is our maximum magnitude for
// normalization. We use normalized screen size vector for resolution independent control
double magnitudeMax = Math.sqrt(centerX*centerX + centerY*centerY);
double normalizedX = gazeRawX / magnitudeMax;
double normalizedY = gazeRawY / magnitudeMax;
double moveX = normalizedX * frameScalar;
double moveY = normalizedY * frameScalar;
if (client.options.smoothCameraEnabled) {
moveX = smoothX.smooth(moveX, moveScalar*deltaTime);
moveY = smoothY.smooth(moveY, moveScalar*deltaTime);
}
// The player entity's needs their facing rotated.
double invertY = 1.0;
double moveMagnitude = Math.sqrt(normalizedX*normalizedX + normalizedY*normalizedY);
if (client.options.getInvertYMouse().getValue()) {
invertY = -1.0;
}
boolean notInDeadzone = (moveMagnitude > MidnightControlsConfig.eyeTrackerDeadzone) && !usingLongRangedTool;
if (client.player != null && notInDeadzone) {
client.player.changeLookDirection(moveX, moveY * invertY);
client.getTutorialManager().onUpdateMouse(moveX, moveY);
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.ring;
import com.google.gson.Gson;
import eu.midnightdust.midnightcontrols.client.enums.ButtonState;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.util.KeyBindingAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.OrderedText;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Supplier;
public class ButtonBindingRingAction extends RingAction {
public static final Factory FACTORY = new Factory();
public final ButtonBinding binding;
public ButtonBindingRingAction(@NotNull ButtonBinding binding) {
super();
this.binding = binding;
}
@Override
public @NotNull String getName() {
return this.binding.getTranslationKey();
}
@Override
public void onAction(@NotNull RingButtonMode mode) {
binding.handle(MinecraftClient.getInstance(), 1.0f, ButtonState.PRESS);
if (binding.asKeyBinding().isPresent()) {
binding.asKeyBinding().get().setPressed(true);
((KeyBindingAccessor)binding.asKeyBinding().get()).midnightcontrols$press();
}
}
@Override
public void drawIcon(@NotNull DrawContext context, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered) {
List<OrderedText> lines = textRenderer.wrapLines(Text.translatable(this.getName()), MidnightRing.ELEMENT_SIZE);
for (int i = 0; i < lines.size(); ++i) {
context.drawCenteredTextWithShadow(textRenderer, lines.get(i), x + MidnightRing.ELEMENT_SIZE / 2, y + MidnightRing.ELEMENT_SIZE / 2 - textRenderer.fontHeight / 2 * (lines.size()-1) - textRenderer.fontHeight / 2 + textRenderer.fontHeight * i, 0xffffff);
}
}
protected static class Factory implements RingAction.Factory {
@Override
public @NotNull Supplier<RingAction> newFromGui(@NotNull Screen screen) {
return () -> null;
}
@Override
public @Nullable RingAction parse(@NotNull Gson config) {
return null;
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.ring;
import eu.midnightdust.midnightcontrols.MidnightControls;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import eu.midnightdust.midnightcontrols.client.controller.InputManager;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Represents a key binding ring.
*
* @author LambdAurora
* @version 1.7.0
* @since 1.4.0
*/
public final class MidnightRing {
public static final int ELEMENT_SIZE = 75;
private final Map<String, RingAction.Factory> actionFactories = new Object2ObjectOpenHashMap<>();
private final List<RingPage> pages = new ArrayList<>(Collections.singletonList(RingPage.DEFAULT));
private int currentPage = 0;
public MidnightRing() {
}
public void registerAction(@NotNull String name, @NotNull RingAction.Factory factory) {
if (this.actionFactories.containsKey(name)) {
MidnightControls.warn("Tried to register a ring action twice: \"" + name + "\".");
return;
}
this.actionFactories.put(name, factory);
}
/**
* Loads the ring from configuration.
*/
public void loadFromConfig() {
List<String> configBindings = MidnightControlsConfig.ringBindings;
if (configBindings != null) {
this.pages.clear();
int bindingIndex = 0;
for (int i = 0; i < MathHelper.ceil(configBindings.size() / 8f); ++i) {
this.pages.add(new RingPage(i+1 + " / " + MathHelper.ceil(configBindings.size() / 8f)));
}
for (String binding : configBindings) {
ButtonBinding buttonBinding = InputManager.getBinding(binding);
if (buttonBinding != null) {
RingPage page = this.pages.get(MathHelper.floor(bindingIndex / 8f));
page.actions[bindingIndex - 8 * (MathHelper.floor(bindingIndex / 8f))] = (new ButtonBindingRingAction(buttonBinding));
++bindingIndex;
}
}
}
if (this.pages.isEmpty()) {
this.pages.add(RingPage.DEFAULT);
}
}
/**
* Loads the ring from all unbound keys.
*/
public void loadFromUnbound() {
List<ButtonBinding> unboundBindings = InputManager.getUnboundBindings();
if (unboundBindings != null) {
this.pages.clear();
int bindingIndex = 0;
for (int i = 0; i < MathHelper.ceil(unboundBindings.size() / 8f); ++i) {
this.pages.add(new RingPage(i+1 + " / " + MathHelper.ceil(unboundBindings.size() / 8f)));
}
for (ButtonBinding buttonBinding : unboundBindings) {
if (buttonBinding != null) {
RingPage page = this.pages.get(MathHelper.floor(bindingIndex / 8f));
page.actions[bindingIndex - 8 * (MathHelper.floor(bindingIndex / 8f))] = (new ButtonBindingRingAction(buttonBinding));
++bindingIndex;
}
}
}
if (this.pages.isEmpty()) {
this.pages.add(RingPage.DEFAULT);
}
}
public int getMaxPages() {
return this.pages.size();
}
public @NotNull RingPage getCurrentPage() {
if (this.currentPage >= this.pages.size())
this.currentPage = this.pages.size() - 1;
else if (this.currentPage < 0)
this.currentPage = 0;
return this.pages.get(this.currentPage);
}
public void cyclePage(boolean forwards) {
if (forwards) {
if (currentPage < pages.size()-1) ++currentPage;
else currentPage = 0;
} else {
if (currentPage > 0) --currentPage;
else currentPage = pages.size();
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.ring;
import com.google.gson.Gson;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Supplier;
/**
* Represents a ring action.
*
* @author LambdAurora
* @version 1.5.0
* @since 1.4.0
*/
public abstract class RingAction implements Nameable {
protected boolean activated = false;
public RingAction() {
}
/**
* Gets the text name of the ring action.
*
* @return the text name
*/
public Text getTextName() {
return Text.translatable(this.getName());
}
/**
* Returns whether the action is activated or not.
*
* @return true if the action is activated, else false
*/
public boolean isActivated() {
return this.activated;
}
public void activate(@NotNull RingButtonMode mode) {
this.activated = !this.activated;
this.onAction(mode);
}
public abstract void onAction(@NotNull RingButtonMode mode);
public void render(@NotNull DrawContext context, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered, int index) {
context.fill(x, y, x + MidnightRing.ELEMENT_SIZE, y + MidnightRing.ELEMENT_SIZE, hovered || RingPage.selected == index ? 0xbb777777 : 0xbb000000);
drawIcon(context, textRenderer, x, y, hovered);
}
public abstract void drawIcon(@NotNull DrawContext context, @NotNull TextRenderer textRenderer, int x, int y, boolean hovered);
/**
* Represents a factory for {@link RingAction}.
*
* @version 1.4.3
* @since 1.4.3
*/
public interface Factory {
@NotNull Supplier<RingAction> newFromGui(@NotNull Screen screen);
@Nullable RingAction parse(@NotNull Gson config);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.ring;
import net.minecraft.text.Text;
import org.aperlambda.lambdacommon.utils.Nameable;
import org.jetbrains.annotations.NotNull;
/**
* Represents the mode of a ring button.
*
* @author LambdAurora
* @version 1.4.0
* @since 1.4.0
*/
public enum RingButtonMode implements Nameable {
PRESS("press"),
HOLD("hold"),
TOGGLE("toggle");
private final String name;
private final Text text;
RingButtonMode(@NotNull String name) {
this.name = name;
this.text = Text.translatable(this.getTranslationKey());
}
/**
* Returns the next ring button mode available.
*
* @return the next ring button mode
*/
public @NotNull RingButtonMode next() {
var v = values();
if (v.length == this.ordinal() + 1)
return v[0];
return v[this.ordinal() + 1];
}
/**
* Returns the translation key of this ring button mode.
*
* @return the translation key of this ring button mode
*/
public @NotNull String getTranslationKey() {
return "midnightcontrols.ring.button_mode." + this.getName();
}
/**
* Gets the translated name of this ring button mode.
*
* @return the translated name of this ring button mode
*/
public @NotNull Text getTranslatedText() {
return this.text;
}
@Override
public @NotNull String getName() {
return this.name;
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.ring;
import eu.midnightdust.midnightcontrols.client.MidnightControlsClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import org.jetbrains.annotations.NotNull;
/**
* Represents a ring page.
*
* @author LambdAurora
* @version 1.5.0
* @since 1.4.0
*/
public class RingPage {
public static final RingPage DEFAULT = new RingPage("Default");
public final String name;
public static int selected = -1;
public RingAction[] actions = new RingAction[8];
public RingPage(@NotNull String name) {
this.name = name;
for (int i = 0; i < 8; i++) {
this.actions[i] = null;
}
}
/**
* Renders the ring page.
*
* @param context the context
* @param width the screen width
* @param height the screen height
* @param mouseX the mouse X-coordinate
* @param mouseY the mouse Y-coordinate
* @param tickDelta the tick delta
*/
public void render(@NotNull DrawContext context, @NotNull TextRenderer textRenderer, int width, int height, int mouseX, int mouseY, float tickDelta) {
int centerX = width / 2;
int centerY = height / 2;
if (MidnightControlsClient.ring.getMaxPages() > 1) context.drawCenteredTextWithShadow(textRenderer, name, centerX, 5, 0xffffff);
int offset = MidnightRing.ELEMENT_SIZE + (MidnightRing.ELEMENT_SIZE / 2) + 5;
int y = centerY - offset;
int x = centerX - offset;
for (int i = 0; i < 3; i++) {
var ringAction = this.actions[i];
if (ringAction != null)
ringAction.render(context, textRenderer, x, y, isHovered(x, y, mouseX, mouseY), i);
x += MidnightRing.ELEMENT_SIZE + 5;
}
y += MidnightRing.ELEMENT_SIZE + 5;
x = centerX - offset;
for (int i = 3; i < 5; i++) {
var ringAction = this.actions[i];
if (ringAction != null)
ringAction.render(context, textRenderer, x, y, isHovered(x, y, mouseX, mouseY), i);
x += (MidnightRing.ELEMENT_SIZE + 5) * 2;
}
y += MidnightRing.ELEMENT_SIZE + 5;
x = centerX - offset;
for (int i = 5; i < 8; i++) {
var ringAction = this.actions[i];
if (ringAction != null)
ringAction.render(context, textRenderer, x, y, isHovered(x, y, mouseX, mouseY), i);
x += MidnightRing.ELEMENT_SIZE + 5;
}
}
private static boolean isHovered(int x, int y, int mouseX, int mouseY) {
return mouseX >= x && mouseY >= y && mouseX <= x + MidnightRing.ELEMENT_SIZE && mouseY <= y + MidnightRing.ELEMENT_SIZE && selected < 0;
}
/**
* Renders the ring page.
*
* @param width the screen width
* @param height the screen height
* @param mouseX the mouse X-coordinate
* @param mouseY the mouse Y-coordinate
*/
public boolean onClick(int width, int height, int mouseX, int mouseY) {
int centerX = width / 2;
int centerY = height / 2;
int offset = MidnightRing.ELEMENT_SIZE + (MidnightRing.ELEMENT_SIZE / 2) + 5;
int y = centerY - offset;
int x = centerX - offset;
for (int i = 0; i < 3; i++) {
var ringAction = this.actions[i];
if (ringAction != null && isHovered(x,y,mouseX,mouseY)) {
ringAction.activate(RingButtonMode.PRESS);
return true;
}
x += MidnightRing.ELEMENT_SIZE + 5;
}
y += MidnightRing.ELEMENT_SIZE + 5;
x = centerX - offset;
for (int i = 3; i < 5; i++) {
var ringAction = this.actions[i];
if (ringAction != null && isHovered(x,y,mouseX,mouseY)) {
ringAction.activate(RingButtonMode.PRESS);
return true;
}
x += (MidnightRing.ELEMENT_SIZE + 5) * 2;
}
y += MidnightRing.ELEMENT_SIZE + 5;
x = centerX - offset;
for (int i = 5; i < 8; i++) {
var ringAction = this.actions[i];
if (ringAction != null && isHovered(x,y,mouseX,mouseY)) {
ringAction.activate(RingButtonMode.PRESS);
return true;
}
x += MidnightRing.ELEMENT_SIZE + 5;
}
return false;
}
}

View File

@@ -0,0 +1,105 @@
package eu.midnightdust.midnightcontrols.client.touch;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.gui.TouchscreenOverlay;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsConfig.doMixedInput;
public class TouchInput {
private static final MinecraftClient client = MinecraftClient.getInstance();
public static long clickStartTime;
public static HitResult firstHitResult = null;
public static void tick() {
if ((client.currentScreen == null && doMixedInput()) || client.currentScreen instanceof TouchscreenOverlay) {
double scaleFactor = client.getWindow().getScaleFactor();
if (clickStartTime > 0 && System.currentTimeMillis() - clickStartTime >= MidnightControlsConfig.touchBreakDelay) {
mouseHeldDown(client.mouse.getX() / scaleFactor, client.mouse.getY() / scaleFactor);
}
}
}
public static void mouseHeldDown(double mouseX, double mouseY) {
assert client != null;
assert client.player != null;
assert client.interactionManager != null;
if (client.player.getMainHandStack() != null && TouchUtils.hasInWorldUseAction(client.player.getMainHandStack())) {
client.interactionManager.interactItem(client.player, client.player.getActiveHand());
return;
}
HitResult result = TouchUtils.getTargetedObject(mouseX, mouseY);
if (result == null || firstHitResult == null) {
client.interactionManager.cancelBlockBreaking();
return;
}
if (result instanceof BlockHitResult blockHit && firstHitResult instanceof BlockHitResult firstBlock && blockHit.getBlockPos().equals(firstBlock.getBlockPos())) {
if (MidnightControlsConfig.debug) System.out.println(blockHit.getBlockPos().toString());
if (client.interactionManager.updateBlockBreakingProgress(blockHit.getBlockPos(), blockHit.getSide())) {
client.particleManager.addBlockBreakingParticles(blockHit.getBlockPos(), blockHit.getSide());
client.player.swingHand(Hand.MAIN_HAND);
} else client.interactionManager.cancelBlockBreaking();
firstHitResult = TouchUtils.getTargetedObject(mouseX, mouseY);
}
else if (result instanceof EntityHitResult entityHit && firstHitResult instanceof EntityHitResult firstEntity && entityHit.getEntity().getUuid().compareTo(firstEntity.getEntity().getUuid()) == 0) {
if (client.interactionManager.interactEntity(client.player, entityHit.getEntity(), client.player.getActiveHand()) == ActionResult.SUCCESS) {
client.player.swingHand(Hand.MAIN_HAND);
}
firstHitResult = TouchUtils.getTargetedObject(mouseX, mouseY);
}
}
public static boolean mouseReleased(double mouseX, double mouseY, int button) {
firstHitResult = null;
if (client.interactionManager != null) client.interactionManager.cancelBlockBreaking();
if ((client.currentScreen == null || !client.currentScreen.mouseReleased(mouseX, mouseY, button)) && System.currentTimeMillis() - clickStartTime < MidnightControlsConfig.touchBreakDelay) {
assert client.player != null;
assert client.world != null;
assert client.interactionManager != null;
clickStartTime = -1;
if (client.player.getMainHandStack() != null && TouchUtils.hasInWorldUseAction(client.player.getMainHandStack())) {
client.interactionManager.stopUsingItem(client.player);
return true;
}
HitResult result = TouchUtils.getTargetedObject(mouseX, mouseY);
if (result == null) return false;
if (result instanceof BlockHitResult blockHit) {
BlockPos blockPos = blockHit.getBlockPos().offset(blockHit.getSide());
BlockState state = client.world.getBlockState(blockPos);
if (client.world.isAir(blockPos) || state.isReplaceable()) {
ItemStack stackInHand = client.player.getMainHandStack();
int previousStackCount = stackInHand.getCount();
var interaction = client.interactionManager.interactBlock(client.player, client.player.getActiveHand(), blockHit);
if (interaction.isAccepted()) {
if (interaction.shouldSwingHand()) {
client.player.swingHand(client.player.preferredHand);
if (!stackInHand.isEmpty() && (stackInHand.getCount() != previousStackCount || client.interactionManager.hasCreativeInventory())) {
client.gameRenderer.firstPersonRenderer.resetEquipProgress(client.player.preferredHand);
}
}
return true;
}
}
}
if (result instanceof EntityHitResult entityHit) {
client.interactionManager.attackEntity(client.player, entityHit.getEntity());
client.player.swingHand(Hand.MAIN_HAND);
return true;
}
}
clickStartTime = -1;
return false;
}
}

View File

@@ -0,0 +1,69 @@
package eu.midnightdust.midnightcontrols.client.touch;
import eu.midnightdust.lib.util.PlatformFunctions;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.enums.TouchMode;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.entity.projectile.ProjectileUtil;
import net.minecraft.item.ItemStack;
import net.minecraft.util.UseAction;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.opengl.GL11;
import static eu.midnightdust.midnightcontrols.client.MidnightReacharound.getPlayerRange;
public class TouchUtils {
private static final MinecraftClient client = MinecraftClient.getInstance();
public static final Matrix4f lastWorldSpaceMatrix = new Matrix4f();
public static final Matrix4f lastProjMat = new Matrix4f();
public static final Matrix4f lastModMat = new Matrix4f();
public static HitResult getTargetedObject(double mouseX, double mouseY) {
if (client.player == null || client.world == null || MidnightControlsConfig.touchMode == TouchMode.CROSSHAIR || PlatformFunctions.isModLoaded("vulkanmod")) {
return client.crosshairTarget;
}
Vec3d near = screenSpaceToWorldSpace(mouseX, mouseY, 0);
Vec3d far = screenSpaceToWorldSpace(mouseX, mouseY, 1);
EntityHitResult entityCast = ProjectileUtil.raycast(client.player, near, far, Box.from(client.player.getPos()).expand(getPlayerRange(client)), entity -> (!entity.isSpectator() && entity.isAttackable()), getPlayerRange(client) * getPlayerRange(client));
if (entityCast != null && entityCast.getType() == HitResult.Type.ENTITY) return entityCast;
BlockHitResult result = client.world.raycast(new RaycastContext(near, far, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.ANY, client.player));
if (client.player.getPos().distanceTo(result.getPos()) > getPlayerRange(client)) return null;
return result;
}
/* Taken from https://github.com/0x3C50/Renderer/blob/master/src/main/java/me/x150/renderer/util/RendererUtils.java#L270
* Credits to 0x3C50 */
public static Vec3d screenSpaceToWorldSpace(double x, double y, double d) {
Camera camera = client.getEntityRenderDispatcher().camera;
int displayHeight = client.getWindow().getScaledHeight();
int displayWidth = client.getWindow().getScaledWidth();
int[] viewport = new int[4];
GL11.glGetIntegerv(GL11.GL_VIEWPORT, viewport);
Vector3f target = new Vector3f();
Matrix4f matrixProj = new Matrix4f(lastProjMat);
Matrix4f matrixModel = new Matrix4f(lastModMat);
matrixProj.mul(matrixModel)
.mul(lastWorldSpaceMatrix)
.unproject((float) x / displayWidth * viewport[2],
(float) (displayHeight - y) / displayHeight * viewport[3], (float) d, viewport, target);
return new Vec3d(target.x, target.y, target.z).add(camera.getPos());
}
public static boolean hasInWorldUseAction(ItemStack stack) {
UseAction action = stack.getUseAction();
return action == UseAction.BOW || action == UseAction.BRUSH || action == UseAction.SPEAR;
}
}

View File

@@ -0,0 +1,42 @@
package eu.midnightdust.midnightcontrols.client.touch.gui;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.widget.SpruceButtonWidget;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import net.minecraft.item.ArmorItem;
import net.minecraft.text.Text;
import net.minecraft.util.UseAction;
public class ItemUseButtonWidget extends SpruceButtonWidget {
public ItemUseButtonWidget(Position position, int width, int height, Text message, PressAction action) {
super(position, width, height, message, action);
}
@Override
protected void onRelease(double mouseX, double mouseY) {
assert client.player != null;
assert client.interactionManager != null;
UseAction action = client.player.getMainHandStack().getUseAction();
if (action == UseAction.SPYGLASS || action == UseAction.TOOT_HORN) client.interactionManager.stopUsingItem(client.player);
super.onRelease(mouseX, mouseY);
}
@Override
public void setVisible(boolean visible) {
if (visible && client.player != null && client.player.getMainHandStack() != null) {
UseAction action = client.player.getMainHandStack().getUseAction();
if (action == UseAction.EAT) {
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.eat"));
} else if (action == UseAction.DRINK) {
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.drink"));
} else if (client.player.getMainHandStack().getItem() instanceof ArmorItem) {
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.equip"));
} else if (!action.equals(UseAction.NONE)) {
this.setMessage(Text.translatable(MidnightControlsConstants.NAMESPACE+".action.use"));
}
}
this.setAlpha(MidnightControlsConfig.touchTransparency / 100f);
super.setVisible(visible);
}
}

View File

@@ -0,0 +1,39 @@
package eu.midnightdust.midnightcontrols.client.touch.gui;
import org.thinkingstudio.obsidianui.Position;
import org.thinkingstudio.obsidianui.widget.SpruceTexturedButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
public class SilentTexturedButtonWidget extends SpruceTexturedButtonWidget {
public SilentTexturedButtonWidget(Position position, int width, int height, Text message, PressAction action, int u, int v, int hoveredVOffset, Identifier texture) {
super(position, width, height, message, action, u, v, hoveredVOffset, texture);
}
public SilentTexturedButtonWidget(Position position, int width, int height, Text message, boolean showMessage, PressAction action, int u, int v, int hoveredVOffset, Identifier texture) {
super(position, width, height, message, showMessage, action, u, v, hoveredVOffset, texture);
}
public SilentTexturedButtonWidget(Position position, int width, int height, Text message, PressAction action, int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight) {
super(position, width, height, message, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight);
}
public SilentTexturedButtonWidget(Position position, int width, int height, Text message, boolean showMessage, PressAction action, int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight) {
super(position, width, height, message, showMessage, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight);
}
@Override
public void playDownSound() {}
@Override
protected void onRelease(double mouseX, double mouseY) {
this.setActive(false);
super.onClick(mouseX, mouseY);
super.onRelease(mouseX, mouseY);
this.setActive(true);
}
@Override
public void onClick(double mouseX, double mouseY) {
this.setActive(true);
super.onClick(mouseX, mouseY);
this.setActive(false);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.util;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import org.jetbrains.annotations.Nullable;
/**
* Represents an accessor to AbstractContainerScreen.
*/
public interface HandledScreenAccessor {
/**
* Gets the left coordinate of the GUI.
*
* @return the left coordinate of the GUI
*/
int getX();
/**
* Gets the top coordinate of the GUI.
*
* @return the top coordinate of the GUI
*/
int getY();
/**
* Gets the slot at position.
*
* @param posX the X position to check
* @param posY the Y position to check
* @return the slot at the specified position
*/
Slot midnightcontrols$getSlotAt(double posX, double posY);
boolean midnightcontrols$isClickOutsideBounds(double mouseX, double mouseY, int x, int y, int button);
/**
* Handles a mouse click on the specified slot.
*
* @param slot the slot instance
* @param slotId the slot id
* @param clickData the click data
* @param actionType the action type
*/
void midnightcontrols$onMouseClick(@Nullable Slot slot, int slotId, int clickData, SlotActionType actionType);
}

View File

@@ -0,0 +1,63 @@
package eu.midnightdust.midnightcontrols.client.util;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.screen.slot.Slot;
import org.aperlambda.lambdacommon.utils.Pair;
import java.util.Comparator;
import java.util.Optional;
import java.util.function.Predicate;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
public class InventoryUtil {
// Finds the closest slot in the GUI within 14 pixels.
public static Optional<Slot> findClosestSlot(HandledScreen<?> inventory, int direction) {
var accessor = (HandledScreenAccessor) inventory;
int guiLeft = accessor.getX();
int guiTop = accessor.getY();
double mouseX = client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth();
double mouseY = client.mouse.getY() * (double) client.getWindow().getScaledHeight() / (double) client.getWindow().getHeight();
// Finds the hovered slot.
var mouseSlot = accessor.midnightcontrols$getSlotAt(mouseX, mouseY);
return inventory.getScreenHandler().slots.parallelStream()
.filter(Predicate.isEqual(mouseSlot).negate())
.map(slot -> {
int posX = guiLeft + slot.x + 8;
int posY = guiTop + slot.y + 8;
int otherPosX = (int) mouseX;
int otherPosY = (int) mouseY;
if (mouseSlot != null) {
otherPosX = guiLeft + mouseSlot.x + 8;
otherPosY = guiTop + mouseSlot.y + 8;
}
// Distance between the slot and the cursor.
double distance = Math.sqrt(Math.pow(posX - otherPosX, 2) + Math.pow(posY - otherPosY, 2));
return Pair.of(slot, distance);
}).filter(entry -> {
var slot = entry.key;
int posX = guiLeft + slot.x + 8;
int posY = guiTop + slot.y + 8;
int otherPosX = (int) mouseX;
int otherPosY = (int) mouseY;
if (mouseSlot != null) {
otherPosX = guiLeft + mouseSlot.x + 8;
otherPosY = guiTop + mouseSlot.y + 8;
}
if (direction == 0)
return posY < otherPosY;
else if (direction == 1)
return posY > otherPosY;
else if (direction == 2)
return posX > otherPosX;
else if (direction == 3)
return posX < otherPosX;
else
return false;
})
.min(Comparator.comparingDouble(p -> p.value))
.map(p -> p.key);
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.client.util;
/**
* Represents a Minecraft keybinding with extra access.
*/
public interface KeyBindingAccessor {
boolean midnightcontrols$press();
boolean midnightcontrols$unpress();
default boolean midnightcontrols$handlePressState(boolean pressed) {
if (pressed)
return this.midnightcontrols$press();
else
return this.midnightcontrols$unpress();
}
}

View File

@@ -0,0 +1,23 @@
package eu.midnightdust.midnightcontrols.client.util;
import net.minecraft.util.math.MathHelper;
public class MathUtil {
public static class PolarUtil {
public float polarX;
public float polarY;
public PolarUtil() {}
public void calculate(float x, float y, float speedFactor) {
calculate(x, y, speedFactor, 0);
}
public void calculate(float x, float y, float speedFactor, double deadZone) {
double inputR = Math.pow(x, 2) + Math.pow(y, 2);
inputR = (Math.abs(speedFactor * MathHelper.clamp(inputR,0.f,1.f)));
inputR = inputR < deadZone ? 0f : (inputR-deadZone) / (1f-deadZone);
double inputTheta = Math.atan2(y, x);
polarX = (float) (inputR *Math.cos(inputTheta));
polarY = (float) (inputR *Math.sin(inputTheta));
}
}
}

View File

@@ -0,0 +1,15 @@
package eu.midnightdust.midnightcontrols.client.util;
import java.awt.*;
public class RainbowColor {
public static float hue;
public static void tick() {
if (hue > 1) hue = 0f;
hue = hue + 0.01f;
}
public static Color radialRainbow(float saturation, float brightness) {
return Color.getHSBColor(hue, saturation, brightness);
}
}

View File

@@ -0,0 +1,47 @@
package eu.midnightdust.midnightcontrols.client.util;
import eu.midnightdust.midnightcontrols.client.MidnightControlsConfig;
import eu.midnightdust.midnightcontrols.client.controller.ButtonBinding;
import static eu.midnightdust.midnightcontrols.client.MidnightControlsClient.client;
public class ToggleSneakSprintUtil {
public static boolean toggleSneak(ButtonBinding button) {
if (client.player == null) return false;
boolean isFlying = client.player.getAbilities().flying;
var option = client.options.getSneakToggled();
button.asKeyBinding().ifPresent(binding -> {
boolean sneakToggled = option.getValue();
if (isFlying && sneakToggled)
option.setValue(false);
else if (MidnightControlsConfig.controllerToggleSneak != sneakToggled)
option.setValue(!sneakToggled);
binding.setPressed(button.isPressed());
if (isFlying && sneakToggled)
option.setValue(true);
else if (MidnightControlsConfig.controllerToggleSneak != sneakToggled)
option.setValue(sneakToggled);
});
return true;
}
public static boolean toggleSprint(ButtonBinding button) {
if (client.player == null) return false;
boolean isFlying = client.player.getAbilities().flying;
var option = client.options.getSprintToggled();
button.asKeyBinding().ifPresent(binding -> {
boolean sprintToggled = option.getValue();
if (isFlying && sprintToggled)
option.setValue(false);
else if (MidnightControlsConfig.controllerToggleSprint != sprintToggled)
option.setValue(!sprintToggled);
binding.setPressed(button.isPressed());
if (client.player.getAbilities().flying && sprintToggled)
option.setValue(true);
else if (MidnightControlsConfig.controllerToggleSprint != sprintToggled)
option.setValue(sprintToggled);
});
return true;
}
}

View File

@@ -0,0 +1,50 @@
package eu.midnightdust.midnightcontrols.client.util.platform;
import dev.architectury.injectables.annotations.ExpectPlatform;
import eu.midnightdust.midnightcontrols.client.mixin.CreativeInventoryScreenAccessor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemGroups;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class ItemGroupUtil {
@ExpectPlatform
public static List<ItemGroup> getVisibleGroups(CreativeInventoryScreen screen) {
throw new AssertionError();
}
@ExpectPlatform
public static boolean cyclePage(boolean next, CreativeInventoryScreen screen) {
throw new AssertionError();
}
public static @NotNull ItemGroup cycleTab(boolean next, MinecraftClient client) {
ItemGroup currentTab = CreativeInventoryScreenAccessor.getSelectedTab();
int currentColumn = currentTab.getColumn();
ItemGroup.Row currentRow = currentTab.getRow();
ItemGroup newTab = null;
List<ItemGroup> visibleTabs = ItemGroupUtil.getVisibleGroups((CreativeInventoryScreen) client.currentScreen);
for (ItemGroup tab : visibleTabs) {
if (tab.getRow().equals(currentRow) && ((newTab == null && ((next && tab.getColumn() > currentColumn) ||
(!next && tab.getColumn() < currentColumn))) || (newTab != null && ((next && tab.getColumn() > currentColumn && tab.getColumn() < newTab.getColumn()) ||
(!next && tab.getColumn() < currentColumn && tab.getColumn() > newTab.getColumn())))))
newTab = tab;
}
if (newTab == null)
for (ItemGroup tab : visibleTabs) {
if ((tab.getRow().compareTo(currentRow)) != 0 && ((next && newTab == null || next && newTab.getColumn() > tab.getColumn()) || (!next && newTab == null) || (!next && newTab.getColumn() < tab.getColumn())))
newTab = tab;
}
if (newTab == null) {
for (ItemGroup tab : visibleTabs) {
if ((next && tab.getRow() == ItemGroup.Row.TOP && tab.getColumn() == 0) ||
!next && tab.getRow() == ItemGroup.Row.BOTTOM && (newTab == null || tab.getColumn() > newTab.getColumn()))
newTab = tab;
}
}
if (newTab == null || newTab.equals(currentTab)) newTab = ItemGroups.getDefaultTab();
return newTab;
}
}

View File

@@ -0,0 +1,16 @@
package eu.midnightdust.midnightcontrols.client.util.platform;
import dev.architectury.injectables.annotations.ExpectPlatform;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.network.packet.Packet;
public class NetworkUtil {
@ExpectPlatform
public static void sendPacketC2S(Packet<?> packet) {
throw new AssertionError();
}
@ExpectPlatform
public static void sendPayloadC2S(CustomPayload payload) {
throw new AssertionError();
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright © 2021 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of midnightcontrols.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/
package eu.midnightdust.midnightcontrols.event;
import eu.midnightdust.midnightcontrols.ControlsMode;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.player.PlayerEntity;
import org.jetbrains.annotations.NotNull;
/**
* Represents an event callback which is fired when a player changes the controls mode.
*
* @author LambdAurora
* @version 1.1.0
* @since 1.1.0
*/
@FunctionalInterface
public interface PlayerChangeControlsModeCallback {
Event<PlayerChangeControlsModeCallback> EVENT = EventFactory.createArrayBacked(PlayerChangeControlsModeCallback.class, listeners -> (player, controlsMode) -> {
for (PlayerChangeControlsModeCallback event : listeners) {
event.apply(player, controlsMode);
}
});
void apply(@NotNull PlayerEntity player, @NotNull ControlsMode controlsMode);
}

View File

@@ -0,0 +1,27 @@
package eu.midnightdust.midnightcontrols.packet;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.packet.CustomPayload;
import java.util.Objects;
public record ControlsModePayload(String controlsMode) implements CustomPayload {
public static final Id<ControlsModePayload> PACKET_ID = new Id<>(MidnightControlsConstants.CONTROLS_MODE_CHANNEL);
public static final PacketCodec<RegistryByteBuf, ControlsModePayload> codec = PacketCodec.of(ControlsModePayload::write, ControlsModePayload::read);
public static ControlsModePayload read(RegistryByteBuf buf) {
return new ControlsModePayload(buf.readString(32));
}
public void write(RegistryByteBuf buf) {
Objects.requireNonNull(controlsMode, "Controls mode cannot be null.");
buf.writeString(controlsMode, 32);
}
@Override
public Id<? extends CustomPayload> getId() {
return PACKET_ID;
}
}

View File

@@ -0,0 +1,43 @@
package eu.midnightdust.midnightcontrols.packet;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import eu.midnightdust.midnightcontrols.MidnightControlsFeature;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.packet.CustomPayload;
public record FeaturePayload(MidnightControlsFeature... features) implements CustomPayload {
public static final Id<FeaturePayload> PACKET_ID = new Id<>(MidnightControlsConstants.FEATURE_CHANNEL);
public static final PacketCodec<RegistryByteBuf, FeaturePayload> codec = PacketCodec.of(FeaturePayload::write, FeaturePayload::read);
public static FeaturePayload read(RegistryByteBuf buf) {
int featureLength = buf.readVarInt();
MidnightControlsFeature[] receivedFeatures = new MidnightControlsFeature[featureLength];
for (int i = 0; i < featureLength; i++) {
var name = buf.readString(64);
boolean allowed = buf.readBoolean();
var feature = MidnightControlsFeature.fromName(name);
if (feature.isPresent()) {
feature.get().setAllowed(allowed);
receivedFeatures[i] = feature.get();
}
}
return new FeaturePayload(receivedFeatures);
}
public void write(RegistryByteBuf buf) {
if (features.length == 0)
throw new IllegalArgumentException("At least one feature must be provided.");
buf.writeVarInt(features.length);
for (var feature : features) {
buf.writeString(feature.getName(), 64);
buf.writeBoolean(feature.isAllowed());
}
}
@Override
public Id<? extends CustomPayload> getId() {
return PACKET_ID;
}
}

View File

@@ -0,0 +1,24 @@
package eu.midnightdust.midnightcontrols.packet;
import eu.midnightdust.midnightcontrols.MidnightControlsConstants;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.packet.CustomPayload;
public record HelloPayload(String version, String controlsMode) implements CustomPayload {
public static final CustomPayload.Id<HelloPayload> PACKET_ID = new CustomPayload.Id<>(MidnightControlsConstants.HELLO_CHANNEL);
public static final PacketCodec<RegistryByteBuf, HelloPayload> codec = PacketCodec.of(HelloPayload::write, HelloPayload::read);
public static HelloPayload read(RegistryByteBuf buf) {
return new HelloPayload(buf.readString(32), buf.readString(32));
}
public void write(RegistryByteBuf buf) {
buf.writeString(version, 32).writeString(controlsMode, 32);
}
@Override
public Id<? extends CustomPayload> getId() {
return PACKET_ID;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,164 @@
{
"midnightcontrols.midnightconfig.title": "MidnightControls Erweiterte Konfiguration",
"key.midnightcontrols.look_down": "Nach unten schauen",
"key.midnightcontrols.look_left": "Nach links schauen",
"key.midnightcontrols.look_right": "Nach rechts schauen",
"key.midnightcontrols.look_up": "Nach oben schauen",
"key.midnightcontrols.ring": "Öffne Ring mit ungebundenen Aktionen",
"midnightcontrols.action.attack": "Angreifen",
"midnightcontrols.action.back": "Zurück",
"midnightcontrols.action.chat": "Chat öffnen",
"midnightcontrols.action.controls_ring": "Öffne Ring mit ungebundenen Aktionen",
"midnightcontrols.action.drop_item": "Item droppen",
"midnightcontrols.action.exit": "Schließen",
"midnightcontrols.action.forward": "Vorwärts",
"midnightcontrols.action.hit": "Schlagen",
"midnightcontrols.action.hotbar_left": "Hotbar nach links",
"midnightcontrols.action.hotbar_right": "Hotbar nach rechts",
"midnightcontrols.action.inventory": "Inventar",
"midnightcontrols.action.jump": "Springen",
"midnightcontrols.action.left": "Links",
"midnightcontrols.action.pause_game": "Spiel pausieren",
"midnightcontrols.action.pick_block": "Block auswählen",
"midnightcontrols.action.pickup": "Aufheben",
"midnightcontrols.action.pickup_all": "Alles aufheben",
"midnightcontrols.action.place": "Platzieren",
"midnightcontrols.action.player_list": "Spielerliste",
"midnightcontrols.action.quick_move": "Schnell bewegen",
"midnightcontrols.action.right": "Rechts",
"midnightcontrols.action.screenshot": "Screenshot aufnehmen",
"midnightcontrols.action.sneak": "Schleichen",
"midnightcontrols.action.sprint": "Sprinten",
"midnightcontrols.action.swap_hands": "Hände wechseln",
"midnightcontrols.action.toggle_perspective": "Perspektive wechseln",
"midnightcontrols.action.toggle_smooth_camera": "Cinematische Kamera",
"midnightcontrols.action.page_back": "Vorherige Seite",
"midnightcontrols.action.page_next": "Nächste Seite",
"midnightcontrols.action.take": "Item nehmen",
"midnightcontrols.action.take_all": "Stack nehmen",
"midnightcontrols.action.use": "Benutzen",
"midnightcontrols.action.zoom": "Zoom",
"midnightcontrols.action.zoom_in": "Zoom erhöhen",
"midnightcontrols.action.zoom_out": "Zoom verringern",
"midnightcontrols.action.zoom_reset": "Zoom zurücksetzen",
"midnightcontrols.button.left_bumper": "Linke Schultertaste",
"midnightcontrols.button.right_bumper": "Rechte Schultertaste",
"midnightcontrols.button.back": "Zurück",
"midnightcontrols.button.start": "Start",
"midnightcontrols.button.guide": "Guide",
"midnightcontrols.button.left_thumb": "Linker Stick",
"midnightcontrols.button.right_thumb": "Rechter Stick",
"midnightcontrols.button.dpad_up": "Steuerkreuz hoch",
"midnightcontrols.button.dpad_right": "Steuerkreuz rechts",
"midnightcontrols.button.dpad_down": "Steuerkreuz runter",
"midnightcontrols.button.dpad_left": "Steuerkreuz links",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "Links X+",
"midnightcontrols.axis.left_y+": "Links Y+",
"midnightcontrols.axis.right_x+": "Rechts X+",
"midnightcontrols.axis.right_y+": "Rechts Y+",
"midnightcontrols.axis.left_trigger": "Linker Trigger",
"midnightcontrols.axis.right_trigger": "Rechter Trigger",
"midnightcontrols.axis.left_x-": "Links X-",
"midnightcontrols.axis.left_y-": "Links Y-",
"midnightcontrols.axis.right_x-": "Rechts X-",
"midnightcontrols.axis.right_y-": "Rechts Y-",
"midnightcontrols.button.unknown": "Unbekannt (%d)",
"midnightcontrols.controller.tutorial.title": "Spiele Minecraft mit Controller!",
"midnightcontrols.controller.tutorial.description": "Gehe zu %s -> %s -> %s",
"midnightcontrols.controller.connected": "Controller %d verbunden.",
"midnightcontrols.controller.disconnected": "Controller %d getrennt.",
"midnightcontrols.controller.mappings.1": "Um die Controller-Mappings anzupassen, benutze %s",
"midnightcontrols.controller.mappings.3": "und füge die Controller-Mappings in den Mapping-Editor ein.",
"midnightcontrols.controller.mappings.error": "Fehler beim Laden der Controller-Mappings.",
"midnightcontrols.controller.mappings.error.write": "Fehler beim Schreiben der Controller Mappings.",
"midnightcontrols.controller.mappings.updated": "Mappings aktualisiert!",
"midnightcontrols.controller_type.default": "Standard",
"midnightcontrols.controller_type.numbered": "Nummerierter Controller",
"midnightcontrols.controls_mode.default": "Tastatur/Maus",
"midnightcontrols.controls_mode.controller": "Controller",
"midnightcontrols.controls_mode.touchscreen": "Touchscreen (In Arbeit)",
"midnightcontrols.hud_side.left": "Links",
"midnightcontrols.hud_side.right": "Rechts",
"midnightcontrols.menu.analog_movement": "Analoge Bewegung",
"midnightcontrols.menu.analog_movement.tooltip": "Aktiviert analoge Bewegung, wenn möglich.",
"midnightcontrols.menu.auto_switch_mode": "Automatischer Wechsel",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Ob der Eingabemodus automatisch auf Controller eingestellt werden soll, wenn einer verbunden wird.",
"midnightcontrols.menu.controller": "Controller",
"midnightcontrols.menu.controller2": "Zweiter Controller",
"midnightcontrols.menu.controller2.tooltip": "Zweiter Controller, zum Beispiel für Joy-Cons.",
"midnightcontrols.menu.controller_type": "Controller-Typ",
"midnightcontrols.menu.controller_type.tooltip": "Der Controllertyp für den die Knöpfe angezeigt werden sollen.",
"midnightcontrols.menu.controls_mode": "Modus",
"midnightcontrols.menu.controls_mode.tooltip": "Der Steuerungsmodus.",
"midnightcontrols.menu.double_tap_to_sprint": "Doppel-Tipp zum Sprinten",
"midnightcontrols.menu.fast_block_placing": "Schnelles Bauen",
"midnightcontrols.menu.fast_block_placing.tooltip": "Während des Fliegens im Kreativmodus das schnelle Platzieren von Knöpfen aktivieren. §cWird von manchen Servern als cheaten eingestuft.",
"midnightcontrols.menu.fly_drifting": "Flug-Drifting",
"midnightcontrols.menu.fly_drifting.tooltip": "Aktiviert das Flug-Drifting von Vanilla Minecraft.",
"midnightcontrols.menu.fly_drifting_vertical": "Vertikales Flug-Drifting",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Aktiviert das vertikale Flug-Drifting von Vanilla Minecraft",
"midnightcontrols.menu.hud_enable": "HUD aktivieren",
"midnightcontrols.menu.hud_enable.tooltip": "(De-)Aktiviert die Knopfanzeige im Spiel",
"midnightcontrols.menu.hud_side": "HUD-Seite",
"midnightcontrols.menu.hud_side.tooltip": "Die Position der Knopfanzeige.",
"midnightcontrols.menu.invert_right_x_axis": "X rechts umkehren",
"midnightcontrols.menu.invert_right_y_axis": "Y rechts umkehren",
"midnightcontrols.menu.keyboard_controls": "Tastatureinstellungen...",
"midnightcontrols.menu.left_dead_zone": "Tote Zone des linken Sticks",
"midnightcontrols.menu.left_dead_zone.tooltip": "Die tote Zone für den linken Analogstick.",
"midnightcontrols.menu.mappings.open_input_str": "Öffne den Controller-Mapping Editor",
"midnightcontrols.menu.max_left_x_value": "Maximalwert Linke X-Achse",
"midnightcontrols.menu.max_left_x_value.tooltip": "Ändert den maximalen Wert der linken X-Achse. Nützlich, wenn deine Achse nicht bis zum Maximum geht.",
"midnightcontrols.menu.max_left_y_value": "Maximalwert Linke Y-Achse",
"midnightcontrols.menu.max_left_y_value.tooltip": "Ändert den maximalen Wert der linken Y-Achse. Nützlich, wenn deine Achse nicht bis zum Maximum geht.",
"midnightcontrols.menu.max_right_x_value": "Maximalwert Rechte X-Achse",
"midnightcontrols.menu.max_right_x_value.tooltip": "Ändert den maximalen Wert der rechten X-Achse. Nützlich, wenn deine Achse nicht bis zum Maximum geht.",
"midnightcontrols.menu.max_right_y_value": "Maximalwert Rechte Y-Achse",
"midnightcontrols.menu.max_right_y_value.tooltip": "Ändert den maximalen Wert der rechten Y-Achse. Nützlich, wenn deine Achse nicht bis zum Maximum geht.",
"midnightcontrols.menu.mouse_speed": "Mausgeschwindigkeit",
"midnightcontrols.menu.mouse_speed.tooltip": "Die emulierte Mausgeschwindigkeit des Controllers.",
"midnightcontrols.menu.reacharound.horizontal": "Vorderes Blockplatzieren",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Aktiviert vorderes Platzieren von Blöcken, §cwird von manchen Servern als cheaten eingestuft§r.",
"midnightcontrols.menu.reacharound.vertical": "Vertikales Umgreifen",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Aktiviert vertikales Platzieren von Blöcken, §cwird von manchen Servern als cheaten eingestuft§r.",
"midnightcontrols.menu.reload_controller_mappings": "Controller-Mappings neuladen",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Lädt die Controller-Mappings neu.",
"midnightcontrols.menu.right_dead_zone": "Tote Zone des rechten Sticks",
"midnightcontrols.menu.right_dead_zone.tooltip": "Die tote Zone für den rechten Analogstick.",
"midnightcontrols.menu.rotation_speed": "Rotationsgeschwindigkeit (X-Achse)",
"midnightcontrols.menu.rotation_speed.tooltip": "Die Kamerarotationsgeschwindigkeit im Controllermodus. (X-Achse)",
"midnightcontrols.menu.y_axis_rotation_speed": "Rotationsgeschwindigkeit (Y-Achse)",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "Die Kamerarotationsgeschwindigkeit im Controllermodus. (Y-Achse)",
"midnightcontrols.menu.separate_controller_profile": "Separates Controller-Profil",
"midnightcontrols.menu.separator.controller": "Controller",
"midnightcontrols.menu.separator.general": "Generell",
"midnightcontrols.menu.title": "MidnightControls - Einstellungen",
"midnightcontrols.menu.title.controller": "Controlleroptionen",
"midnightcontrols.menu.title.controller_controls": "Controller Aktionen",
"midnightcontrols.menu.title.gameplay": "Gameplay Optionen",
"midnightcontrols.menu.title.general": "Generelle Optionen",
"midnightcontrols.menu.title.hud": "HUD Optionen",
"midnightcontrols.menu.title.mappings.string": "Mapping-Datei Editor",
"midnightcontrols.menu.title.visual": "Visuelle Optionen",
"midnightcontrols.menu.unfocused_input": "Unfokussierte Eingabe",
"midnightcontrols.menu.unfocused_input.tooltip": "Erlaube Controllereingabe auch wenn das Fenster nicht fokussiert ist.",
"midnightcontrols.menu.virtual_mouse": "Virtuelle Maus",
"midnightcontrols.menu.virtual_mouse.tooltip": "Aktiviere die virtuelle Maus.",
"midnightcontrols.menu.virtual_mouse.skin": "Aussehen der Virtuellen Maus",
"midnightcontrols.narrator.unbound": "%s entbunden",
"midnightcontrols.not_bound": "Nicht zugewiesen",
"midnightcontrols.virtual_mouse.skin.default_light": "Standard Hell",
"midnightcontrols.virtual_mouse.skin.default_dark": "Standard Dunkel",
"midnightcontrols.virtual_mouse.skin.second_light": "Alternativ Hell",
"midnightcontrols.virtual_mouse.skin.second_dark": "Alternativ Dunkel",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"modmenu.descriptionTranslation.midnightcontrols": "Fügt Controller-Unterstützung hinzu und verbessert allgemein die Steuerung.\nBasiert auf der LambdaControls mod, deren Entwicklung leider eingestellt wurde.",
"modmenu.summaryTranslation.midnightcontrols": "Fügt Controller-Unterstützung hinzu und verbessert allgemein die Steuerung."
}

View File

@@ -0,0 +1,234 @@
{
"midnightcontrols.midnightconfig.title": "MidnightControls Advanced Config",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_LIGHT": "Default Light",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_DARK": "Default Dark",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_LIGHT": "Second Light",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_DARK": "Second Dark",
"midnightcontrols.midnightconfig.enum.ControllerType.DEFAULT": "Default",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSHOCK": "DualShock",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSENSE": "DualSense",
"midnightcontrols.midnightconfig.enum.ControllerType.SWITCH": "Switch/Wii Controller",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX": "Xbox One/Series Controller",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX_360": "Xbox 360 Controller",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_CONTROLLER": "Steam Controller",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_DECK": "Steam Deck",
"midnightcontrols.midnightconfig.enum.ControllerType.OUYA": "OUYA Controller",
"midnightcontrols.midnightconfig.enum.ControllerType.NUMBERED": "Numbered Controller",
"midnightcontrols.midnightconfig.enum.ControlsMode.DEFAULT": "Keyboard/Mouse",
"midnightcontrols.midnightconfig.enum.ControlsMode.CONTROLLER": "Controller",
"midnightcontrols.midnightconfig.enum.ControlsMode.TOUCHSCREEN": "Touchscreen (Beta)",
"midnightcontrols.midnightconfig.enum.HudSide.LEFT": "Left",
"midnightcontrols.midnightconfig.enum.HudSide.RIGHT": "Right",
"midnightcontrols.midnightconfig.enum.TouchMode.CROSSHAIR": "At Crosshair",
"midnightcontrols.midnightconfig.enum.TouchMode.FINGER_POS": "Finger Position",
"midnightcontrols.midnightconfig.enum.CameraMode.FLAT": "Flat",
"midnightcontrols.midnightconfig.enum.CameraMode.ADAPTIVE": "Adaptive",
"key.midnightcontrols.look_down": "Look down",
"key.midnightcontrols.look_left": "Look left",
"key.midnightcontrols.look_right": "Look right",
"key.midnightcontrols.look_up": "Look up",
"key.midnightcontrols.ring": "Open Unbound Keybind Ring",
"midnightcontrols.action.attack": "Attack",
"midnightcontrols.action.back": "Back",
"midnightcontrols.action.chat": "Open Chat",
"midnightcontrols.action.controls_ring": "Open Unbound Keybind Ring",
"midnightcontrols.action.debug_screen": "Open Debug HUD (F3)",
"midnightcontrols.action.drop_item": "Drop Item",
"midnightcontrols.action.drink": "Drink",
"midnightcontrols.action.eat": "Eat",
"midnightcontrols.action.equip": "Equip",
"midnightcontrols.action.exit": "Exit Screen",
"midnightcontrols.action.forward": "Forward",
"midnightcontrols.action.hit": "Hit",
"midnightcontrols.action.hotbar_left": "Hotbar left",
"midnightcontrols.action.hotbar_right": "Hotbar right",
"midnightcontrols.action.inventory": "Inventory",
"midnightcontrols.action.jump": "Jump",
"midnightcontrols.action.left": "Left",
"midnightcontrols.action.pause_game": "Pause Game",
"midnightcontrols.action.pick_block": "Pick Block",
"midnightcontrols.action.pickup": "Pickup",
"midnightcontrols.action.pickup_all": "Pickup all",
"midnightcontrols.action.place": "Place",
"midnightcontrols.action.player_list": "Player List",
"midnightcontrols.action.quick_move": "Quick move",
"midnightcontrols.action.right": "Right",
"midnightcontrols.action.screenshot": "Take Screenshot",
"midnightcontrols.action.slot_up": "Move Slot up",
"midnightcontrols.action.slot_down": "Move Slot down",
"midnightcontrols.action.slot_left": "Move Slot left",
"midnightcontrols.action.slot_right": "Move Slot right",
"midnightcontrols.action.sneak": "Sneak",
"midnightcontrols.action.sprint": "Sprint",
"midnightcontrols.action.swap_hands": "Swap Hands",
"midnightcontrols.action.toggle_perspective": "Toggle Perspective",
"midnightcontrols.action.toggle_smooth_camera": "Toggle Cinematic Camera",
"midnightcontrols.action.page_back": "Previous Page",
"midnightcontrols.action.page_next": "Next Page",
"midnightcontrols.action.tab_back": "Previous Tab",
"midnightcontrols.action.tab_next": "Next Tab",
"midnightcontrols.action.take": "Take Item",
"midnightcontrols.action.take_all": "Take Stack",
"midnightcontrols.action.use": "Use",
"midnightcontrols.action.zoom": "Zoom",
"midnightcontrols.action.zoom_in": "Increase Zoom",
"midnightcontrols.action.zoom_out": "Decrease Zoom",
"midnightcontrols.action.zoom_reset": "Reset Zoom",
"midnightcontrols.action.emi_page_left": "Previous Page",
"midnightcontrols.action.emi_page_right": "Next Page",
"midnightcontrols.category.emi": "EMI",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Left bumper",
"midnightcontrols.button.right_bumper": "Right bumper",
"midnightcontrols.button.back": "Back",
"midnightcontrols.button.start": "Start",
"midnightcontrols.button.guide": "Guide",
"midnightcontrols.button.left_thumb": "Left thumb",
"midnightcontrols.button.right_thumb": "Right thumb",
"midnightcontrols.button.dpad_up": "DPAD up",
"midnightcontrols.button.dpad_right": "DPAD right",
"midnightcontrols.button.dpad_down": "DPAD down",
"midnightcontrols.button.dpad_left": "DPAD left",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "Left X+",
"midnightcontrols.axis.left_y+": "Left Y+",
"midnightcontrols.axis.right_x+": "Right X+",
"midnightcontrols.axis.right_y+": "Right Y+",
"midnightcontrols.axis.left_trigger": "Left trigger",
"midnightcontrols.axis.right_trigger": "Right trigger",
"midnightcontrols.axis.left_x-": "Left X-",
"midnightcontrols.axis.left_y-": "Left Y-",
"midnightcontrols.axis.right_x-": "Right X-",
"midnightcontrols.axis.right_y-": "Right Y-",
"midnightcontrols.button.unknown": "Unknown (%d)",
"midnightcontrols.controller.tutorial.title": "Play the game with a Controller!",
"midnightcontrols.controller.tutorial.description": "Go to %s -> %s -> %s",
"midnightcontrols.controller.connected": "Controller %d connected.",
"midnightcontrols.controller.disconnected": "Controller %d disconnected.",
"midnightcontrols.controller.mappings.1": "To configure the controller mappings, please use %s",
"midnightcontrols.controller.mappings.3": "and paste the mapping in the mappings file editor.",
"midnightcontrols.controller.mappings.error": "Error while loading mappings.",
"midnightcontrols.controller.mappings.error.write": "Error while writing mappings to file.",
"midnightcontrols.controller.mappings.updated": "Updated mappings!",
"midnightcontrols.controller_type.default": "Default",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.dualsense": "DualSense",
"midnightcontrols.controller_type.switch": "Switch/Wii Controller",
"midnightcontrols.controller_type.xbox": "Xbox One/Series Controller",
"midnightcontrols.controller_type.xbox_360": "Xbox 360 Controller",
"midnightcontrols.controller_type.steam_controller": "Steam Controller",
"midnightcontrols.controller_type.steam_deck": "Steam Deck",
"midnightcontrols.controller_type.ouya": "OUYA Controller",
"midnightcontrols.controller_type.numbered": "Numbered Controller",
"midnightcontrols.controls_mode.default": "Keyboard/Mouse",
"midnightcontrols.controls_mode.controller": "Controller",
"midnightcontrols.controls_mode.touchscreen": "Touchscreen (Beta)",
"midnightcontrols.hud_side.left": "Left",
"midnightcontrols.hud_side.right": "Right",
"midnightcontrols.menu.analog_movement": "Analog Movement",
"midnightcontrols.menu.analog_movement.tooltip": "When possible, enables analog movement.",
"midnightcontrols.menu.auto_switch_mode": "Auto Switch Mode",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Whether the controls mode should be switched to Controller automatically if one is connected.",
"midnightcontrols.menu.camera_mode": "Camera Mode",
"midnightcontrols.menu.controller": "Controller",
"midnightcontrols.menu.controller2": "Second Controller",
"midnightcontrols.menu.controller2.tooltip": "Second controller to use, which allows (for example) Joy-Cons support.",
"midnightcontrols.menu.controller_toggle_sneak": "Toggle Sneak on Controller",
"midnightcontrols.menu.controller_toggle_sprint": "Toggle Sprint on Controller",
"midnightcontrols.menu.controller_type": "Controller Type",
"midnightcontrols.menu.controller_type.tooltip": "The controller type you're using (needed to display the correct buttons)",
"midnightcontrols.menu.controls_mode": "Mode",
"midnightcontrols.menu.controls_mode.tooltip": "The controls mode.",
"midnightcontrols.menu.double_tap_to_sprint": "Double-Tap to Sprint",
"midnightcontrols.menu.double_tap_to_sprint.tooltip": "Toggles whether the Walk Forwards key makes the player sprint when double-tapped quickly",
"midnightcontrols.menu.fast_block_placing": "Fast Block Placing",
"midnightcontrols.menu.fast_block_placing.tooltip": "While flying in creative mode, enables fast block placing depending on your speed. §cOn some servers this might be considered as cheating.",
"midnightcontrols.menu.fly_drifting": "Fly Drifting",
"midnightcontrols.menu.fly_drifting.tooltip": "While flying, enables Vanilla drifting/inertia.",
"midnightcontrols.menu.fly_drifting_vertical": "Vertical Fly Drifting",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "While flying, enables Vanilla vertical drifting/intertia.",
"midnightcontrols.menu.hud_enable": "Enable HUD",
"midnightcontrols.menu.hud_enable.tooltip": "Toggles the on-screen controller button indicator.",
"midnightcontrols.menu.hud_side": "HUD Side",
"midnightcontrols.menu.hud_side.tooltip": "The position of the HUD.",
"midnightcontrols.menu.invert_right_x_axis": "Invert Right X",
"midnightcontrols.menu.invert_right_y_axis": "Invert Right Y",
"midnightcontrols.menu.joystick_as_mouse": "Always use left stick as mouse",
"midnightcontrols.menu.joystick_as_mouse.tooltip": "Make the joystick behave like a mouse in every menu.",
"midnightcontrols.menu.eye_tracker_as_mouse": "Use Eye Tracker as Mouse",
"midnightcontrols.menu.eye_tracker_as_mouse.tooltip": "Replace the mouse with an eye tracking device, (for example) the Tobii 5.",
"midnightcontrols.menu.eye_tracker_deadzone": "Eye Tracker Deadzone Size",
"midnightcontrols.menu.eye_tracker_deadzone.tooltip": "Stops camera movement when looking near the cross hair",
"midnightcontrols.menu.keyboard_controls": "Keyboard Controls...",
"midnightcontrols.menu.left_dead_zone": "Left Stick Dead Zone",
"midnightcontrols.menu.left_dead_zone.tooltip": "The dead zone for the controller's left analog stick.",
"midnightcontrols.menu.mappings.open_input_str": "Open Mappings File Editor",
"midnightcontrols.menu.max_left_x_value": "Left X Axis Max Value",
"midnightcontrols.menu.max_left_x_value.tooltip": "Changes what the mod considers the highest value for the left X axis. Useful if your axis does not use the full range and seems slow.",
"midnightcontrols.menu.max_left_y_value": "Left Y Axis Max Value",
"midnightcontrols.menu.max_left_y_value.tooltip": "Changes what the mod considers the highest value for the left Y axis. Useful if your axis does not use the full range and seems slow.",
"midnightcontrols.menu.max_right_x_value": "Right X Axis Max Value",
"midnightcontrols.menu.max_right_x_value.tooltip": "Changes what the mod considers the highest value for the right X axis. Useful if your axis does not use the full range and seems slow.",
"midnightcontrols.menu.max_right_y_value": "Right Y Axis Max Value",
"midnightcontrols.menu.max_right_y_value.tooltip": "Changes what the mod considers the highest value for the right Y axis. Useful if your axis does not use the full range and seems slow.",
"midnightcontrols.menu.mouse_speed": "Mouse Speed",
"midnightcontrols.menu.mouse_speed.tooltip": "The controller's emulated mouse speed.",
"midnightcontrols.menu.move_chat": "Move chat input box to top",
"midnightcontrols.menu.move_chat.tooltip": "Moves the chat input field to the top, for better input on devices with on-screen keyboards.",
"midnightcontrols.menu.reacharound.horizontal": "Front Block Placing",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Enables front block placing, §cmight be considered cheating on some servers§r.",
"midnightcontrols.menu.reacharound.vertical": "Vertical Reacharound",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Enables vertical reacharound, §cmight be considered cheating on some servers§r.",
"midnightcontrols.menu.reload_controller_mappings": "Reload Controller Mappings",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Reloads the controller mappings file.",
"midnightcontrols.menu.right_dead_zone": "Right Stick Dead Zone",
"midnightcontrols.menu.right_dead_zone.tooltip": "The dead zone for the controller's right analog stick.",
"midnightcontrols.menu.rotation_speed": "X Axis Rotation Speed",
"midnightcontrols.menu.rotation_speed.tooltip": "The camera's X Axis rotation speed in controller mode.",
"midnightcontrols.menu.y_axis_rotation_speed": "Y Axis Rotation Speed",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "The camera's Y Axis rotation speed in controller mode.",
"midnightcontrols.menu.separate_controller_profile": "Separate Controller Profile",
"midnightcontrols.menu.separator.controller": "Controller",
"midnightcontrols.menu.separator.general": "General",
"midnightcontrols.menu.title": "MidnightControls - Settings",
"midnightcontrols.menu.title.controller": "Controller Options",
"midnightcontrols.menu.title.controller_controls": "Controller Bindings",
"midnightcontrols.menu.title.gameplay": "Gameplay Options",
"midnightcontrols.menu.title.general": "General Options",
"midnightcontrols.menu.title.hud": "HUD Options",
"midnightcontrols.menu.title.mappings.string": "Mappings File Editor",
"midnightcontrols.menu.title.touch": "Touch Options",
"midnightcontrols.menu.title.visual": "Appearance Options",
"midnightcontrols.menu.touch_break_delay": "Touch Break Delay",
"midnightcontrols.menu.touch_speed": "Touch Speed",
"midnightcontrols.menu.invert_touch": "Invert Touch Direction",
"midnightcontrols.menu.touch_mode": "Touch Interaction Mode",
"midnightcontrols.menu.touch_transparency": "Touch HUD Transparency",
"midnightcontrols.menu.touch_with_controller": "Touch in Controller mode",
"midnightcontrols.menu.unfocused_input": "Unfocused Input",
"midnightcontrols.menu.unfocused_input.tooltip": "Allows controller input when the window is not focused.",
"midnightcontrols.menu.virtual_mouse": "Virtual Mouse",
"midnightcontrols.menu.virtual_mouse.tooltip": "Enables the virtual mouse, which is useful during splitscreen.",
"midnightcontrols.menu.virtual_mouse.skin": "Virtual Mouse Skin",
"midnightcontrols.menu.hide_cursor": "Hide Normal Mouse Cursor",
"midnightcontrols.menu.hide_cursor.tooltip": "Hides the normal mouse cursor, leaving only the virtual mouse visible.",
"midnightcontrols.narrator.unbound": "Unbound %s",
"midnightcontrols.not_bound": "Not bound",
"midnightcontrols.virtual_mouse.skin.default_light": "Default Light",
"midnightcontrols.virtual_mouse.skin.default_dark": "Default Dark",
"midnightcontrols.virtual_mouse.skin.second_light": "Second Light",
"midnightcontrols.virtual_mouse.skin.second_dark": "Second Dark",
"midnightcontrols.midnightconfig.category.controller": "Controller",
"midnightcontrols.midnightconfig.category.misc": "Miscellaneous",
"midnightcontrols.midnightconfig.category.screens": "Screens",
"midnightcontrols.midnightconfig.category.gameplay": "Gameplay",
"midnightcontrols.midnightconfig.category.touch": "Touch",
"midnightcontrols.midnightconfig.category.visual": "Visual",
"modmenu.descriptionTranslation.midnightcontrols": "Adds controller support and enhanced controls overall.\nForked from LambdaControls, which sadly got discontinued."
}

View File

@@ -0,0 +1,152 @@
{
"key.midnightcontrols.look_down": "Mirar hacia abajo",
"key.midnightcontrols.look_left": "Mirar hacia izquierda",
"key.midnightcontrols.look_right": "Mirar hacia derecha",
"key.midnightcontrols.look_up": "Mirar hacia arriba",
"key.midnightcontrols.ring": "Mostrar anillo de controles",
"midnightcontrols.action.attack": "Atacar",
"midnightcontrols.action.back": "Retroceder",
"midnightcontrols.action.chat": "Abrir chat",
"midnightcontrols.action.drop_item": "Tirar ítem",
"midnightcontrols.action.exit": "Salir",
"midnightcontrols.action.forward": "Avanzar",
"midnightcontrols.action.hit": "Golpear",
"midnightcontrols.action.hotbar_left": "Hotbar a la izquierda",
"midnightcontrols.action.hotbar_right": "Hotbar a la derecha",
"midnightcontrols.action.inventory": "Inventario",
"midnightcontrols.action.jump": "Saltar",
"midnightcontrols.action.left": "Izquierda",
"midnightcontrols.action.pause_game": "Pausar juego",
"midnightcontrols.action.pick_block": "Recoger bloque",
"midnightcontrols.action.pickup": "Recoger",
"midnightcontrols.action.pickup_all": "Recoger todo",
"midnightcontrols.action.place": "Poner",
"midnightcontrols.action.player_list": "Lista de jugadores",
"midnightcontrols.action.quick_move": "Mover items rápidamente",
"midnightcontrols.action.right": "Derecha",
"midnightcontrols.action.screenshot": "Tomar captura de pantalla",
"midnightcontrols.action.sneak": "Agacharse",
"midnightcontrols.action.sprint": "Correr",
"midnightcontrols.action.swap_hands": "Intercambiar manos",
"midnightcontrols.action.toggle_perspective": "Cambiar perspectiva",
"midnightcontrols.action.toggle_smooth_camera": "Cambiar cámara cinematográfica",
"midnightcontrols.action.use": "Usar",
"midnightcontrols.action.zoom": "Zoom",
"midnightcontrols.action.zoom_in": "Aumentar zoom",
"midnightcontrols.action.zoom_out": "Disminuir zoom",
"midnightcontrols.action.zoom_reset": "Restablecer zoom",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Bumper izquierda",
"midnightcontrols.button.right_bumper": "Bumper derecha",
"midnightcontrols.button.back": "Regresar",
"midnightcontrols.button.start": "Iniciar",
"midnightcontrols.button.guide": "Guía",
"midnightcontrols.button.left_thumb": "Joystick izquierda",
"midnightcontrols.button.right_thumb": "Joystick derecha",
"midnightcontrols.button.dpad_up": "Cruceta arriba",
"midnightcontrols.button.dpad_right": "Cruceta derecha",
"midnightcontrols.button.dpad_down": "Cruceta abajo",
"midnightcontrols.button.dpad_left": "Cruceta izquierda",
"midnightcontrols.axis.left_x+": "Izquierda X+",
"midnightcontrols.axis.left_y+": "Izquierda Y+",
"midnightcontrols.axis.right_x+": "Derecha X+",
"midnightcontrols.axis.right_y+": "Derecha Y+",
"midnightcontrols.axis.left_trigger": "Gatillo izquierda",
"midnightcontrols.axis.right_trigger": "Gatillo derecha",
"midnightcontrols.axis.left_x-": "Izquierda X-",
"midnightcontrols.axis.left_y-": "Izquierda Y-",
"midnightcontrols.axis.right_x-": "Derecha X-",
"midnightcontrols.axis.right_y-": "Derecha Y-",
"midnightcontrols.button.unknown": "Desconocido (%d)",
"midnightcontrols.controller.connected": "Controlador %d conectado.",
"midnightcontrols.controller.disconnected": "Controlador %d desconectado.",
"midnightcontrols.controller.mappings.1": "Para configurar las asignaciones del controlador, utilice %s",
"midnightcontrols.controller.mappings.3": "y poner el mapeo de asignaciones en `%s.minecraft/config/gamecontrollerdb.txt%s`.",
"midnightcontrols.controller.mappings.error": "Error al cargar asignaciones.",
"midnightcontrols.controller.mappings.error.write": "Error al escribir asignaciones al archivo.",
"midnightcontrols.controller.mappings.updated": "Asignaciones actualizados!",
"midnightcontrols.controller_type.default": "por defecto",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.switch": "Switch",
"midnightcontrols.controller_type.xbox": "Xbox",
"midnightcontrols.controller_type.steam": "Steam",
"midnightcontrols.controller_type.ouya": "OUYA",
"midnightcontrols.controls_mode.default": "Teclado/Ratón",
"midnightcontrols.controls_mode.controller": "Controlador",
"midnightcontrols.controls_mode.touchscreen": "Pantalla táctil",
"midnightcontrols.hud_side.left": "izquierda",
"midnightcontrols.hud_side.right": "derecha",
"midnightcontrols.menu.analog_movement": "Movimiento analógico",
"midnightcontrols.menu.analog_movement.tooltip": "Habilita el movimiento analógico cuando es posible.",
"midnightcontrols.menu.auto_switch_mode": "Cambio de modo automático",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Si el modo de controles debe cambiarse a Controlador automáticamente si hay uno conectado.",
"midnightcontrols.menu.controller": "Controlador",
"midnightcontrols.menu.controller2": "Segundo controlador",
"midnightcontrols.menu.controller2.tooltip": "Segundo controlador a uso, que permite el soporte de Joy-Cons por ejemplo.",
"midnightcontrols.menu.controller_type": "Tipo de controlador",
"midnightcontrols.menu.controller_type.tooltip": "El tipo de controlador para mostrar los botones correctos.",
"midnightcontrols.menu.controls_mode": "Modo",
"midnightcontrols.menu.controls_mode.tooltip": "El modo de controles.",
"midnightcontrols.menu.fast_block_placing": "Colocación rápida de bloques",
"midnightcontrols.menu.fast_block_placing.tooltip": "Mientras vuela en modo creativo, permite la colocación rápida de bloques dependiendo su velocidad. §cEn algunos servidores, esto podría considerarse como trampa.",
"midnightcontrols.menu.fly_drifting": "Volar a la deriva",
"midnightcontrols.menu.fly_drifting.tooltip": "Mientras vuela, habilita la deriva/inercia de vainilla.",
"midnightcontrols.menu.fly_drifting_vertical": "Volar a la deriva vertical",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Mientras vuela, habilita la deriva/inercia vertical de vainilla.",
"midnightcontrols.menu.hud_enable": "Habilitar HUD",
"midnightcontrols.menu.hud_enable.tooltip": "Alterna el indicador del botón del controlador en pantalla.",
"midnightcontrols.menu.hud_side": "Lado de HUD",
"midnightcontrols.menu.hud_side.tooltip": "La posición del HUD.",
"midnightcontrols.menu.invert_right_x_axis": "Invertir derecha X",
"midnightcontrols.menu.invert_right_y_axis": "Invertir derecha Y",
"midnightcontrols.menu.keyboard_controls": "Controles del teclado...",
"midnightcontrols.menu.left_dead_zone": "Zona muerta izquierda",
"midnightcontrols.menu.left_dead_zone.tooltip": "La zona muerta de la palanca analógica izquierda del controlador.",
"midnightcontrols.menu.mappings.open_input_str": "Abrir el editor de archivo de asignaciones",
"midnightcontrols.menu.max_left_x_value": "Valor máximo del eje X izquierdo",
"midnightcontrols.menu.max_left_x_value.tooltip": "Cambia lo que el mod considera el valor más alto para el eje X izquierdo. Útil si su eje no usa el rango completo y parece lento.",
"midnightcontrols.menu.max_left_y_value": "Valor máximo del eje Y izquierdo",
"midnightcontrols.menu.max_left_y_value.tooltip": "Cambia lo que el mod considera el valor más alto para el eje Y izquierdo. Útil si su eje no usa el rango completo y parece lento.",
"midnightcontrols.menu.max_right_x_value": "Valor máximo del eje X derecho",
"midnightcontrols.menu.max_right_x_value.tooltip": "Cambia lo que el mod considera el valor más alto para el eje X derecho. Útil si su eje no usa el rango completo y parece lento.",
"midnightcontrols.menu.max_right_y_value": "Valor máximo del eje Y derecho",
"midnightcontrols.menu.max_right_y_value.tooltip": "Cambia lo que el mod considera el valor más alto para el eje Y derecho. Útil si su eje no usa el rango completo y parece lento.",
"midnightcontrols.menu.mouse_speed": "Velocidad del ratón",
"midnightcontrols.menu.mouse_speed.tooltip": "La velocidad del ratón emulada del controlador.",
"midnightcontrols.menu.reacharound.horizontal": "Colocación de bloque frontal",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Habilita la colocación del bloque frontal, §cpodría considerarse trampa en algunos servidores§r.",
"midnightcontrols.menu.reacharound.vertical": "Alcance vertical",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Habilita el alcance vertical, §cpodría considerarse trampa en algunos servidores§r.",
"midnightcontrols.menu.reload_controller_mappings": "Recargar asignaciones de controlador",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Vuelve a cargar el archivo de asignaciones del controlador.",
"midnightcontrols.menu.right_dead_zone": "Zona muerta derecha",
"midnightcontrols.menu.right_dead_zone.tooltip": "La zona muerta de la palanca analógica derecha del controlador.",
"midnightcontrols.menu.rotation_speed": "Velocidad de rotación (eje X)",
"midnightcontrols.menu.rotation_speed.tooltip": "La velocidad de rotación de la cámara en modo controlador. (eje X)",
"midnightcontrols.menu.y_axis_rotation_speed": "Velocidad de rotación (eje Y)",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "La velocidad de rotación de la cámara en modo controlador. (eje Y)",
"midnightcontrols.menu.separator.controller": "Controlador",
"midnightcontrols.menu.separator.general": "General",
"midnightcontrols.menu.title": "midnightcontrols - Configuraciones",
"midnightcontrols.menu.title.controller": "Opciones de controlador",
"midnightcontrols.menu.title.controller_controls": "Controles de controlador",
"midnightcontrols.menu.title.gameplay": "Opciones de juego",
"midnightcontrols.menu.title.general": "Opciones generales",
"midnightcontrols.menu.title.hud": "Opciones de HUD",
"midnightcontrols.menu.title.mappings.string": "Editor de archivo de asignaciones",
"midnightcontrols.menu.title.visual": "Opciones de apariencia",
"midnightcontrols.menu.unfocused_input": "Entrada desenfocada",
"midnightcontrols.menu.unfocused_input.tooltip": "Habilita entrada del controlador cuando la ventana no está enfocada.",
"midnightcontrols.menu.virtual_mouse": "Ratón virtual",
"midnightcontrols.menu.virtual_mouse.tooltip": "Habilite el ratón virtual que es útil en el caso de una pantalla dividida.",
"midnightcontrols.menu.virtual_mouse.skin": "Piel de ratón virtual",
"midnightcontrols.narrator.unbound": "Resetear %s",
"midnightcontrols.not_bound": "No ligado",
"midnightcontrols.virtual_mouse.skin.default_light": "Ligera por defecto",
"midnightcontrols.virtual_mouse.skin.default_dark": "Oscura por defecto",
"midnightcontrols.virtual_mouse.skin.second_light": "Ligera segundario",
"midnightcontrols.virtual_mouse.skin.second_dark": "Oscura segundario"
}

View File

@@ -0,0 +1,132 @@
{
"midnightcontrols.midnightconfig.title": "MidnightControls täpsem seadistus",
"key.midnightcontrols.look_down": "Vaata alla",
"key.midnightcontrols.look_left": "Vaata vasakule",
"key.midnightcontrols.look_right": "Vaata paremale",
"key.midnightcontrols.look_up": "Vaata üles",
"key.midnightcontrols.ring": "Kuva juhtnupuringi",
"midnightcontrols.action.attack": "Ründa",
"midnightcontrols.action.back": "Tagasi",
"midnightcontrols.action.chat": "Ava vestlus",
"midnightcontrols.action.drop_item": "Viska ese",
"midnightcontrols.action.exit": "Välju",
"midnightcontrols.action.forward": "Edasi",
"midnightcontrols.action.hit": "Löö",
"midnightcontrols.action.hotbar_left": "Plokiriba vasakule",
"midnightcontrols.action.hotbar_right": "Plokiriba paremale",
"midnightcontrols.action.inventory": "Seljakott",
"midnightcontrols.action.jump": "Hüppa",
"midnightcontrols.action.left": "Vasakule",
"midnightcontrols.action.pause_game": "Mäng pausile",
"midnightcontrols.action.pick_block": "Vali plokk",
"midnightcontrols.action.pickup": "Korja üles",
"midnightcontrols.action.pickup_all": "Korja kõik üles",
"midnightcontrols.action.place": "Aseta",
"midnightcontrols.action.player_list": "Mängijate loend",
"midnightcontrols.action.quick_move": "Kiirliigutus",
"midnightcontrols.action.right": "Paremale",
"midnightcontrols.action.screenshot": "Loo kuvatõmmis",
"midnightcontrols.action.sneak": "Hiili",
"midnightcontrols.action.sprint": "Jookse",
"midnightcontrols.action.swap_hands": "Vaheta käsi",
"midnightcontrols.action.toggle_perspective": "Lülita perspektiivi",
"midnightcontrols.action.toggle_smooth_camera": "Lülita kinemaatilist kaamerat",
"midnightcontrols.action.page_back": "Eelmine leht",
"midnightcontrols.action.page_next": "Järgmine leht",
"midnightcontrols.action.take": "Võta ese",
"midnightcontrols.action.take_all": "Võta kuhi",
"midnightcontrols.action.use": "Kasuta",
"midnightcontrols.action.zoom": "Suumi",
"midnightcontrols.action.zoom_in": "Suurenda suumi",
"midnightcontrols.action.zoom_out": "Vähenda suumi",
"midnightcontrols.action.zoom_reset": "Lähtesta suum",
"midnightcontrols.action.key.emotecraft.fastchoose": "Vali kiirelt liigutus",
"midnightcontrols.action.key.emotecraft.stop": "Peata liigutus",
"midnightcontrols.button.left_bumper": "Vasak külgnupp",
"midnightcontrols.button.right_bumper": "Parem külgnupp",
"midnightcontrols.button.left_thumb": "Vasak pöidlanupp",
"midnightcontrols.button.right_thumb": "Parem pöidlanupp",
"midnightcontrols.button.dpad_up": "DPAD üles",
"midnightcontrols.button.dpad_right": "DPAD paremale",
"midnightcontrols.button.dpad_down": "DPAD alla",
"midnightcontrols.button.dpad_left": "DPAD vasakule",
"midnightcontrols.axis.left_x+": "Vasak X+",
"midnightcontrols.axis.left_y+": "Vasak Y+",
"midnightcontrols.axis.right_x+": "Parem X+",
"midnightcontrols.axis.right_y+": "Parem Y+",
"midnightcontrols.axis.left_trigger": "Vasak käiviti",
"midnightcontrols.axis.right_trigger": "Parem käiviti",
"midnightcontrols.axis.left_x-": "Vasak X-",
"midnightcontrols.axis.left_y-": "Vasak Y-",
"midnightcontrols.axis.right_x-": "Parem X-",
"midnightcontrols.axis.right_y-": "Parem Y-",
"midnightcontrols.button.unknown": "Teadmata (%d)",
"midnightcontrols.controller.tutorial.title": "Mängi mängu oma juhtpuldiga!",
"midnightcontrols.controller.tutorial.description": "Mine %s -> %s -> %s",
"midnightcontrols.controller.connected": "Mängupult %d ühendatud.",
"midnightcontrols.controller.disconnected": "Mängupult %d lahti ühendatud.",
"midnightcontrols.controller.mappings.1": "Mängupuldi vastenduste seadistamiseks palun kasuta %s",
"midnightcontrols.controller.mappings.3": "ning aseta vastendus vastenduste failiredaktorisse.",
"midnightcontrols.controller.mappings.error": "Vastenduste laadimisel esines viga.",
"midnightcontrols.controller.mappings.error.write": "Vastenduste faili kirjutamisel esines viga.",
"midnightcontrols.controller.mappings.updated": "Vastendused uuendatud!",
"midnightcontrols.controller_type.default": "vaikimisi",
"midnightcontrols.controls_mode.default": "klaviatuur/hiir",
"midnightcontrols.controls_mode.controller": "mängupult",
"midnightcontrols.controls_mode.touchscreen": "puuteekraan (arenduses)",
"midnightcontrols.hud_side.left": "vasak",
"midnightcontrols.hud_side.right": "parem",
"midnightcontrols.menu.analog_movement": "Analoogliikumine",
"midnightcontrols.menu.auto_switch_mode": "Vaheta režiimi automaatselt",
"midnightcontrols.menu.controller": "Mängupult",
"midnightcontrols.menu.controller2": "Teine mängupult",
"midnightcontrols.menu.controller_type": "Mängupuldi tüüp",
"midnightcontrols.menu.controls_mode": "Režiim",
"midnightcontrols.menu.double_tap_to_sprint": "Jooksmiseks topeltkoputa",
"midnightcontrols.menu.fast_block_placing": "Kiire plokiasetus",
"midnightcontrols.menu.fly_drifting": "Lennutriivimine",
"midnightcontrols.menu.fly_drifting_vertical": "Vertikaalne lennutriivimine",
"midnightcontrols.menu.hud_enable": "Nähtav liides",
"midnightcontrols.menu.hud_side": "Liidese pool",
"midnightcontrols.menu.invert_right_x_axis": "Pööratud parem X",
"midnightcontrols.menu.invert_right_y_axis": "Pööratud parem Y",
"midnightcontrols.menu.keyboard_controls": "Klaviatuuri juhtnupud...",
"midnightcontrols.menu.left_dead_zone": "Vasak surnud tsoon",
"midnightcontrols.menu.mappings.open_input_str": "Ava vastenduste failiredaktor",
"midnightcontrols.menu.max_left_x_value": "Vasaku X-telje maks. väärtus",
"midnightcontrols.menu.max_left_y_value": "Vasaku Y-telje maks. väärtus",
"midnightcontrols.menu.max_right_x_value": "Parema X-telje maks. väärtus",
"midnightcontrols.menu.max_right_y_value": "Parema Y-telje maks. väärtus",
"midnightcontrols.menu.mouse_speed": "Hiire kiirus",
"midnightcontrols.menu.reacharound.horizontal": "Ploki etteasetus",
"midnightcontrols.menu.reacharound.vertical": "Vertikaalne ümberasetus",
"midnightcontrols.menu.reload_controller_mappings": "Laadi mängupuldi vastendused uuesti",
"midnightcontrols.menu.right_dead_zone": "Parem surnud tsoon",
"midnightcontrols.menu.rotation_speed": "X-telje pöördekiirus",
"midnightcontrols.menu.y_axis_rotation_speed": "Y-telje pöördekiirus",
"midnightcontrols.menu.separator.controller": "Mängupult",
"midnightcontrols.menu.separator.general": "Üldine",
"midnightcontrols.menu.title": "MidnightControls - seaded",
"midnightcontrols.menu.title.controller": "Mängupuldi valikud",
"midnightcontrols.menu.title.controller_controls": "Mängupuldi juhtnupud",
"midnightcontrols.menu.title.gameplay": "Mängukogemuse valikud",
"midnightcontrols.menu.title.general": "Üldised valikud",
"midnightcontrols.menu.title.hud": "Liidese valikud",
"midnightcontrols.menu.title.mappings.string": "Vastenduste failiredaktor",
"midnightcontrols.menu.title.visual": "Välimuse valikud",
"midnightcontrols.menu.unfocused_input": "Fookustamata sisend",
"midnightcontrols.menu.virtual_mouse": "Virtuaalne hiir",
"midnightcontrols.menu.virtual_mouse.skin": "Virtuaalse hiire välimus",
"midnightcontrols.narrator.unbound": "Määramata %s",
"midnightcontrols.not_bound": "määramata",
"midnightcontrols.virtual_mouse.skin.default_light": "vaikimisi hele",
"midnightcontrols.virtual_mouse.skin.default_dark": "vaikimisi tume",
"midnightcontrols.virtual_mouse.skin.second_light": "teine hele",
"midnightcontrols.virtual_mouse.skin.second_dark": "teine tume",
"modmenu.descriptionTranslation.midnightcontrols": "Lisab mängupuldi toe ning täiendab juhtnuppe üldiselt.\nFork loodud LambdaControls-ist, mille toetamine kahjuks lõpetati."
}

View File

@@ -0,0 +1,152 @@
{
"key.midnightcontrols.look_down": "Regarder en bas",
"key.midnightcontrols.look_left": "Regarder à gauche",
"key.midnightcontrols.look_right": "Regarder à droite",
"key.midnightcontrols.look_up": "Regarder en haut",
"key.midnightcontrols.ring": "Affiche l'anneau de contrôle",
"midnightcontrols.action.attack": "Attaquer",
"midnightcontrols.action.back": "Reculer",
"midnightcontrols.action.chat": "Ouvrir le tchat",
"midnightcontrols.action.drop_item": "Jeter l'objet",
"midnightcontrols.action.exit": "Sortir",
"midnightcontrols.action.forward": "Avancer",
"midnightcontrols.action.hit": "Taper",
"midnightcontrols.action.hotbar_left": "Case à gauche de la barre d'action",
"midnightcontrols.action.hotbar_right": "Case à droite de la barre d'action",
"midnightcontrols.action.inventory": "Inventaire",
"midnightcontrols.action.jump": "Sauter",
"midnightcontrols.action.left": "Aller à gauche",
"midnightcontrols.action.pause_game": "Mettre en pause le jeu",
"midnightcontrols.action.pick_block": "Choisir le bloc",
"midnightcontrols.action.pickup": "Prendre",
"midnightcontrols.action.pickup_all": "Prendre tout",
"midnightcontrols.action.place": "Placer",
"midnightcontrols.action.player_list": "Afficher la liste des joueurs",
"midnightcontrols.action.quick_move": "Mouvement rapide",
"midnightcontrols.action.right": "Aller à droite",
"midnightcontrols.action.screenshot": "Prendre une capture d'écran",
"midnightcontrols.action.sneak": "S'accroupir",
"midnightcontrols.action.sprint": "Courir",
"midnightcontrols.action.swap_hands": "Échanger de mains",
"midnightcontrols.action.toggle_perspective": "Changer de point de vue",
"midnightcontrols.action.toggle_smooth_camera": "Basculer en mode cinématique",
"midnightcontrols.action.use": "Utiliser",
"midnightcontrols.action.zoom": "Zoom",
"midnightcontrols.action.zoom_in": "Augmenter le zoom",
"midnightcontrols.action.zoom_out": "Diminuer le zoom",
"midnightcontrols.action.zoom_reset": "Remettre le zoom à zéro",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Gâchette gauche",
"midnightcontrols.button.right_bumper": "Gâchette droite",
"midnightcontrols.button.back": "Retour",
"midnightcontrols.button.start": "Touche Menu",
"midnightcontrols.button.guide": "Guide",
"midnightcontrols.button.left_thumb": "Stick gauche",
"midnightcontrols.button.right_thumb": "Stick droit",
"midnightcontrols.button.dpad_up": "D-Pad haut",
"midnightcontrols.button.dpad_right": "D-Pad droit",
"midnightcontrols.button.dpad_down": "D-Pad bas",
"midnightcontrols.button.dpad_left": "D-Pad gauche",
"midnightcontrols.axis.left_x+": "X+ Gauche",
"midnightcontrols.axis.left_y+": "Y+ Gauche",
"midnightcontrols.axis.right_x+": "X+ Droit",
"midnightcontrols.axis.right_y+": "Y+ Droit",
"midnightcontrols.axis.left_trigger": "Gâchette gauche",
"midnightcontrols.axis.right_trigger": "Gâchette droite",
"midnightcontrols.axis.left_x-": "X- Gauche",
"midnightcontrols.axis.left_y-": "Y- Gauche",
"midnightcontrols.axis.right_x-": "X- Droit",
"midnightcontrols.axis.right_y-": "Y- Droit",
"midnightcontrols.button.unknown": "Inconnu (%d)",
"midnightcontrols.controller.connected": "Manette %d connecté.",
"midnightcontrols.controller.disconnected": "Manette %d déconnecté.",
"midnightcontrols.controller.mappings.1": "Pour configurer les correspondances de la manette, veuillez utiliser %s",
"midnightcontrols.controller.mappings.3": "et copier/coller les correspondances dans l'éditeur de manette.",
"midnightcontrols.controller.mappings.error": "Une erreur est apparue pendant le chargement des manettes.",
"midnightcontrols.controller.mappings.error.write": "Une erreur est apparue pendant l'écriture des manettes au fichier.",
"midnightcontrols.controller.mappings.updated": "Configuration des manettes mise à jour!",
"midnightcontrols.controller_type.default": "default",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.switch": "Switch",
"midnightcontrols.controller_type.xbox": "Xbox",
"midnightcontrols.controller_type.steam": "Steam",
"midnightcontrols.controller_type.ouya": "OUYA",
"midnightcontrols.controls_mode.default": "Clavier/Souris",
"midnightcontrols.controls_mode.controller": "Manette",
"midnightcontrols.controls_mode.touchscreen": "Tactile",
"midnightcontrols.hud_side.left": "gauche",
"midnightcontrols.hud_side.right": "droit",
"midnightcontrols.menu.analog_movement": "Mouvement analogique",
"midnightcontrols.menu.auto_switch_mode": "Changement auto de mode",
"midnightcontrols.menu.controller": "Manette",
"midnightcontrols.menu.controller2": "Deuxième manette",
"midnightcontrols.menu.controller_type": "Type de manette",
"midnightcontrols.menu.controls_mode": "Mode",
"midnightcontrols.menu.fast_block_placing": "Placement rapide de blocs",
"midnightcontrols.menu.fly_drifting": "Inertie de vol",
"midnightcontrols.menu.fly_drifting_vertical": "Inertie verticale de vol",
"midnightcontrols.menu.hud_enable": "Activer le HUD",
"midnightcontrols.menu.hud_side": "Côté du HUD",
"midnightcontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)",
"midnightcontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)",
"midnightcontrols.menu.keyboard_controls": "Contrôles clavier...",
"midnightcontrols.menu.left_dead_zone": "Zone morte axe gauche",
"midnightcontrols.menu.mappings.open_input_str": "Ouvrir l'éditeur de fichier des manettes",
"midnightcontrols.menu.max_left_x_value": "Valeur maximale de l'axe X gauche",
"midnightcontrols.menu.max_left_y_value": "Valeur maximale de l'axe Y gauche",
"midnightcontrols.menu.max_right_x_value": "Valeur maximale de l'axe X droit",
"midnightcontrols.menu.max_right_y_value": "Valeur maximale de l'axe Y droit",
"midnightcontrols.menu.mouse_speed": "Vitesse de la souris",
"midnightcontrols.menu.reacharound.horizontal": "Placement avant de bloc",
"midnightcontrols.menu.reacharound.vertical": "Placement vertical",
"midnightcontrols.menu.reload_controller_mappings": "Recharger les manettes",
"midnightcontrols.menu.right_dead_zone": "Zone morte axe droit",
"midnightcontrols.menu.rotation_speed": "Vitesse de rotation (X)",
"midnightcontrols.menu.y_axis_rotation_speed": "Vitesse de rotation (Y)",
"midnightcontrols.menu.separator.general": "Général",
"midnightcontrols.menu.separator.controller": "Manette",
"midnightcontrols.menu.title": "midnightcontrols - Paramètres",
"midnightcontrols.menu.title.controller": "Options de manettes",
"midnightcontrols.menu.title.controller_controls": "Contrôles de la manette",
"midnightcontrols.menu.title.gameplay": "Options de Gameplay",
"midnightcontrols.menu.title.general": "Options générales",
"midnightcontrols.menu.title.hud": "Options du HUD",
"midnightcontrols.menu.title.mappings.string": "Éditeur de manettes",
"midnightcontrols.menu.title.visual": "Options d'apparences",
"midnightcontrols.menu.unfocused_input": "Entrée en fond",
"midnightcontrols.menu.virtual_mouse": "Souris virtuelle",
"midnightcontrols.menu.virtual_mouse.skin": "Apparence souris virtuelle",
"midnightcontrols.narrator.unbound": "Délier %s",
"midnightcontrols.not_bound": "Non défini",
"midnightcontrols.menu.analog_movement.tooltip": "Active le mouvement analogique si possible.",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Détermine si le mode de contrôle doit automatiquement changer sur Manette si une manette est connectée et inversement.",
"midnightcontrols.menu.controller2.tooltip": "Défini une deuxième manette, utile dans le cas d'utilisation de Joy-Cons.",
"midnightcontrols.menu.controller_type.tooltip": "Le type de contrôle n'influe que sur les boutons affichés.",
"midnightcontrols.menu.controls_mode.tooltip": "Change le mode de contrôle.",
"midnightcontrols.menu.fast_block_placing.tooltip": "Active le placement rapide de blocs en vol.",
"midnightcontrols.menu.fly_drifting.tooltip": "Pendant que le joueur vole, active le glissement Vanilla.",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Pendant que le joueur vole, active le glissement vertical Vanilla.",
"midnightcontrols.menu.hud_enable.tooltip": "Détermine si l'indicateur des buttons de la manette doit être affiché ou non.",
"midnightcontrols.menu.hud_side.tooltip": "Change la position du HUD.",
"midnightcontrols.menu.left_dead_zone.tooltip": "Zone morte de l'axe gauche de la manette.",
"midnightcontrols.menu.max_left_x_value.tooltip": "Change ce que le mod considère comme valeur maximale pour l'axe X gauche. Utile si votre axe n'utilise pas l'entièreté de l'ensemble des valeurs et paraît lent.",
"midnightcontrols.menu.max_left_y_value.tooltip": "Change ce que le mod considère comme valeur maximale pour l'axe Y gauche. Utile si votre axe n'utilise pas l'entièreté de l'ensemble des valeurs et paraît lent.",
"midnightcontrols.menu.max_right_x_value.tooltip": "Change ce que le mod considère comme valeur maximale pour l'axe X droit. Utile si votre axe n'utilise pas l'entièreté de l'ensemble des valeurs et paraît lent.",
"midnightcontrols.menu.max_right_y_value.tooltip": "Change ce que le mod considère comme valeur maximale pour l'axe Y droit. Utile si votre axe n'utilise pas l'entièreté de l'ensemble des valeurs et paraît lent.",
"midnightcontrols.menu.mouse_speed.tooltip": "Change la vitesse de la souris émulée par la manette.",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Active le placement avant de blocs, §cpeut être considérer comme de la triche sur certains serveurs§r.",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Active le placement vertical de blocs, c'est-à-dire de blocs en dessous du bloc sur lequel vous êtes placé, §cpeut être considérer comme de la triche sur certains serveurs§r.",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Recharge le fichier de configuration des manettes.",
"midnightcontrols.menu.right_dead_zone.tooltip": "Zone morte de l'axe droit de la manette.",
"midnightcontrols.menu.rotation_speed.tooltip": "Change la vitesse de rotation de la caméra. (X)",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "Change la vitesse de rotation de la caméra. (Y)",
"midnightcontrols.menu.unfocused_input.tooltip": "Autorise les entrées manette quand la fenêtre n'est pas sélectionnée.",
"midnightcontrols.menu.virtual_mouse.tooltip": "Active la souris virtuelle qui est pratique dans le cas d'un écran partagé.",
"midnightcontrols.virtual_mouse.skin.default_light": "défaut clair",
"midnightcontrols.virtual_mouse.skin.default_dark": "défaut foncé",
"midnightcontrols.virtual_mouse.skin.second_light": "second clair",
"midnightcontrols.virtual_mouse.skin.second_dark": "second foncé"
}

View File

@@ -0,0 +1,152 @@
{
"key.midnightcontrols.look_down": "Regarder en bas",
"key.midnightcontrols.look_left": "Regarder à gauche",
"key.midnightcontrols.look_right": "Regarder à droite",
"key.midnightcontrols.look_up": "Regarder en haut",
"key.midnightcontrols.ring": "Affiche l'anneau de contrôle",
"midnightcontrols.action.attack": "Attaquer",
"midnightcontrols.action.back": "Reculer",
"midnightcontrols.action.chat": "Ouvrir le tchat",
"midnightcontrols.action.drop_item": "Jeter l'objet",
"midnightcontrols.action.exit": "Sortir",
"midnightcontrols.action.forward": "Avancer",
"midnightcontrols.action.hit": "Taper",
"midnightcontrols.action.hotbar_left": "Case à gauche de la barre d'action",
"midnightcontrols.action.hotbar_right": "Case à droite de la barre d'action",
"midnightcontrols.action.inventory": "Inventaire",
"midnightcontrols.action.jump": "Sauter",
"midnightcontrols.action.left": "Aller à gauche",
"midnightcontrols.action.pause_game": "Mettre en pause le jeu",
"midnightcontrols.action.pick_block": "Choisir le bloc",
"midnightcontrols.action.pickup": "Prendre",
"midnightcontrols.action.pickup_all": "Prendre tout",
"midnightcontrols.action.place": "Placer",
"midnightcontrols.action.player_list": "Afficher la liste des joueurs",
"midnightcontrols.action.quick_move": "Mouvement rapide",
"midnightcontrols.action.right": "Aller à droite",
"midnightcontrols.action.screenshot": "Prendre une capture d'écran",
"midnightcontrols.action.sneak": "S'accroupir",
"midnightcontrols.action.sprint": "Courir",
"midnightcontrols.action.swap_hands": "Échanger de mains",
"midnightcontrols.action.toggle_perspective": "Changer de point de vue",
"midnightcontrols.action.toggle_smooth_camera": "Basculer en mode cinématique",
"midnightcontrols.action.use": "Utiliser",
"midnightcontrols.action.zoom": "Zoom",
"midnightcontrols.action.zoom_in": "Augmenter le zoom",
"midnightcontrols.action.zoom_out": "Diminuer le zoom",
"midnightcontrols.action.zoom_reset": "Remettre le zoom à zéro",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Gâchette gauche",
"midnightcontrols.button.right_bumper": "Gâchette droite",
"midnightcontrols.button.back": "Retour",
"midnightcontrols.button.start": "Touche Menu",
"midnightcontrols.button.guide": "Guide",
"midnightcontrols.button.left_thumb": "Stick gauche",
"midnightcontrols.button.right_thumb": "Stick droit",
"midnightcontrols.button.dpad_up": "D-Pad haut",
"midnightcontrols.button.dpad_right": "D-Pad droit",
"midnightcontrols.button.dpad_down": "D-Pad bas",
"midnightcontrols.button.dpad_left": "D-Pad gauche",
"midnightcontrols.axis.left_x+": "X+ Gauche",
"midnightcontrols.axis.left_y+": "Y+ Gauche",
"midnightcontrols.axis.right_x+": "X+ Droit",
"midnightcontrols.axis.right_y+": "Y+ Droit",
"midnightcontrols.axis.left_trigger": "Gâchette gauche",
"midnightcontrols.axis.right_trigger": "Gâchette droite",
"midnightcontrols.axis.left_x-": "X- Gauche",
"midnightcontrols.axis.left_y-": "Y- Gauche",
"midnightcontrols.axis.right_x-": "X- Droit",
"midnightcontrols.axis.right_y-": "Y- Droit",
"midnightcontrols.button.unknown": "Inconnu (%d)",
"midnightcontrols.controller.connected": "Manette %d connecté.",
"midnightcontrols.controller.disconnected": "Manette %d déconnecté.",
"midnightcontrols.controller.mappings.1": "Pour configurer les correspondances de la manette, veuillez utiliser %s",
"midnightcontrols.controller.mappings.3": "et copier/coller les correspondances dans l'éditeur de manette.",
"midnightcontrols.controller.mappings.error": "Une erreur est apparue pendant le chargement des manettes.",
"midnightcontrols.controller.mappings.error.write": "Une erreur est apparue pendant l'écriture des manettes au fichier.",
"midnightcontrols.controller.mappings.updated": "Configuration des manettes mise à jour!",
"midnightcontrols.controller_type.default": "default",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.switch": "Switch",
"midnightcontrols.controller_type.xbox": "Xbox",
"midnightcontrols.controller_type.steam": "Steam",
"midnightcontrols.controller_type.ouya": "OUYA",
"midnightcontrols.controls_mode.default": "Clavier/Souris",
"midnightcontrols.controls_mode.controller": "Manette",
"midnightcontrols.controls_mode.touchscreen": "Tactile",
"midnightcontrols.hud_side.left": "gauche",
"midnightcontrols.hud_side.right": "droit",
"midnightcontrols.menu.analog_movement": "Mouvement analogique",
"midnightcontrols.menu.auto_switch_mode": "Changement auto de mode",
"midnightcontrols.menu.controller": "Manette",
"midnightcontrols.menu.controller2": "Deuxième manette",
"midnightcontrols.menu.controller_type": "Type de manette",
"midnightcontrols.menu.controls_mode": "Mode",
"midnightcontrols.menu.fast_block_placing": "Placement rapide de blocs",
"midnightcontrols.menu.fly_drifting": "Inertie de vol",
"midnightcontrols.menu.fly_drifting_vertical": "Inertie verticale de vol",
"midnightcontrols.menu.hud_enable": "Activer le HUD",
"midnightcontrols.menu.hud_side": "Côté du HUD",
"midnightcontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)",
"midnightcontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)",
"midnightcontrols.menu.keyboard_controls": "Contrôles clavier...",
"midnightcontrols.menu.left_dead_zone": "Zone morte axe gauche",
"midnightcontrols.menu.mappings.open_input_str": "Ouvrir l'éditeur de fichier des manettes",
"midnightcontrols.menu.max_left_x_value": "Valeur maximale de l'axe X gauche",
"midnightcontrols.menu.max_left_y_value": "Valeur maximale de l'axe Y gauche",
"midnightcontrols.menu.max_right_x_value": "Valeur maximale de l'axe X droit",
"midnightcontrols.menu.max_right_y_value": "Valeur maximale de l'axe Y droit",
"midnightcontrols.menu.mouse_speed": "Vitesse de la souris",
"midnightcontrols.menu.reacharound.horizontal": "Placement avant de bloc",
"midnightcontrols.menu.reacharound.vertical": "Placement vertical",
"midnightcontrols.menu.reload_controller_mappings": "Recharger les manettes",
"midnightcontrols.menu.right_dead_zone": "Zone morte axe droit",
"midnightcontrols.menu.rotation_speed": "Vitesse de rotation (X)",
"midnightcontrols.menu.y_axis_rotation_speed": "Vitesse de rotation (Y)",
"midnightcontrols.menu.separator.general": "Général",
"midnightcontrols.menu.separator.controller": "Manette",
"midnightcontrols.menu.title": "midnightcontrols - Paramètres",
"midnightcontrols.menu.title.controller": "Options de manettes",
"midnightcontrols.menu.title.controller_controls": "Contrôles de la manette",
"midnightcontrols.menu.title.gameplay": "Options de Gameplay",
"midnightcontrols.menu.title.general": "Options générales",
"midnightcontrols.menu.title.hud": "Options du HUD",
"midnightcontrols.menu.title.mappings.string": "Éditeur de manettes",
"midnightcontrols.menu.title.visual": "Options d'apparences",
"midnightcontrols.menu.unfocused_input": "Entrée en fond",
"midnightcontrols.menu.virtual_mouse": "Souris virtuelle",
"midnightcontrols.menu.virtual_mouse.skin": "Apparence souris virtuelle",
"midnightcontrols.narrator.unbound": "Délier %s",
"midnightcontrols.not_bound": "Non défini",
"midnightcontrols.menu.analog_movement.tooltip": "Active le mouvement analogique si possible.",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Détermine si le mode de contrôle doit automatiquement changer sur Manette si une manette est connectée et inversement.",
"midnightcontrols.menu.controller2.tooltip": "Défini une deuxième manette, utile dans le cas d'utilisation de Joy-Cons.",
"midnightcontrols.menu.controller_type.tooltip": "Le type de contrôle n'influe que sur les boutons affichés.",
"midnightcontrols.menu.controls_mode.tooltip": "Change le mode de contrôle.",
"midnightcontrols.menu.fast_block_placing.tooltip": "Active le placement rapide de blocs en vol.",
"midnightcontrols.menu.fly_drifting.tooltip": "Pendant que le joueur vole, active le glissement Vanilla.",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Pendant que le joueur vole, active le glissement vertical Vanilla.",
"midnightcontrols.menu.hud_enable.tooltip": "Détermine si l'indicateur des buttons de la manette doit être affiché ou non.",
"midnightcontrols.menu.hud_side.tooltip": "Change la position du HUD.",
"midnightcontrols.menu.left_dead_zone.tooltip": "Zone morte de l'axe gauche de la manette.",
"midnightcontrols.menu.max_left_x_value.tooltip": "Change ce que le mod considère comme valeur maximale pour l'axe X gauche. Utile si votre axe n'utilise pas l'entièreté de l'ensemble des valeurs et paraît lent.",
"midnightcontrols.menu.max_left_y_value.tooltip": "Change ce que le mod considère comme valeur maximale pour l'axe Y gauche. Utile si votre axe n'utilise pas l'entièreté de l'ensemble des valeurs et paraît lent.",
"midnightcontrols.menu.max_right_x_value.tooltip": "Change ce que le mod considère comme valeur maximale pour l'axe X droit. Utile si votre axe n'utilise pas l'entièreté de l'ensemble des valeurs et paraît lent.",
"midnightcontrols.menu.max_right_y_value.tooltip": "Change ce que le mod considère comme valeur maximale pour l'axe Y droit. Utile si votre axe n'utilise pas l'entièreté de l'ensemble des valeurs et paraît lent.",
"midnightcontrols.menu.mouse_speed.tooltip": "Change la vitesse de la souris émulée par la manette.",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Active le placement avant de blocs, §cpeut être considérer comme de la triche sur certains serveurs§r.",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Active le placement vertical de blocs, c'est-à-dire de blocs en dessous du bloc sur lequel vous êtes placé, §cpeut être considérer comme de la triche sur certains serveurs§r.",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Recharge le fichier de configuration des manettes.",
"midnightcontrols.menu.right_dead_zone.tooltip": "Zone morte de l'axe droit de la manette.",
"midnightcontrols.menu.rotation_speed.tooltip": "Change la vitesse de rotation de la caméra. (X)",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "Change la vitesse de rotation de la caméra. (Y)",
"midnightcontrols.menu.unfocused_input.tooltip": "Autorise les entrées manette quand la fenêtre n'est pas sélectionnée.",
"midnightcontrols.menu.virtual_mouse.tooltip": "Active la souris virtuelle qui est pratique dans le cas d'un écran partagé.",
"midnightcontrols.virtual_mouse.skin.default_light": "défaut clair",
"midnightcontrols.virtual_mouse.skin.default_dark": "défaut foncé",
"midnightcontrols.virtual_mouse.skin.second_light": "second clair",
"midnightcontrols.virtual_mouse.skin.second_dark": "second foncé"
}

View File

@@ -0,0 +1,203 @@
{
"midnightcontrols.midnightconfig.title": "Configurazione Avanzata MidnightControls",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_LIGHT": "Default Chiaro",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_DARK": "Default Scuro",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_LIGHT": "Chiaro Secondario",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_DARK": "Scuro secondario",
"midnightcontrols.midnightconfig.enum.ControllerType.DEFAULT": "Default",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSHOCK": "DualShock",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSENSE": "DualSense",
"midnightcontrols.midnightconfig.enum.ControllerType.SWITCH": "Controller Switch/Wii",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX": "Controller Xbox One/Series",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX_360": "Controller Xbox 360",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_CONTROLLER": "Controller Steam",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_DECK": "Steam Deck",
"midnightcontrols.midnightconfig.enum.ControllerType.OUYA": "Controller OUYA",
"midnightcontrols.midnightconfig.enum.ControllerType.NUMBERED": "Controller Numerato",
"midnightcontrols.midnightconfig.enum.ControlsMode.DEFAULT": "Tastiera/Mouse",
"midnightcontrols.midnightconfig.enum.ControlsMode.CONTROLLER": "Controller",
"midnightcontrols.midnightconfig.enum.ControlsMode.TOUCHSCREEN": "Touchscreen (WIP)",
"midnightcontrols.midnightconfig.enum.HudSide.LEFT": "Sinistra",
"midnightcontrols.midnightconfig.enum.HudSide.RIGHT": "Destra",
"key.midnightcontrols.look_down": "Guarda in basso",
"key.midnightcontrols.look_left": "Guarda a sinistra",
"key.midnightcontrols.look_right": "Guarda a destra",
"key.midnightcontrols.look_up": "Guarda sopra",
"key.midnightcontrols.ring": "Apri Tasti non impostati",
"midnightcontrols.action.attack": "Attacca",
"midnightcontrols.action.back": "Indietro",
"midnightcontrols.action.chat": "Apri Chat",
"midnightcontrols.action.controls_ring": "Apri Tasti non impostati",
"midnightcontrols.action.debug_screen": "Apri HUD di Debug (F3)",
"midnightcontrols.action.drop_item": "Rilascia oggetto",
"midnightcontrols.action.exit": "Esci",
"midnightcontrols.action.forward": "Avanti",
"midnightcontrols.action.hit": "Colpisci",
"midnightcontrols.action.hotbar_left": "Hotbar sinistra",
"midnightcontrols.action.hotbar_right": "Hotbar destra",
"midnightcontrols.action.inventory": "Inventario",
"midnightcontrols.action.jump": "Salta",
"midnightcontrols.action.left": "Sinistra",
"midnightcontrols.action.pause_game": "Pausa il gioco",
"midnightcontrols.action.pick_block": "Prendi Blocco",
"midnightcontrols.action.pickup": "Raccogli",
"midnightcontrols.action.pickup_all": "Raccogli Tutto",
"midnightcontrols.action.place": "Piazza",
"midnightcontrols.action.player_list": "Lista Giocatori",
"midnightcontrols.action.quick_move": "Muovi veloce",
"midnightcontrols.action.right": "Destra",
"midnightcontrols.action.screenshot": "Fai uno Screenshot",
"midnightcontrols.action.slot_up": "Muovi slot sopra",
"midnightcontrols.action.slot_down": "Muovi slot sotto",
"midnightcontrols.action.slot_left": "Muovi slot a sinistra",
"midnightcontrols.action.slot_right": "Muovi slot a destra",
"midnightcontrols.action.sneak": "Accovaccia",
"midnightcontrols.action.sprint": "Corri",
"midnightcontrols.action.swap_hands": "Scambia le mani",
"midnightcontrols.action.toggle_perspective": "Cambia la prospettiva",
"midnightcontrols.action.toggle_smooth_camera": "Cambia Camera Cinematica",
"midnightcontrols.action.page_back": "Pagina precedente",
"midnightcontrols.action.page_next": "Pagina Successica",
"midnightcontrols.action.tab_back": "Tab Precedente",
"midnightcontrols.action.tab_next": "Tab Successiva",
"midnightcontrols.action.take": "Prendi Item",
"midnightcontrols.action.take_all": "Prendi Stack",
"midnightcontrols.action.use": "Usa",
"midnightcontrols.action.zoom": "Zoom",
"midnightcontrols.action.zoom_in": "Aumenta Zoom",
"midnightcontrols.action.zoom_out": "Diminuisci Zoom",
"midnightcontrols.action.zoom_reset": "Resetta Zoom",
"midnightcontrols.action.emi_page_left": "Pagina Precedente",
"midnightcontrols.action.emi_page_right": "Pagina Successiva",
"midnightcontrols.category.emi": "EMI",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Bumper Destro",
"midnightcontrols.button.right_bumper": "Bumper Sinistro",
"midnightcontrols.button.back": "Indietro",
"midnightcontrols.button.start": "Avvia",
"midnightcontrols.button.guide": "Guida",
"midnightcontrols.button.left_thumb": "Pollice Sinistro",
"midnightcontrols.button.right_thumb": "Pollice Destro",
"midnightcontrols.button.dpad_up": "DPAD Sopra",
"midnightcontrols.button.dpad_right": "DPAD Destra",
"midnightcontrols.button.dpad_down": "DPAD Sotto",
"midnightcontrols.button.dpad_left": "DPAD Sinistra",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "Sinistra X+",
"midnightcontrols.axis.left_y+": "Sinistra Y+",
"midnightcontrols.axis.right_x+": "Destra X+",
"midnightcontrols.axis.right_y+": "Destra Y+",
"midnightcontrols.axis.left_trigger": "Trigger Sinistro",
"midnightcontrols.axis.right_trigger": "Trigger Destro",
"midnightcontrols.axis.left_x-": "Sinistra X-",
"midnightcontrols.axis.left_y-": "Sinistra Y-",
"midnightcontrols.axis.right_x-": "Destra X-",
"midnightcontrols.axis.right_y-": "Destra Y-",
"midnightcontrols.button.unknown": "Sconosciuto",
"midnightcontrols.controller.tutorial.title": "Gioca il gioco con un Controller!",
"midnightcontrols.controller.tutorial.description": "Vai a %s -> %s -> %s",
"midnightcontrols.controller.connected": "Controller %d connesso.",
"midnightcontrols.controller.disconnected": "Controller %d disconnesso",
"midnightcontrols.controller.mappings.1": "Per configurare le mappature dei controller, per favore usa %s",
"midnightcontrols.controller.mappings.3": "e incolla le mappature nel file editor.",
"midnightcontrols.controller.mappings.error": "Errore durante il caricamento delle mappature",
"midnightcontrols.controller.mappings.error.write": "Errore durante la scrittura delle mappature su file",
"midnightcontrols.controller.mappings.updated": "Aggiornate le mappature!",
"midnightcontrols.controller_type.default": "Default",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.dualsense": "DualSense",
"midnightcontrols.controller_type.switch": "Controller Switch/Wii",
"midnightcontrols.controller_type.xbox": "Controller Xbox One/Series",
"midnightcontrols.controller_type.xbox_360": "Controller Xbox 360",
"midnightcontrols.controller_type.steam_controller": "Controller Steam",
"midnightcontrols.controller_type.steam_deck": "Steam Deck",
"midnightcontrols.controller_type.ouya": "Controller OUYA",
"midnightcontrols.controller_type.numbered": "Controller Numerato",
"midnightcontrols.controls_mode.default": "Tastiera/Mouse",
"midnightcontrols.controls_mode.controller": "Controller",
"midnightcontrols.controls_mode.touchscreen": "Touchscreen (WIP)",
"midnightcontrols.hud_side.left": "Left",
"midnightcontrols.hud_side.right": "Destra",
"midnightcontrols.menu.analog_movement": "Movimento Analogo",
"midnightcontrols.menu.auto_switch_mode": "Modalità Cambio Automatico",
"midnightcontrols.menu.controller": "Controller",
"midnightcontrols.menu.controller2": "Controller Secondario",
"midnightcontrols.menu.controller_toggle_sneak": "Attiva/Disattiva Accovacciamento su Controller",
"midnightcontrols.menu.controller_toggle_sprint": "Attiva/Disattiva Correre su Controller",
"midnightcontrols.menu.controller_type": "Tipo di Controller",
"midnightcontrols.menu.controls_mode": "Modalità",
"midnightcontrols.menu.double_tap_to_sprint": "Doppio Tap per Sprintare",
"midnightcontrols.menu.fast_block_placing": "Veloce Piazzamento di Blocchi",
"midnightcontrols.menu.fly_drifting": "Drift in Volo",
"midnightcontrols.menu.fly_drifting_vertical": "Drift in Volo Verticale",
"midnightcontrols.menu.hud_enable": "Attiva HUD",
"midnightcontrols.menu.hud_side": "Lato HUD",
"midnightcontrols.menu.invert_right_x_axis": "Inverti Destro X",
"midnightcontrols.menu.invert_right_y_axis": "Inverti Destro Y",
"midnightcontrols.menu.keyboard_controls": "Controlli da Tastiera...",
"midnightcontrols.menu.left_dead_zone": "Zona Morta dello Stick Sinistro",
"midnightcontrols.menu.mappings.open_input_str": "Apri il File Editor delle Mappature",
"midnightcontrols.menu.max_left_x_value": "Asse X Sinistro Massimo Valore",
"midnightcontrols.menu.max_left_y_value": "Asse Y Sinistro Massimo Valore",
"midnightcontrols.menu.max_right_x_value": "Asse Destro X Massimo Valore",
"midnightcontrols.menu.max_right_y_value": "Asse Destro Y Massimo Valore",
"midnightcontrols.menu.mouse_speed": "Velocità Mouse",
"midnightcontrols.menu.reacharound.horizontal": "Piazza blocchi Davanti",
"midnightcontrols.menu.reacharound.vertical": "Reach Verticale",
"midnightcontrols.menu.reload_controller_mappings": "Ricarica Mappature del Controller",
"midnightcontrols.menu.right_dead_zone": "Zona Morta Stick Destro",
"midnightcontrols.menu.rotation_speed": "Velocità di rotazione dell'Asse X",
"midnightcontrols.menu.y_axis_rotation_speed": "Velocità di rotazione dell'Asse Y",
"midnightcontrols.menu.separate_controller_profile": "Profilo Controller Separato",
"midnightcontrols.menu.separator.controller": "Controller",
"midnightcontrols.menu.separator.general": "Generale",
"midnightcontrols.menu.title": "MidnightControls - Impostazioni",
"midnightcontrols.menu.title.controller": "Opzioni Controller",
"midnightcontrols.menu.title.controller_controls": "Tasti Controller",
"midnightcontrols.menu.title.gameplay": "Opzioni del Gameplay",
"midnightcontrols.menu.title.general": "Impostazioni Generali",
"midnightcontrols.menu.title.hud": "Opzioni della HUD",
"midnightcontrols.menu.title.mappings.string": "Editor di file delle Mappature",
"midnightcontrols.menu.title.visual": "Aspetto",
"midnightcontrols.menu.unfocused_input": "Input non a fuoco",
"midnightcontrols.menu.virtual_mouse": "Mouse Virtuale",
"midnightcontrols.menu.virtual_mouse.skin": "Aspetto Mouse Virtuale",
"midnightcontrols.narrator.unbound": "Disimpostato %s",
"midnightcontrols.not_bound": "Non Impostato",
"midnightcontrols.menu.analog_movement.tooltip": "Quando possibile, attiva movimento analogo.",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Quando la modalità controlli dovrebbe passare a Controller automaticamente se uno è connesso",
"midnightcontrols.menu.controller2.tooltip": "Secondo controller da usare, che permette (per esempio) Supporto dei Joy-Cons.",
"midnightcontrols.menu.controller_type.tooltip": "Il tipo di controller che stai usando (usato per mostrare i pulsanti corretti)",
"midnightcontrols.menu.controls_mode.tooltip": "Modalita controlli.",
"midnightcontrols.menu.double_tap_to_sprint.tooltip": "Attiva quando il tasto \"Cammina Avanti\" fa correre il giocatore con doppio tap velocemente",
"midnightcontrols.menu.fast_block_placing.tooltip": "Quando si vola in creativa, attiva il movimento veloce dei blocchi in base alla tua velocità. §cSu alcuni server potrebbe essere considerato un cheat.§r",
"midnightcontrols.menu.fly_drifting.tooltip": "Quando si vola, attiva L'inerzia del Vanilla",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Quando si vola, attiva l'inerzia verticale Vanilla",
"midnightcontrols.menu.hud_enable.tooltip": "Attiva gli indicatori dei pulsanti dei controller sullo schermo.",
"midnightcontrols.menu.hud_side.tooltip": "La posizione della HUD",
"midnightcontrols.menu.left_dead_zone.tooltip": "La zona morta per lo stick sinistro del controller.",
"midnightcontrols.menu.max_left_x_value.tooltip": "Cambia cosa la mod considera il più alto valore per l'asse X sinistro. Utile se il tuo asse non usa il range massimo e sembra lento",
"midnightcontrols.menu.max_left_y_value.tooltip": "Cambia cosa la mod considera il più alto valore per l'asse Y sinistro. Utile se il tuo asse non usa il range massimo e sembra lento",
"midnightcontrols.menu.max_right_x_value.tooltip": "Cambia cosa la mod considera il più alto valore per l'asse X Destro. Utile se il tuo asse non usa il range massimo e sembra lento.",
"midnightcontrols.menu.max_right_y_value.tooltip": "Cambia cosa la mod considera il più alto valore per l'asse Y Destro. Utile se il tuo asse non usa il range massimo e sembra lento.",
"midnightcontrols.menu.mouse_speed.tooltip": "La velocità del controller emulato.",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Attiva il piazzare blocchi davanti, Potrebbe essere considerato un cheat su alcuni server.",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Attiva il reach verticale, potrebbe essere considerato cheating su alcuni server",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Riavvia il file dlle mappature del controller",
"midnightcontrols.menu.right_dead_zone.tooltip": "La zona morta per lo stick destro del controller",
"midnightcontrols.menu.rotation_speed.tooltip": "L'asse X della velocità di rotazione della camera im modalità controller",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "L'asse Y della velocità di rotazione della camera im modalità controller",
"midnightcontrols.menu.unfocused_input.tooltip": "Permette imput dei controller quando la finestra non è messa a fuoco",
"midnightcontrols.menu.virtual_mouse.tooltip": "Attiva il mouse virtuale, che può essere utile nello schermo diviso.",
"midnightcontrols.virtual_mouse.skin.default_light": "Chiaro Default",
"midnightcontrols.virtual_mouse.skin.default_dark": "Scuro Default",
"midnightcontrols.virtual_mouse.skin.second_light": "Chiaro Secondario",
"midnightcontrols.virtual_mouse.skin.second_dark": "Scuro Secondario",
"modmenu.descriptionTranslation.midnightcontrols": "Aggiunge supporto e controlli migliorati in generale. \\nProveniente da LambdaControls, che è stato rimosso."
}

View File

@@ -0,0 +1,178 @@
{
"midnightcontrols.midnightconfig.title": "MidnightControls 고급 설정",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_LIGHT": "기본 밝게",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_DARK": "기본 어둡게",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_LIGHT": "두 번째 밝게",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_DARK": "두 번째 어둡게",
"midnightcontrols.midnightconfig.enum.ControllerType.DEFAULT": "기본값값",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSHOCK": "듀얼쇼크",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSENSE": "듀얼센스",
"midnightcontrols.midnightconfig.enum.ControllerType.SWITCH": "Switch/Wii 컨트롤러",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX": "엑스박스 원/시리즈 컨트롤러",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX_360": "엑스박스 360 컨트롤러",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_CONTROLLER": "스팀 컨트롤러",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_DECK": "스팀 덱",
"midnightcontrols.midnightconfig.enum.ControllerType.OUYA": "OUYA 컨트롤러",
"midnightcontrols.midnightconfig.enum.ControllerType.NUMBERED": "숫자 컨트롤러",
"midnightcontrols.midnightconfig.enum.ControlsMode.DEFAULT": "키보드/마우스",
"midnightcontrols.midnightconfig.enum.ControlsMode.CONTROLLER": "컨트롤러",
"midnightcontrols.midnightconfig.enum.ControlsMode.TOUCHSCREEN": "터치스크린 (미완성)",
"midnightcontrols.midnightconfig.enum.HudSide.LEFT": "왼쪽",
"midnightcontrols.midnightconfig.enum.HudSide.RIGHT": "오른쪽",
"key.midnightcontrols.look_down": "아래쪽 보기",
"key.midnightcontrols.look_left": "왼쪽 보기",
"key.midnightcontrols.look_right": "오른쪽 보기",
"key.midnightcontrols.look_up": "위쪽 보기",
"key.midnightcontrols.ring": "컨트롤 링 표시",
"midnightcontrols.action.attack": "공격",
"midnightcontrols.action.back": "뒤로",
"midnightcontrols.action.chat": "채팅 열기",
"midnightcontrols.action.debug_screen": "디버그 HUD 열기 (F3)",
"midnightcontrols.action.drop_item": "아이템 버리기",
"midnightcontrols.action.exit": "종료",
"midnightcontrols.action.forward": "앞으로",
"midnightcontrols.action.hit": "때리기",
"midnightcontrols.action.hotbar_left": "단축 바 왼쪽",
"midnightcontrols.action.hotbar_right": "단축 바 오른쪽",
"midnightcontrols.action.inventory": "보관함",
"midnightcontrols.action.jump": "점프",
"midnightcontrols.action.left": "왼쪽",
"midnightcontrols.action.pause_game": "게임 일시 중지",
"midnightcontrols.action.pick_block": "블록 집기",
"midnightcontrols.action.pickup": "집기",
"midnightcontrols.action.pickup_all": "모두 집기",
"midnightcontrols.action.place": "놓기",
"midnightcontrols.action.player_list": "플레이어 목록",
"midnightcontrols.action.quick_move": "빠른 이동",
"midnightcontrols.action.right": "오른쪽",
"midnightcontrols.action.screenshot": "스크린샷 찍기",
"midnightcontrols.action.sneak": "은신",
"midnightcontrols.action.sprint": "달리기",
"midnightcontrols.action.swap_hands": "손 전환",
"midnightcontrols.action.toggle_perspective": "시점 토글",
"midnightcontrols.action.toggle_smooth_camera": "시네마틱 카메라 토글",
"midnightcontrols.action.use": "사용",
"midnightcontrols.action.zoom": "확대/축소",
"midnightcontrols.action.zoom_in": "확대",
"midnightcontrols.action.zoom_out": "축소",
"midnightcontrols.action.zoom_reset": "확대/축소 초기화",
"midnightcontrols.action.key.emotecraft.fastchoose": "이모트 빠른 선택",
"midnightcontrols.action.key.emotecraft.stop": "이모트 중지",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "왼쪽 범퍼",
"midnightcontrols.button.right_bumper": "오른쪽 범퍼",
"midnightcontrols.button.back": "뒤로",
"midnightcontrols.button.start": "시작",
"midnightcontrols.button.guide": "가이드",
"midnightcontrols.button.left_thumb": "왼쪽 엄지",
"midnightcontrols.button.right_thumb": "오른쪽 엄지",
"midnightcontrols.button.dpad_up": "DPAD 위",
"midnightcontrols.button.dpad_right": "DPAD 오른쪽",
"midnightcontrols.button.dpad_down": "DPAD 아래",
"midnightcontrols.button.dpad_left": "DPAD 왼쪽",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "왼쪽 X+",
"midnightcontrols.axis.left_y+": "왼쪽 Y+",
"midnightcontrols.axis.right_x+": "오른쪽 X+",
"midnightcontrols.axis.right_y+": "오른쪽 Y+",
"midnightcontrols.axis.left_trigger": "왼쪽 트리거",
"midnightcontrols.axis.right_trigger": "오른쪽 트리거",
"midnightcontrols.axis.left_x-": "왼쪽 X-",
"midnightcontrols.axis.left_y-": "왼쪽 Y-",
"midnightcontrols.axis.right_x-": "오른쪽 X-",
"midnightcontrols.axis.right_y-": "오른쪽 Y-",
"midnightcontrols.button.unknown": "알 수 없음 (%d)",
"midnightcontrols.controller.connected": "컨트롤러 %d 연결됨.",
"midnightcontrols.controller.disconnected": "컨트롤러 %d 연결 끊김.",
"midnightcontrols.controller.mappings.1": "컨트롤러 매핑을 설정하려면, %s을 사용하세요",
"midnightcontrols.controller.mappings.3": "그리고 매핑 파일 편집기에 매핑을 붙여 넣으세요.",
"midnightcontrols.controller.mappings.error": "매핑을 불러오던 중 오류.",
"midnightcontrols.controller.mappings.error.write": "파일에 매핑 작성 중 오류.",
"midnightcontrols.controller.mappings.updated": "매핑 업데이트됨!",
"midnightcontrols.controller_type.default": "기본",
"midnightcontrols.controller_type.dualshock": "듀얼쇼크",
"midnightcontrols.controller_type.switch": "스위치",
"midnightcontrols.controller_type.xbox": "엑스박스",
"midnightcontrols.controller_type.steam": "스팀",
"midnightcontrols.controller_type.ouya": "우야",
"midnightcontrols.controls_mode.default": "키보드/마우스",
"midnightcontrols.controls_mode.controller": "컨트롤러",
"midnightcontrols.controls_mode.touchscreen": "터치스크린",
"midnightcontrols.hud_side.left": "왼쪽",
"midnightcontrols.hud_side.right": "오른쪽",
"midnightcontrols.menu.analog_movement": "아날로그 움직임",
"midnightcontrols.menu.auto_switch_mode": "자동 전환 모드",
"midnightcontrols.menu.controller": "컨트롤러",
"midnightcontrols.menu.controller2": "보조 컨트롤러",
"midnightcontrols.menu.controller_type": "컨트롤러 종류",
"midnightcontrols.menu.controls_mode": "모드",
"midnightcontrols.menu.fast_block_placing": "빠른 블록 배치",
"midnightcontrols.menu.fly_drifting": "비행 관성",
"midnightcontrols.menu.fly_drifting_vertical": "비행 수직 관성",
"midnightcontrols.menu.hud_enable": "HUD 사용",
"midnightcontrols.menu.hud_side": "HUD 측면",
"midnightcontrols.menu.invert_right_x_axis": "오른쪽 X 반전",
"midnightcontrols.menu.invert_right_y_axis": "오른쪽 Y 반전",
"midnightcontrols.menu.keyboard_controls": "키보드 컨트롤...",
"midnightcontrols.menu.left_dead_zone": "왼쪽 데드 존",
"midnightcontrols.menu.mappings.open_input_str": "매핑 파일 편집기 열기",
"midnightcontrols.menu.max_left_x_value": "왼쪽 X 축 최댓값",
"midnightcontrols.menu.max_left_y_value": "왼쪽 Y 축 최댓값",
"midnightcontrols.menu.max_right_x_value": "오른쪽 X 축 최댓값",
"midnightcontrols.menu.max_right_y_value": "오른쪽 Y 축 최댓값",
"midnightcontrols.menu.mouse_speed": "마우스 속도",
"midnightcontrols.menu.reacharound.horizontal": "블록 전면 배치",
"midnightcontrols.menu.reacharound.vertical": "블록 수직 배치",
"midnightcontrols.menu.reload_controller_mappings": "컨트롤러 매핑 다시 불러오기",
"midnightcontrols.menu.right_dead_zone": "오른쪽 데드 존",
"midnightcontrols.menu.rotation_speed": "회전 속도",
"midnightcontrols.menu.separator.controller": "컨트롤러",
"midnightcontrols.menu.separator.general": "일반",
"midnightcontrols.menu.title": "MidnightControls - 설정",
"midnightcontrols.menu.title.controller": "컨트롤러 옵션",
"midnightcontrols.menu.title.controller_controls": "컨트롤러 제어",
"midnightcontrols.menu.title.gameplay": "게임플레이 옵션",
"midnightcontrols.menu.title.general": "일반 옵션",
"midnightcontrols.menu.title.hud": "HUD 옵션",
"midnightcontrols.menu.title.mappings.string": "매핑 파일 편집기",
"midnightcontrols.menu.title.visual": "외형 옵션",
"midnightcontrols.menu.unfocused_input": "언포커스 인풋",
"midnightcontrols.menu.virtual_mouse": "가상 마우스",
"midnightcontrols.menu.virtual_mouse.skin": "가상 마우스 스킨",
"midnightcontrols.narrator.unbound": "%s 해제됨",
"midnightcontrols.not_bound": "바인딩되지 않음",
"midnightcontrols.menu.analog_movement.tooltip": "가능한 경우 아날로그 움직임을 사용합니다.",
"midnightcontrols.menu.auto_switch_mode.tooltip": "컨트롤러가 연결되면 자동으로 컨트롤 모드가 컨트롤러로 전환됩니다.",
"midnightcontrols.menu.controller2.tooltip": "보조 컨트롤러를 사용하려면 Joy-Cons 지원을 허용하세요.",
"midnightcontrols.menu.controller_type.tooltip": "올바른 버튼을 표시할 컨트롤러 종류입니다.",
"midnightcontrols.menu.controls_mode.tooltip": "컨트롤 모드.",
"midnightcontrols.menu.fast_block_placing.tooltip": "크리에이티브 모드에서 비행하는 동안 사용자 속도에 따른 빠른 블록 배치를 사용합니다. §c일부 서버에선 치팅 행위로 취급될 수 있습니다.",
"midnightcontrols.menu.fly_drifting.tooltip": "비행하는 동안 바닐라처럼 관성을 적용합니다.",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "비행하는 동안 바닐라처럼 수직 관성을 적용합니다.",
"midnightcontrols.menu.hud_enable.tooltip": "화면 컨트롤러 버튼 표시기 토글",
"midnightcontrols.menu.hud_side.tooltip": "HUD 위치.",
"midnightcontrols.menu.left_dead_zone.tooltip": "컨트롤러 왼쪽 아날로그 스틱 데드 존.",
"midnightcontrols.menu.max_left_x_value.tooltip": "모드가 인식하는 왼쪽 X 축 최댓값을 변경합니다. 사용자의 축이 전체 범위를 사용하지 않고 느리게 보일 경우 유용합니다.",
"midnightcontrols.menu.max_left_y_value.tooltip": "모드가 인식하는 왼쪽 Y 축 최댓값을 변경합니다. 사용자의 축이 전체 범위를 사용하지 않고 느리게 보일 경우 유용합니다.",
"midnightcontrols.menu.max_right_x_value.tooltip": "모드가 인식하는 오른쪽 X 축 최댓값을 변경합니다. 사용자의 축이 전체 범위를 사용하지 않고 느리게 보일 경우 유용합니다.",
"midnightcontrols.menu.max_right_y_value.tooltip": "모드가 인식하는 오른쪽 Y 축 최댓값을 변경합니다. 사용자의 축이 전체 범위를 사용하지 않고 느리게 보일 경우 유용합니다.",
"midnightcontrols.menu.mouse_speed.tooltip": "컨트롤러의 에뮬레이트된 마우스 속도.",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "블록 전면 배치를 사용합니다. §c일부 서버에선 치팅 행위로 취급될 수 있습니다§r.",
"midnightcontrols.menu.reacharound.vertical.tooltip": "블록 수직 배치를 사용합니다. 블록 아래에 다른 블록을 배치할 수 있습니다. §c일부 서버에선 치팅 행위로 취급될 수 있습니다§r.",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "컨트롤러 매핑 파일을 다시 불러옵니다.",
"midnightcontrols.menu.right_dead_zone.tooltip": "컨트롤러 오른쪽 아날로그 스틱 데드 존",
"midnightcontrols.menu.rotation_speed.tooltip": "컨트롤러 모드에서 적용되는 카메라 회전 속도.",
"midnightcontrols.menu.unfocused_input.tooltip": "창이 활성화된 상태가 아니여도 컨트롤러 입력 허용.",
"midnightcontrols.menu.virtual_mouse.tooltip": "분할 화면에선 도움 되는 가상 마우스를 사용합니다.",
"midnightcontrols.virtual_mouse.skin.default_light": "기본 밝게",
"midnightcontrols.virtual_mouse.skin.default_dark": "기본 어둡게",
"midnightcontrols.virtual_mouse.skin.second_light": "두 번째 밝게",
"midnightcontrols.virtual_mouse.skin.second_dark": "두 번째 어둡게",
"modmenu.descriptionTranslation.midnightcontrols": "컨트롤러 제어 및 전반적인 향상된 제어 기능을 추가합니다.\n안타깝게도 업데이트가 끊긴 LambdaControls의 Fork 버전입니다."
}

View File

@@ -0,0 +1,177 @@
{
"midnightcontrols.midnightconfig.title": "Расширенная Конфигурация MidnightControls",
"key.midnightcontrols.look_down": "Смотреть Вниз",
"key.midnightcontrols.look_left": "Смотреть Налево",
"key.midnightcontrols.look_right": "Смотреть Направо",
"key.midnightcontrols.look_up": "Смотрeть Вверх",
"key.midnightcontrols.ring": "Показать Кольцо Управления",
"midnightcontrols.action.attack": "Атаковать",
"midnightcontrols.action.back": "Назад",
"midnightcontrols.action.chat": "Открыть Чат",
"midnightcontrols.action.debug_screen": "Открыть Отладку (F3)",
"midnightcontrols.action.drop_item": "Выбросить Предмет",
"midnightcontrols.action.exit": "Выйти",
"midnightcontrols.action.forward": "Вперед",
"midnightcontrols.action.hit": "Ударить",
"midnightcontrols.action.hotbar_left": "Панель Быстрого Доступа Налево",
"midnightcontrols.action.hotbar_right": "Панель Быстрого Доступа Направо",
"midnightcontrols.action.inventory": "Инвентарь",
"midnightcontrols.action.jump": "Прыжок",
"midnightcontrols.action.left": "Налево",
"midnightcontrols.action.pause_game": "Приостановить игру",
"midnightcontrols.action.pick_block": "Выбор Блока",
"midnightcontrols.action.pickup": "Взять Половину",
"midnightcontrols.action.pickup_all": "Взять Всё",
"midnightcontrols.action.place": "Поставить",
"midnightcontrols.action.player_list": "Лист Игроков",
"midnightcontrols.action.quick_move": "Переложить",
"midnightcontrols.action.right": "Направо",
"midnightcontrols.action.screenshot": "Сделать Скриншот",
"midnightcontrols.action.slot_up": "Двинуть На Слот Вверх",
"midnightcontrols.action.slot_down": "Двинуть На Слот Вниз",
"midnightcontrols.action.slot_left": "Двинуть На Слот Влево",
"midnightcontrols.action.slot_right": "Двинуть На Слот Вправо",
"midnightcontrols.action.sneak": "Красться",
"midnightcontrols.action.sprint": "Бег",
"midnightcontrols.action.swap_hands": "Поменять Руку",
"midnightcontrols.action.toggle_perspective": "Переключить Перспективу",
"midnightcontrols.action.toggle_smooth_camera": "Переключить Кинематографическую Камеру",
"midnightcontrols.action.page_back": "Предыдущая Страница",
"midnightcontrols.action.page_next": "Следующая Страница",
"midnightcontrols.action.tab_back": "Предыдущая Секция",
"midnightcontrols.action.tab_next": "Следующая Секция",
"midnightcontrols.action.take": "Взять Предмет",
"midnightcontrols.action.take_all": "Взять Стак",
"midnightcontrols.action.use": "Использовать",
"midnightcontrols.action.zoom": "Приблизить",
"midnightcontrols.action.zoom_in": "Увеличить Масштаб",
"midnightcontrols.action.zoom_out": "Уменьшить Масштаб",
"midnightcontrols.action.zoom_reset": "Сбросить Масштаб",
"midnightcontrols.action.emi_page_left": "Предыдущая Страница",
"midnightcontrols.action.emi_page_right": "Следующая Страница",
"midnightcontrols.category.emi": "ЕМИ",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Левый Бампер",
"midnightcontrols.button.right_bumper": "Правый Бампер",
"midnightcontrols.button.back": "Назад",
"midnightcontrols.button.start": "Старт",
"midnightcontrols.button.guide": "Гайд Кнопка",
"midnightcontrols.button.left_thumb": "Левый Стик",
"midnightcontrols.button.right_thumb": "Правый Стик",
"midnightcontrols.button.dpad_up": "Крестовина Вверх",
"midnightcontrols.button.dpad_right": "Крестовина Вправо",
"midnightcontrols.button.dpad_down": "Крестовина Вниз",
"midnightcontrols.button.dpad_left": "Крестовина Влево",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "Левый Стик X+",
"midnightcontrols.axis.left_y+": "Левый Стик Y+",
"midnightcontrols.axis.right_x+": "Правый Стик X+",
"midnightcontrols.axis.right_y+": "Правый Стик Y+",
"midnightcontrols.axis.left_trigger": "Левый Триггер",
"midnightcontrols.axis.right_trigger": "Правый Триггер",
"midnightcontrols.axis.left_x-": "Левый Стик X-",
"midnightcontrols.axis.left_y-": "Левый Стик Y-",
"midnightcontrols.axis.right_x-": "Правый Стик X-",
"midnightcontrols.axis.right_y-": "Правый Стик Y-",
"midnightcontrols.button.unknown": "Неизвестный (%d)",
"midnightcontrols.controller.connected": "Контроллер %d Был Присоединен.",
"midnightcontrols.controller.disconnected": "Контроллер %d Был Отключён.",
"midnightcontrols.controller.mappings.1": "Чтобы настроить раскладку контроллера, используйте %s",
"midnightcontrols.controller.mappings.3": "и вставте ваш итог в редактор файлов раскладок.",
"midnightcontrols.controller.mappings.error": "Ошибка при загрузке раскладки.",
"midnightcontrols.controller.mappings.error.write": "Ошибка при записи раскладки в файл.",
"midnightcontrols.controller.mappings.updated": "Раскладка обновлена!",
"midnightcontrols.controller_type.default": "По стандарту",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.dualsense": "DualSense",
"midnightcontrols.controller_type.switch": "Switch",
"midnightcontrols.controller_type.xbox": "Xbox",
"midnightcontrols.controller_type.xbox_360": "Xbox 360",
"midnightcontrols.controller_type.steam_controller": "Steam Controller",
"midnightcontrols.controller_type.steam_deck": "Steam Deck",
"midnightcontrols.controller_type.ouya": "OUYA",
"midnightcontrols.controls_mode.default": "Клавиатура/Мышь",
"midnightcontrols.controls_mode.controller": "Контроллер",
"midnightcontrols.controls_mode.touchscreen": "Сенсорный Экран (Разраб.)",
"midnightcontrols.hud_side.left": "Больше Налево",
"midnightcontrols.hud_side.right": "Больше Направо",
"midnightcontrols.menu.analog_movement": "Аналоговое Движение",
"midnightcontrols.menu.auto_switch_mode": "Автоматическое Переключение",
"midnightcontrols.menu.controller": "Контроллер",
"midnightcontrols.menu.controller2": "Дополнительный Контроллер",
"midnightcontrols.menu.controller_type": "Тип контроллера",
"midnightcontrols.menu.controls_mode": "Тип",
"midnightcontrols.menu.double_tap_to_sprint": "Дважды Отвести Левый Стик, Чтобы Начать Бежать",
"midnightcontrols.menu.fast_block_placing": "Быстрая Поставка Блоков",
"midnightcontrols.menu.fly_drifting": "Инерция При Полёте",
"midnightcontrols.menu.fly_drifting_vertical": "Вертикальная Инерция При Полёте",
"midnightcontrols.menu.hud_enable": "Включить Иконоки Контроллера",
"midnightcontrols.menu.hud_side": "Расположение Интерфейса",
"midnightcontrols.menu.invert_right_x_axis": "Инвертировать Правый X",
"midnightcontrols.menu.invert_right_y_axis": "Инвертировать Правый Y",
"midnightcontrols.menu.keyboard_controls": "Клавиатурная Настройка...",
"midnightcontrols.menu.left_dead_zone": "Мертвая Зона Левого Стика",
"midnightcontrols.menu.mappings.open_input_str": "Открыть Редактор Файлов Раскладок",
"midnightcontrols.menu.max_left_x_value": "Максимальное Значение Ось Левого X",
"midnightcontrols.menu.max_left_y_value": "Максимальное Значение Ось Левого Y",
"midnightcontrols.menu.max_right_x_value": "Максимальное Значение Ось Правого X",
"midnightcontrols.menu.max_right_y_value": "Максимальное Значение Ось Правого Y",
"midnightcontrols.menu.mouse_speed": "Скорость Курсора",
"midnightcontrols.menu.reacharound.horizontal": "Размещение Переднего Блока",
"midnightcontrols.menu.reacharound.vertical": "Вертикальный Охват",
"midnightcontrols.menu.reload_controller_mappings": "Перезагрузить Раскладку Контроллера",
"midnightcontrols.menu.right_dead_zone": "Мертвая Зона Правого Стика",
"midnightcontrols.menu.rotation_speed": "Скорость Вращения По Оси X",
"midnightcontrols.menu.y_axis_rotation_speed": "Скорость Вращения По Оси Y",
"midnightcontrols.menu.separator.controller": "Контроллер",
"midnightcontrols.menu.separator.general": "Общие",
"midnightcontrols.menu.title": "MidnightControls - Настройки",
"midnightcontrols.menu.title.controller": "Настройки Контроллера",
"midnightcontrols.menu.title.controller_controls": "Управление Контроллером",
"midnightcontrols.menu.title.gameplay": "Во Время Игры Настройки",
"midnightcontrols.menu.title.general": "Общие Настройки",
"midnightcontrols.menu.title.hud": "Настройка Интерфейса",
"midnightcontrols.menu.title.mappings.string": "Редактор Файлов Раскладок",
"midnightcontrols.menu.title.visual": "Настройка Внешнего Вида",
"midnightcontrols.menu.unfocused_input": "Несфокусированный Ввод",
"midnightcontrols.menu.virtual_mouse": "Виртуальныя Мышка",
"midnightcontrols.menu.virtual_mouse.skin": "Дизайн Виртуальной Мышки",
"midnightcontrols.narrator.unbound": "Освобождённый %s",
"midnightcontrols.not_bound": "Не Назначанно",
"midnightcontrols.menu.analog_movement.tooltip": "Включает аналоговое движение, когда это возможно.",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Автоматическое переключение на новый, только подключенный контроллер.",
"midnightcontrols.menu.controller2.tooltip": "Дополнительный контроллер, например для Джой-конов.",
"midnightcontrols.menu.controller_type.tooltip": "Тип контроллера, чтобы правильно сопоставить иконки кнопок.",
"midnightcontrols.menu.controls_mode.tooltip": "Тип контроля над игрой.",
"midnightcontrols.menu.double_tap_to_sprint.tooltip": "При двойном перемещением на левый стик, персонаж начинает бежать.",
"midnightcontrols.menu.fast_block_placing.tooltip": "Во время полета в творческом режиме позволяет быстро размещать блоки в зависимости от вашей скорости. §cНа некоторых серверах это может расцениваться как читерство.§r",
"midnightcontrols.menu.fly_drifting.tooltip": "Во время полета включает ванильную инерцию.",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Во время полета включает ванильную вертикальную инерцию.",
"midnightcontrols.menu.hud_enable.tooltip": "Переключает видение иконок кнопок контроллера на экране.",
"midnightcontrols.menu.hud_side.tooltip": "Расположение интерфейса в определённую сторону.",
"midnightcontrols.menu.left_dead_zone.tooltip": "Мертвая зона для левого аналогового стика контроллера.",
"midnightcontrols.menu.max_left_x_value.tooltip": "Изменяет то, что мод в итоге считает максимальным значением для левой оси X. Полезно, если ваша ось не использует весь диапазон и кажется медленной.",
"midnightcontrols.menu.max_left_y_value.tooltip": "Изменяет то, что мод в итоге считает максимальным значением для левой оси Y. Полезно, если ваша ось не использует весь диапазон и кажется медленной.",
"midnightcontrols.menu.max_right_x_value.tooltip": "Изменяет то, что мод в итоге считает максимальным значением для правой оси X. Полезно, если ваша ось не использует весь диапазон и кажется медленной.",
"midnightcontrols.menu.max_right_y_value.tooltip": "Изменяет то, что мод в итоге считает максимальным значением для правой оси Y. Полезно, если ваша ось не использует весь диапазон и кажется медленной.",
"midnightcontrols.menu.mouse_speed.tooltip": "Скорость мыши, эмулируемая контроллером.",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Позволяет размещать передний блок перед собой. §cМожет рассматриваться как читерство на некоторых серверах§r.",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Обеспечивает вертикальный охват. §cМожет рассматриваться как мошенничество на некоторых серверах§r.",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Перезагружает файл раскладки контроллеров.",
"midnightcontrols.menu.right_dead_zone.tooltip": "Мертвая зона для правого аналогового стика контроллера.",
"midnightcontrols.menu.rotation_speed.tooltip": "Скорость вращения камеры по оси X в режиме контроллера.",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "Скорость вращения камеры по оси Y в режиме контроллера.",
"midnightcontrols.menu.unfocused_input.tooltip": "Разрешить ввод с контроллера, даже если игра на сфокусирована.",
"midnightcontrols.menu.virtual_mouse.tooltip": "Включить виртуальную мышь, очень полезно при игре двоём на одном компьютере.",
"midnightcontrols.virtual_mouse.skin.default_light": "Обычный Светлый",
"midnightcontrols.virtual_mouse.skin.default_dark": "Обычный Тёмный",
"midnightcontrols.virtual_mouse.skin.second_light": "Дополнительный Светлый",
"midnightcontrols.virtual_mouse.skin.second_dark": "Дополнительный Тёмный",
"modmenu.descriptionTranslation.midnightcontrols": "Добавление поддержки контроллера и улучшенние элементов управления в целом.\nРазветвлен от LambdaControls, поддержка которого, к сожалению, прекращена.."
}

View File

@@ -0,0 +1,136 @@
{
"key.midnightcontrols.look_down": "Aşağı bak",
"key.midnightcontrols.look_left": "Sola bak",
"key.midnightcontrols.look_right": "Sağa bak",
"key.midnightcontrols.look_up": "Yukarıya bak",
"key.midnightcontrols.ring": "Kontroller halkasını göster",
"midnightcontrols.action.attack": "Saldırma/Kazma",
"midnightcontrols.action.back": "Geri",
"midnightcontrols.action.chat": "Sohbeti Açma",
"midnightcontrols.action.drop_item": "Seçili Eşyayı Bırakma",
"midnightcontrols.action.exit": ık",
"midnightcontrols.action.forward": "İleri",
"midnightcontrols.action.hit": "Vurma",
"midnightcontrols.action.hotbar_left": "Sık Kullanılanlar'da sola git",
"midnightcontrols.action.hotbar_right": "Sık Kullanılanlar'da sağa git",
"midnightcontrols.action.inventory": "Envanter",
"midnightcontrols.action.jump": "Zıplama",
"midnightcontrols.action.left": "Sol",
"midnightcontrols.action.pause_game": "Oyunu durdur",
"midnightcontrols.action.pick_block": "Blok Seçme",
"midnightcontrols.action.pickup": "Al",
"midnightcontrols.action.pickup_all": "Hepsini Al",
"midnightcontrols.action.place": "Yerleştir",
"midnightcontrols.action.player_list": "Oyuncu Listesi",
"midnightcontrols.action.quick_move": "Hızlı Hareket",
"midnightcontrols.action.right": "Sağ",
"midnightcontrols.action.screenshot": "Ekran Görüntüsü Alma",
"midnightcontrols.action.sneak": "Eğilme",
"midnightcontrols.action.sprint": "Koşma",
"midnightcontrols.action.swap_hands": "Ögeyi Elden Ele Değiştir",
"midnightcontrols.action.toggle_perspective": "Perspektifi Değiştirme",
"midnightcontrols.action.toggle_smooth_camera": "Sinemaitk Kameraya Geçme",
"midnightcontrols.action.use": "Kullanma",
"midnightcontrols.action.zoom": "Büyütme",
"midnightcontrols.action.zoom_in": "Büyütmeyi Arttır",
"midnightcontrols.action.zoom_out": "Büyütmeyi Azalt",
"midnightcontrols.action.zoom_reset": "Büyütmeyi Sıfırla",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Sol yassı tuş",
"midnightcontrols.button.right_bumper": "Sağ yassı tuş",
"midnightcontrols.button.back": "Back",
"midnightcontrols.button.start": "Start",
"midnightcontrols.button.guide": "Guide",
"midnightcontrols.button.left_thumb": "Sol çubuk",
"midnightcontrols.button.right_thumb": "Sağ çubuk",
"midnightcontrols.button.dpad_up": "Yukarı yön tuşu",
"midnightcontrols.button.dpad_right": "Sağ yön tuşu",
"midnightcontrols.button.dpad_down": "Aşağı yön tuşu",
"midnightcontrols.button.dpad_left": "Sol yön tuşu",
"midnightcontrols.axis.left_x+": "Sol X+",
"midnightcontrols.axis.left_y+": "Sol Y+",
"midnightcontrols.axis.right_x+": "Sağ X+",
"midnightcontrols.axis.right_y+": "Sağ Y+",
"midnightcontrols.axis.left_trigger": "Sol tetik tuşu",
"midnightcontrols.axis.right_trigger": "Sağ tetik tuşu",
"midnightcontrols.axis.left_x-": "Sol X-",
"midnightcontrols.axis.left_y-": "Sol Y-",
"midnightcontrols.axis.right_x-": "Sağ X-",
"midnightcontrols.axis.right_y-": "Sağ Y-",
"midnightcontrols.button.unknown": "Bilinmeyen (%d)",
"midnightcontrols.controller.connected": "%d oyun kolu bağlandı.",
"midnightcontrols.controller.disconnected": "%d oyun kolunun bağlantısı kesildi.",
"midnightcontrols.controller.mappings.1": "Oyun kolunun tuş eşleştirme ayarını yapacaksanız, lütfen sunu kullanın: %sSDL2 Gamepad Tool%s",
"midnightcontrols.controller.mappings.3": "ve eşleştirme dosyasını da şuraya koyun: `%s.minecraft/config/gamecontrollerdb.txt%s`.",
"midnightcontrols.controller.mappings.error": "Eşleştirme yüklenirken hata oluştu.",
"midnightcontrols.controller.mappings.error.write": "Dosyaya eşleştirme yazılırken hata oluştu.",
"midnightcontrols.controller.mappings.updated": "Eşleştirme güncellendi!",
"midnightcontrols.controller_type.default": "varsayılan",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.switch": "Switch",
"midnightcontrols.controller_type.xbox": "Xbox",
"midnightcontrols.controller_type.steam": "Steam",
"midnightcontrols.controller_type.ouya": "OUYA",
"midnightcontrols.controls_mode.default": "Klavye/Fare",
"midnightcontrols.controls_mode.controller": "Oyun Kolu",
"midnightcontrols.controls_mode.touchscreen": "Dokunmatik Ekran",
"midnightcontrols.hud_side.left": "sol",
"midnightcontrols.hud_side.right": "sağ",
"midnightcontrols.menu.auto_switch_mode": "Otomatik Değiştirme Modu",
"midnightcontrols.menu.controller": "Oyun Kolu",
"midnightcontrols.menu.controller2": "İkincil Oyun Kolu",
"midnightcontrols.menu.controller_type": "Oyun Kolu Türü",
"midnightcontrols.menu.controls_mode": "Mod",
"midnightcontrols.menu.dead_zone": "Ölü Bölge",
"midnightcontrols.menu.fast_block_placing": "Hızlı Blok Yerleştirme",
"midnightcontrols.menu.fly_drifting": "Kayarak Uç",
"midnightcontrols.menu.fly_drifting_vertical": "Dikey uçuşta kayaaak git",
"midnightcontrols.menu.hud_enable": "HUD'u Etkinleştir",
"midnightcontrols.menu.hud_side": "HUD Yanı",
"midnightcontrols.menu.invert_right_x_axis": "Sağ X'i Terse Çevir",
"midnightcontrols.menu.invert_right_y_axis": "Sağ Y'i Terse Çevir.",
"midnightcontrols.menu.keyboard_controls": "Klavye Kontrolleri...",
"midnightcontrols.menu.mappings.open_input_str": "Eşleştirme Dosya Editörünü Aç",
"midnightcontrols.menu.mouse_speed": "Fare Hızı",
"midnightcontrols.menu.reacharound.horizontal": "Alt Öne Blok Koyma",
"midnightcontrols.menu.reacharound.vertical": "En Alta Blok Koyma",
"midnightcontrols.menu.reload_controller_mappings": "Oyun Kolu Eşleştirmelerini Yenile",
"midnightcontrols.menu.rotation_speed": "Dönme Hızı (X)",
"midnightcontrols.menu.y_axis_rotation_speed": "Dönme Hızı (Y)",
"midnightcontrols.menu.title": "midnightcontrols - Ayarlar",
"midnightcontrols.menu.title.controller": "Oyun Kolu Seçenekleri",
"midnightcontrols.menu.title.controller_controls": "Oyun Kolu Kontrolleri",
"midnightcontrols.menu.title.gameplay": "Oynanış Seçenekleri",
"midnightcontrols.menu.title.general": "Genel Seçenekler",
"midnightcontrols.menu.title.hud": "HUD Seçenekleri",
"midnightcontrols.menu.title.mappings.string": "Eşleştirme Dosya Editörü",
"midnightcontrols.menu.unfocused_input": "Odaklanmamış Giriş Aygıtı",
"midnightcontrols.menu.virtual_mouse": "Sanal Fare",
"midnightcontrols.menu.virtual_mouse.skin": "Sanal Fare Görünümü",
"midnightcontrols.narrator.unbound": "%s Atanmamış",
"midnightcontrols.not_bound": "Tuş ataması yok",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Eğer bir tanesi bağlandıysa, kontrol modu Oyun Kolu olarak değişmeli.",
"midnightcontrols.menu.controller2.tooltip": "Kullanılacak ikinci oyun kolu, örnek olarak Joy-Con desteği de mümkün.",
"midnightcontrols.menu.controller_type.tooltip": "Doğru tuşları göstermesi için oyun kolu türü.",
"midnightcontrols.menu.controls_mode.tooltip": "Kontrol Modu",
"midnightcontrols.menu.fast_block_placing.tooltip": "Yaratıcı modda uçarken, hızına bağlı olarak hızlı blok koymayı etkinleştirir. §cBazı sunucular bunun hile olduğunu düşünebilir.",
"midnightcontrols.menu.fly_drifting.tooltip": "Uçarken, Vanilla'daki gibi ani duruşlarda kayma efektini etkinleştirir.",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Yukarı/aşağı uçarken, Vanilla'daki gibi ani duruşlarda kayma efektini etkinleştirir.",
"midnightcontrols.menu.hud_enable.tooltip": "Ekranın üstünde oyun kolu tuşu göstergesini açar/kapatır.",
"midnightcontrols.menu.hud_side.tooltip": "HUD'un konumu",
"midnightcontrols.menu.mouse_speed.tooltip": "Oyun kolunun taklit edilen fare hızı.",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Hızlı blok koymayı etkinleştirir. §cBazı sunucular bunun hile olduğunu düşünebilir.§r.",
"midnightcontrols.menu.reacharound.vertical.tooltip": "En alta blok koymayı etkinleştirir. §cBazı sunucular bunun hile olduğunu düşünebilir.§r.",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Oyun kolu için eşleştirme dosyasını yeniler.",
"midnightcontrols.menu.rotation_speed.tooltip": "Oyun kolu modunda olan kamera dönme hızı (X)",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "Oyun kolu modunda olan kamera dönme hızı (Y)",
"midnightcontrols.menu.unfocused_input.tooltip": "Oyun penceresinde değilken oyun kolu girişine izine verir.",
"midnightcontrols.menu.virtual_mouse.tooltip": "Sanal fareyi etkinleştirir. Çift ekran oynanılacağı zaman işe yarar.",
"midnightcontrols.virtual_mouse.skin.default_light": "Varsayılan Aydınlık Tema",
"midnightcontrols.virtual_mouse.skin.default_dark": "Varsayılan Karanlık Tema",
"midnightcontrols.virtual_mouse.skin.second_light": "İkincil Aydınlık Tema",
"midnightcontrols.virtual_mouse.skin.second_dark": "İkincil Karanlık Tema"
}

View File

@@ -0,0 +1,184 @@
{
"midnightcontrols.midnightconfig.title": "Розширена конфігурація MidnightControls",
"key.midnightcontrols.look_down": "Дивитись вниз",
"key.midnightcontrols.look_left": "Дивитись ліворуч",
"key.midnightcontrols.look_right": "Дивитись направо",
"key.midnightcontrols.look_up": "Дивитись вгору",
"key.midnightcontrols.ring": "Показати кільце управління",
"midnightcontrols.action.attack": "Атакувати",
"midnightcontrols.action.back": "Назад",
"midnightcontrols.action.chat": "Відкрити чат",
"midnightcontrols.action.debug_screen": "Відкрити налагодження (F3)",
"midnightcontrols.action.drop_item": "Викинути предмет",
"midnightcontrols.action.exit": "Вийти",
"midnightcontrols.action.forward": "Вперед",
"midnightcontrols.action.hit": "Вдарити",
"midnightcontrols.action.hotbar_left": "Панель швидкого доступу ліворуч",
"midnightcontrols.action.hotbar_right": "Панель швидкого доступу праворуч",
"midnightcontrols.action.inventory": "Інвентар",
"midnightcontrols.action.jump": "Стрибок",
"midnightcontrols.action.left": "Наліво",
"midnightcontrols.action.pause_game": "Зупинити гру",
"midnightcontrols.action.pick_block": "Вибір блоку",
"midnightcontrols.action.pickup": "Взяти половину",
"midnightcontrols.action.pickup_all": "Взяти все",
"midnightcontrols.action.place": "Поставити",
"midnightcontrols.action.player_list": "Аркуш гравців",
"midnightcontrols.action.quick_move": "Перекласти",
"midnightcontrols.action.right": "Направо",
"midnightcontrols.action.screenshot": "Зробити скріншот",
"midnightcontrols.action.slot_up": "Двинути на слот вгору",
"midnightcontrols.action.slot_down": "Повернути на слот вниз",
"midnightcontrols.action.slot_left": "Двинути на слот вліво",
"midnightcontrols.action.slot_right": "Повернути на слот вправо",
"midnightcontrols.action.sneak": "Крастися",
"midnightcontrols.action.sprint": "Біг",
"midnightcontrols.action.swap_hands": "Змінити руку",
"midnightcontrols.action.toggle_perspective": "Переключити перспективу",
"midnightcontrols.action.toggle_smooth_camera": "Переключити кінематографічну камеру",
"midnightcontrols.action.page_back": "Попередня сторінка",
"midnightcontrols.action.page_next": "Наступна сторінка",
"midnightcontrols.action.tab_back": "Попередня секція",
"midnightcontrols.action.tab_next": "Наступна секція",
"midnightcontrols.action.take": "Взяти предмет",
"midnightcontrols.action.take_all": "Взяти стак",
"midnightcontrols.action.use": "Використовувати",
"midnightcontrols.action.zoom": "Наблизити",
"midnightcontrols.action.zoom_in": "Збільшити масштаб",
"midnightcontrols.action.zoom_out": "Зменшити масштаб",
"midnightcontrols.action.zoom_reset": "Скинути масштаб",
"midnightcontrols.action.emi_page_left": "Попередня сторінка",
"midnightcontrols.action.emi_page_right": "Наступна сторінка",
"midnightcontrols.category.emi": "ЕМІ",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Лівий бампер",
"midnightcontrols.button.right_bumper": "Правий бампер",
"midnightcontrols.button.back": "Назад",
"midnightcontrols.button.start": "Старт",
"midnightcontrols.button.guide": "Гайд кнопка",
"midnightcontrols.button.left_thumb": "Лівий стік",
"midnightcontrols.button.right_thumb": "Правий стік",
"midnightcontrols.button.dpad_up": "Хрестовина вг.",
"midnightcontrols.button.dpad_right": "Хрестовина пр.",
"midnightcontrols.button.dpad_down": "Хрестовина вн.",
"midnightcontrols.button.dpad_left": "Хрестовина лів.",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "Лівий cтік X+",
"midnightcontrols.axis.left_y+": "Лівий cтік Y+",
"midnightcontrols.axis.right_x+": "Правий cтік X+",
"midnightcontrols.axis.right_y+": "Правий cтік Y+",
"midnightcontrols.axis.left_trigger": "Лівий тригер",
"midnightcontrols.axis.right_trigger": "Правий тригер",
"midnightcontrols.axis.left_x-": "Лівий стік X-",
"midnightcontrols.axis.left_y-": "Лівий стік Y-",
"midnightcontrols.axis.right_x-": "Правий стік X-",
"midnightcontrols.axis.right_y-": "Правий стік Y-",
"midnightcontrols.button.unknown": "Невідомий (%d)",
"midnightcontrols.controller.connected": "Контролер %d був приєднаний.",
"midnightcontrols.controller.disconnected": "Контролер %d вимкнений.",
"midnightcontrols.controller.mappings.1": "Щоб налаштувати розкладку контролера, використовуйте %s",
"midnightcontrols.controller.mappings.3": "і вставте ваш підсумок у редактор файлів розкладок.",
"midnightcontrols.controller.mappings.error": "Помилка під час завантаження розкладки.",
"midnightcontrols.controller.mappings.error.write": "Помилка під час запису розкладки у файл.",
"midnightcontrols.controller.mappings.updated": "Розкладка оновлена!",
"midnightcontrols.controller_type.default": "За стандартом",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.dualsense": "DualSense",
"midnightcontrols.controller_type.switch": "Switch",
"midnightcontrols.controller_type.xbox": "Xbox",
"midnightcontrols.controller_type.xbox_360": "Xbox 360",
"midnightcontrols.controller_type.steam_controller": "Steam Controller",
"midnightcontrols.controller_type.steam_deck": "Steam Deck",
"midnightcontrols.controller_type.ouya": "OUYA",
"midnightcontrols.controls_mode.default": "Клавіатура/Миша",
"midnightcontrols.controls_mode.controller": "Контролер",
"midnightcontrols.controls_mode.touchscreen": "Сенсорний екран (Розроб.)",
"midnightcontrols.hud_side.left": "Більше ліворуч",
"midnightcontrols.hud_side.right": "Більше направо",
"midnightcontrols.menu.analog_movement": "Аналоговий рух",
"midnightcontrols.menu.auto_switch_mode": "Автоматичне перемикання",
"midnightcontrols.menu.controller": "Контролер",
"midnightcontrols.menu.controller2": "Додатковий контролер",
"midnightcontrols.menu.controller_type": "Тип контролера",
"midnightcontrols.menu.controls_mode": "Тип",
"midnightcontrols.menu.double_tap_to_sprint": "Двічі відвести лівий стік, щоб почати бігти",
"midnightcontrols.menu.fast_block_placing": "Швидка постачання блоків",
"midnightcontrols.menu.fly_drifting": "Інерція при польоті",
"midnightcontrols.menu.fly_drifting_vertical": "Вертикальна інерція при польоті",
"midnightcontrols.menu.hud_enable": "Увімкнути іконоки контролера",
"midnightcontrols.menu.hud_side": "Розташування інтерфейсу",
"midnightcontrols.menu.invert_right_x_axis": "Інвертувати прав. X",
"midnightcontrols.menu.invert_right_y_axis": "Інвертувати прав. Y",
"midnightcontrols.menu.keyboard_controls": "Клавіатурне налаштування...",
"midnightcontrols.menu.left_dead_zone": "Мертва зона лівого стіка",
"midnightcontrols.menu.mappings.open_input_str": "Відкрити редактор файлів розкладок",
"midnightcontrols.menu.max_left_x_value": "Максимальне значення вісь лівого X",
"midnightcontrols.menu.max_left_y_value": "Максимальне значення вісь лівого Y",
"midnightcontrols.menu.max_right_x_value": "Максимальне значення вісь правого X",
"midnightcontrols.menu.max_right_y_value": "Максимальне значення Вісь правого Y",
"midnightcontrols.menu.mouse_speed": "Швидкість курсору",
"midnightcontrols.menu.reacharound.horizontal": "Розміщення переднього блоку",
"midnightcontrols.menu.reacharound.vertical": "Вертикальний охоплення",
"midnightcontrols.menu.reload_controller_mappings": "Перезавантажити розкладку контролера",
"midnightcontrols.menu.right_dead_zone": "Мертва зона правого стіка",
"midnightcontrols.menu.rotation_speed": "Швидкість обертання по осі X",
"midnightcontrols.menu.y_axis_rotation_speed": "Швидкість обертання по осі Y",
"midnightcontrols.menu.separator.controller": "Контролер",
"midnightcontrols.menu.separator.general": "Загальні",
"midnightcontrols.menu.title": "MidnightControls - налаштування",
"midnightcontrols.menu.title.controller": "Налаштування контролера",
"midnightcontrols.menu.title.controller_controls": "Управління контролером",
"midnightcontrols.menu.title.gameplay": "Налаштування під час гри",
"midnightcontrols.menu.title.general": "Загальні Налаштування",
"midnightcontrols.menu.title.hud": "Налаштування інтерфейсу",
"midnightcontrols.menu.title.mappings.string": "Редактор файлів розкладок",
"midnightcontrols.menu.title.visual": "Налаштування зовнішнього вигляду",
"midnightcontrols.menu.unfocused_input": "Несфокусоване введення",
"midnightcontrols.menu.virtual_mouse": "Віртуальні мишки",
"midnightcontrols.menu.virtual_mouse.skin": "Дизайн віртуальної мишки",
"midnightcontrols.narrator.unbound": "Звільнений %s",
"midnightcontrols.not_bound": "Не призначено",
"midnightcontrols.menu.analog_movement.tooltip": "Включає аналоговий рух, коли це можливо.",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Автоматичне перемикання на новий, тільки підключений контролер.",
"midnightcontrols.menu.controller2.tooltip": "Додатковий контролер, наприклад для джой-конів.",
"midnightcontrols.menu.controller_type.tooltip": "Тип контролера, щоб правильно зіставити іконки кнопок.",
"midnightcontrols.menu.controls_mode.tooltip": "Тип контролю за грою.",
"midnightcontrols.menu.double_tap_to_sprint.tooltip": "При подвійному переміщенні на лівий стик, персонаж починає бігти.",
"midnightcontrols.menu.fast_block_placing.tooltip": "Під час польоту в творчому режимі дозволяє швидко розміщувати блоки в залежності від вашої швидкості. §cНа деяких серверах це може розцінюватися як читерство.§r",
"midnightcontrols.menu.fly_drifting.tooltip": "Під час польоту включає ванільну інерцію.",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Під час польоту включає ванільну вертикальну інерцію.",
"midnightcontrols.menu.hud_enable.tooltip": "Переключає бачення іконок кнопок контролера на екрані.",
"midnightcontrols.menu.hud_side.tooltip": "Розташування інтерфейсу у певний бік.",
"midnightcontrols.menu.left_dead_zone.tooltip": "Мертва зона для лівого аналогового стику контролера.",
"midnightcontrols.menu.max_left_x_value.tooltip": "Змінює те, що мод в результаті вважає максимальним значенням для лівої осі X. Корисно, якщо ваша вісь не використовує весь діапазон і здається повільною.",
"midnightcontrols.menu.max_left_y_value.tooltip": "Змінює те, що мод в результаті вважає максимальним значенням для лівої осі Y. Корисно, якщо ваша вісь не використовує весь діапазон і здається повільною.",
"midnightcontrols.menu.max_right_x_value.tooltip": "Змінює те, що мод в результаті вважає максимальним значенням для правої осі X. Корисно, якщо ваша вісь не використовує весь діапазон і здається повільною.",
"midnightcontrols.menu.max_right_y_value.tooltip": "Змінює те, що мод в результаті вважає максимальним значенням для правої осі Y. Корисно, якщо ваша вісь не використовує весь діапазон і здається повільною.",
"midnightcontrols.menu.mouse_speed.tooltip": "Швидкість миші, що емульується контролером.",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Дозволяє розміщувати передній блок перед собою. §cМоже розглядатися як читерство на деяких серверах§r.",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Забезпечує вертикальне охоплення. §cМоже розглядатися як шахрайство на деяких серверах§r.",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Перезавантажує файл розкладки контролерів.",
"midnightcontrols.menu.right_dead_zone.tooltip": "Мертва зона для правого аналогового стику контролера.",
"midnightcontrols.menu.rotation_speed.tooltip": "Швидкість обертання камери по осі X в режимі контролера.",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "Швидкість обертання камери по осі Y в режимі контролера.",
"midnightcontrols.menu.unfocused_input.tooltip": "Дозволити введення з контролера, навіть якщо гра сфокусована.",
"midnightcontrols.menu.virtual_mouse.tooltip": "Включити віртуальну мишу дуже корисно при грі двом на одному комп'ютері.",
"midnightcontrols.virtual_mouse.skin.default_light": "Звичайний світлий",
"midnightcontrols.virtual_mouse.skin.default_dark": "Звичайний темний",
"midnightcontrols.virtual_mouse.skin.second_light": "Додатковий світлий",
"midnightcontrols.virtual_mouse.skin.second_dark": "Додатковий темний",
"modmenu.descriptionTranslation.midnightcontrols": "Додавання підтримки контролера та покращення елементів керування в цілому.\nРозгалужений від LambdaControls, підтримка якого, на жаль, припинена..",
"midnightcontrols.menu.joystick_as_mouse": "Завжди використовуйте лівий стік як мишку",
"midnightcontrols.menu.joystick_as_mouse.tooltip": "Джойстик поводиться як миша в кожному меню.",
"midnightcontrols.menu.controller_toggle_sneak": "Перемикач присідання на контролері",
"midnightcontrols.menu.controller_toggle_sprint": "Перемикач бігу на контролері",
"midnightcontrols.menu.move_chat": "Перемістити поле введення чату вгору",
"midnightcontrols.action.controls_ring": "Показати кільце Управління",
"midnightcontrols.menu.separate_controller_profile": "Окремий профіль контролера"
}

View File

@@ -0,0 +1,214 @@
{
"midnightcontrols.midnightconfig.title": "Cấu hình MidnightControls nâng cao",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_LIGHT": "Sáng mặc định",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.DEFAULT_DARK": "Tối mặc định",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_LIGHT": "Sáng thứ hai",
"midnightcontrols.midnightconfig.enum.VirtualMouseSkin.SECOND_DARK": "Tối thứ hai",
"midnightcontrols.midnightconfig.enum.ControllerType.DEFAULT": "Mặc định",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSHOCK": "DualShock",
"midnightcontrols.midnightconfig.enum.ControllerType.DUALSENSE": "DualSense",
"midnightcontrols.midnightconfig.enum.ControllerType.SWITCH": "Bộ điều khiển Switch/Wii",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX": "Bộ điều khiển Xbox One/Series",
"midnightcontrols.midnightconfig.enum.ControllerType.XBOX_360": "Bộ điều khiển Xbox 360",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_CONTROLLER": "Bộ điều khiển Steam",
"midnightcontrols.midnightconfig.enum.ControllerType.STEAM_DECK": "Steam Deck",
"midnightcontrols.midnightconfig.enum.ControllerType.OUYA": "Bộ điều khiển OUYA",
"midnightcontrols.midnightconfig.enum.ControllerType.NUMBERED": "Bộ điều khiển được đánh số",
"midnightcontrols.midnightconfig.enum.ControlsMode.DEFAULT": "Bàn phím/Chuột",
"midnightcontrols.midnightconfig.enum.ControlsMode.CONTROLLER": "Bộ điều khiển",
"midnightcontrols.midnightconfig.enum.ControlsMode.TOUCHSCREEN": "Màn hình cảm ứng (WIP)",
"midnightcontrols.midnightconfig.enum.HudSide.LEFT": "Trái",
"midnightcontrols.midnightconfig.enum.HudSide.RIGHT": "Phải",
"key.midnightcontrols.look_down": "Nhìn xuống",
"key.midnightcontrols.look_left": "Nhìn trái",
"key.midnightcontrols.look_right": "Nhìn phải",
"key.midnightcontrols.look_up": "Nhìn lên",
"key.midnightcontrols.ring": "Mở vòng liên kết phím không liên kết",
"midnightcontrols.action.attack": "Tấn công",
"midnightcontrols.action.back": "Trở lại",
"midnightcontrols.action.chat": "Mở trò chuyện",
"midnightcontrols.action.controls_ring": "Mở vòng liên kết phím không liên kết",
"midnightcontrols.action.debug_screen": "Mở HUD gỡ lỗi (F3)",
"midnightcontrols.action.drop_item": "Thả vật phẩm",
"midnightcontrols.action.exit": "Màn hình thoát",
"midnightcontrols.action.forward": "Tiến",
"midnightcontrols.action.hit": "Đánh",
"midnightcontrols.action.hotbar_left": "Thanh nóng bên trái",
"midnightcontrols.action.hotbar_right": "Thanh nóng bên phải",
"midnightcontrols.action.inventory": "Túi đồ",
"midnightcontrols.action.jump": "Nhảy",
"midnightcontrols.action.left": "Trái",
"midnightcontrols.action.pause_game": "Tạm dừng trò chơi",
"midnightcontrols.action.pick_block": "Chọn khối",
"midnightcontrols.action.pickup": "Nhặt",
"midnightcontrols.action.pickup_all": "Nhặt tất cả",
"midnightcontrols.action.place": "Đặt",
"midnightcontrols.action.player_list": "Danh sách người chơi",
"midnightcontrols.action.quick_move": "Di chuyển nhanh",
"midnightcontrols.action.right": "Phải",
"midnightcontrols.action.screenshot": "Chụp màn hình",
"midnightcontrols.action.slot_up": "Di chuyển vị trí lên",
"midnightcontrols.action.slot_down": "Di chuyển vị trí xuống",
"midnightcontrols.action.slot_left": "Di chuyển vị trí qua trái",
"midnightcontrols.action.slot_right": "Di chuyển vị trí qua phải",
"midnightcontrols.action.sneak": "Đi rón rén",
"midnightcontrols.action.sprint": "Chạy nhanh",
"midnightcontrols.action.swap_hands": "Đổi tay",
"midnightcontrols.action.toggle_perspective": "Đổi góc nhìn",
"midnightcontrols.action.toggle_smooth_camera": "Đổi góc nhìn kịch tính",
"midnightcontrols.action.page_back": "Trang trước",
"midnightcontrols.action.page_next": "Trang tiếp theo",
"midnightcontrols.action.tab_back": "Tab trước",
"midnightcontrols.action.tab_next": "Tab trước",
"midnightcontrols.action.take": "Lấy vật phẩm",
"midnightcontrols.action.take_all": "Lấy Stack",
"midnightcontrols.action.use": "Sử dụng",
"midnightcontrols.action.zoom": "Phóng",
"midnightcontrols.action.zoom_in": "Tăng Phóng",
"midnightcontrols.action.zoom_out": "Giảm Phóng",
"midnightcontrols.action.zoom_reset": "Đặt lại Phóng",
"midnightcontrols.action.emi_page_left": "Trang trước",
"midnightcontrols.action.emi_page_right": "Trang tiếp theo",
"midnightcontrols.category.emi": "EMI",
"midnightcontrols.button.a": "A",
"midnightcontrols.button.b": "B",
"midnightcontrols.button.x": "X",
"midnightcontrols.button.y": "Y",
"midnightcontrols.button.left_bumper": "Cản trái",
"midnightcontrols.button.right_bumper": "Cản phải",
"midnightcontrols.button.back": "Trở lại",
"midnightcontrols.button.start": "Bắt đầu",
"midnightcontrols.button.guide": "Hướng dẫn",
"midnightcontrols.button.left_thumb": "Ngón cái trái",
"midnightcontrols.button.right_thumb": "Ngón cái phải",
"midnightcontrols.button.dpad_up": "DPAD lên",
"midnightcontrols.button.dpad_right": "DPAD qua phải",
"midnightcontrols.button.dpad_down": "DPAD xuống",
"midnightcontrols.button.dpad_left": "DPAD qua trái",
"midnightcontrols.button.l4": "L4",
"midnightcontrols.button.l5": "L5",
"midnightcontrols.button.r4": "R4",
"midnightcontrols.button.r5": "L5",
"midnightcontrols.axis.left_x+": "X+ trái",
"midnightcontrols.axis.left_y+": "Y+ phải",
"midnightcontrols.axis.right_x+": "X+ phải",
"midnightcontrols.axis.right_y+": "Y+ trái",
"midnightcontrols.axis.left_trigger": "Bấm nút trái",
"midnightcontrols.axis.right_trigger": "Bấm nút phải",
"midnightcontrols.axis.left_x-": "X- trái",
"midnightcontrols.axis.left_y-": "Y- phải",
"midnightcontrols.axis.right_x-": "X- phải",
"midnightcontrols.axis.right_y-": "Y- trái",
"midnightcontrols.button.unknown": "Không biết (%d)",
"midnightcontrols.controller.tutorial.title": "Chơi trò chơi với Bộ điều khiển!",
"midnightcontrols.controller.tutorial.description": "Đi tới %s -> %s -> %s",
"midnightcontrols.controller.connected": "Bộ điều khiển %d đã được kết nối.",
"midnightcontrols.controller.disconnected": "Bộ điều khiển %d đã bị ngắt kết nối.",
"midnightcontrols.controller.mappings.1": "Để định cấu hình mapping bộ điều khiển, vui lòng sử dụng %s",
"midnightcontrols.controller.mappings.3": "và dán mapping vào trình chỉnh sửa tệp mapping.",
"midnightcontrols.controller.mappings.error": "Lỗi khi tải mapping.",
"midnightcontrols.controller.mappings.error.write": "Lỗi khi ghi mapping vào tệp.",
"midnightcontrols.controller.mappings.updated": "Mapping đã được cập nhật!",
"midnightcontrols.controller_type.default": "Mặc định",
"midnightcontrols.controller_type.dualshock": "DualShock",
"midnightcontrols.controller_type.dualsense": "DualSense",
"midnightcontrols.controller_type.switch": "Bộ điều khiển Switch/Wii",
"midnightcontrols.controller_type.xbox": "Bộ điều khiển Xbox One/Series",
"midnightcontrols.controller_type.xbox_360": "Bộ điều khiển Xbox 360",
"midnightcontrols.controller_type.steam_controller": "Bộ điều khiển Steam",
"midnightcontrols.controller_type.steam_deck": "Steam Deck",
"midnightcontrols.controller_type.ouya": "Bộ điều khiển OUYA",
"midnightcontrols.controller_type.numbered": "Bộ điều khiển được đánh số",
"midnightcontrols.controls_mode.default": "Bàn phím/Chuột",
"midnightcontrols.controls_mode.controller": "Bộ điều khiển",
"midnightcontrols.controls_mode.touchscreen": "Màn hình cảm ứng (WIP)",
"midnightcontrols.hud_side.left": "Trái",
"midnightcontrols.hud_side.right": "Phải",
"midnightcontrols.menu.analog_movement": "Chuyển động tương tự",
"midnightcontrols.menu.analog_movement.tooltip": "Khi có thể, hãy bật chuyển động tương tự.",
"midnightcontrols.menu.auto_switch_mode": "Chế độ tự động chuyển đổi",
"midnightcontrols.menu.auto_switch_mode.tooltip": "Có nên tự động chuyển chế độ điều khiển sang Bộ điều khiển nếu một bộ điều khiển được kết nối hay không.",
"midnightcontrols.menu.controller": "Bộ điều khiển",
"midnightcontrols.menu.controller2": "Bộ điều khiển thứ hai",
"midnightcontrols.menu.controller2.tooltip": "Bộ điều khiển thứ hai để sử dụng, cho phép (ví dụ) hỗ trợ Joy-Cons.",
"midnightcontrols.menu.controller_toggle_sneak": "Đổi đi rón rén trên bộ điều khiển",
"midnightcontrols.menu.controller_toggle_sprint": "Đổi chạy nhanh trên bộ điều khiển",
"midnightcontrols.menu.controller_type": "Loại bộ điều khiển",
"midnightcontrols.menu.controller_type.tooltip": "Loại bộ điều khiển bạn đang sử dụng (cần thiết để hiển thị các nút chính xác)",
"midnightcontrols.menu.controls_mode": "Chế độ",
"midnightcontrols.menu.controls_mode.tooltip": "Chế độ điều khiển.",
"midnightcontrols.menu.double_tap_to_sprint": "Nhấn đúp để chạy nhanh",
"midnightcontrols.menu.double_tap_to_sprint.tooltip": "Chuyển đổi liệu phím Tiến lên phía trước có khiến người chơi chạy nhanh khi nhấn chạm nhanh hai lần hay không",
"midnightcontrols.menu.fast_block_placing": "Đặt khối nhanh",
"midnightcontrols.menu.fast_block_placing.tooltip": "Trong khi bay ở chế độ sáng tạo, cho phép đặt khối nhanh tùy thuộc vào tốc độ của bạn. §cTrên một số máy chủ, điều này có thể bị coi là gian lận.",
"midnightcontrols.menu.fly_drifting": "Trôi bay",
"midnightcontrols.menu.fly_drifting.tooltip": "Khi đang bay, cho phép trôi dọc/xen kẽ truyền thống.",
"midnightcontrols.menu.fly_drifting_vertical": "Trôi theo phương thẳng đứng",
"midnightcontrols.menu.fly_drifting_vertical.tooltip": "Trong khi bay, cho phép trôi dọc/xen kẽ truyền thống.",
"midnightcontrols.menu.hud_enable": "Bật HUD",
"midnightcontrols.menu.hud_enable.tooltip": "Chuyển đổi chỉ báo nút điều khiển trên màn hình.",
"midnightcontrols.menu.hud_side": "Phía HUD",
"midnightcontrols.menu.hud_side.tooltip": "Vị trí của HUD.",
"midnightcontrols.menu.invert_right_x_axis": "Đảo ngược X phải",
"midnightcontrols.menu.invert_right_y_axis": "Đảo ngược Y phải",
"midnightcontrols.menu.joystick_as_mouse": "Luôn sử dụng thanh bên trái làm chuột",
"midnightcontrols.menu.joystick_as_mouse.tooltip": "Làm cho cần điều khiển hoạt động giống như một con chuột trong mọi menu.",
"midnightcontrols.menu.keyboard_controls": "Điều khiển bàn phím...",
"midnightcontrols.menu.left_dead_zone": "Vùng chết thanh bên trái",
"midnightcontrols.menu.left_dead_zone.tooltip": "Vùng chết cho thanh analog bên trái của bộ điều khiển.",
"midnightcontrols.menu.mappings.open_input_str": "Mở Trình chỉnh sửa tệp mapping",
"midnightcontrols.menu.max_left_x_value": "Giá trị tối đa của trục X bên trái",
"midnightcontrols.menu.max_left_x_value.tooltip": "Thay đổi những gì mod coi là giá trị cao nhất cho trục X bên trái. Hữu ích nếu trục của bạn không sử dụng hết phạm vi và có vẻ chậm.",
"midnightcontrols.menu.max_left_y_value": "Giá trị tối đa của trục Y bên trái",
"midnightcontrols.menu.max_left_y_value.tooltip": "Thay đổi những gì mod coi là giá trị cao nhất cho trục Y bên trái. Hữu ích nếu trục của bạn không sử dụng hết phạm vi và có vẻ chậm.",
"midnightcontrols.menu.max_right_x_value": "Giá trị tối đa của trục X bên phải",
"midnightcontrols.menu.max_right_x_value.tooltip": "Thay đổi những gì mod coi là giá trị cao nhất cho trục X bên phải. Hữu ích nếu trục của bạn không sử dụng hết phạm vi và có vẻ chậm.",
"midnightcontrols.menu.max_right_y_value": "Giá trị tối đa của trục Y bên phải",
"midnightcontrols.menu.max_right_y_value.tooltip": "Thay đổi những gì mod coi là giá trị cao nhất cho trục Y bên phải. Hữu ích nếu trục của bạn không sử dụng hết phạm vi và có vẻ chậm.",
"midnightcontrols.menu.mouse_speed": "Tốc độ chuột",
"midnightcontrols.menu.mouse_speed.tooltip": "Tốc độ chuột mô phỏng của bộ điều khiển.",
"midnightcontrols.menu.move_chat": "Di chuyển hộp nhập trò chuyện lên trên cùng",
"midnightcontrols.menu.move_chat.tooltip": "Di chuyển phần nhập trò chuyện lên trên cùng để nhập tốt hơn trên các thiết bị có bàn phím ảo.",
"midnightcontrols.menu.reacharound.horizontal": "Đặt khối phía trước",
"midnightcontrols.menu.reacharound.horizontal.tooltip": "Cho phép đặt khối phía trước, §cmcó thể bị coi là gian lận trên một số máy chủ §r.",
"midnightcontrols.menu.reacharound.vertical": "Tiếp cận theo chiều dọc",
"midnightcontrols.menu.reacharound.vertical.tooltip": "Cho phép tiếp cận theo chiều dọc, §cm có thể bị coi là gian lận trên một số máy chủ §r.",
"midnightcontrols.menu.reload_controller_mappings": "Tải lại mapping bộ điều khiển",
"midnightcontrols.menu.reload_controller_mappings.tooltip": "Tải lại tệp mapping bộ điều khiển.",
"midnightcontrols.menu.right_dead_zone": "Vùng chết thanh bên phải",
"midnightcontrols.menu.right_dead_zone.tooltip": "Vùng chết cho thanh analog bên phải của bộ điều khiển.",
"midnightcontrols.menu.rotation_speed": "Tốc độ quay trục X",
"midnightcontrols.menu.rotation_speed.tooltip": "Tốc độ quay Trục X của máy ảnh ở chế độ bộ điều khiển.",
"midnightcontrols.menu.y_axis_rotation_speed": "Tốc độ quay trục Y",
"midnightcontrols.menu.y_axis_rotation_speed.tooltip": "Tốc độ quay Trục Y của máy ảnh ở chế độ bộ điều khiển.",
"midnightcontrols.menu.separate_controller_profile": "Hồ sơ bộ điều khiển riêng biệt",
"midnightcontrols.menu.separator.controller": "Bộ điều khiển",
"midnightcontrols.menu.separator.general": "Chung",
"midnightcontrols.menu.title": "MidnightControls - Cài đặt",
"midnightcontrols.menu.title.controller": "Tùy chọn bộ điều khiển",
"midnightcontrols.menu.title.controller_controls": "Ràng buộc bộ điều khiển",
"midnightcontrols.menu.title.gameplay": "Tuỳ chọn Gameplay",
"midnightcontrols.menu.title.general": "Tuỳ chọn chung",
"midnightcontrols.menu.title.hud": "Tuỳ chọn HUD",
"midnightcontrols.menu.title.mappings.string": "Trình chỉnh sửa tệp mapping",
"midnightcontrols.menu.title.visual": "Tùy chọn giao diện",
"midnightcontrols.menu.unfocused_input": "Đầu vào không tập trung",
"midnightcontrols.menu.unfocused_input.tooltip": "Cho phép đầu vào bộ điều khiển khi cửa sổ không được đặt tiêu điểm.",
"midnightcontrols.menu.virtual_mouse": "Chuột ảo",
"midnightcontrols.menu.virtual_mouse.tooltip": "Bật chuột ảo, rất hữu ích trong quá trình chia đôi màn hình.",
"midnightcontrols.menu.virtual_mouse.skin": "Skin chuột ảo",
"midnightcontrols.menu.hide_cursor": "Ẩn con trỏ chuột bình thường",
"midnightcontrols.menu.hide_cursor.tooltip": "Ẩn con trỏ chuột bình thường, chỉ để lại con chuột ảo.",
"midnightcontrols.narrator.unbound": "Bỏ ràng buộc %s",
"midnightcontrols.not_bound": "Không ràng buộc",
"midnightcontrols.virtual_mouse.skin.default_light": "Sáng mặc định",
"midnightcontrols.virtual_mouse.skin.default_dark": "Tối mặc định",
"midnightcontrols.virtual_mouse.skin.second_light": "Sáng thứ hai",
"midnightcontrols.virtual_mouse.skin.second_dark": "Tối thứ hai",
"midnightcontrols.midnightconfig.category.controller": "Bộ điều khiển",
"midnightcontrols.midnightconfig.category.misc": "Khác",
"midnightcontrols.midnightconfig.category.screens": "Màn hình",
"midnightcontrols.midnightconfig.category.gameplay": "Gameplay",
"midnightcontrols.midnightconfig.category.visual": "Hình ảnh",
"modmenu.descriptionTranslation.midnightcontrols": "Thêm hỗ trợ bộ điều khiển và kiểm soát nâng cao tổng thể.\nĐược phân tách từ LambdaControls, đáng tiếc là đã ngừng hoạt động."
}

Some files were not shown because too many files have changed in this diff Show More