From 6c14025b2d416105915440e114de927c26e925ac Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Fri, 23 Mar 2012 20:23:03 +0200 Subject: [PATCH] Add event manager and use it to trigger sounds --- games/mesetint/mods/default/init.lua | 4 + src/camera.cpp | 10 ++- src/client.cpp | 8 +- src/client.h | 7 +- src/event.h | 72 +++++++++++++++++ src/event_manager.h | 115 +++++++++++++++++++++++++++ src/game.cpp | 79 +++++++++++++++++- src/gamedef.h | 7 +- src/nodedef.cpp | 21 +++++ src/nodedef.h | 6 +- src/player.cpp | 10 +++ src/scriptapi.cpp | 28 +++++++ src/server.cpp | 12 ++- src/server.h | 5 ++ src/sound.h | 18 +++++ 15 files changed, 388 insertions(+), 14 deletions(-) create mode 100644 src/event.h create mode 100644 src/event_manager.h diff --git a/games/mesetint/mods/default/init.lua b/games/mesetint/mods/default/init.lua index c850597b..0041e358 100644 --- a/games/mesetint/mods/default/init.lua +++ b/games/mesetint/mods/default/init.lua @@ -654,6 +654,10 @@ minetest.register_node("default:dirt_with_grass", { is_ground_content = true, groups = {crumbly=3}, drop = 'default:dirt', + sounds = { + --footstep = "default_grass_footstep", + footstep = {name="default_grass_footstep", gain=0.5}, + }, }) minetest.register_node("default:dirt_with_grass_footsteps", { diff --git a/src/camera.cpp b/src/camera.cpp index 8dad8dc6..fada4760 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" // easeCurve #include "gamedef.h" #include "sound.h" +#include "event.h" Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, IGameDef *gamedef): @@ -177,8 +178,10 @@ void Camera::step(f32 dtime) bool step = (was == 0 || (was < 0.5f && m_view_bobbing_anim >= 0.5f) || (was > 0.5f && m_view_bobbing_anim <= 0.5f)); - if(step) - m_gamedef->sound()->playSound("default_grass_walk", false, 1.0); + if(step){ + MtEvent *e = new SimpleTriggerEvent("ViewBobbingStep"); + m_gamedef->event()->put(e); + } } } @@ -190,7 +193,8 @@ void Camera::step(f32 dtime) { m_digging_anim = 0; m_digging_button = -1; - m_gamedef->sound()->playSound("dig", false, 1.0); + MtEvent *e = new SimpleTriggerEvent("CameraDig"); + m_gamedef->event()->put(e); } } } diff --git a/src/client.cpp b/src/client.cpp index cb6d2075..d8fb4eb7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -226,12 +226,14 @@ Client::Client( IWritableTextureSource *tsrc, IWritableItemDefManager *itemdef, IWritableNodeDefManager *nodedef, - ISoundManager *sound + ISoundManager *sound, + MtEventManager *event ): m_tsrc(tsrc), m_itemdef(itemdef), m_nodedef(nodedef), m_sound(sound), + m_event(event), m_mesh_update_thread(this), m_env( new ClientMap(this, this, control, @@ -2330,4 +2332,8 @@ ISoundManager* Client::getSoundManager() { return m_sound; } +MtEventManager* Client::getEventManager() +{ + return m_event; +} diff --git a/src/client.h b/src/client.h index 2a1681e9..13b36106 100644 --- a/src/client.h +++ b/src/client.h @@ -44,6 +44,7 @@ class IWritableNodeDefManager; //class IWritableCraftDefManager; class ClientEnvironment; struct MapDrawControl; +class MtEventManager; class ClientNotReadyException : public BaseException { @@ -175,7 +176,8 @@ public: IWritableTextureSource *tsrc, IWritableItemDefManager *itemdef, IWritableNodeDefManager *nodedef, - ISoundManager *sound + ISoundManager *sound, + MtEventManager *event ); ~Client(); @@ -308,6 +310,7 @@ public: virtual ITextureSource* getTextureSource(); virtual u16 allocateUnknownNodeId(const std::string &name); virtual ISoundManager* getSoundManager(); + virtual MtEventManager* getEventManager(); private: @@ -335,6 +338,8 @@ private: IWritableItemDefManager *m_itemdef; IWritableNodeDefManager *m_nodedef; ISoundManager *m_sound; + MtEventManager *m_event; + MeshUpdateThread m_mesh_update_thread; ClientEnvironment m_env; con::Connection m_con; diff --git a/src/event.h b/src/event.h new file mode 100644 index 00000000..032cb238 --- /dev/null +++ b/src/event.h @@ -0,0 +1,72 @@ +/* +Minetest-c55 +Copyright (C) 2012 celeron55, Perttu Ahola + +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 2 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, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef EVENT_HEADER +#define EVENT_HEADER + +class MtEvent +{ +public: + virtual ~MtEvent(){}; + //virtual MtEvent* clone(){ return new IEvent; } + virtual const char* getType() const = 0; + + MtEvent* checkIs(const std::string &type) + { + if(type == getType()) + return this; + return NULL; + } +}; + +// An event with no parameters and customizable name +class SimpleTriggerEvent: public MtEvent +{ + const char *type; +public: + SimpleTriggerEvent(const char *type): + type(type) + {} + const char* getType() const + {return type;} +}; + +class MtEventReceiver +{ +public: + virtual ~MtEventReceiver(){}; + virtual void onEvent(MtEvent *e) = 0; +}; + +typedef void (*event_receive_func)(MtEvent *e, void *data); + +class MtEventManager +{ +public: + virtual ~MtEventManager(){}; + virtual void put(MtEvent *e) = 0; + virtual void reg(const char *type, event_receive_func f, void *data) = 0; + // If data==NULL, every occurence of f is deregistered. + virtual void dereg(const char *type, event_receive_func f, void *data) = 0; + virtual void reg(MtEventReceiver *r, const char *type) = 0; + virtual void dereg(MtEventReceiver *r, const char *type) = 0; +}; + +#endif + diff --git a/src/event_manager.h b/src/event_manager.h new file mode 100644 index 00000000..b33d5a29 --- /dev/null +++ b/src/event_manager.h @@ -0,0 +1,115 @@ +/* +Minetest-c55 +Copyright (C) 2012 celeron55, Perttu Ahola + +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 2 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, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef EVENT_MANAGER_HEADER +#define EVENT_MANAGER_HEADER + +#include "event.h" +#include +#include + +class EventManager: public MtEventManager +{ + static void receiverReceive(MtEvent *e, void *data) + { + MtEventReceiver *r = (MtEventReceiver*)data; + r->onEvent(e); + } + struct FuncSpec{ + event_receive_func f; + void *d; + FuncSpec(event_receive_func f, void *d): + f(f), d(d) + {} + }; + struct Dest{ + std::list funcs; + }; + std::map m_dest; + +public: + ~EventManager() + { + } + void put(MtEvent *e) + { + std::map::iterator i = m_dest.find(e->getType()); + if(i != m_dest.end()){ + std::list &funcs = i->second.funcs; + for(std::list::iterator i = funcs.begin(); + i != funcs.end(); i++){ + (*(i->f))(e, i->d); + } + } + delete e; + } + void reg(const char *type, event_receive_func f, void *data) + { + std::map::iterator i = m_dest.find(type); + if(i != m_dest.end()){ + i->second.funcs.push_back(FuncSpec(f, data)); + } else{ + std::list funcs; + Dest dest; + dest.funcs.push_back(FuncSpec(f, data)); + m_dest[type] = dest; + } + } + void dereg(const char *type, event_receive_func f, void *data) + { + if(type != NULL){ + std::map::iterator i = m_dest.find(type); + if(i != m_dest.end()){ + std::list &funcs = i->second.funcs; + std::list::iterator i = funcs.begin(); + while(i != funcs.end()){ + bool remove = (i->f == f && (!data || i->d == data)); + if(remove) + funcs.erase(i++); + else + i++; + } + } + } else{ + for(std::map::iterator + i = m_dest.begin(); i != m_dest.end(); i++){ + std::list &funcs = i->second.funcs; + std::list::iterator i = funcs.begin(); + while(i != funcs.end()){ + bool remove = (i->f == f && (!data || i->d == data)); + if(remove) + funcs.erase(i++); + else + i++; + } + } + } + } + void reg(MtEventReceiver *r, const char *type) + { + reg(type, EventManager::receiverReceive, r); + } + void dereg(MtEventReceiver *r, const char *type) + { + dereg(type, EventManager::receiverReceive, r); + } +}; + +#endif + diff --git a/src/game.cpp b/src/game.cpp index 1b24df59..4c9442c0 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -59,6 +59,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_AUDIO #include "sound_openal.h" #endif +#include "event_manager.h" #include /* @@ -779,6 +780,58 @@ public: } }; +class SoundMaker +{ +public: + ISoundManager *m_sound; + + SimpleSoundSpec m_player_step_sound; + float m_player_step_timer; + + SoundMaker(ISoundManager *sound): + m_sound(sound), + m_player_step_sound("default_grass_walk"), + m_player_step_timer(0) + { + } + + void playPlayerStep() + { + if(m_player_step_timer <= 0 && m_player_step_sound.exists()){ + m_player_step_timer = 0.03; + m_sound->playSound(m_player_step_sound, false); + } + } + + static void viewBobbingStep(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker*)data; + sm->playPlayerStep(); + } + + static void playerRegainGround(MtEvent *e, void *data) + { + SoundMaker *sm = (SoundMaker*)data; + sm->playPlayerStep(); + } + + static void playerJump(MtEvent *e, void *data) + { + } + + void registerReceiver(MtEventManager *mgr) + { + mgr->reg("ViewBobbingStep", SoundMaker::viewBobbingStep, this); + mgr->reg("PlayerRegainGround", SoundMaker::playerRegainGround, this); + mgr->reg("PlayerJump", SoundMaker::playerJump, this); + } + + void step(float dtime) + { + m_player_step_timer -= dtime; + } +}; + void the_game( bool &kill, bool random_input, @@ -841,10 +894,19 @@ void the_game( sound = &dummySoundManager; sound_is_dummy = true; } + + // Event manager + EventManager eventmgr; + + // Sound maker + SoundMaker soundmaker(sound); + soundmaker.registerReceiver(&eventmgr); // Test sounds - sound->loadSound("default_grass_walk", porting::path_share + DIR_DELIM + sound->loadSound("default_grass_footstep", porting::path_share + DIR_DELIM + "sounds" + DIR_DELIM + "default_grass_walk3_mono.ogg"); + sound->loadSound("default_grass_footstep", porting::path_share + DIR_DELIM + + "sounds" + DIR_DELIM + "default_grass_walk4_mono.ogg"); //sound->playSound("default_grass_walk", false, 1.0); //sound->playSoundAt("default_grass_walk", true, 1.0, v3f(0,10,0)*BS); @@ -879,7 +941,7 @@ void the_game( MapDrawControl draw_control; Client client(device, playername.c_str(), password, draw_control, - tsrc, itemdef, nodedef, sound); + tsrc, itemdef, nodedef, sound, &eventmgr); // Client acts as our GameDef IGameDef *gamedef = &client; @@ -1257,7 +1319,7 @@ void the_game( if(object_hit_delay_timer >= 0) object_hit_delay_timer -= dtime; time_from_last_punch += dtime; - + g_profiler->add("Elapsed time", dtime); g_profiler->avg("FPS", 1./dtime); @@ -1954,6 +2016,17 @@ void the_game( camera.getCameraNode()->getTarget(), camera.getCameraNode()->getUpVector()); + /* + Update sound maker + */ + { + soundmaker.step(dtime); + + ClientMap &map = client.getEnv().getClientMap(); + MapNode n = map.getNodeNoEx(player->getStandingNodePos()); + soundmaker.m_player_step_sound = nodedef->get(n).sound_footstep; + } + /* Calculate what block is the crosshair pointing to */ diff --git a/src/gamedef.h b/src/gamedef.h index 91bcc0e6..3b5ad5ca 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -28,6 +28,7 @@ class INodeDefManager; class ICraftDefManager; class ITextureSource; class ISoundManager; +class MtEventManager; /* An interface for fetching game-global definitions like tool and @@ -47,17 +48,19 @@ public: // pointers in other threads than main thread will make things explode. virtual ITextureSource* getTextureSource()=0; - virtual ISoundManager* getSoundManager()=0; - // Used for keeping track of names/ids of unknown nodes virtual u16 allocateUnknownNodeId(const std::string &name)=0; + virtual ISoundManager* getSoundManager()=0; + virtual MtEventManager* getEventManager()=0; + // Shorthands IItemDefManager* idef(){return getItemDefManager();} INodeDefManager* ndef(){return getNodeDefManager();} ICraftDefManager* cdef(){return getCraftDefManager();} ITextureSource* tsrc(){return getTextureSource();} ISoundManager* sound(){return getSoundManager();} + MtEventManager* event(){return getEventManager();} }; #endif diff --git a/src/nodedef.cpp b/src/nodedef.cpp index f1946830..2a21136b 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -78,6 +78,22 @@ void MaterialSpec::deSerialize(std::istream &is) backface_culling = readU8(is); } +/* + SimpleSoundSpec serialization +*/ + +static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss, + std::ostream &os) +{ + os< #include #include @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tile.h" #endif #include "itemgroup.h" +#include "sound.h" // SimpleSoundSpec class IItemDefManager; class ITextureSource; class IGameDef; @@ -200,6 +201,9 @@ struct ContentFeatures // Set to true if wall_mounted used to be set to true bool legacy_wallmounted; + // Sound properties + SimpleSoundSpec sound_footstep; + /* Methods */ diff --git a/src/player.cpp b/src/player.cpp index 48d2c290..0d4a1cb6 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "collision.h" #include "environment.h" #include "gamedef.h" +#include "event.h" Player::Player(IGameDef *gamedef): touching_ground(false), @@ -367,6 +368,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Player is allowed to jump when this is true. */ + bool touching_ground_was = touching_ground; touching_ground = false; /*std::cout<<"Checking collisions for (" @@ -608,6 +610,11 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, collision_info->push_back(info); } } + + if(!touching_ground_was && touching_ground){ + MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); + m_gamedef->event()->put(e); + } } void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) @@ -723,6 +730,9 @@ void LocalPlayer::applyControl(float dtime) { speed.Y = 6.5*BS; setSpeed(speed); + + MtEvent *e = new SimpleTriggerEvent("PlayerJump"); + m_gamedef->event()->put(e); } } // Use the oscillating value for getting out of water diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 27ab2950..79da15c4 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -807,6 +807,25 @@ static void push_pointed_thing(lua_State *L, const PointedThing& pointed) } } +/* + SimpleSoundSpec +*/ + +static SimpleSoundSpec read_soundspec(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + SimpleSoundSpec spec; + if(lua_isnil(L, index)){ + } else if(lua_istable(L, index)){ + getstringfield(L, index, "name", spec.name); + getfloatfield(L, index, "gain", spec.gain); + } else if(lua_isstring(L, index)){ + spec.name = lua_tostring(L, index); + } + return spec; +} + /* ItemDefinition */ @@ -1038,6 +1057,15 @@ static ContentFeatures read_content_features(lua_State *L, int index) getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple); // Set to true if wall_mounted used to be set to true getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted); + + // Sound table + lua_getfield(L, index, "sounds"); + if(lua_istable(L, -1)){ + lua_getfield(L, -1, "footstep"); + f.sound_footstep = read_soundspec(L, -1); + lua_pop(L, 1); + } + lua_pop(L, 1); return f; } diff --git a/src/server.cpp b/src/server.cpp index d762f268..e781f128 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tool.h" #include "utility_string.h" #include "sound.h" // dummySoundManager +#include "event_manager.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -853,6 +854,7 @@ Server::Server( m_itemdef(createItemDefManager()), m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), + m_event(new EventManager()), m_thread(this), m_emergethread(this), m_time_of_day_send_timer(0), @@ -1064,10 +1066,10 @@ Server::~Server() delete i.getNode()->getValue(); } } - - // Delete Environment + + // Delete things in the reverse order of creation delete m_env; - + delete m_event; delete m_itemdef; delete m_nodedef; delete m_craftdef; @@ -4275,6 +4277,10 @@ ISoundManager* Server::getSoundManager() { return &dummySoundManager; } +MtEventManager* Server::getEventManager() +{ + return m_event; +} IWritableItemDefManager* Server::getWritableItemDefManager() { diff --git a/src/server.h b/src/server.h index cdedd06d..3baeb433 100644 --- a/src/server.h +++ b/src/server.h @@ -40,6 +40,7 @@ typedef struct lua_State lua_State; class IWritableItemDefManager; class IWritableNodeDefManager; class IWritableCraftDefManager; +class EventManager; class ServerError : public std::exception { @@ -514,6 +515,7 @@ public: virtual ITextureSource* getTextureSource(); virtual u16 allocateUnknownNodeId(const std::string &name); virtual ISoundManager* getSoundManager(); + virtual MtEventManager* getEventManager(); IWritableItemDefManager* getWritableItemDefManager(); IWritableNodeDefManager* getWritableNodeDefManager(); @@ -684,6 +686,9 @@ private: // Craft definition manager IWritableCraftDefManager *m_craftdef; + // Event manager + EventManager *m_event; + // Mods core::list m_mods; diff --git a/src/sound.h b/src/sound.h index 72764345..6363d614 100644 --- a/src/sound.h +++ b/src/sound.h @@ -33,6 +33,18 @@ public: std::set > &dst_datas) = 0; }; +struct SimpleSoundSpec +{ + std::string name; + float gain; + SimpleSoundSpec(std::string name="", float gain=1.0): + name(name), + gain(gain) + {} + bool exists() {return name != "";} + // Serialization intentionally left out +}; + class ISoundManager { public: @@ -47,6 +59,7 @@ public: const std::vector &filedata) = 0; virtual void updateListener(v3f pos, v3f vel, v3f at, v3f up) = 0; + // playSound functions return -1 on failure, otherwise a handle to the // sound virtual int playSound(const std::string &name, bool loop, @@ -54,6 +67,11 @@ public: virtual int playSoundAt(const std::string &name, bool loop, float volume, v3f pos) = 0; virtual void stopSound(int sound) = 0; + + int playSound(const SimpleSoundSpec &spec, bool loop) + { return playSound(spec.name, loop, spec.gain); } + int playSoundAt(const SimpleSoundSpec &spec, bool loop, v3f pos) + { return playSoundAt(spec.name, loop, spec.gain, pos); } }; class DummySoundManager: public ISoundManager -- 2.30.2