make foci/spellbooks overlays based

This commit is contained in:
gamma-delta 2022-06-13 18:38:24 -05:00
parent d0f43ff0d9
commit ba9028010f
47 changed files with 204 additions and 99 deletions

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.api.item; package at.petrak.hexcasting.api.item;
import at.petrak.hexcasting.api.spell.iota.Iota; import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.common.lib.HexIotaTypes; import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
@ -23,6 +24,12 @@ import java.util.List;
* and the appropriate cap/CC will be attached. * and the appropriate cap/CC will be attached.
*/ */
public interface IotaHolderItem { public interface IotaHolderItem {
/**
* If this key is set on the item, we ignore the rest of the item and render this as if it were of the
* {@link at.petrak.hexcasting.api.spell.iota.IotaType IotaType} given by the resource location.
* <p>
* This is not useful to the player at all.
*/
String TAG_OVERRIDE_VISUALLY = "VisualOverride"; String TAG_OVERRIDE_VISUALLY = "VisualOverride";
@Nullable CompoundTag readIotaTag(ItemStack stack); @Nullable CompoundTag readIotaTag(ItemStack stack);
@ -47,6 +54,15 @@ public interface IotaHolderItem {
return null; return null;
} }
default int getColor(ItemStack stack) {
var tag = stack.getTag();
if (tag == null) {
return HexUtils.ERROR_COLOR;
}
return HexIotaTypes.getColor(tag);
}
boolean canWrite(ItemStack stack, @Nullable Iota iota); boolean canWrite(ItemStack stack, @Nullable Iota iota);
void writeDatum(ItemStack stack, @Nullable Iota iota); void writeDatum(ItemStack stack, @Nullable Iota iota);

View file

@ -2,9 +2,11 @@
package at.petrak.hexcasting.api.utils package at.petrak.hexcasting.api.utils
import at.petrak.hexcasting.api.spell.LegacySpellDatum
import at.petrak.hexcasting.api.spell.SpellList import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.spell.math.HexCoord import at.petrak.hexcasting.api.spell.math.HexCoord
import at.petrak.hexcasting.common.lib.HexIotaTypes
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.nbt.* import net.minecraft.nbt.*
import net.minecraft.network.chat.* import net.minecraft.network.chat.*
@ -85,7 +87,7 @@ fun pxToCoord(px: Vec2, size: Float, offset: Vec2): HexCoord {
else else
HexCoord(q, r + (rf + 0.5 * qf).roundToInt()) HexCoord(q, r + (rf + 0.5 * qf).roundToInt())
} }
s
fun String.withStyle(op: (Style) -> Style): MutableComponent = asTextComponent.withStyle(op) fun String.withStyle(op: (Style) -> Style): MutableComponent = asTextComponent.withStyle(op)
fun String.withStyle(style: Style): MutableComponent = asTextComponent.withStyle(style) fun String.withStyle(style: Style): MutableComponent = asTextComponent.withStyle(style)
fun String.withStyle(formatting: ChatFormatting): MutableComponent = asTextComponent.withStyle(formatting) fun String.withStyle(formatting: ChatFormatting): MutableComponent = asTextComponent.withStyle(formatting)
@ -231,13 +233,11 @@ inline operator fun <T> WeakValue<T>.setValue(thisRef: Any?, property: KProperty
/** /**
* Returns an empty list if it's too complicated. * Returns an empty list if it's too complicated.
*/ */
fun Iterable<LegacySpellDatum<*>>.serializeToNBT(): ListTag { fun Iterable<Iota>.serializeToNBT() =
val out = LegacySpellDatum.make(SpellList.LList(0, this.toList())).serializeToNBT() if (HexIotaTypes.isTooLargeToSerialize(this))
return if (out.contains(LegacySpellDatum.TAG_WIDGET))
ListTag() ListTag()
else else
out.getList(LegacySpellDatum.TAG_LIST, Tag.TAG_COMPOUND) ListIota(SpellList.LList(this.toList())).serialize()
}
// Copy the impl from forge // Copy the impl from forge
fun ItemStack.serializeToNBT(): CompoundTag { fun ItemStack.serializeToNBT(): CompoundTag {
@ -247,7 +247,7 @@ fun ItemStack.serializeToNBT(): CompoundTag {
} }
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun <T : Tag> Tag.downcast(type: TagType<T>) : T { fun <T : Tag> Tag.downcast(type: TagType<T>): T {
if (this.type == type) { if (this.type == type) {
return this as T return this as T
} else { } else {
@ -256,3 +256,5 @@ fun <T : Tag> Tag.downcast(type: TagType<T>) : T {
) )
} }
} }
const val ERROR_COLOR = 0xff_f800f8.toInt()

View file

@ -3,11 +3,8 @@ package at.petrak.hexcasting.client;
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus; import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus; import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry; import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.item.MediaHolderItem; import at.petrak.hexcasting.api.item.MediaHolderItem;
import at.petrak.hexcasting.api.misc.ManaConstants; import at.petrak.hexcasting.api.misc.ManaConstants;
import at.petrak.hexcasting.api.spell.LegacySpellDatum;
import at.petrak.hexcasting.api.spell.Widget;
import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.client.be.BlockEntityAkashicBookshelfRenderer; import at.petrak.hexcasting.client.be.BlockEntityAkashicBookshelfRenderer;
import at.petrak.hexcasting.client.be.BlockEntitySlateRenderer; import at.petrak.hexcasting.client.be.BlockEntitySlateRenderer;
@ -16,10 +13,7 @@ import at.petrak.hexcasting.client.particles.ConjureParticle;
import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicBookshelf; import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicBookshelf;
import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicRecord; import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicRecord;
import at.petrak.hexcasting.common.entities.HexEntities; import at.petrak.hexcasting.common.entities.HexEntities;
import at.petrak.hexcasting.common.items.ItemFocus; import at.petrak.hexcasting.common.items.*;
import at.petrak.hexcasting.common.items.ItemScroll;
import at.petrak.hexcasting.common.items.ItemSlate;
import at.petrak.hexcasting.common.items.ItemWand;
import at.petrak.hexcasting.common.items.magic.ItemMediaBattery; import at.petrak.hexcasting.common.items.magic.ItemMediaBattery;
import at.petrak.hexcasting.common.items.magic.ItemPackagedHex; import at.petrak.hexcasting.common.items.magic.ItemPackagedHex;
import at.petrak.hexcasting.common.lib.HexBlockEntities; import at.petrak.hexcasting.common.lib.HexBlockEntities;
@ -29,6 +23,7 @@ import at.petrak.hexcasting.common.lib.HexParticles;
import at.petrak.hexcasting.xplat.IClientXplatAbstractions; import at.petrak.hexcasting.xplat.IClientXplatAbstractions;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.client.color.item.ItemColor;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -49,13 +44,13 @@ import net.minecraft.world.level.material.MaterialColor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Locale; import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
public class RegisterClientStuff { public class RegisterClientStuff {
public static void init() { public static void init() {
registerDataHolderOverrides(HexItems.FOCUS);
registerDataHolderOverrides(HexItems.SPELLBOOK);
registerPackagedSpellOverrides(HexItems.CYPHER); registerPackagedSpellOverrides(HexItems.CYPHER);
registerPackagedSpellOverrides(HexItems.TRINKET); registerPackagedSpellOverrides(HexItems.TRINKET);
registerPackagedSpellOverrides(HexItems.ARTIFACT); registerPackagedSpellOverrides(HexItems.ARTIFACT);
@ -107,6 +102,43 @@ public class RegisterClientStuff {
addScryingLensStuff(); addScryingLensStuff();
} }
public static void registerColorProviders(BiConsumer<ItemColor, Item> colorProviderRegistry) {
colorProviderRegistry.accept(makeIotaStorageColorer(
stack -> HexItems.FOCUS.readIotaTag(stack) != null,
ItemFocus::isSealed,
HexItems.FOCUS::getColor),
HexItems.FOCUS);
colorProviderRegistry.accept(makeIotaStorageColorer(
stack -> HexItems.SPELLBOOK.readIotaTag(stack) != null,
ItemSpellbook::isSealed,
HexItems.SPELLBOOK::getColor),
HexItems.SPELLBOOK);
}
/**
* Helper function to colorize the layers of an item that stores an iota, in the manner of foci and spellbooks.
* <br>
* 0 = base; 1 = unsealed overlay; 2 = sealed overlay.
*/
public static ItemColor makeIotaStorageColorer(Predicate<ItemStack> hasIota, Predicate<ItemStack> isSealed,
ToIntFunction<ItemStack> getColor) {
return (stack, idx) -> {
if (idx == 0) {
return 0xff_000000;
}
if (!hasIota.test(stack)) {
return 0; // no higher overlays
}
var sealed = isSealed.test(stack);
if (!sealed && idx == 1 || sealed && idx == 2) {
return getColor.applyAsInt(stack);
} else {
return 0; // this layer is invisible
}
};
}
private static void addScryingLensStuff() { private static void addScryingLensStuff() {
ScryingLensOverlayRegistry.addPredicateDisplayer( ScryingLensOverlayRegistry.addPredicateDisplayer(
(state, pos, observer, world, direction, lensHand) -> state.getBlock() instanceof BlockAbstractImpetus, (state, pos, observer, world, direction, lensHand) -> state.getBlock() instanceof BlockAbstractImpetus,
@ -253,32 +285,6 @@ public class RegisterClientStuff {
(stack, level, holder, holderID) -> NBTHelper.hasString(stack, ItemScroll.TAG_OP_ID) ? 1f : 0f); (stack, level, holder, holderID) -> NBTHelper.hasString(stack, ItemScroll.TAG_OP_ID) ? 1f : 0f);
} }
private static void registerDataHolderOverrides(IotaHolderItem item) {
IClientXplatAbstractions.INSTANCE.registerItemProperty((Item) item, ItemFocus.DATATYPE_PRED,
(stack, level, holder, holderID) -> {
var datum = item.readIotaTag(stack);
String override = NBTHelper.getString(stack, IotaHolderItem.TAG_OVERRIDE_VISUALLY);
String typename = null;
if (override != null) {
typename = override;
} else if (datum != null) {
typename = datum.getAllKeys().iterator().next();
}
return typename == null ? 0f : switch (typename) {
case LegacySpellDatum.TAG_ENTITY -> 1f;
case LegacySpellDatum.TAG_DOUBLE -> 2f;
case LegacySpellDatum.TAG_VEC3 -> 3f;
case LegacySpellDatum.TAG_WIDGET -> 4f;
case LegacySpellDatum.TAG_LIST -> 5f;
case LegacySpellDatum.TAG_PATTERN -> 6f;
default -> 0f; // uh oh
};
});
IClientXplatAbstractions.INSTANCE.registerItemProperty((Item) item, ItemFocus.SEALED_PRED,
(stack, level, holder, holderID) -> item.canWrite(stack, LegacySpellDatum.make(Widget.NULL)) ? 0f : 1f);
}
private static void registerPackagedSpellOverrides(ItemPackagedHex item) { private static void registerPackagedSpellOverrides(ItemPackagedHex item) {
IClientXplatAbstractions.INSTANCE.registerItemProperty(item, ItemPackagedHex.HAS_PATTERNS_PRED, IClientXplatAbstractions.INSTANCE.registerItemProperty(item, ItemPackagedHex.HAS_PATTERNS_PRED,
(stack, level, holder, holderID) -> (stack, level, holder, holderID) ->

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.common.blocks.circles.impetuses; package at.petrak.hexcasting.common.blocks.circles.impetuses;
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus; import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
import at.petrak.hexcasting.api.spell.DatumType; import at.petrak.hexcasting.api.spell.iota.EntityIota;
import at.petrak.hexcasting.common.blocks.entity.BlockEntityStoredPlayerImpetus; import at.petrak.hexcasting.common.blocks.entity.BlockEntityStoredPlayerImpetus;
import at.petrak.hexcasting.common.lib.HexSounds; import at.petrak.hexcasting.common.lib.HexSounds;
import at.petrak.hexcasting.xplat.IXplatAbstractions; import at.petrak.hexcasting.xplat.IXplatAbstractions;
@ -11,7 +11,6 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
@ -53,15 +52,15 @@ public class BlockStoredPlayerImpetus extends BlockAbstractImpetus {
if (datumContainer != null) { if (datumContainer != null) {
if (pLevel instanceof ServerLevel level) { if (pLevel instanceof ServerLevel level) {
var stored = datumContainer.readIota(level); var stored = datumContainer.readIota(level);
if (stored != null && stored.getType() == DatumType.ENTITY) { if (stored instanceof EntityIota eieio) {
var entity = (Entity) stored.getPayload(); var entity = eieio.getEntity();
if (entity instanceof Player player) { if (entity instanceof Player player) {
// phew, we got something // phew, we got something
tile.setPlayer(player.getGameProfile(), entity.getUUID()); tile.setPlayer(player.getGameProfile(), entity.getUUID());
level.sendBlockUpdated(pPos, pState, pState, Block.UPDATE_CLIENTS); level.sendBlockUpdated(pPos, pState, pState, Block.UPDATE_CLIENTS);
pLevel.playSound(null, pPos, HexSounds.IMPETUS_STOREDPLAYER_DING, SoundSource.BLOCKS, pLevel.playSound(pPlayer, pPos, HexSounds.IMPETUS_STOREDPLAYER_DING,
1f, 1f); SoundSource.BLOCKS, 1f, 1f);
} }
} }
} }

View file

@ -1,9 +1,10 @@
package at.petrak.hexcasting.common.items; package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.api.item.IotaHolderItem; import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.spell.LegacySpellDatum; import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.spell.Widget; import at.petrak.hexcasting.api.spell.iota.NullIota;
import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@ -39,22 +40,22 @@ public class ItemFocus extends Item implements IotaHolderItem {
} }
@Override @Override
public @Nullable LegacySpellDatum<?> emptyIota(ItemStack stack) { public @Nullable Iota emptyIota(ItemStack stack) {
return LegacySpellDatum.make(Widget.NULL); return new NullIota();
} }
@Override @Override
public boolean canWrite(ItemStack stack, LegacySpellDatum<?> datum) { public boolean canWrite(ItemStack stack, Iota datum) {
return datum == null || !NBTHelper.getBoolean(stack, TAG_SEALED); return datum == null || !NBTHelper.getBoolean(stack, TAG_SEALED);
} }
@Override @Override
public void writeDatum(ItemStack stack, LegacySpellDatum<?> datum) { public void writeDatum(ItemStack stack, Iota datum) {
if (datum == null) { if (datum == null) {
stack.removeTagKey(TAG_DATA); stack.removeTagKey(TAG_DATA);
stack.removeTagKey(TAG_SEALED); stack.removeTagKey(TAG_SEALED);
} else if (!NBTHelper.getBoolean(stack, TAG_SEALED)) { } else if (!isSealed(stack)) {
NBTHelper.put(stack, TAG_DATA, datum.serializeToNBT()); NBTHelper.put(stack, TAG_DATA, HexIotaTypes.serialize(datum));
} }
} }
@ -63,4 +64,8 @@ public class ItemFocus extends Item implements IotaHolderItem {
TooltipFlag pIsAdvanced) { TooltipFlag pIsAdvanced) {
IotaHolderItem.appendHoverText(this, pStack, pTooltipComponents, pIsAdvanced); IotaHolderItem.appendHoverText(this, pStack, pTooltipComponents, pIsAdvanced);
} }
public static boolean isSealed(ItemStack stack) {
return NBTHelper.getBoolean(stack, TAG_SEALED);
}
} }

View file

@ -1,9 +1,10 @@
package at.petrak.hexcasting.common.items; package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.api.item.IotaHolderItem; import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.spell.LegacySpellDatum; import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.spell.Widget; import at.petrak.hexcasting.api.spell.iota.NullIota;
import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
@ -44,11 +45,11 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
@Override @Override
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltip, public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltip,
TooltipFlag isAdvanced) { TooltipFlag isAdvanced) {
boolean sealed = IsSealed(stack); boolean sealed = isSealed(stack);
boolean empty = false; boolean empty = false;
if (NBTHelper.hasNumber(stack, TAG_SELECTED_PAGE)) { if (NBTHelper.hasNumber(stack, TAG_SELECTED_PAGE)) {
var pageIdx = NBTHelper.getInt(stack, TAG_SELECTED_PAGE); var pageIdx = NBTHelper.getInt(stack, TAG_SELECTED_PAGE);
int highest = HighestPage(stack); int highest = highestPage(stack);
if (highest != 0) { if (highest != 0) {
if (sealed) { if (sealed) {
tooltip.add(new TranslatableComponent("hexcasting.tooltip.spellbook.page.sealed", tooltip.add(new TranslatableComponent("hexcasting.tooltip.spellbook.page.sealed",
@ -93,7 +94,7 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
@Override @Override
public void inventoryTick(ItemStack stack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) { public void inventoryTick(ItemStack stack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
int index = GetPage(stack, 0); int index = getPage(stack, 0);
NBTHelper.putInt(stack, TAG_SELECTED_PAGE, index); NBTHelper.putInt(stack, TAG_SELECTED_PAGE, index);
int shiftedIdx = Math.max(1, index); int shiftedIdx = Math.max(1, index);
@ -106,14 +107,14 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
} }
} }
public static boolean ArePagesEmpty(ItemStack stack) { public static boolean arePagesEmpty(ItemStack stack) {
CompoundTag tag = NBTHelper.getCompound(stack, TAG_PAGES); CompoundTag tag = NBTHelper.getCompound(stack, TAG_PAGES);
return tag == null || tag.isEmpty(); return tag == null || tag.isEmpty();
} }
@Override @Override
public @Nullable CompoundTag readIotaTag(ItemStack stack) { public @Nullable CompoundTag readIotaTag(ItemStack stack) {
int idx = GetPage(stack, 1); int idx = getPage(stack, 1);
var key = String.valueOf(idx); var key = String.valueOf(idx);
var tag = NBTHelper.getCompound(stack, TAG_PAGES); var tag = NBTHelper.getCompound(stack, TAG_PAGES);
if (tag != null && tag.contains(key, Tag.TAG_COMPOUND)) { if (tag != null && tag.contains(key, Tag.TAG_COMPOUND)) {
@ -124,22 +125,22 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
} }
@Override @Override
public @Nullable LegacySpellDatum<?> emptyIota(ItemStack stack) { public @Nullable Iota emptyIota(ItemStack stack) {
return LegacySpellDatum.make(Widget.NULL); return new NullIota();
} }
@Override @Override
public boolean canWrite(ItemStack stack, LegacySpellDatum<?> datum) { public boolean canWrite(ItemStack stack, Iota datum) {
return datum == null || !IsSealed(stack); return datum == null || !isSealed(stack);
} }
@Override @Override
public void writeDatum(ItemStack stack, LegacySpellDatum<?> datum) { public void writeDatum(ItemStack stack, Iota datum) {
if (datum != null && IsSealed(stack)) { if (datum != null && isSealed(stack)) {
return; return;
} }
int idx = GetPage(stack, 1); int idx = getPage(stack, 1);
var key = String.valueOf(idx); var key = String.valueOf(idx);
CompoundTag pages = NBTHelper.getCompound(stack, TAG_PAGES); CompoundTag pages = NBTHelper.getCompound(stack, TAG_PAGES);
if (pages != null) { if (pages != null) {
@ -147,21 +148,21 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
pages.remove(key); pages.remove(key);
NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key); NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key);
} else { } else {
pages.put(key, datum.serializeToNBT()); pages.put(key, HexIotaTypes.serialize(datum));
} }
if (pages.isEmpty()) { if (pages.isEmpty()) {
NBTHelper.remove(stack, TAG_PAGES); NBTHelper.remove(stack, TAG_PAGES);
} }
} else if (datum != null) { } else if (datum != null) {
NBTHelper.getOrCreateCompound(stack, TAG_PAGES).put(key, datum.serializeToNBT()); NBTHelper.getOrCreateCompound(stack, TAG_PAGES).put(key, HexIotaTypes.serialize(datum));
} else { } else {
NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key); NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key);
} }
} }
public static int GetPage(ItemStack stack, int ifEmpty) { public static int getPage(ItemStack stack, int ifEmpty) {
if (ArePagesEmpty(stack)) { if (arePagesEmpty(stack)) {
return ifEmpty; return ifEmpty;
} else if (NBTHelper.hasNumber(stack, TAG_SELECTED_PAGE)) { } else if (NBTHelper.hasNumber(stack, TAG_SELECTED_PAGE)) {
int index = NBTHelper.getInt(stack, TAG_SELECTED_PAGE); int index = NBTHelper.getInt(stack, TAG_SELECTED_PAGE);
@ -174,8 +175,8 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
} }
} }
public static void SetSealed(ItemStack stack, boolean sealed) { public static void setSealed(ItemStack stack, boolean sealed) {
int index = GetPage(stack, 1); int index = getPage(stack, 1);
String nameKey = String.valueOf(index); String nameKey = String.valueOf(index);
CompoundTag names = NBTHelper.getOrCreateCompound(stack, TAG_SEALED); CompoundTag names = NBTHelper.getOrCreateCompound(stack, TAG_SEALED);
@ -194,15 +195,15 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
} }
public static boolean IsSealed(ItemStack stack) { public static boolean isSealed(ItemStack stack) {
int index = GetPage(stack, 1); int index = getPage(stack, 1);
String nameKey = String.valueOf(index); String nameKey = String.valueOf(index);
CompoundTag names = NBTHelper.getCompound(stack, TAG_SEALED); CompoundTag names = NBTHelper.getCompound(stack, TAG_SEALED);
return NBTHelper.getBoolean(names, nameKey); return NBTHelper.getBoolean(names, nameKey);
} }
public static int HighestPage(ItemStack stack) { public static int highestPage(ItemStack stack) {
CompoundTag tag = NBTHelper.getCompound(stack, TAG_PAGES); CompoundTag tag = NBTHelper.getCompound(stack, TAG_PAGES);
if (tag == null) { if (tag == null) {
return 0; return 0;
@ -216,8 +217,8 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
}).max(Integer::compare).orElse(0); }).max(Integer::compare).orElse(0);
} }
public static int RotatePageIdx(ItemStack stack, boolean increase) { public static int rotatePageIdx(ItemStack stack, boolean increase) {
int idx = GetPage(stack, 0); int idx = getPage(stack, 0);
if (idx != 0) { if (idx != 0) {
idx += increase ? 1 : -1; idx += increase ? 1 : -1;
idx = Math.max(1, idx); idx = Math.max(1, idx);

View file

@ -2,7 +2,9 @@ package at.petrak.hexcasting.common.lib;
import at.petrak.hexcasting.api.HexAPI; import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.spell.iota.*; import at.petrak.hexcasting.api.spell.iota.*;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.xplat.IXplatAbstractions; import at.petrak.hexcasting.xplat.IXplatAbstractions;
import com.mojang.datafixers.util.Pair;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
@ -12,6 +14,8 @@ import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -26,6 +30,8 @@ public class HexIotaTypes {
public static final String public static final String
KEY_TYPE = HexAPI.MOD_ID + ":type", KEY_TYPE = HexAPI.MOD_ID + ":type",
KEY_DATA = HexAPI.MOD_ID + ":data"; KEY_DATA = HexAPI.MOD_ID + ":data";
public static final int MAX_SERIALIZATION_DEPTH = 256;
public static final int MAX_SERIALIZATION_TOTAL = 1024;
public static CompoundTag serialize(Iota iota) { public static CompoundTag serialize(Iota iota) {
var type = iota.getType(); var type = iota.getType();
@ -35,6 +41,12 @@ public class HexIotaTypes {
"Tried to serialize an unregistered iota type. Iota: " + iota "Tried to serialize an unregistered iota type. Iota: " + iota
+ " ; Type" + type.getClass().getTypeName()); + " ; Type" + type.getClass().getTypeName());
} }
// We check if it's too big on serialization; if it is we just return a garbage.
if (iota instanceof ListIota listIota && isTooLargeToSerialize(listIota.getList())) {
// Garbage will never be too large so we just recurse
return serialize(new GarbageIota());
}
var dataTag = iota.serialize(); var dataTag = iota.serialize();
var out = new CompoundTag(); var out = new CompoundTag();
out.putString(KEY_TYPE, typeId.toString()); out.putString(KEY_TYPE, typeId.toString());
@ -42,6 +54,34 @@ public class HexIotaTypes {
return out; return out;
} }
public static boolean isTooLargeToSerialize(Iterable<Iota> examinee) {
// We don't recurse here, just a work queue (or work stack, if we liked.)
// Each element is a found sub-iota, and how deep it is.
//
// TODO: is it worth trying to cache the depth and size statically on a SpellList.
var listsToExamine = new ArrayDeque<>(Collections.singleton(new Pair<>(examinee, 0)));
int totalEltsFound = 1; // count the first list
while (!listsToExamine.isEmpty()) {
var iotaPair = listsToExamine.removeFirst();
var sublist = iotaPair.getFirst();
int depth = iotaPair.getSecond();
for (var iota : sublist) {
totalEltsFound++;
if (totalEltsFound >= MAX_SERIALIZATION_TOTAL) {
return true; // too bad
}
if (iota instanceof ListIota subsublist) {
if (depth + 1 >= MAX_SERIALIZATION_DEPTH) {
return true;
}
listsToExamine.addLast(new Pair<>(subsublist.getList(), depth + 1));
}
}
}
// we made it!
return false;
}
/** /**
* This method attempts to find the type from the {@code type} key. * This method attempts to find the type from the {@code type} key.
* See {@link HexIotaTypes#getTypeFromTag} for the storage format. * See {@link HexIotaTypes#getTypeFromTag} for the storage format.
@ -98,6 +138,14 @@ public class HexIotaTypes {
return type.display(tag); return type.display(tag);
} }
public static int getColor(CompoundTag tag) {
var type = getTypeFromTag(tag);
if (type == null) {
return HexUtils.ERROR_COLOR;
}
return type.color();
}
@ApiStatus.Internal @ApiStatus.Internal
public static void registerTypes() { public static void registerTypes() {
BiConsumer<IotaType<?>, ResourceLocation> r = (type, id) -> Registry.register(REGISTRY, id, type); BiConsumer<IotaType<?>, ResourceLocation> r = (type, id) -> Registry.register(REGISTRY, id, type);

View file

@ -60,11 +60,11 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
} }
private void spellbook(ServerPlayer sender, ItemStack stack) { private void spellbook(ServerPlayer sender, ItemStack stack) {
var newIdx = ItemSpellbook.RotatePageIdx(stack, this.scrollDelta < 0.0); var newIdx = ItemSpellbook.rotatePageIdx(stack, this.scrollDelta < 0.0);
var len = ItemSpellbook.HighestPage(stack); var len = ItemSpellbook.highestPage(stack);
var sealed = ItemSpellbook.IsSealed(stack); var sealed = ItemSpellbook.isSealed(stack);
MutableComponent component; MutableComponent component;
if (hand == InteractionHand.OFF_HAND && stack.hasCustomHoverName()) { if (hand == InteractionHand.OFF_HAND && stack.hasCustomHoverName()) {

View file

@ -22,7 +22,7 @@ public class SealSpellbookRecipe extends ShapelessRecipe {
private static ItemStack getSealedStack() { private static ItemStack getSealedStack() {
ItemStack output = new ItemStack(HexItems.SPELLBOOK); ItemStack output = new ItemStack(HexItems.SPELLBOOK);
ItemSpellbook.SetSealed(output, true); ItemSpellbook.setSealed(output, true);
NBTHelper.putString(output, IotaHolderItem.TAG_OVERRIDE_VISUALLY, "any"); NBTHelper.putString(output, IotaHolderItem.TAG_OVERRIDE_VISUALLY, "any");
return output; return output;
} }
@ -51,7 +51,7 @@ public class SealSpellbookRecipe extends ShapelessRecipe {
} }
if (!out.isEmpty()) { if (!out.isEmpty()) {
ItemSpellbook.SetSealed(out, true); ItemSpellbook.setSealed(out, true);
out.setCount(1); out.setCount(1);
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -9,6 +9,7 @@ import at.petrak.hexcasting.fabric.network.FabricPacketHandler
import net.fabricmc.api.ClientModInitializer import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
@ -47,5 +48,8 @@ object FabricHexClientInitializer : ClientModInitializer {
BlockEntityRendererRegistry.register(type, berp) BlockEntityRendererRegistry.register(type, berp)
} }
}) })
RegisterClientStuff.registerColorProviders { colorizer, item ->
ColorProviderRegistry.ITEM.register(colorizer, item)
}
} }
} }

View file

@ -5,6 +5,7 @@ import at.petrak.hexcasting.client.HexAdditionalRenderers;
import at.petrak.hexcasting.client.RegisterClientStuff; import at.petrak.hexcasting.client.RegisterClientStuff;
import at.petrak.hexcasting.client.ShiftScrollListener; import at.petrak.hexcasting.client.ShiftScrollListener;
import at.petrak.hexcasting.client.shader.HexShaders; import at.petrak.hexcasting.client.shader.HexShaders;
import net.minecraft.client.color.item.ItemColors;
import net.minecraftforge.client.event.*; import net.minecraftforge.client.event.*;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.TickEvent;
@ -16,9 +17,18 @@ import java.io.IOException;
// This is Java because I can't kotlin-fu some of the consumers // This is Java because I can't kotlin-fu some of the consumers
public class ForgeHexClientInitializer { public class ForgeHexClientInitializer {
// We copy Fabric's example; it mixes in on the return of the initializer and sticks it in a global variable.
// So here's our global.
public static ItemColors GLOBAL_ITEM_COLORS;
@SubscribeEvent @SubscribeEvent
public static void clientInit(FMLClientSetupEvent evt) { public static void clientInit(FMLClientSetupEvent evt) {
evt.enqueueWork(RegisterClientStuff::init); evt.enqueueWork(() -> {
RegisterClientStuff.init();
RegisterClientStuff.registerColorProviders((colorizer, item) -> {
GLOBAL_ITEM_COLORS.register(colorizer, item);
});
});
var evBus = MinecraftForge.EVENT_BUS; var evBus = MinecraftForge.EVENT_BUS;

View file

@ -0,0 +1,17 @@
package at.petrak.hexcasting.forge.mixin;
import at.petrak.hexcasting.forge.ForgeHexClientInitializer;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.color.item.ItemColors;
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.CallbackInfoReturnable;
@Mixin(ItemColors.class)
public class ForgeMixinItemColors {
@Inject(method = "createDefault", at = @At("RETURN"))
private static void hex$onCreateDefault(BlockColors blockColors, CallbackInfoReturnable<ItemColors> info) {
ForgeHexClientInitializer.GLOBAL_ITEM_COLORS = info.getReturnValue();
}
}

View file

@ -2,10 +2,9 @@ package at.petrak.hexcasting.forge.recipe;
import at.petrak.hexcasting.api.addldata.ADIotaHolder; import at.petrak.hexcasting.api.addldata.ADIotaHolder;
import at.petrak.hexcasting.api.item.IotaHolderItem; import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.spell.DatumType; import at.petrak.hexcasting.api.spell.iota.NullIota;
import at.petrak.hexcasting.api.spell.LegacySpellDatum;
import at.petrak.hexcasting.api.spell.Widget;
import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import at.petrak.hexcasting.xplat.IXplatAbstractions; import at.petrak.hexcasting.xplat.IXplatAbstractions;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@ -19,18 +18,16 @@ import net.minecraftforge.common.crafting.NBTIngredient;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
public class ForgeUnsealedIngredient extends AbstractIngredient { public class ForgeUnsealedIngredient extends AbstractIngredient {
private final ItemStack stack; private final ItemStack stack;
protected ForgeUnsealedIngredient(ItemStack stack) { protected ForgeUnsealedIngredient(ItemStack stack) {
super(Arrays.stream(DatumType.values()) super(HexIotaTypes.REGISTRY.keySet().stream()
.filter((it) -> it != DatumType.EMPTY && it != DatumType.OTHER) .map(it -> {
.map((type) -> {
ItemStack newStack = stack.copy(); ItemStack newStack = stack.copy();
NBTHelper.putString(newStack, IotaHolderItem.TAG_OVERRIDE_VISUALLY, LegacySpellDatum.tagForType(type)); NBTHelper.putString(newStack, IotaHolderItem.TAG_OVERRIDE_VISUALLY, it.toString());
return new Ingredient.ItemValue(newStack); return new Ingredient.ItemValue(newStack);
})); }));
this.stack = stack; this.stack = stack;
@ -51,7 +48,7 @@ public class ForgeUnsealedIngredient extends AbstractIngredient {
if (this.stack.getItem() == input.getItem() && this.stack.getDamageValue() == input.getDamageValue()) { if (this.stack.getItem() == input.getItem() && this.stack.getDamageValue() == input.getDamageValue()) {
ADIotaHolder holder = IXplatAbstractions.INSTANCE.findDataHolder(this.stack); ADIotaHolder holder = IXplatAbstractions.INSTANCE.findDataHolder(this.stack);
if (holder != null) { if (holder != null) {
return holder.readIotaTag() != null && holder.writeIota(LegacySpellDatum.make(Widget.NULL), true); return holder.readIotaTag() != null && holder.writeIota(new NullIota(), true);
} }
} }

View file

@ -4,5 +4,5 @@
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_17",
"refmap": "hexcasting.mixins.refmap.json", "refmap": "hexcasting.mixins.refmap.json",
"package": "at.petrak.hexcasting.forge.mixin", "package": "at.petrak.hexcasting.forge.mixin",
"mixins": ["ForgeAccessorRegistry", "ForgeMixinCursedRecipeSerializerBase"] "mixins": ["ForgeAccessorRegistry", "ForgeMixinCursedRecipeSerializerBase"], "client": ["ForgeMixinItemColors"]
} }