From de0f0e888a4c0767c8e8ca911113482a3b1f9d5c Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Wed, 6 Mar 2024 21:49:08 +0100 Subject: Renormalize line endings to be \n (#1112) --- .../skyhanni/events/RenderEntityOutlineEvent.kt | 236 ++++++------- .../skyhanni/features/combat/mobs/SpawnTimers.kt | 220 ++++++------ .../features/dungeon/DungeonFinderFeatures.kt | 372 ++++++++++----------- .../features/dungeon/DungeonTeammateOutlines.kt | 78 ++--- .../features/inventory/QuickCraftFeatures.kt | 196 +++++------ .../skyhanni/features/misc/PartyMemberOutlines.kt | 64 ++-- .../features/misc/items/GlowingDroppedItems.kt | 170 +++++----- .../skyhanni/mixins/hooks/RenderGlobalHook.kt | 42 +-- .../mixins/hooks/RendererLivingEntityHook.kt | 42 +-- .../mixins/transformers/CustomRenderGlobal.java | 32 +- .../mixins/transformers/MixinRenderGlobal.java | 80 ++--- .../transformers/MixinRendererLivingEntity.java | 42 +-- .../at/hannibal2/skyhanni/utils/LorenzRarity.kt | 110 +++--- 13 files changed, 842 insertions(+), 842 deletions(-) (limited to 'src') diff --git a/src/main/java/at/hannibal2/skyhanni/events/RenderEntityOutlineEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/RenderEntityOutlineEvent.kt index d061becf9..85c3211fd 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/RenderEntityOutlineEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/RenderEntityOutlineEvent.kt @@ -1,118 +1,118 @@ -package at.hannibal2.skyhanni.events - -import net.minecraft.client.Minecraft -import net.minecraft.entity.Entity -import net.minecraft.entity.item.EntityArmorStand -import net.minecraft.entity.item.EntityItemFrame -import java.util.function.Consumer - -class RenderEntityOutlineEvent(theType: Type?, potentialEntities: HashSet?) : - LorenzEvent() { - - /** - * The phase of the event (see [Type] - */ - var type: Type? = null - - /** - * The entities to outline. This is progressively cumulated from [.entitiesToChooseFrom] - */ - var entitiesToOutline: HashMap? = null - - /** - * The entities we can outline. Note that this set and [.entitiesToOutline] are disjoint at all times. - */ - var entitiesToChooseFrom: HashSet? = null - - /** - * Constructs the event, given the type and optional entities to outline. - * - * - * This will modify {@param potentialEntities} internally, so make a copy before passing it if necessary. - * - * @param theType of the event (see [Type] - */ - init { - type = theType - entitiesToChooseFrom = potentialEntities - if (potentialEntities != null) { - entitiesToOutline = HashMap(potentialEntities.size) - } - } - - /** - * Conditionally queue entities around which to render entities - * Selects from the pool of [.entitiesToChooseFrom] to speed up the predicate testing on subsequent calls. - * Is more efficient (theoretically) than calling [.queueEntityToOutline] for each entity because lists are handled internally. - * - * - * This function loops through all entities and so is not very efficient. - * It's advisable to encapsulate calls to this function with global checks (those not dependent on an individual entity) for efficiency purposes. - * - * @param outlineColor a function to test - */ - fun queueEntitiesToOutline(outlineColor: ((entity: Entity) -> Int?)? = null) { - if (outlineColor == null) { - return - } - if (entitiesToChooseFrom == null) { - computeAndCacheEntitiesToChooseFrom() - } - val itr: MutableIterator = entitiesToChooseFrom!!.iterator() - while (itr.hasNext()) { - val e: Entity = itr.next() - val i: Int? = outlineColor(e) - if (i != null) { - entitiesToOutline!![e] = i - itr.remove() - } - } - } - - /** - * Adds a single entity to the list of the entities to outline - * - * @param entity the entity to add - * @param outlineColor the color with which to outline - */ - fun queueEntityToOutline(entity: Entity?, outlineColor: Int) { - if (entity == null) { - return - } - if (entitiesToChooseFrom == null) { - computeAndCacheEntitiesToChooseFrom() - } - if (!entitiesToChooseFrom!!.contains(entity)) { - return - } - entitiesToOutline!![entity] = outlineColor - entitiesToChooseFrom!!.remove(entity) - } - - /** - * Used for on-the-fly generation of entities. Driven by event handlers in a decentralized fashion - */ - private fun computeAndCacheEntitiesToChooseFrom() { - val entities: List = Minecraft.getMinecraft().theWorld.getLoadedEntityList() - // Only render outlines around non-null entities within the camera frustum - entitiesToChooseFrom = HashSet(entities.size) - // Only consider entities that aren't invisible armorstands to increase FPS significantly - entities.forEach(Consumer { e: Entity? -> - if (e != null && !(e is EntityArmorStand && e.isInvisible()) && e !is EntityItemFrame) { - entitiesToChooseFrom!!.add(e) - } - }) - entitiesToOutline = HashMap(entitiesToChooseFrom!!.size) - } - - /** - * The phase of the event. - * [.XRAY] means that this directly precedes entities whose outlines are rendered through walls (Vanilla 1.9+) - * [.NO_XRAY] means that this directly precedes entities whose outlines are rendered only when visible to the client - */ - enum class Type { - - XRAY, - NO_XRAY - } -} +package at.hannibal2.skyhanni.events + +import net.minecraft.client.Minecraft +import net.minecraft.entity.Entity +import net.minecraft.entity.item.EntityArmorStand +import net.minecraft.entity.item.EntityItemFrame +import java.util.function.Consumer + +class RenderEntityOutlineEvent(theType: Type?, potentialEntities: HashSet?) : + LorenzEvent() { + + /** + * The phase of the event (see [Type] + */ + var type: Type? = null + + /** + * The entities to outline. This is progressively cumulated from [.entitiesToChooseFrom] + */ + var entitiesToOutline: HashMap? = null + + /** + * The entities we can outline. Note that this set and [.entitiesToOutline] are disjoint at all times. + */ + var entitiesToChooseFrom: HashSet? = null + + /** + * Constructs the event, given the type and optional entities to outline. + * + * + * This will modify {@param potentialEntities} internally, so make a copy before passing it if necessary. + * + * @param theType of the event (see [Type] + */ + init { + type = theType + entitiesToChooseFrom = potentialEntities + if (potentialEntities != null) { + entitiesToOutline = HashMap(potentialEntities.size) + } + } + + /** + * Conditionally queue entities around which to render entities + * Selects from the pool of [.entitiesToChooseFrom] to speed up the predicate testing on subsequent calls. + * Is more efficient (theoretically) than calling [.queueEntityToOutline] for each entity because lists are handled internally. + * + * + * This function loops through all entities and so is not very efficient. + * It's advisable to encapsulate calls to this function with global checks (those not dependent on an individual entity) for efficiency purposes. + * + * @param outlineColor a function to test + */ + fun queueEntitiesToOutline(outlineColor: ((entity: Entity) -> Int?)? = null) { + if (outlineColor == null) { + return + } + if (entitiesToChooseFrom == null) { + computeAndCacheEntitiesToChooseFrom() + } + val itr: MutableIterator = entitiesToChooseFrom!!.iterator() + while (itr.hasNext()) { + val e: Entity = itr.next() + val i: Int? = outlineColor(e) + if (i != null) { + entitiesToOutline!![e] = i + itr.remove() + } + } + } + + /** + * Adds a single entity to the list of the entities to outline + * + * @param entity the entity to add + * @param outlineColor the color with which to outline + */ + fun queueEntityToOutline(entity: Entity?, outlineColor: Int) { + if (entity == null) { + return + } + if (entitiesToChooseFrom == null) { + computeAndCacheEntitiesToChooseFrom() + } + if (!entitiesToChooseFrom!!.contains(entity)) { + return + } + entitiesToOutline!![entity] = outlineColor + entitiesToChooseFrom!!.remove(entity) + } + + /** + * Used for on-the-fly generation of entities. Driven by event handlers in a decentralized fashion + */ + private fun computeAndCacheEntitiesToChooseFrom() { + val entities: List = Minecraft.getMinecraft().theWorld.getLoadedEntityList() + // Only render outlines around non-null entities within the camera frustum + entitiesToChooseFrom = HashSet(entities.size) + // Only consider entities that aren't invisible armorstands to increase FPS significantly + entities.forEach(Consumer { e: Entity? -> + if (e != null && !(e is EntityArmorStand && e.isInvisible()) && e !is EntityItemFrame) { + entitiesToChooseFrom!!.add(e) + } + }) + entitiesToOutline = HashMap(entitiesToChooseFrom!!.size) + } + + /** + * The phase of the event. + * [.XRAY] means that this directly precedes entities whose outlines are rendered through walls (Vanilla 1.9+) + * [.NO_XRAY] means that this directly precedes entities whose outlines are rendered only when visible to the client + */ + enum class Type { + + XRAY, + NO_XRAY + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/SpawnTimers.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/SpawnTimers.kt index f92e8dccd..ee8f2b8f0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/SpawnTimers.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/SpawnTimers.kt @@ -1,110 +1,110 @@ -package at.hannibal2.skyhanni.features.combat.mobs - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.data.IslandType -import at.hannibal2.skyhanni.events.LorenzChatEvent -import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent -import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent -import at.hannibal2.skyhanni.events.PacketEvent -import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland -import at.hannibal2.skyhanni.utils.LorenzVec -import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText -import at.hannibal2.skyhanni.utils.SimpleTimeMark -import at.hannibal2.skyhanni.utils.StringUtils.matches -import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import at.hannibal2.skyhanni.utils.TimeUtils.format -import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern -import at.hannibal2.skyhanni.utils.toLorenzVec -import net.minecraft.network.play.server.S2APacketParticles -import net.minecraft.util.EnumParticleTypes -import net.minecraftforge.fml.common.eventhandler.EventPriority -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import kotlin.time.Duration.Companion.seconds - -class SpawnTimers { - - private val config get() = SkyHanniMod.feature.combat.mobs - - private val patternGroup = RepoPattern.group("combat.mobs.spawntime.arachne") - private val arachneFragmentPattern by patternGroup.pattern( - "fragment", - "^☄ [a-z0-9_]{2,22} placed an arachne's calling! something is awakening! \\(4/4\\)\$" - ) - private val arachneCrystalPattern by patternGroup.pattern( - "crystal", - "^☄ [a-z0-9_]{2,22} placed an arachne crystal! something is awakening!$" - ) - - private val arachneAltarLocation = LorenzVec(-283f, 51f, -179f) - private var arachneSpawnTime = SimpleTimeMark.farPast() - private var saveNextTickParticles = false - private var particleCounter = 0 - private var tickTime: Long = 0 - private var searchTime: Long = 0 - - @SubscribeEvent - fun onWorldChange(event: LorenzWorldChangeEvent) { - searchTime = 0 - tickTime = 0 - particleCounter = 0 - saveNextTickParticles = false - arachneSpawnTime = SimpleTimeMark.farPast() - } - - @SubscribeEvent - fun onRenderWorld(event: LorenzRenderWorldEvent) { - if (!isEnabled()) return - if (arachneSpawnTime.isInPast()) return - val countDown = arachneSpawnTime.timeUntil() - - val format = countDown.format(showMilliSeconds = true) - event.drawDynamicText(arachneAltarLocation, "§b$format", 1.5) - } - - @SubscribeEvent - fun onChatReceived(event: LorenzChatEvent) { - if (!isEnabled()) return - val message = event.message.removeColor().lowercase() - - if (arachneFragmentPattern.matches(message) || arachneCrystalPattern.matches(message)) { - if (arachneCrystalPattern.matches(message)) { - saveNextTickParticles = true - searchTime = System.currentTimeMillis() - particleCounter = 0 - tickTime = 0L - } else arachneSpawnTime = SimpleTimeMark.now() + 19.seconds - } - } - - // All this to detect "quickspawn" vs regular arachne spawn - @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) - fun onChatPacket(event: PacketEvent.ReceiveEvent) { - if (!saveNextTickParticles) return - if (System.currentTimeMillis() <= searchTime + 3000) return - - if (particleCounter == 0 && tickTime == 0L) tickTime = System.currentTimeMillis() - - if (System.currentTimeMillis() > tickTime + 60) { - arachneSpawnTime = if (particleCounter <= 20) { - SimpleTimeMark.now() + 21.seconds - } else { - SimpleTimeMark.now() + 37.seconds - } - saveNextTickParticles = false - return - } - - val packet = event.packet - if (packet is S2APacketParticles) { - val location = packet.toLorenzVec().round(2) - if (arachneAltarLocation.distance(location) > 30) return - if (packet.particleType == EnumParticleTypes.REDSTONE && packet.particleSpeed == 1.0f) { - particleCounter += 1 - } - } - } - - fun isEnabled() = - IslandType.SPIDER_DEN.isInIsland() && LorenzUtils.skyBlockArea == "Arachne's Sanctuary" && config.showArachneSpawnTimer -} +package at.hannibal2.skyhanni.features.combat.mobs + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent +import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.events.PacketEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.StringUtils.matches +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.TimeUtils.format +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import at.hannibal2.skyhanni.utils.toLorenzVec +import net.minecraft.network.play.server.S2APacketParticles +import net.minecraft.util.EnumParticleTypes +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration.Companion.seconds + +class SpawnTimers { + + private val config get() = SkyHanniMod.feature.combat.mobs + + private val patternGroup = RepoPattern.group("combat.mobs.spawntime.arachne") + private val arachneFragmentPattern by patternGroup.pattern( + "fragment", + "^☄ [a-z0-9_]{2,22} placed an arachne's calling! something is awakening! \\(4/4\\)\$" + ) + private val arachneCrystalPattern by patternGroup.pattern( + "crystal", + "^☄ [a-z0-9_]{2,22} placed an arachne crystal! something is awakening!$" + ) + + private val arachneAltarLocation = LorenzVec(-283f, 51f, -179f) + private var arachneSpawnTime = SimpleTimeMark.farPast() + private var saveNextTickParticles = false + private var particleCounter = 0 + private var tickTime: Long = 0 + private var searchTime: Long = 0 + + @SubscribeEvent + fun onWorldChange(event: LorenzWorldChangeEvent) { + searchTime = 0 + tickTime = 0 + particleCounter = 0 + saveNextTickParticles = false + arachneSpawnTime = SimpleTimeMark.farPast() + } + + @SubscribeEvent + fun onRenderWorld(event: LorenzRenderWorldEvent) { + if (!isEnabled()) return + if (arachneSpawnTime.isInPast()) return + val countDown = arachneSpawnTime.timeUntil() + + val format = countDown.format(showMilliSeconds = true) + event.drawDynamicText(arachneAltarLocation, "§b$format", 1.5) + } + + @SubscribeEvent + fun onChatReceived(event: LorenzChatEvent) { + if (!isEnabled()) return + val message = event.message.removeColor().lowercase() + + if (arachneFragmentPattern.matches(message) || arachneCrystalPattern.matches(message)) { + if (arachneCrystalPattern.matches(message)) { + saveNextTickParticles = true + searchTime = System.currentTimeMillis() + particleCounter = 0 + tickTime = 0L + } else arachneSpawnTime = SimpleTimeMark.now() + 19.seconds + } + } + + // All this to detect "quickspawn" vs regular arachne spawn + @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) + fun onChatPacket(event: PacketEvent.ReceiveEvent) { + if (!saveNextTickParticles) return + if (System.currentTimeMillis() <= searchTime + 3000) return + + if (particleCounter == 0 && tickTime == 0L) tickTime = System.currentTimeMillis() + + if (System.currentTimeMillis() > tickTime + 60) { + arachneSpawnTime = if (particleCounter <= 20) { + SimpleTimeMark.now() + 21.seconds + } else { + SimpleTimeMark.now() + 37.seconds + } + saveNextTickParticles = false + return + } + + val packet = event.packet + if (packet is S2APacketParticles) { + val location = packet.toLorenzVec().round(2) + if (arachneAltarLocation.distance(location) > 30) return + if (packet.particleType == EnumParticleTypes.REDSTONE && packet.particleSpeed == 1.0f) { + particleCounter += 1 + } + } + } + + fun isEnabled() = + IslandType.SPIDER_DEN.isInIsland() && LorenzUtils.skyBlockArea == "Arachne's Sanctuary" && config.showArachneSpawnTimer +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt index 4521aa2b9..d636f24e3 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt @@ -1,186 +1,186 @@ -package at.hannibal2.skyhanni.features.dungeon - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator -import at.hannibal2.skyhanni.events.GuiContainerEvent -import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent -import at.hannibal2.skyhanni.events.LorenzToolTipEvent -import at.hannibal2.skyhanni.events.RenderItemTipEvent -import at.hannibal2.skyhanni.utils.InventoryUtils -import at.hannibal2.skyhanni.utils.InventoryUtils.getInventoryName -import at.hannibal2.skyhanni.utils.ItemUtils.getLore -import at.hannibal2.skyhanni.utils.ItemUtils.name -import at.hannibal2.skyhanni.utils.LorenzColor -import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary -import at.hannibal2.skyhanni.utils.RenderUtils.highlight -import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher -import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import net.minecraft.client.gui.inventory.GuiChest -import net.minecraft.inventory.ContainerChest -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class DungeonFinderFeatures { - - private val config get() = SkyHanniMod.feature.dungeon.partyFinder - - private val pricePattern = "([0-9]{2,3}K|[0-9]{1,3}M|[0-9]+\\.[0-9]M|[0-9] ?mil)".toRegex(RegexOption.IGNORE_CASE) - private val carryPattern = "(carry|cary|carries|caries|comp|to cata [0-9]{2})".toRegex(RegexOption.IGNORE_CASE) - private val nonPugPattern = "(perm|vc|discord)".toRegex(RegexOption.IGNORE_CASE) - private val memberPattern = "^ §.*?§.: §.([A-Z]+)§. \\(§.([0-9]+)§.\\)".toRegex(RegexOption.IGNORE_CASE) - private val ineligiblePattern = - "^§c(Requires .*$|You don't meet the requirement!|Complete previous floor first!$)".toRegex() - private val classLevelPattern = " §.(?.*)§f: §e(?.*)§b \\(§e(?.*)§b\\)".toPattern() - private val notePattern = "^(§7§7Note: |§f[^§])".toRegex() - - private var selectedClass = "" - - @SubscribeEvent - fun onRenderItemTip(event: RenderItemTipEvent) { - if (!LorenzUtils.inSkyBlock || LorenzUtils.skyBlockArea != "Dungeon Hub") return - if (!config.floorAsStackSize) return - - val itemName = event.stack.name?.removeColor() ?: "" - val invName = InventoryUtils.openInventoryName() - - if (invName == "Select Floor") { - if (itemName == "Any") { - event.stackTip = "A" - } else if (itemName == "Entrance") { - event.stackTip = "E" - } else if (itemName.startsWith("Floor ")) { - event.stackTip = itemName.split(' ').last().romanToDecimalIfNecessary().toString() - } - } else if (itemName.startsWith("The Catacombs - ") || itemName.startsWith("MM Catacombs -")) { - val floor = itemName.split(" - ").last().removeColor() - val floorNum = floor.split(' ').last().romanToDecimalIfNecessary().toString() - val isMasterMode = itemName.contains("MM ") - - event.stackTip = if (floor.contains("Entrance")) { - "E" - } else if (isMasterMode) { - "M${floorNum}" - } else { - "F${floorNum}" - } - } else if (itemName.endsWith("'s Party")) { - val floor = event.stack.getLore().find { it.startsWith("§7Floor: ") } ?: return - val dungeon = event.stack.getLore().find { it.startsWith("§7Dungeon: ") } ?: return - val floorNum = floor.split(' ').last().romanToDecimalIfNecessary().toString() - val isMasterMode = dungeon.contains("Master Mode") - - event.stackTip = if (floor.contains("Entrance")) { - "E" - } else if (isMasterMode) { - "M${floorNum}" - } else { - "F${floorNum}" - } - } - } - - @SubscribeEvent - fun onInventoryOpen(event: InventoryFullyOpenedEvent) { - if (!LorenzUtils.inSkyBlock || LorenzUtils.skyBlockArea != "Dungeon Hub") return - if (event.inventoryName != "Catacombs Gate") return - - val lore = event.inventoryItems[45]?.getLore() ?: return - - if (lore[0] == "§7View and select a dungeon class.") { - selectedClass = lore[2].split(" ").last().removeColor() - } - } - - @SubscribeEvent - fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { - if (!LorenzUtils.inSkyBlock) return - if (event.gui !is GuiChest) return - - val chest = event.gui.inventorySlots as ContainerChest - val inventoryName = chest.getInventoryName() - if (inventoryName != "Party Finder") return - - for (slot in chest.inventorySlots) { - if (slot == null) continue - if (slot.slotNumber != slot.slotIndex) continue - if (slot.stack == null) continue - - val itemName = slot.stack.name ?: continue - if (!itemName.endsWith(" Party")) continue - - if (config.markIneligibleGroups && slot.stack.getLore().any { ineligiblePattern.matches(it) }) { - slot highlight LorenzColor.DARK_RED - continue - } - - if (config.markPaidCarries) { - val note = slot.stack.getLore().filter { notePattern.containsMatchIn(it) }.joinToString(" ") - - if (pricePattern.containsMatchIn(note) && carryPattern.containsMatchIn(note)) { - slot highlight LorenzColor.RED - continue - } - } - - if (config.markNonPugs) { - val note = slot.stack.getLore().filter { notePattern.containsMatchIn(it) }.joinToString(" ") - - if (nonPugPattern.containsMatchIn(note)) { - slot highlight LorenzColor.LIGHT_PURPLE - continue - } - } - - val members = slot.stack.getLore().filter { memberPattern.matches(it) } - val memberLevels = members.map { memberPattern.matchEntire(it)?.groupValues?.get(2)?.toInt() ?: 0 } - val memberClasses = members.map { memberPattern.matchEntire(it)?.groupValues?.get(1) ?: "" } - - if (memberLevels.any { it <= config.markBelowClassLevel }) { - slot highlight LorenzColor.YELLOW - continue - } - - if (config.markMissingClass && memberClasses.none { it == selectedClass }) { - slot highlight LorenzColor.GREEN - } - } - } - - @SubscribeEvent - fun onItemTooltip(event: LorenzToolTipEvent) { - if (!LorenzUtils.inSkyBlock) return - if (!config.coloredClassLevel) return - - val chestName = InventoryUtils.openInventoryName() - if (chestName != "Party Finder") return - - val stack = event.itemStack - - for ((index, line) in stack.getLore().withIndex()) { - classLevelPattern.matchMatcher(line) { - val playerName = group("playerName") - val className = group("className") - val level = group("level").toInt() - val color = getColor(level) - event.toolTip[index + 1] = " §b$playerName§f: §e$className $color$level" - } - } - } - - @SubscribeEvent - fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { - event.move(2, "dungeon.partyFinderColoredClassLevel", "dungeon.partyFinder.coloredClassLevel") - } -} - -fun getColor(level: Int): String { - if (level >= 50) return "§c§l" - if (level >= 45) return "§c" - if (level >= 40) return "§d" - if (level >= 35) return "§6" - if (level >= 30) return "§5" - if (level >= 25) return "§9" - if (level >= 20) return "§a" - if (level >= 10) return "§f" - return "§7" -} +package at.hannibal2.skyhanni.features.dungeon + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator +import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent +import at.hannibal2.skyhanni.events.LorenzToolTipEvent +import at.hannibal2.skyhanni.events.RenderItemTipEvent +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.InventoryUtils.getInventoryName +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary +import at.hannibal2.skyhanni.utils.RenderUtils.highlight +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class DungeonFinderFeatures { + + private val config get() = SkyHanniMod.feature.dungeon.partyFinder + + private val pricePattern = "([0-9]{2,3}K|[0-9]{1,3}M|[0-9]+\\.[0-9]M|[0-9] ?mil)".toRegex(RegexOption.IGNORE_CASE) + private val carryPattern = "(carry|cary|carries|caries|comp|to cata [0-9]{2})".toRegex(RegexOption.IGNORE_CASE) + private val nonPugPattern = "(perm|vc|discord)".toRegex(RegexOption.IGNORE_CASE) + private val memberPattern = "^ §.*?§.: §.([A-Z]+)§. \\(§.([0-9]+)§.\\)".toRegex(RegexOption.IGNORE_CASE) + private val ineligiblePattern = + "^§c(Requires .*$|You don't meet the requirement!|Complete previous floor first!$)".toRegex() + private val classLevelPattern = " §.(?.*)§f: §e(?.*)§b \\(§e(?.*)§b\\)".toPattern() + private val notePattern = "^(§7§7Note: |§f[^§])".toRegex() + + private var selectedClass = "" + + @SubscribeEvent + fun onRenderItemTip(event: RenderItemTipEvent) { + if (!LorenzUtils.inSkyBlock || LorenzUtils.skyBlockArea != "Dungeon Hub") return + if (!config.floorAsStackSize) return + + val itemName = event.stack.name?.removeColor() ?: "" + val invName = InventoryUtils.openInventoryName() + + if (invName == "Select Floor") { + if (itemName == "Any") { + event.stackTip = "A" + } else if (itemName == "Entrance") { + event.stackTip = "E" + } else if (itemName.startsWith("Floor ")) { + event.stackTip = itemName.split(' ').last().romanToDecimalIfNecessary().toString() + } + } else if (itemName.startsWith("The Catacombs - ") || itemName.startsWith("MM Catacombs -")) { + val floor = itemName.split(" - ").last().removeColor() + val floorNum = floor.split(' ').last().romanToDecimalIfNecessary().toString() + val isMasterMode = itemName.contains("MM ") + + event.stackTip = if (floor.contains("Entrance")) { + "E" + } else if (isMasterMode) { + "M${floorNum}" + } else { + "F${floorNum}" + } + } else if (itemName.endsWith("'s Party")) { + val floor = event.stack.getLore().find { it.startsWith("§7Floor: ") } ?: return + val dungeon = event.stack.getLore().find { it.startsWith("§7Dungeon: ") } ?: return + val floorNum = floor.split(' ').last().romanToDecimalIfNecessary().toString() + val isMasterMode = dungeon.contains("Master Mode") + + event.stackTip = if (floor.contains("Entrance")) { + "E" + } else if (isMasterMode) { + "M${floorNum}" + } else { + "F${floorNum}" + } + } + } + + @SubscribeEvent + fun onInventoryOpen(event: InventoryFullyOpenedEvent) { + if (!LorenzUtils.inSkyBlock || LorenzUtils.skyBlockArea != "Dungeon Hub") return + if (event.inventoryName != "Catacombs Gate") return + + val lore = event.inventoryItems[45]?.getLore() ?: return + + if (lore[0] == "§7View and select a dungeon class.") { + selectedClass = lore[2].split(" ").last().removeColor() + } + } + + @SubscribeEvent + fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (!LorenzUtils.inSkyBlock) return + if (event.gui !is GuiChest) return + + val chest = event.gui.inventorySlots as ContainerChest + val inventoryName = chest.getInventoryName() + if (inventoryName != "Party Finder") return + + for (slot in chest.inventorySlots) { + if (slot == null) continue + if (slot.slotNumber != slot.slotIndex) continue + if (slot.stack == null) continue + + val itemName = slot.stack.name ?: continue + if (!itemName.endsWith(" Party")) continue + + if (config.markIneligibleGroups && slot.stack.getLore().any { ineligiblePattern.matches(it) }) { + slot highlight LorenzColor.DARK_RED + continue + } + + if (config.markPaidCarries) { + val note = slot.stack.getLore().filter { notePattern.containsMatchIn(it) }.joinToString(" ") + + if (pricePattern.containsMatchIn(note) && carryPattern.containsMatchIn(note)) { + slot highlight LorenzColor.RED + continue + } + } + + if (config.markNonPugs) { + val note = slot.stack.getLore().filter { notePattern.containsMatchIn(it) }.joinToString(" ") + + if (nonPugPattern.containsMatchIn(note)) { + slot highlight LorenzColor.LIGHT_PURPLE + continue + } + } + + val members = slot.stack.getLore().filter { memberPattern.matches(it) } + val memberLevels = members.map { memberPattern.matchEntire(it)?.groupValues?.get(2)?.toInt() ?: 0 } + val memberClasses = members.map { memberPattern.matchEntire(it)?.groupValues?.get(1) ?: "" } + + if (memberLevels.any { it <= config.markBelowClassLevel }) { + slot highlight LorenzColor.YELLOW + continue + } + + if (config.markMissingClass && memberClasses.none { it == selectedClass }) { + slot highlight LorenzColor.GREEN + } + } + } + + @SubscribeEvent + fun onItemTooltip(event: LorenzToolTipEvent) { + if (!LorenzUtils.inSkyBlock) return + if (!config.coloredClassLevel) return + + val chestName = InventoryUtils.openInventoryName() + if (chestName != "Party Finder") return + + val stack = event.itemStack + + for ((index, line) in stack.getLore().withIndex()) { + classLevelPattern.matchMatcher(line) { + val playerName = group("playerName") + val className = group("className") + val level = group("level").toInt() + val color = getColor(level) + event.toolTip[index + 1] = " §b$playerName§f: §e$className $color$level" + } + } + } + + @SubscribeEvent + fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { + event.move(2, "dungeon.partyFinderColoredClassLevel", "dungeon.partyFinder.coloredClassLevel") + } +} + +fun getColor(level: Int): String { + if (level >= 50) return "§c§l" + if (level >= 45) return "§c" + if (level >= 40) return "§d" + if (level >= 35) return "§6" + if (level >= 30) return "§5" + if (level >= 25) return "§9" + if (level >= 20) return "§a" + if (level >= 10) return "§f" + return "§7" +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonTeammateOutlines.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonTeammateOutlines.kt index 103bf35df..33eabaff1 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonTeammateOutlines.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonTeammateOutlines.kt @@ -1,39 +1,39 @@ -package at.hannibal2.skyhanni.features.dungeon - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent -import at.hannibal2.skyhanni.utils.LorenzUtils -import net.minecraft.client.Minecraft -import net.minecraft.client.entity.EntityOtherPlayerMP -import net.minecraft.client.gui.FontRenderer -import net.minecraft.entity.Entity -import net.minecraft.scoreboard.ScorePlayerTeam -import net.minecraft.scoreboard.Team -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class DungeonTeammateOutlines { - - private val config get() = SkyHanniMod.feature.dungeon - - @SubscribeEvent - fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) { - if (isEnabled() && event.type === RenderEntityOutlineEvent.Type.XRAY) { - event.queueEntitiesToOutline { entity -> getEntityOutlineColor(entity) } - } - } - - private fun isEnabled() = LorenzUtils.inSkyBlock && LorenzUtils.inDungeons && config.highlightTeammates - - private fun getEntityOutlineColor(entity: Entity): Int? { - if (entity !is EntityOtherPlayerMP || entity.team == null) return null - - // Must be visible on the scoreboard - val team = entity.team as ScorePlayerTeam - if (team.nameTagVisibility == Team.EnumVisible.NEVER) return null - - val colorFormat = FontRenderer.getFormatFromString(team.colorPrefix) - return if (colorFormat.length >= 2) - Minecraft.getMinecraft().fontRendererObj.getColorCode(colorFormat[1]) - else null - } -} +package at.hannibal2.skyhanni.features.dungeon + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.client.Minecraft +import net.minecraft.client.entity.EntityOtherPlayerMP +import net.minecraft.client.gui.FontRenderer +import net.minecraft.entity.Entity +import net.minecraft.scoreboard.ScorePlayerTeam +import net.minecraft.scoreboard.Team +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class DungeonTeammateOutlines { + + private val config get() = SkyHanniMod.feature.dungeon + + @SubscribeEvent + fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) { + if (isEnabled() && event.type === RenderEntityOutlineEvent.Type.XRAY) { + event.queueEntitiesToOutline { entity -> getEntityOutlineColor(entity) } + } + } + + private fun isEnabled() = LorenzUtils.inSkyBlock && LorenzUtils.inDungeons && config.highlightTeammates + + private fun getEntityOutlineColor(entity: Entity): Int? { + if (entity !is EntityOtherPlayerMP || entity.team == null) return null + + // Must be visible on the scoreboard + val team = entity.team as ScorePlayerTeam + if (team.nameTagVisibility == Team.EnumVisible.NEVER) return null + + val colorFormat = FontRenderer.getFormatFromString(team.colorPrefix) + return if (colorFormat.length >= 2) + Minecraft.getMinecraft().fontRendererObj.getColorCode(colorFormat[1]) + else null + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/QuickCraftFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/QuickCraftFeatures.kt index 0126ee2f2..bf036b9c0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/QuickCraftFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/QuickCraftFeatures.kt @@ -1,98 +1,98 @@ -package at.hannibal2.skyhanni.features.inventory - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.data.ItemRenderBackground.Companion.background -import at.hannibal2.skyhanni.events.GuiContainerEvent -import at.hannibal2.skyhanni.events.LorenzToolTipEvent -import at.hannibal2.skyhanni.events.RepositoryReloadEvent -import at.hannibal2.skyhanni.utils.InventoryUtils -import at.hannibal2.skyhanni.utils.ItemUtils.name -import at.hannibal2.skyhanni.utils.KeyboardManager -import at.hannibal2.skyhanni.utils.LorenzColor -import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import net.minecraft.client.gui.inventory.GuiChest -import net.minecraft.inventory.ContainerChest -import net.minecraft.item.ItemStack -import net.minecraftforge.fml.common.eventhandler.EventPriority -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class QuickCraftFeatures { - - private val config get() = SkyHanniMod.feature.inventory - private val quickCraftSlots = listOf(16, 25, 34) - private var quickCraftableItems = emptyList() - - enum class InventoryType(val inventoryName: String) { - CRAFT_ITEM("Craft Item"), - MORE_QUICK_CRAFT_OPTIONS("Quick Crafting"), - ; - } - - private fun InventoryType.ignoreSlot(slotNumber: Int?): Boolean = when (this) { - InventoryType.CRAFT_ITEM -> slotNumber !in quickCraftSlots - InventoryType.MORE_QUICK_CRAFT_OPTIONS -> slotNumber !in 10..44 - } - - @SubscribeEvent - fun onRepoReload(event: RepositoryReloadEvent) { - quickCraftableItems = event.getConstant>("QuickCraftableItems") - } - - @SubscribeEvent - fun onToolTip(event: LorenzToolTipEvent) { - val inventoryType = getInventoryType() ?: return - if (inventoryType.ignoreSlot(event.slot.slotNumber)) return - - if (needsQuickCraftConfirmation(event.itemStack)) { - event.toolTip.replaceAll { - it.replace( - "Click to craft!", - "§c${KeyboardManager.getModifierKeyName()} + Click to craft!" - ) - } - } - } - - @SubscribeEvent - fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { - val inventoryType = getInventoryType() ?: return - if (KeyboardManager.isModifierKeyDown()) return - if (event.gui !is GuiChest) return - val chest = event.gui.inventorySlots as ContainerChest - - for (slot in chest.inventorySlots) { - if (slot == null) continue - if (inventoryType.ignoreSlot(slot.slotNumber)) continue - val stack = slot.stack ?: continue - val name = stack.name ?: continue - if (name == "§cQuick Crafting Slot") continue - if (needsQuickCraftConfirmation(stack)) { - val color = LorenzColor.DARK_GRAY.addOpacity(180) - stack.background = color.rgb - } - } - } - - @SubscribeEvent(priority = EventPriority.HIGH) - fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { - val inventoryType = getInventoryType() ?: return - if (inventoryType.ignoreSlot(event.slot?.slotNumber)) return - - val clickedItem = event.slot?.stack ?: return - if (!KeyboardManager.isModifierKeyDown() && needsQuickCraftConfirmation(clickedItem)) { - event.isCanceled = true - } - } - - private fun needsQuickCraftConfirmation(item: ItemStack): Boolean { - return !quickCraftableItems.contains(item.displayName.removeColor()) - } - - private fun getInventoryType(): InventoryType? { - if (!LorenzUtils.inSkyBlock || !config.quickCraftingConfirmation) return null - - val inventoryName = InventoryUtils.openInventoryName() - return InventoryType.entries.firstOrNull { it.inventoryName == inventoryName } - } -} +package at.hannibal2.skyhanni.features.inventory + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.ItemRenderBackground.Companion.background +import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.LorenzToolTipEvent +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.KeyboardManager +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class QuickCraftFeatures { + + private val config get() = SkyHanniMod.feature.inventory + private val quickCraftSlots = listOf(16, 25, 34) + private var quickCraftableItems = emptyList() + + enum class InventoryType(val inventoryName: String) { + CRAFT_ITEM("Craft Item"), + MORE_QUICK_CRAFT_OPTIONS("Quick Crafting"), + ; + } + + private fun InventoryType.ignoreSlot(slotNumber: Int?): Boolean = when (this) { + InventoryType.CRAFT_ITEM -> slotNumber !in quickCraftSlots + InventoryType.MORE_QUICK_CRAFT_OPTIONS -> slotNumber !in 10..44 + } + + @SubscribeEvent + fun onRepoReload(event: RepositoryReloadEvent) { + quickCraftableItems = event.getConstant>("QuickCraftableItems") + } + + @SubscribeEvent + fun onToolTip(event: LorenzToolTipEvent) { + val inventoryType = getInventoryType() ?: return + if (inventoryType.ignoreSlot(event.slot.slotNumber)) return + + if (needsQuickCraftConfirmation(event.itemStack)) { + event.toolTip.replaceAll { + it.replace( + "Click to craft!", + "§c${KeyboardManager.getModifierKeyName()} + Click to craft!" + ) + } + } + } + + @SubscribeEvent + fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + val inventoryType = getInventoryType() ?: return + if (KeyboardManager.isModifierKeyDown()) return + if (event.gui !is GuiChest) return + val chest = event.gui.inventorySlots as ContainerChest + + for (slot in chest.inventorySlots) { + if (slot == null) continue + if (inventoryType.ignoreSlot(slot.slotNumber)) continue + val stack = slot.stack ?: continue + val name = stack.name ?: continue + if (name == "§cQuick Crafting Slot") continue + if (needsQuickCraftConfirmation(stack)) { + val color = LorenzColor.DARK_GRAY.addOpacity(180) + stack.background = color.rgb + } + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { + val inventoryType = getInventoryType() ?: return + if (inventoryType.ignoreSlot(event.slot?.slotNumber)) return + + val clickedItem = event.slot?.stack ?: return + if (!KeyboardManager.isModifierKeyDown() && needsQuickCraftConfirmation(clickedItem)) { + event.isCanceled = true + } + } + + private fun needsQuickCraftConfirmation(item: ItemStack): Boolean { + return !quickCraftableItems.contains(item.displayName.removeColor()) + } + + private fun getInventoryType(): InventoryType? { + if (!LorenzUtils.inSkyBlock || !config.quickCraftingConfirmation) return null + + val inventoryName = InventoryUtils.openInventoryName() + return InventoryType.entries.firstOrNull { it.inventoryName == inventoryName } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/PartyMemberOutlines.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/PartyMemberOutlines.kt index e90265ef3..9780c203b 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/PartyMemberOutlines.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/PartyMemberOutlines.kt @@ -1,32 +1,32 @@ -package at.hannibal2.skyhanni.features.misc - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.config.enums.OutsideSbFeature -import at.hannibal2.skyhanni.data.PartyAPI -import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent -import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.SpecialColour -import net.minecraft.client.entity.EntityOtherPlayerMP -import net.minecraft.entity.Entity -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -class PartyMemberOutlines { - - private val config get() = SkyHanniMod.feature.misc.highlightPartyMembers - - @SubscribeEvent - fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) { - if (isEnabled() && event.type === RenderEntityOutlineEvent.Type.NO_XRAY) { - event.queueEntitiesToOutline { entity -> getEntityOutlineColor(entity) } - } - } - - fun isEnabled() = config.enabled && - (LorenzUtils.inSkyBlock || OutsideSbFeature.HIGHLIGHT_PARTY_MEMBERS.isSelected()) && !LorenzUtils.inDungeons - - private fun getEntityOutlineColor(entity: Entity): Int? { - if (entity !is EntityOtherPlayerMP || !PartyAPI.partyMembers.contains(entity.name)) return null - - return SpecialColour.specialToChromaRGB(config.outlineColor) - } -} +package at.hannibal2.skyhanni.features.misc + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.enums.OutsideSbFeature +import at.hannibal2.skyhanni.data.PartyAPI +import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.SpecialColour +import net.minecraft.client.entity.EntityOtherPlayerMP +import net.minecraft.entity.Entity +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class PartyMemberOutlines { + + private val config get() = SkyHanniMod.feature.misc.highlightPartyMembers + + @SubscribeEvent + fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) { + if (isEnabled() && event.type === RenderEntityOutlineEvent.Type.NO_XRAY) { + event.queueEntitiesToOutline { entity -> getEntityOutlineColor(entity) } + } + } + + fun isEnabled() = config.enabled && + (LorenzUtils.inSkyBlock || OutsideSbFeature.HIGHLIGHT_PARTY_MEMBERS.isSelected()) && !LorenzUtils.inDungeons + + private fun getEntityOutlineColor(entity: Entity): Int? { + if (entity !is EntityOtherPlayerMP || !PartyAPI.partyMembers.contains(entity.name)) return null + + return SpecialColour.specialToChromaRGB(config.outlineColor) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/items/GlowingDroppedItems.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/items/GlowingDroppedItems.kt index 3e5bf7989..15d33fee7 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/items/GlowingDroppedItems.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/items/GlowingDroppedItems.kt @@ -1,85 +1,85 @@ -package at.hannibal2.skyhanni.features.misc.items - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.data.IslandType -import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent -import at.hannibal2.skyhanni.features.garden.pests.SprayType -import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull -import at.hannibal2.skyhanni.utils.ItemUtils.getItemRarityOrNull -import at.hannibal2.skyhanni.utils.ItemUtils.name -import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.RecalculatingValue -import net.minecraft.entity.Entity -import net.minecraft.entity.item.EntityArmorStand -import net.minecraft.entity.item.EntityItem -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import kotlin.time.Duration.Companion.seconds - -class GlowingDroppedItems { - - private val config get() = SkyHanniMod.feature.misc.glowingDroppedItems - - /** - * List of SkyBlock locations where we might see items in showcases - */ - private val showcaseItemLocations = setOf( - "The End", - "Jerry's Workshop", - "Dark Auction", - "Photon Pathway", - "Barrier Street", - "Village Plaza", - "Déjà Vu Alley" - ) - - private val showcaseItemIslands = setOf( - IslandType.HUB, - IslandType.PRIVATE_ISLAND, - IslandType.PRIVATE_ISLAND_GUEST, - IslandType.CRIMSON_ISLE - ) - - @SubscribeEvent - fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) { - if (isEnabled() && event.type === RenderEntityOutlineEvent.Type.XRAY) { - event.queueEntitiesToOutline { getEntityOutlineColor(it) } - } - } - - private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled - - private fun getEntityOutlineColor(entity: Entity): Int? { - val item = entity as? EntityItem ?: return null - if (shouldHideShowcaseItem(entity)) return null - - val entityItem = item.entityItem - if (!config.highlightFishingBait && entityItem.name?.endsWith(" Bait") == true) { - return null - } - - val internalName = entityItem.getInternalNameOrNull() ?: return null - val isSprayItem = LorenzUtils.enumValueOfOrNull(internalName.asString()) != null - if (isSprayItem) return null - val rarity = entityItem.getItemRarityOrNull() - return rarity?.color?.toColor()?.rgb - } - - private val isShowcaseArea = RecalculatingValue(1.seconds) { - showcaseItemIslands.contains(LorenzUtils.skyBlockIsland) || showcaseItemLocations.contains(LorenzUtils.skyBlockArea) - } - - private fun shouldHideShowcaseItem(entity: EntityItem): Boolean { - if (!isShowcaseArea.getValue() || config.highlightShowcase) return false - - for (entityArmorStand in entity.worldObj.getEntitiesWithinAABB( - EntityArmorStand::class.java, - entity.entityBoundingBox - )) { - if (entityArmorStand.isInvisible) { - return true - } - } - - return false - } -} +package at.hannibal2.skyhanni.features.misc.items + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.RenderEntityOutlineEvent +import at.hannibal2.skyhanni.features.garden.pests.SprayType +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull +import at.hannibal2.skyhanni.utils.ItemUtils.getItemRarityOrNull +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RecalculatingValue +import net.minecraft.entity.Entity +import net.minecraft.entity.item.EntityArmorStand +import net.minecraft.entity.item.EntityItem +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration.Companion.seconds + +class GlowingDroppedItems { + + private val config get() = SkyHanniMod.feature.misc.glowingDroppedItems + + /** + * List of SkyBlock locations where we might see items in showcases + */ + private val showcaseItemLocations = setOf( + "The End", + "Jerry's Workshop", + "Dark Auction", + "Photon Pathway", + "Barrier Street", + "Village Plaza", + "Déjà Vu Alley" + ) + + private val showcaseItemIslands = setOf( + IslandType.HUB, + IslandType.PRIVATE_ISLAND, + IslandType.PRIVATE_ISLAND_GUEST, + IslandType.CRIMSON_ISLE + ) + + @SubscribeEvent + fun onRenderEntityOutlines(event: RenderEntityOutlineEvent) { + if (isEnabled() && event.type === RenderEntityOutlineEvent.Type.XRAY) { + event.queueEntitiesToOutline { getEntityOutlineColor(it) } + } + } + + private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled + + private fun getEntityOutlineColor(entity: Entity): Int? { + val item = entity as? EntityItem ?: return null + if (shouldHideShowcaseItem(entity)) return null + + val entityItem = item.entityItem + if (!config.highlightFishingBait && entityItem.name?.endsWith(" Bait") == true) { + return null + } + + val internalName = entityItem.getInternalNameOrNull() ?: return null + val isSprayItem = LorenzUtils.enumValueOfOrNull(internalName.asString()) != null + if (isSprayItem) return null + val rarity = entityItem.getItemRarityOrNull() + return rarity?.color?.toColor()?.rgb + } + + private val isShowcaseArea = RecalculatingValue(1.seconds) { + showcaseItemIslands.contains(LorenzUtils.skyBlockIsland) || showcaseItemLocations.contains(LorenzUtils.skyBlockArea) + } + + private fun shouldHideShowcaseItem(entity: EntityItem): Boolean { + if (!isShowcaseArea.getValue() || config.highlightShowcase) return false + + for (entityArmorStand in entity.worldObj.getEntitiesWithinAABB( + EntityArmorStand::class.java, + entity.entityBoundingBox + )) { + if (entityArmorStand.isInvisible) { + return true + } + } + + return false + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RenderGlobalHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RenderGlobalHook.kt index 92e7a95ab..4ac45b644 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RenderGlobalHook.kt +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RenderGlobalHook.kt @@ -1,21 +1,21 @@ -package at.hannibal2.skyhanni.mixins.hooks - -import at.hannibal2.skyhanni.utils.EntityOutlineRenderer -import at.hannibal2.skyhanni.utils.RenderUtils -import net.minecraft.client.Minecraft -import net.minecraft.client.renderer.culling.ICamera -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable - -class RenderGlobalHook { - - fun renderEntitiesOutlines(camera: ICamera?, partialTicks: Float): Boolean { - val vec = RenderUtils.exactLocation(Minecraft.getMinecraft().renderViewEntity, partialTicks) - return EntityOutlineRenderer.renderEntityOutlines(camera!!, partialTicks, vec) - } - - fun shouldRenderEntityOutlines(cir: CallbackInfoReturnable) { - if (EntityOutlineRenderer.shouldRenderEntityOutlines()) { - cir.returnValue = true - } - } -} +package at.hannibal2.skyhanni.mixins.hooks + +import at.hannibal2.skyhanni.utils.EntityOutlineRenderer +import at.hannibal2.skyhanni.utils.RenderUtils +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.culling.ICamera +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable + +class RenderGlobalHook { + + fun renderEntitiesOutlines(camera: ICamera?, partialTicks: Float): Boolean { + val vec = RenderUtils.exactLocation(Minecraft.getMinecraft().renderViewEntity, partialTicks) + return EntityOutlineRenderer.renderEntityOutlines(camera!!, partialTicks, vec) + } + + fun shouldRenderEntityOutlines(cir: CallbackInfoReturnable) { + if (EntityOutlineRenderer.shouldRenderEntityOutlines()) { + cir.returnValue = true + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RendererLivingEntityHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RendererLivingEntityHook.kt index 8fb195a3b..8b724037a 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RendererLivingEntityHook.kt +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/RendererLivingEntityHook.kt @@ -1,21 +1,21 @@ -package at.hannibal2.skyhanni.mixins.hooks - -import at.hannibal2.skyhanni.utils.EntityOutlineRenderer -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.entity.EntityLivingBase - -class RendererLivingEntityHook { - - fun setOutlineColor(red: Float, green: Float, blue: Float, alpha: Float, entity: EntityLivingBase) { - val color = EntityOutlineRenderer.getCustomOutlineColor(entity) - - if (color != null) { - val colorRed = (color shr 16 and 255).toFloat() / 255.0f - val colorGreen = (color shr 8 and 255).toFloat() / 255.0f - val colorBlue = (color and 255).toFloat() / 255.0f - GlStateManager.color(colorRed, colorGreen, colorBlue, alpha) - } else { - GlStateManager.color(red, green, blue, alpha) - } - } -} +package at.hannibal2.skyhanni.mixins.hooks + +import at.hannibal2.skyhanni.utils.EntityOutlineRenderer +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.entity.EntityLivingBase + +class RendererLivingEntityHook { + + fun setOutlineColor(red: Float, green: Float, blue: Float, alpha: Float, entity: EntityLivingBase) { + val color = EntityOutlineRenderer.getCustomOutlineColor(entity) + + if (color != null) { + val colorRed = (color shr 16 and 255).toFloat() / 255.0f + val colorGreen = (color shr 8 and 255).toFloat() / 255.0f + val colorBlue = (color and 255).toFloat() / 255.0f + GlStateManager.color(colorRed, colorGreen, colorBlue, alpha) + } else { + GlStateManager.color(red, green, blue, alpha) + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/CustomRenderGlobal.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/CustomRenderGlobal.java index bd440d3ea..50d8ec9be 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/CustomRenderGlobal.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/CustomRenderGlobal.java @@ -1,17 +1,17 @@ -package at.hannibal2.skyhanni.mixins.transformers; - -import net.minecraft.client.renderer.RenderGlobal; -import net.minecraft.client.shader.Framebuffer; -import net.minecraft.client.shader.ShaderGroup; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(RenderGlobal.class) -public interface CustomRenderGlobal { - @Accessor("entityOutlineFramebuffer") - Framebuffer getEntityOutlineFramebuffer_skyhanni(); - - @Accessor("entityOutlineShader") - ShaderGroup getEntityOutlineShader_skyhanni(); - +package at.hannibal2.skyhanni.mixins.transformers; + +import net.minecraft.client.renderer.RenderGlobal; +import net.minecraft.client.shader.Framebuffer; +import net.minecraft.client.shader.ShaderGroup; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(RenderGlobal.class) +public interface CustomRenderGlobal { + @Accessor("entityOutlineFramebuffer") + Framebuffer getEntityOutlineFramebuffer_skyhanni(); + + @Accessor("entityOutlineShader") + ShaderGroup getEntityOutlineShader_skyhanni(); + } \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRenderGlobal.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRenderGlobal.java index a56c42e2e..14c17f995 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRenderGlobal.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRenderGlobal.java @@ -1,40 +1,40 @@ -package at.hannibal2.skyhanni.mixins.transformers; - -import at.hannibal2.skyhanni.mixins.hooks.RenderGlobalHook; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.RenderGlobal; -import net.minecraft.client.renderer.culling.ICamera; -import net.minecraft.entity.Entity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(RenderGlobal.class) -public abstract class MixinRenderGlobal { - - @Shadow - abstract boolean isRenderEntityOutlines(); - - @Unique - private final RenderGlobalHook skyHanni$hook = new RenderGlobalHook(); - - @Redirect(method = "renderEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;isRenderEntityOutlines()Z")) - public boolean renderEntitiesOutlines(RenderGlobal self, Entity renderViewEntity, ICamera camera, float partialTicks) { - return skyHanni$hook.renderEntitiesOutlines(camera, partialTicks) && this.isRenderEntityOutlines(); - } - - @Inject(method = "isRenderEntityOutlines", at = @At(value = "HEAD"), cancellable = true) - public void isRenderEntityOutlinesWrapper(CallbackInfoReturnable cir) { - skyHanni$hook.shouldRenderEntityOutlines(cir); - } - - @Inject(method = "renderEntityOutlineFramebuffer", at = @At(value = "RETURN")) - public void afterFramebufferDraw(CallbackInfo callbackInfo) { - GlStateManager.enableDepth(); - } -} +package at.hannibal2.skyhanni.mixins.transformers; + +import at.hannibal2.skyhanni.mixins.hooks.RenderGlobalHook; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderGlobal; +import net.minecraft.client.renderer.culling.ICamera; +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(RenderGlobal.class) +public abstract class MixinRenderGlobal { + + @Shadow + abstract boolean isRenderEntityOutlines(); + + @Unique + private final RenderGlobalHook skyHanni$hook = new RenderGlobalHook(); + + @Redirect(method = "renderEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;isRenderEntityOutlines()Z")) + public boolean renderEntitiesOutlines(RenderGlobal self, Entity renderViewEntity, ICamera camera, float partialTicks) { + return skyHanni$hook.renderEntitiesOutlines(camera, partialTicks) && this.isRenderEntityOutlines(); + } + + @Inject(method = "isRenderEntityOutlines", at = @At(value = "HEAD"), cancellable = true) + public void isRenderEntityOutlinesWrapper(CallbackInfoReturnable cir) { + skyHanni$hook.shouldRenderEntityOutlines(cir); + } + + @Inject(method = "renderEntityOutlineFramebuffer", at = @At(value = "RETURN")) + public void afterFramebufferDraw(CallbackInfo callbackInfo) { + GlStateManager.enableDepth(); + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRendererLivingEntity.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRendererLivingEntity.java index a83585114..3e09dbd9f 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRendererLivingEntity.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinRendererLivingEntity.java @@ -1,21 +1,21 @@ -package at.hannibal2.skyhanni.mixins.transformers; - -import at.hannibal2.skyhanni.mixins.hooks.RendererLivingEntityHook; -import net.minecraft.client.renderer.entity.RendererLivingEntity; -import net.minecraft.entity.EntityLivingBase; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(RendererLivingEntity.class) -public class MixinRendererLivingEntity { - - @Unique - private final RendererLivingEntityHook skyHanni$hook = new RendererLivingEntityHook(); - - @Redirect(method = "setScoreTeamColor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;color(FFFF)V")) - public void setOutlineColor(float colorRed, float colorGreen, float colorBlue, float colorAlpha, EntityLivingBase entity) { - skyHanni$hook.setOutlineColor(colorRed, colorGreen, colorBlue, colorAlpha, entity); - } -} +package at.hannibal2.skyhanni.mixins.transformers; + +import at.hannibal2.skyhanni.mixins.hooks.RendererLivingEntityHook; +import net.minecraft.client.renderer.entity.RendererLivingEntity; +import net.minecraft.entity.EntityLivingBase; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(RendererLivingEntity.class) +public class MixinRendererLivingEntity { + + @Unique + private final RendererLivingEntityHook skyHanni$hook = new RendererLivingEntityHook(); + + @Redirect(method = "setScoreTeamColor", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;color(FFFF)V")) + public void setOutlineColor(float colorRed, float colorGreen, float colorBlue, float colorAlpha, EntityLivingBase entity) { + skyHanni$hook.setOutlineColor(colorRed, colorGreen, colorBlue, colorAlpha, entity); + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzRarity.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzRarity.kt index 75412b83f..100d93b19 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzRarity.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzRarity.kt @@ -1,55 +1,55 @@ -package at.hannibal2.skyhanni.utils - -import at.hannibal2.skyhanni.test.command.ErrorManager - -// TODO: replace id with ordinal -enum class LorenzRarity(val color: LorenzColor, val id: Int) { - - COMMON(LorenzColor.WHITE, 0), - UNCOMMON(LorenzColor.GREEN, 1), - RARE(LorenzColor.BLUE, 2), - EPIC(LorenzColor.DARK_PURPLE, 3), - LEGENDARY(LorenzColor.GOLD, 4), - MYTHIC(LorenzColor.LIGHT_PURPLE, 5), - DIVINE(LorenzColor.AQUA, 6), - SUPREME(LorenzColor.DARK_RED, 7), - SPECIAL(LorenzColor.RED, 8), - VERY_SPECIAL(LorenzColor.RED, 9), - ULTIMATE(LorenzColor.DARK_RED, 10), - ; - - val chatColorCode by lazy { color.getChatColor() } - val rawName = name.replace("_", " ") - - fun oneBelow(logError: Boolean = true): LorenzRarity? { - val rarityBelow = getById(ordinal - 1) - if (rarityBelow == null && logError) { - ErrorManager.logErrorStateWithData( - "Problem with item rarity detected.", - "Trying to get an item rarity below common", - "ordinal" to ordinal - ) - } - return rarityBelow - } - - fun oneAbove(logError: Boolean = true): LorenzRarity? { - val rarityBelow = getById(ordinal + 1) - if (rarityBelow == null && logError) { - ErrorManager.logErrorStateWithData( - "Problem with item rarity detected.", - "Trying to get an item rarity above special", - "ordinal" to ordinal - ) - } - return rarityBelow - } - - fun isAtLeast(other: LorenzRarity): Boolean = this.ordinal >= other.ordinal - - companion object { - - fun getById(id: Int) = if (entries.size > id) entries[id] else null - fun getByName(name: String) = entries.firstOrNull { it.name == name } - } -} +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.test.command.ErrorManager + +// TODO: replace id with ordinal +enum class LorenzRarity(val color: LorenzColor, val id: Int) { + + COMMON(LorenzColor.WHITE, 0), + UNCOMMON(LorenzColor.GREEN, 1), + RARE(LorenzColor.BLUE, 2), + EPIC(LorenzColor.DARK_PURPLE, 3), + LEGENDARY(LorenzColor.GOLD, 4), + MYTHIC(LorenzColor.LIGHT_PURPLE, 5), + DIVINE(LorenzColor.AQUA, 6), + SUPREME(LorenzColor.DARK_RED, 7), + SPECIAL(LorenzColor.RED, 8), + VERY_SPECIAL(LorenzColor.RED, 9), + ULTIMATE(LorenzColor.DARK_RED, 10), + ; + + val chatColorCode by lazy { color.getChatColor() } + val rawName = name.replace("_", " ") + + fun oneBelow(logError: Boolean = true): LorenzRarity? { + val rarityBelow = getById(ordinal - 1) + if (rarityBelow == null && logError) { + ErrorManager.logErrorStateWithData( + "Problem with item rarity detected.", + "Trying to get an item rarity below common", + "ordinal" to ordinal + ) + } + return rarityBelow + } + + fun oneAbove(logError: Boolean = true): LorenzRarity? { + val rarityBelow = getById(ordinal + 1) + if (rarityBelow == null && logError) { + ErrorManager.logErrorStateWithData( + "Problem with item rarity detected.", + "Trying to get an item rarity above special", + "ordinal" to ordinal + ) + } + return rarityBelow + } + + fun isAtLeast(other: LorenzRarity): Boolean = this.ordinal >= other.ordinal + + companion object { + + fun getById(id: Int) = if (entries.size > id) entries[id] else null + fun getByName(name: String) = entries.firstOrNull { it.name == name } + } +} -- cgit