make the casting UI less of an eyesore maybe

This commit is contained in:
gamma-delta 2022-06-16 10:15:27 -05:00
parent 8ea2a6d66d
commit d80ace0812
29 changed files with 372 additions and 119 deletions

View file

@ -25,7 +25,7 @@ interface Action {
fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult

View file

@ -19,7 +19,7 @@ interface ConstManaAction : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
if (this.argc > stack.size)
@ -31,6 +31,6 @@ interface ConstManaAction : Action {
val sideEffects = mutableListOf<OperatorSideEffect>(OperatorSideEffect.ConsumeMana(this.manaCost))
return OperationResult(continuation, stack, local, sideEffects)
return OperationResult(continuation, stack, ravenmind, sideEffects)
}
}

View file

@ -10,6 +10,6 @@ import at.petrak.hexcasting.api.spell.iota.Iota
data class OperationResult(
val newContinuation: SpellContinuation,
val newStack: List<Iota>,
val newLocalIota: Iota,
val newLocalIota: Iota?,
val sideEffects: List<OperatorSideEffect>
)

View file

@ -242,6 +242,8 @@ fun List<Iota>.getLongOrList(idx: Int, argc: Int = 0): Either<Long, SpellList> {
)
}
fun Iota?.orNull() = this ?: NullIota()
// TODO do we make this work on lists
// there should probably be some way to abstract function application over lists, vecs, and numbers,
// and i bet it's fucking monads

View file

@ -21,14 +21,14 @@ interface SpellAction : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
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 executeResult = this.execute(args, ctx) ?: return OperationResult(continuation, stack, local, listOf())
val executeResult = this.execute(args, ctx) ?: return OperationResult(continuation, stack, ravenmind, listOf())
val (spell, mana, particles) = executeResult
val sideEffects = mutableListOf<OperatorSideEffect>()
@ -49,7 +49,7 @@ interface SpellAction : Action {
for (spray in particles)
sideEffects.add(OperatorSideEffect.Particles(spray))
return OperationResult(continuation, stack, local, sideEffects)
return OperationResult(continuation, stack, ravenmind, sideEffects)
}
}

View file

@ -13,7 +13,6 @@ import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.spell.iota.NullIota
import at.petrak.hexcasting.api.spell.iota.PatternIota
import at.petrak.hexcasting.api.spell.math.HexDir
import at.petrak.hexcasting.api.spell.math.HexPattern
@ -35,7 +34,7 @@ import kotlin.math.min
*/
class CastingHarness private constructor(
var stack: MutableList<Iota>,
var ravenmind: Iota,
var ravenmind: Iota?,
var parenCount: Int,
var parenthesized: List<Iota>,
var escapeNext: Boolean,
@ -47,7 +46,7 @@ class CastingHarness private constructor(
constructor(
ctx: CastingContext,
prepackagedColorizer: FrozenColorizer? = null
) : this(mutableListOf(), NullIota(), 0, mutableListOf(), false, ctx, prepackagedColorizer)
) : this(mutableListOf(), null, 0, mutableListOf(), false, ctx, prepackagedColorizer)
/**
* Execute a single iota.
@ -79,14 +78,20 @@ class CastingHarness private constructor(
}
if (continuation is SpellContinuation.NotDone) {
lastResolutionType = if (lastResolutionType.success) ResolvedPatternType.EVALUATED else ResolvedPatternType.ERRORED
lastResolutionType =
if (lastResolutionType.success) ResolvedPatternType.EVALUATED else ResolvedPatternType.ERRORED
}
val (stackDescs, parenDescs, ravenmind) = generateDescs()
return ControllerInfo(
info.playSound,
this.stack.isEmpty() && this.parenCount == 0 && !this.escapeNext,
lastResolutionType,
generateDescs()
stackDescs,
parenDescs,
ravenmind,
this.parenCount
)
}
@ -248,7 +253,11 @@ class CastingHarness private constructor(
}
}
fun generateDescs() = stack.map(Iota::display)
fun generateDescs() = Triple(
stack.map(HexIotaTypes::serialize),
parenthesized.map(HexIotaTypes::serialize),
ravenmind?.let(HexIotaTypes::serialize)
)
/**
* Return the functional update represented by the current state (for use with `copy`)
@ -459,7 +468,8 @@ class CastingHarness private constructor(
fun serializeToNBT() = NBTBuilder {
TAG_STACK %= stack.serializeToNBT()
TAG_LOCAL %= HexIotaTypes.serialize(ravenmind)
if (ravenmind != null)
TAG_LOCAL %= HexIotaTypes.serialize(ravenmind!!)
TAG_PAREN_COUNT %= parenCount
TAG_ESCAPE_NEXT %= escapeNext
@ -488,12 +498,15 @@ class CastingHarness private constructor(
stack.add(datum)
}
val localIota = HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world)
val ravenmind = if (nbt.contains(TAG_LOCAL))
HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world)
else
null
val parenthesized = mutableListOf<Iota>()
val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND)
for (subtag in parenTag) {
parenthesized.add(HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world))
parenthesized.add(HexIotaTypes.deserialize(subtag.downcast(CompoundTag.TYPE), ctx.world))
}
val parenCount = nbt.getInt(TAG_PAREN_COUNT)
@ -505,7 +518,7 @@ class CastingHarness private constructor(
null
}
CastingHarness(stack, localIota, parenCount, parenthesized, escapeNext, ctx, colorizer)
CastingHarness(stack, ravenmind, parenCount, parenthesized, escapeNext, ctx, colorizer)
} catch (exn: Exception) {
CastingHarness(ctx)
}

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.spell.casting
import net.minecraft.network.chat.Component
import net.minecraft.nbt.CompoundTag
/**
* Information for the sake of the GUI.
@ -9,5 +9,8 @@ data class ControllerInfo(
val makesCastSound: Boolean,
val isStackClear: Boolean,
val resolutionType: ResolvedPatternType,
val stackDesc: List<Component>
val stack: List<CompoundTag>,
val parenthesized: List<CompoundTag>,
val ravenmind: CompoundTag?,
val parenCount: Int,
)

View file

@ -1,10 +1,13 @@
package at.petrak.hexcasting.api.spell.iota;
import net.minecraft.client.gui.Font;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.FormattedCharSequence;
import javax.annotation.Nullable;
import java.util.List;
// Take notes from ForgeRegistryEntry
public abstract class IotaType<T extends Iota> {
@ -14,7 +17,7 @@ public abstract class IotaType<T extends Iota> {
* The {@code type} key is given when registering the spell datum type; this method
* deserializes the tag associated with {@code "datum"}.
* <p>
* Returning {@code null} makes the resulting datum be {@link at.petrak.hexcasting.api.spell.Widget Widget.NULL}.
* Returning {@code null} makes the resulting datum be {@link NullIota}.
* Throwing an exception raises a mishap.
*/
@Nullable
@ -26,6 +29,15 @@ public abstract class IotaType<T extends Iota> {
*/
public abstract Component display(Tag tag);
/**
* Get a display of this datum from the {@code data} tag, with a maximum width.
* This is for use on the client.
*/
public List<FormattedCharSequence> displayWithWidth(Tag tag, int maxWidth, Font font) {
var display = this.display(tag);
return font.split(display, maxWidth);
}
/**
* Get the color associated with this datum type.
*/

View file

@ -4,12 +4,15 @@ import at.petrak.hexcasting.api.spell.SpellList;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.Font;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.FormattedCharSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -111,6 +114,54 @@ public class ListIota extends Iota {
return out;
}
@Override
public List<FormattedCharSequence> displayWithWidth(Tag tag, int maxWidth, Font font) {
// We aim to not break one iota between lines
var listTag = HexUtils.downcast(tag, ListTag.TYPE);
var start = FormattedCharSequence.forward("[", Style.EMPTY.withColor(ChatFormatting.DARK_PURPLE));
var cursor = font.width(start);
var currentLine = new ArrayList<>(List.of(start));
var out = new ArrayList<FormattedCharSequence>();
for (int i = 0; i < listTag.size(); i++) {
Tag subtag = listTag.get(i);
var cSubtag = HexUtils.downcast(subtag, CompoundTag.TYPE);
var translation = HexIotaTypes.getDisplay(cSubtag);
var currentElement = translation.getVisualOrderText();
String addl;
if (i < listTag.size() - 1) {
addl = ", ";
} else {
// Last go-around, so add the closing bracket
addl = "]";
}
currentElement = FormattedCharSequence.composite(currentElement,
FormattedCharSequence.forward(addl, Style.EMPTY.withColor(ChatFormatting.DARK_PURPLE)));
var width = font.width(currentElement);
if (cursor + width > maxWidth) {
out.add(FormattedCharSequence.composite(currentLine));
currentLine = new ArrayList<>();
// Indent further lines by two spaces
var indentation = FormattedCharSequence.forward(" ", Style.EMPTY);
var lineStart = FormattedCharSequence.composite(indentation, currentElement);
currentLine.add(lineStart);
cursor = font.width(lineStart);
} else {
currentLine.add(currentElement);
cursor += width;
}
}
if (!currentLine.isEmpty()) {
out.add(FormattedCharSequence.composite(currentLine));
}
return out;
}
@Override
public int color() {
return 0xff_aa00aa;

View file

@ -1,4 +1,5 @@
@file:JvmName("RenderLib")
package at.petrak.hexcasting.client
import at.petrak.hexcasting.api.mod.HexConfig
@ -205,8 +206,16 @@ fun makeZappy(points: List<Vec2>, hops: Int, variance: Float, speed: Float, flow
// (We use i, j (segment #, subsegment #) as seeds for the Perlin noise,
// and zSeed (i.e. time elapsed) to perturb the shape gradually over time)
val minorPerturb = NOISE.getValue(i.toDouble(), j.toDouble(), sin(zSeed)) * flowIrregular
val theta = (3 * NOISE.getValue(i.toDouble() + j.toDouble() / (hops + 1) + minorPerturb - zSeed, 1337.0, 0.0) * TAU).toFloat()
val r = (NOISE.getValue(i.toDouble() + j.toDouble() / (hops + 1) - zSeed, 69420.0, 0.0) * maxVariance * scaleVariance(progress)).toFloat()
val theta = (3 * NOISE.getValue(
i.toDouble() + j.toDouble() / (hops + 1) + minorPerturb - zSeed,
1337.0,
0.0
) * TAU).toFloat()
val r = (NOISE.getValue(
i.toDouble() + j.toDouble() / (hops + 1) - zSeed,
69420.0,
0.0
) * maxVariance * scaleVariance(progress)).toFloat()
val randomHop = Vec2(r * Mth.cos(theta), r * Mth.sin(theta))
// Then record the new location.
zappyPts.add(pos.add(randomHop))
@ -339,3 +348,28 @@ fun renderEntity(
immediate.endBatch()
ms.popPose()
}
/**
* Make sure you have the `PositionColorShader` set
*/
fun renderQuad(
ps: PoseStack, x: Float, y: Float, w: Float, h: Float, color: Int
) {
val mat = ps.last().pose()
val tess = Tesselator.getInstance()
val buf = tess.builder
buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR)
buf.vertex(mat, x, y, 0f)
.color(color)
.endVertex()
buf.vertex(mat, x, y + h, 0f)
.color(color)
.endVertex()
buf.vertex(mat, x + w, y + h, 0f)
.color(color)
.endVertex()
buf.vertex(mat, x + w, y, 0f)
.color(color)
.endVertex()
tess.end()
}

View file

@ -9,11 +9,15 @@ import at.petrak.hexcasting.api.spell.math.HexCoord
import at.petrak.hexcasting.api.spell.math.HexDir
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import at.petrak.hexcasting.api.utils.gold
import at.petrak.hexcasting.api.utils.otherHand
import at.petrak.hexcasting.client.ClientTickCounter
import at.petrak.hexcasting.client.drawPatternFromPoints
import at.petrak.hexcasting.client.drawSpot
import at.petrak.hexcasting.client.renderQuad
import at.petrak.hexcasting.client.sound.GridSoundInstance
import at.petrak.hexcasting.common.items.ItemSpellbook
import at.petrak.hexcasting.common.lib.HexIotaTypes
import at.petrak.hexcasting.common.lib.HexItems
import at.petrak.hexcasting.common.lib.HexSounds
import at.petrak.hexcasting.common.network.MsgNewSpellPatternSyn
@ -25,20 +29,26 @@ import net.minecraft.client.Minecraft
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
import net.minecraft.nbt.CompoundTag
import net.minecraft.sounds.SoundSource
import net.minecraft.util.FormattedCharSequence
import net.minecraft.util.Mth
import net.minecraft.world.InteractionHand
import net.minecraft.world.phys.Vec2
import kotlin.math.atan2
import kotlin.math.roundToInt
import kotlin.math.sqrt
import kotlin.math.*
class GuiSpellcasting(
class GuiSpellcasting constructor(
private val handOpenedWith: InteractionHand,
private var patterns: MutableList<ResolvedPattern>,
private var stackDescs: List<Component>
private var cachedStack: List<CompoundTag>,
private var cachedParens: List<CompoundTag>,
private var cachedRavenmind: CompoundTag?,
private var parenCount: Int,
) : Screen("gui.hexcasting.spellcasting".asTranslatedComponent) {
private var stackDescs: List<FormattedCharSequence> = listOf()
private var parenDescs: List<FormattedCharSequence> = listOf()
private var ravenmind: List<FormattedCharSequence>? = null
private var drawState: PatternDrawState = PatternDrawState.BetweenPatterns
private val usedSpots: MutableSet<HexCoord> = HashSet()
@ -48,16 +58,17 @@ class GuiSpellcasting(
for ((pattern, origin) in patterns) {
this.usedSpots.addAll(pattern.positions(origin))
}
this.calculateIotaDisplays()
}
fun recvServerUpdate(info: ControllerInfo) {
this.stackDescs = info.stackDesc
this.patterns.lastOrNull()?.let {
it.type = info.resolutionType
}
val mc = Minecraft.getInstance()
if (info.resolutionType.success) {
Minecraft.getInstance().soundManager.play(
mc.soundManager.play(
SimpleSoundInstance(
HexSounds.ADD_PATTERN,
SoundSource.PLAYERS,
@ -69,6 +80,34 @@ class GuiSpellcasting(
)
)
}
this.cachedStack = info.stack
this.cachedParens = info.parenthesized
this.cachedRavenmind = info.ravenmind
this.parenCount = info.parenCount
this.calculateIotaDisplays()
}
fun calculateIotaDisplays() {
val mc = Minecraft.getInstance()
val width = (this.width * LHS_IOTAS_ALLOCATION).toInt()
this.stackDescs =
this.cachedStack.flatMap { HexIotaTypes.getDisplayWithMaxWidth(it, width, mc.font).asReversed() }
.asReversed()
this.parenDescs = if (this.cachedParens.isNotEmpty())
this.cachedParens.flatMap { HexIotaTypes.getDisplayWithMaxWidth(it, width, mc.font) }
else if (this.parenCount > 0)
listOf("...".gold.visualOrderText)
else
emptyList()
this.ravenmind =
this.cachedRavenmind?.let {
HexIotaTypes.getDisplayWithMaxWidth(
it,
(this.width * RHS_IOTAS_ALLOCATION).toInt(),
mc.font
)
}
}
override fun init() {
@ -80,6 +119,8 @@ class GuiSpellcasting(
this.ambianceSoundInstance = GridSoundInstance(player)
soundManager.play(this.ambianceSoundInstance!!)
}
this.calculateIotaDisplays()
}
override fun tick() {
@ -251,13 +292,13 @@ class GuiSpellcasting(
super.onClose()
}
override fun render(poseStack: PoseStack, pMouseX: Int, pMouseY: Int, pPartialTick: Float) {
super.render(poseStack, pMouseX, pMouseY, pPartialTick)
override fun render(ps: PoseStack, pMouseX: Int, pMouseY: Int, pPartialTick: Float) {
super.render(ps, pMouseX, pMouseY, pPartialTick)
this.ambianceSoundInstance?.mousePosX = pMouseX / this.width.toDouble()
this.ambianceSoundInstance?.mousePosY = pMouseX / this.width.toDouble()
val mat = poseStack.last().pose()
val mat = ps.last().pose()
val prevShader = RenderSystem.getShader()
RenderSystem.setShader(GameRenderer::getPositionColorShader)
RenderSystem.disableDepthTest()
@ -326,15 +367,73 @@ class GuiSpellcasting(
drawPatternFromPoints(mat, points, false, 0xff_64c8ff_u.toInt(), 0xff_fecbe6_u.toInt(), 0.1f)
}
RenderSystem.setShader { prevShader }
RenderSystem.enableDepthTest()
val mc = Minecraft.getInstance()
val font = mc.font
for ((i, s) in this.stackDescs.withIndex()) {
val offsetIdx = this.stackDescs.size - i - 1
font.draw(poseStack, s, 10f, 10f + 9f * offsetIdx, -1)
ps.pushPose()
ps.translate(10.0, 10.0, 0.0)
if (this.parenCount > 0) {
val boxHeight = (this.parenDescs.size + 1f) * 10f
RenderSystem.setShader(GameRenderer::getPositionColorShader)
RenderSystem.defaultBlendFunc()
drawBox(ps, 0f, 0f, (this.width * LHS_IOTAS_ALLOCATION + 5).toFloat(), boxHeight, 7.5f)
ps.translate(0.0, 0.0, 1.0)
val time = ClientTickCounter.getTotal() * 0.8f
val opacity = (Mth.map(cos(time), -1f, 1f, 200f, 255f)).toInt()
val color = 0x00_ffffff or (opacity shl 24)
RenderSystem.setShader { prevShader }
for (desc in this.parenDescs) {
font.draw(ps, desc, 10f, 7f, color)
ps.translate(0.0, 10.0, 0.0)
}
ps.translate(0.0, 15.0, 0.0)
}
if (this.stackDescs.isNotEmpty()) {
val boxHeight = (this.stackDescs.size + 1f) * 10f
RenderSystem.setShader(GameRenderer::getPositionColorShader)
RenderSystem.enableBlend()
drawBox(ps, 0f, 0f, (this.width * LHS_IOTAS_ALLOCATION + 5).toFloat(), boxHeight)
ps.translate(0.0, 0.0, 1.0)
RenderSystem.setShader { prevShader }
for (desc in this.stackDescs) {
font.draw(ps, desc, 5f, 7f, -1)
ps.translate(0.0, 10.0, 0.0)
}
}
ps.popPose()
if (!this.ravenmind.isNullOrEmpty()) {
val kotlinBad = this.ravenmind!!
ps.pushPose()
ps.translate(this.width * 0.8, 10.0, 0.0)
val boxHeight = (kotlinBad.size + 0.5f) * 10f
val addlScale = 1.5f
RenderSystem.setShader(GameRenderer::getPositionColorShader)
RenderSystem.enableBlend()
drawBox(
ps, 0f, 0f,
((this.width * RHS_IOTAS_ALLOCATION + 5) * addlScale).toFloat(), boxHeight * addlScale,
)
ps.translate(5.0, 5.0, 1.0)
ps.scale(addlScale, addlScale, 1f)
val time = ClientTickCounter.getTotal() * 0.42f
val opacity = (Mth.map(sin(time), -1f, 1f, 150f, 255f)).toInt()
val color = 0x00_ffffff or (opacity shl 24)
RenderSystem.setShader { prevShader }
for (desc in kotlinBad) {
font.draw(ps, desc, 0f, 0f, color)
ps.translate(0.0, 10.0, 0.0)
}
ps.popPose()
}
RenderSystem.setShader { prevShader }
}
// why the hell is this default true
@ -369,4 +468,16 @@ class GuiSpellcasting(
/** We've started drawing a pattern for real. */
data class Drawing(val start: HexCoord, var current: HexCoord, val wipPattern: HexPattern) : PatternDrawState()
}
companion object {
const val LHS_IOTAS_ALLOCATION = 0.7
const val RHS_IOTAS_ALLOCATION = 0.1
fun drawBox(ps: PoseStack, x: Float, y: Float, w: Float, h: Float, leftMargin: Float = 2.5f) {
RenderSystem.setShader(GameRenderer::getPositionColorShader)
RenderSystem.enableBlend()
renderQuad(ps, x, y, w, h, 0x50_303030)
renderQuad(ps, x + leftMargin, y + 2.5f, w - leftMargin - 2.5f, h - 5f, 0x50_303030)
}
}
}

View file

@ -12,7 +12,7 @@ object OpEval : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
val instrs = stack.getList(stack.lastIndex)
@ -29,6 +29,6 @@ object OpEval : Action {
}
val frame = ContinuationFrame.Evaluate(instrs)
return OperationResult(newCont.pushFrame(frame), stack, local, listOf())
return OperationResult(newCont.pushFrame(frame), stack, ravenmind, listOf())
}
}

View file

@ -1,13 +1,18 @@
package at.petrak.hexcasting.common.casting.operators.eval
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.Action
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.iota.Iota
object OpEvalDelay : Action {
override fun operate(continuation: SpellContinuation, stack: MutableList<Iota>, local: Iota, ctx: CastingContext): OperationResult {
return OperationResult(continuation, stack, local, listOf())
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
return OperationResult(continuation, stack, ravenmind, listOf())
}
}

View file

@ -13,7 +13,7 @@ object OpForEach : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
if (stack.size < 2)
@ -29,7 +29,7 @@ object OpForEach : Action {
return OperationResult(
continuation.pushFrame(frame),
stack,
local,
ravenmind,
listOf()
)
}

View file

@ -1,16 +1,16 @@
package at.petrak.hexcasting.common.casting.operators.eval
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.Action
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.iota.Iota
object OpHalt : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
var newStack = stack.toList()
@ -29,6 +29,6 @@ object OpHalt : Action {
newStack = listOf()
}
return OperationResult(newCont, newStack, local, listOf())
return OperationResult(newCont, newStack, ravenmind, listOf())
}
}

View file

@ -13,7 +13,7 @@ object OpLastNToList : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
if (stack.isEmpty())
@ -28,6 +28,6 @@ object OpLastNToList : Action {
}
stack.addAll(output.asActionResult)
return OperationResult(continuation, stack, local, listOf())
return OperationResult(continuation, stack, ravenmind, listOf())
}
}

View file

@ -1,19 +1,20 @@
package at.petrak.hexcasting.common.casting.operators.local
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.Action
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.orNull
object OpPeekLocal : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
stack.add(local)
return OperationResult(continuation, stack, local, listOf())
stack.add(ravenmind.orNull())
return OperationResult(continuation, stack, ravenmind, listOf())
}
}

View file

@ -1,17 +1,17 @@
package at.petrak.hexcasting.common.casting.operators.local
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.Action
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
object OpPushLocal : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
if (stack.isEmpty())

View file

@ -15,7 +15,7 @@ object OpPrint : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
if (stack.isEmpty()) {
@ -23,7 +23,7 @@ object OpPrint : Action {
}
val datum = stack[stack.lastIndex]
return OperationResult(
continuation, stack, local, listOf(
continuation, stack, ravenmind, listOf(
OperatorSideEffect.AttemptSpell(Spell(datum), hasCastingSound = false, awardStat = false)
)
)

View file

@ -14,7 +14,7 @@ object OpAlwinfyHasAscendedToABeingOfPureMath : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
if (stack.isEmpty())
@ -48,7 +48,7 @@ object OpAlwinfyHasAscendedToABeingOfPureMath : Action {
return OperationResult(
continuation,
stack,
local,
ravenmind,
listOf()
)
}

View file

@ -14,7 +14,7 @@ object OpBitMask : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
if (stack.size < 1)
@ -38,6 +38,6 @@ object OpBitMask : Action {
}
}
return OperationResult(continuation, out.asReversed(), local, listOf())
return OperationResult(continuation, out.asReversed(), ravenmind, listOf())
}
}

View file

@ -12,7 +12,7 @@ object OpFisherman : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
if (stack.size < 2)
@ -23,6 +23,6 @@ object OpFisherman : Action {
val fish = stack.removeAt(stack.size - 1 - depth)
stack.add(fish)
return OperationResult(continuation, stack, local, listOf())
return OperationResult(continuation, stack, ravenmind, listOf())
}
}

View file

@ -11,10 +11,10 @@ object OpStackSize : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
stack.add(DoubleIota(stack.size.toDouble()))
return OperationResult(continuation, stack, local, listOf())
return OperationResult(continuation, stack, ravenmind, listOf())
}
}

View file

@ -35,9 +35,11 @@ public class ItemStaff extends Item {
if (!world.isClientSide() && player instanceof ServerPlayer serverPlayer) {
var harness = IXplatAbstractions.INSTANCE.getHarness(serverPlayer, hand);
var patterns = IXplatAbstractions.INSTANCE.getPatterns(serverPlayer);
var descs = harness.generateDescs();
IXplatAbstractions.INSTANCE.sendPacketToPlayer(serverPlayer,
new MsgOpenSpellGuiAck(hand, patterns, harness.generateDescs()));
new MsgOpenSpellGuiAck(hand, patterns, descs.getFirst(), descs.getSecond(), descs.getThird(),
harness.getParenCount()));
}
player.awardStat(Stats.ITEM_USED.get(this));

View file

@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.spell.iota.*;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.gui.Font;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
@ -12,9 +13,11 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.FormattedCharSequence;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.*;
import java.util.function.BiConsumer;
@ -23,6 +26,7 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Stores the registry for iota types, some utility methods, and all the types Hexcasting itself defines.
*/
@ParametersAreNonnullByDefault
public class HexIotaTypes {
public static final Registry<IotaType<?>> REGISTRY = IXplatAbstractions.INSTANCE.getIotaTypeRegistry();
public static final String
@ -141,6 +145,18 @@ public class HexIotaTypes {
return type.display(data);
}
public static List<FormattedCharSequence> getDisplayWithMaxWidth(CompoundTag tag, int maxWidth, Font font) {
var type = getTypeFromTag(tag);
if (type == null) {
return List.of();
}
var data = tag.get(KEY_DATA);
if (data == null) {
return List.of();
}
return type.displayWithWidth(data, maxWidth, font);
}
public static int getColor(CompoundTag tag) {
var type = getTypeFromTag(tag);
if (type == null) {

View file

@ -7,10 +7,9 @@ import at.petrak.hexcasting.common.lib.HexSounds;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import java.util.ArrayList;
import java.util.Optional;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
@ -31,14 +30,15 @@ public record MsgNewSpellPatternAck(ControllerInfo info) implements IMessage {
var wasSpellCast = buf.readBoolean();
var isStackEmpty = buf.readBoolean();
var resolutionType = buf.readEnum(ResolvedPatternType.class);
var descsLen = buf.readInt();
var desc = new ArrayList<Component>(descsLen);
for (int i = 0; i < descsLen; i++) {
desc.add(buf.readComponent());
}
var stack = buf.readList(FriendlyByteBuf::readNbt);
var parens = buf.readList(FriendlyByteBuf::readNbt);
var raven = buf.readOptional(FriendlyByteBuf::readNbt).orElse(null);
var parenCount = buf.readVarInt();
return new MsgNewSpellPatternAck(
new ControllerInfo(wasSpellCast, isStackEmpty, resolutionType, desc)
new ControllerInfo(wasSpellCast, isStackEmpty, resolutionType, stack, parens, raven, parenCount)
);
}
@ -47,10 +47,12 @@ public record MsgNewSpellPatternAck(ControllerInfo info) implements IMessage {
buf.writeBoolean(this.info.getMakesCastSound());
buf.writeBoolean(this.info.isStackClear());
buf.writeEnum(this.info.getResolutionType());
buf.writeInt(this.info.getStackDesc().size());
for (var desc : this.info.getStackDesc()) {
buf.writeComponent(desc);
}
buf.writeCollection(this.info.getStack(), FriendlyByteBuf::writeNbt);
buf.writeCollection(this.info.getParenthesized(), FriendlyByteBuf::writeNbt);
buf.writeOptional(Optional.ofNullable(this.info.getRavenmind()), FriendlyByteBuf::writeNbt);
buf.writeVarInt(this.info.getParenCount());
}
public static void handle(MsgNewSpellPatternAck self) {

View file

@ -25,7 +25,7 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent client->server when the player finishes drawing a pattern.
* Server will send back a MsgNewSpellPatternAck packet
* Server will send back a {@link MsgNewSpellPatternAck} packet
*/
public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern,
List<ResolvedPattern> resolvedPatterns)
@ -40,12 +40,12 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
public static MsgNewSpellPatternSyn deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var hand = buf.readEnum(InteractionHand.class);
var pattern = HexPattern.fromNBT(buf.readAnySizeNbt());
var pattern = HexPattern.fromNBT(buf.readNbt());
var resolvedPatternsLen = buf.readInt();
var resolvedPatterns = new ArrayList<ResolvedPattern>(resolvedPatternsLen);
for (int i = 0; i < resolvedPatternsLen; i++) {
resolvedPatterns.add(ResolvedPattern.fromNBT(buf.readAnySizeNbt()));
resolvedPatterns.add(ResolvedPattern.fromNBT(buf.readNbt()));
}
return new MsgNewSpellPatternSyn(hand, pattern, resolvedPatterns);
}
@ -86,8 +86,9 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
ControllerInfo clientInfo;
if (autoFail) {
var descs = harness.generateDescs();
clientInfo = new ControllerInfo(false, harness.getStack().isEmpty(), ResolvedPatternType.INVALID,
harness.generateDescs());
descs.getFirst(), descs.getSecond(), descs.getThird(), harness.getParenCount());
} else {
clientInfo = harness.executeIota(new PatternIota(this.pattern), sender.getLevel());

View file

@ -4,12 +4,11 @@ import at.petrak.hexcasting.api.spell.casting.ResolvedPattern;
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import java.util.ArrayList;
import java.util.List;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
@ -17,7 +16,12 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client when the player opens the spell gui to request the server provide the current stack.
*/
public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> patterns, List<Component> components)
public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> patterns,
List<CompoundTag> stack,
List<CompoundTag> parenthesized,
CompoundTag ravenmind,
int parenCount
)
implements IMessage {
public static final ResourceLocation ID = modLoc("cgui");
@ -31,33 +35,27 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
var hand = buf.readEnum(InteractionHand.class);
var patternsLen = buf.readInt();
var patterns = new ArrayList<ResolvedPattern>(patternsLen);
for (int i = 0; i < patternsLen; i++) {
patterns.add(ResolvedPattern.fromNBT(buf.readAnySizeNbt()));
}
var patterns = buf.readList(fbb -> ResolvedPattern.fromNBT(fbb.readAnySizeNbt()));
var descsLen = buf.readInt();
var desc = new ArrayList<Component>(descsLen);
for (int i = 0; i < descsLen; i++) {
desc.add(buf.readComponent());
}
var stack = buf.readList(FriendlyByteBuf::readNbt);
var parens = buf.readList(FriendlyByteBuf::readNbt);
var raven = buf.readAnySizeNbt();
return new MsgOpenSpellGuiAck(hand, patterns, desc);
var parenCount = buf.readVarInt();
return new MsgOpenSpellGuiAck(hand, patterns, stack, parens, raven, parenCount);
}
public void serialize(FriendlyByteBuf buf) {
buf.writeEnum(this.hand);
buf.writeInt(this.patterns.size());
for (var pattern : this.patterns) {
buf.writeNbt(pattern.serializeToNBT());
}
buf.writeCollection(this.patterns, (fbb, pat) -> fbb.writeNbt(pat.serializeToNBT()));
buf.writeInt(this.components.size());
for (var desc : this.components) {
buf.writeComponent(desc);
}
buf.writeCollection(this.stack, FriendlyByteBuf::writeNbt);
buf.writeCollection(this.parenthesized, FriendlyByteBuf::writeNbt);
buf.writeNbt(this.ravenmind);
buf.writeVarInt(this.parenCount);
}
public static void handle(MsgOpenSpellGuiAck msg) {
@ -65,7 +63,9 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
@Override
public void run() {
var mc = Minecraft.getInstance();
mc.setScreen(new GuiSpellcasting(msg.hand(), msg.patterns(), msg.components()));
mc.setScreen(
new GuiSpellcasting(msg.hand(), msg.patterns(), msg.stack, msg.parenthesized, msg.ravenmind,
msg.parenCount));
}
});
}

View file

@ -1,14 +1,14 @@
{
"item.hexcasting.book": "Hex Notebook",
"item.hexcasting.wand_oak": "Oak Staff",
"item.hexcasting.wand_spruce": "Spruce Staff",
"item.hexcasting.wand_birch": "Birch Staff",
"item.hexcasting.wand_jungle": "Jungle Staff",
"item.hexcasting.wand_acacia": "Acacia Staff",
"item.hexcasting.wand_dark_oak": "Dark Oak Staff",
"item.hexcasting.wand_crimson": "Crimson Staff",
"item.hexcasting.wand_warped": "Warped Staff",
"item.hexcasting.wand_akashic": "Edified Staff",
"item.hexcasting.oak_staff": "Oak Staff",
"item.hexcasting.spruce_staff": "Spruce Staff",
"item.hexcasting.birch_staff": "Birch Staff",
"item.hexcasting.jungle_staff": "Jungle Staff",
"item.hexcasting.acacia_staff": "Acacia Staff",
"item.hexcasting.dark_oak_staff": "Dark Oak Staff",
"item.hexcasting.crimson_staff": "Crimson Staff",
"item.hexcasting.warped_staff": "Warped Staff",
"item.hexcasting.edified_staff": "Edified Staff",
"item.hexcasting.focus": "Focus",
"item.hexcasting.focus.sealed": "Sealed Focus",
"item.hexcasting.spellbook": "Spellbook",