diff --git a/CHANGELOG.md b/CHANGELOG.md index 20440cf905..593f73316b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Fixed an error when trying to cast a Great Spell that no longer exists, such as when an addon has been removed ([#1335](https://github.com/FallingColors/HexMod/pull/1335)) @Robotgiggle - Fixed a broken translation key when referring to a jump iota in a mishap message ([#1350](https://github.com/FallingColors/HexMod/pull/1350)) @hauveli - Fixed Thoth's Gambit counting one extra pattern towards the pattern limit every iteration ([#1352](https://github.com/FallingColors/HexMod/pull/1352)) @Robotgiggle +- Fixed an exploit that let you read the pattern from a for-sale Ancient Scroll without buying it ([#1363](https://github.com/FallingColors/HexMod/pull/1363)) @Robotgiggle +- Fixed Erase Item not working properly on Ancient Scrolls ([#1363](https://github.com/FallingColors/HexMod/pull/1363)) @Robotgiggle ## `0.11.4` - 2026-08-26 diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpErase.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpErase.kt index 938a57eab9..a1c720616c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpErase.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpErase.kt @@ -6,6 +6,9 @@ import at.petrak.hexcasting.api.casting.eval.CastingEnvironment import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.mishaps.MishapBadOffhandItem import at.petrak.hexcasting.api.misc.MediaConstants +import at.petrak.hexcasting.api.utils.hasString +import at.petrak.hexcasting.api.utils.remove +import at.petrak.hexcasting.common.items.storage.ItemScroll import at.petrak.hexcasting.xplat.IXplatAbstractions import net.minecraft.world.item.ItemStack @@ -50,6 +53,9 @@ object OpErase : SpellAction { if (datumHolder != null && datumHolder.writeIota(null, true)) datumHolder.writeIota(null, false) + + if (stack.hasString(ItemScroll.TAG_OP_ID)) + stack.remove(ItemScroll.TAG_OP_ID) } } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/storage/ItemScroll.java b/Common/src/main/java/at/petrak/hexcasting/common/items/storage/ItemScroll.java index 09a8f44b7c..d03e28fd9c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/items/storage/ItemScroll.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/items/storage/ItemScroll.java @@ -166,8 +166,8 @@ public Component getName(ItemStack pStack) { @Override public void inventoryTick(ItemStack pStack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) { - // the needs_purchase tag is used so you can't see the pattern on scrolls sold by a wandering trader - // once you put the scroll into your inventory, this removes the tag to reveal the pattern + // this tag applies the "purchase to show pattern" tooltip line to scrolls sold by wandering traders + // once you buy the scroll and put it in your inventory, the tag (and thus the tooltip) gets removed if (NBTHelper.getBoolean(pStack, TAG_NEEDS_PURCHASE)) { NBTHelper.remove(pStack, TAG_NEEDS_PURCHASE); } @@ -212,7 +212,7 @@ public void appendHoverText(ItemStack pStack, @Nullable Level pLevel, List getTooltipImage(ItemStack stack) { var compound = NBTHelper.getCompound(stack, TAG_PATTERN); - if (compound != null && !NBTHelper.getBoolean(stack, TAG_NEEDS_PURCHASE)) { + if (compound != null) { var pattern = HexPattern.fromNBT(compound); return Optional.of(new PatternTooltip( pattern, diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java index 3cd0e47c32..96817e052c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java @@ -1,8 +1,6 @@ package at.petrak.hexcasting.common.lib; import at.petrak.hexcasting.api.misc.MediaConstants; -import at.petrak.hexcasting.api.utils.HexUtils; -import at.petrak.hexcasting.api.casting.ActionRegistryEntry; import at.petrak.hexcasting.api.mod.HexTags; import at.petrak.hexcasting.common.items.ItemJewelerHammer; import at.petrak.hexcasting.common.items.ItemLens; @@ -14,8 +12,6 @@ import at.petrak.hexcasting.xplat.IXplatAbstractions; import com.google.common.base.Suppliers; import net.minecraft.Util; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.food.FoodProperties; @@ -161,16 +157,12 @@ public static Item.Properties unstackable() { } private static void generateScrollEntries() { - var keyList = new ArrayList>(); - Registry regi = IXplatAbstractions.INSTANCE.getActionRegistry(); - for (var key : regi.registryKeySet()) - if (HexUtils.isOfTag(regi, key, HexTags.Actions.PER_WORLD_PATTERN)) - keyList.add(key); - keyList.sort( (a, b) -> a.location().compareTo(b.location()) ); - for (var key : keyList) { + var actionIDs = IXplatAbstractions.INSTANCE.getActionRegistry().getTag(HexTags.Actions.PER_WORLD_PATTERN) + .orElseThrow().stream().map(holder -> holder.unwrapKey().orElseThrow().location().toString()) + .sorted().toList(); + for (var id : actionIDs) { addToTab(() -> ItemScroll.withPerWorldPattern( - new ItemStack(HexItems.SCROLL_LARGE), - key.location().toString() + new ItemStack(HexItems.SCROLL_LARGE), id ),HexCreativeTabs.SCROLLS); } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/loot/AddPerWorldPatternToScrollFunc.java b/Common/src/main/java/at/petrak/hexcasting/common/loot/AddPerWorldPatternToScrollFunc.java index ade3941732..b5291ab063 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/loot/AddPerWorldPatternToScrollFunc.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/loot/AddPerWorldPatternToScrollFunc.java @@ -1,9 +1,7 @@ package at.petrak.hexcasting.common.loot; import at.petrak.hexcasting.api.HexAPI; -import at.petrak.hexcasting.api.casting.ActionRegistryEntry; import at.petrak.hexcasting.api.mod.HexTags; -import at.petrak.hexcasting.api.utils.HexUtils; import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.common.casting.PatternRegistryManifest; import at.petrak.hexcasting.common.items.storage.ItemScroll; @@ -12,11 +10,8 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; +import net.minecraft.core.Holder; import net.minecraft.util.RandomSource; -import net.minecraft.core.Registry; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.storage.loot.LootContext; @@ -24,8 +19,6 @@ import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; -import java.util.ArrayList; - /** * Slap a random per-world pattern on the scroll. *

@@ -41,23 +34,17 @@ public AddPerWorldPatternToScrollFunc(LootItemCondition[] lootItemConditions) { * This doesn't actually have any params so extract behaviour out for the benefit of forge */ public static ItemStack doStatic(ItemStack stack, RandomSource rand, ServerLevel overworld) { - var perWorldKeys = new ArrayList>(); - Registry regi = IXplatAbstractions.INSTANCE.getActionRegistry(); - for (var key : regi.registryKeySet()) { - if (HexUtils.isOfTag(regi, key, HexTags.Actions.PER_WORLD_PATTERN)) { - perWorldKeys.add(key); - } - } - var patternKey = perWorldKeys.get(rand.nextInt(perWorldKeys.size())); - var patternID = patternKey.location().toString(); - var pat = PatternRegistryManifest.getCanonicalStrokesPerWorld(patternKey, overworld); + var actionKey = IXplatAbstractions.INSTANCE.getActionRegistry().getTag(HexTags.Actions.PER_WORLD_PATTERN) + .flatMap(set -> set.getRandomElement(rand)).flatMap(Holder::unwrapKey).orElseThrow(); + var actionID = actionKey.location().toString(); + var pat = PatternRegistryManifest.getCanonicalStrokesPerWorld(actionKey, overworld); if (pat == null) { - HexAPI.LOGGER.error("No per-world pattern found for action " + patternID + " while generating loot scroll! " + + HexAPI.LOGGER.error("No per-world pattern found for action " + actionID + " while generating loot scroll! " + "Try running '/hexcasting recalcPatterns' to generate the patterns for this world."); - NBTHelper.putString(stack, ItemScroll.TAG_RECALC_WARNING, patternID); + NBTHelper.putString(stack, ItemScroll.TAG_RECALC_WARNING, actionID); return stack; } - NBTHelper.putString(stack, ItemScroll.TAG_OP_ID, patternID); + NBTHelper.putString(stack, ItemScroll.TAG_OP_ID, actionID); NBTHelper.put(stack, ItemScroll.TAG_PATTERN, pat.serializeToNBT()); return stack; } diff --git a/Common/src/main/java/at/petrak/hexcasting/mixin/MixinWanderingTrader.java b/Common/src/main/java/at/petrak/hexcasting/mixin/MixinWanderingTrader.java index 6ccfa837fc..1d10b8ab2f 100644 --- a/Common/src/main/java/at/petrak/hexcasting/mixin/MixinWanderingTrader.java +++ b/Common/src/main/java/at/petrak/hexcasting/mixin/MixinWanderingTrader.java @@ -1,16 +1,17 @@ package at.petrak.hexcasting.mixin; +import at.petrak.hexcasting.api.mod.HexTags; +import at.petrak.hexcasting.xplat.IXplatAbstractions; +import net.minecraft.core.Holder; import net.minecraft.world.entity.npc.WanderingTrader; import net.minecraft.world.item.trading.MerchantOffers; import net.minecraft.world.item.trading.MerchantOffer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.util.RandomSource; -import net.minecraft.server.MinecraftServer; import at.petrak.hexcasting.api.mod.HexConfig; import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.common.lib.HexItems; -import at.petrak.hexcasting.common.loot.AddPerWorldPatternToScrollFunc; import at.petrak.hexcasting.common.items.storage.ItemScroll; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -28,8 +29,10 @@ private void addNewTrades(CallbackInfo ci) { return; RandomSource rand = self.getRandom(); if (rand.nextFloat() < HexConfig.server().traderScrollChance() && self.getServer() != null) { - ItemStack scroll = new ItemStack(HexItems.SCROLL_LARGE); - AddPerWorldPatternToScrollFunc.doStatic(scroll, rand, self.getServer().overworld()); + var actionID = IXplatAbstractions.INSTANCE.getActionRegistry().getTag(HexTags.Actions.PER_WORLD_PATTERN) + .flatMap(set -> set.getRandomElement(rand)).flatMap(Holder::unwrapKey) + .map(key -> key.location().toString()).orElseThrow(); + ItemStack scroll = ItemScroll.withPerWorldPattern(new ItemStack(HexItems.SCROLL_LARGE), actionID); NBTHelper.putBoolean(scroll, ItemScroll.TAG_NEEDS_PURCHASE, true); offerList.set(5, new MerchantOffer(new ItemStack(Items.EMERALD, 12), scroll, 1, 1, 1)); } diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index 42e0f40ef5..54727c47ba 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -26,13 +26,13 @@ scroll_small: { "": "Small Scroll", - of: "How did you get this item of %s", + of: "Ancient Scroll of %s", empty: "Empty Small Scroll", }, scroll_medium: { "": "Medium Scroll", - of: "How did you get this item of %s", + of: "Ancient Scroll of %s", empty: "Empty Medium Scroll", },