Merge pull request #21 from Noobulus/lists_but_awesome

List utils, destroy water tweaks, and fire spells
This commit is contained in:
petrak@ 2022-02-11 11:24:52 -06:00 committed by GitHub
commit 6392596363
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 333 additions and 13 deletions

View file

@ -5,16 +5,10 @@ import at.petrak.hexcasting.api.Operator;
import at.petrak.hexcasting.api.PatternRegistry;
import at.petrak.hexcasting.api.SpellDatum;
import at.petrak.hexcasting.common.casting.operators.*;
import at.petrak.hexcasting.common.casting.operators.eval.OpEval;
import at.petrak.hexcasting.common.casting.operators.eval.OpEvalDelay;
import at.petrak.hexcasting.common.casting.operators.eval.OpForEach;
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.OpIndex;
import at.petrak.hexcasting.common.casting.operators.eval.*;
import at.petrak.hexcasting.common.casting.operators.lists.*;
import at.petrak.hexcasting.common.casting.operators.math.*;
import at.petrak.hexcasting.common.casting.operators.selectors.OpGetCaster;
import at.petrak.hexcasting.common.casting.operators.selectors.OpGetEntitiesBy;
import at.petrak.hexcasting.common.casting.operators.selectors.OpGetEntityAt;
import at.petrak.hexcasting.common.casting.operators.selectors.*;
import at.petrak.hexcasting.common.casting.operators.spells.*;
import at.petrak.hexcasting.common.casting.operators.spells.great.*;
import at.petrak.hexcasting.common.casting.operators.spells.sentinel.OpCreateSentinel;
@ -108,9 +102,12 @@ public class RegisterPatterns {
OpColorize.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aqawqadaq", HexDir.SOUTH_EAST), prefix("create_water"),
OpCreateWater.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dedwedade", HexDir.SOUTH_WEST),
prefix("destroy_water"),
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dedwedade", HexDir.SOUTH_WEST), prefix("destroy_water"),
OpDestroyWater.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ddedwdwd", HexDir.EAST), prefix("ignite"),
OpIgnite.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aaqawawa", HexDir.WEST), prefix("extinguish"),
OpExtinguish.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqa", HexDir.NORTH_EAST), prefix("conjure_block"),
new OpConjure(false));
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqd", HexDir.NORTH_EAST), prefix("conjure_light"),
@ -295,6 +292,16 @@ public class RegisterPatterns {
OpIndex.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("dadad", HexDir.NORTH_EAST), prefix("for_each"),
OpForEach.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("aqaeaq", HexDir.EAST), prefix("list_size"),
OpListSize.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("adeeed", HexDir.EAST), prefix("singleton"),
OpSingleton.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqaeaae", HexDir.NORTH_EAST), prefix("empty_list"),
OpEmptyList.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("qqqaede", HexDir.EAST), prefix("reverse_list"),
OpReverski.INSTANCE);
PatternRegistry.mapPattern(HexPattern.FromAnglesSig("ewdqdwe", HexDir.SOUTH_WEST), prefix("last_n_list"),
OpLastNToList.INSTANCE);
} catch (PatternRegistry.RegisterPatternException exn) {
exn.printStackTrace();

View file

@ -0,0 +1,13 @@
package at.petrak.hexcasting.common.casting.operators.lists
import at.petrak.hexcasting.api.ConstManaOperator
import at.petrak.hexcasting.api.Operator.Companion.spellListOf
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.common.casting.CastingContext
object OpEmptyList : ConstManaOperator {
override val argc = 0
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
return spellListOf(emptyList<SpellDatum<*>>()) // sorry for taking all the easy impls, hudeler
}
}

View file

@ -0,0 +1,36 @@
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.Operator.Companion.spellListOf
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.OperatorSideEffect
object OpLastNToList : Operator {
val manaCost: Int
get() = 0
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
if (stack.isEmpty())
throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, 1, stack.size)
val arg = stack.takeLast(1).getChecked<Double>(0)
stack.removeLast()
if (arg < 0) {
throw CastException(CastException.Reason.INVALID_VALUE, "integer greater than 0", arg)
}
val output = emptyList<SpellDatum<*>>().toMutableList()
output.addAll(stack.takeLast(arg.toInt()))
val endSize = stack.size - output.toList().size
while (stack.size != endSize) {
stack.removeLast()
}
stack.addAll(spellListOf(output))
val sideEffects = mutableListOf<OperatorSideEffect>(OperatorSideEffect.ConsumeMana(this.manaCost))
return OperationResult(stack, sideEffects)
}
}

View file

@ -0,0 +1,15 @@
package at.petrak.hexcasting.common.casting.operators.lists
import at.petrak.hexcasting.api.ConstManaOperator
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.Operator.Companion.spellListOf
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.common.casting.CastingContext
// it's still called beancounter's distillation in my heart
object OpListSize : ConstManaOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
return spellListOf(args.getChecked<List<SpellDatum<*>>>(0).toList().size.toDouble()) // mmm one-liner
}
}

View file

@ -0,0 +1,14 @@
package at.petrak.hexcasting.common.casting.operators.lists
import at.petrak.hexcasting.api.ConstManaOperator
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.Operator.Companion.spellListOf
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.common.casting.CastingContext
object OpReverski : ConstManaOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
return spellListOf(args.getChecked<List<SpellDatum<*>>>(0).asReversed()) // okay kotlin kinda pogged for this
}
}

View file

@ -0,0 +1,13 @@
package at.petrak.hexcasting.common.casting.operators.lists
import at.petrak.hexcasting.api.ConstManaOperator
import at.petrak.hexcasting.api.Operator.Companion.spellListOf
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.common.casting.CastingContext
object OpSingleton : ConstManaOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> {
return spellListOf(listOf(args[0])) // god i love one-liners
}
}

View file

@ -11,12 +11,12 @@ import net.minecraft.core.Direction
import net.minecraft.core.particles.ParticleTypes
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.tags.FluidTags
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.BucketPickup
import net.minecraft.world.level.block.LiquidBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.level.material.Material
import net.minecraft.world.phys.Vec3
@ -42,6 +42,9 @@ object OpDestroyWater : SpellOperator {
val todo = ArrayDeque<BlockPos>()
val seen = HashSet<BlockPos>()
todo.add(BlockPos(target))
for (dir in Direction.values()) { // a little extra range on the initial cast to make it feel more intuitive
todo.add(BlockPos(target).relative(dir))
}
var successes = 0
while (todo.isNotEmpty() && successes <= MAX_DESTROY_COUNT) {
@ -51,7 +54,7 @@ object OpDestroyWater : SpellOperator {
if (distFromFocus < Operator.MAX_DISTANCE * Operator.MAX_DISTANCE && seen.add(here)) {
// never seen this pos in my life
val fluid = ctx.world.getFluidState(here)
if (fluid.`is`(FluidTags.WATER)) {
if (fluid != Fluids.EMPTY.defaultFluidState()) {
val blockstate = ctx.world.getBlockState(here)
val material = blockstate.material
val success =

View file

@ -0,0 +1,105 @@
package at.petrak.hexcasting.common.casting.operators.spells
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.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.particles.ParticleTypes
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.world.InteractionHand
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.context.UseOnContext
import net.minecraft.world.level.block.*
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.Vec3
object OpExtinguish : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
200_000,
listOf(target)
)
}
const val MAX_DESTROY_COUNT = 1024
private data class Spell(val target: Vec3) : RenderedSpell {
override fun cast(ctx: CastingContext) {
// how many levels of "borrowed" code are we on now
val todo = ArrayDeque<BlockPos>()
val seen = HashSet<BlockPos>()
todo.add(BlockPos(target))
var successes = 0
while (todo.isNotEmpty() && successes <= MAX_DESTROY_COUNT) {
val here = todo.removeFirst()
val distFromFocus =
ctx.caster.position().distanceToSqr(Vec3(here.x.toDouble(), here.y.toDouble(), here.z.toDouble()))
val distFromTarget =
target.distanceTo(Vec3(here.x.toDouble(), here.y.toDouble(), here.z.toDouble())) // max distance to prevent runaway shenanigans
if (distFromFocus < Operator.MAX_DISTANCE * Operator.MAX_DISTANCE && seen.add(here) && distFromTarget < 10) {
// never seen this pos in my life
val blockstate = ctx.world.getBlockState(here)
val success =
when (blockstate.block) {
is BaseFireBlock -> {ctx.world.setBlock(here, Blocks.AIR.defaultBlockState(), 3); true}
is CampfireBlock -> {if (blockstate.getValue(CampfireBlock.LIT)) { // check if campfire is lit before putting it out
val wilson = Items.WOODEN_SHOVEL // summon shovel from the ether to do our bidding
val hereVec = (Vec3(here.x.toDouble(), here.y.toDouble(), here.z.toDouble()))
wilson.useOn(UseOnContext(ctx.world, null, InteractionHand.MAIN_HAND, ItemStack(wilson.asItem()), BlockHitResult(hereVec, Direction.UP, here, false))); true}
else false}
is AbstractCandleBlock -> {
if (blockstate.getValue(AbstractCandleBlock.LIT)) { // same check for candles
AbstractCandleBlock.extinguish(null, blockstate, ctx.world, here); true}
else false}
is NetherPortalBlock -> {ctx.world.setBlock(here, Blocks.AIR.defaultBlockState(), 3); true}
else -> false
}
if (success) {
ctx.world.sendParticles(
ParticleTypes.SMOKE,
here.x + 0.5 + Math.random() * 0.4 - 0.2,
here.y + 0.5 + Math.random() * 0.4 - 0.2,
here.z + 0.5 + Math.random() * 0.4 - 0.2,
2,
0.0,
0.05,
0.0,
0.0
)
successes++
}
for (dir in Direction.values()) {
todo.add(here.relative(dir))
}
}
}
if (successes > 0) {
ctx.world.playSound(
null,
target.x,
target.y,
target.z,
SoundEvents.FIRE_EXTINGUISH,
SoundSource.BLOCKS,
1.0f,
0.95f
)
}
}
}
}

View file

@ -0,0 +1,44 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.InteractionHand
import net.minecraft.world.item.FireChargeItem
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.context.UseOnContext
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.Vec3
object OpIgnite : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
10_000,
listOf(target)
)
}
private data class Spell(val target: Vec3) : RenderedSpell {
override fun cast(ctx: CastingContext) {
// steal petra code that steals bucket code
val maxwell = Items.FIRE_CHARGE
if (maxwell is FireChargeItem) {
// help
maxwell.useOn(UseOnContext(ctx.world, null, InteractionHand.MAIN_HAND, ItemStack(maxwell.asItem()), BlockHitResult(target, Direction.UP, BlockPos(target), false)))
} else {
HexMod.getLogger().warn("Items.FIRE_CHARGE wasn't a FireChargeItem?")
}
}
}
}

View file

@ -84,6 +84,11 @@
"hexcasting.spell.hexcasting:concat": "Combination Distillation",
"hexcasting.spell.hexcasting:index": "Selection Distillation",
"hexcasting.spell.hexcasting:for_each": "Thoth's Gambit",
"hexcasting.spell.hexcasting:list_size": "Abacus Purification",
"hexcasting.spell.hexcasting:singleton": "Single's Purification",
"hexcasting.spell.hexcasting:empty_list": "Vacant Reflection",
"hexcasting.spell.hexcasting:reverse_list": "Retrograde Purification",
"hexcasting.spell.hexcasting:last_n_list": "Flock's Gambit",
"hexcasting.spell.hexcasting:get_entity": "Entity Purification",
"hexcasting.spell.hexcasting:get_entity/animal": "Entity Prfn.: Animal",
"hexcasting.spell.hexcasting:get_entity/monster": "Entity Prfn.: Monster",
@ -128,6 +133,8 @@
"hexcasting.spell.hexcasting:erase": "Erase Item",
"hexcasting.spell.hexcasting:create_water": "Create Water",
"hexcasting.spell.hexcasting:destroy_water": "Destroy Water",
"hexcasting.spell.hexcasting:ignite": "Ignite Block",
"hexcasting.spell.hexcasting:extinguish": "Extinguish Area",
"hexcasting.spell.hexcasting:conjure_block": "Conjure Block",
"hexcasting.spell.hexcasting:conjure_light": "Conjure Light",
"hexcasting.spell.hexcasting:bonemeal": "Overgrow",
@ -348,6 +355,8 @@
"hexcasting.page.blockworks5": "Conjure a barrier, using my colorizer. Costs about one $(item)Amethyst Dust/$.",
"hexcasting.page.blockworks6": "Conjure a magical light, using my colorizer. Costs about one $(item)Amethyst Dust/$.",
"hexcasting.page.blockworks7": "Encourage a plant or sapling at the target position to grow, as if $(item)Bonemeal/$ was applied. Costs about one $(item)Amethyst Dust/$.",
"hexcasting.page.blockworks8": "Start a fire on top of the given location, as if a $(item)Fire Charge/$ was applied. Costs about one $(item)Amethyst Dust/$.",
"hexcasting.page.blockworks9": "Extinguish blocks in a large area. Costs about two $(item)Amethyst Crystal/$s.",
"hexcasting.page.colorize1": "I must be holding a $(item)Pigment/$ in my other hand to cast this spell. When I do, it will consume the dye and permanently change my mind's coloration (well, until I cast the spell again). Costs about 1 $(item)Amethyst Dust/$.",
@ -415,6 +424,11 @@
"hexcasting.page.lists1": "Remove the number at the top of the stack, then replace the list at the top with the nth element of that list (where n is the number you removed). Replaces the list with Null if the number is out of bounds.",
"hexcasting.page.lists2": "Remove the top of the stack, then add it to the end of the list at the top of the stack.",
"hexcasting.page.lists3": "Remove the list at the top of the stack, then add all its elements to the end of the list at the top of the stack.",
"hexcasting.page.lists4": "Push an empty list to the top of the stack.",
"hexcasting.page.lists5": "Remove the top of the stack, then push a list containing only that element.",
"hexcasting.page.lists6": "Remove num elements from the stack, then add them to a list at the top of the stack.",
"hexcasting.page.lists7": "Remove the list at the top of the stack, then push the number of elements in the list to the stack.",
"hexcasting.page.lists8": "Reverse the list at the top of the stack.",
"hexcasting.entry.math": "Math",
"hexcasting.page.math1": "Many mathematical operations function on both numbers and vectors. Such arguments are written as \"num/vec\".",

View file

@ -29,6 +29,46 @@
"input": "list, list",
"output": "list",
"text": "hexcasting.page.lists3"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:empty_list",
"anchor": "hexcasting:empty_list",
"input": "",
"output": "list",
"text": "hexcasting.page.lists4"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:singleton",
"anchor": "hexcasting:singleton",
"input": "any",
"output": "list",
"text": "hexcasting.page.lists5"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:last_n_list",
"anchor": "hexcasting:last_n_list",
"input": "num",
"output": "list",
"text": "hexcasting.page.lists6"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:list_size",
"anchor": "hexcasting:list_size",
"input": "list",
"output": "num",
"text": "hexcasting.page.lists7"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:reverse_list",
"anchor": "hexcasting:reverse_list",
"input": "list",
"output": "list",
"text": "hexcasting.page.lists8"
}
]
}

View file

@ -61,6 +61,22 @@
"input": "vector",
"output": "",
"text": "hexcasting.page.blockworks7"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:ignite",
"anchor": "hexcasting:ignite",
"input": "vector",
"output": "",
"text": "hexcasting.page.blockworks8"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:extinguish",
"anchor": "hexcasting:extinguish",
"input": "vector",
"output": "",
"text": "hexcasting.page.blockworks9"
}
]
}