Index: data/runes.lua ================================================================== --- data/runes.lua +++ data/runes.lua @@ -415,10 +415,35 @@ rarity = 34; amulets = { sapphire = { name = 'Unsealing'; desc = 'Wielding this amulet, a touch of your hand will unravel even the mightiest protective magics, leaving doors unsealed and walls free to tear down'; + cast = function(ctx) + if ctx.target.type ~= 'node' then return false end + local r = 2 + math.floor(ctx.stats.power / 2) + local cast = false + for x = -r,r do + for y = -r,r do + for z = -r,r do + if x^2 + y^2 + z^2 <= r^2 then + local pos = vector.offset(ctx.target.under, x,y,z) + local abv = vector.offset(ctx.target.above, x,y,z) + local meta = minetest.get_meta(pos) + if meta:contains('sorcery_seal_mode') then + meta:set_string('sorcery_seal_mode', '') + meta:set_string('sorcery_wand_key', '') + meta:set_string('owner', '') + sorcery.vfx.enchantment_sparkle({under=pos,above=abv}, sorcery.lib.color(12,38,255)) + cast = true + end + end + end end end + if cast then + minetest.sound_play('sorcery_disjoin',{object=target},true) + end + return cast + end; }; amethyst = { name = 'Purging'; desc = 'Free yourself from the grip of any malicious spellwork with a snap of your fingers — interrupting all of your own active spells in the process, including impending translocations'; cast = function(ctx) purge(ctx.caster) end; @@ -458,11 +483,11 @@ duration = 10 + ctx.stats.power * 3; timeline = { [0] = function(s,_,tl) local ttns = 0.8 local vel = s.range / ttns - s.visual_caster { + s.visual_subjects { amount = 300, time = ttns, glow = 14; texture = sorcery.lib.image('sorcery_sputter.png'):glow(sorcery.lib.color(160,255,80)):render(); minpos = { x = -0.0, y = h*0.5,z = -0.0 }; maxpos = { x = 0.0, y = h*0.5,z = 0.0 }; minvel = { x = -vel, y = -0.0, z = -vel }; @@ -665,16 +690,14 @@ } end; }; intervals = { {period = 0.1, after = 0.15, fn = function(c) - print('interval running') for i = 1,80 do local life = 0.2 + math.random() * 2 local dir = vector.new(math.random()-0.5,0,math.random()-0.5):normalize() local pos = epicenter + (dir * (math.random()*radius)) - print('setting particle at',pos) minetest.add_particle { texture = tex; pos = pos; expirationtime = life; velocity = {x = 0, y = math.random() * 0.3 + 0.1, z = 0}; @@ -1073,14 +1096,364 @@ name = 'Genesis'; tone = {235,0,175}; minpower = 5; rarity = 23; amulets = { + amethyst = { + name = 'Shelter'; + desc = 'Pour the power of this amulet into the soil or sand and out will grow a warm and well-lit place of shelter, no matter how far you may be from civilization.'; + cast = function(ctx) + local ctr = ctx.caster:get_pos() + local dim = { rmax = 2 + 6 * (ctx.stats.power*0.1) } + dim.rx = math.random(math.max(3, dim.rmax - 4), dim.rmax) + dim.rz = math.random(math.max(3, dim.rmax - 4), dim.rmax) + dim.y = math.random(4, 7) + for i = 1,10 do + if sorcery.lib.node.is_air(ctr) then + ctr = ctr:offset(0, -1, 0) + else break end + end + local soilp = function(pos) + local name = minetest.get_node(pos).name + return minetest.get_item_group(name, 'soil') ~= 0 or + minetest.get_item_group(name, 'sand') ~= 0 + end + if not soilp(ctr) then return false end + -- scan map to determine maximum dimensions + for x = -dim.rx, dim.rx do + for z = -dim.rz, dim.rz do + local pos = ctr:offset(x,0,z) + if not soilp(pos) then + dim.rx = math.min(dim.rx, math.abs(x)) + dim.rz = math.min(dim.rz, math.abs(z)) + end + for y = 1, dim.y do + if not sorcery.lib.node.is_air(pos:offset(0,y,0)) then + dim.y = math.min(dim.y, y) + end + end + end + end + -- bail if not enough room + if dim.rx < 2 or dim.rz < 2 or dim.y < 2 then + return false + end + local materials = { + walls = {}; + corners = {}; + floor = {}; + roof = {}; + door = {}; + door_lock = {}; + lamp_floor = {}; + lamp_ceil = {}; + lamp_wall = {}; + lamp_ext = {}; + } + local try = function(ary, ...) + local a = {} + for _, item in pairs{...} do + if item and minetest.registered_items[item] then + table.insert(a, item) + end + end + if next(a) then + table.insert(ary, a) + return true + end + return false + end + for name,tree in pairs(sorcery.data.trees) do + try(materials.corners, tree.node) + try(materials.walls, tree.plank) + try(materials.floor, tree.plank) + end + try(materials.roof, 'farming:straw') + try(materials.floor, 'farming:straw') + try(materials.roof, 'default:brick') + try(materials.roof, 'default:desert_stonebrick') + try(materials.walls, 'default:brick') try(materials.corners, 'default:brick') + try(materials.walls, 'default:stonebrick') try(materials.corners, 'default:stonebrick') + try(materials.walls, 'default:cobble', 'default:mossycobble') + try(materials.walls, 'default:desert_cobble') + try(materials.walls, 'default:desert_stonebrick') + + try(materials.corners, 'default:desert_stonebrick') + try(materials.corners, 'default:desert_stonebrick') + try(materials.corners, 'default:desert_stone_block') + + try(materials.walls, 'default:sandstone') + try(materials.walls, 'default:sandstonebrick') + try(materials.walls, 'default:desert_sandstone') + try(materials.walls, 'default:desert_sandstone_brick') + try(materials.walls, 'default:silver_sandstone') + try(materials.walls, 'default:silver_sandstone_brick') + + try(materials.walls, 'default:sandstone', 'default:silver_sandstone', 'default:desert_sandstone') + try(materials.walls, 'default:sandstonebrick', 'default:silver_sandstone_brick', 'default:desert_sandstone_brick') + try(materials.roof, 'default:sandstonebrick', 'default:silver_sandstone_brick', 'default:desert_sandstone_brick') + + try(materials.corners, 'default:sandstonebrick') + try(materials.corners, 'default:desert_sandstone_brick') + try(materials.corners, 'default:silver_sandstone_brick') + if math.random(1,10) == 7 then + try(materials.corners, 'default:obsidianbrick') + end + + try(materials.lamp_wall, 'default:torch_wall') + try(materials.lamp_wall, 'morelights_modern:wall_lamp') + + try(materials.lamp_ext, 'default:torch') + try(materials.lamp_ext, 'morelights_modern:wall_lamp') + try(materials.lamp_ext, 'morelights_modern:lantern_f') + + for _, l in pairs { + 'default:meselamp'; + 'morelights_modern:barlight_s'; + 'morelights_modern:ceilinglight'; + 'morelights_modern:canlight_d'; + 'morelights_modern:canlight_l'; + 'morelights_vintage:hangingbulb'; + 'morelights_vintage:chandelier'; + } do try(materials.lamp_ceil, l) end + + for _, l in pairs { + 'default:meselamp'; + 'morelights_extras:f_block', + 'morelights_extras:sandstone_block', + 'morelights_extras:stone_block', + 'morelights_modern:block', + 'morelights_vintage:block', + } do try(materials.lamp_floor, l) end + + for _, d in pairs { + 'doors:door_wood'; + 'doors:woodglass_door'; + 'doors:slide_door'; + 'doors:japanese_door'; + 'doors:screen_door'; + 'doors:door_glass'; + } do try(materials.door, d) end + + for _, d in pairs { + 'doors:door_steel'; + 'xpanes:door_steel_bar'; + } do try(materials.door_lock, d) end + + + for k,v in pairs(materials) do + if next(v) then + materials[k] = select(2, sorcery.lib.tbl.pick(v)) + end + end + local timeline = {} + local per = 0.05 + local i = 0 + local spark = function(s,where) + s.visual { + amount = 30; + time = 0.2; + minpos = where:offset(-0.5, 0.0, -0.5); + maxpos = where:offset( 0.5, 1.0, 0.5); + minvel = vector.new(-0.4, -0.2, -0.4); + maxvel = vector.new( 0.4, 0.2, 0.4); + minacc = vector.new( 0, 0.2, 0); + maxacc = vector.new( 0, 0.6, 0); + minexptime = 0.2, maxexptime = 2.0; + minsize = 0.3, maxsize = 2; + texture = sorcery.vfx.glowspark(sorcery.lib.color(255,12,89)):render(); + glow = minetest.LIGHT_MAX; + animation = { + type = 'vertical_frames', length = 3.1; + aspect_w = 16, aspect_h = 16; + } + } + end + local setplane = function(y, mcat) + for x = -dim.rx, dim.rx do + for z = -dim.rz, dim.rz do + timeline[{whence=0, secs=per*i}] = function(s) + local node = select(2, sorcery.lib.tbl.pick(mcat)) + local p = ctr:offset(x,y,z) + minetest.set_node(p, {name=node}) + spark(s,p) + end + end + i = i + 1 + end + end + setplane(0, materials.floor) + local mpick=function(t) + return select(2, sorcery.lib.tbl.pick(materials[t])) + end + local t_supports = per*i + for y = 1, dim.y-1 do + local mx,mz = dim.rx, dim.rz + timeline[{whence=0, secs=per*i}] = function(s) + for _,where in pairs { + ctr:offset( mx,y, mz); + ctr:offset(-mx,y, mz); + ctr:offset(-mx,y,-mz); + ctr:offset( mx,y,-mz); + } do + minetest.set_node(where, { name = mpick'corners' }) + end + end + i=i+1 + end + local t_roof = per*i + setplane(dim.y, materials.roof) + timeline[{whence=0,secs=per*i - 0.4}] = function(s) + s.visual { + time = 30, amount = 2500; + minpos = ctr:offset(-dim.rx,dim.y - 0.5,-dim.rz); + maxpos = ctr:offset( dim.rx,dim.y + 0.5, dim.rz); + minacc = vector.new(0,-0.5,0); + maxacc = vector.new(0, 0.3,0); + texture = sorcery.lib.image('sorcery_sputter.png'):glow(255,17,86):render(); + glow = minetest.LIGHT_MAX; + minexptime = 2.4, maxexptime = 8; + minsize = 0.5, maxsize = 3; + animation = { + type = 'vertical_frames', length = 1.1; + aspect_w = 16, aspect_h = 16; + }; + } + end + local t_walls = per*i + for y = dim.y-1,1,-1 do + timeline[{whence=0, secs=per*i}] = function(s) + local xe, ze = dim.rx-1, dim.rz-1 + for x=-xe,xe do + minetest.set_node(ctr:offset(x,y, dim.rz), {name=mpick'walls'}) + minetest.set_node(ctr:offset(x,y,-dim.rz), {name=mpick'walls'}) + end + for z=-ze,ze do + minetest.set_node(ctr:offset(-dim.rx,y,z), {name=mpick'walls'}) + minetest.set_node(ctr:offset( dim.rx,y,z), {name=mpick'walls'}) + end + end + i=i+1 + end + local t_built = per*i + local lighting = math.random(1,2) + if lighting == 1 then + local lh = math.ceil(dim.y * .6) + local wlamps = { + ctr:offset( dim.rx - 1 , lh,0); + ctr:offset(-(dim.rx - 1), lh,0); + ctr:offset(0, lh, dim.rz - 1); + ctr:offset(0, lh, -(dim.rz - 1)); + } + sorcery.lib.tbl.shuffle(wlamps) + for _, where in pairs(wlamps) do + i = i + 10 + timeline[{whence=0, secs = per*i}] = function(s) + spark(s,where) + local node = select(2,sorcery.lib.tbl.pick(materials.lamp_wall)) + minetest.sound_play('sorcery_put',{pos=where,gain=0.8},true) + minetest.set_node(where, { + name=node; + param2=minetest.dir_to_wallmounted(vector.normalize(ctr:offset(0,lh,0) - where)*-1); + }) + end + end i=i+1 + elseif lighting == 2 then + local which = math.random(1,3) + if which == 1 or which == 2 then + i = i + 20 + timeline[{whence=0, secs = per*i}] = function(s) + local where = ctr:offset(0,dim.y,0) + spark(s,where) + minetest.sound_play('sorcery_put',{pos=where,gain=0.7},true) + minetest.item_place(ItemStack(mpick'lamp_ceil'), nil, { + type = "node"; + under = where; + above = where:offset(0,-1,0); + }) + end + end + if which == 1 or which == 3 then + i = i + 20 + local flamps = { + ctr:offset( (dim.rx - 1), 0, (dim.rz - 1)); + ctr:offset(-(dim.rx - 1), 0, (dim.rz - 1)); + ctr:offset(-(dim.rx - 1), 0, -(dim.rz - 1)); + ctr:offset( (dim.rx - 1), 0, -(dim.rz - 1)); + } + sorcery.lib.tbl.shuffle(flamps) + for _, v in pairs(flamps) do + timeline[{whence=0, secs = per*i}] = function(s) + local node = select(2,sorcery.lib.tbl.pick(materials.lamp_floor)) + spark(s,v) + minetest.sound_play('sorcery_put',{pos=v,gain=0.7},true) + minetest.set_node(v, {name = node}) + end + i = i + 7 + end + end + end + -- install door + local doorside = ({ + vector.new( dim.rx,1,0); + vector.new(0,1, dim.rz); + vector.new(-dim.rx,1,0); + vector.new(0,1,-dim.rz); + })[math.random(1,4)] + local doorpos + if math.random(1,3) == 1 then + if doorside.z ~= 0 then + doorside.x = doorside.x + math.random(-(dim.rx-1), dim.rx-1) + elseif doorside.x ~= 0 then + doorside.z = doorside.z + math.random(-(dim.rz-1), dim.rz-1) + end + end + doorpos = ctr + doorside + local door = mpick'door' + i=i+5 + timeline[{whence=0,secs=per*i}] = function(s) + minetest.remove_node(doorpos) + minetest.remove_node(doorpos:offset(0,1,0)) + spark(s, doorpos) + spark(s, doorpos:offset(0,1,0)) + minetest.sound_play('sorcery_crunch', {pos = doorpos, gain = 0.9}, true) + -- this is buggy af and needs to be replaced with a proper impl +-- local d = ItemStack(door) +-- d:get_definition().on_place(d, s.caster, { +-- type = 'node'; +-- above = doorpos; +-- under = doorpos:offset(0,-1,0); +-- }) + end + + sorcery.spell.cast { + name = 'sorcery:shelter'; + groups = {'genesis','construct'}; + caster = ctx.caster; + anchor = ctr; + radius = math.max(dim.rz, dim.rx); + duration = per * i; + timeline = timeline; + sounds = { + [{whence=0,secs=t_supports}] = { + sound = 'sorcery_slide'; + where = ctr:offset(0,dim.y,0); + ephemeral = true, pitch = 0.7; + }; + [{whence=0,secs=t_roof}] = { + sound = 'sorcery_slide'; + where = ctr:offset(0,dim.y,0); + ephemeral = true; + }; + }; + } + end; + }; mese = { mingrade = 4; name = 'Duplication'; - desc = 'Bring an exact twin of any object or item into existence, no matter how common or rare it might be'; + desc = 'Bring an exact twin of any object or item into existence, whether it be a thing quotidian or an impossible rarity'; cast = function(ctx) local color = sorcery.lib.color(255,61,205) local dup, sndpos, anchor, sbj, ty if ctx.target.type == 'object' and ctx.target.ref:get_luaentity().name == '__builtin:item' then sorcery.vfx.imbue(color, ctx.target.ref) -- causes graphics card problems??? @@ -1134,10 +1507,11 @@ return true end sorcery.spell.cast { name = 'sorcery:duplicate'; + groups = {'genesis'}; caster = ctx.caster; duration = math.random(10,20) * ((10 - ctx.stats.power)*0.1); anchor = anchor; timeline = { [{whence=0, secs=1}] = function(s,te,tl) Index: data/trees.lua ================================================================== --- data/trees.lua +++ data/trees.lua @@ -6,10 +6,11 @@ -- the appropriate interop code to your repository and open an issue -- on the sorcery tracker for us to remove them. apple = { desc = 'Apple'; node = 'default:tree'; + plank = 'default:wood'; sapling = 'default:sapling'; leaves = 'default:leaves'; sap = 'Apple Syrup'; sapcolor = {218, 238, 66}; lathe = { Index: lib/node.lua ================================================================== --- lib/node.lua +++ lib/node.lua @@ -33,10 +33,16 @@ {x = 1, y = 0, z = 0}; {x = -1, y = 0, z = 0}; {x = 0, y = 0, z = 1}; {x = 0, y = 0, z = -1}; }; + cardinal = { + {x = 1, y = 0, z = 0}; + {x = -1, y = 0, z = 0}; + {x = 0, y = 0, z = 1}; + {x = 0, y = 0, z = -1}; + }; } ofs.adjoining = sorcery.lib.tbl.append(sorcery.lib.tbl.append( ofs.neighbors,ofs.planecorners),ofs.cubecorners)