some reorganizing, add item models, add more operators

This commit is contained in:
gamma-delta 2021-12-27 17:08:57 -06:00
parent e84858c95a
commit 19467a18bb
54 changed files with 663 additions and 259 deletions

View file

@ -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;

View file

@ -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)

View file

@ -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"
}
}

View file

@ -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)
}

View file

@ -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)
}
}

View file

@ -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())
}
}

View file

@ -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)
}

View file

@ -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
)
}
}
}

View 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;
}
}));
}
}

View file

@ -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())
)

View 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}"
}
}

View file

@ -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

View file

@ -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

View file

@ -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)
}
}

View file

@ -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)
}

View file

@ -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<*>) {

View file

@ -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)

View 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)
}

View file

@ -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
}
)
}

View file

@ -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
}
)
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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())
}
}

View file

@ -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
}
)
}

View file

@ -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)
}

View file

@ -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)
}
}

View file

@ -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()
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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
)
}
}

View file

@ -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();

View 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);
}
}

View file

@ -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));

View 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";
}

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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));
}
}
});

View file

@ -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;

View 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()));
}
}

View file

@ -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;

View file

@ -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/

View file

@ -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;

View file

@ -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()))

View file

@ -1,5 +0,0 @@
package at.petrak.hex.lib;
public class LibItemNames {
public static final String WAND = "wand";
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B