aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features/world/FairySouls.kt
blob: b073726b7aeafaf819f4bdcaa523b6b8c1ab9ae5 (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
package moe.nea.firmament.features.world

import io.github.moulberry.repo.data.Coordinate
import me.shedaniel.math.Color
import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ProcessChatEvent
import moe.nea.firmament.events.SkyblockServerUpdateEvent
import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.SkyBlockIsland
import moe.nea.firmament.util.blockPos
import moe.nea.firmament.util.data.Config
import moe.nea.firmament.util.data.ManagedConfig
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld
import moe.nea.firmament.util.unformattedString


object FairySouls {


	@Serializable
	data class Data(
		val foundSouls: MutableMap<SkyBlockIsland, MutableSet<Int>> = mutableMapOf()
	)

	@Config
	object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "found-fairysouls", ::Data)

	@Config
	object TConfig : ManagedConfig("fairy-souls", Category.MISC) {
		val displaySouls by toggle("show") { false }
		val resetSouls by button("reset") {
			DConfig.data?.foundSouls?.clear() != null
			updateMissingSouls()
		}
	}


	val identifier: String get() = "fairy-souls"

	val playerReach = 5
	val playerReachSquared = playerReach * playerReach

	var currentLocationName: SkyBlockIsland? = null
	var currentLocationSouls: List<Coordinate> = emptyList()
	var currentMissingSouls: List<Coordinate> = emptyList()

	fun updateMissingSouls() {
		currentMissingSouls = emptyList()
		val c = DConfig.data ?: return
		val fi = c.foundSouls[currentLocationName] ?: setOf()
		val cms = currentLocationSouls.toMutableList()
		fi.asSequence().sortedDescending().filter { it in cms.indices }.forEach { cms.removeAt(it) }
		currentMissingSouls = cms
	}

	fun updateWorldSouls() {
		currentLocationSouls = emptyList()
		val loc = currentLocationName ?: return
		currentLocationSouls = RepoManager.neuRepo.constants.fairySouls.soulLocations[loc.locrawMode] ?: return
	}

	fun findNearestClickableSoul(): Coordinate? {
		val player = MC.player ?: return null
		val pos = player.position
		val location = SBData.skyblockLocation ?: return null
		val soulLocations: List<Coordinate> =
			RepoManager.neuRepo.constants.fairySouls.soulLocations[location.locrawMode] ?: return null
		return soulLocations
			.map { it to it.blockPos.distToCenterSqr(pos) }
			.filter { it.second < playerReachSquared }
			.minByOrNull { it.second }
			?.first
	}

	private fun markNearestSoul() {
		val nearestSoul = findNearestClickableSoul() ?: return
		val c = DConfig.data ?: return
		val loc = currentLocationName ?: return
		val idx = currentLocationSouls.indexOf(nearestSoul)
		c.foundSouls.computeIfAbsent(loc) { mutableSetOf() }.add(idx)
		DConfig.markDirty()
		updateMissingSouls()
	}

	@Subscribe
	fun onWorldRender(it: WorldRenderLastEvent) {
		if (!TConfig.displaySouls) return
		renderInWorld(it) {
			currentMissingSouls.forEach {
				block(it.blockPos, Color.ofRGBA(176, 0, 255, 128).color)
			}
			currentLocationSouls.forEach {
				wireframeCube(it.blockPos)
			}
		}
	}

	@Subscribe
	fun onProcessChat(it: ProcessChatEvent) {
		when (it.text.unformattedString) {
			"You have already found that Fairy Soul!" -> {
				markNearestSoul()
			}

			"SOUL! You found a Fairy Soul!" -> {
				markNearestSoul()
			}
		}
	}

	@Subscribe
	fun onLocationChange(it: SkyblockServerUpdateEvent) {
		currentLocationName = it.newLocraw?.skyblockLocation
		updateWorldSouls()
		updateMissingSouls()
	}
}