3 new spells and fix a bunch of bugs, closes #1
This commit is contained in:
parent
1a3faecae9
commit
11042f3008
46 changed files with 698 additions and 156 deletions
21
original_ideas_doc.txt
Normal file
21
original_ideas_doc.txt
Normal 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
|
|
@ -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");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ import at.petrak.hex.common.casting.SpellDatum;
|
|||
import at.petrak.hex.common.casting.SpellWidget;
|
||||
import at.petrak.hex.common.casting.operators.*;
|
||||
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.OpExplode;
|
||||
import at.petrak.hex.common.casting.operators.spells.OpPrint;
|
||||
import at.petrak.hex.common.casting.operators.spells.*;
|
||||
import at.petrak.hex.common.items.HexItems;
|
||||
import at.petrak.hex.common.network.HexMessages;
|
||||
import at.petrak.hex.server.TickScheduler;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
@ -24,6 +24,14 @@ import org.apache.logging.log4j.Logger;
|
|||
public class HexMod {
|
||||
// hmm today I will use a popular logging framework :clueless:
|
||||
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";
|
||||
|
||||
|
@ -32,8 +40,10 @@ public class HexMod {
|
|||
var evbus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
evbus.register(HexMod.class);
|
||||
|
||||
HexItems.ITEMS.register(evbus);
|
||||
HexMessages.register();
|
||||
MinecraftForge.EVENT_BUS.register(TickScheduler.INSTANCE);
|
||||
}
|
||||
|
||||
// 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[]{
|
||||
// == Getters ==
|
||||
|
||||
// diamond shape to get the caster
|
||||
new Pair<>("qaq", OpGetCaster.INSTANCE),
|
||||
new Pair<>("ede", OpGetCaster.INSTANCE),
|
||||
// small triangle to get the entity pos
|
||||
new Pair<>("aa", OpEntityPos.INSTANCE),
|
||||
new Pair<>("dd", OpEntityPos.INSTANCE),
|
||||
// Arrow to get the look vector
|
||||
new Pair<>("wa", OpEntityLook.INSTANCE),
|
||||
new Pair<>("wd", OpEntityLook.INSTANCE),
|
||||
|
||||
// CCW battleaxe for block raycast
|
||||
new Pair<>("wqaawdd", OpBlockRaycast.INSTANCE),
|
||||
// and CW for axis raycast
|
||||
new Pair<>("weddwaa", OpBlockAxisRaycast.INSTANCE),
|
||||
// CCW diamond mace thing for entity raycast
|
||||
new Pair<>("weaqa", OpEntityRaycast.INSTANCE),
|
||||
|
||||
// == Modify Stack ==
|
||||
|
||||
// CCW hook for undo
|
||||
new Pair<>("a", OpUndo.INSTANCE),
|
||||
// and CW for null
|
||||
new Pair<>("d", SpellWidget.NULL),
|
||||
// Two triangles holding hands to duplicate
|
||||
new Pair<>("aadaa", OpDuplicate.INSTANCE),
|
||||
// Two opposing triangles to swap
|
||||
new Pair<>("aawdd", OpSwap.INSTANCE),
|
||||
|
||||
// == Math ==
|
||||
|
@ -82,12 +82,13 @@ public class HexMod {
|
|||
|
||||
// == Spells ==
|
||||
|
||||
// hook for debug
|
||||
new Pair<>("de", OpPrint.INSTANCE),
|
||||
new Pair<>("aq", OpPrint.INSTANCE),
|
||||
// nuclear sign for explosion
|
||||
new Pair<>("aawaawaa", OpExplode.INSTANCE),
|
||||
new Pair<>("weeewdq", OpAddMotion.INSTANCE),
|
||||
new Pair<>("qaqqqqq", OpPlaceBlock.INSTANCE),
|
||||
new Pair<>("eeeeede", OpBreakBlock.INSTANCE),
|
||||
new Pair<>("waadwawdaaweewq", OpLightning.INSTANCE),
|
||||
|
||||
// == Meta stuff ==
|
||||
new Pair<>("qqq", SpellWidget.OPEN_PAREN),
|
||||
|
@ -96,8 +97,8 @@ public class HexMod {
|
|||
// http://www.toroidalsnark.net/mkss3-pix/CalderheadJMM2014.pdf
|
||||
// eval being a space filling curve feels apt doesn't it
|
||||
new Pair<>("deaqq", OpEval.INSTANCE),
|
||||
new Pair<>("aqqqqq", OpReadFromSpellbook.INSTANCE),
|
||||
new Pair<>("deeeee", OpWriteToSpellbook.INSTANCE),
|
||||
new Pair<>("aqqqqq", OpRead.INSTANCE),
|
||||
new Pair<>("deeeee", OpWrite.INSTANCE),
|
||||
}) {
|
||||
PatternRegistry.addRegularPattern(p.getFirst(), p.getSecond());
|
||||
count++;
|
||||
|
|
|
@ -7,7 +7,7 @@ import at.petrak.hex.common.casting.SpellDatum
|
|||
/**
|
||||
* A SimpleOperator that always costs the same amount of mana.
|
||||
*/
|
||||
interface SimpleFreeOperator : SpellOperator {
|
||||
interface ConstManaOperator : SpellOperator {
|
||||
val argc: Int
|
||||
val manaCost: Int
|
||||
get() = 0
|
||||
|
|
|
@ -10,16 +10,17 @@ import at.petrak.hex.common.casting.SpellDatum
|
|||
*/
|
||||
interface SimpleOperator : SpellOperator {
|
||||
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)
|
||||
throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, this.argc, stack.size)
|
||||
val args = stack.takeLast(this.argc)
|
||||
// there's gotta be a better way to do this
|
||||
for (_idx in 0 until this.argc)
|
||||
stack.removeLast()
|
||||
val newData = this.execute(args, ctx)
|
||||
val (newData, mana) = this.execute(args, ctx)
|
||||
stack.addAll(newData)
|
||||
return mana
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
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.SpellDatum
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
@ -14,10 +13,10 @@ import net.minecraft.world.phys.Vec3
|
|||
* Implementors MUST NOT mutate the context.
|
||||
*/
|
||||
interface SpellOperator {
|
||||
val manaCost: Int
|
||||
get() = 0
|
||||
|
||||
fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext)
|
||||
/**
|
||||
* Operate on the stack and return the mana cost.
|
||||
*/
|
||||
fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): Int
|
||||
|
||||
companion object {
|
||||
// I see why vzakii did this: you can't raycast out to infinity!
|
||||
|
@ -42,15 +41,6 @@ interface SpellOperator {
|
|||
this.getChecked<T>(idx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the vector is in range of the player.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun assertVecInRange(vec: Vec3, ctx: CastingContext) {
|
||||
if (vec.distanceToSqr(ctx.caster.position()) > MAX_DISTANCE * MAX_DISTANCE)
|
||||
throw CastException(CastException.Reason.TOO_FAR, vec)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun spellListOf(vararg vs: Any): List<SpellDatum<*>> {
|
||||
val out = ArrayList<SpellDatum<*>>(vs.size)
|
||||
|
@ -61,11 +51,12 @@ interface SpellOperator {
|
|||
}
|
||||
|
||||
@JvmStatic
|
||||
fun makeConstantOp(x: SpellDatum<*>): SpellOperator = object : SimpleOperator {
|
||||
fun makeConstantOp(x: SpellDatum<*>): SpellOperator = object : ConstManaOperator {
|
||||
override val argc: Int
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ public class RegisterClientStuff {
|
|||
evt.enqueueWork(() -> ItemProperties.register(HexItems.FOCUS.get(), ItemFocus.PREDICATE,
|
||||
(stack, level, holder, holderID) -> {
|
||||
if (stack.hasTag()) {
|
||||
var tagname = stack.getTag().getAllKeys().iterator().next();
|
||||
var tagname = stack.getTag().getCompound(ItemFocus.TAG_DATA).getAllKeys().iterator().next();
|
||||
return switch (tagname) {
|
||||
case SpellDatum.TAG_ENTITY -> 1f;
|
||||
case SpellDatum.TAG_DOUBLE -> 2f;
|
||||
|
|
|
@ -57,6 +57,20 @@ class CastException(val reason: Reason, vararg val data: Any) : Exception() {
|
|||
* `no args`
|
||||
*/
|
||||
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
|
||||
|
@ -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_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_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"
|
||||
}
|
||||
}
|
|
@ -1,29 +1,166 @@
|
|||
package at.petrak.hex.common.casting
|
||||
|
||||
import at.petrak.hex.HexMod
|
||||
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.ItemWand
|
||||
import at.petrak.hex.common.lib.LibDamageSources
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.item.Item
|
||||
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.
|
||||
*/
|
||||
@JvmRecord
|
||||
data class CastingContext(
|
||||
val caster: ServerPlayer,
|
||||
val wandHand: InteractionHand,
|
||||
) {
|
||||
private var depth: Int = 0
|
||||
val world: ServerLevel get() = caster.getLevel()
|
||||
val otherHand: InteractionHand get() = HexUtils.OtherHand(this.wandHand)
|
||||
|
||||
fun getSpellbook(): ItemStack {
|
||||
val handItem =
|
||||
caster.getItemInHand(HexUtils.OtherHand(wandHand))
|
||||
caster.getItemInHand(this.otherHand)
|
||||
return if (handItem.item is ItemSpellbook) {
|
||||
handItem
|
||||
} else {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import net.minecraft.nbt.ListTag
|
|||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Keeps track of a player casting a spell on the server.
|
||||
|
@ -63,19 +64,23 @@ class CastingHarness private constructor(
|
|||
this.escapeNext = false
|
||||
HexMod.LOGGER.info("Escaping onto stack")
|
||||
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 {
|
||||
// Plain ol operator
|
||||
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)
|
||||
}
|
||||
// we know the operator is ok here
|
||||
val manaCost = operator!!.modifyStack(this.stack, this.ctx)
|
||||
|
||||
// prevent poor impls from gaining you mana
|
||||
ctx.withdrawMana(max(0, manaCost), true)
|
||||
if (ctx.caster.isDeadOrDying)
|
||||
return CastResult.Died
|
||||
}
|
||||
|
||||
if (this.parenCount > 0) {
|
||||
|
@ -172,5 +177,8 @@ class CastingHarness private constructor(
|
|||
|
||||
/** uh-oh */
|
||||
data class Error(val exn: CastException) : CastResult()
|
||||
|
||||
/** YOU DIED due to casting too hard from hit points. */
|
||||
object Died : CastResult()
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
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
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,7 @@ import at.petrak.hex.api.SpellOperator.Companion.spellListOf
|
|||
*
|
||||
* They act as operators that push themselves.
|
||||
*/
|
||||
enum class SpellWidget : SimpleOperator {
|
||||
enum class SpellWidget : ConstManaOperator {
|
||||
NULL,
|
||||
OPEN_PAREN, CLOSE_PAREN, ESCAPE;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.Companion.getChecked
|
||||
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.Vec3
|
||||
|
||||
object OpBlockAxisRaycast : SimpleOperator {
|
||||
object OpBlockAxisRaycast : ConstManaOperator {
|
||||
override val argc = 2
|
||||
override val manaCost = 10
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val origin: Vec3 = args.getChecked(0)
|
||||
val look: Vec3 = args.getChecked(1)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.Companion.getChecked
|
||||
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.Vec3
|
||||
|
||||
object OpBlockRaycast : SimpleOperator {
|
||||
object OpBlockRaycast : ConstManaOperator {
|
||||
override val argc = 2
|
||||
override val manaCost = 10
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val origin: Vec3 = args.getChecked(0)
|
||||
val look: Vec3 = args.getChecked(1)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
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.spellListOf
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
|
||||
object OpDuplicate : SimpleOperator {
|
||||
object OpDuplicate : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 1
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
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.spellListOf
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpEntityLook : SimpleOperator {
|
||||
object OpEntityLook : ConstManaOperator {
|
||||
override val argc = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.spellListOf
|
||||
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.player.Player
|
||||
|
||||
object OpEntityPos : SimpleOperator {
|
||||
object OpEntityPos : ConstManaOperator {
|
||||
override val argc = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.Companion.getChecked
|
||||
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.Vec3
|
||||
|
||||
object OpEntityRaycast : SimpleOperator {
|
||||
object OpEntityRaycast : ConstManaOperator {
|
||||
override val argc = 2
|
||||
override val manaCost = 10
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val origin: Vec3 = args.getChecked(0)
|
||||
val look: Vec3 = args.getChecked(1)
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.CastingHarness
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
|
||||
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)
|
||||
object OpEval : SpellOperator {
|
||||
override fun modifyStack(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): Int {
|
||||
val instrs: List<SpellDatum<*>> = stack.getChecked(stack.lastIndex)
|
||||
stack.removeLastOrNull()
|
||||
val ctxDeeper = ctx.withIncDepth()
|
||||
val harness = CastingHarness.Default(ctxDeeper)
|
||||
harness.stack.addAll(stack)
|
||||
stack.clear()
|
||||
for (pat in instrs) {
|
||||
val res = harness.update(pat.tryGet())
|
||||
if (res is CastingHarness.CastResult.Error) {
|
||||
|
@ -22,6 +22,8 @@ object OpEval : SimpleOperator {
|
|||
// in ANY OTHER CASE JUST KEEP GOING
|
||||
// including if there's RenderedSpells on the stack or the stack becomes clear
|
||||
}
|
||||
return harness.stack
|
||||
stack.addAll(harness.stack)
|
||||
|
||||
return 50
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpGetCaster : SimpleOperator {
|
||||
object OpGetCaster : ConstManaOperator {
|
||||
override val argc = 0
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
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.SpellDatum
|
||||
import at.petrak.hex.common.casting.SpellWidget
|
||||
import at.petrak.hex.common.items.ItemSpellbook
|
||||
import at.petrak.hex.common.items.ItemDataHolder
|
||||
|
||||
object OpReadFromSpellbook : SimpleOperator {
|
||||
override val argc: Int
|
||||
get() = 0
|
||||
object OpRead : ConstManaOperator {
|
||||
override val argc = 0
|
||||
override val manaCost = 10
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val spellbook = ctx.getSpellbook()
|
||||
val datum = ItemSpellbook.ReadDatum(spellbook.orCreateTag, ctx)
|
||||
val dataer = ctx.getDataHolder()
|
||||
val datum = (dataer.item as ItemDataHolder).readDatum(dataer.orCreateTag, ctx)
|
||||
return listOf(datum ?: SpellDatum.make(SpellWidget.NULL))
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
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.spellListOf
|
||||
import at.petrak.hex.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
|
||||
object OpSwap : SimpleOperator {
|
||||
object OpSwap : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
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.SpellDatum
|
||||
|
||||
object OpUndo : SimpleOperator {
|
||||
object OpUndo : ConstManaOperator {
|
||||
override val argc = 1
|
||||
|
||||
// Do literally nothing!
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import at.petrak.hex.common.items.ItemSpellbook
|
||||
import at.petrak.hex.common.items.ItemDataHolder
|
||||
|
||||
object OpWriteToSpellbook : SimpleOperator {
|
||||
override val argc: Int
|
||||
get() = 1
|
||||
object OpWrite : ConstManaOperator {
|
||||
override val argc = 1
|
||||
override val manaCost = 10
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val spellbook = ctx.getSpellbook()
|
||||
ItemSpellbook.WriteDatum(spellbook.orCreateTag, args[0])
|
||||
val dataer = ctx.getDataHolder()
|
||||
(dataer.item as ItemDataHolder).writeDatum(dataer.orCreateTag, args[0])
|
||||
return spellListOf()
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
object OpAbsLen : SimpleOperator {
|
||||
object OpAbsLen : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 1
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
|
||||
object OpAdd : SimpleOperator {
|
||||
object OpAdd : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpDivCross : SimpleOperator {
|
||||
object OpDivCross : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
|
||||
object OpMulDot : SimpleOperator {
|
||||
object OpMulDot : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.pow
|
||||
|
||||
object OpPowProj : SimpleOperator {
|
||||
object OpPowProj : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
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.common.casting.CastingContext
|
||||
import at.petrak.hex.common.casting.SpellDatum
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpSub : SimpleOperator {
|
||||
object OpSub : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
|
|
|
@ -18,10 +18,13 @@ object OpAddMotion : SimpleOperator, RenderedSpellImpl {
|
|||
override val argc: Int
|
||||
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 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) {
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
}
|
|
@ -1,30 +1,41 @@
|
|||
package at.petrak.hex.common.casting.operators.spells
|
||||
|
||||
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.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.util.Mth
|
||||
import net.minecraft.world.level.Explosion
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpExplode : SimpleOperator, RenderedSpellImpl {
|
||||
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)
|
||||
assertVecInRange(pos, ctx)
|
||||
return spellListOf(RenderedSpell(OpExplode, spellListOf(pos)))
|
||||
val strength = args.getChecked<Double>(1)
|
||||
ctx.assertVecInRange(pos)
|
||||
return Pair(
|
||||
spellListOf(RenderedSpell(OpExplode, spellListOf(pos, strength))),
|
||||
(strength * 100.0).toInt(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun cast(args: List<SpellDatum<*>>, ctx: CastingContext) {
|
||||
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.caster, pos.x, pos.y, pos.z, 4.0f, Explosion.BlockInteraction.BREAK)
|
||||
ctx.world.explode(
|
||||
ctx.caster,
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z,
|
||||
Mth.clamp(strength.toFloat(), 0f, 10f),
|
||||
Explosion.BlockInteraction.BREAK
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
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.spellListOf
|
||||
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.network.chat.TextComponent
|
||||
|
||||
object OpPrint : SimpleOperator, RenderedSpellImpl {
|
||||
object OpPrint : ConstManaOperator, RenderedSpellImpl {
|
||||
override val argc = 1
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
|
||||
val datum = args.getChecked<Any>(0)
|
||||
|
|
|
@ -2,23 +2,46 @@ package at.petrak.hex.common.items;
|
|||
|
||||
import at.petrak.hex.HexMod;
|
||||
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.ItemStack;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
|
||||
public class HexItems {
|
||||
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,
|
||||
() -> new ItemWand(unstackable()));
|
||||
() -> new ItemWand(new Item.Properties().stacksTo(1)));
|
||||
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();
|
||||
return new Item.Properties().tab(TAB);
|
||||
}
|
||||
|
||||
public static Item.Properties unstackable() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,31 @@
|
|||
package at.petrak.hex.common.items;
|
||||
|
||||
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.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 String TAG_DATA = "data";
|
||||
|
||||
public ItemFocus(Properties 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import net.minecraft.network.chat.Component;
|
|||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
@ -16,7 +15,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ItemSpellbook extends Item {
|
||||
public class ItemSpellbook extends ItemDataHolder {
|
||||
public static String TAG_SELECTED_PAGE = "page_idx";
|
||||
// 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"
|
||||
|
@ -64,7 +63,29 @@ public class ItemSpellbook extends Item {
|
|||
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;
|
||||
if (tag.contains(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) {
|
||||
var highestKey = tag.getAllKeys().stream().flatMap(s -> {
|
||||
try {
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
package at.petrak.hex.common.items;
|
||||
|
||||
import at.petrak.hex.HexMod;
|
||||
import at.petrak.hex.client.gui.GuiSpellcasting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
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) {
|
||||
super(pProperties);
|
||||
}
|
||||
|
@ -22,4 +29,61 @@ public class ItemWand extends Item {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package at.petrak.hex.common.network;
|
||||
|
||||
import at.petrak.hex.client.gui.GuiSpellcasting;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
@ -27,7 +28,7 @@ public record MsgNewSpellPatternAck(boolean quitCasting) {
|
|||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(() ->
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
|
||||
if (quitCasting) {
|
||||
if (quitCasting && Minecraft.getInstance().screen instanceof GuiSpellcasting) {
|
||||
Minecraft.getInstance().setScreen(null);
|
||||
}
|
||||
})
|
||||
|
|
|
@ -42,7 +42,8 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
|
|||
var held = sender.getItemInHand(this.handUsed);
|
||||
if (held.getItem() instanceof ItemWand) {
|
||||
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);
|
||||
if (res instanceof CastResult.Success success) {
|
||||
|
@ -52,15 +53,17 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
|
|||
}
|
||||
|
||||
boolean quit;
|
||||
CompoundTag nextHarnessTag;
|
||||
if (res instanceof CastResult.Nothing) {
|
||||
// save the changes
|
||||
held.setTag(harness.serializeToNBT());
|
||||
nextHarnessTag = harness.serializeToNBT();
|
||||
quit = false;
|
||||
} else {
|
||||
// Else we requested to quit in some way or another
|
||||
held.setTag(new CompoundTag());
|
||||
nextHarnessTag = new CompoundTag();
|
||||
quit = true;
|
||||
}
|
||||
tag.put(ItemWand.TAG_HARNESS, nextHarnessTag);
|
||||
HexMessages.getNetwork()
|
||||
.send(PacketDistributor.PLAYER.with(() -> sender),
|
||||
new MsgNewSpellPatternAck(quit));
|
||||
|
|
|
@ -26,7 +26,7 @@ public record MsgQuitSpellcasting() {
|
|||
var held = sender.getMainHandItem();
|
||||
if (held.getItem() instanceof ItemWand) {
|
||||
// Todo: appropriate consequences for quitting a spell like this
|
||||
held.setTag(new CompoundTag());
|
||||
held.getOrCreateTag().put(ItemWand.TAG_HARNESS, new CompoundTag());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,32 @@
|
|||
package at.petrak.hex.server
|
||||
|
||||
import net.minecraftforge.event.TickEvent
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import java.util.*
|
||||
|
||||
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)
|
||||
}
|
|
@ -3,5 +3,7 @@
|
|||
"item.hex.focus": "Focus",
|
||||
"item.hex.spellbook": "Spellbook",
|
||||
"hex.spellbook.tooltip.page": "Selected Page %d/%d",
|
||||
"hex.spelldata.desc.entity": "Entity %s"
|
||||
"hex.spelldata.desc.entity": "Entity %s",
|
||||
|
||||
"itemGroup.hex": "Hex"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue