diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua
index f040011f842f102dc72c2ffbc976c82c94365782..a19b4eaac08c2901ee8f2c84539634842361c0e5 100644
--- a/game/engines/default/engine/utils.lua
+++ b/game/engines/default/engine/utils.lua
@@ -696,6 +696,17 @@ setmetatable(tstring, {
 })
 
 
+dir_to_angle = {
+	[1] = 225,
+	[2] = 270,
+	[3] = 315,
+	[4] = 180,
+	[5] = 0,
+	[6] = 0,
+	[7] = 135,
+	[8] = 90,
+	[9] = 45,
+}
 dir_to_coord = {
 	[1] = {-1, 1},
 	[2] = { 0, 1},
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index e37c3a4452bdf96300971eec00d0472d8ef82b1f..f3ca821080ee62fb245a247eb49e6417bca719e7 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -658,7 +658,7 @@ function _M:setupCommands()
 				a.faction = "enemies"
 				self.zone:addEntity(self.level, a, "actor", self.player.x+1, self.player.y)
 --]]
-				game.level.map:particleEmitter(self.player.x, self.player.y, 1, "breath_fire")
+				game.level.map:particleEmitter(self.player.x, self.player.y, 1, "breath_cold", {radius=6, tx=10, ty=2})
 			end
 		end,
 	}
diff --git a/game/modules/tome/data/gfx/particle_cloud.png b/game/modules/tome/data/gfx/particle_cloud.png
new file mode 100644
index 0000000000000000000000000000000000000000..061ff9c7e6ba080a6a7eca53c45b03123b4c6b91
Binary files /dev/null and b/game/modules/tome/data/gfx/particle_cloud.png differ
diff --git a/game/modules/tome/data/gfx/particles/breath_acid.lua b/game/modules/tome/data/gfx/particles/breath_acid.lua
new file mode 100644
index 0000000000000000000000000000000000000000..320567da9f0e9de30d29f88966de49f8c5f8f9ed
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/breath_acid.lua
@@ -0,0 +1,76 @@
+-- ToME - Tales of Middle-Earth
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- 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 <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+local nb = 12
+local dir
+local spread = spread or 55/2
+local radius = radius or 6
+
+local max = math.max(math.abs(tx), math.abs(ty))
+tx = tx / max
+ty = ty / max
+if tx >= 0.5 then tx = 1 elseif tx <= -0.5 then tx = -1 else tx = 0 end
+if ty >= 0.5 then ty = -1 elseif ty <= -0.5 then ty = 1 else ty = 0 end -- Why the hell is ty inverted ? .. but it works :/
+
+if tx == 1 and ty == 0 then dir = 0
+elseif tx ==  1 and ty == -1 then dir = 45
+elseif tx ==  0 and ty == -1 then dir = 90
+elseif tx == -1 and ty == -1 then dir = 135
+elseif tx == -1 and ty ==  0 then dir = 180
+elseif tx == -1 and ty ==  1 then dir = 225
+elseif tx ==  0 and ty ==  1 then dir = 270
+elseif tx ==  1 and ty ==  1 then dir = 315
+end
+
+return { generator = function()
+	local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+	local ad = rng.float(dir - spread, dir + spread)
+	local a = math.rad(ad)
+	local r = 0
+	local x = r * math.cos(a)
+	local y = r * math.sin(a)
+	local static = rng.percent(40)
+	local vel = sradius * ((24 - nb * 1.4) / 24) / 12
+
+	return {
+		trail = 1,
+		life = 12,
+		size = 12 - (12 - nb) * 0.7, sizev = 0, sizea = 0,
+
+		x = x, xv = 0, xa = 0,
+		y = y, yv = 0, ya = 0,
+		dir = a, dirv = 0, dira = 0,
+		vel = rng.float(vel * 0.6, vel * 1.2), velv = 0, vela = 0,
+
+		r = rng.range(0, 0)/255,   rv = 0, ra = 0,
+		g = rng.range(80, 200)/255,   gv = 0.005, ga = 0.0005,
+		b = rng.range(0, 0)/255,      bv = 0, ba = 0,
+		a = rng.range(255, 255)/255,    av = static and -0.034 or 0, aa = 0.005,
+	}
+end, },
+function(self)
+	if nb > 0 then
+		local i = math.min(nb, 6)
+		i = (i * i) * radius
+		self.ps:emit(i)
+		nb = nb - 1
+	end
+end,
+30*radius*7*12,
+"particle_cloud"
diff --git a/game/modules/tome/data/gfx/particles/breath_cold.lua b/game/modules/tome/data/gfx/particles/breath_cold.lua
new file mode 100644
index 0000000000000000000000000000000000000000..62724c083e9b1467a2fd8cf3edd5f775498de32c
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/breath_cold.lua
@@ -0,0 +1,76 @@
+-- ToME - Tales of Middle-Earth
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- 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 <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+local nb = 12
+local dir
+local spread = spread or 55/2
+local radius = radius or 6
+
+local max = math.max(math.abs(tx), math.abs(ty))
+tx = tx / max
+ty = ty / max
+if tx >= 0.5 then tx = 1 elseif tx <= -0.5 then tx = -1 else tx = 0 end
+if ty >= 0.5 then ty = -1 elseif ty <= -0.5 then ty = 1 else ty = 0 end -- Why the hell is ty inverted ? .. but it works :/
+
+if tx == 1 and ty == 0 then dir = 0
+elseif tx ==  1 and ty == -1 then dir = 45
+elseif tx ==  0 and ty == -1 then dir = 90
+elseif tx == -1 and ty == -1 then dir = 135
+elseif tx == -1 and ty ==  0 then dir = 180
+elseif tx == -1 and ty ==  1 then dir = 225
+elseif tx ==  0 and ty ==  1 then dir = 270
+elseif tx ==  1 and ty ==  1 then dir = 315
+end
+
+return { generator = function()
+	local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+	local ad = rng.float(dir - spread, dir + spread)
+	local a = math.rad(ad)
+	local r = 0
+	local x = r * math.cos(a)
+	local y = r * math.sin(a)
+	local static = rng.percent(40)
+	local vel = sradius * ((24 - nb * 1.4) / 24) / 12
+
+	return {
+		trail = 1,
+		life = 12,
+		size = 12 - (12 - nb) * 0.7, sizev = 0, sizea = 0,
+
+		x = x, xv = 0, xa = 0,
+		y = y, yv = 0, ya = 0,
+		dir = a, dirv = 0, dira = 0,
+		vel = rng.float(vel * 0.6, vel * 1.2), velv = 0, vela = 0,
+
+		r = 0,   rv = 0, ra = 0,
+		g = rng.range(170, 210)/255,   gv = 0, ga = 0,
+		b = rng.range(200, 255)/255,   gv = 0, ga = 0,
+		a = rng.range(230, 225)/255,   av = 0, aa = 0,
+	}
+end, },
+function(self)
+	if nb > 0 then
+		local i = math.min(nb, 6)
+		i = (i * i) * radius
+		self.ps:emit(i)
+		nb = nb - 1
+	end
+end,
+30*radius*7*12,
+"particle_cloud"
diff --git a/game/modules/tome/data/gfx/particles/breath_earth.lua b/game/modules/tome/data/gfx/particles/breath_earth.lua
new file mode 100644
index 0000000000000000000000000000000000000000..ff020def46f6bb4dfc4426e24f738af568a0b0cb
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/breath_earth.lua
@@ -0,0 +1,76 @@
+-- ToME - Tales of Middle-Earth
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- 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 <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+local nb = 12
+local dir
+local spread = spread or 55/2
+local radius = radius or 6
+
+local max = math.max(math.abs(tx), math.abs(ty))
+tx = tx / max
+ty = ty / max
+if tx >= 0.5 then tx = 1 elseif tx <= -0.5 then tx = -1 else tx = 0 end
+if ty >= 0.5 then ty = -1 elseif ty <= -0.5 then ty = 1 else ty = 0 end -- Why the hell is ty inverted ? .. but it works :/
+
+if tx == 1 and ty == 0 then dir = 0
+elseif tx ==  1 and ty == -1 then dir = 45
+elseif tx ==  0 and ty == -1 then dir = 90
+elseif tx == -1 and ty == -1 then dir = 135
+elseif tx == -1 and ty ==  0 then dir = 180
+elseif tx == -1 and ty ==  1 then dir = 225
+elseif tx ==  0 and ty ==  1 then dir = 270
+elseif tx ==  1 and ty ==  1 then dir = 315
+end
+
+return { generator = function()
+	local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+	local ad = rng.float(dir - spread, dir + spread)
+	local a = math.rad(ad)
+	local r = 0
+	local x = r * math.cos(a)
+	local y = r * math.sin(a)
+	local static = rng.percent(40)
+	local vel = sradius * ((24 - nb * 1.4) / 24) / 12
+
+	return {
+		trail = 1,
+		life = 12,
+		size = 12 - (12 - nb) * 0.7, sizev = 0, sizea = 0,
+
+		x = x, xv = 0, xa = 0,
+		y = y, yv = 0, ya = 0,
+		dir = a, dirv = 0, dira = 0,
+		vel = rng.float(vel * 0.6, vel * 1.2), velv = 0, vela = 0,
+
+		r = rng.range(200, 230)/255,   rv = 0, ra = 0,
+		g = rng.range(130, 160)/255,   gv = 0.005, ga = 0.0005,
+		b = rng.range(50, 70)/255,      bv = 0, ba = 0,
+		a = rng.range(255, 255)/255,    av = static and -0.034 or 0, aa = 0.005,
+	}
+end, },
+function(self)
+	if nb > 0 then
+		local i = math.min(nb, 6)
+		i = (i * i) * radius
+		self.ps:emit(i)
+		nb = nb - 1
+	end
+end,
+30*radius*7*12,
+"particle_cloud"
diff --git a/game/modules/tome/data/gfx/particles/breath_fire.lua b/game/modules/tome/data/gfx/particles/breath_fire.lua
index 10b39656b6aae6fa7fd7439a8ce855bf91c600ff..d688e7ceab507c2d2a84e288217d020b0e78b272 100644
--- a/game/modules/tome/data/gfx/particles/breath_fire.lua
+++ b/game/modules/tome/data/gfx/particles/breath_fire.lua
@@ -17,34 +17,60 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
-local nb = 0
+local nb = 12
+local dir
+local spread = spread or 55/2
+local radius = radius or 6
 
-return { gas = {w=60, h=60},
-generator = function()
-	if nb < 10 then
-		nb = nb + 1
-		return {
---[[
-			{ sx = 3, sy = 29, dx = 3, dy = -3 },
-			{ sx = 3, sy = 30, dx = 3, dy = 0 },
-			{ sx = 3, sy = 31, dx = 3, dy = 3 },
+local max = math.max(math.abs(tx), math.abs(ty))
+tx = tx / max
+ty = ty / max
+if tx >= 0.5 then tx = 1 elseif tx <= -0.5 then tx = -1 else tx = 0 end
+if ty >= 0.5 then ty = -1 elseif ty <= -0.5 then ty = 1 else ty = 0 end -- Why the hell is ty inverted ? .. but it works :/
 
-			{ sx = 4, sy = 29, dx = 3, dy = -3 },
-			{ sx = 4, sy = 30, dx = 3, dy = 0 },
-			{ sx = 4, sy = 31, dx = 3, dy = 3 },
-]]
-			{ sx = 29, sy = 29, dx = -3, dy = -3 },
-			{ sx = 31, sy = 29, dx =  3, dy = -3 },
-			{ sx = 29, sy = 31, dx = -3, dy =  3 },
-			{ sx = 31, sy = 31, dx =  3, dy =  3 },
+if tx == 1 and ty == 0 then dir = 0
+elseif tx ==  1 and ty == -1 then dir = 45
+elseif tx ==  0 and ty == -1 then dir = 90
+elseif tx == -1 and ty == -1 then dir = 135
+elseif tx == -1 and ty ==  0 then dir = 180
+elseif tx == -1 and ty ==  1 then dir = 225
+elseif tx ==  0 and ty ==  1 then dir = 270
+elseif tx ==  1 and ty ==  1 then dir = 315
+end
 
-			{ sx = 30, sy = 29, dx =  0, dy = -3 },
-			{ sx = 30, sy = 31, dx =  0, dy =  3 },
-			{ sx = 29, sy = 30, dx = -3, dy =  0 },
-			{ sx = 31, sy = 30, dx =  3, dy =  0 },
-		}
-	else return {}
-	end
+return { generator = function()
+	local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+	local ad = rng.float(dir - spread, dir + spread)
+	local a = math.rad(ad)
+	local r = 0
+	local x = r * math.cos(a)
+	local y = r * math.sin(a)
+	local static = rng.percent(40)
+	local vel = sradius * ((24 - nb * 1.4) / 24) / 12
+
+	return {
+		trail = 1,
+		life = 12,
+		size = 12 - (12 - nb) * 0.7, sizev = 0, sizea = 0,
+
+		x = x, xv = 0, xa = 0,
+		y = y, yv = 0, ya = 0,
+		dir = a, dirv = 0, dira = 0,
+		vel = rng.float(vel * 0.6, vel * 1.2), velv = 0, vela = 0,
+
+		r = rng.range(200, 255)/255,   rv = 0, ra = 0,
+		g = rng.range(120, 170)/255,   gv = 0.005, ga = 0.0005,
+		b = rng.range(0, 10)/255,      bv = 0, ba = 0,
+		a = rng.range(25, 220)/255,    av = static and -0.034 or 0, aa = 0.005,
+	}
 end, },
-function(self)end,
-30*6
+function(self)
+	if nb > 0 then
+		local i = math.min(nb, 6)
+		i = (i * i) * radius
+		self.ps:emit(i)
+		nb = nb - 1
+	end
+end,
+30*radius*7*12,
+"particle_cloud"
diff --git a/game/modules/tome/data/gfx/particles/breath_fire_navier.lua b/game/modules/tome/data/gfx/particles/breath_fire_navier.lua
new file mode 100644
index 0000000000000000000000000000000000000000..10b39656b6aae6fa7fd7439a8ce855bf91c600ff
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/breath_fire_navier.lua
@@ -0,0 +1,50 @@
+-- ToME - Tales of Middle-Earth
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- 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 <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+local nb = 0
+
+return { gas = {w=60, h=60},
+generator = function()
+	if nb < 10 then
+		nb = nb + 1
+		return {
+--[[
+			{ sx = 3, sy = 29, dx = 3, dy = -3 },
+			{ sx = 3, sy = 30, dx = 3, dy = 0 },
+			{ sx = 3, sy = 31, dx = 3, dy = 3 },
+
+			{ sx = 4, sy = 29, dx = 3, dy = -3 },
+			{ sx = 4, sy = 30, dx = 3, dy = 0 },
+			{ sx = 4, sy = 31, dx = 3, dy = 3 },
+]]
+			{ sx = 29, sy = 29, dx = -3, dy = -3 },
+			{ sx = 31, sy = 29, dx =  3, dy = -3 },
+			{ sx = 29, sy = 31, dx = -3, dy =  3 },
+			{ sx = 31, sy = 31, dx =  3, dy =  3 },
+
+			{ sx = 30, sy = 29, dx =  0, dy = -3 },
+			{ sx = 30, sy = 31, dx =  0, dy =  3 },
+			{ sx = 29, sy = 30, dx = -3, dy =  0 },
+			{ sx = 31, sy = 30, dx =  3, dy =  0 },
+		}
+	else return {}
+	end
+end, },
+function(self)end,
+30*6
diff --git a/game/modules/tome/data/gfx/particles/breath_lightning.lua b/game/modules/tome/data/gfx/particles/breath_lightning.lua
new file mode 100644
index 0000000000000000000000000000000000000000..06e81a80f8d30ed95bc23016e3311a0c6d8b6791
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/breath_lightning.lua
@@ -0,0 +1,76 @@
+-- ToME - Tales of Middle-Earth
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- 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 <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+local nb = 12
+local dir
+local spread = spread or 55/2
+local radius = radius or 6
+
+local max = math.max(math.abs(tx), math.abs(ty))
+tx = tx / max
+ty = ty / max
+if tx >= 0.5 then tx = 1 elseif tx <= -0.5 then tx = -1 else tx = 0 end
+if ty >= 0.5 then ty = -1 elseif ty <= -0.5 then ty = 1 else ty = 0 end -- Why the hell is ty inverted ? .. but it works :/
+
+if tx == 1 and ty == 0 then dir = 0
+elseif tx ==  1 and ty == -1 then dir = 45
+elseif tx ==  0 and ty == -1 then dir = 90
+elseif tx == -1 and ty == -1 then dir = 135
+elseif tx == -1 and ty ==  0 then dir = 180
+elseif tx == -1 and ty ==  1 then dir = 225
+elseif tx ==  0 and ty ==  1 then dir = 270
+elseif tx ==  1 and ty ==  1 then dir = 315
+end
+
+return { generator = function()
+	local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+	local ad = rng.float(dir - spread, dir + spread)
+	local a = math.rad(ad)
+	local r = 0
+	local x = r * math.cos(a)
+	local y = r * math.sin(a)
+	local static = rng.percent(40)
+	local vel = sradius * ((24 - nb * 1.4) / 24) / 12
+
+	return {
+		trail = 1,
+		life = 12,
+		size = 12 - (12 - nb) * 0.7, sizev = 0, sizea = 0,
+
+		x = x, xv = 0, xa = 0,
+		y = y, yv = 0, ya = 0,
+		dir = a, dirv = 0, dira = 0,
+		vel = rng.float(vel * 0.6, vel * 1.2), velv = 0, vela = 0,
+
+		r = rng.range(140, 200)/255, rv = 0, ra = 0,
+		g = rng.range(180, 220)/255, gv = 0, ga = 0,
+		b = rng.range(220, 240)/255, bv = 0, ba = 0,
+		a = rng.range(230, 255)/255, av = 0, aa = 0,
+	}
+end, },
+function(self)
+	if nb > 0 then
+		local i = math.min(nb, 6)
+		i = (i * i) * radius
+		self.ps:emit(i)
+		nb = nb - 1
+	end
+end,
+30*radius*7*12,
+"particle_cloud"
diff --git a/game/modules/tome/data/gfx/particles/breath_slime.lua b/game/modules/tome/data/gfx/particles/breath_slime.lua
new file mode 100644
index 0000000000000000000000000000000000000000..06844e7cd799ed7b228f9c32d23bf51215171c25
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/breath_slime.lua
@@ -0,0 +1,76 @@
+-- ToME - Tales of Middle-Earth
+-- Copyright (C) 2009, 2010 Nicolas Casalini
+--
+-- 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 <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+local nb = 12
+local dir
+local spread = spread or 55/2
+local radius = radius or 6
+
+local max = math.max(math.abs(tx), math.abs(ty))
+tx = tx / max
+ty = ty / max
+if tx >= 0.5 then tx = 1 elseif tx <= -0.5 then tx = -1 else tx = 0 end
+if ty >= 0.5 then ty = -1 elseif ty <= -0.5 then ty = 1 else ty = 0 end -- Why the hell is ty inverted ? .. but it works :/
+
+if tx == 1 and ty == 0 then dir = 0
+elseif tx ==  1 and ty == -1 then dir = 45
+elseif tx ==  0 and ty == -1 then dir = 90
+elseif tx == -1 and ty == -1 then dir = 135
+elseif tx == -1 and ty ==  0 then dir = 180
+elseif tx == -1 and ty ==  1 then dir = 225
+elseif tx ==  0 and ty ==  1 then dir = 270
+elseif tx ==  1 and ty ==  1 then dir = 315
+end
+
+return { generator = function()
+	local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+	local ad = rng.float(dir - spread, dir + spread)
+	local a = math.rad(ad)
+	local r = 0
+	local x = r * math.cos(a)
+	local y = r * math.sin(a)
+	local static = rng.percent(40)
+	local vel = sradius * ((24 - nb * 1.4) / 24) / 12
+
+	return {
+		trail = 1,
+		life = 12,
+		size = 12 - (12 - nb) * 0.7, sizev = 0, sizea = 0,
+
+		x = x, xv = 0, xa = 0,
+		y = y, yv = 0, ya = 0,
+		dir = a, dirv = 0, dira = 0,
+		vel = rng.float(vel * 0.6, vel * 1.2), velv = 0, vela = 0,
+
+		r = 0,  rv = 0, ra = 0,
+		g = rng.range(80, 200)/255,      gv = 0, ga = 0,
+		b = 0,      bv = 0, ba = 0,
+		a = rng.range(80, 220)/255,   av = 0, aa = 0,
+	}
+end, },
+function(self)
+	if nb > 0 then
+		local i = math.min(nb, 6)
+		i = (i * i) * radius
+		self.ps:emit(i)
+		nb = nb - 1
+	end
+end,
+30*radius*7*12,
+"particle_cloud"
diff --git a/game/modules/tome/data/talents/gifts/cold-drake.lua b/game/modules/tome/data/talents/gifts/cold-drake.lua
index 6ef30cf79ab053d910ee31d5d7fd472e25ef0e24..f4dca7d2096ae0abb3dcf7b6096a794a1005e46f 100644
--- a/game/modules/tome/data/talents/gifts/cold-drake.lua
+++ b/game/modules/tome/data/talents/gifts/cold-drake.lua
@@ -141,7 +141,8 @@ newTalent{
 		local tg = {type="cone", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.ICE, 30 + self:getStr(50) * self:getTalentLevel(t), {type="freeze"})
+		self:project(tg, x, y, DamageType.ICE, 30 + self:getStr(50) * self:getTalentLevel(t))
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_cold", {radius=tg.radius, tx=x-self.x, ty=y-self.y})
 		game:playSoundNear(self, "talents/breath")
 		return true
 	end,
diff --git a/game/modules/tome/data/talents/gifts/fire-drake.lua b/game/modules/tome/data/talents/gifts/fire-drake.lua
index 52e4090ac3d75b9e8b045ef15c86f28658b635e3..7c64bd62780e1084c0c01f53ea8c87628a2504b8 100644
--- a/game/modules/tome/data/talents/gifts/fire-drake.lua
+++ b/game/modules/tome/data/talents/gifts/fire-drake.lua
@@ -126,7 +126,8 @@ newTalent{
 		local tg = {type="cone", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.FIREBURN, 30 + self:getStr(65) * self:getTalentLevel(t), {type="flame"})
+		self:project(tg, x, y, DamageType.FIREBURN, 30 + self:getStr(65) * self:getTalentLevel(t))
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_fire", {radius=tg.radius, tx=x-self.x, ty=y-self.y})
 		game:playSoundNear(self, "talents/breath")
 		return true
 	end,
diff --git a/game/modules/tome/data/talents/gifts/sand-drake.lua b/game/modules/tome/data/talents/gifts/sand-drake.lua
index 6677d773046cb4e32a3cfa30aab4a0bf61ed40de..b7ae0d901ba2df2536d3e1b2b8b4579d6561e189 100644
--- a/game/modules/tome/data/talents/gifts/sand-drake.lua
+++ b/game/modules/tome/data/talents/gifts/sand-drake.lua
@@ -116,7 +116,8 @@ newTalent{
 		local tg = {type="cone", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.SAND, {dur=2+self:getTalentLevelRaw(t), dam=10 + self:getStr() * 0.3 * self:getTalentLevel(t)}, {type="flame"})
+		self:project(tg, x, y, DamageType.SAND, {dur=2+self:getTalentLevelRaw(t), dam=10 + self:getStr() * 0.3 * self:getTalentLevel(t)})
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_earth", {radius=tg.radius, tx=x-self.x, ty=y-self.y})
 		game:playSoundNear(self, "talents/breath")
 		return true
 	end,
diff --git a/game/modules/tome/data/talents/gifts/storm-drake.lua b/game/modules/tome/data/talents/gifts/storm-drake.lua
index b372715b2d742fbcfeb87becb8bc5c7be3de4e82..6dfdea7947a401c9551c51776e3f6299e8953632 100644
--- a/game/modules/tome/data/talents/gifts/storm-drake.lua
+++ b/game/modules/tome/data/talents/gifts/storm-drake.lua
@@ -168,7 +168,8 @@ newTalent{
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
 		local dam = 40 + self:getStr(80) * self:getTalentLevel(t)
-		self:project(tg, x, y, DamageType.LIGHTNING_DAZE, rng.avg(dam / 3, dam, 3), {type="lightning_explosion"})
+		self:project(tg, x, y, DamageType.LIGHTNING_DAZE, rng.avg(dam / 3, dam, 3))
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_lightning", {radius=tg.radius, tx=x-self.x, ty=y-self.y})
 		game:playSoundNear(self, "talents/breath")
 		return true
 	end,
diff --git a/game/modules/tome/data/talents/gifts/summon-distance.lua b/game/modules/tome/data/talents/gifts/summon-distance.lua
index 50ddbeb6cafb04b0790d8f80454200301592e402..ecc83cf947896dea2cac516c9e1000c98f8e09e5 100644
--- a/game/modules/tome/data/talents/gifts/summon-distance.lua
+++ b/game/modules/tome/data/talents/gifts/summon-distance.lua
@@ -56,7 +56,8 @@ newTalent{
 		local tg = {type="cone", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.ACID, 30 + self:getWil(50) * self:getTalentLevel(t), {type="acid"})
+		self:project(tg, x, y, DamageType.ACID, 30 + self:getWil(50) * self:getTalentLevel(t))
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_acid", {radius=tg.radius, tx=x-self.x, ty=y-self.y})
 		game:playSoundNear(self, "talents/breath")
 		return true
 	end,
@@ -84,7 +85,8 @@ newTalent{
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
 		local dam = 30 + self:getWil(80) * self:getTalentLevel(t)
-		self:project(tg, x, y, DamageType.LIGHTNING, rng.avg(dam / 3, dam, 3), {type="lightning_explosion"})
+		self:project(tg, x, y, DamageType.LIGHTNING, rng.avg(dam / 3, dam, 3))
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_lightning", {radius=tg.radius, tx=x-self.x, ty=y-self.y})
 		game:playSoundNear(self, "talents/lightning")
 		return true
 	end,
@@ -115,7 +117,8 @@ newTalent{
 		local tg = {type="cone", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.POISON, 30 + self:getWil(70) * self:getTalentLevel(t), {type="slime"})
+		self:project(tg, x, y, DamageType.POISON, 30 + self:getWil(70) * self:getTalentLevel(t))
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_slime", {radius=tg.radius, tx=x-self.x, ty=y-self.y})
 		game:playSoundNear(self, "talents/breath")
 		return true
 	end,
diff --git a/game/modules/tome/data/talents/spells/fire.lua b/game/modules/tome/data/talents/spells/fire.lua
index 7a4f42c63f929fa51eae18af795e0c935c8f918b..99d32e4fc74118e3ae21cab75ae0eefd5cbc245a 100644
--- a/game/modules/tome/data/talents/spells/fire.lua
+++ b/game/modules/tome/data/talents/spells/fire.lua
@@ -53,7 +53,7 @@ newTalent{
 	points = 5,
 	random_ego = "attack",
 	mana = 30,
-	cooldown = 18,
+--	cooldown = 18,
 	tactical = {
 		ATTACKAREA = 10,
 	},
@@ -63,7 +63,8 @@ newTalent{
 		local tg = {type="cone", range=0, radius=3 + self:getTalentLevelRaw(t), friendlyfire=false, talent=t}
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.FLAMESHOCK, {dur=self:getTalentLevelRaw(t) + 2, dam=self:spellCrit(self:combatTalentSpellDamage(t, 10, 120))}, {type="flame"})
+		self:project(tg, x, y, DamageType.FLAMESHOCK, {dur=self:getTalentLevelRaw(t) + 2, dam=self:spellCrit(self:combatTalentSpellDamage(t, 10, 120))})
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_fire", {radius=tg.radius, tx=x-self.x, ty=y-self.y})
 		game:playSoundNear(self, "talents/fire")
 		return true
 	end,
diff --git a/game/modules/tome/data/talents/spells/stone-alchemy.lua b/game/modules/tome/data/talents/spells/stone-alchemy.lua
index 52d39ba6a38f80e20e2da8636820f57ad089cef6..893c8abc7d223ca695e7bc18e45dc55fce6ea43f 100644
--- a/game/modules/tome/data/talents/spells/stone-alchemy.lua
+++ b/game/modules/tome/data/talents/spells/stone-alchemy.lua
@@ -68,7 +68,7 @@ newTalent{
 			self:removeObject(inven, item)
 
 			local level = o.material_level or 1
-			local gem = game.zone:makeEntity(game.level, "object", {type="gem", special=function(e) return e.material_level == level end}, nil, true)
+			local gem = game.zone:makeEntity(game.level, "object", {type="gem", special=function(e) return not e.unique and e.material_level == level end}, nil, true)
 			if gem then
 				self:addObject(self.INVEN_INVEN, gem)
 				game.logPlayer(self, "You extract: %s", gem:getName{do_color=true, do_count=true})