Skip to content

Commit

Permalink
Scroll tweaks (#837)
Browse files Browse the repository at this point in the history
  • Loading branch information
SamsTheNerd authored Feb 3, 2025
2 parents 1d3a855 + 4940868 commit 7233cc1
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ public interface ServerConfigAccess {

boolean trueNameHasAmbit();

double traderScrollChance();

int DEFAULT_MAX_OP_COUNT = 100_000;
int DEFAULT_MAX_SPELL_CIRCLE_LENGTH = 1024;
int DEFAULT_OP_BREAK_HARVEST_LEVEL = 3;

double DEFAULT_TRADER_SCROLL_CHANCE = 0.2;

boolean DEFAULT_VILLAGERS_DISLIKE_MIND_MURDER = true;

List<String> DEFAULT_DIM_TP_DENYLIST = List.of("twilightforest:twilight_forest");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,47 @@
import at.petrak.hexcasting.common.entities.EntityWallScroll;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import at.petrak.hexcasting.common.misc.PatternTooltip;
import at.petrak.hexcasting.common.casting.PatternRegistryManifest;
import at.petrak.hexcasting.interop.inline.InlinePatternData;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.tooltip.TooltipComponent;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;

import java.util.Optional;
import java.util.List;

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

/**
* TAG_OP_ID and TAG_PATTERN: "Ancient Scroll of %s" (Great Spells)
* TAG_OP_ID and TAG_PATTERN: "Ancient Scroll of %s" (per-world pattern preloaded)
* <br>
* TAG_OP_ID: "Ancient Scroll of %s" (per-world pattern loaded on inv tick)
* <br>
* TAG_PATTERN: "Scroll" (custom)
* <br>
* (none): "Empty Scroll"
* <br>
* TAG_OP_ID: invalid
*/
public class ItemScroll extends Item implements IotaHolderItem {
public static final String TAG_OP_ID = "op_id";
public static final String TAG_PATTERN = "pattern";
public static final String TAG_NEEDS_PURCHASE = "needs_purchase";
public static final ResourceLocation ANCIENT_PREDICATE = modLoc("ancient");

public final int blockSize;
Expand All @@ -50,6 +59,15 @@ public ItemScroll(Properties pProperties, int blockSize) {
this.blockSize = blockSize;
}

// this produces a scroll that will load the correct pattern for your world once it ticks
public static ItemStack withPerWorldPattern(ItemStack stack, String op_id) {
Item item = stack.getItem();
if (item instanceof ItemScroll)
NBTHelper.putString(stack, TAG_OP_ID, op_id);

return stack;
}

@Override
public @Nullable
CompoundTag readIotaTag(ItemStack stack) {
Expand Down Expand Up @@ -133,7 +151,7 @@ public Component getName(ItemStack pStack) {
return Component.translatable(descID + ".of",
Component.translatable("hexcasting.action." + ResourceLocation.tryParse(ancientId)));
} else if (NBTHelper.hasCompound(pStack, TAG_PATTERN)) {
var compound = NBTHelper.getCompound(pStack, ItemScroll.TAG_PATTERN);
var compound = NBTHelper.getCompound(pStack, TAG_PATTERN);
var patternLabel = Component.literal("");
if (compound != null) {
var pattern = HexPattern.fromNBT(compound);
Expand All @@ -145,16 +163,47 @@ public Component getName(ItemStack pStack) {
}
}

// purposely no hover text
@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
if (NBTHelper.getBoolean(pStack, TAG_NEEDS_PURCHASE)) {
NBTHelper.remove(pStack, TAG_NEEDS_PURCHASE);
}
// if op_id is set but there's no stored pattern, attempt to load the pattern on inv tick
if (NBTHelper.hasString(pStack, TAG_OP_ID) && !NBTHelper.hasCompound(pStack, TAG_PATTERN) && pEntity.getServer() != null) {
var opID = ResourceLocation.tryParse(NBTHelper.getString(pStack, TAG_OP_ID));
if (opID == null) {
// if the provided op_id is invalid, remove it so we don't keep trying every tick
NBTHelper.remove(pStack, TAG_OP_ID);
return;
}
var patternKey = ResourceKey.create(IXplatAbstractions.INSTANCE.getActionRegistry().key(), opID);
var pat = PatternRegistryManifest.getCanonicalStrokesPerWorld(patternKey, pEntity.getServer().overworld());
NBTHelper.put(pStack, TAG_PATTERN, pat.serializeToNBT());
}
}

@Override
public void appendHoverText(ItemStack pStack, @Nullable Level pLevel, List<Component> pTooltipComponents,
TooltipFlag pIsAdvanced) {
if (NBTHelper.getBoolean(pStack, TAG_NEEDS_PURCHASE)) {
var needsPurchase = Component.translatable("hexcasting.tooltip.scroll.needs_purchase");
pTooltipComponents.add(needsPurchase.withStyle(ChatFormatting.GRAY));
} else if (NBTHelper.hasString(pStack, TAG_OP_ID) && !NBTHelper.hasCompound(pStack, TAG_PATTERN)) {
var notLoaded = Component.translatable("hexcasting.tooltip.scroll.pattern_not_loaded");
pTooltipComponents.add(notLoaded.withStyle(ChatFormatting.GRAY));
}
}

@Override
public Optional<TooltipComponent> getTooltipImage(ItemStack stack) {
var compound = NBTHelper.getCompound(stack, ItemScroll.TAG_PATTERN);
if (compound != null) {
var compound = NBTHelper.getCompound(stack, TAG_PATTERN);
if (compound != null && !NBTHelper.getBoolean(stack, TAG_NEEDS_PURCHASE)) {
var pattern = HexPattern.fromNBT(compound);
return Optional.of(new PatternTooltip(
pattern,
NBTHelper.hasString(stack, ItemScroll.TAG_OP_ID)
NBTHelper.hasString(stack, TAG_OP_ID)
? PatternTooltipComponent.ANCIENT_BG
: PatternTooltipComponent.PRISTINE_BG));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.ItemStack;
import at.petrak.hexcasting.common.items.storage.ItemScroll;

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

public static final CreativeModeTab SCROLLS = register("scrolls", CreativeModeTab.builder(CreativeModeTab.Row.TOP, 7)
.icon(() -> ItemScroll.withPerWorldPattern(new ItemStack(HexItems.SCROLL_LARGE),"")));

private static CreativeModeTab register(String name, CreativeModeTab.Builder tabBuilder) {
var tab = tabBuilder.title(Component.translatable("itemGroup." + name)).build();
var tab = tabBuilder.title(Component.translatable("itemGroup.hexcasting." + name)).build();
var old = TABS.put(modLoc(name), tab);
if (old != null) {
throw new IllegalArgumentException("Typo? Duplicate id " + name);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
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;
import at.petrak.hexcasting.common.items.ItemLoreFragment;
Expand All @@ -14,6 +17,8 @@
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;
Expand All @@ -35,6 +40,7 @@ public static void registerItems(BiConsumer<Item, ResourceLocation> r) {
}

public static void registerItemCreativeTab(CreativeModeTab.Output r, CreativeModeTab tab) {
if (tab == HexCreativeTabs.SCROLLS) generateScrollEntries();
for (var item : ITEM_TABS.getOrDefault(tab, List.of())) {
item.register(r);
}
Expand Down Expand Up @@ -107,7 +113,6 @@ public static void registerItemCreativeTab(CreativeModeTab.Output r, CreativeMod
new ItemStack(HexItems.BATTERY),
MediaConstants.QUENCHED_SHARD_UNIT * 64,
MediaConstants.QUENCHED_SHARD_UNIT * 64), HexCreativeTabs.HEX);

public static final Supplier<ItemStack> BATTERY_QUENCHED_BLOCK_STACK = addToTab(() -> ItemMediaBattery.withMedia(
new ItemStack(HexItems.BATTERY),
MediaConstants.QUENCHED_BLOCK_UNIT * 64,
Expand Down Expand Up @@ -156,6 +161,21 @@ public static Item.Properties unstackable() {
return props().stacksTo(1);
}

private static void generateScrollEntries() {
var keyList = new ArrayList<ResourceKey<ActionRegistryEntry>>();
Registry<ActionRegistryEntry> 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) {
addToTab(() -> ItemScroll.withPerWorldPattern(
new ItemStack(HexItems.SCROLL_LARGE),
key.location().toString()
),HexCreativeTabs.SCROLLS);
}
}

private static <T extends Item> T make(ResourceLocation id, T item, @Nullable CreativeModeTab tab) {
var old = ITEMS.put(id, item);
if (old != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
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;
import at.petrak.hexcasting.common.lib.HexLootFunctions;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
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;
import net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction;
Expand All @@ -35,30 +39,24 @@ 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, LootContext ctx) {
var rand = ctx.getRandom();
public static ItemStack doStatic(ItemStack stack, RandomSource rand, ServerLevel overworld) {
var perWorldKeys = new ArrayList<ResourceKey<ActionRegistryEntry>>();
Registry<ActionRegistryEntry> regi = IXplatAbstractions.INSTANCE.getActionRegistry();
for (var key : regi.registryKeySet()) {
if (HexUtils.isOfTag(regi, key, HexTags.Actions.PER_WORLD_PATTERN)) {
perWorldKeys.add(key);
}
}
var key = perWorldKeys.get(rand.nextInt(perWorldKeys.size()));

var pat = PatternRegistryManifest.getCanonicalStrokesPerWorld(key, ctx.getLevel().getServer().overworld());
var tag = new CompoundTag();
tag.putString(ItemScroll.TAG_OP_ID, key.location().toString());
tag.put(ItemScroll.TAG_PATTERN, pat.serializeToNBT());

stack.getOrCreateTag().merge(tag);

var patternKey = perWorldKeys.get(rand.nextInt(perWorldKeys.size()));
var pat = PatternRegistryManifest.getCanonicalStrokesPerWorld(patternKey, overworld);
NBTHelper.putString(stack, ItemScroll.TAG_OP_ID, patternKey.location().toString());
NBTHelper.put(stack, ItemScroll.TAG_PATTERN, pat.serializeToNBT());
return stack;
}

@Override
protected ItemStack run(ItemStack stack, LootContext ctx) {
return doStatic(stack, ctx);
return doStatic(stack, ctx.getRandom(), ctx.getLevel().getServer().overworld());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package at.petrak.hexcasting.mixin;

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;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

// Adds ancient scrolls to the wandering trader by replacing the special trade in the last slot
@Mixin(WanderingTrader.class)
public class MixinWanderingTrader {
@Inject(method = "updateTrades", at = @At("RETURN"))
private void addNewTrades(CallbackInfo ci) {
var self = (WanderingTrader) (Object) this;
MerchantOffers offerList = self.getOffers();
if (offerList == null)
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());
NBTHelper.putBoolean(scroll, ItemScroll.TAG_NEEDS_PURCHASE, true);
offerList.set(5, new MerchantOffer(new ItemStack(Items.EMERALD, 12), scroll, 1, 1, 1));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@
},

"itemGroup.hexcasting": {
"": "Hexcasting",
creative_tab: "Hexcasting",
hexcasting: "Hex Casting",
scrolls: "Hex Casting (Scrolls)",
},

"gui.hexcasting": {
Expand Down Expand Up @@ -377,6 +377,10 @@
"": "Villagers Offended By Mind Murder",
"@Tooltip": "Whether villagers should be angry at the player when other villagers are mindflayed",
},
traderScrollChance: {
"": "Wandering Trader Scroll Chance",
"@Tooltip": "The chance for wandering traders to sell an Ancient Scroll",
},
scrollInjectionsRaw: {
"": "Scroll Injection Weights",
"@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.",
Expand All @@ -401,11 +405,6 @@
"": "Amethyst Shard Drop Rate Change",
"@Tooltip": "How much the number of amethyst shards dropped from clusters is increased/decreased.",
},

// TODO: are these used anywhere??
"fewScrollTables.@Tooltip": "Loot tables that a small number of Ancient Scrolls are injected into",
"someScrollTables.@Tooltip": "Loot tables that a decent number of Ancient Scrolls are injected into",
"manyScrollTables.@Tooltip": "Loot tables that a huge number of Ancient Scrolls are injected into",
},
},
},
Expand Down Expand Up @@ -561,6 +560,11 @@
},
sealed: "Sealed",
},

scroll: {
needs_purchase: "Purchase to show pattern",
pattern_not_loaded: "Place in inventory to load pattern",
},

abacus: {
"": "%d",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@
},

"itemGroup.hexcasting": {
"": "Hexcasting",
creative_tab: "Hexcasting",
hexcasting: "Hex Casting",
scrolls: "Hex Casting (Scrolls)",
},

"gui.hexcasting": {
Expand Down Expand Up @@ -335,11 +335,6 @@
"": "Amethyst Shard Drop Rate Change",
"@Tooltip": "How much the number of amethyst shards dropped from clusters is increased/decreased.",
},

// TODO: are these used anywhere??
"fewScrollTables.@Tooltip": "Loot tables that a small number of Ancient Scrolls are injected into",
"someScrollTables.@Tooltip": "Loot tables that a decent number of Ancient Scrolls are injected into",
"manyScrollTables.@Tooltip": "Loot tables that a huge number of Ancient Scrolls are injected into",
},
},
},
Expand Down
Loading

0 comments on commit 7233cc1

Please sign in to comment.