Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scroll tweaks #837

Merged
merged 13 commits into from
Feb 3, 2025
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 @@ -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
Robotgiggle marked this conversation as resolved.
Show resolved Hide resolved
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) {
SamsTheNerd marked this conversation as resolved.
Show resolved Hide resolved
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",
SamsTheNerd marked this conversation as resolved.
Show resolved Hide resolved
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
Loading