Added Quests and Real Settings (yay)
This commit is contained in:
@@ -18,6 +18,9 @@ public class Config {
|
|||||||
public int fpsY = 1;
|
public int fpsY = 1;
|
||||||
public String lastModule = "FPS";
|
public String lastModule = "FPS";
|
||||||
|
|
||||||
|
public int keyX = 1;
|
||||||
|
public int keyY = 1;
|
||||||
|
|
||||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||||
private static final Path PATH = FabricLoader.getInstance()
|
private static final Path PATH = FabricLoader.getInstance()
|
||||||
.getConfigDir()
|
.getConfigDir()
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package de.fullrisk.citrusDev.UI;
|
||||||
|
|
||||||
|
import io.wispforest.owo.ui.component.LabelComponent;
|
||||||
|
import io.wispforest.owo.ui.component.UIComponents;
|
||||||
|
import io.wispforest.owo.ui.container.FlowLayout;
|
||||||
|
import io.wispforest.owo.ui.container.UIContainers;
|
||||||
|
import io.wispforest.owo.ui.core.*;
|
||||||
|
import io.wispforest.owo.ui.hud.Hud;
|
||||||
|
import net.minecraft.client.KeyMapping;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.Identifier;
|
||||||
|
import de.fullrisk.citrusDev.Config;
|
||||||
|
|
||||||
|
public class Module_KeyStrokes {
|
||||||
|
|
||||||
|
private static FlowLayout keyContainer;
|
||||||
|
private static FlowLayout topRow;
|
||||||
|
private static FlowLayout bottomRow;
|
||||||
|
|
||||||
|
private static FlowLayout wBox, aBox, sBox, dBox;
|
||||||
|
private static LabelComponent wLabel, aLabel, sLabel, dLabel;
|
||||||
|
|
||||||
|
static boolean toggle = true;
|
||||||
|
private static boolean shown = false;
|
||||||
|
public static boolean isEnabled() {
|
||||||
|
return toggle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setEnabled(boolean value) {
|
||||||
|
toggle = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Config CONFIG = Config.get();
|
||||||
|
|
||||||
|
private static final Surface GLASS_PANEL = Surface.blur(5f, 15f)
|
||||||
|
.and(Surface.flat(0x40FFFFFF));
|
||||||
|
|
||||||
|
private static final int KEY_SIZE = 16;
|
||||||
|
private static final int PRESSED_COLOR = 0xFF64de4b;
|
||||||
|
private static final int IDLE_COLOR = 0x40FFFFFF;
|
||||||
|
|
||||||
|
public static void setPosition(int x, int y) {
|
||||||
|
if (keyContainer != null) {
|
||||||
|
keyContainer.positioning(Positioning.relative(x, y));
|
||||||
|
}
|
||||||
|
CONFIG.keyX = x;
|
||||||
|
CONFIG.keyY = y;
|
||||||
|
CONFIG.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register() {
|
||||||
|
Hud.add(Identifier.fromNamespaceAndPath("citrus-dev", "keystrokes_display"), () -> {
|
||||||
|
if (keyContainer == null) {
|
||||||
|
wLabel = UIComponents.label(Component.literal("W"));
|
||||||
|
aLabel = UIComponents.label(Component.literal("A"));
|
||||||
|
sLabel = UIComponents.label(Component.literal("S"));
|
||||||
|
dLabel = UIComponents.label(Component.literal("D"));
|
||||||
|
|
||||||
|
wBox = makeKeyBox(wLabel);
|
||||||
|
aBox = makeKeyBox(aLabel);
|
||||||
|
sBox = makeKeyBox(sLabel);
|
||||||
|
dBox = makeKeyBox(dLabel);
|
||||||
|
|
||||||
|
topRow = UIContainers.horizontalFlow(Sizing.content(), Sizing.content());
|
||||||
|
topRow.gap(2);
|
||||||
|
topRow.horizontalAlignment(HorizontalAlignment.CENTER);
|
||||||
|
topRow.child(UIContainers.horizontalFlow(Sizing.fixed(KEY_SIZE), Sizing.fixed(KEY_SIZE))); // spacer
|
||||||
|
topRow.child(wBox);
|
||||||
|
topRow.child(UIContainers.horizontalFlow(Sizing.fixed(KEY_SIZE), Sizing.fixed(KEY_SIZE))); // spacer
|
||||||
|
|
||||||
|
bottomRow = UIContainers.horizontalFlow(Sizing.content(), Sizing.content());
|
||||||
|
bottomRow.gap(2);
|
||||||
|
bottomRow.horizontalAlignment(HorizontalAlignment.CENTER);
|
||||||
|
bottomRow.child(aBox);
|
||||||
|
bottomRow.child(sBox);
|
||||||
|
bottomRow.child(dBox);
|
||||||
|
|
||||||
|
keyContainer = UIContainers.verticalFlow(Sizing.content(), Sizing.content());
|
||||||
|
keyContainer.gap(2);
|
||||||
|
keyContainer.verticalAlignment(VerticalAlignment.CENTER);
|
||||||
|
keyContainer.horizontalAlignment(HorizontalAlignment.CENTER);
|
||||||
|
keyContainer.padding(Insets.of(0));
|
||||||
|
|
||||||
|
keyContainer.positioning(Positioning.relative(CONFIG.keyX, CONFIG.keyY));
|
||||||
|
}
|
||||||
|
return keyContainer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FlowLayout makeKeyBox(LabelComponent label) {
|
||||||
|
FlowLayout box = UIContainers.verticalFlow(Sizing.fixed(KEY_SIZE), Sizing.fixed(KEY_SIZE));
|
||||||
|
box.horizontalAlignment(HorizontalAlignment.CENTER);
|
||||||
|
box.verticalAlignment(VerticalAlignment.CENTER);
|
||||||
|
box.surface(Surface.flat(IDLE_COLOR));
|
||||||
|
box.child(label);
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tick() {
|
||||||
|
if (keyContainer == null) return;
|
||||||
|
|
||||||
|
if (toggle && !shown) {
|
||||||
|
keyContainer.clearChildren(); // <-- safety net against duplicate adds
|
||||||
|
keyContainer.surface(GLASS_PANEL);
|
||||||
|
keyContainer.padding(Insets.of(Config.get().fpsP));
|
||||||
|
keyContainer.child(topRow);
|
||||||
|
keyContainer.child(bottomRow);
|
||||||
|
shown = true;
|
||||||
|
} else if (!toggle && shown) {
|
||||||
|
keyContainer.clearChildren();
|
||||||
|
keyContainer.surface(Surface.flat(0));
|
||||||
|
keyContainer.padding(Insets.of(0));
|
||||||
|
shown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!toggle) return;
|
||||||
|
|
||||||
|
Minecraft client = Minecraft.getInstance();
|
||||||
|
KeyMapping up = client.options.keyUp;
|
||||||
|
KeyMapping left = client.options.keyLeft;
|
||||||
|
KeyMapping down = client.options.keyDown;
|
||||||
|
KeyMapping right = client.options.keyRight;
|
||||||
|
|
||||||
|
wBox.surface(Surface.flat(up.isDown() ? PRESSED_COLOR : IDLE_COLOR));
|
||||||
|
aBox.surface(Surface.flat(left.isDown() ? PRESSED_COLOR : IDLE_COLOR));
|
||||||
|
sBox.surface(Surface.flat(down.isDown() ? PRESSED_COLOR : IDLE_COLOR));
|
||||||
|
dBox.surface(Surface.flat(right.isDown() ? PRESSED_COLOR : IDLE_COLOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,6 +56,7 @@ public class MyScreen extends BaseOwoScreen<FlowLayout> {
|
|||||||
private FlowLayout content;
|
private FlowLayout content;
|
||||||
private final String initialModule;
|
private final String initialModule;
|
||||||
|
|
||||||
|
|
||||||
public MyScreen() {
|
public MyScreen() {
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
@@ -130,8 +131,8 @@ public class MyScreen extends BaseOwoScreen<FlowLayout> {
|
|||||||
|
|
||||||
rootComponent.child(panel);
|
rootComponent.child(panel);
|
||||||
|
|
||||||
if ("FPS".equals(initialModule)) {
|
if (initialModule != null) {
|
||||||
openModuleSettings("FPS", fpsModuleSettings());
|
openModuleByKey(initialModule);
|
||||||
} else {
|
} else {
|
||||||
buildGeneralPanel(content);
|
buildGeneralPanel(content);
|
||||||
}
|
}
|
||||||
@@ -249,72 +250,51 @@ public class MyScreen extends BaseOwoScreen<FlowLayout> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void buildGeneralPanel(FlowLayout content) {
|
private void buildGeneralPanel(FlowLayout content) {
|
||||||
FlowLayout iconButton = UIContainers.horizontalFlow(Sizing.content(), Sizing.content());
|
FlowLayout fpsRow = ModuleRow.build(
|
||||||
iconButton.gap(8);
|
GLASS_PANEL, DISABLED, FLAT,
|
||||||
iconButton.verticalAlignment(VerticalAlignment.TOP);
|
|
||||||
iconButton.horizontalAlignment(HorizontalAlignment.CENTER);
|
|
||||||
iconButton.padding(Insets.of(6));
|
|
||||||
iconButton.horizontalSizing(Sizing.fixed(0));
|
|
||||||
iconButton.verticalSizing(Sizing.fixed(0));
|
|
||||||
iconButton.surface(toggle ? GLASS_PANEL : DISABLED);
|
|
||||||
iconButton.child(UIComponents.texture(
|
|
||||||
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/block_culling_file.png"),
|
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/block_culling_file.png"),
|
||||||
0, 0, 18, 12, 18, 12
|
18, 12,
|
||||||
)).horizontalAlignment(HorizontalAlignment.LEFT)
|
MC_FIVE,
|
||||||
.verticalAlignment(VerticalAlignment.CENTER);
|
"FPS",
|
||||||
|
"Displays your FPS",
|
||||||
FlowLayout textStack = UIContainers.verticalFlow(Sizing.content(), Sizing.content());
|
new ModuleRow.ToggleState() {
|
||||||
textStack.gap(2);
|
public boolean get() { return toggle; }
|
||||||
textStack.horizontalAlignment(HorizontalAlignment.LEFT);
|
public void set(boolean value) { toggle = value; }
|
||||||
textStack.verticalAlignment(VerticalAlignment.CENTER);
|
},
|
||||||
|
|
||||||
var fpsLabel = UIComponents.label(
|
|
||||||
Component.literal("FPS")
|
|
||||||
.withStyle(style -> style.withFont(new FontDescription.Resource(MC_FIVE)))
|
|
||||||
).color(Color.ofRgb(toggle ? 0xffffff : 0x797b86));
|
|
||||||
|
|
||||||
textStack.child(fpsLabel);
|
|
||||||
|
|
||||||
textStack.child(UIComponents.label(
|
|
||||||
Component.literal("Displays your FPS"))
|
|
||||||
.color(Color.ofRgb(0xa3a3ac))
|
|
||||||
.maxWidth(130)
|
|
||||||
);
|
|
||||||
|
|
||||||
iconButton.child(textStack);
|
|
||||||
iconButton.child(UIComponents.spacer());
|
|
||||||
|
|
||||||
FlowLayout settingsButton = SettingsControls.makeSmallButton(
|
|
||||||
FLAT,
|
|
||||||
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/settings.png"),
|
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/settings.png"),
|
||||||
() -> {
|
() -> {
|
||||||
Config.get().lastModule = "FPS";
|
Config.get().lastModule = "FPS";
|
||||||
Config.get().save();
|
Config.get().save();
|
||||||
|
|
||||||
openModuleSettings("FPS", fpsModuleSettings());
|
openModuleSettings("FPS", fpsModuleSettings());
|
||||||
}
|
},
|
||||||
|
"Module Enabled", "FPS counter is now active",
|
||||||
|
"Module Disabled", "FPS counter is now inactive"
|
||||||
);
|
);
|
||||||
iconButton.child(settingsButton);
|
|
||||||
|
|
||||||
iconButton.mouseEnter().subscribe(() -> iconButton.surface(toggle ? GLASS_PANEL : DISABLED));
|
FlowLayout keyStrokesRow = ModuleRow.build(
|
||||||
iconButton.mouseLeave().subscribe(() -> iconButton.surface(toggle ? GLASS_PANEL : DISABLED));
|
GLASS_PANEL, DISABLED, FLAT,
|
||||||
|
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/block_culling_file.png"),
|
||||||
iconButton.mouseDown().subscribe((click, doubled) -> {
|
18, 12,
|
||||||
toggle = !toggle;
|
MC_FIVE,
|
||||||
fpsLabel.color(Color.ofRgb(toggle ? 0xffffff : 0x797b86));
|
"Keystrokes",
|
||||||
iconButton.surface(toggle ? GLASS_PANEL : DISABLED);
|
"Current Key Presses",
|
||||||
CitrusToast.show("Module Enabled", "FPS counter is now active");
|
new ModuleRow.ToggleState() {
|
||||||
|
public boolean get() { return Module_KeyStrokes.isEnabled(); }
|
||||||
Minecraft.getInstance().getSoundManager().play(
|
public void set(boolean value) { Module_KeyStrokes.setEnabled(value); }
|
||||||
SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 2.0F, 2)
|
},
|
||||||
|
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/settings.png"),
|
||||||
|
() -> {
|
||||||
|
Config.get().lastModule = "Key";
|
||||||
|
Config.get().save();
|
||||||
|
openModuleSettings("Key", keyStrokesSettings());
|
||||||
|
},
|
||||||
|
"Module Enabled", "Keystrokes is now active",
|
||||||
|
"Module Disabled", "KeyStrokes is now inactive"
|
||||||
);
|
);
|
||||||
return true;
|
content.child(keyStrokesRow);
|
||||||
});
|
content.child(fpsRow);
|
||||||
|
|
||||||
iconButton.horizontalSizing().animate(150, Easing.CUBIC, Sizing.fixed(200)).forwards();
|
|
||||||
iconButton.verticalSizing().animate(150, Easing.CUBIC, Sizing.fixed(35)).forwards();
|
|
||||||
|
|
||||||
content.child(iconButton);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -366,6 +346,14 @@ public class MyScreen extends BaseOwoScreen<FlowLayout> {
|
|||||||
content.child(scrollArea);
|
content.child(scrollArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openModuleByKey(String key) {
|
||||||
|
switch (key) {
|
||||||
|
case "FPS" -> openModuleSettings("FPS", fpsModuleSettings());
|
||||||
|
case "Key" -> openModuleSettings("Keystrokes", keyStrokesSettings());
|
||||||
|
default -> buildGeneralPanel(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private FlowLayout makeSettingRow(SettingRow setting) {
|
private FlowLayout makeSettingRow(SettingRow setting) {
|
||||||
FlowLayout row = UIContainers.horizontalFlow(Sizing.fixed(CONTENT_WIDTH - 8), Sizing.fixed(16));
|
FlowLayout row = UIContainers.horizontalFlow(Sizing.fixed(CONTENT_WIDTH - 8), Sizing.fixed(16));
|
||||||
row.verticalAlignment(VerticalAlignment.CENTER);
|
row.verticalAlignment(VerticalAlignment.CENTER);
|
||||||
@@ -398,12 +386,48 @@ public class MyScreen extends BaseOwoScreen<FlowLayout> {
|
|||||||
value -> Module_FPS.fpsContainer.gap((int) value)
|
value -> Module_FPS.fpsContainer.gap((int) value)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
int currentIndex = positionIndex(Config.get().fpsX, Config.get().fpsY);
|
||||||
|
|
||||||
return List.of(
|
return List.of(
|
||||||
new SettingRow("Position", SettingsControls.makeCyclingButton(GLASS_PANEL, positionStates)),
|
new SettingRow("Position", SettingsControls.makeCyclingButton(GLASS_PANEL, positionStates, currentIndex)),
|
||||||
new SettingRow("Size", sizeSlider)
|
new SettingRow("Size", sizeSlider)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<SettingRow> keyStrokesSettings() {
|
||||||
|
List<SettingsControls.ButtonState> positionStates = List.of(
|
||||||
|
new SettingsControls.ButtonState("L-TOP", () -> Module_KeyStrokes.setPosition(1, 1)),
|
||||||
|
new SettingsControls.ButtonState("R-TOP", () -> Module_KeyStrokes.setPosition(99, 1)),
|
||||||
|
new SettingsControls.ButtonState("R-BOTTOM", () -> Module_KeyStrokes.setPosition(99, 99)),
|
||||||
|
new SettingsControls.ButtonState("L-BOTTOM", () -> Module_KeyStrokes.setPosition(1, 99))
|
||||||
|
);
|
||||||
|
|
||||||
|
FlowLayout sizeSlider = SettingsControls.makeSlider(
|
||||||
|
FLAT,
|
||||||
|
80,
|
||||||
|
0.0, 20.0,
|
||||||
|
4.0,
|
||||||
|
value -> (int) value + "px",
|
||||||
|
value -> {} // fixed: was Module_FPS.fpsContainer.gap(...)
|
||||||
|
);
|
||||||
|
|
||||||
|
int currentIndex = positionIndex(Config.get().keyX, Config.get().keyY);
|
||||||
|
|
||||||
|
return List.of(
|
||||||
|
new SettingRow("Position", SettingsControls.makeCyclingButton(GLASS_PANEL, positionStates, currentIndex)),
|
||||||
|
new SettingRow("Size", sizeSlider)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int positionIndex(int x, int y) {
|
||||||
|
boolean right = x > 50;
|
||||||
|
boolean bottom = y > 50;
|
||||||
|
if (!right && !bottom) return 0; // L-TOP
|
||||||
|
if (right && !bottom) return 1; // R-TOP
|
||||||
|
if (right && bottom) return 2; // R-BOTTOM
|
||||||
|
return 3; // L-BOTTOM
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPauseScreen() {
|
public boolean isPauseScreen() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package de.fullrisk.citrusDev.UI;
|
package de.fullrisk.citrusDev.UI;
|
||||||
|
|
||||||
|
import io.wispforest.owo.ui.component.LabelComponent;
|
||||||
import io.wispforest.owo.ui.component.UIComponents;
|
import io.wispforest.owo.ui.component.UIComponents;
|
||||||
import io.wispforest.owo.ui.container.FlowLayout;
|
import io.wispforest.owo.ui.container.FlowLayout;
|
||||||
import io.wispforest.owo.ui.container.UIContainers;
|
import io.wispforest.owo.ui.container.UIContainers;
|
||||||
@@ -7,12 +8,15 @@ import io.wispforest.owo.ui.core.*;
|
|||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.chat.FontDescription;
|
import net.minecraft.network.chat.FontDescription;
|
||||||
import de.fullrisk.citrusDev.client.PlaytimeTracker;
|
import de.fullrisk.citrusDev.client.PlaytimeTracker;
|
||||||
|
import de.fullrisk.citrusDev.UIHelper.CitrusToast;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class QuestsPanel {
|
public class QuestsPanel {
|
||||||
|
|
||||||
private static final int ACCENT = 0xbaff86;
|
private static final int ACCENT = 0x64de4b;
|
||||||
|
|
||||||
private record QuestData(String title, String description, int requiredPlaytime) {
|
private record QuestData(String title, String description, int requiredPlaytime) {
|
||||||
boolean isUnlocked() {
|
boolean isUnlocked() {
|
||||||
@@ -20,7 +24,50 @@ public class QuestsPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void build(FlowLayout content, int contentWidth, int contentHeight) {
|
public static class Handle {
|
||||||
|
private final LabelComponent playtimeLabel;
|
||||||
|
private final LabelComponent percentLabel;
|
||||||
|
private final FlowLayout barFill;
|
||||||
|
private final int barWidth;
|
||||||
|
private final List<QuestData> quests;
|
||||||
|
private final Set<String> notifiedTitles = new HashSet<>();
|
||||||
|
|
||||||
|
private Handle(LabelComponent playtimeLabel, LabelComponent percentLabel, FlowLayout barFill, int barWidth, List<QuestData> quests) {
|
||||||
|
this.playtimeLabel = playtimeLabel;
|
||||||
|
this.percentLabel = percentLabel;
|
||||||
|
this.barFill = barFill;
|
||||||
|
this.barWidth = barWidth;
|
||||||
|
this.quests = quests;
|
||||||
|
|
||||||
|
// seed with whatever's already unlocked on open, so we don't spam
|
||||||
|
// toasts for quests the player unlocked before opening this screen
|
||||||
|
for (QuestData quest : quests) {
|
||||||
|
if (quest.isUnlocked()) {
|
||||||
|
notifiedTitles.add(quest.title());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
int playtime = PlaytimeTracker.getCurrentPlaytimeSeconds();
|
||||||
|
int maxPlaytime = 360;
|
||||||
|
int progress = Math.min(playtime * 100 / maxPlaytime, 100);
|
||||||
|
|
||||||
|
playtimeLabel.text(Component.literal(formatPlaytime(playtime)));
|
||||||
|
percentLabel.text(Component.literal(progress + "%"));
|
||||||
|
|
||||||
|
int fillWidth = Math.max(barWidth * progress / 100, progress > 0 ? 2 : 0);
|
||||||
|
barFill.horizontalSizing(Sizing.fixed(fillWidth));
|
||||||
|
|
||||||
|
for (QuestData quest : quests) {
|
||||||
|
if (quest.isUnlocked() && notifiedTitles.add(quest.title())) {
|
||||||
|
CitrusToast.show("Quest Unlocked", quest.title());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Handle build(FlowLayout content, int contentWidth, int contentHeight) {
|
||||||
List<QuestData> quests = quests();
|
List<QuestData> quests = quests();
|
||||||
int playtime = PlaytimeTracker.getCurrentPlaytimeSeconds();
|
int playtime = PlaytimeTracker.getCurrentPlaytimeSeconds();
|
||||||
int maxPlaytime = 360;
|
int maxPlaytime = 360;
|
||||||
@@ -28,7 +75,6 @@ public class QuestsPanel {
|
|||||||
int barWidth = contentWidth - 8;
|
int barWidth = contentWidth - 8;
|
||||||
long unlockedCount = quests.stream().filter(QuestData::isUnlocked).count();
|
long unlockedCount = quests.stream().filter(QuestData::isUnlocked).count();
|
||||||
|
|
||||||
// --- status row: playtime + percentage ---
|
|
||||||
FlowLayout statusBar = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.fixed(20));
|
FlowLayout statusBar = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.fixed(20));
|
||||||
statusBar.gap(4);
|
statusBar.gap(4);
|
||||||
statusBar.padding(Insets.of(4));
|
statusBar.padding(Insets.of(4));
|
||||||
@@ -36,15 +82,17 @@ public class QuestsPanel {
|
|||||||
statusBar.horizontalAlignment(HorizontalAlignment.LEFT);
|
statusBar.horizontalAlignment(HorizontalAlignment.LEFT);
|
||||||
statusBar.surface(MyScreen.GLASS_PANEL);
|
statusBar.surface(MyScreen.GLASS_PANEL);
|
||||||
|
|
||||||
statusBar.child(UIComponents.label(Component.literal("Playtime: " + formatPlaytime(playtime)))
|
LabelComponent playtimeLabel = UIComponents.label(Component.literal(formatPlaytime(playtime)));
|
||||||
.color(Color.ofRgb(0xFFFFFF)));
|
playtimeLabel.color(Color.ofRgb(0xFFFFFF));
|
||||||
|
statusBar.child(playtimeLabel);
|
||||||
statusBar.child(UIComponents.spacer());
|
statusBar.child(UIComponents.spacer());
|
||||||
statusBar.child(UIComponents.label(Component.literal(progress + "%"))
|
|
||||||
.color(Color.ofRgb(ACCENT)));
|
LabelComponent percentLabel = UIComponents.label(Component.literal(progress + "%"));
|
||||||
|
percentLabel.color(Color.ofRgb(ACCENT));
|
||||||
|
statusBar.child(percentLabel);
|
||||||
|
|
||||||
content.child(statusBar);
|
content.child(statusBar);
|
||||||
|
|
||||||
// --- progress bar ---
|
|
||||||
FlowLayout barBg = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.fixed(6));
|
FlowLayout barBg = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.fixed(6));
|
||||||
barBg.surface(Surface.flat(0x30FFFFFF).and(Surface.outline(0x30FFFFFF)));
|
barBg.surface(Surface.flat(0x30FFFFFF).and(Surface.outline(0x30FFFFFF)));
|
||||||
barBg.horizontalAlignment(HorizontalAlignment.LEFT);
|
barBg.horizontalAlignment(HorizontalAlignment.LEFT);
|
||||||
@@ -58,7 +106,6 @@ public class QuestsPanel {
|
|||||||
|
|
||||||
content.child(barBg);
|
content.child(barBg);
|
||||||
|
|
||||||
// --- unlocked count summary ---
|
|
||||||
FlowLayout summaryRow = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.content());
|
FlowLayout summaryRow = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.content());
|
||||||
summaryRow.padding(Insets.of(3, 0, 2, 2));
|
summaryRow.padding(Insets.of(3, 0, 2, 2));
|
||||||
|
|
||||||
@@ -69,7 +116,6 @@ public class QuestsPanel {
|
|||||||
|
|
||||||
content.child(summaryRow);
|
content.child(summaryRow);
|
||||||
|
|
||||||
// --- quest list (scrollable) ---
|
|
||||||
FlowLayout questList = UIContainers.verticalFlow(Sizing.fixed(barWidth), Sizing.content());
|
FlowLayout questList = UIContainers.verticalFlow(Sizing.fixed(barWidth), Sizing.content());
|
||||||
questList.gap(3);
|
questList.gap(3);
|
||||||
questList.padding(Insets.of(2, 0, 2, 2));
|
questList.padding(Insets.of(2, 0, 2, 2));
|
||||||
@@ -78,7 +124,7 @@ public class QuestsPanel {
|
|||||||
questList.child(buildQuestEntry(quest, contentWidth));
|
questList.child(buildQuestEntry(quest, contentWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
int usedHeight = 20 + 6 + 18 + 6; // statusBar + barBg + summaryRow + gaps
|
int usedHeight = 20 + 6 + 18 + 6;
|
||||||
int scrollHeight = contentHeight - usedHeight;
|
int scrollHeight = contentHeight - usedHeight;
|
||||||
|
|
||||||
var scrollArea = UIContainers.verticalScroll(
|
var scrollArea = UIContainers.verticalScroll(
|
||||||
@@ -87,6 +133,8 @@ public class QuestsPanel {
|
|||||||
questList
|
questList
|
||||||
);
|
);
|
||||||
content.child(scrollArea);
|
content.child(scrollArea);
|
||||||
|
|
||||||
|
return new Handle(playtimeLabel, percentLabel, barFill, barWidth, quests);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String formatPlaytime(int seconds) {
|
private static String formatPlaytime(int seconds) {
|
||||||
@@ -130,7 +178,7 @@ public class QuestsPanel {
|
|||||||
: Surface.flat(0x40FFFFFF));
|
: Surface.flat(0x40FFFFFF));
|
||||||
iconBox.horizontalAlignment(HorizontalAlignment.CENTER);
|
iconBox.horizontalAlignment(HorizontalAlignment.CENTER);
|
||||||
iconBox.verticalAlignment(VerticalAlignment.CENTER);
|
iconBox.verticalAlignment(VerticalAlignment.CENTER);
|
||||||
iconBox.child(UIComponents.label(Component.literal(unlocked ? "✓" : ""))
|
iconBox.child(UIComponents.label(Component.literal(""))
|
||||||
.color(Color.ofRgb(0x1c1c1c)));
|
.color(Color.ofRgb(0x1c1c1c)));
|
||||||
row.child(iconBox);
|
row.child(iconBox);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package de.fullrisk.citrusDev.UIHelper;
|
||||||
|
|
||||||
|
import io.wispforest.owo.ui.component.UIComponents;
|
||||||
|
import io.wispforest.owo.ui.container.FlowLayout;
|
||||||
|
import io.wispforest.owo.ui.container.UIContainers;
|
||||||
|
import io.wispforest.owo.ui.core.*;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.FontDescription;
|
||||||
|
import net.minecraft.resources.Identifier;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
|
|
||||||
|
public class ModuleRow {
|
||||||
|
|
||||||
|
/** Bridge to whatever boolean field actually holds the module's on/off state. */
|
||||||
|
public interface ToggleState {
|
||||||
|
boolean get();
|
||||||
|
void set(boolean value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FlowLayout build(
|
||||||
|
Surface enabledSurface,
|
||||||
|
Surface disabledSurface,
|
||||||
|
Surface flatSurface,
|
||||||
|
Identifier icon,
|
||||||
|
int iconWidth,
|
||||||
|
int iconHeight,
|
||||||
|
Identifier titleFont,
|
||||||
|
String title,
|
||||||
|
String description,
|
||||||
|
ToggleState state,
|
||||||
|
Identifier settingsIcon,
|
||||||
|
Runnable onSettingsClick,
|
||||||
|
String enabledToastTitle,
|
||||||
|
String enabledToastMessage,
|
||||||
|
String disabledToastTitle,
|
||||||
|
String disabledToastMessage
|
||||||
|
) {
|
||||||
|
FlowLayout row = UIContainers.horizontalFlow(Sizing.content(), Sizing.content());
|
||||||
|
row.gap(8);
|
||||||
|
row.verticalAlignment(VerticalAlignment.TOP);
|
||||||
|
row.horizontalAlignment(HorizontalAlignment.CENTER);
|
||||||
|
row.padding(Insets.of(6));
|
||||||
|
row.horizontalSizing(Sizing.fixed(0));
|
||||||
|
row.verticalSizing(Sizing.fixed(0));
|
||||||
|
row.surface(state.get() ? enabledSurface : disabledSurface);
|
||||||
|
|
||||||
|
row.child(UIComponents.texture(icon, 0, 0, iconWidth, iconHeight, iconWidth, iconHeight))
|
||||||
|
.horizontalAlignment(HorizontalAlignment.LEFT)
|
||||||
|
.verticalAlignment(VerticalAlignment.CENTER);
|
||||||
|
|
||||||
|
FlowLayout textStack = UIContainers.verticalFlow(Sizing.content(), Sizing.content());
|
||||||
|
textStack.gap(2);
|
||||||
|
textStack.horizontalAlignment(HorizontalAlignment.LEFT);
|
||||||
|
textStack.verticalAlignment(VerticalAlignment.CENTER);
|
||||||
|
|
||||||
|
var titleLabel = UIComponents.label(
|
||||||
|
Component.literal(title)
|
||||||
|
.withStyle(style -> style.withFont(new FontDescription.Resource(titleFont)))
|
||||||
|
);
|
||||||
|
titleLabel.color(Color.ofRgb(state.get() ? 0xffffff : 0x797b86));
|
||||||
|
textStack.child(titleLabel);
|
||||||
|
|
||||||
|
textStack.child(UIComponents.label(Component.literal(description))
|
||||||
|
.color(Color.ofRgb(0xa3a3ac))
|
||||||
|
.maxWidth(130));
|
||||||
|
|
||||||
|
row.child(textStack);
|
||||||
|
row.child(UIComponents.spacer());
|
||||||
|
|
||||||
|
FlowLayout settingsButton = SettingsControls.makeSmallButton(flatSurface, settingsIcon, onSettingsClick);
|
||||||
|
row.child(settingsButton);
|
||||||
|
|
||||||
|
row.mouseEnter().subscribe(() -> row.surface(state.get() ? enabledSurface : disabledSurface));
|
||||||
|
row.mouseLeave().subscribe(() -> row.surface(state.get() ? enabledSurface : disabledSurface));
|
||||||
|
|
||||||
|
row.mouseDown().subscribe((click, doubled) -> {
|
||||||
|
boolean newState = !state.get();
|
||||||
|
state.set(newState);
|
||||||
|
|
||||||
|
titleLabel.color(Color.ofRgb(newState ? 0xffffff : 0x797b86));
|
||||||
|
row.surface(newState ? enabledSurface : disabledSurface);
|
||||||
|
|
||||||
|
if (newState) {
|
||||||
|
CitrusToast.show(enabledToastTitle, enabledToastMessage);
|
||||||
|
} else {
|
||||||
|
CitrusToast.show(disabledToastTitle, disabledToastMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
Minecraft.getInstance().getSoundManager().play(
|
||||||
|
SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 2.0F, 2)
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
row.horizontalSizing().animate(150, Easing.CUBIC, Sizing.fixed(200)).forwards();
|
||||||
|
row.verticalSizing().animate(150, Easing.CUBIC, Sizing.fixed(35)).forwards();
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -73,15 +73,15 @@ public class SettingsControls {
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FlowLayout makeCyclingButton(Surface baseSurface, List<ButtonState> states) {
|
public static FlowLayout makeCyclingButton(Surface baseSurface, List<ButtonState> states, int initialIndex) {
|
||||||
int[] currentIndex = {0};
|
int[] currentIndex = { initialIndex };
|
||||||
|
|
||||||
FlowLayout button = UIContainers.verticalFlow(Sizing.fixed(50), Sizing.fixed(16));
|
FlowLayout button = UIContainers.verticalFlow(Sizing.fixed(50), Sizing.fixed(16));
|
||||||
button.verticalAlignment(VerticalAlignment.CENTER);
|
button.verticalAlignment(VerticalAlignment.CENTER);
|
||||||
button.horizontalAlignment(HorizontalAlignment.CENTER);
|
button.horizontalAlignment(HorizontalAlignment.CENTER);
|
||||||
button.surface(baseSurface);
|
button.surface(baseSurface);
|
||||||
|
|
||||||
var label = UIComponents.label(Component.literal(states.get(0).label()))
|
var label = UIComponents.label(Component.literal(states.get(initialIndex).label()))
|
||||||
.color(Color.ofRgb(0xFFFFFF));
|
.color(Color.ofRgb(0xFFFFFF));
|
||||||
button.child(label);
|
button.child(label);
|
||||||
|
|
||||||
@@ -104,6 +104,11 @@ public class SettingsControls {
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keep the old call sites compiling; defaults to index 0
|
||||||
|
public static FlowLayout makeCyclingButton(Surface baseSurface, List<ButtonState> states) {
|
||||||
|
return makeCyclingButton(baseSurface, states, 0);
|
||||||
|
}
|
||||||
|
|
||||||
public static FlowLayout makeSlider(Surface baseSurface, int width, double min, double max, double initial,
|
public static FlowLayout makeSlider(Surface baseSurface, int width, double min, double max, double initial,
|
||||||
DoubleFunction<String> labelFormatter, DoubleConsumer onChange) {
|
DoubleFunction<String> labelFormatter, DoubleConsumer onChange) {
|
||||||
int labelWidth = 34;
|
int labelWidth = 34;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package de.fullrisk.citrusDev.client;
|
|||||||
|
|
||||||
import de.fullrisk.citrusDev.Config;
|
import de.fullrisk.citrusDev.Config;
|
||||||
import de.fullrisk.citrusDev.UI.Module_FPS;
|
import de.fullrisk.citrusDev.UI.Module_FPS;
|
||||||
|
import de.fullrisk.citrusDev.UI.Module_KeyStrokes;
|
||||||
import de.fullrisk.citrusDev.UI.MyScreen;
|
import de.fullrisk.citrusDev.UI.MyScreen;
|
||||||
import de.fullrisk.citrusDev.UI.OverviewScreen;
|
import de.fullrisk.citrusDev.UI.OverviewScreen;
|
||||||
import io.wispforest.owo.ui.hud.Hud;
|
import io.wispforest.owo.ui.hud.Hud;
|
||||||
@@ -21,6 +22,7 @@ public class CitrusDevClient implements ClientModInitializer {
|
|||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
Module_FPS.register();
|
Module_FPS.register();
|
||||||
|
Module_KeyStrokes.register();
|
||||||
|
|
||||||
KeyMapping.Category category = KeyMapping.Category.register(
|
KeyMapping.Category category = KeyMapping.Category.register(
|
||||||
Identifier.fromNamespaceAndPath("citrus-dev", "general"));
|
Identifier.fromNamespaceAndPath("citrus-dev", "general"));
|
||||||
@@ -41,7 +43,8 @@ public class CitrusDevClient implements ClientModInitializer {
|
|||||||
PlaytimeTracker.tick();
|
PlaytimeTracker.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
Module_FPS.tick(); // update every tick, unconditionally
|
Module_FPS.tick();
|
||||||
|
Module_KeyStrokes.tick();
|
||||||
while (openScreenKey.consumeClick()) {
|
while (openScreenKey.consumeClick()) {
|
||||||
client.setScreen(new MyScreen(null)); // always general tab
|
client.setScreen(new MyScreen(null)); // always general tab
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user