aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerQuestWarning.kt
blob: ecfc439230a5ccb299291c7155e5944ec1ab2afc (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
167
168
package at.hannibal2.skyhanni.features.slayer

import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.api.event.HandleEvent
import at.hannibal2.skyhanni.data.ClickType
import at.hannibal2.skyhanni.data.SlayerAPI
import at.hannibal2.skyhanni.events.EntityHealthUpdateEvent
import at.hannibal2.skyhanni.events.ItemClickEvent
import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent
import at.hannibal2.skyhanni.features.event.diana.DianaAPI
import at.hannibal2.skyhanni.features.rift.RiftAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.CollectionUtils.nextAfter
import at.hannibal2.skyhanni.utils.DelayedRun
import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull
import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.getLorenzVec
import net.minecraft.entity.EntityLivingBase
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

@SkyHanniModule
object SlayerQuestWarning {

    private val config get() = SkyHanniMod.feature.slayer

    private var lastWeaponUse = SimpleTimeMark.farPast()
    private val voidItem = "ASPECT_OF_THE_VOID".asInternalName()
    private val endItem = "ASPECT_OF_THE_END".asInternalName()

    private val outsideRiftData = SlayerData()
    private val insideRiftData = SlayerData()

    class SlayerData {
        var currentSlayerState: String? = null
        var lastSlayerType: SlayerType? = null
    }

    @SubscribeEvent
    fun onScoreboardChange(event: ScoreboardUpdateEvent) {
        val slayerType = event.full.nextAfter("Slayer Quest")
        val slayerProgress = event.full.nextAfter("Slayer Quest", skip = 2) ?: "no slayer"
        val new = slayerProgress.removeColor()
        val slayerData = getSlayerData()

        if (slayerData.currentSlayerState == new) return

        slayerData.currentSlayerState?.let {
            change(it, new)
        }
        slayerData.currentSlayerState = new
        slayerType?.let {
            slayerData.lastSlayerType = SlayerType.getByName(it)
        }
    }

    private fun getSlayerData() = if (RiftAPI.inRift()) outsideRiftData else insideRiftData

    private fun String.inCombat() = contains("Combat") || contains("Kills")
    private fun String.inBoss() = this == "Slay the boss!"
    private fun String?.bossSlain() = this == "Boss slain!"
    private fun String.noSlayer() = this == "no slayer"

    private fun change(old: String, new: String) {
        if (!old.inCombat() && new.inCombat()) {
            needSlayerQuest = false
        }
        if (old.inBoss() && new.noSlayer()) {
            needNewQuest("The old slayer quest has failed!")
        }
        if (new.bossSlain()) {
            DelayedRun.runDelayed(2.seconds) {
                if (getSlayerData().currentSlayerState.bossSlain()) {
                    needNewQuest("You have no Auto-Slayer active!")
                }
            }
        }
    }

    private var needSlayerQuest = false
    private var lastWarning = SimpleTimeMark.farPast()
    private var currentReason = ""

    private fun needNewQuest(reason: String) {
        currentReason = reason
        needSlayerQuest = true
    }

    private fun tryWarn() {
        if (!needSlayerQuest) return
        warn("New Slayer Quest!", "Start a new slayer quest! $currentReason")
    }

    private fun warn(titleMessage: String, chatMessage: String) {
        if (!config.questWarning) return
        if (lastWarning.passedSince() < 10.seconds) return

        if (DianaAPI.isDoingDiana()) return
        // prevent warnings when mobs are hit by other players
        if (lastWeaponUse.passedSince() > 500.milliseconds) return

        lastWarning = SimpleTimeMark.now()
        ChatUtils.chat(chatMessage)

        if (config.questWarningTitle) {
            LorenzUtils.sendTitle("§e$titleMessage", 2.seconds)
        }
    }

    @SubscribeEvent
    fun onEntityHealthUpdate(event: EntityHealthUpdateEvent) {
        if (!LorenzUtils.inSkyBlock) return

        val entity = event.entity
        if (entity.getLorenzVec().distanceToPlayer() < 6 && isSlayerMob(entity)) {
            tryWarn()
        }
    }

    private fun isSlayerMob(entity: EntityLivingBase): Boolean {
        val slayerType = SlayerAPI.getSlayerTypeForCurrentArea() ?: return false

        // workaround for rift mob that is unrelated to slayer
        if (entity.name == "Oubliette Guard") return false
        // workaround for Bladesoul in  Crimson Isle
        if (LorenzUtils.skyBlockArea == "Stronghold" && entity.name == "Skeleton") return false

        val isSlayer = slayerType.clazz.isInstance(entity)
        if (!isSlayer) return false

        SlayerAPI.activeSlayer?.let {
            if (slayerType != it) {
                val activeSlayerName = it.displayName
                val slayerName = slayerType.displayName
                SlayerAPI.latestWrongAreaWarning = SimpleTimeMark.now()
                warn(
                    "Wrong Slayer!",
                    "Wrong slayer selected! You have $activeSlayerName selected and you are in an $slayerName area!",
                )
            }
        }

        return getSlayerData().lastSlayerType == slayerType
    }

    @HandleEvent(onlyOnSkyblock = true)
    fun onItemClick(event: ItemClickEvent) {
        val internalName = event.itemInHand?.getInternalNameOrNull()

        if (event.clickType == ClickType.RIGHT_CLICK) {
            if (internalName == voidItem || internalName == endItem) {
                // ignore harmless teleportation
                return
            }
            if (internalName == null) {
                // ignore harmless right click
                return
            }
        }
        lastWeaponUse = SimpleTimeMark.now()
    }
}