Skip to content

Commit

Permalink
feat: add configurable lore for map item
Browse files Browse the repository at this point in the history
closes #4
  • Loading branch information
Machine-Maker committed Sep 24, 2023
1 parent 7fd8a4f commit 66a5128
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,78 @@
*/
package me.machinemaker.treasuremapsplus;

import java.util.Collections;
import java.util.List;
import me.machinemaker.treasuremapsplus.listener.PlayerInteract;
import me.machinemaker.treasuremapsplus.loot.ExplorationMapItemFunctionOverride;
import me.machinemaker.treasuremapsplus.villager.VillagerTradeOverride;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.minecraft.server.MinecraftServer;
import org.bukkit.NamespacedKey;
import org.bukkit.plugin.java.JavaPlugin;

import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
import static net.kyori.adventure.text.format.Style.style;
import static net.kyori.adventure.text.format.TextDecoration.ITALIC;

public final class TreasureMapsPlus extends JavaPlugin {

public static final String NAMESPACE = "treasuremapsplus";
public static final NamespacedKey IS_MAP = new NamespacedKey(NAMESPACE, "is_map");
public static final NamespacedKey MAP_STRUCTURE_TAG_KEY = new NamespacedKey(NAMESPACE, "map_tag_key");
public static final Component LORE = text("Use to receive treasure!", style(GREEN).decoration(ITALIC, false));

private final ExplorationMapItemFunctionOverride explorationMapItemFunctionOverride;
private final int changedVillagerTrades;
private final List<Component> mapUseLore;
private final boolean replaceChests;
private final boolean replaceMonuments;
private final boolean replaceMansions;

private final ReplacementResult result;

public TreasureMapsPlus() throws Exception {
this.saveDefaultConfig();
this.explorationMapItemFunctionOverride = new ExplorationMapItemFunctionOverride(MinecraftServer.getServer().registryAccess(), this.getConfig().getBoolean("replace.chests", false));
this.explorationMapItemFunctionOverride.override();
DatapackOverride.deleteLeftoversAndReload();
this.changedVillagerTrades = VillagerTradeOverride.setup(
this.getConfig().getBoolean("replace.villagers.monument", false),
this.getConfig().getBoolean("replace.villagers.mansion", false)
);
this.getConfig().options().copyDefaults(true);
this.saveConfig();
this.mapUseLore = this.readLore();
this.replaceChests = this.getConfig().getBoolean("replace.chests", false);
this.replaceMonuments = this.getConfig().getBoolean("replace.villagers.monument", false);
this.replaceMansions = this.getConfig().getBoolean("replace.villagers.mansion", false);
final ExplorationMapItemFunctionOverride mapFunctionOverride = new ExplorationMapItemFunctionOverride(MinecraftServer.getServer().registryAccess(), this);
mapFunctionOverride.override();
DatapackOverride.deleteLeftoversAndReload(); // must reload resources override to count how many were changed
final VillagerTradeOverride villagerTradeOverride = new VillagerTradeOverride(this);
final int replacedTrades = villagerTradeOverride.override();
this.result = new ReplacementResult(mapFunctionOverride.replaceCount(), replacedTrades);
}

@Override
public void onEnable() {
this.getServer().getPluginManager().registerEvents(new PlayerInteract(), this);
this.getSLF4JLogger().info("Found and replaced {} loot tables with a treasure map", this.explorationMapItemFunctionOverride.replaceCount());
this.getSLF4JLogger().info("Found and replaced {} villager trades with a treasure map", this.changedVillagerTrades);
this.getSLF4JLogger().info("Found and replaced {} loot tables with a treasure map", this.result.lootTableCount);
this.getSLF4JLogger().info("Found and replaced {} villager trades with a treasure map", this.result.tradeCount);

}

private List<Component> readLore() {
final List<String> list = this.getConfig().getStringList("messages.map.use");
if (list.isEmpty()) {
return Collections.emptyList();
}
return list.stream().map(MiniMessage.miniMessage()::deserialize).toList();
}

public List<Component> getMapUseLore() {
return this.mapUseLore;
}

public boolean shouldReplaceChests() {
return this.replaceChests;
}

public boolean shouldReplaceMonuments() {
return this.replaceMonuments;
}

public boolean shouldReplaceMansions() {
return this.replaceMansions;
}

private record ReplacementResult(int lootTableCount, int tradeCount) {
}
}
1 change: 1 addition & 0 deletions src/main/java/me/machinemaker/treasuremapsplus/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public static <T, E extends Throwable> T sneaky(final CheckedSupplier<T, E> supp
}
}

@FunctionalInterface
public interface CheckedSupplier<T, E extends Throwable> {

T get() throws E;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@ public void onEvent(final PlayerInteractEvent event) {
event.getItem().setAmount(event.getItem().getAmount() - 1);
}
for (final net.minecraft.world.item.ItemStack randomItem : randomItems) {
event.getPlayer().getWorld().dropItem(event.getPlayer().getLocation(), Utils.getBukkitStackMirror(randomItem.copy()), item1 -> {
item1.setPickupDelay(0);
});
event.getPlayer().getWorld().dropItem(
event.getPlayer().getLocation(),
Utils.getBukkitStackMirror(randomItem.copy()),
item1 -> item1.setPickupDelay(0)
);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import me.machinemaker.treasuremapsplus.RegistryOverride;
import me.machinemaker.treasuremapsplus.TreasureMapsPlus;
import me.machinemaker.treasuremapsplus.Utils;
import net.kyori.adventure.text.Component;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.ByteTag;
Expand All @@ -48,7 +49,6 @@ public class ExplorationMapItemFunctionOverride {
@VisibleForTesting
static final ResourceKey<LootItemFunctionType> EXPLORATION_FUNCTION_KEY = ResourceKey.create(Registries.LOOT_FUNCTION_TYPE, new ResourceLocation(ResourceLocation.DEFAULT_NAMESPACE, "exploration_map"));
private static final LootItemFunction.Builder SET_PDC_FUNCTION;
private static final LootItemFunction.Builder SET_LORE_FUNCTION = SetLoreFunction.setLore().addLine(PaperAdventure.asVanilla(TreasureMapsPlus.LORE));

static {
final CompoundTag pdcNbt = new CompoundTag();
Expand All @@ -63,12 +63,19 @@ public class ExplorationMapItemFunctionOverride {
@VisibleForTesting
final RegistryOverride<LootItemFunctionType> registryOverride;
private final RegistryAccess access;
private final List<Component> lore;
private final boolean replaceChests;
private final BiMap<LootItemFunction, SequenceFunction> functionMap = HashBiMap.create(3);

public ExplorationMapItemFunctionOverride(final RegistryAccess registryAccess, final TreasureMapsPlus plugin) {
this(registryAccess, plugin.getMapUseLore(), plugin.shouldReplaceChests());
}

@VisibleForTesting
@SuppressWarnings("unchecked")
public ExplorationMapItemFunctionOverride(final RegistryAccess registryAccess, final boolean replaceChests) {
ExplorationMapItemFunctionOverride(final RegistryAccess registryAccess, final List<Component> lore, final boolean replaceChests) {
this.access = registryAccess;
this.lore = lore;
this.replaceChests = replaceChests;
final MapCodec.MapCodecCodec<ExplorationMapFunction> mapCodecCodec = (MapCodec.MapCodecCodec<ExplorationMapFunction>) LootItemFunctions.EXPLORATION_MAP.codec();
final Codec<? extends LootItemFunction> replacementCodec = mapCodecCodec.codec().xmap(this::createSequenceFunction, this::retrieveExplorationFunction).codec();
Expand All @@ -90,7 +97,9 @@ public int replaceCount() {

private SequenceFunction createSequenceFunction(final LootItemFunction explorationFunction) {
return this.functionMap.computeIfAbsent(explorationFunction, ignored -> {
return SequenceFunction.of(List.of(SET_PDC_FUNCTION.build(), SET_LORE_FUNCTION.build()));
final SetLoreFunction.Builder setLoreBuilder = SetLoreFunction.setLore();
this.lore.stream().map(PaperAdventure::asVanilla).forEach(setLoreBuilder::addLine);
return SequenceFunction.of(List.of(SET_PDC_FUNCTION.build(), setLoreBuilder.build()));
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@
*/
package me.machinemaker.treasuremapsplus.villager;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.mojang.logging.LogUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import me.machinemaker.treasuremapsplus.TreasureMapsPlus;
import me.machinemaker.treasuremapsplus.Utils;
import net.kyori.adventure.text.Component;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.npc.VillagerProfession;
Expand All @@ -34,17 +37,17 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.trading.MerchantOffer;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory;

import static me.machinemaker.treasuremapsplus.Utils.sneaky;
import static net.kyori.adventure.text.Component.translatable;

public final class VillagerTradeOverride {
public class VillagerTradeOverride {

private static final Logger LOGGER = LogUtils.getClassLogger();
private static final Class<?> TREASURE_MAP_TRADE_LISTING_CLASS;
Expand All @@ -71,17 +74,29 @@ public final class VillagerTradeOverride {
TYPE_SPECIFIC_TRADE_PROXY = factory.reflectionProxy(TypeSpecificTradeProxy.class);
}

private VillagerTradeOverride() {
private final List<Component> lore;
private final boolean replaceMonuments;
private final boolean replaceMansions;

public VillagerTradeOverride(final TreasureMapsPlus plugin) {
this(plugin.getMapUseLore(), plugin.shouldReplaceMonuments(), plugin.shouldReplaceMansions());
}

@VisibleForTesting
VillagerTradeOverride(final List<Component> lore, final boolean replaceMonuments, final boolean replaceMansions) {
this.lore = lore;
this.replaceMonuments = replaceMonuments;
this.replaceMansions = replaceMansions;
}

public static int setup(final boolean replaceMonument, final boolean replaceMansion) {
public int override() {
int changed = 0;
for (final Collection<VillagerTrades.ItemListing[]> tradeSet : TRADE_SETS) {
for (final VillagerTrades.ItemListing[] listings : tradeSet) {
for (int i = 0; i < listings.length; i++) {
final VillagerTrades.ItemListing listing = listings[i];
if (TREASURE_MAP_TRADE_LISTING_CLASS.isInstance(listing)) {
final VillagerTrades.ItemListing override = createOverride(listing, replaceMonument, replaceMansion);
final VillagerTrades.ItemListing override = this.createOverride(listing);
if (listing != override) {
changed++;
}
Expand All @@ -93,7 +108,7 @@ public static int setup(final boolean replaceMonument, final boolean replaceMans
if (!(TREASURE_MAP_TRADE_LISTING_CLASS.isInstance(entry.getValue()))) {
newBuilder.put(entry);
} else {
final VillagerTrades.ItemListing override = createOverride(entry.getValue(), replaceMonument, replaceMansion);
final VillagerTrades.ItemListing override = this.createOverride(entry.getValue());
if (entry.getValue() != override) {
changed++;
}
Expand All @@ -109,15 +124,15 @@ public static int setup(final boolean replaceMonument, final boolean replaceMans
return changed;
}

private static VillagerTrades.ItemListing createOverride(final VillagerTrades.ItemListing original, final boolean replaceBuriedTreasure, final boolean replaceMansion) {
private VillagerTrades.ItemListing createOverride(final VillagerTrades.ItemListing original) {
if (original instanceof OverrideListing) {
return original;
}
final String name = TREASURE_MAP_PROXY.displayName(original);
if (name.endsWith("monument")) {
return replaceBuriedTreasure ? new OverrideListing(original) : original;
return this.replaceMonuments ? new OverrideListing(original, this::createStack) : original;
} else if (name.endsWith("mansion")) {
return replaceMansion ? new OverrideListing(original) : original;
return this.replaceMansions ? new OverrideListing(original, this::createStack) : original;
} else if (name.startsWith("filled_map.village_") || name.startsWith("filled_map.explorer_")) {
// skip these maps, no point in replacing them, it defeats their whole purpose
return original;
Expand All @@ -127,7 +142,20 @@ private static VillagerTrades.ItemListing createOverride(final VillagerTrades.It
}
}

private record OverrideListing(VillagerTrades.ItemListing original) implements VillagerTrades.ItemListing {
private ItemStack createStack(final VillagerTrades.ItemListing listing) {
final ItemStack map = new ItemStack(Items.MAP);
final org.bukkit.inventory.ItemStack bukkitStack = Utils.getBukkitStackMirror(map);
final boolean edited = bukkitStack.editMeta(meta -> {
meta.displayName(translatable(TREASURE_MAP_PROXY.displayName(listing)));
meta.lore(this.lore);
meta.getPersistentDataContainer().set(TreasureMapsPlus.IS_MAP, PersistentDataType.BYTE, (byte) 1);
meta.getPersistentDataContainer().set(TreasureMapsPlus.MAP_STRUCTURE_TAG_KEY, PersistentDataType.STRING, TREASURE_MAP_PROXY.destination(listing).location().toString());
});
Preconditions.checkState(edited, "Could not edit itemstack meta");
return map;
}

private record OverrideListing(VillagerTrades.ItemListing original, Function<VillagerTrades.ItemListing, ItemStack> mapStackCreator) implements VillagerTrades.ItemListing {

private static final float PRICE_MULTIPLIER = 0.2f;

Expand All @@ -136,18 +164,10 @@ private record OverrideListing(VillagerTrades.ItemListing original) implements V
if (!entity.level().paperConfig().environment.treasureMaps.enabled) {
return null;
}
final ItemStack map = new ItemStack(Items.MAP);
final org.bukkit.inventory.ItemStack bukkitStack = Utils.getBukkitStackMirror(map);
final ItemMeta meta = bukkitStack.getItemMeta();
meta.displayName(translatable(TREASURE_MAP_PROXY.displayName(this.original)));
meta.lore(List.of(TreasureMapsPlus.LORE));
meta.getPersistentDataContainer().set(TreasureMapsPlus.IS_MAP, PersistentDataType.BYTE, (byte) 1);
meta.getPersistentDataContainer().set(TreasureMapsPlus.MAP_STRUCTURE_TAG_KEY, PersistentDataType.STRING, TREASURE_MAP_PROXY.destination(this.original).location().toString());
bukkitStack.setItemMeta(meta);
return new MerchantOffer(
new ItemStack(Items.EMERALD, TREASURE_MAP_PROXY.emeraldCost(this.original)),
new ItemStack(Items.COMPASS),
map,
this.mapStackCreator.apply(this.original),
TREASURE_MAP_PROXY.maxUses(this.original),
TREASURE_MAP_PROXY.villagerXp(this.original),
PRICE_MULTIPLIER
Expand Down
8 changes: 8 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ replace:
monument: true
# Replace mansion map offers from villagers
mansion: false

# Configure messages for the plugin
messages:
# Configure messages relating to the map
map:
# Configure the lore added to maps that can be used to get the treasure
use:
- <green>Use to receive treasure!
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package me.machinemaker.treasuremapsplus.loot;

import java.util.Collections;
import net.minecraft.SharedConstants;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.BuiltInRegistries;
Expand All @@ -40,7 +41,7 @@ static void init() {

@Test
void testFunctionOverride() {
final ExplorationMapItemFunctionOverride override = new ExplorationMapItemFunctionOverride(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY), true);
final ExplorationMapItemFunctionOverride override = new ExplorationMapItemFunctionOverride(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY), Collections.emptyList(), true);
final LootItemFunctionType replacementValue = override.registryOverride.value();
final LootItemFunctionType oldValue = LootItemFunctions.EXPLORATION_MAP;
assertEquals(oldValue, BuiltInRegistries.LOOT_FUNCTION_TYPE.getOrThrow(ExplorationMapItemFunctionOverride.EXPLORATION_FUNCTION_KEY), "the value in the registry didn't match the value in the field");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package me.machinemaker.treasuremapsplus.villager;

import java.util.Collections;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import org.junit.jupiter.api.BeforeAll;
Expand All @@ -36,6 +37,7 @@ static void init() {

@Test
void testVillagerOverrideReflection() {
assertEquals(/* cartographer */2 + /* experimental cartographers */2, VillagerTradeOverride.setup(true, true));
final VillagerTradeOverride villagerTradeOverride = new VillagerTradeOverride(Collections.emptyList(), true, true);
assertEquals(/* cartographer */2 + /* experimental cartographers */2, villagerTradeOverride.override());
}
}

0 comments on commit 66a5128

Please sign in to comment.