From f61cb70c5147d8a57fd4931496ed408accb02544 Mon Sep 17 00:00:00 2001
From: DarkGod <darkgod@net-core.org>
Date: Fri, 19 Dec 2014 01:50:03 +0100
Subject: [PATCH] when time stops .. time stops

---
 game/modules/tome/class/Game.lua              |  9 +++
 game/modules/tome/class/Player.lua            |  6 ++
 .../data/gfx/shaders/main_fbo/timestop.frag   | 76 +++++++++++++++++++
 .../data/gfx/shaders/main_fbo/timestop.lua    | 31 ++++++++
 .../modules/tome/data/timed_effects/other.lua |  7 ++
 .../tome/data/zones/town-elvala/npcs.lua      |  2 +-
 src/core_lua.c                                | 17 +++++
 src/main.c                                    |  7 +-
 src/main.h                                    |  2 +
 9 files changed, 154 insertions(+), 3 deletions(-)
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/timestop.frag
 create mode 100644 game/modules/tome/data/gfx/shaders/main_fbo/timestop.lua

diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index c0805fd928..c1896654eb 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -532,6 +532,7 @@ function _M:createFBOs()
 			underwater = Shader.new("main_fbo/underwater"),
 			motionblur = Shader.new("main_fbo/motionblur"),
 			blur = Shader.new("main_fbo/blur"),
+			timestop = Shader.new("main_fbo/timestop"),
 		}
 		self.posteffects_use = { self.fbo_shader.shad }
 		if not self.fbo_shader.shad then self.fbo = nil self.fbo_shader = nil end 
@@ -1393,6 +1394,14 @@ function _M:displayMap(nb_keyframes)
 		local changed = map.changed
 		if changed then self:updateFOV() end
 
+		-- Ugh I dont like that but .. special case for timestop, for now it'll do!
+		if self.player and self.player:attr("timestopping") and self.player.x and self.posteffects and self.posteffects.timestop and self.posteffects.timestop.shad then
+			self.posteffects.timestop.shad:paramNumber2("texSize", map.viewport.width, map.viewport.height)
+			local sx, sy = map:getTileToScreen(self.player.x, self.player.y)
+			self.posteffects.timestop.shad:paramNumber2("playerPos", sx + map.tile_w / 2, sy + map.tile_h / 2)
+			self.posteffects.timestop.shad:paramNumber("tick_real", core.game.getTime())
+		end
+
 		-- Display using Framebuffer, so that we can use shaders and all
 		if self.fbo then
 			self.fbo:use(true)
diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua
index 5bebc15e05..ffd76f8381 100644
--- a/game/modules/tome/class/Player.lua
+++ b/game/modules/tome/class/Player.lua
@@ -458,6 +458,12 @@ function _M:updateMainShader()
 			end
 		end
 
+		-- Timestop shader
+		if self:attr("timestopping") and pf.timestop and pf.timestop.shad then
+			effects[pf.timestop.shad] = true
+			pf.timestop.shad:paramNumber("tick_start", core.game.getTime())
+		end
+
 		game.posteffects_use = table.keys(effects)
 		game.posteffects_use[#game.posteffects_use+1] = game.fbo_shader.shad
 	end
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/timestop.frag b/game/modules/tome/data/gfx/shaders/main_fbo/timestop.frag
new file mode 100644
index 0000000000..3768c263ab
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/timestop.frag
@@ -0,0 +1,76 @@
+// uniform sampler2D tex; 
+// uniform vec2 texSize;
+// uniform vec2 playerPos;
+// uniform float innerRadius;
+// uniform float outerRadius;
+// uniform float tick_start;
+// uniform float tick_real;
+// uniform float growSpeed;
+
+// float wave(float x)
+// {
+// 	return (-x*x*x / 3.0 + x*x / 2.0) * 6.0;
+// }
+
+// vec4 ConvertGrayscale(vec4 color)
+// {
+// 	float val = color.r * 0.299 + color.g * 0.587 + 0.114 * color.b;
+// 	return vec4(val, val, val, color.a);
+// }
+// void main(void)
+// {
+// 	float growRatio = clamp((tick_real - tick_start) * growSpeed, 0.0, 1.0);
+// 	growRatio = 1.0 - pow(1.0 - growRatio, 2.0);
+
+// 	// float circleRatio = clamp((length(gl_FragCoord.xy - playerPos) - innerRadius) / (outerRadius - innerRadius), 0.0, 1.0);
+// 	float circleRatio = clamp((length(gl_FragCoord.xy - vec2(playerPos.x, texSize.y - playerPos.y)) - innerRadius) / (outerRadius - innerRadius), 0.0, 1.0);
+
+// 	float scale = growRatio * wave(circleRatio);
+
+// 	vec4 backgroundColor = texture2D(tex, gl_FragCoord.xy / texSize);
+// 	vec4 grayscaleColor = ConvertGrayscale(backgroundColor);
+// 	gl_FragColor = scale * grayscaleColor + (1.0 - scale) * backgroundColor;
+// 	return;
+// }
+
+uniform sampler2D tex; 
+uniform vec2 texSize;
+uniform vec2 playerPos;
+uniform float innerRadius;
+uniform float outerRadius;
+uniform float tick_start;
+uniform float tick_real;
+uniform float growSpeed;
+
+float wave(float x)
+{
+	return (-x*x*x / 3.0 + x*x / 2.0) * 6.0;
+}
+
+vec4 ConvertGrayscale(vec4 color, vec4 lowColor, vec4 highColor)
+{
+	float val = color.r * 0.299 + color.g * 0.587 + 0.114 * color.b;
+	return highColor * val + lowColor * (1.0 - val);
+}
+void main(void)
+{
+	float growRatio = clamp((tick_real - tick_start) * growSpeed, 0.0, 1.0);
+	growRatio = 1.0 - pow(1.0 - growRatio, 2.0);
+
+	innerRadius += innerRadius * 0.1 * sin(tick_real * growSpeed / 3);
+	outerRadius += outerRadius * 0.1 * cos(tick_real * growSpeed / 3);
+	float circleRatio = clamp((length(gl_FragCoord.xy - vec2(playerPos.x, texSize.y - playerPos.y)) - innerRadius) / (outerRadius - innerRadius), 0.0, 1.0);
+	// float circleRatio = clamp((length(gl_FragCoord.xy - playerPos) - innerRadius) / (outerRadius - innerRadius), 0.0, 1.0);
+	float scale = growRatio * wave(circleRatio);
+
+	vec4 backgroundColor = texture2D(tex, gl_FragCoord.xy / texSize);
+
+	vec4 highColor = vec4(243.0/255.0, 234.0/255.0, 255.0/255.0, 1.0);
+	vec4 lowColor  = vec4(6.0/255.0  , 0.0  /255.0, 15.0 /255.0, 1.0);
+	/*vec4 highColor = vec4(1.0, 1.0, 1.0, 1.0);
+	vec4 lowColor  = vec4(0.0, 0.0, 0.0, 1.0);*/
+
+	vec4 grayscaleColor = ConvertGrayscale(backgroundColor, lowColor, highColor);
+	gl_FragColor = scale * grayscaleColor + (1.0 - scale) * backgroundColor;
+	return;
+}
diff --git a/game/modules/tome/data/gfx/shaders/main_fbo/timestop.lua b/game/modules/tome/data/gfx/shaders/main_fbo/timestop.lua
new file mode 100644
index 0000000000..661b2e61b6
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/main_fbo/timestop.lua
@@ -0,0 +1,31 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2014 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 = "main_fbo/timestop",
+	vert = nil,
+	args = {
+		tex = { texture = 0 },
+		innerRadius = 70,
+		outerRadius = 90,
+		growSpeed = 0.001,
+	},
+	clone = false,
+	permanent = true,
+}
diff --git a/game/modules/tome/data/timed_effects/other.lua b/game/modules/tome/data/timed_effects/other.lua
index d93de637b7..eb55d698b1 100644
--- a/game/modules/tome/data/timed_effects/other.lua
+++ b/game/modules/tome/data/timed_effects/other.lua
@@ -2475,10 +2475,17 @@ newEffect{
 		if core.shader.active(4) then
 			eff.particle1, eff.particle2 = self:addParticles3D("volumetric", {kind="vertical_and_awesome", radius=1.4, growSpeed=0.004, img="coggy_00"})
 		end
+		self:effectTemporaryValue(eff, "timestopping", 1)
+		self.no_leave_control = true
+		core.display.pauseAnims(true)
+		self:updateMainShader()
 	end,
 	deactivate = function(self, eff)
+		self.no_leave_control = false
+		core.display.pauseAnims(false)
 		self:removeParticles(eff.particle1)
 		self:removeParticles(eff.particle2)
+		self:updateMainShader()
 	end,
 }
 
diff --git a/game/modules/tome/data/zones/town-elvala/npcs.lua b/game/modules/tome/data/zones/town-elvala/npcs.lua
index 0063473fe0..a188aa3408 100644
--- a/game/modules/tome/data/zones/town-elvala/npcs.lua
+++ b/game/modules/tome/data/zones/town-elvala/npcs.lua
@@ -109,7 +109,7 @@ newEntity{ base = "BASE_NPC_ELVALA_OGRE_TOWN",
 	level_range = {1, nil}, exp_worth = 1,
 	rarity = 3,
 
-	resolvers.inscriptions(4, "rune"),
+	resolvers.inscriptions(3, {"shielding rune", "phase door rune", "heat beam rune", "acid wave rune", "lightning rune"}),
 	max_life = resolvers.rngavg(70,80),
 	resolvers.equip{
 		{type="weapon", subtype="longsword", autoreq=true},
diff --git a/src/core_lua.c b/src/core_lua.c
index e2d6196a80..d6ab43e57d 100644
--- a/src/core_lua.c
+++ b/src/core_lua.c
@@ -3077,6 +3077,22 @@ static int fbo_texture_bind(lua_State *L)
 	return 0;
 }
 
+static int pause_anims_started = 0;
+static int display_pause_anims(lua_State *L) {
+	bool new_state = lua_toboolean(L, 1);
+	if (new_state == anims_paused) return 0;
+
+	if (new_state) {
+		anims_paused = TRUE;
+		pause_anims_started = SDL_GetTicks();
+	} else {
+		anims_paused = FALSE;
+		frame_tick_paused_time += SDL_GetTicks() - pause_anims_started;
+	}
+	printf("[DISPLAY] Animations paused: %d\n", anims_paused);
+	return 0;
+}
+
 static const struct luaL_Reg displaylib[] =
 {
 	{"setTextBlended", set_text_aa},
@@ -3108,6 +3124,7 @@ static const struct luaL_Reg displaylib[] =
 	{"setMouseCursor", sdl_set_mouse_cursor},
 	{"setMouseDrag", sdl_set_mouse_cursor_drag},
 	{"setGamma", sdl_set_gamma},
+	{"pauseAnims", display_pause_anims},
 	{"glTranslate", gl_translate},
 	{"glScale", gl_scale},
 	{"glRotate", gl_rotate},
diff --git a/src/main.c b/src/main.c
index a086c914ae..fd085aed1c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -75,12 +75,14 @@ bool no_sound = FALSE;
 bool no_steam = FALSE;
 bool isActive = TRUE;
 bool tickPaused = FALSE;
+bool anims_paused = FALSE;
 int mouse_cursor_ox, mouse_cursor_oy;
 int mouse_drag_w = 32, mouse_drag_h = 32;
 int mouse_drag_tex = 0, mouse_drag_tex_ref = LUA_NOREF;
 int mousex = 0, mousey = 0;
 float gamma_correction = 1;
 int cur_frame_tick = 0;
+int frame_tick_paused_time = 0;
 /* The currently requested fps for the program */
 int requested_fps = 30;
 /* The requested fps for when the program is idle (i.e., doesn't have focus) */
@@ -516,7 +518,7 @@ void call_draw(int nb_keyframes)
 	if (nb_keyframes > 30) nb_keyframes = 30;
 
 	// Notify the particles threads that there are new keyframes
-	thread_particle_new_keyframes(nb_keyframes);
+	if (!anims_paused) thread_particle_new_keyframes(nb_keyframes);
 
 	if (current_game != LUA_NOREF)
 	{
@@ -578,7 +580,8 @@ void on_redraw()
 	/* Gather our frames per second */
 	Frames++;
 	if (!is_waiting()) {
-		int t = cur_frame_tick = SDL_GetTicks();
+		int t = SDL_GetTicks();
+		if (!anims_paused) cur_frame_tick = t - frame_tick_paused_time;
 		if (t - T0 >= 1000) {
 			float seconds = (t - T0) / 1000.0;
 			float fps = Frames / seconds;
diff --git a/src/main.h b/src/main.h
index 9b0f17bc4a..73d51abcb9 100644
--- a/src/main.h
+++ b/src/main.h
@@ -62,6 +62,8 @@ extern bool safe_mode;
 extern bool fbo_active;
 extern bool multitexture_active;
 extern long total_keyframes;
+extern bool anims_paused;
+extern int frame_tick_paused_time;
 extern int cur_frame_tick;
 extern int g_argc;
 extern char **g_argv;
-- 
GitLab