some resources and encapsulating patterns themselves as spelldata
11
src/generated/resources/.cache/cache
Normal file
|
@ -0,0 +1,11 @@
|
|||
3509b8ca1efaec4a08f543c9b88708aa0b34a09b assets/hex/models/item/focus.json
|
||||
13e30c053b46626bdabf9fb24baaa16268856743 assets/hex/models/item/focus_double.json
|
||||
1bf03d6ad80d323bceb7c8ffabaa966a7bb6e55a assets/hex/models/item/focus_empty.json
|
||||
1f428723bf0abf9a1dbaa293444fc00d7c4a4acf assets/hex/models/item/focus_entity.json
|
||||
cbcff532ab24e5bf839cf157d7ffbc6697e84725 assets/hex/models/item/focus_list.json
|
||||
322434f18e4d566f2ff3cbf35e766c8cc8419c69 assets/hex/models/item/focus_patterns.json
|
||||
34fceb1b604be4945274183042d0be13fb49f9f3 assets/hex/models/item/focus_spell.json
|
||||
76f4af83258b403304aeaad0ace5e5915df7f213 assets/hex/models/item/focus_vec3.json
|
||||
ce06bdaeaf73d59c85d67e4cd8b51f54160ebef0 assets/hex/models/item/focus_widget.json
|
||||
a354a2dc83d220c43d4c9f156059cbd8255e9e19 assets/hex/models/item/spellbook.json
|
||||
837e7ed749afa44bd3be4114c7d81f3b8fe47852 assets/hex/models/item/wand.json
|
52
src/generated/resources/assets/hex/models/item/focus.json
Normal file
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"overrides": [
|
||||
{
|
||||
"predicate": {
|
||||
"hex:datatype": -0.01
|
||||
},
|
||||
"model": "hex:item/focus_empty"
|
||||
},
|
||||
{
|
||||
"predicate": {
|
||||
"hex:datatype": 0.99
|
||||
},
|
||||
"model": "hex:item/focus_entity"
|
||||
},
|
||||
{
|
||||
"predicate": {
|
||||
"hex:datatype": 1.99
|
||||
},
|
||||
"model": "hex:item/focus_double"
|
||||
},
|
||||
{
|
||||
"predicate": {
|
||||
"hex:datatype": 2.99
|
||||
},
|
||||
"model": "hex:item/focus_vec3"
|
||||
},
|
||||
{
|
||||
"predicate": {
|
||||
"hex:datatype": 3.99
|
||||
},
|
||||
"model": "hex:item/focus_spell"
|
||||
},
|
||||
{
|
||||
"predicate": {
|
||||
"hex:datatype": 4.99
|
||||
},
|
||||
"model": "hex:item/focus_widget"
|
||||
},
|
||||
{
|
||||
"predicate": {
|
||||
"hex:datatype": 5.99
|
||||
},
|
||||
"model": "hex:item/focus_list"
|
||||
},
|
||||
{
|
||||
"predicate": {
|
||||
"hex:datatype": 6.99
|
||||
},
|
||||
"model": "hex:item/focus_patterns"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/focus_double"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/focus_empty"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/focus_entity"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/focus_list"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/focus_patterns"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/focus_spell"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/focus_vec3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/focus_widget"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/spellbook"
|
||||
}
|
||||
}
|
6
src/generated/resources/assets/hex/models/item/wand.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "hex:item/wand"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package at.petrak.hex;
|
||||
|
||||
import at.petrak.hex.client.HexRenderOverlays;
|
||||
import at.petrak.hex.common.items.HexItems;
|
||||
import at.petrak.hex.common.network.HexMessages;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
@ -21,7 +20,6 @@ public class HexMod {
|
|||
var evbus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
HexItems.ITEMS.register(evbus);
|
||||
MinecraftForge.EVENT_BUS.register(HexRenderOverlays.class);
|
||||
HexMessages.register();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package at.petrak.hex.client;
|
||||
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
// https://github.com/gamma-delta/VCC/blob/master/src/main/java/me/gammadelta/client/VCCRenderOverlays.java⌈
|
||||
public class HexRenderOverlays {
|
||||
@SubscribeEvent
|
||||
public static void renderOverlay(RenderGameOverlayEvent e) {
|
||||
|
||||
}
|
||||
}
|
41
src/main/java/at/petrak/hex/client/ShiftScrollListener.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
package at.petrak.hex.client;
|
||||
|
||||
import at.petrak.hex.HexMod;
|
||||
import at.petrak.hex.common.items.ItemSpellbook;
|
||||
import at.petrak.hex.common.network.HexMessages;
|
||||
import at.petrak.hex.common.network.MsgShiftScrollSyn;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.InputEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = HexMod.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.FORGE)
|
||||
public class ShiftScrollListener {
|
||||
@SubscribeEvent
|
||||
public static void onScroll(InputEvent.MouseScrollEvent evt) {
|
||||
HexMod.LOGGER.info("scrolling {}", evt.getScrollDelta());
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
if (player.isCrouching()) {
|
||||
InteractionHand hand = null;
|
||||
if (player.getMainHandItem().getItem() != Items.AIR) {
|
||||
hand = InteractionHand.MAIN_HAND;
|
||||
} else if (player.getOffhandItem().getItem() != Items.AIR) {
|
||||
hand = InteractionHand.OFF_HAND;
|
||||
}
|
||||
|
||||
if (hand != null) {
|
||||
var item = player.getItemInHand(hand).getItem();
|
||||
if (item instanceof ItemSpellbook) {
|
||||
evt.setCanceled(true);
|
||||
|
||||
HexMessages.getNetwork().sendToServer(new MsgShiftScrollSyn(hand, evt.getScrollDelta()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ 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() {
|
||||
class CastException(val reason: Reason, vararg val data: Any) : Exception() {
|
||||
enum class Reason {
|
||||
// Compilation
|
||||
/**
|
||||
|
@ -36,6 +36,13 @@ class CastException(val reason: at.petrak.hex.common.casting.CastException.Reaso
|
|||
*/
|
||||
NOT_ENOUGH_ARGS,
|
||||
|
||||
/**
|
||||
* There are too many close parentheses.
|
||||
*
|
||||
* `no args`
|
||||
*/
|
||||
TOO_MANY_CLOSE_PARENS,
|
||||
|
||||
// Execution
|
||||
/**
|
||||
* Tried to interact with a vector that was too far away
|
||||
|
@ -47,10 +54,11 @@ class CastException(val reason: at.petrak.hex.common.casting.CastException.Reaso
|
|||
|
||||
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}"
|
||||
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_MANY_CLOSE_PARENS -> "too many close parentheses"
|
||||
Reason.TOO_FAR -> "tried to interact with something too far away at ${this.data[0] as Vec3}"
|
||||
}
|
||||
}
|
|
@ -13,6 +13,9 @@ import net.minecraft.server.level.ServerPlayer
|
|||
*/
|
||||
class CastingHarness private constructor(
|
||||
val stack: MutableList<SpellDatum<*>>,
|
||||
var parenCount: Int,
|
||||
var parenthesized: MutableList<HexPattern>,
|
||||
var escapeNext: Boolean,
|
||||
val ctx: CastingContext,
|
||||
) {
|
||||
/**
|
||||
|
@ -21,19 +24,92 @@ class CastingHarness private constructor(
|
|||
*/
|
||||
fun update(newPat: HexPattern): CastResult {
|
||||
return try {
|
||||
val operator = SpellOperator.fromPattern(newPat)
|
||||
HexMod.LOGGER.info("Executing operator: $operator")
|
||||
operator.modifyStack(this.stack, this.ctx)
|
||||
HexMod.LOGGER.info("Modified stack: ${this.stack}")
|
||||
|
||||
if (this.stack.isEmpty()) {
|
||||
return CastResult.QuitCasting
|
||||
var exn: CastException? = null
|
||||
val operator = try {
|
||||
SpellOperator.fromPattern(newPat)
|
||||
} catch (e: CastException) {
|
||||
exn = e
|
||||
null
|
||||
}
|
||||
val maybeSpell = this.stack[0]
|
||||
if (this.stack.size == 1 && maybeSpell.payload is RenderedSpell) {
|
||||
CastResult.Success(maybeSpell.payload)
|
||||
if (this.parenCount > 0) {
|
||||
if (this.escapeNext) {
|
||||
this.escapeNext = false
|
||||
this.parenthesized.add(newPat)
|
||||
HexMod.LOGGER.info("Escaping onto parenthesized")
|
||||
} else if (operator == SpellWidget.ESCAPE) {
|
||||
this.escapeNext = true
|
||||
} else if (operator == SpellWidget.OPEN_PAREN) {
|
||||
// we have escaped the parens onto the stack; we just also record our count.
|
||||
this.parenthesized.add(newPat)
|
||||
this.parenCount++
|
||||
} else if (operator == SpellWidget.CLOSE_PAREN) {
|
||||
this.parenCount--
|
||||
if (this.parenCount == 0) {
|
||||
HexMod.LOGGER.info("Finished parenthesizing things")
|
||||
this.stack.add(SpellDatum.make(this.parenthesized.map { SpellDatum.make(it) }))
|
||||
} else if (this.parenCount < 0) {
|
||||
throw CastException(CastException.Reason.TOO_MANY_CLOSE_PARENS)
|
||||
} else {
|
||||
// we have this situation: "(()"
|
||||
// we need to add the close paren
|
||||
this.parenthesized.add(newPat)
|
||||
}
|
||||
} else {
|
||||
this.parenthesized.add(newPat)
|
||||
}
|
||||
} else if (this.escapeNext) {
|
||||
this.escapeNext = false
|
||||
HexMod.LOGGER.info("Escaping onto stack")
|
||||
this.stack.add(SpellDatum.make(newPat))
|
||||
} else {
|
||||
CastResult.Nothing
|
||||
// Plain ol operator
|
||||
val sig = newPat.anglesSignature()
|
||||
if (sig.startsWith("aqaa") || sig.startsWith("dedd")) {
|
||||
val negate = sig.startsWith("dedd")
|
||||
var accumulator = 0.0
|
||||
for (chr in sig.substring(4)) {
|
||||
when (chr) {
|
||||
'w' -> accumulator += 1
|
||||
'q' -> accumulator += 5
|
||||
'e' -> accumulator += 10
|
||||
'a' -> accumulator *= 2
|
||||
'd' -> accumulator /= 2
|
||||
}
|
||||
}
|
||||
if (negate) {
|
||||
accumulator = -accumulator
|
||||
}
|
||||
this.stack.add(SpellDatum.make(accumulator))
|
||||
} else if (exn != null) {
|
||||
// there was a problem finding the pattern and it was NOT due to numbers
|
||||
throw exn
|
||||
} else if (operator == SpellWidget.OPEN_PAREN) {
|
||||
this.parenCount++
|
||||
} else if (operator == SpellWidget.CLOSE_PAREN) {
|
||||
throw CastException(CastException.Reason.TOO_MANY_CLOSE_PARENS)
|
||||
} else {
|
||||
// we know the operator is ok here
|
||||
operator!!.modifyStack(this.stack, this.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.parenCount > 0) {
|
||||
HexMod.LOGGER.info("Paren level ${this.parenCount}; ${this.parenthesized}")
|
||||
}
|
||||
HexMod.LOGGER.info("New stack: ${this.stack}")
|
||||
if (this.stack.isEmpty()) {
|
||||
if (this.parenCount == 0) {
|
||||
CastResult.QuitCasting
|
||||
} else {
|
||||
CastResult.Nothing
|
||||
}
|
||||
} else {
|
||||
val maybeSpell = this.stack[0]
|
||||
if (this.stack.size == 1 && maybeSpell.payload is RenderedSpell) {
|
||||
CastResult.Success(maybeSpell.payload)
|
||||
} else {
|
||||
CastResult.Nothing
|
||||
}
|
||||
}
|
||||
} catch (e: CastException) {
|
||||
CastResult.Error(e)
|
||||
|
@ -48,8 +124,13 @@ class CastingHarness private constructor(
|
|||
stackTag.add(datum.serializeToNBT())
|
||||
out.put(TAG_STACK, stackTag)
|
||||
|
||||
val pointsTag = ListTag()
|
||||
out.put(TAG_POINTS, pointsTag)
|
||||
out.putInt(TAG_PAREN_COUNT, this.parenCount)
|
||||
out.putBoolean(TAG_ESCAPE_NEXT, this.escapeNext)
|
||||
|
||||
val parensTag = ListTag()
|
||||
for (pat in this.parenthesized)
|
||||
parensTag.add(pat.serializeToNBT())
|
||||
out.put(TAG_PARENTHESIZED, parensTag)
|
||||
|
||||
return out
|
||||
}
|
||||
|
@ -57,6 +138,9 @@ class CastingHarness private constructor(
|
|||
companion object {
|
||||
const val TAG_STACK = "stack"
|
||||
const val TAG_POINTS = "points"
|
||||
const val TAG_PAREN_COUNT = "open_parens"
|
||||
const val TAG_PARENTHESIZED = "parenthesized"
|
||||
const val TAG_ESCAPE_NEXT = "escape_next"
|
||||
|
||||
@JvmStatic
|
||||
fun DeserializeFromNBT(nbt: Tag?, caster: ServerPlayer): CastingHarness {
|
||||
|
@ -71,12 +155,24 @@ class CastingHarness private constructor(
|
|||
stack.add(datum)
|
||||
}
|
||||
|
||||
CastingHarness(stack, ctx)
|
||||
val parenthesized = mutableListOf<HexPattern>()
|
||||
val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND.toInt())
|
||||
for (subtag in parenTag) {
|
||||
parenthesized.add(HexPattern.DeserializeFromNBT(subtag as CompoundTag))
|
||||
}
|
||||
|
||||
val parenCount = nbt.getInt(TAG_PAREN_COUNT)
|
||||
val escapeNext = nbt.getBoolean(TAG_ESCAPE_NEXT)
|
||||
|
||||
CastingHarness(stack, parenCount, parenthesized, escapeNext, ctx)
|
||||
} catch (exn: Exception) {
|
||||
HexMod.LOGGER.warn("Couldn't load harness from nbt tag, falling back to default: $nbt: $exn")
|
||||
CastingHarness(mutableListOf(), ctx)
|
||||
Default(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Default(ctx: CastingContext): CastingHarness =
|
||||
CastingHarness(mutableListOf(), 0, mutableListOf(), false, ctx)
|
||||
}
|
||||
|
||||
sealed class CastResult {
|
||||
|
|
|
@ -17,7 +17,7 @@ import net.minecraft.world.phys.Vec3
|
|||
* * [Vec3][net.minecraft.world.phys.Vec3] as both position and (when normalized) direction
|
||||
* * [RenderedSpell]
|
||||
* * [SpellWidget]; [SpellWidget.NULL] is used as our null value
|
||||
* * [ArrayList<SpellDatum<*>>][ArrayList]
|
||||
* * [List<SpellDatum<*>>][List]
|
||||
* * [HexPattern]! Yes, we have meta-evaluation everyone.
|
||||
* The constructor guarantees we won't pass a type that isn't one of those types.
|
||||
*
|
||||
|
@ -87,9 +87,15 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
}
|
||||
|
||||
companion object {
|
||||
fun <T : Any> make(payload: T): SpellDatum<T> =
|
||||
fun make(payload: Any): SpellDatum<*> =
|
||||
if (!IsValidType(payload)) {
|
||||
throw CastException(CastException.Reason.INVALID_TYPE, payload)
|
||||
// Check to see if it's a java boxed double
|
||||
if (payload is java.lang.Double) {
|
||||
val num = payload.toDouble()
|
||||
SpellDatum(if (num.isFinite()) num else 0.0)
|
||||
} else {
|
||||
throw CastException(CastException.Reason.INVALID_TYPE, payload)
|
||||
}
|
||||
} else {
|
||||
SpellDatum(payload)
|
||||
}
|
||||
|
@ -148,9 +154,9 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
Double::class.java,
|
||||
Vec3::class.java,
|
||||
RenderedSpell::class.java,
|
||||
ArrayList::class.java,
|
||||
List::class.java,
|
||||
SpellWidget::class.java,
|
||||
PatternList::class.java,
|
||||
HexPattern::class.java,
|
||||
)
|
||||
|
||||
// Mapping of string keys for spell implers to their implementation
|
||||
|
@ -168,8 +174,10 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
const val TAG_PATTERN = "pattern"
|
||||
|
||||
fun <T : Any> IsValidType(checkee: T): Boolean =
|
||||
if (checkee is ArrayList<*>) {
|
||||
checkee.all { it is SpellDatum<*> && IsValidType(it) }
|
||||
if (checkee is List<*>) {
|
||||
// note it should be impossible to pass a spell datum that doesn't contain a valid type,
|
||||
// but we best make sure.
|
||||
checkee.all { it is SpellDatum<*> && IsValidType(it.payload) }
|
||||
} else {
|
||||
ValidTypes.any { clazz -> clazz.isAssignableFrom(checkee.javaClass) }
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package at.petrak.hex.common.casting
|
||||
|
||||
import at.petrak.hex.common.casting.operators.*
|
||||
import at.petrak.hex.common.casting.operators.spells.OpAddMotion
|
||||
import at.petrak.hex.common.casting.operators.spells.OpExplode
|
||||
import at.petrak.hex.common.casting.operators.spells.OpPrint
|
||||
import at.petrak.hex.hexmath.HexPattern
|
||||
|
@ -23,6 +24,7 @@ interface SpellOperator {
|
|||
companion object {
|
||||
val PatternMap: Map<String, SpellOperator> = mapOf(
|
||||
// == Getters ==
|
||||
|
||||
// diamond shape to get the caster
|
||||
"qaq" to OpGetCaster,
|
||||
"ede" to OpGetCaster,
|
||||
|
@ -41,6 +43,7 @@ interface SpellOperator {
|
|||
"weaqa" to OpEntityRaycast,
|
||||
|
||||
// == Modify Stack ==
|
||||
|
||||
// CCW hook for undo
|
||||
"a" to OpUndo,
|
||||
// and CW for null
|
||||
|
@ -57,6 +60,15 @@ interface SpellOperator {
|
|||
"aq" to OpPrint,
|
||||
// nuclear sign for explosion
|
||||
"aawaawaa" to OpExplode,
|
||||
"weeewdq" to OpAddMotion,
|
||||
|
||||
// == Meta stuff ==
|
||||
"qqq" to SpellWidget.OPEN_PAREN,
|
||||
"eee" to SpellWidget.CLOSE_PAREN,
|
||||
"aaaqw" to SpellWidget.ESCAPE,
|
||||
// http://www.toroidalsnark.net/mkss3-pix/CalderheadJMM2014.pdf
|
||||
// eval being a space filling curve feels apt doesn't it
|
||||
"deaqq" to OpEval
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -78,14 +90,8 @@ interface SpellOperator {
|
|||
* Try to get a value of the given type.
|
||||
*/
|
||||
@JvmStatic
|
||||
inline fun <reified T : Any> List<SpellDatum<*>>.getChecked(idx: Int): T {
|
||||
val datum = this[idx]
|
||||
val casted = datum.tryGet<T>()
|
||||
return if (casted is Double && !casted.isFinite())
|
||||
0.0 as T
|
||||
else
|
||||
casted
|
||||
}
|
||||
inline fun <reified T : Any> List<SpellDatum<*>>.getChecked(idx: Int): T =
|
||||
this[idx].tryGet()
|
||||
|
||||
/**
|
||||
* Check if the value at the given index is OK. Will throw an error otherwise.
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package at.petrak.hex.common.casting.operators
|
||||
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.CastingHarness
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellOperator.Companion.getChecked
|
||||
|
||||
object OpEval : SimpleOperator {
|
||||
override val argc: Int
|
||||
get() = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val instrs: List<SpellDatum<*>> = args.getChecked(0)
|
||||
|
||||
val harness = CastingHarness.Default(ctx)
|
||||
for (pat in instrs) {
|
||||
val res = harness.update(pat.tryGet())
|
||||
if (res is CastingHarness.CastResult.Error) {
|
||||
throw res.exn
|
||||
}
|
||||
// in ANY OTHER CASE JUST KEEP GOING
|
||||
// including if there's RenderedSpells on the stack or the stack becomes clear
|
||||
}
|
||||
return harness.stack
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
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.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpAddMotion : SimpleOperator, RenderedSpellImpl {
|
||||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val target = args.getChecked<Entity>(0)
|
||||
val motion = args.getChecked<Vec3>(1)
|
||||
return spellListOf(RenderedSpell(OpAddMotion, spellListOf(target, motion)))
|
||||
}
|
||||
|
||||
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {
|
||||
val target = args.getChecked<Entity>(0)
|
||||
val motion = args.getChecked<Vec3>(1)
|
||||
target.deltaMovement = target.deltaMovement.add(motion)
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@ public class HexItems {
|
|||
() -> new ItemWand(unstackable()));
|
||||
public static final RegistryObject<Item> FOCUS = ITEMS.register(LibItemNames.FOCUS,
|
||||
() -> new ItemFocus(props()));
|
||||
public static final RegistryObject<Item> SPELLBOOK = ITEMS.register(LibItemNames.SPELLBOOK,
|
||||
() -> new ItemSpellbook(unstackable()));
|
||||
|
||||
public static Item.Properties props() {
|
||||
return new Item.Properties();
|
||||
|
|
64
src/main/java/at/petrak/hex/common/items/ItemSpellbook.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
package at.petrak.hex.common.items;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ItemSpellbook extends Item {
|
||||
public static String TAG_SELECTED_PAGE = "page_idx";
|
||||
// this is a CompoundTag of string numerical keys to SpellData
|
||||
public static String TAG_PAGES = "pages";
|
||||
|
||||
public ItemSpellbook(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltip,
|
||||
TooltipFlag isAdvanced) {
|
||||
var tag = stack.getOrCreateTag();
|
||||
if (tag.contains(TAG_SELECTED_PAGE)) {
|
||||
var pageIdx = tag.getInt(TAG_SELECTED_PAGE);
|
||||
var pages = tag.getCompound(ItemSpellbook.TAG_PAGES);
|
||||
tooltip.add(new TranslatableComponent("hex.spellbook.tooltip.page", pageIdx, HighestPage(pages)));
|
||||
|
||||
var key = String.valueOf(pageIdx);
|
||||
if (pages.contains(key)) {
|
||||
var datum = pages.getCompound(String.valueOf(pageIdx));
|
||||
// I know this is ugly i dont care
|
||||
tooltip.add(new TextComponent(datum.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int HighestPage(CompoundTag tag) {
|
||||
var highestKey = tag.getAllKeys().stream().flatMap(s -> {
|
||||
try {
|
||||
return Stream.of(Integer.parseInt(s));
|
||||
} catch (NumberFormatException e) {
|
||||
return Stream.empty();
|
||||
}
|
||||
}).max(Integer::compare);
|
||||
return highestKey.orElse(0);
|
||||
}
|
||||
|
||||
public static void RotatePageIdx(CompoundTag tag, boolean increase) {
|
||||
int newIdx;
|
||||
if (tag.contains(ItemSpellbook.TAG_SELECTED_PAGE)) {
|
||||
var delta = increase ? 1 : -1;
|
||||
newIdx = Math.max(0, tag.getInt(ItemSpellbook.TAG_SELECTED_PAGE) + delta);
|
||||
} else {
|
||||
newIdx = 0;
|
||||
}
|
||||
tag.putInt(ItemSpellbook.TAG_SELECTED_PAGE, newIdx);
|
||||
}
|
||||
}
|
|
@ -3,4 +3,5 @@ package at.petrak.hex.common.lib;
|
|||
public class LibItemNames {
|
||||
public static final String WAND = "wand";
|
||||
public static final String FOCUS = "focus";
|
||||
public static final String SPELLBOOK = "spellbook";
|
||||
}
|
||||
|
|
|
@ -27,5 +27,7 @@ public class HexMessages {
|
|||
MsgNewSpellPatternAck::deserialize, MsgNewSpellPatternAck::handle);
|
||||
NETWORK.registerMessage(messageIdx++, MsgQuitSpellcasting.class, MsgQuitSpellcasting::serialize,
|
||||
MsgQuitSpellcasting::deserialize, MsgQuitSpellcasting::handle);
|
||||
NETWORK.registerMessage(messageIdx++, MsgShiftScrollSyn.class, MsgShiftScrollSyn::serialize,
|
||||
MsgShiftScrollSyn::deserialize, MsgShiftScrollSyn::handle);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
|
|||
if (res instanceof CastResult.Success success) {
|
||||
success.getSpell().cast(new CastingContext(sender));
|
||||
} else if (res instanceof CastResult.Error error) {
|
||||
sender.sendMessage(new TextComponent(error.getExn().toString()), Util.NIL_UUID);
|
||||
sender.sendMessage(new TextComponent(error.getExn().getMessage()), Util.NIL_UUID);
|
||||
}
|
||||
|
||||
boolean quit;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package at.petrak.hex.common.network;
|
||||
|
||||
import at.petrak.hex.common.items.ItemSpellbook;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Sent client->server when the client shift+scrolls with a shift-scrollable item.
|
||||
*/
|
||||
public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta) {
|
||||
public static MsgShiftScrollSyn deserialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
var hand = InteractionHand.values()[buf.readInt()];
|
||||
var scrollDelta = buf.readDouble();
|
||||
return new MsgShiftScrollSyn(hand, scrollDelta);
|
||||
}
|
||||
|
||||
public void serialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
buf.writeInt(this.hand.ordinal());
|
||||
buf.writeDouble(this.scrollDelta);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(() -> {
|
||||
ServerPlayer sender = ctx.get().getSender();
|
||||
if (sender != null) {
|
||||
var stack = sender.getItemInHand(hand);
|
||||
|
||||
if (stack.getItem() instanceof ItemSpellbook) {
|
||||
spellbook(sender, stack);
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
private void spellbook(ServerPlayer sender, ItemStack stack) {
|
||||
var tag = stack.getOrCreateTag();
|
||||
ItemSpellbook.RotatePageIdx(tag, this.scrollDelta < 0.0);
|
||||
|
||||
var newIdx = tag.getInt(ItemSpellbook.TAG_SELECTED_PAGE);
|
||||
var len = ItemSpellbook.HighestPage(tag.getCompound(ItemSpellbook.TAG_PAGES));
|
||||
sender.displayClientMessage(new TranslatableComponent("hex.spellbook.tooltip.page", newIdx, len), true);
|
||||
}
|
||||
}
|
22
src/main/java/at/petrak/hex/datagen/DataGenerators.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package at.petrak.hex.datagen;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.forge.event.lifecycle.GatherDataEvent;
|
||||
|
||||
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public class DataGenerators {
|
||||
@SubscribeEvent
|
||||
public static void generateData(GatherDataEvent ev) {
|
||||
DataGenerator gen = ev.getGenerator();
|
||||
ExistingFileHelper efh = ev.getExistingFileHelper();
|
||||
if (ev.includeClient()) {
|
||||
gen.addProvider(new ItemModels(gen, efh));
|
||||
}
|
||||
if (ev.includeServer()) {
|
||||
// recipes
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,19 +6,21 @@ 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.ItemModelProvider;
|
||||
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) {
|
||||
public class ItemModels extends ItemModelProvider {
|
||||
public ItemModels(DataGenerator generator, ExistingFileHelper existingFileHelper) {
|
||||
super(generator, HexMod.MOD_ID, existingFileHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerModels() {
|
||||
simpleItem(HexItems.WAND.get());
|
||||
simpleItem(HexItems.SPELLBOOK.get());
|
||||
|
||||
simpleItem(modLoc("focus_none"));
|
||||
simpleItem(modLoc("focus_empty"));
|
||||
simpleItem(modLoc("focus_entity"));
|
||||
simpleItem(modLoc("focus_double"));
|
||||
simpleItem(modLoc("focus_vec3"));
|
||||
|
@ -29,7 +31,7 @@ public class ItemModelProvider extends net.minecraftforge.client.model.generator
|
|||
getBuilder(HexItems.FOCUS.get().getRegistryName().getPath())
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, -0.01f)
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_none")))
|
||||
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_empty")))
|
||||
.end()
|
||||
.override()
|
||||
.predicate(ItemFocus.PREDICATE, 1 - 0.01f)
|
7
src/main/resources/assets/hex/lang/en_US.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"item.hex.wand": "Staff",
|
||||
"item.hex.focus": "Focus",
|
||||
"item.hex.spellbook": "Spellbook",
|
||||
"hex.spellbook.tooltip.page": "Selected Page %d/%d",
|
||||
"hex.spelldata.desc.entity": "Entity %s"
|
||||
}
|
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
Before Width: | Height: | Size: 413 B After Width: | Height: | Size: 413 B |
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 414 B |
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 429 B |
Before Width: | Height: | Size: 416 B After Width: | Height: | Size: 416 B |
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 420 B |
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 420 B |
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
BIN
src/main/resources/assets/hex/textures/item/spellbook.png
Normal file
After Width: | Height: | Size: 438 B |
Before Width: | Height: | Size: 261 B After Width: | Height: | Size: 261 B |