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", {
#include "noise.h" // easeCurve
#include "gamedef.h"
#include "sound.h"
+#include "event.h"
Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
IGameDef *gamedef):
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);
+ }
}
}
{
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);
}
}
}
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,
{
return m_sound;
}
+MtEventManager* Client::getEventManager()
+{
+ return m_event;
+}
//class IWritableCraftDefManager;
class ClientEnvironment;
struct MapDrawControl;
+class MtEventManager;
class ClientNotReadyException : public BaseException
{
IWritableTextureSource *tsrc,
IWritableItemDefManager *itemdef,
IWritableNodeDefManager *nodedef,
- ISoundManager *sound
+ ISoundManager *sound,
+ MtEventManager *event
);
~Client();
virtual ITextureSource* getTextureSource();
virtual u16 allocateUnknownNodeId(const std::string &name);
virtual ISoundManager* getSoundManager();
+ virtual MtEventManager* getEventManager();
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;
--- /dev/null
+/*
+Minetest-c55
+Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+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
+
--- /dev/null
+/*
+Minetest-c55
+Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+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 <list>
+#include <map>
+
+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<FuncSpec> funcs;
+ };
+ std::map<std::string, Dest> m_dest;
+
+public:
+ ~EventManager()
+ {
+ }
+ void put(MtEvent *e)
+ {
+ std::map<std::string, Dest>::iterator i = m_dest.find(e->getType());
+ if(i != m_dest.end()){
+ std::list<FuncSpec> &funcs = i->second.funcs;
+ for(std::list<FuncSpec>::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<std::string, Dest>::iterator i = m_dest.find(type);
+ if(i != m_dest.end()){
+ i->second.funcs.push_back(FuncSpec(f, data));
+ } else{
+ std::list<FuncSpec> 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<std::string, Dest>::iterator i = m_dest.find(type);
+ if(i != m_dest.end()){
+ std::list<FuncSpec> &funcs = i->second.funcs;
+ std::list<FuncSpec>::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<std::string, Dest>::iterator
+ i = m_dest.begin(); i != m_dest.end(); i++){
+ std::list<FuncSpec> &funcs = i->second.funcs;
+ std::list<FuncSpec>::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
+
#if USE_AUDIO
#include "sound_openal.h"
#endif
+#include "event_manager.h"
#include <list>
/*
}
};
+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,
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);
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;
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);
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
*/
class ICraftDefManager;
class ITextureSource;
class ISoundManager;
+class MtEventManager;
/*
An interface for fetching game-global definitions like tool and
// 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
backface_culling = readU8(is);
}
+/*
+ SimpleSoundSpec serialization
+*/
+
+static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss,
+ std::ostream &os)
+{
+ os<<serializeString(ss.name);
+ writeF1000(os, ss.gain);
+}
+static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
+{
+ ss.name = deSerializeString(is);
+ ss.gain = readF1000(is);
+}
+
/*
ContentFeatures
*/
selection_box = NodeBox();
legacy_facedir_simple = false;
legacy_wallmounted = false;
+ sound_footstep = SimpleSoundSpec();
}
void ContentFeatures::serialize(std::ostream &os)
selection_box.serialize(os);
writeU8(os, legacy_facedir_simple);
writeU8(os, legacy_wallmounted);
+ serializeSimpleSoundSpec(sound_footstep, os);
}
void ContentFeatures::deSerialize(std::istream &is)
selection_box.deSerialize(is);
legacy_facedir_simple = readU8(is);
legacy_wallmounted = readU8(is);
+ try{
+ deSerializeSimpleSoundSpec(sound_footstep, is);
+ }catch(SerializationError &e) {};
}
/*
#ifndef NODEDEF_HEADER
#define NODEDEF_HEADER
-#include "common_irrlicht.h"
+#include "irrlichttypes.h"
#include <string>
#include <iostream>
#include <map>
#include "tile.h"
#endif
#include "itemgroup.h"
+#include "sound.h" // SimpleSoundSpec
class IItemDefManager;
class ITextureSource;
class IGameDef;
// Set to true if wall_mounted used to be set to true
bool legacy_wallmounted;
+ // Sound properties
+ SimpleSoundSpec sound_footstep;
+
/*
Methods
*/
#include "collision.h"
#include "environment.h"
#include "gamedef.h"
+#include "event.h"
Player::Player(IGameDef *gamedef):
touching_ground(false),
Player is allowed to jump when this is true.
*/
+ bool touching_ground_was = touching_ground;
touching_ground = false;
/*std::cout<<"Checking collisions for ("
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)
{
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
}
}
+/*
+ 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
*/
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;
}
#include "tool.h"
#include "utility_string.h"
#include "sound.h" // dummySoundManager
+#include "event_manager.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
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),
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;
{
return &dummySoundManager;
}
+MtEventManager* Server::getEventManager()
+{
+ return m_event;
+}
IWritableItemDefManager* Server::getWritableItemDefManager()
{
class IWritableItemDefManager;
class IWritableNodeDefManager;
class IWritableCraftDefManager;
+class EventManager;
class ServerError : public std::exception
{
virtual ITextureSource* getTextureSource();
virtual u16 allocateUnknownNodeId(const std::string &name);
virtual ISoundManager* getSoundManager();
+ virtual MtEventManager* getEventManager();
IWritableItemDefManager* getWritableItemDefManager();
IWritableNodeDefManager* getWritableNodeDefManager();
// Craft definition manager
IWritableCraftDefManager *m_craftdef;
+ // Event manager
+ EventManager *m_event;
+
// Mods
core::list<ModSpec> m_mods;
std::set<std::vector<char> > &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:
const std::vector<char> &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,
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