finally banged the evaluator into shape, minus a couple rough edges (eval)
This commit is contained in:
parent
9cf873a40c
commit
72f2c662ef
39 changed files with 418 additions and 357 deletions
|
@ -17,3 +17,8 @@ colorizer = [12161791]
|
|||
[Alwinfy]
|
||||
#colorizer = [0x3327e3]
|
||||
colorizer = [3352547]
|
||||
|
||||
[Dev]
|
||||
# 0xff0000 0x00ff00 0x0000ff
|
||||
# For testing purposes
|
||||
colorizer = [16711680, 65280, 255]
|
||||
|
|
|
@ -17,7 +17,8 @@ interface ConstManaOperator : Operator {
|
|||
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
|
||||
if (this.argc > stack.size)
|
||||
throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, this.argc, stack.size)
|
||||
val args = stack.dropLast(this.argc)
|
||||
val args = stack.takeLast(this.argc)
|
||||
for (_i in 0 until this.argc) stack.removeLast()
|
||||
val newData = this.execute(args, ctx)
|
||||
stack.addAll(newData)
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import at.petrak.hexcasting.common.casting.CastingContext
|
|||
import at.petrak.hexcasting.common.casting.Widget
|
||||
import at.petrak.hexcasting.hexmath.HexPattern
|
||||
import net.minecraft.nbt.*
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.TextComponent
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
|
@ -71,12 +73,12 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
append(']')
|
||||
}
|
||||
|
||||
fun display(): String = buildString {
|
||||
fun display(): Component = TextComponent(buildString {
|
||||
when (val pl = this@SpellDatum.payload) {
|
||||
is List<*> -> {
|
||||
append("[")
|
||||
for ((i, v) in pl.withIndex()) {
|
||||
append((v as SpellDatum<*>).display())
|
||||
append((v as SpellDatum<*>).display().contents)
|
||||
if (i != pl.lastIndex) {
|
||||
append(", ")
|
||||
}
|
||||
|
@ -93,7 +95,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
append("HexPattern(")
|
||||
append(pl.startDir)
|
||||
append(" ")
|
||||
append(pl.angles)
|
||||
append(pl.anglesSignature())
|
||||
append(")")
|
||||
}
|
||||
is Entity -> {
|
||||
|
@ -104,7 +106,7 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
|
|||
}
|
||||
else -> append(pl.toString())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
|
|
|
@ -13,7 +13,8 @@ interface SpellOperator : Operator {
|
|||
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
|
||||
if (this.argc > stack.size)
|
||||
throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, this.argc, stack.size)
|
||||
val args = stack.dropLast(this.argc)
|
||||
val args = stack.takeLast(this.argc)
|
||||
for (_i in 0 until this.argc) stack.removeLast()
|
||||
val (spell, mana, particlePoses) = this.execute(args, ctx)
|
||||
|
||||
val sideEffects = mutableListOf(
|
||||
|
|
|
@ -41,9 +41,9 @@ public class HexAdditionalRenderers {
|
|||
var mc = Minecraft.getInstance();
|
||||
var playerPos = mc.gameRenderer.getMainCamera().getPosition();
|
||||
ps.translate(
|
||||
sentinel.position.x - playerPos.x + 0.5,
|
||||
sentinel.position.y - playerPos.y + 0.5,
|
||||
sentinel.position.z - playerPos.z + 0.5);
|
||||
sentinel.position.x - playerPos.x,
|
||||
sentinel.position.y - playerPos.y,
|
||||
sentinel.position.z - playerPos.z);
|
||||
|
||||
var time = mc.level.getLevelData().getGameTime() + partialTicks;
|
||||
var bobSpeed = 1f / 20;
|
||||
|
|
|
@ -3,7 +3,7 @@ package at.petrak.hexcasting.client.gui
|
|||
import at.petrak.hexcasting.HexUtils
|
||||
import at.petrak.hexcasting.HexUtils.TAU
|
||||
import at.petrak.hexcasting.client.RenderLib
|
||||
import at.petrak.hexcasting.common.items.HexItems
|
||||
import at.petrak.hexcasting.common.casting.ControllerInfo
|
||||
import at.petrak.hexcasting.common.items.ItemSpellbook
|
||||
import at.petrak.hexcasting.common.lib.HexSounds
|
||||
import at.petrak.hexcasting.common.network.HexMessages
|
||||
|
@ -21,6 +21,7 @@ import net.minecraft.client.renderer.GameRenderer
|
|||
import net.minecraft.client.resources.sounds.AbstractSoundInstance
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.resources.sounds.SoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.TextComponent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.Mth
|
||||
|
@ -38,13 +39,19 @@ class GuiSpellcasting(private val handOpenedWith: InteractionHand) : Screen(Text
|
|||
|
||||
private var ambianceSoundInstance: AbstractSoundInstance? = null
|
||||
|
||||
private var stackDescs: List<String> = emptyList()
|
||||
private var stackDescs: List<Component> = emptyList()
|
||||
|
||||
fun recvServerUpdate(stackDescs: List<String>, prevPatternBad: Boolean) {
|
||||
fun recvServerUpdate(info: ControllerInfo, stackDescs: List<Component>) {
|
||||
this.stackDescs = stackDescs
|
||||
this.patterns.lastOrNull()?.let { it.valid = if (prevPatternBad) PatternValidity.ERROR else PatternValidity.OK }
|
||||
this.patterns.lastOrNull()?.let {
|
||||
it.valid = if (info.status == ControllerInfo.Status.PREV_PATTERN_INVALID)
|
||||
PatternValidity.ERROR
|
||||
else
|
||||
PatternValidity.OK
|
||||
}
|
||||
|
||||
val sound = if (prevPatternBad) HexSounds.FAIL_PATTERN.get() else HexSounds.ADD_PATTERN.get()
|
||||
val sound =
|
||||
if (info.status == ControllerInfo.Status.PREV_PATTERN_INVALID) HexSounds.FAIL_PATTERN.get() else HexSounds.ADD_PATTERN.get()
|
||||
Minecraft.getInstance().soundManager.play(
|
||||
SimpleSoundInstance.forUI(
|
||||
sound,
|
||||
|
@ -288,14 +295,14 @@ class GuiSpellcasting(private val handOpenedWith: InteractionHand) : Screen(Text
|
|||
RenderSystem.enableDepthTest()
|
||||
|
||||
val mc = Minecraft.getInstance()
|
||||
if (mc.player?.getItemInHand(HexUtils.OtherHand(handOpenedWith))?.`is`(HexItems.SCRYING_LENS.get()) == true) {
|
||||
// if (mc.player?.getItemInHand(HexUtils.OtherHand(handOpenedWith))?.`is`(HexItems.SCRYING_LENS.get()) == true) {
|
||||
|
||||
val font = mc.font
|
||||
for ((i, s) in this.stackDescs.withIndex()) {
|
||||
val offsetIdx = this.stackDescs.size - i - 1
|
||||
font.draw(poseStack, s, 10f, 10f + 9f * offsetIdx, -1)
|
||||
}
|
||||
val font = mc.font
|
||||
for ((i, s) in this.stackDescs.withIndex()) {
|
||||
val offsetIdx = this.stackDescs.size - i - 1
|
||||
font.draw(poseStack, s, 10f, 10f + 9f * offsetIdx, -1)
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
// why the hell is this default true
|
||||
|
|
|
@ -20,17 +20,15 @@ import net.minecraft.world.InteractionHand
|
|||
import net.minecraft.world.item.ItemStack
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.nextInt
|
||||
|
||||
/**
|
||||
* Keeps track of a player casting a spell on the server.
|
||||
* It's stored as NBT on the wand.
|
||||
*/
|
||||
class CastingHarness private constructor(
|
||||
val stack: MutableList<SpellDatum<*>>,
|
||||
var stack: MutableList<SpellDatum<*>>,
|
||||
var parenCount: Int,
|
||||
var parenthesized: MutableList<HexPattern>,
|
||||
var parenthesized: List<HexPattern>,
|
||||
var escapeNext: Boolean,
|
||||
val ctx: CastingContext,
|
||||
val prepackagedColorizer: ItemStack?
|
||||
|
@ -38,84 +36,179 @@ class CastingHarness private constructor(
|
|||
|
||||
constructor(ctx: CastingContext) : this(mutableListOf(), 0, mutableListOf(), false, ctx, null)
|
||||
|
||||
/**
|
||||
* Given a pattern, do all the updating/side effects/etc required.
|
||||
*/
|
||||
fun executeNewPattern(newPat: HexPattern, world: ServerLevel): ControllerInfo {
|
||||
val result = this.getUpdate(newPat, world)
|
||||
this.applyFunctionalData(result.newData)
|
||||
return this.performSideEffects(result.sideEffects)
|
||||
}
|
||||
|
||||
/**
|
||||
* When the server gets a packet from the client with a new pattern,
|
||||
* handle it.
|
||||
* handle it functionally.
|
||||
*/
|
||||
fun update(newPat: HexPattern, world: ServerLevel): CastResult {
|
||||
return try {
|
||||
var exn: CastException? = null
|
||||
val operator = try {
|
||||
PatternRegistry.matchPattern(newPat, world)
|
||||
} catch (e: CastException) {
|
||||
exn = e
|
||||
null
|
||||
}
|
||||
if (this.parenCount > 0) {
|
||||
if (this.escapeNext) {
|
||||
this.escapeNext = false
|
||||
this.parenthesized.add(newPat)
|
||||
} else if (operator == Widget.ESCAPE) {
|
||||
this.escapeNext = true
|
||||
} else if (operator == Widget.OPEN_PAREN) {
|
||||
// we have escaped the parens onto the stack; we just also record our count.
|
||||
this.parenthesized.add(newPat)
|
||||
this.parenCount++
|
||||
} else if (operator == Widget.CLOSE_PAREN) {
|
||||
this.parenCount--
|
||||
if (this.parenCount == 0) {
|
||||
this.stack.add(SpellDatum.make(this.parenthesized.map { SpellDatum.make(it) }))
|
||||
this.parenthesized.clear()
|
||||
} else if (this.parenCount < 0) {
|
||||
throw CastException(CastException.Reason.TOO_MANY_CLOSE_PARENS)
|
||||
} else {
|
||||
// we have this situation: "(()"
|
||||
// we need to add the close paren
|
||||
this.parenthesized.add(newPat)
|
||||
}
|
||||
} else {
|
||||
this.parenthesized.add(newPat)
|
||||
}
|
||||
} else if (this.escapeNext) {
|
||||
this.escapeNext = false
|
||||
this.stack.add(SpellDatum.make(newPat))
|
||||
} else if (operator == Widget.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 == Widget.OPEN_PAREN) {
|
||||
this.parenCount++
|
||||
} else if (operator == Widget.CLOSE_PAREN) {
|
||||
throw CastException(CastException.Reason.TOO_MANY_CLOSE_PARENS)
|
||||
} else {
|
||||
// we know the operator is ok here
|
||||
val (stackPrime, sideEffects) = operator!!.operate(this.stack, this.ctx)
|
||||
|
||||
|
||||
fun getUpdate(newPat: HexPattern, world: ServerLevel): CastResult {
|
||||
try {
|
||||
// wouldn't it be nice to be able to go paren'
|
||||
// i guess i'll call it paren2
|
||||
val paren2 = this.handleParentheses(newPat)
|
||||
if (paren2 != null) {
|
||||
return CastResult(
|
||||
paren2,
|
||||
listOf(),
|
||||
)
|
||||
}
|
||||
|
||||
if (spellsToCast.isNotEmpty()) {
|
||||
CastResult.Cast(spellsToCast, this.stack.isEmpty())
|
||||
} else if (this.stack.isEmpty() && !this.escapeNext) {
|
||||
if (this.parenCount == 0) {
|
||||
CastResult.QuitCasting
|
||||
} else {
|
||||
CastResult.Continue(false)
|
||||
}
|
||||
} else {
|
||||
CastResult.Continue(false)
|
||||
}
|
||||
// Don't catch this one
|
||||
val operator = PatternRegistry.matchPattern(newPat, world)
|
||||
val (stack2, sideEffectsUnmut) = operator.operate(this.stack.toMutableList(), this.ctx)
|
||||
// Stick a poofy particle effect at the caster position
|
||||
val sideEffects = sideEffectsUnmut.toMutableList()
|
||||
sideEffects.add(OperatorSideEffect.Particles(this.ctx.position))
|
||||
|
||||
val fd = this.getFunctionalData().copy(
|
||||
stack = stack2,
|
||||
)
|
||||
|
||||
return CastResult(
|
||||
fd,
|
||||
sideEffects,
|
||||
)
|
||||
} catch (exn: CastException) {
|
||||
return CastResult(
|
||||
this.getFunctionalData(),
|
||||
listOf(OperatorSideEffect.Mishap(exn)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the side effects of a cast, and then tell the client what to think about it.
|
||||
*/
|
||||
fun performSideEffects(sideEffects: List<OperatorSideEffect>): ControllerInfo {
|
||||
var status = ControllerInfo.Status.NONE
|
||||
for (haskellProgrammersShakingandCryingRN in sideEffects) {
|
||||
if (haskellProgrammersShakingandCryingRN is OperatorSideEffect.Mishap)
|
||||
status = ControllerInfo.Status.PREV_PATTERN_INVALID
|
||||
|
||||
val mustStop = haskellProgrammersShakingandCryingRN.performEffect(this)
|
||||
if (mustStop)
|
||||
break
|
||||
|
||||
if (haskellProgrammersShakingandCryingRN is OperatorSideEffect.AttemptSpell)
|
||||
status = ControllerInfo.Status.SPELL_CAST
|
||||
}
|
||||
|
||||
if (status == ControllerInfo.Status.SPELL_CAST && this.stack.isEmpty())
|
||||
status = ControllerInfo.Status.SPELL_CAST_AND_DONE
|
||||
|
||||
return ControllerInfo(
|
||||
status,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the functional update represented by the current state (for use with `copy`)
|
||||
*/
|
||||
private fun getFunctionalData() = FunctionalData(
|
||||
this.stack.toList(),
|
||||
this.parenCount,
|
||||
this.parenthesized.toList(),
|
||||
this.escapeNext,
|
||||
)
|
||||
|
||||
/**
|
||||
* Apply the functional update.
|
||||
*/
|
||||
private fun applyFunctionalData(data: FunctionalData) {
|
||||
this.stack.clear()
|
||||
this.stack.addAll(data.stack)
|
||||
this.parenCount = data.parenCount
|
||||
this.parenthesized = data.parenthesized
|
||||
this.escapeNext = data.escapeNext
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a non-null value if we handled this in some sort of parenthesey way,
|
||||
* either escaping it onto the stack or changing the parenthese-handling state.
|
||||
*/
|
||||
private fun handleParentheses(newPat: HexPattern): FunctionalData? {
|
||||
val operator = try {
|
||||
PatternRegistry.matchPattern(newPat, this.ctx.world)
|
||||
} catch (e: CastException) {
|
||||
if (e.reason == CastException.Reason.INVALID_PATTERN) {
|
||||
val idx = Random.nextInt(0..this.stack.size)
|
||||
this.stack.add(idx, SpellDatum.make(Widget.GARBAGE))
|
||||
CastResult.Continue(true)
|
||||
} else {
|
||||
CastResult.Error(e)
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
return if (this.parenCount > 0) {
|
||||
if (this.escapeNext) {
|
||||
val newParens = this.parenthesized.toMutableList()
|
||||
newParens.add(newPat)
|
||||
this.getFunctionalData().copy(
|
||||
escapeNext = false,
|
||||
parenthesized = newParens
|
||||
)
|
||||
} else if (operator == Widget.ESCAPE) {
|
||||
this.getFunctionalData().copy(
|
||||
escapeNext = true,
|
||||
)
|
||||
} else if (operator == Widget.OPEN_PAREN) {
|
||||
// we have escaped the parens onto the stack; we just also record our count.
|
||||
val newParens = this.parenthesized.toMutableList()
|
||||
newParens.add(newPat)
|
||||
this.getFunctionalData().copy(
|
||||
parenthesized = newParens,
|
||||
parenCount = this.parenCount + 1
|
||||
)
|
||||
} else if (operator == Widget.CLOSE_PAREN) {
|
||||
val newParenCount = this.parenCount - 1
|
||||
if (newParenCount == 0) {
|
||||
val newStack = this.stack.toMutableList()
|
||||
newStack.add(SpellDatum.make(this.parenthesized.map { SpellDatum.make(it) }))
|
||||
this.getFunctionalData().copy(
|
||||
stack = newStack,
|
||||
parenCount = newParenCount,
|
||||
parenthesized = listOf()
|
||||
)
|
||||
} else if (newParenCount < 0) {
|
||||
throw CastException(CastException.Reason.TOO_MANY_CLOSE_PARENS)
|
||||
} else {
|
||||
// we have this situation: "(()"
|
||||
// we need to add the close paren
|
||||
val newParens = this.parenthesized.toMutableList()
|
||||
newParens.add(newPat)
|
||||
this.getFunctionalData().copy(
|
||||
parenCount = newParenCount,
|
||||
parenthesized = newParens
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val newParens = this.parenthesized.toMutableList()
|
||||
newParens.add(newPat)
|
||||
this.getFunctionalData().copy(
|
||||
parenthesized = newParens
|
||||
)
|
||||
}
|
||||
} else if (this.escapeNext) {
|
||||
val newStack = this.stack.toMutableList()
|
||||
newStack.add(SpellDatum.make(newPat))
|
||||
this.getFunctionalData().copy(
|
||||
stack = newStack,
|
||||
escapeNext = false,
|
||||
)
|
||||
} else if (operator == Widget.ESCAPE) {
|
||||
this.getFunctionalData().copy(
|
||||
escapeNext = true
|
||||
)
|
||||
} else if (operator == Widget.OPEN_PAREN) {
|
||||
this.getFunctionalData().copy(
|
||||
parenCount = this.parenCount + 1
|
||||
)
|
||||
} else if (operator == Widget.CLOSE_PAREN) {
|
||||
throw CastException(CastException.Reason.TOO_MANY_CLOSE_PARENS)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,35 +351,8 @@ class CastingHarness private constructor(
|
|||
}
|
||||
}
|
||||
|
||||
sealed class CastResult {
|
||||
/** Casting still in progress */
|
||||
data class Continue(val prevPatternBad: Boolean) : CastResult()
|
||||
|
||||
/** Non-catastrophic quit */
|
||||
object QuitCasting : CastResult()
|
||||
|
||||
/** Finished casting */
|
||||
data class Cast(val sideEffects: List<OperatorSideEffect>, val quit: Boolean) : CastResult()
|
||||
|
||||
/** uh-oh */
|
||||
data class Error(val exn: CastException) : CastResult()
|
||||
|
||||
/** YOU DIED due to casting too hard from hit points. */
|
||||
object Died : CastResult()
|
||||
|
||||
fun quitStatus(): QuitStatus =
|
||||
when (this) {
|
||||
QuitCasting, Died, is Error -> QuitStatus.QUIT
|
||||
is Cast -> if (this.quit) QuitStatus.QUIT else QuitStatus.OK
|
||||
is Continue -> if (this.prevPatternBad) QuitStatus.LAST_PATTERN_INVALID else QuitStatus.OK
|
||||
}
|
||||
}
|
||||
|
||||
enum class QuitStatus {
|
||||
OK,
|
||||
|
||||
/** Keep going, but the pattern you just drew was sussy */
|
||||
LAST_PATTERN_INVALID,
|
||||
QUIT
|
||||
}
|
||||
data class CastResult(
|
||||
val newData: FunctionalData,
|
||||
val sideEffects: List<OperatorSideEffect>,
|
||||
)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package at.petrak.hexcasting.common.casting
|
||||
|
||||
/**
|
||||
* Information for the sake of the GUI.
|
||||
*/
|
||||
data class ControllerInfo(val status: Status) {
|
||||
enum class Status {
|
||||
NONE,
|
||||
SPELL_CAST,
|
||||
SPELL_CAST_AND_DONE,
|
||||
PREV_PATTERN_INVALID,
|
||||
}
|
||||
|
||||
fun shouldQuit(): Boolean = this.status == Status.SPELL_CAST_AND_DONE
|
||||
fun wasSpellCast(): Boolean = this.status == Status.SPELL_CAST || this.status == Status.SPELL_CAST_AND_DONE
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package at.petrak.hexcasting.common.casting
|
||||
|
||||
import at.petrak.hexcasting.api.SpellDatum
|
||||
import at.petrak.hexcasting.hexmath.HexPattern
|
||||
|
||||
/**
|
||||
* A change to the data in a CastHarness after a pattern is drawn.
|
||||
*
|
||||
* [wasThisPatternInvalid] is for the benefit of the controller.
|
||||
*/
|
||||
data class FunctionalData(
|
||||
val stack: List<SpellDatum<*>>,
|
||||
val parenCount: Int,
|
||||
val parenthesized: List<HexPattern>,
|
||||
val escapeNext: Boolean,
|
||||
) {
|
||||
/**
|
||||
* Whether this by itself is enough to get the client to quit casting.
|
||||
*
|
||||
* Note the client may want to quit for other reasons.
|
||||
*/
|
||||
fun shouldQuit(): Boolean =
|
||||
this.stack.isEmpty() && this.parenCount == 0 && !this.escapeNext
|
||||
}
|
|
@ -7,6 +7,7 @@ import at.petrak.hexcasting.datagen.Advancements
|
|||
import com.mojang.math.Vector3f
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.core.particles.DustParticleOptions
|
||||
import net.minecraft.network.chat.TextComponent
|
||||
import net.minecraft.network.chat.TranslatableComponent
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
@ -55,21 +56,23 @@ sealed class OperatorSideEffect {
|
|||
override fun performEffect(harness: CastingHarness): Boolean {
|
||||
val colorizer = harness.getColorizer()
|
||||
|
||||
for (i in 0 until 6) {
|
||||
for (i in 0 until 20) {
|
||||
// For the colors, pick any random time to get a mix of colors
|
||||
val color =
|
||||
CapPreferredColorizer.getColor(colorizer, harness.ctx.caster, Random.nextFloat() * 256f, Vec3.ZERO)
|
||||
val r = FastColor.ARGB32.red(color)
|
||||
val g = FastColor.ARGB32.green(color)
|
||||
val b = FastColor.ARGB32.blue(color)
|
||||
harness.ctx.world.addParticle(
|
||||
harness.ctx.world.sendParticles(
|
||||
DustParticleOptions(Vector3f(r.toFloat(), g.toFloat(), b.toFloat()), 1f),
|
||||
position.x,
|
||||
position.y,
|
||||
position.z,
|
||||
1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -77,10 +80,21 @@ sealed class OperatorSideEffect {
|
|||
}
|
||||
}
|
||||
|
||||
object AddGarbage : OperatorSideEffect() {
|
||||
data class Mishap(val exn: CastException) : OperatorSideEffect() {
|
||||
override fun performEffect(harness: CastingHarness): Boolean {
|
||||
val idx = Random.nextInt(0..harness.stack.size)
|
||||
harness.stack.add(idx, SpellDatum.make(Widget.GARBAGE))
|
||||
harness.ctx.caster.sendMessage(
|
||||
TextComponent(exn.toString()),
|
||||
Util.NIL_UUID
|
||||
)
|
||||
|
||||
when (exn.reason) {
|
||||
CastException.Reason.INVALID_PATTERN -> {
|
||||
val idx = Random.nextInt(0..harness.stack.size)
|
||||
harness.stack.add(idx, SpellDatum.make(Widget.GARBAGE))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import at.petrak.hexcasting.api.SpellDatum;
|
|||
import at.petrak.hexcasting.common.casting.operators.*;
|
||||
import at.petrak.hexcasting.common.casting.operators.lists.OpAppend;
|
||||
import at.petrak.hexcasting.common.casting.operators.lists.OpConcat;
|
||||
import at.petrak.hexcasting.common.casting.operators.lists.OpForEach;
|
||||
import at.petrak.hexcasting.common.casting.operators.lists.OpIndex;
|
||||
import at.petrak.hexcasting.common.casting.operators.math.*;
|
||||
import at.petrak.hexcasting.common.casting.operators.selectors.OpGetCaster;
|
||||
|
|
|
@ -33,9 +33,9 @@ object OpBlockRaycast : ConstManaOperator {
|
|||
// this is weird (for example, casting OpBreakBlock at this position will not break the block we're looking at)
|
||||
// so we return the block pos instead
|
||||
Vec3(
|
||||
blockHitResult.blockPos.x.toDouble(),
|
||||
blockHitResult.blockPos.y.toDouble(),
|
||||
blockHitResult.blockPos.z.toDouble()
|
||||
blockHitResult.blockPos.x.toDouble() + 0.5,
|
||||
blockHitResult.blockPos.y.toDouble() + 0.5,
|
||||
blockHitResult.blockPos.z.toDouble() + 0.5
|
||||
)
|
||||
} else {
|
||||
Widget.NULL
|
||||
|
|
|
@ -3,10 +3,10 @@ package at.petrak.hexcasting.common.casting.operators
|
|||
import at.petrak.hexcasting.api.OperationResult
|
||||
import at.petrak.hexcasting.api.Operator
|
||||
import at.petrak.hexcasting.api.Operator.Companion.getChecked
|
||||
import at.petrak.hexcasting.api.RenderedSpell
|
||||
import at.petrak.hexcasting.api.SpellDatum
|
||||
import at.petrak.hexcasting.common.casting.CastingContext
|
||||
import at.petrak.hexcasting.common.casting.CastingHarness
|
||||
import at.petrak.hexcasting.common.casting.OperatorSideEffect
|
||||
|
||||
object OpEval : Operator {
|
||||
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
|
||||
|
@ -16,20 +16,16 @@ object OpEval : Operator {
|
|||
ctx.incDepth()
|
||||
val harness = CastingHarness(ctx)
|
||||
harness.stack.addAll(stack)
|
||||
stack.clear()
|
||||
|
||||
val spellsToCast = mutableListOf<RenderedSpell>()
|
||||
val sideEffects = mutableListOf<OperatorSideEffect>()
|
||||
/*
|
||||
for (pat in instrs) {
|
||||
val res = harness.update(pat.tryGet(), ctx.world)
|
||||
when (res) {
|
||||
is CastingHarness.CastResult.Error -> throw res.exn
|
||||
is CastingHarness.CastResult.Cast -> spellsToCast.addAll(res.spells)
|
||||
else -> {}
|
||||
}
|
||||
if (res.quitStatus() == CastingHarness.QuitStatus.QUIT) break
|
||||
val res = harness.getUpdate(pat.tryGet(), ctx.world)
|
||||
sideEffects.addAll(res.sideEffects)
|
||||
|
||||
}
|
||||
stack.addAll(harness.stack)
|
||||
|
||||
return OperationResult(50_000, spellsToCast)
|
||||
*/
|
||||
return OperationResult(harness.stack, sideEffects)
|
||||
}
|
||||
}
|
|
@ -2,47 +2,11 @@ package at.petrak.hexcasting.common.casting.operators
|
|||
|
||||
import at.petrak.hexcasting.api.OperationResult
|
||||
import at.petrak.hexcasting.api.Operator
|
||||
import at.petrak.hexcasting.api.Operator.Companion.getChecked
|
||||
import at.petrak.hexcasting.api.RenderedSpell
|
||||
import at.petrak.hexcasting.api.SpellDatum
|
||||
import at.petrak.hexcasting.common.casting.CastingContext
|
||||
import at.petrak.hexcasting.common.casting.CastingHarness
|
||||
import at.petrak.hexcasting.server.TickScheduler
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object OpEvalDelay : Operator {
|
||||
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
|
||||
val instrs: List<SpellDatum<*>> = stack.getChecked(stack.lastIndex - 1)
|
||||
val delay: Double = stack.getChecked(stack.lastIndex)
|
||||
stack.removeLastOrNull()
|
||||
stack.removeLastOrNull()
|
||||
|
||||
val ticks = max((delay * 20.0).roundToInt(), 1)
|
||||
|
||||
val harness = CastingHarness(ctx)
|
||||
harness.stack.addAll(stack)
|
||||
stack.clear()
|
||||
|
||||
TickScheduler.schedule(ticks) {
|
||||
val spellsToCast = mutableListOf<RenderedSpell>()
|
||||
for (pat in instrs) {
|
||||
val res = harness.update(pat.tryGet(), ctx.world)
|
||||
when (res) {
|
||||
is CastingHarness.CastResult.Error -> throw res.exn
|
||||
is CastingHarness.CastResult.Cast -> spellsToCast.addAll(res.spells)
|
||||
else -> {}
|
||||
}
|
||||
if (res.quitStatus() == CastingHarness.QuitStatus.QUIT) break
|
||||
}
|
||||
// god i hate how this would just work too
|
||||
// stack.addAll(harness.stack)
|
||||
// it is so weird to think about garbage collection
|
||||
for (spell in spellsToCast) {
|
||||
spell.cast(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return OperationResult(80_000, emptyList())
|
||||
return OperationResult(stack, listOf())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package at.petrak.hexcasting.common.casting.operators
|
||||
|
||||
import at.petrak.hexcasting.api.OperationResult
|
||||
import at.petrak.hexcasting.api.Operator
|
||||
import at.petrak.hexcasting.api.SpellDatum
|
||||
import at.petrak.hexcasting.common.casting.CastingContext
|
||||
|
||||
object OpForEach : Operator {
|
||||
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
|
||||
return OperationResult(stack, listOf())
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package at.petrak.hexcasting.common.casting.operators.lists
|
||||
|
||||
import at.petrak.hexcasting.api.OperationResult
|
||||
import at.petrak.hexcasting.api.Operator
|
||||
import at.petrak.hexcasting.api.Operator.Companion.getChecked
|
||||
import at.petrak.hexcasting.api.RenderedSpell
|
||||
import at.petrak.hexcasting.api.SpellDatum
|
||||
import at.petrak.hexcasting.common.casting.CastException
|
||||
import at.petrak.hexcasting.common.casting.CastingContext
|
||||
import at.petrak.hexcasting.common.casting.CastingHarness
|
||||
import at.petrak.hexcasting.hexmath.HexPattern
|
||||
|
||||
object OpForEach : Operator {
|
||||
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
|
||||
val last = stack.lastIndex
|
||||
val maybeProgram = stack[last - 1]
|
||||
val vals = stack.getChecked<List<SpellDatum<*>>>(last)
|
||||
stack.removeLastOrNull()
|
||||
stack.removeLastOrNull()
|
||||
|
||||
val program = when (maybeProgram.payload) {
|
||||
is HexPattern -> listOf(maybeProgram.payload)
|
||||
is List<*> -> maybeProgram.payload.map { (it as SpellDatum<*>).tryGet() }
|
||||
else -> throw CastException(CastException.Reason.OP_WRONG_TYPE, List::class.java, maybeProgram.payload)
|
||||
}
|
||||
|
||||
val outvalues = mutableListOf<SpellDatum<*>>()
|
||||
val spellsToCast = mutableListOf<RenderedSpell>()
|
||||
for (v in vals) {
|
||||
ctx.incDepth()
|
||||
val harness = CastingHarness(ctx)
|
||||
// Put the entire current stack on there, then the next value
|
||||
harness.stack.addAll(stack)
|
||||
harness.stack.add(v)
|
||||
for (pat in program) {
|
||||
val res = harness.update(pat, ctx.world)
|
||||
when (res) {
|
||||
is CastingHarness.CastResult.Error -> throw res.exn
|
||||
is CastingHarness.CastResult.Cast -> spellsToCast.addAll(res.spells)
|
||||
else -> {}
|
||||
}
|
||||
if (res.quitStatus() == CastingHarness.QuitStatus.QUIT) break
|
||||
}
|
||||
outvalues.addAll(harness.stack)
|
||||
}
|
||||
|
||||
stack.addAll(outvalues)
|
||||
return OperationResult(10_000, spellsToCast)
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ object OpAddMotion : SpellOperator {
|
|||
return Triple(
|
||||
Spell(target, motion),
|
||||
(motion.lengthSqr() * 10_000f).toInt(),
|
||||
listOf(),
|
||||
listOf(target.position()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,38 +16,43 @@ import kotlin.math.roundToInt
|
|||
|
||||
object OpBlink : SpellOperator {
|
||||
override val argc = 2
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<Entity>(0)
|
||||
val delta = args.getChecked<Double>(1)
|
||||
|
||||
ctx.assertVecInRange(target.position())
|
||||
ctx.assertVecInRange(target.position().add(target.lookAngle.scale(delta)))
|
||||
val dvec = targetDelta(ctx, target, delta)
|
||||
|
||||
return Pair(
|
||||
ctx.assertVecInRange(target.position())
|
||||
ctx.assertVecInRange(target.position().add(dvec))
|
||||
|
||||
return Triple(
|
||||
Spell(target, delta),
|
||||
20_000 * delta.roundToInt(),
|
||||
50_000 * delta.roundToInt(),
|
||||
listOf(target.position().add(dvec))
|
||||
)
|
||||
}
|
||||
|
||||
private data class Spell(val target: Entity, val delta: Double) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val look = target.lookAngle
|
||||
// https://github.com/VazkiiMods/Psi/blob/master/src/main/java/vazkii/psi/common/spell/trick/entity/PieceTrickBlink.java#L74
|
||||
// IIRC this is to prevent you from teleporting into blocks because people tend to look a little bit down
|
||||
// ... but isn't the condition backwards?
|
||||
val dx = look.x * delta
|
||||
val dy = if (target != ctx.caster) {
|
||||
look.y * delta
|
||||
} else {
|
||||
max(0.0, look.y * delta)
|
||||
}
|
||||
val dz = look.z * delta
|
||||
|
||||
val dvec = Vec3(dx, dy, dz)
|
||||
val dvec = targetDelta(ctx, target, delta)
|
||||
target.setPos(target.position().add(dvec))
|
||||
if (target is ServerPlayer) {
|
||||
HexMessages.getNetwork().send(PacketDistributor.PLAYER.with { target }, MsgBlinkAck(dvec))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun targetDelta(ctx: CastingContext, target: Entity, delta: Double): Vec3 {
|
||||
val look = target.lookAngle
|
||||
// https://github.com/VazkiiMods/Psi/blob/master/src/main/java/vazkii/psi/common/spell/trick/entity/PieceTrickBlink.java#L74
|
||||
val dx = look.x * delta
|
||||
val dy = if (target != ctx.caster) {
|
||||
look.y * delta
|
||||
} else {
|
||||
max(0.0, look.y * delta)
|
||||
}
|
||||
val dz = look.z * delta
|
||||
|
||||
return Vec3(dx, dy, dz)
|
||||
}
|
||||
}
|
|
@ -14,12 +14,13 @@ object OpBreakBlock : SpellOperator {
|
|||
override val argc: Int
|
||||
get() = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val pos = args.getChecked<Vec3>(0)
|
||||
ctx.assertVecInRange(pos)
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(pos),
|
||||
20_000
|
||||
20_000,
|
||||
listOf(pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -38,7 +39,7 @@ object OpBreakBlock : SpellOperator {
|
|||
|| TierSortingRegistry.isCorrectTierForDrops(tier, blockstate))
|
||||
) {
|
||||
ctx.world.destroyBlock(pos, true, ctx.caster)
|
||||
} // TODO: else some kind of failureific particle effect?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,15 +9,17 @@ import at.petrak.hexcasting.common.lib.HexCapabilities
|
|||
import at.petrak.hexcasting.common.network.HexMessages
|
||||
import at.petrak.hexcasting.common.network.MsgColorizerUpdateAck
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraftforge.network.PacketDistributor
|
||||
|
||||
object OpColorize : SpellOperator {
|
||||
override val argc = 0
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
return Pair(
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
return Triple(
|
||||
Spell,
|
||||
10_000
|
||||
10_000,
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,14 @@ import net.minecraft.world.phys.Vec3
|
|||
|
||||
object OpCreateWater : SpellOperator {
|
||||
override val argc = 1
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<Vec3>(0)
|
||||
ctx.assertVecInRange(target)
|
||||
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(target),
|
||||
10_000
|
||||
10_000,
|
||||
listOf(target)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,13 +22,14 @@ import net.minecraft.world.phys.Vec3
|
|||
|
||||
object OpDestroyWater : SpellOperator {
|
||||
override val argc = 1
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<Vec3>(0)
|
||||
ctx.assertVecInRange(target)
|
||||
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(target),
|
||||
200_000
|
||||
200_000,
|
||||
listOf(target)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,14 @@ class OpExplode(val fire: Boolean) : SpellOperator {
|
|||
override val argc: Int
|
||||
get() = 2
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val pos = args.getChecked<Vec3>(0)
|
||||
val strength = args.getChecked<Double>(1)
|
||||
ctx.assertVecInRange(pos)
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(pos, strength, this.fire),
|
||||
((1 + strength + if (this.fire) 2 else 0) * 50_000.0).toInt(),
|
||||
listOf(pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,24 +10,21 @@ import at.petrak.hexcasting.common.casting.ManaHelper
|
|||
import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell
|
||||
import at.petrak.hexcasting.hexmath.HexPattern
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class OpMakePackagedSpell<T : ItemPackagedSpell>(val type: Class<T>, val cost: Int) : SpellOperator {
|
||||
override val argc = 2
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
|
||||
if (!type.isAssignableFrom(otherHandItem.item.javaClass)) {
|
||||
throw CastException(CastException.Reason.BAD_OFFHAND_ITEM, type, otherHandItem)
|
||||
}
|
||||
|
||||
val entity = args.getChecked<Entity>(0)
|
||||
val entity = args.getChecked<ItemEntity>(0)
|
||||
val patterns = args.getChecked<List<SpellDatum<*>>>(1).map { it.tryGet<HexPattern>() }
|
||||
|
||||
if (entity !is ItemEntity)
|
||||
throw CastException(CastException.Reason.OP_WRONG_TYPE, ItemEntity::class.java, entity)
|
||||
|
||||
return Pair(Spell(entity, patterns), cost)
|
||||
return Triple(Spell(entity, patterns), cost, listOf(entity.position()))
|
||||
}
|
||||
|
||||
private data class Spell(val itemEntity: ItemEntity, val patterns: List<HexPattern>) : RenderedSpell {
|
||||
|
|
|
@ -22,12 +22,13 @@ object OpPlaceBlock : SpellOperator {
|
|||
override val argc: Int
|
||||
get() = 1
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val pos = args.getChecked<Vec3>(0)
|
||||
ctx.assertVecInRange(pos)
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(pos),
|
||||
10_000
|
||||
10_000,
|
||||
listOf(pos)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,14 @@ import at.petrak.hexcasting.common.casting.CastingContext
|
|||
import net.minecraft.world.effect.MobEffect
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.max
|
||||
|
||||
class OpPotionEffect(val effect: MobEffect, val baseCost: Int, val potency: Boolean) : SpellOperator {
|
||||
override val argc: Int
|
||||
get() = if (this.potency) 3 else 2
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<LivingEntity>(0)
|
||||
val duration = max(args.getChecked(1), 0.0)
|
||||
val potency = if (this.potency)
|
||||
|
@ -22,9 +23,10 @@ class OpPotionEffect(val effect: MobEffect, val baseCost: Int, val potency: Bool
|
|||
else 1.0
|
||||
|
||||
val cost = this.baseCost * duration * potency
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(effect, target, duration, potency),
|
||||
cost.toInt()
|
||||
cost.toInt(),
|
||||
listOf(target.position())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,19 +5,23 @@ import at.petrak.hexcasting.api.Operator
|
|||
import at.petrak.hexcasting.api.RenderedSpell
|
||||
import at.petrak.hexcasting.api.SpellDatum
|
||||
import at.petrak.hexcasting.common.casting.CastingContext
|
||||
import at.petrak.hexcasting.common.casting.OperatorSideEffect
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.network.chat.TextComponent
|
||||
|
||||
object OpPrint : Operator {
|
||||
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
|
||||
val datum = stack[stack.lastIndex]
|
||||
return OperationResult(0, listOf(Spell(datum)))
|
||||
return OperationResult(
|
||||
stack, listOf(
|
||||
OperatorSideEffect.AttemptSpell(Spell(datum), false)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private data class Spell(val datum: SpellDatum<*>) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
ctx.caster.sendMessage(
|
||||
TextComponent(datum.display()),
|
||||
datum.display(),
|
||||
Util.NIL_UUID
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@ import at.petrak.hexcasting.common.casting.ManaHelper
|
|||
import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpRecharge : SpellOperator {
|
||||
override val argc = 1
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
|
||||
if (otherHandItem.item !is ItemPackagedSpell) {
|
||||
throw CastException(CastException.Reason.BAD_OFFHAND_ITEM, ItemPackagedSpell::class.java, otherHandItem)
|
||||
|
@ -21,7 +22,7 @@ object OpRecharge : SpellOperator {
|
|||
|
||||
val entity = args.getChecked<ItemEntity>(0)
|
||||
|
||||
return Pair(Spell(entity), 100_000)
|
||||
return Triple(Spell(entity), 100_000, listOf(entity.position()))
|
||||
}
|
||||
|
||||
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {
|
||||
|
|
|
@ -17,13 +17,14 @@ import net.minecraft.world.phys.Vec3
|
|||
|
||||
object OpTheOnlyReasonAnyoneDownloadedPsi : SpellOperator {
|
||||
override val argc = 1
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<Vec3>(0)
|
||||
ctx.assertVecInRange(target)
|
||||
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(target),
|
||||
10_000
|
||||
10_000,
|
||||
listOf(target)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,13 +14,14 @@ import net.minecraft.world.phys.Vec3
|
|||
object OpCreateLava : SpellOperator {
|
||||
override val argc = 1
|
||||
override val isGreat = true
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<Vec3>(0)
|
||||
ctx.assertVecInRange(target)
|
||||
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(target),
|
||||
100_000
|
||||
100_000,
|
||||
listOf(target),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -26,16 +26,17 @@ import kotlin.math.roundToInt
|
|||
object OpFlight : SpellOperator {
|
||||
override val argc = 3
|
||||
override val isGreat = true
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<ServerPlayer>(0)
|
||||
val timeRaw = max(args.getChecked(1), 0.0)
|
||||
val radiusRaw = max(args.getChecked(2), 0.0)
|
||||
|
||||
// Convert to ticks
|
||||
val time = (timeRaw * 20.0).roundToInt()
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(target, time, radiusRaw, ctx.position),
|
||||
10_000 * (timeRaw * radiusRaw + 1.0).roundToInt()
|
||||
10_000 * (timeRaw * radiusRaw + 1.0).roundToInt(),
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,12 +13,13 @@ object OpLightning : SpellOperator {
|
|||
override val argc = 1
|
||||
override val isGreat = true
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<Vec3>(0)
|
||||
ctx.assertVecInRange(target)
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(target),
|
||||
150_000
|
||||
150_000,
|
||||
listOf(target)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,15 @@ import net.minecraftforge.network.PacketDistributor
|
|||
object OpTeleport : SpellOperator {
|
||||
override val argc = 2
|
||||
override val isGreat = true
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val teleportee = args.getChecked<Entity>(0)
|
||||
val delta = args.getChecked<Vec3>(1)
|
||||
|
||||
ctx.assertVecInRange(teleportee.position())
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(teleportee, delta),
|
||||
1_000_000
|
||||
1_000_000,
|
||||
listOf(teleportee.position().add(delta))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,14 @@ import net.minecraftforge.network.PacketDistributor
|
|||
|
||||
class OpCreateSentinel(val extendsRange: Boolean) : SpellOperator {
|
||||
override val argc = 1
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
val target = args.getChecked<Vec3>(0)
|
||||
ctx.assertVecInRange(target)
|
||||
|
||||
return Pair(
|
||||
return Triple(
|
||||
Spell(target, this.extendsRange),
|
||||
10_000
|
||||
10_000,
|
||||
listOf(target)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,14 +7,16 @@ import at.petrak.hexcasting.common.casting.CastingContext
|
|||
import at.petrak.hexcasting.common.lib.HexCapabilities
|
||||
import at.petrak.hexcasting.common.network.HexMessages
|
||||
import at.petrak.hexcasting.common.network.MsgSentinelStatusUpdateAck
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraftforge.network.PacketDistributor
|
||||
|
||||
object OpDestroySentinel : SpellOperator {
|
||||
override val argc = 0
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
|
||||
return Pair(
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
|
||||
return Triple(
|
||||
Spell,
|
||||
1_000
|
||||
1_000,
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,8 @@ import at.petrak.hexcasting.common.casting.CastingHarness;
|
|||
import at.petrak.hexcasting.common.casting.ManaHelper;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import at.petrak.hexcasting.hexmath.HexPattern;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
|
@ -59,15 +57,8 @@ public abstract class ItemPackagedSpell extends Item {
|
|||
var harness = new CastingHarness(ctx);
|
||||
List<HexPattern> patterns = getPatterns(tag);
|
||||
for (var pattern : patterns) {
|
||||
var res = harness.update(pattern, sPlayer.getLevel());
|
||||
if (res instanceof CastingHarness.CastResult.Error error) {
|
||||
sPlayer.sendMessage(new TextComponent(error.getExn().getMessage()), Util.NIL_UUID);
|
||||
} else if (res instanceof CastingHarness.CastResult.Cast cast) {
|
||||
for (var spell : cast.getSpells()) {
|
||||
spell.cast(ctx);
|
||||
}
|
||||
}
|
||||
if (res.quitStatus() == CastingHarness.QuitStatus.QUIT) {
|
||||
var info = harness.executeNewPattern(pattern, sPlayer.getLevel());
|
||||
if (info.shouldQuit()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
package at.petrak.hexcasting.common.network;
|
||||
|
||||
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
|
||||
import at.petrak.hexcasting.common.casting.CastingHarness;
|
||||
import at.petrak.hexcasting.common.casting.ControllerInfo;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
@ -21,49 +18,49 @@ import java.util.function.Supplier;
|
|||
/**
|
||||
* Sent server->client when the player finishes casting a spell.
|
||||
*/
|
||||
public record MsgNewSpellPatternAck(CastingHarness.QuitStatus state, List<String> stackDesc) {
|
||||
public record MsgNewSpellPatternAck(ControllerInfo info, List<Component> stackDesc) {
|
||||
private static final String TAG_DESC = "desc";
|
||||
|
||||
public static MsgNewSpellPatternAck deserialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
var state = CastingHarness.QuitStatus.values()[buf.readInt()];
|
||||
var descsTag = buf.readNbt().getList(TAG_DESC, Tag.TAG_STRING);
|
||||
var descs = new ArrayList<String>(descsTag.size());
|
||||
for (int i = 0; i < descsTag.size(); i++) {
|
||||
descs.add(descsTag.getString(i));
|
||||
|
||||
var status = ControllerInfo.Status.values()[buf.readInt()];
|
||||
var descsLen = buf.readInt();
|
||||
var desc = new ArrayList<Component>(descsLen);
|
||||
for (int i = 0; i < descsLen; i++) {
|
||||
desc.add(buf.readComponent());
|
||||
}
|
||||
|
||||
return new MsgNewSpellPatternAck(state, descs);
|
||||
return new MsgNewSpellPatternAck(
|
||||
new ControllerInfo(status),
|
||||
desc
|
||||
);
|
||||
}
|
||||
|
||||
public void serialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
buf.writeInt(this.state.ordinal());
|
||||
|
||||
var descsCTag = new CompoundTag();
|
||||
var descsTag = new ListTag();
|
||||
for (String s : this.stackDesc) {
|
||||
descsTag.add(StringTag.valueOf(s));
|
||||
buf.writeInt(this.info.getStatus().ordinal());
|
||||
buf.writeInt(this.stackDesc.size());
|
||||
for (var desc : this.stackDesc) {
|
||||
buf.writeComponent(desc);
|
||||
}
|
||||
descsCTag.put(TAG_DESC, descsTag);
|
||||
buf.writeNbt(descsCTag);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(() ->
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
|
||||
var mc = Minecraft.getInstance();
|
||||
if (this.state == CastingHarness.QuitStatus.QUIT) {
|
||||
if (this.info.shouldQuit()) {
|
||||
// don't pay attention to the screen, so it also stops when we die
|
||||
mc.getSoundManager().stop(HexSounds.CASTING_AMBIANCE.getId(), null);
|
||||
}
|
||||
var screen = Minecraft.getInstance().screen;
|
||||
if (screen instanceof GuiSpellcasting spellGui) {
|
||||
if (this.state == CastingHarness.QuitStatus.QUIT) {
|
||||
if (this.info.shouldQuit()) {
|
||||
mc.setScreen(null);
|
||||
} else {
|
||||
spellGui.recvServerUpdate(this.stackDesc,
|
||||
this.state == CastingHarness.QuitStatus.LAST_PATTERN_INVALID);
|
||||
spellGui.recvServerUpdate(this.info, this.stackDesc);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
package at.petrak.hexcasting.common.network;
|
||||
|
||||
import at.petrak.hexcasting.common.casting.CastingContext;
|
||||
import at.petrak.hexcasting.common.casting.CastingHarness;
|
||||
import at.petrak.hexcasting.common.casting.CastingHarness.CastResult;
|
||||
import at.petrak.hexcasting.common.items.ItemWand;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import at.petrak.hexcasting.hexmath.HexPattern;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
|
@ -48,21 +45,16 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
|
|||
var harness = CastingHarness.DeserializeFromNBT(tag.getCompound(ItemWand.TAG_HARNESS), sender,
|
||||
this.handUsed);
|
||||
|
||||
var res = harness.update(this.pattern, sender.getLevel());
|
||||
if (res instanceof CastResult.Cast success) {
|
||||
var castCtx = new CastingContext(sender, this.handUsed);
|
||||
for (var spell : success.getSpells()) {
|
||||
spell.cast(castCtx);
|
||||
}
|
||||
var clientInfo = harness.executeNewPattern(this.pattern, sender.getLevel());
|
||||
|
||||
if (clientInfo.wasSpellCast()) {
|
||||
sender.level.playSound(null, sender.getX(), sender.getY(), sender.getZ(),
|
||||
HexSounds.ACTUALLY_CAST.get(), SoundSource.PLAYERS, 1f,
|
||||
1f + ((float) Math.random() - 0.5f) * 0.2f);
|
||||
} else if (res instanceof CastResult.Error error) {
|
||||
sender.sendMessage(new TextComponent(error.getExn().getMessage()), Util.NIL_UUID);
|
||||
}
|
||||
|
||||
CompoundTag nextHarnessTag;
|
||||
if (res.quitStatus() == CastingHarness.QuitStatus.QUIT) {
|
||||
if (clientInfo.shouldQuit()) {
|
||||
// discard the changes
|
||||
nextHarnessTag = new CompoundTag();
|
||||
} else {
|
||||
|
@ -71,14 +63,14 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
|
|||
}
|
||||
tag.put(ItemWand.TAG_HARNESS, nextHarnessTag);
|
||||
|
||||
var descs = new ArrayList<String>(harness.getStack().size());
|
||||
var descs = new ArrayList<Component>(harness.getStack().size());
|
||||
for (var datum : harness.getStack()) {
|
||||
descs.add(datum.display());
|
||||
}
|
||||
|
||||
HexMessages.getNetwork()
|
||||
.send(PacketDistributor.PLAYER.with(() -> sender),
|
||||
new MsgNewSpellPatternAck(res.quitStatus(), descs));
|
||||
new MsgNewSpellPatternAck(clientInfo, descs));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
"anchor": "hexcasting:blink",
|
||||
"input": "entity, number",
|
||||
"output": "",
|
||||
"text": "Remove an entity and length from the stack, then teleport the given entity along its look vector by the given length.$(br)Costs about 2 $(item)Amethyst Dust/$s times the square of the distance teleported-- thus, teleporting 5 blocks would cost five $(item)Charged Amethyst Crystals$(0) or equivalent."
|
||||
"text": "Remove an entity and length from the stack, then teleport the given entity along its look vector by the given length.$(br)Costs about 1 $(item)Amethyst Shard/$s times the the distance teleported."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue