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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import net.tfminecraft.magic.attunement.ArtifactCareCache;
import net.tfminecraft.magic.model.ElementDef;
import net.tfminecraft.magic.registry.ElementRegistry;
import net.tfminecraft.magic.util.MagicNumbers;

public final class ArtifactCareStore {

Expand Down Expand Up @@ -93,6 +94,7 @@ public static boolean apply(ItemStack stack, boolean housed, long nowMs, Persist
if (stack == null || stack.getType().isAir() || ChargeIds.isCharge(stack)) {
return false;
}
boolean storesAura = storesAura(stack);
ItemMeta meta = metaOf(stack);
PersistentDataContainer root = meta.getPersistentDataContainer();
long last = readLong(root, ArtifactKeys.careLastTick());
Expand All @@ -107,13 +109,18 @@ public static boolean apply(ItemStack stack, boolean housed, long nowMs, Persist
if (ArtifactCareCache.muffledEnabled && last > 0L) {
if (housed) {
muffle = clamp01(muffle - ArtifactCareCache.muffledRecoverPerHour * dtHours);
} else {
} else if (storesAura) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
muffle = clamp01(muffle + ArtifactCareCache.muffledOffPerHour * dtHours);
}
}
boolean force = persist == Persist.ALWAYS;
boolean visible = ArtifactLore.careVisibleWouldChange(stack, muffle, dtHours);
if (!force && !visible) {
boolean anchorEmptyClock = ArtifactCareCache.muffledEnabled
&& !housed
&& !storesAura
&& last > 0L
&& emptyStorageClockIsStale(dtHours);
if (!force && !visible && !anchorEmptyClock) {
return false;
}
if (ArtifactCareCache.muffledEnabled) {
Expand Down Expand Up @@ -145,6 +152,25 @@ public static void stampUser(ItemStack stack, String characterId, long nowMs) {
stack.setItemMeta(meta);
}

private static boolean storesAura(ItemStack stack) {
Artifact artifact = Artifact.fromItem(stack);
return artifact != null && artifact.hasStoredAura();
}

/**
* Chest scans persist only when lore would change. An empty artifact has no lore
* change, so its clock would stay put and a later fill would muffle for the empty time.
* Anchor once the gap would have shown on the muffle percent, and leave shorter gaps
* alone so an open chest is not rewritten on every scan.
*/
private static boolean emptyStorageClockIsStale(double dtHours) {
double gained = ArtifactCareCache.muffledOffPerHour * dtHours;
if (gained <= 0.0) {
return false;
}
return !MagicNumbers.format(gained * 100.0).equals(MagicNumbers.format(0));
}

private static void decayFill(ItemStack stack, double dtHours) {
if (dtHours <= 0.0) {
return;
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ attunement:
# Muffle 0-1. Scales with tick.seconds_per_hour (3600 = real time).
# Off: 1/24 per hour (full muffle in 24h). Recover: 1/168 per hour (clear in 7d).
# Accepted: player inv, pedestal, artifact_display, item frames.
# Chests and other containers raise muffle. No chat.
# Chests and other containers raise muffle only while aura is stored. No chat.
muffled:
recover_per_hour: 0.00595238095
off_per_hour: 0.04166666667
Expand Down
56 changes: 56 additions & 0 deletions src/test/java/net/tfminecraft/magic/artifact/ArtifactCareTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,62 @@ void housedArtifactsRecoverAndUnhousedArtifactsMuffleOnlyAfterFirstTick() {
assertEquals(0, ArtifactCareStore.usableMax(10, 2));
}

@Test
void unhousedArtifactsMuffleOnlyWhileAuraIsStored() {
var empty = artifact();
var emptied = Artifact.fromItem(empty);
emptied.setFill("fire", 0);
emptied.persistPdc(empty);
try (var lore = mockStatic(ArtifactLore.class)) {
assertTrue(ArtifactCareStore.tick(empty, false, 1000));
assertTrue(ArtifactCareStore.tick(empty, false, 11000));
assertEquals(0, ArtifactCareStore.readMuffle(empty));
assertEquals(11000, ArtifactCareStore.readLastTickMs(empty));

emptied.setFill("fire", 5);
emptied.persistPdc(empty);
assertTrue(ArtifactCareStore.tick(empty, false, 12000));
assertEquals(.1, ArtifactCareStore.readMuffle(empty), 1e-10);

var drained = artifact();
assertTrue(ArtifactCareStore.setMuffle(drained, .4, 1000));
var drainedAura = Artifact.fromItem(drained);
drainedAura.setFill("fire", 0);
drainedAura.persistPdc(drained);
assertTrue(ArtifactCareStore.tick(drained, false, 5000));
assertEquals(.4, ArtifactCareStore.readMuffle(drained), 1e-10);
assertTrue(ArtifactCareStore.tick(drained, true, 7000));
assertEquals(0, ArtifactCareStore.readMuffle(drained), 1e-10);
}
}

@Test
void emptyUnhousedArtifactsAnchorTheClockWhenAChestScanWouldSkipThem() {
var item = artifact();
var aura = Artifact.fromItem(item);
aura.setFill("fire", 0);
aura.persistPdc(item);
try (var lore = mockStatic(ArtifactLore.class)) {
assertTrue(ArtifactCareStore.tick(item, false, 1000));
assertTrue(ArtifactCareStore.apply(item, false, 11000, ArtifactCareStore.Persist.IF_VISIBLE));
assertEquals(0, ArtifactCareStore.readMuffle(item));
assertEquals(11000, ArtifactCareStore.readLastTickMs(item));
lore.verify(() -> ArtifactLore.apply(any()), never());

assertFalse(ArtifactCareStore.apply(item, false, 11000, ArtifactCareStore.Persist.IF_VISIBLE));
assertEquals(11000, ArtifactCareStore.readLastTickMs(item));
ArtifactCareCache.muffledOffPerHour = .00001;
assertFalse(ArtifactCareStore.apply(item, false, 12000, ArtifactCareStore.Persist.IF_VISIBLE));
assertEquals(11000, ArtifactCareStore.readLastTickMs(item));
ArtifactCareCache.muffledOffPerHour = .1;

aura.setFill("fire", 5);
aura.persistPdc(item);
assertTrue(ArtifactCareStore.tick(item, false, 12000));
assertEquals(.1, ArtifactCareStore.readMuffle(item), 1e-10);
}
}

@Test
void setMuffleClampsTheValueAndResetsTheCareClock() {
var item = artifact();
Expand Down
Loading