mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-06 16:05:06 +00:00
270 lines
6.6 KiB
C++
270 lines
6.6 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2006 - 2009 by Karol Nowak <grzywacz@sul.uni.lodz.pl>
|
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2
|
|
or at your option any later version.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#include "global.hpp"
|
|
|
|
|
|
#include "display.hpp"
|
|
#include "foreach.hpp"
|
|
#include "log.hpp"
|
|
#include "pathutils.hpp"
|
|
#include "sound.hpp"
|
|
#include "soundsource.hpp"
|
|
|
|
namespace soundsource {
|
|
|
|
const unsigned DEFAULT_CHANCE = 100;
|
|
const unsigned DEFAULT_DELAY = 1000;
|
|
const unsigned DEFAULT_FULL_RANGE = 3;
|
|
const unsigned DEFAULT_FADE_RANGE = 14;
|
|
|
|
unsigned int positional_source::last_id = 0;
|
|
|
|
manager::manager(const display &disp) :
|
|
sources_(),
|
|
disp_(disp)
|
|
{
|
|
disp_.scroll_event().attach_handler(this);
|
|
update_positions();
|
|
}
|
|
|
|
manager::~manager()
|
|
{
|
|
for(positional_source_iterator it = sources_.begin(); it != sources_.end(); ++it) {
|
|
delete (*it).second;
|
|
}
|
|
|
|
sources_.clear();
|
|
}
|
|
|
|
void manager::handle_generic_event(const std::string &event_name)
|
|
{
|
|
if(event_name == "scrolled")
|
|
update_positions();
|
|
}
|
|
|
|
void manager::add(const sourcespec &spec)
|
|
{
|
|
positional_source_iterator it;
|
|
|
|
if((it = sources_.find(spec.id())) == sources_.end()) {
|
|
sources_[spec.id()] = new positional_source(spec);
|
|
} else {
|
|
delete (*it).second;
|
|
(*it).second = new positional_source(spec);
|
|
}
|
|
}
|
|
|
|
void manager::remove(const std::string &id)
|
|
{
|
|
positional_source_iterator it;
|
|
|
|
if((it = sources_.find(id)) == sources_.end())
|
|
return;
|
|
else {
|
|
delete (*it).second;
|
|
sources_.erase(it);
|
|
}
|
|
}
|
|
|
|
void manager::update()
|
|
{
|
|
unsigned int time = SDL_GetTicks();
|
|
|
|
for(positional_source_iterator it = sources_.begin(); it != sources_.end(); ++it) {
|
|
(*it).second->update(time, disp_);
|
|
}
|
|
}
|
|
|
|
void manager::update_positions()
|
|
{
|
|
unsigned int time = SDL_GetTicks();
|
|
|
|
for(positional_source_iterator it = sources_.begin(); it != sources_.end(); ++it) {
|
|
(*it).second->update_positions(time, disp_);
|
|
}
|
|
}
|
|
|
|
void manager::add_location(const std::string &id, const map_location &loc)
|
|
{
|
|
positional_source_iterator it = sources_.find(id);
|
|
|
|
if(it == sources_.end())
|
|
return;
|
|
else
|
|
(*it).second->add_location(loc);
|
|
}
|
|
|
|
void manager::write_sourcespecs(config& cfg) const
|
|
{
|
|
for(positional_source_const_iterator i = sources_.begin(); i != sources_.end(); ++i) {
|
|
assert(i->second);
|
|
|
|
config& child = cfg.add_child("sound_source");
|
|
child["id"] = i->first;
|
|
i->second->write_config(child);
|
|
}
|
|
}
|
|
|
|
positional_source::positional_source(const sourcespec &spec) :
|
|
last_played_(0),
|
|
min_delay_(spec.minimum_delay()),
|
|
chance_(spec.chance()),
|
|
loops_(spec.loops()),
|
|
id_(last_id++),
|
|
range_(spec.full_range()),
|
|
faderange_(spec.fade_range()),
|
|
check_fogged_(spec.check_fogged()),
|
|
files_(spec.files()),
|
|
locations_(spec.get_locations())
|
|
{
|
|
assert(range_ > 0);
|
|
assert(faderange_ > 0);
|
|
}
|
|
|
|
positional_source::~positional_source()
|
|
{
|
|
sound::reposition_sound(id_, DISTANCE_SILENT);
|
|
}
|
|
|
|
void positional_source::update(unsigned int time, const display &disp)
|
|
{
|
|
if(time - last_played_ < min_delay_ || sound::is_sound_playing(id_))
|
|
return;
|
|
|
|
unsigned int i = rand() % 100 + 1;
|
|
|
|
if(i <= chance_) {
|
|
last_played_ = time;
|
|
|
|
// If no locations have been specified, treat the source as if
|
|
// it was present everywhere on the map
|
|
if(locations_.size() == 0) {
|
|
sound::play_sound_positioned(files_, id_, loops_, 0); // max volume
|
|
return;
|
|
}
|
|
|
|
int distance_volume = DISTANCE_SILENT;
|
|
for(std::vector<map_location>::iterator i = locations_.begin(); i != locations_.end(); ++i) {
|
|
int v = calculate_volume(*i, disp);
|
|
if(v < distance_volume) {
|
|
distance_volume = v;
|
|
}
|
|
}
|
|
|
|
if(distance_volume >= DISTANCE_SILENT)
|
|
return;
|
|
|
|
sound::play_sound_positioned(files_, id_, loops_, distance_volume);
|
|
}
|
|
}
|
|
|
|
void positional_source::update_positions(unsigned int time, const display &disp)
|
|
{
|
|
int distance_volume = DISTANCE_SILENT;
|
|
for(std::vector<map_location>::iterator i = locations_.begin(); i != locations_.end(); ++i) {
|
|
if(disp.shrouded(*i) || (check_fogged_ && disp.fogged(*i)))
|
|
continue;
|
|
|
|
int v = calculate_volume(*i, disp);
|
|
if(v < distance_volume) {
|
|
distance_volume = v;
|
|
}
|
|
}
|
|
|
|
if(sound::is_sound_playing(id_)) {
|
|
sound::reposition_sound(id_, distance_volume);
|
|
} else {
|
|
update(time, disp);
|
|
}
|
|
}
|
|
|
|
int positional_source::calculate_volume(const map_location &loc, const display &disp)
|
|
{
|
|
assert(range_ > 0);
|
|
assert(faderange_ > 0);
|
|
|
|
SDL_Rect area = disp.map_area();
|
|
map_location center = disp.hex_clicked_on(area.x + area.w / 2, area.y + area.h / 2);
|
|
size_t distance = distance_between(loc, center);
|
|
|
|
if(distance <= range_) {
|
|
return 0;
|
|
}
|
|
|
|
return static_cast<int>(( ( (distance - range_) / (double) faderange_) * DISTANCE_SILENT));
|
|
}
|
|
|
|
void positional_source::add_location(const map_location &loc)
|
|
{
|
|
locations_.push_back(loc);
|
|
}
|
|
|
|
void positional_source::write_config(config& cfg) const
|
|
{
|
|
cfg["sounds"] = this->files_;
|
|
cfg["delay"] = str_cast<unsigned int>(this->min_delay_);
|
|
cfg["chance"] = str_cast<unsigned int>(this->chance_);
|
|
cfg["check_fogged"] = this->check_fogged_ ? "yes" : "no";
|
|
|
|
cfg["x"] = cfg["y"] = "";
|
|
bool first_loc = true;
|
|
foreach(const map_location& loc, locations_) {
|
|
if(!first_loc) {
|
|
cfg["x"] += ",";
|
|
cfg["y"] += ",";
|
|
} else {
|
|
first_loc = false;
|
|
}
|
|
cfg["x"] += str_cast<unsigned int>(loc.x);
|
|
cfg["y"] += str_cast<unsigned int>(loc.y);
|
|
}
|
|
|
|
cfg["loop"] = str_cast<unsigned int>(this->loops_);
|
|
cfg["full_range"] = str_cast<unsigned int>(this->range_);
|
|
cfg["fade_range"] = str_cast<unsigned int>(this->faderange_);
|
|
}
|
|
|
|
sourcespec::sourcespec(const config& cfg) :
|
|
id_(cfg["id"]),
|
|
files_(cfg["sounds"]),
|
|
min_delay_(lexical_cast_default<int>(cfg["delay"], DEFAULT_DELAY)),
|
|
chance_(lexical_cast_default<int>(cfg["chance"], DEFAULT_CHANCE)),
|
|
loops_(lexical_cast_default<int>(cfg["loop"], 0)),
|
|
range_(lexical_cast_default<int>(cfg["full_range"], 3)),
|
|
faderange_(lexical_cast_default<int>(cfg["fade_range"], 14)),
|
|
check_fogged_(utils::string_bool(cfg["check_fogged"], true)),
|
|
locations_()
|
|
{
|
|
const std::vector<std::string>& vx = utils::split(cfg["x"]);
|
|
const std::vector<std::string>& vy = utils::split(cfg["y"]);
|
|
|
|
if(vx.empty() || vy.empty()) {
|
|
lg::wml_error << "missing sound source locations";
|
|
}
|
|
|
|
if(vx.size() != vy.size()) {
|
|
lg::wml_error << "mismatched number of sound source location coordinates";
|
|
}
|
|
else {
|
|
for(unsigned int i = 0; i < std::min(vx.size(), vy.size()); ++i) {
|
|
map_location loc(lexical_cast<int>(vx[i]), lexical_cast<int>(vy[i]));
|
|
locations_.push_back(loc);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace soundsource
|
|
|