diff options
Diffstat (limited to 'src/main/kotlin/tech/thatgravyboat/rewardclaim/ui')
5 files changed, 618 insertions, 0 deletions
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/RewardClaimGui.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/RewardClaimGui.kt new file mode 100644 index 0000000..8f1d535 --- /dev/null +++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/RewardClaimGui.kt @@ -0,0 +1,308 @@ +package tech.thatgravyboat.rewardclaim.ui + +import gg.essential.api.utils.Multithreading.runAsync +import gg.essential.api.utils.Multithreading.schedule +import gg.essential.elementa.WindowScreen +import gg.essential.elementa.components.* +import gg.essential.elementa.constraints.CenterConstraint +import gg.essential.elementa.dsl.* +import gg.essential.universal.ChatColor +import gg.essential.universal.UDesktop +import gg.essential.vigilance.gui.VigilancePalette +import org.apache.commons.io.IOUtils +import tech.thatgravyboat.rewardclaim.RewardConfiguration +import tech.thatgravyboat.rewardclaim.types.WebData +import java.awt.Color +import java.net.* +import java.nio.charset.Charset +import java.util.concurrent.TimeUnit +import java.util.regex.Matcher +import java.util.regex.Pattern + +private val BUTTON_HOVER = Color(0, 212, 105) + +private val SECURITY_REGEX = Pattern.compile("window\\.securityToken = \"(?<token>.*)\";") +private val DATA_REGEX = Pattern.compile("window\\.appData = '(?<data>\\{.*})';") +private val I18N_REGEX = Pattern.compile("window.i18n = \\{(?<translations>.*)};", Pattern.DOTALL) + +class RewardClaimGui(private val id : String) : WindowScreen() { + + private var state: State = State.LOADING + private var selected = -1 + + //Raw Data + private lateinit var data: WebData + + init { + runAsync(Runnable { + try { + val cookieManager = CookieManager() + CookieHandler.setDefault(cookieManager) + val url = URL("https://rewards.hypixel.net/claim-reward/$id") + (url.openConnection() as HttpURLConnection).apply { + requestMethod = "GET" + useCaches = true + addRequestProperty("User-Agent", RewardConfiguration.userAgent) + readTimeout = 15000 + connectTimeout = 15000 + doOutput = true + inputStream.use { + val html = IOUtils.toString(it, Charset.defaultCharset()) + val securityMatcher: Matcher = SECURITY_REGEX.matcher(html) + val dataMatcher: Matcher = DATA_REGEX.matcher(html) + val i18nMatcher: Matcher = I18N_REGEX.matcher(html) + val securityFound = securityMatcher.find() + val dataFound = dataMatcher.find() + val i18nFound = i18nMatcher.find() + + if (securityFound && dataFound && i18nFound) { + data = WebData(securityMatcher, dataMatcher, i18nMatcher) + + if (data.rewards.isEmpty()) { + state = State.FAILED_REWARDS + errorPopup("Rewards were empty.") + }else { + state = State.SUCCESSFUL + updateElements() + } + + if (data.skippable || data.duration == 0){ + adPopup(true) + }else { + schedule({ adPopup(true) }, data.duration.toLong(), TimeUnit.SECONDS) + } + } else { + state = State.FAILED_REWARDS + errorPopup("Regex could not be found.\nSecurity: $securityFound\nI18n: $i18nFound\nData: $dataFound") + } + } + } + } catch (e: Exception) { + state = State.FAILED + errorPopup("Error: " + e.message) + e.printStackTrace() + } + }) + adPopup(false) + } + + private val background = UIBlock(VigilancePalette.getBackground()).constrain { + width = 100.percent() + height = 100.percent() + } childOf this.window + + private val selectedReward = UISelectedReward(30.percent() - 0.5.pixel()) childOf background + + init { + UIBlock(VigilancePalette.getDivider()).constrain { + width = 1.pixel() + height = 75.percent() + x = 50.percent() - 0.5.pixel() + y = 12.5.percent() + } childOf background + + UIBlock(VigilancePalette.getDivider()).constrain { + width = 1.pixel() + height = 75.percent() + x = 10.percent() - 0.5.pixel() + y = 12.5.percent() + } childOf background + + UIBlock(VigilancePalette.getDivider()).constrain { + width = 30.percent() - 1.pixel() + height = 1.pixel() + x = 15.percent() + y = 70.percent() + } childOf background + + UIText("Click to claim 1 of the 3 Rewards").apply { + constrain { + x = 30.percent() - 0.5.pixel() - (getTextWidth() / 2f).pixel() + y = 15.percent() + } childOf background + } + + UIText("Reward Options").apply { + constrain { + x = 75.percent() - (getTextWidth() / 2f).pixel() + y = 15.percent() + } childOf background + } + + UIText("§lClaim Reward", false).let { text -> + UIBlock(VigilancePalette.getAccent()).let { button -> + button.constrain { + width = text.getTextWidth().pixel() + 18.pixel() + height = 17.pixel() + y = 60.percent() + x = 30.percent() - 0.5.pixel() - (text.getTextWidth() / 2f + 9f).pixel() + } childOf background + + text.constrain { + x = CenterConstraint() + y = CenterConstraint() + } childOf button + + button.onMouseEnter { setColor(BUTTON_HOVER) } + button.onMouseLeave { setColor(VigilancePalette.getAccent()) } + button.onMouseClick { event -> + if (event.mouseButton == 0 && selected != -1) confirmPopup() + event.stopPropagation() + } + } + } + + UIText("Daily Streak").apply { + constrain { + x = 30.percent() - 0.5.pixel() - (getTextWidth() / 2f).pixel() + y = 72.percent() + } childOf background + } + + adPopup(false) + } + + //region Daily Streak + + private val streakSubHeading = UIText(String.format("Current: %d Highest: %d", 0, 0)).apply { + constrain { + x = 30.percent() - 0.5.pixel() - (getTextWidth() / 2f).pixel() + y = 77.percent() + } childOf background + } + + private val streaks = Array(9){ i -> + UICircle(5f, VigilancePalette.getDivider(), 15).let { + it.constrain { + x = ((15.percent() + (15.percent() - 0.5.pixel())) - 45.pixel()) + (it.getWidth() * i).pixel() + y = 83.percent() + } childOf background + } + } + + //endregion + + private val rewards = Array(3){ i -> UIReward(57.5.percent(), 30.percent() + (18 * i).percent()) childOf background}.also { + for (reward in it) { + reward.onMouseClick { event -> + if (event.mouseButton == 0 && state == State.SUCCESSFUL) { + for (j in 0..2) { + it[j].setSelected(it[j] == reward) + if (it[j] != event.currentTarget) continue + selected = j + selectedReward.updateInfo(data.rewards[selected], data.language) + } + } + } + reward.hide(true) + } + } + + private fun updateElements() { + data.let { + for (i in 0 until data.streak.progress) streaks[i].setColor(VigilancePalette.getAccent()) + streakSubHeading.setText(String.format("Current: %d Highest: %d", data.streak.current, data.streak.highest)) + streakSubHeading.setX(30.percent() - 0.5.pixel() - (streakSubHeading.getTextWidth() / 2f).pixel()) + + for (i in 0 until 3) { + rewards[i].let { + it.setData(data.rewards[i], data.language) + it.unhide(true) + } + } + } + } + + private var popup : UIPopup? = null + + private fun errorPopup(error : String) { + Window.enqueueRenderOperation { + popup?.let { + if (it.parent.hasParent){ + it.parent.removeChild(it) + popup = null + } + } + popup = UIPopup( + "${ChatColor.RED}An Error Occurred!", error, + UIImage.ofResourceCached("/vigilance/cancel.png"), + { restorePreviousScreen() }, + "${ChatColor.BOLD}Close", + UIImage.ofResourceCached("/rewardclaim/external_link.png"), + { UDesktop.browse(URI("https://rewards.hypixel.net/claim-reward/${id}")) }, + "${ChatColor.BOLD}Reward" + ) childOf this.window + } + } + + private fun confirmPopup() { + popup = UIPopup("Confirmation", "Are you sure you want to claim this reward. Click 'Back' if you would like to change which reward you would like to claim or 'Continue' if you like to go ahead with your reward.", + null, { removePopup() }, "${ChatColor.BOLD}Back", + null, { + runAsync { + try { + (URL("https://rewards.hypixel.net/claim-reward/claim?option=$selected&id=$id&activeAd=${data.activeAd}&_csrf=${data.securityToken}&watchedFallback=false").openConnection() as HttpURLConnection).apply { + requestMethod = "POST" + useCaches = true + addRequestProperty("User-Agent", RewardConfiguration.userAgent) + readTimeout = 15000 + connectTimeout = 15000 + responseCode + CookieManager.setDefault(null) + restorePreviousScreen() + } + }catch (ignored : Exception){ + //IGNORED + } + } + }, "${ChatColor.BOLD}Continue" + ) childOf this.window + } + + private fun removePopup() { + Window.enqueueRenderOperation { + popup?.let { + if (it.parent.hasParent){ + it.parent.removeChild(it) + popup = null + } + } + } + } + + private fun adPopup(skip: Boolean) { + Window.enqueueRenderOperation { + popup?.let { + if (it.parent.hasParent){ + it.parent.removeChild(it) + popup = null + } + } + popup = if (skip) { + UIPopup( + "Reward Claim AD", + "Hypixel is a great server and as such we don't want to remove their ad for their store. You can click the skip button when it appears to remove this message and claim your reward.", + UIImage.ofResourceCached("/rewardclaim/external_link.png"), { UDesktop.browse(URI(data.adLink)) }, "${ChatColor.BOLD}Store", + null, { removePopup() }, "${ChatColor.BOLD}Skip" + ) childOf this.window + } else { + UIPopup( + "Reward Claim AD", + "Hypixel is a great server and as such we don't want to remove their ad for their store. You can click the skip button when it appears to remove this message and claim your reward.", + UIImage.ofResourceCached("/rewardclaim/external_link.png"), { UDesktop.browse(URI(data.adLink)) }, "${ChatColor.BOLD}Store" + ) childOf this.window + } + } + } + + override fun onDrawScreen(mouseX: Int, mouseY: Int, partialTicks: Float) { + this.window.draw() + } + + private enum class State { + LOADING, + FAILED, + SUCCESSFUL, + FAILED_REWARDS + } +}
\ No newline at end of file diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIButton.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIButton.kt new file mode 100644 index 0000000..e1037d0 --- /dev/null +++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIButton.kt @@ -0,0 +1,51 @@ +package tech.thatgravyboat.rewardclaim.ui + +import gg.essential.elementa.components.UIBlock +import gg.essential.elementa.components.UIImage +import gg.essential.elementa.components.UIText +import gg.essential.elementa.constraints.CenterConstraint +import gg.essential.elementa.constraints.XConstraint +import gg.essential.elementa.dsl.* +import gg.essential.vigilance.gui.VigilancePalette +import java.awt.Color + +private val BUTTON_HOVER = Color(0, 212, 105) + +class UIButton(private val image : UIImage?, private val widthIn : Float, private var xConstraint : XConstraint, private val text : UIText, alignment: Alignment) : UIBlock(VigilancePalette.getAccent()) { + + init { + if (alignment == Alignment.LEFT) { + xConstraint += (widthIn / 2f + 9f).pixel() + } else if (alignment == Alignment.RIGHT) { + xConstraint -= (widthIn / 2f + 9f).pixel() + } + + constrain { + width = widthIn.pixel() + height = 17.pixel() + x = xConstraint + y = 95.percent() - 17.pixel() + } + + text.constrain { + x = CenterConstraint() - (if (image != null) 9 else 0).pixel() + y = CenterConstraint() + } childOf this + + image?.let { + it.constrain { + width = 9.pixel() + height = 9.pixel() + x = 100.percent() + 9.pixel() + y = CenterConstraint() + } childOf text + } + this.onMouseEnter { this.setColor(BUTTON_HOVER) } + this.onMouseLeave { this.setColor(VigilancePalette.getAccent()) } + } + +} + +enum class Alignment { + LEFT, RIGHT, MIDDLE +}
\ No newline at end of file diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIPopup.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIPopup.kt new file mode 100644 index 0000000..a3b4363 --- /dev/null +++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIPopup.kt @@ -0,0 +1,70 @@ +package tech.thatgravyboat.rewardclaim.ui + +import gg.essential.elementa.UIComponent +import gg.essential.elementa.components.UIBlock +import gg.essential.elementa.components.UIImage +import gg.essential.elementa.components.UIText +import gg.essential.elementa.components.UIWrappedText +import gg.essential.elementa.constraints.CenterConstraint +import gg.essential.elementa.dsl.* +import gg.essential.elementa.events.UIClickEvent +import gg.essential.elementa.utils.withAlpha +import gg.essential.vigilance.gui.VigilancePalette +import gg.essential.vigilance.gui.VigilancePalette.getBackground + +class UIPopup private constructor(title : String, text : String) : UIBlock(getBackground().withAlpha(200)) { + + private val box: UIBlock + + constructor(title : String, text : String, image : UIImage? = null, event : (UIComponent.(event: UIClickEvent) -> Unit), buttonText : String + ) : this(title, text) { + + val btnText = UIText(buttonText, false) + val width = btnText.getTextWidth() + 18 + if (image != null) 18 else 0 + + UIButton(image, width, CenterConstraint(), btnText, Alignment.MIDDLE).onMouseClick(event) childOf box + } + + constructor(title : String, text : String, + image1 : UIImage? = null, event1 : (UIComponent.(event: UIClickEvent) -> Unit), buttonText : String, + image2 : UIImage? = null, event2 : (UIComponent.(event: UIClickEvent) -> Unit), buttonText2 : String + ) : this(title, text) { + + val btn1Text = UIText(buttonText, false) + val btn1Width = btn1Text.getTextWidth() + 18 + if (image1 != null) 18 else 0 + val btn2Text = UIText(buttonText2, false) + val btn2Width = btn2Text.getTextWidth() + 18 + if (image2 != null) 18 else 0 + + val width = btn1Width.coerceAtLeast(btn2Width) + + UIButton(image1, width, CenterConstraint() - 5.percent(), btn1Text, Alignment.RIGHT).onMouseClick(event1) childOf box + UIButton(image2, width, CenterConstraint() + 5.percent(), btn2Text, Alignment.LEFT).onMouseClick(event2) childOf box + } + + init { + constrain { + width = 100.percent() + height = 100.percent() + } + + box = UIBlock(VigilancePalette.getHighlight()).constrain { + width = 50.percent() + height = 45.percent() + x = CenterConstraint() + y = CenterConstraint() + } childOf this + + UIText(title).constrain { + y = 5.percent() + x = CenterConstraint() + } childOf box + + UIWrappedText(text, true, centered = true, trimText = true).constrain { + width = 90.percent() + height = 80.percent() + x = 5.percent() + y = 5.percent() + 11.pixel() + } childOf box + } + +}
\ No newline at end of file diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIReward.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIReward.kt new file mode 100644 index 0000000..da5014f --- /dev/null +++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIReward.kt @@ -0,0 +1,103 @@ +package tech.thatgravyboat.rewardclaim.ui + +import gg.essential.elementa.components.UIBlock +import gg.essential.elementa.components.UIImage +import gg.essential.elementa.components.UIText +import gg.essential.elementa.constraints.XConstraint +import gg.essential.elementa.constraints.YConstraint +import gg.essential.elementa.dsl.* +import gg.essential.elementa.effects.OutlineEffect +import gg.essential.elementa.utils.withAlpha +import gg.essential.universal.ChatColor +import gg.essential.vigilance.gui.VigilancePalette +import tech.thatgravyboat.rewardclaim.MappedImageCache +import tech.thatgravyboat.rewardclaim.RewardLanguage +import tech.thatgravyboat.rewardclaim.types.RewardData + +class UIReward(xConstraint: XConstraint, yConstraint: YConstraint) : UIBlock(VigilancePalette.getHighlight().withAlpha(204)) { + + private val imageBackground : UIBlock + private val title : UIText + private val rarityDesc : UIText + private val amountDesc : UIText + + init { + constrain { + width = 35.percent() + height = 16.percent() + x = xConstraint + y = yConstraint + } + + imageBackground = UIBlock(VigilancePalette.getBrightHighlight()).constrain { + width = 20.57.percent() + height = 80.percent() + x = 2.545.percent() + y = 10.percent() + } childOf this + + UIBlock(VigilancePalette.getDivider()).constrain { + width = 1.pixel() + height = 95.percent() + x = 25.66.percent() + y = 2.5.percent() + } childOf this + + val rightSideStart = (25.66.percent() + 2.pixel()) + 1.28.percent() + + UIBlock(VigilancePalette.getDivider()).constrain { + width = 71.78.percent() - 1.pixel() + height = 1.pixel() + x = rightSideStart + y = 26.percent() + } childOf this + + title = UIText("Unknown Reward").constrain { + x = rightSideStart + y = 26.percent() - (getHeight() + 1).pixel() + } childOf this + + rarityDesc = UIText("Rarity: Unknown").constrain { + x = rightSideStart + y = 26.percent() + 3.pixel() + } childOf this + + amountDesc = UIText("Amount: ${ChatColor.GOLD}0").constrain { + x = rightSideStart + y = 26.percent() + (5+rarityDesc.getHeight()).pixel() + } childOf this + amountDesc.hide(true) + } + + fun setSelected(selected : Boolean) { + if (selected) { + enableEffect(OutlineEffect(VigilancePalette.getAccent(), 1F)) + }else { + removeEffect<OutlineEffect>() + } + } + + fun setData(data : RewardData, language : RewardLanguage) { + title.setText(data.getDisplayName(language)) + rarityDesc.setText("Rarity: ${data.rarity.color}${language.translate(data.rarity.translationKey)}") + + data.amount?.let { + amountDesc.setText("Amount: ${ChatColor.GOLD}$it") + amountDesc.unhide(true) + } + + data.boxes?.let { + amountDesc.setText("Boxes: ${ChatColor.GOLD}$it") + amountDesc.unhide(true) + } + + data.image?.let { + it.url?.let { url -> + UIImage.ofURL(url, MappedImageCache).constrain { + width = 100.percent() + height = it.height.percent() + } childOf imageBackground + } + } + } +}
\ No newline at end of file diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UISelectedReward.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UISelectedReward.kt new file mode 100644 index 0000000..47763dc --- /dev/null +++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UISelectedReward.kt @@ -0,0 +1,86 @@ +package tech.thatgravyboat.rewardclaim.ui + +import gg.essential.elementa.components.UIBlock +import gg.essential.elementa.components.UIImage +import gg.essential.elementa.components.UIText +import gg.essential.elementa.components.UIWrappedText +import gg.essential.elementa.constraints.XConstraint +import gg.essential.elementa.dsl.* +import gg.essential.elementa.effects.OutlineEffect +import gg.essential.elementa.utils.withAlpha +import gg.essential.universal.ChatColor +import gg.essential.vigilance.gui.VigilancePalette +import tech.thatgravyboat.rewardclaim.MappedImageCache +import tech.thatgravyboat.rewardclaim.RewardLanguage +import tech.thatgravyboat.rewardclaim.types.RewardData + +class UISelectedReward(middle : XConstraint) : UIBlock(VigilancePalette.getHighlight().withAlpha(204)) { + + private var image : UIImage? = null + + private val displayName = UIText("Select a Reward!").constrain { + x = 31.percent() + y = 10.3.percent() + } childOf this + + private val imageBackground = UIBlock(VigilancePalette.getBrightHighlight()).constrain { + x = 5.percent() + y = 10.3.percent() + width = 20.5.percent() + height = 42.6.percent() + } childOf this + + private val rarity = UIText().constrain { + x = 31.percent() + y = 10.3.percent() + 13.pixel() + } childOf this + + private val amount = UIText().constrain { + x = 31.percent() + y = 10.3.percent() + 25.pixel() + } childOf this + + private val desc = UIWrappedText().constrain { + x = 5.percent() + y = 52.9.percent() + 3.pixel() + width = 90.percent() + } childOf this + + init { + constrain { + width = 35.percent() + height = 30.percent() + x = middle - 17.5.percent() + y = 25.percent() + } + + UIBlock(VigilancePalette.getDivider()).constrain { + width = 64.5.percent() + height = 1.pixel() + x = 30.5.percent() + y = 10.3.percent() + 11.pixel() + } childOf this + } + + fun updateInfo(data: RewardData, language: RewardLanguage) { + displayName.setText(data.getDisplayName(language)) + enableEffect(OutlineEffect(data.rarity.color.color!!, 1F)) + rarity.setText("Rarity: ${data.rarity.color}${language.translate(data.rarity.translationKey)}") + + if (data.amount != null) amount.setText("Amount: ${ChatColor.GOLD}${data.amount}") + else if (data.boxes != null) amount.setText("Boxes: ${ChatColor.GOLD}${data.boxes}") + else amount.setText("") + desc.setText(data.getDescription(language)) + + data.image?.let { + it.url?.let { url -> + image?.let(imageBackground::removeChild) + image = UIImage.ofURL(url, MappedImageCache).constrain { + height = it.height.percent() + width = 100.percent() + } childOf imageBackground + } + } + + } +}
\ No newline at end of file |