@@ -21,6 +21,10 @@ public final class FitsInRemover {
2121 // teal blue Apotheosis uses for the Fits In header and category bullets
2222 private static final int FITS_IN_COLOR = 720650 ;
2323
24+ // A category in a Fits In bullet: its rendered name, its Apoth id from the translation key (null
25+ // when the key shape is unexpected), and the original component so a filtered bullet stays translatable.
26+ private record Category (String name , String id , Component component ) {}
27+
2428 // Output order for ultra mode's category line: weapons, armor, mining tools, then Other. Within a
2529 // group, input order is kept.
2630 private static final LinkedHashMap <String , Set <String >> CATEGORY_GROUPS = new LinkedHashMap <>();
@@ -82,7 +86,7 @@ public static void apply(List<Component> tooltip) {
8286
8387 if (isFits ) {
8488 if (ultra ) {
85- List <String > names = sortByGroup (filterHidden (extractCategoryNames (tooltip , i + 1 , afterBullets )));
89+ List <String > names = sortByGroup (namesOf ( filterHidden (extractCategoriesRange (tooltip , i + 1 , afterBullets ) )));
8690 removeRange (tooltip , i , afterBullets );
8791 if (!names .isEmpty ()) {
8892 tooltip .add (i , joinedCategoryBullet (String .join (", " , names )));
@@ -96,20 +100,19 @@ public static void apply(List<Component> tooltip) {
96100 i ++;
97101 int bulletIndex = i ;
98102 while (bulletIndex < afterBullets && TooltipMatcher .isBulletPrefix (tooltip .get (bulletIndex ))) {
99- String inner = extractBulletText (tooltip .get (bulletIndex ));
100- if (inner == null || inner .isEmpty ()) {
103+ List < Category > cats = extractCategories (tooltip .get (bulletIndex ));
104+ if (cats .isEmpty ()) {
101105 bulletIndex ++;
102106 continue ;
103107 }
104- List <String > names = splitCategories (inner );
105- List <String > kept = filterHidden (names );
106- if (kept .size () == names .size ()) {
108+ List <Category > kept = filterHidden (cats );
109+ if (kept .size () == cats .size ()) {
107110 bulletIndex ++;
108111 } else if (kept .isEmpty ()) {
109112 tooltip .remove (bulletIndex );
110113 afterBullets --;
111114 } else {
112- tooltip .set (bulletIndex , categoryBullet ( String . join ( ", " , kept ) ));
115+ tooltip .set (bulletIndex , rebuildCategoryBullet ( kept ));
113116 bulletIndex ++;
114117 }
115118 }
@@ -163,8 +166,7 @@ public static void apply(List<Component> tooltip) {
163166 // full mode keeps Apoth's layout, so walk each Fits In block and drop hidden categories. A bullet
164167 // with nothing left is removed, and so is a header with no bullets under it.
165168 private static void filterFullCategories (List <Component > tooltip ) {
166- List <? extends String > hiddenCategories = Config .HIDDEN_GEM_CATEGORIES .get ();
167- if (hiddenCategories == null || hiddenCategories .isEmpty ()) {
169+ if (hiddenSet ().isEmpty ()) {
168170 return ;
169171 }
170172
@@ -186,20 +188,19 @@ private static void filterFullCategories(List<Component> tooltip) {
186188 }
187189
188190 while (bulletIndex < afterBullets ) {
189- String inner = extractBulletText (tooltip .get (bulletIndex ));
190- if (inner == null || inner .isEmpty ()) {
191+ List < Category > cats = extractCategories (tooltip .get (bulletIndex ));
192+ if (cats .isEmpty ()) {
191193 bulletIndex ++;
192194 continue ;
193195 }
194- List <String > names = splitCategories (inner );
195- List <String > kept = filterHidden (names );
196- if (kept .size () == names .size ()) {
196+ List <Category > kept = filterHidden (cats );
197+ if (kept .size () == cats .size ()) {
197198 bulletIndex ++;
198199 } else if (kept .isEmpty ()) {
199200 tooltip .remove (bulletIndex );
200201 afterBullets --;
201202 } else {
202- tooltip .set (bulletIndex , categoryBullet ( String . join ( ", " , kept ) ));
203+ tooltip .set (bulletIndex , rebuildCategoryBullet ( kept ));
203204 bulletIndex ++;
204205 }
205206 }
@@ -233,8 +234,7 @@ private static void filterSocketInByCategory(List<Component> tooltip) {
233234 int bulletIndex = i + 1 ;
234235 int kept = 0 ;
235236 while (bulletIndex < tooltip .size () && TooltipMatcher .isBulletPrefix (tooltip .get (bulletIndex ))) {
236- String id = socketBonusCategoryId (tooltip .get (bulletIndex ));
237- if (id != null && hidden .contains (id )) {
237+ if (socketBonusHidden (tooltip .get (bulletIndex ), hidden )) {
238238 tooltip .remove (bulletIndex );
239239 } else {
240240 kept ++;
@@ -251,35 +251,35 @@ private static void filterSocketInByCategory(List<Component> tooltip) {
251251 }
252252 }
253253
254- // Returns the gem_class id from a Pattern B socket bonus bullet (dot_prefix to "%s: %s" to arg[0]
255- // keyed gem_class.<id>), or null if the bullet is not that shape .
256- private static String socketBonusCategoryId (Component bullet ) {
254+ // Returns true when a Pattern B socket bonus bullet (dot_prefix to "%s: %s" to arg[0] keyed
255+ // gem_class.<id>) belongs to a hidden category, matching either the gem_class id or its rendered name .
256+ private static boolean socketBonusHidden (Component bullet , Set < String > hidden ) {
257257 TranslatableContents outer = TooltipMatcher .translatable (bullet );
258258 if (outer == null || outer .getArgs ().length == 0 ) {
259- return null ;
259+ return false ;
260260 }
261261 if (!(outer .getArgs ()[0 ] instanceof Component join )) {
262- return null ;
262+ return false ;
263263 }
264264
265265 TranslatableContents joinTc = TooltipMatcher .translatable (join );
266266 if (joinTc == null || joinTc .getArgs ().length == 0 ) {
267- return null ;
267+ return false ;
268268 }
269269 if (!(joinTc .getArgs ()[0 ] instanceof Component gemClass )) {
270- return null ;
270+ return false ;
271271 }
272272
273273 TranslatableContents gcTc = TooltipMatcher .translatable (gemClass );
274274 if (gcTc == null ) {
275- return null ;
275+ return false ;
276276 }
277277 String key = gcTc .getKey ();
278278 int at = key .lastIndexOf ("gem_class." );
279- if (at < 0 ) {
280- return null ;
279+ if (at >= 0 && hidden . contains ( key . substring ( at + "gem_class." . length ())) ) {
280+ return true ;
281281 }
282- return key . substring ( at + "gem_class." . length ());
282+ return hidden . contains ( gemClass . getString ());
283283 }
284284
285285 // Returns the hidden_gem_categories config as a case insensitive set, empty if unset.
@@ -350,16 +350,65 @@ private static List<String> sortByGroup(List<String> categories) {
350350 return sorted ;
351351 }
352352
353- private static List <String > extractCategoryNames (List <Component > tooltip , int from , int toExclusive ) {
354- List <String > names = new ArrayList <>();
355- for (int k = from ; k < toExclusive ; k ++) {
356- String inner = extractBulletText (tooltip .get (k ));
357- if (inner == null || inner .isEmpty ()) {
358- continue ;
353+ // Pulls each category out of a Fits In dot_prefix bullet as a name and Apoth id. Apoth holds the
354+ // categories as the args of an inner translatable, one component per category keyed
355+ // text.apotheosis.category.<id>.plural, falling back to splitting the rendered text when the shape
356+ // is unexpected such as the "Anything" line or a plain literal.
357+ private static List <Category > extractCategories (Component bullet ) {
358+ TranslatableContents outer = TooltipMatcher .translatable (bullet );
359+ if (outer == null || outer .getArgs ().length == 0 ) {
360+ return Collections .emptyList ();
361+ }
362+ if (!(outer .getArgs ()[0 ] instanceof Component inner )) {
363+ return Collections .emptyList ();
364+ }
365+
366+ TranslatableContents innerTc = TooltipMatcher .translatable (inner );
367+ if (innerTc != null && innerTc .getArgs ().length > 0 ) {
368+ List <Category > out = new ArrayList <>();
369+ for (Object arg : innerTc .getArgs ()) {
370+ if (arg instanceof Component cat ) {
371+ out .add (new Category (cat .getString (), categoryId (cat ), cat ));
372+ } else {
373+ String name = String .valueOf (arg );
374+ out .add (new Category (name , null , Component .literal (name )));
375+ }
359376 }
360- names . addAll ( splitCategories ( inner )) ;
377+ return out ;
361378 }
362- return names ;
379+
380+ List <Category > out = new ArrayList <>();
381+ for (String name : splitCategories (inner .getString ())) {
382+ out .add (new Category (name , null , Component .literal (name )));
383+ }
384+ return out ;
385+ }
386+
387+ private static List <Category > extractCategoriesRange (List <Component > tooltip , int from , int toExclusive ) {
388+ List <Category > out = new ArrayList <>();
389+ for (int k = from ; k < toExclusive ; k ++) {
390+ out .addAll (extractCategories (tooltip .get (k )));
391+ }
392+ return out ;
393+ }
394+
395+ // Returns the Apoth category id from a category name component's key (text.apotheosis.category.<id>.plural),
396+ // so config values like "chestplate" match regardless of the rendered "Chestplates" text or locale.
397+ private static String categoryId (Component cat ) {
398+ TranslatableContents tc = TooltipMatcher .translatable (cat );
399+ if (tc == null ) {
400+ return null ;
401+ }
402+ String key = tc .getKey ();
403+ int at = key .lastIndexOf ("category." );
404+ if (at < 0 ) {
405+ return null ;
406+ }
407+ String id = key .substring (at + "category." .length ());
408+ if (id .endsWith (".plural" )) {
409+ id = id .substring (0 , id .length () - ".plural" .length ());
410+ }
411+ return id ;
363412 }
364413
365414 // Splits "Swords, Bows, ..." into trimmed, non empty names.
@@ -374,27 +423,46 @@ private static List<String> splitCategories(String inner) {
374423 return names ;
375424 }
376425
377- // Drops any names listed in hidden_gem_categories, case insensitive.
378- private static List <String > filterHidden (List <String > categories ) {
379- List <? extends String > hidden = Config .HIDDEN_GEM_CATEGORIES .get ();
380- if (hidden == null || hidden .isEmpty ()) {
426+ // Keeps the categories the user has not hidden, matching the Apoth id from the translation key (so
427+ // config can use ids like "chestplate") and the rendered name (so "Chestplates" works too).
428+ private static List <Category > filterHidden (List <Category > categories ) {
429+ Set <String > hidden = hiddenSet ();
430+ if (hidden .isEmpty ()) {
381431 return categories ;
382432 }
383- Set <String > hiddenSet = new TreeSet <>(String .CASE_INSENSITIVE_ORDER );
384- hiddenSet .addAll (hidden );
385- List <String > filtered = new ArrayList <>(categories .size ());
386- for (String name : categories ) {
387- if (!hiddenSet .contains (name )) {
388- filtered .add (name );
433+ List <Category > kept = new ArrayList <>(categories .size ());
434+ for (Category cat : categories ) {
435+ boolean hide = (cat .id () != null && hidden .contains (cat .id ())) || hidden .contains (cat .name ());
436+ if (!hide ) {
437+ kept .add (cat );
389438 }
390439 }
391- return filtered ;
440+ return kept ;
392441 }
393442
394- // Rebuilds a Fits In category bullet with the teal styling after dropping hidden categories, used
395- // by both compact and full mode.
396- private static Component categoryBullet (String text ) {
397- return Component .translatable ("text.apotheosis.dot_prefix" , Component .literal (text ))
443+ private static List <String > namesOf (List <Category > categories ) {
444+ List <String > names = new ArrayList <>(categories .size ());
445+ for (Category cat : categories ) {
446+ names .add (cat .name ());
447+ }
448+ return names ;
449+ }
450+
451+ // Rebuilds a Fits In category bullet from the kept categories after dropping hidden ones. Apoth
452+ // joins the categories through a "%s, %s" translatable, so reconstruct that with one %s per kept
453+ // category and the original category components as args, keeping them translatable.
454+ private static Component rebuildCategoryBullet (List <Category > kept ) {
455+ StringBuilder fmt = new StringBuilder ();
456+ Object [] args = new Object [kept .size ()];
457+ for (int i = 0 ; i < kept .size (); i ++) {
458+ fmt .append ("%s" );
459+ if (i < kept .size () - 1 ) {
460+ fmt .append (", " );
461+ }
462+ args [i ] = kept .get (i ).component ();
463+ }
464+ Component join = Component .translatable (fmt .toString (), args );
465+ return Component .translatable ("text.apotheosis.dot_prefix" , join )
398466 .withStyle (Style .EMPTY .withColor (FITS_IN_COLOR ));
399467 }
400468
0 commit comments