Add event manager and use it to trigger sounds

master
Perttu Ahola 2012-03-23 20:23:03 +02:00
parent e53794868e
commit 6c14025b2d
15 changed files with 388 additions and 14 deletions

View File

@ -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", {

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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;

72
src/event.h Normal file
View File

@ -0,0 +1,72 @@
/*
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

115
src/event_manager.h Normal file
View File

@ -0,0 +1,115 @@
/*
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

View File

@ -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 <list>
/*
@ -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
*/

View File

@ -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

View File

@ -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<<serializeString(ss.name);
writeF1000(os, ss.gain);
}
static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is)
{
ss.name = deSerializeString(is);
ss.gain = readF1000(is);
}
/*
ContentFeatures
*/
@ -139,6 +155,7 @@ void ContentFeatures::reset()
selection_box = NodeBox();
legacy_facedir_simple = false;
legacy_wallmounted = false;
sound_footstep = SimpleSoundSpec();
}
void ContentFeatures::serialize(std::ostream &os)
@ -185,6 +202,7 @@ 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)
@ -236,6 +254,9 @@ 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) {};
}
/*

View File

@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef NODEDEF_HEADER
#define NODEDEF_HEADER
#include "common_irrlicht.h"
#include "irrlichttypes.h"
#include <string>
#include <iostream>
#include <map>
@ -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
*/

View File

@ -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

View File

@ -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;
}

View File

@ -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()
{

View File

@ -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<ModSpec> m_mods;

View File

@ -33,6 +33,18 @@ public:
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:
@ -47,6 +59,7 @@ 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,
@ -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