1+ using BepInEx ; // requires BepInEx.dll and BepInEx.Harmony.dll
2+ using UnityEngine ; // requires UnityEngine.dll, UnityEngine.CoreModule.dll, and UnityEngine.AssetBundleModule.dll
3+ using HarmonyLib ; // requires 0Harmony.dll
4+ using System . Collections ;
5+ using System . Reflection ;
6+ using System . Linq ;
7+ using System . Collections . Generic ;
8+ using System ;
9+ using System . Runtime . CompilerServices ;
10+ // requires Assembly-CSharp.dll
11+ // requires MMHOOK-Assembly-CSharp.dll
12+
13+ namespace CardChoiceSpawnUniqueCardPatch
14+ {
15+ [ BepInPlugin ( ModId , ModName , "0.0.0.0" ) ]
16+ [ BepInProcess ( "Rounds.exe" ) ]
17+ public class CardChoiceSpawnUniqueCardPatch : BaseUnityPlugin
18+ {
19+ private void Awake ( )
20+ {
21+ new Harmony ( ModId ) . PatchAll ( ) ;
22+ }
23+ private void Start ( )
24+ {
25+
26+ }
27+
28+ private const string ModId = "pykess.rounds.plugins.cardchoicespawnuniquecardpatch" ;
29+
30+ private const string ModName = "CardChoiceSpawnUniqueCardPatch" ;
31+ }
32+ // stolen from PCE
33+ public sealed class Cards
34+ {
35+ // singleton design
36+ public static readonly Cards instance = new Cards ( ) ;
37+ private Cards ( )
38+ {
39+ Cards instance = this ;
40+ }
41+ public bool CardIsUniqueFromCards ( CardInfo card , CardInfo [ ] cards )
42+ {
43+ bool unique = true ;
44+
45+ foreach ( CardInfo otherCard in cards )
46+ {
47+ if ( card . cardName == otherCard . cardName )
48+ {
49+ unique = false ;
50+ }
51+ }
52+
53+ return unique ;
54+ }
55+
56+ public bool CardDoesNotConflictWithCards ( CardInfo card , CardInfo [ ] cards )
57+ {
58+ bool conflicts = false ;
59+
60+ foreach ( CardInfo otherCard in cards )
61+ {
62+ if ( card . categories . Intersect ( otherCard . blacklistedCategories ) . Any ( ) )
63+ {
64+ conflicts = true ;
65+ }
66+ }
67+
68+ return conflicts ;
69+ }
70+
71+ public bool PlayerIsAllowedCard ( Player player , CardInfo card )
72+ {
73+ bool blacklisted = false ;
74+
75+ foreach ( CardInfo currentCard in player . data . currentCards )
76+ {
77+ if ( card . categories . Intersect ( currentCard . blacklistedCategories ) . Any ( ) )
78+ {
79+ blacklisted = true ;
80+ }
81+ }
82+
83+ return ! blacklisted && ( card . allowMultiple || ! player . data . currentCards . Where ( cardinfo => cardinfo . name == card . name ) . Any ( ) ) ;
84+
85+ }
86+ public CardInfo GetRandomCardWithCondition ( CardChoice cardChoice , Player player , Func < CardInfo , Player , bool > condition , int maxattempts = 1000 )
87+ {
88+
89+ CardInfo card = ( ( GameObject ) typeof ( CardChoice ) . InvokeMember ( "GetRanomCard" ,
90+ BindingFlags . Instance | BindingFlags . InvokeMethod |
91+ BindingFlags . NonPublic , null , cardChoice , new object [ ] { } ) ) . GetComponent < CardInfo > ( ) ;
92+
93+ int i = 0 ;
94+
95+ // draw a random card until it's an uncommon or the maximum number of attempts was reached
96+ while ( ! condition ( card , player ) && i < maxattempts )
97+ {
98+ card = ( ( GameObject ) typeof ( CardChoice ) . InvokeMember ( "GetRanomCard" ,
99+ BindingFlags . Instance | BindingFlags . InvokeMethod |
100+ BindingFlags . NonPublic , null , cardChoice , new object [ ] { } ) ) . GetComponent < CardInfo > ( ) ;
101+ i ++ ;
102+ }
103+
104+ if ( ! condition ( card , player ) )
105+ {
106+ return null ;
107+ }
108+ else
109+ {
110+ return card ;
111+ }
112+
113+ }
114+
115+ public int GetCardID ( CardInfo card )
116+ {
117+ return Array . IndexOf ( global ::CardChoice . instance . cards , card ) ;
118+ }
119+ public CardInfo GetCardWithID ( int cardID )
120+ {
121+ return global ::CardChoice . instance . cards [ cardID ] ;
122+ }
123+ }
124+
125+ [ Serializable ]
126+ [ HarmonyPatch ( typeof ( CardChoice ) , "SpawnUniqueCard" ) ]
127+ class CardChoicePatchSpawnUniqueCard
128+ {
129+ private static bool Prefix ( ref GameObject __result , CardChoice __instance , Vector3 pos , Quaternion rot )
130+ {
131+ Player player ;
132+ if ( ( PickerType ) Traverse . Create ( __instance ) . Field ( "pickerType" ) . GetValue ( ) == PickerType . Team )
133+ {
134+ player = PlayerManager . instance . GetPlayersInTeam ( __instance . pickrID ) [ 0 ] ;
135+ }
136+ else
137+ {
138+ player = PlayerManager . instance . players [ __instance . pickrID ] ;
139+ }
140+
141+ CardInfo validCard = Cards . instance . GetRandomCardWithCondition ( __instance , player , CardChoicePatchSpawnUniqueCard . GetCondition ( __instance ) ) ;
142+
143+ if ( validCard != null )
144+ {
145+ GameObject gameObject = ( GameObject ) typeof ( CardChoice ) . InvokeMember ( "Spawn" ,
146+ BindingFlags . Instance | BindingFlags . InvokeMethod |
147+ BindingFlags . NonPublic , null , __instance , new object [ ] { validCard . gameObject , pos , rot } ) ;
148+ gameObject . GetComponent < CardInfo > ( ) . sourceCard = validCard . GetComponent < CardInfo > ( ) ;
149+ gameObject . GetComponentInChildren < DamagableEvent > ( ) . GetComponent < Collider2D > ( ) . enabled = false ;
150+
151+ __result = gameObject ;
152+ }
153+ else
154+ {
155+ // there are no valid cards left - this is an extremely unlikely scenario, only achievable if most of the cards have been disabled
156+
157+ // if any valid cards were found, just return one of those, even though its a duplicate
158+
159+
160+ List < GameObject > spawnedCards = ( List < GameObject > ) Traverse . Create ( __instance ) . Field ( "spawnedCards" ) . GetValue ( ) ;
161+
162+ GameObject card ;
163+
164+ if ( spawnedCards . Count > 0 )
165+ {
166+ CardInfo cardInfo = Cards . instance . GetCardWithID ( Cards . instance . GetCardID ( spawnedCards [ 0 ] . GetComponent < CardInfo > ( ) . sourceCard ) ) ;
167+ card = cardInfo . gameObject ;
168+ }
169+
170+ // if no valid cards could be found, then just get any card at all because that's better than crashing the game
171+ else
172+ {
173+ card = ( ( GameObject ) typeof ( CardChoice ) . InvokeMember ( "GetRanomCard" ,
174+ BindingFlags . Instance | BindingFlags . InvokeMethod |
175+ BindingFlags . NonPublic , null , __instance , new object [ ] { } ) ) ;
176+ }
177+ GameObject gameObject = ( GameObject ) typeof ( CardChoice ) . InvokeMember ( "Spawn" ,
178+ BindingFlags . Instance | BindingFlags . InvokeMethod |
179+ BindingFlags . NonPublic , null , __instance , new object [ ] { card . gameObject , pos , rot } ) ;
180+ gameObject . GetComponent < CardInfo > ( ) . sourceCard = card . GetComponent < CardInfo > ( ) ;
181+ gameObject . GetComponentInChildren < DamagableEvent > ( ) . GetComponent < Collider2D > ( ) . enabled = false ;
182+
183+
184+ __result = gameObject ;
185+
186+ }
187+
188+ return false ; // do not run the original method (BAD IDEA)
189+ }
190+ private static Func < CardInfo , Player , bool > GetCondition ( CardChoice instance )
191+ {
192+ return ( card , player ) => ( CardChoicePatchSpawnUniqueCard . BaseCondition ( instance ) ( card , player ) && CardChoicePatchSpawnUniqueCard . CorrectedCondition ( instance ) ( card , player ) ) ;
193+ }
194+ private static Func < CardInfo , Player , bool > CorrectedCondition ( CardChoice instance )
195+ {
196+ return ( card , player ) => ( Cards . instance . PlayerIsAllowedCard ( player , card ) ) ;
197+ }
198+ private static Func < CardInfo , Player , bool > BaseCondition ( CardChoice instance )
199+ {
200+ return ( card , player ) =>
201+ {
202+ List < GameObject > spawnedCards = ( List < GameObject > ) Traverse . Create ( instance ) . Field ( "spawnedCards" ) . GetValue ( ) ;
203+ for ( int i = 0 ; i < spawnedCards . Count ; i ++ )
204+ {
205+ bool flag = spawnedCards [ i ] . GetComponent < CardInfo > ( ) . cardName == card . cardName ;
206+ if ( instance . pickrID != - 1 )
207+ {
208+ Holdable holdable = player . data . GetComponent < Holding > ( ) . holdable ;
209+ if ( holdable )
210+ {
211+ Gun component2 = holdable . GetComponent < Gun > ( ) ;
212+ Gun component3 = card . GetComponent < Gun > ( ) ;
213+ if ( component3 && component2 && component3 . lockGunToDefault && component2 . lockGunToDefault )
214+ {
215+ flag = true ;
216+ }
217+ }
218+ for ( int j = 0 ; j < player . data . currentCards . Count ; j ++ )
219+ {
220+ CardInfo component4 = player . data . currentCards [ j ] . GetComponent < CardInfo > ( ) ;
221+ for ( int k = 0 ; k < component4 . blacklistedCategories . Length ; k ++ )
222+ {
223+ for ( int l = 0 ; l < card . categories . Length ; l ++ )
224+ {
225+ if ( card . categories [ l ] == component4 . blacklistedCategories [ k ] )
226+ {
227+ flag = true ;
228+ }
229+ }
230+ }
231+ if ( ! component4 . allowMultiple && card . cardName == component4 . cardName )
232+ {
233+ flag = true ;
234+ }
235+ }
236+ }
237+ if ( flag )
238+ {
239+ return false ;
240+ }
241+ }
242+ return true ;
243+ } ;
244+ }
245+ }
246+ }
0 commit comments