kitgit

tirbofish/dropbear · diff

08baee6 · Thribhu K

feature: animation scripting works. jarvis, run github actions

Unverified

diff --git a/crates/eucalyptus-core/src/animation/mod.rs b/crates/eucalyptus-core/src/animation/mod.rs
index 6fa3b19..07f2a19 100644
--- a/crates/eucalyptus-core/src/animation/mod.rs
+++ b/crates/eucalyptus-core/src/animation/mod.rs
@@ -5,7 +5,13 @@ use dropbear_engine::animation::{AnimationComponent, AnimationSettings};
 use dropbear_engine::asset::ASSET_REGISTRY;
 use dropbear_engine::entity::MeshRenderer;
 use dropbear_engine::graphics::SharedGraphicsContext;
+use jni::objects::JObject;
+use jni::JNIEnv;
 use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent};
+use crate::ptr::WorldPtr;
+use crate::scripting::jni::utils::ToJObject;
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
 
 #[typetag::serde]
 impl SerializedComponent for AnimationComponent {}
@@ -75,14 +81,22 @@ impl InspectableComponent for AnimationComponent {
                 "No Animations"
             };
 
+            let mut selection_changed = false;
             ComboBox::from_label("Animation")
                 .selected_text(selected_label)
                 .show_ui(ui, |ui| {
                     for (index, name) in self.available_animations.iter().enumerate() {
-                        ui.selectable_value(&mut selected_index, index, name);
+                        if ui.selectable_value(&mut selected_index, index, name).changed() {
+                            selection_changed = true;
+                        }
                     }
                 });
 
+            if selection_changed && has_animations {
+                self.active_animation_index = Some(selected_index);
+                enabled = true;
+            }
+
             ui.horizontal(|ui| {
                 ui.label("Active");
 
@@ -138,4 +152,395 @@ impl InspectableComponent for AnimationComponent {
             }
         });
     }
+}
+
+fn collect_available_animations(
+    world: &World,
+    entity: Entity,
+    component: &AnimationComponent,
+) -> Vec<String> {
+    if !component.available_animations.is_empty() {
+        return component.available_animations.clone();
+    }
+
+    let Ok(renderer) = world.get::<&MeshRenderer>(entity) else {
+        return Vec::new();
+    };
+
+    let handle = renderer.model();
+    if handle.is_null() {
+        return Vec::new();
+    }
+
+    let registry = ASSET_REGISTRY.read();
+    let Some(model) = registry.get_model(handle) else {
+        return Vec::new();
+    };
+
+    model
+        .animations
+        .iter()
+        .map(|animation| animation.name.clone())
+        .collect()
+}
+
+struct NStringArray {
+    values: Vec<String>,
+}
+
+impl ToJObject for NStringArray {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let array = env
+            .new_object_array(self.values.len() as i32, "java/lang/String", JObject::null())
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        for (index, value) in self.values.iter().enumerate() {
+            let jstring = env
+                .new_string(value)
+                .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+            env.set_object_array_element(&array, index as i32, JObject::from(jstring))
+                .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        }
+
+        Ok(JObject::from(array))
+    }
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "animationComponentExistsForEntity")
+)]
+fn animation_component_exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<bool> {
+    Ok(world.get::<&AnimationComponent>(entity).is_ok())
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getActiveAnimationIndex")
+)]
+fn get_active_animation_index(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<Option<i32>> {
+    let component = world
+        .get::<&AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(component.active_animation_index.map(|index| index as i32))
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setActiveAnimationIndex")
+)]
+fn set_active_animation_index(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    index: Option<i32>,
+) -> DropbearNativeResult<()> {
+    let mut component = world
+        .get::<&mut AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    let index = match index {
+        Some(value) if value >= 0 => Some(value as usize),
+        Some(_) => return Err(DropbearNativeError::InvalidArgument),
+        None => None,
+    };
+
+    if let Some(value) = index {
+        if !component.available_animations.is_empty()
+            && value >= component.available_animations.len()
+        {
+            return Err(DropbearNativeError::InvalidArgument);
+        }
+    }
+
+    component.active_animation_index = index;
+    Ok(())
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getTime")
+)]
+fn get_time(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<f64> {
+    let component = world
+        .get::<&AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    if let Some(index) = component.active_animation_index {
+        if let Some(settings) = component.animation_settings.get(&index) {
+            return Ok(settings.time as f64);
+        }
+    }
+
+    Ok(component.time as f64)
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setTime")
+)]
+fn set_time(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    value: f64,
+) -> DropbearNativeResult<()> {
+    let mut component = world
+        .get::<&mut AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    component.time = value as f32;
+
+    if let Some(index) = component.active_animation_index {
+        let (time, speed, looping, is_playing) = (
+            component.time,
+            component.speed,
+            component.looping,
+            component.is_playing,
+        );
+        let settings = component
+            .animation_settings
+            .entry(index)
+            .or_insert_with(|| AnimationSettings {
+                time,
+                speed,
+                looping,
+                is_playing,
+            });
+        settings.time = time;
+    }
+
+    Ok(())
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getSpeed")
+)]
+fn get_speed(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<f64> {
+    let component = world
+        .get::<&AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    if let Some(index) = component.active_animation_index {
+        if let Some(settings) = component.animation_settings.get(&index) {
+            return Ok(settings.speed as f64);
+        }
+    }
+
+    Ok(component.speed as f64)
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setSpeed")
+)]
+fn set_speed(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    value: f64,
+) -> DropbearNativeResult<()> {
+    let mut component = world
+        .get::<&mut AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    component.speed = value as f32;
+
+    if let Some(index) = component.active_animation_index {
+        let (time, speed, looping, is_playing) = (
+            component.time,
+            component.speed,
+            component.looping,
+            component.is_playing,
+        );
+        let settings = component
+            .animation_settings
+            .entry(index)
+            .or_insert_with(|| AnimationSettings {
+                time,
+                speed,
+                looping,
+                is_playing,
+            });
+        settings.speed = speed;
+    }
+
+    Ok(())
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getLooping")
+)]
+fn get_looping(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<bool> {
+    let component = world
+        .get::<&AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    if let Some(index) = component.active_animation_index {
+        if let Some(settings) = component.animation_settings.get(&index) {
+            return Ok(settings.looping);
+        }
+    }
+
+    Ok(component.looping)
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setLooping")
+)]
+fn set_looping(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    value: bool,
+) -> DropbearNativeResult<()> {
+    let mut component = world
+        .get::<&mut AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    component.looping = value;
+
+    if let Some(index) = component.active_animation_index {
+        let (time, speed, looping, is_playing) = (
+            component.time,
+            component.speed,
+            component.looping,
+            component.is_playing,
+        );
+        let settings = component
+            .animation_settings
+            .entry(index)
+            .or_insert_with(|| AnimationSettings {
+                time,
+                speed,
+                looping,
+                is_playing,
+            });
+        settings.looping = value;
+    }
+
+    Ok(())
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getIsPlaying")
+)]
+fn get_is_playing(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<bool> {
+    let component = world
+        .get::<&AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    if let Some(index) = component.active_animation_index {
+        if let Some(settings) = component.animation_settings.get(&index) {
+            return Ok(settings.is_playing);
+        }
+    }
+
+    Ok(component.is_playing)
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "setIsPlaying")
+)]
+fn set_is_playing(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    value: bool,
+) -> DropbearNativeResult<()> {
+    let mut component = world
+        .get::<&mut AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    component.is_playing = value;
+
+    if let Some(index) = component.active_animation_index {
+        let (time, speed, looping, is_playing) = (
+            component.time,
+            component.speed,
+            component.looping,
+            component.is_playing,
+        );
+        let settings = component
+            .animation_settings
+            .entry(index)
+            .or_insert_with(|| AnimationSettings {
+                time,
+                speed,
+                looping,
+                is_playing,
+            });
+        settings.is_playing = value;
+    }
+
+    Ok(())
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getIndexFromString")
+)]
+fn get_index_from_string(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    name: String,
+) -> DropbearNativeResult<Option<i32>> {
+    let component = world
+        .get::<&AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    Ok(component.available_animations.iter().enumerate().find_map(|(i, l)| {
+        if *l == name {
+            Some(i as i32)
+        } else {
+            None
+        }
+    }))
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.animation.AnimationComponentNative", func = "getAvailableAnimations")
+)]
+fn get_available_animations(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<NStringArray> {
+    let component = world
+        .get::<&AnimationComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(NStringArray {
+        values: collect_available_animations(world, entity, &component),
+    })
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/lighting.rs b/crates/eucalyptus-core/src/lighting.rs
index 0b75a2c..479ed5f 100644
--- a/crates/eucalyptus-core/src/lighting.rs
+++ b/crates/eucalyptus-core/src/lighting.rs
@@ -5,6 +5,7 @@ use crate::scripting::jni::utils::{FromJObject, ToJObject};
 use crate::scripting::native::DropbearNativeError;
 use crate::scripting::result::DropbearNativeResult;
 use crate::types::NVector3;
+use dropbear_engine::attenuation::ATTENUATION_PRESETS;
 use dropbear_engine::entity::{EntityTransform, Transform};
 use dropbear_engine::lighting::{Light, LightType};
 use glam::{DQuat, DVec3, Vec3};
@@ -130,10 +131,13 @@ impl InspectableComponent for Light {
 
             if matches!(self.component.light_type, LightType::Point | LightType::Spot) {
                 ui.horizontal(|ui| {
-                    ui.label("Attenuation");
-                    ui.add(DragValue::new(&mut self.component.attenuation.constant).speed(0.01));
-                    ui.add(DragValue::new(&mut self.component.attenuation.linear).speed(0.01));
-                    ui.add(DragValue::new(&mut self.component.attenuation.quadratic).speed(0.01));
+                    ComboBox::from_id_salt("Attenuation Range")
+                        .selected_text(format!("Range {}", self.component.attenuation.range))
+                        .show_ui(ui, |ui| {
+                            for (preset, label) in ATTENUATION_PRESETS {
+                                ui.selectable_value(&mut self.component.attenuation, *preset, *label);
+                            }
+                        });
                 });
             }
 
@@ -170,8 +174,6 @@ impl InspectableComponent for Light {
             if self.component.depth.end < self.component.depth.start {
                 self.component.depth.end = self.component.depth.start;
             }
-
-            
         });
     }
 }
diff --git a/crates/eucalyptus-core/src/scripting/jni/primitives.rs b/crates/eucalyptus-core/src/scripting/jni/primitives.rs
index 45b327b..58c9b70 100644
--- a/crates/eucalyptus-core/src/scripting/jni/primitives.rs
+++ b/crates/eucalyptus-core/src/scripting/jni/primitives.rs
@@ -1,7 +1,7 @@
 use jni::JNIEnv;
 use jni::objects::{JObject, JValue};
 use jni::sys::{jdouble, jint, jlong};
-use crate::scripting::jni::utils::ToJObject;
+use crate::scripting::jni::utils::{FromJObject, ToJObject};
 use crate::scripting::native::DropbearNativeError;
 use crate::scripting::result::DropbearNativeResult;
 
@@ -20,6 +20,33 @@ impl ToJObject for Option<i32> {
     }
 }
 
+impl FromJObject for Option<i32> {
+    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
+        if obj.is_null() {
+            return Ok(None);
+        }
+
+        let class = env
+            .find_class("java/lang/Integer")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        if !env
+            .is_instance_of(obj, &class)
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?
+        {
+            return Err(DropbearNativeError::InvalidArgument);
+        }
+
+        let value = env
+            .call_method(obj, "intValue", "()I", &[])
+            .map_err(|_| DropbearNativeError::JNIMethodNotFound)?
+            .i()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        Ok(Some(value as i32))
+    }
+}
+
 impl ToJObject for Vec<i32> {
     fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
         self.as_slice().to_jobject(env)
diff --git a/scripting/commonMain/kotlin/com/dropbear/animation/AnimationComponent.kt b/scripting/commonMain/kotlin/com/dropbear/animation/AnimationComponent.kt
index a0f7dcf..8994769 100644
--- a/scripting/commonMain/kotlin/com/dropbear/animation/AnimationComponent.kt
+++ b/scripting/commonMain/kotlin/com/dropbear/animation/AnimationComponent.kt
@@ -1,18 +1,24 @@
 package com.dropbear.animation
 
 import com.dropbear.EntityId
+import com.dropbear.components.Camera
+import com.dropbear.components.cameraExistsForEntity
 import com.dropbear.ecs.Component
+import com.dropbear.ecs.ComponentType
 
-class AnimationComponent(parentEntity: EntityId) : Component(parentEntity, "AnimationComponent") {
+class AnimationComponent(val parentEntity: EntityId) : Component(parentEntity, "AnimationComponent") {
+    val availableAnimations: List<String>
+        get() = getAvailableAnimations()
+    
     var activeAnimationIndex: Int?
         get() = getActiveAnimationIndex()
         set(value) = setActiveAnimationIndex(value)
 
-    var time: Float
+    var time: Double
         get() = getTime()
         set(value) = setTime(value)
 
-    var speed: Float
+    var speed: Double
         get() = getSpeed()
         set(value) = setSpeed(value)
 
@@ -29,29 +35,49 @@ class AnimationComponent(parentEntity: EntityId) : Component(parentEntity, "Anim
     }
 
     fun play() {
+        if (activeAnimationIndex == null) {
+            val first = availableAnimations.firstOrNull()
+            if (first != null) {
+                activeAnimationIndex = 0
+            }
+        }
         isPlaying = true
     }
 
     fun stop() {
         isPlaying = false
-        time = 0f
+        time = 0.0
         activeAnimationIndex = null
     }
 
     fun reset() {
-        time = 0f
+        time = 0.0
     }
 
-    fun setAnimation(index: Int, speed: Float = 1f) = setActiveAnimationIndex(index).let { setSpeed(speed) }
+    fun setAnimation(index: Int, speed: Double = 1.0) = setActiveAnimationIndex(index).let { setSpeed(speed) }
+    fun setAnimation(animationName: String, speed: Double = 1.0) {
+        val index = getIndexFromString(animationName) ?: return
+        setActiveAnimationIndex(index).let { setSpeed(speed) }
+    }
+
+    companion object : ComponentType<AnimationComponent> {
+        override fun get(entityId: EntityId): AnimationComponent? {
+            return if (animationComponentExistsForEntity(entityId)) AnimationComponent(entityId) else null
+        }
+    }
 }
 
+expect fun animationComponentExistsForEntity(entityId: EntityId): Boolean
+
 expect fun AnimationComponent.getActiveAnimationIndex(): Int?
 expect fun AnimationComponent.setActiveAnimationIndex(index: Int?)
-expect fun AnimationComponent.getTime(): Float
-expect fun AnimationComponent.setTime(value: Float)
-expect fun AnimationComponent.getSpeed(): Float
-expect fun AnimationComponent.setSpeed(value: Float)
+expect fun AnimationComponent.getTime(): Double
+expect fun AnimationComponent.setTime(value: Double)
+expect fun AnimationComponent.getSpeed(): Double
+expect fun AnimationComponent.setSpeed(value: Double)
 expect fun AnimationComponent.getLooping(): Boolean
 expect fun AnimationComponent.setLooping(value: Boolean)
 expect fun AnimationComponent.getIsPlaying(): Boolean
-expect fun AnimationComponent.setIsPlaying(value: Boolean)
\ No newline at end of file
+expect fun AnimationComponent.setIsPlaying(value: Boolean)
+expect fun AnimationComponent.getIndexFromString(name: String): Int?
+expect fun AnimationComponent.getAvailableAnimations(): List<String>
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/components/AnimationComponent.kt b/scripting/commonMain/kotlin/com/dropbear/components/AnimationComponent.kt
deleted file mode 100644
index 05c8207..0000000
--- a/scripting/commonMain/kotlin/com/dropbear/components/AnimationComponent.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.dropbear.components
-
-import com.dropbear.EntityId
-import com.dropbear.ecs.Component
-
-class AnimationComponent(parentEntity: EntityId) : Component(parentEntity, "AnimationComponent") {
-    var activeAnimationIndex: Int?
-        get() = getActiveAnimationIndex()
-        set(value) = setActiveAnimationIndex(value)
-
-    var time: Float
-        get() = getTime()
-        set(value) = setTime(value)
-
-    var speed: Float
-        get() = getSpeed()
-        set(value) = setSpeed(value)
-
-    var looping: Boolean
-        get() = getLooping()
-        set(value) = setLooping(value)
-
-    var isPlaying: Boolean
-        get() = getIsPlaying()
-        set(value) = setIsPlaying(value)
-
-    fun pause() {
-        isPlaying = false
-    }
-
-    fun play() {
-        isPlaying = true
-    }
-
-    fun stop() {
-        isPlaying = false
-        time = 0f
-        activeAnimationIndex = null
-    }
-
-    fun reset() {
-        time = 0f
-    }
-
-    fun setAnimation(index: Int, speed: Float = 1f) = setActiveAnimationIndex(index).let { setSpeed(speed) }
-}
-
-expect fun AnimationComponent.getActiveAnimationIndex(): Int?
-expect fun AnimationComponent.setActiveAnimationIndex(index: Int?)
-expect fun AnimationComponent.getTime(): Float
-expect fun AnimationComponent.setTime(value: Float)
-expect fun AnimationComponent.getSpeed(): Float
-expect fun AnimationComponent.setSpeed(value: Float)
-expect fun AnimationComponent.getLooping(): Boolean
-expect fun AnimationComponent.setLooping(value: Boolean)
-expect fun AnimationComponent.getIsPlaying(): Boolean
-expect fun AnimationComponent.setIsPlaying(value: Boolean)
\ No newline at end of file
diff --git a/scripting/jvmMain/java/com/dropbear/animation/AnimationComponentNative.java b/scripting/jvmMain/java/com/dropbear/animation/AnimationComponentNative.java
new file mode 100644
index 0000000..ec62c11
--- /dev/null
+++ b/scripting/jvmMain/java/com/dropbear/animation/AnimationComponentNative.java
@@ -0,0 +1,24 @@
+package com.dropbear.animation;
+
+import com.dropbear.EucalyptusCoreLoader;
+
+public class AnimationComponentNative {
+    static {
+        new EucalyptusCoreLoader().ensureLoaded();
+    }
+
+    public static native boolean animationComponentExistsForEntity(long worldHandle, long entityId);
+
+    public static native Integer getActiveAnimationIndex(long worldHandle, long entityId);
+    public static native void setActiveAnimationIndex(long worldHandle, long entityId, Integer index);
+    public static native double getTime(long worldHandle, long entityId);
+    public static native void setTime(long worldHandle, long entityId, double value);
+    public static native double getSpeed(long worldHandle, long entityId);
+    public static native void setSpeed(long worldHandle, long entityId, double value);
+    public static native boolean getLooping(long worldHandle, long entityId);
+    public static native void setLooping(long worldHandle, long entityId, boolean value);
+    public static native boolean getIsPlaying(long worldHandle, long entityId);
+    public static native void setIsPlaying(long worldHandle, long entityId, boolean value);
+    public static native Integer getIndexFromString(long worldHandle, long entityId, String name);
+    public static native String[] getAvailableAnimations(long worldHandle, long entityId);
+}
diff --git a/scripting/jvmMain/kotlin/com/dropbear/animation/AnimationComponent.jvm.kt b/scripting/jvmMain/kotlin/com/dropbear/animation/AnimationComponent.jvm.kt
index 3901075..88bef1b 100644
--- a/scripting/jvmMain/kotlin/com/dropbear/animation/AnimationComponent.jvm.kt
+++ b/scripting/jvmMain/kotlin/com/dropbear/animation/AnimationComponent.jvm.kt
@@ -1,38 +1,56 @@
 package com.dropbear.animation
 
-import com.dropbear.animation.AnimationComponent
+import com.dropbear.DropbearEngine
+import com.dropbear.EntityId
 
 actual fun AnimationComponent.getActiveAnimationIndex(): Int? {
-    TODO("Not yet implemented")
+    return AnimationComponentNative.getActiveAnimationIndex(DropbearEngine.native.worldHandle, parentEntity.raw)
 }
 
 actual fun AnimationComponent.setActiveAnimationIndex(index: Int?) {
+    return AnimationComponentNative.setActiveAnimationIndex(DropbearEngine.native.worldHandle, parentEntity.raw, index)
 }
 
-actual fun AnimationComponent.getTime(): Float {
-    TODO("Not yet implemented")
+actual fun AnimationComponent.getTime(): Double {
+    return AnimationComponentNative.getTime(DropbearEngine.native.worldHandle, parentEntity.raw)
 }
 
-actual fun AnimationComponent.setTime(value: Float) {
+actual fun AnimationComponent.setTime(value: Double) {
+    return AnimationComponentNative.setTime(DropbearEngine.native.worldHandle, parentEntity.raw, value)
 }
 
-actual fun AnimationComponent.getSpeed(): Float {
-    TODO("Not yet implemented")
+actual fun AnimationComponent.getSpeed(): Double {
+    return AnimationComponentNative.getSpeed(DropbearEngine.native.worldHandle, parentEntity.raw)
 }
 
-actual fun AnimationComponent.setSpeed(value: Float) {
+actual fun AnimationComponent.setSpeed(value: Double) {
+    return AnimationComponentNative.setSpeed(DropbearEngine.native.worldHandle, parentEntity.raw, value)
 }
 
 actual fun AnimationComponent.getLooping(): Boolean {
-    TODO("Not yet implemented")
+    return AnimationComponentNative.getLooping(DropbearEngine.native.worldHandle, parentEntity.raw)
 }
 
 actual fun AnimationComponent.setLooping(value: Boolean) {
+    return AnimationComponentNative.setLooping(DropbearEngine.native.worldHandle, parentEntity.raw, value)
 }
 
 actual fun AnimationComponent.getIsPlaying(): Boolean {
-    TODO("Not yet implemented")
+    return AnimationComponentNative.getIsPlaying(DropbearEngine.native.worldHandle, parentEntity.raw)
 }
 
 actual fun AnimationComponent.setIsPlaying(value: Boolean) {
+    return AnimationComponentNative.setIsPlaying(DropbearEngine.native.worldHandle, parentEntity.raw, value)
+}
+
+actual fun AnimationComponent.getIndexFromString(name: String): Int? {
+    return AnimationComponentNative.getIndexFromString(DropbearEngine.native.worldHandle, parentEntity.raw, name)
+}
+
+actual fun AnimationComponent.getAvailableAnimations(): List<String> {
+    return AnimationComponentNative.getAvailableAnimations(DropbearEngine.native.worldHandle, parentEntity.raw).asList()
+}
+
+actual fun animationComponentExistsForEntity(entityId: EntityId): Boolean {
+    return AnimationComponentNative.animationComponentExistsForEntity(DropbearEngine.native.worldHandle, entityId.raw)
 }
\ No newline at end of file
diff --git a/scripting/jvmMain/kotlin/com/dropbear/components/AnimationComponent.jvm.kt b/scripting/jvmMain/kotlin/com/dropbear/components/AnimationComponent.jvm.kt
deleted file mode 100644
index eb9b5f7..0000000
--- a/scripting/jvmMain/kotlin/com/dropbear/components/AnimationComponent.jvm.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.dropbear.components
-
-actual fun AnimationComponent.getActiveAnimationIndex(): Int? {
-    return null
-}
-
-actual fun AnimationComponent.setActiveAnimationIndex(index: Int?) {
-    TODO("Not yet implemented")
-}
-
-actual fun AnimationComponent.getTime(): Float {
-    return 0f
-}
-
-actual fun AnimationComponent.setTime(value: Float) {
-    TODO("Not yet implemented")
-}
-
-actual fun AnimationComponent.getSpeed(): Float {
-    return 1f
-}
-
-actual fun AnimationComponent.setSpeed(value: Float) {
-    TODO("Not yet implemented")
-}
-
-actual fun AnimationComponent.getLooping(): Boolean {
-    return false
-}
-
-actual fun AnimationComponent.setLooping(value: Boolean) {
-    TODO("Not yet implemented")
-}
-
-actual fun AnimationComponent.getIsPlaying(): Boolean {
-    return false
-}
-
-actual fun AnimationComponent.setIsPlaying(value: Boolean) {
-    TODO("Not yet implemented")
-}
diff --git a/scripting/nativeMain/kotlin/com/dropbear/animation/AnimationComponent.native.kt b/scripting/nativeMain/kotlin/com/dropbear/animation/AnimationComponent.native.kt
index 3901075..0e3595d 100644
--- a/scripting/nativeMain/kotlin/com/dropbear/animation/AnimationComponent.native.kt
+++ b/scripting/nativeMain/kotlin/com/dropbear/animation/AnimationComponent.native.kt
@@ -1,6 +1,6 @@
 package com.dropbear.animation
 
-import com.dropbear.animation.AnimationComponent
+import com.dropbear.EntityId
 
 actual fun AnimationComponent.getActiveAnimationIndex(): Int? {
     TODO("Not yet implemented")
@@ -9,18 +9,18 @@ actual fun AnimationComponent.getActiveAnimationIndex(): Int? {
 actual fun AnimationComponent.setActiveAnimationIndex(index: Int?) {
 }
 
-actual fun AnimationComponent.getTime(): Float {
+actual fun AnimationComponent.getTime(): Double {
     TODO("Not yet implemented")
 }
 
-actual fun AnimationComponent.setTime(value: Float) {
+actual fun AnimationComponent.setTime(value: Double) {
 }
 
-actual fun AnimationComponent.getSpeed(): Float {
+actual fun AnimationComponent.getSpeed(): Double {
     TODO("Not yet implemented")
 }
 
-actual fun AnimationComponent.setSpeed(value: Float) {
+actual fun AnimationComponent.setSpeed(value: Double) {
 }
 
 actual fun AnimationComponent.getLooping(): Boolean {
@@ -35,4 +35,16 @@ actual fun AnimationComponent.getIsPlaying(): Boolean {
 }
 
 actual fun AnimationComponent.setIsPlaying(value: Boolean) {
+}
+
+actual fun AnimationComponent.getIndexFromString(name: String): Int? {
+    TODO("Not yet implemented")
+}
+
+actual fun AnimationComponent.getAvailableAnimations(): List<String> {
+    TODO("Not yet implemented")
+}
+
+actual fun animationComponentExistsForEntity(entityId: EntityId): Boolean {
+    TODO("Not yet implemented")
 }
\ No newline at end of file
diff --git a/scripting/nativeMain/kotlin/com/dropbear/components/AnimationComponent.native.kt b/scripting/nativeMain/kotlin/com/dropbear/components/AnimationComponent.native.kt
deleted file mode 100644
index eb9b5f7..0000000
--- a/scripting/nativeMain/kotlin/com/dropbear/components/AnimationComponent.native.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.dropbear.components
-
-actual fun AnimationComponent.getActiveAnimationIndex(): Int? {
-    return null
-}
-
-actual fun AnimationComponent.setActiveAnimationIndex(index: Int?) {
-    TODO("Not yet implemented")
-}
-
-actual fun AnimationComponent.getTime(): Float {
-    return 0f
-}
-
-actual fun AnimationComponent.setTime(value: Float) {
-    TODO("Not yet implemented")
-}
-
-actual fun AnimationComponent.getSpeed(): Float {
-    return 1f
-}
-
-actual fun AnimationComponent.setSpeed(value: Float) {
-    TODO("Not yet implemented")
-}
-
-actual fun AnimationComponent.getLooping(): Boolean {
-    return false
-}
-
-actual fun AnimationComponent.setLooping(value: Boolean) {
-    TODO("Not yet implemented")
-}
-
-actual fun AnimationComponent.getIsPlaying(): Boolean {
-    return false
-}
-
-actual fun AnimationComponent.setIsPlaying(value: Boolean) {
-    TODO("Not yet implemented")
-}