217 lines
8.4 KiB
Java
217 lines
8.4 KiB
Java
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 net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.FontDescription;
|
|
import de.fullrisk.citrusDev.client.PlaytimeTracker;
|
|
import de.fullrisk.citrusDev.UIHelper.CitrusToast;
|
|
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
public class QuestsPanel {
|
|
|
|
private static final int ACCENT = 0x64de4b;
|
|
|
|
private static Handle activeHandle;
|
|
private static int tickCounter = 0;
|
|
|
|
private record QuestData(String title, String description, int requiredPlaytime) {
|
|
boolean isUnlocked() {
|
|
return PlaytimeTracker.getCurrentPlaytimeSeconds() >= requiredPlaytime;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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();
|
|
int playtime = PlaytimeTracker.getCurrentPlaytimeSeconds();
|
|
int maxPlaytime = 360;
|
|
int progress = Math.min(playtime * 100 / maxPlaytime, 100);
|
|
int barWidth = contentWidth - 8;
|
|
long unlockedCount = quests.stream().filter(QuestData::isUnlocked).count();
|
|
|
|
FlowLayout statusBar = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.fixed(20));
|
|
statusBar.gap(4);
|
|
statusBar.padding(Insets.of(4));
|
|
statusBar.verticalAlignment(VerticalAlignment.CENTER);
|
|
statusBar.horizontalAlignment(HorizontalAlignment.LEFT);
|
|
statusBar.surface(MainUI.GLASS_PANEL);
|
|
|
|
LabelComponent playtimeLabel = UIComponents.label(Component.literal(formatPlaytime(playtime)));
|
|
playtimeLabel.color(Color.ofRgb(0xFFFFFF));
|
|
statusBar.child(playtimeLabel);
|
|
statusBar.child(UIComponents.spacer());
|
|
|
|
LabelComponent percentLabel = UIComponents.label(Component.literal(progress + "%"));
|
|
percentLabel.color(Color.ofRgb(ACCENT));
|
|
statusBar.child(percentLabel);
|
|
|
|
content.child(statusBar);
|
|
|
|
FlowLayout barBg = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.fixed(6));
|
|
barBg.surface(Surface.flat(0x30FFFFFF).and(Surface.outline(0x30FFFFFF)));
|
|
barBg.horizontalAlignment(HorizontalAlignment.LEFT);
|
|
barBg.gap(0);
|
|
barBg.padding(Insets.of(0));
|
|
|
|
int fillWidth = Math.max(barWidth * progress / 100, progress > 0 ? 2 : 0);
|
|
FlowLayout barFill = UIContainers.horizontalFlow(Sizing.fixed(fillWidth), Sizing.fixed(6));
|
|
barFill.surface(Surface.flat(0xFF000000 | ACCENT));
|
|
barBg.child(barFill);
|
|
|
|
content.child(barBg);
|
|
|
|
FlowLayout summaryRow = UIContainers.horizontalFlow(Sizing.fixed(barWidth), Sizing.content());
|
|
summaryRow.padding(Insets.of(3, 0, 2, 2));
|
|
|
|
summaryRow.child(UIComponents.label(
|
|
Component.literal("quests " + unlockedCount + "/" + quests.size())
|
|
.withStyle(style -> style.withFont(new FontDescription.Resource(MainUI.MC_FIVE))))
|
|
.color(Color.ofRgb(0xFF5c5c66)));
|
|
|
|
content.child(summaryRow);
|
|
|
|
FlowLayout questList = UIContainers.verticalFlow(Sizing.fixed(barWidth), Sizing.content());
|
|
questList.gap(3);
|
|
questList.padding(Insets.of(2, 0, 2, 2));
|
|
|
|
for (QuestData quest : quests) {
|
|
questList.child(buildQuestEntry(quest, contentWidth));
|
|
}
|
|
|
|
int usedHeight = 20 + 6 + 18 + 6;
|
|
int scrollHeight = contentHeight - usedHeight;
|
|
|
|
var scrollArea = UIContainers.verticalScroll(
|
|
Sizing.fixed(barWidth),
|
|
Sizing.fixed(scrollHeight),
|
|
questList
|
|
);
|
|
content.child(scrollArea);
|
|
|
|
Handle handle = new Handle(playtimeLabel, percentLabel, barFill, barWidth, quests);
|
|
activeHandle = handle;
|
|
return handle;
|
|
}
|
|
|
|
public static void tick() {
|
|
if (activeHandle != null && tickCounter++ % 20 == 0) {
|
|
activeHandle.refresh();
|
|
}
|
|
}
|
|
|
|
public static void clearActiveHandle() {
|
|
activeHandle = null;
|
|
}
|
|
|
|
private static String formatPlaytime(int seconds) {
|
|
int hours = seconds / 3600;
|
|
int minutes = (seconds % 3600) / 60;
|
|
int secs = seconds % 60;
|
|
if (hours > 0) {
|
|
return hours + "h " + minutes + "m " + secs + "s";
|
|
} else if (minutes > 0) {
|
|
return minutes + "m " + secs + "s";
|
|
} else {
|
|
return secs + "s";
|
|
}
|
|
}
|
|
|
|
private static List<QuestData> quests() {
|
|
return List.of(
|
|
new QuestData("first steps", "Play for 5 minutes", 300),
|
|
new QuestData("getting started", "Play for 30 minutes", 1800),
|
|
new QuestData("dedicated player", "Play for 1 hour", 3600),
|
|
new QuestData("veteran", "Play for 3 hours", 10800)
|
|
);
|
|
}
|
|
|
|
private static FlowLayout buildQuestEntry(QuestData quest, int contentWidth) {
|
|
boolean unlocked = quest.isUnlocked();
|
|
int entryWidth = contentWidth - 12;
|
|
|
|
FlowLayout row = UIContainers.horizontalFlow(Sizing.fixed(entryWidth), Sizing.fixed(32));
|
|
row.gap(6);
|
|
row.padding(Insets.of(4));
|
|
row.verticalAlignment(VerticalAlignment.CENTER);
|
|
row.horizontalAlignment(HorizontalAlignment.LEFT);
|
|
row.surface(unlocked
|
|
? Surface.flat(0x1AFFFFFF).and(Surface.outline(0x30 << 24 | ACCENT))
|
|
: Surface.flat(0x12000000));
|
|
|
|
FlowLayout iconBox = UIContainers.verticalFlow(Sizing.fixed(12), Sizing.fixed(12));
|
|
iconBox.surface(unlocked
|
|
? Surface.flat(0xFF000000 | ACCENT)
|
|
: Surface.flat(0x40FFFFFF));
|
|
iconBox.horizontalAlignment(HorizontalAlignment.CENTER);
|
|
iconBox.verticalAlignment(VerticalAlignment.CENTER);
|
|
iconBox.child(UIComponents.label(Component.literal(""))
|
|
.color(Color.ofRgb(0x1c1c1c)));
|
|
row.child(iconBox);
|
|
|
|
FlowLayout textStack = UIContainers.verticalFlow(Sizing.fixed(entryWidth - 24), Sizing.content());
|
|
textStack.gap(1);
|
|
textStack.horizontalAlignment(HorizontalAlignment.LEFT);
|
|
textStack.verticalAlignment(VerticalAlignment.TOP);
|
|
|
|
var titleLabel = UIComponents.label(Component.literal(quest.title())
|
|
.withStyle(style -> style.withFont(new FontDescription.Resource(MainUI.MC_FIVE))))
|
|
.color(Color.ofRgb(unlocked ? 0xFFFFFF : 0x797b86));
|
|
textStack.child(titleLabel);
|
|
|
|
var descLabel = UIComponents.label(Component.literal(quest.description()))
|
|
.color(Color.ofRgb(0xFF5c5c66))
|
|
.maxWidth(entryWidth - 24);
|
|
textStack.child(descLabel);
|
|
|
|
row.child(textStack);
|
|
|
|
return row;
|
|
}
|
|
} |