diff --git a/data/firefly.png b/data/firefly.png
new file mode 100644
index 0000000000000000000000000000000000000000..d5a444b039f45e3bc76e692ed15b258357aa3047
Binary files /dev/null and b/data/firefly.png differ
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index dc5ac400f5da3f05f400291ade4911ab7f220866..dfeaea85ab09babf8d62f7a07b22a058046ea353 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -752,4 +752,161 @@ void Oerkki1CAO::initialize(const std::string &data)
 	updateNodePos();
 }
 
+/*
+	FireflyCAO
+*/
+
+// Prototype
+FireflyCAO proto_FireflyCAO;
+
+FireflyCAO::FireflyCAO():
+	ClientActiveObject(0),
+	m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS/2.,BS/3.),
+	m_node(NULL),
+	m_position(v3f(0,10*BS,0)),
+	m_yaw(0)
+{
+	ClientActiveObject::registerType(getType(), create);
+}
 
+FireflyCAO::~FireflyCAO()
+{
+}
+
+ClientActiveObject* FireflyCAO::create()
+{
+	return new FireflyCAO();
+}
+
+void FireflyCAO::addToScene(scene::ISceneManager *smgr)
+{
+	if(m_node != NULL)
+		return;
+	
+	video::IVideoDriver* driver = smgr->getVideoDriver();
+	
+	scene::SMesh *mesh = new scene::SMesh();
+	scene::IMeshBuffer *buf = new scene::SMeshBuffer();
+	video::SColor c(255,255,255,255);
+	video::S3DVertex vertices[4] =
+	{
+		video::S3DVertex(0,0,0, 0,0,0, c, 0,1),
+		video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
+		video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
+		video::S3DVertex(0,BS/2,0, 0,0,0, c, 0,0),
+	};
+	u16 indices[] = {0,1,2,2,3,0};
+	buf->append(vertices, 4, indices, 6);
+	// Set material
+	buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
+	buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
+	//buf->getMaterial().setTexture(0, NULL);
+	buf->getMaterial().setTexture
+			(0, driver->getTexture(getTexturePath("firefly.png").c_str()));
+	buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
+	buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
+	buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+	// Add to mesh
+	mesh->addMeshBuffer(buf);
+	buf->drop();
+	m_node = smgr->addMeshSceneNode(mesh, NULL);
+	mesh->drop();
+	// Set it to use the materials of the meshbuffers directly.
+	// This is needed for changing the texture in the future
+	m_node->setReadOnlyMaterials(true);
+	updateNodePos();
+}
+
+void FireflyCAO::removeFromScene()
+{
+	if(m_node == NULL)
+		return;
+
+	m_node->remove();
+	m_node = NULL;
+}
+
+void FireflyCAO::updateLight(u8 light_at_pos)
+{
+	if(m_node == NULL)
+		return;
+
+	u8 li = 255;
+	video::SColor color(255,li,li,li);
+
+	scene::IMesh *mesh = m_node->getMesh();
+	if(mesh == NULL)
+		return;
+	
+	u16 mc = mesh->getMeshBufferCount();
+	for(u16 j=0; j<mc; j++)
+	{
+		scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
+		video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
+		u16 vc = buf->getVertexCount();
+		for(u16 i=0; i<vc; i++)
+		{
+			vertices[i].Color = color;
+		}
+	}
+}
+
+v3s16 FireflyCAO::getLightPosition()
+{
+	return floatToInt(m_position+v3f(0,BS*0.5,0), BS);
+}
+
+void FireflyCAO::updateNodePos()
+{
+	if(m_node == NULL)
+		return;
+
+	//m_node->setPosition(m_position);
+	m_node->setPosition(pos_translator.vect_show);
+
+	v3f rot = m_node->getRotation();
+	rot.Y = 180.0 - m_yaw;
+	m_node->setRotation(rot);
+}
+
+void FireflyCAO::step(float dtime, ClientEnvironment *env)
+{
+	pos_translator.translate(dtime);
+	updateNodePos();
+}
+
+void FireflyCAO::processMessage(const std::string &data)
+{
+	//dstream<<"FireflyCAO: Got message"<<std::endl;
+	std::istringstream is(data, std::ios::binary);
+	// command
+	u8 cmd = readU8(is);
+	if(cmd == 0)
+	{
+		// pos
+		m_position = readV3F1000(is);
+		pos_translator.update(m_position);
+		// yaw
+		m_yaw = readF1000(is);
+		updateNodePos();
+	}
+}
+
+void FireflyCAO::initialize(const std::string &data)
+{
+	//dstream<<"FireflyCAO: Got init data"<<std::endl;
+	
+	{
+		std::istringstream is(data, std::ios::binary);
+		// version
+		u8 version = readU8(is);
+		// check version
+		if(version != 0)
+			return;
+		// pos
+		m_position = readV3F1000(is);
+		pos_translator.init(m_position);
+	}
+	
+	updateNodePos();
+}
diff --git a/src/content_cao.h b/src/content_cao.h
index 146e23b0c985f0db3bd7f5fa0071866bbd88e543..b984be1361cb79d98fbf016659058cec538f65dd 100644
--- a/src/content_cao.h
+++ b/src/content_cao.h
@@ -243,6 +243,48 @@ class Oerkki1CAO : public ClientActiveObject
 	bool m_damage_texture_enabled;
 };
 
+/*
+	FireflyCAO
+*/
+
+class FireflyCAO : public ClientActiveObject
+{
+public:
+	FireflyCAO();
+	virtual ~FireflyCAO();
+	
+	u8 getType() const
+	{
+		return ACTIVEOBJECT_TYPE_FIREFLY;
+	}
+	
+	static ClientActiveObject* create();
+
+	void addToScene(scene::ISceneManager *smgr);
+	void removeFromScene();
+	void updateLight(u8 light_at_pos);
+	v3s16 getLightPosition();
+	void updateNodePos();
+
+	void step(float dtime, ClientEnvironment *env);
+
+	void processMessage(const std::string &data);
+
+	void initialize(const std::string &data);
+	
+	core::aabbox3d<f32>* getSelectionBox()
+		{return &m_selection_box;}
+	v3f getPosition()
+		{return m_position;}
+
+private:
+	core::aabbox3d<f32> m_selection_box;
+	scene::IMeshSceneNode *m_node;
+	v3f m_position;
+	float m_yaw;
+	SmoothTranslator pos_translator;
+};
+
 
 #endif
 
diff --git a/src/content_inventory.cpp b/src/content_inventory.cpp
index 7d995cb5f1e7893f32a94c0a25f38a4630805913..1068defb5e8772a3289704fcef077aba8b5dfd03 100644
--- a/src/content_inventory.cpp
+++ b/src/content_inventory.cpp
@@ -65,6 +65,8 @@ std::string item_craft_get_image_name(const std::string &subname)
 		return "clay_brick.png";
 	else if(subname == "rat")
 		return "rat.png";
+	else if(subname == "firefly")
+		return "firefly.png";
 	else
 		return "cloud.png"; // just something
 }
@@ -77,13 +79,18 @@ ServerActiveObject* item_craft_create_object(const std::string &subname,
 		ServerActiveObject *obj = new RatSAO(env, id, pos);
 		return obj;
 	}
+	else if(subname == "firefly")
+	{
+		ServerActiveObject *obj = new FireflySAO(env, id, pos);
+		return obj;
+	}
 
 	return NULL;
 }
 
 s16 item_craft_get_drop_count(const std::string &subname)
 {
-	if(subname == "rat")
+	if(subname == "rat" || subname == "firefly")
 		return 1;
 
 	return -1;
diff --git a/src/content_object.h b/src/content_object.h
index ecabd8a3877ab5eaf850857564df86a5b06a3ee0..47f93d7d4ad492828609f671d139613d00f2aa02 100644
--- a/src/content_object.h
+++ b/src/content_object.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define ACTIVEOBJECT_TYPE_ITEM 2
 #define ACTIVEOBJECT_TYPE_RAT 3
 #define ACTIVEOBJECT_TYPE_OERKKI1 4
+#define ACTIVEOBJECT_TYPE_FIREFLY 5
 
 #endif
 
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index c41f4ed784297e0aaf08efb5ac6dc1f0bba0a07c..aeeafc2f88d649cecd6e8825a22d007a36d11098 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -693,4 +693,179 @@ void Oerkki1SAO::doDamage(u16 d)
 	}
 }
 
+/*
+	FireflySAO
+*/
+
+// Prototype
+FireflySAO proto_FireflySAO(NULL, 0, v3f(0,0,0));
+
+FireflySAO::FireflySAO(ServerEnvironment *env, u16 id, v3f pos):
+	ServerActiveObject(env, id, pos),
+	m_is_active(false),
+	m_speed_f(0,0,0)
+{
+	ServerActiveObject::registerType(getType(), create);
+
+	m_oldpos = v3f(0,0,0);
+	m_last_sent_position = v3f(0,0,0);
+	m_yaw = 0;
+	m_counter1 = 0;
+	m_counter2 = 0;
+	m_age = 0;
+	m_touching_ground = false;
+}
+
+ServerActiveObject* FireflySAO::create(ServerEnvironment *env, u16 id, v3f pos,
+		const std::string &data)
+{
+	std::istringstream is(data, std::ios::binary);
+	char buf[1];
+	// read version
+	is.read(buf, 1);
+	u8 version = buf[0];
+	// check if version is supported
+	if(version != 0)
+		return NULL;
+	return new FireflySAO(env, id, pos);
+}
+
+void FireflySAO::step(float dtime, bool send_recommended)
+{
+	assert(m_env);
+
+	if(m_is_active == false)
+	{
+		if(m_inactive_interval.step(dtime, 0.5)==false)
+			return;
+	}
+
+	/*
+		The AI
+	*/
+
+	// Apply (less) gravity
+	m_speed_f.Y -= dtime*3*BS;
+
+	/*
+		Move around if some player is close
+	*/
+	bool player_is_close = false;
+	// Check connected players
+	core::list<Player*> players = m_env->getPlayers(true);
+	core::list<Player*>::Iterator i;
+	for(i = players.begin();
+			i != players.end(); i++)
+	{
+		Player *player = *i;
+		v3f playerpos = player->getPosition();
+		if(m_base_position.getDistanceFrom(playerpos) < BS*10.0)
+		{
+			player_is_close = true;
+			break;
+		}
+	}
+
+	m_is_active = player_is_close;
+	
+	if(player_is_close == false)
+	{
+		m_speed_f.X = 0;
+		m_speed_f.Z = 0;
+	}
+	else
+	{
+		// Move around
+		v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
+		f32 speed = BS/2;
+		m_speed_f.X = speed * dir.X;
+		m_speed_f.Z = speed * dir.Z;
+
+		if(m_touching_ground && (m_oldpos - m_base_position).getLength()
+				< dtime*speed/2)
+		{
+			m_counter1 -= dtime;
+			if(m_counter1 < 0.0)
+			{
+				m_counter1 += 1.0;
+				m_speed_f.Y = 5.0*BS;
+			}
+		}
+
+		{
+			m_counter2 -= dtime;
+			if(m_counter2 < 0.0)
+			{
+				m_counter2 += (float)(myrand()%100)/100*3.0;
+				m_yaw += ((float)(myrand()%200)-100)/100*180;
+				m_yaw = wrapDegrees(m_yaw);
+			}
+		}
+	}
+	
+	m_oldpos = m_base_position;
 
+	/*
+		Move it, with collision detection
+	*/
+
+	core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
+	collisionMoveResult moveresult;
+	// Maximum movement without glitches
+	f32 pos_max_d = BS*0.25;
+	// Limit speed
+	if(m_speed_f.getLength()*dtime > pos_max_d)
+		m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
+	v3f pos_f = getBasePosition();
+	v3f pos_f_old = pos_f;
+	moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d,
+			box, dtime, pos_f, m_speed_f);
+	m_touching_ground = moveresult.touching_ground;
+	
+	setBasePosition(pos_f);
+
+	if(send_recommended == false)
+		return;
+
+	if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
+	{
+		m_last_sent_position = pos_f;
+
+		std::ostringstream os(std::ios::binary);
+		// command (0 = update position)
+		writeU8(os, 0);
+		// pos
+		writeV3F1000(os, m_base_position);
+		// yaw
+		writeF1000(os, m_yaw);
+		// create message and add to list
+		ActiveObjectMessage aom(getId(), false, os.str());
+		m_messages_out.push_back(aom);
+	}
+}
+
+std::string FireflySAO::getClientInitializationData()
+{
+	std::ostringstream os(std::ios::binary);
+	// version
+	writeU8(os, 0);
+	// pos
+	writeV3F1000(os, m_base_position);
+	return os.str();
+}
+
+std::string FireflySAO::getStaticData()
+{
+	//dstream<<__FUNCTION_NAME<<std::endl;
+	std::ostringstream os(std::ios::binary);
+	// version
+	writeU8(os, 0);
+	return os.str();
+}
+
+InventoryItem* FireflySAO::createPickedUpItem()
+{
+	std::istringstream is("CraftItem firefly 1", std::ios_base::binary);
+	InventoryItem *item = InventoryItem::deSerialize(is);
+	return item;
+}
diff --git a/src/content_sao.h b/src/content_sao.h
index 030232a9ee05317fc8e0173d1a58984968645041..e5b1223d466c68c54bf221d6ceff5c1beb2dde85 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -113,6 +113,30 @@ class Oerkki1SAO : public ServerActiveObject
 	float m_after_jump_timer;
 };
 
+class FireflySAO : public ServerActiveObject
+{
+public:
+	FireflySAO(ServerEnvironment *env, u16 id, v3f pos);
+	u8 getType() const
+		{return ACTIVEOBJECT_TYPE_FIREFLY;}
+	static ServerActiveObject* create(ServerEnvironment *env, u16 id, v3f pos,
+			const std::string &data);
+	void step(float dtime, bool send_recommended);
+	std::string getClientInitializationData();
+	std::string getStaticData();
+	InventoryItem* createPickedUpItem();
+private:
+	bool m_is_active;
+	IntervalLimiter m_inactive_interval;
+	v3f m_speed_f;
+	v3f m_oldpos;
+	v3f m_last_sent_position;
+	float m_yaw;
+	float m_counter1;
+	float m_counter2;
+	float m_age;
+	bool m_touching_ground;
+};
 
 #endif
 
diff --git a/src/environment.cpp b/src/environment.cpp
index d55aa38d172bc96eda77f07c0e217687afa10778..df41dc63f24fc40feb39300b772853bd5799ce49 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -976,7 +976,8 @@ void ServerEnvironment::step(float dtime)
 		//TestSAO *obj = new TestSAO(this, 0, pos);
 		//ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
 		//ServerActiveObject *obj = new RatSAO(this, 0, pos);
-		ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
+		//ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
+		ServerActiveObject *obj = new FireflySAO(this, 0, pos);
 		addActiveObject(obj);
 	}
 #endif