Skip to content

Commit

Permalink
Initial JumpTo GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
MattSturgeon committed Nov 28, 2023
1 parent 82466d9 commit 12230a7
Show file tree
Hide file tree
Showing 12 changed files with 574 additions and 4 deletions.
46 changes: 44 additions & 2 deletions common/src/main/java/net/xolt/freecam/Freecam.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import net.minecraft.text.Text;
import net.minecraft.util.math.ChunkPos;
import net.xolt.freecam.config.ModConfig;
import net.xolt.freecam.gui.jumpTo.JumpToScreen;
import net.xolt.freecam.util.FreeCamera;
import net.xolt.freecam.util.FreecamPosition;
import org.lwjgl.glfw.GLFW;

import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.stream.Stream;

public class Freecam {
Expand All @@ -30,6 +33,8 @@ public class Freecam {
"key.freecam.playerControl", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, "category.freecam.freecam");
public static final KeyBinding KEY_TRIPOD_RESET = new KeyBinding(
"key.freecam.tripodReset", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, "category.freecam.freecam");
public static final KeyBinding KEY_JUMP_GUI = new KeyBinding(
"key.freecam.jumpTo", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_J, "category.freecam.freecam");
public static final KeyBinding KEY_CONFIG_GUI = new KeyBinding(
"key.freecam.configGui", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, "category.freecam.freecam");

Expand All @@ -46,7 +51,7 @@ public class Freecam {

public static void init() {
ModConfig.init();
Stream.of(KEY_TOGGLE, KEY_PLAYER_CONTROL, KEY_TRIPOD_RESET, KEY_CONFIG_GUI).forEach(KeyMappingRegistry::register);
Stream.of(KEY_TOGGLE, KEY_PLAYER_CONTROL, KEY_TRIPOD_RESET, KEY_JUMP_GUI, KEY_CONFIG_GUI).forEach(KeyMappingRegistry::register);

ClientTickEvent.CLIENT_POST.register(client -> {
if (KEY_TRIPOD_RESET.isPressed()) {
Expand Down Expand Up @@ -74,6 +79,10 @@ public static void init() {
switchControls();
}

while (KEY_JUMP_GUI.wasPressed()) {
MC.setScreen(new JumpToScreen());
}

while (KEY_CONFIG_GUI.wasPressed()) {
MC.setScreen(AutoConfig.getConfigScreen(ModConfig.class, MC.currentScreen).get());
}
Expand All @@ -97,6 +106,35 @@ public static void toggle() {
}
}

public static void jumpTo(FreecamPosition position, String name, boolean perspective) {
long notifDelay = tripodEnabled || !freecamEnabled
? 1500 : 1;

if (tripodEnabled) {
toggleTripod(activeTripod);
}

if (!freecamEnabled) {
toggle();
}

freeCamera.applyPosition(position);
if (perspective) {
freeCamera.applyPerspective(ModConfig.INSTANCE.hidden.jumpToPerspective, checkInitialCollision());
}

if (ModConfig.INSTANCE.notification.notifyJumpTo) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
if (freecamEnabled) {
MC.player.sendMessage(Text.translatable("msg.freecam.jumpTo", name), true);
}
}
}, notifDelay);
}
}

private static void toggleTripod(Integer keyCode) {
if (keyCode == null) {
return;
Expand Down Expand Up @@ -181,7 +219,7 @@ private static void onDisableTripod() {
private static void onEnableFreecam() {
onEnable();
freeCamera = new FreeCamera(-420);
freeCamera.applyPerspective(ModConfig.INSTANCE.visual.perspective, ModConfig.INSTANCE.collision.alwaysCheck || !ModConfig.INSTANCE.collision.ignoreAll);
freeCamera.applyPerspective(ModConfig.INSTANCE.visual.perspective, checkInitialCollision());
freeCamera.spawn();
MC.setCameraEntity(freeCamera);

Expand Down Expand Up @@ -268,6 +306,10 @@ public static HashMap<Integer, FreecamPosition> getTripodsForDimension() {
return result;
}

private static boolean checkInitialCollision() {
return ModConfig.INSTANCE.collision.alwaysCheck || !ModConfig.INSTANCE.collision.ignoreAll;
}

public static boolean disableNextTick() {
return disableNextTick;
}
Expand Down
10 changes: 10 additions & 0 deletions common/src/main/java/net/xolt/freecam/config/ModConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import me.shedaniel.autoconfig.annotation.ConfigEntry;
import me.shedaniel.autoconfig.serializer.JanksonConfigSerializer;
import me.shedaniel.clothconfig2.gui.entries.SelectionListEntry;
import org.jetbrains.annotations.NotNull;

import static me.shedaniel.autoconfig.annotation.ConfigEntry.Gui.EnumHandler.EnumDisplayOption;

Expand Down Expand Up @@ -100,6 +101,15 @@ public static class NotificationConfig {

@ConfigEntry.Gui.Tooltip
public boolean notifyTripod = true;

@ConfigEntry.Gui.Tooltip
public boolean notifyJumpTo = true;
}

@ConfigEntry.Gui.Excluded
public Hidden hidden = new Hidden();
public static class Hidden {
public Perspective jumpToPerspective = Perspective.THIRD_PERSON;
}

public enum FlightMode implements SelectionListEntry.Translatable {
Expand Down
245 changes: 245 additions & 0 deletions common/src/main/java/net/xolt/freecam/gui/jumpTo/JumpToScreen.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
package net.xolt.freecam.gui.jumpTo;

import me.shedaniel.autoconfig.AutoConfig;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.*;
import net.minecraft.client.input.KeyCodes;
import net.minecraft.screen.ScreenTexts;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.xolt.freecam.Freecam;
import net.xolt.freecam.config.ModConfig;
import net.xolt.freecam.gui.Texture;
import net.xolt.freecam.util.FreeCamera;
import org.lwjgl.glfw.GLFW;

import java.util.*;
import java.util.stream.Collectors;

public class JumpToScreen extends Screen {
private static final int GUI_WIDTH = 236;
private static final int GUI_TOP = 50;
private static final int LIST_TOP = GUI_TOP + 8;
private static final int LIST_ITEM_HEIGHT = 36;
private static final Identifier SEARCH_ICON_TEXTURE = new Identifier("icon/search");
private static final Text SEARCH_TEXT = Text.translatable("gui.recipebook.search_hint").formatted(Formatting.ITALIC).formatted(Formatting.GRAY);
private static final Texture JUMP_BACKGROUND = new Texture(new Identifier(Freecam.MOD_ID, "textures/gui/jump_background.png"));
private static final Texture JUMP_LIST_BACKGROUND = new Texture(new Identifier(Freecam.MOD_ID, "textures/gui/jump_list_background.png"));

private Tab tab = Tab.PLAYER;
private ListWidget list;
private boolean initialized;
private ButtonWidget buttonBack;
private ButtonWidget buttonJump;
private CyclingButtonWidget<ModConfig.Perspective> buttonPerspective;
private TextFieldWidget searchBox;
private String currentSearch;

public JumpToScreen() {
super(Text.translatable("gui.freecam.jumpTo.title"));
}

@Override
protected void init() {
super.init();

if (!this.initialized) {
this.list = new ListWidget(this, this.client, this.width, this.height, 0, 0, LIST_ITEM_HEIGHT);
this.searchBox = new TextFieldWidget(this.textRenderer, 0, 15, SEARCH_TEXT);
this.searchBox.setPlaceholder(SEARCH_TEXT);
this.searchBox.setMaxLength(16);
this.searchBox.setVisible(true);
this.searchBox.setEditableColor(0xFFFFFF);
this.searchBox.setChangedListener(this::onSearchChange);

this.buttonJump = ButtonWidget.builder(Text.translatable("gui.freecam.jumpTo.button.jump"), button -> this.jump())
.tooltip(Tooltip.of(Text.translatable("gui.freecam.jumpTo.button.jump.@Tooltip")))
.width(48)
.build();

this.buttonPerspective = CyclingButtonWidget
.builder((ModConfig.Perspective value) -> Text.translatable(value.getKey()))
.values(ModConfig.Perspective.values())
.initially(ModConfig.INSTANCE.hidden.jumpToPerspective)
.tooltip(value -> Tooltip.of(Text.translatable("gui.freecam.jumpTo.button.perspective.@Tooltip", value)))
.omitKeyText()
.build(0, 0, 80, 20, null, (button, value) -> {
ModConfig.INSTANCE.hidden.jumpToPerspective = value;
AutoConfig.getConfigHolder(ModConfig.class).save();
});

this.buttonBack = ButtonWidget.builder(ScreenTexts.BACK, button -> this.close()).width(48).build();
}

int listTop = LIST_TOP + 16;
int listBottom = this.getListBottom();
int innerWidth = GUI_WIDTH - 10;
int innerX = (this.width - innerWidth) / 2;

this.list.updateSize(this.width, this.height, listTop, listBottom);
this.searchBox.setPosition(innerX + 20, LIST_TOP + 1);
this.searchBox.setWidth(this.list.getRowWidth() - 19);

SimplePositioningWidget positioner = new SimplePositioningWidget(innerX, listBottom + 3, innerWidth, 0);
positioner.getMainPositioner()
.alignBottom()
.alignRight();
DirectionalLayoutWidget layout = positioner.add(DirectionalLayoutWidget.horizontal());
layout.getMainPositioner()
.alignBottom()
.marginX(2);

layout.add(this.buttonBack);
if (tab == Tab.PLAYER) {
layout.add(this.buttonPerspective);
}
layout.add(this.buttonJump);

positioner.refreshPositions();
positioner.forEachChild(this::addDrawableChild);

List.of(this.searchBox, this.list).forEach(this::addDrawableChild);
this.setInitialFocus(this.list);

this.initialized = true;
}

@Override
public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) {
super.renderBackground(context, mouseX, mouseY, delta);
int left = (this.width - GUI_WIDTH) / 2;
JUMP_BACKGROUND.draw(context, left, GUI_TOP, 0, GUI_WIDTH, this.getGuiHeight());
JUMP_LIST_BACKGROUND.draw(context, left + 7, LIST_TOP - 1, 0, this.list.getRowWidth() + 2, this.getListHeight() + 2);
context.drawGuiTexture(SEARCH_ICON_TEXTURE, left + 10, LIST_TOP + 3, 12, 12);
}

// GUI height
private int getGuiHeight() {
return Math.max(52, this.height - (GUI_TOP * 2));
}

// List height including search bar
private int getListHeight() {
return this.getGuiHeight() - 29 - 8;
}

private int getListBottom() {
return LIST_TOP + this.getListHeight();
}

@Override
public void tick() {
super.tick();
if (this.initialized) {
this.updateEntries();
}
}

@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (this.searchBox.isFocused()) {
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
this.focusOn(null);
return true;
}
} else {
if (Freecam.KEY_JUMP_GUI.matchesKey(keyCode, scanCode)) {
this.close();
return true;
}
}
if (this.list.getSelectedOrNull() != null) {
if (KeyCodes.isToggle(keyCode)) {
this.jump();
return true;
}
if (this.list.keyPressed(keyCode, scanCode, modifiers)) {
return true;
}
}
return super.keyPressed(keyCode, scanCode, modifiers);
}

@Override
public boolean shouldPause() {
return false;
}

public void updateEntries() {
List<ListEntry> entries = (switch (tab) {
case PLAYER -> updatePlayerEntries();
case COORDS -> updateCoordEntries();
}).stream()
.filter(entry -> this.currentSearch == null
|| this.currentSearch.isEmpty()
|| entry.matches(this.currentSearch))
.sorted()
.toList();

// Update only if the list has changed
if (!Objects.equals(this.list.children(), entries)) {
this.list.updateEntries(entries);
}
}

private List<ListEntry> updateCoordEntries() {
// TODO
return Collections.emptyList();
}

private List<ListEntry> updatePlayerEntries() {
// Store the existing entries in a UUID map for easy lookup
Map<UUID, PlayerListEntry> currentEntries = this.list.children()
.parallelStream()
.filter(PlayerListEntry.class::isInstance)
.map(PlayerListEntry.class::cast)
.collect(Collectors.toUnmodifiableMap(PlayerListEntry::getUUID, entry -> entry));

// Map the in-range players into PlayerListEntries
// Use existing entries if possible
return this.client.world.getPlayers()
.parallelStream()
.filter(player -> !(player instanceof FreeCamera))
.map(player -> {
PlayerListEntry entry = currentEntries.get(player.getUuid());
if (entry == null) {
return new PlayerListEntry(this.client, this, player);
} else {
entry.update(player);
return entry;
}
})
.map(ListEntry.class::cast)
.toList();
}

private void onSearchChange(String search) {
this.currentSearch = search.toLowerCase(Locale.ROOT);
}

public void select(ListEntry entry) {
this.list.setSelected(entry);
this.updateButtonState();
}

public void updateButtonState() {
ListEntry selected = this.list.getSelectedOrNull();
this.buttonJump.active = selected != null;
}

public void jump() {
Optional.ofNullable(this.list.getSelectedOrNull())
.ifPresent(listEntry -> {
boolean perspective = this.buttonPerspective != null && this.buttonPerspective.active;
this.close();
Freecam.jumpTo(listEntry.getPosition(), listEntry.getName(), perspective);
});
}

private enum Tab {
PLAYER, COORDS;
}
}
Loading

0 comments on commit 12230a7

Please sign in to comment.