198 lines
8.4 KiB
Kotlin
198 lines
8.4 KiB
Kotlin
package at.petrak.hexcasting.common.casting.operators.spells
|
|
|
|
import at.petrak.hexcasting.api.casting.ParticleSpray
|
|
import at.petrak.hexcasting.api.casting.RenderedSpell
|
|
import at.petrak.hexcasting.api.casting.castables.SpellAction
|
|
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
|
|
import at.petrak.hexcasting.api.casting.getPlayer
|
|
import at.petrak.hexcasting.api.casting.getPositiveDouble
|
|
import at.petrak.hexcasting.api.casting.iota.Iota
|
|
import at.petrak.hexcasting.api.misc.MediaConstants
|
|
import at.petrak.hexcasting.api.pigment.FrozenPigment
|
|
import at.petrak.hexcasting.api.player.FlightAbility
|
|
import at.petrak.hexcasting.common.lib.HexItems
|
|
import at.petrak.hexcasting.common.lib.HexSounds
|
|
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
|
import net.minecraft.Util
|
|
import net.minecraft.server.level.ServerLevel
|
|
import net.minecraft.server.level.ServerPlayer
|
|
import net.minecraft.sounds.SoundSource
|
|
import net.minecraft.util.Mth
|
|
import net.minecraft.world.item.DyeColor
|
|
import net.minecraft.world.item.ItemStack
|
|
import net.minecraft.world.phys.Vec3
|
|
import kotlin.math.max
|
|
import kotlin.math.roundToInt
|
|
|
|
class OpFlight(val type: Type) : SpellAction {
|
|
override val argc = 2
|
|
override fun execute(
|
|
args: List<Iota>,
|
|
ctx: CastingEnvironment
|
|
): SpellAction.Result {
|
|
val target = args.getPlayer(0, argc)
|
|
val theArg = args.getPositiveDouble(1, argc)
|
|
ctx.assertEntityInRange(target)
|
|
|
|
val cost = when (this.type) {
|
|
Type.LimitRange -> theArg * MediaConstants.DUST_UNIT
|
|
// A second of flight should cost 1 shard
|
|
Type.LimitTime -> theArg * MediaConstants.SHARD_UNIT
|
|
}.roundToInt()
|
|
|
|
// Convert to ticks
|
|
return SpellAction.Result(
|
|
Spell(this.type, target, theArg),
|
|
cost,
|
|
listOf(ParticleSpray(target.position(), Vec3(0.0, 2.0, 0.0), 0.0, 0.1))
|
|
)
|
|
}
|
|
|
|
enum class Type {
|
|
LimitRange,
|
|
LimitTime;
|
|
|
|
}
|
|
|
|
data class Spell(val type: Type, val target: ServerPlayer, val theArg: Double) : RenderedSpell {
|
|
override fun cast(ctx: CastingEnvironment) {
|
|
if (target.abilities.mayfly) {
|
|
// Don't accidentally clobber someone else's flight
|
|
// TODO make this a mishap?
|
|
return
|
|
}
|
|
|
|
val dim = target.level.dimension()
|
|
val origin = target.position()
|
|
|
|
val flight = when (this.type) {
|
|
Type.LimitRange -> FlightAbility(-1, dim, origin, theArg)
|
|
Type.LimitTime -> FlightAbility((theArg * 20.0).roundToInt(), dim, origin, -1.0)
|
|
}
|
|
|
|
IXplatAbstractions.INSTANCE.setFlight(target, flight)
|
|
|
|
target.abilities.mayfly = true
|
|
target.onUpdateAbilities()
|
|
}
|
|
}
|
|
|
|
|
|
companion object {
|
|
// blocks from the edge
|
|
private val DIST_DANGER_THRESHOLD = 4.0
|
|
|
|
// seconds left
|
|
private val TIME_DANGER_THRESHOLD = 7.0 * 20.0
|
|
|
|
@JvmStatic
|
|
fun tickAllPlayers(world: ServerLevel) {
|
|
for (player in world.players()) {
|
|
tickDownFlight(player)
|
|
}
|
|
}
|
|
|
|
@JvmStatic
|
|
fun tickDownFlight(player: ServerPlayer) {
|
|
val flight = IXplatAbstractions.INSTANCE.getFlight(player)
|
|
|
|
if (flight != null) {
|
|
val danger = getDanger(player, flight)
|
|
if (danger >= 1.0) {
|
|
IXplatAbstractions.INSTANCE.setFlight(player, null)
|
|
// stop shin smashing bonke
|
|
|
|
if (!player.isCreative && !player.isSpectator) {
|
|
val abilities = player.abilities
|
|
abilities.flying = false
|
|
abilities.mayfly = false
|
|
player.onUpdateAbilities()
|
|
}
|
|
player.level.playSound(null, player.x, player.y, player.z, HexSounds.FLIGHT_FINISH, SoundSource.PLAYERS, 2f, 1f)
|
|
val superDangerSpray = ParticleSpray(player.position(), Vec3(0.0, 1.0, 0.0), Math.PI, 0.4, count = 20)
|
|
superDangerSpray.sprayParticles(player.getLevel(), FrozenPigment(ItemStack(HexItems.DYE_COLORIZERS[DyeColor.RED]!!), Util.NIL_UUID))
|
|
superDangerSpray.sprayParticles(player.getLevel(), FrozenPigment(ItemStack(HexItems.DYE_COLORIZERS[DyeColor.BLACK]!!), Util.NIL_UUID))
|
|
} else {
|
|
if (!player.abilities.mayfly) {
|
|
player.abilities.mayfly = true
|
|
player.onUpdateAbilities()
|
|
}
|
|
val time2 = if (flight.timeLeft >= 0) {
|
|
flight.timeLeft - 1
|
|
} else {
|
|
flight.timeLeft
|
|
}
|
|
IXplatAbstractions.INSTANCE.setFlight(
|
|
player,
|
|
FlightAbility(
|
|
time2,
|
|
flight.dimension,
|
|
flight.origin,
|
|
flight.radius
|
|
)
|
|
)
|
|
|
|
val particleCount = 5
|
|
val dangerParticleCount = (particleCount * danger).roundToInt()
|
|
val okParticleCount = particleCount - dangerParticleCount
|
|
val oneDangerParticleCount = Mth.ceil(dangerParticleCount / 2.0)
|
|
val color = IXplatAbstractions.INSTANCE.getColorizer(player)
|
|
|
|
// TODO: have the particles go in the opposite direction of the velocity?
|
|
ParticleSpray(player.position(), Vec3(0.0, -0.6, 0.0), 0.6, Math.PI * 0.3, count = okParticleCount)
|
|
.sprayParticles(player.getLevel(), color)
|
|
val dangerSpray = ParticleSpray(player.position(), Vec3(0.0, 1.0, 0.0), 0.3, Math.PI * 0.75, count = 0)
|
|
dangerSpray.copy(count = oneDangerParticleCount)
|
|
.sprayParticles(player.getLevel(), FrozenPigment(ItemStack(HexItems.DYE_COLORIZERS[DyeColor.BLACK]!!), Util.NIL_UUID))
|
|
dangerSpray.copy(count = oneDangerParticleCount)
|
|
.sprayParticles(player.getLevel(), FrozenPigment(ItemStack(HexItems.DYE_COLORIZERS[DyeColor.RED]!!), Util.NIL_UUID))
|
|
|
|
if (player.level.random.nextFloat() < 0.02)
|
|
player.level.playSound(null, player.x, player.y, player.z, HexSounds.FLIGHT_AMBIENCE, SoundSource.PLAYERS, 0.2f, 1f)
|
|
|
|
if (flight.radius >= 0.0) {
|
|
// Show the origin
|
|
val spoofedOrigin = flight.origin.add(0.0, 1.0, 0.0)
|
|
ParticleSpray(spoofedOrigin, Vec3(0.0, 1.0, 0.0), 0.5, Math.PI * 0.1, count = 5)
|
|
.sprayParticles(player.getLevel(), color)
|
|
ParticleSpray(spoofedOrigin, Vec3(0.0, -1.0, 0.0), 1.5, Math.PI * 0.25, count = 5)
|
|
.sprayParticles(player.getLevel(), color)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return a number from 0 (totally fine) to 1 (danger will robinson, stop the flight)
|
|
// it's a double for particle reason
|
|
private fun getDanger(player: ServerPlayer, flight: FlightAbility): Double {
|
|
val radiusDanger = if (flight.radius >= 0.0) {
|
|
if (player.level.dimension() != flight.dimension) {
|
|
1.0
|
|
} else {
|
|
// Limit it only in X/Z
|
|
val posXZ = Vec3(player.x, 0.0, player.z)
|
|
val originXZ = Vec3(flight.origin.x, 0.0, flight.origin.z)
|
|
val dist = posXZ.distanceTo(originXZ)
|
|
val distFromEdge = flight.radius - dist
|
|
if (distFromEdge >= DIST_DANGER_THRESHOLD) {
|
|
0.0
|
|
} else if (dist > flight.radius) {
|
|
1.0
|
|
} else {
|
|
1.0 - (distFromEdge / DIST_DANGER_THRESHOLD)
|
|
}
|
|
}
|
|
} else 0.0
|
|
val timeDanger = if (flight.timeLeft >= 0) {
|
|
if (flight.timeLeft >= TIME_DANGER_THRESHOLD) {
|
|
0.0
|
|
} else {
|
|
val timeDanger = TIME_DANGER_THRESHOLD - flight.timeLeft
|
|
timeDanger / TIME_DANGER_THRESHOLD
|
|
}
|
|
} else 0.0
|
|
return max(radiusDanger, timeDanger)
|
|
}
|
|
}
|
|
}
|