Differences From
Artifact [829a9ad70b]:
413 413 tone = {159,235,0};
414 414 minpower = 4;
415 415 rarity = 34;
416 416 amulets = {
417 417 sapphire = {
418 418 name = 'Unsealing';
419 419 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';
420 + cast = function(ctx)
421 + if ctx.target.type ~= 'node' then return false end
422 + local r = 2 + math.floor(ctx.stats.power / 2)
423 + local cast = false
424 + for x = -r,r do
425 + for y = -r,r do
426 + for z = -r,r do
427 + if x^2 + y^2 + z^2 <= r^2 then
428 + local pos = vector.offset(ctx.target.under, x,y,z)
429 + local abv = vector.offset(ctx.target.above, x,y,z)
430 + local meta = minetest.get_meta(pos)
431 + if meta:contains('sorcery_seal_mode') then
432 + meta:set_string('sorcery_seal_mode', '')
433 + meta:set_string('sorcery_wand_key', '')
434 + meta:set_string('owner', '')
435 + sorcery.vfx.enchantment_sparkle({under=pos,above=abv}, sorcery.lib.color(12,38,255))
436 + cast = true
437 + end
438 + end
439 + end end end
440 + if cast then
441 + minetest.sound_play('sorcery_disjoin',{object=target},true)
442 + end
443 + return cast
444 + end;
420 445 };
421 446 amethyst = {
422 447 name = 'Purging';
423 448 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';
424 449 cast = function(ctx) purge(ctx.caster) end;
425 450 };
426 451 emerald = {
................................................................................
456 481 subjects = {{player=ctx.caster}};
457 482 disjunction = true, range = 4 + ctx.stats.power;
458 483 duration = 10 + ctx.stats.power * 3;
459 484 timeline = {
460 485 [0] = function(s,_,tl)
461 486 local ttns = 0.8
462 487 local vel = s.range / ttns
463 - s.visual_caster {
488 + s.visual_subjects {
464 489 amount = 300, time = ttns, glow = 14;
465 490 texture = sorcery.lib.image('sorcery_sputter.png'):glow(sorcery.lib.color(160,255,80)):render();
466 491 minpos = { x = -0.0, y = h*0.5,z = -0.0 };
467 492 maxpos = { x = 0.0, y = h*0.5,z = 0.0 };
468 493 minvel = { x = -vel, y = -0.0, z = -vel };
469 494 maxvel = { x = vel, y = 0.0, z = vel };
470 495 minacc = { x = -0.2, y = -0.0, z = -0.2 };
................................................................................
663 688 aspect_w = 16, aspect_h = 16;
664 689 }
665 690 }
666 691 end;
667 692 };
668 693 intervals = {
669 694 {period = 0.1, after = 0.15, fn = function(c)
670 - print('interval running')
671 695 for i = 1,80 do
672 696 local life = 0.2 + math.random() * 2
673 697 local dir = vector.new(math.random()-0.5,0,math.random()-0.5):normalize()
674 698 local pos = epicenter + (dir * (math.random()*radius))
675 - print('setting particle at',pos)
676 699 minetest.add_particle {
677 700 texture = tex;
678 701 pos = pos;
679 702 expirationtime = life;
680 703 velocity = {x = 0, y = math.random() * 0.3 + 0.1, z = 0};
681 704 size = 0.4 + math.random() * 3;
682 705 glow = 14;
................................................................................
1071 1094 };
1072 1095 genesis = {
1073 1096 name = 'Genesis';
1074 1097 tone = {235,0,175};
1075 1098 minpower = 5;
1076 1099 rarity = 23;
1077 1100 amulets = {
1101 + amethyst = {
1102 + name = 'Shelter';
1103 + 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.';
1104 + cast = function(ctx)
1105 + local ctr = ctx.caster:get_pos()
1106 + local dim = { rmax = 2 + 6 * (ctx.stats.power*0.1) }
1107 + dim.rx = math.random(math.max(3, dim.rmax - 4), dim.rmax)
1108 + dim.rz = math.random(math.max(3, dim.rmax - 4), dim.rmax)
1109 + dim.y = math.random(4, 7)
1110 + for i = 1,10 do
1111 + if sorcery.lib.node.is_air(ctr) then
1112 + ctr = ctr:offset(0, -1, 0)
1113 + else break end
1114 + end
1115 + local soilp = function(pos)
1116 + local name = minetest.get_node(pos).name
1117 + return minetest.get_item_group(name, 'soil') ~= 0 or
1118 + minetest.get_item_group(name, 'sand') ~= 0
1119 + end
1120 + if not soilp(ctr) then return false end
1121 + -- scan map to determine maximum dimensions
1122 + for x = -dim.rx, dim.rx do
1123 + for z = -dim.rz, dim.rz do
1124 + local pos = ctr:offset(x,0,z)
1125 + if not soilp(pos) then
1126 + dim.rx = math.min(dim.rx, math.abs(x))
1127 + dim.rz = math.min(dim.rz, math.abs(z))
1128 + end
1129 + for y = 1, dim.y do
1130 + if not sorcery.lib.node.is_air(pos:offset(0,y,0)) then
1131 + dim.y = math.min(dim.y, y)
1132 + end
1133 + end
1134 + end
1135 + end
1136 + -- bail if not enough room
1137 + if dim.rx < 2 or dim.rz < 2 or dim.y < 2 then
1138 + return false
1139 + end
1140 + local materials = {
1141 + walls = {};
1142 + corners = {};
1143 + floor = {};
1144 + roof = {};
1145 + door = {};
1146 + door_lock = {};
1147 + lamp_floor = {};
1148 + lamp_ceil = {};
1149 + lamp_wall = {};
1150 + lamp_ext = {};
1151 + }
1152 + local try = function(ary, ...)
1153 + local a = {}
1154 + for _, item in pairs{...} do
1155 + if item and minetest.registered_items[item] then
1156 + table.insert(a, item)
1157 + end
1158 + end
1159 + if next(a) then
1160 + table.insert(ary, a)
1161 + return true
1162 + end
1163 + return false
1164 + end
1165 + for name,tree in pairs(sorcery.data.trees) do
1166 + try(materials.corners, tree.node)
1167 + try(materials.walls, tree.plank)
1168 + try(materials.floor, tree.plank)
1169 + end
1170 + try(materials.roof, 'farming:straw')
1171 + try(materials.floor, 'farming:straw')
1172 + try(materials.roof, 'default:brick')
1173 + try(materials.roof, 'default:desert_stonebrick')
1174 + try(materials.walls, 'default:brick') try(materials.corners, 'default:brick')
1175 + try(materials.walls, 'default:stonebrick') try(materials.corners, 'default:stonebrick')
1176 + try(materials.walls, 'default:cobble', 'default:mossycobble')
1177 + try(materials.walls, 'default:desert_cobble')
1178 + try(materials.walls, 'default:desert_stonebrick')
1179 +
1180 + try(materials.corners, 'default:desert_stonebrick')
1181 + try(materials.corners, 'default:desert_stonebrick')
1182 + try(materials.corners, 'default:desert_stone_block')
1183 +
1184 + try(materials.walls, 'default:sandstone')
1185 + try(materials.walls, 'default:sandstonebrick')
1186 + try(materials.walls, 'default:desert_sandstone')
1187 + try(materials.walls, 'default:desert_sandstone_brick')
1188 + try(materials.walls, 'default:silver_sandstone')
1189 + try(materials.walls, 'default:silver_sandstone_brick')
1190 +
1191 + try(materials.walls, 'default:sandstone', 'default:silver_sandstone', 'default:desert_sandstone')
1192 + try(materials.walls, 'default:sandstonebrick', 'default:silver_sandstone_brick', 'default:desert_sandstone_brick')
1193 + try(materials.roof, 'default:sandstonebrick', 'default:silver_sandstone_brick', 'default:desert_sandstone_brick')
1194 +
1195 + try(materials.corners, 'default:sandstonebrick')
1196 + try(materials.corners, 'default:desert_sandstone_brick')
1197 + try(materials.corners, 'default:silver_sandstone_brick')
1198 + if math.random(1,10) == 7 then
1199 + try(materials.corners, 'default:obsidianbrick')
1200 + end
1201 +
1202 + try(materials.lamp_wall, 'default:torch_wall')
1203 + try(materials.lamp_wall, 'morelights_modern:wall_lamp')
1204 +
1205 + try(materials.lamp_ext, 'default:torch')
1206 + try(materials.lamp_ext, 'morelights_modern:wall_lamp')
1207 + try(materials.lamp_ext, 'morelights_modern:lantern_f')
1208 +
1209 + for _, l in pairs {
1210 + 'default:meselamp';
1211 + 'morelights_modern:barlight_s';
1212 + 'morelights_modern:ceilinglight';
1213 + 'morelights_modern:canlight_d';
1214 + 'morelights_modern:canlight_l';
1215 + 'morelights_vintage:hangingbulb';
1216 + 'morelights_vintage:chandelier';
1217 + } do try(materials.lamp_ceil, l) end
1218 +
1219 + for _, l in pairs {
1220 + 'default:meselamp';
1221 + 'morelights_extras:f_block',
1222 + 'morelights_extras:sandstone_block',
1223 + 'morelights_extras:stone_block',
1224 + 'morelights_modern:block',
1225 + 'morelights_vintage:block',
1226 + } do try(materials.lamp_floor, l) end
1227 +
1228 + for _, d in pairs {
1229 + 'doors:door_wood';
1230 + 'doors:woodglass_door';
1231 + 'doors:slide_door';
1232 + 'doors:japanese_door';
1233 + 'doors:screen_door';
1234 + 'doors:door_glass';
1235 + } do try(materials.door, d) end
1236 +
1237 + for _, d in pairs {
1238 + 'doors:door_steel';
1239 + 'xpanes:door_steel_bar';
1240 + } do try(materials.door_lock, d) end
1241 +
1242 +
1243 + for k,v in pairs(materials) do
1244 + if next(v) then
1245 + materials[k] = select(2, sorcery.lib.tbl.pick(v))
1246 + end
1247 + end
1248 + local timeline = {}
1249 + local per = 0.05
1250 + local i = 0
1251 + local spark = function(s,where)
1252 + s.visual {
1253 + amount = 30;
1254 + time = 0.2;
1255 + minpos = where:offset(-0.5, 0.0, -0.5);
1256 + maxpos = where:offset( 0.5, 1.0, 0.5);
1257 + minvel = vector.new(-0.4, -0.2, -0.4);
1258 + maxvel = vector.new( 0.4, 0.2, 0.4);
1259 + minacc = vector.new( 0, 0.2, 0);
1260 + maxacc = vector.new( 0, 0.6, 0);
1261 + minexptime = 0.2, maxexptime = 2.0;
1262 + minsize = 0.3, maxsize = 2;
1263 + texture = sorcery.vfx.glowspark(sorcery.lib.color(255,12,89)):render();
1264 + glow = minetest.LIGHT_MAX;
1265 + animation = {
1266 + type = 'vertical_frames', length = 3.1;
1267 + aspect_w = 16, aspect_h = 16;
1268 + }
1269 + }
1270 + end
1271 + local setplane = function(y, mcat)
1272 + for x = -dim.rx, dim.rx do
1273 + for z = -dim.rz, dim.rz do
1274 + timeline[{whence=0, secs=per*i}] = function(s)
1275 + local node = select(2, sorcery.lib.tbl.pick(mcat))
1276 + local p = ctr:offset(x,y,z)
1277 + minetest.set_node(p, {name=node})
1278 + spark(s,p)
1279 + end
1280 + end
1281 + i = i + 1
1282 + end
1283 + end
1284 + setplane(0, materials.floor)
1285 + local mpick=function(t)
1286 + return select(2, sorcery.lib.tbl.pick(materials[t]))
1287 + end
1288 + local t_supports = per*i
1289 + for y = 1, dim.y-1 do
1290 + local mx,mz = dim.rx, dim.rz
1291 + timeline[{whence=0, secs=per*i}] = function(s)
1292 + for _,where in pairs {
1293 + ctr:offset( mx,y, mz);
1294 + ctr:offset(-mx,y, mz);
1295 + ctr:offset(-mx,y,-mz);
1296 + ctr:offset( mx,y,-mz);
1297 + } do
1298 + minetest.set_node(where, { name = mpick'corners' })
1299 + end
1300 + end
1301 + i=i+1
1302 + end
1303 + local t_roof = per*i
1304 + setplane(dim.y, materials.roof)
1305 + timeline[{whence=0,secs=per*i - 0.4}] = function(s)
1306 + s.visual {
1307 + time = 30, amount = 2500;
1308 + minpos = ctr:offset(-dim.rx,dim.y - 0.5,-dim.rz);
1309 + maxpos = ctr:offset( dim.rx,dim.y + 0.5, dim.rz);
1310 + minacc = vector.new(0,-0.5,0);
1311 + maxacc = vector.new(0, 0.3,0);
1312 + texture = sorcery.lib.image('sorcery_sputter.png'):glow(255,17,86):render();
1313 + glow = minetest.LIGHT_MAX;
1314 + minexptime = 2.4, maxexptime = 8;
1315 + minsize = 0.5, maxsize = 3;
1316 + animation = {
1317 + type = 'vertical_frames', length = 1.1;
1318 + aspect_w = 16, aspect_h = 16;
1319 + };
1320 + }
1321 + end
1322 + local t_walls = per*i
1323 + for y = dim.y-1,1,-1 do
1324 + timeline[{whence=0, secs=per*i}] = function(s)
1325 + local xe, ze = dim.rx-1, dim.rz-1
1326 + for x=-xe,xe do
1327 + minetest.set_node(ctr:offset(x,y, dim.rz), {name=mpick'walls'})
1328 + minetest.set_node(ctr:offset(x,y,-dim.rz), {name=mpick'walls'})
1329 + end
1330 + for z=-ze,ze do
1331 + minetest.set_node(ctr:offset(-dim.rx,y,z), {name=mpick'walls'})
1332 + minetest.set_node(ctr:offset( dim.rx,y,z), {name=mpick'walls'})
1333 + end
1334 + end
1335 + i=i+1
1336 + end
1337 + local t_built = per*i
1338 + local lighting = math.random(1,2)
1339 + if lighting == 1 then
1340 + local lh = math.ceil(dim.y * .6)
1341 + local wlamps = {
1342 + ctr:offset( dim.rx - 1 , lh,0);
1343 + ctr:offset(-(dim.rx - 1), lh,0);
1344 + ctr:offset(0, lh, dim.rz - 1);
1345 + ctr:offset(0, lh, -(dim.rz - 1));
1346 + }
1347 + sorcery.lib.tbl.shuffle(wlamps)
1348 + for _, where in pairs(wlamps) do
1349 + i = i + 10
1350 + timeline[{whence=0, secs = per*i}] = function(s)
1351 + spark(s,where)
1352 + local node = select(2,sorcery.lib.tbl.pick(materials.lamp_wall))
1353 + minetest.sound_play('sorcery_put',{pos=where,gain=0.8},true)
1354 + minetest.set_node(where, {
1355 + name=node;
1356 + param2=minetest.dir_to_wallmounted(vector.normalize(ctr:offset(0,lh,0) - where)*-1);
1357 + })
1358 + end
1359 + end i=i+1
1360 + elseif lighting == 2 then
1361 + local which = math.random(1,3)
1362 + if which == 1 or which == 2 then
1363 + i = i + 20
1364 + timeline[{whence=0, secs = per*i}] = function(s)
1365 + local where = ctr:offset(0,dim.y,0)
1366 + spark(s,where)
1367 + minetest.sound_play('sorcery_put',{pos=where,gain=0.7},true)
1368 + minetest.item_place(ItemStack(mpick'lamp_ceil'), nil, {
1369 + type = "node";
1370 + under = where;
1371 + above = where:offset(0,-1,0);
1372 + })
1373 + end
1374 + end
1375 + if which == 1 or which == 3 then
1376 + i = i + 20
1377 + local flamps = {
1378 + ctr:offset( (dim.rx - 1), 0, (dim.rz - 1));
1379 + ctr:offset(-(dim.rx - 1), 0, (dim.rz - 1));
1380 + ctr:offset(-(dim.rx - 1), 0, -(dim.rz - 1));
1381 + ctr:offset( (dim.rx - 1), 0, -(dim.rz - 1));
1382 + }
1383 + sorcery.lib.tbl.shuffle(flamps)
1384 + for _, v in pairs(flamps) do
1385 + timeline[{whence=0, secs = per*i}] = function(s)
1386 + local node = select(2,sorcery.lib.tbl.pick(materials.lamp_floor))
1387 + spark(s,v)
1388 + minetest.sound_play('sorcery_put',{pos=v,gain=0.7},true)
1389 + minetest.set_node(v, {name = node})
1390 + end
1391 + i = i + 7
1392 + end
1393 + end
1394 + end
1395 + -- install door
1396 + local doorside = ({
1397 + vector.new( dim.rx,1,0);
1398 + vector.new(0,1, dim.rz);
1399 + vector.new(-dim.rx,1,0);
1400 + vector.new(0,1,-dim.rz);
1401 + })[math.random(1,4)]
1402 + local doorpos
1403 + if math.random(1,3) == 1 then
1404 + if doorside.z ~= 0 then
1405 + doorside.x = doorside.x + math.random(-(dim.rx-1), dim.rx-1)
1406 + elseif doorside.x ~= 0 then
1407 + doorside.z = doorside.z + math.random(-(dim.rz-1), dim.rz-1)
1408 + end
1409 + end
1410 + doorpos = ctr + doorside
1411 + local door = mpick'door'
1412 + i=i+5
1413 + timeline[{whence=0,secs=per*i}] = function(s)
1414 + minetest.remove_node(doorpos)
1415 + minetest.remove_node(doorpos:offset(0,1,0))
1416 + spark(s, doorpos)
1417 + spark(s, doorpos:offset(0,1,0))
1418 + minetest.sound_play('sorcery_crunch', {pos = doorpos, gain = 0.9}, true)
1419 + -- this is buggy af and needs to be replaced with a proper impl
1420 +-- local d = ItemStack(door)
1421 +-- d:get_definition().on_place(d, s.caster, {
1422 +-- type = 'node';
1423 +-- above = doorpos;
1424 +-- under = doorpos:offset(0,-1,0);
1425 +-- })
1426 + end
1427 +
1428 + sorcery.spell.cast {
1429 + name = 'sorcery:shelter';
1430 + groups = {'genesis','construct'};
1431 + caster = ctx.caster;
1432 + anchor = ctr;
1433 + radius = math.max(dim.rz, dim.rx);
1434 + duration = per * i;
1435 + timeline = timeline;
1436 + sounds = {
1437 + [{whence=0,secs=t_supports}] = {
1438 + sound = 'sorcery_slide';
1439 + where = ctr:offset(0,dim.y,0);
1440 + ephemeral = true, pitch = 0.7;
1441 + };
1442 + [{whence=0,secs=t_roof}] = {
1443 + sound = 'sorcery_slide';
1444 + where = ctr:offset(0,dim.y,0);
1445 + ephemeral = true;
1446 + };
1447 + };
1448 + }
1449 + end;
1450 + };
1078 1451 mese = {
1079 1452 mingrade = 4;
1080 1453 name = 'Duplication';
1081 - desc = 'Bring an exact twin of any object or item into existence, no matter how common or rare it might be';
1454 + desc = 'Bring an exact twin of any object or item into existence, whether it be a thing quotidian or an impossible rarity';
1082 1455 cast = function(ctx)
1083 1456 local color = sorcery.lib.color(255,61,205)
1084 1457 local dup, sndpos, anchor, sbj, ty
1085 1458 if ctx.target.type == 'object' and ctx.target.ref:get_luaentity().name == '__builtin:item' then
1086 1459 sorcery.vfx.imbue(color, ctx.target.ref) -- causes graphics card problems???
1087 1460 sndpos = 'subjects'
1088 1461 sbj = {{player = ctx.target.ref}}
................................................................................
1132 1505 end
1133 1506 if minetest.get_item_group(ty,'do_not_duplicate') ~= 0 then
1134 1507 return true
1135 1508 end
1136 1509
1137 1510 sorcery.spell.cast {
1138 1511 name = 'sorcery:duplicate';
1512 + groups = {'genesis'};
1139 1513 caster = ctx.caster;
1140 1514 duration = math.random(10,20) * ((10 - ctx.stats.power)*0.1);
1141 1515 anchor = anchor;
1142 1516 timeline = {
1143 1517 [{whence=0, secs=1}] = function(s,te,tl)
1144 1518 local mag = sbj and 0.5 or 0.7
1145 1519 local pv = sbj and vector.new(0,0,0) or ctx.target.under