diff --git a/CHANGELOG.md b/CHANGELOG.md index c3379b6631..57563dd97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added - Updated to Minecraft 1.21.1 ([#985](https://github.com/FallingColors/HexMod/pull/985)) @SuperKnux @slava110 +- Added Neural Fiber, a new endgame material harvested from villagers using a modified mindflaying process ([#1296](https://github.com/FallingColors/HexMod/pull/1296)) @Robotgiggle @Falkory220 +- Added Grand Caster Robes, a hex-based armor set with a variety of abilities ([#1296](https://github.com/FallingColors/HexMod/pull/1296)) @Robotgiggle @Falkory220 + - Provides defense values midway between iron and diamond + - Provides the scrying lens effect, increased ambit, and a casting cost discount + - Allows you to pan the hex grid to fit in significantly more patterns - Added Simulate, which causes the next pattern drawn to be simulated (to check for mishaps) rather than executed ([#1194](https://github.com/FallingColors/HexMod/pull/1194)) @Robotgiggle - Added the `hex_unbreakable` tag for blocks that should be immune to Break Block regardless of the configured mining tier ([#1186](https://github.com/FallingColors/HexMod/pull/1186)) @Robotgiggle @slava110 - Added a new Ancient Cypher hex that impulses nearby items towards the caster ([#1106](https://github.com/FallingColors/HexMod/pull/1106)) @IridescentVoid diff --git a/Common/src/generated/resources/data/hexcasting/damage_type/forgot_to_breathe.json b/Common/src/generated/resources/data/hexcasting/damage_type/forgot_to_breathe.json new file mode 100644 index 0000000000..d3a0ca991f --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/damage_type/forgot_to_breathe.json @@ -0,0 +1,5 @@ +{ + "exhaustion": 0.0, + "message_id": "hexcasting.forgot_to_breathe", + "scaling": "when_caused_by_living_non_player" +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/minecraft/tags/damage_type/bypasses_armor.json b/Common/src/generated/resources/data/minecraft/tags/damage_type/bypasses_armor.json index 4209c89aa3..319bb24df4 100644 --- a/Common/src/generated/resources/data/minecraft/tags/damage_type/bypasses_armor.json +++ b/Common/src/generated/resources/data/minecraft/tags/damage_type/bypasses_armor.json @@ -1,5 +1,6 @@ { "values": [ - "hexcasting:overcast" + "hexcasting:overcast", + "hexcasting:forgot_to_breathe" ] } \ No newline at end of file diff --git a/Common/src/generated/resources/data/minecraft/tags/damage_type/bypasses_effects.json b/Common/src/generated/resources/data/minecraft/tags/damage_type/bypasses_effects.json index 4209c89aa3..319bb24df4 100644 --- a/Common/src/generated/resources/data/minecraft/tags/damage_type/bypasses_effects.json +++ b/Common/src/generated/resources/data/minecraft/tags/damage_type/bypasses_effects.json @@ -1,5 +1,6 @@ { "values": [ - "hexcasting:overcast" + "hexcasting:overcast", + "hexcasting:forgot_to_breathe" ] } \ No newline at end of file diff --git a/Common/src/generated/resources/data/minecraft/tags/damage_type/no_knockback.json b/Common/src/generated/resources/data/minecraft/tags/damage_type/no_knockback.json index 4209c89aa3..319bb24df4 100644 --- a/Common/src/generated/resources/data/minecraft/tags/damage_type/no_knockback.json +++ b/Common/src/generated/resources/data/minecraft/tags/damage_type/no_knockback.json @@ -1,5 +1,6 @@ { "values": [ - "hexcasting:overcast" + "hexcasting:overcast", + "hexcasting:forgot_to_breathe" ] } \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/api/mod/HexConfig.java b/Common/src/main/java/at/petrak/hexcasting/api/mod/HexConfig.java index 5a63fdef8a..060dbe5572 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/mod/HexConfig.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/mod/HexConfig.java @@ -51,6 +51,8 @@ public interface ClientConfigAccess { boolean clickingTogglesDrawing(); + int gridPanMouseButton(); + boolean advancedTooltipsShowsIotaNBT(); boolean staticActiveSlates(); @@ -61,6 +63,7 @@ public interface ClientConfigAccess { boolean DEFAULT_INVERT_ABACUS_SCROLL = false; double DEFAULT_GRID_SNAP_THRESHOLD = 0.5; boolean DEFAULT_CLICKING_TOGGLES_DRAWING = false; + int DEFAULT_GRID_PAN_MOUSE_BUTTON = 1; boolean DEFAULT_ADVANCED_TOOLTIPS_SHOWS_IOTA_NBT = false; boolean DEFAULT_STATIC_ACTIVE_SLATES = false; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt index fdd0e828ba..a8c383416d 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt @@ -10,13 +10,17 @@ import at.petrak.hexcasting.api.casting.math.HexCoord import at.petrak.hexcasting.api.casting.validateSubIotas import at.petrak.hexcasting.api.mod.HexTags import com.mojang.datafixers.util.Function6 +import com.mojang.serialization.Codec +import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.ChatFormatting import net.minecraft.core.HolderLookup import net.minecraft.core.Registry import net.minecraft.nbt.* +import net.minecraft.network.RegistryFriendlyByteBuf import net.minecraft.network.chat.Component import net.minecraft.network.chat.MutableComponent import net.minecraft.network.chat.Style +import net.minecraft.network.codec.ByteBufCodecs import net.minecraft.network.codec.StreamCodec import net.minecraft.resources.ResourceKey import net.minecraft.resources.ResourceLocation @@ -61,14 +65,24 @@ fun vecFromNBT(tag: CompoundTag): Vec3 { Vec3(tag.getDouble("x"), tag.getDouble("y"), tag.getDouble("z")) } -fun Vec2.serializeToNBT(): LongArrayTag = - LongArrayTag(longArrayOf(this.x.toDouble().toRawBits(), this.y.toDouble().toRawBits())) +fun Vec2.serializeToNBT(): CompoundTag { + val tag = CompoundTag() + tag.putFloat("x", this.x) + tag.putFloat("y", this.y) + return tag +} fun vec2FromNBT(tag: LongArray): Vec2 = if (tag.size != 2) Vec2.ZERO else Vec2( Double.fromBits(tag[0]).toFloat(), Double.fromBits(tag[1]).toFloat(), ) +fun vec2FromNBT(tag: CompoundTag): Vec2 { + return if (!tag.contains("x") || !tag.contains("y")) + Vec2.ZERO + else + Vec2(tag.getFloat("x"), tag.getFloat("y")) +} fun otherHand(hand: InteractionHand) = if (hand == InteractionHand.MAIN_HAND) InteractionHand.OFF_HAND else InteractionHand.MAIN_HAND @@ -333,6 +347,20 @@ fun validateIotaList(iotaList: TreeList, serverLevel: ServerLevel) return iotaList.map { validateIota(it, serverLevel) } } +// why is there not already a codec defined for vec2 +@JvmField +val VEC2_CODEC: Codec = RecordCodecBuilder.create({ instance -> + instance.group( + Codec.FLOAT.fieldOf("x").forGetter { it.x }, + Codec.FLOAT.fieldOf("y").forGetter { it.y } + ).apply(instance, ::Vec2) +}).orElseGet({ Vec2.ZERO }) +@JvmField +val VEC2_STREAM_CODEC: StreamCodec = StreamCodec.composite( + ByteBufCodecs.FLOAT, Vec2::x, + ByteBufCodecs.FLOAT, Vec2::y, + ::Vec2 +) // vanilla's StreamCodec.composite() only supports up to six fields in 1.21 fun compositeCodecSeven( diff --git a/Common/src/main/java/at/petrak/hexcasting/client/Keybinds.java b/Common/src/main/java/at/petrak/hexcasting/client/Keybinds.java index 46fe6d5d03..6f1ec3267d 100644 --- a/Common/src/main/java/at/petrak/hexcasting/client/Keybinds.java +++ b/Common/src/main/java/at/petrak/hexcasting/client/Keybinds.java @@ -20,7 +20,13 @@ public class Keybinds { CATEGORY ); - public static List ALL_BINDS = List.of(spellbookPrev, spellbookNext); + public static KeyMapping gridPanOverride = new KeyMapping( + "key.hexcasting.grid_pan_override", + InputConstants.UNKNOWN.getValue(), + CATEGORY + ); + + public static List ALL_BINDS = List.of(spellbookPrev, spellbookNext, gridPanOverride); public static void clientTickEnd() { // because of how mouse scrolling works (scrolling upward moves the page down), a positive diff --git a/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt b/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt index 30cd7676bb..c6c3236111 100644 --- a/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt +++ b/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt @@ -1,5 +1,6 @@ package at.petrak.hexcasting.client.gui +import at.petrak.hexcasting.api.HexAPI import at.petrak.hexcasting.api.casting.eval.ExecutionClientView import at.petrak.hexcasting.api.casting.eval.ResolvedPattern import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType @@ -18,16 +19,19 @@ import at.petrak.hexcasting.client.ShiftScrollListener import at.petrak.hexcasting.client.ktxt.accumulatedScroll import at.petrak.hexcasting.client.render.* import at.petrak.hexcasting.client.sound.GridSoundInstance +import at.petrak.hexcasting.common.items.armor.ItemRobes import at.petrak.hexcasting.common.lib.HexAttributes import at.petrak.hexcasting.common.lib.HexSounds import at.petrak.hexcasting.common.lib.hex.HexActions import at.petrak.hexcasting.common.msgs.MsgNewSpellPatternC2S +import at.petrak.hexcasting.common.msgs.MsgPannedGridC2S import at.petrak.hexcasting.xplat.IClientXplatAbstractions import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.PoseStack import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.player.LocalPlayer import net.minecraft.client.renderer.GameRenderer import net.minecraft.client.resources.sounds.SimpleSoundInstance import net.minecraft.client.resources.sounds.SoundInstance @@ -45,6 +49,7 @@ class GuiSpellcasting constructor( private var cachedStack: List, private var cachedRavenmind: Iota?, private var parenCount: Int, + private var panOffset: Vec2, ) : Screen("gui.hexcasting.spellcasting".asTranslatedComponent) { private var stackDescs: List = listOf() private var parenDescs: List = listOf() @@ -53,6 +58,11 @@ class GuiSpellcasting constructor( private var drawState: PatternDrawState = PatternDrawState.BetweenPatterns private val usedSpots: MutableSet = HashSet() + private var panningAllowed = false + private var forcePanning = false + private var prevPanOffset = panOffset + private val bgLocation = HexAPI.modLoc("textures/gui/casting_bg.png") + private var ambianceSoundInstance: GridSoundInstance? = null private val randSrc = SoundInstance.createUnseededRandom() @@ -64,6 +74,29 @@ class GuiSpellcasting constructor( this.calculateIotaDisplays() } + fun getPanDistance(): Float { + return panOffset.length() / 20 + } + + fun syncPanOffset() { + if (panOffset != prevPanOffset || ClientTickCounter.ticksInGame % 10 == 0L) { + IClientXplatAbstractions.INSTANCE.sendPacketToServer(MsgPannedGridC2S(panOffset)) + prevPanOffset = panOffset + } + } + + fun validatePanAbility(player: LocalPlayer) { + panningAllowed = ItemRobes.isWearingFullSet(player) + if (!panningAllowed && panOffset != Vec2.ZERO) { + panOffset = Vec2.ZERO + syncPanOffset() + } + } + + fun matchesPanInput(pButton: Int): Boolean { + return forcePanning || pButton == HexConfig.client().gridPanMouseButton() + } + fun recvServerUpdate(info: ExecutionClientView, index: Int) { if (info.isStackClear) { this.minecraft?.setScreen(null) @@ -129,6 +162,7 @@ class GuiSpellcasting constructor( if (player != null) { this.ambianceSoundInstance = GridSoundInstance(player) soundManager.play(this.ambianceSoundInstance!!) + this.validatePanAbility(player) } this.calculateIotaDisplays() @@ -141,6 +175,10 @@ class GuiSpellcasting constructor( val heldItem = player.getItemInHand(handOpenedWith) if (heldItem.isEmpty || !heldItem.`is`(HexTags.Items.STAVES) || player.getAttributeValue(HexAttributes.FEEBLE_MIND) > 0) closeForReal() + validatePanAbility(player) + if (this.panningAllowed) { + syncPanOffset() + } } } @@ -148,6 +186,8 @@ class GuiSpellcasting constructor( if (super.mouseClicked(mxOut, myOut, pButton)) { return true } + if (matchesPanInput(pButton) && this.panningAllowed) + return false if (HexConfig.client().clickingTogglesDrawing()) { return if (this.drawState is PatternDrawState.BetweenPatterns) drawStart(mxOut, myOut) @@ -190,14 +230,27 @@ class GuiSpellcasting constructor( } override fun mouseDragged(mxOut: Double, myOut: Double, pButton: Int, pDragX: Double, pDragY: Double): Boolean { - if (super.mouseDragged(mxOut, myOut, pButton, pDragX, pDragY)) { + if (super.mouseDragged(mxOut, myOut, pButton, pDragX, pDragY)) return true + if (matchesPanInput(pButton) && this.panningAllowed + && this.drawState is PatternDrawState.BetweenPatterns) { + return panGrid(pDragX, pDragY) } if (HexConfig.client().clickingTogglesDrawing()) return false return drawMove(mxOut, myOut) } + private fun panGrid(pDragX: Double, pDragY: Double): Boolean { + val shift = Vec2(pDragX.toFloat(), pDragY.toFloat()) + val newOffset = this.panOffset.add(shift) + if (newOffset.lengthSquared() < 900*900) + this.panOffset = newOffset + else + this.panOffset = newOffset.normalized().scale(900f); + return false; + } + private fun drawMove(mxOut: Double, myOut: Double): Boolean { val mx = Mth.clamp(mxOut, 0.0, this.width.toDouble()) val my = Mth.clamp(myOut, 0.0, this.height.toDouble()) @@ -270,6 +323,8 @@ class GuiSpellcasting constructor( } if (HexConfig.client().clickingTogglesDrawing()) return false + if (matchesPanInput(pButton) && this.panningAllowed) + return false return drawEnd() } @@ -335,6 +390,18 @@ class GuiSpellcasting constructor( } else if (Keybinds.spellbookNext.matches(key, scancode)) { ShiftScrollListener.onScroll(-1.0, false, false) return true + } else if (Keybinds.gridPanOverride.matches(key, scancode)) { + forcePanning = true + return true + } + + return false + } + + override fun keyReleased(key: Int, scancode: Int, modifiers: Int): Boolean { + if (Keybinds.gridPanOverride.matches(key, scancode)) { + forcePanning = false + return true } return false @@ -353,6 +420,12 @@ class GuiSpellcasting constructor( super.onClose() } + override fun renderBackground(guiGraphics: GuiGraphics, i: Int, j: Int, f: Float) { + guiGraphics.setColor(1f,1f,1f, (getPanDistance() / 15).coerceAtMost(1f)) + renderMenuBackgroundTexture(guiGraphics, bgLocation, 0, 0, 0f, 0f, this.width, this.height) + guiGraphics.setColor(1f, 1f, 1f, 1f) + this.renderBlurredBackground(f) + } override fun render(graphics: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTick: Float) { super.render(graphics, pMouseX, pMouseY, pPartialTick) @@ -400,12 +473,13 @@ class GuiSpellcasting constructor( for ((idx, elts) in this.patterns.withIndex()) { val (pat, origin, valid) = elts + val points = pat.toLines(this.hexSize(), this.coordToPx(origin)) + val center = Vec2(graphics.guiWidth() / 2f, graphics.guiHeight() / 2f) + if (points.all{ point -> point.distanceToSqr(center) > center.lengthSquared()+2500 }) + continue // don't render the pattern if it's completely offscreen drawPatternFromPoints( mat, - pat.toLines( - this.hexSize(), - this.coordToPx(origin) - ), + points, findDupIndices(pat.positions()), true, valid.color or (0xC8 shl 24), @@ -527,7 +601,7 @@ class GuiSpellcasting constructor( return (baseScale / scaleModifier).toFloat() } - fun coordsOffset(): Vec2 = Vec2(this.width.toFloat() * 0.5f, this.height.toFloat() * 0.5f) + fun coordsOffset(): Vec2 = Vec2(this.width.toFloat() * 0.5f, this.height.toFloat() * 0.5f).add(this.panOffset) fun coordToPx(coord: HexCoord) = at.petrak.hexcasting.api.utils.coordToPx(coord, this.hexSize(), this.coordsOffset()) @@ -536,7 +610,7 @@ class GuiSpellcasting constructor( private sealed class PatternDrawState { - /** We're waiting on the player to right-click again */ + /** We're waiting on the player to left-click again */ object BetweenPatterns : PatternDrawState() /** We just started drawing and haven't drawn the first line yet. */ diff --git a/Common/src/main/java/at/petrak/hexcasting/common/effects/DissociationEffect.java b/Common/src/main/java/at/petrak/hexcasting/common/effects/DissociationEffect.java new file mode 100644 index 0000000000..c426e97936 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/effects/DissociationEffect.java @@ -0,0 +1,59 @@ +package at.petrak.hexcasting.common.effects; + +import at.petrak.hexcasting.api.HexAPI; +import at.petrak.hexcasting.common.lib.HexDamageTypes; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectCategory; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Player; + +/** + * Applied when the casting grid is panned too far. During the first second of duration (ie continuously, so long as + * the effect is constantly being topped up) the player slowly loses oxygen. Once the grid is closed or panned back + * to center, the effect can start to tick down, and the oxygen drain stops. + *

+ * Make sure to use the provided constants for effect duration, or the oxygen drain system may behave oddly. + */ +public class DissociationEffect extends MobEffect { + public static final int AMP_0_DURATION = 300; + public static final int AMP_1_DURATION = 600; + + public DissociationEffect() { + super(MobEffectCategory.HARMFUL, 0x8932b8); + this.addAttributeModifier(Attributes.MOVEMENT_SPEED, HexAPI.modLoc("dissociation.slow"), -0.2, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL); + this.addAttributeModifier(Attributes.BLOCK_BREAK_SPEED, HexAPI.modLoc("dissociation.fatigue.a"), -0.45, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL); + this.addAttributeModifier(Attributes.ATTACK_SPEED, HexAPI.modLoc("dissociation.fatigue.b"), -0.1, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL); + this.addAttributeModifier(Attributes.ATTACK_DAMAGE, HexAPI.modLoc("dissociation.weak"), -4.0, AttributeModifier.Operation.ADD_VALUE); + } + + public static boolean shouldPreventBreathing(int duration, int amplifier) { + return switch (amplifier) { + case 0 -> duration > AMP_0_DURATION - 20; + case 1 -> duration > AMP_1_DURATION - 20; + default -> false; + }; + } + + @Override + public boolean applyEffectTick(LivingEntity entity, int amplifier) { + if (((Player)entity).getAbilities().invulnerable) return true; + entity.setAirSupply(entity.getAirSupply() - 1); + if (entity.getAirSupply() == -4) { + entity.setAirSupply(0); + entity.hurt(entity.damageSources().source(HexDamageTypes.FORGOT_TO_BREATHE), 2.0F); + } + return true; + } + + @Override + public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) { + if (!shouldPreventBreathing(duration, amplifier)) return false; + return switch (amplifier) { + case 0 -> duration % 10 == 0; + case 1 -> duration % 5 == 0; + default -> false; + }; + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemStaff.java b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemStaff.java index 7ad1f4b5af..ee79020e44 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemStaff.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemStaff.java @@ -46,13 +46,14 @@ public InteractionResultHolder use(Level world, Player player, Intera if (!world.isClientSide() && player instanceof ServerPlayer serverPlayer) { var vm = IXplatAbstractions.INSTANCE.getStaffcastVM(serverPlayer, hand); var patterns = IXplatAbstractions.INSTANCE.getPatternsSavedInUi(serverPlayer); + var panOffset = IXplatAbstractions.INSTANCE.getPanOffset(serverPlayer); @Nullable Iota ravenmind = vm.getImage().ravenmind().orElse(null); IXplatAbstractions.INSTANCE.sendPacketToPlayer(serverPlayer, new MsgOpenSpellGuiS2C(hand, patterns, vm.getImage().getStack(), ravenmind, - 0)); // TODO: Fix! + 0, panOffset)); // TODO: Fix! } player.awardStat(Stats.ITEM_USED.get(this)); diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/armor/ItemRobes.java b/Common/src/main/java/at/petrak/hexcasting/common/items/armor/ItemRobes.java index 0e3d52798f..a39c9086dd 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/items/armor/ItemRobes.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/items/armor/ItemRobes.java @@ -5,7 +5,9 @@ import at.petrak.hexcasting.api.item.VariantItem; import at.petrak.hexcasting.client.model.HexModelLayers; import at.petrak.hexcasting.client.model.HexRobesModel; +import at.petrak.hexcasting.common.items.ItemLens; import at.petrak.hexcasting.common.lib.HexArmorMaterials; +import at.petrak.hexcasting.common.lib.HexAttributes; import net.minecraft.client.Minecraft; import net.minecraft.client.model.geom.EntityModelSet; import net.minecraft.core.Holder; @@ -13,11 +15,20 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.EquipmentSlotGroup; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.item.ArmorItem; import net.minecraft.world.item.ArmorMaterial; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.ItemAttributeModifiers; import org.jetbrains.annotations.Nullable; +import java.util.List; + +import static at.petrak.hexcasting.api.HexAPI.modLoc; + /** * To get the armor model in; * On forge: client item extension in ForgeHexClientInitializer (line 161) @@ -27,11 +38,54 @@ public class ItemRobes extends ArmorItem implements VariantItem { public final Type type; private @Nullable HexRobesModel[] models; + public static ItemAttributeModifiers HOOD_MODIFIERS = ItemAttributeModifiers.builder() + .add(HexAttributes.SCRY_SIGHT, ItemLens.SCRY_SIGHT, EquipmentSlotGroup.HEAD) + .add(HexAttributes.GRID_ZOOM, ItemLens.GRID_ZOOM, EquipmentSlotGroup.HEAD) + .add(Attributes.ARMOR, new AttributeModifier( + modLoc("robes_hood_armor"), 3.0, AttributeModifier.Operation.ADD_VALUE + ), EquipmentSlotGroup.HEAD) + .build(); + + public static ItemAttributeModifiers TUNIC_MODIFIERS = ItemAttributeModifiers.builder() + .add(HexAttributes.MEDIA_CONSUMPTION_MODIFIER, new AttributeModifier( + modLoc("robes_tunic_discount"), -0.1, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL + ), EquipmentSlotGroup.CHEST) + .add(Attributes.ARMOR, new AttributeModifier( + modLoc("robes_tunic_armor"), 7.0, AttributeModifier.Operation.ADD_VALUE + ), EquipmentSlotGroup.CHEST) + .build(); + + public static ItemAttributeModifiers LEGS_MODIFIERS = ItemAttributeModifiers.builder() + .add(HexAttributes.AMBIT_RADIUS, new AttributeModifier( + modLoc("robes_legs_ambit"), 4.0, AttributeModifier.Operation.ADD_VALUE + ), EquipmentSlotGroup.LEGS) + .add(Attributes.ARMOR, new AttributeModifier( + modLoc("robes_legs_armor"), 6.0, AttributeModifier.Operation.ADD_VALUE + ), EquipmentSlotGroup.LEGS) + .build(); + + public static ItemAttributeModifiers BOOTS_MODIFIERS = ItemAttributeModifiers.builder() + .add(HexAttributes.SENTINEL_RADIUS, new AttributeModifier( + modLoc("robes_boots_sentinel_ambit"), 2.0, AttributeModifier.Operation.ADD_VALUE + ), EquipmentSlotGroup.FEET) + .add(Attributes.ARMOR, new AttributeModifier( + modLoc("robes_boots_armor"), 2.0, AttributeModifier.Operation.ADD_VALUE + ), EquipmentSlotGroup.FEET) + .build(); + public ItemRobes(Type type, Properties properties) { super(HexArmorMaterials.ROBES, type, properties); this.type = type; } + public static boolean isWearingFullSet(LivingEntity entity) { + for (var slot : List.of(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET)) { + if (!(entity.getItemBySlot(slot).getItem() instanceof ItemRobes)) + return false; + } + return true; + } + public static HexRobesModel[] provideArmorModelsForSlot(EquipmentSlot slot) { EntityModelSet models = Minecraft.getInstance().getEntityModels(); return new HexRobesModel[] { diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexAttributes.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexAttributes.java index 707ec6544c..e5469995b6 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexAttributes.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexAttributes.java @@ -33,7 +33,7 @@ public static void register() { //a multiplier to adjust media consumption across the board public static final Holder MEDIA_CONSUMPTION_MODIFIER = REGISTER.registerHolder("media_consumption", () -> new RangedAttribute( - MOD_ID + ".attributes.media_consumption", 1.0, 0.0, Double.MAX_VALUE).setSyncable(true)); + MOD_ID + ".attributes.media_consumption", 1.0, 0.0, Double.MAX_VALUE).setSyncable(true).setSentiment(Attribute.Sentiment.NEGATIVE)); public static final Holder AMBIT_RADIUS = REGISTER.registerHolder("ambit_radius", () -> new RangedAttribute( MOD_ID + ".attributes.ambit_radius", PlayerBasedCastEnv.DEFAULT_AMBIT_RADIUS, 0.0, Double.MAX_VALUE).setSyncable(true)); diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexDamageTypes.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexDamageTypes.java index 6bd0f900fc..cdb9c5bb9a 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexDamageTypes.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexDamageTypes.java @@ -3,6 +3,7 @@ import net.minecraft.core.registries.Registries; import net.minecraft.data.worldgen.BootstrapContext; import net.minecraft.resources.ResourceKey; +import net.minecraft.world.damagesource.DamageEffects; import net.minecraft.world.damagesource.DamageScaling; import net.minecraft.world.damagesource.DamageType; @@ -10,6 +11,7 @@ public class HexDamageTypes { public static final ResourceKey OVERCAST = ResourceKey.create(Registries.DAMAGE_TYPE, modLoc("overcast")); + public static final ResourceKey FORGOT_TO_BREATHE = ResourceKey.create(Registries.DAMAGE_TYPE, modLoc("forgot_to_breathe")); public static void bootstrap(BootstrapContext ctx) { ctx.register(OVERCAST, new DamageType( @@ -17,5 +19,10 @@ public static void bootstrap(BootstrapContext ctx) { DamageScaling.WHEN_CAUSED_BY_LIVING_NON_PLAYER, 0f )); + ctx.register(FORGOT_TO_BREATHE, new DamageType( + "hexcasting.forgot_to_breathe", + DamageScaling.WHEN_CAUSED_BY_LIVING_NON_PLAYER, + 0f + )); } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java index a471511be3..f4f21daf35 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.common.lib; +import at.petrak.hexcasting.api.HexAPI; import at.petrak.hexcasting.api.casting.ActionRegistryEntry; import at.petrak.hexcasting.api.misc.MediaConstants; import at.petrak.hexcasting.api.mod.HexTags; @@ -76,11 +77,6 @@ public static void registerItemsForCreativeTab(ResourceKey tabK IXplatAbstractions.INSTANCE.addEquipSlotFabric(EquipmentSlot.HEAD) .stacksTo(1).attributes(ItemLens.MODIFIERS))); - public static final Supplier ROBES_HOOD = make("robes/hood", () -> new ItemRobes(ArmorItem.Type.HELMET, unstackable().rarity(Rarity.UNCOMMON))); - public static final Supplier ROBES_TUNIC = make("robes/tunic", () -> new ItemRobes(ArmorItem.Type.CHESTPLATE, unstackable().rarity(Rarity.UNCOMMON))); - public static final Supplier ROBES_LEGS = make("robes/legs", () -> new ItemRobes(ArmorItem.Type.LEGGINGS, unstackable().rarity(Rarity.UNCOMMON))); - public static final Supplier ROBES_BOOTS = make("robes/boots", () -> new ItemRobes(ArmorItem.Type.BOOTS, unstackable().rarity(Rarity.UNCOMMON))); - public static final Supplier ABACUS = make("abacus", () -> new ItemAbacus(unstackable())); public static final Supplier THOUGHT_KNOT = make("thought_knot", () -> new ItemThoughtKnot(unstackable())); public static final Supplier FOCUS = make("focus", () -> new ItemFocus(unstackable())); @@ -108,6 +104,35 @@ public static void registerItemsForCreativeTab(ResourceKey tabK public static final Supplier NEURAL_FIBER = make("neural_fiber", () -> new Item(props().rarity(Rarity.UNCOMMON))); + public static final Supplier ROBES_HOOD = make("robes/hood", () -> + new ItemRobes(ArmorItem.Type.HELMET, unstackable() + .rarity(Rarity.UNCOMMON) + .durability(270) + .attributes(ItemRobes.HOOD_MODIFIERS) + ) + ); + public static final Supplier ROBES_TUNIC = make("robes/tunic", () -> + new ItemRobes(ArmorItem.Type.CHESTPLATE, unstackable() + .rarity(Rarity.UNCOMMON) + .durability(400) + .attributes(ItemRobes.TUNIC_MODIFIERS) + ) + ); + public static final Supplier ROBES_LEGS = make("robes/legs", () -> + new ItemRobes(ArmorItem.Type.LEGGINGS, unstackable() + .rarity(Rarity.UNCOMMON) + .durability(350) + .attributes(ItemRobes.LEGS_MODIFIERS) + ) + ); + public static final Supplier ROBES_BOOTS = make("robes/boots", () -> + new ItemRobes(ArmorItem.Type.BOOTS, unstackable() + .rarity(Rarity.UNCOMMON) + .durability(315) + .attributes(ItemRobes.BOOTS_MODIFIERS) + ) + ); + public static final Supplier SCROLL_SMOL = make("scroll_small", () -> new ItemScroll(props(), 1)); public static final Supplier SCROLL_MEDIUM = make("scroll_medium", () -> new ItemScroll(props(), 2)); public static final Supplier SCROLL_LARGE = make("scroll", () -> new ItemScroll(props(), 3)); diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexMobEffects.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexMobEffects.java index 30fc8f0299..3a5586c616 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexMobEffects.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexMobEffects.java @@ -1,6 +1,7 @@ package at.petrak.hexcasting.common.lib; import at.petrak.hexcasting.api.HexAPI; +import at.petrak.hexcasting.common.effects.DissociationEffect; import at.petrak.hexcasting.common.misc.HexMobEffect; import at.petrak.hexcasting.common.particles.ConjureParticleOptions; import at.petrak.hexcasting.xplat.IXplatAbstractions; @@ -22,10 +23,11 @@ public static void register() { public static final Holder ENLARGE_GRID = REGISTER.registerHolder("enlarge_grid", () -> new HexMobEffect(MobEffectCategory.BENEFICIAL, 0xc875ff).addAttributeModifier(HexAttributes.GRID_ZOOM, HexAPI.modLoc("enlarge_grid"), - 0.25, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)); + 0.25, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)); public static final Holder SHRINK_GRID = REGISTER.registerHolder("shrink_grid", () -> new HexMobEffect(MobEffectCategory.HARMFUL, 0xc0e660).addAttributeModifier(HexAttributes.GRID_ZOOM, HexAPI.modLoc("shrink_grid"), -0.2, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)); public static final Holder CRYSTALLIZED = REGISTER.registerHolder("crystallized", () -> new HexMobEffect(MobEffectCategory.HARMFUL, 0x8932b8, new ConjureParticleOptions(0x8932b8, true))); + public static final Holder DISSOCIATION = REGISTER.registerHolder("dissociation", DissociationEffect::new); } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgOpenSpellGuiS2C.java b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgOpenSpellGuiS2C.java index db261af133..11f988d77b 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgOpenSpellGuiS2C.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgOpenSpellGuiS2C.java @@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.casting.eval.ResolvedPattern; import at.petrak.hexcasting.api.casting.iota.Iota; import at.petrak.hexcasting.api.casting.iota.IotaType; +import at.petrak.hexcasting.api.utils.HexUtils; import at.petrak.hexcasting.client.gui.GuiSpellcasting; import net.minecraft.client.Minecraft; import net.minecraft.network.RegistryFriendlyByteBuf; @@ -11,6 +12,7 @@ import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.world.InteractionHand; +import net.minecraft.world.phys.Vec2; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -23,7 +25,8 @@ public record MsgOpenSpellGuiS2C(InteractionHand hand, List pat List stack, @Nullable Iota ravenmind, - int parenCount + int parenCount, + Vec2 panOffset ) implements CustomPacketPayload { public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(HexAPI.modLoc("cgui")); @@ -40,6 +43,7 @@ public record MsgOpenSpellGuiS2C(InteractionHand hand, List pat Optional::ofNullable ), MsgOpenSpellGuiS2C::ravenmind, ByteBufCodecs.VAR_INT, MsgOpenSpellGuiS2C::parenCount, + HexUtils.VEC2_STREAM_CODEC, MsgOpenSpellGuiS2C::panOffset, MsgOpenSpellGuiS2C::new ); @@ -59,7 +63,7 @@ public static void handle(MsgOpenSpellGuiS2C msg) { var mc = Minecraft.getInstance(); mc.setScreen( new GuiSpellcasting(msg.hand(), msg.patterns(), msg.stack, msg.ravenmind, - msg.parenCount)); + msg.parenCount, msg.panOffset)); }); } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgPannedGridC2S.java b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgPannedGridC2S.java new file mode 100644 index 0000000000..b9efb19b7f --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/msgs/MsgPannedGridC2S.java @@ -0,0 +1,53 @@ +package at.petrak.hexcasting.common.msgs; + +import at.petrak.hexcasting.api.HexAPI; +import at.petrak.hexcasting.api.casting.eval.env.StaffCastEnv; +import at.petrak.hexcasting.api.utils.HexUtils; +import at.petrak.hexcasting.client.gui.GuiSpellcasting; +import at.petrak.hexcasting.common.effects.DissociationEffect; +import at.petrak.hexcasting.common.lib.HexMobEffects; +import at.petrak.hexcasting.common.misc.HexMobEffect; +import at.petrak.hexcasting.xplat.IXplatAbstractions; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.phys.Vec2; +import org.joml.Vector3f; + +/** + * Sent client->server to sync the player's casting grid pan offset and potentially apply the Dissociation debuff + * if they've panned too far. Sent whenever the pan offset is upated, and also every 10 ticks while the GUI is + * open so that the debuff can be kept active. + */ +public record MsgPannedGridC2S(Vec2 panOffset) implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(HexAPI.modLoc("pan_cs")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + HexUtils.VEC2_STREAM_CODEC, MsgPannedGridC2S::panOffset, + MsgPannedGridC2S::new + ); + + @Override + public Type type() { + return TYPE; + } + + public void handle(MinecraftServer server, ServerPlayer sender) { + server.execute(() -> { + IXplatAbstractions.INSTANCE.setPanOffset(sender, panOffset); + var inst = sender.getEffect(HexMobEffects.DISSOCIATION); + int currentDur = (inst != null) ? inst.getDuration() : 0; + if (panOffset.lengthSquared() > 600*600) { + int newDur = Math.min(currentDur + 80, DissociationEffect.AMP_1_DURATION); + sender.addEffect(new MobEffectInstance(HexMobEffects.DISSOCIATION, newDur, 1, false, false, true)); + } else if (panOffset.lengthSquared() > 300*300) { + int newDur = Math.min(currentDur + 40, DissociationEffect.AMP_0_DURATION); + sender.addEffect(new MobEffectInstance(HexMobEffects.DISSOCIATION, newDur, 0, false, false, true)); + } + }); + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/datagen/tag/HexDamageTypeTagProvider.java b/Common/src/main/java/at/petrak/hexcasting/datagen/tag/HexDamageTypeTagProvider.java index 220598df0c..77be411044 100644 --- a/Common/src/main/java/at/petrak/hexcasting/datagen/tag/HexDamageTypeTagProvider.java +++ b/Common/src/main/java/at/petrak/hexcasting/datagen/tag/HexDamageTypeTagProvider.java @@ -25,6 +25,11 @@ protected void addTags(@NotNull HolderLookup.Provider provider) { DamageTypeTags.BYPASSES_SHIELD, DamageTypeTags.NO_KNOCKBACK ); + add(HexDamageTypes.FORGOT_TO_BREATHE, + DamageTypeTags.BYPASSES_ARMOR, + DamageTypeTags.BYPASSES_EFFECTS, + DamageTypeTags.NO_KNOCKBACK + ); } @SafeVarargs diff --git a/Common/src/main/java/at/petrak/hexcasting/mixin/MixinLivingEntity.java b/Common/src/main/java/at/petrak/hexcasting/mixin/MixinLivingEntity.java index 4b85154bc5..38d215f17c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/mixin/MixinLivingEntity.java +++ b/Common/src/main/java/at/petrak/hexcasting/mixin/MixinLivingEntity.java @@ -1,13 +1,9 @@ package at.petrak.hexcasting.mixin; -import at.petrak.hexcasting.api.HexAPI; import at.petrak.hexcasting.common.lib.HexItems; import at.petrak.hexcasting.common.lib.HexMobEffects; -import at.petrak.hexcasting.common.misc.HexMobEffect; -import at.petrak.hexcasting.xplat.IXplatAbstractions; +import at.petrak.hexcasting.common.effects.DissociationEffect; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.ai.Brain; import net.minecraft.world.entity.npc.Villager; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -37,4 +33,13 @@ private void dropCrystallizedLoot(CallbackInfo ci) { self.spawnAtLocation(new ItemStack(HexItems.AMETHYST_DUST.get(), dust + extra)); } } + + @Inject(method = "increaseAirSupply", at = @At("HEAD"), cancellable = true) + private void forgetToBreathe(int i, CallbackInfoReturnable cir) { + var self = (LivingEntity) (Object) this; + var inst = self.getEffect(HexMobEffects.DISSOCIATION); + if (inst != null && DissociationEffect.shouldPreventBreathing(inst.getDuration(), inst.getAmplifier())) { + cir.setReturnValue(i); + } + } } diff --git a/Common/src/main/java/at/petrak/hexcasting/mixin/client/MixinGameRenderer.java b/Common/src/main/java/at/petrak/hexcasting/mixin/client/MixinGameRenderer.java new file mode 100644 index 0000000000..9f1e1e2c34 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/mixin/client/MixinGameRenderer.java @@ -0,0 +1,28 @@ +package at.petrak.hexcasting.mixin.client; + +import at.petrak.hexcasting.api.HexAPI; +import at.petrak.hexcasting.client.gui.GuiSpellcasting; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.PostChain; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(GameRenderer.class) +public abstract class MixinGameRenderer { + @WrapOperation( + method = "processBlurEffect", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/PostChain;setUniform(Ljava/lang/String;F)V") + ) + private void scaleBlurWhenPanningGrid(PostChain instance, String string, float f, Operation original) { + GameRenderer renderer = (GameRenderer)(Object)this; + Screen screen = renderer.getMinecraft().screen; + if (screen instanceof GuiSpellcasting grid) { + original.call(instance, string, Math.min(grid.getPanDistance(), 12f)); + } else { + original.call(instance, string, f); + } + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/xplat/DummyXplatAbstractions.kt b/Common/src/main/java/at/petrak/hexcasting/xplat/DummyXplatAbstractions.kt index f21675d6dc..4d36722bff 100644 --- a/Common/src/main/java/at/petrak/hexcasting/xplat/DummyXplatAbstractions.kt +++ b/Common/src/main/java/at/petrak/hexcasting/xplat/DummyXplatAbstractions.kt @@ -56,6 +56,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.material.Fluid import net.minecraft.world.level.storage.loot.predicates.LootItemCondition +import net.minecraft.world.phys.Vec2 import net.minecraft.world.phys.Vec3 import java.util.function.BiFunction import java.util.function.Supplier @@ -88,12 +89,14 @@ internal class DummyXplatAbstractions: IXplatAbstractions { override fun setAltiora(target: Player, altiora: AltioraAbility?) = error("Found use of DummyXplatAbstractions.") override fun setStaffcastImage(target: ServerPlayer, image: CastingImage?) = error("Found use of DummyXplatAbstractions.") override fun setPatterns(target: ServerPlayer, patterns: List) = error("Found use of DummyXplatAbstractions.") + override fun setPanOffset(target: ServerPlayer, panOffset: Vec2) = error("Found use of DummyXplatAbstractions.") override fun getFlight(player: ServerPlayer): FlightAbility? = error("Found use of DummyXplatAbstractions.") override fun getAltiora(player: Player): AltioraAbility? = error("Found use of DummyXplatAbstractions.") override fun getPigment(player: Player): FrozenPigment = error("Found use of DummyXplatAbstractions.") override fun getSentinel(player: Player): Sentinel? = error("Found use of DummyXplatAbstractions.") override fun getStaffcastVM(player: ServerPlayer, hand: InteractionHand): CastingVM = error("Found use of DummyXplatAbstractions.") override fun getPatternsSavedInUi(player: ServerPlayer): List = error("Found use of DummyXplatAbstractions.") + override fun getPanOffset(player: ServerPlayer): Vec2? = error("Found use of DummyXplatAbstractions.") override fun clearCastingData(player: ServerPlayer) = error("Found use of DummyXplatAbstractions.") override fun findMediaHolder(stack: ItemStack): ADMediaHolder? = error("Found use of DummyXplatAbstractions.") override fun findDataHolder(stack: ItemStack): ADIotaHolder? = error("Found use of DummyXplatAbstractions.") diff --git a/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java b/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java index c2cc4ec351..d64a582211 100644 --- a/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java +++ b/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java @@ -38,7 +38,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Tier; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -47,6 +46,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; +import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; @@ -103,6 +103,8 @@ public interface IXplatAbstractions { void setPatterns(ServerPlayer target, List patterns); + void setPanOffset(ServerPlayer target, Vec2 panOffset); + @Nullable FlightAbility getFlight(ServerPlayer player); @Nullable AltioraAbility getAltiora(Player player); @@ -115,6 +117,8 @@ public interface IXplatAbstractions { List getPatternsSavedInUi(ServerPlayer player); + Vec2 getPanOffset(ServerPlayer player); + void clearCastingData(ServerPlayer player); @Nullable diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index 2ca034e0fe..eff6c008f5 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -229,7 +229,8 @@ "effect.hexcasting": { enlarge_grid: "Clarity", shrink_grid: "Clouding", - crystallized: "Crystallized" + crystallized: "Crystallized", + dissociation: "Dissociation", }, "item.minecraft.potion.effect": { @@ -284,6 +285,7 @@ "key.hexcasting": { "spellbook_prev": "Previous Spellbook Page", "spellbook_next": "Next Spellbook Page", + "grid_pan_override": "Grid Pan Override", }, "tag.hexcasting": { @@ -375,6 +377,10 @@ "": "Clicking Toggles Drawing", "@Tooltip": "Whether you click to start and stop drawing instead of clicking and dragging to draw", }, + gridPanMouseButton: { + "": "Grid Pan Mouse Button", + "@Tooltip": "Which mouse button is used to pan the hex grid (once that ability is unlocked)", + }, advancedTooltipsShowsIotaNBT: { "": "Advanced Tooltips Shows Iota NBT", "@Tooltip": "Whether enabling advanced tooltips (F3+H) should display the full NBT of iotas stored in items like foci and spellbooks", @@ -534,6 +540,10 @@ "": "%s's mind was subsumed into energy", player: "%s's mind was subsumed into energy while fighting %s", }, + forgot_to_breathe: { + "": "%s forgot to breathe", + player: "%s forgot to breathe while fighting %s", + } }, "command.hexcasting": { @@ -1329,6 +1339,7 @@ akashiclib: "Akashic Libraries", quenching_allays: "Quenching Allays", neural_fiber: "Neural Inversion", + caster_robes: "Caster's Garments", fanciful_staves: "Fanciful Staves", // and the actions @@ -1794,7 +1805,14 @@ "2": "Viscera suffused with micro-fractal splinters of cognition, living and thinking and-- USELESS! Nothing but a mass of meat and _media, in the end.$(br2)But the nerves... the nerves become conduits, skeins of thought reified in shimmering fiber. These I can USE. Free them from the flesh and they are $(o)mine/$.$(br2)I could weave them into myself -- more $(o)space/$ to think in -- no. I am not being rational.", "3": "The process itself is simple: apply $(l:items/potions)Clarity/$ to render the body receptive, cast $(l:greatwork/brainsweeping)$(action)Flay Mind/$ with the $(l:patterns/basics#hexcasting:entity_pos/eye)$(action)subject’s own head/$ as the destination, then dissect to harvest the fiber. Any villager will do, though a more developed mind will provide a higher yield.$(br2)I wonder... does it understand, even excised? Does it feel?", }, - + + caster_robes: { + "1": "With $(l:greatwork/neural_fiber)Neural Fiber/$, I finally have the key to augmenting my own skill! Robes woven from the fibers streamline the _media around me -- I can cast further, cheaper, $(o)greater/$.$(br2)But I would not settle for that alone. With these robes I can think $(o)outside/$ myself, mind shared between thinking flesh and thinking fabric, threads woven as thoughts are woven as Nature is-- ENOUGH.", + "2": "So long as I wear the full set of robes, I gain the ability to $(o)pan/$ the hex grid with right-click. This vastly expands the number of patterns I can draw at once, though not without risks. Drifting further into this mental space pulls my awareness away from my body -- go too far, and I might even forget to breathe.$(br2)The nature of the robes also renders them receptive to $(l:patterns/spells/cyclevariant)$(action)Caster's Glamour/$, should I wish to style myself differently.", + "3": "Should I wish to pan the grid using something other than right-click, I can change which mouse button is required using the client config.$(br2)I can also hold $(k:hexcasting.grid_pan_override) to make $(o)all/$ mouse buttons work to pan the grid rather than drawing patterns.", + "3.header": "Alternate Controls", + }, + "fanciful_staves.1": "It is only right as I shed the husk of ignorance I replace my tools, my palm-polished staves. These new constructions of mine have no additional properties -- but they are so glorious, oh so Glorious... They match the radiance winking at the corners of my sight.", // Patterns diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/caster_robes.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/caster_robes.json new file mode 100644 index 0000000000..bcb48bcfde --- /dev/null +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/caster_robes.json @@ -0,0 +1,33 @@ +{ + "name": "hexcasting.entry.caster_robes", + "category": "hexcasting:greatwork", + "icon": "hexcasting:robes/hood", + "advancement": "hexcasting:enlightenment", + "entry_color": "54398a", + "sortnum": 7, + "pages": [ + { + "type": "patchouli:text", + "text": "hexcasting.page.caster_robes.1" + }, + { + "type": "patchouli:text", + "text": "hexcasting.page.caster_robes.2" + }, + { + "type": "patchouli:crafting", + "recipe": "hexcasting:robes/hood", + "recipe2": "hexcasting:robes/tunic" + }, + { + "type": "patchouli:crafting", + "recipe": "hexcasting:robes/legs", + "recipe2": "hexcasting:robes/boots" + }, + { + "type": "patchouli:text", + "title": "hexcasting.page.caster_robes.3.header", + "text": "hexcasting.page.caster_robes.3" + } + ] +} \ No newline at end of file diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/neural_fiber.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/neural_fiber.json index 1c085ec08c..55eede5e9f 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/neural_fiber.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/neural_fiber.json @@ -17,7 +17,8 @@ { "type": "patchouli:spotlight", "item": "hexcasting:neural_fiber", - "text": "hexcasting.page.neural_fiber.3" + "text": "hexcasting.page.neural_fiber.3", + "link_recipe": true } ] } \ No newline at end of file diff --git a/Common/src/main/resources/assets/hexcasting/textures/gui/casting_bg.png b/Common/src/main/resources/assets/hexcasting/textures/gui/casting_bg.png new file mode 100644 index 0000000000..7f94529482 Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/gui/casting_bg.png differ diff --git a/Common/src/main/resources/assets/hexcasting/textures/mob_effect/dissociation.png b/Common/src/main/resources/assets/hexcasting/textures/mob_effect/dissociation.png new file mode 100644 index 0000000000..2d90323597 Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/mob_effect/dissociation.png differ diff --git a/Common/src/main/resources/hexplat.mixins.json b/Common/src/main/resources/hexplat.mixins.json index 4916e585bd..8037e24e39 100644 --- a/Common/src/main/resources/hexplat.mixins.json +++ b/Common/src/main/resources/hexplat.mixins.json @@ -7,6 +7,7 @@ "mixins": [ "MixinLivingEntity", "MixinAbstractVillager", + "MixinLivingEntity", "MixinMob", "MixinRaider", "MixinVillager", @@ -27,6 +28,7 @@ "accessor.client.AccessorRenderStateShard", "accessor.client.AccessorRenderType", "client.MixinClientLevel", - "client.MixinPlayerRenderer" + "client.MixinPlayerRenderer", + "client.MixinGameRenderer" ] } diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt b/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt index 6ad22c56f1..a42dc2ebbe 100644 --- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt @@ -4,6 +4,7 @@ import at.petrak.hexcasting.client.ClientTickCounter import at.petrak.hexcasting.client.Keybinds import at.petrak.hexcasting.client.RegisterClientStuff import at.petrak.hexcasting.client.ShiftScrollListener +import at.petrak.hexcasting.client.gui.GuiSpellcasting import at.petrak.hexcasting.client.gui.PatternTooltipComponent import at.petrak.hexcasting.client.model.HexModelLayers import at.petrak.hexcasting.client.render.HexAdditionalRenderers @@ -38,7 +39,7 @@ object FabricHexClientInitializer : ClientModInitializer { } HudRenderCallback.EVENT.register(HexAdditionalRenderers::overlayGui) WorldRenderEvents.START.register { ClientTickCounter.renderTickStart(it.tickCounter().gameTimeDeltaTicks) } - ClientTickEvents.END_CLIENT_TICK.register { + ClientTickEvents.END_CLIENT_TICK.register { ctx -> ClientTickCounter.clientTickEnd() Keybinds.clientTickEnd() ShiftScrollListener.clientTickEnd() diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexConfig.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexConfig.java index 09dd3727e7..aaf82542ff 100644 --- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexConfig.java +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexConfig.java @@ -140,6 +140,8 @@ public static final class Client implements HexConfig.ClientConfigAccess, Config @ConfigEntry.Gui.Tooltip private boolean clickingTogglesDrawing = DEFAULT_CLICKING_TOGGLES_DRAWING; @ConfigEntry.Gui.Tooltip + private int gridPanMouseButton = DEFAULT_GRID_PAN_MOUSE_BUTTON; + @ConfigEntry.Gui.Tooltip private boolean advancedTooltipsShowsIotaNBT = DEFAULT_ADVANCED_TOOLTIPS_SHOWS_IOTA_NBT; @ConfigEntry.Gui.Tooltip private boolean staticActiveSlates = DEFAULT_STATIC_ACTIVE_SLATES; @@ -147,6 +149,7 @@ public static final class Client implements HexConfig.ClientConfigAccess, Config @Override public void validatePostLoad() throws ValidationException { this.gridSnapThreshold = Mth.clamp(this.gridSnapThreshold, 0.5, 1.0); + this.gridPanMouseButton = Math.max(this.gridPanMouseButton, 1); } @Override @@ -179,6 +182,9 @@ public boolean clickingTogglesDrawing() { return clickingTogglesDrawing; } + @Override + public int gridPanMouseButton() { return gridPanMouseButton; } + @Override public boolean advancedTooltipsShowsIotaNBT() { return advancedTooltipsShowsIotaNBT; diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/CCPanOffset.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/CCPanOffset.java new file mode 100644 index 0000000000..6af7bed9b0 --- /dev/null +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/CCPanOffset.java @@ -0,0 +1,33 @@ +package at.petrak.hexcasting.fabric.cc; + +import at.petrak.hexcasting.api.utils.HexUtils; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.Vec2; +import org.ladysnake.cca.api.v3.component.Component; +import org.ladysnake.cca.api.v3.component.sync.AutoSyncedComponent; + +public class CCPanOffset implements Component, AutoSyncedComponent { + public static final String TAG_PAN_OFFSET = "pan_offset"; + + private final Player owner; + private Vec2 panOffset = Vec2.ZERO; + + public CCPanOffset(ServerPlayer owner) { + this.owner = owner; + } + + public Vec2 getPanOffset() { return panOffset; } + public void setPanOffset(Vec2 newOffset) { this.panOffset = newOffset; } + + @Override + public void readFromNbt(CompoundTag tag, HolderLookup.Provider registryLookup) { + this.panOffset = HexUtils.vec2FromNBT(tag.getCompound(TAG_PAN_OFFSET)); + } + + public void writeToNbt(CompoundTag tag, HolderLookup.Provider registryLookup) { + tag.put(TAG_PAN_OFFSET, HexUtils.serializeToNBT(this.panOffset)); + } +} diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/HexCardinalComponents.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/HexCardinalComponents.java index b60bc9a1c6..9dd5c9e10e 100644 --- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/HexCardinalComponents.java +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/HexCardinalComponents.java @@ -41,6 +41,8 @@ public class HexCardinalComponents implements EntityComponentInitializer, ItemCo CCStaffcastImage.class); public static final ComponentKey PATTERNS = ComponentRegistry.getOrCreate(modLoc("patterns"), CCPatterns.class); + public static final ComponentKey PAN_OFFSET = ComponentRegistry.getOrCreate(modLoc("pan_offset"), + CCPanOffset.class); public static final ComponentKey CLIENT_CASTING_STACK = ComponentRegistry.getOrCreate(modLoc("client_casting_stack"), CCClientCastingStack.class); @@ -78,6 +80,7 @@ public void registerEntityComponentFactories(EntityComponentFactoryRegistry regi registry.registerFor(ServerPlayer.class, FLIGHT, CCFlight::new); registry.registerFor(ServerPlayer.class, STAFFCAST_IMAGE, CCStaffcastImage::new); registry.registerFor(ServerPlayer.class, PATTERNS, CCPatterns::new); + registry.registerFor(ServerPlayer.class, PAN_OFFSET, CCPanOffset::new); registry.registerFor(ItemEntity.class, IOTA_HOLDER, wrapItemEntityDelegate( diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/network/FabricPacketHandler.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/network/FabricPacketHandler.java index c7e2346e9b..8ee9e166a5 100644 --- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/network/FabricPacketHandler.java +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/network/FabricPacketHandler.java @@ -16,6 +16,7 @@ public static void initPackets() { PayloadTypeRegistry.playC2S().register(MsgShiftScrollC2S.TYPE, MsgShiftScrollC2S.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(MsgNewSpellPatternC2S.TYPE, MsgNewSpellPatternC2S.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(MsgNewSpellPatternS2C.TYPE, MsgNewSpellPatternS2C.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(MsgPannedGridC2S.TYPE, MsgPannedGridC2S.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(MsgOpenSpellGuiS2C.TYPE, MsgOpenSpellGuiS2C.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(MsgBeepS2C.TYPE, MsgBeepS2C.STREAM_CODEC); PayloadTypeRegistry.playS2C().register(MsgShiftScrollC2S.TYPE, MsgShiftScrollC2S.STREAM_CODEC); @@ -31,6 +32,8 @@ public static void init() { makeServerBoundHandler(MsgShiftScrollC2S::handle)); ServerPlayNetworking.registerGlobalReceiver(MsgNewSpellPatternC2S.TYPE, makeServerBoundHandler(MsgNewSpellPatternC2S::handle)); + ServerPlayNetworking.registerGlobalReceiver(MsgPannedGridC2S.TYPE, + makeServerBoundHandler(MsgPannedGridC2S::handle)); } private static ServerPlayNetworking.PlayPayloadHandler makeServerBoundHandler( diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java index fbad939683..eda1e88482 100644 --- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java @@ -80,6 +80,7 @@ import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; import net.minecraft.world.level.storage.loot.predicates.MatchTool; +import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import virtuoel.pehkui.api.ScaleTypes; @@ -189,6 +190,12 @@ public void setPatterns(ServerPlayer target, List patterns) { cc.setPatterns(patterns); } + @Override + public void setPanOffset(ServerPlayer target, Vec2 panOffset) { + var cc = HexCardinalComponents.PAN_OFFSET.get(target); + cc.setPanOffset(panOffset); + } + @Override public boolean isBrainswept(Mob mob) { var cc = HexCardinalComponents.BRAINSWEPT.get(mob); @@ -231,10 +238,17 @@ public List getPatternsSavedInUi(ServerPlayer player) { return cc.getPatterns(); } + @Override + public Vec2 getPanOffset(ServerPlayer player) { + var cc = HexCardinalComponents.PAN_OFFSET.get(player); + return cc.getPanOffset(); + } + @Override public void clearCastingData(ServerPlayer player) { this.setStaffcastImage(player, null); this.setPatterns(player, List.of()); + this.setPanOffset(player, Vec2.ZERO); } @Override diff --git a/Fabric/src/main/resources/fabric.mod.json b/Fabric/src/main/resources/fabric.mod.json index 053571f52b..798dec41db 100644 --- a/Fabric/src/main/resources/fabric.mod.json +++ b/Fabric/src/main/resources/fabric.mod.json @@ -77,6 +77,7 @@ "hexcasting:altiora", "hexcasting:harness", "hexcasting:patterns", + "hexcasting:pan_offset", "hexcasting:client_casting_stack", "hexcasting:pigment", "hexcasting:iota_holder", diff --git a/Neoforge/src/generated/resources/data/hexcasting/damage_type/forgot_to_breathe.json b/Neoforge/src/generated/resources/data/hexcasting/damage_type/forgot_to_breathe.json new file mode 100644 index 0000000000..d3a0ca991f --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/damage_type/forgot_to_breathe.json @@ -0,0 +1,5 @@ +{ + "exhaustion": 0.0, + "message_id": "hexcasting.forgot_to_breathe", + "scaling": "when_caused_by_living_non_player" +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/bypasses_armor.json b/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/bypasses_armor.json index 4209c89aa3..319bb24df4 100644 --- a/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/bypasses_armor.json +++ b/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/bypasses_armor.json @@ -1,5 +1,6 @@ { "values": [ - "hexcasting:overcast" + "hexcasting:overcast", + "hexcasting:forgot_to_breathe" ] } \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/bypasses_effects.json b/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/bypasses_effects.json index 4209c89aa3..319bb24df4 100644 --- a/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/bypasses_effects.json +++ b/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/bypasses_effects.json @@ -1,5 +1,6 @@ { "values": [ - "hexcasting:overcast" + "hexcasting:overcast", + "hexcasting:forgot_to_breathe" ] } \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/no_knockback.json b/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/no_knockback.json index 4209c89aa3..319bb24df4 100644 --- a/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/no_knockback.json +++ b/Neoforge/src/generated/resources/data/minecraft/tags/damage_type/no_knockback.json @@ -1,5 +1,6 @@ { "values": [ - "hexcasting:overcast" + "hexcasting:overcast", + "hexcasting:forgot_to_breathe" ] } \ No newline at end of file diff --git a/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java b/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java index b6233f91bf..4234e2af13 100644 --- a/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java +++ b/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java @@ -4,6 +4,7 @@ import at.petrak.hexcasting.client.Keybinds; import at.petrak.hexcasting.client.RegisterClientStuff; import at.petrak.hexcasting.client.ShiftScrollListener; +import at.petrak.hexcasting.client.gui.GuiSpellcasting; import at.petrak.hexcasting.client.gui.PatternTooltipComponent; import at.petrak.hexcasting.client.model.AltioraLayer; import at.petrak.hexcasting.client.model.HexModelLayers; diff --git a/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexConfig.java b/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexConfig.java index a9a48d4201..c582804ecb 100644 --- a/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexConfig.java +++ b/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexConfig.java @@ -86,6 +86,7 @@ public static class Client implements HexConfig.ClientConfigAccess { private static ModConfigSpec.BooleanValue invertAbacusScrollDirection; private static ModConfigSpec.DoubleValue gridSnapThreshold; private static ModConfigSpec.BooleanValue clickingTogglesDrawing; + private static ModConfigSpec.IntValue gridPanMouseButton; private static ModConfigSpec.BooleanValue disableInworldScrolling; private static ModConfigSpec.BooleanValue advancedTooltipsShowsIotaNBT; private static ModConfigSpec.BooleanValue staticActiveSlates; @@ -111,6 +112,9 @@ public Client(ModConfigSpec.Builder builder) { clickingTogglesDrawing = builder.comment( "Whether you click to start and stop drawing instead of clicking and dragging") .define("clickingTogglesDrawing", DEFAULT_CLICKING_TOGGLES_DRAWING); + gridPanMouseButton = builder.comment( + "Which mouse button is used to pan the hex grid (once that ability is unlocked)") + .defineInRange("gridPanMouseButton", DEFAULT_GRID_PAN_MOUSE_BUTTON, 1, Integer.MAX_VALUE); advancedTooltipsShowsIotaNBT = builder.comment( "Whether enabling advanced tooltips (F3+H) should display the full NBT of iotas stored in items " + "like foci and spellbooks") @@ -150,6 +154,9 @@ public boolean clickingTogglesDrawing() { return clickingTogglesDrawing.get(); } + @Override + public int gridPanMouseButton() { return gridPanMouseButton.get(); } + @Override public boolean advancedTooltipsShowsIotaNBT() { return advancedTooltipsShowsIotaNBT.get(); diff --git a/Neoforge/src/main/java/at/petrak/hexcasting/forge/network/ForgePacketHandler.java b/Neoforge/src/main/java/at/petrak/hexcasting/forge/network/ForgePacketHandler.java index c53a796a4e..5c95098709 100644 --- a/Neoforge/src/main/java/at/petrak/hexcasting/forge/network/ForgePacketHandler.java +++ b/Neoforge/src/main/java/at/petrak/hexcasting/forge/network/ForgePacketHandler.java @@ -21,6 +21,8 @@ public static void init(IEventBus modBus) { // Client -> server registar.playToServer(MsgNewSpellPatternC2S.TYPE, MsgNewSpellPatternC2S.STREAM_CODEC, makeServerBoundHandler(MsgNewSpellPatternC2S::handle)); + registar.playToServer(MsgPannedGridC2S.TYPE, MsgPannedGridC2S.STREAM_CODEC, + makeServerBoundHandler(MsgPannedGridC2S::handle)); registar.playToServer(MsgShiftScrollC2S.TYPE, MsgShiftScrollC2S.STREAM_CODEC, makeServerBoundHandler(MsgShiftScrollC2S::handle)); diff --git a/Neoforge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java b/Neoforge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java index 19d0b90813..33112f9120 100644 --- a/Neoforge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java +++ b/Neoforge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java @@ -60,7 +60,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Tier; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -70,6 +69,7 @@ import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import net.neoforged.api.distmarker.Dist; import net.neoforged.fml.ModContainer; @@ -232,6 +232,11 @@ public void setPatterns(ServerPlayer player, List patterns) { player.getPersistentData().put(TAG_PATTERNS, ResolvedPattern.CODEC.listOf().encodeStart(NbtOps.INSTANCE, patterns).getOrThrow()); } + @Override + public void setPanOffset(ServerPlayer player, Vec2 panOffset) { + player.getPersistentData().put(TAG_PAN_OFFSET, HexUtils.VEC2_CODEC.encodeStart(NbtOps.INSTANCE, panOffset).getOrThrow()); + } + @Override public boolean isBrainswept(Mob e) { return e.getPersistentData().getBoolean(TAG_BRAINSWEPT); @@ -295,10 +300,16 @@ public List getPatternsSavedInUi(ServerPlayer player) { return ResolvedPattern.CODEC.listOf().parse(NbtOps.INSTANCE, player.getPersistentData().getList(TAG_PATTERNS, Tag.TAG_COMPOUND)).getOrThrow(); } + @Override + public Vec2 getPanOffset(ServerPlayer player) { + return HexUtils.VEC2_CODEC.parse(NbtOps.INSTANCE, player.getPersistentData().getCompound(TAG_PAN_OFFSET)).getOrThrow(); + } + @Override public void clearCastingData(ServerPlayer player) { player.getPersistentData().remove(TAG_VM); player.getPersistentData().remove(TAG_PATTERNS); + player.getPersistentData().remove(TAG_PAN_OFFSET); } @Override @@ -572,4 +583,5 @@ public void setScale(Entity e, float scale) { public static final String TAG_VM = "hexcasting:spell_harness"; public static final String TAG_PATTERNS = "hexcasting:spell_patterns"; + public static final String TAG_PAN_OFFSET = "hexcasting:pan_offset"; }