#ifndef STAR_LUA_ANIMATION_COMPONENT_HPP #define STAR_LUA_ANIMATION_COMPONENT_HPP #include "StarLuaComponents.hpp" #include "StarJsonExtra.hpp" #include "StarLightSource.hpp" #include "StarDrawable.hpp" #include "StarEntityRenderingTypes.hpp" #include "StarMixer.hpp" #include "StarParticleDatabase.hpp" #include "StarParticle.hpp" #include "StarRoot.hpp" #include "StarAssets.hpp" #include "StarLuaConverters.hpp" namespace Star { STAR_EXCEPTION(LuaAnimationComponentException, LuaComponentException); // Lua component that allows lua to directly produce drawables, light sources, // audios, and particles. Adds a "localAnimation" callback table. template class LuaAnimationComponent : public Base { public: LuaAnimationComponent(); List>> const& drawables(); List const& lightSources(); List pullNewParticles(); List pullNewAudios(); protected: // Clears looping audio on context shutdown void contextShutdown() override; private: List m_pendingParticles; List m_pendingAudios; List m_activeAudio; List>> m_drawables; List m_lightSources; }; template LuaAnimationComponent::LuaAnimationComponent() { LuaCallbacks animationCallbacks; animationCallbacks.registerCallback("playAudio", [this](String const& sound, Maybe loops, Maybe volume) { auto audio = make_shared(*Root::singleton().assets()->audio(sound)); audio->setLoops(loops.value(0)); audio->setVolume(volume.value(1.0f)); m_pendingAudios.append(audio); m_activeAudio.append(audio); }); animationCallbacks.registerCallback("spawnParticle", [this](Json const& particleConfig, Maybe const& position) { auto particle = Root::singleton().particleDatabase()->particle(particleConfig); particle.translate(position.value()); m_pendingParticles.append(particle); }); animationCallbacks.registerCallback("clearDrawables", [this]() { m_drawables.clear(); }); animationCallbacks.registerCallback("addDrawable", [this](LuaTable const& drawableTable, Maybe renderLayerName) { Maybe renderLayer; if (renderLayerName) renderLayer = parseRenderLayer(*renderLayerName); Color color = drawableTable.get>("color").value(Color::White); Drawable drawable; if (auto line = drawableTable.get>("line")) drawable = Drawable::makeLine(line.take(), drawableTable.get("width"), color); else if (auto poly = drawableTable.get>("poly")) drawable = Drawable::makePoly(poly.take(), color); else if (auto image = drawableTable.get>("image")) drawable = Drawable::makeImage(image.take(), 1.0f / TilePixels, drawableTable.get>("centered").value(true), Vec2F(), color); else throw LuaAnimationComponentException("Drawable table must have 'line', 'poly', or 'image'"); if (auto transformation = drawableTable.get>("transformation")) drawable.transform(*transformation); if (auto rotation = drawableTable.get>("rotation")) drawable.rotate(*rotation); if (drawableTable.get("mirrored")) drawable.scale(Vec2F(-1, 1)); if (auto scale = drawableTable.get>("scale")) drawable.scale(*scale); if (auto position = drawableTable.get>("position")) drawable.translate(*position); drawable.fullbright = drawableTable.get("fullbright"); m_drawables.append({move(drawable), renderLayer}); }); animationCallbacks.registerCallback("clearLightSources", [this]() { m_lightSources.clear(); }); animationCallbacks.registerCallback("addLightSource", [this](LuaTable const& lightSourceTable) { m_lightSources.append({ lightSourceTable.get("position"), lightSourceTable.get("color").toRgb(), lightSourceTable.get>("pointLight").value(), lightSourceTable.get>("pointBeam").value(), lightSourceTable.get>("beamAngle").value(), lightSourceTable.get>("beamAmbience").value() }); }); Base::addCallbacks("localAnimator", move(animationCallbacks)); } template List>> const& LuaAnimationComponent::drawables() { return m_drawables; } template List const& LuaAnimationComponent::lightSources() { return m_lightSources; } template List LuaAnimationComponent::pullNewParticles() { return take(m_pendingParticles); } template List LuaAnimationComponent::pullNewAudios() { eraseWhere(m_activeAudio, [](AudioInstancePtr const& audio) { return audio->finished(); }); return take(m_pendingAudios); } template void LuaAnimationComponent::contextShutdown() { for (auto const& audio : m_activeAudio) audio->setLoops(0); m_activeAudio.clear(); Base::contextShutdown(); } } #endif