Skip to content

Commit

Permalink
Added animation system and spin_animation animation
Browse files Browse the repository at this point in the history
  • Loading branch information
WillFP committed Jan 13, 2024
1 parent 1fde5ed commit db71527
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -457,5 +457,6 @@ object Effects : Registry<Effect<*>>() {
register(EffectAddGlobalPoints)
register(EffectDropItemSlot)
register(EffectKeepLevel)
register(EffectAnimation)
}
}
Original file line number Diff line number Diff line change
@@ -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<AnimationBlock<*, *>?>("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 <T, T2> playAnimation(animationBlock: AnimationBlock<T, T2>) {
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
)
}
}
Original file line number Diff line number Diff line change
@@ -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<T, T2>(
override val id: String
) : Compilable<T>() {
/**
* 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)
}
}
Original file line number Diff line number Diff line change
@@ -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<T, T2>(
val animation: Animation<T, T2>,
override val config: Config,
override val compileData: T
) : Compiled<T> {
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)
}
Original file line number Diff line number Diff line change
@@ -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<Animation<*, *>>() {
/**
* 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 <T, T2> makeBlock(
animation: Animation<T, T2>,
config: Config,
context: ViolationContext
): AnimationBlock<T, T2>? {
if (!animation.checkConfig(config, context)) {
return null
}

val compileData = animation.makeCompileData(config, context)

return AnimationBlock(
animation,
config,
compileData,
)
}

init {
register(AnimationSpinItem)
}
}
Original file line number Diff line number Diff line change
@@ -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<NoCompileData, List<ArmorStand>>("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<ArmorStand> {
val armorStands = mutableListOf<ArmorStand>()
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<ArmorStand>
): 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<ArmorStand>
) {
data.forEach { it.remove() }
}
}

0 comments on commit db71527

Please sign in to comment.