diff --git a/data/images/engine/editor/music.png b/data/images/engine/editor/music.png index 8f2172f36ec..eeca404198e 100644 Binary files a/data/images/engine/editor/music.png and b/data/images/engine/editor/music.png differ diff --git a/data/images/engine/editor/objects.stoi b/data/images/engine/editor/objects.stoi index a9d88d655a3..b476fddac94 100644 --- a/data/images/engine/editor/objects.stoi +++ b/data/images/engine/editor/objects.stoi @@ -356,24 +356,21 @@ (object (class "particles-ghosts") (icon "images/engine/editor/ghostparticles.png")) - - - ;; +-- This should be disabled, as it probably won't support multiple textures ;; | per config object. ;; v ~ Semphris (object (class "particles-custom") (icon "images/engine/editor/particle.png")) - - - (object (class "particles-custom-file") (icon "images/engine/editor/particle_file.png")) (object (class "textscroller") (icon "images/engine/editor/textscroller.png")) + (object + (class "sound-object") + (icon "images/engine/editor/sound.png")) ) (objectgroup diff --git a/data/images/engine/editor/sound.png b/data/images/engine/editor/sound.png new file mode 100644 index 00000000000..180744a5b32 Binary files /dev/null and b/data/images/engine/editor/sound.png differ diff --git a/src/audio/dummy_sound_source.cpp b/src/audio/dummy_sound_source.cpp index 65d2c755cf7..92fc06020a0 100644 --- a/src/audio/dummy_sound_source.cpp +++ b/src/audio/dummy_sound_source.cpp @@ -34,7 +34,7 @@ class DummySoundSource final : public SoundSource is_playing = true; } - virtual void stop() override + virtual void stop(bool) override { is_playing = false; } diff --git a/src/audio/openal_sound_source.cpp b/src/audio/openal_sound_source.cpp index 6ad5a03454a..f009a6baef0 100644 --- a/src/audio/openal_sound_source.cpp +++ b/src/audio/openal_sound_source.cpp @@ -40,7 +40,7 @@ OpenALSoundSource::~OpenALSoundSource() } void -OpenALSoundSource::stop() +OpenALSoundSource::stop(bool unload_buffer) { #ifdef WIN32 // See commit 417a8e7a8c599bfc2dceaec7b6f64ac865318ef1 @@ -48,7 +48,8 @@ OpenALSoundSource::stop() #else alSourceStop(m_source); #endif - alSourcei(m_source, AL_BUFFER, AL_NONE); + if (unload_buffer) + alSourcei(m_source, AL_BUFFER, AL_NONE); try { SoundManager::check_al_error("Problem stopping audio source: "); diff --git a/src/audio/openal_sound_source.hpp b/src/audio/openal_sound_source.hpp index 50769a47e15..c0649c10a36 100644 --- a/src/audio/openal_sound_source.hpp +++ b/src/audio/openal_sound_source.hpp @@ -30,7 +30,7 @@ class OpenALSoundSource : public SoundSource ~OpenALSoundSource() override; virtual void play() override; - virtual void stop() override; + virtual void stop(bool unload_buffer = true) override; virtual bool playing() const override; virtual void set_looping(bool looping) override; diff --git a/src/audio/sound_source.hpp b/src/audio/sound_source.hpp index 50bc40bbaa5..d1ccde14133 100644 --- a/src/audio/sound_source.hpp +++ b/src/audio/sound_source.hpp @@ -29,7 +29,7 @@ class SoundSource virtual ~SoundSource() {} virtual void play() = 0; - virtual void stop() = 0; + virtual void stop(bool unload_buffer = true) = 0; virtual bool playing() const = 0; virtual void set_looping(bool looping) = 0; diff --git a/src/object/ambient_sound.cpp b/src/object/ambient_sound.cpp index dc40c9533e3..9e6e4eb0427 100644 --- a/src/object/ambient_sound.cpp +++ b/src/object/ambient_sound.cpp @@ -1,5 +1,6 @@ // SuperTux // Copyright (C) 2006 Matthias Braun +// 2023 mrkubax10 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,7 +22,7 @@ #include "audio/sound_manager.hpp" #include "audio/sound_source.hpp" #include "editor/editor.hpp" -#include "object/camera.hpp" +#include "object/player.hpp" #include "supertux/sector.hpp" #include "util/reader_mapping.hpp" #include "video/drawing_context.hpp" @@ -29,15 +30,12 @@ AmbientSound::AmbientSound(const ReaderMapping& mapping) : MovingObject(mapping), ExposedObject(this), - sample(), - sound_source(), - latency(), - distance_factor(), - distance_bias(), - silence_distance(), - maximumvolume(), - targetvolume(), - currentvolume(0) + m_sample(), + m_sound_source(), + m_radius(), + m_radius_in_px(), + m_volume(), + m_has_played_sound(false) { m_col.m_group = COLGROUP_DISABLED; @@ -48,67 +46,35 @@ AmbientSound::AmbientSound(const ReaderMapping& mapping) : mapping.get("height", h, 32.0f); m_col.m_bbox.set_size(w, h); - mapping.get("distance_factor",distance_factor, 0.0f); - mapping.get("distance_bias" ,distance_bias , 0.0f); - mapping.get("sample" ,sample , ""); - mapping.get("volume" ,maximumvolume , 1.0f); + mapping.get("radius", m_radius, 1.0f); + mapping.get("sample", m_sample, ""); + mapping.get("volume", m_volume, 1.0f); - // Square all distances (saves us a sqrt later). + m_radius_in_px = m_radius*32.0f; - if (!Editor::is_active()) { - distance_bias*=distance_bias; - distance_factor*=distance_factor; - } - - // Set default silence_distance. - - if (distance_factor == 0) - silence_distance = std::numeric_limits::max(); - else - silence_distance = 1/distance_factor; - - mapping.get("silence_distance",silence_distance); - - if (!Editor::is_active()) { - sound_source.reset(); // Resetting the sound source to stop playing at the beginning. - SoundManager::current()->preload(sample); - } - latency=0; + prepare_sound_source(); } -AmbientSound::AmbientSound(const Vector& pos, float factor, float bias, float vol, const std::string& file) : +AmbientSound::AmbientSound(const Vector& pos, float radius, float vol, const std::string& file) : ExposedObject(this), - sample(file), - sound_source(), - latency(0), - distance_factor(factor * factor), - distance_bias(bias * bias), - silence_distance(), - maximumvolume(vol), - targetvolume(), - currentvolume() + m_sample(file), + m_sound_source(), + m_radius(radius), + m_radius_in_px(m_radius*32.0f), + m_volume(vol), + m_has_played_sound(false) { m_col.m_group = COLGROUP_DISABLED; m_col.m_bbox.set_pos(pos); m_col.m_bbox.set_size(32, 32); - // Set default silence_distance. - - if (distance_factor == 0) - silence_distance = std::numeric_limits::max(); - else - silence_distance = 1/distance_factor; - - if (!Editor::is_active()) { - sound_source.reset(); // Resetting the sound source to stop playing at the beginning. - SoundManager::current()->preload(sample); - } + prepare_sound_source(); } AmbientSound::~AmbientSound() { - stop_playing(); + stop_looping_sounds(); } ObjectSettings @@ -116,107 +82,15 @@ AmbientSound::get_settings() { ObjectSettings result = MovingObject::get_settings(); - result.add_sound(_("Sound"), &sample, "sample"); - result.add_float(_("Distance factor"), &distance_factor, "distance_factor"); - result.add_float(_("Distance bias"), &distance_bias, "distance_bias"); - result.add_float(_("Volume"), &maximumvolume, "volume"); + result.add_sound(_("Sound"), &m_sample, "sample"); + result.add_float(_("Radius (in tiles)"), &m_radius, "radius"); + result.add_float(_("Volume"), &m_volume, "volume"); - result.reorder({"sample", "distance_factor", "distance_bias", "volume", "region", "name", "x", "y", "width", "height"}); + result.reorder({"sample", "radius", "volume", "region", "name", "x", "y", "width", "height"}); return result; } -void -AmbientSound::after_editor_set() -{ -} - -void -AmbientSound::stop_playing() -{ - sound_source.reset(); -} - -void -AmbientSound::start_playing() -{ - if (Editor::is_active()) return; - - try { - sound_source = SoundManager::current()->create_sound_source(sample); - if (!sound_source) - throw std::runtime_error("file not found"); - - sound_source->set_gain(0); - sound_source->set_looping(true); - sound_source->set_position(m_col.m_bbox.get_middle()); - currentvolume=targetvolume=1e-20f; - sound_source->play(); - } catch(std::exception& e) { - log_warning << "Couldn't play '" << sample << "': " << e.what() << "" << std::endl; - sound_source.reset(); - remove_me(); - } -} - -void -AmbientSound::update(float dt_sec) -{ - if (latency-- <= 0) { - float px,py; - float rx,ry; - - // Get the central position of the camera. - px=Sector::get().get_camera().get_center().x; - py=Sector::get().get_camera().get_center().y; - - // Determine the nearest point within the area bounds. - rx=pxset_gain(currentvolume*maximumvolume); - - if (sqrdistance>=silence_distance && currentvolume < 1e-3f) - stop_playing(); - latency=0; - } else { - if (sqrdistance(0.001f / distance_factor); - //(int)(10*((sqrdistance-silence_distance)/silence_distance)); - } - } - - // Heuristically measured "good" maximum latency. - - // if (latency > 0.001 / distance_factor) - // latency = -} - #ifndef SCRIPTING_API void AmbientSound::set_pos(const Vector& pos) @@ -252,22 +126,91 @@ AmbientSound::collision(GameObject& other, const CollisionHit& hit_) void AmbientSound::draw(DrawingContext& context) { - if (Editor::is_active()) { + if (Editor::is_active()) context.color().draw_filled_rect(m_col.m_bbox, Color(0.0f, 0.0f, 1.0f, 0.6f), 0.0f, LAYER_OBJECTS); - } } void AmbientSound::stop_looping_sounds() { - stop_playing(); + if (m_sound_source) + m_sound_source->stop(false); } void AmbientSound::play_looping_sounds() { - start_playing(); + if (Editor::is_active()) + return; + + m_sound_source->play(); +} + +void +AmbientSound::update(float dt_sec) +{ + const Player* const nearest_player = Sector::get().get_nearest_player(get_bbox().get_middle()); + if (!nearest_player) + return; + const Rectf& player_bbox = nearest_player->get_bbox(); + const Vector player_center = player_bbox.get_middle(); + + if (get_bbox().contains(player_bbox)) + m_sound_source->set_gain(m_volume); + else + { + float player_distance = m_radius+1; + if (player_center.x >= get_bbox().get_left() && player_center.x <= get_bbox().get_right()) + player_distance = player_center.y < get_bbox().get_top() ? get_bbox().get_top() - player_center.y : player_center.y - get_bbox().get_bottom(); + else if (player_center.y >= get_bbox().get_top() && player_center.y <= get_bbox().get_bottom()) + player_distance = player_center.x < get_bbox().get_left() ? get_bbox().get_left() - player_center.x : player_center.x - get_bbox().get_right(); + else if (player_center.x <= get_bbox().get_left() && player_center.y <= get_bbox().get_top()) + player_distance = glm::distance(player_center, get_bbox().p1()); + else if (player_center.x >= get_bbox().get_right() && player_center.y <= get_bbox().get_top()) + player_distance = glm::distance(player_center, get_bbox().p1() + Vector(get_bbox().get_width(), 0)); + else if (player_center.x <= get_bbox().get_left() && player_center.y >= get_bbox().get_bottom()) + player_distance = glm::distance(player_center, get_bbox().p1() + Vector(0, get_bbox().get_height())); + else if (player_center.x >= get_bbox().get_right() && player_center.y >= get_bbox().get_bottom()) + player_distance = glm::distance(player_center, get_bbox().p2()); + m_sound_source->set_gain(std::max(m_radius_in_px - player_distance, 0.0f) / m_radius_in_px * m_volume); + } + + if (!m_has_played_sound) + { + m_sound_source->play(); + m_has_played_sound = true; + } +} + +void +AmbientSound::prepare_sound_source() +{ + if (Editor::is_active()) + return; + + if (m_sample.empty()) + { + remove_me(); + return; + } + + try + { + m_sound_source = SoundManager::current()->create_sound_source(m_sample); + if (!m_sound_source) + throw std::runtime_error("file not found"); + + m_sound_source->set_gain(0); + m_sound_source->set_looping(true); + m_sound_source->set_relative(true); + } + catch(const std::exception& e) + { + log_warning << "Couldn't load '" << m_sample << "': " << e.what() << std::endl; + m_sound_source.reset(); + remove_me(); + } } /* EOF */ diff --git a/src/object/ambient_sound.hpp b/src/object/ambient_sound.hpp index 7e94aceb81f..2bf0994f10d 100644 --- a/src/object/ambient_sound.hpp +++ b/src/object/ambient_sound.hpp @@ -1,5 +1,6 @@ // SuperTux // Copyright (C) 2006 Matthias Braun +// 2023 mrkubax10 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,28 +15,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -/** - * Ambient Sound Source, gamma version. Features: - * - * - "rounded rectangle" geometry with position, dimension and - * "rounding radius" (extending in all directions) of a 100% - * volume area, adjustable maximum volume, inverse square - * falloff outside area. - * - * - degenerates gracefully to a disc for dimension=0 - * - * - parameters: - * - * x, y position - * width, height dimension - * distance_factor high = steep falloff - * distance_bias high = big "100% disc" - * silence_distance defaults reasonably. - * sample sample to be played back in loop mode - * - * basti_ - */ - #ifndef HEADER_SUPERTUX_OBJECT_AMBIENT_SOUND_HPP #define HEADER_SUPERTUX_OBJECT_AMBIENT_SOUND_HPP @@ -50,11 +29,11 @@ class ReaderMapping; class SoundSource; class AmbientSound final : public MovingObject, - public ExposedObject + public ExposedObject { public: AmbientSound(const ReaderMapping& mapping); - AmbientSound(const Vector& pos, float factor, float bias, float vol, const std::string& file); + AmbientSound(const Vector& pos, float radius, float vol, const std::string& file); ~AmbientSound() override; virtual HitResponse collision(GameObject& other, const CollisionHit& hit_) override; @@ -78,7 +57,6 @@ class AmbientSound final : public MovingObject, virtual void draw(DrawingContext& context) override; virtual ObjectSettings get_settings() override; - virtual void after_editor_set() override; virtual int get_layer() const override { return LAYER_OBJECTS; } @@ -87,21 +65,18 @@ class AmbientSound final : public MovingObject, protected: virtual void update(float dt_sec) override; - virtual void start_playing(); - virtual void stop_playing(); private: - std::string sample; - std::unique_ptr sound_source; - int latency; + void prepare_sound_source(); - float distance_factor; /// distance scaling - float distance_bias; /// 100% volume disc radius - float silence_distance; /// not implemented yet +private: + std::string m_sample; + std::unique_ptr m_sound_source; - float maximumvolume; /// maximum volume - float targetvolume; /// how loud we want to be - float currentvolume; /// how loud we are + float m_radius; + float m_radius_in_px; + float m_volume; + bool m_has_played_sound; private: AmbientSound(const AmbientSound&) = delete; diff --git a/src/object/sound_object.cpp b/src/object/sound_object.cpp new file mode 100644 index 00000000000..7b54fc85939 --- /dev/null +++ b/src/object/sound_object.cpp @@ -0,0 +1,124 @@ +// SuperTux +// Copyright (C) 2023 mrkubax10 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "object/sound_object.hpp" + +#include + +#include "audio/sound_manager.hpp" +#include "audio/sound_source.hpp" +#include "editor/editor.hpp" +#include "util/reader_mapping.hpp" + +SoundObject::SoundObject(const ReaderMapping& mapping) : + GameObject(mapping), + ExposedObject(this), + m_sample(), + m_sound_source(), + m_volume() +{ + mapping.get("sample", m_sample, ""); + mapping.get("volume", m_volume, 1.0f); + + prepare_sound_source(); +} + +SoundObject::SoundObject(float vol, const std::string& file) : + ExposedObject(this), + m_sample(file), + m_sound_source(), + m_volume(vol) +{ + prepare_sound_source(); +} + +SoundObject::~SoundObject() +{ + stop_looping_sounds(); +} + +ObjectSettings +SoundObject::get_settings() +{ + ObjectSettings result = GameObject::get_settings(); + + result.add_sound(_("Sound"), &m_sample, "sample"); + result.add_float(_("Volume"), &m_volume, "volume"); + + result.reorder({"sample", "volume", "name"}); + + result.add_remove(); + + return result; +} + +void +SoundObject::stop_looping_sounds() +{ + if (m_sound_source) + m_sound_source->stop(false); +} + +void +SoundObject::play_looping_sounds() +{ + if (!Editor::is_active() && m_sound_source) + m_sound_source->play(); +} + +void +SoundObject::prepare_sound_source() +{ + if (Editor::is_active()) + return; + + if (m_sample.empty()) + { + remove_me(); + return; + } + + try + { + m_sound_source = SoundManager::current()->create_sound_source(m_sample); + if (!m_sound_source) + throw std::runtime_error("file not found"); + + // Maybe FIXME: Apparently this is an OpenAL error, if gain is not set to 0 before making source looping + // it won't be possible to pause it. + m_sound_source->set_gain(0); + m_sound_source->set_looping(true); + m_sound_source->set_relative(true); + m_sound_source->set_gain(m_volume); + + m_sound_source->play(); + } + catch(const std::exception& e) + { + log_warning << "Couldn't load '" << m_sample << "': " << e.what() << std::endl; + m_sound_source.reset(); + remove_me(); + } +} + +void +SoundObject::set_volume(float volume) +{ + m_volume = volume; + m_sound_source->set_gain(volume); +} + +/* EOF */ diff --git a/src/object/sound_object.hpp b/src/object/sound_object.hpp new file mode 100644 index 00000000000..9d6bf8c1e2c --- /dev/null +++ b/src/object/sound_object.hpp @@ -0,0 +1,72 @@ +// SuperTux +// Copyright (C) 2023 mrkubax10 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef HEADER_SUPERTUX_SOUND_OBJECT_HPP +#define HEADER_SUPERTUX_SOUND_OBJECT_HPP + +#include "supertux/game_object.hpp" +#include "squirrel/exposed_object.hpp" + +#include "scripting/sound_object.hpp" + +class ReaderMapping; +class SoundSource; + +/** Plays sound at given interval with specified volume hearable in entire Sector */ +class SoundObject final : public GameObject, + public ExposedObject +{ +public: + SoundObject(const ReaderMapping& mapping); + SoundObject(float vol, const std::string& file); + ~SoundObject() override; + + virtual void draw(DrawingContext& context) override {} + virtual void update(float dt_sec) override {} + + static std::string class_name() { return "sound-object"; } + virtual std::string get_class_name() const override { return class_name(); } + static std::string display_name() { return _("Sound"); } + virtual std::string get_display_name() const override { return display_name(); } + virtual const std::string get_icon_path() const override { return "images/engine/editor/sound.png"; } + + virtual ObjectSettings get_settings() override; + + virtual void stop_looping_sounds() override; + virtual void play_looping_sounds() override; + + /** @name Scriptable methods + @{ */ + void set_volume(float volume); + float get_volume() const { return m_volume; } + /** @} */ + +private: + std::string m_sample; + std::unique_ptr m_sound_source; + float m_volume; + +private: + void prepare_sound_source(); + +private: + SoundObject(const SoundObject&) = delete; + SoundObject& operator=(const SoundObject&) = delete; +}; + +#endif + +/* EOF */ diff --git a/src/scripting/sound_object.cpp b/src/scripting/sound_object.cpp new file mode 100644 index 00000000000..d43442696f8 --- /dev/null +++ b/src/scripting/sound_object.cpp @@ -0,0 +1,53 @@ +// SuperTux +// Copyright (C) 2023 mrkubax10 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "scripting/sound_object.hpp" + +#include "object/sound_object.hpp" + +namespace scripting { + +void +SoundObject::start_playing() +{ + SCRIPT_GUARD_VOID; + object.play_looping_sounds(); +} + +void +SoundObject::stop_playing() +{ + SCRIPT_GUARD_VOID; + object.stop_looping_sounds(); +} + +void +SoundObject::set_volume(float volume) +{ + SCRIPT_GUARD_VOID; + object.set_volume(volume); +} + +float +SoundObject::get_volume() const +{ + SCRIPT_GUARD_DEFAULT; + return object.get_volume(); +} + +} // namespace scripting + +/* EOF */ diff --git a/src/scripting/sound_object.hpp b/src/scripting/sound_object.hpp new file mode 100644 index 00000000000..a6df79b9f5b --- /dev/null +++ b/src/scripting/sound_object.hpp @@ -0,0 +1,74 @@ +// SuperTux +// Copyright (C) 2023 mrkubax10 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef HEADER_SUPERTUX_SCRIPTING_SOUND_OBJECT_HPP +#define HEADER_SUPERTUX_SCRIPTING_SOUND_OBJECT_HPP + +#ifndef SCRIPTING_API +#include "scripting/game_object.hpp" + +class SoundObject; +#endif + +namespace scripting { + +/** + * @summary A ""SoundObject"" that was given a name can be controlled by scripts. + * @instances A ""SoundObject"" is instantiated by placing a definition inside a level. + It can then be accessed by its name from a script or via ""sector.name"" from the console. + */ +class SoundObject final +#ifndef SCRIPTING_API + : public GameObject<::SoundObject> +#endif +{ +#ifndef SCRIPTING_API +public: + using GameObject::GameObject; + +private: + SoundObject(const SoundObject&) = delete; + SoundObject& operator=(const SoundObject&) = delete; +#endif + +public: + /** + * Starts playing sound if it was stopped previously. + */ + void start_playing(); + + /** + * Stops playing sound. + */ + void stop_playing(); + + /** + * Sets the volume of sound played by SoundObject. + * @param float $volume + */ + void set_volume(float volume); + + /** + * Returns the volume of sound played by SoundObject. + */ + float get_volume() const; +}; + +} // namespace scripting + +#endif + +/* EOF */ diff --git a/src/scripting/wrapper.cpp b/src/scripting/wrapper.cpp index a9f65a75561..7f50cf09b13 100644 --- a/src/scripting/wrapper.cpp +++ b/src/scripting/wrapper.cpp @@ -8571,6 +8571,119 @@ static SQInteger Sector_set_gravity_wrapper(HSQUIRRELVM vm) } +static SQInteger SoundObject_release_hook(SQUserPointer ptr, SQInteger ) +{ + scripting::SoundObject* _this = reinterpret_cast (ptr); + delete _this; + return 0; +} + +static SQInteger SoundObject_start_playing_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) { + sq_throwerror(vm, _SC("'start_playing' called without instance")); + return SQ_ERROR; + } + scripting::SoundObject* _this = reinterpret_cast (data); + + + try { + _this->start_playing(); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'start_playing'")); + return SQ_ERROR; + } + +} + +static SQInteger SoundObject_stop_playing_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) { + sq_throwerror(vm, _SC("'stop_playing' called without instance")); + return SQ_ERROR; + } + scripting::SoundObject* _this = reinterpret_cast (data); + + + try { + _this->stop_playing(); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'stop_playing'")); + return SQ_ERROR; + } + +} + +static SQInteger SoundObject_set_volume_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) { + sq_throwerror(vm, _SC("'set_volume' called without instance")); + return SQ_ERROR; + } + scripting::SoundObject* _this = reinterpret_cast (data); + + SQFloat arg0; + if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) { + sq_throwerror(vm, _SC("Argument 1 not a float")); + return SQ_ERROR; + } + + try { + _this->set_volume(arg0); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'set_volume'")); + return SQ_ERROR; + } + +} + +static SQInteger SoundObject_get_volume_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, nullptr, SQTrue)) || !data) { + sq_throwerror(vm, _SC("'get_volume' called without instance")); + return SQ_ERROR; + } + scripting::SoundObject* _this = reinterpret_cast (data); + + + try { + float return_value = _this->get_volume(); + + sq_pushfloat(vm, return_value); + return 1; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'get_volume'")); + return SQ_ERROR; + } + +} + static SQInteger Spotlight_release_hook(SQUserPointer ptr, SQInteger ) { scripting::Spotlight* _this = reinterpret_cast (ptr); @@ -13469,6 +13582,32 @@ void create_squirrel_instance(HSQUIRRELVM v, scripting::Sector* object, bool set sq_remove(v, -2); // remove root table } +void create_squirrel_instance(HSQUIRRELVM v, scripting::SoundObject* object, bool setup_releasehook) +{ + using namespace wrapper; + + sq_pushroottable(v); + sq_pushstring(v, "SoundObject", -1); + if(SQ_FAILED(sq_get(v, -2))) { + std::ostringstream msg; + msg << "Couldn't resolved squirrel type 'SoundObject'"; + throw SquirrelError(v, msg.str()); + } + + if(SQ_FAILED(sq_createinstance(v, -1)) || SQ_FAILED(sq_setinstanceup(v, -1, object))) { + std::ostringstream msg; + msg << "Couldn't setup squirrel instance for object of type 'SoundObject'"; + throw SquirrelError(v, msg.str()); + } + sq_remove(v, -2); // remove object name + + if(setup_releasehook) { + sq_setreleasehook(v, -1, SoundObject_release_hook); + } + + sq_remove(v, -2); // remove root table +} + void create_squirrel_instance(HSQUIRRELVM v, scripting::Spotlight* object, bool setup_releasehook) { using namespace wrapper; @@ -16417,6 +16556,45 @@ void register_supertux_wrapper(HSQUIRRELVM v) throw SquirrelError(v, "Couldn't register class 'ScriptedObject'"); } + // Register class SoundObject + sq_pushstring(v, "SoundObject", -1); + if(sq_newclass(v, SQFalse) < 0) { + std::ostringstream msg; + msg << "Couldn't create new class 'SoundObject'"; + throw SquirrelError(v, msg.str()); + } + sq_pushstring(v, "start_playing", -1); + sq_newclosure(v, &SoundObject_start_playing_wrapper, 0); + sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, "."); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'start_playing'"); + } + + sq_pushstring(v, "stop_playing", -1); + sq_newclosure(v, &SoundObject_stop_playing_wrapper, 0); + sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, "."); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'stop_playing'"); + } + + sq_pushstring(v, "set_volume", -1); + sq_newclosure(v, &SoundObject_set_volume_wrapper, 0); + sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, ".b|n"); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'set_volume'"); + } + + sq_pushstring(v, "get_volume", -1); + sq_newclosure(v, &SoundObject_get_volume_wrapper, 0); + sq_setparamscheck(v, SQ_MATCHTYPEMASKSTRING, "."); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'get_volume'"); + } + + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register class 'SoundObject'"); + } + // Register class Spotlight sq_pushstring(v, "Spotlight", -1); if(sq_newclass(v, SQFalse) < 0) { diff --git a/src/scripting/wrapper.hpp b/src/scripting/wrapper.hpp index d2625106a44..7cfad2044ad 100644 --- a/src/scripting/wrapper.hpp +++ b/src/scripting/wrapper.hpp @@ -58,6 +58,8 @@ class ScriptedObject; void create_squirrel_instance(HSQUIRRELVM v, scripting::ScriptedObject* object, bool setup_releasehook = false); class Sector; void create_squirrel_instance(HSQUIRRELVM v, scripting::Sector* object, bool setup_releasehook = false); +class SoundObject; +void create_squirrel_instance(HSQUIRRELVM v, scripting::SoundObject* object, bool setup_releasehook = false); class Spotlight; void create_squirrel_instance(HSQUIRRELVM v, scripting::Spotlight* object, bool setup_releasehook = false); class Text; diff --git a/src/scripting/wrapper.interface.hpp b/src/scripting/wrapper.interface.hpp index 0827d26202d..7b3ee4deeb3 100644 --- a/src/scripting/wrapper.interface.hpp +++ b/src/scripting/wrapper.interface.hpp @@ -25,6 +25,7 @@ #include "scripting/rock.hpp" #include "scripting/scripted_object.hpp" #include "scripting/sector.hpp" +#include "scripting/sound_object.hpp" #include "scripting/spotlight.hpp" #include "scripting/text.hpp" #include "scripting/text_array.hpp" diff --git a/src/supertux/game_object_factory.cpp b/src/supertux/game_object_factory.cpp index 3e49d90f2fc..cabc900e4ed 100644 --- a/src/supertux/game_object_factory.cpp +++ b/src/supertux/game_object_factory.cpp @@ -117,6 +117,7 @@ #include "object/scripted_object.hpp" #include "object/shard.hpp" #include "object/snow_particle_system.hpp" +#include "object/sound_object.hpp" #include "object/spawnpoint.hpp" #include "object/spotlight.hpp" #include "object/text_array_object.hpp" @@ -277,6 +278,7 @@ GameObjectFactory::init_factories() add_factory("scriptedobject"); add_factory("shard", OBJ_PARAM_DISPENSABLE); add_type_factory("skull_tile", UnstableTile::DELAYED); // Backward compatibility. + add_factory("sound-object"); add_factory("particles-snow"); add_factory("spotlight"); add_factory("textscroller");