diff --git a/Game.cpp b/Game.cpp index e358c3f..2d46a8a 100644 --- a/Game.cpp +++ b/Game.cpp @@ -228,8 +228,12 @@ public: control |= 8; else if (key == OgreBites::SDLK_LSHIFT) control |= 16; + else if (key == 'e') + control |= 32; + else if (key == 'f') + control |= 64; if (key == 'w' || key == 'a' || key == 's' || key == 'd' || - key == OgreBites::SDLK_LSHIFT) + key == 'e' || key == OgreBites::SDLK_LSHIFT) return true; return false; } diff --git a/assets/blender/edited-normal-female.blend b/assets/blender/edited-normal-female.blend index c0af344..1dc5dfe 100644 --- a/assets/blender/edited-normal-female.blend +++ b/assets/blender/edited-normal-female.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3373f5d81a125eea24d73e487be0bc335bf28de5019e69a1fe72e591fea15643 -size 16554740 +oid sha256:0011c2f09089497cd7ab75535e4bcf0bd04f729de36efe118cc50e3d233b7463 +size 16951489 diff --git a/assets/blender/edited-normal-male.blend b/assets/blender/edited-normal-male.blend index af19d31..e6758f4 100644 --- a/assets/blender/edited-normal-male.blend +++ b/assets/blender/edited-normal-male.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d702f3ebec3df919d00e43ab365a64cb77b559b34a489e6a4bd8dba5d9e464e -size 12542174 +oid sha256:98e8b9e8af77703eca5e2ee483e8a6e12ac62c59223754cf7383cd99e1300771 +size 12965958 diff --git a/assets/blender/vehicles/raft.blend b/assets/blender/vehicles/raft.blend index b924aa4..8f18c8c 100644 --- a/assets/blender/vehicles/raft.blend +++ b/assets/blender/vehicles/raft.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:302a581e88d6f54848185fe523ba158e8fa506dbd569d0b70c04eab995fe7f9c -size 395032 +oid sha256:5928eab3aa214694e8b5e995dcb64f0ac353c11dce4975fabebe16d9079b9a92 +size 395001 diff --git a/lua-scripts/data.lua b/lua-scripts/data.lua index 52d208b..6a5544b 100644 --- a/lua-scripts/data.lua +++ b/lua-scripts/data.lua @@ -228,13 +228,14 @@ function StartGameQuest() quest.activate = function(this) print('activate...') local mc_is_free = function() - this.boat_id = ecs_vehicle_set("raft", 0, 0, -20, 1.75) - this.npc_id = ecs_npc_set("normal-female.glb", 0, 2, -20, 1.75) - this.boat = true --- ecs_set_slot(this.boat_id, this.npc_id, "captain_seat") --- ecs_character_physics_control(this.npc_id, false) - ecs_character_params_set("player", "gravity", true) - ecs_character_params_set("player", "buoyancy", true) + this.boat_id = ecs_vehicle_set("raft", 0, 0, -10, 1.75) + this.npc_id = ecs_npc_set("normal-female.glb", 0, 2, -10, 1.75) + this.boat = true +-- ecs_set_slot(this.boat_id, this.npc_id, "captain_seat") +-- ecs_character_physics_control(this.npc_id, false) + local ent = ecs_get_player_entity() + ecs_character_params_set(ent, "gravity", true) + ecs_character_params_set(ent, "buoyancy", true) end this.story:bind('mc_is_free', mc_is_free) this.base.activate(this) @@ -248,7 +249,128 @@ function StartGameQuest() end return quest end +function create_actuator() + return { + is_complete = false, + complete = function(this) + return this.is_complete + end, + finish = function(this) + this.is_complete = true + ecs_character_set_actuator(this.entity, "") + ecs_character_physics_control(this.entity, true) + print("COMPLETE") + end, + forward = function(this) + if (this.forward_animation) then + this:animation(this.forward_animation) + end + end, + animation = function(this, animation) + print("ANIMATION: ", animation) + ecs_character_set_actuator(this.entity, animation) + end, + event = function(this, event, trigger_entity, what_entity) + print("actuator events: ", event) + if event == "actuator_forward" then + this:forward() + return true + elseif event == "_in_actuator_forward" then + this:forward() + return true + elseif event == "actuator_exit" then + this:finish() + return true + end + if this.finish_events then + for i, p in ipairs(this.finish_events) do + if p == event then + this:finish() + break + end + end + if this.is_complete then + return true + end + end + return false + end, + } +end quests = {} +local actuator = nil +function check_actuator_event(event, trigger_entity, what_entity) + print("check_actuator_event: ", event) + if event == "actuator_enter" then + if not ecs_character_is_player(what_entity) then + return + end + ecs_character_physics_control(what_entity, false) + local animation = ecs_trigger_get_animation(trigger_entity) + ecs_character_set_actuator(what_entity, animation) + ecs_trigger_set_position(trigger_entity, what_entity) + local ent = ecs_get_entity(what_entity) + if (ent.is_character()) then + print("character") + end + if (ent.is_player()) then + print("player") + end +-- crash() + actuator = create_actuator() + actuator.trigger = trigger_entity + actuator.entity = what_entity + actuator.forward = function(this) + this:animation("swimming-edge-climb") + local ent = ecs_get_player_entity() + ecs_character_params_set(ent, "gravity", true) + ecs_character_params_set(ent, "buoyancy", true) + end + actuator.base_event = actuator.event + actuator.finish_events = {"animation:swimming-edge-climb:end"} + actuator.event = function(this, event, te, we) + print("actuator events 1: ", event) + if this.base_event(this, event, te, we) then + return true + end + return false + end + elseif event == "character_enter" then + if not ecs_character_is_player(trigger_entity) then + return + end + actuator = create_actuator() + actuator.trigger = trigger_entity + actuator.entity = trigger_entity + actuator.other_entity = what_entity + actuator.forward_animation = "pass-character" + actuator.finish_events = {"animation:pass-character:end"} + actuator.base_event = actuator.event + actuator.event = function(this, event, te, we) + print("actuator events 2: ", event) + if event == "actuator_exit" then + this:animation("idle") + return true + end + if this.base_event(this, event, te, we) then + return true + end + if event == "character_enter" then + -- why? +-- ecs_character_set_actuator(this.entity, "idle") +-- ecs_character_set_actuator(this.entity, "idle") + return true + elseif event == "actuator_enter" then + -- why? +-- ecs_character_set_actuator(this.entity, "idle") +-- ecs_character_set_actuator(this.entity, "idle") + return true + end + return false + end + actuator:animation("idle") + end +end -- ecs_set_debug_drawing(true) setup_handler(function(event, trigger_entity, what_entity) print(event) @@ -268,27 +390,27 @@ setup_handler(function(event, trigger_entity, what_entity) local answer = narration_get_answer() print("answered:", answer) elseif event == "new_game" then - ecs_character_params_set("player", "gravity", true) - ecs_character_params_set("player", "buoyancy", false) + local ent = ecs_get_player_entity() + ecs_character_params_set(ent, "gravity", true) + ecs_character_params_set(ent, "buoyancy", false) local quest = StartGameQuest() quests[quest.name] = quest for k, v in pairs(quests) do print(k, v.active) end quest:activate() - elseif event == "actuator_enter" then - ecs_character_physics_control(what_entity, false) - ecs_character_set_actuator(what_entity, "swimming-hold-edge") - ecs_trigger_set_position(trigger_entity, what_entity) - local ent = ecs_get_entity(what_entity) - if (ent.is_character()) then - print("character") + else + if not actuator then + check_actuator_event(event, trigger_entity, what_entity) + else + if not actuator:event(event, trigger_entity, what_entity) then + crash() + end + if actuator:complete() then + print("ACTUATOR COMPLETE") + print("EXIT ACTUATOR") + actuator = nil + end end - if (ent.is_player()) then - print("player") - end --- crash() - elseif event == "actuator_exit" then - crash() end end) diff --git a/src/gamedata/BoatModule.cpp b/src/gamedata/BoatModule.cpp index bd54d2b..2c7b150 100644 --- a/src/gamedata/BoatModule.cpp +++ b/src/gamedata/BoatModule.cpp @@ -89,7 +89,7 @@ BoatModule::BoatModule(flecs::world &ecs) ECS::get() .mWorld->addRigidBody( 0, boat.mEnt, - Ogre::Bullet::CT_TRIMESH, + Ogre::Bullet::CT_BOX, nullptr, 2, 0x7fffffff); b2e.entities[body.body] = e; std::vector slots = diff --git a/src/gamedata/CharacterAnimationModule.cpp b/src/gamedata/CharacterAnimationModule.cpp index 0b04bc1..17fbc52 100644 --- a/src/gamedata/CharacterAnimationModule.cpp +++ b/src/gamedata/CharacterAnimationModule.cpp @@ -1,5 +1,6 @@ #include #include "Components.h" +#include "EventTriggerModule.h" #include "CharacterModule.h" #include "CharacterAnimationModule.h" namespace ECS @@ -17,10 +18,18 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) ch.mSkeleton->setBlendMode( Ogre::ANIMBLEND_CUMULATIVE); Ogre::String animNames[] = { - "idle", "walking", - "running", "treading_water", - "swimming", "hanging-idle", - "hanging-climb", "swimming-hold-edge" + "idle", + "walking", + "running", + "treading_water", + "swimming", + "hanging-idle", + "hanging-climb", + "swimming-hold-edge", + "swimming-edge-climb", + "character-talk", + "pass-character", + "idle-act" }; int state_count = sizeof(animNames) / sizeof(animNames[0]); @@ -28,15 +37,17 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) new AnimationSystem(false); for (i = 0; i < state_count; i++) { Animation *animation = new Animation( + ch.mSkeleton, ch.mBodyEnt->getAnimationState( animNames[i]), ch.mSkeleton->getAnimation( - animNames[i]) - - ); + animNames[i]), + e); +#ifdef VDEBUG std::cout << "animation: " << animNames[i] << std::endl; +#endif animation->setLoop(true); anim.mAnimationSystem->add_animation( animNames[i], animation); @@ -71,16 +82,34 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) ->end() ->end() ->state("actuator") - ->state_machine(ANIM_FADE_SPEED, "actuator-state") + ->state_machine(ANIM_FADE_SPEED * 10.0f, "actuator-state") ->state("hanging-idle") ->animation("hanging-idle") ->end() ->state("swimming-hold-edge") ->animation("swimming-hold-edge") ->end() + ->state("swimming-edge-climb") + ->animation("swimming-edge-climb") + ->trigger(e, "end_of_climb", 0.99f, "animation:swimming-edge-climb:end") + ->end() ->state("hanging-climb") ->animation("hanging-climb") + ->trigger(e, "end_of_climb2", 0.99f, "animation:hanging-climb:end") ->end() + ->state("idle") + ->animation("idle-act") + ->end() + ->state("pass-character") + ->animation("pass-character") + ->trigger(e, "pass-character", 0.99f, "animation:pass-character:end") + ->end() + ->state("character-talk") + ->animation("character-talk") + ->end() + ->transition_end("swimming-edge-climb", "idle") + ->transition_end("hanging-climb", "idle") + ->transition_end("pass-character", "idle") ->end() ->end() ->end(); @@ -88,32 +117,85 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) anim.mAnimationSystem ->get("main") - ->setAnimation("locomotion"); + ->setAnimation("locomotion", true); anim.mAnimationSystem ->get( "locomotion-state") - ->setAnimation("idle"); + ->setAnimation("idle", true); anim.configured = true; } }); +#if 0 + ecs.system("RootMotionStart") + .kind(flecs::OnUpdate) + .each([this](flecs::entity e, CharacterBase &ch) { + ch.mBoneMotion = Ogre::Vector3::ZERO; + }); +#endif ecs.system( "HandleAnimations1") .kind(flecs::OnUpdate) .each([this](flecs::entity e, const EngineData &eng, CharacterBase &ch, AnimationControl &anim) { float delta = eng.delta; - anim.mAnimationSystem->addTime(delta); + // ch.mBoneMotion = Ogre::Vector3::ZERO; + bool result = anim.mAnimationSystem->addTime(delta); if (!ch.mRootBone) return; - ch.mBoneMotion += ch.mRootBone->getPosition(); + // The value we get is interpolated value. When result is true it is new step +#if 0 + Ogre::Vector3 offset = ch.mRootBone->getPosition(); + ch.mBoneMotion = static_cast( + anim.mListener) + ->getDeltaMotion(); + ch.mRootBone->setPosition(Ogre::Vector3::ZERO); + Ogre::Vector3 d = offset - ch.mBonePrevMotion; + ch.mBonePrevMotion = offset; + + std::cout << "length: " << d.length() << std::endl; + if (d.squaredLength() > 0.02f * 0.02f) + d = offset; + if (d.squaredLength() > 0.02f * 0.02f) + d = Ogre::Vector3::ZERO; + std::cout << "length2: " << d.length() << std::endl; + OgreAssert(d.length() < 0.5f, "bad offset"); + ch.mBoneMotion = d; +#endif +#if 0 + if (result) { + if (d.squaredLength() > 0.0f) + ch.mBoneMotion = + ch.mRootBone->getPosition() - + ch.mBoneMotion; + else + ch.mBoneMotion = + ch.mRootBone->getPosition(); + } else { + ch.mBoneMotion = ch.mRootBone->getPosition() - + ch.mBoneMotion; + } +#endif + +#undef VDEBUG +#ifdef VDEBUG + std::cout << "root motion: " << delta << ": " + << ch.mBoneMotion << " - " + << ch.mRootBone->getPosition() + << " result: " << result << std::endl; +#endif +#undef VDEBUG +#if 0 + // ch.mRootBone->setPosition(Ogre::Vector3::ZERO); + ch.mBonePrevMotion = offset; +#endif }); - ecs.system( + ecs.system( "HandleRootMotionVelocity") .kind(flecs::OnUpdate) .with() .with() .each([this](flecs::entity e, const EngineData &eng, - const CharacterBase &ch, CharacterVelocity &v) { + CharacterBase &ch, CharacterVelocity &v) { if (eng.delta < 0.0000001f) return; if (!ch.mBodyNode) @@ -121,7 +203,9 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) Ogre::Quaternion rot = ch.mBodyNode->getOrientation(); Ogre::Vector3 pos = ch.mBodyNode->getPosition(); Ogre::Vector3 boneMotion = ch.mBoneMotion; - v.velocity = rot * boneMotion / eng.delta; + v.velocity = Ogre::Math::lerp( + v.velocity, rot * boneMotion / eng.delta, + 0.99f); if (!e.has() && !e.has()) { if (eng.startupDelay <= 0.0f) @@ -129,6 +213,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) v.velocity.y = Ogre::Math::Clamp( v.velocity.y, -10.5f, 1000000.0f); } + // ch.mBoneMotion = Ogre::Vector3::ZERO; }); ecs.system("HandleSwimming") @@ -182,8 +267,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) penetration = body.mController ->isPenetrating(); if (is_on_floor) - v.gvelocity = Ogre::Vector3( - 0.0f, 0.0f, 0.0f); + v.gvelocity = + Ogre::Vector3::ZERO; btTransform from( Ogre::Bullet::convert( @@ -192,14 +277,21 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) Ogre::Bullet::convert( ch.mBodyNode ->getPosition())); - ch.mBodyNode->setPosition( - ch.mBodyNode->getPosition() + + ch.mBodyNode->_setDerivedPosition( + ch.mBodyNode + ->_getDerivedPosition() + rotMotion); - - ch.mBoneMotion = Ogre::Vector3(0, 0, 0); } } }); + ecs.system("HandleRootMotionEnd") + .kind(flecs::OnUpdate) + .each([this](flecs::entity e, CharacterVelocity &v, + CharacterBase &ch) { + // zero the velocity; + // v.velocity = Ogre::Vector3::ZERO; + // ch.mBoneMotion = Ogre::Vector3::ZERO; + }); ecs.system( "HandleNPCAnimations") @@ -243,8 +335,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) "actuator-state"); Ogre::String current_state = main_sm->getCurrentState(); if (current_state != "actuator") - main_sm->setAnimation("actuator"); - actuator_sm->setAnimation(inact.animationState); + main_sm->setAnimation("actuator", true); + actuator_sm->setAnimation(inact.animationState, true); }); ecs.system( "HandlePlayerAnimationsNoActuator") @@ -261,7 +353,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) "main"); Ogre::String current_state = main_sm->getCurrentState(); if (current_state != "locomotion") - main_sm->setAnimation("locomotion"); + main_sm->setAnimation("locomotion", true); }); ecs.system( "HandlePlayerAnimations") @@ -303,7 +395,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) else if (current_state != "idle" && !ch.is_submerged) next_state = "idle"; - state_machine->setAnimation(next_state, true); + state_machine->setAnimation(next_state); } else if (start_motion) { if (ch.is_submerged) { if (input.fast) @@ -343,5 +435,187 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) state_machine->setAnimation(next_state); } }); + ecs.system("HandlePlayerAnimations2") + .kind(flecs::OnUpdate) + .with() + .with() + .each([](flecs::entity e, const Input &input, + const CharacterBase &ch, AnimationControl &anim, + CharacterInActuator &act) { + bool controls_idle = input.motion.zeroLength(); + if (!controls_idle) { + std::cout << "motion.z: " + << Ogre::Math::Abs(input.motion.z - + act.prevMotion.z) + << std::endl; + bool trigger_event = false; + e.each([&](flecs::entity trig) { + if (Ogre::Math::Abs(input.motion.z - + act.prevMotion.z) > + 0.001f) { + if (input.motion.z < 0) + ECS::get() + .mLua + ->call_handler( + "actuator_forward", + trig, + e); + if (input.motion.z > 0) + ECS::get_mut() + .mLua + ->call_handler( + "actuator_backward", + trig, + e); + } + if (input.act_pressed) + ECS::get_mut() + .mLua->call_handler( + "actuator_action", + trig, e); + // ECS::get_mut().call_handler( + // "actuator_update", trig, e); + trigger_event = true; + }); + if (!trigger_event) { + if (Ogre::Math::Abs(input.motion.z - + act.prevMotion.z) > + 0.001f) { + if (input.motion.z < 0) + ECS::get() + .mLua + ->call_handler( + "_in_actuator_forward", + e, e); + if (input.motion.z > 0) + ECS::get_mut() + .mLua + ->call_handler( + "_in_actuator_backward", + e, e); + } + if (input.act_pressed) + ECS::get_mut() + .mLua->call_handler( + "_in_actuator_action", + e, e); + // ECS::get_mut().call_handler( + // "actuator_update", trig, e); + } + act.prevMotion.x = input.motion.x; + act.prevMotion.y = input.motion.y; + act.prevMotion.z = input.motion.z; + } + +#if 0 + if (!controls_idle) { + if (Ogre::Math::Abs(input.motion.z - act.prevMotion.z) > 0.001f) { + if (input.motion.z < 0) + ECS::get_mut().call_handler("actuator_forward", e); + } + ECS::get_mut().call_handler("actuator_controls_update"); + } +#endif + act.prevMotion = input.motion; + }); +#ifdef VDEBUG + ecs.system("CharacterGravityStatus") + .kind(flecs::OnUpdate) + .with() + .with() + .each([](flecs::entity e, const CharacterBase &ch) { + if (e.has()) + std::cout << "gravity\n"; + else + std::cout << "no gravity\n"; + if (e.has()) + std::cout << "in water\n"; + else + std::cout << "out of water\n"; + std::cout + << "h=" << ch.mBodyNode->_getDerivedPosition().y + << std::endl; + }); +#endif + ecs.system( + "UpdateBodyCast") + .kind(flecs::OnUpdate) + .without() + .each([](flecs::entity e, const EngineData &eng, + const CharacterBase &ch, CharacterBody &body) { + struct ResultCallback + : public btCollisionWorld::RayResultCallback { + btCollisionObject *m_me; + btVector3 m_from, m_to, m_hitNormalWorld, + m_hitPointWorld; + ResultCallback(btCollisionObject *me, + const btVector3 &from, + const btVector3 &to) + : m_me(me) + , m_from(from) + , m_to(to) + { + } + btScalar addSingleResult( + btCollisionWorld::LocalRayResult + &rayResult, + bool normalInWorldSpace) override + { + if (rayResult.m_collisionObject == m_me) + return 1.0f; + if (!btPairCachingGhostObject::upcast( + rayResult.m_collisionObject)) + return 1.0f; + if (!(rayResult.m_collisionObject + ->getCollisionFlags() & + btCollisionObject:: + CF_CHARACTER_OBJECT)) + return 1.0f; + m_closestHitFraction = + rayResult.m_hitFraction; + m_collisionObject = + rayResult.m_collisionObject; + if (normalInWorldSpace) + m_hitNormalWorld = + rayResult + .m_hitNormalLocal; + else + m_hitNormalWorld = + m_collisionObject + ->getWorldTransform() + .getBasis() * + rayResult + .m_hitNormalLocal; + m_hitPointWorld.setInterpolate3( + m_from, m_to, + rayResult.m_hitFraction); + return rayResult.m_hitFraction; + } + }; + Ogre::Vector3 offset(0.0f, 0.5f, 0.0f); + float dist = 0.5f; + btVector3 a = Ogre::Bullet::convert( + ch.mBodyNode->getPosition() + offset), + b(Ogre::Bullet::convert( + ch.mBodyNode->getPosition() + + ch.mBodyNode->getOrientation() * + Ogre::Vector3(0, 0, dist) + + offset)); + ResultCallback result(body.mGhostObject, a, b); + // body.mGhostObject->rayTest(a, b, result); + eng.mWorld->getBtWorld()->rayTest(a, b, result); + if (result.hasHit()) { + std::cout << "Hit!!! " << result.m_hitPointWorld + << std::endl; + e.set( + { "idle", { 0, 0, 0 } }); + ECS::get().mLua->call_handler( + "character_enter", e, + ECS::get().entities.at( + const_cast( + result.m_collisionObject))); + } + }); } } diff --git a/src/gamedata/CharacterAnimationModule.h b/src/gamedata/CharacterAnimationModule.h index 868d19f..185fc7a 100644 --- a/src/gamedata/CharacterAnimationModule.h +++ b/src/gamedata/CharacterAnimationModule.h @@ -3,9 +3,74 @@ #include #include #include "GameData.h" +#include "CharacterModule.h" #include "LuaData.h" namespace ECS { +class RootMotionListener : public Ogre::NodeAnimationTrack::Listener { + Ogre::Vector3 prevTranslation; + mutable Ogre::Vector3 deltaMotion; + flecs::entity e; + +public: + RootMotionListener(flecs::entity e) + : Ogre::NodeAnimationTrack::Listener() + , e(e) + , prevTranslation(Ogre::Vector3::ZERO) + , deltaMotion(Ogre::Vector3::ZERO) + { + } + bool getInterpolatedKeyFrame(const Ogre::AnimationTrack *t, + const Ogre::TimeIndex &timeIndex, + Ogre::KeyFrame *kf) override + { + Ogre::TransformKeyFrame *vkf = + static_cast(kf); + Ogre::KeyFrame *kf1, *kf2; + Ogre::TransformKeyFrame *k1, *k2; + unsigned short firstKeyIndex; + float tm = t->getKeyFramesAtTime(timeIndex, &kf1, &kf2, + &firstKeyIndex); + k1 = static_cast(kf1); + k2 = static_cast(kf2); + Ogre::Vector3 translation; + Ogre::Quaternion rotation; + if (tm == 0.0f) { + rotation = k1->getRotation(); + translation = k1->getTranslate(); + deltaMotion = translation; + + // vkf->setRotation(k1->getRotation()); + // vkf->setTranslate(k1->getTranslate()); + // vkf->setScale(k1->getScale()); + } else { + rotation = Ogre::Quaternion::nlerp( + tm, k1->getRotation(), k2->getRotation(), true); + translation = + k1->getTranslate() + + (k2->getTranslate() - k1->getTranslate()) * tm; + deltaMotion = translation - prevTranslation; + if (deltaMotion.squaredLength() > + translation.squaredLength()) + deltaMotion = translation; + } +#if 0 + std::cout << "time: " << tm + << " Position: " << deltaMotion; + std::cout << " Quaternion: " << rotation; + std::cout << std::endl; +#endif + vkf->setTranslate(deltaMotion); + // vkf->setTranslate(translation); + vkf->setRotation(rotation); + vkf->setScale(Ogre::Vector3(1, 1, 1)); + prevTranslation = translation; + e.get_mut().mBoneMotion = deltaMotion; + e.get_mut().mBonePrevMotion = prevTranslation; + e.modified(); + return true; + } +}; struct AnimationTrigger; struct AnimationTriggerSubscriber { virtual void operator()(const AnimationTrigger *trigger) = 0; @@ -65,17 +130,20 @@ struct Animation { Ogre::Animation *mSkelAnimation; Ogre::NodeAnimationTrack *mHipsTrack; Ogre::NodeAnimationTrack *mRootTrack; + RootMotionListener *mListener; float m_weight; float m_accWeight; - std::multimap trigger_list; - Animation(Ogre::AnimationState *animState, - Ogre::Animation *skelAnimation) + Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState, + Ogre::Animation *skelAnimation, flecs::entity e) : mAnimationState(animState) , mSkelAnimation(skelAnimation) + , mListener(OGRE_NEW RootMotionListener(e)) , m_weight(0) , m_accWeight(0) { int j; + mRootTrack = nullptr; + mHipsTrack = nullptr; for (const auto &it : mSkelAnimation->_getNodeTrackList()) { Ogre::NodeAnimationTrack *track = it.second; Ogre::String trackName = @@ -87,10 +155,21 @@ struct Animation { // mRootTracks[i]->removeAllKeyFrames(); } } + if (!mRootTrack) { + Ogre::Bone *bone = skeleton->getBone("Root"); + mRootTrack = mSkelAnimation->createNodeTrack( + bone->getHandle(), bone); + Ogre::TransformKeyFrame *kf = + mRootTrack->createNodeKeyFrame(0.0f); + kf->setTranslate(Ogre::Vector3::ZERO); + kf->setRotation(Ogre::Quaternion::IDENTITY); + } + mRootTrack->setListener(mListener); #if 0 OgreAssert(mHipsTrack, "no hips track"); OgreAssert(mRootTrack, "no Root track"); #endif +#if 0 if (mRootTrack) { Ogre::Vector3 delta = Ogre::Vector3::ZERO; Ogre::Vector3 motion = Ogre::Vector3::ZERO; @@ -107,6 +186,55 @@ struct Animation { motion = trans; } } +#endif +#if 0 + if (!mMetaRootTrack) { + Ogre::Bone *bone = nullptr; + OgreAssert(skeleton->hasBone("MetaRoot"), + "no bone MetaRoot"); + if (skeleton->hasBone("MetaRoot")) + bone = skeleton->getBone("MetaRoot"); +#if 0 + else + bone = skeleton->createBone("MetaRoot"); +#endif + bone->setPosition(Ogre::Vector3::ZERO); + bone->setOrientation(Ogre::Quaternion::IDENTITY); + std::vector< + std::pair > > + keyframes; + for (j = 0; j < mRootTrack->getNumKeyFrames(); j++) { + Ogre::TransformKeyFrame *kf = + mRootTrack->getNodeKeyFrame(j); + const Ogre::Vector3 &pt = kf->getTranslate(); + const Ogre::Quaternion &rt = kf->getRotation(); + float tp = kf->getTime(); + keyframes.push_back({ tp, { pt, rt } }); +#if 0 + new_kf->setTranslate(pt); + new_kf->setTranslate(kf->getTranslate()); + new_kf->setRotation(kf->getRotation()); +#endif + kf->setTranslate(Ogre::Vector3::ZERO); + kf->setRotation(Ogre::Quaternion::IDENTITY); + } + mMetaRootTrack = mSkelAnimation->createNodeTrack( + bone->getHandle()); + OgreAssert(mMetaRootTrack, + "failed to create node track"); + for (j = 0; j < keyframes.size(); j++) { + Ogre::TransformKeyFrame *new_kf = + mMetaRootTrack->createNodeKeyFrame( + keyframes[j].first); + new_kf->setTranslate(keyframes[j].second.first); + new_kf->setRotation(keyframes[j].second.second); + } + } +#if 0 + mRootTrack = track; +#endif +#endif } Ogre::String getName() { @@ -139,11 +267,22 @@ struct Animation { { return m_weight; } - void addTime(float time) + bool addTime(float time) { - preUpdateTriggers(); + bool result = mAnimationState->getEnabled(); + if (!result) + return result; + Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex( + mAnimationState->getTimePosition()); + Ogre::KeyFrame *kf1, *kf2; + unsigned short prev_index, next_index; + mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &prev_index); + unsigned int previous_frame = index.getKeyIndex(); mAnimationState->addTime(time); - postUpdateTriggers(time); + index = mSkelAnimation->_getTimeIndex( + mAnimationState->getTimePosition()); + mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &next_index); + return prev_index != next_index; } void reset() { @@ -161,55 +300,9 @@ struct Animation { { return m_accWeight; } - void addTrigger(AnimationTrigger *trigger) - { - trigger_list.insert(std::pair( - trigger->getTriggerTime(), trigger)); - } - void clearTriggers() - { - auto it = trigger_list.begin(); - while (it != trigger_list.end()) { - delete it->second; - it++; - } - trigger_list.clear(); - } - float mpreUpdateTime; - void preUpdateTriggers() - { - mpreUpdateTime = mAnimationState->getTimePosition() / - mAnimationState->getLength(); - } - void postUpdateTriggers(float delta) - { - float postUpdateTime = mAnimationState->getTimePosition() / - mAnimationState->getLength(); - bool positive = delta >= 0.0f; - if (positive) - updateTriggers(mpreUpdateTime, postUpdateTime); - else - updateTriggers(postUpdateTime, mpreUpdateTime); - } - void updateTriggers(float currentTime, float nextTime) - { - int i; - float weight = getWeight(); - if (currentTime <= nextTime) { - auto it = trigger_list.lower_bound(currentTime); - while (it != trigger_list.end()) { - if (nextTime <= - it->second->getTriggerTime()) // in future, sorrted by time - return; - it->second->notify(weight); - } - } else { - updateTriggers(currentTime, 1); - updateTriggers(0, nextTime); - } - } float getLength() const { + return mAnimationState->getLength(); if (getEnabled()) return mAnimationState->getLength(); else @@ -217,6 +310,7 @@ struct Animation { } float getTimePosition() const { + return mAnimationState->getTimePosition(); if (getEnabled()) return mAnimationState->getTimePosition(); else @@ -228,11 +322,12 @@ struct AnimationNode { std::vector children; float m_weight; Ogre::String m_name; + std::multimap trigger_list; AnimationNode() : m_weight(0) { } - virtual void addTime(float time) = 0; + virtual bool addTime(float time) = 0; virtual void setWeight(float weight) = 0; virtual void reset() = 0; virtual float getLength() const = 0; @@ -256,6 +351,52 @@ struct AnimationNode { return getTimePosition() / l; return 0.0f; } + void addTrigger(AnimationTrigger *trigger) + { + trigger_list.insert(std::pair( + trigger->getTriggerTime(), trigger)); + } + void clearTriggers() + { + auto it = trigger_list.begin(); + while (it != trigger_list.end()) { + delete it->second; + it++; + } + trigger_list.clear(); + } + float mpreUpdateTime; + void preUpdateTriggers() + { + mpreUpdateTime = getTime(); + } + void postUpdateTriggers(float delta) + { + float postUpdateTime = getTime(); + bool positive = delta >= 0.0f; + if (positive) + updateTriggers(mpreUpdateTime, postUpdateTime); + else + updateTriggers(postUpdateTime, mpreUpdateTime); + } + void updateTriggers(float currentTime, float nextTime) + { + int i; + float weight = getWeight(); + if (currentTime <= nextTime) { + auto it = trigger_list.lower_bound(currentTime); + while (it != trigger_list.end()) { + if (nextTime <= + it->second->getTriggerTime()) // in future, sorrted by time + return; + it->second->notify(weight); + it++; + } + } else { + updateTriggers(currentTime, 1); + updateTriggers(0, nextTime); + } + } }; struct AnimationNodeAnimation : AnimationNode { @@ -266,9 +407,13 @@ struct AnimationNodeAnimation : AnimationNode { , mAnimation(animation) { } - void addTime(float time) + bool addTime(float time) { - mAnimation->addTime(time); + bool ret; + preUpdateTriggers(); + ret = mAnimation->addTime(time); + postUpdateTriggers(time); + return ret; } void setWeight(float weight) { @@ -281,6 +426,7 @@ struct AnimationNodeAnimation : AnimationNode { } float getLength() const { + return mAnimation->getLength(); if (enabled) return mAnimation->getLength(); else @@ -288,6 +434,7 @@ struct AnimationNodeAnimation : AnimationNode { } float getTimePosition() const { + return mAnimation->getTimePosition(); if (enabled) return mAnimation->getTimePosition(); else @@ -299,9 +446,13 @@ struct AnimationNodeStateMachineState : AnimationNode { : AnimationNode() { } - void addTime(float time) + bool addTime(float time) { - children[0]->addTime(time); + bool ret; + preUpdateTriggers(); + ret = children[0]->addTime(time); + postUpdateTriggers(time); + return ret; } void setWeight(float weight) { @@ -319,7 +470,7 @@ struct AnimationNodeStateMachineState : AnimationNode { } float getTimePosition() const { - return children[0]->getLength(); + return children[0]->getTimePosition(); } }; struct AnimationNodeSpeed : AnimationNode { @@ -331,9 +482,13 @@ struct AnimationNodeSpeed : AnimationNode { , enabled(false) { } - void addTime(float time) + bool addTime(float time) { - children[0]->addTime(time * m_speed); + bool ret; + preUpdateTriggers(); + ret = children[0]->addTime(time * m_speed); + postUpdateTriggers(time); + return ret; } void setWeight(float weight) { @@ -383,36 +538,43 @@ struct AnimationNodeStateMachine : AnimationNode { { m_weight = 1.0f; } - void addTime(float time) + bool addTime(float time) { int i; + preUpdateTriggers(); if (!configured) { configure(); configured = true; } +#ifdef VDEBUG if (debug) { std::cout << "state machine addTime" << std::endl; std::cout << "state machine children: " << children.size() << std::endl; } +#endif for (i = 0; i < children.size(); i++) { +#ifdef VDEBUG if (debug) std::cout << "child weight: " << i << " " << children[i]->getWeight() << std::endl; +#endif AnimationNode *child = children[i]; if (fade_in.find(child) != fade_in.end()) { Ogre::Real newWeight = child->getWeight() + time * fade_speed; child->setWeight(Ogre::Math::Clamp( newWeight * m_weight, 0, m_weight)); +#ifdef VDEBUG if (debug) { std::cout << "fade in: " << newWeight << std::endl; std::cout << "m_weight: " << m_weight << std::endl; } +#endif if (newWeight >= 1) fade_in.erase(child); } @@ -426,8 +588,11 @@ struct AnimationNodeStateMachine : AnimationNode { } } OgreAssert(currentAnim, "bad current anim"); + bool ret = false; if (currentAnim) - currentAnim->addTime(time); + ret = currentAnim->addTime(time); + postUpdateTriggers(time); + return ret; } void setWeight(float weight) { @@ -522,9 +687,13 @@ struct AnimationNodeOutput : AnimationNode { , m_speed(1.0f) { } - void addTime(float time) + bool addTime(float time) { - children[0]->addTime(time * m_speed); + bool ret; + preUpdateTriggers(); + ret = children[0]->addTime(time * m_speed); + postUpdateTriggers(time); + return ret; } void setWeight(float weight) { @@ -614,8 +783,9 @@ struct AnimationSystem : AnimationNode { Ogre::String event; void operator()(const AnimationTrigger *trigger) { - ECS::get_mut().call_handler( - event, ent, ent); + ECS::get_mut() + .mLua->call_handler(event, ent, + ent); } EventSubscriber(flecs::entity e, const Ogre::String &event) @@ -625,15 +795,57 @@ struct AnimationSystem : AnimationNode { } }; OgreAssert(parent, "bad parent"); - Animation *animation = - static_cast(parent) - ->mAnimation; - OgreAssert(animation, "bad animation"); AnimationTrigger *trigger = new AnimationTrigger(name, time, 0.1f); EventSubscriber *sub = new EventSubscriber(e, event); trigger->addSubscriber(sub); - animation->addTrigger(trigger); + parent->addTrigger(trigger); + return this; + } // leaf too... + AnimationSystemBuilder * + transition_end(const Ogre::String &state_from, + const Ogre::String &state_to) + { + struct EndTransitionSubscriber + : AnimationTriggerSubscriber { + AnimationNodeStateMachine *sm; + Ogre::String next_state; + bool reset; + void operator()(const AnimationTrigger *trigger) + { + sm->setAnimation(next_state, reset); + } + EndTransitionSubscriber( + AnimationNodeStateMachine *sm, + const Ogre::String &next_state, + bool reset = true) + : sm(sm) + , next_state(next_state) + , reset(reset) + { + } + }; + OgreAssert(parent, "no parent"); + AnimationNodeStateMachine *sm = + static_cast( + parent); + OgreAssert(sm, "no state machine"); + AnimationTrigger *trigger = new AnimationTrigger( + "transition:" + state_from + "_" + state_to, + 0.99f, 0.1f); + EndTransitionSubscriber *sub = + new EndTransitionSubscriber(sm, state_to); + int i; + bool ok = false; + for (i = 0; i < sm->children.size(); i++) { + if (sm->children[i]->getName() == state_from) { + trigger->addSubscriber(sub); + sm->children[i]->addTrigger(trigger); + ok = true; + break; + } + } + OgreAssert(ok, "Failed to set transition"); return this; } AnimationSystemBuilder *speed(float speed, @@ -685,10 +897,11 @@ struct AnimationSystem : AnimationNode { } }; AnimationSystemBuilder m_builder; - void addTime(float time) + bool addTime(float time) { int i; - m_builder.animation_nodes[0]->addTime(time); + preUpdateTriggers(); + bool ret = m_builder.animation_nodes[0]->addTime(time); for (i = 0; i < m_builder.animationNodeList.size(); i++) { AnimationNodeAnimation *anim = m_builder.animationNodeList[i]; @@ -706,13 +919,17 @@ struct AnimationSystem : AnimationNode { float weight = vanimation_list[i]->getAccWeight(); vanimation_list[i]->setWeight(weight); vanimation_list[i]->resetAccWeight(); +#define VDEBUG #ifdef VDEBUG - if (debug) + if (debug && vanimation_list[i]->getEnabled()) std::cout << i << " animation: " << vanimation_list[i]->getName() << " " << weight << std::endl; #endif +#undef VDEBUG } + postUpdateTriggers(time); + return ret; } void setWeight(float weight) { diff --git a/src/gamedata/CharacterModule.cpp b/src/gamedata/CharacterModule.cpp index 44c5710..33f0bac 100644 --- a/src/gamedata/CharacterModule.cpp +++ b/src/gamedata/CharacterModule.cpp @@ -51,6 +51,22 @@ CharacterModule::CharacterModule(flecs::world &ecs) zaxis = 0.0f; else zaxis = Ogre::Math::Sign(zaxis); + if (active & 32) + input.act = true; + else + input.act = false; + if (pressed & 32) + input.act_pressed = true; + else + input.act_pressed = false; + if (active & 64) + input.act2 = true; + else + input.act2 = false; + if (pressed & 64) + input.act2_pressed = true; + else + input.act2_pressed = false; input.motion.z = zaxis; float xaxis = input.motion.x; xaxis *= 0.9f; @@ -246,11 +262,15 @@ CharacterModule::CharacterModule(flecs::world &ecs) ch.mBodyNode->setPosition(loc.position); ch.mBodyNode->attachObject(ch.mBodyEnt); ch.mSkeleton = ch.mBodyEnt->getSkeleton(); + OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"), + "No root bone"); OgreAssert(ch.mSkeleton->hasBone("Root"), "No root bone"); ch.mRootBone = ch.mSkeleton->getBone("Root"); OgreAssert(ch.mRootBone, "No root bone"); body.mController = nullptr; + ch.mBoneMotion = Ogre::Vector3::ZERO; + ch.mBonePrevMotion = Ogre::Vector3::ZERO; e.set( { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); body.checkGround = false; @@ -283,8 +303,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) ->calculatePrincipalAxisTransform( masses, principal, inertia); } - body.mGhostObject->setCollisionFlags( - btCollisionObject::CF_KINEMATIC_OBJECT /*| + body.mGhostObject->setCollisionFlags(body.mGhostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT + /*btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_NO_CONTACT_RESPONSE */); body.mGhostObject->setActivationState( DISABLE_DEACTIVATION); @@ -295,6 +315,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) e.add(); e.add(); anim.configured = false; + OgreAssert(body.mGhostObject->hasContactResponse(), + "need contact response"); }); ecs.system( "UpdateCharacterPhysics") @@ -434,8 +456,11 @@ CharacterModule::CharacterModule(flecs::world &ecs) .with() .each([](flecs::entity e, const WaterBody &waterb, const CharacterBase &ch, CharacterBody &body) { - if (waterb.isInWater(body.mGhostObject) && - ch.mBodyNode->_getDerivedPosition().y > 0.05f) + float h = ch.mBodyNode->_getDerivedPosition().y; + if (waterb.isInWater(body.mGhostObject) && h > 0.05f) + e.remove(); + else if (!waterb.isInWater(body.mGhostObject) && + h > 0.05f) e.remove(); }); #if 0 diff --git a/src/gamedata/CharacterModule.h b/src/gamedata/CharacterModule.h index 43fb18a..8dc0639 100644 --- a/src/gamedata/CharacterModule.h +++ b/src/gamedata/CharacterModule.h @@ -19,6 +19,7 @@ struct CharacterBase { Ogre::Skeleton *mSkeleton; Ogre::Node *mRootBone; Ogre::Vector3 mBoneMotion; + Ogre::Vector3 mBonePrevMotion; Ogre::Vector3 mGoalDirection; // actual intended direction in world-space bool is_submerged; }; @@ -42,6 +43,7 @@ struct CharacterVelocity { }; struct CharacterInActuator { Ogre::String animationState; + Vector3 prevMotion; }; struct CharacterModule { CharacterModule(flecs::world &ecs); diff --git a/src/gamedata/Components.h b/src/gamedata/Components.h index 02b1e4f..3d0a62d 100644 --- a/src/gamedata/Components.h +++ b/src/gamedata/Components.h @@ -45,6 +45,10 @@ struct Input { bool mouse_moved; bool wheel_moved; bool fast; + bool act; + bool act_pressed; + bool act2; + bool act2_pressed; Input() : control(0) , control_prev(0) diff --git a/src/gamedata/EventTriggerModule.cpp b/src/gamedata/EventTriggerModule.cpp index 00b6f8b..53bee6b 100644 --- a/src/gamedata/EventTriggerModule.cpp +++ b/src/gamedata/EventTriggerModule.cpp @@ -258,6 +258,14 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs) .entities .end(), "No body to entity mapping"); + flecs::entity obj_e = + ECS::get< + Body2Entity>() + .entities + .at(const_cast< + btCollisionObject + *>( + other)); ECS::get< LuaBase>() .mLua @@ -265,13 +273,12 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs) evt.event + "_enter", e, - ECS::get< - Body2Entity>() - .entities - .at(const_cast< - btCollisionObject - *>( - other))); + obj_e); + obj_e.add< + InTrigger>( + e); + e.add( + obj_e); } } } @@ -283,14 +290,17 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs) while (it != body.contactBodies.end()) { if (currentContactBodies.find(*it) == currentContactBodies.end()) { - ECS::get().mLua->call_handler( - evt.event + "_exit", e, + flecs::entity obj_e = ECS::get() .entities .at(const_cast< btCollisionObject *>( - *it))); + *it)); + ECS::get().mLua->call_handler( + evt.event + "_exit", e, obj_e); + obj_e.remove(e); + e.remove(obj_e); std::cout << "body exited" << std::endl; it = body.contactBodies.erase(it); if (it == body.contactBodies.end()) diff --git a/src/gamedata/EventTriggerModule.h b/src/gamedata/EventTriggerModule.h index cc9d5dc..c0f3c7b 100644 --- a/src/gamedata/EventTriggerModule.h +++ b/src/gamedata/EventTriggerModule.h @@ -16,6 +16,8 @@ struct EventTrigger { struct EventTriggerData { std::set entities; }; +struct InTrigger {}; +struct TriggeredBy {}; struct EventTriggerModule { EventTriggerModule(flecs::world &ecs); }; diff --git a/src/gamedata/LuaData.cpp b/src/gamedata/LuaData.cpp index 0df0eb2..72d14d4 100644 --- a/src/gamedata/LuaData.cpp +++ b/src/gamedata/LuaData.cpp @@ -91,7 +91,7 @@ int LuaData::call_handler(const Ogre::String &event, flecs::entity e, return 0; } -int luaLibraryLoader(lua_State *L) +static int luaLibraryLoader(lua_State *L) { int i; if (!lua_isstring(L, 1)) { @@ -260,11 +260,11 @@ LuaData::LuaData() lua_setglobal(L, "main_menu"); lua_pushcfunction(L, [](lua_State *L) -> int { OgreAssert(lua_gettop(L) == 3, "Invalid parameters"); - luaL_checktype(L, 1, LUA_TSTRING); + luaL_checktype(L, 1, LUA_TNUMBER); luaL_checktype(L, 2, LUA_TSTRING); luaL_checktype(L, 3, LUA_TBOOLEAN); bool enable = lua_toboolean(L, 3); - flecs::entity e = ECS::get().lookup(lua_tostring(L, 1)); + flecs::entity e = idmap.get_entity(lua_tointeger(L, 1)); Ogre::String what = lua_tostring(L, 2); OgreAssert(e.is_valid(), "Invalid character"); OgreAssert(e.has(), "Not a character"); @@ -401,6 +401,24 @@ LuaData::LuaData() return 0; }); lua_setglobal(L, "ecs_trigger_set_position"); + lua_pushcfunction(L, [](lua_State *L) -> int { + luaL_checktype(L, 1, LUA_TNUMBER); // trigger + int trigger = lua_tointeger(L, 1); + flecs::entity trigger_e = idmap.get_entity(trigger); + Ogre::SceneNode *node = trigger_e.get().node; + Ogre::Any animationAny = + node->getUserObjectBindings().getUserAny( + "trigger_animation"); + if (animationAny.has_value()) { + Ogre::String animation = + Ogre::any_cast(animationAny); + lua_pushstring(L, animation.c_str()); + return 1; + } + lua_pushnil(L); + return 1; + }); + lua_setglobal(L, "ecs_trigger_get_animation"); lua_pushcfunction(L, [](lua_State *L) -> int { OgreAssert(lua_gettop(L) == 5, "Invalid parameters"); luaL_checktype(L, 1, LUA_TSTRING); // type @@ -472,6 +490,14 @@ LuaData::LuaData() return 0; }); lua_setglobal(L, "ecs_character_physics_control"); + lua_pushcfunction(L, [](lua_State *L) -> int { + luaL_checktype(L, 1, LUA_TNUMBER); // object + int object = lua_tointeger(L, 1); + flecs::entity object_e = idmap.get_entity(object); + lua_pushboolean(L, object_e.has()); + return 1; + }); + lua_setglobal(L, "ecs_character_is_player"); lua_pushcfunction(L, [](lua_State *L) -> int { luaL_checktype(L, 1, LUA_TNUMBER); // object luaL_checktype(L, 2, LUA_TSTRING); // animation @@ -479,8 +505,10 @@ LuaData::LuaData() int object = lua_tointeger(L, 1); flecs::entity object_e = idmap.get_entity(object); - if (animation.length()) - object_e.set({ animation }); + object_e.set({ { 0, 0, 0 }, { 0, 0, 0 } }); + if (animation.length() > 0) + object_e.set( + { animation, { 0, 0, 0 } }); else object_e.remove(); return 0; @@ -500,6 +528,13 @@ LuaData::LuaData() return 0; }); lua_setglobal(L, "ecs_set_slot"); + lua_pushcfunction(L, [](lua_State *L) -> int { + flecs::entity e = ECS::get().lookup("player"); + int result = idmap.add_entity(e); + lua_pushinteger(L, result); + return result; + }); + lua_setglobal(L, "ecs_get_player_entity"); lua_pushcfunction(L, [](lua_State *L) -> int { luaL_checktype(L, 1, LUA_TNUMBER); // entity id int id = lua_tointeger(L, 1);