3#include <Tempest/SoundEffect>
18const float WorldSound::maxDist = 7000;
51void WorldSound::Effect::setOcclusion(
float v) {
53 eff.setVolume(occ*vol);
56void WorldSound::Effect::setVolume(
float v) {
58 eff.setVolume(occ*vol);
62 :game(game), owner(owner) {
63 plPos = {-1000000,-1000000,-1000000};
71 def.reset(
new Zone());
72 def->bbox[0] = {vob.bbox.min.x, vob.bbox.min.y, vob.bbox.min.z};
73 def->bbox[1] = {vob.bbox.max.x, vob.bbox.max.y, vob.bbox.max.z};
74 def->name = vob.vob_name;
79 z.
bbox[0] = {vob.bbox.min.x, vob.bbox.min.y, vob.bbox.min.z};
80 z.
bbox[1] = {vob.bbox.max.x, vob.bbox.max.y, vob.bbox.max.z};
81 z.
name = vob.vob_name;
83 zones.emplace_back(std::move(z));
89 s.
loop = vob.mode==zenkit::SoundMode::LOOP;
90 s.
active = vob.initially_playing;
91 s.
delay = uint64_t(vob.random_delay * 1000);
92 s.
delayVar = uint64_t(vob.random_delay_var * 1000);
95 s.
pos = {vob.position.x,vob.position.y,vob.position.z};
98 if(vob.type==zenkit::VirtualObjectType::zCVobSoundDaytime) {
99 auto& prDay = (
const zenkit::VSoundDaytime&) vob;
100 float b = prDay.start_time;
101 float e = prDay.end_time;
111 worldEff.emplace_back(std::move(s));
121 auto ret = implAddSound(game.
loadSound(snd), pos,range);
125 std::lock_guard<std::mutex> guard(sync);
127 timeLen = snd.timeLength();
128 effect.emplace_back(ret.val);
132Sound WorldSound::implAddSound(
const SoundFx& eff,
const Tempest::Vec3& pos,
float rangeMax) {
134 auto ret = implAddSound(game.
loadSound(eff,loop), pos,rangeMax);
135 ret.setLooping(loop);
139Sound WorldSound::implAddSound(Tempest::SoundEffect&& eff,
const Tempest::Vec3& pos,
float rangeMax) {
142 auto ex = std::make_shared<Effect>();
143 eff.setPosition(pos);
144 eff.setMaxDistance(rangeMax);
146 ex->eff = std::move(eff);
148 ex->vol = ex->eff.volume();
149 ex->maxDist = rangeMax;
156 std::lock_guard<std::mutex> guard(sync);
163 for(
auto& i:worldEff) {
164 if(!i.active || !i.current.isFinished())
166 if(i.current.isFinished())
169 if(i.restartTimeout>owner.
tickCount() && !i.loop)
175 auto time = owner.
time();
176 time =
gtime(0,time.hour(),time.minute());
179 if(i.sndStart<= time && time<i.sndEnd) {
188 i.current = implAddSound(*snd,i.pos,i.sndRadius);
189 if(!i.current.isEmpty()) {
190 effect.emplace_back(i.current.val);
194 i.restartTimeout = owner.
tickCount() + i.delay;
196 i.restartTimeout += uint64_t(std::rand())%i.delayVar;
204 for(
auto& i:freeSlot)
206 tickSoundZone(player);
211 for(
auto& i:worldEff)
219void WorldSound::tickSoundZone(
Npc& player) {
222 nextSoundUpdate = owner.
tickCount()+5*1000;
224 Zone* zone = def.get();
225 if(currentZone!=
nullptr &&
230 if(z.checkPos(plPos.x,plPos.y+player.
translateY(),plPos.z)) {
237 bool isDay = (
gtime(4,0)<=time && time<=
gtime(21,0));
250 if(currentZone==zone && currentTags==tags)
256 Zone* zTry[] = {zone, def.get()};
263 for(auto mode:modeTry) {
264 const size_t sep = zone->name.find(
'_');
265 const char* tag = zone->name.c_str();
266 if(sep!=std::string::npos)
270 if(setMusic(tag,tags))
275void WorldSound::tickSlot(std::vector<PEffect>& effect) {
276 for(
size_t i=0;i<effect.size();) {
277 auto& e = *effect[i];
278 if(e.eff.isFinished() && !(e.loop && e.active)){
279 effect[i]=std::move(effect.back());
285 for(
auto& i:effect) {
290void WorldSound::tickSlot(
Effect& slot) {
291 if(slot.eff.isFinished()) {
298 slot.setOcclusion(1.f);
300 auto dyn = owner.
physic();
305 if((pos-head).quadLength()<slot.maxDist*slot.maxDist)
306 occ = dyn->soundOclusion(head, pos);
307 slot.setOcclusion(std::max(0.f,1.f-occ));
311void WorldSound::initSlot(WorldSound::Effect& slot) {
312 auto dyn = owner.
physic();
315 slot.setOcclusion(std::max(0.f,1.f-occ));
318bool WorldSound::setMusic(std::string_view zone,
GameMusic::Tags tags) {
320 std::string_view smode =
"STD";
326 string_frm name(zone,
'_',(isDay ?
"DAY" :
"NGT"),
'_',smode);
335 float dist = sndRgn+800;
336 return (pos-plPos).quadLength()<dist*dist;
340 auto dyn = owner.
physic();
341 for(
auto& i:effect3d) {
342 auto rc = dyn->
ray(p, i->pos);
351 std::lock_guard<std::mutex> guard(sync);
ListenerPos listenerPosition() const
float soundOclusion(const Tempest::Vec3 &from, const Tempest::Vec3 &to) const
RayLandResult ray(const Tempest::Vec3 &from, const Tempest::Vec3 &to) const
static Tags mkTags(Tags daytime, Tags mode)
static GameMusic & inst()
void updateListenerPos(const Camera::ListenerPos &lpos)
auto loadSound(const Tempest::Sound &raw) -> Tempest::SoundEffect
SoundFx * loadSoundFx(std::string_view name)
static const MusicDefinitions & musicDef()
void emitGlobalSound(std::string_view sfx)
auto weaponState() const -> WeaponState
static Tempest::Sound loadSoundBuffer(std::string_view name)
bool canSeeSource(const Tempest::Vec3 &npc) const
Sound addDlgSound(std::string_view s, const Tempest::Vec3 &pos, float range, uint64_t &timeLen)
void setDefaultZone(const zenkit::VZoneMusic &vob)
bool isInListenerRange(const Tempest::Vec3 &pos, float sndRgn) const
WorldSound(GameSession &game, World &world)
bool execTriggerEvent(const TriggerEvent &e)
static const float talkRange
void addSound(const zenkit::VSound &vob)
void addZone(const zenkit::VZoneMusic &vob)
void aiOutput(const Tempest::Vec3 &pos, std::string_view outputname)
DynamicWorld * physic() const
bool isTargeted(Npc &npc)
uint64_t tickCount() const
bool checkPos(float x, float y, float z) const