From b00182fc6e52e6fece36e7033ea26120076b9cd0 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 31 Aug 2022 14:00:12 +0700 Subject: [PATCH] Improve android research tree visual feedback --- .../mc/otm/android/AndroidResearch.kt | 56 +++++++++++++++---- .../mc/otm/android/AndroidResearchType.kt | 19 ++++++- .../otm/client/screen/AndroidStationScreen.kt | 48 +++++++++++++--- 3 files changed, 103 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt index d8af845b1..97860c3dc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt @@ -69,15 +69,18 @@ abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability: return false } - if (!allPrerequisitesResearched || anyBlockerResearched) { + if (!isPrerequisitesResearched || isAnyBlockerResearched) { return false } return true } - val anyBlockerResearched: Boolean get() { - for (research in blockedBy) { + /** + * Determines if this research is already blocked directly by another research. + */ + val isAnyBlockerResearched: Boolean get() { + for (research in type.flatBlockedBy) { if (capability.getResearch(research).isResearched) { return true } @@ -86,8 +89,46 @@ abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability: return false } - val allPrerequisitesResearched: Boolean get() { - for (research in prerequisites) { + /** + * Determines if this research is already blocked by another research, directly or indirectly. + * + * This is slower than [isAnyBlockerResearched] and makes sense only to check this for visualization purposes, + * since indirect blocking research will just turn [isPrerequisitesResearched] false (under normal conditions). + */ + val isAnyBlockerResearchedIndirect: Boolean get() { + for (research in type.allBlockedBy) { + if (capability.getResearch(research).isResearched) { + return true + } + } + + return false + } + + /** + * Determines if this research' direct prerequisites are researched. + */ + val isPrerequisitesResearched: Boolean get() { + for (research in type.flatPrerequisites) { + if (!capability.getResearch(research).isResearched) { + return false + } + } + + return true + } + + /** + * Determines if this research' prerequisites are researched, direct or indirect. + * + * Doesn't make much sense to check for this, since all prerequisites *should* have their prerequisites + * researched and so on. + * + * Strictly speaking, if someone tamper with research tree and mark all direct prerequisites unlocked + * without unlocking their prerequisites, [isPrerequisitesResearched] will return true, but *this* will return *false*. + */ + val isPrerequisitesResearchedIndirect: Boolean get() { + for (research in type.allPrerequisites) { if (!capability.getResearch(research).isResearched) { return false } @@ -152,9 +193,4 @@ abstract class AndroidResearch(val type: AndroidResearchType<*>, val capability: override fun deserializeNBT(nbt: CompoundTag) { isResearched = nbt.getBoolean("researched") } - - inline val prerequisites get() = type.flatPrerequisites - inline val unlocks get() = type.flatUnlocks - inline val blockedBy get() = type.flatBlockedBy - inline val blocking get() = type.flatBlocking } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt index 053bb0d19..56e9ea9fc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt @@ -231,7 +231,7 @@ open class AndroidResearchType( * * Returns list which also doubles as set (for contains method). */ - val flatBlockedBy: List> by lazy { + val flatBlockedBy: ListSet> by lazy { val list = ImmutableList.builder>() for (blocker in definedBlockedBy) { @@ -252,6 +252,23 @@ open class AndroidResearchType( ListSet(list.build()) } + /** + * All research blocking this research, even indirect ones (e.g. blocked by through one of parent research). + * + * Returns list which also doubles as set (for contains method). + */ + val allBlockedBy: ListSet> by lazy { + val list = HashSet>() + + list.addAll(flatBlockedBy) + + for (parent in allPrerequisites) { + list.addAll(parent.flatBlockedBy) + } + + ListSet(list) + } + /** * All research directly unlocked by this research. * diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt index 46231a05f..9405bcca2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt @@ -22,7 +22,6 @@ import ru.dbotthepony.mc.otm.client.render.drawColor import ru.dbotthepony.mc.otm.client.render.drawLine import ru.dbotthepony.mc.otm.client.render.drawRect import ru.dbotthepony.mc.otm.client.screen.panels.* -import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel import ru.dbotthepony.mc.otm.core.RGBAColor import ru.dbotthepony.mc.otm.ifPresentK @@ -294,6 +293,8 @@ private open class AndroidResearchButton( AndroidStationScreen.RESEARCHED.setSystemColor() } else if (node.canResearch) { AndroidStationScreen.CAN_BE_RESEARCHED.setSystemColor() + } else if (node.isAnyBlockerResearchedIndirect) { + AndroidStationScreen.ALREADY_BLOCKED.setSystemColor() } else { AndroidStationScreen.CAN_NOT_BE_RESEARCHED.setSystemColor() } @@ -310,7 +311,7 @@ private open class AndroidResearchButton( RGBAColor.RED.setSystemColor() Widgets18.CROSS.render(stack) - } else if (node.anyBlockerResearched) { + } else if (node.isAnyBlockerResearched) { RGBAColor.RED.setSystemColor() Widgets18.FORWARD_SLASH.render(stack) @@ -334,16 +335,44 @@ private open class AndroidResearchButton( } hovered ?: return - val lines = highlightLines[hovered.type] ?: return + val pathLines = highlightLines[hovered.type] - drawColor = RGBAColor.LIGHT_GREEN + if (pathLines != null) { + drawColor = RGBAColor.LIGHT_GREEN - for (line in lines) { - val (pos1, pos2) = line - val (x1, y1) = pos1 - val (x2, y2) = pos2 + for (line in pathLines) { + val (pos1, pos2) = line + val (x1, y1) = pos1 + val (x2, y2) = pos2 - drawLine(stack, x1, y1, x2, y2, 0.5f) + drawLine(stack, x1, y1, x2, y2, 0.5f) + } + } + + // very lazy solution + val mark = Any() + val drawn = IdentityHashMap() + + for (blocker in hovered.type.allBlocking) { + val blockLines = highlightLines[blocker] + + if (blockLines != null) { + drawColor = RGBAColor.RED + + for (line in blockLines) { + if (drawn.containsKey(line)) { + continue + } + + drawn[line] = mark + + val (pos1, pos2) = line + val (x1, y1) = pos1 + val (x2, y2) = pos2 + + drawLine(stack, x1, y1, x2, y2, 0.5f) + } + } } } @@ -494,5 +523,6 @@ class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: I val RESEARCHED = RGBAColor(150, 150, 200) val CAN_BE_RESEARCHED = RGBAColor(150, 200, 150) val CAN_NOT_BE_RESEARCHED = RGBAColor(200, 150, 150) + val ALREADY_BLOCKED = RGBAColor(105, 79, 79) } } \ No newline at end of file