From f58fa99cf345f6543937e3326128322a4d3f5c90 Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Sun, 29 Jul 2012 17:47:57 +0000
Subject: [PATCH] Particles can have shaders attached New shader-enabled damage
 shield effect

git-svn-id: http://svn.net-core.org/repos/t-engine4@5387 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/engines/default/engine/Particles.lua     |  16 +++++-
 game/engines/default/engine/Shader.lua        |   2 +
 game/modules/tome/class/Actor.lua             |   7 +++
 game/modules/tome/class/Game.lua              |   9 +++-
 .../tome/data/gfx/particles/shader_shield.lua |  48 +++++++++++++++++
 .../data/gfx/particles_images/shield2.png     | Bin 0 -> 16944 bytes
 .../modules/tome/data/gfx/shaders/shield.frag |  49 ++++++++++++++++++
 game/modules/tome/data/gfx/shaders/shield.lua |  32 ++++++++++++
 .../tome/data/timed_effects/magical.lua       |  14 ++++-
 src/particles.c                               |   7 +++
 src/particles.h                               |   2 +
 11 files changed, 182 insertions(+), 4 deletions(-)
 create mode 100644 game/modules/tome/data/gfx/particles/shader_shield.lua
 create mode 100644 game/modules/tome/data/gfx/particles_images/shield2.png
 create mode 100644 game/modules/tome/data/gfx/shaders/shield.frag
 create mode 100644 game/modules/tome/data/gfx/shaders/shield.lua

diff --git a/game/engines/default/engine/Particles.lua b/game/engines/default/engine/Particles.lua
index 47fd3cca34..d1aa83ab0e 100644
--- a/game/engines/default/engine/Particles.lua
+++ b/game/engines/default/engine/Particles.lua
@@ -26,10 +26,11 @@ module(..., package.seeall, class.make)
 local __particles_gl = {}
 
 --- Make a particle emitter
-function _M:init(def, radius, args)
+function _M:init(def, radius, args, shader)
 	self.args = args
 	self.def = def
 	self.radius = radius or 1
+	self.shader = shader
 
 	self:loaded()
 end
@@ -72,7 +73,18 @@ function _M:loaded()
 	args = args.."tile_w="..engine.Map.tile_w.."\ntile_h="..engine.Map.tile_h
 
 	self.update = fct
-	self.ps = core.particles.newEmitter("/data/gfx/particles/"..self.def..".lua", args, self.zoom, config.settings.particles_density or 100, gl)
+
+	local sha = nil
+	if self.shader then
+		if not self._shader then
+			local Shader = require 'engine.Shader'
+			self._shader = Shader.new(self.shader.type, self.shader)
+		end
+
+		sha = self._shader.shad
+	end
+
+	self.ps = core.particles.newEmitter("/data/gfx/particles/"..self.def..".lua", args, self.zoom, config.settings.particles_density or 100, gl, sha)
 end
 
 function _M:updateZoom()
diff --git a/game/engines/default/engine/Shader.lua b/game/engines/default/engine/Shader.lua
index d25c249230..fa62ad9499 100644
--- a/game/engines/default/engine/Shader.lua
+++ b/game/engines/default/engine/Shader.lua
@@ -27,6 +27,8 @@ _M.verts = {}
 _M.frags = {}
 _M.progs = {}
 
+loadNoDelay = true
+
 --- Make a shader
 function _M:init(name, args)
 	self.args = args or {}
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 2b60589ca5..504099b9fb 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -1974,6 +1974,13 @@ function _M:onTakeHit(value, src)
 end
 
 function _M:takeHit(value, src, death_note)
+	for eid, p in pairs(self.tmp) do
+		local e = self.tempeffect_def[eid]
+		if e.damage_feedback then
+			e.damage_feedback(self, p, src, value)
+		end
+	end
+
 	local dead, val = engine.interface.ActorLife.takeHit(self, value, src, death_note)
 
 	if dead and src and src.attr and src:attr("overkill") and src.project and not src.turn_procs.overkill then
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 21ff7ce9cf..b0417e50b5 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -1213,8 +1213,15 @@ function _M:setupCommands()
 			end end
 		end end,
 		[{"_g","ctrl"}] = function() if config.settings.cheat then
-			self:registerDialog(require("mod.dialogs.DownloadCharball").new())
+			local ps = next(game.player.__particles)
+
+			local a = math.rad(rng.float(0, 360))
+			local r = rng.float(0.2, 0.4)
+			ps._shader:setUniform("impact", {math.cos(a) * r, math.sin(a) * r})
+			ps._shader:setUniform("impact_tick", core.game.getTime())
+
 do return end
+			self:registerDialog(require("mod.dialogs.DownloadCharball").new())
 			local f, err = loadfile("/data/general/events/snowstorm.lua")
 			print(f, err)
 			setfenv(f, setmetatable({level=self.level, zone=self.zone}, {__index=_G}))
diff --git a/game/modules/tome/data/gfx/particles/shader_shield.lua b/game/modules/tome/data/gfx/particles/shader_shield.lua
new file mode 100644
index 0000000000..211fbb36a5
--- /dev/null
+++ b/game/modules/tome/data/gfx/particles/shader_shield.lua
@@ -0,0 +1,48 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010, 2011, 2012 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
+
+base_size = 32
+
+local r = 1
+local g = 1
+local b = 1
+local a = 1
+
+return { generator = function()
+	return {
+		trail = 0,
+		life = 10,
+		size = 38, sizev = 0, sizea = 0,
+
+		x = 0, xv = 0, xa = 0,
+		y = 0, yv = 0, ya = 0,
+		dir = 0, dirv = dirv, dira = 0,
+		vel = 0, velv = 0, vela = 0,
+
+		r = r, rv = 0, ra = 0,
+		g = g, gv = 0, ga = 0,
+		b = b, bv = 0, ba = 0,
+		a = a, av = -0.02, aa = 0.005,
+	}
+end, },
+function(self)
+	self.ps:emit(1)
+end,
+1,
+"particles_images/shield2"
diff --git a/game/modules/tome/data/gfx/particles_images/shield2.png b/game/modules/tome/data/gfx/particles_images/shield2.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa9a097833754ab205c1a9e60a749686cd40354a
GIT binary patch
literal 16944
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
A0RR91

literal 0
HcmV?d00001

diff --git a/game/modules/tome/data/gfx/shaders/shield.frag b/game/modules/tome/data/gfx/shaders/shield.frag
new file mode 100644
index 0000000000..1e255718ba
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/shield.frag
@@ -0,0 +1,49 @@
+uniform sampler2D tex;
+uniform float tick;
+uniform float aadjust;
+uniform vec3 color;
+uniform float time_factor;
+
+vec3 impact_color = vec3(1.0, 0.3, 1.0);
+uniform vec2 impact;
+uniform float impact_tick;
+
+void main(void)
+{
+	vec2 uv = vec2(0.5, 0.5) - gl_TexCoord[0].xy;
+	float l = length(uv) * 2.0;
+	float ll = l * l;
+
+	vec4 c1 = texture2D(tex, (uv * ll / 1.3 + vec2(tick / time_factor, 0.0)));
+	vec4 c2 = texture2D(tex, (uv * ll / 1.3 + vec2(0.0, tick / time_factor)));
+	vec4 c = c1 * c2;
+
+	float dist = max(min(1.0, 1.0 - l), 0.0) * 3.0;
+	c.a *= c.a * dist;
+
+	float z = l;
+	c.a *= z;
+
+	if (l > 1.0) c.a = 0.0;
+	if (l < 0.5) {
+		c.a *= ll * 4.0;
+	}
+
+	// Impact
+	float it = tick - impact_tick;
+	if (it < 400.0) {
+		float v = (400.0 - it) / 400.0;
+		float il = distance(impact / ll, (vec2(0.5) - gl_TexCoord[0].xy) / ll);
+		if (il < 0.5 * (1.0 - v)) {
+			v *= v * v;
+			float ic = (1.0 - length(uv - impact)) * v * 3.0;
+			c.rgb = mix(c.rgb, impact_color, ic);
+			aadjust *= 1.0 + v * 3.0;
+		}
+	}
+
+	c.a *= aadjust;
+
+	c.rgb *= color;
+	gl_FragColor = c;
+}
diff --git a/game/modules/tome/data/gfx/shaders/shield.lua b/game/modules/tome/data/gfx/shaders/shield.lua
new file mode 100644
index 0000000000..f510bd0b3c
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/shield.lua
@@ -0,0 +1,32 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010, 2011, 2012 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
+
+return {
+	frag = "shield",
+	vert = nil,
+	args = {
+		tex = { texture = 0 },
+		color = color or {0.4, 0.7, 1.0},
+		time_factor = time_factor or 4000,
+		aadjust = aadjust or 10,
+		impact = {0, 0},
+		impact_tick = -1000,
+	},
+	clone = false,
+}
diff --git a/game/modules/tome/data/timed_effects/magical.lua b/game/modules/tome/data/timed_effects/magical.lua
index 3a2656a7b5..103258d9f7 100644
--- a/game/modules/tome/data/timed_effects/magical.lua
+++ b/game/modules/tome/data/timed_effects/magical.lua
@@ -435,6 +435,14 @@ newEffect{
 	on_aegis = function(self, eff, aegis)
 		self.damage_shield_absorb = self.damage_shield_absorb + eff.power * aegis / 100
 	end,
+	damage_feedback = function(self, eff, src, value)
+		if eff.particle and eff.particle._shader and eff.particle._shader.shad and src and src.x and src.y then
+			local r = -rng.float(0.2, 0.4)
+			local a = math.atan2(src.y - self.y, src.x - self.x)
+			eff.particle._shader:setUniform("impact", {math.cos(a) * r, math.sin(a) * r})
+			eff.particle._shader:setUniform("impact_tick", core.game.getTime())
+		end
+	end,
 	activate = function(self, eff)
 		if self:attr("shield_factor") then eff.power = eff.power * (100 + self:attr("shield_factor")) / 100 end
 		if self:attr("shield_dur") then eff.dur = eff.dur + self:attr("shield_dur") end
@@ -443,7 +451,11 @@ newEffect{
 		--- Warning there can be only one time shield active at once for an actor
 		self.damage_shield_absorb = eff.power
 		self.damage_shield_absorb_max = eff.power
-		eff.particle = self:addParticles(Particles.new("damage_shield", 1))
+		if core.shader.active() then
+			eff.particle = self:addParticles(Particles.new("shader_shield", 1, nil, {type="shield", color={0.4, 0.7, 1.0}}))
+		else
+			eff.particle = self:addParticles(Particles.new("damage_shield", 1))
+		end
 	end,
 	deactivate = function(self, eff)
 		self:removeParticles(eff.particle)
diff --git a/src/particles.c b/src/particles.c
index 58823eea9d..d008615ae3 100644
--- a/src/particles.c
+++ b/src/particles.c
@@ -83,6 +83,8 @@ static int particles_new(lua_State *L)
 	// float zoom = luaL_checknumber(L, 3);
 	int density = luaL_checknumber(L, 4);
 	GLuint *texture = (GLuint*)auxiliar_checkclass(L, "gl{texture}", 5);
+	shader_type *s = NULL;
+	if (lua_isuserdata(L, 6)) s = (shader_type*)auxiliar_checkclass(L, "gl{program}", 6);
 
 	particles_type *ps = (particles_type*)lua_newuserdata(L, sizeof(particles_type));
 	auxiliar_setclass(L, "core{particles}", -1);
@@ -100,6 +102,7 @@ static int particles_new(lua_State *L)
 	ps->particles = NULL;
 	ps->init = FALSE;
 	ps->texture = *texture;
+	ps->shader = s;
 
 	thread_add(ps);
 	return 1;
@@ -182,6 +185,8 @@ static int particles_to_screen(lua_State *L)
 	glScalef(ps->zoom * zoom, ps->zoom * zoom, ps->zoom * zoom);
 	glRotatef(ps->rotate, 0, 0, 1);
 
+	if (ps->shader) useShader(ps->shader, 1, 1, 1, 1, 1, 1, 1, 1);
+
 	int remaining = ps->batch_nb;
 	while (remaining >= PARTICLES_PER_ARRAY)
 	{
@@ -190,6 +195,8 @@ static int particles_to_screen(lua_State *L)
 	}
 	if (remaining) glDrawArrays(GL_QUADS, 0, remaining);
 
+	if (ps->shader) glUseProgramObjectARB(0);
+
 	glRotatef(-ps->rotate, 0, 0, 1);
 	glPopMatrix();
 	glTranslatef(-x, -y, 0);
diff --git a/src/particles.h b/src/particles.h
index ef4a85afcf..46dc615773 100644
--- a/src/particles.h
+++ b/src/particles.h
@@ -22,6 +22,7 @@
 #define _PARTICLES_H_
 
 #include "tgl.h"
+#include "useshader.h"
 
 typedef struct {
 	float size, sizev, sizea;
@@ -40,6 +41,7 @@ typedef struct {
 
 	// Read only by main
 	GLuint texture;
+	shader_type *shader;
 
 	// W by main, R by thread
 	const char *name_def;
-- 
GitLab