HexCasting/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpExtinguish.kt
2022-06-12 15:06:10 -05:00

125 lines
5.1 KiB
Kotlin

package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.misc.ManaConstants
import at.petrak.hexcasting.api.spell.*
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.ktxt.UseOnContext
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.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<ParticleSpray>> {
val target = args.getChecked<Vec3>(0, argc)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
ManaConstants.DUST_UNIT * 6,
listOf(ParticleSpray.burst(target, 1.0))
)
}
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.atCenterOf(here))
val distFromTarget =
target.distanceTo(Vec3.atCenterOf(here)) // max distance to prevent runaway shenanigans
if (distFromFocus < Operator.MAX_DISTANCE * Operator.MAX_DISTANCE && seen.add(here) && distFromTarget < 10 && ctx.world.mayInteract(
ctx.caster,
here
)
) {
// 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.atCenterOf(here)
wilson.useOn(
UseOnContext(
ctx.world,
null,
InteractionHand.MAIN_HAND,
ItemStack(wilson),
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
)
}
}
}
}