diff --git a/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt b/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt index 95c78ca81..0f3800eb7 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/core/Config.kt @@ -313,6 +313,17 @@ object Config : Vigilant( ) var partyFinderStats = false + @Property( + type = PropertyType.SELECTOR, name = "Run Breakdown", + description = "§b[WIP] Shows a Breakdown on what players did in the dungeon.\n§eNote: Requires teammates to use Skytils Websocket otherwise it's highly inaccurate.", + options = ["Disabled", "Enabled", "Enabled + Terminals"], + category = "Dungeons", subcategory = "Miscellaneous", + i18nName = "skytils.config.dungeons.miscellaneous.run_breakdown", + i18nCategory = "skytils.config.dungeons", + i18nSubcategory = "skytils.config.dungeons.miscellaneous" + ) + var runBreakdown = 0 + @Property( type = PropertyType.SWITCH, name = "Dungeon Chest Profit", description = "Shows the estimated profit for items from chests in dungeons.", diff --git a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/DungeonMapPlayer.kt b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/DungeonMapPlayer.kt index 3a74ef9d5..d13f597bc 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/DungeonMapPlayer.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/features/impl/dungeons/catlas/core/DungeonMapPlayer.kt @@ -18,9 +18,12 @@ package gg.skytils.skytilsmod.features.impl.dungeons.catlas.core +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonScanner +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.MapUtils import gg.skytils.skytilsmod.listeners.DungeonListener import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EnumPlayerModelParts +import net.minecraft.util.BlockPos import net.minecraft.util.ResourceLocation data class DungeonMapPlayer(val teammate: DungeonListener.DungeonTeammate, val skin: ResourceLocation) { @@ -41,4 +44,15 @@ data class DungeonMapPlayer(val teammate: DungeonListener.DungeonTeammate, val s uuid = player.uniqueID.toString() playerLoaded = true } + + fun getBlockPos(): BlockPos { + val playerPos = this.teammate.player?.playerLocation + if (playerPos != null) return playerPos + + val x = (this.mapX.toFloat() - MapUtils.startCorner.first) / MapUtils.coordMultiplier + DungeonScanner.startX - 15 + val y = 0.0 + val z = (this.mapZ.toFloat() - MapUtils.startCorner.second) / MapUtils.coordMultiplier + DungeonScanner.startZ - 15 + + return BlockPos(x, y, z) + } } diff --git a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt index 2690a4a92..1c3f83e1f 100644 --- a/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt +++ b/src/main/kotlin/gg/skytils/skytilsmod/listeners/DungeonListener.kt @@ -38,6 +38,7 @@ import gg.skytils.skytilsmod.features.impl.dungeons.ScoreCalculation import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.DungeonMapPlayer import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.Room import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.RoomType +import gg.skytils.skytilsmod.features.impl.dungeons.catlas.core.map.UniqueRoom import gg.skytils.skytilsmod.features.impl.dungeons.catlas.handlers.DungeonInfo import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.ScanUtils import gg.skytils.skytilsmod.features.impl.handlers.CooldownTracker @@ -113,6 +114,7 @@ object DungeonListener { private val secretsRegex = Regex("\\s*§7(?\\d+)\\/(?\\d+) Secrets") private val keyPickupRegex = Regex("§r§e§lRIGHT CLICK §r§7on §r§7.+?§r§7 to open it\\. This key can only be used to open §r§a(?\\d+)§r§7 door!§r") private val witherDoorOpenedRegex = Regex("^(?:\\[.+?] )?(?\\w+) opened a WITHER door!$") + private val terminalCompletedRegex = Regex("§r§.(?\\w+)§r§a (?:activated|completed) a (?device|terminal|lever)! \\(§r§c(?\\d)§r§a\\/(?\\d)\\)§r") private const val bloodOpenedString = "§r§cThe §r§c§lBLOOD DOOR§r§c has been opened!§r" val outboundRoomQueue = ConcurrentLinkedQueue() var isSoloDungeon = false @@ -149,6 +151,8 @@ object DungeonListener { val room = tile.uniqueRoom ?: return@setFoundSecrets if (room.foundSecrets != sec) { room.foundSecrets = sec + updateSecrets(room) + if (team.size > 1) WSClient.sendPacketAsync(C2SPacketDungeonRoomSecret(SBInfo.server ?: return@setFoundSecrets, room.mainRoom.data.name, sec)) } @@ -160,9 +164,18 @@ object DungeonListener { DungeonFeatures.DungeonSecretDisplay.maxSecrets = -1 } } else { - if (text.stripControlCodes() - .trim() == "> EXTRA STATS <" - ) { + terminalCompletedRegex.find(text)?.let { + val completer = team[it.groups["username"]?.value] + val type = it.groups["type"]?.value + + if (completer != null && type != null) { + when (type) { + "lever" -> completer.leversDone++ + "terminal", "device" -> completer.terminalsDone++ + } + } + } + if (text.stripControlCodes().trim() == "> EXTRA STATS <") { if (team.size > 1) { SBInfo.server?.let { WSClient.sendPacketAsync(C2SPacketDungeonEnd(it)) @@ -181,6 +194,35 @@ object DungeonListener { if (Skytils.config.autoRepartyOnDungeonEnd) { RepartyCommand.processCommand(mc.thePlayer, emptyArray()) } + if (Skytils.config.runBreakdown != 0) { + tickTimer(6) { + val output = team.map { + val secretsDone = "§aSecrets: §6${ + if (it.value.minimumSecretsDone == it.value.maximumSecretsDone) { + "${it.value.minimumSecretsDone}" + } else "${it.value.minimumSecretsDone}§a - §6${it.value.maximumSecretsDone}" + }" + + val roomsDone = "§aRooms: §6${ + if (it.value.minimumRoomsDone == it.value.maximumRoomsDone) { + "${it.value.minimumRoomsDone}" + } else "${it.value.minimumRoomsDone}§a - §6${it.value.maximumRoomsDone}" + }" + + //TODO: Maybe also save the rank color? + var output = + "§6${it.key}§a | $secretsDone§a | $roomsDone§a | Deaths: §6${it.value.deaths}" + + if (Skytils.config.runBreakdown == 2 && DungeonFeatures.dungeonFloorNumber == 7) { + output += "§a | Terminals: §6${it.value.terminalsDone}§a | Levers: §6${it.value.leversDone}" + } + + output + } + + UChat.chat(output.joinToString("\n")) + } + } } else if (text.startsWith("§r§c ☠ ")) { if (text.endsWith(" §r§7reconnected§r§7.§r")) { val match = reconnectedRegex.find(text) ?: return @@ -494,6 +536,41 @@ object DungeonListener { } } + fun updateSecrets(room: UniqueRoom) { + if (room.mainRoom.data.secrets < 1 || (room.foundSecrets ?: -1) < 1) return + + val finders = team.filter { entry -> + val location = entry.value.mapPlayer.getBlockPos() + val playerRoom = ScanUtils.getRoomFromPos(location)?.uniqueRoom + + playerRoom != null && playerRoom.name != "Unknown" && room.mainRoom.data.name == playerRoom.name + } + + if (finders.size >= 2) { + finders.forEach { + team[it.key]?.let { member -> + member.maximumSecretsDone++ + + if (room.foundSecrets == room.mainRoom.data.secrets) { + member.maximumRoomsDone++ + } + } + } + } else if (finders.size == 1) { + finders.forEach { + team[it.key]?.let { member -> + member.minimumSecretsDone++ + member.maximumSecretsDone++ + + if (room.foundSecrets == room.mainRoom.data.secrets) { + member.minimumRoomsDone++ + member.maximumRoomsDone++ + } + } + } + } + } + data class DungeonTeammate( val playerName: String, val dungeonClass: DungeonClass, @@ -510,6 +587,13 @@ object DungeonListener { } var dead = false var deaths = 0 + var minimumSecretsDone = 0 + var maximumSecretsDone = 0 + var minimumRoomsDone = 0 + var maximumRoomsDone = 0 + var terminalsDone = 0 + var leversDone = 0 + var lastLivingStateChange: Long? = null val mapPlayer = DungeonMapPlayer(this, skin) diff --git a/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt b/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt index 1a0837375..d7189bec8 100644 --- a/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt +++ b/src/main/kotlin/gg/skytils/skytilsws/client/PacketHandler.kt @@ -29,6 +29,7 @@ import gg.skytils.skytilsmod.features.impl.dungeons.catlas.utils.ScanUtils import gg.skytils.skytilsmod.features.impl.mining.CHWaypoints import gg.skytils.skytilsmod.features.impl.mining.CHWaypoints.CHInstance import gg.skytils.skytilsmod.features.impl.mining.CHWaypoints.chWaypointsList +import gg.skytils.skytilsmod.listeners.DungeonListener.updateSecrets import gg.skytils.skytilsmod.utils.SBInfo import gg.skytils.skytilsws.shared.IPacketHandler import gg.skytils.skytilsws.shared.SkytilsWS @@ -59,9 +60,10 @@ object PacketHandler : IPacketHandler { } } is S2CPacketDungeonRoomSecret -> { - DungeonInfo.uniqueRooms.find { it.mainRoom.data.name == packet.roomId }?.let { - if (packet.secretCount > (it.foundSecrets ?: -1)) { - it.foundSecrets = packet.secretCount + DungeonInfo.uniqueRooms.find { it.mainRoom.data.name == packet.roomId }?.let { room -> + if (packet.secretCount > (room.foundSecrets ?: -1)) { + room.foundSecrets = packet.secretCount + updateSecrets(room) } } } diff --git a/src/main/resources/assets/skytils/lang/en_US.lang b/src/main/resources/assets/skytils/lang/en_US.lang index c64d5306d..13affca83 100644 --- a/src/main/resources/assets/skytils/lang/en_US.lang +++ b/src/main/resources/assets/skytils/lang/en_US.lang @@ -23,6 +23,7 @@ skytils.config.dungeons.miscellaneous.auto_copy_fails_to_clipboard=Auto Copy Fai skytils.config.dungeons.quality_of_life.autoreparty_on_dungeon_ending=Auto-Reparty on Dungeon Ending skytils.config.dungeons.miscellaneous.death_counter=Death Counter skytils.config.dungeons.party_finder.party_finder_stats=Party Finder Stats +skytils.config.dungeons.miscellaneous.run_breakdown=Run Breakdown skytils.config.dungeons.miscellaneous.dungeon_chest_profit=Dungeon Chest Profit skytils.config.dungeons.miscellaneous.dungeon_chest_profit_includes_essence=Dungeon Chest Profit Includes Essence skytils.config.dungeons.miscellaneous.highlight_unopened_croesus_chests=Highlight Unopened Croesus Chests