How to add new Blanket Rods and explaining how they are done
The Basics
Blanket rods provide the core values for the BreederWorkableElectricMultiblockMachine class to perform breeder operations.
If you are a KubeJS dev who merely needs to know how to add new Breeder Rods, only the first two sections of this page will be useful.
StartupEvents.registry("block", event => {
event.create('uranium_blanket_rod', 'phoenix_fission:fission_blanket_rod')
.displayName('U-238 Blanket Rod')
.tier(2) // Used to decide what rod is primary.
.durationTicks(2400) // The amount of time in ticks it takes at base to consume one instance of the input fuel item.
.amountPerCycle(1) // The amount of fuel used per durationTicks.
.inputKey('gtceu:uranium_238_nugget') // Fully realized Forge ID for input fuel.
.addOutput('gtceu:plutonium_nugget', 70, 1) // Fully realized list of Forge IDs for output fuel,
.addOutput('gtceu:plutonium_241_nugget', 20, 3) // The first digit for the outputs list is the weight, aka the chance of said output.
.addOutput('gtceu:plutonium_238_nugget', 10, 4) // The second digit for the list is the instability.
// If the driver rod has a high spectrum bias, a higher instability means this fuel output will have a higher weight.
.blanketMaterial(() => GTMaterials.get('uranium_238'))
.texture('kubejs:block/fission/uranium_blanket_rod'); // Also needs a texture the same name with _active appended to the end.
});
To expound on weight/instability, I will give an example of it in practice.
The formula used to determine the effect fuel rods have on blanket outputs is
adjustedWeight= baseWeight * exp(bias * instability * k);
Where exp is the truly random roulette chance, bias is the value defined by duel rod, instability is defined by our blanket rod, and k is a flat variable defined as 0.45.
Say we have a Fuel Rod with a spectrum bias of 4, that turns our outputs into
Plutonium -> 71.26Plutonium 241 -> 21.12Plutonium 238 -> 10.75
Understanding how to actually use the blanket rods
These blocks are designed to go in instances of the BreederWorkableElectricMultiblockMachine class, while they can be placed in other multiblocks they will not do anything special.
Only the highest tier of Breeder Rod decides the timer on which inputs are consumed and outputs are given. However, all Breeder Rods will still tick for inputs/outputs.
The primary Breeder Rod also is the one chosen to show in the machine ui/jade. While you do not technically need coolers/fuel rods to run a breeder reactor, the reactor will not give power without them. So it is still recommended to encourage their use.
Anything past this point is purely Java , meant to teach those who intend to help out with the Fission system here.
You have been warned.
Understanding the classes
Now, back to talking about blanket rod blocks.
They are registered in two parts, an interface named IFissionBlanketType and a block class named FissionBlanketBlock.
package net.phoenix.core.integration.phoenix_fission.api.block;
import com.gregtechceu.gtceu.api.data.chemical.material.Material;
import com.gregtechceu.gtceu.common.data.GTMaterials;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.util.Lazy;
import net.phoenix.core.PhoenixAPI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator;
import java.util.List;
import static net.phoenix.core.common.data.PhoenixMaterialRegistry.getMaterial;
public interface IFissionBlanketType {
@NotNull
String getName();
default int getTintColor() {
Material m = getMaterial();
if (m != null && m != GTMaterials.NULL) {
try {
return 0xFF000000 | m.getMaterialRGB();
} catch (Throwable ignored) {
}
}
return 0xFFFFFFFF;
}
int getTier();
int getDurationTicks();
int getAmountPerCycle();
@NotNull
String getInputKey();
public record BlanketOutput(String key, int weight, int instability) {
}
List<BlanketOutput> getOutputs();
@NotNull
ResourceLocation getTexture();
@Nullable
default Material tryResolveMaterial() {
Material mat = getMaterial();
if (mat == null || mat == GTMaterials.NULL) return null;
return mat;
}
Material getMaterial();
Lazy<IFissionBlanketType[]> ALL_BLANKETS_BY_TIER = Lazy.of(() -> PhoenixAPI.FISSION_BLANKETS.keySet().stream()
.sorted(Comparator.comparingInt(IFissionBlanketType::getTier))
.toArray(IFissionBlanketType[]::new));
}
FissionBlanketBlock and any other classes using the same logic.
getName. This controls the registry name of theBreeder Block.getTintColor. Currently not working attempt atauto tintingblocks, one day it will be real.getTier. This controls theTierof theBreeder Block.getDurationTicks. This controls the blanket fuel's use inticks.getAmountPerCycle. This controls the blanket fuel's use perduration tickscycle.getInputKey. This controls theblanket fuelitself.BlanketOutput. This controls the list of outputs.getOutputs. Works togethor withBlanketOutputas backwards compat.getTexture. Controls the texture used by theBreeder Block.tryResolveMaterial. Tries to resolve the forge registry fuel as a gtm material.getMaterial. Controls the gtm material linked to it, used for some internal names.
Then, finally, we have the api call. ALL_BLANKETS_BY_TIER is passed to be stored by the PhoenixAPI class. This allows us to pass every class using this interface to the predicate.
package net.phoenix.core.common.block;
import com.gregtechceu.gtceu.api.block.ActiveBlock;
import com.gregtechceu.gtceu.api.data.chemical.material.Material;
import com.gregtechceu.gtceu.common.data.GTMaterials;
import com.gregtechceu.gtceu.utils.GTUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.BlockGetter;
import net.phoenix.core.integration.phoenix_fission.api.block.IFissionBlanketType;
import net.phoenix.core.integration.phoenix_fission.api.block.IFissionBlanketType.BlanketOutput;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import javax.annotation.ParametersAreNonnullByDefault;
@Getter
@ParametersAreNonnullByDefault
public class FissionBlanketBlock extends ActiveBlock {
private final IFissionBlanketType blanketType;
public FissionBlanketBlock(Properties properties, IFissionBlanketType blanketType) {
super(properties);
this.blanketType = blanketType;
}
@Override
public void appendHoverText(ItemStack stack, @Nullable BlockGetter level,
List<Component> tooltip, TooltipFlag flag) {
if (!GTUtil.isShiftDown()) {
tooltip.add(Component.literal("Hold §fShift§7 for details")
.withStyle(ChatFormatting.GRAY));
return;
}
Component inputName = FissionFuelRodBlock.getRegistryDisplayName(blanketType.getInputKey());
tooltip.add(Component.translatable("phoenix.fission.blanket_input", inputName)
.withStyle(ChatFormatting.LIGHT_PURPLE));
tooltip.add(Component.translatable("phoenix.fission.blanket_outputs")
.withStyle(ChatFormatting.GOLD));
List<BlanketOutput> outs = blanketType.getOutputs();
if (outs == null || outs.isEmpty()) {
tooltip.add(Component.literal("• (none)")
.withStyle(ChatFormatting.DARK_GRAY));
} else {
int shown = 0;
for (BlanketOutput o : outs) {
if (o == null) continue;
if (shown++ >= 5) break;
Component outName = FissionFuelRodBlock.getRegistryDisplayName(o.key());
tooltip.add(Component.literal("• ")
.append(outName)
.append(Component.literal(" w=" + o.weight() + " inst=" + o.instability())
.withStyle(ChatFormatting.DARK_GRAY))
.withStyle(ChatFormatting.GRAY));
}
}
double seconds = blanketType.getDurationTicks() / 20.0;
tooltip.add(Component.translatable(
"phoenix.fission.blanket_cycle",
Component.literal(String.valueOf(blanketType.getAmountPerCycle()))
.withStyle(ChatFormatting.WHITE),
Component.literal(String.format("%.2f", seconds))
.withStyle(ChatFormatting.GOLD))
.withStyle(ChatFormatting.GRAY));
}
public enum BreederBlanketTypes implements StringRepresentable, IFissionBlanketType {
PLUTONIUM_BREEDER("plutonium_breeder",
2, 2400, 1,
"gtceu:uranium_238_nugget",
List.of(
new BlanketOutput("gtceu:plutonium_nugget", 70, 1),
new BlanketOutput("gtceu:plutonium_241_nugget", 20, 3),
new BlanketOutput("gtceu:plutonium_238_nugget", 10, 4)
),
0xFFB07CFF),
THORIUM_BREEDER("thorium_breeder",
1, 240, 1,
"gtceu:uranium_235_nugget",
List.of(
new BlanketOutput("gtceu:plutonium_nugget", 85, 1),
new BlanketOutput("gtceu:plutonium_241_nugget", 15, 3)
),
0xFFFFD27D);
@Getter
@NotNull
private final String name;
@Getter
private final int tier;
@Getter
private final int durationTicks;
@Getter
private final int amountPerCycle;
@Getter
@NotNull
private final String inputKey;
@Getter
@NotNull
private final List<BlanketOutput> outputs;
@Getter
@NotNull
private final ResourceLocation texture;
@Getter
private final int tintColor;
BreederBlanketTypes(String name, int tier, int duration, int amount,
String in, List<BlanketOutput> outs, int tintColor) {
this.name = name;
this.tier = tier;
this.durationTicks = duration;
this.amountPerCycle = amount;
this.inputKey = in;
this.outputs = outs;
this.texture = new ResourceLocation("phoenix_fission", "block/blanket/" + name);
this.tintColor = tintColor;
}
@Override
public @NotNull String getSerializedName() {
return name;
}
@Override
public int getTintColor() {
return tintColor;
}
@Override
public Material getMaterial() {
return GTMaterials.NULL;
}
}
}
Tooltips
Hold Shiftto show the full info, helps to keep inventory cleaner.- The
fuelused and the list of outputfuels. - How often/how much of the input
fuelis used.
Values
String name. AStringdesigned to hold the registration name of theBreeder Block, still needs to follow a-z, 0-9.int tier. Anintdesigned to hold thetierof theBreeder Block, handlesprimary blanketlogic in reactor.int duration. Anintdesigned to hold the full duration inticksoffueluse.int amount. Anintdesigned to hold the amount offuelused per cycle.String in. AStringdesigned to handle one fully realizedForge ID.List BlanketOutputs. AListof all theblanket outputsand theirweight/instabilityfields.int tintColor. Currently doesn't do anything, just put 0xFFFFFF.