diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 13538a888fcf1c6807165768afa35ebf12306ce0..e6f5082a6718a5db9c7b3e6ce42a8a7d46f526cc 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -2778,6 +2778,14 @@ function _M:checkEncumbrance() end end +--- Return attachement coords +function _M:attachementSpot(kind, particle) + if not self.attachement_spots or not self.attachement_spots[kind] then return end + local x, y = 0, 0 + if particle then x, y = -0.5, -0.5 end + return self.attachement_spots[kind].x + x, self.attachement_spots[kind].y + y +end + --- Update tile for races that can handle it function _M:updateModdableTile() if not self.moddable_tile or Map.tiles.no_moddable_tiles then return end diff --git a/game/modules/tome/data/birth/classes/mage.lua b/game/modules/tome/data/birth/classes/mage.lua index 0a22ca867529cefb62df3fddc395c4621a4a7a03..dabe624f7f57ac26a245989ccaf88557c2bcebea 100644 --- a/game/modules/tome/data/birth/classes/mage.lua +++ b/game/modules/tome/data/birth/classes/mage.lua @@ -127,10 +127,11 @@ newBirthDescriptor{ stats = { mag=5, wil=3, cun=1, }, birth_example_particles = { function(actor) - if core.shader.active(4) then actor:addParticles(Particles.new("shader_wings", 1, {infinite=1})) + if core.shader.active(4) then local x, y = actor:attachementSpot("back", true) actor:addParticles(Particles.new("shader_wings", 1, {x=y, y=y, infinite=1})) else actor:addParticles(Particles.new("wildfire", 1)) end end, + --[[ function(actor) if core.shader.active(4) then actor:addParticles(Particles.new("shader_ring_rotating", 1, {radius=1.1}, {type="flames", hide_center=0, time_factor=1700, zoom=0.3, npow=1, color1={0.6, 0.3, 0.8, 1}, color2={0.8, 0, 0.8, 1}, xy={0,0}})) else actor:addParticles(Particles.new("ultrashield", 1, {rm=180, rM=220, gm=10, gM=50, bm=190, bM=220, am=120, aM=200, radius=0.4, density=100, life=8, instop=20})) @@ -154,6 +155,7 @@ newBirthDescriptor{ else actor:addParticles(Particles.new("tempest", 1)) end end, + ]] }, talents_types = { ["spell/arcane"]={true, 0.3}, diff --git a/game/modules/tome/data/birth/classes/warrior.lua b/game/modules/tome/data/birth/classes/warrior.lua index 0770ada3a80ca2df78c067829faa4653a66ea09c..39c280a3125881a217e0931c38d10360102bbb84 100644 --- a/game/modules/tome/data/birth/classes/warrior.lua +++ b/game/modules/tome/data/birth/classes/warrior.lua @@ -17,6 +17,8 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org +local Particles = require "engine.Particles" + newBirthDescriptor{ type = "class", name = "Warrior", @@ -231,6 +233,18 @@ newBirthDescriptor{ unlockable_talents_types = { ["spell/stone"]={false, 0.1, "mage_geomancer"}, }, + birth_example_particles = { + function(actor) if core.shader.active(4) then + local slow = rng.percent(50) + local h1x, h1y = actor:attachementSpot("hand1", true) if h1x then actor:addParticles(Particles.new("shader_shield", 1, {img="fireball", a=0.7, size_factor=0.4, x=h1x, y=h1y-0.1}, {type="flamehands", time_factor=slow and 700 or 1000})) end + local h2x, h2y = actor:attachementSpot("hand2", true) if h2x then actor:addParticles(Particles.new("shader_shield", 1, {img="fireball", a=0.7, size_factor=0.4, x=h2x, y=h2y-0.1}, {type="flamehands", time_factor=not slow and 700 or 1000})) end + end end, + function(actor) if core.shader.active(4) then + local slow = rng.percent(50) + local h1x, h1y = actor:attachementSpot("hand1", true) if h1x then actor:addParticles(Particles.new("shader_shield", 1, {img="lightningwings", a=0.7, size_factor=0.4, x=h1x, y=h1y-0.1}, {type="flamehands", time_factor=slow and 700 or 1000})) end + local h2x, h2y = actor:attachementSpot("hand2", true) if h2x then actor:addParticles(Particles.new("shader_shield", 1, {img="lightningwings", a=0.7, size_factor=0.4, x=h2x, y=h2y-0.1}, {type="flamehands", time_factor=not slow and 700 or 1000})) end + end end, + }, talents = { [ActorTalents.T_FLAME] = 1, [ActorTalents.T_ARCANE_COMBAT] = 1, diff --git a/game/modules/tome/data/birth/races/dwarf.lua b/game/modules/tome/data/birth/races/dwarf.lua index 1c6eb4dfe27721063c7951327b6ab80786fe1f7a..d8812976010ef1a282e2fd7f948c8c049eb62df1 100644 --- a/game/modules/tome/data/birth/races/dwarf.lua +++ b/game/modules/tome/data/birth/races/dwarf.lua @@ -50,6 +50,20 @@ newBirthDescriptor{ }, random_escort_possibilities = { {"tier1.1", 1, 2}, {"tier1.2", 1, 2}, {"daikara", 1, 2}, {"old-forest", 1, 4}, {"dreadfell", 1, 8}, {"reknor", 1, 2}, }, + moddable_attachement_spots = { base=128, + female = { + head = {x=66, y=19}, + back = {x=66, y=51}, + hand1 = {x=19, y=78}, + hand2 = {x=110, y=78}, + }, + male = { + head = {x=65, y=20}, + back = {x=65, y=51}, + hand1 = {x=20, y=78}, + hand2 = {x=110, y=78}, + }, + }, cosmetic_unlock = { cosmetic_race_human_redhead = { {name="Redhead [donator only]", donator=true, on_actor=function(actor) if actor.moddable_tile then actor.moddable_tile_base = "base_redhead_01.png" actor.moddable_tile_ornament={male="beard_redhead_02"} end end, check=function(birth) return birth.descriptors_by_type.sex == "Male" end}, diff --git a/game/modules/tome/data/birth/races/human.lua b/game/modules/tome/data/birth/races/human.lua index ee4e36c5b9ddbbb8d933705f7172c718fa9c6eec..20f1dded9fbc20b3d64007911e0f9436b54b1f72 100644 --- a/game/modules/tome/data/birth/races/human.lua +++ b/game/modules/tome/data/birth/races/human.lua @@ -56,6 +56,20 @@ newBirthDescriptor{ }, random_escort_possibilities = { {"tier1.1", 1, 2}, {"tier1.2", 1, 2}, {"daikara", 1, 2}, {"old-forest", 1, 4}, {"dreadfell", 1, 8}, {"reknor", 1, 2}, }, + moddable_attachement_spots = { base=128, + female = { + head = {x=64, y=6}, + back = {x=66, y=34}, + hand1 = {x=38, y=66}, + hand2 = {x=90, y=66}, + }, + male = { + head = {x=60, y=6}, + back = {x=64, y=31}, + hand1 = {x=31, y=66}, + hand2 = {x=95, y=66}, + }, + }, cosmetic_unlock = { cosmetic_race_human_redhead = { {name="Redhead [donator only]", donator=true, on_actor=function(actor) if actor.moddable_tile then actor.moddable_tile_base = "base_redhead_01.png" end end}, diff --git a/game/modules/tome/data/gfx/particles/bolt_fire.lua b/game/modules/tome/data/gfx/particles/bolt_fire.lua index dcaaa7de560a11c05119d4d01d680d33b940131d..dbf63c6623981e1a7701f54a496e207fe0a64a64 100644 --- a/game/modules/tome/data/gfx/particles/bolt_fire.lua +++ b/game/modules/tome/data/gfx/particles/bolt_fire.lua @@ -24,12 +24,15 @@ if core.shader.active(4) then use_shader = {type="fireball"} base_size = 64 +local basedir = math.atan2(ty or 1, tx or 0) +local dir = math.deg(basedir) + return { - system_rotation = rng.range(0,359), system_rotationv = 3, + system_rotation = 0 or dir, system_rotationv = 0, generator = function() return { life = 1000, - size = 40, sizev = 0, sizea = 0, + size = 80, sizev = 0, sizea = 0, x = 0, xv = 0, xa = 0, y = 0, yv = 0, ya = 0, @@ -45,7 +48,7 @@ end, }, function(self) self.ps:emit(1) end, -1 +1, "particles_images/fireball" -------------------------------------------------------------------------------------- diff --git a/game/modules/tome/data/gfx/particles/fireflash.lua b/game/modules/tome/data/gfx/particles/fireflash.lua index c05c585c01f41039b689fb55a65ad1e348e1b31e..c50593553d1b03120d4ebd60896a98e3beeb707b 100644 --- a/game/modules/tome/data/gfx/particles/fireflash.lua +++ b/game/modules/tome/data/gfx/particles/fireflash.lua @@ -21,13 +21,12 @@ -- Advanced shaders -------------------------------------------------------------------------------------- if core.shader.active(4) then -use_shader = {type="fireball"} +use_shader = {type="fireboom"} base_size = 64 local nb = 0 return { - system_rotation = rng.range(0,359), system_rotationv = 5, generator = function() return { life = 16, @@ -50,7 +49,7 @@ function(self) end nb = nb + 1 end, -1 +1, "particles_images/fireboom" -------------------------------------------------------------------------------------- diff --git a/game/modules/tome/data/gfx/particles/firetrail.lua b/game/modules/tome/data/gfx/particles/firetrail.lua index 0bfc9ca511ce45e12589b49075eb966b2fe02b63..c9cd7578952108b0e0bc8b22396f9ab8e9296ec5 100644 --- a/game/modules/tome/data/gfx/particles/firetrail.lua +++ b/game/modules/tome/data/gfx/particles/firetrail.lua @@ -44,9 +44,11 @@ use_shader = {type="fireball"} base_size = 64 local nb = 0 +local basedir = math.atan2(ty or 1, tx or 0) +local dir = math.deg(basedir) return { - system_rotation = rng.range(0,359), system_rotationv = 3, + system_rotation = 0 or dir, system_rotationv = 0, generator = function() return { life = 10, @@ -69,7 +71,7 @@ function(self) end nb = nb + 1 end, -1 +1, "particles_images/fireball" -------------------------------------------------------------------------------------- diff --git a/game/modules/tome/data/gfx/particles/shader_shield.lua b/game/modules/tome/data/gfx/particles/shader_shield.lua index a6ddd812934e64476253070252da417b88a38659..3573a900d415b4642106174b050b7800518e0f7d 100644 --- a/game/modules/tome/data/gfx/particles/shader_shield.lua +++ b/game/modules/tome/data/gfx/particles/shader_shield.lua @@ -22,7 +22,7 @@ base_size = 64 local r = 1 local g = 1 local b = 1 -local a = 1 +local a = a or 1 return { generator = function() return { @@ -30,8 +30,8 @@ return { generator = function() life = 10, size = 2*38 * (size_factor or 1), sizev = 0, sizea = 0, - x = (x or 0) * engine.Map.tile_w, xv = 0, xa = 0, - y = (y or 0) * engine.Map.tile_h, yv = 0, ya = 0, + x = (x or 0) * 64, xv = 0, xa = 0, + y = (y or 0) * 64, yv = 0, ya = 0, dir = 0, dirv = dirv, dira = 0, vel = 0, velv = 0, vela = 0, diff --git a/game/modules/tome/data/gfx/particles/shader_wings.lua b/game/modules/tome/data/gfx/particles/shader_wings.lua index cfc8ebbf023935910d35b4d487f0470a7069e1cf..4b4b442c78da8544baf08b9638f4a91efd08e4b0 100644 --- a/game/modules/tome/data/gfx/particles/shader_wings.lua +++ b/game/modules/tome/data/gfx/particles/shader_wings.lua @@ -33,8 +33,8 @@ return { generator = function() life = life or 10, size = 64 * (size_factor or 1), sizev = 0, sizea = 0, - x = x or 0, xv = 0, xa = 0, - y = y or -25, yv = 0, ya = 0, + x = (x or 0) * 32, xv = 0, xa = 0, + y = (y or -0.781) * 32, yv = 0, ya = 0, dir = 0, dirv = dirv, dira = 0, vel = 0, velv = 0, vela = 0, diff --git a/game/modules/tome/data/gfx/particles_images/fireball.png b/game/modules/tome/data/gfx/particles_images/fireball.png new file mode 100644 index 0000000000000000000000000000000000000000..2a7c146f95771b22b2c3fe0ec23a1ae8d572299c Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/fireball.png differ diff --git a/game/modules/tome/data/gfx/shaders/fireball.frag b/game/modules/tome/data/gfx/shaders/fireball.frag index 7dd6071142d626694e8752b5c32b25722b922d9e..176ce1940559671b573da269a0e71360e346f8bf 100644 --- a/game/modules/tome/data/gfx/shaders/fireball.frag +++ b/game/modules/tome/data/gfx/shaders/fireball.frag @@ -1,244 +1,230 @@ -// Fireball -// Awd -// @AlexWDunn +uniform sampler2D tex; +uniform float tick; +uniform float time_factor; -#ifdef GL_ES -precision highp float; -#endif + +vec4 permute( vec4 x ) { -uniform float tick; + return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 ); -#define saturate(oo) clamp(oo, 0.0, 1.0) - -// Quality Settings -#define MarchSteps 6 - -// Scene Settings -#define ExpPosition vec3(0.0) -#define Radius 2.0 -#define Background vec4(0.0, 0.0, 0.0, 0.0) - -// Noise Settings -#define NoiseSteps 4 -#define NoiseAmplitude 0.15 -#define NoiseFrequency 1.0 -#define Animation vec3(0.0, -3.0, 0.5) - -uniform float speed; - -// Colour Gradient -uniform vec4 color1; -uniform vec4 color2; -uniform vec4 color3; -uniform vec4 color4; -/* -#define color1 vec4(0.0, 0.0, 0.0, 0.0) -#define color2 vec4(0.5, 0.2, 0.0, 0.5) -#define color3 vec4(0.7, 0.2, 0.0, 0.7) -#define color4 vec4(1.0, 0.8, 0.0, 1.0) -//*/ -/* -#define color1 vec4(0.0, 0.0, 0.0, 0.0) -#define color2 vec4(.0, .0, 1.0, 0.3) -#define color3 vec4(0.0, 1.03, 1.0, 0.5) -#define color4 vec4(0.05, 0.02, 0.2, 0.8) -*/ - -/* Bright fel-green -#define color1 vec4(1.0, 1.0, 1.0, 1.0) -#define color2 vec4(1.0, 0.8, 0.2, 1.0) -#define color3 vec4(1.0, 0.8, 0.0, 1.0) -#define color4 vec4(0.6, 0.6, 0.1, 1.0) -*/ - -// Description : Array and textureless GLSL 2D/3D/4D simplex -// noise functions. -// Author : Ian McEwan, Ashima Arts. -// Maintainer : ijm -// Lastmod : 20110822 (ijm) -// License : Copyright (C) 2011 Ashima Arts. All rights reserved. -// Distributed under the MIT License. See LICENSE file. -// https://github.com/ashima/webgl-noise -// - -vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } -vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } -vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); } -vec4 taylorInvSqrt(vec4 r){ return 1.79284291400159 - 0.85373472095314 * r; } - -float snoise(vec3 v) -{ - const vec2 C = vec2(1.0/6.0, 1.0/3.0); - const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); +} + +vec4 taylorInvSqrt( vec4 r ) { + + return 1.79284291400159 - 0.85373472095314 * r; + +} + +float snoise( vec3 v ) { + + const vec2 C = vec2( 1.0 / 6.0, 1.0 / 3.0 ); + const vec4 D = vec4( 0.0, 0.5, 1.0, 2.0 ); // First corner - vec3 i = floor(v + dot(v, C.yyy)); - vec3 x0 = v - i + dot(i, C.xxx); + + vec3 i = floor( v + dot( v, C.yyy ) ); + vec3 x0 = v - i + dot( i, C.xxx ); // Other corners - vec3 g = step(x0.yzx, x0.xyz); + + vec3 g = step( x0.yzx, x0.xyz ); vec3 l = 1.0 - g; - vec3 i1 = min(g.xyz, l.zxy); - vec3 i2 = max(g.xyz, l.zxy); - vec3 x1 = x0 - i1 + C.xxx; - vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y - vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y + vec3 i1 = min( g.xyz, l.zxy ); + vec3 i2 = max( g.xyz, l.zxy ); + + vec3 x1 = x0 - i1 + 1.0 * C.xxx; + vec3 x2 = x0 - i2 + 2.0 * C.xxx; + vec3 x3 = x0 - 1. + 3.0 * C.xxx; // Permutations - i = mod289(i); - vec4 p = permute( permute( permute( i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); - // Gradients: 7x7 points over a square, mapped onto an octahedron. - // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) - float n_ = 0.142857142857; // 1.0/7.0 - vec3 ns = n_ * D.wyz - D.xzx; - vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) + i = mod( i, 289.0 ); + vec4 p = permute( permute( permute( + i.z + vec4( 0.0, i1.z, i2.z, 1.0 ) ) + + i.y + vec4( 0.0, i1.y, i2.y, 1.0 ) ) + + i.x + vec4( 0.0, i1.x, i2.x, 1.0 ) ); + + // Gradients + // ( N*N points uniformly over a square, mapped onto an octahedron.) + + float n_ = 1.0 / 7.0; // N=7 - vec4 x_ = floor(j * ns.z); - vec4 y_ = floor(j - 7.0 * x_); // mod(j,N) + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor( p * ns.z *ns.z ); // mod(p,N*N) + + vec4 x_ = floor( j * ns.z ); + vec4 y_ = floor( j - 7.0 * x_ ); // mod(j,N) vec4 x = x_ *ns.x + ns.yyyy; vec4 y = y_ *ns.x + ns.yyyy; + vec4 h = 1.0 - abs( x ) - abs( y ); - vec4 h = 1.0 - abs(x) - abs(y); - vec4 b0 = vec4(x.xy, y.xy); - vec4 b1 = vec4(x.zw, y.zw); + vec4 b0 = vec4( x.xy, y.xy ); + vec4 b1 = vec4( x.zw, y.zw ); - vec4 s0 = floor(b0) * 2.0 + 1.0; - vec4 s1 = floor(b1) * 2.0 + 1.0; - vec4 sh = -step(h, vec4(0.0)); + + vec4 s0 = floor( b0 ) * 2.0 + 1.0; + vec4 s1 = floor( b1 ) * 2.0 + 1.0; + vec4 sh = -step( h, vec4( 0.0 ) ); vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; - vec3 p0 = vec3(a0.xy, h.x); - vec3 p1 = vec3(a0.zw, h.y); - vec3 p2 = vec3(a1.xy, h.z); - vec3 p3 = vec3(a1.zw, h.w); + vec3 p0 = vec3( a0.xy, h.x ); + vec3 p1 = vec3( a0.zw, h.y ); + vec3 p2 = vec3( a1.xy, h.z ); + vec3 p3 = vec3( a1.zw, h.w ); - //Normalise gradients - vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + // Normalise gradients + vec4 norm = taylorInvSqrt( vec4( dot( p0, p0 ), dot( p1, p1 ), dot( p2, p2 ), dot( p3, p3 ) ) ); p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w; // Mix final noise value - vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + + vec4 m = max( 0.6 - vec4( dot( x0, x0 ), dot( x1, x1 ), dot( x2, x2 ), dot( x3, x3 ) ), 0.0 ); m = m * m; + return 42.0 * dot( m*m, vec4( dot( p0, x0 ), dot( p1, x1 ), + dot( p2, x2 ), dot( p3, x3 ) ) ); - return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3))); -} +} -float Turbulence(vec3 position, float minFreq, float maxFreq, float qWidth) +vec2 snoise2(vec3 pos) { - float value = 0.0; - float cutoff = clamp(0.5/qWidth, 0.0, maxFreq); - float fade; - float fOut = minFreq; - - for(int i=NoiseSteps ; i>=0 ; i++) - { - if(fOut >= 0.5 * cutoff) break; - - fOut *= 2.0; - value += abs(snoise(position * fOut))/fOut; - } - - fade = clamp(2.0 * (cutoff-fOut)/cutoff, 0.0, 1.0); - value += fade * abs(snoise(position * fOut))/fOut; - - return 1.0-value; + return vec2(snoise(pos), snoise(pos + vec3(0.0, 0.0, 1.0))); } -float SphereDist(vec3 position) +float GetFireDelta(float currTime, vec2 pos, float freqMult, float stretchMult, float scrollSpeed, float evolutionSpeed) { - return length(position - ExpPosition) - Radius; -} + //firewall + float delta = 0; +// pos.y += (1.0 - pos.y) * 0.5; + //pos.y += 0.5; + pos.y /= stretchMult; + pos *= freqMult; + pos.y += currTime * scrollSpeed; +// pos.y -= currTime * 3.0; -vec4 Shade(float distance) -{ - float c1 = saturate(distance*5.0 + 0.5); - float c2 = saturate(distance*5.0); - float c3 = saturate(distance*3.4 - 0.5); - vec4 a = mix(color1,color2, c1); - vec4 b = mix(a, color3, c2); - return mix(b, color4, c3); -} - + delta += snoise(vec3(pos * 1.0, currTime * 1.0 * evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 2.0, currTime * 2.0 * evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 4.0, currTime * 4.0 * evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 8.0, currTime * 8.0 * evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 16.0, currTime * 16.0 * evolutionSpeed)) * 0.5; -// Draws the scene -float RenderScene(vec3 position, out float distance) + return delta; +} +vec4 GetFireColor(float currTime, vec2 pos, float freqMult, float stretchMult, float ampMult) { - float time = tick / speed; - float noise = Turbulence(position * NoiseFrequency + Animation*time, 0.1, 1.5, 0.03) * NoiseAmplitude; - noise = saturate(abs(noise)); - distance = SphereDist(position) - noise; - - return noise; + float delta = GetFireDelta(currTime, pos, freqMult, stretchMult, 3.0, 0.5); + delta *= min(1.0, max(0.0, 1.0 * (1.0 - pos.y))); + delta *= min(1.0, max(0.0, 1.0 * (0.0 + pos.y))); + vec2 displacedPoint = pos + vec2(0, delta * ampMult); + displacedPoint.y = min(0.99, displacedPoint.y); + displacedPoint.y = max(0.01, displacedPoint.y); + + return texture2D(tex, displacedPoint); } -// Basic ray marching method. -vec4 March(vec3 rayOrigin, vec3 rayStep) +vec4 GetCheckboardColor(vec2 pos) { - vec3 position = rayOrigin; - - float distance; - float displacement; - - for(int step = MarchSteps; step >=0 ; --step) + vec4 col = vec4(0.0, 0.0, 0.0, 0.0); + if(pos.x > 0.0 && pos.x < 1.0 && pos.y > 0.0 && pos.y < 1.0) { - displacement = RenderScene(position, distance); - - if(distance < 0.05) break; - - position += rayStep * distance; + if(mod(pos.x, 0.1) < 0.05 ^^ mod(pos.y, 0.1) < 0.05) + col = vec4(pos.x, pos.y, 0.0, 1.0); + else + col = vec4(0.0, 0.0, 0.0, 1.0); } - return mix(Shade(displacement), Background, float(distance >= 0.5)); + return col; } -bool IntersectSphere(vec3 ro, vec3 rd, vec3 pos, float radius, out vec3 intersectPoint) +vec4 GetFireBallColor(float currTime, vec2 pos, float freqMult, float stretchMult, float ampMult, float power, float radius1, float radius2, vec2 velocity, float paletteCoord) { - vec3 relDistance = (ro - pos); - - float b = dot(relDistance, rd); - float c = dot(relDistance, relDistance) - radius*radius; - float d = b*b - c; + float pi = 3.141593; + vec2 velocityDir = vec2(1, 0); + if(length(velocity) > 0) + velocityDir = velocity / length(velocity); + vec2 velocityPerp = vec2(-velocityDir.y, velocityDir.x); - intersectPoint = ro + rd*(-b - sqrt(d)); - - return d >= 0.0; + float ang = atan(dot(pos, velocityPerp), dot(pos, velocityDir)); + vec4 fireballColor = vec4(0.0, 0.0, 0.0, 0.0); + if(length(pos) < radius1) + { + float sinAlpha = length(pos) / radius1; + float alpha = 0.0; + alpha = asin(sinAlpha); + + vec2 sphericalProjectedCoord = vec2(0.5, 0.5) + pos * (alpha / (3.141592 / 2.0)) / length(pos); + //fireballColor = GetCheckboardColor(sphericalProjectedCoord); + float delta = GetFireDelta(currTime, sphericalProjectedCoord, freqMult * 0.1, 1.0, 0.0, 1.5) * (1.0 - pow(length(pos) / radius1, 3.0)) * 0.5; + + float verticalPos = 0.99 - delta * delta; + verticalPos = min(0.99, verticalPos); + verticalPos = max(0.01, verticalPos); + + fireballColor = texture2D(tex, vec2(0.75, verticalPos)); + fireballColor.a = 1.0; + }else + { + float dist = (length(pos) - radius1) / (radius2 - radius1); + + + vec2 polarPos = vec2(ang, dist); + + float dstAng = (1.0 - pow(1.0 - abs(polarPos.x / pi), 4.0)) * pi; + if(polarPos.x < 0.0) dstAng = -dstAng; + + polarPos.x = dstAng + (polarPos.x - dstAng) * exp(-polarPos.y * 2.0); + polarPos.y *= 0.15 + 1.4 * pow(abs(polarPos.x) / pi, 2.0); + //polarPos.y *= exp(-(1.0 - pow(abs(polarPos.x) / pi, 2.0)) * 2.0); + + //polarPos.x = (1.0 - pow(1.0 - abs(polarPos.x / pi), 1.0)) * pi; + //polarPos.x *= 2.0 - 1.0 * exp(-(1.0 - pow(1.0 - abs(polarPos.x) / pi, 3.0)) * 5.0 * polarPos.y); + //polarPos.x *= 1.0 + 1.0 * exp(-abs(polarPos.x));//0.1 + 0.9 * (1.0 - pow(1.0 - abs(polarPos.x) / pi, 0.5)); + + + vec2 planarPos = vec2((polarPos.x + pi) / (2.0 * pi), 1.0 - polarPos.y * 1.0); + + if(planarPos.y > 0.0) + { + //return GetCheckboardColor(planarPos); + + float delta = + GetFireDelta(currTime, planarPos, freqMult, stretchMult, 2.5, 0.5) * (1.0 - planarPos.x) + + GetFireDelta(currTime, vec2(planarPos.x - 1.0, planarPos.y), freqMult, stretchMult, 2.5, 0.5) * planarPos.x; + + delta *= min(1.0, max(0.0, 1.0 * (1.0 - planarPos.y))); + delta *= min(1.0, max(0.0, 1.0 * (0.0 + planarPos.y))); + + float verticalPos = planarPos.y + delta * ampMult; + verticalPos = min(0.99, verticalPos); + verticalPos = max(0.01, verticalPos); + + fireballColor = texture2D(tex, vec2(0.25, verticalPos)); + } + } + return fireballColor; } void main(void) { - vec2 resolution = vec2(640.0, 640.0); - //vec2 p = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0; - vec2 p = gl_TexCoord[0].xy - 0.5; + vec2 radius = gl_TexCoord[0].xy - vec2(0.5, 0.5); - p.x *= resolution.x/resolution.y; - - float rotx = 0.00; - float roty = 0.0; - float zoom = 9.0; - - // camera - vec3 ro = zoom * normalize(vec3(cos(roty), cos(rotx), sin(roty))); - vec3 ww = normalize(vec3(0.0, 0.0, 0.0) - ro); - vec3 uu = normalize(cross( vec3(0.0, 1.0, 0.0), ww)); - vec3 vv = normalize(cross(ww, uu)); - vec3 rd = normalize(p.x*uu + p.y*vv + 1.5*ww); - - vec4 col = Background; - - vec3 origin; + //on-hit wobbling effect + float radiusLen = length(radius); + - if(IntersectSphere(ro, rd, ExpPosition, Radius + NoiseAmplitude*6.0, origin)) - { - col = March(origin, rd); - } + float ballRadius = 0.1; + float coronaWidth = 0.05; + + vec4 c; + c = GetFireBallColor(tick / time_factor + 0.0 , radius, 6, 15.0, 1, 2, ballRadius, ballRadius + coronaWidth, vec2(1, 0), 0.75); + c.a *= gl_Color.a; - gl_FragColor = col * gl_Color; + gl_FragColor = c; } diff --git a/game/modules/tome/data/gfx/shaders/fireball.lua b/game/modules/tome/data/gfx/shaders/fireball.lua index 39541c01f31ed85429b19e0ab8ba98e5fe1863af..33452d6812d4209536a33d40776fb183d4fdf212 100644 --- a/game/modules/tome/data/gfx/shaders/fireball.lua +++ b/game/modules/tome/data/gfx/shaders/fireball.lua @@ -21,13 +21,11 @@ return { frag = "fireball", vert = nil, args = { - resolution = resolution or {128,128}, - zoom = zoom or 1, - speed = 400, - color1 = {1.0, 1.0, 1.0, 0.85}, - color2 = {1.0, 0.9, 0.1, 0.85}, - color3 = {1.0, 0.4, 0.0, 0.85}, - color4 = {0.6, 0.1, 0.0, 0.85}, + tex = { texture = 0 }, + color = color or {0.4, 0.7, 1.0}, + time_factor = time_factor or 4000, + ellipsoidalFactor = ellipsoidalFactor or 1.0, --1 is perfect circle, >1 is ellipsoidal + oscillationSpeed = oscillationSpeed or 0.0, --oscillation between ellipsoidal and spherical form }, clone = false, } diff --git a/game/modules/tome/data/gfx/shaders/flamehands.frag b/game/modules/tome/data/gfx/shaders/flamehands.frag new file mode 100644 index 0000000000000000000000000000000000000000..97cd0ce7236748bdbee1f4a977d7758902794754 --- /dev/null +++ b/game/modules/tome/data/gfx/shaders/flamehands.frag @@ -0,0 +1,219 @@ +uniform sampler2D tex; +uniform float tick; +uniform float time_factor; + + +vec4 permute( vec4 x ) { + + return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 ); + +} + +vec4 taylorInvSqrt( vec4 r ) { + + return 1.79284291400159 - 0.85373472095314 * r; + +} + +float snoise( vec3 v ) { + + const vec2 C = vec2( 1.0 / 6.0, 1.0 / 3.0 ); + const vec4 D = vec4( 0.0, 0.5, 1.0, 2.0 ); + + // First corner + + vec3 i = floor( v + dot( v, C.yyy ) ); + vec3 x0 = v - i + dot( i, C.xxx ); + + // Other corners + + vec3 g = step( x0.yzx, x0.xyz ); + vec3 l = 1.0 - g; + vec3 i1 = min( g.xyz, l.zxy ); + vec3 i2 = max( g.xyz, l.zxy ); + + vec3 x1 = x0 - i1 + 1.0 * C.xxx; + vec3 x2 = x0 - i2 + 2.0 * C.xxx; + vec3 x3 = x0 - 1. + 3.0 * C.xxx; + + // Permutations + + i = mod( i, 289.0 ); + vec4 p = permute( permute( permute( + i.z + vec4( 0.0, i1.z, i2.z, 1.0 ) ) + + i.y + vec4( 0.0, i1.y, i2.y, 1.0 ) ) + + i.x + vec4( 0.0, i1.x, i2.x, 1.0 ) ); + + // Gradients + // ( N*N points uniformly over a square, mapped onto an octahedron.) + + float n_ = 1.0 / 7.0; // N=7 + + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor( p * ns.z *ns.z ); // mod(p,N*N) + + vec4 x_ = floor( j * ns.z ); + vec4 y_ = floor( j - 7.0 * x_ ); // mod(j,N) + + vec4 x = x_ *ns.x + ns.yyyy; + vec4 y = y_ *ns.x + ns.yyyy; + vec4 h = 1.0 - abs( x ) - abs( y ); + + vec4 b0 = vec4( x.xy, y.xy ); + vec4 b1 = vec4( x.zw, y.zw ); + + + vec4 s0 = floor( b0 ) * 2.0 + 1.0; + vec4 s1 = floor( b1 ) * 2.0 + 1.0; + vec4 sh = -step( h, vec4( 0.0 ) ); + + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + + vec3 p0 = vec3( a0.xy, h.x ); + vec3 p1 = vec3( a0.zw, h.y ); + vec3 p2 = vec3( a1.xy, h.z ); + vec3 p3 = vec3( a1.zw, h.w ); + + // Normalise gradients + + vec4 norm = taylorInvSqrt( vec4( dot( p0, p0 ), dot( p1, p1 ), dot( p2, p2 ), dot( p3, p3 ) ) ); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + + vec4 m = max( 0.6 - vec4( dot( x0, x0 ), dot( x1, x1 ), dot( x2, x2 ), dot( x3, x3 ) ), 0.0 ); + m = m * m; + return 42.0 * dot( m*m, vec4( dot( p0, x0 ), dot( p1, x1 ), + dot( p2, x2 ), dot( p3, x3 ) ) ); + +} + +vec2 snoise2(vec3 pos) +{ + return vec2(snoise(pos), snoise(pos + vec3(0.0, 0.0, 1.0))); +} + +float GetFireDelta(float currTime, vec2 pos, float freqMult, float stretchMult, float scrollSpeed, float evolutionSpeed) +{ + //firewall + float delta = 0; +// pos.y += (1.0 - pos.y) * 0.5; + //pos.y += 0.5; + pos.y /= stretchMult; + pos *= freqMult; + pos.y += currTime * scrollSpeed; +// pos.y -= currTime * 3.0; + + + delta += snoise(vec3(pos * 1.0, currTime * 1.0 * evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 2.0, currTime * 2.0 * evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 4.0, currTime * 4.0 * evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 8.0, currTime * 8.0 * evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 16.0, currTime * 16.0 * evolutionSpeed)) * 0.5; + + return delta; +} +vec4 GetFireColor(float currTime, vec2 pos, float freqMult, float stretchMult, float ampMult) +{ + float delta = GetFireDelta(currTime, pos, freqMult, stretchMult, 3.0, 0.5); + delta *= min(1.0, max(0.0, 1.0 * (1.0 - pos.y))); + delta *= min(1.0, max(0.0, 1.0 * (0.0 + pos.y))); + vec2 displacedPoint = pos + vec2(0, delta * ampMult); + displacedPoint.y = min(0.99, displacedPoint.y); + displacedPoint.y = max(0.01, displacedPoint.y); + + return texture2D(tex, displacedPoint); +} + +vec4 GetCheckboardColor(vec2 pos) +{ + vec4 col = vec4(0.0, 0.0, 0.0, 0.0); + if(pos.x > 0.0 && pos.x < 1.0 && pos.y > 0.0 && pos.y < 1.0) + { + if(mod(pos.x, 0.1) < 0.05 ^^ mod(pos.y, 0.1) < 0.05) + col = vec4(pos.x, pos.y, 0.0, 1.0); + else + col = vec4(0.0, 0.0, 0.0, 1.0); + } + return col; +} + +vec4 GetFireCandleColor(float currTime, vec2 pos, float freqMult, float stretchMult, float ampMult, float power, float radius, float flameHeight, float paletteCoord) +{ + float pi = 3.141593; + + vec4 fireballColor = vec4(0.0, 0.0, 0.0, 0.0); + if(length(pos) < radius) + { + float ang = atan(pos.x, pos.y); + + float sinAlpha = length(pos) / radius; + float alpha = 0.0; + alpha = asin(sinAlpha); + + vec2 sphericalProjectedCoord = vec2(0.5, 0.5) + pos * (alpha / (3.141592 / 2.0)) / length(pos); + //fireballColor = GetCheckboardColor(sphericalProjectedCoord); + float delta = GetFireDelta(currTime, sphericalProjectedCoord, freqMult * 0.3, 1.0, 0.0, 1.5) * (1.0 - pow(length(pos) / radius, 3.0)) * 0.5; + + float verticalPos = 0.99 - delta * delta; + verticalPos = min(0.99, verticalPos); + verticalPos = max(0.01, verticalPos); + + fireballColor = texture2D(tex, vec2(0.75, verticalPos)); + fireballColor.a = 1.0; + }else + { + float bottomPos = 0; + + pos.x += cos(pos.y * 20.0 + currTime * 20.0) * 0.05 * (1.0 - pow(1.0 - max(0.0, min((-radius - pos.y) * 2.0, 1.0)), 2.0)); + if(abs(pos.x) < radius) + { + bottomPos = -sqrt(radius * radius - pos.x * pos.x); +// float ratio = (pos.y - bottomPos) / (bottomPos * (flameHeight / radius) - bottomPos); + float ratio = (pos.y - bottomPos) / (bottomPos - flameHeight - radius - bottomPos) * (1.0 + 0.5 * pow(abs(pos.x) / radius, 4.0)); + + vec2 planarPos = vec2(pos.x / radius, 1.0 - ratio); + planarPos.x *= 1.0 + (1.0 - planarPos.y) * 2.5; + planarPos.x = planarPos.x / 2.0 + 0.5; + + if(planarPos.x > 0.0 && planarPos.x < 1.0 && planarPos.y > 0.0 && planarPos.y < 1.0) + { + float delta = GetFireDelta(currTime, planarPos, freqMult * 0.2, 0.6, 1.0, 0.2); + delta *= min(1.0, max(0.0, 1.0 * (1.0 - planarPos.y))); + delta *= min(1.0, max(0.0, 1.0 * (0.0 + planarPos.y))); + + float verticalPos = pow(planarPos.y, 0.7) + delta * 1.0; + verticalPos = min(0.99, verticalPos); + verticalPos = max(0.01, verticalPos); + + //fireballColor = GetCheckboardColor(planarPos); + fireballColor = texture2D(tex, vec2(0.25, verticalPos)); + } + } + } + return fireballColor; +} + +void main(void) +{ + vec2 radius = gl_TexCoord[0].xy - vec2(0.5, 0.8); + + //on-hit wobbling effect + float radiusLen = length(radius); + + + float ballRadius = 0.1; + float flameHeight = 0.6; + + vec4 c; + c = GetFireCandleColor(tick / time_factor + 0.0 , radius, 2, 3.0, 1, 2, ballRadius, flameHeight, 0.75); + c.a *= gl_Color.a; + //c.a += 0.5; + + gl_FragColor = c; +} diff --git a/game/modules/tome/data/gfx/shaders/flamehands.lua b/game/modules/tome/data/gfx/shaders/flamehands.lua new file mode 100644 index 0000000000000000000000000000000000000000..ece3f346c22efaa3f807ea27cab27552331aeb52 --- /dev/null +++ b/game/modules/tome/data/gfx/shaders/flamehands.lua @@ -0,0 +1,28 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009, 2010, 2011, 2012, 2013 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 = "flamehands", + vert = nil, + args = { + tex = { texture = 0 }, + time_factor = time_factor or 700, + }, + clone = false, +} diff --git a/game/modules/tome/data/talents/spells/enhancement.lua b/game/modules/tome/data/talents/spells/enhancement.lua index 85cda0cb2b5e18482f66eb59e3506adb5e807f6b..f356b447af4167eaf2465e7ece0f3e945e4394db 100644 --- a/game/modules/tome/data/talents/spells/enhancement.lua +++ b/game/modules/tome/data/talents/spells/enhancement.lua @@ -30,13 +30,22 @@ newTalent{ getFireDamageIncrease = function(self, t) return self:combatTalentSpellDamage(t, 5, 14) end, activate = function(self, t) game:playSoundNear(self, "talents/fire") - return { + local ret = { + particle = particle, dam = self:addTemporaryValue("melee_project", {[DamageType.FIRE] = t.getFireDamage(self, t)}), per = self:addTemporaryValue("inc_damage", {[DamageType.FIRE] = t.getFireDamageIncrease(self, t)}), sta = self:addTemporaryValue("stamina_regen_on_hit", self:getTalentLevel(t) / 3), } + if core.shader.active(4) then + local slow = rng.percent(50) + local h1x, h1y = self:attachementSpot("hand1", true) if h1x then ret.particle1 = self:addParticles(Particles.new("shader_shield", 1, {img="fireball", a=0.7, size_factor=0.4, x=h1x, y=h1y-0.1}, {type="flamehands", time_factor=slow and 700 or 1000})) end + local h2x, h2y = self:attachementSpot("hand2", true) if h2x then ret.particle2 = self:addParticles(Particles.new("shader_shield", 1, {img="fireball", a=0.7, size_factor=0.4, x=h2x, y=h2y-0.1}, {type="flamehands", time_factor=not slow and 700 or 1000})) end + end + return ret end, deactivate = function(self, t, p) + if p.particle1 then self:removeParticles(p.particle1) end + if p.particle2 then self:removeParticles(p.particle2) end self:removeTemporaryValue("melee_project", p.dam) self:removeTemporaryValue("inc_damage", p.per) self:removeTemporaryValue("stamina_regen_on_hit", p.sta) @@ -89,13 +98,21 @@ newTalent{ getIceDamageIncrease = function(self, t) return self:combatTalentSpellDamage(t, 5, 14) end, activate = function(self, t) game:playSoundNear(self, "talents/lightning") - return { + local ret = { dam = self:addTemporaryValue("melee_project", {[DamageType.LIGHTNING_DAZE] = t.getIceDamage(self, t)}), per = self:addTemporaryValue("inc_damage", {[DamageType.LIGHTNING] = t.getIceDamageIncrease(self, t)}), man = self:addTemporaryValue("mana_regen_on_hit", self:getTalentLevel(t) / 3), } + if core.shader.active(4) then + local slow = rng.percent(50) + local h1x, h1y = self:attachementSpot("hand1", true) if h1x then ret.particle1 = self:addParticles(Particles.new("shader_shield", 1, {img="lightningwings", a=0.7, size_factor=0.4, x=h1x, y=h1y-0.1}, {type="flamehands", time_factor=slow and 700 or 1000})) end + local h2x, h2y = self:attachementSpot("hand2", true) if h2x then ret.particle2 = self:addParticles(Particles.new("shader_shield", 1, {img="lightningwings", a=0.7, size_factor=0.4, x=h2x, y=h2y-0.1}, {type="flamehands", time_factor=not slow and 700 or 1000})) end + end + return ret end, deactivate = function(self, t, p) + if p.particle1 then self:removeParticles(p.particle1) end + if p.particle2 then self:removeParticles(p.particle2) end self:removeTemporaryValue("melee_project", p.dam) self:removeTemporaryValue("inc_damage", p.per) self:removeTemporaryValue("mana_regen_on_hit", p.man) diff --git a/game/modules/tome/data/talents/spells/wildfire.lua b/game/modules/tome/data/talents/spells/wildfire.lua index 42a6e791d13a62f5fd51ab0e6a0c44cb1410d336..48ad5bcf0570e73fc17cf5bb439e6e9cedc02d5d 100644 --- a/game/modules/tome/data/talents/spells/wildfire.lua +++ b/game/modules/tome/data/talents/spells/wildfire.lua @@ -124,7 +124,8 @@ newTalent{ local particle if core.shader.active(4) then - particle = self:addParticles(Particles.new("shader_wings", 1, {infinite=1, x=self.wings_x, y=self.wings_y})) + local bx, by = self:attachementSpot("back", true) + particle = self:addParticles(Particles.new("shader_wings", 1, {infinite=1, x=bx, y=by})) else particle = self:addParticles(Particles.new("wildfire", 1)) end diff --git a/game/modules/tome/data/zones/town-angolwen/npcs.lua b/game/modules/tome/data/zones/town-angolwen/npcs.lua index e156b501c43a2e005cf308a2a6f20754110c4f74..91ca2abca2795dcb308b89bab42e40fa6b6d12d7 100644 --- a/game/modules/tome/data/zones/town-angolwen/npcs.lua +++ b/game/modules/tome/data/zones/town-angolwen/npcs.lua @@ -94,7 +94,7 @@ newEntity{ define_as = "SUPREME_ARCHMAGE_LINANIIL", [Talents.T_KEEN_SENSES]=5, [Talents.T_PREMONITION]=5, }, - wings_x = 2, wings_y = -33, + attachement_spots = { back={x=35/64, y=1/64} }, resolvers.sustains_at_birth(), can_talk = "angolwen-leader", diff --git a/game/modules/tome/dialogs/Birther.lua b/game/modules/tome/dialogs/Birther.lua index 50a439022a058b8aea3f3ff6694b72491ef62572..5cca8e2cc5653e80e373ec81f198a720ea148f0d 100644 --- a/game/modules/tome/dialogs/Birther.lua +++ b/game/modules/tome/dialogs/Birther.lua @@ -1035,6 +1035,7 @@ function _M:setTile(f, w, h, last) self.actor:removeAllMOs() if not f then if not self.has_custom_tile then + local dbr = self.birth_descriptor_def.race[self.descriptors_by_type.race or "Human"] local dr = self.birth_descriptor_def.subrace[self.descriptors_by_type.subrace or "Cornac"] local ds = self.birth_descriptor_def.sex[self.descriptors_by_type.sex or "Female"] self.actor.image = "player/"..(self.descriptors_by_type.subrace or "Cornac"):lower():gsub("[^a-z0-9_]", "_").."_"..(self.descriptors_by_type.sex or "Female"):lower():gsub("[^a-z0-9_]", "_")..".png" @@ -1044,10 +1045,24 @@ function _M:setTile(f, w, h, last) self.actor.moddable_tile = dr.copy.moddable_tile self.actor.moddable_tile_base = dr.copy.moddable_tile_base self.actor.moddable_tile_ornament = dr.copy.moddable_tile_ornament + self.actor.attachement_spots = nil + local moddable_attachement_spots = dr.moddable_attachement_spots or dbr.moddable_attachement_spots + if moddable_attachement_spots then + local base = moddable_attachement_spots.base + local b = moddable_attachement_spots.all + if not b then b = self.actor.female and moddable_attachement_spots.female or moddable_attachement_spots.male end + local t = {} + self.actor.attachement_spots = t + for kind, d in pairs(b) do + t[kind] = {} + for o, p in pairs(d) do t[kind][o] = p / base end + end + end end else self.actor.make_tile = nil self.actor.moddable_tile = nil + self.actor.attachement_spots = nil if h > w then self.actor.image = "invis.png" self.actor.add_mos = {{image=f, display_h=2, display_y=-1}}