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 @@ -27,6 +27,20 @@ public static long readLastTickMs(ItemStack stack) {
return readLong(stack, ArtifactKeys.careLastTick());
}

/** Sets an artifact's muffle level and resets its care clock. */
public static boolean setMuffle(ItemStack stack, double muffle, long nowMs) {
if (Artifact.fromItem(stack) == null) {
return false;
}
ItemMeta meta = metaOf(stack);
PersistentDataContainer root = meta.getPersistentDataContainer();
root.set(ArtifactKeys.careMuffle(), PersistentDataType.DOUBLE, clamp01(muffle));
root.set(ArtifactKeys.careLastTick(), PersistentDataType.LONG, nowMs);
stack.setItemMeta(meta);
ArtifactLore.apply(stack);
return true;
}

public static Map<String, Long> readUsers(ItemStack stack) {
ItemMeta meta = metaOf(stack);
if (meta == null) {
Expand Down
72 changes: 70 additions & 2 deletions src/main/java/net/tfminecraft/magic/command/MagicCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
import net.tfminecraft.magic.Cache;
import net.tfminecraft.magic.Magic;
import net.tfminecraft.magic.Messages;
import net.tfminecraft.magic.artifact.Artifact;
import net.tfminecraft.magic.artifact.ArtifactCareStore;
import net.tfminecraft.magic.artifact.ArtifactIds;
import net.tfminecraft.magic.artifact.aura.AuraVessel;
import net.tfminecraft.magic.artifact.aura.AuraVessels;
import net.tfminecraft.magic.artifact.ArtifactIds;
import net.tfminecraft.magic.artifact.fillchest.ArtifactFillChestService;
import net.tfminecraft.magic.artifact.config.ArtifactRarityDef;
import net.tfminecraft.magic.artifact.config.ArtifactRarityRegistry;
Expand Down Expand Up @@ -183,6 +185,12 @@ private static boolean handleArtifact(CommandSender sender, String[] args) {
if ("setfill".equalsIgnoreCase(args[1])) {
return handleArtifactSetFill(sender, args);
}
if ("setmuffle".equalsIgnoreCase(args[1])) {
return handleArtifactSetMuffle(sender, args, false);
}
if ("unmuffle".equalsIgnoreCase(args[1])) {
return handleArtifactSetMuffle(sender, args, true);
}
sender.sendMessage(Messages.get("artifact.usage"));
return true;
}
Expand Down Expand Up @@ -457,6 +465,64 @@ private static boolean handleArtifactSetFill(CommandSender sender, String[] args
return true;
}

private static boolean handleArtifactSetMuffle(CommandSender sender, String[] args, boolean clear) {
if (clear ? args.length != 2 : args.length != 3) {
sender.sendMessage(Messages.get(clear
? "artifact.unmuffle.usage"
: "artifact.setmuffle.usage"));
return true;
}
if (!(sender instanceof Player player)) {
sender.sendMessage(Messages.get(clear
? "artifact.unmuffle.players_only"
: "artifact.setmuffle.players_only"));
return true;
}
ItemStack item = heldArtifactItem(player);
if (item == null || Artifact.fromItem(item) == null) {
sender.sendMessage(Messages.get(clear
? "artifact.unmuffle.no_item"
: "artifact.setmuffle.no_item"));
return true;
}
UUID id = ArtifactIds.read(item);
if (id == null) {
sender.sendMessage(Messages.get(clear
? "artifact.unmuffle.no_id"
: "artifact.setmuffle.no_id"));
return true;
}
double requested;
if (clear) {
requested = 0.0;
} else {
Double percent = parseAmount(args[2]);
if (percent == null || percent < 0.0 || percent > 100.0) {
sender.sendMessage(Messages.get("artifact.setmuffle.invalid_amount"));
return true;
}
requested = percent / 100.0;
}
double before = ArtifactCareStore.readMuffle(item);
if (!ArtifactCareStore.setMuffle(item, requested, System.currentTimeMillis())) {
sender.sendMessage(Messages.get(clear
? "artifact.unmuffle.no_item"
: "artifact.setmuffle.no_item"));
return true;
}
player.getInventory().setItemInMainHand(item);
AuraLog.append(
"setmuffle player=%s artifact=%s muffle=%s->%s",
player.getName(),
id.toString(),
AuraLog.n(before),
AuraLog.n(requested));
sender.sendMessage(Messages.get(
clear ? "artifact.unmuffle.ok" : "artifact.setmuffle.ok",
"muffle", MagicNumbers.format(requested * 100.0)));
return true;
}

private static ItemStack heldArtifactItem(Player player) {
ItemStack item = player.getInventory().getItemInMainHand();
if (item.getType().isAir()) {
Expand Down Expand Up @@ -673,7 +739,9 @@ public List<String> onTabComplete(CommandSender sender, Command command, String
return Collections.emptyList();
}
if (args.length == 2) {
return filterPrefix(List.of("roll", "give", "create", "path", "setfill"), args[1]);
return filterPrefix(
List.of("roll", "give", "create", "path", "setfill", "setmuffle", "unmuffle"),
args[1]);
}
if ("setfill".equalsIgnoreCase(args[1])) {
if (args.length == 3) {
Expand Down
31 changes: 28 additions & 3 deletions src/main/resources/messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ admin:

no_permission: "#ff5555You do not have permission to use this command."

usage: "#aaaaaaUsage: /magic rune keybind <trigger> #555555| #aaaaaa/magic reload #555555| #aaaaaa/magic open #555555| #aaaaaa/magic resonance #555555| #aaaaaa/magic artifact roll #555555| #aaaaaa/magic artifact give #555555| #aaaaaa/magic artifact create #555555| #aaaaaa/magic artifact setfill #555555| #aaaaaa/magic fillchest #555555| #aaaaaa/magic shrine fill"
usage: "#aaaaaaUsage: /magic rune keybind <trigger> #555555| #aaaaaa/magic reload #555555| #aaaaaa/magic open #555555| #aaaaaa/magic resonance #555555| #aaaaaa/magic artifact roll #555555| #aaaaaa/magic artifact give #555555| #aaaaaa/magic artifact create #555555| #aaaaaa/magic artifact setfill #555555| #aaaaaa/magic artifact setmuffle #555555| #aaaaaa/magic artifact unmuffle #555555| #aaaaaa/magic fillchest #555555| #aaaaaa/magic shrine fill"

rune:

Expand Down Expand Up @@ -117,7 +117,7 @@ resonance:

artifact:

usage: "#aaaaaaUsage: /magic artifact roll #555555| #aaaaaa/magic artifact give #555555| #aaaaaa/magic artifact create #555555| #aaaaaa/magic artifact path #555555| #aaaaaa/magic artifact setfill"
usage: "#aaaaaaUsage: /magic artifact roll #555555| #aaaaaa/magic artifact give #555555| #aaaaaa/magic artifact create #555555| #aaaaaa/magic artifact path #555555| #aaaaaa/magic artifact setfill #555555| #aaaaaa/magic artifact setmuffle #555555| #aaaaaa/magic artifact unmuffle"

path:

Expand Down Expand Up @@ -187,6 +187,32 @@ artifact:

ok: "#55ff55Set {element} fill to {fill}."

setmuffle:

usage: "#aaaaaaUsage: /magic artifact setmuffle <percent 0-100>"

players_only: "#ff5555Only players can set artifact muffle."

no_item: "#ff5555Hold an artifact in your main hand."

no_id: "#ff5555That item has no artifact id."

invalid_amount: "#ff5555Muffle must be a number from 0 to 100."

ok: "#55ff55Set artifact muffle to {muffle}%."

unmuffle:

usage: "#aaaaaaUsage: /magic artifact unmuffle"

players_only: "#ff5555Only players can unmuffle artifacts."

no_item: "#ff5555Hold an artifact in your main hand."

no_id: "#ff5555That item has no artifact id."

ok: "#55ff55Cleared artifact muffle ({muffle}%)."

meditation:

no_artifacts: "#aaaaaa§oCan't meditate here, no artifacts"
Expand Down Expand Up @@ -344,4 +370,3 @@ gear:
failed: "#ff5555Could not refresh that weapon. Check console."

ok: "#55ff55Weapon refreshed against the live gear config."

49 changes: 48 additions & 1 deletion src/test/java/net/tfminecraft/magic/CommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,53 @@ void artifactFillValidatesIdentityCapAndAmount() {
9, AuraVessels.fromItem(player.getInventory().getItemInMainHand()).getFill("fire"));
}

@Test
void artifactMuffleCommandsValidateAndUpdateHeldArtifact() {
expect("artifact.setmuffle.usage", "artifact", "setmuffle");
expect("artifact.unmuffle.usage", "artifact", "unmuffle", "extra");
var console = mock(CommandSender.class);
when(console.hasPermission(anyString())).thenReturn(true);
run(console, "artifact", "setmuffle", "25");
run(console, "artifact", "unmuffle");
verify(console).sendMessage("artifact.setmuffle.players_only");
verify(console).sendMessage("artifact.unmuffle.players_only");

expect("artifact.setmuffle.no_item", "artifact", "setmuffle", "25");
expect("artifact.unmuffle.no_item", "artifact", "unmuffle");
player.getInventory().setItemInMainHand(new ItemStack(Material.STONE));
expect("artifact.setmuffle.no_item", "artifact", "setmuffle", "25");
expect("artifact.unmuffle.no_item", "artifact", "unmuffle");

var noId = new ItemStack(Material.STONE);
var noIdArtifact = Artifact.create();
noIdArtifact.setCap("fire", 10);
noIdArtifact.persistPdc(noId);
player.getInventory().setItemInMainHand(noId);
expect("artifact.setmuffle.no_id", "artifact", "setmuffle", "25");
expect("artifact.unmuffle.no_id", "artifact", "unmuffle");

var item = artifact();
player.getInventory().setItemInMainHand(item);
expect("artifact.setmuffle.invalid_amount", "artifact", "setmuffle", "bad");
expect("artifact.setmuffle.invalid_amount", "artifact", "setmuffle", "-1");
expect("artifact.setmuffle.invalid_amount", "artifact", "setmuffle", "101");
expect("artifact.setmuffle.ok", "artifact", "setmuffle", "25");
ItemStack held = player.getInventory().getItemInMainHand();
assertEquals(.25, ArtifactCareStore.readMuffle(held), 1e-10);
assertTrue(held.getItemMeta().getLore().stream().anyMatch(line -> line.contains("Muffled: 25%")));

try (var care = mockStatic(ArtifactCareStore.class)) {
expect("artifact.setmuffle.no_item", "artifact", "setmuffle", "50");
expect("artifact.unmuffle.no_item", "artifact", "unmuffle");
}

expect("artifact.unmuffle.ok", "artifact", "unmuffle");
held = player.getInventory().getItemInMainHand();
assertEquals(0, ArtifactCareStore.readMuffle(held));
assertTrue(ArtifactCareStore.readLastTickMs(held) > 0);
assertTrue(held.getItemMeta().getLore().stream().noneMatch(line -> line.contains("Muffled:")));
}

@Test
void nonFiniteAmountsAreRejectedWithoutChangingArtifactOrResonance() {
player.getInventory().setItemInMainHand(artifact());
Expand Down Expand Up @@ -278,7 +325,7 @@ void chestModesAndTabSuggestionsRespectPermissionsEnabledTypesAndPositions() {
assertEquals(List.of("fill"), tab("shrine", ""));
assertEquals(2, tab("shrine", "fill", "").size());
assertTrue(tab("shrine", "other", "").isEmpty());
assertEquals(5, tab("artifact", "").size());
assertEquals(7, tab("artifact", "").size());
assertEquals(2, tab("artifact", "setfill", "").size());
assertTrue(tab("artifact", "setfill", "fire", "").isEmpty());
assertEquals(List.of("random", "fire"), tab("artifact", "roll", ""));
Expand Down
16 changes: 16 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,22 @@ void housedArtifactsRecoverAndUnhousedArtifactsMuffleOnlyAfterFirstTick() {
assertEquals(0, ArtifactCareStore.usableMax(10, 2));
}

@Test
void setMuffleClampsTheValueAndResetsTheCareClock() {
var item = artifact();
try (var lore = mockStatic(ArtifactLore.class)) {
assertTrue(ArtifactCareStore.setMuffle(item, -1, 2000));
assertEquals(0, ArtifactCareStore.readMuffle(item));
assertEquals(2000, ArtifactCareStore.readLastTickMs(item));
lore.verify(() -> ArtifactLore.apply(item));

assertTrue(ArtifactCareStore.setMuffle(item, 2, 4000));
assertEquals(1, ArtifactCareStore.readMuffle(item));
assertEquals(4000, ArtifactCareStore.readLastTickMs(item));
assertFalse(ArtifactCareStore.setMuffle(new ItemStack(Material.STONE), .5, 5000));
}
}

@Test
void chargeItemsNeverAcquireArtifactCareState() {
assertFalse(ArtifactCareStore.tick(null, false, 1000));
Expand Down
Loading