some reorganizing, add item models, add more operators
|
@ -1,8 +1,8 @@
|
|||
package at.petrak.hex;
|
||||
|
||||
import at.petrak.hex.client.HexRenderOverlays;
|
||||
import at.petrak.hex.items.HexItems;
|
||||
import at.petrak.hex.network.HexMessages;
|
||||
import at.petrak.hex.common.items.HexItems;
|
||||
import at.petrak.hex.common.network.HexMessages;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
|
|
|
@ -3,6 +3,6 @@ Hello, intrepid Github reader!
|
|||
The "flavor text" words for things in this mod and the internal names are different. (Sorry.)
|
||||
|
||||
- A "Hex" is a `Spell`, cast through a [`CastingHarness`](casting/CastingHarness.kt)
|
||||
- A "Pattern" is a [`HexPattern`](hexes/HexPattern.kt)
|
||||
- An "Action" is a [`SpellOperator`](casting/operators/SpellOperator.kt)
|
||||
- A "Pattern" is a [`HexPattern`](hexmath/HexPattern.kt)
|
||||
- An "Action" is a [`SpellOperator`](casting/SpellOperator.kt)
|
||||
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
package at.petrak.hex.casting
|
||||
|
||||
import at.petrak.hex.hexes.HexPattern
|
||||
|
||||
class CastException(val reason: Reason, vararg val data: Any) : Exception() {
|
||||
enum class Reason {
|
||||
// Compilation
|
||||
/**
|
||||
* We couldn't match this pattern to an operator.
|
||||
*
|
||||
* `pattern: HexPattern`
|
||||
*/
|
||||
INVALID_PATTERN,
|
||||
|
||||
/**
|
||||
* Completely invalid type for spellcasting.
|
||||
* If you're seeing this error I messed up really bad
|
||||
*
|
||||
* `perpetrator: Any`
|
||||
*/
|
||||
INVALID_TYPE,
|
||||
|
||||
// Pre-execution
|
||||
/**
|
||||
* When executing an operator we expected a different type.
|
||||
*
|
||||
* `expected: Class<*>, got: Any`
|
||||
*/
|
||||
OP_WRONG_TYPE,
|
||||
|
||||
/**
|
||||
* We need at least this much on the stack to cast the spell but only got this much.
|
||||
*
|
||||
* `requiredArgc: Int, gotArgc: Int`
|
||||
*/
|
||||
NOT_ENOUGH_ARGS,
|
||||
|
||||
// Execution
|
||||
/**
|
||||
* Tried to interact with a vector that was too far away
|
||||
*
|
||||
* `<no args>`
|
||||
*/
|
||||
TOO_FAR,
|
||||
}
|
||||
|
||||
override val message: String
|
||||
get() = when (this.reason) {
|
||||
Reason.INVALID_PATTERN -> "could not match pattern to operator: ${this.data[0] as HexPattern}"
|
||||
Reason.INVALID_TYPE -> "cannot use ${this.data[0]} as a SpellDatum (type ${this.data[0].javaClass.typeName})"
|
||||
Reason.OP_WRONG_TYPE -> "operator expected ${(this.data[0] as Class<*>).typeName} but got ${this.data[1]} (type ${this.data[1].javaClass.typeName})"
|
||||
Reason.NOT_ENOUGH_ARGS -> "required at least ${this.data[0] as Int} args on the stack but only had ${this.data[1] as Int}"
|
||||
Reason.TOO_FAR -> "tried to interact with something too far away"
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package at.petrak.hex.casting
|
||||
|
||||
/**
|
||||
* The result of a spell being cast.
|
||||
*
|
||||
* A "spell" is just a [SpellOperator] that returns a [RenderedSpell],
|
||||
* Once a spell stack has nothing on it but a single [RenderedSpell], the casting is nearly successful.
|
||||
* If the caster has enough mana to cast the [RenderedSpell], it is cast! (Otherwise, we might cast from
|
||||
* hitpoints or just kill the caster.)
|
||||
*/
|
||||
fun interface RenderedSpell {
|
||||
fun cast(ctx: CastingContext)
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package at.petrak.hex.casting.operators
|
||||
|
||||
import at.petrak.hex.casting.CastingContext
|
||||
import at.petrak.hex.casting.SpellDatum
|
||||
import at.petrak.hex.casting.operators.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.casting.operators.SpellOperator.Companion.spellListOf
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpEntityLook : SpellOperator {
|
||||
override val argc = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val e: Entity = args.getChecked(0)
|
||||
return spellListOf(e.lookAngle)
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package at.petrak.hex.casting.operators
|
||||
|
||||
import at.petrak.hex.casting.CastingContext
|
||||
import at.petrak.hex.casting.SpellDatum
|
||||
import at.petrak.hex.casting.operators.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.casting.operators.SpellOperator.Companion.spellListOf
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpEntityPos : SpellOperator {
|
||||
override val argc = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val e: Entity = args.getChecked(0)
|
||||
return spellListOf(e.position())
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package at.petrak.hex.casting.operators
|
||||
|
||||
import at.petrak.hex.casting.CastingContext
|
||||
import at.petrak.hex.casting.SpellDatum
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpGetCaster : SpellOperator {
|
||||
override val argc = 0
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =
|
||||
SpellOperator.spellListOf(ctx.caster as Entity)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package at.petrak.hex.casting.operators.spells
|
||||
|
||||
import at.petrak.hex.casting.CastingContext
|
||||
import at.petrak.hex.casting.RenderedSpell
|
||||
import at.petrak.hex.casting.SpellDatum
|
||||
import at.petrak.hex.casting.operators.SpellOperator
|
||||
import at.petrak.hex.casting.operators.SpellOperator.Companion.spellListOf
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.network.chat.TextComponent
|
||||
|
||||
object OpPrint : SpellOperator {
|
||||
override val argc = 1
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
return spellListOf(Spell(args[0]))
|
||||
}
|
||||
|
||||
class Spell(private val datum: SpellDatum<*>) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
ctx.caster.sendMessage(
|
||||
TextComponent(if (datum.payload is Unit) "null" else datum.payload.toString()),
|
||||
Util.NIL_UUID
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
36
src/main/java/at/petrak/hex/client/RegisterClientStuff.java
Normal file
|
@ -0,0 +1,36 @@
|
|||
package at.petrak.hex.client;
|
||||
|
||||
import at.petrak.hex.HexMod;
|
||||
import at.petrak.hex.common.casting.SpellDatum;
|
||||
import at.petrak.hex.common.items.HexItems;
|
||||
import at.petrak.hex.common.items.ItemFocus;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = HexMod.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public class RegisterClientStuff {
|
||||
@SubscribeEvent
|
||||
public static void init(final FMLClientSetupEvent evt) {
|
||||
evt.enqueueWork(() -> ItemProperties.register(HexItems.FOCUS.get(), ItemFocus.PREDICATE,
|
||||
(stack, level, holder, holderID) -> {
|
||||
if (stack.hasTag()) {
|
||||
var tagname = stack.getTag().getAllKeys().iterator().next();
|
||||
return switch (tagname) {
|
||||
case SpellDatum.TAG_ENTITY -> 1f;
|
||||
case SpellDatum.TAG_DOUBLE -> 2f;
|
||||
case SpellDatum.TAG_VEC3 -> 3f;
|
||||
case SpellDatum.TAG_SPELL -> 4f;
|
||||
case SpellDatum.TAG_WIDGET -> 5f;
|
||||
case SpellDatum.TAG_LIST -> 6f;
|
||||
case SpellDatum.TAG_PATTERN -> 7f;
|
||||
default -> 0f; // uh oh
|
||||
};
|
||||
} else {
|
||||
return 0f;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -2,12 +2,10 @@ package at.petrak.hex.client.gui
|
|||
|
||||
import at.petrak.hex.HexMod
|
||||
import at.petrak.hex.HexUtils.TAU
|
||||
import at.petrak.hex.hexes.HexCoord
|
||||
import at.petrak.hex.hexes.HexDir
|
||||
import at.petrak.hex.hexes.HexPattern
|
||||
import at.petrak.hex.network.HexMessages
|
||||
import at.petrak.hex.network.MsgNewSpellPatternSyn
|
||||
import at.petrak.hex.network.MsgQuitSpellcasting
|
||||
import at.petrak.hex.hexmath.HexAngle
|
||||
import at.petrak.hex.hexmath.HexCoord
|
||||
import at.petrak.hex.hexmath.HexDir
|
||||
import at.petrak.hex.hexmath.HexPattern
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
|
@ -18,12 +16,15 @@ import net.minecraft.client.Minecraft
|
|||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.renderer.GameRenderer
|
||||
import net.minecraft.network.chat.TextComponent
|
||||
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
|
||||
|
||||
class GuiSpellcasting : Screen(TextComponent("")) {
|
||||
const val SQRT_3 = 1.7320508f
|
||||
|
||||
class GuiSpellcasting(private val handOpenedWith: InteractionHand) : Screen(TextComponent("")) {
|
||||
private var patterns: MutableList<Pair<HexPattern, Vec2>> = mutableListOf()
|
||||
private var drawState: PatternDrawState = PatternDrawState.BetweenPatterns
|
||||
|
||||
|
@ -72,7 +73,7 @@ class GuiSpellcasting : Screen(TextComponent("")) {
|
|||
val success = ds.wipPattern.tryAppendDir(newdir)
|
||||
if (success) {
|
||||
ds.current = idealNextLoc
|
||||
HexMod.LOGGER.info("Added to pattern: ${ds.wipPattern} ; New current pos: (${ds.current.x}, ${ds.current.y})")
|
||||
HexMod.LOGGER.info("Added to pattern: ${ds.wipPattern}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +101,12 @@ class GuiSpellcasting : Screen(TextComponent("")) {
|
|||
this.drawState = PatternDrawState.BetweenPatterns
|
||||
this.patterns.add(Pair(pat, start))
|
||||
|
||||
HexMessages.getNetwork().sendToServer(MsgNewSpellPatternSyn(0, pat))
|
||||
at.petrak.hex.common.network.HexMessages.getNetwork().sendToServer(
|
||||
at.petrak.hex.common.network.MsgNewSpellPatternSyn(
|
||||
this.handOpenedWith,
|
||||
pat
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +114,8 @@ class GuiSpellcasting : Screen(TextComponent("")) {
|
|||
}
|
||||
|
||||
override fun onClose() {
|
||||
HexMessages.getNetwork().sendToServer(MsgQuitSpellcasting())
|
||||
at.petrak.hex.common.network.HexMessages.getNetwork()
|
||||
.sendToServer(at.petrak.hex.common.network.MsgQuitSpellcasting())
|
||||
|
||||
super.onClose()
|
||||
}
|
||||
|
@ -116,47 +123,109 @@ class GuiSpellcasting : Screen(TextComponent("")) {
|
|||
override fun render(poseStack: PoseStack, pMouseX: Int, pMouseY: Int, pPartialTick: Float) {
|
||||
super.render(poseStack, pMouseX, pMouseY, pPartialTick)
|
||||
|
||||
fun drawLineSeq(mat: Matrix4f, points: List<Vec2>, color: Int) {
|
||||
fun drawLineSeq(mat: Matrix4f, points: List<Vec2>, r: Int, g: Int, b: Int, a: Int) {
|
||||
// they spell it wrong at mojang lmao
|
||||
val tess = Tesselator.getInstance()
|
||||
val buf = tess.builder
|
||||
buf.begin(VertexFormat.Mode.LINE_STRIP, DefaultVertexFormat.POSITION_COLOR)
|
||||
for (pos in points) {
|
||||
buf.vertex(mat, pos.x, pos.y, 0f).color(color).endVertex()
|
||||
|
||||
for ((p1, p2) in points.zipWithNext()) {
|
||||
// https://github.com/not-fl3/macroquad/blob/master/src/shapes.rs#L163
|
||||
// GuiComponent::innerFill line 52
|
||||
// fedor have useful variable names challenge (99% can't beat)
|
||||
val dx = p2.x - p1.x
|
||||
val dy = p2.y - p1.y
|
||||
// normal x and y, presumably?
|
||||
val nx = -dy
|
||||
val ny = dx
|
||||
val width = 1.5f
|
||||
// thickness?
|
||||
val tlen = Mth.sqrt(nx * nx + ny * ny) / (width * 0.5f)
|
||||
val tx = nx / tlen
|
||||
val ty = ny / tlen
|
||||
|
||||
buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR)
|
||||
buf.vertex(mat, p1.x + tx, p1.y + ty, 0f).color(r, g, b, a).endVertex()
|
||||
buf.vertex(mat, p2.x + tx, p2.y + ty, 0f).color(r, g, b, a).endVertex()
|
||||
buf.vertex(mat, p2.x - tx, p2.y - ty, 0f).color(r, g, b, a).endVertex()
|
||||
buf.vertex(mat, p1.x - tx, p1.y - ty, 0f).color(r, g, b, a).endVertex()
|
||||
|
||||
tess.end()
|
||||
}
|
||||
}
|
||||
|
||||
fun drawSpot(mat: Matrix4f, point: Vec2, r: Int, g: Int, b: Int, a: Int) {
|
||||
val tess = Tesselator.getInstance()
|
||||
val buf = tess.builder
|
||||
// https://stackoverflow.com/questions/20394727/gl-triangle-strip-vs-gl-triangle-fan
|
||||
// Starting point is the center
|
||||
buf.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_COLOR)
|
||||
buf.vertex(mat, point.x, point.y, 0f).color(r, g, b, a).endVertex()
|
||||
|
||||
// https://github.com/not-fl3/macroquad/blob/master/src/shapes.rs#L98
|
||||
val fracOfCircle = 32
|
||||
val radius = 1.0f
|
||||
// run 0 AND last; this way the circle closes
|
||||
for (i in 0..32) {
|
||||
val theta = i.toFloat() / fracOfCircle * TAU.toFloat()
|
||||
val rx = Mth.cos(theta) * radius + point.x
|
||||
val ry = Mth.sin(theta) * radius + point.y
|
||||
buf.vertex(mat, rx, ry, 0f).color(r, g, b, a).endVertex()
|
||||
}
|
||||
|
||||
tess.end()
|
||||
}
|
||||
|
||||
val mat = poseStack.last().pose()
|
||||
|
||||
val posColorShader = GameRenderer.getPositionColorShader()
|
||||
val prevShader = RenderSystem.getShader()
|
||||
RenderSystem.setShader { posColorShader }
|
||||
RenderSystem.setShader(GameRenderer::getPositionColorShader)
|
||||
RenderSystem.disableDepthTest()
|
||||
RenderSystem.disableCull()
|
||||
|
||||
for ((pat, origin) in this.patterns) {
|
||||
drawLineSeq(mat, pat.positions().map { pos -> this.coordToPx(pos, origin) }, 0xaaaaff)
|
||||
drawLineSeq(mat, pat.positions().map { pos -> this.coordToPx(pos, origin) }, 127, 127, 255, 200)
|
||||
}
|
||||
|
||||
// Now draw the currently WIP pattern
|
||||
if (this.drawState !is PatternDrawState.BetweenPatterns) {
|
||||
val points = mutableListOf<Vec2>()
|
||||
|
||||
if (this.drawState is PatternDrawState.JustStarted) {
|
||||
val (dirs, spotAnchor) = if (this.drawState is PatternDrawState.JustStarted) {
|
||||
val ds = this.drawState as PatternDrawState.JustStarted
|
||||
points.add(ds.start)
|
||||
Pair(HexDir.values().toList(), ds.start)
|
||||
} else if (this.drawState is PatternDrawState.Drawing) {
|
||||
val ds = this.drawState as PatternDrawState.Drawing
|
||||
for (pos in ds.wipPattern.positions()) {
|
||||
val pix = this.coordToPx(pos, ds.start)
|
||||
points.add(pix)
|
||||
}
|
||||
val finalDir = ds.wipPattern.finalDir()
|
||||
Pair(
|
||||
HexAngle.values().flatMap {
|
||||
if (it == HexAngle.BACK) {
|
||||
emptyList()
|
||||
} else {
|
||||
listOf(finalDir * it)
|
||||
}
|
||||
},
|
||||
ds.current
|
||||
)
|
||||
} else {
|
||||
throw NotImplementedError("unreachable")
|
||||
}
|
||||
|
||||
points.add(Vec2(pMouseX.toFloat(), pMouseY.toFloat()))
|
||||
drawLineSeq(mat, points, 0xccccff)
|
||||
drawLineSeq(mat, points, 200, 200, 255, 255)
|
||||
|
||||
for (dir in dirs) {
|
||||
val pos = this.coordToPx(dir.asDelta(), spotAnchor)
|
||||
drawSpot(mat, pos, 200, 200, 230, 255)
|
||||
}
|
||||
}
|
||||
|
||||
RenderSystem.setShader { prevShader }
|
||||
RenderSystem.enableDepthTest()
|
||||
}
|
||||
|
||||
// why the hell is this default true
|
||||
|
@ -166,10 +235,11 @@ class GuiSpellcasting : Screen(TextComponent("")) {
|
|||
fun hexSize(): Float =
|
||||
this.width.toFloat() / 32.0f
|
||||
|
||||
|
||||
fun coordToPx(coord: HexCoord, origin: Vec2) =
|
||||
origin.add(
|
||||
Vec2(
|
||||
sqrt(3.0f) * coord.q.toFloat() + sqrt(3.0f) / 2.0f * coord.r.toFloat(),
|
||||
SQRT_3 * coord.q.toFloat() + SQRT_3 / 2.0f * coord.r.toFloat(),
|
||||
1.5f * coord.r.toFloat()
|
||||
).scale(this.hexSize())
|
||||
)
|
||||
|
|
56
src/main/java/at/petrak/hex/common/casting/CastException.kt
Normal file
|
@ -0,0 +1,56 @@
|
|||
package at.petrak.hex.common.casting
|
||||
|
||||
import at.petrak.hex.hexmath.HexPattern
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class CastException(val reason: at.petrak.hex.common.casting.CastException.Reason, vararg val data: Any) : Exception() {
|
||||
enum class Reason {
|
||||
// Compilation
|
||||
/**
|
||||
* We couldn't match this pattern to an operator.
|
||||
*
|
||||
* `pattern: HexPattern`
|
||||
*/
|
||||
INVALID_PATTERN,
|
||||
|
||||
/**
|
||||
* Completely invalid type for spellcasting.
|
||||
* If you're seeing this error I messed up really bad
|
||||
*
|
||||
* `perpetrator: Any`
|
||||
*/
|
||||
INVALID_TYPE,
|
||||
|
||||
// Pre-execution
|
||||
/**
|
||||
* When executing an operator we expected a different type.
|
||||
*
|
||||
* `expected: Class<*>, got: Any`
|
||||
*/
|
||||
OP_WRONG_TYPE,
|
||||
|
||||
/**
|
||||
* We need at least this much on the stack to cast the spell but only got this much.
|
||||
*
|
||||
* `requiredArgc: Int, gotArgc: Int`
|
||||
*/
|
||||
NOT_ENOUGH_ARGS,
|
||||
|
||||
// Execution
|
||||
/**
|
||||
* Tried to interact with a vector that was too far away
|
||||
*
|
||||
* `vec: Vec3`
|
||||
*/
|
||||
TOO_FAR,
|
||||
}
|
||||
|
||||
override val message: String
|
||||
get() = when (this.reason) {
|
||||
at.petrak.hex.common.casting.CastException.Reason.INVALID_PATTERN -> "could not match pattern to operator: ${this.data[0] as HexPattern}"
|
||||
at.petrak.hex.common.casting.CastException.Reason.INVALID_TYPE -> "cannot use ${this.data[0]} as a SpellDatum (type ${this.data[0].javaClass.typeName})"
|
||||
at.petrak.hex.common.casting.CastException.Reason.OP_WRONG_TYPE -> "operator expected ${(this.data[0] as Class<*>).typeName} but got ${this.data[1]} (type ${this.data[1].javaClass.typeName})"
|
||||
at.petrak.hex.common.casting.CastException.Reason.NOT_ENOUGH_ARGS -> "required at least ${this.data[0] as Int} args on the stack but only had ${this.data[1] as Int}"
|
||||
at.petrak.hex.common.casting.CastException.Reason.TOO_FAR -> "tried to interact with something too far away at ${this.data[0] as Vec3}"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.casting
|
||||
package at.petrak.hex.common.casting
|
||||
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
|
@ -1,8 +1,7 @@
|
|||
package at.petrak.hex.casting
|
||||
package at.petrak.hex.common.casting
|
||||
|
||||
import at.petrak.hex.HexMod
|
||||
import at.petrak.hex.casting.operators.SpellOperator
|
||||
import at.petrak.hex.hexes.HexPattern
|
||||
import at.petrak.hex.hexmath.HexPattern
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.Tag
|
||||
|
@ -24,17 +23,8 @@ class CastingHarness private constructor(
|
|||
return try {
|
||||
val operator = SpellOperator.fromPattern(newPat)
|
||||
HexMod.LOGGER.info("Executing operator: $operator")
|
||||
// now execute the operator
|
||||
if (operator.argc > this.stack.size)
|
||||
throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, operator.argc, this.stack.size)
|
||||
val args = this.stack.takeLast(operator.argc)
|
||||
// there's gotta be a better way to do this
|
||||
for (_idx in 0 until operator.argc)
|
||||
this.stack.removeLast()
|
||||
val newData = operator.execute(args, this.ctx)
|
||||
this.stack.addAll(newData)
|
||||
|
||||
HexMod.LOGGER.info("Added new data to stack: ${this.stack}")
|
||||
operator.modifyStack(this.stack, this.ctx)
|
||||
HexMod.LOGGER.info("Modified stack: ${this.stack}")
|
||||
|
||||
if (this.stack.isEmpty()) {
|
||||
return CastResult.QuitCasting
|
|
@ -0,0 +1,8 @@
|
|||
package at.petrak.hex.common.casting
|
||||
|
||||
@JvmRecord
|
||||
data class RenderedSpell(val spell: RenderedSpellImpl, val args: List<SpellDatum<*>>) {
|
||||
fun cast(ctx: CastingContext) {
|
||||
this.spell.cast(args, ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package at.petrak.hex.common.casting
|
||||
|
||||
/**
|
||||
* The implementation of a spellcast.
|
||||
*
|
||||
* A "spell" is just a [SpellOperator] that returns a [RenderedSpellImpl],
|
||||
* Once a spell stack has nothing on it but a single [RenderedSpellImpl], the casting is nearly successful.
|
||||
* If the caster has enough mana to cast the [RenderedSpellImpl], it is cast! (Otherwise, we might cast from
|
||||
* hitpoints or just kill the caster.)
|
||||
*
|
||||
* This contains just the code for what to do. Due to *ahem* issues with Java, we can't have an abstract
|
||||
* constructor from NBT. So, we encode a RenderedSpell as a string key plus a list of SpellDatum arguments.
|
||||
* When a [RenderedSpellImpl] is successfully returned, we stick it inside a RenderedSpell with its data.
|
||||
* The implementation is responsible for re-de-serializing the data out of the list and casting the spell.
|
||||
*/
|
||||
interface RenderedSpellImpl {
|
||||
fun cast(args: List<SpellDatum<*>>, ctx: CastingContext)
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
package at.petrak.hex.casting
|
||||
package at.petrak.hex.common.casting
|
||||
|
||||
import at.petrak.hex.HexUtils
|
||||
import at.petrak.hex.HexUtils.serializeToNBT
|
||||
import at.petrak.hex.common.casting.operators.spells.OpPrint
|
||||
import at.petrak.hex.hexmath.HexPattern
|
||||
import net.minecraft.nbt.*
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
@ -14,11 +16,12 @@ import net.minecraft.world.phys.Vec3
|
|||
* * [Double]
|
||||
* * [Vec3][net.minecraft.world.phys.Vec3] as both position and (when normalized) direction
|
||||
* * [RenderedSpell]
|
||||
* * [Unit] as our type-safe null
|
||||
* * [SpellWidget]; [SpellWidget.NULL] is used as our null value
|
||||
* * [ArrayList<SpellDatum<*>>][ArrayList]
|
||||
* * [HexPattern]! Yes, we have meta-evaluation everyone.
|
||||
* The constructor guarantees we won't pass a type that isn't one of those types.
|
||||
*
|
||||
* Please do not access the `payload` field directly (use [tryGet])
|
||||
*
|
||||
*/
|
||||
class SpellDatum<T : Any> private constructor(val payload: T) {
|
||||
val clazz: Class<T> = payload.javaClass
|
||||
|
@ -46,16 +49,30 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
is Vec3 -> out.put(
|
||||
TAG_VEC3, pl.serializeToNBT()
|
||||
)
|
||||
// use an empty list as unit? works for me i guess
|
||||
// it doesn't really matter what we put
|
||||
is Unit -> out.put(TAG_UNIT, ListTag())
|
||||
|
||||
is ArrayList<*> -> {
|
||||
val subtag = ListTag()
|
||||
for (elt in pl)
|
||||
subtag.add((elt as SpellDatum<*>).serializeToNBT())
|
||||
out.put(TAG_LIST, subtag)
|
||||
}
|
||||
is RenderedSpell -> {
|
||||
val subtag = CompoundTag()
|
||||
val implerKey = SpellImpls.entries.firstOrNull { it.value == pl.spell }?.key
|
||||
?: throw RuntimeException("cannot find a string key for ${this.payload}")
|
||||
subtag.putString(TAG_SPELL_NAME, implerKey)
|
||||
|
||||
val argsTag = ListTag()
|
||||
for (arg in pl.args) {
|
||||
argsTag.add(arg.serializeToNBT())
|
||||
}
|
||||
subtag.put(TAG_SPELL_ARGS, argsTag)
|
||||
}
|
||||
is SpellWidget -> {
|
||||
out.putString(TAG_WIDGET, pl.name)
|
||||
}
|
||||
is HexPattern -> {
|
||||
out.put(TAG_PATTERN, pl.serializeToNBT())
|
||||
}
|
||||
else -> throw RuntimeException("cannot serialize $pl because it is of type ${pl.javaClass.canonicalName} which is not serializable")
|
||||
}
|
||||
|
||||
|
@ -77,6 +94,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
SpellDatum(payload)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun DeserializeFromNBT(nbt: CompoundTag, ctx: CastingContext): SpellDatum<*> {
|
||||
val keys = nbt.allKeys
|
||||
if (keys.size != 1)
|
||||
|
@ -87,11 +105,10 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
val uuid = nbt.getUUID(key)
|
||||
val entity = ctx.world.getEntity(uuid)
|
||||
// If the entity died or something return Unit
|
||||
SpellDatum(if (entity == null || !entity.isAlive) Unit else entity)
|
||||
SpellDatum(if (entity == null || !entity.isAlive) SpellWidget.NULL else entity)
|
||||
}
|
||||
TAG_DOUBLE -> SpellDatum(nbt.getDouble(key))
|
||||
TAG_VEC3 -> SpellDatum(HexUtils.deserializeVec3FromNBT(nbt.getLongArray(key)))
|
||||
TAG_UNIT -> SpellDatum(Unit)
|
||||
TAG_LIST -> {
|
||||
val arr = nbt.getList(key, Tag.TAG_COMPOUND.toInt())
|
||||
val out = ArrayList<SpellDatum<*>>(arr.size)
|
||||
|
@ -101,24 +118,54 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
}
|
||||
SpellDatum(out)
|
||||
}
|
||||
TAG_SPELL -> {
|
||||
val spellTag = nbt.getCompound(key)
|
||||
val implerName = spellTag.getString(TAG_SPELL_NAME)
|
||||
val impler = SpellImpls.getOrElse(implerName) {
|
||||
throw IllegalStateException("did not have an impl for $implerName")
|
||||
}
|
||||
val argsTag = spellTag.getList(TAG_SPELL_ARGS, Tag.TAG_COMPOUND.toInt())
|
||||
val args = ArrayList<SpellDatum<*>>(argsTag.size)
|
||||
for (subtag in argsTag) {
|
||||
// this is safe because otherwise we wouldn't have been able to get the list before
|
||||
args.add(DeserializeFromNBT(subtag as CompoundTag, ctx))
|
||||
}
|
||||
SpellDatum(RenderedSpell(impler, args))
|
||||
}
|
||||
TAG_WIDGET -> {
|
||||
SpellDatum(SpellWidget.valueOf(nbt.getString(key)))
|
||||
}
|
||||
TAG_PATTERN -> {
|
||||
SpellDatum(HexPattern.DeserializeFromNBT(nbt.getCompound(TAG_PATTERN)))
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown key $key: $nbt")
|
||||
}
|
||||
}
|
||||
|
||||
// Maps the class to the tag name
|
||||
// Set of legal types to go in a spell
|
||||
val ValidTypes: Set<Class<*>> = setOf(
|
||||
Entity::class.java,
|
||||
Double::class.java,
|
||||
Vec3::class.java,
|
||||
RenderedSpell::class.java,
|
||||
Unit::class.java,
|
||||
ArrayList::class.java,
|
||||
SpellWidget::class.java,
|
||||
PatternList::class.java,
|
||||
)
|
||||
|
||||
// Mapping of string keys for spell implers to their implementation
|
||||
val SpellImpls: Map<String, RenderedSpellImpl> = mapOf(
|
||||
"print" to OpPrint
|
||||
)
|
||||
const val TAG_ENTITY = "entity"
|
||||
const val TAG_DOUBLE = "double"
|
||||
const val TAG_VEC3 = "vec3"
|
||||
const val TAG_UNIT = "unit"
|
||||
const val TAG_LIST = "list"
|
||||
const val TAG_SPELL = "spell"
|
||||
const val TAG_SPELL_NAME = "impler"
|
||||
const val TAG_SPELL_ARGS = "args"
|
||||
const val TAG_WIDGET = "widget"
|
||||
const val TAG_PATTERN = "pattern"
|
||||
|
||||
fun <T : Any> IsValidType(checkee: T): Boolean =
|
||||
if (checkee is ArrayList<*>) {
|
|
@ -1,27 +1,28 @@
|
|||
package at.petrak.hex.casting.operators
|
||||
package at.petrak.hex.common.casting
|
||||
|
||||
import at.petrak.hex.casting.CastException
|
||||
import at.petrak.hex.casting.CastingContext
|
||||
import at.petrak.hex.casting.SpellDatum
|
||||
import at.petrak.hex.casting.operators.spells.OpPrint
|
||||
import at.petrak.hex.hexes.HexPattern
|
||||
import at.petrak.hex.common.casting.operators.*
|
||||
import at.petrak.hex.common.casting.operators.spells.OpExplode
|
||||
import at.petrak.hex.common.casting.operators.spells.OpPrint
|
||||
import at.petrak.hex.hexmath.HexPattern
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Manipulates the stack in some way, usually by popping some number of values off the stack
|
||||
* and pushing one new value.
|
||||
* For a more "traditional" pop arguments, push return experience, see
|
||||
* [SimpleOperator][at.petrak.hex.common.casting.operators.SimpleOperator]
|
||||
*
|
||||
* Implementors MUST NOT mutate the stack or the context.
|
||||
* Implementors MUST NOT mutate the context.
|
||||
*/
|
||||
interface SpellOperator {
|
||||
val argc: Int
|
||||
val manaCost: Int
|
||||
get() = 0
|
||||
|
||||
fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>>
|
||||
fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext)
|
||||
|
||||
companion object {
|
||||
val PatternMap: Map<String, SpellOperator> = mapOf(
|
||||
// == Getters ==
|
||||
// diamond shape to get the caster
|
||||
"qaq" to OpGetCaster,
|
||||
"ede" to OpGetCaster,
|
||||
|
@ -39,11 +40,23 @@ interface SpellOperator {
|
|||
// CCW diamond mace thing for entity raycast
|
||||
"weaqa" to OpEntityRaycast,
|
||||
|
||||
// ===
|
||||
// == Modify Stack ==
|
||||
// CCW hook for undo
|
||||
"a" to OpUndo,
|
||||
// and CW for null
|
||||
"d" to SpellWidget.NULL,
|
||||
// Two triangles holding hands to duplicate
|
||||
"aadaa" to OpDuplicate,
|
||||
// Two opposing triangles to swap
|
||||
"aawdd" to OpSwap,
|
||||
|
||||
// == Spells ==
|
||||
|
||||
// hook for debug
|
||||
"de" to OpPrint,
|
||||
"aq" to OpPrint,
|
||||
// nuclear sign for explosion
|
||||
"aawaawaa" to OpExplode,
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -74,6 +87,23 @@ interface SpellOperator {
|
|||
casted
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value at the given index is OK. Will throw an error otherwise.
|
||||
*/
|
||||
@JvmStatic
|
||||
inline fun <reified T : Any> List<SpellDatum<*>>.assertChecked(idx: Int) {
|
||||
this.getChecked<T>(idx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the vector is in range of the player.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun assertVecInRange(vec: Vec3, ctx: CastingContext) {
|
||||
if (vec.distanceToSqr(ctx.caster.position()) > MAX_DISTANCE * MAX_DISTANCE)
|
||||
throw CastException(CastException.Reason.TOO_FAR, vec)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun spellListOf(vararg vs: Any): List<SpellDatum<*>> {
|
||||
val out = ArrayList<SpellDatum<*>>(vs.size)
|
20
src/main/java/at/petrak/hex/common/casting/SpellWidget.kt
Normal file
|
@ -0,0 +1,20 @@
|
|||
package at.petrak.hex.common.casting
|
||||
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.spellListOf
|
||||
import at.petrak.hex.common.casting.operators.SimpleOperator
|
||||
|
||||
/**
|
||||
* Miscellaneous spell datums used as markers, etc.
|
||||
*
|
||||
* They act as operators that push themselves.
|
||||
*/
|
||||
enum class SpellWidget : SimpleOperator {
|
||||
NULL,
|
||||
OPEN_PAREN, CLOSE_PAREN, ESCAPE;
|
||||
|
||||
override val argc: Int
|
||||
get() = 0
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =
|
||||
spellListOf(this)
|
||||
}
|
|
@ -1,13 +1,15 @@
|
|||
package at.petrak.hex.casting.operators
|
||||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.casting.CastingContext
|
||||
import at.petrak.hex.casting.SpellDatum
|
||||
import at.petrak.hex.casting.operators.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellWidget
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpBlockAxisRaycast : SpellOperator {
|
||||
object OpBlockAxisRaycast : SimpleOperator {
|
||||
override val argc = 2
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val origin: Vec3 = args.getChecked(0)
|
||||
|
@ -27,7 +29,7 @@ object OpBlockAxisRaycast : SpellOperator {
|
|||
if (blockHitResult.type == HitResult.Type.BLOCK) {
|
||||
Vec3(blockHitResult.direction.step())
|
||||
} else {
|
||||
Unit
|
||||
SpellWidget.NULL
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,13 +1,15 @@
|
|||
package at.petrak.hex.casting.operators
|
||||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.casting.CastingContext
|
||||
import at.petrak.hex.casting.SpellDatum
|
||||
import at.petrak.hex.casting.operators.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellWidget
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpBlockRaycast : SpellOperator {
|
||||
object OpBlockRaycast : SimpleOperator {
|
||||
override val argc = 2
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val origin: Vec3 = args.getChecked(0)
|
||||
|
@ -27,7 +29,7 @@ object OpBlockRaycast : SpellOperator {
|
|||
if (blockHitResult.type == HitResult.Type.BLOCK) {
|
||||
blockHitResult.location
|
||||
} else {
|
||||
Unit
|
||||
SpellWidget.NULL
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.spellListOf
|
||||
|
||||
object OpDuplicate : SimpleOperator {
|
||||
override val argc: Int
|
||||
get() = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val datum = args.getChecked<Any>(0)
|
||||
return spellListOf(datum, datum)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.spellListOf
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpEntityLook : SimpleOperator {
|
||||
override val argc = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val e: Entity = args.getChecked(0)
|
||||
return spellListOf(e.lookAngle)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.spellListOf
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpEntityPos : SimpleOperator {
|
||||
override val argc = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val e: Entity = args.getChecked(0)
|
||||
return spellListOf(e.position())
|
||||
}
|
||||
}
|
|
@ -1,14 +1,16 @@
|
|||
package at.petrak.hex.casting.operators
|
||||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.casting.CastingContext
|
||||
import at.petrak.hex.casting.SpellDatum
|
||||
import at.petrak.hex.casting.operators.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellWidget
|
||||
import net.minecraft.world.entity.projectile.ProjectileUtil
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpEntityRaycast : SpellOperator {
|
||||
object OpEntityRaycast : SimpleOperator {
|
||||
override val argc = 2
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val origin: Vec3 = args.getChecked(0)
|
||||
|
@ -27,7 +29,7 @@ object OpEntityRaycast : SpellOperator {
|
|||
if (entityHitResult != null && entityHitResult.type == HitResult.Type.ENTITY) {
|
||||
entityHitResult.entity
|
||||
} else {
|
||||
Unit
|
||||
SpellWidget.NULL
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpGetCaster : SimpleOperator {
|
||||
override val argc = 0
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =
|
||||
SpellOperator.spellListOf(ctx.caster as Entity)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.spellListOf
|
||||
|
||||
object OpSwap : SimpleOperator {
|
||||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val a = args.getChecked<Any>(0)
|
||||
val b = args.getChecked<Any>(1)
|
||||
return spellListOf(b, a)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
|
||||
object OpUndo : SimpleOperator {
|
||||
override val argc = 1
|
||||
|
||||
// Do literally nothing!
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =
|
||||
emptyList()
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.common.casting.CastException
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator
|
||||
|
||||
/**
|
||||
* An operator that acts in the expected method of popping some arguments
|
||||
* and pushing some more arguments.
|
||||
*/
|
||||
interface SimpleOperator : SpellOperator {
|
||||
val argc: Int
|
||||
fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>>
|
||||
|
||||
override fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext) {
|
||||
if (this.argc > stack.size)
|
||||
throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, this.argc, stack.size)
|
||||
val args = stack.takeLast(this.argc)
|
||||
// there's gotta be a better way to do this
|
||||
for (_idx in 0 until this.argc)
|
||||
stack.removeLast()
|
||||
val newData = this.execute(args, ctx)
|
||||
stack.addAll(newData)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package at.petrak.hex.common.casting.operators.spells
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.RenderedSpell
|
||||
import at.petrak.hex.common.casting.RenderedSpellImpl
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.assertVecInRange
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.spellListOf
|
||||
import at.petrak.hex.common.casting.operators.SimpleOperator
|
||||
import net.minecraft.world.level.Explosion
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpExplode : SimpleOperator, RenderedSpellImpl {
|
||||
override val argc: Int
|
||||
get() = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val pos = args.getChecked<Vec3>(0)
|
||||
assertVecInRange(pos, ctx)
|
||||
return spellListOf(RenderedSpell(OpExplode, spellListOf(pos)))
|
||||
}
|
||||
|
||||
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {
|
||||
val pos = args.getChecked<Vec3>(0)
|
||||
|
||||
// 4.0 is the strength of TNT, i guess
|
||||
ctx.world.explode(ctx.caster, pos.x, pos.y, pos.z, 4.0f, Explosion.BlockInteraction.BREAK)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package at.petrak.hex.common.casting.operators.spells
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.RenderedSpell
|
||||
import at.petrak.hex.common.casting.RenderedSpellImpl
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.spellListOf
|
||||
import at.petrak.hex.common.casting.operators.SimpleOperator
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.network.chat.TextComponent
|
||||
|
||||
object OpPrint : SimpleOperator, RenderedSpellImpl {
|
||||
override val argc = 1
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val datum = args.getChecked<Any>(0)
|
||||
return spellListOf(RenderedSpell(OpPrint, spellListOf(datum)))
|
||||
}
|
||||
|
||||
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {
|
||||
val datum = args[0]
|
||||
ctx.caster.sendMessage(
|
||||
TextComponent(datum.payload.toString()),
|
||||
Util.NIL_UUID
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package at.petrak.hex.items;
|
||||
package at.petrak.hex.common.items;
|
||||
|
||||
import at.petrak.hex.HexMod;
|
||||
import at.petrak.hex.lib.LibItemNames;
|
||||
import at.petrak.hex.common.lib.LibItemNames;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
@ -10,8 +10,10 @@ import net.minecraftforge.registries.RegistryObject;
|
|||
public class HexItems {
|
||||
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, HexMod.MOD_ID);
|
||||
|
||||
public static final RegistryObject<Item> wand = ITEMS.register(LibItemNames.WAND,
|
||||
public static final RegistryObject<Item> WAND = ITEMS.register(LibItemNames.WAND,
|
||||
() -> new ItemWand(unstackable()));
|
||||
public static final RegistryObject<Item> FOCUS = ITEMS.register(LibItemNames.FOCUS,
|
||||
() -> new ItemFocus(props()));
|
||||
|
||||
public static Item.Properties props() {
|
||||
return new Item.Properties();
|
13
src/main/java/at/petrak/hex/common/items/ItemFocus.java
Normal file
|
@ -0,0 +1,13 @@
|
|||
package at.petrak.hex.common.items;
|
||||
|
||||
import at.petrak.hex.HexMod;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
public class ItemFocus extends Item {
|
||||
public static final ResourceLocation PREDICATE = new ResourceLocation(HexMod.MOD_ID, "datatype");
|
||||
|
||||
public ItemFocus(Properties pProperties) {
|
||||
super(pProperties);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.items;
|
||||
package at.petrak.hex.common.items;
|
||||
|
||||
import at.petrak.hex.client.gui.GuiSpellcasting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -17,7 +17,7 @@ public class ItemWand extends Item {
|
|||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||
if (world.isClientSide()) {
|
||||
Minecraft.getInstance().setScreen(new GuiSpellcasting());
|
||||
Minecraft.getInstance().setScreen(new GuiSpellcasting(hand));
|
||||
}
|
||||
|
||||
return InteractionResultHolder.success(player.getItemInHand(hand));
|
6
src/main/java/at/petrak/hex/common/lib/LibItemNames.java
Normal file
|
@ -0,0 +1,6 @@
|
|||
package at.petrak.hex.common.lib;
|
||||
|
||||
public class LibItemNames {
|
||||
public static final String WAND = "wand";
|
||||
public static final String FOCUS = "focus";
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.lib;
|
||||
package at.petrak.hex.common.lib;
|
||||
|
||||
import at.petrak.hex.HexMod;
|
||||
import net.minecraft.resources.ResourceLocation;
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.network;
|
||||
package at.petrak.hex.common.network;
|
||||
|
||||
import at.petrak.hex.HexMod;
|
||||
import net.minecraft.resources.ResourceLocation;
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.network;
|
||||
package at.petrak.hex.common.network;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -13,17 +13,15 @@ import java.util.function.Supplier;
|
|||
/**
|
||||
* Sent server->client when the player finishes casting a spell.
|
||||
*/
|
||||
public record MsgNewSpellPatternAck(int windowID, boolean quitCasting) {
|
||||
public record MsgNewSpellPatternAck(boolean quitCasting) {
|
||||
public static MsgNewSpellPatternAck deserialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
var windowID = buf.readInt();
|
||||
var quitCasting = buf.readBoolean();
|
||||
return new MsgNewSpellPatternAck(windowID, quitCasting);
|
||||
return new MsgNewSpellPatternAck(quitCasting);
|
||||
}
|
||||
|
||||
public void serialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
buf.writeInt(this.windowID);
|
||||
buf.writeBoolean(this.quitCasting);
|
||||
}
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
package at.petrak.hex.network;
|
||||
package at.petrak.hex.common.network;
|
||||
|
||||
import at.petrak.hex.casting.CastingContext;
|
||||
import at.petrak.hex.casting.CastingHarness;
|
||||
import at.petrak.hex.casting.CastingHarness.CastResult;
|
||||
import at.petrak.hex.hexes.HexPattern;
|
||||
import at.petrak.hex.items.ItemWand;
|
||||
import at.petrak.hex.common.casting.CastingContext;
|
||||
import at.petrak.hex.common.casting.CastingHarness;
|
||||
import at.petrak.hex.common.casting.CastingHarness.CastResult;
|
||||
import at.petrak.hex.common.items.ItemWand;
|
||||
import at.petrak.hex.hexmath.HexPattern;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
|
||||
|
@ -20,20 +21,17 @@ import java.util.function.Supplier;
|
|||
* Sent client->server when the player finishes drawing a pattern.
|
||||
* Server will send back a MsgNewSpellPatternAck packet
|
||||
*/
|
||||
public record MsgNewSpellPatternSyn(int windowID, HexPattern pattern) {
|
||||
// Not actually sure if you can check for window ID
|
||||
// but i'll leave space for it just in case
|
||||
|
||||
public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern) {
|
||||
public static MsgNewSpellPatternSyn deserialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
var windowID = buf.readInt();
|
||||
var hand = InteractionHand.values()[buf.readInt()];
|
||||
var pattern = HexPattern.DeserializeFromNBT(buf.readAnySizeNbt());
|
||||
return new MsgNewSpellPatternSyn(windowID, pattern);
|
||||
return new MsgNewSpellPatternSyn(hand, pattern);
|
||||
}
|
||||
|
||||
public void serialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
buf.writeInt(this.windowID);
|
||||
buf.writeInt(this.handUsed.ordinal());
|
||||
buf.writeNbt(this.pattern.serializeToNBT());
|
||||
}
|
||||
|
||||
|
@ -41,17 +39,15 @@ public record MsgNewSpellPatternSyn(int windowID, HexPattern pattern) {
|
|||
ctx.get().enqueueWork(() -> {
|
||||
ServerPlayer sender = ctx.get().getSender();
|
||||
if (sender != null) {
|
||||
var held = sender.getMainHandItem();
|
||||
var held = sender.getItemInHand(this.handUsed);
|
||||
if (held.getItem() instanceof ItemWand) {
|
||||
var tag = held.getOrCreateTag();
|
||||
var harness = CastingHarness.DeserializeFromNBT(tag, sender);
|
||||
|
||||
var res = harness.update(this.pattern);
|
||||
if (res instanceof CastResult.Success) {
|
||||
CastResult.Success success = (CastResult.Success) res;
|
||||
if (res instanceof CastResult.Success success) {
|
||||
success.getSpell().cast(new CastingContext(sender));
|
||||
} else if (res instanceof CastResult.Error) {
|
||||
CastResult.Error error = (CastResult.Error) res;
|
||||
} else if (res instanceof CastResult.Error error) {
|
||||
sender.sendMessage(new TextComponent(error.getExn().toString()), Util.NIL_UUID);
|
||||
}
|
||||
|
||||
|
@ -67,7 +63,7 @@ public record MsgNewSpellPatternSyn(int windowID, HexPattern pattern) {
|
|||
}
|
||||
HexMessages.getNetwork()
|
||||
.send(PacketDistributor.PLAYER.with(() -> sender),
|
||||
new MsgNewSpellPatternAck(this.windowID, quit));
|
||||
new MsgNewSpellPatternAck(quit));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hex.network;
|
||||
package at.petrak.hex.common.network;
|
||||
|
||||
import at.petrak.hex.items.ItemWand;
|
||||
import at.petrak.hex.common.items.ItemWand;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
72
src/main/java/at/petrak/hex/datagen/ItemModelProvider.java
Normal file
|
@ -0,0 +1,72 @@
|
|||
package at.petrak.hex.datagen;
|
||||
|
||||
import at.petrak.hex.HexMod;
|
||||
import at.petrak.hex.common.items.HexItems;
|
||||
import at.petrak.hex.common.items.ItemFocus;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.client.model.generators.ModelFile;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
|
||||
public class ItemModelProvider extends net.minecraftforge.client.model.generators.ItemModelProvider {
|
||||
public ItemModelProvider(DataGenerator generator, ExistingFileHelper existingFileHelper) {
|
||||
super(generator, HexMod.MOD_ID, existingFileHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerModels() {
|
||||
simpleItem(HexItems.WAND.get());
|
||||
|
||||
simpleItem(modLoc("focus_none"));
|
||||
simpleItem(modLoc("focus_entity"));
|
||||
simpleItem(modLoc("focus_double"));
|
||||
simpleItem(modLoc("focus_vec3"));
|
||||
simpleItem(modLoc("focus_spell"));
|
||||
simpleItem(modLoc("focus_widget"));
|
||||
simpleItem(modLoc("focus_list"));
|
||||
simpleItem(modLoc("focus_patterns"));
|
||||
getBuilder(HexItems.FOCUS.get().getRegistryName().getPath())
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, -0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_none")))
|
||||
.end()
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, 1 - 0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_entity")))
|
||||
.end()
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, 2 - 0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_double")))
|
||||
.end()
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, 3 - 0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_vec3")))
|
||||
.end()
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, 4 - 0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_spell")))
|
||||
.end()
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, 5 - 0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_widget")))
|
||||
.end()
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, 6 - 0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_list")))
|
||||
.end()
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, 7 - 0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_patterns")))
|
||||
.end();
|
||||
}
|
||||
|
||||
public void simpleItem(Item item) {
|
||||
simpleItem(item.getRegistryName());
|
||||
}
|
||||
|
||||
public void simpleItem(ResourceLocation path) {
|
||||
singleTexture(path.getPath(), new ResourceLocation("item/handheld"),
|
||||
"layer0", new ResourceLocation(HexMod.MOD_ID, "item/" + path.getPath()));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.hexes
|
||||
package at.petrak.hex.hexmath
|
||||
|
||||
enum class HexAngle {
|
||||
FORWARD, RIGHT, RIGHT_BACK, BACK, LEFT_BACK, LEFT;
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.hexes
|
||||
package at.petrak.hex.hexmath
|
||||
|
||||
/**
|
||||
* Uses axial coordinates as per https://www.redblobgames.com/grids/hexagons/
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.hexes
|
||||
package at.petrak.hex.hexmath
|
||||
|
||||
enum class HexDir {
|
||||
NORTH_EAST, EAST, SOUTH_EAST, SOUTH_WEST, WEST, NORTH_WEST;
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hex.hexes
|
||||
package at.petrak.hex.hexmath
|
||||
|
||||
import net.minecraft.nbt.ByteArrayTag
|
||||
import net.minecraft.nbt.ByteTag
|
||||
|
@ -25,6 +25,7 @@ data class HexPattern(val startDir: HexDir, val angles: MutableList<HexAngle> =
|
|||
cursor += compass
|
||||
compass *= a
|
||||
}
|
||||
cursor += compass
|
||||
|
||||
val potentialNewLine = Pair(cursor, newDir)
|
||||
if (potentialNewLine in linesSeen) return false
|
||||
|
@ -50,6 +51,10 @@ data class HexPattern(val startDir: HexDir, val angles: MutableList<HexAngle> =
|
|||
return out
|
||||
}
|
||||
|
||||
fun finalDir(): HexDir =
|
||||
this.angles.fold(this.startDir) { acc, angle -> acc * angle }
|
||||
|
||||
|
||||
fun serializeToNBT(): CompoundTag {
|
||||
val out = CompoundTag()
|
||||
out.put(TAG_START_DIR, ByteTag.valueOf(this.startDir.ordinal.toByte()))
|
|
@ -1,5 +0,0 @@
|
|||
package at.petrak.hex.lib;
|
||||
|
||||
public class LibItemNames {
|
||||
public static final String WAND = "wand";
|
||||
}
|
BIN
src/main/resources/assets/hex/textures/focus_double.png
Normal file
After Width: | Height: | Size: 421 B |
BIN
src/main/resources/assets/hex/textures/focus_empty.png
Normal file
After Width: | Height: | Size: 413 B |
BIN
src/main/resources/assets/hex/textures/focus_entity.png
Normal file
After Width: | Height: | Size: 414 B |
BIN
src/main/resources/assets/hex/textures/focus_list.png
Normal file
After Width: | Height: | Size: 429 B |
BIN
src/main/resources/assets/hex/textures/focus_patterns.png
Normal file
After Width: | Height: | Size: 416 B |
BIN
src/main/resources/assets/hex/textures/focus_spell.png
Normal file
After Width: | Height: | Size: 420 B |
BIN
src/main/resources/assets/hex/textures/focus_vec3.png
Normal file
After Width: | Height: | Size: 420 B |
BIN
src/main/resources/assets/hex/textures/focus_widget.png
Normal file
After Width: | Height: | Size: 421 B |
BIN
src/main/resources/assets/hex/textures/wand.png
Normal file
After Width: | Height: | Size: 261 B |