@@ -1,6 +1,124 @@ sorcery.vfx = {} local L = sorcery.lib + +sorcery.vfx.particle = { + flicker = { fw = 64 }; + crackle = { fw = 64 }; + sparking = { fw = 16 }; + spark = { fw = 16 }; + sputter = { fw = 16 }; + glitter = { fw = 16 }; + poof = { fw = 16 }; + fog = { fw = 16}; +} + +sorcery.vfx.show = function(def) + local sp = {} + local p + if def.kind then + p = sorcery.vfx.particle[def.kind] + local img = L.image(p.img or string.format('sorcery_%s.png', def.kind)) + if def.color then img = img:glow(def.color) end + if def.warp then img = def:warp(img) end + sp.texture = img:render() + else + sp.texture = def.texture + end + + sp.collisiondetection = def.collisiondetection + sp.collision_removal = def.collision_removal + sp.object_collision = def.object_collision + + local function q(bp, rp, ao, so) + ao = ao or bp + so = so or bp + local min, max + + if def[bp] or def[rp] then + local b = def[bp] or vector.new(0,0,0) + if def[rp] then + local r + if type(def[rp]) == 'number' then + r = vector.new(def[rp],def[rp],def[rp]) + else r = def[rp] end + min = vector.subtract(b, r) + max = vector.add (b, r) + else + min, max = b,b + end + else + local dflt = vector.new(0,0,0) + min, max = def['min'..bp] or dflt, def['max'..bp] or dflt + end + + if def.amount and def.amount > 1 then + sp['min'..ao], sp['max'..ao] = min, max + else + if vector.equals(min,max) then sp[so] = min else + local d = vector.subtract(max, min) + sp[so] = vector.add(min, vector.multiply(d, math.random())) + end + end + end + q('pos','radius') + q('vel','velrange', 'vel', 'velocity') + q('acc','accrange', 'acc', 'acceleration') + local vary = def.varysize or 0 + if def.amount and def.amount > 1 then + sp.amount = def.amount + sp.time = def.time or def.life or 1 + def.life = def.life or sp.time + + sp.minsize = def.minsize or ((def.size or 1) - vary) + sp.maxsize = def.maxsize or ((def.size or 1) + vary) + sp.attached = def.attached + else + if def.minsize and def.maxsize then + sp.size = def.minsize + ((def.maxsize - def.minsize) * math.random()) + elseif vary > 0 then + sp.size = ((def.size or 1) - vary) + (vary*2*math.random()) + else sp.size = def.size end + if def.attached then + sp.pos = vector.add( + vector.rotate(sp.pos, + {x = 0, y = def.attached:get_yaw() or def.attached:get_look_horizontal(), z = 0} + ), + def.attached:get_pos() + ) + + sp.velocity = vector.add(sp.velocity, def.attached:get_velocity()) + end + end + sp.node = def.node + sp.playername = def.playername + sp.vertical = def.vertical + sp.glow = def.glow + if def.life then + if sp.amount then + if def.varylife then + sp.minexptime = def.life - def.varylife + sp.maxexptime = def.life + def.varylife + else + sp.minexptime, sp.maxexptime = def.life, def.life + end + else + sp.expirationtime = def.life + end + end + if p and p.fw then + sp.animation = { + type = 'vertical_frames'; + aspect_w = p.fw, aspect_h = p.fh or p.fw; + length = (sp.maxexptime or sp.life or 1) + 0.1; + } + end + if sp.amount then + minetest.add_particlespawner(sp) + else + minetest.add_particle(sp) + end +end sorcery.vfx.glowspark = function(color) local spark = L.image('sorcery_spark.png') return spark:blit(spark:multiply(color)) @@ -10,30 +128,48 @@ local ofs = pos and function(x) return vector.add(pos,x) end or function(x) return x end local height = caster:get_properties().eye_height - minetest.add_particlespawner { - amount = 70 * strength; - time = duration or 1.5; + sorcery.vfx.show { + amount = 4 * (0.5+strength*0.5), time = duration or 1.5; + kind = 'spark', color = color, glow = 14, life = 0.5; + -- minpos = ofs({ x = 0.0, z = 0.6, y = height - 0.7}); + -- maxpos = ofs({ x = 0.4, z = 0.2, y = height - 0.3}); + pos = ofs(vector.new(0.2, height - 0.5, 0.4)), radius = 0.2; + vel = vector.new(0,0,0.8); + velrange = 0.7, acc = vector.new(0,0.5,0); + minsize = 5, maxsize = 10; attached = caster; - -- texture = L.image('sorcery_spark.png'):multiply(color):render(); - texture = sorcery.vfx.glowspark(color):render(); - minpos = ofs({ x = 0.0, z = 0.6, y = height*0.7}); - maxpos = ofs({ x = 0.4, z = 0.2, y = height*1.1}); - minvel = { x = -0.5, z = -0.5, y = -0.5}; - maxvel = { x = 0.5, z = 0.5, y = 0.5}; - minacc = { x = 0.0, z = 0.0, y = 0.5}; - maxacc = { x = 0.0, z = 0.0, y = 0.5}; - minsize = 0.4, maxsize = 0.8; - minexptime = 1, maxexptime = 1; - glow = 14; - animation = { - type = 'vertical_frames'; - aspect_w = 16; - aspect_h = 16; - length = 1.1; - }; + } + sorcery.vfx.show { + amount = 140 * strength, time = 0.3 + (duration or 1.5); + kind = 'sputter', color = color, glow = 14, life = 3; + pos = ofs(vector.new(0.2, height - 0.5, 0.5)), radius = 0.3; + vel = vector.new(0,0,1.2); + velrange = 0.4, acc = vector.new(0,0.5,-0.7); + minsize = 0.05, maxsize = 0.4; + attached = caster; } + -- minetest.add_particlespawner { + -- amount = 40 * strength; + -- time = duration or 1.5; + -- attached = caster; + -- -- texture = L.image('sorcery_spark.png'):multiply(color):render(); + -- texture = sorcery.vfx.glowspark(color):render(); + -- minvel = { x = -0.5, z = -0.5, y = -0.5}; + -- maxvel = { x = 0.5, z = 0.5, y = 0.5}; + -- minacc = { x = 0.0, z = 0.0, y = 0.5}; + -- maxacc = { x = 0.0, z = 0.0, y = 0.5}; + -- minsize = 0.2, maxsize = 1.5; + -- minexptime = 1, maxexptime = 1; + -- glow = 14; + -- animation = { + -- type = 'vertical_frames'; + -- aspect_w = 16; + -- aspect_h = 16; + -- length = 1.1; + -- }; + -- } end sorcery.vfx.body_sparkle = function(body,color,str,pos) local tex = L.image('sorcery_spark.png')