Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
<advancedcrafting.version>2.2.5</advancedcrafting.version>
<geminfusion.version>2.2.5</geminfusion.version>
<gunsandgadgets.version>2.0.6</gunsandgadgets.version>
<magic.version>0.4.7</magic.version>
<!-- Spent artifact husks need ArtifactWear from 0.9.0. -->
<magic.version>0.9.0</magic.version>
<tlibs.version>2.6.0</tlibs.version>
</properties>

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/net/tfminecraft/recycler/Cache.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ private Cache() {}
"common", 4, "uncommon", 8, "rare", 12, "epic", 16, "legendary", 28);
public static int artifactDefaultReturn = 4;
public static double artifactMinMuffle = 0.0;
/** Share of the rarity amount a spent artifact husk returns, rounded down. */
public static double artifactHuskFraction = 0.5;
public static boolean blockConfirmWhenZeroYield = true;

public static boolean depositWhitelistMode = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public final class ConfigLoader implements LoaderInterface {
private static final double DEFAULT_SCRAP_RATE = 0.5;
private static final double DEFAULT_RECIPE_RATE = 1.0;
private static final String DEFAULT_ARTIFACT_ITEM = "m.currency.enchanted_dust";
private static final double DEFAULT_HUSK_FRACTION = 0.5;
private static final java.util.Map<String, Integer> DEFAULT_ARTIFACT_RETURNS = java.util.Map.of(
"common", 4, "uncommon", 8, "rare", 12, "epic", 16, "legendary", 28);

Expand Down Expand Up @@ -151,8 +152,11 @@ private static void applyArtifactReturns(ConfigurationSection section) {
Cache.artifactReturnItem = DEFAULT_ARTIFACT_ITEM;
Cache.artifactDefaultReturn = 4;
Cache.artifactMinMuffle = 0.0;
Cache.artifactHuskFraction = DEFAULT_HUSK_FRACTION;
if (section != null) {
Cache.artifactMinMuffle = readRate(section, "min_muffle", 0.0, 0.0);
Cache.artifactHuskFraction = readRate(
section, "husk_fraction", DEFAULT_HUSK_FRACTION, DEFAULT_HUSK_FRACTION);
Cache.artifactReturnItem = section.getString("item", DEFAULT_ARTIFACT_ITEM).trim();
Cache.artifactDefaultReturn = readAmount(section, "default", 4);
ConfigurationSection rarities = section.getConfigurationSection("rarities");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,40 @@
import net.tfminecraft.magic.artifact.Artifact;
import net.tfminecraft.magic.artifact.ArtifactCareStore;
import net.tfminecraft.magic.artifact.ArtifactKeys;
import net.tfminecraft.magic.artifact.ArtifactWear;
import net.tfminecraft.recycler.Cache;
import net.tfminecraft.recycler.model.RecycleOutput;

/**
* Magic artifacts muffled at least {@code artifact_returns.min_muffle} (0 = any artifact).
* They are found rather than crafted, so there is no record of what went into them: the
* return is a fixed amount by rarity from {@code artifact_returns}. Stored aura is not returned.
*
* <p>Spent husks (artifacts worn out in meditation) hold no aura, so min_muffle does not
* apply to them. They return {@code artifact_returns.husk_fraction} of their rarity's amount,
* rounded down.
*/
public final class ArtifactProvider implements RecycleProvider {

/**
* Husks exist from Magic 0.9.0. Against an older Magic the class is missing, and
* touching it would break every recycle, since the chain asks this provider first.
*/
public static boolean huskSupport = classPresent("net.tfminecraft.magic.artifact.ArtifactWear");

public static boolean classPresent(String name) {
try {
Class.forName(name, false, ArtifactProvider.class.getClassLoader());
return true;
} catch (ClassNotFoundException | LinkageError ex) {
return false;
}
}

private static boolean isHusk(ItemStack item) {
return huskSupport && ArtifactWear.isHusk(item);
}

@Override
public int priority() {
return 16;
Expand All @@ -32,6 +56,9 @@ public double returnRate() {

@Override
public boolean canHandle(ItemStack item) {
if (isHusk(item)) {
return true;
}
return Artifact.fromItem(item) != null && !belowMinMuffle(item);
}

Expand All @@ -41,6 +68,9 @@ public boolean canHandle(ItemStack item) {
* artifact cannot get around either setting.
*/
public boolean refuses(ItemStack item) {
if (isHusk(item)) {
return outputsFor(item).isEmpty();
}
return Artifact.fromItem(item) != null && (belowMinMuffle(item) || outputsFor(item).isEmpty());
}

Expand All @@ -50,14 +80,17 @@ private static boolean belowMinMuffle(ItemStack item) {

@Override
public List<RecycleOutput> resolveBaseOutputs(ItemStack item) {
if (Artifact.fromItem(item) == null) {
if (!isHusk(item) && Artifact.fromItem(item) == null) {
return List.of();
}
return outputsFor(item);
}

private static List<RecycleOutput> outputsFor(ItemStack item) {
int amount = Cache.artifactRarityReturns.getOrDefault(rarity(item), Cache.artifactDefaultReturn);
if (isHusk(item)) {
amount = (int) Math.floor(amount * Cache.artifactHuskFraction);
}
if (amount <= 0 || Cache.artifactReturnItem.isBlank()) {
return List.of();
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,15 @@ artifact_returns:
# Lowest muffle (0.0-1.0) an artifact needs before it can be recycled. 0.0 accepts any
# artifact; 1.0 accepts only fully muffled ones (about a day in a chest).
min_muffle: 0.0
# Spent husks (artifacts worn out in meditation) return this share of their rarity's
# amount, rounded down. min_muffle does not apply to them. 0.0-1.0.
husk_fraction: 0.5
item: m.currency.enchanted_dust
rarities:
common: 4
uncommon: 8
rare: 12
# Magic no longer rolls Epic; this keeps older Epic artifacts recycling at their value.
epic: 16
legendary: 28
default: 4
Expand Down
43 changes: 43 additions & 0 deletions src/test/java/net/tfminecraft/recycler/ArtifactProviderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void artifactsReturnDustByRarityAndMinMuffleGatesThem() {
var unrecorded = artifact(null);
try (var art = mockStatic(Artifact.class);
var keys = mockStatic(ArtifactKeys.class);
var wear = mockStatic(ArtifactWear.class);
var care = mockStatic(ArtifactCareStore.class)) {
keys.when(ArtifactKeys::artifactRarity).thenReturn(RARITY);
var found = mock(Artifact.class);
Expand Down Expand Up @@ -69,6 +70,47 @@ void artifactsReturnDustByRarityAndMinMuffleGatesThem() {
}
}

@Test
void spentHusksReturnTheirShareOfTheRarityAmountWhateverTheirMuffle() {
var p = new ArtifactProvider();
var legendary = artifact("legendary");
var common = artifact("common");
try (var art = mockStatic(Artifact.class);
var keys = mockStatic(ArtifactKeys.class);
var wear = mockStatic(ArtifactWear.class);
var care = mockStatic(ArtifactCareStore.class)) {
keys.when(ArtifactKeys::artifactRarity).thenReturn(RARITY);
wear.when(() -> ArtifactWear.isHusk(legendary)).thenReturn(true);
wear.when(() -> ArtifactWear.isHusk(common)).thenReturn(true);
Cache.artifactMinMuffle = 1.0;
assertTrue(p.canHandle(legendary), "A husk has no aura, so min_muffle does not apply");
assertFalse(p.refuses(legendary));
assertEquals(
List.of(new RecycleOutput("m.currency.enchanted_dust", 14)),
p.resolveBaseOutputs(legendary));
Cache.artifactHuskFraction = .3;
assertEquals(
List.of(new RecycleOutput("m.currency.enchanted_dust", 1)),
p.resolveBaseOutputs(common),
"Rounded down");
Cache.artifactHuskFraction = 0;
assertTrue(p.refuses(common), "A husk worth nothing is refused, not sent to recipes");
assertTrue(p.resolveBaseOutputs(common).isEmpty());
art.verifyNoInteractions();
care.verifyNoInteractions();
ArtifactProvider.huskSupport = false;
try {
assertFalse(p.canHandle(common), "Without husk support it is just a non-artifact");
assertFalse(p.refuses(common));
assertTrue(p.resolveBaseOutputs(common).isEmpty());
} finally {
ArtifactProvider.huskSupport = true;
}
}
assertTrue(ArtifactProvider.classPresent("net.tfminecraft.magic.artifact.ArtifactWear"));
assertFalse(ArtifactProvider.classPresent("net.tfminecraft.magic.artifact.Missing"));
}

@Test
void chainRefusesArtifactsBelowMinMuffleEvenWhenARecipeMatches() throws Exception {
yaml("recipes.yml", "recipes:\n rod:\n input: v.blaze_rod\n outputs: ['v.stick 1']");
Expand All @@ -81,6 +123,7 @@ void chainRefusesArtifactsBelowMinMuffleEvenWhenARecipeMatches() throws Exceptio
try (var art = mockStatic(Artifact.class);
var keys = mockStatic(ArtifactKeys.class);
var care = mockStatic(ArtifactCareStore.class);
var wear = mockStatic(ArtifactWear.class);
var gear = mockStatic(net.tfminecraft.magic.gear.GearProvenance.class)) {
keys.when(ArtifactKeys::artifactRarity).thenReturn(RARITY);
art.when(() -> Artifact.fromItem(stack)).thenReturn(mock(Artifact.class));
Expand Down
9 changes: 7 additions & 2 deletions src/test/java/net/tfminecraft/recycler/LoadersTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,33 +89,38 @@ void artifactReturnsUseDefaultsOverridesAndClampNegatives() throws Exception {
Cache.artifactRarityReturns);
assertEquals(4, Cache.artifactDefaultReturn);
assertEquals(0, Cache.artifactMinMuffle, "Any artifact is accepted by default");
assertEquals(.5, Cache.artifactHuskFraction, "Husks return half by default");
loader.loadSafe(
yaml(
"artifacts.yml",
"return_rates:\n artifacts: 0.5\nartifact_returns:\n item: ' m.currency.other '\n"
+ " min_muffle: 2\n default: -2\n rarities:\n Common: 0\n mythic: 9\n rare: -1\n"
+ " min_muffle: 2\n husk_fraction: 3\n default: -2\n rarities:\n Common: 0\n mythic: 9\n rare: -1\n"
+ " legendary: seven\n odd: x\n")
.toFile());
assertEquals(.5, Cache.artifactReturnRate);
assertEquals("m.currency.other", Cache.artifactReturnItem);
assertEquals(0, Cache.artifactDefaultReturn, "Negative amounts become 0");
assertEquals(1, Cache.artifactMinMuffle, "min_muffle is clamped to 0.0-1.0");
assertEquals(1, Cache.artifactHuskFraction, "husk_fraction is clamped to 0.0-1.0");
assertEquals(
Map.of(
"common", 0, "uncommon", 8, "rare", 0, "epic", 16, "legendary", 28, "mythic", 9, "odd",
0),
Cache.artifactRarityReturns,
"A non-number keeps the built-in amount, or default for a new rarity");
loader.loadSafe(
yaml("no-rarities.yml", "artifact_returns:\n default: 3\n min_muffle: 0.5\n").toFile());
yaml("no-rarities.yml", "artifact_returns:\n default: 3\n min_muffle: 0.5\n husk_fraction: 0.25\n")
.toFile());
assertEquals(3, Cache.artifactDefaultReturn);
assertEquals(.5, Cache.artifactMinMuffle);
assertEquals(.25, Cache.artifactHuskFraction);
assertEquals(28, Cache.artifactRarityReturns.get("legendary"));
loader.loadSafe(yaml("none.yml", "{}").toFile());
assertEquals("m.currency.enchanted_dust", Cache.artifactReturnItem);
assertEquals(4, Cache.artifactDefaultReturn);
assertEquals(4, Cache.artifactRarityReturns.get("common"));
assertEquals(0, Cache.artifactMinMuffle);
assertEquals(.5, Cache.artifactHuskFraction);
}

@Test
Expand Down
Loading