1010import net .minecraftforge .fml .ModLoader ;
1111
1212import com .google .common .collect .ImmutableList ;
13+ import it .unimi .dsi .fastutil .ints .IntArrayList ;
14+ import it .unimi .dsi .fastutil .ints .IntList ;
1315import it .unimi .dsi .fastutil .objects .Object2IntMap ;
1416import org .jetbrains .annotations .ApiStatus ;
1517import org .jetbrains .annotations .NotNull ;
1618import org .jetbrains .annotations .Nullable ;
1719import org .jetbrains .annotations .Unmodifiable ;
1820
21+ import java .util .ArrayList ;
1922import java .util .Collections ;
2023import java .util .List ;
2124
@@ -115,7 +118,7 @@ public String toString() {
115118 /**
116119 * Chanced Output Logic where only the first ingredient succeeding its roll will be produced
117120 */
118- public static final ChanceLogic XOR = new ChanceLogic ("xor " ) {
121+ public static final ChanceLogic FIRST = new ChanceLogic ("first " ) {
119122
120123 @ Override
121124 public @ Unmodifiable List <@ NotNull Content > roll (@ NotNull @ Unmodifiable List <@ NotNull Content > chancedEntries ,
@@ -141,6 +144,88 @@ public String toString() {
141144 return builder .build ();
142145 }
143146
147+ @ Override
148+ public @ NotNull Component getTranslation () {
149+ return Component .translatable ("gtceu.chance_logic.first" );
150+ }
151+
152+ @ Override
153+ public String toString () {
154+ return "ChanceLogic{FIRST}" ;
155+ }
156+ };
157+
158+ /**
159+ * Chanced Output Logic where only one of the ingredients will be output, in a manner weighted to the input chances
160+ */
161+ public static final ChanceLogic XOR = new ChanceLogic ("xor" ) {
162+
163+ @ Override
164+ public @ Unmodifiable List <@ NotNull Content > roll (@ NotNull @ Unmodifiable List <@ NotNull Content > chancedEntries ,
165+ @ NotNull ChanceBoostFunction boostFunction ,
166+ int recipeTier , int chanceTier ,
167+ @ Nullable Object2IntMap <?> cache , int times ) {
168+ // Have to set up a system where all chances are set to be out of 10000
169+ IntList chancesOutOfTenThousand = new IntArrayList ();
170+
171+ for (Content orig : chancedEntries ) {
172+ if (orig .maxChance == getMaxChancedValue ()) {
173+ chancesOutOfTenThousand .add (orig .chance );
174+ } else {
175+ chancesOutOfTenThousand .add ((int ) ((orig .chance / (float ) orig .maxChance ) * getMaxChancedValue ()));
176+ }
177+ }
178+
179+ int chanceTotal = 0 ;
180+ for (int chance : chancesOutOfTenThousand ) {
181+ chanceTotal += chance ;
182+ }
183+
184+ // Here, if the newly calculated chances don't add up to 10000, they're renormalized
185+ if (chanceTotal != getMaxChancedValue ()) {
186+ int chanceTotalDecremented = getMaxChancedValue ();
187+ for (int i = 0 ; i < chancesOutOfTenThousand .size (); i ++) {
188+ int newChance = (int ) (chancesOutOfTenThousand .getInt (i ) *
189+ ((float ) getMaxChancedValue () / (float ) chanceTotal ));
190+ // last chance ends up being set to the remainder in case things don't line up
191+ if (i == chancesOutOfTenThousand .size () - 1 ) {
192+ chancesOutOfTenThousand .set (i , chanceTotalDecremented );
193+ } else {
194+ chancesOutOfTenThousand .set (i , newChance );
195+ }
196+ chanceTotalDecremented -= newChance ;
197+ }
198+ }
199+
200+ // Finally, generate a new Content list with the changes
201+ List <Content > normalizedEntries = new ArrayList <>();
202+ for (int i = 0 ; i < chancesOutOfTenThousand .size (); i ++) {
203+ normalizedEntries .add (new Content (chancedEntries .get (i ).content , chancesOutOfTenThousand .getInt (i ),
204+ getMaxChancedValue (), chancedEntries .get (i ).tierChanceBoost ));
205+ }
206+
207+ // Use the new, normalized list for the logic
208+ ImmutableList .Builder <Content > builder = ImmutableList .builder ();
209+ for (int i = 0 ; i < times ; ++i ) {
210+ Content selected = null ;
211+ int maxChance = getMaxChancedValue ();
212+ for (Content entry : normalizedEntries ) {
213+ int newChance = getChance (entry , boostFunction , recipeTier , chanceTier );
214+ int cached = getCachedChance (entry , cache );
215+ int chance = newChance + cached ;
216+ if (passesChance (chance , maxChance )) {
217+ selected = entry ;
218+ newChance -= maxChance ;
219+ }
220+ updateCachedChance (entry .content , cache , newChance / 2 + cached );
221+ if (selected != null ) break ;
222+ maxChance -= newChance ;
223+ }
224+ if (selected != null ) builder .add (selected );
225+ }
226+ return builder .build ();
227+ }
228+
144229 @ Override
145230 public @ NotNull Component getTranslation () {
146231 return Component .translatable ("gtceu.chance_logic.xor" );
0 commit comments