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))
end
sorcery.vfx.cast_sparkle = function(caster,color,strength,duration,pos)
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
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;
}
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')
local pi = tex:blit(tex:multiply(color)):render()
local ofs = pos
and function(x) return vector.add(pos,x) end
or function(x) return x end
return minetest.add_particlespawner {
amount = 25 * str;
time = 0.5;
attached = body;
minpos = ofs{x = -0.5, y = -0.5, z = -0.5};
maxpos = ofs{x = 0.5, y = 1.5, z = 0.5};
minacc = {x = -0.3, y = 0.0, z = 0.3};
maxacc = {x = -0.3, y = 0.0, z = 0.3};
minvel = {x = -0.6, y = -0.2, z = 0.6};
maxvel = {x = -0.6, y = 0.2, z = 0.6};
minexptime = 1.0;
maxexptime = 1.5;
texture = pi;
glow = 14;
animation = {
type = 'vertical_frames';
aspect_w = 16, aspect_h = 16;
length = 1.6;
};
}
end
sorcery.vfx.enchantment_sparkle = function(tgt,color)
local minvel, maxvel
if minetest.get_node(vector.add(tgt.under,{y=1,z=0,x=0})).name == 'air' then
minvel = vector.new(-0.3,0.3,-0.3) maxvel = vector.new(0.6,1.5,0.6);
else
local dir = vector.subtract(tgt.above,tgt.under)
minvel = vector.multiply(dir, 0.3)
maxvel = vector.multiply(dir, 1.2)
end
print(minetest.get_player_by_name('singleplayer'))
return minetest.add_particlespawner {
amount = 450;--50;
time = 2;--0.5;
-- old syntax
minpos = vector.subtract(tgt.under, 0.5);
maxpos = vector.add(tgt.under, 0.5);
minvel = minvel, maxvel = maxvel;
minexptime = 1, maxexptime = 2;
minsize = 0.5, maxsize = 2;
texture = L.image('sorcery_spark.png'):multiply(color):render();
--new syntax
-- can't use this shit until it gets merged :(
-- pos = {
-- min = vector.subtract(tgt.under, 0.5);
-- max = vector.add(tgt.under, 0.5);
-- };
-- pos = {
-- min = vector.subtract(tgt.under, 0.8);
-- max = vector.add (tgt.under, 0.8);
-- };
-- vel_tween = {
-- {min = minvel, max = maxvel};
-- {min = minvel * 3, max = maxvel * 3};
-- };
-- acc = {
-- min = vector.new(0,-2,0);
-- max = vector.new(0,-5,0);
-- };
-- bounce = { min = 0.3, max = 2 };
-- exptime = { min = 0.5, max = 10 };
-- size = { min = 0.5, max = 1 };
-- collisiondetection = true;
--
-- texpool = {
-- {
-- img = L.image('sorcery_spark.png'):multiply(color):render();
-- -- img = 'sorcery_inferno_crystal.png';
-- alpha_tween = { 0.8, 1; style = 'pulse', reps = 4};
-- -- scale = {x=4,y=0.5};
-- scale_tween = { {x=2,y=3}; {x=0,y=0}; style = 'pulse', reps = 6 };
-- };
-- -- {
-- -- img = L.image('sorcery_divine_radiance_1.png'):multiply(L.color(255,0,0)):render();
-- -- fade = 'pulse';
-- -- fade_reps = 3;
-- -- };
-- };
-- radius = {
-- min = vector.new(0,0,0);
-- max = 0;
-- };
-- animation = {
-- type = 'vertical_frames';
-- aspect_w = 16, aspect_h = 16;
-- length = -1;
-- };
-- glow = 14;
}
end
sorcery.vfx.bloodburst = function(pos,size)
for i=0, size or 48 do
minetest.add_particle{
texture = 'sorcery_blood_' .. math.random(5) .. '.png',
size = 7,
expirationtime = 2 + math.random(),
glow = 1,
pos = pos,
velocity = {
x = (math.random() * 3.0) - 1.5,
y = math.random(),
z = (math.random() * 3.0) - 1.5
},
acceleration = {
x = 0,
y = -1,
z = 0
}
}
end
end
-- target can be an entity or a pos vector
sorcery.vfx.imbue = function(color, target, strength, height)
local tpos if target.get_pos then
tpos = target:get_pos()
if target.get_properties then
height = height or ((target:get_properties().eye_height or 1)*1.3)
end
else
tpos = vector.offset(target, 0,0.5,0)
end
height = height or 1
local scenter = vector.add(tpos, {x=0,y=height/2,z=0})
for i=1,math.random(24*(strength or 1),32*(strength or 1)) do
local high = (height+0.8)*math.random() - 0.8
local far = (high >= -0.5 and high <= height) and
(math.random() * 0.3 + 0.4) or
(math.random() * 0.5)
local yaw = {x=0, y = math.random()*(2*math.pi), z=0}
local po = vector.rotate({x=far,y=high,z=0}, yaw)
local ppos = vector.add(po,tpos)
local dir = vector.direction(ppos,scenter)
local vel = math.random() * 0.8 + 0.4
local col if type(color) == 'function'
then col = color(i, {high = high, far = far, dir = dir, vel = vel, pos = po})
else col = color
end
local et = math.floor((far/vel)*10)*.1 -- avoid freeze
minetest.add_particle {
pos = ppos;
velocity = vector.multiply(dir,vel);
expirationtime = 0.4;
size = math.random()*2.4 + 0.6;
texture = L.image('sorcery_sputter.png'):glow(col):render();
glow = 14;
animation = {
type = 'vertical_frames', length = 0.4;
aspect_w = 16, aspect_h = 16
};
}
end
end
function sorcery.vfx.drip(liquid, noz, amt, time, exp)
if type(liquid) == 'string' then liquid = sorcery.register.liquid.db[liquid] end
local minnoz = vector.offset(noz, -0.03, 0.0, -0.03);
local maxnoz = vector.offset(noz, 0.03, 0.0, 0.03);
local drop = L.image('sorcery_drop.png'):multiply(L.color(liquid.color))
return minetest.add_particlespawner {
amount = amt, time = time;
texture = drop:render();
minpos = minnoz, maxpos = maxnoz;
minvel = vector.new(0,0,0);
maxvel = vector.new(0,-0.2,0);
minacc = vector.new(0,-0.2,0);
maxacc = vector.new(0,-0.23,0);
minsize = 0.4, maxsize = 1;
glow = liquid.glow or 2;
minexptime = exp, maxexptime = exp;
animation = {
type = 'sheet_2d';
frames_w = 10;
frames_h = 1;
frame_length = (exp/10) + 0.01;
};
vertical = true;
}
end