Skip to content

Commit

Permalink
Prevent a NullPointerException when building example wand replacement…
Browse files Browse the repository at this point in the history
… recipes (#82)

* More null-safety checks to prevent crashes

* Limit placeholders to only vanilla caps/rods

* Hard-code placeholder items and example recipes to only vanilla parts

* Put cost explanation in research text
  • Loading branch information
rndmorris authored Jan 14, 2025
1 parent f6ac958 commit 4cca0fe
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 129 deletions.
191 changes: 102 additions & 89 deletions src/main/java/dev/rndmorris/salisarcana/common/CustomResearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,53 +77,57 @@ private static IArcaneRecipe[][] exampleCapRecipes() {
final var staffList = new ArrayList<IArcaneRecipe>();
final var scepterList = new ArrayList<IArcaneRecipe>();

for (var capItem : PlaceholderItem.capPlaceholder.getAllBaseItems()) {
if (baseCap.getItem()
.isItemEqual(capItem)) {
continue;
}
final var cap = WandHelper.getWandCapFromItem(capItem);
if (cap == null) {
continue;
}
final var outputWand = wandItem.copy();
final var outputStaff = staffItem.copy();
final var outputscepter = scepterItem.copy();
wand.setCap(outputWand, cap);
wand.setCap(outputStaff, cap);
wand.setCap(outputscepter, cap);

final var wandCost = WandType.WAND.getCraftingVisCost(cap, baseWandRod);
wandList.add(
new ShapelessArcaneRecipe(
null,
outputWand,
AspectHelper.primalList(wandCost),
wandItem.copy(),
capItem,
capItem));

final var staffCost = WandType.STAFF.getCraftingVisCost(cap, baseStaffRod);
staffList.add(
new ShapelessArcaneRecipe(
null,
outputStaff,
AspectHelper.primalList(staffCost),
staffItem.copy(),
capItem,
capItem));

final var scepterCost = WandType.SCEPTER.getCraftingVisCost(cap, baseWandRod);
scepterList.add(
new ShapelessArcaneRecipe(
null,
outputscepter,
AspectHelper.primalList(scepterCost),
scepterItem.copy(),
capItem,
capItem,
capItem));
}
WandHelper.allVanillaCaps()
.stream()
.filter(
wandCap -> wandCap != null && wandCap.getItem() != null
&& wandCap.getItem()
.getItem() != null)
.forEach(wandCap -> {
if (baseCap == wandCap) {
return;
}

final ItemStack wandCapItem = wandCap.getItem();

final var outputWand = wandItem.copy();
final var outputStaff = staffItem.copy();
final var outputScepter = scepterItem.copy();
wand.setCap(outputWand, wandCap);
wand.setCap(outputStaff, wandCap);
wand.setCap(outputScepter, wandCap);

final var wandCost = WandType.WAND.getCraftingVisCost(wandCap, baseWandRod);
wandList.add(
new ShapelessArcaneRecipe(
null,
outputWand,
AspectHelper.primalList(wandCost),
wandItem,
wandCapItem,
wandCapItem));

final var staffCost = WandType.STAFF.getCraftingVisCost(wandCap, baseStaffRod);
staffList.add(
new ShapelessArcaneRecipe(
null,
outputStaff,
AspectHelper.primalList(staffCost),
staffItem.copy(),
wandCapItem,
wandCapItem));

final var scepterCost = WandType.SCEPTER.getCraftingVisCost(wandCap, baseWandRod);
scepterList.add(
new ShapelessArcaneRecipe(
null,
outputScepter,
AspectHelper.primalList(scepterCost),
scepterItem.copy(),
wandCapItem,
wandCapItem,
wandCapItem));
});

return new IArcaneRecipe[][] { wandList.toArray(new IArcaneRecipe[0]), staffList.toArray(new IArcaneRecipe[0]),
scepterList.toArray(new IArcaneRecipe[0]), };
Expand Down Expand Up @@ -152,49 +156,58 @@ private static IArcaneRecipe[][] exampleRodRecipes() {
final var scepterList = new ArrayList<IArcaneRecipe>();
final var staffList = new ArrayList<IArcaneRecipe>();

for (var rodItem : PlaceholderItem.rodPlaceholder.getAllBaseItems()) {
final var rod = WandHelper.getWandRodFromItem(rodItem);
if (rod == null) {
continue;
}
if (rod instanceof StaffRod) {
if (rod == baseStaffRod) {
continue;
}
final var outputStaff = staffItem.copy();
wand.setRod(outputStaff, rod);

final var staffCost = WandType.STAFF.getCraftingVisCost(baseCap, rod);
staffList.add(
new ShapelessArcaneRecipe(
null,
outputStaff,
AspectHelper.primalList(staffCost),
staffItem,
rodItem));
} else {
if (rod == baseWandRod) {
continue;
WandHelper.allVanillaRods()
.stream()
.filter(
wandRod -> wandRod != null && wandRod.getItem() != null
&& wandRod.getItem()
.getItem() != null)
.forEach(wandRod -> {
final ItemStack rodItem = wandRod.getItem();

if (wandRod instanceof StaffRod) {
if (wandRod == baseStaffRod) {
return;
}
final var outputStaff = staffItem.copy();
wand.setRod(outputStaff, wandRod);

final var staffCost = WandType.STAFF.getCraftingVisCost(baseCap, wandRod);
staffList.add(
new ShapelessArcaneRecipe(
null,
outputStaff,
AspectHelper.primalList(staffCost),
staffItem,
rodItem));
} else {
if (wandRod == baseWandRod) {
return;
}
final var outputWand = wandItem.copy();
final var outputScepter = scepterItem.copy();
wand.setRod(outputWand, wandRod);
wand.setRod(outputScepter, wandRod);

final var wandCost = WandType.WAND.getCraftingVisCost(baseCap, wandRod);
wandList.add(
new ShapelessArcaneRecipe(
null,
outputWand,
AspectHelper.primalList(wandCost),
wandItem,
rodItem));

final var scepterCost = WandType.SCEPTER.getCraftingVisCost(baseCap, wandRod);
scepterList.add(
new ShapelessArcaneRecipe(
null,
outputScepter,
AspectHelper.primalList(scepterCost),
scepterItem,
rodItem));
}
final var outputWand = wandItem.copy();
final var outputScepter = scepterItem.copy();
wand.setRod(outputWand, rod);
wand.setRod(outputScepter, rod);

final var wandCost = WandType.WAND.getCraftingVisCost(baseCap, rod);
wandList.add(
new ShapelessArcaneRecipe(null, outputWand, AspectHelper.primalList(wandCost), wandItem, rodItem));

final var scepterCost = WandType.SCEPTER.getCraftingVisCost(baseCap, rod);
scepterList.add(
new ShapelessArcaneRecipe(
null,
outputScepter,
AspectHelper.primalList(scepterCost),
scepterItem,
rodItem));
}
}
});

return new IArcaneRecipe[][] { wandList.toArray(new IArcaneRecipe[0]),
scepterList.toArray(new IArcaneRecipe[0]), staffList.toArray(new IArcaneRecipe[0]), };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import static dev.rndmorris.salisarcana.SalisArcana.MODID;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
Expand All @@ -18,6 +16,7 @@
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import dev.rndmorris.salisarcana.config.ConfigModuleRoot;
import dev.rndmorris.salisarcana.lib.WandHelper;
import thaumcraft.api.wands.WandCap;
import thaumcraft.api.wands.WandRod;

Expand Down Expand Up @@ -49,17 +48,12 @@ public PlaceholderItem() {

private ItemStack[] baseItemCache() {
if (baseItemCache == null) {
baseItemCache = getBaseItems().toArray(ItemStack[]::new);
baseItemCache = getBaseItems().filter(i -> i != null && i.getItem() != null)
.toArray(ItemStack[]::new);
}
return baseItemCache;
}

public List<ItemStack> getAllBaseItems() {
final var result = new ArrayList<ItemStack>();
Collections.addAll(result, baseItemCache());
return result;
}

@SideOnly(Side.CLIENT)
private IIcon[] cachedIcons() {
if (cachedIcons == null) {
Expand Down Expand Up @@ -115,8 +109,9 @@ public static class WandCapPlaceholderItem extends PlaceholderItem {

@Override
public Stream<ItemStack> getBaseItems() {
return WandCap.caps.values()
return WandHelper.allVanillaCaps()
.stream()
.filter(Objects::nonNull)
.map(WandCap::getItem);
}
}
Expand All @@ -125,8 +120,9 @@ public static class WandRodPlaceholderItem extends PlaceholderItem {

@Override
public Stream<ItemStack> getBaseItems() {
return WandRod.rods.values()
return WandHelper.allVanillaRods()
.stream()
.filter(Objects::nonNull)
.map(WandRod::getItem);
}
}
Expand Down
67 changes: 40 additions & 27 deletions src/main/java/dev/rndmorris/salisarcana/lib/WandHelper.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package dev.rndmorris.salisarcana.lib;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;

import net.minecraft.item.ItemStack;

import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.aspects.AspectList;
import thaumcraft.api.wands.WandCap;
import thaumcraft.api.wands.WandRod;
import thaumcraft.common.config.ConfigItems;
import thaumcraft.common.items.wands.ItemWandCasting;

public class WandHelper {
Expand Down Expand Up @@ -109,32 +111,43 @@ public class WandHelper {
return wandInstance;
}

public static boolean isScepter(@Nullable ItemStack itemStack) {
final var wandInstance = getWandItem(itemStack);
return wandInstance != null && wandInstance.isSceptre(itemStack);
}

public static boolean isStaff(@Nullable ItemStack itemStack) {
final var wandInstance = getWandItem(itemStack);
return wandInstance != null && wandInstance.isStaff(itemStack);
}

public static boolean isWand(@Nullable ItemStack itemStack) {
final var wandInstance = getWandItem(itemStack);
return wandInstance != null && !wandInstance.isSceptre(itemStack) && !wandInstance.isStaff(itemStack);
}

public static AspectList primalAspectList(int amount) {
return primalAspectList(amount, amount, amount, amount, amount, amount);
private static ArrayList<WandCap> allVanillaCaps;

public static List<WandCap> allVanillaCaps() {
if (allVanillaCaps == null) {
final var allCaps = new WandCap[] { ConfigItems.WAND_CAP_IRON, ConfigItems.WAND_CAP_COPPER,
ConfigItems.WAND_CAP_GOLD, ConfigItems.WAND_CAP_SILVER, ConfigItems.WAND_CAP_THAUMIUM,
ConfigItems.WAND_CAP_VOID, };
allVanillaCaps = new ArrayList<>(allCaps.length);
for (var wandCap : allCaps) {
if (wandCap == null) {
continue;
}
allVanillaCaps.add(wandCap);
}
}
return new ArrayList<>(allVanillaCaps);
}

public static AspectList primalAspectList(int airAmount, int earthAmount, int fireAmount, int waterAmount,
int orderAmount, int entropyAmount) {
return new AspectList().add(Aspect.AIR, airAmount)
.add(Aspect.EARTH, earthAmount)
.add(Aspect.FIRE, fireAmount)
.add(Aspect.WATER, waterAmount)
.add(Aspect.ORDER, orderAmount)
.add(Aspect.ENTROPY, entropyAmount);
private static ArrayList<WandRod> allVanillaRods;

public static List<WandRod> allVanillaRods() {
if (allVanillaRods == null) {
final var allRods = new WandRod[] { ConfigItems.WAND_ROD_WOOD, ConfigItems.WAND_ROD_GREATWOOD,
ConfigItems.WAND_ROD_OBSIDIAN, ConfigItems.WAND_ROD_BLAZE, ConfigItems.WAND_ROD_ICE,
ConfigItems.WAND_ROD_QUARTZ, ConfigItems.WAND_ROD_BONE, ConfigItems.WAND_ROD_REED,
ConfigItems.WAND_ROD_SILVERWOOD, ConfigItems.STAFF_ROD_GREATWOOD, ConfigItems.STAFF_ROD_OBSIDIAN,
ConfigItems.STAFF_ROD_BLAZE, ConfigItems.STAFF_ROD_ICE, ConfigItems.STAFF_ROD_QUARTZ,
ConfigItems.STAFF_ROD_BONE, ConfigItems.STAFF_ROD_REED, ConfigItems.STAFF_ROD_SILVERWOOD,
ConfigItems.STAFF_ROD_PRIMAL, };
allVanillaRods = new ArrayList<>(allRods.length);
for (var wandRod : allRods) {
if (wandRod == null) {
continue;
}
allVanillaRods.add(wandRod);
}
}
return new ArrayList<>(allVanillaRods);
}
}
4 changes: 2 additions & 2 deletions src/main/resources/assets/salisarcana/lang/en_US.lang
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ salisarcana:tcinventoryscan.thaumometerTooltipMore=Hover items with the Thaumome

tc.research_name.salisarcana:REPLACEWANDCAPS=Wand Cap Substitution
tc.research_text.salisarcana:REPLACEWANDCAPS=Improving what is
tc.research_page.salisarcana:REPLACEWANDCAPS.0=With proper technique, a casting implement's caps can be replaced. This destroys the implement's existing caps, but preserves its core.
tc.research_page.salisarcana:REPLACEWANDCAPS.0=With proper technique, a casting implement's caps can be replaced. This destroys the implement's existing caps, but preserves its core.<BR>Replacing an implement's caps requires the same amount of vis as it would take to craft the same implement from scratch. Upgrading a Gold Banded Greatwood Wand to a Thaumium Bossed Greatwood Wand will cost just as much vis as creating a Thaumium Bossed Greatwood Wand directly.

tc.research_name.salisarcana:REPLACEWANDCORE=Wand Core Substitution
tc.research_text.salisarcana:REPLACEWANDCORE=Improving what is
tc.research_page.salisarcana:REPLACEWANDCORE.0=With proper technique, a casting implement's core can be replaced. This destroys the implement's existing core, but preserves its caps.
tc.research_page.salisarcana:REPLACEWANDCORE.0=With proper technique, a casting implement's core can be replaced. This destroys the implement's existing core, but preserves its caps.<BR>Replacing an implement's core requires the same amount of vis as it would take to craft the same implement from scratch. Upgrading a Gold Banded Greatwood Wand to a Gold Banded Silverwood Wand will cost just as much vis as creating a Gold Banded Silverwood Wand directly.

0 comments on commit 4cca0fe

Please sign in to comment.