1+ using System ;
2+ using System . Collections ;
3+ using System . Collections . Generic ;
4+ using System . Linq ;
5+ using System . Text ;
6+ using BepInEx ;
7+ using BepInEx . Unity . IL2CPP . Utils . Collections ;
8+ using HarmonyLib ;
9+ using Il2CppInterop . Runtime . InteropTypes . Fields ;
10+ using ModExplorer . Data ;
11+ using Reactor . Utilities ;
12+ using Reactor . Utilities . Attributes ;
13+ using TMPro ;
14+ using UnityEngine ;
15+ using UnityEngine . Events ;
16+ using UnityEngine . UI ;
17+
18+ namespace ModExplorer . Components ;
19+
20+ [ RegisterInIl2Cpp ]
21+ public class ModExplorerComponent ( IntPtr cppPtr ) : MonoBehaviour ( cppPtr )
22+ {
23+ public static ModExplorerComponent Instance { get ; private set ; }
24+
25+ public List < ModListElement > elements ;
26+ public Il2CppReferenceField < GameObject > mainContainer ;
27+ public Il2CppReferenceField < ScrollRect > scrollRect ;
28+ public Il2CppReferenceField < TMP_InputField > searchInputField ;
29+ public Il2CppReferenceField < RectTransform > modListContainer ;
30+ public Il2CppReferenceField < TextMeshProUGUI > infoMainText ;
31+ public Il2CppReferenceField < TextMeshProUGUI > infoBottomText ;
32+ public Il2CppReferenceField < TextMeshProUGUI > infoLinkText ;
33+ public Il2CppReferenceField < Button > closeButton ;
34+ public Il2CppReferenceField < Button > folderButton ;
35+
36+ public static ModExplorerComponent Create ( )
37+ {
38+ var explorer = Instantiate ( Assets . MenuPrefab . Get ( ) ) . GetComponent < ModExplorerComponent > ( ) ;
39+ return explorer ;
40+ }
41+
42+ public void Awake ( )
43+ {
44+ if ( Instance )
45+ {
46+ Logger < ModExplorerPlugin > . Warning ( $ "An instance of the explorer already exists. Destroying the new one.") ;
47+ Destroy ( gameObject ) ;
48+ return ;
49+ }
50+ Instance = this ;
51+
52+ var transition = mainContainer . Value . AddComponent < TransitionOpen > ( ) ;
53+ transition . OnClose . AddListener ( ( UnityAction ) ( ( ) => Destroy ( gameObject ) ) ) ;
54+
55+ closeButton . Value . onClick = new Button . ButtonClickedEvent ( ) ;
56+ closeButton . Value . onClick . AddListener ( ( UnityAction ) ( ( ) =>
57+ {
58+ transition . Close ( ) ;
59+ closeButton . Value . gameObject . SetActive ( false ) ;
60+ folderButton . Value . gameObject . SetActive ( false ) ;
61+ } ) ) ;
62+
63+ folderButton . Value . onClick = new Button . ButtonClickedEvent ( ) ;
64+ folderButton . Value . onClick . AddListener ( ( UnityAction ) ( ( ) =>
65+ {
66+ Application . OpenURL ( "file:///" + Paths . PluginPath . Replace ( "\\ " , "/" ) ) ;
67+ } ) ) ;
68+
69+ searchInputField . Value . onValueChanged = new TMP_InputField . OnChangeEvent ( ) ;
70+ searchInputField . Value . onValueChanged . AddListener ( ( UnityAction < string > ) SetSearchResults ) ;
71+
72+ infoMainText . Value . text = string . Empty ;
73+ infoBottomText . Value . text = string . Empty ;
74+ infoLinkText . Value . text = string . Empty ;
75+
76+ modListContainer . Value . DestroyChildren ( ) ;
77+ elements = new ( ) ;
78+ foreach ( var data in ModDataFinder . Mods )
79+ {
80+ var element = ModListElement . Create ( modListContainer ) ;
81+ var btn = element . GetComponent < Button > ( ) ;
82+ element . SetFromData ( data ) ;
83+ btn . onClick = new Button . ButtonClickedEvent ( ) ;
84+ btn . onClick . AddListener ( ( UnityAction ) ( ( ) => OpenInfoPageFor ( element ) ) ) ;
85+ elements . Add ( element ) ;
86+ }
87+ }
88+
89+ public void OpenInfoPageFor ( ModListElement element )
90+ {
91+ // do are you have stupid
92+ var colors = element . button . Value . colors ;
93+ elements . Do ( x =>
94+ {
95+ x . button . Value . colors = colors with { normalColor = Color . white } ;
96+ } ) ;
97+ element . button . Value . colors = colors with { normalColor = Palette . AcceptedGreen } ;
98+
99+ var data = element . ModData ;
100+ StringBuilder mainBuilder = new ( ) ;
101+ mainBuilder . AppendLine (
102+ $ "<size=150>{ data . Name } </size> <color=grey><font=\" LiberationSans SDF\" >v{ data . Version } </font></color>") ;
103+ if ( data . Authors . Length > 0 )
104+ mainBuilder . AppendLine ( $ "<font=\" LiberationSans SDF\" >by { string . Join ( ", " , data . Authors ) } </font>\n ") ;
105+ if ( ! data . Description . IsNullOrWhiteSpace ( ) )
106+ mainBuilder . AppendLine ( $ "<font=\" LiberationSans SDF\" >{ data . Description } </font>") ;
107+ infoMainText . Value . text = mainBuilder . ToString ( ) ;
108+
109+ StringBuilder bottomBuilder = new ( ) ;
110+ bottomBuilder . AppendLine ( $ "Mod ID: { data . ID } ") ;
111+ if ( ! data . License . IsNullOrWhiteSpace ( ) )
112+ bottomBuilder . AppendLine ( $ "License: { data . License } ") ;
113+ if ( data . Dependencies . Length > 0 )
114+ bottomBuilder . AppendLine ( $ "Dependencies: { string . Join ( ", " , data . Dependencies ) } ") ;
115+ infoBottomText . Value . text = bottomBuilder . ToString ( ) ;
116+
117+ StringBuilder linkBuilder = new ( ) ;
118+ foreach ( var ( type , link ) in data . Links )
119+ {
120+ linkBuilder . AppendLine ( $ "{ type } ") ;
121+ }
122+ infoLinkText . Value . text = linkBuilder . ToString ( ) ;
123+ }
124+
125+ public void SetSearchResults ( string search )
126+ {
127+ if ( string . IsNullOrEmpty ( search ) )
128+ {
129+ elements . Do ( x => x . gameObject . SetActive ( true ) ) ;
130+ return ;
131+ }
132+
133+ var valid = elements
134+ . Where ( x => x . ModData . Name . Contains ( search , StringComparison . InvariantCultureIgnoreCase ) ||
135+ x . ModData . Tags . Any ( t => t . Contains ( search , StringComparison . InvariantCultureIgnoreCase ) ) )
136+ . OrderBy ( x => x . ModData . Name )
137+ . ThenBy ( x => x . ModData . Name . Equals ( search , StringComparison . OrdinalIgnoreCase ) )
138+ . ThenBy ( x => x . ModData . Name . Contains ( search , StringComparison . InvariantCultureIgnoreCase ) )
139+ . ToList ( ) ;
140+
141+ elements . Do ( x => x . gameObject . SetActive ( false ) ) ;
142+ valid . Do ( x => x . gameObject . SetActive ( true ) ) ;
143+ }
144+ }
0 commit comments