Skip to content

Commit e88c58b

Browse files
committed
Fix macerators giving wrong number of overclock bonuses
Add JUnit tests to verify chanced output scaling logic for both standard machines and the special macerator logic that makes ULV and LV recipes not grant bonuses until after MV. Fixes #24
1 parent 19b5a76 commit e88c58b

2 files changed

Lines changed: 212 additions & 3 deletions

File tree

src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityMacerator.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,24 @@ public MetaTileEntityMacerator(ResourceLocation metaTileEntityId, RecipeMap<?> r
2626
@Override
2727
protected RecipeLogicEnergy createWorkable(RecipeMap<?> recipeMap) {
2828
final RecipeLogicEnergy result = new RecipeLogicEnergy(this, recipeMap, () -> energyContainer) {
29+
/*
30+
* Lower tier macerators are unable to produce byproducts. MV macerators used to have two slots, so the
31+
* reported tier for computing overclocking bonuses was adjusted so that no bonuses are granted until
32+
* after MV. Macerators now only gain slots at HV, but the prior convention is retained.
33+
*/
2934
@Override
3035
protected int getMachineTierForRecipe(Recipe recipe) {
36+
// if the recipe base tier is above MV, use default logic
37+
int baseTier = recipe.getBaseTier();
38+
if(baseTier > GTValues.MV)
39+
return super.getMachineTierForRecipe(recipe);
40+
41+
// otherwise, reduce the number of bonuses applied as though the recipe is MV base
3142
int tier = GTUtility.getTierByVoltage(getMaxVoltage());
3243
if (tier > GTValues.MV) {
33-
return tier - GTValues.MV;
44+
return tier - (GTValues.MV - baseTier);
3445
}
35-
return 0;
46+
return baseTier;
3647
}
3748
};
3849
result.enableOverclockVoltage();

src/test/java/gregtech/api/capability/impl/AbstractRecipeLogicTest.java

Lines changed: 199 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@
66
import gregtech.api.recipes.builders.*;
77
import gregtech.api.render.*;
88
import gregtech.api.util.world.DummyWorld;
9+
import gregtech.common.metatileentities.electric.MetaTileEntityMacerator;
910
import net.minecraft.init.*;
1011
import net.minecraft.item.*;
11-
import net.minecraft.tileentity.TileEntity;
1212
import net.minecraft.util.*;
1313
import net.minecraft.world.World;
1414
import org.junit.jupiter.api.*;
15+
import org.junit.jupiter.params.*;
16+
import org.junit.jupiter.params.provider.*;
17+
18+
import java.lang.reflect.InvocationTargetException;
19+
import java.util.stream.Stream;
1520

1621
import static org.junit.jupiter.api.Assertions.*;
22+
import static gregtech.api.GTValues.*;
1723

1824
public class AbstractRecipeLogicTest {
1925

@@ -198,4 +204,196 @@ protected long getMaxVoltage() {
198204
assertEquals(GTValues.V[GTValues.ZPM] / 2, oc[0]);
199205
assertEquals(1, oc[1]);
200206
}
207+
208+
/** Base chance for standard_chanced_outputs test */
209+
private static final int BASE_CHANCE = 7500;
210+
/** Bonus chance per tier for standard_chanced_outputs test */
211+
private static final int BONUS_CHANCE = 500;
212+
213+
/** Argument stream for the standard_chanced_outputs test */
214+
static Stream<Arguments> standardChancedArgs() {
215+
return Stream.of(
216+
Arguments.of( LV, BASE_CHANCE + BONUS_CHANCE * 0),
217+
Arguments.of( MV, BASE_CHANCE + BONUS_CHANCE * 1), // starts to gain bonuses here
218+
Arguments.of( HV, BASE_CHANCE + BONUS_CHANCE * 2),
219+
Arguments.of( EV, BASE_CHANCE + BONUS_CHANCE * 3),
220+
Arguments.of( IV, BASE_CHANCE + BONUS_CHANCE * 4), // hits speed cap here
221+
Arguments.of(LuV, BASE_CHANCE + BONUS_CHANCE * 5), // hits chance cap here
222+
Arguments.of(ZPM, BASE_CHANCE + BONUS_CHANCE * 5),
223+
Arguments.of( UV, BASE_CHANCE + BONUS_CHANCE * 5));
224+
}
225+
226+
/**
227+
* Test to verify AbstractRecipeLogic handles chanced output computations appropriately.
228+
*/
229+
@ParameterizedTest
230+
@MethodSource("standardChancedArgs")
231+
public void standard_chanced_outputs(int tier, int chanceExpected) {
232+
World world = DummyWorld.INSTANCE;
233+
234+
// Create an empty recipe map to work with
235+
RecipeMap<SimpleRecipeBuilder> map = new RecipeMap<>("fakemachine",
236+
1,
237+
1,
238+
1,
239+
3,
240+
0,
241+
0,
242+
0,
243+
0,
244+
new SimpleRecipeBuilder());
245+
246+
var machine = GregTechAPI.registerMetaTileEntity(70+tier,
247+
new SimpleMachineMetaTileEntity(new ResourceLocation(GTValues.MODID, "fakemachine." + GTValues.VN[tier].toLowerCase()),
248+
map, Textures.CHEMICAL_REACTOR_OVERLAY, tier));
249+
250+
MetaTileEntity macerTE = new MetaTileEntityHolder().setMetaTileEntity(machine);
251+
macerTE.getHolder().setWorld(world);
252+
253+
// stone makes cobblestone and has a chance of gravel
254+
map.recipeBuilder()
255+
.inputs(new ItemStack(Blocks.STONE))
256+
.outputs(new ItemStack(Blocks.COBBLESTONE))
257+
.chancedOutput(new ItemStack(Blocks.GRAVEL), BASE_CHANCE, BONUS_CHANCE)
258+
.EUt(30).duration(150) // LV base recipe
259+
.buildAndRegister();
260+
261+
AbstractRecipeLogic arl = new AbstractRecipeLogic(macerTE, map) {
262+
@Override
263+
protected long getEnergyStored() {
264+
return Long.MAX_VALUE;
265+
}
266+
267+
@Override
268+
protected long getEnergyCapacity() {
269+
return Long.MAX_VALUE;
270+
}
271+
272+
@Override
273+
protected boolean drawEnergy(int recipeEUt) {
274+
return true;
275+
}
276+
277+
@Override
278+
protected long getMaxVoltage() {
279+
return GTValues.V[tier];
280+
}
281+
};
282+
283+
arl.isOutputsFull = false;
284+
arl.invalidInputsForRecipes = false;
285+
286+
// put a stack of stone in the machine
287+
arl.getInputInventory().insertItem(0, new ItemStack(Blocks.STONE, 16), false);
288+
arl.trySearchNewRecipe();
289+
// ensure that it found a recipe and consumed inputs
290+
assertTrue(arl.isActive);
291+
292+
// get the computed chance of the gravel stack
293+
var chancedOut = arl.chancedItemOutputs.get(0);
294+
assertEquals(chanceExpected, chancedOut.getRight());
295+
}
296+
297+
/** Base chance for macerator_chanced_outputs test */
298+
private static final int MACER_BASE_CHANCE = 1400;
299+
/** Bonus chance per tier for macerator_chanced_outputs test */
300+
private static final int MACER_BONUS_CHANCE = 850;
301+
302+
/** Argument stream for the macerator_chanced_outputs test */
303+
static Stream<Arguments> macerChancedArgs() {
304+
return Stream.of(
305+
Arguments.of(1, LV, MACER_BASE_CHANCE + MACER_BONUS_CHANCE * 0),
306+
Arguments.of(1, MV, MACER_BASE_CHANCE + MACER_BONUS_CHANCE * 0),
307+
Arguments.of(3, HV, MACER_BASE_CHANCE + MACER_BONUS_CHANCE * 1), // starts to gain bonuses here
308+
Arguments.of(3, EV, MACER_BASE_CHANCE + MACER_BONUS_CHANCE * 2),
309+
Arguments.of(3, IV, MACER_BASE_CHANCE + MACER_BONUS_CHANCE * 3),
310+
Arguments.of(3, LuV, MACER_BASE_CHANCE + MACER_BONUS_CHANCE * 4),
311+
Arguments.of(3, ZPM, MACER_BASE_CHANCE + MACER_BONUS_CHANCE * 5),
312+
Arguments.of(3, UV, MACER_BASE_CHANCE + MACER_BONUS_CHANCE * 6));
313+
}
314+
315+
/**
316+
* Test to verify Macerators handle chanced output computations appropriately.
317+
*/
318+
@ParameterizedTest
319+
@MethodSource("macerChancedArgs")
320+
public void macerator_chanced_outputs(int outputs, int tier, int chanceExpected) throws
321+
NoSuchMethodException,
322+
InvocationTargetException,
323+
IllegalAccessException {
324+
World world = DummyWorld.INSTANCE;
325+
326+
// Create an empty recipe map to work with
327+
RecipeMap<SimpleRecipeBuilder> map = new RecipeMap<>("macerator_2",
328+
1,
329+
1,
330+
1,
331+
3,
332+
0,
333+
0,
334+
0,
335+
0,
336+
new SimpleRecipeBuilder());
337+
338+
var macer = GregTechAPI.registerMetaTileEntity(60+tier,
339+
new MetaTileEntityMacerator(new ResourceLocation(GTValues.MODID, "macerator." + GTValues.VN[tier].toLowerCase()),
340+
map, outputs, Textures.MACERATOR_OVERLAY, tier));
341+
342+
MetaTileEntity macerTE = new MetaTileEntityHolder().setMetaTileEntity(macer);
343+
macerTE.getHolder().setWorld(world);
344+
345+
// stone makes cobblestone and has a chance of gravel
346+
map.recipeBuilder()
347+
.inputs(new ItemStack(Blocks.STONE))
348+
.outputs(new ItemStack(Blocks.COBBLESTONE))
349+
.chancedOutput(new ItemStack(Blocks.GRAVEL), MACER_BASE_CHANCE, MACER_BONUS_CHANCE)
350+
.EUt(12).duration(200) // ULV-scaling LV-base-tier recipe, emulating crushedCentrifuged macer recipes
351+
.buildAndRegister();
352+
353+
// get at the Macerator custom workable logic
354+
var workableFn = MetaTileEntityMacerator.class.getDeclaredMethod("createWorkable", RecipeMap.class);
355+
workableFn.setAccessible(true);
356+
RecipeLogicEnergy workable = (RecipeLogicEnergy) workableFn.invoke(macer, map);
357+
358+
AbstractRecipeLogic arl = new AbstractRecipeLogic(macerTE, map) {
359+
360+
@Override
361+
protected int getMachineTierForRecipe(Recipe recipe) {
362+
return workable.getMachineTierForRecipe(recipe);
363+
}
364+
365+
@Override
366+
protected long getEnergyStored() {
367+
return Long.MAX_VALUE;
368+
}
369+
370+
@Override
371+
protected long getEnergyCapacity() {
372+
return Long.MAX_VALUE;
373+
}
374+
375+
@Override
376+
protected boolean drawEnergy(int recipeEUt) {
377+
return true;
378+
}
379+
380+
@Override
381+
protected long getMaxVoltage() {
382+
return GTValues.V[tier];
383+
}
384+
};
385+
386+
arl.isOutputsFull = false;
387+
arl.invalidInputsForRecipes = false;
388+
389+
// put a stack of stone in the machine
390+
arl.getInputInventory().insertItem(0, new ItemStack(Blocks.STONE, 16), false);
391+
arl.trySearchNewRecipe();
392+
// ensure that it found a recipe and consumed inputs
393+
assertTrue(arl.isActive);
394+
395+
// get the computed chance of the gravel stack
396+
var chancedOut = arl.chancedItemOutputs.get(0);
397+
assertEquals(chanceExpected, chancedOut.getRight());
398+
}
201399
}

0 commit comments

Comments
 (0)