aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt
blob: 584d2291f8a960529b64285ddea45917b4a665ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package at.hannibal2.skyhanni.data

import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigManager
import at.hannibal2.skyhanni.data.Mayor.Companion.setAssumeMayor
import at.hannibal2.skyhanni.data.Mayor.Companion.setAssumeMayorJson
import at.hannibal2.skyhanni.data.jsonobjects.other.MayorCandidate
import at.hannibal2.skyhanni.data.jsonobjects.other.MayorElection
import at.hannibal2.skyhanni.data.jsonobjects.other.MayorJson
import at.hannibal2.skyhanni.events.ConfigLoadEvent
import at.hannibal2.skyhanni.events.DebugDataCollectEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.utils.APIUtil
import at.hannibal2.skyhanni.utils.CollectionUtils.put
import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.asTimeMark
import at.hannibal2.skyhanni.utils.SkyBlockTime
import at.hannibal2.skyhanni.utils.json.fromJson
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes

object MayorAPI {

    val group = RepoPattern.group("mayorapi")
    val foxyExtraEventPattern by group.pattern(
        "foxy.extraevent",
        "Schedules an extra §.(?<event>.*) §.event during the year\\."
    )
    private val electionOver by group.pattern(
        "election.over",
        "§eThe election room is now closed\\. Clerk Seraphine is doing a final count of the votes\\.\\.\\."
    )

    private var lastMayor: Mayor? = null

    var lastUpdate = SimpleTimeMark.farPast()
    private var dispatcher = Dispatchers.IO

    private var rawMayorData: MayorJson? = null
    var candidates = mapOf<Int, MayorCandidate>()
        private set
    var currentMayor: Mayor? = null
        private set
    var timeTillNextMayor = Duration.ZERO
        private set

    private const val ELECTION_END_MONTH = 3 // Late Spring
    private const val ELECTION_END_DAY = 27

    /**
     * @param input: The name of the mayor
     * @return: The neu color of the mayor; If no mayor was found, it will return "§c"
     */
    fun mayorNameToColorCode(input: String): String = Mayor.getMayorFromName(input)?.color ?: "§c"

    /**
     * @param input: The name of the mayor
     * @return: The neu color of the mayor + the name of the mayor; If no mayor was found, it will return "§c[input]"
     */
    fun mayorNameWithColorCode(input: String) = mayorNameToColorCode(input) + input

    @SubscribeEvent
    fun onSecondPassed(event: SecondPassedEvent) {
        if (!LorenzUtils.onHypixel) return
        if (event.repeatSeconds(2)) {
            checkHypixelAPI()
            getTimeTillNextMayor()
        }
    }

    @SubscribeEvent
    fun onChat(event: LorenzChatEvent) {
        if (!LorenzUtils.onHypixel) return

        if (electionOver.matches(event.message)) {
            lastMayor = currentMayor
            currentMayor = Mayor.UNKNOWN
        }
    }

    private fun calculateNextMayorTime(): SimpleTimeMark {
        var mayorYear = SkyBlockTime.now().year

        // Check if either the month is already over or the day after 27th in the third month
        if (SkyBlockTime.now().month > ELECTION_END_MONTH || (SkyBlockTime.now().day >= ELECTION_END_DAY && SkyBlockTime.now().month == ELECTION_END_MONTH)) {
            // If so, the next mayor will be in the next year
            mayorYear++
        }

        return SkyBlockTime(mayorYear, ELECTION_END_MONTH, day = ELECTION_END_DAY).asTimeMark()
    }

    private fun getTimeTillNextMayor() {
        val nextMayorTime = calculateNextMayorTime()
        timeTillNextMayor = nextMayorTime - SimpleTimeMark.now()
    }

    private fun checkCurrentMayor() {
        val nextMayorTime = calculateNextMayorTime()

        // Check if it is still the mayor from the old SkyBlock year
        currentMayor = candidates[nextMayorTime.toSkyBlockTime().year - 1]?.let {
            if (it.name == lastMayor?.name) return

            // TODO: Once Jerry is active, add the sub mayor perks in here
            setAssumeMayorJson(it.name, it.perks)
        }
    }

    private fun checkHypixelAPI() {
        if (lastUpdate.passedSince() < 20.minutes || (currentMayor == Mayor.UNKNOWN && lastUpdate.passedSince() < 1.minutes)) return
        lastUpdate = SimpleTimeMark.now()

        SkyHanniMod.coroutineScope.launch {
            val url = "https://api.hypixel.net/v2/resources/skyblock/election"
            val jsonObject = withContext(dispatcher) { APIUtil.getJSONResponse(url) }
            rawMayorData = ConfigManager.gson.fromJson<MayorJson>(jsonObject)
            val data = rawMayorData ?: return@launch
            val map = mutableMapOf<Int, MayorCandidate>()
            map put data.mayor.election.getPairs()
            data.current?.let {
                map put data.current.getPairs()
            }
            candidates = map
            checkCurrentMayor()
        }
    }

    private fun MayorElection.getPairs() = year + 1 to candidates.bestCandidate()

    private fun List<MayorCandidate>.bestCandidate() = maxBy { it.votes }

    @SubscribeEvent
    fun onConfigReload(event: ConfigLoadEvent) {
        SkyHanniMod.feature.dev.debug.assumeMayor.onToggle {
            val mayor = SkyHanniMod.feature.dev.debug.assumeMayor.get()

            if (mayor == Mayor.DISABLED) {
                checkCurrentMayor()
            } else {
                mayor.setAssumeMayor(mayor.perks.toList())
                currentMayor = mayor
            }
        }
    }

    @SubscribeEvent
    fun onDebugDataCollect(event: DebugDataCollectEvent) {
        event.title("Mayor")
        event.addIrrelevant {
            add("Current Mayor: ${currentMayor?.name ?: "Unknown"}")
            add("Active Perks: ${currentMayor?.activePerks}")
            add("Last Update: $lastUpdate (${lastUpdate.passedSince()} ago)")
            add("Time Till Next Mayor: $timeTillNextMayor")
        }
    }
}