From f0013fa28826bb67344566a26f522c566d700ff3 Mon Sep 17 00:00:00 2001 From: Sheng Date: Tue, 27 Jan 2026 16:02:48 +1100 Subject: [PATCH 1/6] add detection for strengthened skills --- app/src/main/assets/En/skill_strengthened.png | Bin 0 -> 1743 bytes app/src/main/assets/Jp/skill_strengthened.png | Bin 0 -> 1743 bytes .../ui/pref_support/PreferredSupportScreen.kt | 51 ++++++++++++++++++ app/src/main/res/values/localized.xml | 1 + .../prefs/SupportPreferences.kt | 4 ++ .../prefs/core/SupportPrefsCore.kt | 4 ++ .../fate_grand_automata/scripts/Images.kt | 1 + .../scripts/prefs/ISupportPreferences.kt | 4 ++ .../supportSelection/ServantSelection.kt | 41 +++++++++++++- 9 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 app/src/main/assets/En/skill_strengthened.png create mode 100644 app/src/main/assets/Jp/skill_strengthened.png diff --git a/app/src/main/assets/En/skill_strengthened.png b/app/src/main/assets/En/skill_strengthened.png new file mode 100644 index 0000000000000000000000000000000000000000..02cbfbd4d536d1d422989a95c679b5941a52b0e7 GIT binary patch literal 1743 zcmb_cU2NM_6uxfLEu;+A4T-7MRxgYRjm3ZIy2MI@(q?T+m$pd*qp524RrYhM@I;Gc{I z!a;6G~got}_bT|2y=vDVE9<_8JwLJvnu0qvK-svBo&;-f{GopcuobjZ40et)CWMmxzJ(C zPzb{E#_OhRak<>6K-5-WXsNDjd!e-^10Hb<*UK|3&oLz1%d-s6^q@ysmS>F1Z=Kx(bqu{8)`CqO6HhmNa;U87Zgr+3iN4>Qi1P z*2P++tXfG65~K|a%~A~A3?`=}O;S^mTwUcRY}LFafhH!lh-+B3gh7#;N7Mzm3^J=( zPSRtfz>g&oD%K)m29GIXQp^fzZ+WvlW{S=1wK|n4#zh^cqkf>ss!&re%ej~ey3y1K z9gxzJG9e2w>}4yEI#r{$$u?OFbd=i^r_R!doXE|nIJPNEXo*V1CdN8^b+dTCpy2@p z!#D0Vn#94d4vvXzGzn!gHg~V%Gd7TqhOsK8bH)+$9_2jrf0T`Y$!36hWg`G~6T|U0 zBS2}*0QI^$e>gR~nU|zQBq{Cvf377D-N;)B>7k9Lt82n?hnrexajy-K8`p)`cHzU) z?{I@G5Bq%)>)y7-;z)~tE|yk``}Bk|H@Jvj-1*49XP2i3uG!D8uKl>OD|mhF`zxLE zuZ)Zw8lAWJe!kR^nk(!r-6-7t%k$mRA0J%J$oad+Bagj%cj~}IrqJDGO}Z}flJeOn zyYGA)qAoDM-nQnSKed1Aio4x?d0{m0WO4sH`cGH?+p524RrYhM@I;Gc{I z!a;6G~got}_bT|2y=vDVE9<_8JwLJvnu0qvK-svBo&;-f{GopcuobjZ40et)CWMmxzJ(C zPzb{E#_OhRak<>6K-5-WXsNDjd!e-^10Hb<*UK|3&oLz1%d-s6^q@ysmS>F1Z=Kx(bqu{8)`CqO6HhmNa;U87Zgr+3iN4>Qi1P z*2P++tXfG65~K|a%~A~A3?`=}O;S^mTwUcRY}LFafhH!lh-+B3gh7#;N7Mzm3^J=( zPSRtfz>g&oD%K)m29GIXQp^fzZ+WvlW{S=1wK|n4#zh^cqkf>ss!&re%ej~ey3y1K z9gxzJG9e2w>}4yEI#r{$$u?OFbd=i^r_R!doXE|nIJPNEXo*V1CdN8^b+dTCpy2@p z!#D0Vn#94d4vvXzGzn!gHg~V%Gd7TqhOsK8bH)+$9_2jrf0T`Y$!36hWg`G~6T|U0 zBS2}*0QI^$e>gR~nU|zQBq{Cvf377D-N;)B>7k9Lt82n?hnrexajy-K8`p)`cHzU) z?{I@G5Bq%)>)y7-;z)~tE|yk``}Bk|H@Jvj-1*49XP2i3uG!D8uKl>OD|mhF`zxLE zuZ)Zw8lAWJe!kR^nk(!r-6-7t%k$mRA0J%J$oad+Bagj%cj~}IrqJDGO}Z}flJeOn zyYGA)qAoDM-nQnSKed1Aio4x?d0{m0WO4sH`cGH?+> +) { + Row(verticalAlignment = Alignment.CenterVertically) { + skills.forEachIndexed { index, pref -> + if (index != 0) { + Text("/", modifier = Modifier.padding(horizontal = 8.dp)) + } + + var rankUp by pref.remember() + + Card( + elevation = cardElevation(5.dp), + colors = CardDefaults.cardColors( + containerColor = if (rankUp > 0) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.surfaceVariant, + contentColor = if (rankUp > 0) MaterialTheme.colorScheme.onSecondary else MaterialTheme.colorScheme.onSurfaceVariant + ) + ) { + Box( + contentAlignment = Alignment.Center, +// change to '(rankUp + 1) % 4' in future if there's 3 rank up quests on the same skill + modifier = Modifier + .clickable { rankUp = (rankUp + 1) % 3 } + .size(40.dp) + ) { + Text(rankUp.toString()) + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/values/localized.xml b/app/src/main/res/values/localized.xml index 75aacc6b7..8e579d803 100644 --- a/app/src/main/res/values/localized.xml +++ b/app/src/main/res/values/localized.xml @@ -114,6 +114,7 @@ Skills" "Friend name images can be added using 'Support Image Maker' script" "Also check All" "Max Skills" + "Strengthened Skills" "Game Server" "Auto-detect" "Danger mode requires Auto-targeting" diff --git a/prefs/src/main/java/io/github/fate_grand_automata/prefs/SupportPreferences.kt b/prefs/src/main/java/io/github/fate_grand_automata/prefs/SupportPreferences.kt index ec19cebe7..5b905106b 100644 --- a/prefs/src/main/java/io/github/fate_grand_automata/prefs/SupportPreferences.kt +++ b/prefs/src/main/java/io/github/fate_grand_automata/prefs/SupportPreferences.kt @@ -34,6 +34,10 @@ internal class SupportPreferences( override val skill2Max by prefs.skill2Max override val skill3Max by prefs.skill3Max + override val skill1Strengthened by prefs.skill1Strengthened + override val skill2Strengthened by prefs.skill2Strengthened + override val skill3Strengthened by prefs.skill3Strengthened + override val grandServant by prefs.grandServant override val bondCEEffect by prefs.bondCEEffect override val requireBothNormalAndRewardMatch by prefs.requireBothNormalAndRewardMatch diff --git a/prefs/src/main/java/io/github/fate_grand_automata/prefs/core/SupportPrefsCore.kt b/prefs/src/main/java/io/github/fate_grand_automata/prefs/core/SupportPrefsCore.kt index 0bb492c89..888201850 100644 --- a/prefs/src/main/java/io/github/fate_grand_automata/prefs/core/SupportPrefsCore.kt +++ b/prefs/src/main/java/io/github/fate_grand_automata/prefs/core/SupportPrefsCore.kt @@ -36,6 +36,10 @@ class SupportPrefsCore( val skill2Max = maker.bool("support_skill_max_2") val skill3Max = maker.bool("support_skill_max_3") + val skill1Strengthened = maker.int("support_skill_strengthened_1") + val skill2Strengthened = maker.int("support_skill_strengthened_2") + val skill3Strengthened = maker.int("support_skill_strengthened_3") + val grandServant = maker.bool("support_grand_servant") val bondCEEffect = maker.enum( "support_bond_ce_effect", diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/Images.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/Images.kt index e06078b59..df0e6cbd2 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/Images.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/Images.kt @@ -94,4 +94,5 @@ enum class Images(val path: String) { GrandCeLabel("grand_ce_label.png"), BondCeEffectDefault("bond_ce_effect_default.png"), BondCeEffectNP("bond_ce_effect_np.png"), + SkillStrengthened("skill_strengthened.png"), } \ No newline at end of file diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/prefs/ISupportPreferences.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/prefs/ISupportPreferences.kt index 6ab1b3871..1ea361529 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/prefs/ISupportPreferences.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/prefs/ISupportPreferences.kt @@ -21,6 +21,10 @@ interface ISupportPreferences { val skill2Max: Boolean val skill3Max: Boolean + val skill1Strengthened: Int + val skill2Strengthened: Int + val skill3Strengthened: Int + val grandServant: Boolean val bondCEEffect: BondCEEffectEnum val requireBothNormalAndRewardMatch: Boolean diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt index ae31b4c62..66eb0aef1 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt @@ -49,6 +49,18 @@ class ServantSelection @Inject constructor( !skillCheckNeeded || checkMaxedSkills(needMaxedSkills, whichSkillsAreMaxed(bounds.region)) } + .filter { + val needStrengthenedSkills = listOf( + supportPrefs.skill1Strengthened, + supportPrefs.skill2Strengthened, + supportPrefs.skill3Strengthened + ) + + val skillStrengthenedCheckNeeded = needStrengthenedSkills.any { it > 0} + + !skillStrengthenedCheckNeeded || checkStrengthenedSkills(bounds.region, needStrengthenedSkills).all{ it } + } + return matched.isNotEmpty() } @@ -102,7 +114,7 @@ class ServantSelection @Inject constructor( skillRegion.exists(images[Images.SkillTen], similarity = 0.68) } } - + private fun checkMaxedSkills(expectedSkills: List, actualSkills: List): Boolean { val result = expectedSkills .zip(actualSkills) { expected, actual -> @@ -118,4 +130,29 @@ class ServantSelection @Inject constructor( return result.all { it } } -} \ No newline at end of file + + /** + * Check if the skill is strengthened(rank-up quest cleared) + * Currently restricted to level 2 (2 rank-up quests) for each skill, can modify in UI [PreferredSupportScreen.kt] to allow more + */ + private fun checkStrengthenedSkills(bounds: Region, needStrengthenedSkills: List): List { + val y = bounds.y + 325 + 28 + val x = bounds.x + 1610 + 41 + val skillMargin = 90 + val rankUpMargin = 18 + + return needStrengthenedSkills.mapIndexed { index, requirement -> + if (requirement > 0) { + val loc = Location( + x + index * skillMargin, + y - (requirement - 1) * rankUpMargin + ) + val skillRegion = Region(loc, Size(24, 24)) + skillRegion.exists(images[Images.SkillStrengthened], similarity = 0.68) + } else { + // If requirement is 0, this skill passes automatically + true + } + } + } +} From 5600131bc5bd400d32511e323accb390b9ed19fc Mon Sep 17 00:00:00 2001 From: sheng-28 Date: Wed, 28 Jan 2026 18:03:42 +1100 Subject: [PATCH 2/6] Address reviews --- .../ui/pref_support/PreferredSupportScreen.kt | 38 +++++++++++++++++-- .../locations/SupportScreenLocations.kt | 2 + .../supportSelection/ServantSelection.kt | 20 +++++----- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/io/github/fate_grand_automata/ui/pref_support/PreferredSupportScreen.kt b/app/src/main/java/io/github/fate_grand_automata/ui/pref_support/PreferredSupportScreen.kt index f556db978..4d859c024 100644 --- a/app/src/main/java/io/github/fate_grand_automata/ui/pref_support/PreferredSupportScreen.kt +++ b/app/src/main/java/io/github/fate_grand_automata/ui/pref_support/PreferredSupportScreen.kt @@ -8,18 +8,23 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults.cardElevation +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -71,6 +76,21 @@ private fun PreferredSupport( val prefServants by config.preferredServants.remember() val prefCEs by config.preferredCEs.remember() + // + var showStrengthenedNote by androidx.compose.runtime.remember { mutableStateOf(false) } + if (showStrengthenedNote) { + AlertDialog( + onDismissRequest = { showStrengthenedNote = false }, + confirmButton = { + TextButton(onClick = { showStrengthenedNote = false }) { + Text(stringResource(android.R.string.ok)) + } + }, + title = { Text("Strengthened Skills") }, + text = { Text("This feature has been tested on JP server and NA server. Contact Github/Discord if it is not working on your server.") } + ) + } + LazyColumn { item { Heading(stringResource(R.string.p_support_mode_preferred)) @@ -128,11 +148,21 @@ private fun PreferredSupport( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(16.dp) ) { - Text( -// // todo: need localized versions? - stringResource(R.string.p_strengthened_skills), + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f) - ) + ) { + Text(stringResource(R.string.p_strengthened_skills)) + Icon( + painter = painterResource(R.drawable.ic_info), + contentDescription = "Info", + modifier = Modifier + .padding(start = 8.dp) + .size(20.dp) + .clickable { showStrengthenedNote = true }, + tint = MaterialTheme.colorScheme.secondary + ) + } StrengthenedSkills( skills = listOf( diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/locations/SupportScreenLocations.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/locations/SupportScreenLocations.kt index ed4d96c3b..4de290609 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/locations/SupportScreenLocations.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/locations/SupportScreenLocations.kt @@ -66,6 +66,8 @@ class SupportScreenLocations @Inject constructor( val listSwipeStart = Location(-59, if (canLongSwipe) 1000 else 1190) + supportOffset val listSwipeEnd = Location(-89, if (canLongSwipe) 300 else 660) + supportOffset + val strengthenedSkillRegion = Region(1495, 216, 24, 24) + supportOffset + fun locate(supportClass: SupportClass) = when (supportClass) { SupportClass.None -> 0 SupportClass.All -> 184 diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt index 66eb0aef1..ecf6d97fe 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt @@ -34,6 +34,7 @@ class ServantSelection @Inject constructor( } } .filter { + println("Servant found at: ${it.region}") // todo [TESTING] !supportPrefs.maxAscended || isMaxAscended(it.region) } .filter { @@ -58,7 +59,7 @@ class ServantSelection @Inject constructor( val skillStrengthenedCheckNeeded = needStrengthenedSkills.any { it > 0} - !skillStrengthenedCheckNeeded || checkStrengthenedSkills(bounds.region, needStrengthenedSkills).all{ it } + !skillStrengthenedCheckNeeded || checkStrengthenedSkills(it.region, needStrengthenedSkills).all{ it } } return matched.isNotEmpty() @@ -84,7 +85,7 @@ class ServantSelection @Inject constructor( private fun isMaxAscended(servant: Region): Boolean { val maxAscendedRegion = locations.support.maxAscendedRegion .copy(y = servant.y) - + println("Max Ascended Region: ${maxAscendedRegion}") //todo [TESTING] return starChecker.isStarPresent(maxAscendedRegion) } @@ -135,20 +136,19 @@ class ServantSelection @Inject constructor( * Check if the skill is strengthened(rank-up quest cleared) * Currently restricted to level 2 (2 rank-up quests) for each skill, can modify in UI [PreferredSupportScreen.kt] to allow more */ - private fun checkStrengthenedSkills(bounds: Region, needStrengthenedSkills: List): List { - val y = bounds.y + 325 + 28 - val x = bounds.x + 1610 + 41 + private fun checkStrengthenedSkills(servant: Region, needStrengthenedSkills: List): List { + val strengthenedSkillRegion = locations.support.strengthenedSkillRegion val skillMargin = 90 val rankUpMargin = 18 return needStrengthenedSkills.mapIndexed { index, requirement -> if (requirement > 0) { - val loc = Location( - x + index * skillMargin, - y - (requirement - 1) * rankUpMargin + val strengthenedSkillLocation = Location( + servant.x + index * skillMargin, + servant.y - (requirement - 1) * rankUpMargin ) - val skillRegion = Region(loc, Size(24, 24)) - skillRegion.exists(images[Images.SkillStrengthened], similarity = 0.68) + println("Strengthened Skill Region: ${strengthenedSkillRegion + strengthenedSkillLocation}") + (strengthenedSkillRegion + strengthenedSkillLocation).exists(images[Images.SkillStrengthened], similarity = 0.68) } else { // If requirement is 0, this skill passes automatically true From 910299a5ec4958712f072c2e294b91a2a9f5e4ac Mon Sep 17 00:00:00 2001 From: sheng-28 Date: Thu, 29 Jan 2026 17:01:58 +1100 Subject: [PATCH 3/6] Add detection of empty strengthened skills, add new Exit to stop the script when required strengthened skills are empty --- .../main/assets/En/skill_unstrengthened.png | Bin 0 -> 1715 bytes .../fate_grand_automata/ui/exit/BattleExit.kt | 1 + .../ui/pref_support/PreferredSupportScreen.kt | 22 ++++++------ app/src/main/res/values/localized.xml | 2 ++ .../fate_grand_automata/scripts/Images.kt | 1 + .../scripts/entrypoints/AutoBattle.kt | 2 ++ .../supportSelection/ServantSelection.kt | 32 +++++++++++++----- 7 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 app/src/main/assets/En/skill_unstrengthened.png diff --git a/app/src/main/assets/En/skill_unstrengthened.png b/app/src/main/assets/En/skill_unstrengthened.png new file mode 100644 index 0000000000000000000000000000000000000000..62bfe3e9b33e393a5dcabe3c570804a0b78ab903 GIT binary patch literal 1715 zcmb_cU2NM_6uxfSu1iP>K#5T4= z+=0e4&`>4N(n*7ZQsXg{&NE++^UwSjIQj3aN!sJ-TM8D{&5z_2mS98@y8DrZQ|XIPfyEg4uR zSs$4^$PH7Rj~QeJDF#N6C11iyURDfdkB{ZpT|QFB1uPlZo5#}zuys~kPSkB6uT(ZW zG8BSnKRfHl?SpAfo{mCaBPdIms^a zMlCV{al5zLgFtf^9)~)|X`IX48?dvH%XF$p2_lw@MsMVe?y*}X=*%8m&;|U?4wkD zie>|W0L^eT$9ciSYfLMqWO)_isVd1TPZ%3%UDixlRY;pxnp8{XkjG;Osz23rVoP3& zq!_idAVJ!;&@9E!tzcSCHB}?0YV}nxh=5RY6WQt4RuW z3jA0$YhW`e7x9=bPsxf@5UZOlI4QQWuhpqaF)mxUkO~8ZVn9vBKn>cgZ^nhxf~p_V zq%;=U8l*wh?Cr2ktP`a{-;6s{nly0wp#-5DYVlea8}Rjg;=_`OLpp|UMzs6G%dlQH zkX-E#%3*Bn-oR(?9iK{ILoJl-(N?ze{it2Vczh3*#JfLy&PME8~0*A=)tZbZYuWY^A} z->~^F_M*K%l)rj&t(O-Ai}$zRzWUD#Usm?_`9A9!9SNTL_>J6^N8djD^7ME2 zmTx>?JRAEnwPW_gJ1>S-#QwInXTR-@{q#ie+RVc4^K|d8ue>UZ{kZ)5rp@8Vy`H>R zx+U#+T_3o<=h_EbzTb8GZr_=O;f}dG?}z42Jb7;BQhDX-Ywy12zeq)wMo%9x0(}oI zc7HT5zHs^x*U@6-&drX|jh%n{KfgO$)Ia_0((!G7^e)YYT=&NY&)HXCbmTzz`tY$+ F{{nD0@yP%H literal 0 HcmV?d00001 diff --git a/app/src/main/java/io/github/fate_grand_automata/ui/exit/BattleExit.kt b/app/src/main/java/io/github/fate_grand_automata/ui/exit/BattleExit.kt index fbc59068c..a0abea214 100644 --- a/app/src/main/java/io/github/fate_grand_automata/ui/exit/BattleExit.kt +++ b/app/src/main/java/io/github/fate_grand_automata/ui/exit/BattleExit.kt @@ -77,6 +77,7 @@ private fun AutoBattle.ExitReason.text(): String = when (this) { AutoBattle.ExitReason.FirstClearRewards -> stringResource(R.string.first_clear_rewards) AutoBattle.ExitReason.Paused -> stringResource(R.string.script_paused) AutoBattle.ExitReason.StopAfterThisRun -> stringResource(R.string.stop_after_this_run) + is AutoBattle.ExitReason.StrengthenedSkillEmpty -> stringResource(R.string.strengthened_skill_empty, skill, requirement) } @Composable diff --git a/app/src/main/java/io/github/fate_grand_automata/ui/pref_support/PreferredSupportScreen.kt b/app/src/main/java/io/github/fate_grand_automata/ui/pref_support/PreferredSupportScreen.kt index 4d859c024..dfa2946a9 100644 --- a/app/src/main/java/io/github/fate_grand_automata/ui/pref_support/PreferredSupportScreen.kt +++ b/app/src/main/java/io/github/fate_grand_automata/ui/pref_support/PreferredSupportScreen.kt @@ -76,7 +76,7 @@ private fun PreferredSupport( val prefServants by config.preferredServants.remember() val prefCEs by config.preferredCEs.remember() - // + // todo: remove this note later when the feature is working fine on all servers var showStrengthenedNote by androidx.compose.runtime.remember { mutableStateOf(false) } if (showStrengthenedNote) { AlertDialog( @@ -86,8 +86,8 @@ private fun PreferredSupport( Text(stringResource(android.R.string.ok)) } }, - title = { Text("Strengthened Skills") }, - text = { Text("This feature has been tested on JP server and NA server. Contact Github/Discord if it is not working on your server.") } +// title = { Text(stringResource(R.string.note)) }, + text = { Text(stringResource(R.string.p_strengthened_skills_note)) } ) } @@ -165,7 +165,7 @@ private fun PreferredSupport( } StrengthenedSkills( - skills = listOf( + strengthenedSkills = listOf( config.skill1Strengthened, config.skill2Strengthened, config.skill3Strengthened @@ -341,31 +341,31 @@ private fun MaxSkills( @Composable private fun StrengthenedSkills( - skills: List> + strengthenedSkills: List> ) { Row(verticalAlignment = Alignment.CenterVertically) { - skills.forEachIndexed { index, pref -> + strengthenedSkills.forEachIndexed { index, pref -> if (index != 0) { Text("/", modifier = Modifier.padding(horizontal = 8.dp)) } - var rankUp by pref.remember() + var strengthened by pref.remember() Card( elevation = cardElevation(5.dp), colors = CardDefaults.cardColors( - containerColor = if (rankUp > 0) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.surfaceVariant, - contentColor = if (rankUp > 0) MaterialTheme.colorScheme.onSecondary else MaterialTheme.colorScheme.onSurfaceVariant + containerColor = if (strengthened > 0) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.surfaceVariant, + contentColor = if (strengthened > 0) MaterialTheme.colorScheme.onSecondary else MaterialTheme.colorScheme.onSurfaceVariant ) ) { Box( contentAlignment = Alignment.Center, // change to '(rankUp + 1) % 4' in future if there's 3 rank up quests on the same skill modifier = Modifier - .clickable { rankUp = (rankUp + 1) % 3 } + .clickable { strengthened = (strengthened + 1) % 3 } .size(40.dp) ) { - Text(rankUp.toString()) + Text(strengthened.toString()) } } } diff --git a/app/src/main/res/values/localized.xml b/app/src/main/res/values/localized.xml index 8e579d803..810e5c4ac 100644 --- a/app/src/main/res/values/localized.xml +++ b/app/src/main/res/values/localized.xml @@ -115,6 +115,8 @@ Skills" "Also check All" "Max Skills" "Strengthened Skills" + "This feature has been tested on JP and NA server. Contact Github/Discord if it is not working on your server." + "Strengthening %2$d may not be available for Skill %1$d. Please check your configuration." "Game Server" "Auto-detect" "Danger mode requires Auto-targeting" diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/Images.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/Images.kt index df0e6cbd2..3bcd2128a 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/Images.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/Images.kt @@ -95,4 +95,5 @@ enum class Images(val path: String) { BondCeEffectDefault("bond_ce_effect_default.png"), BondCeEffectNP("bond_ce_effect_np.png"), SkillStrengthened("skill_strengthened.png"), + SkillUnstrengthened("skill_unstrengthened.png"), } \ No newline at end of file diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/entrypoints/AutoBattle.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/entrypoints/AutoBattle.kt index 8f163d919..bc37fb345 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/entrypoints/AutoBattle.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/entrypoints/AutoBattle.kt @@ -74,6 +74,8 @@ class AutoBattle @Inject constructor( class CardPriorityParseError(val msg: String) : ExitReason() data object Paused : ExitReason() data object StopAfterThisRun : ExitReason() + // Inside sealed class ExitReason + class StrengthenedSkillEmpty(val skill: Int, val requirement: Int) : ExitReason() } internal class BattleExitException(val reason: ExitReason) : Exception(reason.cause) diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt index ecf6d97fe..2460c91f2 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt @@ -5,6 +5,7 @@ import io.github.fate_grand_automata.scripts.IFgoAutomataApi import io.github.fate_grand_automata.scripts.Images import io.github.fate_grand_automata.scripts.ScriptLog import io.github.fate_grand_automata.scripts.prefs.ISupportPreferences +import io.github.fate_grand_automata.scripts.entrypoints.AutoBattle import io.github.lib_automata.Location import io.github.lib_automata.Pattern import io.github.lib_automata.Region @@ -34,7 +35,6 @@ class ServantSelection @Inject constructor( } } .filter { - println("Servant found at: ${it.region}") // todo [TESTING] !supportPrefs.maxAscended || isMaxAscended(it.region) } .filter { @@ -52,13 +52,12 @@ class ServantSelection @Inject constructor( .filter { val needStrengthenedSkills = listOf( - supportPrefs.skill1Strengthened, - supportPrefs.skill2Strengthened, - supportPrefs.skill3Strengthened + supportPrefs.skill1Strengthened.coerceIn(0..2), + supportPrefs.skill2Strengthened.coerceIn(0..2), + supportPrefs.skill3Strengthened.coerceIn(0..2) ) val skillStrengthenedCheckNeeded = needStrengthenedSkills.any { it > 0} - !skillStrengthenedCheckNeeded || checkStrengthenedSkills(it.region, needStrengthenedSkills).all{ it } } @@ -85,7 +84,7 @@ class ServantSelection @Inject constructor( private fun isMaxAscended(servant: Region): Boolean { val maxAscendedRegion = locations.support.maxAscendedRegion .copy(y = servant.y) - println("Max Ascended Region: ${maxAscendedRegion}") //todo [TESTING] + return starChecker.isStarPresent(maxAscendedRegion) } @@ -141,14 +140,31 @@ class ServantSelection @Inject constructor( val skillMargin = 90 val rankUpMargin = 18 + /* + When the servant is found near the bottom of the screen, return false to skip detection to avoid exception + todo: might need to tune the value of y and add to support locations + */ + if (servant.y > 1000) { + return List(needStrengthenedSkills.size) { false } + } return needStrengthenedSkills.mapIndexed { index, requirement -> if (requirement > 0) { val strengthenedSkillLocation = Location( servant.x + index * skillMargin, servant.y - (requirement - 1) * rankUpMargin ) - println("Strengthened Skill Region: ${strengthenedSkillRegion + strengthenedSkillLocation}") - (strengthenedSkillRegion + strengthenedSkillLocation).exists(images[Images.SkillStrengthened], similarity = 0.68) + if((strengthenedSkillRegion + strengthenedSkillLocation).exists(images[Images.SkillStrengthened], similarity = 0.68)) { + true + } else { + if((strengthenedSkillRegion + strengthenedSkillLocation).exists(images[Images.SkillUnstrengthened], similarity = 0.68)) { + false + } else { + // Unstrengthened template not found, exit script + throw AutoBattle.BattleExitException( + AutoBattle.ExitReason.StrengthenedSkillEmpty(index + 1, requirement) + ) + } + } } else { // If requirement is 0, this skill passes automatically true From 331e614eaaa763e6448f8c7161f8292b8d4d80d7 Mon Sep 17 00:00:00 2001 From: sheng-28 <50127137+sheng-28@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:13:18 +1100 Subject: [PATCH 4/6] Update app/src/main/res/values/localized.xml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- app/src/main/res/values/localized.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/localized.xml b/app/src/main/res/values/localized.xml index 810e5c4ac..d6ca06e4f 100644 --- a/app/src/main/res/values/localized.xml +++ b/app/src/main/res/values/localized.xml @@ -115,7 +115,7 @@ Skills" "Also check All" "Max Skills" "Strengthened Skills" - "This feature has been tested on JP and NA server. Contact Github/Discord if it is not working on your server." + "This feature has been tested on JP and NA servers. Contact GitHub/Discord if it is not working on your server." "Strengthening %2$d may not be available for Skill %1$d. Please check your configuration." "Game Server" "Auto-detect" From 05ed3ea9b9d6a71c30c82bf1deacad598be30e18 Mon Sep 17 00:00:00 2001 From: sheng-28 Date: Mon, 16 Feb 2026 15:10:31 +1100 Subject: [PATCH 5/6] Replace servant portrait region with support entry bounds for detection --- .../locations/SupportScreenLocations.kt | 2 +- .../supportSelection/ServantSelection.kt | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/locations/SupportScreenLocations.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/locations/SupportScreenLocations.kt index 4de290609..9ad3edb17 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/locations/SupportScreenLocations.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/locations/SupportScreenLocations.kt @@ -66,7 +66,7 @@ class SupportScreenLocations @Inject constructor( val listSwipeStart = Location(-59, if (canLongSwipe) 1000 else 1190) + supportOffset val listSwipeEnd = Location(-89, if (canLongSwipe) 300 else 660) + supportOffset - val strengthenedSkillRegion = Region(1495, 216, 24, 24) + supportOffset + val strengthenedSkillRegion = Region(0, 0, 24, 24) + supportOffset fun locate(supportClass: SupportClass) = when (supportClass) { SupportClass.None -> 0 diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt index 2460c91f2..595ce6e94 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt @@ -58,7 +58,7 @@ class ServantSelection @Inject constructor( ) val skillStrengthenedCheckNeeded = needStrengthenedSkills.any { it > 0} - !skillStrengthenedCheckNeeded || checkStrengthenedSkills(it.region, needStrengthenedSkills).all{ it } + !skillStrengthenedCheckNeeded || checkStrengthenedSkills(bounds.region, needStrengthenedSkills).all{ it } } return matched.isNotEmpty() @@ -135,28 +135,29 @@ class ServantSelection @Inject constructor( * Check if the skill is strengthened(rank-up quest cleared) * Currently restricted to level 2 (2 rank-up quests) for each skill, can modify in UI [PreferredSupportScreen.kt] to allow more */ - private fun checkStrengthenedSkills(servant: Region, needStrengthenedSkills: List): List { - val strengthenedSkillRegion = locations.support.strengthenedSkillRegion + private fun checkStrengthenedSkills(bounds: Region, needStrengthenedSkills: List): List { val skillMargin = 90 val rankUpMargin = 18 /* - When the servant is found near the bottom of the screen, return false to skip detection to avoid exception - todo: might need to tune the value of y and add to support locations + When the servant is found near the bottom of the screen, + return false to skip detection of 'Images.SkillUnstrengthened' + to avoid false exception */ - if (servant.y > 1000) { + if (bounds.y > 1000) { return List(needStrengthenedSkills.size) { false } } return needStrengthenedSkills.mapIndexed { index, requirement -> if (requirement > 0) { val strengthenedSkillLocation = Location( - servant.x + index * skillMargin, - servant.y - (requirement - 1) * rankUpMargin + bounds.x + 1651 + index * skillMargin, + bounds.y + 351 - (requirement - 1) * rankUpMargin ) - if((strengthenedSkillRegion + strengthenedSkillLocation).exists(images[Images.SkillStrengthened], similarity = 0.68)) { + val strengthenedSkillRegion = locations.support.strengthenedSkillRegion.copy(x = strengthenedSkillLocation.x, y = strengthenedSkillLocation.y) + if(strengthenedSkillRegion.exists(images[Images.SkillStrengthened], similarity = 0.68)) { true } else { - if((strengthenedSkillRegion + strengthenedSkillLocation).exists(images[Images.SkillUnstrengthened], similarity = 0.68)) { + if(strengthenedSkillRegion.exists(images[Images.SkillUnstrengthened], similarity = 0.68)) { false } else { // Unstrengthened template not found, exit script From bce777d1e7f80a1a434cccda582fd5bd107c6905 Mon Sep 17 00:00:00 2001 From: sheng-28 Date: Wed, 18 Feb 2026 15:11:38 +1100 Subject: [PATCH 6/6] Replace nested if-else with when --- .../supportSelection/ServantSelection.kt | 50 ++++++++----------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt index 595ce6e94..5f6019d37 100644 --- a/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt +++ b/scripts/src/main/java/io/github/fate_grand_automata/scripts/supportSelection/ServantSelection.kt @@ -133,42 +133,34 @@ class ServantSelection @Inject constructor( /** * Check if the skill is strengthened(rank-up quest cleared) - * Currently restricted to level 2 (2 rank-up quests) for each skill, can modify in UI [PreferredSupportScreen.kt] to allow more + * Currently restricted to level 2 (2 rank-up quests) for each skill, can modify in UI to allow more */ private fun checkStrengthenedSkills(bounds: Region, needStrengthenedSkills: List): List { val skillMargin = 90 val rankUpMargin = 18 - /* - When the servant is found near the bottom of the screen, - return false to skip detection of 'Images.SkillUnstrengthened' - to avoid false exception - */ - if (bounds.y > 1000) { - return List(needStrengthenedSkills.size) { false } - } + /** + * When the servant portrait is found near the bottom of the screen + * return false to skip detection of 'Images.SkillUnstrengthened' + * to avoid false exception + */ + if (bounds.y > 1000) return List(needStrengthenedSkills.size) { false } + return needStrengthenedSkills.mapIndexed { index, requirement -> - if (requirement > 0) { - val strengthenedSkillLocation = Location( - bounds.x + 1651 + index * skillMargin, - bounds.y + 351 - (requirement - 1) * rankUpMargin + if (requirement <= 0) return@mapIndexed true + + val strengthenedSkillLocation = Location( + bounds.x + 1650 + index * skillMargin, + bounds.y + 351 - (requirement - 1) * rankUpMargin + ) + val strengthenedSkillRegion = locations.support.strengthenedSkillRegion.copy(x = strengthenedSkillLocation.x, y = strengthenedSkillLocation.y) + when{ + strengthenedSkillRegion.exists(images[Images.SkillStrengthened], similarity = 0.68) -> true + strengthenedSkillRegion.exists(images[Images.SkillUnstrengthened], similarity = 0.68) -> false + else -> throw AutoBattle.BattleExitException( + // 'Images.SkillUnstrengthened' not found, exit script and prompt user to check configuration + AutoBattle.ExitReason.StrengthenedSkillEmpty(index + 1, requirement) ) - val strengthenedSkillRegion = locations.support.strengthenedSkillRegion.copy(x = strengthenedSkillLocation.x, y = strengthenedSkillLocation.y) - if(strengthenedSkillRegion.exists(images[Images.SkillStrengthened], similarity = 0.68)) { - true - } else { - if(strengthenedSkillRegion.exists(images[Images.SkillUnstrengthened], similarity = 0.68)) { - false - } else { - // Unstrengthened template not found, exit script - throw AutoBattle.BattleExitException( - AutoBattle.ExitReason.StrengthenedSkillEmpty(index + 1, requirement) - ) - } - } - } else { - // If requirement is 0, this skill passes automatically - true } } }