diff --git a/core/src/main/kotlin/com/willfp/libreforge/effects/Effects.kt b/core/src/main/kotlin/com/willfp/libreforge/effects/Effects.kt index 6bfc2b80..a4f88526 100644 --- a/core/src/main/kotlin/com/willfp/libreforge/effects/Effects.kt +++ b/core/src/main/kotlin/com/willfp/libreforge/effects/Effects.kt @@ -457,5 +457,6 @@ object Effects : Registry>() { register(EffectAddGlobalPoints) register(EffectDropItemSlot) register(EffectKeepLevel) + register(EffectAnimation) } } diff --git a/core/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectAnimation.kt b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectAnimation.kt new file mode 100644 index 00000000..e80402d7 --- /dev/null +++ b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectAnimation.kt @@ -0,0 +1,75 @@ +package com.willfp.libreforge.effects.impl + +import com.willfp.eco.core.config.interfaces.Config +import com.willfp.libreforge.ViolationContext +import com.willfp.libreforge.arguments +import com.willfp.libreforge.effects.Effect +import com.willfp.libreforge.effects.impl.animations.AnimationBlock +import com.willfp.libreforge.effects.impl.animations.Animations +import com.willfp.libreforge.plugin +import com.willfp.libreforge.toFloat3 +import com.willfp.libreforge.triggers.TriggerData +import com.willfp.libreforge.triggers.TriggerParameter +import com.willfp.libreforge.xz + +object EffectAnimation : Effect?>("animation") { + override val parameters = setOf( + TriggerParameter.LOCATION + ) + + override val arguments = arguments { + require("animation", "You must specify a valid animation!", Config::getString) { + Animations[it] != null + } + + inherit("animation_args") { Animations[it.getString("animation")] } + } + + override fun onTrigger(config: Config, data: TriggerData, compileData: AnimationBlock<*, *>?): Boolean { + val location = data.location?.clone() ?: return false + compileData ?: return false + + var tick = 0 + + fun playAnimation(animationBlock: AnimationBlock) { + val animationData = animationBlock.setUp( + location, + location.direction.toFloat3(), + data + ) + + plugin.runnableFactory.create { + if ( + animationBlock.play( + tick, + location, + location.direction.toFloat3(), + data, + animationData + ) + ) { + animationBlock.finish( + location, + location.direction.toFloat3(), + data, + animationData + ) + it.cancel() + } + + tick++ + }.runTaskTimer(0, 1) + } + + playAnimation(compileData) + + return true + } + + override fun makeCompileData(config: Config, context: ViolationContext): AnimationBlock<*, *>? { + return Animations.compile( + config, + context + ) + } +} diff --git a/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/Animation.kt b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/Animation.kt new file mode 100644 index 00000000..27b5982e --- /dev/null +++ b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/Animation.kt @@ -0,0 +1,64 @@ +package com.willfp.libreforge.effects.impl.animations + +import com.willfp.eco.core.config.interfaces.Config +import com.willfp.libreforge.Compilable +import com.willfp.libreforge.triggers.TriggerData +import dev.romainguy.kotlin.math.Float2 +import dev.romainguy.kotlin.math.Float3 +import org.bukkit.Location +import java.util.Objects + +abstract class Animation( + override val id: String +) : Compilable() { + /** + * Set up the animation. + */ + @Suppress("UNCHECKED_CAST") + open fun setUp( + sourceLocation: Location, + direction: Float3, + config: Config, + data: TriggerData, + compileData: T + ): T2 = Unit as T2 + + /** + * Play the animation, returning true if the animation is complete. + */ + abstract fun play( + tick: Int, + sourceLocation: Location, + direction: Float3, + config: Config, + triggerData: TriggerData, + compileData: T, + data: T2 + ): Boolean + + /** + * Finish the animation. + */ + open fun finish( + sourceLocation: Location, + direction: Float3, + config: Config, + triggerData: TriggerData, + compileData: T, + data: T2 + ) { + + } + + override fun equals(other: Any?): Boolean { + if (other !is Animation<*, *>) { + return false + } + + return this.id == other.id + } + + override fun hashCode(): Int { + return Objects.hash(this.id) + } +} diff --git a/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/AnimationBlock.kt b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/AnimationBlock.kt new file mode 100644 index 00000000..24f2bbbf --- /dev/null +++ b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/AnimationBlock.kt @@ -0,0 +1,39 @@ +package com.willfp.libreforge.effects.impl.animations + +import com.willfp.eco.core.config.interfaces.Config +import com.willfp.libreforge.Compiled +import com.willfp.libreforge.triggers.TriggerData +import dev.romainguy.kotlin.math.Float2 +import dev.romainguy.kotlin.math.Float3 +import org.bukkit.Location +import org.bukkit.entity.Player + +/** + * A single animation config block. + */ +class AnimationBlock( + val animation: Animation, + override val config: Config, + override val compileData: T +) : Compiled { + fun setUp( + sourceLocation: Location, + direction: Float3, + data: TriggerData + ) = animation.setUp(sourceLocation, direction, config, data, compileData) + + fun play( + tick: Int, + sourceLocation: Location, + direction: Float3, + triggerData: TriggerData, + data: T2 + ) = animation.play(tick, sourceLocation, direction, config, triggerData, compileData, data) + + fun finish( + sourceLocation: Location, + direction: Float3, + triggerData: TriggerData, + data: T2 + ) = animation.finish(sourceLocation, direction, config, triggerData, compileData, data) +} diff --git a/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/Animations.kt b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/Animations.kt new file mode 100644 index 00000000..106a27b7 --- /dev/null +++ b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/Animations.kt @@ -0,0 +1,48 @@ +package com.willfp.libreforge.effects.impl.animations + +import com.willfp.eco.core.config.interfaces.Config +import com.willfp.eco.core.registry.Registry +import com.willfp.libreforge.ConfigViolation +import com.willfp.libreforge.ViolationContext +import com.willfp.libreforge.effects.impl.animations.impl.AnimationSpinItem + + +@Suppress("UNUSED") +object Animations : Registry>() { + /** + * Compile a [config] into an AnimationBlock in a given [context]. + */ + fun compile(config: Config, context: ViolationContext): AnimationBlock<*, *>? { + val animationID = config.getString("animation") + val animation = get(animationID) + + if (animation == null) { + context.log(ConfigViolation("animation", "Invalid animation specified: ${animationID}!")) + return null + } + + return makeBlock(animation, config.getSubsection("animation_args"), context.with("animation args")) + } + + private fun makeBlock( + animation: Animation, + config: Config, + context: ViolationContext + ): AnimationBlock? { + if (!animation.checkConfig(config, context)) { + return null + } + + val compileData = animation.makeCompileData(config, context) + + return AnimationBlock( + animation, + config, + compileData, + ) + } + + init { + register(AnimationSpinItem) + } +} diff --git a/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/impl/AnimationSpinItem.kt b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/impl/AnimationSpinItem.kt new file mode 100644 index 00000000..0b36a921 --- /dev/null +++ b/core/src/main/kotlin/com/willfp/libreforge/effects/impl/animations/impl/AnimationSpinItem.kt @@ -0,0 +1,96 @@ +package com.willfp.libreforge.effects.impl.animations.impl + +import com.willfp.eco.core.config.interfaces.Config +import com.willfp.eco.core.items.Items +import com.willfp.libreforge.NoCompileData +import com.willfp.libreforge.arguments +import com.willfp.libreforge.effects.impl.animations.Animation +import com.willfp.libreforge.getDoubleFromExpression +import com.willfp.libreforge.getIntFromExpression +import com.willfp.libreforge.triggers.TriggerData +import dev.romainguy.kotlin.math.Float3 +import org.bukkit.Location +import org.bukkit.entity.ArmorStand +import org.bukkit.util.EulerAngle +import kotlin.math.cos +import kotlin.math.sin + +object AnimationSpinItem : Animation>("spin_item") { + override val arguments = arguments { + require("item", "You must specify the item!") + require("amount", "You must specify the amount of items!") + require("duration", "You must specify the duration!") + require("radius", "You must specify the radius!") + require("speed", "You must specify the speed!") + } + + override fun setUp( + sourceLocation: Location, + direction: Float3, + config: Config, + data: TriggerData, + compileData: NoCompileData + ): List { + val armorStands = mutableListOf() + val item = Items.lookup(config.getString("item")).item + + for (i in 0..config.getIntFromExpression("amount", data)) { + val armorStand = sourceLocation.world!!.spawn(sourceLocation, ArmorStand::class.java) { + it.setGravity(false) + it.isVisible = false + it.isInvulnerable = true + it.setArms(true) + it.setBasePlate(false) + it.canPickupItems = false + it.isCollidable = false + it.isMarker = true + it.isSilent = true + it.removeWhenFarAway = false + it.equipment.setItemInMainHand(item) + } + + armorStands.add(armorStand) + } + + return armorStands + } + + override fun play( + tick: Int, + sourceLocation: Location, + direction: Float3, + config: Config, + triggerData: TriggerData, + compileData: NoCompileData, + data: List + ): Boolean { + val speed = config.getDoubleFromExpression("speed", triggerData) + val radius = config.getDoubleFromExpression("radius", triggerData) + val amount = config.getIntFromExpression("amount", triggerData) + + val angle = tick * speed * 10 + + data.forEachIndexed { index, armorStand -> + val armorStandAngle = angle + index * 2 * Math.PI / amount // evenly spaced in radians + val x = cos(armorStandAngle) * radius + val z = sin(armorStandAngle) * radius + + val armorStandLocation = sourceLocation.clone().add(x, 0.0, z) + armorStand.teleport(armorStandLocation.add(0.0,0.5, 0.0)) + armorStand.rightArmPose = EulerAngle(0.0, armorStandAngle + Math.PI, 0.0) // Add PI to make the item point outwards + } + + return tick >= config.getDoubleFromExpression("duration", triggerData) + } + + override fun finish( + sourceLocation: Location, + direction: Float3, + config: Config, + triggerData: TriggerData, + compileData: NoCompileData, + data: List + ) { + data.forEach { it.remove() } + } +}