Skip to content

Commit 7233cc1

Browse files
authored
Scroll tweaks (#837)
2 parents 1d3a855 + 4940868 commit 7233cc1

File tree

13 files changed

+170
-52
lines changed

13 files changed

+170
-52
lines changed

Common/src/main/java/at/petrak/hexcasting/api/mod/HexConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,14 @@ public interface ServerConfigAccess {
7474

7575
boolean trueNameHasAmbit();
7676

77+
double traderScrollChance();
78+
7779
int DEFAULT_MAX_OP_COUNT = 100_000;
7880
int DEFAULT_MAX_SPELL_CIRCLE_LENGTH = 1024;
7981
int DEFAULT_OP_BREAK_HARVEST_LEVEL = 3;
8082

83+
double DEFAULT_TRADER_SCROLL_CHANCE = 0.2;
84+
8185
boolean DEFAULT_VILLAGERS_DISLIKE_MIND_MURDER = true;
8286

8387
List<String> DEFAULT_DIM_TP_DENYLIST = List.of("twilightforest:twilight_forest");

Common/src/main/java/at/petrak/hexcasting/common/items/storage/ItemScroll.java

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,47 @@
99
import at.petrak.hexcasting.common.entities.EntityWallScroll;
1010
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
1111
import at.petrak.hexcasting.common.misc.PatternTooltip;
12+
import at.petrak.hexcasting.common.casting.PatternRegistryManifest;
1213
import at.petrak.hexcasting.interop.inline.InlinePatternData;
14+
import at.petrak.hexcasting.xplat.IXplatAbstractions;
15+
import net.minecraft.ChatFormatting;
1316
import net.minecraft.core.BlockPos;
1417
import net.minecraft.core.Direction;
1518
import net.minecraft.nbt.CompoundTag;
1619
import net.minecraft.network.chat.Component;
1720
import net.minecraft.resources.ResourceLocation;
21+
import net.minecraft.resources.ResourceKey;
1822
import net.minecraft.world.InteractionResult;
23+
import net.minecraft.world.entity.Entity;
1924
import net.minecraft.world.entity.EntityType;
2025
import net.minecraft.world.entity.player.Player;
2126
import net.minecraft.world.inventory.tooltip.TooltipComponent;
2227
import net.minecraft.world.item.Item;
2328
import net.minecraft.world.item.ItemStack;
29+
import net.minecraft.world.item.TooltipFlag;
2430
import net.minecraft.world.item.context.UseOnContext;
2531
import net.minecraft.world.level.gameevent.GameEvent;
32+
import net.minecraft.world.level.Level;
2633
import org.jetbrains.annotations.Nullable;
2734

2835
import java.util.Optional;
36+
import java.util.List;
2937

3038
import static at.petrak.hexcasting.api.HexAPI.modLoc;
3139

3240
/**
33-
* TAG_OP_ID and TAG_PATTERN: "Ancient Scroll of %s" (Great Spells)
41+
* TAG_OP_ID and TAG_PATTERN: "Ancient Scroll of %s" (per-world pattern preloaded)
42+
* <br>
43+
* TAG_OP_ID: "Ancient Scroll of %s" (per-world pattern loaded on inv tick)
3444
* <br>
3545
* TAG_PATTERN: "Scroll" (custom)
3646
* <br>
3747
* (none): "Empty Scroll"
38-
* <br>
39-
* TAG_OP_ID: invalid
4048
*/
4149
public class ItemScroll extends Item implements IotaHolderItem {
4250
public static final String TAG_OP_ID = "op_id";
4351
public static final String TAG_PATTERN = "pattern";
52+
public static final String TAG_NEEDS_PURCHASE = "needs_purchase";
4453
public static final ResourceLocation ANCIENT_PREDICATE = modLoc("ancient");
4554

4655
public final int blockSize;
@@ -50,6 +59,15 @@ public ItemScroll(Properties pProperties, int blockSize) {
5059
this.blockSize = blockSize;
5160
}
5261

62+
// this produces a scroll that will load the correct pattern for your world once it ticks
63+
public static ItemStack withPerWorldPattern(ItemStack stack, String op_id) {
64+
Item item = stack.getItem();
65+
if (item instanceof ItemScroll)
66+
NBTHelper.putString(stack, TAG_OP_ID, op_id);
67+
68+
return stack;
69+
}
70+
5371
@Override
5472
public @Nullable
5573
CompoundTag readIotaTag(ItemStack stack) {
@@ -133,7 +151,7 @@ public Component getName(ItemStack pStack) {
133151
return Component.translatable(descID + ".of",
134152
Component.translatable("hexcasting.action." + ResourceLocation.tryParse(ancientId)));
135153
} else if (NBTHelper.hasCompound(pStack, TAG_PATTERN)) {
136-
var compound = NBTHelper.getCompound(pStack, ItemScroll.TAG_PATTERN);
154+
var compound = NBTHelper.getCompound(pStack, TAG_PATTERN);
137155
var patternLabel = Component.literal("");
138156
if (compound != null) {
139157
var pattern = HexPattern.fromNBT(compound);
@@ -145,16 +163,47 @@ public Component getName(ItemStack pStack) {
145163
}
146164
}
147165

148-
// purposely no hover text
166+
@Override
167+
public void inventoryTick(ItemStack pStack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
168+
// the needs_purchase tag is used so you can't see the pattern on scrolls sold by a wandering trader
169+
// once you put the scroll into your inventory, this removes the tag to reveal the pattern
170+
if (NBTHelper.getBoolean(pStack, TAG_NEEDS_PURCHASE)) {
171+
NBTHelper.remove(pStack, TAG_NEEDS_PURCHASE);
172+
}
173+
// if op_id is set but there's no stored pattern, attempt to load the pattern on inv tick
174+
if (NBTHelper.hasString(pStack, TAG_OP_ID) && !NBTHelper.hasCompound(pStack, TAG_PATTERN) && pEntity.getServer() != null) {
175+
var opID = ResourceLocation.tryParse(NBTHelper.getString(pStack, TAG_OP_ID));
176+
if (opID == null) {
177+
// if the provided op_id is invalid, remove it so we don't keep trying every tick
178+
NBTHelper.remove(pStack, TAG_OP_ID);
179+
return;
180+
}
181+
var patternKey = ResourceKey.create(IXplatAbstractions.INSTANCE.getActionRegistry().key(), opID);
182+
var pat = PatternRegistryManifest.getCanonicalStrokesPerWorld(patternKey, pEntity.getServer().overworld());
183+
NBTHelper.put(pStack, TAG_PATTERN, pat.serializeToNBT());
184+
}
185+
}
186+
187+
@Override
188+
public void appendHoverText(ItemStack pStack, @Nullable Level pLevel, List<Component> pTooltipComponents,
189+
TooltipFlag pIsAdvanced) {
190+
if (NBTHelper.getBoolean(pStack, TAG_NEEDS_PURCHASE)) {
191+
var needsPurchase = Component.translatable("hexcasting.tooltip.scroll.needs_purchase");
192+
pTooltipComponents.add(needsPurchase.withStyle(ChatFormatting.GRAY));
193+
} else if (NBTHelper.hasString(pStack, TAG_OP_ID) && !NBTHelper.hasCompound(pStack, TAG_PATTERN)) {
194+
var notLoaded = Component.translatable("hexcasting.tooltip.scroll.pattern_not_loaded");
195+
pTooltipComponents.add(notLoaded.withStyle(ChatFormatting.GRAY));
196+
}
197+
}
149198

150199
@Override
151200
public Optional<TooltipComponent> getTooltipImage(ItemStack stack) {
152-
var compound = NBTHelper.getCompound(stack, ItemScroll.TAG_PATTERN);
153-
if (compound != null) {
201+
var compound = NBTHelper.getCompound(stack, TAG_PATTERN);
202+
if (compound != null && !NBTHelper.getBoolean(stack, TAG_NEEDS_PURCHASE)) {
154203
var pattern = HexPattern.fromNBT(compound);
155204
return Optional.of(new PatternTooltip(
156205
pattern,
157-
NBTHelper.hasString(stack, ItemScroll.TAG_OP_ID)
206+
NBTHelper.hasString(stack, TAG_OP_ID)
158207
? PatternTooltipComponent.ANCIENT_BG
159208
: PatternTooltipComponent.PRISTINE_BG));
160209
}

Common/src/main/java/at/petrak/hexcasting/common/lib/HexCreativeTabs.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import net.minecraft.resources.ResourceLocation;
55
import net.minecraft.world.item.CreativeModeTab;
66
import net.minecraft.world.item.ItemStack;
7+
import at.petrak.hexcasting.common.items.storage.ItemScroll;
78

89
import java.util.LinkedHashMap;
910
import java.util.Map;
@@ -23,8 +24,11 @@ public static void registerCreativeTabs(BiConsumer<CreativeModeTab, ResourceLoca
2324
public static final CreativeModeTab HEX = register("hexcasting", CreativeModeTab.builder(CreativeModeTab.Row.TOP, 7)
2425
.icon(() -> new ItemStack(HexItems.SPELLBOOK)));
2526

27+
public static final CreativeModeTab SCROLLS = register("scrolls", CreativeModeTab.builder(CreativeModeTab.Row.TOP, 7)
28+
.icon(() -> ItemScroll.withPerWorldPattern(new ItemStack(HexItems.SCROLL_LARGE),"")));
29+
2630
private static CreativeModeTab register(String name, CreativeModeTab.Builder tabBuilder) {
27-
var tab = tabBuilder.title(Component.translatable("itemGroup." + name)).build();
31+
var tab = tabBuilder.title(Component.translatable("itemGroup.hexcasting." + name)).build();
2832
var old = TABS.put(modLoc(name), tab);
2933
if (old != null) {
3034
throw new IllegalArgumentException("Typo? Duplicate id " + name);

Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package at.petrak.hexcasting.common.lib;
22

33
import at.petrak.hexcasting.api.misc.MediaConstants;
4+
import at.petrak.hexcasting.api.utils.HexUtils;
5+
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
6+
import at.petrak.hexcasting.api.mod.HexTags;
47
import at.petrak.hexcasting.common.items.ItemJewelerHammer;
58
import at.petrak.hexcasting.common.items.ItemLens;
69
import at.petrak.hexcasting.common.items.ItemLoreFragment;
@@ -14,6 +17,8 @@
1417
import at.petrak.hexcasting.xplat.IXplatAbstractions;
1518
import com.google.common.base.Suppliers;
1619
import net.minecraft.Util;
20+
import net.minecraft.core.Registry;
21+
import net.minecraft.resources.ResourceKey;
1722
import net.minecraft.resources.ResourceLocation;
1823
import net.minecraft.world.entity.EquipmentSlot;
1924
import net.minecraft.world.food.FoodProperties;
@@ -35,6 +40,7 @@ public static void registerItems(BiConsumer<Item, ResourceLocation> r) {
3540
}
3641

3742
public static void registerItemCreativeTab(CreativeModeTab.Output r, CreativeModeTab tab) {
43+
if (tab == HexCreativeTabs.SCROLLS) generateScrollEntries();
3844
for (var item : ITEM_TABS.getOrDefault(tab, List.of())) {
3945
item.register(r);
4046
}
@@ -107,7 +113,6 @@ public static void registerItemCreativeTab(CreativeModeTab.Output r, CreativeMod
107113
new ItemStack(HexItems.BATTERY),
108114
MediaConstants.QUENCHED_SHARD_UNIT * 64,
109115
MediaConstants.QUENCHED_SHARD_UNIT * 64), HexCreativeTabs.HEX);
110-
111116
public static final Supplier<ItemStack> BATTERY_QUENCHED_BLOCK_STACK = addToTab(() -> ItemMediaBattery.withMedia(
112117
new ItemStack(HexItems.BATTERY),
113118
MediaConstants.QUENCHED_BLOCK_UNIT * 64,
@@ -156,6 +161,21 @@ public static Item.Properties unstackable() {
156161
return props().stacksTo(1);
157162
}
158163

164+
private static void generateScrollEntries() {
165+
var keyList = new ArrayList<ResourceKey<ActionRegistryEntry>>();
166+
Registry<ActionRegistryEntry> regi = IXplatAbstractions.INSTANCE.getActionRegistry();
167+
for (var key : regi.registryKeySet())
168+
if (HexUtils.isOfTag(regi, key, HexTags.Actions.PER_WORLD_PATTERN))
169+
keyList.add(key);
170+
keyList.sort( (a, b) -> a.location().compareTo(b.location()) );
171+
for (var key : keyList) {
172+
addToTab(() -> ItemScroll.withPerWorldPattern(
173+
new ItemStack(HexItems.SCROLL_LARGE),
174+
key.location().toString()
175+
),HexCreativeTabs.SCROLLS);
176+
}
177+
}
178+
159179
private static <T extends Item> T make(ResourceLocation id, T item, @Nullable CreativeModeTab tab) {
160180
var old = ITEMS.put(id, item);
161181
if (old != null) {

Common/src/main/java/at/petrak/hexcasting/common/loot/AddPerWorldPatternToScrollFunc.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@
33
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
44
import at.petrak.hexcasting.api.mod.HexTags;
55
import at.petrak.hexcasting.api.utils.HexUtils;
6+
import at.petrak.hexcasting.api.utils.NBTHelper;
67
import at.petrak.hexcasting.common.casting.PatternRegistryManifest;
78
import at.petrak.hexcasting.common.items.storage.ItemScroll;
89
import at.petrak.hexcasting.common.lib.HexLootFunctions;
910
import at.petrak.hexcasting.xplat.IXplatAbstractions;
1011
import com.google.gson.JsonDeserializationContext;
1112
import com.google.gson.JsonObject;
1213
import com.google.gson.JsonSerializationContext;
14+
import net.minecraft.util.RandomSource;
1315
import net.minecraft.core.Registry;
1416
import net.minecraft.nbt.CompoundTag;
1517
import net.minecraft.resources.ResourceKey;
18+
import net.minecraft.resources.ResourceLocation;
19+
import net.minecraft.server.level.ServerLevel;
1620
import net.minecraft.world.item.ItemStack;
1721
import net.minecraft.world.level.storage.loot.LootContext;
1822
import net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction;
@@ -35,30 +39,24 @@ public AddPerWorldPatternToScrollFunc(LootItemCondition[] lootItemConditions) {
3539
/**
3640
* This doesn't actually have any params so extract behaviour out for the benefit of forge
3741
*/
38-
public static ItemStack doStatic(ItemStack stack, LootContext ctx) {
39-
var rand = ctx.getRandom();
42+
public static ItemStack doStatic(ItemStack stack, RandomSource rand, ServerLevel overworld) {
4043
var perWorldKeys = new ArrayList<ResourceKey<ActionRegistryEntry>>();
4144
Registry<ActionRegistryEntry> regi = IXplatAbstractions.INSTANCE.getActionRegistry();
4245
for (var key : regi.registryKeySet()) {
4346
if (HexUtils.isOfTag(regi, key, HexTags.Actions.PER_WORLD_PATTERN)) {
4447
perWorldKeys.add(key);
4548
}
4649
}
47-
var key = perWorldKeys.get(rand.nextInt(perWorldKeys.size()));
48-
49-
var pat = PatternRegistryManifest.getCanonicalStrokesPerWorld(key, ctx.getLevel().getServer().overworld());
50-
var tag = new CompoundTag();
51-
tag.putString(ItemScroll.TAG_OP_ID, key.location().toString());
52-
tag.put(ItemScroll.TAG_PATTERN, pat.serializeToNBT());
53-
54-
stack.getOrCreateTag().merge(tag);
55-
50+
var patternKey = perWorldKeys.get(rand.nextInt(perWorldKeys.size()));
51+
var pat = PatternRegistryManifest.getCanonicalStrokesPerWorld(patternKey, overworld);
52+
NBTHelper.putString(stack, ItemScroll.TAG_OP_ID, patternKey.location().toString());
53+
NBTHelper.put(stack, ItemScroll.TAG_PATTERN, pat.serializeToNBT());
5654
return stack;
5755
}
5856

5957
@Override
6058
protected ItemStack run(ItemStack stack, LootContext ctx) {
61-
return doStatic(stack, ctx);
59+
return doStatic(stack, ctx.getRandom(), ctx.getLevel().getServer().overworld());
6260
}
6361

6462
@Override
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package at.petrak.hexcasting.mixin;
2+
3+
import net.minecraft.world.entity.npc.WanderingTrader;
4+
import net.minecraft.world.item.trading.MerchantOffers;
5+
import net.minecraft.world.item.trading.MerchantOffer;
6+
import net.minecraft.world.item.ItemStack;
7+
import net.minecraft.world.item.Items;
8+
import net.minecraft.util.RandomSource;
9+
import net.minecraft.server.MinecraftServer;
10+
import at.petrak.hexcasting.api.mod.HexConfig;
11+
import at.petrak.hexcasting.api.utils.NBTHelper;
12+
import at.petrak.hexcasting.common.lib.HexItems;
13+
import at.petrak.hexcasting.common.loot.AddPerWorldPatternToScrollFunc;
14+
import at.petrak.hexcasting.common.items.storage.ItemScroll;
15+
import org.spongepowered.asm.mixin.Mixin;
16+
import org.spongepowered.asm.mixin.injection.At;
17+
import org.spongepowered.asm.mixin.injection.Inject;
18+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
19+
20+
// Adds ancient scrolls to the wandering trader by replacing the special trade in the last slot
21+
@Mixin(WanderingTrader.class)
22+
public class MixinWanderingTrader {
23+
@Inject(method = "updateTrades", at = @At("RETURN"))
24+
private void addNewTrades(CallbackInfo ci) {
25+
var self = (WanderingTrader) (Object) this;
26+
MerchantOffers offerList = self.getOffers();
27+
if (offerList == null)
28+
return;
29+
RandomSource rand = self.getRandom();
30+
if (rand.nextFloat() < HexConfig.server().traderScrollChance() && self.getServer() != null) {
31+
ItemStack scroll = new ItemStack(HexItems.SCROLL_LARGE);
32+
AddPerWorldPatternToScrollFunc.doStatic(scroll, rand, self.getServer().overworld());
33+
NBTHelper.putBoolean(scroll, ItemScroll.TAG_NEEDS_PURCHASE, true);
34+
offerList.set(5, new MerchantOffer(new ItemStack(Items.EMERALD, 12), scroll, 1, 1, 1));
35+
}
36+
}
37+
}

Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,8 @@
257257
},
258258

259259
"itemGroup.hexcasting": {
260-
"": "Hexcasting",
261-
creative_tab: "Hexcasting",
260+
hexcasting: "Hex Casting",
261+
scrolls: "Hex Casting (Scrolls)",
262262
},
263263

264264
"gui.hexcasting": {
@@ -377,6 +377,10 @@
377377
"": "Villagers Offended By Mind Murder",
378378
"@Tooltip": "Whether villagers should be angry at the player when other villagers are mindflayed",
379379
},
380+
traderScrollChance: {
381+
"": "Wandering Trader Scroll Chance",
382+
"@Tooltip": "The chance for wandering traders to sell an Ancient Scroll",
383+
},
380384
scrollInjectionsRaw: {
381385
"": "Scroll Injection Weights",
382386
"@Tooltip": "Maps the names of loot tables to the amount of per-world patterns on scrolls should go in them. There's about a 50% chance to get any scrolls in a given chest marked here; once that is met, between 1 and that many scrolls are generated.",
@@ -401,11 +405,6 @@
401405
"": "Amethyst Shard Drop Rate Change",
402406
"@Tooltip": "How much the number of amethyst shards dropped from clusters is increased/decreased.",
403407
},
404-
405-
// TODO: are these used anywhere??
406-
"fewScrollTables.@Tooltip": "Loot tables that a small number of Ancient Scrolls are injected into",
407-
"someScrollTables.@Tooltip": "Loot tables that a decent number of Ancient Scrolls are injected into",
408-
"manyScrollTables.@Tooltip": "Loot tables that a huge number of Ancient Scrolls are injected into",
409408
},
410409
},
411410
},
@@ -561,6 +560,11 @@
561560
},
562561
sealed: "Sealed",
563562
},
563+
564+
scroll: {
565+
needs_purchase: "Purchase to show pattern",
566+
pattern_not_loaded: "Place in inventory to load pattern",
567+
},
564568

565569
abacus: {
566570
"": "%d",

Common/src/main/resources/assets/hexcasting/lang/ru_ru.flatten.json5

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@
211211
},
212212

213213
"itemGroup.hexcasting": {
214-
"": "Hexcasting",
215-
creative_tab: "Hexcasting",
214+
hexcasting: "Hex Casting",
215+
scrolls: "Hex Casting (Scrolls)",
216216
},
217217

218218
"gui.hexcasting": {
@@ -335,11 +335,6 @@
335335
"": "Amethyst Shard Drop Rate Change",
336336
"@Tooltip": "How much the number of amethyst shards dropped from clusters is increased/decreased.",
337337
},
338-
339-
// TODO: are these used anywhere??
340-
"fewScrollTables.@Tooltip": "Loot tables that a small number of Ancient Scrolls are injected into",
341-
"someScrollTables.@Tooltip": "Loot tables that a decent number of Ancient Scrolls are injected into",
342-
"manyScrollTables.@Tooltip": "Loot tables that a huge number of Ancient Scrolls are injected into",
343338
},
344339
},
345340
},

0 commit comments

Comments
 (0)