aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/misc
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2023-08-29 20:25:01 +0200
committerGitHub <noreply@github.com>2023-08-29 20:25:01 +0200
commit71a6b08b376be7d742107df982905cac0d7ecf70 (patch)
treea4b5f0cc511444ccd833c281b8600d9ff434231b /src/main/java/at/hannibal2/skyhanni/features/misc
parent238085dd49de8bca06ed816bc7a8d369390cdf30 (diff)
downloadskyhanni-71a6b08b376be7d742107df982905cac0d7ecf70.tar.gz
skyhanni-71a6b08b376be7d742107df982905cac0d7ecf70.tar.bz2
skyhanni-71a6b08b376be7d742107df982905cac0d7ecf70.zip
Add default option selector #400
* Allow for update only options * Clean/Split up mass configuration options * Merge remote-tracking branch 'origin/beta' into feat/defaultoptions * Fix merge errors
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features/misc')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigFeatures.kt112
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt193
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt70
3 files changed, 375 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigFeatures.kt
new file mode 100644
index 000000000..72f205b5e
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigFeatures.kt
@@ -0,0 +1,112 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import io.github.moulberry.moulconfig.processor.ConfigProcessorDriver
+import net.minecraft.client.Minecraft
+import net.minecraft.command.CommandBase
+import net.minecraft.event.ClickEvent
+import net.minecraft.util.ChatComponentText
+import net.minecraft.util.ChatStyle
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+
+object DefaultConfigFeatures {
+ var didNotifyOnce = false
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent) {
+ if (didNotifyOnce) return
+ val player = Minecraft.getMinecraft().thePlayer ?: return
+ didNotifyOnce = true
+ val knownToggles = SkyHanniMod.feature.storage.knownFeatureToggles
+ val updated = SkyHanniMod.version !in knownToggles
+ val processor = FeatureToggleProcessor()
+ ConfigProcessorDriver.processConfig(SkyHanniMod.feature.javaClass, SkyHanniMod.feature, processor)
+ knownToggles[SkyHanniMod.version] = processor.allOptions.map { it.path }
+ SkyHanniMod.configManager.saveConfig("Updated known feature flags")
+ if (!SkyHanniMod.feature.storage.hasPlayedBefore) {
+ SkyHanniMod.feature.storage.hasPlayedBefore = true
+ player.addChatMessage(
+ ChatComponentText(
+ "§e[SkyHanni] Looks like this is the first time you are using SkyHanni." +
+ " Click here to configure default options, or run /shdefaultoptions."
+ ).setChatStyle(
+ ChatStyle().setChatClickEvent(
+ ClickEvent(
+ ClickEvent.Action.RUN_COMMAND,
+ "/shdefaultoptions"
+ )
+ )
+ )
+ )
+
+ } else if (updated) {
+ val mostFeatureFulOldVersion = knownToggles.maxByOrNull { if (it.key != SkyHanniMod.version) it.value.size else -1 }
+ val command = "/shdefaultoptions ${mostFeatureFulOldVersion?.key} ${SkyHanniMod.version}"
+ player.addChatMessage(
+ ChatComponentText(
+ "§e[SkyHanni] Looks like you updated SkyHanni." +
+ " Click here to configure the newly introduced options, or run $command."
+ ).setChatStyle(
+ ChatStyle().setChatClickEvent(
+ ClickEvent(
+ ClickEvent.Action.RUN_COMMAND,
+ command
+ )
+ )
+ )
+ )
+ }
+ }
+
+ fun onCommand(old: String, new: String) {
+ val processor = FeatureToggleProcessor()
+ ConfigProcessorDriver.processConfig(SkyHanniMod.feature.javaClass, SkyHanniMod.feature, processor)
+ var optionList = processor.orderedOptions
+ val knownToggles = SkyHanniMod.feature.storage.knownFeatureToggles
+ val togglesInNewVersion = knownToggles[new]
+ if (new != "null" && togglesInNewVersion == null) {
+ LorenzUtils.chat("§e[SkyHanni] Unknown version $new")
+ return
+ }
+ val togglesInOldVersion = knownToggles[old]
+ if (old != "null" && togglesInOldVersion == null) {
+ LorenzUtils.chat("§e[SkyHanni] Unknown version $old")
+ return
+ }
+ optionList = optionList.mapValues { it.value.filter { (togglesInNewVersion == null || it.path in togglesInNewVersion) && (togglesInOldVersion == null || it.path !in togglesInOldVersion) } }
+ .filter { it.value.isNotEmpty() }
+ SkyHanniMod.screenToOpen = DefaultConfigOptionGui(optionList, old, new)
+ }
+
+ fun applyCategorySelections(
+ resetSuggestionState: MutableMap<FeatureToggleProcessor.Category, DefaultConfigOptionGui.ResetSuggestionState>,
+ orderedOptions: Map<FeatureToggleProcessor.Category, List<FeatureToggleProcessor.FeatureToggleableOption>>
+ ) {
+ orderedOptions.forEach { cat, options ->
+ for (option in options) {
+ val resetState = option.toggleOverride ?: resetSuggestionState[cat]!!
+ if (resetState == DefaultConfigOptionGui.ResetSuggestionState.LEAVE_DEFAULTS) continue
+ val onState = option.isTrueEnabled
+ val setTo = if (resetState == DefaultConfigOptionGui.ResetSuggestionState.TURN_ALL_ON) {
+ onState
+ } else {
+ !onState
+ }
+ option.setter(setTo)
+ }
+ }
+ }
+
+ fun onComplete(strings: Array<String>): List<String> {
+ if (strings.size <= 2)
+ return CommandBase.getListOfStringsMatchingLastWord(
+ strings,
+ SkyHanniMod.feature.storage.knownFeatureToggles.keys + listOf("null")
+ )
+ return listOf()
+ }
+
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt
new file mode 100644
index 000000000..00fc88dd4
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt
@@ -0,0 +1,193 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+import io.github.moulberry.moulconfig.internal.GlScissorStack
+import io.github.moulberry.moulconfig.internal.RenderUtils
+import io.github.moulberry.moulconfig.internal.TextRenderUtils
+import io.github.moulberry.notenoughupdates.util.Utils
+import net.minecraft.client.gui.GuiScreen
+import net.minecraft.client.gui.ScaledResolution
+import net.minecraft.client.renderer.GlStateManager
+import org.lwjgl.input.Mouse
+
+class DefaultConfigOptionGui(
+ val orderedOptions: Map<FeatureToggleProcessor.Category, List<FeatureToggleProcessor.FeatureToggleableOption>>,
+ old: String,
+ new: String
+) :
+ GuiScreen() {
+ val title = if (old == "null") {
+ if (new == "null")
+ "§5SkyHanni Default Options"
+ else
+ "§5SkyHanni Options In Version $new"
+ } else {
+ if (new == "null") {
+ "§5SkyHanni Options since $old"
+ } else {
+ "§5SkyHanni Options $old → $new"
+ }
+ }
+ val xSize = 400
+ val ySize = 300
+ val barSize = 40
+ val padding = 10
+ var wasMouseDown = false
+ val cardHeight = 30
+
+ var currentScrollOffset = 0
+
+ enum class ResetSuggestionState(val label: String) {
+ TURN_ALL_OFF("§c§lTurn all off"),
+ TURN_ALL_ON("§a§lTurn all on"),
+ LEAVE_DEFAULTS("§b§lLeave unchanged"), ;
+
+ val next get() = entries[(ordinal + 1) % entries.size]
+ }
+
+ val resetSuggestionState =
+ orderedOptions.keys.associateWith { ResetSuggestionState.LEAVE_DEFAULTS }.toMutableMap()
+
+ override fun drawScreen(mouseX: Int, mouseY: Int, partialTicks: Float) {
+ super.drawScreen(mouseX, mouseY, partialTicks)
+ drawDefaultBackground()
+ RenderUtils.drawFloatingRectDark((width - xSize) / 2, (height - ySize) / 2, xSize, ySize)
+ val sr = ScaledResolution(mc)
+ var hoveringTextToDraw: List<String>? = null
+ var mx = mouseX - ((width - xSize) / 2) - padding
+ val isMouseDown = Mouse.isButtonDown(0)
+ val shouldClick = isMouseDown && !wasMouseDown
+ wasMouseDown = isMouseDown
+ val isMouseInScrollArea =
+ mx in 0..xSize && mouseY in ((height - ySize) / 2) + barSize..((height + ySize) / 2 - barSize)
+ var my = mouseY - ((height - ySize) / 2 + barSize) + currentScrollOffset
+
+ GlStateManager.pushMatrix()
+ GlStateManager.translate(width / 2F, (height - ySize) / 2F, 0F)
+ GlStateManager.scale(2f, 2f, 1f)
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(
+ title,
+ mc.fontRendererObj,
+ 0F,
+ mc.fontRendererObj.FONT_HEIGHT.toFloat(),
+ false,
+ xSize / 2 - padding,
+ -1
+ )
+ GlStateManager.popMatrix()
+
+ GlStateManager.pushMatrix()
+ GlStateManager.translate(
+ (width - xSize) / 2F + padding,
+ (height + ySize) / 2F - mc.fontRendererObj.FONT_HEIGHT * 2,
+ 0F
+ )
+ var i = 0
+ fun bt(title: String, tooltip: List<String>, func: () -> Unit) {
+ val lw = mc.fontRendererObj.getStringWidth(title)
+ var s = false
+ if (mouseX - ((width - xSize) / 2 + padding) in i..(i + lw)
+ && mouseY - (height + ySize) / 2 in -barSize..0
+ ) {
+ s = true
+ hoveringTextToDraw = tooltip
+ if (shouldClick) {
+ func()
+ }
+ }
+ RenderUtils.drawFloatingRectDark(i - 1, -3, lw + 4, 14)
+ mc.fontRendererObj.drawString(title, 2 + i.toFloat(), 0F, if (s) 0xFF00FF00.toInt() else -1, s)
+ i += lw + 12
+ }
+ bt("Apply choices", listOf()) {
+ DefaultConfigFeatures.applyCategorySelections(resetSuggestionState, orderedOptions)
+ mc.displayGuiScreen(null)
+ }
+ bt("Turn all on", listOf()) {
+ resetSuggestionState.entries.forEach {
+ it.setValue(ResetSuggestionState.TURN_ALL_ON);
+ orderedOptions[it.key]!!.forEach { it.toggleOverride = null }
+ }
+ }
+ bt("Turn all off", listOf()) {
+ resetSuggestionState.entries.forEach {
+ it.setValue(ResetSuggestionState.TURN_ALL_OFF)
+ orderedOptions[it.key]!!.forEach { it.toggleOverride = null }
+ }
+ }
+ bt("Leave all untouched", listOf()) {
+ resetSuggestionState.entries.forEach {
+ it.setValue(ResetSuggestionState.LEAVE_DEFAULTS)
+ orderedOptions[it.key]!!.forEach { it.toggleOverride = null }
+ }
+ }
+ bt("Cancel", listOf()) {
+ mc.displayGuiScreen(null)
+ }
+ GlStateManager.popMatrix()
+
+ GlStateManager.pushMatrix()
+ GlScissorStack.push(
+ (width - xSize) / 2,
+ (height - ySize) / 2 + barSize,
+ (width + xSize) / 2,
+ (height + ySize) / 2 - barSize,
+ sr
+ )
+ GlStateManager.translate(
+ (width - xSize) / 2F + padding,
+ (height - ySize) / 2F + barSize - currentScrollOffset,
+ 0F
+ )
+
+ for ((cat) in orderedOptions.entries) {
+ val suggestionState = resetSuggestionState[cat]!!
+ drawRect(0, 0, xSize - padding * 2, 1, 0xFF808080.toInt())
+ drawRect(0, 30, xSize - padding * 2, cardHeight + 1, 0xFF808080.toInt())
+ drawRect(0, 0, 1, cardHeight, 0xFF808080.toInt())
+ drawRect(xSize - padding * 2 - 1, 0, xSize - padding * 2, cardHeight, 0xFF808080.toInt())
+ mc.fontRendererObj.drawString("§e${cat.name} ${suggestionState.label}", 4, 4, -1)
+ mc.fontRendererObj.drawSplitString("§7${cat.description}", 4, 14, xSize - padding * 2 - 8, -1)
+ if (isMouseInScrollArea && my in 0..cardHeight) {
+ hoveringTextToDraw =
+ listOf(
+ "§e${cat.name}",
+ "§7${cat.description}",
+ "§7Current plan: ${suggestionState.label}",
+ "§aClick to toggle!",
+ "§7Hold shift to show all options"
+ )
+ if (isShiftKeyDown()) {
+ hoveringTextToDraw = listOf(
+ "§e${cat.name}",
+ "§7${cat.description}",
+ ) + orderedOptions[cat]!!.map { "§7 - §a" + it.name }
+ }
+ if (shouldClick) {
+ resetSuggestionState[cat] = suggestionState.next
+ orderedOptions[cat]!!.forEach { it.toggleOverride = null }
+ }
+ }
+ my -= cardHeight
+ GlStateManager.translate(0F, cardHeight.toFloat(), 0F)
+ }
+
+
+ GlStateManager.popMatrix()
+ GlScissorStack.pop(sr)
+ if (hoveringTextToDraw != null) {
+ Utils.drawHoveringText(hoveringTextToDraw, mouseX, mouseY, width, height, 100, mc.fontRendererObj)
+ }
+
+ }
+
+ fun scroll(s: Int) {
+ currentScrollOffset =
+ Math.max(0, Math.min(s, (orderedOptions.size + 1) * cardHeight - ySize + barSize + padding * 2))
+ }
+
+ override fun handleMouseInput() {
+ super.handleMouseInput()
+ if (Mouse.getEventDWheel() != 0)
+ scroll(currentScrollOffset - Mouse.getEventDWheel())
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt
new file mode 100644
index 000000000..c6b1beea5
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/FeatureToggleProcessor.kt
@@ -0,0 +1,70 @@
+package at.hannibal2.skyhanni.features.misc.massconfiguration
+
+import at.hannibal2.skyhanni.config.FeatureToggle
+import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean
+import io.github.moulberry.moulconfig.annotations.ConfigOption
+import io.github.moulberry.moulconfig.processor.ConfigStructureReader
+import java.lang.reflect.Field
+import java.util.*
+
+class FeatureToggleProcessor : ConfigStructureReader {
+
+ var latestCategory: Category? = null
+ val pathStack = Stack<String>()
+
+ val allOptions = mutableListOf<FeatureToggleableOption>()
+ val orderedOptions by lazy {
+ allOptions.groupBy { it.category }
+ }
+
+ data class FeatureToggleableOption(
+ val name: String, val description: String, val previouslyEnabled: Boolean,
+ val isTrueEnabled: Boolean, val category: Category,
+ val setter: (Boolean) -> Unit,
+ val path: String,
+ var toggleOverride: DefaultConfigOptionGui.ResetSuggestionState? = null
+ )
+
+ data class Category(val name: String, val description: String)
+
+ override fun beginCategory(baseObject: Any?, field: Field?, name: String, description: String) {
+ latestCategory = Category(name, description)
+ }
+
+ override fun endCategory() {
+ }
+
+ override fun beginAccordion(baseObject: Any?, field: Field?, option: ConfigOption?, id: Int) {
+ }
+
+ override fun endAccordion() {
+ }
+
+ override fun pushPath(fieldPath: String) {
+ pathStack.push(fieldPath)
+ }
+
+ override fun popPath() {
+ pathStack.pop()
+ }
+
+ override fun emitOption(baseObject: Any, field: Field, option: ConfigOption) {
+ val featureToggle = field.getAnnotation(FeatureToggle::class.java) ?: return
+ field.getAnnotation(ConfigEditorBoolean::class.java)
+ ?: error("Feature toggle found without ConfigEditorBoolean: $field")
+ allOptions.add(
+ FeatureToggleableOption(
+ option.name,
+ option.desc,
+ field.getBoolean(baseObject),
+ featureToggle.trueIsEnabled,
+ latestCategory!!,
+ { field.setBoolean(baseObject, it) },
+ pathStack.joinToString(".") + "." + field.name
+ )
+ )
+ }
+
+ override fun emitGuiOverlay(baseObject: Any?, field: Field?, option: ConfigOption?) {
+ }
+} \ No newline at end of file