This commit is contained in:
yrsegal@gmail.com 2022-04-11 23:59:16 -04:00
commit 7b87da018c
84 changed files with 1302 additions and 933 deletions

View file

@ -10,6 +10,7 @@ import at.petrak.hexcasting.common.command.HexCommands
import at.petrak.hexcasting.common.entities.HexEntities
import at.petrak.hexcasting.common.items.HexItems
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import at.petrak.hexcasting.common.lib.HexSounds
import at.petrak.hexcasting.common.lib.HexStatistics
import at.petrak.hexcasting.common.misc.Brainsweeping
@ -77,6 +78,7 @@ object HexMod {
evBus.register(HexCommands::class.java)
evBus.register(TickScheduler)
evBus.register(HexCapabilities::class.java)
evBus.register(HexPlayerDataHelper::class.java)
evBus.register(OpFlight)
evBus.register(Brainsweeping::class.java)
@ -104,4 +106,4 @@ object HexMod {
@JvmStatic
fun getLogger() = this.LOGGER
}
}

View file

@ -4,10 +4,11 @@ import at.petrak.hexcasting.HexConfig;
import at.petrak.hexcasting.api.spell.ParticleSpray;
import at.petrak.hexcasting.common.casting.CastingContext;
import at.petrak.hexcasting.common.casting.CastingHarness;
import at.petrak.hexcasting.common.casting.ManaHelper;
import at.petrak.hexcasting.common.casting.SpellCircleContext;
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer;
import at.petrak.hexcasting.common.items.HexItems;
import at.petrak.hexcasting.common.lib.HexCapabilities;
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper;
import at.petrak.hexcasting.common.lib.HexSounds;
import at.petrak.paucal.api.PaucalBlockEntity;
import com.mojang.datafixers.util.Pair;
@ -108,8 +109,7 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
this.nextBlock = this.getBlockPos();
this.trackedBlocks = new ArrayList<>();
this.knownBlocks = new HashSet<>();
var maybeCap = activator.getCapability(HexCapabilities.PREFERRED_COLORIZER).resolve();
maybeCap.ifPresent(capPreferredColorizer -> this.colorizer = capPreferredColorizer.colorizer);
this.colorizer = HexPlayerDataHelper.getColorizer(activator);
this.level.setBlockAndUpdate(this.getBlockPos(),
this.getBlockState().setValue(BlockAbstractImpetus.ENERGIZED, true));
@ -171,8 +171,10 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
@Override
protected void loadModData(CompoundTag tag) {
if (tag.contains(TAG_ACTIVATOR) && tag.contains(TAG_COLORIZER) && tag.contains(TAG_NEXT_BLOCK)
&& tag.contains(TAG_TRACKED_BLOCKS)) {
if (tag.contains(TAG_ACTIVATOR, Tag.TAG_INT_ARRAY) &&
tag.contains(TAG_COLORIZER, Tag.TAG_COMPOUND) &&
tag.contains(TAG_NEXT_BLOCK, Tag.TAG_COMPOUND) &&
tag.contains(TAG_TRACKED_BLOCKS, Tag.TAG_COMPOUND)) {
this.activator = tag.getUUID(TAG_ACTIVATOR);
this.colorizer = FrozenColorizer.deserialize(tag.getCompound(TAG_COLORIZER));
this.nextBlock = NbtUtils.readBlockPos(tag.getCompound(TAG_NEXT_BLOCK));
@ -185,12 +187,20 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
this.trackedBlocks.add(pos);
this.knownBlocks.add(pos);
}
} else {
this.activator = null;
this.colorizer = null;
this.nextBlock = null;
this.foundAll = false;
this.trackedBlocks = new ArrayList<>();
this.knownBlocks = new HashSet<>();
}
this.mana = tag.getInt(TAG_MANA);
if (tag.contains(TAG_LAST_MISHAP)) {
if (tag.contains(TAG_LAST_MISHAP, Tag.TAG_STRING)) {
this.lastMishap = Component.Serializer.fromJson(tag.getString(TAG_LAST_MISHAP));
}
} else
this.lastMishap = null;
}
void stepCircle() {
@ -288,6 +298,7 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
var harness = new CastingHarness(ctx);
var castSpell = false;
var makeSound = false;
BlockPos erroredPos = null;
for (var tracked : this.trackedBlocks) {
var bs = this.level.getBlockState(tracked);
@ -297,6 +308,8 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
var info = harness.executeNewPattern(newPattern, splayer.getLevel());
if (info.getWasSpellCast()) {
castSpell = true;
if (info.getHasCastingSound())
makeSound = true;
}
if (info.getWasPrevPatternInvalid()) {
erroredPos = tracked;
@ -306,15 +319,17 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
}
}
if (castSpell) {
if (castSpell && makeSound) {
this.level.playSound(null, this.getBlockPos(), HexSounds.SPELL_CIRCLE_CAST.get(), SoundSource.BLOCKS,
2f, 1f);
}
if (erroredPos != null) {
this.setLastMishap(null);
this.setChanged();
this.sfx(erroredPos, false);
}
} else
this.setLastMishap(null);
this.setChanged();
}
}
@ -491,8 +506,8 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
@NotNull
@Override
public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) {
var manamount = getManaAmount(stack);
if (manamount != null) {
var manamount = ManaHelper.extractMana(stack, -1, true, simulate);
if (manamount > 0) {
if (!simulate) {
BlockEntityAbstractImpetus.this.mana += manamount;
BlockEntityAbstractImpetus.this.setChanged();
@ -516,23 +531,7 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
@Override
public boolean isItemValid(int slot, @NotNull ItemStack stack) {
return getManaAmount(stack) != null;
}
// a separate method from the ctx or harness or whatever cause it's different and special
private static @Nullable Integer getManaAmount(ItemStack stack) {
int baseAmt;
if (stack.is(HexItems.AMETHYST_DUST.get())) {
baseAmt = HexConfig.dustManaAmount.get();
} else if (stack.is(Items.AMETHYST_SHARD)) {
baseAmt = HexConfig.shardManaAmount.get();
} else if (stack.is(HexItems.CHARGED_AMETHYST.get())) {
baseAmt = HexConfig.chargedCrystalManaAmount.get();
} else {
return null;
}
return baseAmt * stack.getCount();
return ManaHelper.extractMana(stack, -1, false, true) > 0;
}
};
}

View file

@ -30,9 +30,14 @@ public interface DataHolder {
}
}
boolean canWrite(CompoundTag tag, @Nullable SpellDatum<?> datum);
@Nullable
default SpellDatum<?> emptyDatum(ItemStack stack) {
return null;
}
void writeDatum(CompoundTag tag, @Nullable SpellDatum<?> datum);
boolean canWrite(ItemStack stack, @Nullable SpellDatum<?> datum);
void writeDatum(ItemStack stack, @Nullable SpellDatum<?> datum);
static void appendHoverText(DataHolder self, ItemStack pStack, List<Component> pTooltipComponents,
TooltipFlag pIsAdvanced) {

View file

@ -0,0 +1,26 @@
package at.petrak.hexcasting.api.item;
public interface IManaReservoir {
int getMana();
int getMaxMana();
void setMana(int mana);
boolean canRecharge();
boolean canProvide();
int getConsumptionPriority();
boolean canConstructBattery();
default int withdrawMana(int cost, boolean simulate) {
var manaHere = getMana();
if (cost < 0)
cost = manaHere;
if (!simulate) {
var manaLeft = manaHere - cost;
setMana(manaLeft);
}
return Math.min(cost, manaHere);
}
}

View file

@ -0,0 +1,32 @@
package at.petrak.hexcasting.api.item;
import net.minecraft.world.item.ItemStack;
/**
* Don't use this interface's methods directly. Instead, use an IManaReservoir capability.
*/
public interface ManaHolder {
int getMana(ItemStack stack);
int getMaxMana(ItemStack stack);
void setMana(ItemStack stack, int mana);
boolean manaProvider(ItemStack stack);
default float getManaFullness(ItemStack stack) {
int max = getMaxMana(stack);
if (max == 0)
return 0;
return (float) getMana(stack) / (float) max;
}
default int withdrawMana(ItemStack stack, int cost, boolean simulate) {
var manaHere = getMana(stack);
if (cost < 0)
cost = manaHere;
if (!simulate) {
var manaLeft = manaHere - cost;
setMana(stack, manaLeft);
}
return Math.min(cost, manaHere);
}
}

View file

@ -0,0 +1,19 @@
package at.petrak.hexcasting.api.item;
import at.petrak.hexcasting.hexmath.HexPattern;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public interface SpellHolder extends ManaHolder {
boolean canDrawManaFromInventory(ItemStack stack);
@Nullable
List<HexPattern> getPatterns(ItemStack stack);
void writePattern(ItemStack stack, List<HexPattern> patterns, int mana);
void clearPatterns(ItemStack stack);
}

View file

@ -7,21 +7,24 @@ import at.petrak.hexcasting.common.casting.mishaps.MishapNotEnoughArgs
interface SpellOperator : Operator {
val argc: Int
val hasCastingSound: Boolean get() = true
fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>>
): Triple<RenderedSpell, Int, List<ParticleSpray>>?
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size)
val args = stack.takeLast(this.argc)
for (_i in 0 until this.argc) stack.removeLast()
val (spell, mana, particles) = this.execute(args, ctx)
val executeResult = this.execute(args, ctx) ?: return OperationResult(stack, listOf())
val (spell, mana, particles) = executeResult
val sideEffects = mutableListOf(
OperatorSideEffect.ConsumeMana(mana),
OperatorSideEffect.AttemptSpell(spell, this.isGreat)
OperatorSideEffect.AttemptSpell(spell, this.isGreat, this.hasCastingSound)
)
for (spray in particles) {
sideEffects.add(OperatorSideEffect.Particles(spray))
@ -30,4 +33,4 @@ interface SpellOperator : Operator {
return OperationResult(stack, sideEffects)
}
}
}

View file

@ -1,10 +1,9 @@
package at.petrak.hexcasting.client;
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
import at.petrak.hexcasting.common.casting.colors.CapPreferredColorizer;
import at.petrak.hexcasting.common.casting.operators.spells.sentinel.CapSentinel;
import at.petrak.hexcasting.common.casting.operators.spells.sentinel.Sentinel;
import at.petrak.hexcasting.common.items.HexItems;
import at.petrak.hexcasting.common.lib.HexCapabilities;
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
@ -31,11 +30,10 @@ public class HexAdditionalRenderers {
@SubscribeEvent
public static void overlayLevel(RenderLevelLastEvent evt) {
var player = Minecraft.getInstance().player;
var maybeSentinelCap = player.getCapability(HexCapabilities.SENTINEL).resolve();
if (maybeSentinelCap.isPresent()) {
var cap = maybeSentinelCap.get();
if (cap.hasSentinel && player.getLevel().dimension().equals(cap.dimension)) {
renderSentinel(cap, player, evt.getPoseStack(), evt.getPartialTick());
if (player != null) {
var sentinel = HexPlayerDataHelper.getSentinel(player);
if (sentinel.hasSentinel() && player.getLevel().dimension().equals(sentinel.dimension())) {
renderSentinel(sentinel, player, evt.getPoseStack(), evt.getPartialTick());
}
}
}
@ -47,8 +45,8 @@ public class HexAdditionalRenderers {
}
}
private static void renderSentinel(CapSentinel sentinel, LocalPlayer owner,
PoseStack ps, float partialTicks) {
private static void renderSentinel(Sentinel sentinel, LocalPlayer owner,
PoseStack ps, float partialTicks) {
ps.pushPose();
// zero vector is the player
@ -56,9 +54,9 @@ public class HexAdditionalRenderers {
var camera = mc.gameRenderer.getMainCamera();
var playerPos = camera.getPosition();
ps.translate(
sentinel.position.x - playerPos.x,
sentinel.position.y - playerPos.y,
sentinel.position.z - playerPos.z);
sentinel.position().x - playerPos.x,
sentinel.position().y - playerPos.y,
sentinel.position().z - playerPos.z);
var time = mc.level.getLevelData().getGameTime() + partialTicks;
var bobSpeed = 1f / 20;
@ -66,7 +64,7 @@ public class HexAdditionalRenderers {
ps.translate(0, Mth.sin(bobSpeed * time) * magnitude, 0);
var spinSpeed = 1f / 30;
ps.mulPose(Quaternion.fromXYZ(new Vector3f(0, spinSpeed * time, 0)));
if (sentinel.extendsRange) {
if (sentinel.extendsRange()) {
ps.mulPose(Quaternion.fromXYZ(new Vector3f(spinSpeed * time / 8f, 0, 0)));
}
@ -83,21 +81,12 @@ public class HexAdditionalRenderers {
RenderSystem.disableCull();
RenderSystem.lineWidth(5f);
var maybeColorizerCap = owner.getCapability(HexCapabilities.PREFERRED_COLORIZER).resolve();
CapPreferredColorizer cap = null;
if (maybeColorizerCap.isPresent()) {
cap = maybeColorizerCap.get();
}
CapPreferredColorizer finalCap = cap;
var colorizer = HexPlayerDataHelper.getColorizer(mc.player);
BiConsumer<float[], float[]> v = (l, r) -> {
int lcolor = -1, rcolor = -1;
int lcolor = colorizer.getColor(time, new Vec3(l[0], l[1], l[2])),
rcolor = colorizer.getColor(time, new Vec3(r[0], r[1], r[2]));
var normal = new Vector3f(r[0] - l[0], r[1] - l[1], r[2] - l[2]);
normal.normalize();
if (finalCap != null) {
lcolor = finalCap.colorizer.getColor(time, new Vec3(l[0], l[1], l[2]));
rcolor = finalCap.colorizer.getColor(time, new Vec3(r[0], r[1], r[2]));
}
buf.vertex(neo, l[0], l[1], l[2])
.color(lcolor)
.normal(ps.last().normal(), normal.x(), normal.y(), normal.z())

View file

@ -8,6 +8,7 @@ import at.petrak.hexcasting.common.items.ItemSlate;
import at.petrak.hexcasting.hexmath.HexPattern;
import com.mojang.datafixers.util.Either;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
import net.minecraft.nbt.Tag;
import net.minecraft.world.inventory.tooltip.TooltipComponent;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
@ -35,11 +36,11 @@ public class HexTooltips {
if (!stack.isEmpty()) {
if (stack.is(HexItems.SCROLL.get())) {
var tag = stack.getOrCreateTag();
if (tag.contains(ItemScroll.TAG_PATTERN)) {
if (tag.contains(ItemScroll.TAG_PATTERN, Tag.TAG_COMPOUND)) {
var pattern = HexPattern.DeserializeFromNBT(tag.getCompound(ItemScroll.TAG_PATTERN));
evt.getTooltipElements().add(Either.right(new PatternTooltipGreeble(
pattern,
tag.contains(ItemScroll.TAG_OP_ID)
tag.contains(ItemScroll.TAG_OP_ID, Tag.TAG_STRING)
? PatternTooltipGreeble.ANCIENT_BG : PatternTooltipGreeble.PRISTINE_BG)));
}
} else if (stack.is(HexItems.SLATE.get()) && ItemSlate.hasPattern(stack)) {

View file

@ -4,6 +4,7 @@ import at.petrak.hexcasting.HexConfig;
import at.petrak.hexcasting.api.circle.BlockAbstractImpetus;
import at.petrak.hexcasting.api.circle.BlockEntityAbstractImpetus;
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
import at.petrak.hexcasting.api.item.ManaHolder;
import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.client.be.BlockEntityAkashicBookshelfRenderer;
import at.petrak.hexcasting.client.be.BlockEntitySlateRenderer;
@ -28,18 +29,21 @@ import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ComparatorBlock;
import net.minecraft.world.level.block.RepeaterBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.ComparatorBlockEntity;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.ComparatorMode;
import net.minecraftforge.client.event.EntityRenderersEvent;
import net.minecraftforge.client.event.ParticleFactoryRegisterEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.registries.RegistryObject;
import java.util.ArrayList;
import java.util.List;
@ -89,28 +93,26 @@ public class RegisterClientStuff {
return 0f;
}
});
for (RegistryObject<Item> packager : new RegistryObject[]{
HexItems.CYPHER,
HexItems.TRINKET,
HexItems.ARTIFACT,
for (ItemPackagedSpell packager : new ItemPackagedSpell[]{
HexItems.CYPHER.get(),
HexItems.TRINKET.get(),
HexItems.ARTIFACT.get(),
}) {
ItemProperties.register(packager.get(), ItemPackagedSpell.HAS_PATTERNS_PRED,
ItemProperties.register(packager, ItemPackagedSpell.HAS_PATTERNS_PRED,
(stack, level, holder, holderID) ->
stack.getOrCreateTag().contains(ItemPackagedSpell.TAG_PATTERNS) ? 1f : 0f
packager.getPatterns(stack) != null ? 1f : 0f
);
}
ItemProperties.register(HexItems.BATTERY.get(), ItemManaBattery.MANA_PREDICATE,
(stack, level, holder, holderID) -> {
var item = (ItemManaBattery) stack.getItem();
var tag = stack.getOrCreateTag();
return item.getManaFullness(tag);
var item = (ManaHolder) stack.getItem();
return item.getManaFullness(stack);
});
ItemProperties.register(HexItems.BATTERY.get(), ItemManaBattery.MAX_MANA_PREDICATE,
(stack, level, holder, holderID) -> {
var item = (ItemManaBattery) stack.getItem();
var tag = stack.getOrCreateTag();
var max = item.getMaxManaAmt(tag);
var max = item.getMaxMana(stack);
return (float) Math.sqrt((float) max / HexConfig.chargedCrystalManaAmount.get() / 10);
});
@ -202,6 +204,34 @@ public class RegisterClientStuff {
new TextComponent(String.valueOf(state.getValue(BlockStateProperties.POWER)))
.withStyle(ChatFormatting.RED))
));
ScryingLensOverlayRegistry.addDisplayer(Blocks.COMPARATOR,
(state, pos, observer, world, lensHand) -> {
BlockEntity be = world.getBlockEntity(pos);
if (be instanceof ComparatorBlockEntity comparator) {
return List.of(
new Pair<>(
new ItemStack(Items.REDSTONE),
new TextComponent(String.valueOf(comparator.getOutputSignal()))
.withStyle(ChatFormatting.RED)),
new Pair<>(
new ItemStack(Items.REDSTONE_TORCH),
new TextComponent(state.getValue(ComparatorBlock.MODE) == ComparatorMode.COMPARE ? ">" : "-")
.withStyle(ChatFormatting.RED)));
} else
return List.of();
});
ScryingLensOverlayRegistry.addDisplayer(Blocks.REPEATER,
(state, pos, observer, world, lensHand) -> List.of(
new Pair<>(
new ItemStack(Items.REDSTONE),
new TextComponent(String.valueOf(state.getValue(RepeaterBlock.POWERED) ? 15 : 0))
.withStyle(ChatFormatting.RED)),
new Pair<>(
new ItemStack(Items.CLOCK),
new TextComponent(String.valueOf(state.getValue(RepeaterBlock.DELAY)))
.withStyle(ChatFormatting.YELLOW))));
}
@SubscribeEvent(priority = EventPriority.LOWEST)

View file

@ -184,7 +184,7 @@ class GuiSpellcasting(private val handOpenedWith: InteractionHand,
if (playSound) {
Minecraft.getInstance().soundManager.play(
SimpleSoundInstance(
HexSounds.START_PATTERN.get(),
HexSounds.ADD_LINE.get(),
SoundSource.PLAYERS,
0.25f,
1f + (Math.random().toFloat() - 0.5f) * 0.1f,

View file

@ -103,6 +103,7 @@ public class ConjureParticle extends TextureSheetParticle {
public void begin(BufferBuilder buf, TextureManager texMan) {
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
RenderSystem.depthMask(false);
RenderSystem.enableDepthTest();
RenderSystem.enableBlend();
RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);

View file

@ -40,8 +40,8 @@ class GridSoundInstance(val player: Player) : AbstractTickableSoundInstance(HexS
val normalVector = calculateVectorFromPitchAndYaw(player.xRot, player.yRot)
val newPos = player.eyePosition
.add(normalVector)
.add(horizontalPlanarVector.scale(2 * (mousePosX - 0.5)))
.add(verticalPlanarVector.scale(2 * (mousePosY - 0.5)))
.add(horizontalPlanarVector.scale(PAN_SCALE * 2 * (mousePosX - 0.5)))
.add(verticalPlanarVector.scale(PAN_SCALE * 2 * (mousePosY - 0.5)))
this.x = newPos.x
this.y = newPos.y
this.z = newPos.z
@ -57,4 +57,8 @@ class GridSoundInstance(val player: Player) : AbstractTickableSoundInstance(HexS
val azimuthVertical = Mth.sin(radiansPitch).toDouble()
return Vec3(zComponent * azimuthHorizontal, -azimuthVertical, xComponent * azimuthHorizontal)
}
companion object {
const val PAN_SCALE = 0.5
}
}

View file

@ -92,9 +92,7 @@ public class BlockEntityConjured extends PaucalBlockEntity {
@Override
protected void loadModData(CompoundTag tag) {
if (tag.contains(TAG_COLORIZER)) {
this.setColorizer(FrozenColorizer.deserialize(tag.getCompound(TAG_COLORIZER)));
}
this.setColorizer(FrozenColorizer.deserialize(tag.getCompound(TAG_COLORIZER)));
}
public FrozenColorizer getColorizer() {

View file

@ -55,7 +55,7 @@ public class BlockAkashicBookshelf extends BlockAkashicFloodfiller implements En
var stack = pPlayer.getItemInHand(pHand);
if (stack.getItem() instanceof ItemScroll scroll) {
if (!pLevel.isClientSide()) {
scroll.writeDatum(stack.getOrCreateTag(), SpellDatum.make(shelf.getPattern()));
scroll.writeDatum(stack, SpellDatum.make(shelf.getPattern()));
}
pLevel.playSound(pPlayer, pPos, HexSounds.SCROLL_SCRIBBLE.get(), SoundSource.BLOCKS, 1f, 1f);
return InteractionResult.sidedSuccess(pLevel.isClientSide);

View file

@ -5,6 +5,7 @@ import at.petrak.hexcasting.hexmath.HexPattern;
import at.petrak.paucal.api.PaucalBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
@ -29,14 +30,14 @@ public class BlockEntitySlate extends PaucalBlockEntity {
@Override
protected void loadModData(CompoundTag tag) {
if (tag.contains(TAG_PATTERN)) {
if (tag.contains(TAG_PATTERN, Tag.TAG_COMPOUND)) {
CompoundTag patternTag = tag.getCompound(TAG_PATTERN);
if (HexPattern.IsHexPattern(patternTag))
this.pattern = HexPattern.DeserializeFromNBT(patternTag);
else
this.pattern = null;
}
} else
this.pattern = null;
}
}

View file

@ -96,7 +96,7 @@ public class BlockSlate extends BlockCircleComponent implements EntityBlock, Sim
if (be instanceof BlockEntitySlate slate) {
ItemStack stack = new ItemStack(HexItems.SLATE.get());
if (slate.pattern != null)
HexItems.SLATE.get().writeDatum(stack.getOrCreateTag(), SpellDatum.make(slate.pattern));
HexItems.SLATE.get().writeDatum(stack, SpellDatum.make(slate.pattern));
return stack;
}

View file

@ -7,6 +7,7 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.InteractionHand;
@ -79,8 +80,9 @@ public class BlockEntityStoredPlayerImpetus extends BlockEntityAbstractImpetus {
@Override
protected void loadModData(CompoundTag tag) {
super.loadModData(tag);
if (tag.contains(TAG_STORED_PLAYER)) {
if (tag.contains(TAG_STORED_PLAYER, Tag.TAG_INT_ARRAY)) {
this.storedPlayer = tag.getUUID(TAG_STORED_PLAYER);
}
} else
this.storedPlayer = null;
}
}

View file

@ -9,6 +9,7 @@ import at.petrak.hexcasting.common.casting.mishaps.MishapEntityTooFarAway
import at.petrak.hexcasting.common.casting.mishaps.MishapEvalTooDeep
import at.petrak.hexcasting.common.casting.mishaps.MishapLocationTooFarAway
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import at.petrak.hexcasting.common.lib.RegisterHelper.prefix
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
@ -38,15 +39,15 @@ data class CastingContext(
private val entitiesGivenMotion = mutableSetOf<Entity>()
inline fun getHeldItemToOperateOn(acceptItemIf: (ItemStack) -> Boolean): ItemStack {
inline fun getHeldItemToOperateOn(acceptItemIf: (ItemStack) -> Boolean): Pair<ItemStack, InteractionHand> {
if (this.spellCircle == null) {
return caster.getItemInHand(otherHand)
return caster.getItemInHand(otherHand) to otherHand
}
val handItem = caster.getItemInHand(castingHand)
if (!acceptItemIf(handItem))
return caster.getItemInHand(otherHand)
return handItem
return caster.getItemInHand(otherHand) to otherHand
return handItem to castingHand
}
/**
@ -79,16 +80,14 @@ data class CastingContext(
}
fun isVecInRange(vec: Vec3): Boolean {
val maybeSentinel = this.caster.getCapability(HexCapabilities.SENTINEL).resolve()
if (maybeSentinel.isPresent) {
val sentinel = maybeSentinel.get()
if (sentinel.hasSentinel
&& sentinel.extendsRange
&& world.dimension() == sentinel.dimension
&& vec.distanceToSqr(sentinel.position) < Operator.MAX_DISTANCE_FROM_SENTINEL * Operator.MAX_DISTANCE_FROM_SENTINEL
)
return true
}
val sentinel = HexPlayerDataHelper.getSentinel(caster)
if (sentinel.hasSentinel
&& sentinel.extendsRange
&& world.dimension() == sentinel.dimension
&& vec.distanceToSqr(sentinel.position) < Operator.MAX_DISTANCE_FROM_SENTINEL * Operator.MAX_DISTANCE_FROM_SENTINEL
)
return true
if (this.spellCircle != null) {
// we use the eye position cause thats where the caster gets their "position" from
@ -152,8 +151,9 @@ data class CastingContext(
val inv = this.caster.inventory
// TODO: withdraw from ender chest given a specific ender charm?
val stacksToExamine = inv.items.asReversed().toMutableList()
val stacksToExamine = inv.items.toMutableList().apply { removeAt(inv.selected) }.asReversed().toMutableList()
stacksToExamine.addAll(inv.offhand)
stacksToExamine.add(inv.getSelected())
fun matches(stack: ItemStack): Boolean =
!stack.isEmpty && stack.`is`(item)

View file

@ -4,6 +4,7 @@ import at.petrak.hexcasting.HexConfig
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.PatternRegistry
import at.petrak.hexcasting.api.circle.BlockEntityAbstractImpetus
import at.petrak.hexcasting.api.item.SpellHolder
import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.SpellDatum
@ -14,6 +15,7 @@ import at.petrak.hexcasting.common.items.ItemWand
import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexDamageSources
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import at.petrak.hexcasting.common.lib.HexStatistics
import at.petrak.hexcasting.datagen.HexAdvancements
import at.petrak.hexcasting.hexmath.HexPattern
@ -112,6 +114,7 @@ class CastingHarness private constructor(
fun performSideEffects(sideEffects: List<OperatorSideEffect>): ControllerInfo {
var wasSpellCast = false
var wasPrevPatternInvalid = false
var hasCastingSound = false
for (haskellProgrammersShakingandCryingRN in sideEffects) {
val mustStop = haskellProgrammersShakingandCryingRN.performEffect(this)
if (mustStop) {
@ -120,12 +123,16 @@ class CastingHarness private constructor(
}
if (haskellProgrammersShakingandCryingRN is OperatorSideEffect.AttemptSpell)
if (haskellProgrammersShakingandCryingRN is OperatorSideEffect.AttemptSpell) {
wasSpellCast = true
if (haskellProgrammersShakingandCryingRN.hasCastingSound)
hasCastingSound = true
}
}
return ControllerInfo(
wasSpellCast,
hasCastingSound,
this.stack.isEmpty() && this.parenCount == 0 && !this.escapeNext,
wasPrevPatternInvalid,
generateDescs()
@ -268,13 +275,13 @@ class CastingHarness private constructor(
} else {
val casterStack = this.ctx.caster.getItemInHand(this.ctx.castingHand)
val casterItem = casterStack.item
val ipsCanDrawFromInv = if (casterItem is ItemPackagedSpell) {
val tag = casterStack.orCreateTag
val manaAvailable = tag.getInt(ItemPackagedSpell.TAG_MANA)
val ipsCanDrawFromInv = if (casterItem is SpellHolder) {
val mana = casterStack.getCapability(HexCapabilities.MANA).resolve().get()
val manaAvailable = mana.mana
val manaToTake = min(costLeft, manaAvailable)
tag.putInt(ItemPackagedSpell.TAG_MANA, manaAvailable - manaToTake)
mana.mana = manaAvailable - manaToTake
costLeft -= manaToTake
casterItem.canDrawManaFromInventory()
casterItem.canDrawManaFromInventory(casterStack)
} else {
false
}
@ -283,7 +290,7 @@ class CastingHarness private constructor(
.filter(ManaHelper::isManaItem)
.sortedWith(Comparator(ManaHelper::compare).reversed())
for (stack in manableItems) {
costLeft -= ManaHelper.extractMana(stack, costLeft)!!
costLeft -= ManaHelper.extractMana(stack, costLeft)
if (costLeft <= 0)
break
}
@ -319,12 +326,7 @@ class CastingHarness private constructor(
if (this.prepackagedColorizer != null)
return this.prepackagedColorizer
val maybeCap = this.ctx.caster.getCapability(HexCapabilities.PREFERRED_COLORIZER).resolve()
if (maybeCap.isEmpty) {
// uh oh
return FrozenColorizer.DEFAULT
}
return maybeCap.get().colorizer
return HexPlayerDataHelper.getColorizer(ctx.caster)
}

View file

@ -7,7 +7,8 @@ import net.minecraft.network.chat.Component
*/
data class ControllerInfo(
val wasSpellCast: Boolean,
val hasCastingSound: Boolean,
val isStackClear: Boolean,
val wasPrevPatternInvalid: Boolean,
val stackDesc: List<Component>
)
)

View file

@ -1,99 +1,57 @@
package at.petrak.hexcasting.common.casting
import at.petrak.hexcasting.HexConfig
import at.petrak.hexcasting.common.items.HexItems
import at.petrak.hexcasting.common.items.magic.ItemManaBattery
import at.petrak.hexcasting.common.lib.HexCapabilities
import net.minecraft.util.Mth
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import kotlin.math.ceil
import kotlin.math.min
import kotlin.math.roundToInt
object ManaHelper {
@JvmStatic
fun isManaItem(stack: ItemStack): Boolean {
return stack.`is`(HexItems.AMETHYST_DUST.get())
|| stack.`is`(Items.AMETHYST_SHARD)
|| stack.`is`(HexItems.CHARGED_AMETHYST.get())
|| stack.item is ItemManaBattery
return stack.getCapability(HexCapabilities.MANA).map { it.canProvide() }.orElse(false) && extractMana(stack, simulate = true) > 0
}
/**
* Try to extract the given amount of mana from this item.
* This may mutate the itemstack.
* Extract [cost] mana from [stack]. If [cost] is less than zero, extract all mana instead.
* This may mutate [stack] (and may consume it) unless [simulate] is set.
*
* Return the actual amount of mana extracted, or null if this cannot have mana extracted.
*/
fun extractMana(stack: ItemStack, cost: Int): Int? {
val base = when (stack.item) {
HexItems.AMETHYST_DUST.get() -> HexConfig.dustManaAmount.get()
Items.AMETHYST_SHARD -> HexConfig.shardManaAmount.get()
HexItems.CHARGED_AMETHYST.get() -> HexConfig.chargedCrystalManaAmount.get()
HexItems.BATTERY.get() -> {
val battery = stack.item as ItemManaBattery
return battery.withdrawMana(stack.orCreateTag, cost)
}
else -> return null
}
val itemsReqd = ceil(cost.toFloat() / base.toFloat()).toInt()
val actualItemsConsumed = min(stack.count, itemsReqd)
stack.shrink(actualItemsConsumed)
return base * actualItemsConsumed
}
/**
* Extract the entirety of the mana out of this.
* This may mutate the itemstack (and will probably consume it).
* If [drainForBatteries] is false, this will only consider forms of mana that can be used to make new batteries.
*
* Return the amount of mana extracted, or null if this cannot have mana extracted.
* Return the amount of mana extracted. This may be over [cost] if mana is wasted.
*/
fun extractAllMana(stack: ItemStack): Int? {
val base = when (stack.item) {
HexItems.AMETHYST_DUST.get() -> HexConfig.dustManaAmount.get()
Items.AMETHYST_SHARD -> HexConfig.shardManaAmount.get()
HexItems.CHARGED_AMETHYST.get() -> HexConfig.chargedCrystalManaAmount.get()
@JvmStatic
@JvmOverloads
fun extractMana(stack: ItemStack, cost: Int = -1, drainForBatteries: Boolean = false, simulate: Boolean = false): Int {
val manaCapability = stack.getCapability(HexCapabilities.MANA).resolve()
HexItems.BATTERY.get() -> {
val battery = stack.item as ItemManaBattery
val tag = stack.orCreateTag
val manaThere = battery.getManaAmt(tag)
return battery.withdrawMana(tag, manaThere)
}
else -> return null
}
val count = stack.count
stack.shrink(count)
return base * count
if (!manaCapability.isPresent)
return 0
val manaReservoir = manaCapability.get()
if (drainForBatteries && !manaReservoir.canConstructBattery())
return 0
return manaReservoir.withdrawMana(cost, simulate)
}
/**
* Sorted from least important to most important
*/
fun compare(astack: ItemStack, bstack: ItemStack): Int {
val aitem = astack.item
val bitem = bstack.item
val aMana = astack.getCapability(HexCapabilities.MANA).resolve()
val bMana = bstack.getCapability(HexCapabilities.MANA).resolve()
return if (aitem != bitem) {
fun intcode(item: Item): Int =
when (item) {
HexItems.CHARGED_AMETHYST.get() -> 1
Items.AMETHYST_SHARD -> 2
HexItems.AMETHYST_DUST.get() -> 3
HexItems.BATTERY.get() -> 4
else -> 0
}
intcode(aitem) - intcode(bitem)
} else if (aitem == HexItems.BATTERY.get()) {
val atag = astack.orCreateTag
val btag = bstack.orCreateTag
val battery = aitem as ItemManaBattery
battery.getManaAmt(atag) - battery.getManaAmt(btag)
return if (astack.item != bstack.item) {
aMana.map { it.consumptionPriority }.orElse(0) - bMana.map { it.consumptionPriority }.orElse(0)
} else if (aMana.isPresent && bMana.isPresent) {
aMana.get().mana - bMana.get().mana
} else {
astack.count - bstack.count
}
}
@JvmStatic
fun barColor(mana: Int, maxMana: Int): Int {
val amt = if (maxMana == 0) {
0f
@ -107,6 +65,7 @@ object ManaHelper {
return Mth.color(r / 255f, g / 255f, b / 255f)
}
@JvmStatic
fun barWidth(mana: Int, maxMana: Int): Int {
val amt = if (maxMana == 0) {
0f
@ -115,4 +74,4 @@ object ManaHelper {
}
return (13f * amt).roundToInt()
}
}
}

View file

@ -22,7 +22,7 @@ sealed class OperatorSideEffect {
abstract fun performEffect(harness: CastingHarness): Boolean
/** Try to cast a spell */
data class AttemptSpell(val spell: RenderedSpell, val isGreat: Boolean) : OperatorSideEffect() {
data class AttemptSpell(val spell: RenderedSpell, val isGreat: Boolean, val hasCastingSound: Boolean = true) : OperatorSideEffect() {
override fun performEffect(harness: CastingHarness): Boolean {
return if (this.isGreat && !harness.ctx.isCasterEnlightened) {
harness.ctx.caster.sendMessage(
@ -94,4 +94,4 @@ sealed class OperatorSideEffect {
return true
}
}
}
}

View file

@ -135,9 +135,11 @@ public class RegisterPatterns {
OpIdentityKindOf.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ewq", HexDir.EAST), prefix("floor"),
OpFloor.INSTANCE);
OpFloor.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qwe", HexDir.EAST), prefix("ceil"),
OpCeil.INSTANCE);
OpCeil.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eqaqe", HexDir.NORTH_WEST), prefix("logarithm"),
OpLog.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqqqaa", HexDir.SOUTH_EAST), prefix("sin"),
OpSin.INSTANCE);
@ -153,7 +155,7 @@ public class RegisterPatterns {
OpArcTan.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("eqqq", HexDir.NORTH_WEST), prefix("random"),
OpRandom.INSTANCE);
OpRandom.INSTANCE);
// == Spells ==
@ -197,6 +199,9 @@ public class RegisterPatterns {
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wqaqwd", HexDir.NORTH_EAST), prefix("edify"),
OpEdifySapling.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("adaa", HexDir.WEST), prefix("beep"),
OpBeep.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("waqqqqq", HexDir.EAST), prefix("craft/cypher"),
new OpMakePackagedSpell<>(HexItems.CYPHER.get(), 100_000));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("wwaqqqqqeaqeaeqqqeaeq", HexDir.EAST),

View file

@ -1,49 +0,0 @@
package at.petrak.hexcasting.common.casting.colors;
import at.petrak.hexcasting.common.lib.HexCapabilities;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.common.util.LazyOptional;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The colorizer item favored by this player.
*/
public class CapPreferredColorizer implements ICapabilitySerializable<CompoundTag> {
public static final String CAP_NAME = "preferred_colorizer";
public static final String TAG_COLOR = "colorizer";
public FrozenColorizer colorizer;
public CapPreferredColorizer(FrozenColorizer colorizer) {
this.colorizer = colorizer;
}
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
return HexCapabilities.PREFERRED_COLORIZER.orEmpty(cap, LazyOptional.of(() -> this));
}
@Override
public CompoundTag serializeNBT() {
var tag = new CompoundTag();
tag.put(TAG_COLOR, this.colorizer.serialize());
return tag;
}
@Override
public void deserializeNBT(CompoundTag nbt) {
if (nbt.contains(TAG_COLOR, Tag.TAG_COMPOUND)) {
var colorizerTag = nbt.getCompound(TAG_COLOR);
this.colorizer = FrozenColorizer.deserialize(colorizerTag);
} else {
this.colorizer = FrozenColorizer.DEFAULT;
}
}
}

View file

@ -24,6 +24,10 @@ import java.util.UUID;
* A colorizer item and the player who owned it at the time of making the color.
*/
public record FrozenColorizer(Item item, UUID owner) {
private static final int[] MINIMUM_LUMINANCE_COLOR_WHEEL = {
0xFF200000, 0xFF202000, 0xFF002000, 0xFF002020, 0xFF000020, 0xFF200020
};
public static final String TAG_ITEM = "item";
public static final String TAG_OWNER = "owner";
@ -38,6 +42,8 @@ public record FrozenColorizer(Item item, UUID owner) {
}
public static FrozenColorizer deserialize(CompoundTag tag) {
if (tag.isEmpty())
return FrozenColorizer.DEFAULT;
try {
var itemID = new ResourceLocation(tag.getString(TAG_ITEM));
var item = ForgeRegistries.ITEMS.getValue(itemID);
@ -55,11 +61,36 @@ public record FrozenColorizer(Item item, UUID owner) {
}
/**
* Gets a color with a minimum luminance applied.
* @param time absolute world time in ticks
* @param position a position for the icosahedron, a randomish number for particles.
* @return an AARRGGBB color.
*/
public int getColor(float time, Vec3 position) {
int raw = getRawColor(time, position);
int r = (raw & 0xFF0000) >> 16;
int g = (raw & 0xFF00) >> 8;
int b = (raw & 0xFF);
double luminance = 0.2126 * r / 0xFF + 0.7152 * g / 0xFF + 0.0722 * b / 0xFF; // Standard relative luminance calculation
if (luminance < 0.1) {
int rawMod = morphBetweenColors(MINIMUM_LUMINANCE_COLOR_WHEEL, new Vec3(0.1, 0.1, 0.1), time / 20 / 20, position);
r += (rawMod & 0xFF0000) >> 16;
g += (rawMod & 0xFF00) >> 8;
b += (rawMod & 0xFF);
}
return 0xff_000000 | (r << 16) | (g << 8) | b;
}
/**
* @param time absolute world time in ticks
* @param position a position for the icosahedron, a randomish number for particles.
* @return an AARRGGBB color.
*/
public int getRawColor(float time, Vec3 position) {
if (this.item instanceof ItemDyeColorizer dye) {
return dye.getDyeColor().getTextColor() | 0xff_000000;
} else if (this.item instanceof ItemPrideColorizer politics) {

View file

@ -5,16 +5,17 @@ import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.InteractionHand
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.ItemStack
class MishapBadOffhandItem(val item: ItemStack, val wanted: Component) : Mishap() {
class MishapBadOffhandItem(val item: ItemStack, val hand: InteractionHand, val wanted: Component) : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BROWN)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<SpellDatum<*>>) {
val item = ctx.caster.getItemInHand(ctx.otherHand).copy()
ctx.caster.setItemInHand(ctx.otherHand, ItemStack.EMPTY.copy())
val item = ctx.caster.getItemInHand(hand).copy()
ctx.caster.setItemInHand(hand, ItemStack.EMPTY.copy())
val delta = ctx.caster.lookAngle.scale(0.5)
yeetItem(item, ctx, delta)
@ -29,8 +30,8 @@ class MishapBadOffhandItem(val item: ItemStack, val wanted: Component) : Mishap(
companion object {
@JvmStatic
fun of(item: ItemStack, stub: String, vararg args: Any): MishapBadOffhandItem {
return MishapBadOffhandItem(item, TranslatableComponent("hexcasting.mishap.bad_item.$stub", *args))
fun of(item: ItemStack, hand: InteractionHand, stub: String, vararg args: Any): MishapBadOffhandItem {
return MishapBadOffhandItem(item, hand, TranslatableComponent("hexcasting.mishap.bad_item.$stub", *args))
}
}
}

View file

@ -46,7 +46,7 @@ class MishapInvalidIota(
@JvmStatic
fun ofClass(perpetrator: SpellDatum<*>, reverseIdx: Int, cls: Class<*>): MishapInvalidIota {
val key = "hexcasting.mishap.invalid_value.class." + when {
Double::class.java.isAssignableFrom(cls) -> "double"
Double::class.java.isAssignableFrom(cls) || Double::class.javaObjectType.isAssignableFrom(cls) -> "double"
Vec3::class.java.isAssignableFrom(cls) -> "vector"
List::class.java.isAssignableFrom(cls) -> "list"
Widget::class.java.isAssignableFrom(cls) -> "widget"
@ -66,4 +66,4 @@ class MishapInvalidIota(
return MishapInvalidIota(perpetrator, reverseIdx, TranslatableComponent(key))
}
}
}
}

View file

@ -10,13 +10,15 @@ object OpRead : ConstManaOperator {
override val argc = 0
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val handStack = ctx.getHeldItemToOperateOn {
val (handStack, hand) = ctx.getHeldItemToOperateOn {
val item = it.item
item is DataHolder && item.readDatum(it, ctx.world) != null
item is DataHolder && (item.readDatum(it, ctx.world) != null || item.emptyDatum(it) != null)
}
val handItem = handStack.item
val datum = (handItem as? DataHolder)?.readDatum(handStack, ctx.world) ?: throw MishapBadOffhandItem.of(handStack, "iota.read")
val datum = (handItem as? DataHolder)?.readDatum(handStack, ctx.world)
?: (handItem as? DataHolder)?.emptyDatum(handStack)
?: throw MishapBadOffhandItem.of(handStack, hand, "iota.read")
return listOf(datum)
}

View file

@ -25,7 +25,9 @@ object OpTheCoolerRead : ConstManaOperator {
ctx.assertEntityInRange(target)
val datum = item.readDatum(stack, ctx.world) ?: throw MishapBadItem.of(stack, "iota.read")
val datum = item.readDatum(stack, ctx.world)
?: item.emptyDatum(stack)
?: throw MishapBadItem.of(stack, "iota.read")
return listOf(datum)
}
}

View file

@ -20,19 +20,17 @@ object OpWrite : SpellOperator {
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val datum = args[0]
val handStack = ctx.getHeldItemToOperateOn {
val (handStack, hand) = ctx.getHeldItemToOperateOn {
val item = it.item
if (item is DataHolder) {
val tag = it.orCreateTag
item.canWrite(tag, datum)
item.canWrite(it, datum)
} else false
}
val handItem = handStack.item as? DataHolder ?: throw MishapBadOffhandItem.of(handStack, "iota.write")
val tag = handStack.orCreateTag
val handItem = handStack.item as? DataHolder ?: throw MishapBadOffhandItem.of(handStack, hand, "iota.write")
if (!handItem.canWrite(tag, datum))
throw MishapBadOffhandItem.of(handStack, "iota.readonly", datum.display())
if (!handItem.canWrite(handStack, datum))
throw MishapBadOffhandItem.of(handStack, hand, "iota.readonly", datum.display())
val trueName = MishapOthersName.getTrueNameFromDatum(datum, ctx.caster)
if (trueName != null)
@ -47,18 +45,16 @@ object OpWrite : SpellOperator {
private data class Spell(val datum: SpellDatum<*>) : RenderedSpell {
override fun cast(ctx: CastingContext) {
val handStack = ctx.getHeldItemToOperateOn {
val (handStack) = ctx.getHeldItemToOperateOn {
val item = it.item
if (item is DataHolder) {
val tag = it.orCreateTag
item.canWrite(tag, datum)
item.canWrite(it, datum)
} else false
}
val handItem = handStack.item
if (handItem is DataHolder) {
val tag = handStack.orCreateTag
handItem.writeDatum(tag, datum)
handItem.writeDatum(handStack, datum)
}
}

View file

@ -10,6 +10,7 @@ import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.mishaps.MishapNoAkashicRecord
import at.petrak.hexcasting.common.casting.mishaps.MishapOthersName
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import at.petrak.hexcasting.common.lib.HexSounds
import at.petrak.hexcasting.hexmath.HexPattern
import net.minecraft.core.BlockPos
@ -56,13 +57,12 @@ object OpAkashicWrite : SpellOperator {
1f, 0.8f
)
ctx.caster.getCapability(HexCapabilities.PREFERRED_COLORIZER).ifPresent {
// val normal = record.blockState.getValue(BlockAkashicBookshelf.FACING).normal
// ParticleSpray(
// Vec3.atCenterOf(record.blockPos), Vec3.atBottomCenterOf(normal),
// 0.5, Math.PI / 4, 10
// ).sprayParticles(ctx.world, it.colorizer)
}
// val colorizer = HexPlayerDataHelper.getColorizer(ctx.caster)
// val normal = record.blockState.getValue(BlockAkashicBookshelf.FACING).normal
// ParticleSpray(
// Vec3.atCenterOf(record.blockPos), Vec3.atBottomCenterOf(normal),
// 0.5, Math.PI / 4, 10
// ).sprayParticles(ctx.world, colorizer)
}
}
}

View file

@ -10,6 +10,8 @@ import at.petrak.hexcasting.common.casting.OperatorSideEffect
import at.petrak.hexcasting.common.casting.mishaps.MishapInvalidIota
import at.petrak.hexcasting.common.casting.mishaps.MishapNotEnoughArgs
import net.minecraft.network.chat.TranslatableComponent
import kotlin.math.abs
import kotlin.math.roundToInt
object OpLastNToList : Operator {
val manaCost: Int
@ -21,7 +23,7 @@ object OpLastNToList : Operator {
val arg = stack.takeLast(1).getChecked<Double>(0)
val datum = stack[stack.lastIndex]
stack.removeLast()
if (arg < 0 || arg > stack.size) {
if (arg < 0 || arg > stack.size || abs(arg.roundToInt() - arg) >= 0.05f) {
throw MishapInvalidIota(
datum,
0,

View file

@ -0,0 +1,26 @@
package at.petrak.hexcasting.common.casting.operators.math
import at.petrak.hexcasting.api.spell.ConstManaOperator
import at.petrak.hexcasting.api.spell.Operator.Companion.getChecked
import at.petrak.hexcasting.api.spell.Operator.Companion.spellListOf
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.mishaps.MishapDivideByZero
import at.petrak.hexcasting.common.casting.mishaps.MishapInvalidIota
import net.minecraft.network.chat.TranslatableComponent
import kotlin.math.acos
import kotlin.math.asin
import kotlin.math.log
object OpLog : ConstManaOperator {
override val argc: Int
get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val value = args.getChecked<Double>(0)
val base = args.getChecked<Double>(1)
if (value <= 0.0 || base <= 0.0 || base == 1.0)
throw MishapDivideByZero.of(value, base, "logarithm")
return spellListOf(log(value, base))
}
}

View file

@ -0,0 +1,50 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.spell.Operator.Companion.getChecked
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.RenderedSpell
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.OperatorSideEffect
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgBeepAck
import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
import net.minecraft.world.phys.Vec3
import net.minecraftforge.network.PacketDistributor
import net.minecraftforge.network.PacketDistributor.TargetPoint
object OpBeep : SpellOperator {
override val argc = 3
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
val instrument = args.getChecked<Double>(1).toInt().coerceIn(0, NoteBlockInstrument.values().size - 1)
val note = args.getChecked<Double>(2).toInt().coerceIn(0, 24)
ctx.assertVecInRange(target)
return Triple(
Spell(target, note, NoteBlockInstrument.values()[instrument]),
1_000,
listOf(ParticleSpray.Cloud(target, 1.0))
)
}
override val hasCastingSound: Boolean
get() = false
private data class Spell(val target: Vec3, val note: Int, val instrument: NoteBlockInstrument) : RenderedSpell {
override fun cast(ctx: CastingContext) {
HexMessages.getNetwork().send(PacketDistributor.NEAR.with {
TargetPoint(
target.x, target.y, target.z,
128.0 * 128.0, ctx.world.dimension()
)
}, MsgBeepAck(target, note, instrument))
}
}
}

View file

@ -8,6 +8,7 @@ import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer
import at.petrak.hexcasting.common.casting.mishaps.MishapBadOffhandItem
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgColorizerUpdateAck
import net.minecraftforge.network.PacketDistributor
@ -19,10 +20,11 @@ object OpColorize : SpellOperator {
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val handStack = ctx.getHeldItemToOperateOn { FrozenColorizer.isColorizer(it.item) }
val (handStack, hand) = ctx.getHeldItemToOperateOn { FrozenColorizer.isColorizer(it.item) }
if (!FrozenColorizer.isColorizer(handStack.item)) {
throw MishapBadOffhandItem.of(
handStack,
hand,
"colorizer"
)
}
@ -35,18 +37,11 @@ object OpColorize : SpellOperator {
private object Spell : RenderedSpell {
override fun cast(ctx: CastingContext) {
val maybeCap = ctx.caster.getCapability(HexCapabilities.PREFERRED_COLORIZER).resolve()
if (!maybeCap.isPresent)
return
val cap = maybeCap.get()
val handStack = ctx.getHeldItemToOperateOn { FrozenColorizer.isColorizer(it.item) }
val (handStack) = ctx.getHeldItemToOperateOn { FrozenColorizer.isColorizer(it.item) }
if (FrozenColorizer.isColorizer(handStack.item)) {
val item = handStack.item
if (ctx.withdrawItem(handStack.item, 1, true)) {
cap.colorizer = FrozenColorizer(item, ctx.caster.uuid)
HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with { ctx.caster }, MsgColorizerUpdateAck(cap))
HexPlayerDataHelper.setColorizer(ctx.caster, FrozenColorizer(item, ctx.caster.uuid))
}
}
}

View file

@ -11,6 +11,7 @@ import at.petrak.hexcasting.common.blocks.HexBlocks
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.mishaps.MishapBadBlock
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.item.ItemStack
@ -45,13 +46,10 @@ class OpConjure(val light: Boolean) : SpellOperator {
if (state != null) {
ctx.world.setBlock(pos, state, 2)
val maybeCap = ctx.caster.getCapability(HexCapabilities.PREFERRED_COLORIZER).resolve()
if (!maybeCap.isPresent)
return
val cap = maybeCap.get()
val colorizer = HexPlayerDataHelper.getColorizer(ctx.caster)
if (ctx.world.getBlockState(pos).block is BlockConjured) {
BlockConjured.setColor(ctx.world, pos, cap.colorizer)
BlockConjured.setColor(ctx.world, pos, colorizer)
}
}
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.item.DataHolder
import at.petrak.hexcasting.api.item.SpellHolder
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.RenderedSpell
import at.petrak.hexcasting.api.spell.SpellDatum
@ -16,16 +17,17 @@ class OpErase : SpellOperator {
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val handStack = ctx.getHeldItemToOperateOn {
val (handStack, hand) = ctx.getHeldItemToOperateOn {
val item = it.item
item is ItemPackagedSpell || (item is DataHolder && it.hasTag() && item.canWrite(it.orCreateTag, null))
(item is SpellHolder && item.getPatterns(it) != null) ||
(item is DataHolder && it.hasTag() && item.canWrite(it, null))
}
val handItem = handStack.item
if (handItem !is ItemPackagedSpell &&
if ((handItem !is SpellHolder || handItem.getPatterns(handStack) == null) &&
(handItem !is DataHolder ||
!handStack.hasTag() ||
!handItem.canWrite(handStack.orCreateTag, null))) {
throw MishapBadOffhandItem.of(handStack, "eraseable")
!handItem.canWrite(handStack, null))) {
throw MishapBadOffhandItem.of(handStack, hand, "eraseable")
}
return Triple(Spell, 10_000, listOf())
@ -33,19 +35,17 @@ class OpErase : SpellOperator {
private object Spell : RenderedSpell {
override fun cast(ctx: CastingContext) {
val handStack = ctx.getHeldItemToOperateOn {
val (handStack) = ctx.getHeldItemToOperateOn {
val item = it.item
item is ItemPackagedSpell || (item is DataHolder && it.hasTag() && item.canWrite(it.orCreateTag, null))
(item is SpellHolder && item.getPatterns(it) != null) ||
(item is DataHolder && it.hasTag() && item.canWrite(it, null))
}
val handItem = handStack.item
if (handStack.hasTag()) {
val tag = handStack.orCreateTag
if (handItem is ItemPackagedSpell) {
tag.remove(ItemPackagedSpell.TAG_MANA)
tag.remove(ItemPackagedSpell.TAG_MAX_MANA)
tag.remove(ItemPackagedSpell.TAG_PATTERNS)
} else if (handItem is DataHolder && handItem.canWrite(tag, null)) {
handItem.writeDatum(tag, null)
if (handItem is SpellHolder && handItem.getPatterns(handStack) != null) {
handItem.clearPatterns(handStack)
} else if (handItem is DataHolder && handItem.canWrite(handStack, null)) {
handItem.writeDatum(handStack, null)
}
}
}

View file

@ -7,7 +7,7 @@ import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.ManaHelper
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer
import at.petrak.hexcasting.common.casting.mishaps.MishapBadItem
import at.petrak.hexcasting.common.casting.mishaps.MishapBadOffhandItem
import at.petrak.hexcasting.common.items.HexItems
import at.petrak.hexcasting.common.items.magic.ItemManaHolder
@ -23,26 +23,28 @@ object OpMakeBattery : SpellOperator {
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val entity = args.getChecked<ItemEntity>(0)
val otherHandItem = ctx.getHeldItemToOperateOn { it.item == Items.GLASS_BOTTLE }
val (handStack, hand) = ctx.getHeldItemToOperateOn { it.item == Items.GLASS_BOTTLE }
if (otherHandItem.item != Items.GLASS_BOTTLE) {
if (handStack.item != Items.GLASS_BOTTLE) {
throw MishapBadOffhandItem.of(
otherHandItem,
handStack,
hand,
"bottle"
)
}
if (otherHandItem.count != 1) {
if (handStack.count != 1) {
throw MishapBadOffhandItem.of(
otherHandItem,
handStack,
hand,
"only_one"
)
}
ctx.assertEntityInRange(entity)
if (!ManaHelper.isManaItem(entity.item)) {
throw MishapBadOffhandItem.of(
otherHandItem,
if (!ManaHelper.isManaItem(entity.item) || ManaHelper.extractMana(entity.item, drainForBatteries = true, simulate = true) <= 0) {
throw MishapBadItem.of(
entity.item,
"mana"
)
}
@ -52,17 +54,17 @@ object OpMakeBattery : SpellOperator {
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {
override fun cast(ctx: CastingContext) {
val handStack = ctx.getHeldItemToOperateOn { it.item == Items.GLASS_BOTTLE }
val (handStack, hand) = ctx.getHeldItemToOperateOn { it.item == Items.GLASS_BOTTLE }
if (handStack.item == Items.GLASS_BOTTLE && itemEntity.isAlive) {
val manaAmt = ManaHelper.extractAllMana(itemEntity.item)
if (manaAmt != null) {
val replaceItem = ItemStack(HexItems.BATTERY.get())
val tag = replaceItem.orCreateTag
tag.putInt(ItemManaHolder.TAG_MANA, manaAmt)
tag.putInt(ItemManaHolder.TAG_MAX_MANA, manaAmt)
ctx.caster.setItemInHand(ctx.otherHand, replaceItem)
val entityStack = itemEntity.item.copy()
val manaAmt = ManaHelper.extractMana(entityStack, drainForBatteries = true)
if (manaAmt > 0) {
ctx.caster.setItemInHand(hand, ItemManaHolder.withMana(ItemStack(HexItems.BATTERY.get()), manaAmt, manaAmt))
}
itemEntity.item = entityStack
if (entityStack.isEmpty)
itemEntity.kill()
}
}
}

View file

@ -1,5 +1,6 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.item.SpellHolder
import at.petrak.hexcasting.api.spell.Operator.Companion.getChecked
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.RenderedSpell
@ -7,11 +8,13 @@ import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.ManaHelper
import at.petrak.hexcasting.common.casting.mishaps.MishapBadItem
import at.petrak.hexcasting.common.casting.mishaps.MishapBadOffhandItem
import at.petrak.hexcasting.common.casting.mishaps.MishapInvalidIota
import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell
import at.petrak.hexcasting.hexmath.HexPattern
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.entity.item.ItemEntity
@ -29,15 +32,15 @@ class OpMakePackagedSpell<T : ItemPackagedSpell>(val itemType: T, val cost: Int)
throw MishapInvalidIota(it, 0, TranslatableComponent("hexcasting.mishap.invalid_value.list.pattern"))
}
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
if (!otherHandItem.`is`(itemType)) {
throw MishapBadOffhandItem(otherHandItem, itemType.description)
val (handStack, hand) = ctx.getHeldItemToOperateOn { it.`is`(itemType) }
if (!handStack.`is`(itemType)) {
throw MishapBadOffhandItem(handStack, hand, itemType.description)
}
ctx.assertEntityInRange(entity)
if (!ManaHelper.isManaItem(entity.item)) {
throw MishapBadOffhandItem.of(
otherHandItem,
if (!ManaHelper.isManaItem(entity.item) || ManaHelper.extractMana(entity.item, drainForBatteries = true, simulate = true) <= 0) {
throw MishapBadItem.of(
entity.item,
"mana"
)
}
@ -45,32 +48,24 @@ class OpMakePackagedSpell<T : ItemPackagedSpell>(val itemType: T, val cost: Int)
return Triple(Spell(entity, patterns), cost, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
}
private data class Spell(val itemEntity: ItemEntity, val patterns: List<HexPattern>) : RenderedSpell {
private inner class Spell(val itemEntity: ItemEntity, val patterns: List<HexPattern>) : RenderedSpell {
override fun cast(ctx: CastingContext) {
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
val tag = otherHandItem.orCreateTag
if (otherHandItem.item is ItemPackagedSpell
&& !tag.contains(ItemPackagedSpell.TAG_MANA)
&& !tag.contains(ItemPackagedSpell.TAG_MAX_MANA)
&& !tag.contains(ItemPackagedSpell.TAG_PATTERNS)
val (handStack) = ctx.getHeldItemToOperateOn { it.`is`(itemType) }
val handItem = handStack.item
if (handItem is SpellHolder
&& handItem.getPatterns(handStack) == null
&& itemEntity.isAlive
) {
val manaAmt = ManaHelper.extractAllMana(itemEntity.item)
if (manaAmt != null) {
val tag = otherHandItem.orCreateTag
tag.putInt(ItemPackagedSpell.TAG_MANA, manaAmt)
tag.putInt(ItemPackagedSpell.TAG_MAX_MANA, manaAmt)
val patsTag = ListTag()
for (pat in patterns) {
patsTag.add(pat.serializeToNBT())
}
tag.put(ItemPackagedSpell.TAG_PATTERNS, patsTag)
if (itemEntity.item.isEmpty)
itemEntity.kill()
val entityStack = itemEntity.item.copy()
val manaAmt = ManaHelper.extractMana(entityStack, drainForBatteries = true)
if (manaAmt > 0) {
handItem.writePattern(handStack, patterns, manaAmt)
}
itemEntity.item = entityStack
if (entityStack.isEmpty)
itemEntity.kill()
}
}
}
}
}

View file

@ -1,5 +1,6 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.item.ManaHolder
import at.petrak.hexcasting.api.spell.Operator.Companion.getChecked
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.RenderedSpell
@ -10,7 +11,7 @@ import at.petrak.hexcasting.common.casting.ManaHelper
import at.petrak.hexcasting.common.casting.mishaps.MishapBadItem
import at.petrak.hexcasting.common.casting.mishaps.MishapBadOffhandItem
import at.petrak.hexcasting.common.items.magic.ItemManaHolder
import net.minecraft.util.Mth
import at.petrak.hexcasting.common.lib.HexCapabilities
import net.minecraft.world.entity.item.ItemEntity
object OpRecharge : SpellOperator {
@ -18,12 +19,18 @@ object OpRecharge : SpellOperator {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val handStack = ctx.getHeldItemToOperateOn { it.item is ItemManaHolder }
): Triple<RenderedSpell, Int, List<ParticleSpray>>? {
val (handStack, hand) = ctx.getHeldItemToOperateOn {
val mana = it.getCapability(HexCapabilities.MANA).resolve()
mana.isPresent && mana.get().canRecharge() && mana.get().mana < mana.get().maxMana
}
if (handStack.item !is ItemManaHolder)
val mana = handStack.getCapability(HexCapabilities.MANA).resolve()
if (!mana.isPresent || !mana.get().canRecharge())
throw MishapBadOffhandItem.of(
handStack,
hand,
"rechargable"
)
@ -37,27 +44,33 @@ object OpRecharge : SpellOperator {
)
}
if (mana.get().mana >= mana.get().maxMana)
return null
return Triple(Spell(entity), 100_000, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
}
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {
override fun cast(ctx: CastingContext) {
val handStack = ctx.getHeldItemToOperateOn { it.item is ItemManaHolder }
val (handStack) = ctx.getHeldItemToOperateOn {
val mana = it.getCapability(HexCapabilities.MANA).resolve()
mana.isPresent && mana.get().canRecharge() && mana.get().mana < mana.get().maxMana
}
val mana = handStack.getCapability(HexCapabilities.MANA).resolve()
if (handStack.item is ItemManaHolder && itemEntity.isAlive) {
val manaAmt = ManaHelper.extractAllMana(itemEntity.item)
if (manaAmt != null) {
val tag = handStack.orCreateTag
val maxMana = if (tag.contains(ItemManaHolder.TAG_MAX_MANA))
tag.getInt(ItemManaHolder.TAG_MAX_MANA)
else
Int.MAX_VALUE
val existingMana = if (tag.contains(ItemManaHolder.TAG_MANA))
tag.getInt(ItemManaHolder.TAG_MANA)
else
0
tag.putInt(ItemManaHolder.TAG_MANA, Mth.clamp(existingMana + manaAmt, 0, maxMana))
}
if (mana.isPresent && itemEntity.isAlive) {
val entityStack = itemEntity.item.copy()
val maxMana = mana.get().maxMana
val existingMana = mana.get().mana
val manaAmt = ManaHelper.extractMana(entityStack, maxMana - existingMana)
mana.get().mana = manaAmt + existingMana
itemEntity.item = entityStack
if (entityStack.isEmpty)
itemEntity.kill()
}
}
}

View file

@ -0,0 +1,11 @@
package at.petrak.hexcasting.common.casting.operators.spells.great;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public record FlightAbility(boolean allowed, int timeLeft, ResourceKey<Level> dimension, Vec3 origin, double radius) {
public static FlightAbility deny() {
return new FlightAbility(false, 0, Level.OVERWORLD, Vec3.ZERO, 0);
}
}

View file

@ -9,6 +9,7 @@ import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgAddMotionAck
import net.minecraft.core.Direction
@ -51,16 +52,8 @@ object OpFlight : SpellOperator {
// Don't accidentally clobber someone else's flight
return
}
val maybeCap = target.getCapability(HexCapabilities.FLIGHT).resolve()
if (!maybeCap.isPresent) {
// uh oh
return
}
val cap = maybeCap.get()
cap.allowed = true
cap.flightTime = time
cap.radius = radius
cap.origin = origin
HexPlayerDataHelper.setFlight(target, FlightAbility(true, time, target.level.dimension(), origin, radius))
target.abilities.mayfly = true
target.abilities.flying = true
@ -71,65 +64,20 @@ object OpFlight : SpellOperator {
}
}
const val CAP_NAME = "flight"
const val TAG_ALLOWED = "can_fly"
const val TAG_FLIGHT_TIME = "flight_time"
const val TAG_ORIGIN = "origin"
const val TAG_RADIUS = "radius"
class CapFlight(var allowed: Boolean, var flightTime: Int, var origin: Vec3, var radius: Double) :
ICapabilitySerializable<CompoundTag> {
override fun <T : Any?> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> =
HexCapabilities.FLIGHT.orEmpty(cap, LazyOptional.of { this })
override fun serializeNBT(): CompoundTag {
val out = CompoundTag()
if (this.allowed) {
out.putBoolean(TAG_ALLOWED, this.allowed)
out.putInt(TAG_FLIGHT_TIME, flightTime)
out.put(TAG_ORIGIN, this.origin.serializeToNBT())
out.putDouble(TAG_RADIUS, this.radius)
}
return out
}
override fun deserializeNBT(nbt: CompoundTag) {
this.allowed = nbt.getBoolean(TAG_ALLOWED)
if (this.allowed) {
this.flightTime = nbt.getInt(TAG_FLIGHT_TIME)
this.origin = HexUtils.DeserializeVec3FromNBT(nbt.getLongArray(TAG_ORIGIN))
this.radius = nbt.getDouble(TAG_RADIUS)
}
}
}
@SubscribeEvent
fun tickDownFlight(evt: LivingEvent.LivingUpdateEvent) {
val entity = evt.entityLiving
if (entity !is ServerPlayer) return
val maybeCap = entity.getCapability(HexCapabilities.FLIGHT).resolve()
if (!maybeCap.isPresent) {
// nah we were just capping
return
}
val cap = maybeCap.get()
if (cap.allowed) {
cap.flightTime--
if (cap.flightTime < 0 || cap.origin.distanceToSqr(entity.position()) > cap.radius * cap.radius) {
val flight = HexPlayerDataHelper.getFlight(entity)
if (flight.allowed) {
val flightTime = flight.timeLeft - 1
if (flightTime < 0 || flight.origin.distanceToSqr(entity.position()) > flight.radius * flight.radius || flight.dimension != entity.level.dimension()) {
if (!entity.isOnGround) {
entity.fallDistance = 1_000_000f
/*
val move = entity.deltaMovement
HexMessages.getNetwork()
.send(
PacketDistributor.PLAYER.with { entity },
MsgAddMotionAck(Vec3(0.0, -move.y - 100.0, 0.0))
)
*/
}
cap.allowed = false
HexPlayerDataHelper.setFlight(entity, FlightAbility.deny())
if (!entity.isCreative && !entity.isSpectator) {
val abilities = entity.abilities
@ -137,8 +85,9 @@ object OpFlight : SpellOperator {
abilities.mayfly = false
entity.onUpdateAbilities()
}
}
} else
HexPlayerDataHelper.setFlight(entity, FlightAbility(true, flightTime, flight.dimension, flight.origin, flight.radius))
}
}
}
}

View file

@ -1,61 +0,0 @@
package at.petrak.hexcasting.common.casting.operators.spells.sentinel;
import at.petrak.hexcasting.HexUtils;
import at.petrak.hexcasting.common.lib.HexCapabilities;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.common.util.LazyOptional;
import org.jetbrains.annotations.NotNull;
// it *really* doesn't like this being kotlin
public class CapSentinel implements ICapabilitySerializable<CompoundTag> {
public static final String CAP_NAME = "sentinel";
public static final String TAG_EXISTS = "exists";
public static final String TAG_EXTENDS_RANGE = "extends_range";
public static final String TAG_POSITION = "position";
public static final String TAG_DIMENSION = "dimension";
public boolean hasSentinel;
public boolean extendsRange;
public Vec3 position;
public ResourceKey<Level> dimension;
public CapSentinel(boolean hasSentinel, boolean extendsRange, Vec3 position, ResourceKey<Level> dimension) {
this.hasSentinel = hasSentinel;
this.extendsRange = extendsRange;
this.position = position;
this.dimension = dimension;
}
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, Direction dir) {
return HexCapabilities.SENTINEL.orEmpty(cap, LazyOptional.of(() -> this));
}
@Override
public CompoundTag serializeNBT() {
var tag = new CompoundTag();
tag.putBoolean(TAG_EXISTS, this.hasSentinel);
tag.putBoolean(TAG_EXTENDS_RANGE, this.extendsRange);
tag.put(TAG_POSITION, HexUtils.serializeToNBT(this.position));
tag.putString(TAG_DIMENSION, dimension.location().toString());
return tag;
}
@Override
public void deserializeNBT(CompoundTag tag) {
this.hasSentinel = tag.getBoolean(TAG_EXISTS);
this.extendsRange = tag.getBoolean(TAG_EXTENDS_RANGE);
this.dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(tag.getString(TAG_DIMENSION)));
this.position = HexUtils.DeserializeVec3FromNBT(tag.getLongArray(TAG_POSITION));
}
}

View file

@ -7,6 +7,7 @@ import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgSentinelStatusUpdateAck
import net.minecraft.world.phys.Vec3
@ -32,17 +33,7 @@ class OpCreateSentinel(val extendsRange: Boolean) : SpellOperator {
private data class Spell(val target: Vec3, val extendsRange: Boolean) : RenderedSpell {
override fun cast(ctx: CastingContext) {
val maybeCap = ctx.caster.getCapability(HexCapabilities.SENTINEL).resolve()
if (!maybeCap.isPresent)
return
val cap = maybeCap.get()
cap.hasSentinel = true
cap.extendsRange = extendsRange
cap.position = target
cap.dimension = ctx.world.dimension()
HexMessages.getNetwork().send(PacketDistributor.PLAYER.with { ctx.caster }, MsgSentinelStatusUpdateAck(cap))
HexPlayerDataHelper.setSentinel(ctx.caster, Sentinel(true, extendsRange, target, ctx.world.dimension()))
}
}
}

View file

@ -7,6 +7,7 @@ import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.mishaps.MishapLocationInWrongDimension
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgSentinelStatusUpdateAck
import net.minecraftforge.network.PacketDistributor
@ -18,12 +19,10 @@ object OpDestroySentinel : SpellOperator {
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val particles = mutableListOf<ParticleSpray>()
val maybeCap = ctx.caster.getCapability(HexCapabilities.SENTINEL).resolve()
maybeCap.ifPresent {
if (it.dimension != ctx.world.dimension())
throw MishapLocationInWrongDimension(it.dimension.location())
particles.add(ParticleSpray.Cloud(it.position, 2.0))
}
val sentinel = HexPlayerDataHelper.getSentinel(ctx.caster)
if (sentinel.dimension != ctx.world.dimension())
throw MishapLocationInWrongDimension(sentinel.dimension.location())
particles.add(ParticleSpray.Cloud(sentinel.position, 2.0))
return Triple(
Spell,
@ -34,14 +33,7 @@ object OpDestroySentinel : SpellOperator {
private object Spell : RenderedSpell {
override fun cast(ctx: CastingContext) {
val maybeCap = ctx.caster.getCapability(HexCapabilities.SENTINEL).resolve()
if (!maybeCap.isPresent)
return
val cap = maybeCap.get()
cap.hasSentinel = false
HexMessages.getNetwork().send(PacketDistributor.PLAYER.with { ctx.caster }, MsgSentinelStatusUpdateAck(cap))
HexPlayerDataHelper.setSentinel(ctx.caster, Sentinel.none())
}
}
}

View file

@ -7,21 +7,18 @@ import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.Widget
import at.petrak.hexcasting.common.casting.mishaps.MishapLocationInWrongDimension
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
object OpGetSentinelPos : ConstManaOperator {
override val argc = 0
override val manaCost = 1_000
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val maybeCap = ctx.caster.getCapability(HexCapabilities.SENTINEL).resolve()
if (!maybeCap.isPresent)
return spellListOf(Widget.NULL)
val cap = maybeCap.get()
if (cap.dimension != ctx.world.dimension())
throw MishapLocationInWrongDimension(cap.dimension.location())
val sentinel = HexPlayerDataHelper.getSentinel(ctx.caster)
if (sentinel.dimension != ctx.world.dimension())
throw MishapLocationInWrongDimension(sentinel.dimension.location())
return spellListOf(
if (cap.hasSentinel)
cap.position
if (sentinel.hasSentinel)
sentinel.position
else
Widget.NULL
)

View file

@ -8,6 +8,7 @@ import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.Widget
import at.petrak.hexcasting.common.casting.mishaps.MishapLocationInWrongDimension
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper
import net.minecraft.world.phys.Vec3
object OpGetSentinelWayfind : ConstManaOperator {
@ -16,18 +17,15 @@ object OpGetSentinelWayfind : ConstManaOperator {
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val from = args.getChecked<Vec3>(0)
val maybeCap = ctx.caster.getCapability(HexCapabilities.SENTINEL).resolve()
if (!maybeCap.isPresent)
return spellListOf(Widget.NULL)
val sentinel = HexPlayerDataHelper.getSentinel(ctx.caster)
val cap = maybeCap.get()
if (cap.dimension != ctx.world.dimension())
throw MishapLocationInWrongDimension(cap.dimension.location())
if (sentinel.dimension != ctx.world.dimension())
throw MishapLocationInWrongDimension(sentinel.dimension.location())
val sentinelPos = if (!cap.hasSentinel)
val sentinelPos = if (!sentinel.hasSentinel)
return spellListOf(Widget.NULL)
else
cap.position
sentinel.position
return spellListOf(sentinelPos.subtract(from).normalize())
}

View file

@ -0,0 +1,12 @@
package at.petrak.hexcasting.common.casting.operators.spells.sentinel;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public record Sentinel(boolean hasSentinel, boolean extendsRange, Vec3 position,
ResourceKey<Level> dimension) {
public static Sentinel none() {
return new Sentinel(false, false, Vec3.ZERO, Level.OVERWORLD);
}
}

View file

@ -39,4 +39,4 @@ object OpFisherman : Operator {
return OperationResult(stack, sideEffects)
}
}
}

View file

@ -7,7 +7,7 @@ public class HexCommands {
@SubscribeEvent
public static void register(RegisterCommandsEvent evt) {
var dp = evt.getDispatcher();
ListPatsCommand.register(dp);
ListPatternsCommand.register(dp);
RecalcPatternsCommand.register(dp);
BrainsweepCommand.register(dp);
}

View file

@ -1,11 +1,11 @@
package at.petrak.hexcasting.common.command;
import at.petrak.hexcasting.api.PatternRegistry;
import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.common.items.HexItems;
import at.petrak.hexcasting.common.items.ItemScroll;
import at.petrak.hexcasting.hexmath.HexPattern;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.datafixers.util.Pair;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.ResourceLocationArgument;
@ -15,27 +15,25 @@ import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
public class ListPatsCommand {
public class ListPatternsCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("hexcasting:patterns")
.requires(dp -> dp.hasPermission(Commands.LEVEL_ADMINS))
.then(Commands.literal("list").executes(ctx -> {
var lookup = PatternRegistry.getPerWorldPatterns(ctx.getSource().getLevel());
var listing = lookup.keySet()
var listing = lookup.entrySet()
.stream()
.map(key -> new Pair<>(lookup.get(key).getFirst(), key))
.sorted((a, b) -> a.getFirst().compareNamespaced(b.getFirst()))
.sorted((a, b) -> a.getValue().getFirst().compareNamespaced(b.getValue().getFirst()))
.toList();
var bob = new StringBuilder("Patterns in this world:");
ctx.getSource().sendSuccess(new TranslatableComponent("command.hexcasting.pats.listing"), false);
for (var pair : listing) {
bob.append('\n');
bob.append(pair.getFirst());
bob.append(": ");
bob.append(pair.getSecond());
ctx.getSource().sendSuccess(new TextComponent(pair.getValue().getFirst().toString())
.append(": ")
.append(SpellDatum.make(HexPattern.FromAnglesSig(pair.getKey(), pair.getValue().getSecond())).display()), false);
}
ctx.getSource().sendSuccess(new TextComponent(bob.toString()), true);
return lookup.size();
}))

View file

@ -8,6 +8,7 @@ import at.petrak.hexcasting.hexmath.HexPattern;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.syncher.EntityDataAccessor;
@ -58,7 +59,7 @@ public class EntityWallScroll extends HangingEntity implements IEntityAdditional
this.scroll = scroll;
var tag = scroll.getTag();
if (tag != null && tag.contains(ItemScroll.TAG_PATTERN)) {
if (tag != null && tag.contains(ItemScroll.TAG_PATTERN, Tag.TAG_COMPOUND)) {
this.pattern = HexPattern.DeserializeFromNBT(tag.getCompound(ItemScroll.TAG_PATTERN));
if (this.level.isClientSide) {
var pair = RenderLib.getCenteredPattern(pattern, 128, 128, 16f);
@ -66,7 +67,7 @@ public class EntityWallScroll extends HangingEntity implements IEntityAdditional
this.zappyPoints = RenderLib.makeZappy(dots, 10f, 0.8f, 0f);
}
this.isAncient = tag.contains(ItemScroll.TAG_OP_ID);
this.isAncient = tag.contains(ItemScroll.TAG_OP_ID, Tag.TAG_STRING);
} else {
this.pattern = null;
this.zappyPoints = null;

View file

@ -4,10 +4,7 @@ import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.common.blocks.HexBlocks;
import at.petrak.hexcasting.common.items.colorizer.ItemDyeColorizer;
import at.petrak.hexcasting.common.items.colorizer.ItemPrideColorizer;
import at.petrak.hexcasting.common.items.magic.ItemArtifact;
import at.petrak.hexcasting.common.items.magic.ItemCypher;
import at.petrak.hexcasting.common.items.magic.ItemManaBattery;
import at.petrak.hexcasting.common.items.magic.ItemTrinket;
import at.petrak.hexcasting.common.items.magic.*;
import net.minecraft.core.NonNullList;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.CreativeModeTab;
@ -40,11 +37,8 @@ public class HexItems {
1_000_000_000,
};
for (int manamount : manamounts) {
var stack = new ItemStack(BATTERY.get(), 1);
var tag = stack.getOrCreateTag();
tag.putInt(ItemManaBattery.TAG_MANA, manamount);
tag.putInt(ItemManaBattery.TAG_MAX_MANA, manamount);
items.add(stack);
var stack = new ItemStack(BATTERY.get());
items.add(ItemManaHolder.withMana(stack, manamount, manamount));
}
}
};

View file

@ -39,12 +39,12 @@ public class ItemAbacus extends Item implements DataHolder {
}
@Override
public boolean canWrite(CompoundTag tag, SpellDatum<?> datum) {
public boolean canWrite(ItemStack stack, SpellDatum<?> datum) {
return false;
}
@Override
public void writeDatum(CompoundTag tag, SpellDatum<?> datum) {
public void writeDatum(ItemStack stack, SpellDatum<?> datum) {
// nope
}
@ -55,7 +55,7 @@ public class ItemAbacus extends Item implements DataHolder {
var tag = stack.getOrCreateTag();
double oldNum = 0d;
if (tag.contains(TAG_VALUE, Tag.TAG_DOUBLE)) {
if (tag.contains(TAG_VALUE, Tag.TAG_ANY_NUMERIC)) {
oldNum = tag.getDouble(TAG_VALUE);
}
tag.putDouble(TAG_VALUE, 0d);

View file

@ -3,7 +3,9 @@ package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.api.item.DataHolder;
import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.common.casting.Widget;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
@ -29,7 +31,7 @@ public class ItemFocus extends Item implements DataHolder {
return null;
}
var tag = stack.getTag();
if (!tag.contains(TAG_DATA)) {
if (!tag.contains(TAG_DATA, Tag.TAG_COMPOUND)) {
return null;
}
@ -37,12 +39,19 @@ public class ItemFocus extends Item implements DataHolder {
}
@Override
public boolean canWrite(CompoundTag tag, SpellDatum<?> datum) {
return !tag.getBoolean(TAG_SEALED);
public @Nullable SpellDatum<?> emptyDatum(ItemStack stack) {
return SpellDatum.make(Widget.NULL);
}
@Override
public void writeDatum(CompoundTag tag, SpellDatum<?> datum) {
public boolean canWrite(ItemStack stack, SpellDatum<?> datum) {
return !stack.hasTag() || !stack.getTag().getBoolean(TAG_SEALED);
}
@Override
public void writeDatum(ItemStack stack, SpellDatum<?> datum) {
CompoundTag tag = stack.getOrCreateTag();
if (!tag.getBoolean(TAG_SEALED)) {
if (datum == null)
tag.remove(TAG_DATA);

View file

@ -9,6 +9,7 @@ import at.petrak.hexcasting.hexmath.HexPattern;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
@ -42,7 +43,7 @@ public class ItemScroll extends Item implements DataHolder {
@Override
public @Nullable CompoundTag readDatumTag(ItemStack stack) {
var stackTag = stack.getTag();
if (stackTag == null || !stackTag.contains(TAG_PATTERN)) {
if (stackTag == null || !stackTag.contains(TAG_PATTERN, Tag.TAG_COMPOUND)) {
return null;
}
@ -53,14 +54,14 @@ public class ItemScroll extends Item implements DataHolder {
}
@Override
public boolean canWrite(CompoundTag tag, SpellDatum<?> datum) {
return datum != null && datum.getType() == DatumType.PATTERN && !tag.contains(TAG_PATTERN);
public boolean canWrite(ItemStack stack, SpellDatum<?> datum) {
return datum != null && datum.getType() == DatumType.PATTERN && (!stack.hasTag() || !stack.getTag().contains(TAG_PATTERN, Tag.TAG_COMPOUND));
}
@Override
public void writeDatum(CompoundTag tag, SpellDatum<?> datum) {
if (this.canWrite(tag, datum) && datum.getPayload() instanceof HexPattern pat) {
tag.put(TAG_PATTERN, pat.serializeToNBT());
public void writeDatum(ItemStack stack, SpellDatum<?> datum) {
if (this.canWrite(stack, datum) && datum.getPayload() instanceof HexPattern pat) {
stack.getOrCreateTag().put(TAG_PATTERN, pat.serializeToNBT());
}
}
@ -106,10 +107,10 @@ public class ItemScroll extends Item implements DataHolder {
@Override
public Component getName(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
if (tag.contains(TAG_OP_ID)) {
if (tag.contains(TAG_OP_ID, Tag.TAG_STRING)) {
return new TranslatableComponent("item.hexcasting.scroll.of",
new TranslatableComponent("hexcasting.spell." + ResourceLocation.tryParse(tag.getString(TAG_OP_ID))));
} else if (tag.contains(TAG_PATTERN)) {
} else if (tag.contains(TAG_PATTERN, Tag.TAG_COMPOUND)) {
return new TranslatableComponent("item.hexcasting.scroll");
} else {
return new TranslatableComponent("item.hexcasting.scroll.empty");

View file

@ -7,6 +7,7 @@ import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.common.blocks.circles.BlockEntitySlate;
import at.petrak.hexcasting.hexmath.HexPattern;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
@ -30,9 +31,9 @@ public class ItemSlate extends BlockItem implements DataHolder {
public static boolean hasPattern(ItemStack stack) {
var tag = stack.getTag();
if (tag != null && tag.contains("BlockEntityTag")) {
if (tag != null && tag.contains("BlockEntityTag", Tag.TAG_COMPOUND)) {
var bet = tag.getCompound("BlockEntityTag");
return bet.contains(BlockEntitySlate.TAG_PATTERN);
return bet.contains(BlockEntitySlate.TAG_PATTERN, Tag.TAG_COMPOUND);
}
return false;
}
@ -44,7 +45,7 @@ public class ItemSlate extends BlockItem implements DataHolder {
return null;
}
var beTag = stackTag.getCompound("BlockEntityTag");
if (!beTag.contains(BlockEntitySlate.TAG_PATTERN)) {
if (!beTag.contains(BlockEntitySlate.TAG_PATTERN, Tag.TAG_COMPOUND)) {
return null;
}
@ -55,21 +56,24 @@ public class ItemSlate extends BlockItem implements DataHolder {
}
@Override
public boolean canWrite(CompoundTag tag, SpellDatum<?> datum) {
public boolean canWrite(ItemStack stack, SpellDatum<?> datum) {
if (datum == null || datum.getType() != DatumType.PATTERN) {
return false;
}
var beTag = tag.getCompound("BlockEntityTag");
return !beTag.contains(BlockEntitySlate.TAG_PATTERN);
if (!stack.hasTag())
return true;
var beTag = stack.getTagElement("BlockEntityTag");
return beTag == null || !beTag.contains(BlockEntitySlate.TAG_PATTERN, Tag.TAG_COMPOUND);
}
@Override
public void writeDatum(CompoundTag tag, SpellDatum<?> datum) {
if (this.canWrite(tag, datum) && datum.getPayload() instanceof HexPattern pat) {
var beTag = tag.getCompound("BlockEntityTag");
public void writeDatum(ItemStack stack, SpellDatum<?> datum) {
if (this.canWrite(stack, datum) && datum.getPayload() instanceof HexPattern pat) {
CompoundTag tag = stack.getOrCreateTag();
var beTag = stack.getOrCreateTagElement("BlockEntityTag");
beTag.put(BlockEntitySlate.TAG_PATTERN, pat.serializeToNBT());
tag.put("BlockEntityTag", beTag);
}
}
}

View file

@ -3,6 +3,7 @@ package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.api.item.DataHolder;
import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.common.casting.Widget;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
@ -40,10 +41,9 @@ public class ItemSpellbook extends Item implements DataHolder {
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltip,
TooltipFlag isAdvanced) {
var tag = stack.getOrCreateTag();
if (tag.contains(TAG_SELECTED_PAGE)) {
if (tag.contains(TAG_SELECTED_PAGE, Tag.TAG_ANY_NUMERIC)) {
var pageIdx = tag.getInt(TAG_SELECTED_PAGE);
var pages = tag.getCompound(ItemSpellbook.TAG_PAGES);
int highest = HighestPage(pages);
int highest = HighestPage(stack);
if (highest != 0) {
tooltip.add(new TranslatableComponent("hexcasting.tooltip.spellbook.page",
new TextComponent(String.valueOf(pageIdx)).withStyle(ChatFormatting.WHITE),
@ -65,9 +65,9 @@ public class ItemSpellbook extends Item implements DataHolder {
public void inventoryTick(ItemStack stack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
var tag = stack.getOrCreateTag();
int index;
if (ArePagesEmpty(tag)) {
if (ArePagesEmpty(stack)) {
index = 0;
} else if (!tag.contains(TAG_SELECTED_PAGE)) {
} else if (!tag.contains(TAG_SELECTED_PAGE, Tag.TAG_ANY_NUMERIC)) {
index = 1;
} else {
index = tag.getInt(TAG_SELECTED_PAGE);
@ -86,8 +86,11 @@ public class ItemSpellbook extends Item implements DataHolder {
tag.put(TAG_PAGE_NAMES, names);
}
public static boolean ArePagesEmpty(CompoundTag tag) {
return !tag.contains(ItemSpellbook.TAG_PAGES) ||
public static boolean ArePagesEmpty(ItemStack stack) {
if (!stack.hasTag())
return true;
CompoundTag tag = stack.getTag();
return !tag.contains(ItemSpellbook.TAG_PAGES, Tag.TAG_COMPOUND) ||
tag.getCompound(ItemSpellbook.TAG_PAGES).isEmpty();
}
@ -99,13 +102,13 @@ public class ItemSpellbook extends Item implements DataHolder {
var tag = stack.getTag();
int idx;
if (tag.contains(TAG_SELECTED_PAGE)) {
if (tag.contains(TAG_SELECTED_PAGE, Tag.TAG_ANY_NUMERIC)) {
idx = tag.getInt(TAG_SELECTED_PAGE);
} else {
idx = 0;
}
var key = String.valueOf(idx);
if (tag.contains(TAG_PAGES)) {
if (tag.contains(TAG_PAGES, Tag.TAG_COMPOUND)) {
var pagesTag = tag.getCompound(TAG_PAGES);
if (pagesTag.contains(key)) {
return pagesTag.getCompound(key);
@ -118,23 +121,31 @@ public class ItemSpellbook extends Item implements DataHolder {
}
@Override
public boolean canWrite(CompoundTag tag, SpellDatum<?> datum) {
public @Nullable SpellDatum<?> emptyDatum(ItemStack stack) {
return SpellDatum.make(Widget.NULL);
}
@Override
public boolean canWrite(ItemStack stack, SpellDatum<?> datum) {
return true;
}
public void writeDatum(CompoundTag tag, SpellDatum<?> datum) {
@Override
public void writeDatum(ItemStack stack, SpellDatum<?> datum) {
CompoundTag tag = stack.getOrCreateTag();
int idx;
if (tag.contains(TAG_SELECTED_PAGE)) {
if (tag.contains(TAG_SELECTED_PAGE, Tag.TAG_ANY_NUMERIC)) {
idx = tag.getInt(TAG_SELECTED_PAGE);
// But we want to write to page *1* to start if this is our first page
if (idx == 0 && ArePagesEmpty(tag)) {
if (idx == 0 && ArePagesEmpty(stack)) {
idx = 1;
}
} else {
idx = 1;
}
var key = String.valueOf(idx);
if (tag.contains(TAG_PAGES)) {
if (tag.contains(TAG_PAGES, Tag.TAG_COMPOUND)) {
if (datum == null)
tag.getCompound(TAG_PAGES).remove(key);
else
@ -146,7 +157,12 @@ public class ItemSpellbook extends Item implements DataHolder {
}
}
public static int HighestPage(CompoundTag tag) {
public static int HighestPage(ItemStack stack) {
if (!stack.hasTag())
return 0;
CompoundTag tag = stack.getTagElement(TAG_PAGES);
if (tag == null)
return 0;
var highestKey = tag.getAllKeys().stream().flatMap(s -> {
try {
return Stream.of(Integer.parseInt(s));
@ -157,11 +173,12 @@ public class ItemSpellbook extends Item implements DataHolder {
return highestKey.orElse(0);
}
public static void RotatePageIdx(ItemStack stack, CompoundTag tag, boolean increase) {
public static void RotatePageIdx(ItemStack stack, boolean increase) {
CompoundTag tag = stack.getOrCreateTag();
int newIdx;
if (ArePagesEmpty(tag)) {
if (ArePagesEmpty(stack)) {
newIdx = 0;
} else if (tag.contains(TAG_SELECTED_PAGE)) {
} else if (tag.contains(TAG_SELECTED_PAGE, Tag.TAG_ANY_NUMERIC)) {
var delta = increase ? 1 : -1;
newIdx = Math.max(1, tag.getInt(TAG_SELECTED_PAGE) + delta);
} else {

View file

@ -1,14 +1,9 @@
package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.common.casting.CastingContext;
import at.petrak.hexcasting.common.casting.CastingHarness;
import at.petrak.hexcasting.common.casting.ResolvedPattern;
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper;
import at.petrak.hexcasting.common.lib.HexSounds;
import at.petrak.hexcasting.common.network.HexMessages;
import at.petrak.hexcasting.common.network.MsgOpenSpellGuiAck;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
@ -19,12 +14,7 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraftforge.network.PacketDistributor;
import java.util.ArrayList;
import java.util.List;
public class ItemWand extends Item {
public static final String TAG_HARNESS = "hexcasting:spell_harness";
public static final String TAG_PATTERNS = "hexcasting:spell_patterns";
public ItemWand(Properties pProperties) {
super(pProperties);
@ -35,24 +25,14 @@ public class ItemWand extends Item {
if (player.isShiftKeyDown()) {
if (world.isClientSide()) {
player.playSound(HexSounds.FAIL_PATTERN.get(), 1f, 1f);
} else {
player.getPersistentData().remove(TAG_HARNESS);
player.getPersistentData().remove(TAG_PATTERNS);
} else if (player instanceof ServerPlayer serverPlayer) {
HexPlayerDataHelper.clearCastingData(serverPlayer);
}
}
if (!world.isClientSide() && player instanceof ServerPlayer serverPlayer) {
CompoundTag harnessTag = player.getPersistentData().getCompound(TAG_HARNESS);
ListTag patternsTag = player.getPersistentData().getList(TAG_PATTERNS, Tag.TAG_COMPOUND);
var ctx = new CastingContext(serverPlayer, hand);
CastingHarness harness = CastingHarness.DeserializeFromNBT(harnessTag, ctx);
List<ResolvedPattern> patterns = new ArrayList<>(patternsTag.size());
for (int i = 0; i < patternsTag.size(); i++) {
patterns.add(ResolvedPattern.DeserializeFromNBT(patternsTag.getCompound(i)));
}
var harness = HexPlayerDataHelper.getHarness(serverPlayer, hand);
var patterns = HexPlayerDataHelper.getPatterns(serverPlayer);
HexMessages.getNetwork().send(PacketDistributor.PLAYER.with(() -> serverPlayer),
new MsgOpenSpellGuiAck(hand, patterns, harness.generateDescs()));

View file

@ -1,12 +1,14 @@
package at.petrak.hexcasting.common.items.magic;
import net.minecraft.world.item.ItemStack;
public class ItemArtifact extends ItemPackagedSpell {
public ItemArtifact(Properties pProperties) {
super(pProperties);
}
@Override
public boolean canDrawManaFromInventory() {
public boolean canDrawManaFromInventory(ItemStack stack) {
return true;
}

View file

@ -1,12 +1,14 @@
package at.petrak.hexcasting.common.items.magic;
import net.minecraft.world.item.ItemStack;
public class ItemCypher extends ItemPackagedSpell {
public ItemCypher(Properties pProperties) {
super(pProperties);
}
@Override
public boolean canDrawManaFromInventory() {
public boolean canDrawManaFromInventory(ItemStack stack) {
return false;
}

View file

@ -2,6 +2,7 @@ package at.petrak.hexcasting.common.items.magic;
import at.petrak.hexcasting.HexMod;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
public class ItemManaBattery extends ItemManaHolder {
public static final ResourceLocation MANA_PREDICATE = new ResourceLocation(HexMod.MOD_ID, "mana");
@ -10,4 +11,9 @@ public class ItemManaBattery extends ItemManaHolder {
public ItemManaBattery(Properties pProperties) {
super(pProperties);
}
@Override
public boolean manaProvider(ItemStack stack) {
return true;
}
}

View file

@ -1,10 +1,12 @@
package at.petrak.hexcasting.common.items.magic;
import at.petrak.hexcasting.api.item.ManaHolder;
import at.petrak.hexcasting.common.casting.ManaHelper;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.util.Mth;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
@ -13,56 +15,61 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
public abstract class ItemManaHolder extends Item {
public static final String TAG_MANA = "hexcasting:mana";
public static final String TAG_MAX_MANA = "hexcasting:start_mana";
public abstract class ItemManaHolder extends Item implements ManaHolder {
private static final String TAG_MANA = "hexcasting:mana";
private static final String TAG_MAX_MANA = "hexcasting:start_mana";
public ItemManaHolder(Properties pProperties) {
super(pProperties);
}
public int getManaAmt(CompoundTag tag) {
return tag.getInt(TAG_MANA);
public static ItemStack withMana(ItemStack stack, int mana, int maxMana) {
Item item = stack.getItem();
if (item instanceof ItemManaHolder) {
CompoundTag tag = stack.getOrCreateTag();
tag.putInt(TAG_MANA, mana);
tag.putInt(TAG_MAX_MANA, maxMana);
}
return stack;
}
public int getMaxManaAmt(CompoundTag tag) {
return tag.getInt(TAG_MAX_MANA);
@Override
public int getMana(ItemStack stack) {
if (!stack.hasTag())
return 0;
return stack.getTag().getInt(TAG_MANA);
}
public float getManaFullness(CompoundTag tag) {
return (float) getManaAmt(tag) / (float) getMaxManaAmt(tag);
@Override
public int getMaxMana(ItemStack stack) {
if (!stack.hasTag())
return 0;
return stack.getTag().getInt(TAG_MAX_MANA);
}
/**
* Return the actual amount of mana extracted.
*/
public int withdrawMana(CompoundTag tag, int cost) {
var manaHere = getManaAmt(tag);
var manaLeft = manaHere - cost;
tag.putInt(TAG_MANA, Math.max(0, manaLeft));
return Math.min(cost, manaHere);
@Override
public void setMana(ItemStack stack, int mana) {
stack.getOrCreateTag().putInt(TAG_MANA, Mth.clamp(mana, 0, getMaxMana(stack)));
}
@Override
public boolean isBarVisible(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
return tag.contains(TAG_MANA);
return getMaxMana(pStack) > 0;
}
@Override
public int getBarColor(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = getManaAmt(tag);
var maxMana = getMaxManaAmt(tag);
return ManaHelper.INSTANCE.barColor(mana, maxMana);
var mana = getMana(pStack);
var maxMana = getMaxMana(pStack);
return ManaHelper.barColor(mana, maxMana);
}
@Override
public int getBarWidth(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = tag.getInt(TAG_MANA);
var maxMana = tag.getInt(TAG_MAX_MANA);
return ManaHelper.INSTANCE.barWidth(mana, maxMana);
var mana = getMana(pStack);
var maxMana = getMaxMana(pStack);
return ManaHelper.barWidth(mana, maxMana);
}
@Override
@ -78,16 +85,12 @@ public abstract class ItemManaHolder extends Item {
@Override
public void appendHoverText(ItemStack pStack, @Nullable Level pLevel, List<Component> pTooltipComponents,
TooltipFlag pIsAdvanced) {
if (pIsAdvanced.isAdvanced()) {
var imh = (ItemManaHolder) pStack.getItem();
var tag = pStack.getOrCreateTag();
if (tag.contains(TAG_MANA) && tag.contains(TAG_MAX_MANA)) {
pTooltipComponents.add(
new TranslatableComponent("item.hexcasting.manaholder.amount",
String.format("%,d", imh.getManaAmt(tag)),
String.format("%,d", imh.getMaxManaAmt(tag)),
100f * imh.getManaFullness(tag)).withStyle(ChatFormatting.GRAY));
}
if (pIsAdvanced.isAdvanced() && getMaxMana(pStack) > 0) {
pTooltipComponents.add(
new TranslatableComponent("item.hexcasting.manaholder.amount",
String.format("%,d", getMana(pStack)),
String.format("%,d", getMaxMana(pStack)),
100f * getManaFullness(pStack)).withStyle(ChatFormatting.GRAY));
}
super.appendHoverText(pStack, pLevel, pTooltipComponents, pIsAdvanced);

View file

@ -1,11 +1,13 @@
package at.petrak.hexcasting.common.items.magic;
import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.api.item.SpellHolder;
import at.petrak.hexcasting.common.casting.CastingContext;
import at.petrak.hexcasting.common.casting.CastingHarness;
import at.petrak.hexcasting.common.lib.HexSounds;
import at.petrak.hexcasting.hexmath.HexPattern;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
@ -18,6 +20,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -25,7 +28,7 @@ import java.util.List;
/**
* Item that holds a list of patterns in it ready to be cast
*/
public abstract class ItemPackagedSpell extends ItemManaHolder {
public abstract class ItemPackagedSpell extends ItemManaHolder implements SpellHolder {
public static final String TAG_PATTERNS = "patterns";
public static final ResourceLocation HAS_PATTERNS_PRED = new ResourceLocation(HexMod.MOD_ID, "has_patterns");
@ -35,13 +38,50 @@ public abstract class ItemPackagedSpell extends ItemManaHolder {
public abstract boolean singleUse();
public abstract boolean canDrawManaFromInventory();
@Override
public boolean manaProvider(ItemStack stack) {
return false;
}
@Override
public @Nullable List<HexPattern> getPatterns(ItemStack stack) {
if (!stack.hasTag())
return null;
CompoundTag tag = stack.getTag();
if (!tag.contains(TAG_PATTERNS, Tag.TAG_LIST))
return null;
var out = new ArrayList<HexPattern>();
var patsTag = tag.getList(TAG_PATTERNS, Tag.TAG_COMPOUND);
for (var patTag : patsTag) {
out.add(HexPattern.DeserializeFromNBT((CompoundTag) patTag));
}
return out;
}
@Override
public void writePattern(ItemStack stack, List<HexPattern> patterns, int mana) {
ListTag patsTag = new ListTag();
for (HexPattern pat : patterns)
patsTag.add(pat.serializeToNBT());
stack.getOrCreateTag().put(ItemPackagedSpell.TAG_PATTERNS, patsTag);
withMana(stack, mana, mana);
}
@Override
public void clearPatterns(ItemStack stack) {
stack.removeTagKey(ItemPackagedSpell.TAG_PATTERNS);
withMana(stack, 0, 0);
}
@Override
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand usedHand) {
var stack = player.getItemInHand(usedHand);
var tag = stack.getOrCreateTag();
if (!tag.contains(TAG_PATTERNS)) {
List<HexPattern> patterns = getPatterns(stack);
if (patterns == null) {
return InteractionResultHolder.fail(stack);
}
@ -51,7 +91,6 @@ public abstract class ItemPackagedSpell extends ItemManaHolder {
var sPlayer = (ServerPlayer) player;
var ctx = new CastingContext(sPlayer, usedHand);
var harness = new CastingHarness(ctx);
List<HexPattern> patterns = getPatterns(tag);
for (var pattern : patterns) {
var info = harness.executeNewPattern(pattern, sPlayer.getLevel());
if (info.getWasPrevPatternInvalid()) {
@ -89,13 +128,4 @@ public abstract class ItemPackagedSpell extends ItemManaHolder {
public UseAnim getUseAnimation(ItemStack pStack) {
return UseAnim.BLOCK;
}
private static List<HexPattern> getPatterns(CompoundTag tag) {
var out = new ArrayList<HexPattern>();
var patsTag = tag.getList(TAG_PATTERNS, Tag.TAG_COMPOUND);
for (var patTag : patsTag) {
out.add(HexPattern.DeserializeFromNBT((CompoundTag) patTag));
}
return out;
}
}

View file

@ -1,12 +1,14 @@
package at.petrak.hexcasting.common.items.magic;
import net.minecraft.world.item.ItemStack;
public class ItemTrinket extends ItemPackagedSpell {
public ItemTrinket(Properties pProperties) {
super(pProperties);
}
@Override
public boolean canDrawManaFromInventory() {
public boolean canDrawManaFromInventory(ItemStack stack) {
return false;
}

View file

@ -1,133 +1,148 @@
package at.petrak.hexcasting.common.lib;
import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.common.casting.colors.CapPreferredColorizer;
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer;
import at.petrak.hexcasting.common.casting.operators.spells.great.OpFlight;
import at.petrak.hexcasting.common.casting.operators.spells.sentinel.CapSentinel;
import at.petrak.hexcasting.common.items.ItemWand;
import at.petrak.hexcasting.common.misc.Brainsweeping;
import at.petrak.hexcasting.common.network.HexMessages;
import at.petrak.hexcasting.common.network.MsgColorizerUpdateAck;
import at.petrak.hexcasting.common.network.MsgSentinelStatusUpdateAck;
import net.minecraft.nbt.Tag;
import at.petrak.hexcasting.HexConfig;
import at.petrak.hexcasting.api.item.IManaReservoir;
import at.petrak.hexcasting.api.item.ManaHolder;
import at.petrak.hexcasting.common.items.HexItems;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.capabilities.CapabilityToken;
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.common.capabilities.*;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.network.PacketDistributor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class HexCapabilities {
public static final Capability<OpFlight.CapFlight> FLIGHT = CapabilityManager.get(new CapabilityToken<>() {
private static final ResourceLocation MANA_HOLDER_CAPABILITY = new ResourceLocation("hexcasting", "mana_holder");
private static final ResourceLocation MANA_ITEM_CAPABILITY = new ResourceLocation("hexcasting", "mana_item");
public static final Capability<IManaReservoir> MANA = CapabilityManager.get(new CapabilityToken<>() {
});
public static final Capability<CapSentinel> SENTINEL = CapabilityManager.get(new CapabilityToken<>() {
});
public static final Capability<CapPreferredColorizer> PREFERRED_COLORIZER =
CapabilityManager.get(new CapabilityToken<>() {
});
public static final Capability<Brainsweeping.Cap> BRAINSWEPT = CapabilityManager.get(
new CapabilityToken<>() {
});
@SubscribeEvent
public static void registerCaps(RegisterCapabilitiesEvent evt) {
evt.register(OpFlight.CapFlight.class);
evt.register(CapSentinel.class);
evt.register(CapPreferredColorizer.class);
evt.register(Brainsweeping.Cap.class);
evt.register(IManaReservoir.class);
}
@SubscribeEvent
public static void attachCaps(AttachCapabilitiesEvent<Entity> evt) {
if (evt.getObject() instanceof Player) {
evt.addCapability(new ResourceLocation(HexMod.MOD_ID, OpFlight.CAP_NAME),
// generate a new instance of the capability
new OpFlight.CapFlight(false, 0, Vec3.ZERO, 0.0));
evt.addCapability(new ResourceLocation(HexMod.MOD_ID, CapSentinel.CAP_NAME),
new CapSentinel(false, false, Vec3.ZERO, Level.OVERWORLD));
evt.addCapability(new ResourceLocation(HexMod.MOD_ID, CapPreferredColorizer.CAP_NAME),
new CapPreferredColorizer(FrozenColorizer.DEFAULT));
} else if (evt.getObject() instanceof Villager) {
evt.addCapability(new ResourceLocation(HexMod.MOD_ID, Brainsweeping.CAP_NAME),
new Brainsweeping.Cap());
public static void attachCaps(AttachCapabilitiesEvent<ItemStack> evt) {
ItemStack stack = evt.getObject();
if (stack.getItem() instanceof ManaHolder holder)
evt.addCapability(MANA_HOLDER_CAPABILITY, new SimpleProvider<>(MANA, LazyOptional.of(() -> new ManaHolderReservoir(holder, stack))));
else if (stack.is(HexItems.AMETHYST_DUST.get()))
evt.addCapability(MANA_ITEM_CAPABILITY, new SimpleProvider<>(MANA, LazyOptional.of(() -> new ItemReservoir(HexConfig.dustManaAmount, 3, stack))));
else if (stack.is(Items.AMETHYST_SHARD))
evt.addCapability(MANA_ITEM_CAPABILITY, new SimpleProvider<>(MANA, LazyOptional.of(() -> new ItemReservoir(HexConfig.shardManaAmount, 2, stack))));
else if (stack.is(HexItems.CHARGED_AMETHYST.get()))
evt.addCapability(MANA_ITEM_CAPABILITY, new SimpleProvider<>(MANA, LazyOptional.of(() -> new ItemReservoir(HexConfig.chargedCrystalManaAmount, 1, stack))));
}
private record SimpleProvider<CAP>(Capability<CAP> capability, LazyOptional<CAP> instance) implements ICapabilityProvider {
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
return cap == capability ? instance.cast() : LazyOptional.empty();
}
}
// if I were forge i sould simply design an actually useful and useable cap system
@SubscribeEvent
public static void copyCapsOnDeath(PlayerEvent.Clone evt) {
var eitherSidePlayer = evt.getPlayer();
// this apparently defines it in outside scope. the more you know.
if (!(eitherSidePlayer instanceof ServerPlayer player)) {
return;
private record ItemReservoir(ForgeConfigSpec.IntValue baseWorth,
int consumptionPriority,
ItemStack stack) implements IManaReservoir {
@Override
public int getMana() {
return baseWorth.get() * stack.getCount();
}
var proto = evt.getOriginal();
// Copy harness data from this to new player
player.getPersistentData().put(ItemWand.TAG_PATTERNS,
proto.getPersistentData().getList(ItemWand.TAG_PATTERNS, Tag.TAG_COMPOUND));
player.getPersistentData().put(ItemWand.TAG_HARNESS,
proto.getPersistentData().getCompound(ItemWand.TAG_HARNESS));
// Copy caps from this to new player
proto.reviveCaps();
var protoCapSentinel = proto.getCapability(SENTINEL).resolve();
protoCapSentinel.ifPresent(protoSentinel -> {
var capSentinel = player.getCapability(SENTINEL);
capSentinel.ifPresent(sentinel -> {
sentinel.hasSentinel = protoSentinel.hasSentinel;
sentinel.position = protoSentinel.position;
sentinel.extendsRange = protoSentinel.extendsRange;
sentinel.dimension = protoSentinel.dimension;
});
});
var protoCapColor = proto.getCapability(PREFERRED_COLORIZER).resolve();
protoCapColor.ifPresent(protoColorizer -> {
var capColorizer = player.getCapability(PREFERRED_COLORIZER);
capColorizer.ifPresent(colorizer -> {
colorizer.colorizer = protoColorizer.colorizer;
});
});
proto.invalidateCaps();
}
@SubscribeEvent
public static void syncCapsOnLogin(PlayerEvent.PlayerLoggedInEvent evt) {
if (!(evt.getPlayer() instanceof ServerPlayer player)) {
return;
@Override
public int getMaxMana() {
return getMana();
}
syncCaps(player);
}
@SubscribeEvent
public static void syncCapsOnRejoin(PlayerEvent.PlayerRespawnEvent evt) {
if (!(evt.getPlayer() instanceof ServerPlayer player)) {
return;
@Override
public void setMana(int mana) {
// NO-OP
}
syncCaps(player);
@Override
public boolean canRecharge() {
return false;
}
@Override
public boolean canProvide() {
return true;
}
@Override
public int getConsumptionPriority() {
return consumptionPriority;
}
@Override
public boolean canConstructBattery() {
return true;
}
@Override
public int withdrawMana(int cost, boolean simulate) {
int worth = baseWorth.get();
if (cost < 0)
cost = worth * stack.getCount();
double itemsRequired = cost / (double) worth;
int itemsUsed = Math.min((int) Math.ceil(itemsRequired), stack.getCount());
if (!simulate)
stack.shrink(itemsUsed);
return itemsUsed * worth;
}
}
private static void syncCaps(ServerPlayer player) {
var capSentinel = player.getCapability(HexCapabilities.SENTINEL).resolve();
capSentinel.ifPresent(sentinel -> HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> player), new MsgSentinelStatusUpdateAck(sentinel)));
private record ManaHolderReservoir(ManaHolder holder,
ItemStack stack) implements IManaReservoir {
var capColorizer = player.getCapability(HexCapabilities.PREFERRED_COLORIZER).resolve();
capColorizer.ifPresent(colorizer -> HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> player), new MsgColorizerUpdateAck(colorizer)));
@Override
public int getMana() {
return holder.getMana(stack);
}
@Override
public int getMaxMana() {
return holder.getMaxMana(stack);
}
@Override
public void setMana(int mana) {
holder.setMana(stack, mana);
}
@Override
public boolean canRecharge() {
return true;
}
@Override
public boolean canProvide() {
return holder.manaProvider(stack);
}
@Override
public int getConsumptionPriority() {
return 4;
}
@Override
public boolean canConstructBattery() {
return false;
}
@Override
public int withdrawMana(int cost, boolean simulate) {
return holder.withdrawMana(stack, cost, simulate);
}
}
}

View file

@ -0,0 +1,199 @@
package at.petrak.hexcasting.common.lib;
import at.petrak.hexcasting.HexUtils;
import at.petrak.hexcasting.common.casting.CastingContext;
import at.petrak.hexcasting.common.casting.CastingHarness;
import at.petrak.hexcasting.common.casting.ResolvedPattern;
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer;
import at.petrak.hexcasting.common.casting.operators.spells.great.FlightAbility;
import at.petrak.hexcasting.common.casting.operators.spells.sentinel.Sentinel;
import at.petrak.hexcasting.common.network.HexMessages;
import at.petrak.hexcasting.common.network.MsgColorizerUpdateAck;
import at.petrak.hexcasting.common.network.MsgSentinelStatusUpdateAck;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.network.PacketDistributor;
import java.util.ArrayList;
import java.util.List;
public class HexPlayerDataHelper {
public static final String TAG_SENTINEL_EXISTS = "hexcasting:sentinel_exists";
public static final String TAG_SENTINEL_GREATER = "hexcasting:sentinel_extends_range";
public static final String TAG_SENTINEL_POSITION = "hexcasting:sentinel_position";
public static final String TAG_SENTINEL_DIMENSION = "hexcasting:sentinel_dimension";
public static final String TAG_COLOR = "hexcasting:colorizer";
public static final String TAG_FLIGHT_ALLOWED = "hexcasting:flight_allowed";
public static final String TAG_FLIGHT_TIME = "hexcasting:flight_time";
public static final String TAG_FLIGHT_ORIGIN = "hexcasting:flight_origin";
public static final String TAG_FLIGHT_DIMENSION = "hexcasting:flight_origin";
public static final String TAG_FLIGHT_RADIUS = "hexcasting:flight_radius";
public static final String TAG_HARNESS = "hexcasting:spell_harness";
public static final String TAG_PATTERNS = "hexcasting:spell_patterns";
@SubscribeEvent
public static void copyDataOnDeath(PlayerEvent.Clone evt) {
var eitherSidePlayer = evt.getPlayer();
// this apparently defines it in outside scope. the more you know.
if (!(eitherSidePlayer instanceof ServerPlayer player)) {
return;
}
var eitherSideProto = evt.getOriginal();
if (!(eitherSideProto instanceof ServerPlayer proto)) {
return;
}
// Copy data from this to new player
setFlight(player, getFlight(proto));
setSentinel(player, getSentinel(proto));
setColorizer(player, getColorizer(proto));
setHarness(player, getHarness(proto, InteractionHand.MAIN_HAND));
setPatterns(player, getPatterns(proto));
}
@SubscribeEvent
public static void syncDataOnLogin(PlayerEvent.PlayerLoggedInEvent evt) {
if (!(evt.getPlayer() instanceof ServerPlayer player)) {
return;
}
syncSentinel(player);
syncColorizer(player);
}
@SubscribeEvent
public static void syncDataOnRejoin(PlayerEvent.PlayerRespawnEvent evt) {
if (!(evt.getPlayer() instanceof ServerPlayer player)) {
return;
}
syncSentinel(player);
syncColorizer(player);
}
private static void syncSentinel(ServerPlayer player) {
HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> player), new MsgSentinelStatusUpdateAck(getSentinel(player)));
}
private static void syncColorizer(ServerPlayer player) {
HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> player), new MsgColorizerUpdateAck(getColorizer(player)));
}
public static void setFlight(ServerPlayer player, FlightAbility flight) {
CompoundTag tag = player.getPersistentData();
tag.putBoolean(TAG_FLIGHT_ALLOWED, flight.allowed());
if (flight.allowed()) {
tag.putInt(TAG_FLIGHT_TIME, flight.timeLeft());
tag.put(TAG_FLIGHT_ORIGIN, HexUtils.serializeToNBT(flight.origin()));
tag.putString(TAG_FLIGHT_DIMENSION, flight.dimension().location().toString());
tag.putDouble(TAG_FLIGHT_RADIUS, flight.radius());
} else {
tag.remove(TAG_FLIGHT_TIME);
tag.remove(TAG_FLIGHT_ORIGIN);
tag.remove(TAG_FLIGHT_RADIUS);
}
}
public static void setColorizer(Player player, FrozenColorizer colorizer) {
CompoundTag tag = player.getPersistentData();
tag.put(TAG_COLOR, colorizer.serialize());
if (player instanceof ServerPlayer serverPlayer)
syncColorizer(serverPlayer);
}
public static void setSentinel(Player player, Sentinel sentinel) {
CompoundTag tag = player.getPersistentData();
tag.putBoolean(TAG_SENTINEL_EXISTS, sentinel.hasSentinel());
if (sentinel.hasSentinel()) {
tag.putBoolean(TAG_SENTINEL_GREATER, sentinel.extendsRange());
tag.put(TAG_SENTINEL_POSITION, HexUtils.serializeToNBT(sentinel.position()));
tag.putString(TAG_SENTINEL_DIMENSION, sentinel.dimension().location().toString());
} else {
tag.remove(TAG_SENTINEL_GREATER);
tag.remove(TAG_SENTINEL_POSITION);
tag.remove(TAG_SENTINEL_DIMENSION);
}
if (player instanceof ServerPlayer serverPlayer)
syncSentinel(serverPlayer);
}
public static void setHarness(ServerPlayer player, CastingHarness harness) {
player.getPersistentData().put(TAG_HARNESS, harness == null ? new CompoundTag() : harness.serializeToNBT());
}
public static void setPatterns(ServerPlayer player, List<ResolvedPattern> patterns) {
var listTag = new ListTag();
for (ResolvedPattern pattern : patterns) {
listTag.add(pattern.serializeToNBT());
}
player.getPersistentData().put(TAG_PATTERNS, listTag);
}
public static FlightAbility getFlight(ServerPlayer player) {
CompoundTag tag = player.getPersistentData();
boolean allowed = tag.getBoolean(TAG_FLIGHT_ALLOWED);
if (allowed) {
var timeLeft = tag.getInt(TAG_FLIGHT_TIME);
var origin = HexUtils.DeserializeVec3FromNBT(tag.getLongArray(TAG_FLIGHT_ORIGIN));
var radius = tag.getDouble(TAG_FLIGHT_RADIUS);
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(tag.getString(TAG_SENTINEL_DIMENSION)));
return new FlightAbility(true, timeLeft, dimension, origin, radius);
}
return FlightAbility.deny();
}
public static FrozenColorizer getColorizer(Player player) {
return FrozenColorizer.deserialize(player.getPersistentData().getCompound(TAG_COLOR));
}
public static Sentinel getSentinel(Player player) {
CompoundTag tag = player.getPersistentData();
var exists = tag.getBoolean(TAG_SENTINEL_EXISTS);
if (!exists)
return Sentinel.none();
var extendsRange = tag.getBoolean(TAG_SENTINEL_GREATER);
var position = HexUtils.DeserializeVec3FromNBT(tag.getLongArray(TAG_SENTINEL_POSITION));
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(tag.getString(TAG_SENTINEL_DIMENSION)));
return new Sentinel(true, extendsRange, position, dimension);
}
public static CastingHarness getHarness(ServerPlayer player, InteractionHand hand) {
var ctx = new CastingContext(player, hand);
return CastingHarness.DeserializeFromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
}
public static List<ResolvedPattern> getPatterns(ServerPlayer player) {
ListTag patternsTag = player.getPersistentData().getList(TAG_PATTERNS, Tag.TAG_COMPOUND);
List<ResolvedPattern> patterns = new ArrayList<>(patternsTag.size());
for (int i = 0; i < patternsTag.size(); i++) {
patterns.add(ResolvedPattern.DeserializeFromNBT(patternsTag.getCompound(i)));
}
return patterns;
}
public static void clearCastingData(ServerPlayer player) {
player.getPersistentData().remove(TAG_HARNESS);
player.getPersistentData().remove(TAG_PATTERNS);
}
}

View file

@ -1,55 +1,35 @@
package at.petrak.hexcasting.common.misc;
import at.petrak.hexcasting.common.lib.HexCapabilities;
import at.petrak.hexcasting.mixin.AccessorLivingEntity;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.npc.Villager;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraft.world.entity.npc.VillagerDataHolder;
import net.minecraftforge.event.entity.living.LivingConversionEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
public class Brainsweeping {
public static final String CAP_NAME = "brainsweeping";
public static class Cap implements ICapabilitySerializable<CompoundTag> {
public boolean brainswept = false;
public static final String TAG_BRAINSWEPT = "hexcasting:brainswept";
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
return HexCapabilities.BRAINSWEPT.orEmpty(cap, LazyOptional.of(() -> this));
}
public CompoundTag serializeNBT() {
var out = new CompoundTag();
out.putBoolean("brainswept", this.brainswept);
return out;
}
public void deserializeNBT(CompoundTag tag) {
this.brainswept = tag.getBoolean("brainswept");
}
public static boolean isBrainswept(LivingEntity entity) {
return entity instanceof VillagerDataHolder && entity.getPersistentData().getBoolean(TAG_BRAINSWEPT);
}
public static boolean isBrainswept(Villager villager) {
var maybeCap = villager.getCapability(HexCapabilities.BRAINSWEPT).resolve();
return maybeCap.map(cap -> cap.brainswept).orElse(false);
}
public static void brainsweep(LivingEntity entity) {
if (entity instanceof VillagerDataHolder) {
entity.getPersistentData().putBoolean(TAG_BRAINSWEPT, true);
public static void brainsweep(Villager villager) {
var maybeCap = villager.getCapability(HexCapabilities.BRAINSWEPT).resolve();
maybeCap.ifPresent(cap -> {
cap.brainswept = true;
var brain = villager.getBrain();
if (villager.level instanceof ServerLevel slevel) {
brain.stopAll(slevel, villager);
if (entity instanceof Villager villager) {
Brain<Villager> brain = villager.getBrain();
if (entity.level instanceof ServerLevel slevel) {
brain.stopAll(slevel, villager);
}
((AccessorLivingEntity) entity).hex$SetBrain(brain.copyWithoutBehaviors());
}
((AccessorLivingEntity) villager).hex$SetBrain(brain.copyWithoutBehaviors());
});
}
}
@SubscribeEvent
@ -60,7 +40,11 @@ public class Brainsweeping {
}
@SubscribeEvent
public static void copyBrainsweepToZombie(LivingConversionEvent evt) {
public static void copyBrainsweepBetweenZombieAndVillager(LivingConversionEvent.Post evt) {
var outcome = evt.getOutcome();
var original = evt.getEntityLiving();
if (outcome instanceof VillagerDataHolder && original instanceof VillagerDataHolder) {
if (isBrainswept(original)) brainsweep(outcome);
}
}
}

View file

@ -39,5 +39,7 @@ public class HexMessages {
MsgCastParticleAck::deserialize, MsgCastParticleAck::handle);
NETWORK.registerMessage(messageIdx++, MsgOpenSpellGuiAck.class, MsgOpenSpellGuiAck::serialize,
MsgOpenSpellGuiAck::deserialize, MsgOpenSpellGuiAck::handle);
NETWORK.registerMessage(messageIdx++, MsgBeepAck.class, MsgBeepAck::serialize,
MsgBeepAck::deserialize, MsgBeepAck::handle);
}
}

View file

@ -0,0 +1,53 @@
package at.petrak.hexcasting.common.network;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
/**
* Sent server->client to synchronize OpAddMotion when the target is a player.
*/
public record MsgBeepAck(Vec3 target, int note, NoteBlockInstrument instrument) {
public static MsgBeepAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var x = buf.readDouble();
var y = buf.readDouble();
var z = buf.readDouble();
var note = buf.readInt();
var instrument = buf.readEnum(NoteBlockInstrument.class);
return new MsgBeepAck(new Vec3(x, y, z), note, instrument);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeDouble(this.target.x);
buf.writeDouble(this.target.y);
buf.writeDouble(this.target.z);
buf.writeInt(this.note);
buf.writeEnum(instrument);
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
var minecraft = Minecraft.getInstance();
var world = minecraft.level;
if (world != null){
float pitch = (float) Math.pow(2, (note - 12) / 12.0);
world.playLocalSound(target.x, target.y, target.z, instrument.getSoundEvent(), SoundSource.PLAYERS, 3, pitch, false);
world.addParticle(ParticleTypes.NOTE, target.x, target.y + 0.2, target.z, note / 24.0, 0, 0);
}
})
);
ctx.get().setPacketHandled(true);
}
}

View file

@ -1,8 +1,7 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.common.casting.colors.CapPreferredColorizer;
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer;
import at.petrak.hexcasting.common.lib.HexCapabilities;
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
@ -15,32 +14,27 @@ import java.util.function.Supplier;
/**
* Sent server->client to synchronize the status of the sentinel.
*/
public record MsgColorizerUpdateAck(CapPreferredColorizer update) {
public record MsgColorizerUpdateAck(FrozenColorizer update) {
public static MsgColorizerUpdateAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var tag = buf.readAnySizeNbt();
var colorizer = new CapPreferredColorizer(FrozenColorizer.DEFAULT);
colorizer.deserializeNBT(tag);
var colorizer = FrozenColorizer.deserialize(tag);
return new MsgColorizerUpdateAck(colorizer);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeNbt(this.update.serializeNBT());
buf.writeNbt(this.update.serialize());
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
var player = Minecraft.getInstance().player;
var maybeCap = player.getCapability(HexCapabilities.PREFERRED_COLORIZER).resolve();
if (!maybeCap.isPresent()) {
return;
if (player != null) {
HexPlayerDataHelper.setColorizer(player, update);
}
var cap = maybeCap.get();
cap.colorizer = update().colorizer;
})
);
ctx.get().setPacketHandled(true);

View file

@ -23,6 +23,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info) {
var buf = new FriendlyByteBuf(buffer);
var wasSpellCast = buf.readBoolean();
var hasCastingSound = buf.readBoolean();
var isStackEmpty = buf.readBoolean();
var wasPrevPatternInvalid = buf.readBoolean();
var descsLen = buf.readInt();
@ -32,7 +33,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info) {
}
return new MsgNewSpellPatternAck(
new ControllerInfo(wasSpellCast, isStackEmpty, wasPrevPatternInvalid, desc)
new ControllerInfo(wasSpellCast, hasCastingSound, isStackEmpty, wasPrevPatternInvalid, desc)
);
}
@ -40,6 +41,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info) {
var buf = new FriendlyByteBuf(buffer);
buf.writeBoolean(this.info.getWasSpellCast());
buf.writeBoolean(this.info.getHasCastingSound());
buf.writeBoolean(this.info.isStackClear());
buf.writeBoolean(this.info.getWasPrevPatternInvalid());
buf.writeInt(this.info.getStackDesc().size());

View file

@ -1,13 +1,14 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.common.casting.*;
import at.petrak.hexcasting.common.casting.ControllerInfo;
import at.petrak.hexcasting.common.casting.ResolvedPattern;
import at.petrak.hexcasting.common.casting.ResolvedPatternValidity;
import at.petrak.hexcasting.common.items.ItemWand;
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper;
import at.petrak.hexcasting.common.lib.HexSounds;
import at.petrak.hexcasting.hexmath.HexCoord;
import at.petrak.hexcasting.hexmath.HexPattern;
import io.netty.buffer.ByteBuf;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
@ -26,7 +27,7 @@ import java.util.function.Supplier;
public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern, List<ResolvedPattern> resolvedPatterns) {
public static MsgNewSpellPatternSyn deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var hand = InteractionHand.values()[buf.readInt()];
var hand = buf.readEnum(InteractionHand.class);
var pattern = HexPattern.DeserializeFromNBT(buf.readAnySizeNbt());
var resolvedPatternsLen = buf.readInt();
@ -39,7 +40,7 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeInt(this.handUsed.ordinal());
buf.writeEnum(handUsed);
buf.writeNbt(this.pattern.serializeToNBT());
buf.writeInt(this.resolvedPatterns.size());
for (var pat : this.resolvedPatterns) {
@ -67,45 +68,32 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
autoFail = true;
}
var ctx = new CastingContext(sender, this.handUsed);
var tag = sender.getPersistentData();
var harness = CastingHarness.DeserializeFromNBT(tag.getCompound(ItemWand.TAG_HARNESS), ctx);
var harness = HexPlayerDataHelper.getHarness(sender, this.handUsed);
ControllerInfo clientInfo;
if (autoFail) {
clientInfo = new ControllerInfo(false, harness.getStack().isEmpty(), true, harness.generateDescs());
clientInfo = new ControllerInfo(false, false, harness.getStack().isEmpty(), true, harness.generateDescs());
} else {
clientInfo = harness.executeNewPattern(this.pattern, sender.getLevel());
if (clientInfo.getWasSpellCast()) {
if (clientInfo.getWasSpellCast() && clientInfo.getHasCastingSound()) {
sender.level.playSound(null, sender.getX(), sender.getY(), sender.getZ(),
HexSounds.ACTUALLY_CAST.get(), SoundSource.PLAYERS, 1f,
1f + ((float) Math.random() - 0.5f) * 0.2f);
}
}
ListTag patterns = new ListTag();
CompoundTag nextHarnessTag;
if (clientInfo.isStackClear()) {
// discard the changes
nextHarnessTag = new CompoundTag();
HexPlayerDataHelper.setHarness(sender, null);
HexPlayerDataHelper.setPatterns(sender, List.of());
} else {
// save the changes
nextHarnessTag = harness.serializeToNBT();
if (!resolvedPatterns.isEmpty()) {
resolvedPatterns.get(resolvedPatterns.size() - 1)
.setValid(clientInfo.getWasPrevPatternInvalid() ?
ResolvedPatternValidity.ERROR : ResolvedPatternValidity.OK);
}
for (var pat : resolvedPatterns) {
patterns.add(pat.serializeToNBT());
}
HexPlayerDataHelper.setHarness(sender, harness);
if (!resolvedPatterns.isEmpty())
resolvedPatterns.get(resolvedPatterns.size() - 1).setValid(clientInfo.getWasPrevPatternInvalid() ?
ResolvedPatternValidity.ERROR : ResolvedPatternValidity.OK);
HexPlayerDataHelper.setPatterns(sender, resolvedPatterns);
}
tag.put(ItemWand.TAG_HARNESS, nextHarnessTag);
tag.put(ItemWand.TAG_PATTERNS, patterns);
HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> sender), new MsgNewSpellPatternAck(clientInfo));
}

View file

@ -21,7 +21,7 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
public static MsgOpenSpellGuiAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var hand = InteractionHand.values()[buf.readInt()];
var hand = buf.readEnum(InteractionHand.class);
var patternsLen = buf.readInt();
var patterns = new ArrayList<ResolvedPattern>(patternsLen);
@ -41,7 +41,7 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeInt(this.hand.ordinal());
buf.writeEnum(this.hand);
buf.writeInt(this.patterns.size());
for (var pattern : this.patterns) {

View file

@ -1,11 +1,12 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.common.casting.operators.spells.sentinel.CapSentinel;
import at.petrak.hexcasting.common.lib.HexCapabilities;
import at.petrak.hexcasting.common.casting.operators.spells.sentinel.Sentinel;
import at.petrak.hexcasting.common.lib.HexPlayerDataHelper;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.Level;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
@ -16,35 +17,36 @@ import java.util.function.Supplier;
/**
* Sent server->client to synchronize the status of the sentinel.
*/
public record MsgSentinelStatusUpdateAck(CapSentinel update) {
public record MsgSentinelStatusUpdateAck(Sentinel update) {
public static MsgSentinelStatusUpdateAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var tag = buf.readAnySizeNbt();
var sentinel = new CapSentinel(false, false, Vec3.ZERO, Level.OVERWORLD);
sentinel.deserializeNBT(tag);
var exists = buf.readBoolean();
var greater = buf.readBoolean();
var origin = new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble());
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY, buf.readResourceLocation());
var sentinel = new Sentinel(exists, greater, origin, dimension);
return new MsgSentinelStatusUpdateAck(sentinel);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeNbt(this.update.serializeNBT());
buf.writeBoolean(update.hasSentinel());
buf.writeBoolean(update.extendsRange());
buf.writeDouble(update.position().x);
buf.writeDouble(update.position().y);
buf.writeDouble(update.position().z);
buf.writeResourceLocation(update.dimension().location());
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
var player = Minecraft.getInstance().player;
var maybeCap = player.getCapability(HexCapabilities.SENTINEL).resolve();
if (!maybeCap.isPresent()) {
return;
if (player != null) {
HexPlayerDataHelper.setSentinel(player, update);
}
var cap = maybeCap.get();
cap.hasSentinel = update().hasSentinel;
cap.extendsRange = update().extendsRange;
cap.position = update().position;
cap.dimension = update().dimension;
})
);
ctx.get().setPacketHandled(true);

View file

@ -26,7 +26,7 @@ import java.util.function.Supplier;
public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolean isCtrl) {
public static MsgShiftScrollSyn deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var hand = InteractionHand.values()[buf.readInt()];
var hand = buf.readEnum(InteractionHand.class);
var scrollDelta = buf.readDouble();
var isCtrl = buf.readBoolean();
return new MsgShiftScrollSyn(hand, scrollDelta, isCtrl);
@ -34,7 +34,7 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeInt(this.hand.ordinal());
buf.writeEnum(this.hand);
buf.writeDouble(this.scrollDelta);
buf.writeBoolean(this.isCtrl);
}
@ -57,10 +57,10 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
private void spellbook(ServerPlayer sender, ItemStack stack) {
var tag = stack.getOrCreateTag();
ItemSpellbook.RotatePageIdx(stack, tag, this.scrollDelta < 0.0);
ItemSpellbook.RotatePageIdx(stack, this.scrollDelta < 0.0);
var newIdx = tag.getInt(ItemSpellbook.TAG_SELECTED_PAGE);
var len = ItemSpellbook.HighestPage(tag.getCompound(ItemSpellbook.TAG_PAGES));
var len = ItemSpellbook.HighestPage(stack);
MutableComponent component;
if (hand == InteractionHand.OFF_HAND && stack.hasCustomHoverName()) {
@ -106,7 +106,7 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
var datumTag = HexItems.ABACUS.get().readDatumTag(stack);
if (datumTag != null) {
var popup = SpellDatum.DisplayFromTag(datumTag);
sender.displayClientMessage(new TranslatableComponent("hexcasting.tooltip.abacus", popup).withStyle(ChatFormatting.GREEN, ChatFormatting.BOLD), true);
sender.displayClientMessage(new TranslatableComponent("hexcasting.tooltip.abacus", popup).withStyle(ChatFormatting.GREEN), true);
}
}
}

View file

@ -132,7 +132,7 @@ data class HexPattern(val startDir: HexDir, val angles: MutableList<HexAngle> =
@JvmStatic
fun IsHexPattern(tag: CompoundTag): Boolean {
return tag.contains(TAG_START_DIR, Tag.TAG_BYTE.toInt()) && tag.contains(TAG_ANGLES, Tag.TAG_BYTE_ARRAY.toInt())
return tag.contains(TAG_START_DIR, Tag.TAG_ANY_NUMERIC.toInt()) && tag.contains(TAG_ANGLES, Tag.TAG_BYTE_ARRAY.toInt())
}
@JvmStatic

View file

@ -0,0 +1,30 @@
package at.petrak.hexcasting.mixin;
import at.petrak.hexcasting.common.misc.Brainsweeping;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.Mob;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
// Prevents the villager from any of its brain goals or making ambient noise
@Mixin(Mob.class)
public class MixinMob {
@Inject(method = "serverAiStep", at = @At("HEAD"), cancellable = true)
private void onRegisterBrainGoals(CallbackInfo ci) {
var self = (Mob) (Object) this;
if (Brainsweeping.isBrainswept(self)) {
ci.cancel();
}
}
@Inject(method = "getAmbientSound", at = @At("HEAD"), cancellable = true)
protected void onGetAmbientSound(CallbackInfoReturnable<SoundEvent> ci) {
var self = (Mob) (Object) this;
if (Brainsweeping.isBrainswept(self)) {
ci.setReturnValue(null);
}
}
}

View file

@ -1,13 +1,11 @@
package at.petrak.hexcasting.mixin;
import at.petrak.hexcasting.common.misc.Brainsweeping;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.npc.Villager;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
// Prevents the villager from any of its brain goals or making ambient noise
@Mixin(Villager.class)
@ -19,12 +17,4 @@ public class MixinVillager {
ci.cancel();
}
}
@Inject(method = "getAmbientSound", at = @At("HEAD"), cancellable = true)
protected void onGetAmbientSound(CallbackInfoReturnable<SoundEvent> ci) {
var self = (Villager) (Object) this;
if (Brainsweeping.isBrainswept(self)) {
ci.setReturnValue(null);
}
}
}

View file

@ -94,7 +94,7 @@
"hexcasting.tooltip.abacus": "%d",
"hexcasting.tooltip.abacus.reset": "Reset to 0",
"hexcasting.tooltip.abacus.reset.nice": "nice",
"hexcasting.tooltip.lens.impetus.mana": "%s Dusts",
"hexcasting.tooltip.lens.impetus.mana": "%s dust",
"hexcasting.tooltip.lens.impetus.storedplayer": "Bound to %s",
"hexcasting.tooltip.lens.impetus.storedplayer.none": "Unbound",
"hexcasting.tooltip.lens.pattern.invalid": "Invalid Pattern",
@ -130,6 +130,7 @@
"death.attack.hexcasting.overcast": "%s's mind was consumed into energy",
"command.hexcasting.pats.listing": "Patterns in this world:",
"command.hexcasting.pats.all": "Gave you all %d scrolls",
"command.hexcasting.pats.specific.success": "Gave you %s with id %s",
"command.hexcasting.recalc": "Recalculated patterns",
@ -187,17 +188,17 @@
"hexcasting.spell.hexcasting:get_entity/item": "Entity Prfn.: Item",
"hexcasting.spell.hexcasting:get_entity/player": "Entity Prfn.: Player",
"hexcasting.spell.hexcasting:get_entity/living": "Entity Prfn.: Living",
"hexcasting.spell.hexcasting:zone_entity": "Zone Dstln.: Any",
"hexcasting.spell.hexcasting:zone_entity/animal": "Zone Dstln.: Animal",
"hexcasting.spell.hexcasting:zone_entity/monster": "Zone Dstln.: Monster",
"hexcasting.spell.hexcasting:zone_entity/item": "Zone Dstln.: Item",
"hexcasting.spell.hexcasting:zone_entity/player": "Zone Dstln.: Player",
"hexcasting.spell.hexcasting:zone_entity/living": "Zone Dstln.: Living",
"hexcasting.spell.hexcasting:zone_entity/not_animal": "Zone Dstln.: Non-Animal",
"hexcasting.spell.hexcasting:zone_entity/not_monster": "Zone Dstln.: Non-Monster",
"hexcasting.spell.hexcasting:zone_entity/not_item": "Zone Dstln.: Non-Item",
"hexcasting.spell.hexcasting:zone_entity/not_player": "Zone Dstln.: Non-Player",
"hexcasting.spell.hexcasting:zone_entity/not_living": "Zone Dstln.: Non-Living",
"hexcasting.spell.hexcasting:zone_entity": "Zone Dstl.: Any",
"hexcasting.spell.hexcasting:zone_entity/animal": "Zone Dstl.: Animal",
"hexcasting.spell.hexcasting:zone_entity/monster": "Zone Dstl.: Monster",
"hexcasting.spell.hexcasting:zone_entity/item": "Zone Dstl.: Item",
"hexcasting.spell.hexcasting:zone_entity/player": "Zone Dstl.: Player",
"hexcasting.spell.hexcasting:zone_entity/living": "Zone Dstl.: Living",
"hexcasting.spell.hexcasting:zone_entity/not_animal": "Zone Dstl.: Non-Animal",
"hexcasting.spell.hexcasting:zone_entity/not_monster": "Zone Dstl.: Non-Monster",
"hexcasting.spell.hexcasting:zone_entity/not_item": "Zone Dstl.: Non-Item",
"hexcasting.spell.hexcasting:zone_entity/not_player": "Zone Dstl.: Non-Player",
"hexcasting.spell.hexcasting:zone_entity/not_living": "Zone Dstl.: Non-Living",
"hexcasting.spell.hexcasting:undo": "Novice's Gambit",
"hexcasting.spell.hexcasting:const/null": "Nullary Reflection",
"hexcasting.spell.hexcasting:duplicate": "Gemini Decomposition",
@ -207,8 +208,8 @@
"hexcasting.spell.hexcasting:swizzle": "Swindler's Gambit",
"hexcasting.spell.hexcasting:add": "Additive Distillation",
"hexcasting.spell.hexcasting:sub": "Subtractive Distillation",
"hexcasting.spell.hexcasting:mul_dot": "Multiplicative Dstln.",
"hexcasting.spell.hexcasting:div_cross": "Division Dstln.",
"hexcasting.spell.hexcasting:mul_dot": "Multiplicative Dstl.",
"hexcasting.spell.hexcasting:div_cross": "Division Dstl.",
"hexcasting.spell.hexcasting:abs_len": "Length Purification",
"hexcasting.spell.hexcasting:pow_proj": "Power Distillation",
"hexcasting.spell.hexcasting:construct_vec": "Vector Exaltation",
@ -233,8 +234,10 @@
"hexcasting.spell.hexcasting:arccos": "Inverse Cosine Prfn.",
"hexcasting.spell.hexcasting:arctan": "Inverse Tangent Prfn.",
"hexcasting.spell.hexcasting:random": "Entropy Reflection",
"hexcasting.spell.hexcasting:logarithm": "Logarithmic Distillation",
"hexcasting.spell.hexcasting:coerce_axial": "Axial Purification",
"hexcasting.spell.hexcasting:print": "Reveal",
"hexcasting.spell.hexcasting:beep": "Make Note",
"hexcasting.spell.hexcasting:explode": "Explosion",
"hexcasting.spell.hexcasting:explode/fire": "Fireball",
"hexcasting.spell.hexcasting:add_motion": "Impulse",
@ -352,6 +355,7 @@
"hexcasting.mishap.divide_by_zero.divide": "Attempted to divide %s by %s",
"hexcasting.mishap.divide_by_zero.project": "Attempted to project %s onto %s",
"hexcasting.mishap.divide_by_zero.exponent": "Attempted to raise %s to the %s",
"hexcasting.mishap.divide_by_zero.logarithm": "Attempted to get the logarithm of %s in base %s",
"hexcasting.mishap.divide_by_zero.zero": "zero",
"hexcasting.mishap.divide_by_zero.zero.power": "zeroth power",
"hexcasting.mishap.divide_by_zero.zero.vec": "the zero vector",
@ -399,7 +403,7 @@
"hexcasting.entry.couldnt_cast": "A Frustration",
"hexcasting.page.couldnt_cast.1": "Argh! Why won't it let me cast the spell?!$(br2)The scroll I found rings with authenticity. I can $(italic)feel/$ it humming in the scroll-- the pattern is true, or as true as it can be. The spell is $(italic)right there/$.$(p)But it feels if it's on the other side of some thin membrane. I called it-- it tried to manifest-- yet it $(italic)COULD NOT/$.",
"hexcasting.page.couldnt_cast.2": "It felt like the barrier may have weakened ever so slightly from the force that I exerted on the spell; yet despite my greatest efforts-- my deepest focus, my finest amethyst, my precisest drawings-- it $(italic)refuses/$ to cross the barrier. It's maddening.$(p)$(italic)This/$ is where my arcane studies? Cursed by impotence, cursed to lose my rightful powers?$(br2)I should take a deep breath. I should meditate on what I have learned, even if it wasn't very much...",
"hexcasting.page.couldnt_cast.2": "It felt like the barrier may have weakened ever so slightly from the force that I exerted on the spell; yet despite my greatest efforts-- my deepest focus, my finest amethyst, my precisest drawings-- it $(italic)refuses/$ to cross the barrier. It's maddening.$(p)$(italic)This/$ is where my arcane studies end? Cursed by impotence, cursed to lose my rightful powers?$(br2)I should take a deep breath. I should meditate on what I have learned, even if it wasn't very much...",
"hexcasting.page.couldnt_cast.3": "...After careful reflection... I have discovered a change in myself.$(p)It seems... in lieu of $(item)amethyst/$, I've unlocked the ability to cast spells using my own mind and life energy-- just as I read of in the legends of old.$(p)I'm not sure why I can now. It's just... the truth-knowledge-burden was always there, and I see it now. I know it. I bear it.$(br2)Fortunately, I feel my limits as well-- I would get approximately two $(item)Charged Amethyst/$'s worth of _media out of my health at its prime.",
"hexcasting.page.couldnt_cast.4": "I shudder to even consider it-- I've kept my mind mostly intact so far, in my studies. But the fact is-- I form one side of a tenuous link.$(p)I'm connected to some other side-- a side whose boundary has thinned from that trauma. A place where simple actions spell out eternal glory.$(p)Is it so wrong, to want it for myself?",
@ -520,7 +524,7 @@
"hexcasting.page.pigments.1": "Although their names were lost to time, the old practitioners of my art seem to have identified themselves by a color, emblematic of them and their spells. It seems a special kind of pigment, offered to Nature in the right way, would \"[...] paint one's thoughts in a manner pleasing to Nature, inducing a miraculous change in personal colour.\"",
"hexcasting.page.pigments.2": "I'm not certain on the specifics of how it works, but I believe I have isolated the formulae for many different colors of pigments. To use a pigment, I hold it in one hand while casting $(l:patterns/spells/colorize)$(action)Internalize Pigment/$ with the other, consuming the pigment and marking my mind with its color.$(br2)The pigments seem to affect the color of the sparks of media emitted out of a staff when I cast a _Hex, as well as my $(l:patterns/spells/sentinels)$(action)Sentinel/$.",
"hexcasting.page.pigments.3.header": "Chromatic Pigments",
"hexcasting.page.pigments.3": "Pigments all the colors of the rainbow.",
"hexcasting.page.pigments.3": "Pigments in all the colors of the rainbow.",
"hexcasting.page.pigments.4": "And finally, a pigment with a color wholly unique to me.$(br2)$(italic)And all the colors I am inside have not been invented yet./$",
"hexcasting.entry.edified": "Edified Trees",
@ -629,6 +633,7 @@
"hexcasting.page.math.abs_len.2": "Replaces a number with its absolute value, or a vector with its length.",
"hexcasting.page.math.pow_proj.1": "Perform exponentiation or vector projection.",
"hexcasting.page.math.pow_proj.2": "With two numbers, combines them by raising the first to the power of the second.$(li)With a number and a vector, removes the number and raises each component of the vector to the number's power.$(li)With two vectors, combines them into the $(l:https://en.wikipedia.org/wiki/Vector_projection)vector projection/$ of the top of the stack onto the second-from-the-top.$(br2)In the first and second cases, the first argument or its components are the base, and the second argument or its components are the exponent.",
"hexcasting.page.math.logarithm": "Removes the number at the top of the stack, then takes the logarithm of the number at the top using the other number as its base.",
"hexcasting.page.math.floor": "\"Floors\" a number, cutting off the fractional component and leaving an integer value.",
"hexcasting.page.math.ceil": "\"Ceilings\" a number, raising it to the next integer value if it has a fractional component.",
"hexcasting.page.math.construct_vec": "Combine three numbers at the top of the stack into a vector's X, Y, and Z components (top to bottom).",
@ -755,6 +760,8 @@
"hexcasting.page.basic_spell.explode.fire.2": "Costs three $(item)Amethyst Shards/$, plus about one extra $(item)Amethyst Shard/$ per point of explosion power. Otherwise, the same as $(l:patterns/spells/basic_spell#hexcasting:explosion)$(action)Explosion/$, except with fire.",
"hexcasting.page.basic_spell.add_motion": "Remove an entity and direction from the stack, then give a shove to the given entity in the given direction. The strength of the impulse is determined by the length of the vector.$(br)Costs units of $(item)Amethyst Dust/$ equal to the square of the length of the vector.",
"hexcasting.page.basic_spell.blink": "Remove an entity and length from the stack, then teleport the given entity along its look vector by the given length.$(br)Costs about 1 $(item)Amethyst Shard/$ per block travelled.",
"hexcasting.page.basic_spell.beep.1": "Remove a vector and two numbers from the stack. Plays a instrument defined by the first number at the given location, with a note defined by the second number. Costs a negligible amount of _media.",
"hexcasting.page.basic_spell.beep.2": "There appear to be 16 different instruments and 25 different notes. Both are indexed by zero.",
"hexcasting.entry.blockworks": "Blockworks",
"hexcasting.page.blockworks.place_block": "Remove a location from the stack, then pick a block item and place it at the given location.$(br)Costs about 1 $(item)Amethyst Dust/$.",

View file

@ -85,6 +85,22 @@
"type": "patchouli:text",
"text": "hexcasting.page.math.pow_proj.2"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:logarithm",
"anchor": "hexcasting:logarithm",
"input": "num, num",
"output": "num",
"text": "hexcasting.page.math.logarithm"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:random",
"anchor": "hexcasting:random",
"input": "",
"output": "num",
"text": "hexcasting.page.math.random"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:floor",
@ -117,22 +133,6 @@
"output": "num, num, num",
"text": "hexcasting.page.math.deconstruct_vec"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:coerce_axial",
"anchor": "hexcasting:coerce_axial",
"input": "vec",
"output": "vec",
"text": "hexcasting.page.math.coerce_axial"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:random",
"anchor": "hexcasting:random",
"input": "vec",
"output": "vec",
"text": "hexcasting.page.math.random"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:sin",
@ -180,6 +180,14 @@
"input": "num",
"output": "num",
"text": "hexcasting.page.math.arctan"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:coerce_axial",
"anchor": "hexcasting:coerce_axial",
"input": "vec",
"output": "vec",
"text": "hexcasting.page.math.coerce_axial"
}
]
}

View file

@ -45,6 +45,18 @@
"input": "entity, number",
"output": "",
"text": "hexcasting.page.basic_spell.blink"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:beep",
"anchor": "hexcasting:beep",
"input": "vector, number, number",
"output": "",
"text": "hexcasting.page.basic_spell.beep.1"
},
{
"type": "patchouli:text",
"text": "hexcasting.page.basic_spell.beep.2"
}
]
}

View file

@ -5,6 +5,8 @@
"refmap": "hexcasting.mixins.refmap.json",
"package": "at.petrak.hexcasting.mixin",
"mixins": [
"AccessorLivingEntity", "MixinVillager"
"AccessorLivingEntity",
"MixinMob",
"MixinVillager"
]
}
}