greater patterns now aren't just spells

Akashic Read is deliberately not a greater pattern, so you can distribute trinkets based on it on a server or something
This commit is contained in:
yrsegal@gmail.com 2022-05-24 17:44:28 -04:00
parent 37df5d3c56
commit 4f0e390c14
9 changed files with 86 additions and 42 deletions

View file

@ -7,8 +7,7 @@ import net.minecraft.world.phys.Vec3
/** /**
* Manipulates the stack in some way, usually by popping some number of values off the stack * Manipulates the stack in some way, usually by popping some number of values off the stack
* and pushing one new value. * and pushing one new value.
* For a more "traditional" pop arguments, push return experience, see * For a more "traditional" pop arguments, push return experience, see [ConstManaOperator].
* [SimpleOperator][at.petrak.hexcasting.api.spell.ConstManaOperator]
* *
* Implementors MUST NOT mutate the context. * Implementors MUST NOT mutate the context.
*/ */
@ -25,10 +24,22 @@ interface Operator {
fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult fun operate(continuation: SpellContinuation, stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult
/** /**
* Do you need to be enlightened to use this operator? * Do you need to be enlightened to use this operator? (i.e. is this operator a Great Pattern)
*/ */
val isGreat: Boolean get() = false val isGreat: Boolean get() = false
/**
* Should this Great Pattern process and have side effects, even if its user isn't enlightened?
*
* The pattern itself may modify its effects based on whether the user is enlightened or not, regardless of what this value is.
*/
val alwaysProcessGreatSpell: Boolean get() = this is SpellOperator
/**
* Can this Great Pattern give you Blind Diversion?
*/
val causesBlindDiversion: Boolean get() = this is SpellOperator
companion object { companion object {
// I see why vzakii did this: you can't raycast out to infinity! // I see why vzakii did this: you can't raycast out to infinity!
const val MAX_DISTANCE: Double = 32.0 const val MAX_DISTANCE: Double = 32.0

View file

@ -1,8 +1,8 @@
package at.petrak.hexcasting.api.spell package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
interface SpellOperator : Operator { interface SpellOperator : Operator {
@ -25,13 +25,17 @@ interface SpellOperator : Operator {
val executeResult = this.execute(args, ctx) ?: return OperationResult(continuation, stack, local, listOf()) val executeResult = this.execute(args, ctx) ?: return OperationResult(continuation, stack, local, listOf())
val (spell, mana, particles) = executeResult val (spell, mana, particles) = executeResult
val sideEffects = mutableListOf( val sideEffects = mutableListOf<OperatorSideEffect>()
OperatorSideEffect.ConsumeMana(mana),
OperatorSideEffect.AttemptSpell(spell, this.isGreat, this.hasCastingSound(ctx), this.awardsCastingStat(ctx)) if (mana > 0)
) sideEffects.add(OperatorSideEffect.ConsumeMana(mana))
for (spray in particles) {
// Don't have an effect if the caster isn't enlightened, even if processing other side effects
if (!isGreat || ctx.isCasterEnlightened)
sideEffects.add(OperatorSideEffect.AttemptSpell(spell, this.hasCastingSound(ctx), this.awardsCastingStat(ctx)))
for (spray in particles)
sideEffects.add(OperatorSideEffect.Particles(spray)) sideEffects.add(OperatorSideEffect.Particles(spray))
}
return OperationResult(continuation, stack, local, sideEffects) return OperationResult(continuation, stack, local, sideEffects)
} }

View file

@ -147,15 +147,32 @@ class CastingHarness private constructor(
if (!HexConfig.server().isActionAllowed(operatorIdPair.second)) { if (!HexConfig.server().isActionAllowed(operatorIdPair.second)) {
throw MishapDisallowedSpell() throw MishapDisallowedSpell()
} }
val (cont2, stack2, local2, sideEffectsUnmut) = operatorIdPair.first.operate( val pattern = operatorIdPair.first
val unenlightened = pattern.isGreat && !ctx.isCasterEnlightened
val sideEffects = mutableListOf<OperatorSideEffect>()
var stack2: List<SpellDatum<*>>? = null
var cont2 = continuation
if (!unenlightened || pattern.alwaysProcessGreatSpell) {
val result = pattern.operate(
continuation, continuation,
this.stack.toMutableList(), this.stack.toMutableList(),
this.localIota, this.localIota,
this.ctx this.ctx
) )
this.localIota = local2 cont2 = result.newContinuation
stack2 = result.newStack
this.localIota = result.newLocalIota
sideEffects.addAll(result.sideEffects)
}
if (unenlightened) {
sideEffects.add(OperatorSideEffect.RequiredEnlightenment(pattern.causesBlindDiversion))
}
// Stick a poofy particle effect at the caster position // Stick a poofy particle effect at the caster position
val sideEffects = sideEffectsUnmut.toMutableList()
if (this.ctx.spellCircle == null) if (this.ctx.spellCircle == null)
sideEffects.add( sideEffects.add(
OperatorSideEffect.Particles( OperatorSideEffect.Particles(
@ -167,9 +184,11 @@ class CastingHarness private constructor(
) )
) )
val fd = this.getFunctionalData().copy( val fd = stack2?.let {
stack = stack2, this.getFunctionalData().copy(
stack = it,
) )
}
return CastResult( return CastResult(
cont2, cont2,
@ -177,6 +196,7 @@ class CastingHarness private constructor(
ResolvedPatternType.EVALUATED, ResolvedPatternType.EVALUATED,
sideEffects, sideEffects,
) )
} catch (mishap: Mishap) { } catch (mishap: Mishap) {
return CastResult( return CastResult(
continuation, continuation,

View file

@ -22,23 +22,28 @@ sealed class OperatorSideEffect {
/** Return whether to cancel all further [OperatorSideEffect] */ /** Return whether to cancel all further [OperatorSideEffect] */
abstract fun performEffect(harness: CastingHarness): Boolean abstract fun performEffect(harness: CastingHarness): Boolean
/** Try to cast a spell */ data class RequiredEnlightenment(val awardStat: Boolean) : OperatorSideEffect() {
data class AttemptSpell(val spell: RenderedSpell, val isGreat: Boolean, val hasCastingSound: Boolean = true, val awardStat: Boolean = true) :
OperatorSideEffect() {
override fun performEffect(harness: CastingHarness): Boolean { override fun performEffect(harness: CastingHarness): Boolean {
return if (this.isGreat && !harness.ctx.isCasterEnlightened) {
harness.ctx.caster.sendMessage( harness.ctx.caster.sendMessage(
TranslatableComponent("hexcasting.message.cant_great_spell"), TranslatableComponent("hexcasting.message.cant_great_spell"),
Util.NIL_UUID Util.NIL_UUID
) )
if (awardStat)
HexAdvancementTriggers.FAIL_GREAT_SPELL_TRIGGER.trigger(harness.ctx.caster) HexAdvancementTriggers.FAIL_GREAT_SPELL_TRIGGER.trigger(harness.ctx.caster)
true
} else { return true
}
}
/** Try to cast a spell */
data class AttemptSpell(val spell: RenderedSpell, val hasCastingSound: Boolean = true, val awardStat: Boolean = true) :
OperatorSideEffect() {
override fun performEffect(harness: CastingHarness): Boolean {
this.spell.cast(harness.ctx) this.spell.cast(harness.ctx)
if (awardStat) if (awardStat)
harness.ctx.caster.awardStat(HexStatistics.SPELLS_CAST) harness.ctx.caster.awardStat(HexStatistics.SPELLS_CAST)
false return false
}
} }
} }

View file

@ -1,11 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.akashic package at.petrak.hexcasting.common.casting.operators.akashic
import at.petrak.hexcasting.api.misc.ManaConstants import at.petrak.hexcasting.api.misc.ManaConstants
import at.petrak.hexcasting.api.spell.getChecked import at.petrak.hexcasting.api.spell.*
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.RenderedSpell
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.math.HexPattern import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapNoAkashicRecord import at.petrak.hexcasting.api.spell.mishaps.MishapNoAkashicRecord
@ -19,6 +15,8 @@ import net.minecraft.world.phys.Vec3
object OpAkashicWrite : SpellOperator { object OpAkashicWrite : SpellOperator {
override val argc = 3 override val argc = 3
override val isGreat = true
override fun execute( override fun execute(
args: List<SpellDatum<*>>, args: List<SpellDatum<*>>,
ctx: CastingContext ctx: CastingContext

View file

@ -10,6 +10,8 @@ import net.minecraft.world.phys.Vec3
class OpCircleBounds(val max: Boolean) : ConstManaOperator { class OpCircleBounds(val max: Boolean) : ConstManaOperator {
override val argc = 0 override val argc = 0
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
if (ctx.spellCircle == null) if (ctx.spellCircle == null)
throw MishapNoSpellCircle() throw MishapNoSpellCircle()

View file

@ -10,6 +10,8 @@ import at.petrak.hexcasting.api.spell.mishaps.MishapNoSpellCircle
object OpImpetusDir : ConstManaOperator { object OpImpetusDir : ConstManaOperator {
override val argc = 0 override val argc = 0
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
if (ctx.spellCircle == null) if (ctx.spellCircle == null)
throw MishapNoSpellCircle() throw MishapNoSpellCircle()

View file

@ -10,6 +10,8 @@ import net.minecraft.world.phys.Vec3
object OpImpetusPos : ConstManaOperator { object OpImpetusPos : ConstManaOperator {
override val argc = 0 override val argc = 0
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> { override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
if (ctx.spellCircle == null) if (ctx.spellCircle == null)
throw MishapNoSpellCircle() throw MishapNoSpellCircle()

View file

@ -5,8 +5,8 @@ import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.RenderedSpell import at.petrak.hexcasting.api.spell.RenderedSpell
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import net.minecraft.Util import net.minecraft.Util
@ -18,7 +18,7 @@ object OpPrint : Operator {
val datum = stack[stack.lastIndex] val datum = stack[stack.lastIndex]
return OperationResult( return OperationResult(
continuation, stack, local, listOf( continuation, stack, local, listOf(
OperatorSideEffect.AttemptSpell(Spell(datum), false) OperatorSideEffect.AttemptSpell(Spell(datum), hasCastingSound = false, awardStat = false)
) )
) )
} }