mirror of
https://github.com/TeamMidnightDust/MidnightControls.git
synced 2025-12-13 23:25:10 +01:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c53575d17 |
@@ -21,12 +21,14 @@ This mod adds a controller support (and an experimental touchscreen support).
|
||||
- Touchscreen support (very experimental and buggy).
|
||||
- Keyboard controls to look around.
|
||||
- Toggleable on screen button indicator (like in Bedrock Edition).
|
||||
- Vertical reacharound.
|
||||
- Many Bedrock Edition features:
|
||||
- Toggleable fly drifting
|
||||
- Front block placing (be careful with this one)
|
||||
- New controls settings!
|
||||
- Many options in config to change to your liking.
|
||||
- Many controllers supported and in a simply way your own controller mappings.
|
||||
- An easy API for developers to add their own button bindings.
|
||||
|
||||
## 🎮 Supported Controllers:
|
||||
|
||||
|
||||
@@ -21,14 +21,15 @@ import java.util.Optional;
|
||||
* Represents a feature.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.2.0
|
||||
* @version 1.3.2
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class LambdaControlsFeature implements Nameable
|
||||
{
|
||||
private static final List<LambdaControlsFeature> FEATURES = new ArrayList<>();
|
||||
public static final LambdaControlsFeature FRONT_BLOCK_PLACING = new LambdaControlsFeature("front_block_placing", true, false);
|
||||
public static final LambdaControlsFeature FAST_BLOCK_PLACING = new LambdaControlsFeature("fast_block_placing", true, true);
|
||||
public static final LambdaControlsFeature FRONT_BLOCK_PLACING = new LambdaControlsFeature("front_block_placing", true, false);
|
||||
public static final LambdaControlsFeature VERTICAL_REACHAROUND = new LambdaControlsFeature("vertical_reacharound", true, false);
|
||||
|
||||
private final String key;
|
||||
private final boolean defaultAllowed;
|
||||
@@ -155,7 +156,8 @@ public class LambdaControlsFeature implements Nameable
|
||||
}
|
||||
|
||||
static {
|
||||
FEATURES.add(FRONT_BLOCK_PLACING);
|
||||
FEATURES.add(FAST_BLOCK_PLACING);
|
||||
FEATURES.add(FRONT_BLOCK_PLACING);
|
||||
FEATURES.add(VERTICAL_REACHAROUND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import me.lambdaurora.spruceui.event.OpenScreenCallback;
|
||||
import me.lambdaurora.spruceui.hud.HudManager;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
@@ -40,7 +39,7 @@ import org.lwjgl.glfw.GLFW;
|
||||
* Represents the LambdaControls client mod.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.3.0
|
||||
* @version 1.3.2
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class LambdaControlsClient extends LambdaControls implements ClientModInitializer
|
||||
@@ -59,6 +58,7 @@ public class LambdaControlsClient extends LambdaControls implements ClientModIni
|
||||
public static final Identifier CURSOR_TEXTURE = new Identifier(LambdaControlsConstants.NAMESPACE, "textures/gui/cursor.png");
|
||||
public final LambdaControlsConfig config = new LambdaControlsConfig(this);
|
||||
public final LambdaInput input = new LambdaInput(this);
|
||||
public final LambdaReacharound reacharound = new LambdaReacharound();
|
||||
private LambdaControlsHud hud;
|
||||
private ControlsMode previousControlsMode;
|
||||
|
||||
@@ -79,6 +79,7 @@ public class LambdaControlsClient extends LambdaControls implements ClientModIni
|
||||
LambdaControlsFeature.fromName(name).ifPresent(feature -> context.getTaskQueue().execute(() -> feature.setAllowed(allowed)));
|
||||
});
|
||||
|
||||
ClientTickEvents.START_CLIENT_TICK.register(this.reacharound::tick);
|
||||
ClientTickEvents.END_CLIENT_TICK.register(this::onTick);
|
||||
|
||||
OpenScreenCallback.EVENT.register((client, screen) -> {
|
||||
|
||||
@@ -44,7 +44,9 @@ public class LambdaControlsConfig
|
||||
private static final boolean DEFAULT_FLY_DRIFTING = false;
|
||||
private static final boolean DEFAULT_FLY_VERTICAL_DRIFTING = true;
|
||||
private static final boolean DEFAULT_FRONT_BLOCK_PLACING = false;
|
||||
private static final boolean DEFAULT_FRONT_BLOCK_OUTLINE = true;
|
||||
private static final boolean DEFAULT_VERTICAL_REACHAROUND = false;
|
||||
private static final boolean DEFAULT_REACHAROUND_OUTLINE = true;
|
||||
private static final int[] DEFAULT_REACHAROUND_OUTLINE_COLOR = new int[]{255, 255, 255, 102};
|
||||
// Controller
|
||||
private static final ControllerType DEFAULT_CONTROLLER_TYPE = ControllerType.DEFAULT;
|
||||
private static final double DEFAULT_DEAD_ZONE = 0.25;
|
||||
@@ -61,8 +63,8 @@ public class LambdaControlsConfig
|
||||
private ControlsMode controlsMode;
|
||||
private ControllerType controllerType;
|
||||
// Gameplay.
|
||||
private boolean shouldRenderFrontBlockOutline;
|
||||
private int[] frontBlockOutlineColor;
|
||||
private boolean shouldRenderReacharoundOutline;
|
||||
private int[] reacharoundOutlineColor;
|
||||
// Controller settings
|
||||
private double deadZone;
|
||||
private double rotationSpeed;
|
||||
@@ -93,9 +95,10 @@ public class LambdaControlsConfig
|
||||
this.hudSide = HudSide.byId(this.config.getOrElse("hud.side", DEFAULT_HUD_SIDE.getName())).orElse(DEFAULT_HUD_SIDE);
|
||||
// Gameplay
|
||||
LambdaControlsFeature.FAST_BLOCK_PLACING.setEnabled(this.config.getOrElse("gameplay.fast_block_placing", DEFAULT_FAST_BLOCK_INTERACTION));
|
||||
LambdaControlsFeature.FRONT_BLOCK_PLACING.setEnabled(this.config.getOrElse("gameplay.front_block_placing.enabled", DEFAULT_FRONT_BLOCK_PLACING));
|
||||
this.shouldRenderFrontBlockOutline = this.config.getOrElse("gameplay.front_block_placing.outline", DEFAULT_FRONT_BLOCK_OUTLINE);
|
||||
this.frontBlockOutlineColor = this.config.getOptional("gameplay.front_block_placing.outline_color").map(hex -> parseColor((String) hex)).orElse(new int[]{255, 255, 255, 102});
|
||||
LambdaControlsFeature.FRONT_BLOCK_PLACING.setEnabled(this.config.getOrElse("gameplay.reacharound.horizontal", DEFAULT_FRONT_BLOCK_PLACING));
|
||||
LambdaControlsFeature.VERTICAL_REACHAROUND.setEnabled(this.config.getOrElse("gameplay.reacharound.vertical", DEFAULT_VERTICAL_REACHAROUND));
|
||||
this.shouldRenderReacharoundOutline = this.config.getOrElse("gameplay.reacharound.outline", DEFAULT_REACHAROUND_OUTLINE);
|
||||
this.reacharoundOutlineColor = this.config.getOptional("gameplay.reacharound.outline_color").map(hex -> parseColor((String) hex)).orElse(DEFAULT_REACHAROUND_OUTLINE_COLOR);
|
||||
// Controller settings.
|
||||
this.controllerType = ControllerType.byId(this.config.getOrElse("controller.type", DEFAULT_CONTROLLER_TYPE.getName())).orElse(DEFAULT_CONTROLLER_TYPE);
|
||||
this.deadZone = this.config.getOrElse("controller.dead_zone", DEFAULT_DEAD_ZONE);
|
||||
@@ -133,10 +136,19 @@ public class LambdaControlsConfig
|
||||
}
|
||||
});
|
||||
|
||||
// This shouldn't happen if the configuration is new.
|
||||
if (!this.config.contains("gameplay.front_block_placing.enabled") && this.config.contains("gameplay.front_block_placing")) {
|
||||
this.config.remove("gameplay.front_block_placing");
|
||||
this.config.set("gameplay.front_block_placing.enabled", DEFAULT_FRONT_BLOCK_PLACING);
|
||||
if (this.config.contains("gameplay.front_block_placing.enabled")) {
|
||||
this.setFrontBlockPlacing(this.config.getOrElse("gameplay.front_block_placing.enabled", DEFAULT_FRONT_BLOCK_PLACING));
|
||||
this.config.remove("gameplay.front_block_placing.enabled");
|
||||
}
|
||||
|
||||
if (this.config.contains("gameplay.front_block_placing.outline")) {
|
||||
this.setRenderReacharoundOutline(this.config.getOrElse("gameplay.front_block_placing.outline", DEFAULT_REACHAROUND_OUTLINE));
|
||||
this.config.remove("gameplay.front_block_placing.outline");
|
||||
}
|
||||
|
||||
if (this.config.contains("gameplay.front_block_placing.outline_color")) {
|
||||
this.config.getOptional("gameplay.front_block_placing.outline_color").ifPresent(color -> this.config.set("gameplay.reacharound.outline_color", color));
|
||||
this.config.remove("gameplay.front_block_placing.outline_color");
|
||||
}
|
||||
|
||||
this.renamed("controller.controls.tab_left", "controller.controls.tab_back");
|
||||
@@ -165,7 +177,8 @@ public class LambdaControlsConfig
|
||||
this.setFlyDrifting(DEFAULT_FLY_DRIFTING);
|
||||
this.setFlyVerticalDrifting(DEFAULT_FLY_VERTICAL_DRIFTING);
|
||||
this.setFrontBlockPlacing(DEFAULT_FRONT_BLOCK_PLACING);
|
||||
this.setRenderFrontBlockOutline(DEFAULT_FRONT_BLOCK_OUTLINE);
|
||||
this.setVerticalReacharound(DEFAULT_VERTICAL_REACHAROUND);
|
||||
this.setRenderReacharoundOutline(DEFAULT_REACHAROUND_OUTLINE);
|
||||
// Controller
|
||||
this.setControllerType(DEFAULT_CONTROLLER_TYPE);
|
||||
this.setDeadZone(DEFAULT_DEAD_ZONE);
|
||||
@@ -352,7 +365,28 @@ public class LambdaControlsConfig
|
||||
public void setFrontBlockPlacing(boolean enable)
|
||||
{
|
||||
LambdaControlsFeature.FRONT_BLOCK_PLACING.setEnabled(enable);
|
||||
this.config.set("gameplay.front_block_placing.enabled", enable);
|
||||
this.config.set("gameplay.reacharound.horizontal", enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether vertical reacharound is enabled or not.
|
||||
*
|
||||
* @return True if vertical reacharound is enabled, else false.
|
||||
*/
|
||||
public boolean hasVerticalReacharound()
|
||||
{
|
||||
return LambdaControlsFeature.VERTICAL_REACHAROUND.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether vertical reacharound is enabled or not.
|
||||
*
|
||||
* @param enable True if vertical reacharound is enabled, else false.
|
||||
*/
|
||||
public void setVerticalReacharound(boolean enable)
|
||||
{
|
||||
LambdaControlsFeature.VERTICAL_REACHAROUND.setEnabled(enable);
|
||||
this.config.set("gameplay.reacharound.vertical", enable);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,9 +394,9 @@ public class LambdaControlsConfig
|
||||
*
|
||||
* @return True if front block placing outline is enabled, else false.
|
||||
*/
|
||||
public boolean shouldRenderFrontBlockOutline()
|
||||
public boolean shouldRenderReacharoundOutline()
|
||||
{
|
||||
return this.shouldRenderFrontBlockOutline;
|
||||
return this.shouldRenderReacharoundOutline;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,9 +404,9 @@ public class LambdaControlsConfig
|
||||
*
|
||||
* @param render True if front block placing outline is enabled, else false.
|
||||
*/
|
||||
public void setRenderFrontBlockOutline(boolean render)
|
||||
public void setRenderReacharoundOutline(boolean render)
|
||||
{
|
||||
this.config.set("gameplay.front_block_placing.outline", this.shouldRenderFrontBlockOutline = render);
|
||||
this.config.set("gameplay.reacharound.outline", this.shouldRenderReacharoundOutline = render);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,9 +416,9 @@ public class LambdaControlsConfig
|
||||
*
|
||||
* @return The color as a RGBA integer array.
|
||||
*/
|
||||
public int[] getFrontBlockOutlineColor()
|
||||
public int[] getReacharoundOutlineColor()
|
||||
{
|
||||
return this.frontBlockOutlineColor;
|
||||
return this.reacharoundOutlineColor;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
package me.lambdaurora.lambdacontrols.client;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.LambdaControlsFeature;
|
||||
import me.lambdaurora.lambdacontrols.client.compat.LambdaControlsCompat;
|
||||
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
|
||||
import me.lambdaurora.lambdacontrols.client.controller.Controller;
|
||||
@@ -22,10 +21,6 @@ import me.lambdaurora.lambdacontrols.client.mixin.EntryListWidgetAccessor;
|
||||
import me.lambdaurora.lambdacontrols.client.util.HandledScreenAccessor;
|
||||
import me.lambdaurora.lambdacontrols.client.util.MouseAccessor;
|
||||
import me.lambdaurora.spruceui.SpruceLabelWidget;
|
||||
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.client.gui.Element;
|
||||
import net.minecraft.client.gui.ParentElement;
|
||||
@@ -42,14 +37,8 @@ import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget;
|
||||
import net.minecraft.client.gui.widget.EntryListWidget;
|
||||
import net.minecraft.client.gui.widget.SliderWidget;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.screen.slot.SlotActionType;
|
||||
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 org.aperlambda.lambdacommon.utils.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -72,7 +61,7 @@ import static org.lwjgl.glfw.GLFW.*;
|
||||
* Represents the LambdaControls' input handler.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.3.1
|
||||
* @version 1.3.2
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LambdaInput
|
||||
@@ -325,6 +314,7 @@ public class LambdaInput
|
||||
|
||||
if (button == GLFW.GLFW_GAMEPAD_BUTTON_B) {
|
||||
if (client.currentScreen != null) {
|
||||
if (!LambdaControlsCompat.handleMenuBack(client, client.currentScreen))
|
||||
client.currentScreen.onClose();
|
||||
return;
|
||||
}
|
||||
@@ -390,6 +380,8 @@ public class LambdaInput
|
||||
if (screen instanceof CreativeInventoryScreen)
|
||||
if (((CreativeInventoryScreenAccessor) screen).lambdacontrols_isCreativeInventorySlot(slot))
|
||||
actionType = SlotActionType.CLONE;
|
||||
if (slot != null && LambdaControlsCompat.streamCompatHandlers().anyMatch(handler -> handler.isCreativeSlot(screen, slot)))
|
||||
actionType = SlotActionType.CLONE;
|
||||
break;
|
||||
case GLFW.GLFW_GAMEPAD_BUTTON_X:
|
||||
clickData = GLFW_MOUSE_BUTTON_2;
|
||||
@@ -720,71 +712,4 @@ public class LambdaInput
|
||||
this.mouseSpeedY = 0.F;
|
||||
}
|
||||
}
|
||||
|
||||
public static Direction getMoveDirection(@Nullable BlockPos lastPos, @NotNull BlockPos newPos)
|
||||
{
|
||||
if (lastPos == null)
|
||||
return null;
|
||||
BlockPos vector = newPos.subtract(lastPos);
|
||||
if (vector.getX() > 0)
|
||||
return Direction.EAST;
|
||||
else if (vector.getX() < 0)
|
||||
return Direction.WEST;
|
||||
else if (vector.getZ() > 0)
|
||||
return Direction.SOUTH;
|
||||
else if (vector.getZ() < 0)
|
||||
return Direction.NORTH;
|
||||
else if (vector.getY() > 0)
|
||||
return Direction.UP;
|
||||
else if (vector.getY() < 0)
|
||||
return Direction.DOWN;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nullable block hit result if front placing is possible.
|
||||
*
|
||||
* @param client The client instance.
|
||||
* @return A block hit result if front placing is possible.
|
||||
*/
|
||||
public static @Nullable BlockHitResult tryFrontPlace(@NotNull MinecraftClient client)
|
||||
{
|
||||
if (!LambdaControlsFeature.FRONT_BLOCK_PLACING.isAvailable())
|
||||
return null;
|
||||
if (client.player != null && client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.MISS && client.player.isOnGround() && client.player.pitch > 35.0F) {
|
||||
if (client.player.isRiding())
|
||||
return null;
|
||||
BlockPos playerPos = client.player.getBlockPos().down();
|
||||
BlockPos targetPos = new BlockPos(client.crosshairTarget.getPos()).subtract(playerPos);
|
||||
BlockPos vector = new BlockPos(MathHelper.clamp(targetPos.getX(), -1, 1), 0, MathHelper.clamp(targetPos.getZ(), -1, 1));
|
||||
BlockPos blockPos = playerPos.add(vector);
|
||||
|
||||
Direction direction = client.player.getHorizontalFacing();
|
||||
|
||||
BlockState state = client.world.getBlockState(blockPos);
|
||||
if (!state.isAir())
|
||||
return null;
|
||||
BlockState adjacentBlockState = client.world.getBlockState(blockPos.offset(direction.getOpposite()));
|
||||
if (adjacentBlockState.isAir() || adjacentBlockState.getBlock() instanceof FluidBlock || (vector.getX() == 0 && vector.getZ() == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new BlockHitResult(client.crosshairTarget.getPos(), direction, blockPos, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @NotNull BlockHitResult withSideForFrontPlace(@NotNull BlockHitResult result, @Nullable ItemStack stack)
|
||||
{
|
||||
if (stack == null || stack.isEmpty() || !(stack.getItem() instanceof BlockItem))
|
||||
return result;
|
||||
return withSideForFrontPlace(result, Block.getBlockFromItem(stack.getItem()));
|
||||
}
|
||||
|
||||
public static @NotNull BlockHitResult withSideForFrontPlace(@NotNull BlockHitResult result, @NotNull Block block)
|
||||
{
|
||||
if (block instanceof SlabBlock)
|
||||
result = result.withSide(Direction.DOWN);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.client;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.LambdaControlsFeature;
|
||||
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.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.RayTraceContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents the reacharound API of LambdaControls.
|
||||
*
|
||||
* @version 1.3.2
|
||||
* @since 1.3.2
|
||||
*/
|
||||
public class LambdaReacharound
|
||||
{
|
||||
private BlockHitResult lastReacharoundResult = null;
|
||||
private boolean lastReacharoundVertical = false;
|
||||
|
||||
public void tick(@NotNull MinecraftClient client)
|
||||
{
|
||||
this.lastReacharoundResult = this.tryVerticalReachAround(client);
|
||||
if (this.lastReacharoundResult == null) {
|
||||
this.lastReacharoundResult = this.tryFrontPlace(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 True if the reach around is vertical.
|
||||
*/
|
||||
public boolean isLastReacharoundVertical()
|
||||
{
|
||||
return this.lastReacharoundVertical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether reacharound is available or not.
|
||||
*
|
||||
* @return True if reacharound is available, else false.
|
||||
*/
|
||||
public boolean isReacharoundAvailable()
|
||||
{
|
||||
return LambdaControlsFeature.FRONT_BLOCK_PLACING.isAvailable() || LambdaControlsFeature.VERTICAL_REACHAROUND.isAvailable();
|
||||
}
|
||||
|
||||
private float getPlayerRange(@NotNull MinecraftClient client)
|
||||
{
|
||||
return client.interactionManager != null ? client.interactionManager.getReachDistance() : 0.f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nullable block hit result if vertical reacharound is possible.
|
||||
*
|
||||
* @param client The client instance.
|
||||
* @return A block hit result if vertical reacharound is possible, else null.
|
||||
*/
|
||||
public @Nullable BlockHitResult tryVerticalReachAround(@NotNull MinecraftClient client)
|
||||
{
|
||||
if (!LambdaControlsFeature.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.pitch < 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);
|
||||
Vec3d rayVec = pos.add(rotationVec.x * range, rotationVec.y * range, rotationVec.z * range).add(0, 0.75, 0);
|
||||
BlockHitResult result = client.world.rayTrace(new RayTraceContext(pos, rayVec, RayTraceContext.ShapeType.OUTLINE, RayTraceContext.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.getMaterial().isReplaceable())) {
|
||||
return new BlockHitResult(result.getPos(), Direction.DOWN, blockPos, false);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nullable block hit result if front placing is possible.
|
||||
*
|
||||
* @param client The client instance.
|
||||
* @return A block hit result if front placing is possible.
|
||||
*/
|
||||
public @Nullable BlockHitResult tryFrontPlace(@NotNull MinecraftClient client)
|
||||
{
|
||||
if (!LambdaControlsFeature.FRONT_BLOCK_PLACING.isAvailable())
|
||||
return null;
|
||||
if (client.player != null && client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.MISS && client.player.isOnGround() && client.player.pitch > 35.0F) {
|
||||
if (client.player.isRiding())
|
||||
return null;
|
||||
BlockPos playerPos = client.player.getBlockPos().down();
|
||||
BlockPos targetPos = new BlockPos(client.crosshairTarget.getPos()).subtract(playerPos);
|
||||
BlockPos vector = new BlockPos(MathHelper.clamp(targetPos.getX(), -1, 1), 0, MathHelper.clamp(targetPos.getZ(), -1, 1));
|
||||
BlockPos blockPos = playerPos.add(vector);
|
||||
|
||||
Direction direction = client.player.getHorizontalFacing();
|
||||
|
||||
BlockState state = client.world.getBlockState(blockPos);
|
||||
if (!state.isAir())
|
||||
return null;
|
||||
BlockState adjacentBlockState = client.world.getBlockState(blockPos.offset(direction.getOpposite()));
|
||||
if (adjacentBlockState.isAir() || adjacentBlockState.getBlock() instanceof FluidBlock || (vector.getX() == 0 && vector.getZ() == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new BlockHitResult(client.crosshairTarget.getPos(), direction, blockPos, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @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 static @NotNull BlockHitResult withSideForReacharound(@NotNull BlockHitResult result, @NotNull Block block)
|
||||
{
|
||||
if (block instanceof SlabBlock)
|
||||
result = result.withSide(Direction.DOWN);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,20 @@
|
||||
package me.lambdaurora.lambdacontrols.client.compat;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
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.2.0
|
||||
* @version 1.3.2
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public interface CompatHandler
|
||||
@@ -33,10 +39,58 @@ public interface CompatHandler
|
||||
* 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.
|
||||
* @return True if the mouse is required on the specified screen, else false.
|
||||
*/
|
||||
default boolean requireMouseOnScreen(Screen screen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.client.compat;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import org.aperlambda.lambdacommon.utils.LambdaReflection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents HQM compatibility handler.
|
||||
*
|
||||
* 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(@NotNull LambdaControlsClient mod)
|
||||
{
|
||||
this.guiBaseClass = LambdaReflection.getClass(GUI_BASE_CLASS_PATH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requireMouseOnScreen(Screen screen)
|
||||
{
|
||||
return this.guiBaseClass.map(clazz -> clazz.isInstance(screen)).orElse(false);
|
||||
}
|
||||
}
|
||||
@@ -12,18 +12,23 @@ package me.lambdaurora.lambdacontrols.client.compat;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
|
||||
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Represents a compatibility handler.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.1.0
|
||||
* @version 1.3.2
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public class LambdaControlsCompat
|
||||
@@ -45,10 +50,34 @@ public class LambdaControlsCompat
|
||||
mod.log("Adding REI compatiblity...");
|
||||
HANDLERS.add(new ReiCompat());
|
||||
}
|
||||
if (FabricLoader.getInstance().isModLoaded("hardcorequesting") && LambdaReflection.doesClassExist(HQMCompat.GUI_BASE_CLASS_PATH)) {
|
||||
mod.log("Adding HQM compatibility...");
|
||||
HANDLERS.add(new HQMCompat());
|
||||
}
|
||||
HANDLERS.forEach(handler -> handler.handle(mod));
|
||||
InputManager.loadButtonBindings(mod.config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -60,6 +89,57 @@ public class LambdaControlsCompat
|
||||
return HANDLERS.stream().anyMatch(handler -> handler.requireMouseOnScreen(screen));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Roughly Enough Items is present.
|
||||
*
|
||||
|
||||
@@ -17,12 +17,14 @@ import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
|
||||
import me.lambdaurora.lambdacontrols.client.controller.InputHandlers;
|
||||
import me.lambdaurora.lambdacontrols.client.controller.InputManager;
|
||||
import me.lambdaurora.lambdacontrols.client.controller.PressAction;
|
||||
import me.shedaniel.rei.api.REIHelper;
|
||||
import me.shedaniel.rei.api.RecipeCategory;
|
||||
import me.shedaniel.rei.gui.ContainerScreenOverlay;
|
||||
import me.shedaniel.rei.gui.RecipeViewingScreen;
|
||||
import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
|
||||
import me.shedaniel.rei.gui.widget.EntryListWidget;
|
||||
import me.shedaniel.rei.impl.ScreenHelper;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.aperlambda.lambdacommon.utils.LambdaReflection;
|
||||
@@ -37,13 +39,12 @@ import static org.lwjgl.glfw.GLFW.*;
|
||||
* Represents a compatibility handler for REI.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.3.0
|
||||
* @version 1.3.2
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class ReiCompat implements CompatHandler
|
||||
{
|
||||
private static EntryListWidget ENTRY_LIST_WIDGET;
|
||||
public static ButtonBinding TAB_BACK;
|
||||
|
||||
@Override
|
||||
public void handle(@NotNull LambdaControlsClient mod)
|
||||
@@ -106,6 +107,17 @@ public class ReiCompat implements CompatHandler
|
||||
return screen instanceof RecipeViewingScreen || screen instanceof VillagerRecipeViewingScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMenuBack(@NotNull MinecraftClient client, @NotNull Screen screen)
|
||||
{
|
||||
if (!isViewingScreen(screen))
|
||||
return false;
|
||||
|
||||
MinecraftClient.getInstance().openScreen(REIHelper.getInstance().getPreviousContainerScreen());
|
||||
ScreenHelper.getLastOverlay().init();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static EntryListWidget getEntryListWidget()
|
||||
{
|
||||
if (ENTRY_LIST_WIDGET == null) {
|
||||
|
||||
@@ -13,11 +13,12 @@ import me.lambdaurora.lambdacontrols.ControlsMode;
|
||||
import me.lambdaurora.lambdacontrols.LambdaControlsConstants;
|
||||
import me.lambdaurora.lambdacontrols.client.HudSide;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaInput;
|
||||
import me.lambdaurora.lambdacontrols.client.compat.LambdaControlsCompat;
|
||||
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
|
||||
import me.lambdaurora.spruceui.hud.Hud;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.util.Window;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -31,7 +32,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
* Represents the LambdaControls HUD.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.3.0
|
||||
* @version 1.3.2
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LambdaControlsHud extends Hud
|
||||
@@ -51,6 +52,7 @@ public class LambdaControlsHud extends Hud
|
||||
private BlockHitResult placeHitResult;
|
||||
private String attackAction = "";
|
||||
private String placeAction = "";
|
||||
private int ticksDisplayedCrosshair = 0;
|
||||
|
||||
public LambdaControlsHud(@NotNull LambdaControlsClient mod)
|
||||
{
|
||||
@@ -86,6 +88,19 @@ public class LambdaControlsHud extends Hud
|
||||
this.renderFirstSection(matrices, this.mod.config.getHudSide() == HudSide.LEFT ? 2 : client.getWindow().getScaledWidth() - 2, y);
|
||||
this.renderSecondSection(matrices, this.mod.config.getHudSide() == HudSide.RIGHT ? 2 : client.getWindow().getScaledWidth() - 2, y);
|
||||
}
|
||||
|
||||
if (this.mod.reacharound.isLastReacharoundVertical()) {
|
||||
// Render crosshair indicator.
|
||||
Window window = this.client.getWindow();
|
||||
String text = "[ ]";
|
||||
|
||||
float scale = Math.min(5, this.ticksDisplayedCrosshair + tickDelta) / 5F;
|
||||
scale *= scale;
|
||||
int opacity = ((int) (255 * scale)) << 24;
|
||||
|
||||
this.client.textRenderer.draw(matrices, text, window.getScaledWidth() / 2.f - this.client.textRenderer.getWidth(text) / 2.f,
|
||||
window.getScaledHeight() / 2.f - 4, 0xCCCCCC | opacity);
|
||||
}
|
||||
}
|
||||
|
||||
public void renderFirstIcons(MatrixStack matrices, int x, int y)
|
||||
@@ -177,7 +192,7 @@ public class LambdaControlsHud extends Hud
|
||||
|
||||
// Update "Use" tip status.
|
||||
if (this.client.crosshairTarget.getType() == HitResult.Type.MISS) {
|
||||
this.placeHitResult = LambdaInput.tryFrontPlace(this.client);
|
||||
this.placeHitResult = this.mod.reacharound.getLastReacharoundResult();
|
||||
this.attackAction = "";
|
||||
this.attackWidth = 0;
|
||||
} else {
|
||||
@@ -190,8 +205,26 @@ public class LambdaControlsHud extends Hud
|
||||
this.attackWidth = this.width(attackAction);
|
||||
}
|
||||
|
||||
ItemStack stack = this.client.player.getMainHandStack();
|
||||
if ((stack == null || stack.isEmpty()) && ((stack = this.client.player.getOffHandStack()) == null || stack.isEmpty())) {
|
||||
if (this.mod.reacharound.isLastReacharoundVertical()) {
|
||||
if (this.ticksDisplayedCrosshair < 5)
|
||||
this.ticksDisplayedCrosshair++;
|
||||
} else {
|
||||
this.ticksDisplayedCrosshair = 0;
|
||||
}
|
||||
|
||||
String customAttackAction = LambdaControlsCompat.getAttackActionAt(this.client, this.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 (this.placeHitResult != null && stack.getItem() instanceof BlockItem) {
|
||||
@@ -201,6 +234,10 @@ public class LambdaControlsHud extends Hud
|
||||
}
|
||||
}
|
||||
|
||||
String customUseAction = LambdaControlsCompat.getUseActionAt(this.client, this.placeHitResult);
|
||||
if (customUseAction != null)
|
||||
placeAction = customUseAction;
|
||||
|
||||
this.placeAction = placeAction;
|
||||
|
||||
// Cache the "Use" tip width.
|
||||
|
||||
@@ -11,6 +11,7 @@ package me.lambdaurora.lambdacontrols.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaInput;
|
||||
import me.lambdaurora.lambdacontrols.client.controller.ButtonBinding;
|
||||
import me.lambdaurora.lambdacontrols.client.util.HandledScreenAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
@@ -28,7 +29,7 @@ import org.lwjgl.glfw.GLFW;
|
||||
* Represents the LambdaControls renderer.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.3.1
|
||||
* @version 1.3.2
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class LambdaControlsRenderer
|
||||
@@ -228,7 +229,7 @@ public class LambdaControlsRenderer
|
||||
|
||||
public static void renderVirtualCursor(@NotNull MatrixStack matrices, @NotNull MinecraftClient client)
|
||||
{
|
||||
if (!LambdaControlsClient.get().config.hasVirtualMouse() || client.currentScreen == null)
|
||||
if (!LambdaControlsClient.get().config.hasVirtualMouse() || (client.currentScreen == null || LambdaInput.isScreenInteractive(client.currentScreen)))
|
||||
return;
|
||||
|
||||
int mouseX = (int) (client.mouse.getX() * (double) client.getWindow().getScaledWidth() / (double) client.getWindow().getWidth());
|
||||
|
||||
@@ -51,6 +51,7 @@ public class LambdaControlsSettingsScreen extends Screen
|
||||
private final Option autoJumpOption;
|
||||
private final Option fastBlockPlacingOption;
|
||||
private final Option frontBlockPlacingOption;
|
||||
private final Option verticalReacharoundOption;
|
||||
private final Option flyDriftingOption;
|
||||
private final Option flyVerticalDriftingOption;
|
||||
// Controller options
|
||||
@@ -104,8 +105,10 @@ public class LambdaControlsSettingsScreen extends Screen
|
||||
this.autoJumpOption = SpruceBooleanOption.fromVanilla("options.autoJump", Option.AUTO_JUMP, null, true);
|
||||
this.fastBlockPlacingOption = new SpruceBooleanOption("lambdacontrols.menu.fast_block_placing", this.mod.config::hasFastBlockPlacing,
|
||||
this.mod.config::setFastBlockPlacing, new TranslatableText("lambdacontrols.tooltip.fast_block_placing"), true);
|
||||
this.frontBlockPlacingOption = new SpruceBooleanOption("lambdacontrols.menu.front_block_placing", this.mod.config::hasFrontBlockPlacing,
|
||||
this.mod.config::setFrontBlockPlacing, new TranslatableText("lambdacontrols.tooltip.front_block_placing"), true);
|
||||
this.frontBlockPlacingOption = new SpruceBooleanOption("lambdacontrols.menu.reacharound.horizontal", this.mod.config::hasFrontBlockPlacing,
|
||||
this.mod.config::setFrontBlockPlacing, new TranslatableText("lambdacontrols.tooltip.reacharound.horizontal"), true);
|
||||
this.verticalReacharoundOption = new SpruceBooleanOption("lambdacontrols.menu.reacharound.vertical", this.mod.config::hasVerticalReacharound,
|
||||
this.mod.config::setVerticalReacharound, new TranslatableText("lambdacontrols.tooltip.reacharound.vertical"), true);
|
||||
this.flyDriftingOption = new SpruceBooleanOption("lambdacontrols.menu.fly_drifting", this.mod.config::hasFlyDrifting,
|
||||
this.mod.config::setFlyDrifting, new TranslatableText("lambdacontrols.tooltip.fly_drifting"), true);
|
||||
this.flyVerticalDriftingOption = new SpruceBooleanOption("lambdacontrols.menu.fly_drifting_vertical", this.mod.config::hasFlyVerticalDrifting,
|
||||
@@ -239,8 +242,8 @@ public class LambdaControlsSettingsScreen extends Screen
|
||||
this.list.addSingleOptionEntry(this.autoSwitchModeOption);
|
||||
// Gameplay options
|
||||
this.list.addSingleOptionEntry(new SpruceSeparatorOption("lambdacontrols.menu.title.gameplay", true, null));
|
||||
this.list.addSingleOptionEntry(this.autoJumpOption);
|
||||
this.list.addOptionEntry(this.fastBlockPlacingOption, this.frontBlockPlacingOption);
|
||||
this.list.addOptionEntry(this.autoJumpOption, this.fastBlockPlacingOption);
|
||||
this.list.addOptionEntry(this.frontBlockPlacingOption, this.verticalReacharoundOption);
|
||||
this.list.addSingleOptionEntry(this.flyDriftingOption);
|
||||
this.list.addSingleOptionEntry(this.flyVerticalDriftingOption);
|
||||
// Controller options
|
||||
|
||||
@@ -11,9 +11,8 @@ package me.lambdaurora.lambdacontrols.client.mixin;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.LambdaControlsFeature;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaInput;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaReacharound;
|
||||
import me.lambdaurora.lambdacontrols.client.gui.LambdaControlsRenderer;
|
||||
import me.lambdaurora.lambdacontrols.client.util.FrontBlockPlaceResultAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
@@ -40,7 +39,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
@Mixin(MinecraftClient.class)
|
||||
public abstract class MinecraftClientMixin implements FrontBlockPlaceResultAccessor
|
||||
public abstract class MinecraftClientMixin
|
||||
{
|
||||
@Shadow
|
||||
@Nullable
|
||||
@@ -65,18 +64,10 @@ public abstract class MinecraftClientMixin implements FrontBlockPlaceResultAcces
|
||||
@Shadow
|
||||
private int itemUseCooldown;
|
||||
|
||||
private BlockHitResult lambdacontrols_frontBlockPlaceResult = null;
|
||||
|
||||
private BlockPos lambdacontrols_lastTargetPos;
|
||||
private Vec3d lambdacontrols_lastPos;
|
||||
private Direction lambdacontrols_lastTargetSide;
|
||||
|
||||
@Override
|
||||
public @Nullable BlockHitResult lambdacontrols_getFrontBlockPlaceResult()
|
||||
{
|
||||
return this.lambdacontrols_frontBlockPlaceResult;
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onInit(CallbackInfo ci)
|
||||
{
|
||||
@@ -88,7 +79,7 @@ public abstract class MinecraftClientMixin implements FrontBlockPlaceResultAcces
|
||||
{
|
||||
if (this.player == null)
|
||||
return;
|
||||
this.lambdacontrols_frontBlockPlaceResult = LambdaInput.tryFrontPlace(((MinecraftClient) (Object) this));
|
||||
|
||||
if (!LambdaControlsFeature.FAST_BLOCK_PLACING.isAvailable())
|
||||
return;
|
||||
if (this.lambdacontrols_lastPos == null)
|
||||
@@ -131,7 +122,8 @@ public abstract class MinecraftClientMixin implements FrontBlockPlaceResultAcces
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;render(FJZ)V", shift = At.Shift.AFTER))
|
||||
private void renderVirtualCursor(boolean fullRender, CallbackInfo ci) {
|
||||
private void renderVirtualCursor(boolean fullRender, CallbackInfo ci)
|
||||
{
|
||||
LambdaControlsRenderer.renderVirtualCursor(new MatrixStack(), (MinecraftClient) (Object) this);
|
||||
}
|
||||
|
||||
@@ -144,15 +136,15 @@ public abstract class MinecraftClientMixin implements FrontBlockPlaceResultAcces
|
||||
@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 (!stackInHand.isEmpty() && this.player.pitch > 35.0F && LambdaControlsFeature.FRONT_BLOCK_PLACING.isAvailable()) {
|
||||
if (!stackInHand.isEmpty() && this.player.pitch > 35.0F && LambdaControlsClient.get().reacharound.isReacharoundAvailable()) {
|
||||
if (this.crosshairTarget != null && this.crosshairTarget.getType() == HitResult.Type.MISS && this.player.isOnGround()) {
|
||||
if (!stackInHand.isEmpty() && stackInHand.getItem() instanceof BlockItem) {
|
||||
BlockHitResult hitResult = LambdaInput.tryFrontPlace(((MinecraftClient) (Object) this));
|
||||
BlockHitResult hitResult = LambdaControlsClient.get().reacharound.getLastReacharoundResult();
|
||||
|
||||
if (hitResult == null)
|
||||
return;
|
||||
|
||||
hitResult = LambdaInput.withSideForFrontPlace(hitResult, stackInHand);
|
||||
hitResult = LambdaReacharound.withSideForReacharound(hitResult, stackInHand);
|
||||
|
||||
int previousStackCount = stackInHand.getCount();
|
||||
ActionResult result = this.interactionManager.interactBlock(this.player, this.world, hand, hitResult);
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
package me.lambdaurora.lambdacontrols.client.mixin;
|
||||
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaControlsClient;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaInput;
|
||||
import me.lambdaurora.lambdacontrols.client.util.FrontBlockPlaceResultAccessor;
|
||||
import me.lambdaurora.lambdacontrols.client.LambdaReacharound;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
@@ -74,9 +73,9 @@ public abstract class WorldRendererMixin
|
||||
Profiler profiler, Vec3d cameraPos, double x, double y, double z, Matrix4f modelMatrix, boolean bl, Frustum frustum2, boolean bl3,
|
||||
VertexConsumerProvider.Immediate immediate)
|
||||
{
|
||||
if (this.client.crosshairTarget == null || this.client.crosshairTarget.getType() != HitResult.Type.MISS || !LambdaControlsClient.get().config.shouldRenderFrontBlockOutline())
|
||||
if (this.client.crosshairTarget == null || this.client.crosshairTarget.getType() != HitResult.Type.MISS || !LambdaControlsClient.get().config.shouldRenderReacharoundOutline())
|
||||
return;
|
||||
BlockHitResult result = ((FrontBlockPlaceResultAccessor) client).lambdacontrols_getFrontBlockPlaceResult();
|
||||
BlockHitResult result = LambdaControlsClient.get().reacharound.getLastReacharoundResult();
|
||||
if (result == null)
|
||||
return;
|
||||
BlockPos blockPos = result.getBlockPos();
|
||||
@@ -85,14 +84,14 @@ public abstract class WorldRendererMixin
|
||||
if (stack == null || !(stack.getItem() instanceof BlockItem))
|
||||
return;
|
||||
Block block = ((BlockItem) stack.getItem()).getBlock();
|
||||
result = LambdaInput.withSideForFrontPlace(result, block);
|
||||
result = LambdaReacharound.withSideForReacharound(result, block);
|
||||
ItemPlacementContext context = new ItemPlacementContext(new ItemUsageContext(this.client.player, Hand.MAIN_HAND, result));
|
||||
VertexConsumer vertexConsumer = immediate.getBuffer(RenderLayer.getLines());
|
||||
BlockState placementState = block.getPlacementState(context);
|
||||
if (placementState == null)
|
||||
return;
|
||||
VoxelShape outlineShape = placementState.getOutlineShape(this.client.world, blockPos, ShapeContext.of(camera.getFocusedEntity()));
|
||||
int[] color = LambdaControlsClient.get().config.getFrontBlockOutlineColor();
|
||||
int[] color = LambdaControlsClient.get().config.getReacharoundOutlineColor();
|
||||
drawShapeOutline(matrices, vertexConsumer, outlineShape, (double) blockPos.getX() - x, (double) blockPos.getY() - y, (double) blockPos.getZ() - z, color[0] / 255.f, color[1] / 255.f, color[2] / 255.f, color[3] / 255.f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
|
||||
*
|
||||
* This file is part of LambdaControls.
|
||||
*
|
||||
* Licensed under the MIT license. For more information,
|
||||
* see the LICENSE file.
|
||||
*/
|
||||
|
||||
package me.lambdaurora.lambdacontrols.client.util;
|
||||
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an accessor of the BlockHitResult for the front block placing feature.
|
||||
* <p>
|
||||
* It is implemented by {@link net.minecraft.client.MinecraftClient}.
|
||||
*
|
||||
* @author LambdAurora
|
||||
* @version 1.2.0
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface FrontBlockPlaceResultAccessor
|
||||
{
|
||||
/**
|
||||
* Returns the {@link BlockHitResult} if a block can be placed with the front block placing feature.
|
||||
*
|
||||
* @return If possible a {@link BlockHitResult}, else a null value.
|
||||
*/
|
||||
@Nullable BlockHitResult lambdacontrols_getFrontBlockPlaceResult();
|
||||
}
|
||||
@@ -82,13 +82,14 @@
|
||||
"lambdacontrols.menu.fast_block_placing": "Fast Block Placing",
|
||||
"lambdacontrols.menu.fly_drifting": "Fly Drifting",
|
||||
"lambdacontrols.menu.fly_drifting_vertical": "Vertical Fly Drifting",
|
||||
"lambdacontrols.menu.front_block_placing": "Front Block Placing",
|
||||
"lambdacontrols.menu.hud_enable": "Enable HUD",
|
||||
"lambdacontrols.menu.hud_side": "HUD Side",
|
||||
"lambdacontrols.menu.invert_right_x_axis": "Invert Right X",
|
||||
"lambdacontrols.menu.invert_right_y_axis": "Invert Right Y",
|
||||
"lambdacontrols.menu.keyboard_controls": "Keyboard Controls...",
|
||||
"lambdacontrols.menu.mouse_speed": "Mouse Speed",
|
||||
"lambdacontrols.menu.reacharound.horizontal": "Front Block Placing",
|
||||
"lambdacontrols.menu.reacharound.vertical": "Vertical Reacharound",
|
||||
"lambdacontrols.menu.reload_controller_mappings": "Reload Controller Mappings",
|
||||
"lambdacontrols.menu.rotation_speed": "Rotation Speed",
|
||||
"lambdacontrols.menu.title": "LambdaControls - Settings",
|
||||
@@ -111,12 +112,13 @@
|
||||
"lambdacontrols.tooltip.fast_block_placing": "While flying in creative mode, enables fast block placing depending on your speed. §cOn some servers this might be considered as cheating.",
|
||||
"lambdacontrols.tooltip.fly_drifting": "While flying, enables Vanilla drifting/inertia.",
|
||||
"lambdacontrols.tooltip.fly_drifting_vertical": "While flying, enables Vanilla vertical drifting/intertia.",
|
||||
"lambdacontrols.tooltip.front_block_placing": "Enables front block placing, §cmight be considered cheating on some servers§r.",
|
||||
"lambdacontrols.tooltip.hud_enable": "Toggles the on-screen controller button indicator.",
|
||||
"lambdacontrols.tooltip.hud_side": "The position of the HUD.",
|
||||
"lambdacontrols.tooltip.mouse_speed": "The controller's emulated mouse speed.",
|
||||
"lambdacontrols.tooltip.rotation_speed": "The camera rotation speed in controller mode.",
|
||||
"lambdacontrols.tooltip.reacharound.horizontal": "Enables front block placing, §cmight be considered cheating on some servers§r.",
|
||||
"lambdacontrols.tooltip.reacharound.vertical": "Enables vertical reacharound, §cmight be considered cheating on some servers§r.",
|
||||
"lambdacontrols.tooltip.reload_controller_mappings": "Reloads the controller mappings file.",
|
||||
"lambdacontrols.tooltip.rotation_speed": "The camera rotation speed in controller mode.",
|
||||
"lambdacontrols.tooltip.unfocused_input": "Allow controller input when the window is not focused.",
|
||||
"lambdacontrols.tooltip.virtual_mouse": "Enable the virtual mouse which is handful in the case of a splitscreen.",
|
||||
"lambdacontrols.virtual_mouse.skin.default_light": "Default Light",
|
||||
|
||||
@@ -81,13 +81,14 @@
|
||||
"lambdacontrols.menu.dead_zone": "Zone morte",
|
||||
"lambdacontrols.menu.fly_drifting": "Inertie de vol",
|
||||
"lambdacontrols.menu.fly_drifting_vertical": "Inertie verticale de vol",
|
||||
"lambdacontrols.menu.front_block_placing": "Placement avant de bloc",
|
||||
"lambdacontrols.menu.hud_enable": "Activer le HUD",
|
||||
"lambdacontrols.menu.hud_side": "Côté du HUD",
|
||||
"lambdacontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)",
|
||||
"lambdacontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)",
|
||||
"lambdacontrols.menu.keyboard_controls": "Contrôles clavier...",
|
||||
"lambdacontrols.menu.mouse_speed": "Vitesse de la souris",
|
||||
"lambdacontrols.menu.reacharound.horizontal": "Placement avant de bloc",
|
||||
"lambdacontrols.menu.reacharound.vertical": "Placement vertical",
|
||||
"lambdacontrols.menu.reload_controller_mappings": "Recharge la configuration des manettes",
|
||||
"lambdacontrols.menu.rotation_speed": "Vitesse de rotation",
|
||||
"lambdacontrols.menu.title": "LambdaControls - Paramètres",
|
||||
@@ -107,14 +108,16 @@
|
||||
"lambdacontrols.tooltip.controller_type": "Le type de contrôle n'influe que sur les boutons affichés.",
|
||||
"lambdacontrols.tooltip.controls_mode": "Change le mode de contrôle.",
|
||||
"lambdacontrols.tooltip.dead_zone": "Zone morte des axes de la manette.",
|
||||
"lambdacontrols.tooltip.fast_block_placing": "Active le placement rapide de blocs en vol.",
|
||||
"lambdacontrols.tooltip.fly_drifting": "Pendant que le joueur vole, active le glissement Vanilla.",
|
||||
"lambdacontrols.tooltip.fly_drifting_vertical": "Pendant que le joueur vole, active le glissement vertical Vanilla.",
|
||||
"lambdacontrols.tooltip.front_block_placing": "Active le placement avant de blocs, §cpeut être considérer comme de la trice sur certains serveurs§r.",
|
||||
"lambdacontrols.tooltip.hud_enable": "Détermine si l'indicateur des buttons de la manette doit être affiché ou non.",
|
||||
"lambdacontrols.tooltip.hud_side": "Change la position du HUD.",
|
||||
"lambdacontrols.tooltip.mouse_speed": "Change la vitesse de la souris émulée par la manette.",
|
||||
"lambdacontrols.tooltip.rotation_speed": "Change la vitesse de rotation de la caméra.",
|
||||
"lambdacontrols.tooltip.reacharound.horizontal": "Active le placement avant de blocs, §cpeut être considérer comme de la triche sur certains serveurs§r.",
|
||||
"lambdacontrols.tooltip.reacharound.vertical": "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.",
|
||||
"lambdacontrols.tooltip.reload_controller_mappings": "Recharge le fichier de configuration des manettes.",
|
||||
"lambdacontrols.tooltip.rotation_speed": "Change la vitesse de rotation de la caméra.",
|
||||
"lambdacontrols.tooltip.unfocused_input": "Autorise les entrées manette quand la fenêtre n'est pas sélectionnée.",
|
||||
"lambdacontrols.tooltip.virtual_mouse": "Active la souris virtuelle qui est pratique dans le cas d'un écran partagé.",
|
||||
"lambdacontrols.virtual_mouse.skin.default_light": "défaut clair",
|
||||
|
||||
@@ -81,13 +81,14 @@
|
||||
"lambdacontrols.menu.dead_zone": "Zone morte",
|
||||
"lambdacontrols.menu.fly_drifting": "Inertie de vol",
|
||||
"lambdacontrols.menu.fly_drifting_vertical": "Inertie verticale de vol",
|
||||
"lambdacontrols.menu.front_block_placing": "Placement avant de bloc",
|
||||
"lambdacontrols.menu.hud_enable": "Activer le HUD",
|
||||
"lambdacontrols.menu.hud_side": "Côté du HUD",
|
||||
"lambdacontrols.menu.invert_right_x_axis": "Inverser le stick droit (X)",
|
||||
"lambdacontrols.menu.invert_right_y_axis": "Inverser le stick droit (Y)",
|
||||
"lambdacontrols.menu.keyboard_controls": "Contrôles clavier...",
|
||||
"lambdacontrols.menu.mouse_speed": "Vitesse de la souris",
|
||||
"lambdacontrols.menu.reacharound.horizontal": "Placement avant de bloc",
|
||||
"lambdacontrols.menu.reacharound.vertical": "Placement vertical",
|
||||
"lambdacontrols.menu.reload_controller_mappings": "Recharge la configuration des manettes",
|
||||
"lambdacontrols.menu.rotation_speed": "Vitesse de rotation",
|
||||
"lambdacontrols.menu.title": "LambdaControls - Paramètres",
|
||||
@@ -107,14 +108,16 @@
|
||||
"lambdacontrols.tooltip.controller_type": "Le type de contrôle n'influe que sur les boutons affichés.",
|
||||
"lambdacontrols.tooltip.controls_mode": "Change le mode de contrôle.",
|
||||
"lambdacontrols.tooltip.dead_zone": "Zone morte des axes de la manette.",
|
||||
"lambdacontrols.tooltip.fast_block_placing": "Active le placement rapide de blocs en vol.",
|
||||
"lambdacontrols.tooltip.fly_drifting": "Pendant que le joueur vole, active le glissement Vanilla.",
|
||||
"lambdacontrols.tooltip.fly_drifting_vertical": "Pendant que le joueur vole, active le glissement vertical Vanilla.",
|
||||
"lambdacontrols.tooltip.front_block_placing": "Active le placement avant de blocs, §cpeut être considérer comme de la trice sur certains serveurs§r.",
|
||||
"lambdacontrols.tooltip.hud_enable": "Détermine si l'indicateur des buttons de la manette doit être affiché ou non.",
|
||||
"lambdacontrols.tooltip.hud_side": "Change la position du HUD.",
|
||||
"lambdacontrols.tooltip.mouse_speed": "Change la vitesse de la souris émulée par la manette.",
|
||||
"lambdacontrols.tooltip.rotation_speed": "Change la vitesse de rotation de la caméra.",
|
||||
"lambdacontrols.tooltip.reacharound.horizontal": "Active le placement avant de blocs, §cpeut être considérer comme de la triche sur certains serveurs§r.",
|
||||
"lambdacontrols.tooltip.reacharound.vertical": "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.",
|
||||
"lambdacontrols.tooltip.reload_controller_mappings": "Recharge le fichier de configuration des manettes.",
|
||||
"lambdacontrols.tooltip.rotation_speed": "Change la vitesse de rotation de la caméra.",
|
||||
"lambdacontrols.tooltip.unfocused_input": "Autorise les entrées manette quand la fenêtre n'est pas sélectionnée.",
|
||||
"lambdacontrols.tooltip.virtual_mouse": "Active la souris virtuelle qui est pratique dans le cas d'un écran partagé.",
|
||||
"lambdacontrols.virtual_mouse.skin.default_light": "défaut clair",
|
||||
|
||||
@@ -21,9 +21,11 @@ auto_switch_mode = false
|
||||
drifting = false
|
||||
# Enables vertical fly drifting.
|
||||
vertical_drifting = true
|
||||
[gameplay.front_block_placing]
|
||||
[gameplay.reacharound]
|
||||
# Enables front block placing like in Bedrock Edition.
|
||||
enabled = false
|
||||
horizontal = false
|
||||
# Enables vertical reacharound.
|
||||
vertical = false
|
||||
# Enables front block placing outline.
|
||||
outline = true
|
||||
# The color in a hexadecimal format of the outline.
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
"spruceui": ">=1.5.2"
|
||||
},
|
||||
"recommends": {
|
||||
"modmenu": ">=1.12.2",
|
||||
"okzoomer": ">=4.0.0"
|
||||
"modmenu": ">=1.12.2"
|
||||
},
|
||||
"suggests": {
|
||||
"flamingo": "*",
|
||||
"roughlyenoughitems": ">=4.5.5"
|
||||
"roughlyenoughitems": ">=4.5.5",
|
||||
"okzoomer": ">=4.0.0"
|
||||
},
|
||||
"breaks": {
|
||||
"modmenu": "<1.12.2",
|
||||
|
||||
@@ -8,7 +8,7 @@ org.gradle.jvmargs=-Xmx1G
|
||||
loader_version=0.8.8+build.202
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.3.1
|
||||
mod_version = 1.3.2
|
||||
maven_group = me.lambdaurora.lambdacontrols
|
||||
archives_base_name = lambdacontrols
|
||||
|
||||
|
||||
Reference in New Issue
Block a user