Files
starruler-linux/scripts/client/ambience.as
T
2018-07-17 14:15:37 +02:00

136 lines
2.8 KiB
ActionScript 3

const double DROPOFF_FACTOR = 64.0;
const double DROPOFF_FACTOR_SQ = DROPOFF_FACTOR * DROPOFF_FACTOR;
final class SoundEntry {
vec3d pos;
double scale;
};
final class AmbienceGroup {
map entries;
SoundEntry@ active;
const SoundSource@ source;
Sound@ sound;
double volume = 0;
AmbienceGroup(const SoundSource& soundSource) {
@source = soundSource;
}
void addEntry(int id, const vec3d& pos, double scale) {
SoundEntry entry;
entry.pos = pos;
entry.scale = scale;
entries.set(id, @entry);
}
void deleteEntry(int id) {
entries.delete(id);
}
SoundEntry@ findNearest(const vec3d& to) {
SoundEntry@ nearest;
double nearDist = 0;
map_iterator i = entries.iterator();
SoundEntry@ entry;
while(i.iterate(@entry)) {
double d = to.distanceToSQ(entry.pos);
if(d < entry.scale * entry.scale * DROPOFF_FACTOR_SQ && (nearest is null || d < nearDist)) {
@nearest = entry;
nearDist = d;
}
}
return nearest;
}
void tick(double time, const vec3d& pos) {
auto@ entry = findNearest(pos);
if(entry !is active) {
if(active !is null && sound !is null) {
if(volume > 0) {
volume = max(volume - time, 0.0);
sound.volume = volume * sqrt(active.scale);
}
else {
sound.pause();
@active = null;
}
}
else {
@active = entry;
}
}
else if(active !is null) {
if(sound is null) {
@sound = source.play(entry.pos, loop=true, pause=true, priority=true);
if(sound !is null) {
sound.minDistance = entry.scale;
sound.maxDistance = entry.scale * DROPOFF_FACTOR;
sound.volume = 0;
sound.resume();
volume = 0;
}
}
else if(volume < 1.0) {
if(volume == 0.0) {
sound.position = entry.pos;
sound.minDistance = entry.scale;
sound.maxDistance = entry.scale * DROPOFF_FACTOR;
sound.resume();
}
volume = min(1.0, volume+time);
sound.volume = volume * sqrt(entry.scale);
}
}
}
void clear() {
if(sound !is null)
sound.stop();
entries.deleteAll();
}
};
Mutex mtx;
dictionary ambientSounds;
void addAmbientSource(string sound, int id, vec3d pos, double scale) {
Lock lck(mtx);
AmbienceGroup@ group;
if(!ambientSounds.get(sound, @group)) {
const auto@ source = getSound(sound);
if(source is null)
return;
@group = AmbienceGroup(source);
ambientSounds.set(sound, @group);
}
group.addEntry(id, pos, scale);
}
void removeAmbientSource(int id) {
Lock lck(mtx);
AmbienceGroup@ group;
dictionary_iterator i = ambientSounds.iterator();
while(i.iterate(@group))
group.deleteEntry(id);
}
void tick(double time) {
Lock lck(mtx);
AmbienceGroup@ group;
dictionary_iterator i = ambientSounds.iterator();
while(i.iterate(@group))
group.tick(time, cameraPos);
}
void deinit() {
Lock lck(mtx);
AmbienceGroup@ group;
dictionary_iterator i = ambientSounds.iterator();
while(i.iterate(@group))
group.clear();
}