#include "behavior.h" #include #include #include #include #include UF_BEHAVIOR_REGISTER_CPP(ext::BgmEmitterBehavior) UF_BEHAVIOR_TRAITS_CPP(ext::BgmEmitterBehavior, ticks = true, renders = false, multithread = true) #define this ((uf::Object*) &self) void ext::BgmEmitterBehavior::initialize( uf::Object& self ) { auto& metadata = this->getComponent(); auto& metadataJson = this->getComponent(); UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){ if ( !uf::asset::isExpected( payload, uf::asset::Type::AUDIO ) ) return; auto& track = metadata.tracks[payload.uri]; track.userdata = uf::asset::release( payload.filename ); auto& audio = uf::pointeredUserdata::get( track.userdata ); #if UF_AUDIO_MAPPED_VOLUMES auto volume = uf::audio::volumes.count("bgm") > 0 ? uf::audio::volumes.at("bgm") : 1.0; #else auto volume = uf::audio::volumes::bgm; #endif audio.setVolume(track.fade.x > 0 ? 0 : volume); audio.loop( payload.uri != track.intro ); audio.play(); metadata.current = payload.uri; track.active = true; }); this->addHook( "bgm:Play.%UID%", [&](ext::json::Value& payload){ auto filename = payload["filename"].as(); auto delay = payload["delay"].as(0.0f); if ( filename == "" && !metadata.tracks.empty() ) { filename = uf::stl::random_it( metadata.tracks.begin(), metadata.tracks.end() )->first; } if ( metadata.tracks.count( filename ) == 0 ) { if ( filename == "" ) return; } auto& track = metadata.tracks[filename]; if ( track.intro != "" ) filename = track.intro; this->callHook( "asset:QueueLoad.%UID%", this->resolveToPayload( filename ) ); }); if ( !metadata.tracks.empty() || metadata.current != "" ) { ext::json::Value payload; payload["filename"] = metadata.current; this->queueHook( "bgm:Play.%UID%", payload ); } } void ext::BgmEmitterBehavior::tick( uf::Object& self ) { auto& metadata = this->getComponent(); auto& metadataJson = this->getComponent(); #if UF_ENTITY_METADATA_USE_JSON metadata.deserialize(self, metadataJson); #endif #if UF_AUDIO_MAPPED_VOLUMES auto volume = uf::audio::volumes.count("bgm") > 0 ? uf::audio::volumes.at("bgm") : 1.0; #else auto volume = uf::audio::volumes::bgm; #endif if ( metadata.tracks.count( metadata.current ) > 0 ) { auto& track = metadata.tracks[metadata.current]; if ( track.active ) { auto& audio = uf::pointeredUserdata::get( track.userdata ); float current = audio.getTime(); float end = audio.getDuration(); bool isIntro = metadata.current == track.intro; float a = volume; if ( current < track.fade.x ) { a *= current / track.fade.x; } else if ( !audio.loops() && end - current < track.fade.y ) { a *= 1.0f - (end - current) / track.fade.y; } audio.setVolume(a); if ( audio.loops() ) { if ( !audio.playing() ) audio.play(); } else if ( isIntro && end > 0 && (current + track.epsilon >= end || !audio.playing()) ) { auto payload = this->resolveToPayload( track.filename ); this->callHook( "asset:QueueLoad.%UID%", payload ); } } } for ( auto it : metadata.tracks ) { auto track = metadata.tracks[it.first]; if ( !track.active ) continue; auto& audio = uf::pointeredUserdata::get( track.userdata ); if ( audio.playing() ) audio.update(); if ( it.first == metadata.current ) continue; float current = audio.getTime(); float end = audio.getDuration(); if ( current >= end && audio.playing() ) { audio.stop(); uf::pointeredUserdata::destroy( track.userdata ); track.active = false; continue; } float a = volume; if ( current < track.fade.x ) { a *= current / track.fade.x; } else if ( end - current < track.fade.y ) { a *= 1.0f - (end - current) / track.fade.y; } audio.setVolume(a); } } void ext::BgmEmitterBehavior::render( uf::Object& self ){} void ext::BgmEmitterBehavior::destroy( uf::Object& self ){ auto& metadata = this->getComponent(); for ( auto it : metadata.tracks ) { auto track = metadata.tracks[it.first]; if ( !track.active ) continue; auto& audio = uf::pointeredUserdata::get( track.userdata ); audio.stop(); uf::pointeredUserdata::destroy( track.userdata ); track.active = false; } } void ext::BgmEmitterBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ){ for ( auto it : /*this->*/tracks ) { serializer["bgm"]["tracks"][it.first]["filename"] = it.first; serializer["bgm"]["tracks"][it.first]["intro"] = it.second.intro; serializer["bgm"]["tracks"][it.first]["filename"] = it.second.filename; serializer["bgm"]["tracks"][it.first]["epsilon"] = it.second.epsilon; serializer["bgm"]["tracks"][it.first]["fade"] = ext::json::encode( it.second.fade ); } } void ext::BgmEmitterBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){ // iterate by value instead of reference because some keys might not exist and jsoncpp is agony ext::json::forEach( serializer["bgm"]["tracks"], [&]( const uf::stl::string& key, ext::json::Value value ){ auto& track = /*this->*/tracks[key]; track.filename = key; track.intro = value["intro"].as(track.intro); track.epsilon = value["epsilon"].as(track.epsilon); if ( value["fade"].is() ) { track.fade = { track.epsilon, track.epsilon }; } else if ( value["fade"].is() ) { track.fade = { value["fade"].as(), value["fade"].as() }; } else { // YUC track.fade = ext::json::decode( value["fade"], track.fade ); } if ( track.intro != "" ) tracks[track.intro] = track; }); if ( serializer["bgm"]["load"].is() ) { /*this->*/current = serializer["bgm"]["load"].as(/*this->*/current); } } #undef this