some resources and encapsulating patterns themselves as spelldata

This commit is contained in:
gamma-delta 2021-12-28 00:32:35 -06:00
parent 19467a18bb
commit 57b3b2854c
40 changed files with 531 additions and 55 deletions

View 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

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

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/focus_double"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/focus_empty"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/focus_entity"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/focus_list"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/focus_patterns"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/focus_spell"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/focus_vec3"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/focus_widget"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/spellbook"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/wand"
}
}

View file

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

View file

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

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

View file

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

View file

@ -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,20 +24,93 @@ 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
}
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 {
// 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 {

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

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

View file

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 421 B

View file

Before

Width:  |  Height:  |  Size: 413 B

After

Width:  |  Height:  |  Size: 413 B

View file

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 414 B

View file

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 429 B

View file

Before

Width:  |  Height:  |  Size: 416 B

After

Width:  |  Height:  |  Size: 416 B

View file

Before

Width:  |  Height:  |  Size: 420 B

After

Width:  |  Height:  |  Size: 420 B

View file

Before

Width:  |  Height:  |  Size: 420 B

After

Width:  |  Height:  |  Size: 420 B

View file

Before

Width:  |  Height:  |  Size: 421 B

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

View file

Before

Width:  |  Height:  |  Size: 261 B

After

Width:  |  Height:  |  Size: 261 B