3 new spells and fix a bunch of bugs, closes #1

This commit is contained in:
gamma-delta 2021-12-30 16:00:20 -06:00
parent 1a3faecae9
commit 11042f3008
46 changed files with 698 additions and 156 deletions

21
original_ideas_doc.txt Normal file
View file

@ -0,0 +1,21 @@
PieceTrickBlaze.java
PieceTrickBreakLoop.java
PieceTrickChangeSlot.java
PieceTrickDebug.java
PieceTrickDebugSpamless.java
PieceTrickDelay.java
PieceTrickDetonate.java
PieceTrickDie.java
PieceTrickEidosAnchor.java
PieceTrickEidosReversal.java
PieceTrickEvaluate.java
PieceTrickExplode.java
PieceTrickOvergrow.java
PieceTrickParticleTrail.java
PieceTrickPlaySound.java
PieceTrickRussianRoulette.java
PieceTrickSaveVector.java
PieceTrickSmite.java
PieceTrickSpinChamber.java
PieceTrickSwitchTargetSlot.java
PieceTrickTorrent.java

View file

@ -1,2 +1,42 @@
package at.petrak.hex;public class HexConfig { package at.petrak.hex;
import net.minecraft.world.item.Tier;
import net.minecraft.world.item.Tiers;
import net.minecraftforge.common.ForgeConfigSpec;
public class HexConfig {
public final ForgeConfigSpec.IntValue maxRecurseDepth;
public final ForgeConfigSpec.IntValue wandRechargeRate;
public final ForgeConfigSpec.DoubleValue healthToManaRate;
public final ForgeConfigSpec.IntValue opBreakHarvestLevel;
public HexConfig(ForgeConfigSpec.Builder builder) {
maxRecurseDepth = builder.comment("How many times an Eval can recursively cast itself")
.defineInRange("maxRecurseDepth", 15, 0, Integer.MAX_VALUE);
wandRechargeRate = builder.comment("How many mana points a wand recharges per tick")
.defineInRange("wandRechargeRate", 2, 0, Integer.MAX_VALUE);
healthToManaRate = builder.comment("How many points of mana a half-heart is worth when casting from HP")
.defineInRange("healthToManaRate", 10.0, 0.0, 1_000_000.0);
builder.push("spells");
opBreakHarvestLevel = builder.comment(
"The harvest level of the Break Block spell.",
"0 = wood, 1 = stone, 2 = iron, 3 = diamond, 4 = netherite."
).defineInRange("opBreakHarvestLevel", 3, 0, 4);
builder.pop();
}
/**
* i'm not kidding look upon net.minecraftforge.common.TierSortingRegistry and weep
*/
public Tier getOpBreakHarvestLevelBecauseForgeThoughtItWasAGoodIdeaToImplementHarvestTiersUsingAnHonestToGodTopoSort() {
return switch (this.opBreakHarvestLevel.get()) {
case 0 -> Tiers.WOOD;
case 1 -> Tiers.STONE;
case 2 -> Tiers.IRON;
case 3 -> Tiers.DIAMOND;
case 4 -> Tiers.NETHERITE;
default -> throw new RuntimeException("unreachable");
};
}
} }

View file

@ -6,12 +6,12 @@ import at.petrak.hex.common.casting.SpellDatum;
import at.petrak.hex.common.casting.SpellWidget; import at.petrak.hex.common.casting.SpellWidget;
import at.petrak.hex.common.casting.operators.*; import at.petrak.hex.common.casting.operators.*;
import at.petrak.hex.common.casting.operators.math.*; import at.petrak.hex.common.casting.operators.math.*;
import at.petrak.hex.common.casting.operators.spells.OpAddMotion; import at.petrak.hex.common.casting.operators.spells.*;
import at.petrak.hex.common.casting.operators.spells.OpExplode;
import at.petrak.hex.common.casting.operators.spells.OpPrint;
import at.petrak.hex.common.items.HexItems; import at.petrak.hex.common.items.HexItems;
import at.petrak.hex.common.network.HexMessages; import at.petrak.hex.common.network.HexMessages;
import at.petrak.hex.server.TickScheduler;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@ -24,6 +24,14 @@ import org.apache.logging.log4j.Logger;
public class HexMod { public class HexMod {
// hmm today I will use a popular logging framework :clueless: // hmm today I will use a popular logging framework :clueless:
public static final Logger LOGGER = LogManager.getLogger(); public static final Logger LOGGER = LogManager.getLogger();
public static final HexConfig CONFIG;
public static final ForgeConfigSpec CONFIG_SPEC;
static {
final var specPair = new ForgeConfigSpec.Builder().configure(HexConfig::new);
CONFIG = specPair.getLeft();
CONFIG_SPEC = specPair.getRight();
}
public static final String MOD_ID = "hex"; public static final String MOD_ID = "hex";
@ -32,8 +40,10 @@ public class HexMod {
var evbus = FMLJavaModLoadingContext.get().getModEventBus(); var evbus = FMLJavaModLoadingContext.get().getModEventBus();
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
evbus.register(HexMod.class); evbus.register(HexMod.class);
HexItems.ITEMS.register(evbus); HexItems.ITEMS.register(evbus);
HexMessages.register(); HexMessages.register();
MinecraftForge.EVENT_BUS.register(TickScheduler.INSTANCE);
} }
// I guess this means the client will have a big empty map for patterns // I guess this means the client will have a big empty map for patterns
@ -44,32 +54,22 @@ public class HexMod {
for (Pair<String, SpellOperator> p : new Pair[]{ for (Pair<String, SpellOperator> p : new Pair[]{
// == Getters == // == Getters ==
// diamond shape to get the caster
new Pair<>("qaq", OpGetCaster.INSTANCE), new Pair<>("qaq", OpGetCaster.INSTANCE),
new Pair<>("ede", OpGetCaster.INSTANCE), new Pair<>("ede", OpGetCaster.INSTANCE),
// small triangle to get the entity pos
new Pair<>("aa", OpEntityPos.INSTANCE), new Pair<>("aa", OpEntityPos.INSTANCE),
new Pair<>("dd", OpEntityPos.INSTANCE), new Pair<>("dd", OpEntityPos.INSTANCE),
// Arrow to get the look vector
new Pair<>("wa", OpEntityLook.INSTANCE), new Pair<>("wa", OpEntityLook.INSTANCE),
new Pair<>("wd", OpEntityLook.INSTANCE), new Pair<>("wd", OpEntityLook.INSTANCE),
// CCW battleaxe for block raycast
new Pair<>("wqaawdd", OpBlockRaycast.INSTANCE), new Pair<>("wqaawdd", OpBlockRaycast.INSTANCE),
// and CW for axis raycast
new Pair<>("weddwaa", OpBlockAxisRaycast.INSTANCE), new Pair<>("weddwaa", OpBlockAxisRaycast.INSTANCE),
// CCW diamond mace thing for entity raycast
new Pair<>("weaqa", OpEntityRaycast.INSTANCE), new Pair<>("weaqa", OpEntityRaycast.INSTANCE),
// == Modify Stack == // == Modify Stack ==
// CCW hook for undo
new Pair<>("a", OpUndo.INSTANCE), new Pair<>("a", OpUndo.INSTANCE),
// and CW for null
new Pair<>("d", SpellWidget.NULL), new Pair<>("d", SpellWidget.NULL),
// Two triangles holding hands to duplicate
new Pair<>("aadaa", OpDuplicate.INSTANCE), new Pair<>("aadaa", OpDuplicate.INSTANCE),
// Two opposing triangles to swap
new Pair<>("aawdd", OpSwap.INSTANCE), new Pair<>("aawdd", OpSwap.INSTANCE),
// == Math == // == Math ==
@ -82,12 +82,13 @@ public class HexMod {
// == Spells == // == Spells ==
// hook for debug
new Pair<>("de", OpPrint.INSTANCE), new Pair<>("de", OpPrint.INSTANCE),
new Pair<>("aq", OpPrint.INSTANCE), new Pair<>("aq", OpPrint.INSTANCE),
// nuclear sign for explosion
new Pair<>("aawaawaa", OpExplode.INSTANCE), new Pair<>("aawaawaa", OpExplode.INSTANCE),
new Pair<>("weeewdq", OpAddMotion.INSTANCE), new Pair<>("weeewdq", OpAddMotion.INSTANCE),
new Pair<>("qaqqqqq", OpPlaceBlock.INSTANCE),
new Pair<>("eeeeede", OpBreakBlock.INSTANCE),
new Pair<>("waadwawdaaweewq", OpLightning.INSTANCE),
// == Meta stuff == // == Meta stuff ==
new Pair<>("qqq", SpellWidget.OPEN_PAREN), new Pair<>("qqq", SpellWidget.OPEN_PAREN),
@ -96,8 +97,8 @@ public class HexMod {
// http://www.toroidalsnark.net/mkss3-pix/CalderheadJMM2014.pdf // http://www.toroidalsnark.net/mkss3-pix/CalderheadJMM2014.pdf
// eval being a space filling curve feels apt doesn't it // eval being a space filling curve feels apt doesn't it
new Pair<>("deaqq", OpEval.INSTANCE), new Pair<>("deaqq", OpEval.INSTANCE),
new Pair<>("aqqqqq", OpReadFromSpellbook.INSTANCE), new Pair<>("aqqqqq", OpRead.INSTANCE),
new Pair<>("deeeee", OpWriteToSpellbook.INSTANCE), new Pair<>("deeeee", OpWrite.INSTANCE),
}) { }) {
PatternRegistry.addRegularPattern(p.getFirst(), p.getSecond()); PatternRegistry.addRegularPattern(p.getFirst(), p.getSecond());
count++; count++;

View file

@ -7,7 +7,7 @@ import at.petrak.hex.common.casting.SpellDatum
/** /**
* A SimpleOperator that always costs the same amount of mana. * A SimpleOperator that always costs the same amount of mana.
*/ */
interface SimpleFreeOperator : SpellOperator { interface ConstManaOperator : SpellOperator {
val argc: Int val argc: Int
val manaCost: Int val manaCost: Int
get() = 0 get() = 0

View file

@ -10,16 +10,17 @@ import at.petrak.hex.common.casting.SpellDatum
*/ */
interface SimpleOperator : SpellOperator { interface SimpleOperator : SpellOperator {
val argc: Int val argc: Int
fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<List<SpellDatum<*>>, Int>
override fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext) { override fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): Int {
if (this.argc > stack.size) if (this.argc > stack.size)
throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, this.argc, stack.size) throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, this.argc, stack.size)
val args = stack.takeLast(this.argc) val args = stack.takeLast(this.argc)
// there's gotta be a better way to do this // there's gotta be a better way to do this
for (_idx in 0 until this.argc) for (_idx in 0 until this.argc)
stack.removeLast() stack.removeLast()
val newData = this.execute(args, ctx) val (newData, mana) = this.execute(args, ctx)
stack.addAll(newData) stack.addAll(newData)
return mana
} }
} }

View file

@ -1,6 +1,5 @@
package at.petrak.hex.api package at.petrak.hex.api
import at.petrak.hex.common.casting.CastException
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
@ -14,10 +13,10 @@ import net.minecraft.world.phys.Vec3
* Implementors MUST NOT mutate the context. * Implementors MUST NOT mutate the context.
*/ */
interface SpellOperator { interface SpellOperator {
val manaCost: Int /**
get() = 0 * Operate on the stack and return the mana cost.
*/
fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext) fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): Int
companion object { companion object {
// I see why vzakii did this: you can't raycast out to infinity! // I see why vzakii did this: you can't raycast out to infinity!
@ -42,15 +41,6 @@ interface SpellOperator {
this.getChecked<T>(idx) 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 @JvmStatic
fun spellListOf(vararg vs: Any): List<SpellDatum<*>> { fun spellListOf(vararg vs: Any): List<SpellDatum<*>> {
val out = ArrayList<SpellDatum<*>>(vs.size) val out = ArrayList<SpellDatum<*>>(vs.size)
@ -61,11 +51,12 @@ interface SpellOperator {
} }
@JvmStatic @JvmStatic
fun makeConstantOp(x: SpellDatum<*>): SpellOperator = object : SimpleOperator { fun makeConstantOp(x: SpellDatum<*>): SpellOperator = object : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 0 get() = 0
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> = listOf(x) override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =
listOf(x)
} }
} }
} }

View file

@ -17,7 +17,7 @@ public class RegisterClientStuff {
evt.enqueueWork(() -> ItemProperties.register(HexItems.FOCUS.get(), ItemFocus.PREDICATE, evt.enqueueWork(() -> ItemProperties.register(HexItems.FOCUS.get(), ItemFocus.PREDICATE,
(stack, level, holder, holderID) -> { (stack, level, holder, holderID) -> {
if (stack.hasTag()) { if (stack.hasTag()) {
var tagname = stack.getTag().getAllKeys().iterator().next(); var tagname = stack.getTag().getCompound(ItemFocus.TAG_DATA).getAllKeys().iterator().next();
return switch (tagname) { return switch (tagname) {
case SpellDatum.TAG_ENTITY -> 1f; case SpellDatum.TAG_ENTITY -> 1f;
case SpellDatum.TAG_DOUBLE -> 2f; case SpellDatum.TAG_DOUBLE -> 2f;

View file

@ -57,6 +57,20 @@ class CastException(val reason: Reason, vararg val data: Any) : Exception() {
* `no args` * `no args`
*/ */
REQUIRES_SPELLBOOK, REQUIRES_SPELLBOOK,
/**
* An operator needed a data holder in the offhand.
*
* `no args`
*/
REQUIRES_DATA_HOLDER,
/**
* We went too deep!
*
* `maxDepth: Int, gotDepth: Int`
*/
TOO_MANY_RECURSIVE_EVALS,
} }
override val message: String override val message: String
@ -68,5 +82,7 @@ class CastException(val reason: Reason, vararg val data: Any) : Exception() {
Reason.TOO_MANY_CLOSE_PARENS -> "too many close parentheses" 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}" Reason.TOO_FAR -> "tried to interact with something too far away at ${this.data[0] as Vec3}"
Reason.REQUIRES_SPELLBOOK -> "required a spellbook in the other hand" Reason.REQUIRES_SPELLBOOK -> "required a spellbook in the other hand"
Reason.REQUIRES_DATA_HOLDER -> "required a data holder in the other hand"
Reason.TOO_MANY_RECURSIVE_EVALS -> "can only recursively call OpEval ${this.data[0] as Int} times but called it ${this.data[1] as Int} times"
} }
} }

View file

@ -1,29 +1,166 @@
package at.petrak.hex.common.casting package at.petrak.hex.common.casting
import at.petrak.hex.HexMod
import at.petrak.hex.HexUtils import at.petrak.hex.HexUtils
import at.petrak.hex.api.SpellOperator
import at.petrak.hex.common.items.ItemDataHolder
import at.petrak.hex.common.items.ItemSpellbook import at.petrak.hex.common.items.ItemSpellbook
import at.petrak.hex.common.items.ItemWand
import at.petrak.hex.common.lib.LibDamageSources
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionHand
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
import java.util.function.Predicate
import kotlin.math.max
import kotlin.math.min
/** /**
* Info about the moment the spell started being cast. * Info about the moment the spell started being cast.
*/ */
@JvmRecord
data class CastingContext( data class CastingContext(
val caster: ServerPlayer, val caster: ServerPlayer,
val wandHand: InteractionHand, val wandHand: InteractionHand,
) { ) {
private var depth: Int = 0
val world: ServerLevel get() = caster.getLevel() val world: ServerLevel get() = caster.getLevel()
val otherHand: InteractionHand get() = HexUtils.OtherHand(this.wandHand)
fun getSpellbook(): ItemStack { fun getSpellbook(): ItemStack {
val handItem = val handItem =
caster.getItemInHand(HexUtils.OtherHand(wandHand)) caster.getItemInHand(this.otherHand)
return if (handItem.item is ItemSpellbook) { return if (handItem.item is ItemSpellbook) {
handItem handItem
} else { } else {
throw CastException(CastException.Reason.REQUIRES_SPELLBOOK) throw CastException(CastException.Reason.REQUIRES_SPELLBOOK)
} }
} }
fun getDataHolder(): ItemStack {
val handItem =
caster.getItemInHand(this.otherHand)
return if (handItem.item is ItemDataHolder) {
handItem
} else {
throw CastException(CastException.Reason.REQUIRES_DATA_HOLDER)
}
}
/**
* Throws if we get too deep
*/
fun withIncDepth(): CastingContext {
val next = this.copy()
next.depth++
val maxAllowedDepth = HexMod.CONFIG.maxRecurseDepth.get()
if (next.depth > maxAllowedDepth) {
throw CastException(CastException.Reason.TOO_MANY_RECURSIVE_EVALS, maxAllowedDepth, next.depth)
} else {
return next
}
}
/**
* Might cast from hitpoints.
* Returns the mana cost still remaining after we deplete everything. It will be <= 0 if we could pay for it. */
fun withdrawMana(manaCost: Int, allowOvercast: Boolean): Int {
var costLeft = manaCost
val held = caster.getItemInHand(this.wandHand)
if (held.item is ItemWand) {
val tag = held.orCreateTag
val manaHere = tag.getInt(ItemWand.TAG_MANA)
val manaLeft = manaHere - costLeft
tag.putInt(ItemWand.TAG_MANA, max(0, manaLeft))
costLeft = max(0, costLeft - manaHere)
}
if (allowOvercast && costLeft > 0) {
// Cast from HP!
val healthToMana = HexMod.CONFIG.healthToManaRate.get()
val healthtoRemove = healthToMana * costLeft.toDouble()
val manaAbleToCastFromHP =
if (caster.isInvulnerable) Double.POSITIVE_INFINITY else caster.health / healthToMana
caster.hurt(LibDamageSources.OVERCAST, healthtoRemove.toFloat())
costLeft = (costLeft.toDouble() - manaAbleToCastFromHP).toInt()
}
return costLeft
}
fun assertVecInRange(vec: Vec3) {
if (vec.distanceToSqr(this.caster.position()) > SpellOperator.MAX_DISTANCE * SpellOperator.MAX_DISTANCE)
throw CastException(CastException.Reason.TOO_FAR, vec)
}
/**
* Return the slot from which to take blocks and items.
*/
// https://wiki.vg/Inventory is WRONG
// slots 0-8 are the hotbar
// for what purpose i cannot imagine
// http://redditpublic.com/images/b/b2/Items_slot_number.png looks right
// and offhand is 150 Inventory.java:464
fun getOperativeSlot(stackOK: Predicate<ItemStack>): Int? {
val otherHandStack = this.caster.getItemInHand(this.otherHand)
if (stackOK.test(otherHandStack)) {
return when (this.otherHand) {
InteractionHand.MAIN_HAND -> this.caster.inventory.selected
InteractionHand.OFF_HAND -> 150
}
}
val anchorSlot = when (this.wandHand) {
// slot to the right of the wand
InteractionHand.MAIN_HAND -> (this.caster.inventory.selected + 1) % 9
// first hotbar slot
InteractionHand.OFF_HAND -> 0
}
for (delta in 0 until 9) {
val slot = (anchorSlot + delta) % 9
val stack = this.caster.inventory.getItem(slot)
if (stackOK.test(stack)) {
return slot
}
}
return null
}
/**
* Remove the given gound of the specified item from somewhere in the inventory, favoring slots not in the hotbar.
* Return whether the withdrawal was successful.
*/
// https://github.com/VazkiiMods/Psi/blob/master/src/main/java/vazkii/psi/common/spell/trick/block/PieceTrickPlaceBlock.java#L143
fun withdrawItem(item: Item, count: Int, actuallyRemove: Boolean): Boolean {
if (this.caster.isCreative) return true
val inv = this.caster.inventory
// TODO: withdraw from ender chest given a specific ender charm?
val stacksToExamine = inv.items.asReversed()
fun matches(stack: ItemStack): Boolean =
!stack.isEmpty && stack.`is`(item)
val presentCount = stacksToExamine.fold(0) { acc, stack ->
acc + if (matches(stack)) stack.count else 0
}
if (presentCount < count) return false
// now that we know we have enough items, if we don't need to remove anything we're through.
if (!actuallyRemove) return true
var remaining = count
for (stack in stacksToExamine) {
if (matches(stack)) {
val toWithdraw = min(stack.count, remaining)
stack.shrink(toWithdraw)
remaining -= toWithdraw
if (remaining == 0) {
return true
}
}
}
throw RuntimeException("unreachable")
}
} }

View file

@ -8,6 +8,7 @@ import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionHand
import kotlin.math.max
/** /**
* Keeps track of a player casting a spell on the server. * Keeps track of a player casting a spell on the server.
@ -63,19 +64,23 @@ class CastingHarness private constructor(
this.escapeNext = false this.escapeNext = false
HexMod.LOGGER.info("Escaping onto stack") HexMod.LOGGER.info("Escaping onto stack")
this.stack.add(SpellDatum.make(newPat)) this.stack.add(SpellDatum.make(newPat))
} else if (operator == SpellWidget.ESCAPE) {
this.escapeNext = true
} 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 { } else {
// Plain ol operator // we know the operator is ok here
if (exn != null) { val manaCost = operator!!.modifyStack(this.stack, this.ctx)
// there was a problem finding the pattern and it was NOT due to numbers
throw exn // prevent poor impls from gaining you mana
} else if (operator == SpellWidget.OPEN_PAREN) { ctx.withdrawMana(max(0, manaCost), true)
this.parenCount++ if (ctx.caster.isDeadOrDying)
} else if (operator == SpellWidget.CLOSE_PAREN) { return CastResult.Died
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) { if (this.parenCount > 0) {
@ -172,5 +177,8 @@ class CastingHarness private constructor(
/** uh-oh */ /** uh-oh */
data class Error(val exn: CastException) : CastResult() data class Error(val exn: CastException) : CastResult()
/** YOU DIED due to casting too hard from hit points. */
object Died : CastResult()
} }
} }

View file

@ -1,6 +1,6 @@
package at.petrak.hex.common.casting package at.petrak.hex.common.casting
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
/** /**
@ -8,7 +8,7 @@ import at.petrak.hex.api.SpellOperator.Companion.spellListOf
* *
* They act as operators that push themselves. * They act as operators that push themselves.
*/ */
enum class SpellWidget : SimpleOperator { enum class SpellWidget : ConstManaOperator {
NULL, NULL,
OPEN_PAREN, CLOSE_PAREN, ESCAPE; OPEN_PAREN, CLOSE_PAREN, ESCAPE;

View file

@ -1,6 +1,6 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator import at.petrak.hex.api.SpellOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
@ -10,8 +10,9 @@ import net.minecraft.world.level.ClipContext
import net.minecraft.world.phys.HitResult import net.minecraft.world.phys.HitResult
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
object OpBlockAxisRaycast : SimpleOperator { object OpBlockAxisRaycast : ConstManaOperator {
override val argc = 2 override val argc = 2
override val manaCost = 10
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val origin: Vec3 = args.getChecked(0) val origin: Vec3 = args.getChecked(0)
val look: Vec3 = args.getChecked(1) val look: Vec3 = args.getChecked(1)

View file

@ -1,6 +1,6 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator import at.petrak.hex.api.SpellOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
@ -10,8 +10,9 @@ import net.minecraft.world.level.ClipContext
import net.minecraft.world.phys.HitResult import net.minecraft.world.phys.HitResult
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
object OpBlockRaycast : SimpleOperator { object OpBlockRaycast : ConstManaOperator {
override val argc = 2 override val argc = 2
override val manaCost = 10
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val origin: Vec3 = args.getChecked(0) val origin: Vec3 = args.getChecked(0)
val look: Vec3 = args.getChecked(1) val look: Vec3 = args.getChecked(1)

View file

@ -1,12 +1,12 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
object OpDuplicate : SimpleOperator { object OpDuplicate : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 1 get() = 1

View file

@ -1,13 +1,13 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.world.entity.Entity import net.minecraft.world.entity.Entity
object OpEntityLook : SimpleOperator { object OpEntityLook : ConstManaOperator {
override val argc = 1 override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {

View file

@ -1,6 +1,6 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
@ -8,7 +8,7 @@ import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.world.entity.Entity import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
object OpEntityPos : SimpleOperator { object OpEntityPos : ConstManaOperator {
override val argc = 1 override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {

View file

@ -1,6 +1,6 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator import at.petrak.hex.api.SpellOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
@ -10,8 +10,9 @@ import net.minecraft.world.entity.projectile.ProjectileUtil
import net.minecraft.world.phys.AABB import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
object OpEntityRaycast : SimpleOperator { object OpEntityRaycast : ConstManaOperator {
override val argc = 2 override val argc = 2
override val manaCost = 10
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val origin: Vec3 = args.getChecked(0) val origin: Vec3 = args.getChecked(0)
val look: Vec3 = args.getChecked(1) val look: Vec3 = args.getChecked(1)

View file

@ -1,19 +1,19 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.SpellOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.CastingHarness import at.petrak.hex.common.casting.CastingHarness
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
object OpEval : SimpleOperator { object OpEval : SpellOperator {
override val argc: Int override fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): Int {
get() = 1 val instrs: List<SpellDatum<*>> = stack.getChecked(stack.lastIndex)
stack.removeLastOrNull()
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { val ctxDeeper = ctx.withIncDepth()
val instrs: List<SpellDatum<*>> = args.getChecked(0) val harness = CastingHarness.Default(ctxDeeper)
harness.stack.addAll(stack)
val harness = CastingHarness.Default(ctx) stack.clear()
for (pat in instrs) { for (pat in instrs) {
val res = harness.update(pat.tryGet()) val res = harness.update(pat.tryGet())
if (res is CastingHarness.CastResult.Error) { if (res is CastingHarness.CastResult.Error) {
@ -22,6 +22,8 @@ object OpEval : SimpleOperator {
// in ANY OTHER CASE JUST KEEP GOING // in ANY OTHER CASE JUST KEEP GOING
// including if there's RenderedSpells on the stack or the stack becomes clear // including if there's RenderedSpells on the stack or the stack becomes clear
} }
return harness.stack stack.addAll(harness.stack)
return 50
} }
} }

View file

@ -1,12 +1,12 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator import at.petrak.hex.api.SpellOperator
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.world.entity.Entity import net.minecraft.world.entity.Entity
object OpGetCaster : SimpleOperator { object OpGetCaster : ConstManaOperator {
override val argc = 0 override val argc = 0
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> = override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =

View file

@ -1,18 +1,18 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import at.petrak.hex.common.casting.SpellWidget import at.petrak.hex.common.casting.SpellWidget
import at.petrak.hex.common.items.ItemSpellbook import at.petrak.hex.common.items.ItemDataHolder
object OpReadFromSpellbook : SimpleOperator { object OpRead : ConstManaOperator {
override val argc: Int override val argc = 0
get() = 0 override val manaCost = 10
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val spellbook = ctx.getSpellbook() val dataer = ctx.getDataHolder()
val datum = ItemSpellbook.ReadDatum(spellbook.orCreateTag, ctx) val datum = (dataer.item as ItemDataHolder).readDatum(dataer.orCreateTag, ctx)
return listOf(datum ?: SpellDatum.make(SpellWidget.NULL)) return listOf(datum ?: SpellDatum.make(SpellWidget.NULL))
} }
} }

View file

@ -1,12 +1,12 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
object OpSwap : SimpleOperator { object OpSwap : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 2 get() = 2

View file

@ -1,10 +1,10 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
object OpUndo : SimpleOperator { object OpUndo : ConstManaOperator {
override val argc = 1 override val argc = 1
// Do literally nothing! // Do literally nothing!

View file

@ -1,18 +1,18 @@
package at.petrak.hex.common.casting.operators package at.petrak.hex.common.casting.operators
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import at.petrak.hex.common.items.ItemSpellbook import at.petrak.hex.common.items.ItemDataHolder
object OpWriteToSpellbook : SimpleOperator { object OpWrite : ConstManaOperator {
override val argc: Int override val argc = 1
get() = 1 override val manaCost = 10
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val spellbook = ctx.getSpellbook() val dataer = ctx.getDataHolder()
ItemSpellbook.WriteDatum(spellbook.orCreateTag, args[0]) (dataer.item as ItemDataHolder).writeDatum(dataer.orCreateTag, args[0])
return spellListOf() return spellListOf()
} }
} }

View file

@ -1,12 +1,12 @@
package at.petrak.hex.common.casting.operators.math package at.petrak.hex.common.casting.operators.math
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
object OpAbsLen : SimpleOperator { object OpAbsLen : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 1 get() = 1

View file

@ -1,11 +1,11 @@
package at.petrak.hex.common.casting.operators.math package at.petrak.hex.common.casting.operators.math
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
object OpAdd : SimpleOperator { object OpAdd : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 2 get() = 2

View file

@ -1,12 +1,12 @@
package at.petrak.hex.common.casting.operators.math package at.petrak.hex.common.casting.operators.math
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
object OpDivCross : SimpleOperator { object OpDivCross : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 2 get() = 2

View file

@ -1,11 +1,11 @@
package at.petrak.hex.common.casting.operators.math package at.petrak.hex.common.casting.operators.math
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
object OpMulDot : SimpleOperator { object OpMulDot : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 2 get() = 2

View file

@ -1,13 +1,13 @@
package at.petrak.hex.common.casting.operators.math package at.petrak.hex.common.casting.operators.math
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
import kotlin.math.pow import kotlin.math.pow
object OpPowProj : SimpleOperator { object OpPowProj : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 2 get() = 2

View file

@ -1,12 +1,12 @@
package at.petrak.hex.common.casting.operators.math package at.petrak.hex.common.casting.operators.math
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
object OpSub : SimpleOperator { object OpSub : ConstManaOperator {
override val argc: Int override val argc: Int
get() = 2 get() = 2

View file

@ -18,10 +18,13 @@ object OpAddMotion : SimpleOperator, RenderedSpellImpl {
override val argc: Int override val argc: Int
get() = 2 get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<List<SpellDatum<*>>, Int> {
val target = args.getChecked<Entity>(0) val target = args.getChecked<Entity>(0)
val motion = args.getChecked<Vec3>(1) val motion = args.getChecked<Vec3>(1)
return spellListOf(RenderedSpell(OpAddMotion, spellListOf(target, motion))) return Pair(
spellListOf(RenderedSpell(OpAddMotion, spellListOf(target, motion))),
motion.lengthSqr().toInt() * 100
)
} }
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) { override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {

View file

@ -0,0 +1,43 @@
package at.petrak.hex.common.casting.operators.spells
import at.petrak.hex.HexMod
import at.petrak.hex.api.SimpleOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf
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 net.minecraft.core.BlockPos
import net.minecraft.world.phys.Vec3
import net.minecraftforge.common.TierSortingRegistry
object OpBreakBlock : SimpleOperator, RenderedSpellImpl {
override val argc: Int
get() = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<List<SpellDatum<*>>, Int> {
val pos = args.getChecked<Vec3>(0)
ctx.assertVecInRange(pos)
return Pair(
spellListOf(RenderedSpell(OpBreakBlock, spellListOf(pos))),
100
)
}
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {
val pos = BlockPos(args.getChecked<Vec3>(0))
val blockstate = ctx.world.getBlockState(pos)
val tier =
HexMod.CONFIG.opBreakHarvestLevelBecauseForgeThoughtItWasAGoodIdeaToImplementHarvestTiersUsingAnHonestToGodTopoSort
if (!blockstate.isAir && (!blockstate.requiresCorrectToolForDrops() || TierSortingRegistry.isCorrectTierForDrops(
tier,
blockstate
))
) {
ctx.world.destroyBlock(pos, true, ctx.caster)
} // TODO: else some kind of failureific particle effect?
}
}

View file

@ -1,30 +1,41 @@
package at.petrak.hex.common.casting.operators.spells package at.petrak.hex.common.casting.operators.spells
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.SimpleOperator
import at.petrak.hex.api.SpellOperator.Companion.assertVecInRange
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.RenderedSpell import at.petrak.hex.common.casting.RenderedSpell
import at.petrak.hex.common.casting.RenderedSpellImpl import at.petrak.hex.common.casting.RenderedSpellImpl
import at.petrak.hex.common.casting.SpellDatum import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.util.Mth
import net.minecraft.world.level.Explosion import net.minecraft.world.level.Explosion
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
object OpExplode : SimpleOperator, RenderedSpellImpl { object OpExplode : SimpleOperator, RenderedSpellImpl {
override val argc: Int override val argc: Int
get() = 1 get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<List<SpellDatum<*>>, Int> {
val pos = args.getChecked<Vec3>(0) val pos = args.getChecked<Vec3>(0)
assertVecInRange(pos, ctx) val strength = args.getChecked<Double>(1)
return spellListOf(RenderedSpell(OpExplode, spellListOf(pos))) ctx.assertVecInRange(pos)
return Pair(
spellListOf(RenderedSpell(OpExplode, spellListOf(pos, strength))),
(strength * 100.0).toInt(),
)
} }
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) { override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {
val pos = args.getChecked<Vec3>(0) val pos = args.getChecked<Vec3>(0)
val strength = args.getChecked<Double>(1)
// 4.0 is the strength of TNT, i guess ctx.world.explode(
ctx.world.explode(ctx.caster, pos.x, pos.y, pos.z, 4.0f, Explosion.BlockInteraction.BREAK) ctx.caster,
pos.x,
pos.y,
pos.z,
Mth.clamp(strength.toFloat(), 0f, 10f),
Explosion.BlockInteraction.BREAK
)
} }
} }

View file

@ -0,0 +1,34 @@
package at.petrak.hex.common.casting.operators.spells
import at.petrak.hex.api.SimpleOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf
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 net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.LightningBolt
import net.minecraft.world.phys.Vec3
object OpLightning : SimpleOperator, RenderedSpellImpl {
override val argc: Int
get() = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<List<SpellDatum<*>>, Int> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Pair(
spellListOf(RenderedSpell(OpLightning, spellListOf(target))),
1500
)
}
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {
val target = args.getChecked<Vec3>(0)
val lightning = LightningBolt(EntityType.LIGHTNING_BOLT, ctx.world)
lightning.setPosRaw(target.x, target.y, target.z)
ctx.world.addWithUUID(lightning) // why the hell is it called this it doesnt even involve a uuid
}
}

View file

@ -0,0 +1,71 @@
package at.petrak.hex.common.casting.operators.spells
import at.petrak.hex.api.SimpleOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf
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 net.minecraft.core.BlockPos
import net.minecraft.world.InteractionResult
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.context.UseOnContext
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.Vec3
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.common.util.BlockSnapshot
import net.minecraftforge.event.world.BlockEvent
object OpPlaceBlock : SimpleOperator, RenderedSpellImpl {
override val argc: Int
get() = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<List<SpellDatum<*>>, Int> {
val pos = args.getChecked<Vec3>(0)
ctx.assertVecInRange(pos)
return Pair(
spellListOf(RenderedSpell(OpPlaceBlock, spellListOf(pos))),
30
)
}
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {
val vec = args.getChecked<Vec3>(0)
val pos = BlockPos(vec)
val bstate = ctx.world.getBlockState(pos)
if (bstate.isAir || bstate.material.isReplaceable) {
val placeeSlot = ctx.getOperativeSlot { it.item is BlockItem }
if (placeeSlot != null) {
val placeeStack = ctx.caster.inventory.getItem(placeeSlot)
val placee = placeeStack.item as BlockItem
if (ctx.withdrawItem(placee, 1, false)) {
// https://github.com/VazkiiMods/Psi/blob/master/src/main/java/vazkii/psi/common/spell/trick/block/PieceTrickPlaceBlock.java#L143
val evt = BlockEvent.EntityPlaceEvent(
BlockSnapshot.create(ctx.world.dimension(), ctx.world, pos),
ctx.world.getBlockState(pos.above()),
ctx.caster
)
MinecraftForge.EVENT_BUS.post(evt)
// we temporarily give the player the stack, place it using mc code, then give them the old stack back.
val oldStack = ctx.caster.getItemInHand(ctx.wandHand)
val spoofedStack = placeeStack.copy()
spoofedStack.count = 1
ctx.caster.setItemInHand(ctx.wandHand, spoofedStack)
val blockHit = BlockHitResult(
Vec3.ZERO, ctx.caster.direction, pos, false
)
val itemUseCtx = UseOnContext(ctx.caster, ctx.wandHand, blockHit)
val res = spoofedStack.useOn(itemUseCtx)
ctx.caster.setItemInHand(ctx.wandHand, oldStack)
if (res != InteractionResult.FAIL) {
ctx.withdrawItem(placee, 1, true)
}
}
}
}
}
}

View file

@ -1,6 +1,6 @@
package at.petrak.hex.common.casting.operators.spells package at.petrak.hex.common.casting.operators.spells
import at.petrak.hex.api.SimpleOperator import at.petrak.hex.api.ConstManaOperator
import at.petrak.hex.api.SpellOperator.Companion.getChecked import at.petrak.hex.api.SpellOperator.Companion.getChecked
import at.petrak.hex.api.SpellOperator.Companion.spellListOf import at.petrak.hex.api.SpellOperator.Companion.spellListOf
import at.petrak.hex.common.casting.CastingContext import at.petrak.hex.common.casting.CastingContext
@ -10,7 +10,7 @@ import at.petrak.hex.common.casting.SpellDatum
import net.minecraft.Util import net.minecraft.Util
import net.minecraft.network.chat.TextComponent import net.minecraft.network.chat.TextComponent
object OpPrint : SimpleOperator, RenderedSpellImpl { object OpPrint : ConstManaOperator, RenderedSpellImpl {
override val argc = 1 override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
val datum = args.getChecked<Any>(0) val datum = args.getChecked<Any>(0)

View file

@ -2,23 +2,46 @@ package at.petrak.hex.common.items;
import at.petrak.hex.HexMod; import at.petrak.hex.HexMod;
import at.petrak.hex.common.lib.LibItemNames; import at.petrak.hex.common.lib.LibItemNames;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject; import net.minecraftforge.registries.RegistryObject;
public class HexItems { public class HexItems {
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, HexMod.MOD_ID); public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, HexMod.MOD_ID);
public static final CreativeModeTab TAB = new CreativeModeTab("hex") {
@Override
public ItemStack makeIcon() {
return new ItemStack(SPELLBOOK::get);
}
@Override
public void fillItemList(NonNullList<ItemStack> items) {
// Make the wand spawn with some sensible NBT
var tag = new CompoundTag();
tag.putInt(ItemWand.TAG_MANA, 1000);
tag.putInt(ItemWand.TAG_MAX_MANA, 1000);
var stack = new ItemStack(WAND::get);
stack.setTag(tag);
items.add(stack);
super.fillItemList(items);
}
};
public static final RegistryObject<Item> WAND = ITEMS.register(LibItemNames.WAND, public static final RegistryObject<Item> WAND = ITEMS.register(LibItemNames.WAND,
() -> new ItemWand(unstackable())); () -> new ItemWand(new Item.Properties().stacksTo(1)));
public static final RegistryObject<Item> FOCUS = ITEMS.register(LibItemNames.FOCUS, public static final RegistryObject<Item> FOCUS = ITEMS.register(LibItemNames.FOCUS,
() -> new ItemFocus(props())); () -> new ItemFocus(props()));
public static final RegistryObject<Item> SPELLBOOK = ITEMS.register(LibItemNames.SPELLBOOK, public static final RegistryObject<Item> SPELLBOOK = ITEMS.register(LibItemNames.SPELLBOOK,
() -> new ItemSpellbook(unstackable())); () -> new ItemSpellbook(unstackable()));
public static Item.Properties props() { public static Item.Properties props() {
return new Item.Properties(); return new Item.Properties().tab(TAB);
} }
public static Item.Properties unstackable() { public static Item.Properties unstackable() {

View file

@ -1,2 +1,18 @@
package at.petrak.hex.common.items;public class ItemDataDolder { package at.petrak.hex.common.items;
import at.petrak.hex.common.casting.CastingContext;
import at.petrak.hex.common.casting.SpellDatum;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.Item;
import org.jetbrains.annotations.Nullable;
abstract public class ItemDataHolder extends Item {
public ItemDataHolder(Properties pProperties) {
super(pProperties);
}
@Nullable
public abstract SpellDatum<?> readDatum(CompoundTag tag, CastingContext ctx);
public abstract void writeDatum(CompoundTag tag, SpellDatum<?> datum);
} }

View file

@ -1,13 +1,31 @@
package at.petrak.hex.common.items; package at.petrak.hex.common.items;
import at.petrak.hex.HexMod; import at.petrak.hex.HexMod;
import at.petrak.hex.common.casting.CastingContext;
import at.petrak.hex.common.casting.SpellDatum;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item; import org.jetbrains.annotations.Nullable;
public class ItemFocus extends Item { public class ItemFocus extends ItemDataHolder {
public static final ResourceLocation PREDICATE = new ResourceLocation(HexMod.MOD_ID, "datatype"); public static final ResourceLocation PREDICATE = new ResourceLocation(HexMod.MOD_ID, "datatype");
public static final String TAG_DATA = "data";
public ItemFocus(Properties pProperties) { public ItemFocus(Properties pProperties) {
super(pProperties); super(pProperties);
} }
@Override
public @Nullable SpellDatum<?> readDatum(CompoundTag tag, CastingContext ctx) {
try {
return SpellDatum.DeserializeFromNBT(tag.getCompound(TAG_DATA), ctx);
} catch (Exception e) {
return null;
}
}
@Override
public void writeDatum(CompoundTag tag, SpellDatum<?> datum) {
tag.put(TAG_DATA, datum.serializeToNBT());
}
} }

View file

@ -7,7 +7,6 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -16,7 +15,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
public class ItemSpellbook extends Item { public class ItemSpellbook extends ItemDataHolder {
public static String TAG_SELECTED_PAGE = "page_idx"; public static String TAG_SELECTED_PAGE = "page_idx";
// this is a CompoundTag of string numerical keys to SpellData\ // this is a CompoundTag of string numerical keys to SpellData\
// it is 1-indexed, so that 0/0 can be the special case of "it is empty" // it is 1-indexed, so that 0/0 can be the special case of "it is empty"
@ -64,7 +63,29 @@ public class ItemSpellbook extends Item {
tag.getCompound(ItemSpellbook.TAG_PAGES).isEmpty(); tag.getCompound(ItemSpellbook.TAG_PAGES).isEmpty();
} }
public static void WriteDatum(CompoundTag tag, SpellDatum<?> datum) {
@Nullable
public SpellDatum<?> readDatum(CompoundTag tag, CastingContext ctx) {
int idx;
if (tag.contains(TAG_SELECTED_PAGE)) {
idx = tag.getInt(TAG_SELECTED_PAGE);
} else {
idx = 0;
}
var key = String.valueOf(idx);
if (tag.contains(TAG_PAGES)) {
var pagesTag = tag.getCompound(TAG_PAGES);
if (pagesTag.contains(key)) {
return SpellDatum.DeserializeFromNBT(pagesTag.getCompound(key), ctx);
} else {
return null;
}
} else {
return null;
}
}
public void writeDatum(CompoundTag tag, SpellDatum<?> datum) {
int idx; int idx;
if (tag.contains(TAG_SELECTED_PAGE)) { if (tag.contains(TAG_SELECTED_PAGE)) {
idx = tag.getInt(TAG_SELECTED_PAGE); idx = tag.getInt(TAG_SELECTED_PAGE);
@ -85,27 +106,6 @@ public class ItemSpellbook extends Item {
} }
} }
@Nullable
public static SpellDatum<?> ReadDatum(CompoundTag tag, CastingContext ctx) {
int idx;
if (tag.contains(TAG_SELECTED_PAGE)) {
idx = tag.getInt(TAG_SELECTED_PAGE);
} else {
idx = 0;
}
var key = String.valueOf(idx);
if (tag.contains(TAG_PAGES)) {
var pagesTag = tag.getCompound(TAG_PAGES);
if (pagesTag.contains(key)) {
return SpellDatum.DeserializeFromNBT(pagesTag.getCompound(key), ctx);
} else {
return null;
}
} else {
return null;
}
}
public static int HighestPage(CompoundTag tag) { public static int HighestPage(CompoundTag tag) {
var highestKey = tag.getAllKeys().stream().flatMap(s -> { var highestKey = tag.getAllKeys().stream().flatMap(s -> {
try { try {

View file

@ -1,15 +1,22 @@
package at.petrak.hex.common.items; package at.petrak.hex.common.items;
import at.petrak.hex.HexMod;
import at.petrak.hex.client.gui.GuiSpellcasting; import at.petrak.hex.client.gui.GuiSpellcasting;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
public class ItemWand extends Item { public class ItemWand extends Item {
public static final String TAG_MANA = "mana";
public static final String TAG_MAX_MANA = "maxMana";
public static final String TAG_HARNESS = "harness";
public ItemWand(Properties pProperties) { public ItemWand(Properties pProperties) {
super(pProperties); super(pProperties);
} }
@ -22,4 +29,61 @@ public class ItemWand extends Item {
return InteractionResultHolder.success(player.getItemInHand(hand)); return InteractionResultHolder.success(player.getItemInHand(hand));
} }
@Override
public void inventoryTick(ItemStack pStack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
var tag = pStack.getOrCreateTag();
var mana = tag.getInt(TAG_MANA);
var maxMana = tag.getInt(TAG_MAX_MANA);
if (mana < maxMana) {
tag.putInt(TAG_MANA, Math.min(maxMana, mana + HexMod.CONFIG.wandRechargeRate.get()));
}
}
@Override
public boolean isDamageable(ItemStack stack) {
return false;
}
@Override
public boolean canBeDepleted() {
return false;
}
@Override
public boolean isBarVisible(ItemStack pStack) {
return true;
}
@Override
public int getBarColor(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = tag.getInt(TAG_MANA);
var maxMana = tag.getInt(TAG_MAX_MANA);
float amt;
if (maxMana == 0) {
amt = 0f;
} else {
amt = ((float) mana) / ((float) maxMana);
}
var r = Mth.lerp(amt, 149f, 112f);
var g = Mth.lerp(amt, 196f, 219f);
var b = Mth.lerp(amt, 174f, 212f);
return Mth.color(r / 255f, g / 255f, b / 255f);
}
@Override
public int getBarWidth(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = tag.getInt(TAG_MANA);
var maxMana = tag.getInt(TAG_MAX_MANA);
float amt;
if (maxMana == 0) {
amt = 0f;
} else {
amt = ((float) mana) / ((float) maxMana);
}
return Math.round(13f * amt);
}
} }

View file

@ -1,2 +1,7 @@
package at.petrak.hex.common.lib;public class LibDamage { package at.petrak.hex.common.lib;
import net.minecraft.world.damagesource.DamageSource;
public class LibDamageSources {
public static final DamageSource OVERCAST = new DamageSource("hex.overcast").bypassArmor().bypassMagic().setMagic();
} }

View file

@ -1,5 +1,6 @@
package at.petrak.hex.common.network; package at.petrak.hex.common.network;
import at.petrak.hex.client.gui.GuiSpellcasting;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
@ -27,7 +28,7 @@ public record MsgNewSpellPatternAck(boolean quitCasting) {
public void handle(Supplier<NetworkEvent.Context> ctx) { public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> { DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
if (quitCasting) { if (quitCasting && Minecraft.getInstance().screen instanceof GuiSpellcasting) {
Minecraft.getInstance().setScreen(null); Minecraft.getInstance().setScreen(null);
} }
}) })

View file

@ -42,7 +42,8 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
var held = sender.getItemInHand(this.handUsed); var held = sender.getItemInHand(this.handUsed);
if (held.getItem() instanceof ItemWand) { if (held.getItem() instanceof ItemWand) {
var tag = held.getOrCreateTag(); var tag = held.getOrCreateTag();
var harness = CastingHarness.DeserializeFromNBT(tag, sender, this.handUsed); var harness = CastingHarness.DeserializeFromNBT(tag.getCompound(ItemWand.TAG_HARNESS), sender,
this.handUsed);
var res = harness.update(this.pattern); var res = harness.update(this.pattern);
if (res instanceof CastResult.Success success) { if (res instanceof CastResult.Success success) {
@ -52,15 +53,17 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
} }
boolean quit; boolean quit;
CompoundTag nextHarnessTag;
if (res instanceof CastResult.Nothing) { if (res instanceof CastResult.Nothing) {
// save the changes // save the changes
held.setTag(harness.serializeToNBT()); nextHarnessTag = harness.serializeToNBT();
quit = false; quit = false;
} else { } else {
// Else we requested to quit in some way or another // Else we requested to quit in some way or another
held.setTag(new CompoundTag()); nextHarnessTag = new CompoundTag();
quit = true; quit = true;
} }
tag.put(ItemWand.TAG_HARNESS, nextHarnessTag);
HexMessages.getNetwork() HexMessages.getNetwork()
.send(PacketDistributor.PLAYER.with(() -> sender), .send(PacketDistributor.PLAYER.with(() -> sender),
new MsgNewSpellPatternAck(quit)); new MsgNewSpellPatternAck(quit));

View file

@ -26,7 +26,7 @@ public record MsgQuitSpellcasting() {
var held = sender.getMainHandItem(); var held = sender.getMainHandItem();
if (held.getItem() instanceof ItemWand) { if (held.getItem() instanceof ItemWand) {
// Todo: appropriate consequences for quitting a spell like this // Todo: appropriate consequences for quitting a spell like this
held.setTag(new CompoundTag()); held.getOrCreateTag().put(ItemWand.TAG_HARNESS, new CompoundTag());
} }
} }
}); });

View file

@ -1,4 +1,32 @@
package at.petrak.hex.server package at.petrak.hex.server
import net.minecraftforge.event.TickEvent
import net.minecraftforge.eventbus.api.SubscribeEvent
import java.util.*
object TickScheduler { object TickScheduler {
val tasks: MutableList<Task> = LinkedList()
fun schedule(task: Task) {
this.tasks.add(task)
}
fun schedule(ticks: Int, task: Runnable) {
this.tasks.add(Task(ticks, task))
}
@SubscribeEvent
fun onTick(evt: TickEvent.ServerTickEvent) {
this.tasks.removeIf {
it.ticks--
if (it.ticks <= 0) {
it.task.run()
true
} else {
false
}
}
}
data class Task(var ticks: Int, val task: Runnable)
} }

View file

@ -3,5 +3,7 @@
"item.hex.focus": "Focus", "item.hex.focus": "Focus",
"item.hex.spellbook": "Spellbook", "item.hex.spellbook": "Spellbook",
"hex.spellbook.tooltip.page": "Selected Page %d/%d", "hex.spellbook.tooltip.page": "Selected Page %d/%d",
"hex.spelldata.desc.entity": "Entity %s" "hex.spelldata.desc.entity": "Entity %s",
"itemGroup.hex": "Hex"
} }