Overview
Comment: | add duplicate and elevate spells, add more sfx, various tweaks and bugfixes, add object handle class |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
6e106c135c5d0f1556f06759dba2b0f0 |
User & Date: | lexi on 2020-10-30 18:47:34 |
Other Links: | manifest | tags |
Context
2020-10-30
| ||
19:03 | squish oversized sound effect check-in: b96185e88b user: lexi tags: trunk | |
18:47 | add duplicate and elevate spells, add more sfx, various tweaks and bugfixes, add object handle class check-in: 6e106c135c user: lexi tags: trunk | |
2020-10-26
| ||
03:58 | add over-time spellcasting abstraction to enable metamagic and in particular disjunction, add more animations and sound effects, add excavation spell, possibly some others, forget when the last commit was, edit a bunch of magitech to make it subject to the disjunction mechanism (throw up a disjunction aura and waltz right through those force fields bby, wheee), also illumination spells, tweak runeforge and rune frequence to better the balance and also limit player frustration, move some math functions into their own library category, various tweaks and bugfixes, probably other shit i don't remember check-in: 147592b8e9 user: lexi tags: trunk | |
Changes
Modified data/runes.lua from [d5ad4a6740] to [3a290dc995].
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 .. 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 ... 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 ... 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 ... 251 252 253 254 255 256 257 258 259 260 261 262 263 264 ... 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 ... 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 ... 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 ... 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 ... 651 652 653 654 655 656 657 658 659 660 661 662 663 664 ... 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 ... 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 |
texture = sorcery.vfx.glowspark(color):render(); animation = { type = 'vertical_frames', length = 5.1; aspect_w = 16, aspect_h = 16; }; }); end return { translocate = { name = 'Translocate'; tone = {0,235,233}; minpower = 3; rarity = 10; amulets = { amethyst = { name = 'Joining'; desc = 'Give this amulet to another and they can arrive safely at your side in a flash from anywhere in the world — though returning whence they came may be a more difficult matter'; apply = function(ctx) local maker = ctx.user:get_player_name() ctx.meta:set_string('rune_join_target',maker) end; remove = function(ctx) ctx.meta:set_string('rune_join_target','') end; frame = { gold = { name = 'Exchange'; desc = 'Give this amulet to another and they will be able to trade places with you no matter where in the world each of you might be.'; }; cobalt = { name = 'Sending'; desc = 'Give this amulet to another and by wielding this amulet against another they will be able to transport them instantly to your side'; ................................................................................ else local pos = minetest.string_to_pos(ctx.meta:get_string('rune_return_dest')) ctx.meta:set_string('rune_return_dest','') local subjects = { ctx.caster } local center = ctx.caster:get_pos() ctx.sparkle = false local delay = math.max(3,10 - ctx.stats.power) + 3*(math.random()*2-1) if ctx.amulet.frame == 'tungsten' then delay = delay * 0.5 end for _,s in pairs(subjects) do local offset = vector.subtract(s:get_pos(), center) local pt = sorcery.lib.node.get_arrival_point(vector.add(pos,offset)) if pt then -- minetest.sound_play('sorcery_stutter', { -- object = s, gain = 0.8; -- },true) local mydelay = delay + math.random(-10,10)*.1; local sh = s:get_properties().eye_height local color = sorcery.lib.color(29,205,247) sorcery.lib.node.preload(pt,s) sorcery.spell.cast { duration = mydelay; caster = ctx.caster; subjects = {{player=s,dest=pt}}; timeline = { [0] = function(sp,_,timeleft) sparkle(color,sp,timeleft*100, timeleft, 0.3,1.3, sh) sp.windup = (sp.play_now{ sound = 'sorcery_windup'; where = 'subjects'; gain = 0.4; fade = 1.5; })[1] end; [0.4] = function(sp,_,timeleft) sparkle(color,sp,timeleft*150, timeleft, 0.6,1.8, sh) end; [0.7] = function(sp,_,timeleft) sparkle(color,sp,timeleft*80, timeleft, 2,4, sh) end; [1] = function(sp) sp.silence(sp.windup) minetest.sound_play('sorcery_zap', { pos = pt, gain = 0.4 },true) minetest.sound_play('sorcery_zap', { pos = s:get_pos(), gain = 0.4 },true) sorcery.vfx.body_sparkle(nil,sorcery.lib.color(20,255,120),2,s:get_pos()) s:set_pos(pt) sorcery.vfx.body_sparkle(s,sorcery.lib.color(20,120,255),2) end; }; sounds = { [0] = { sound = 'sorcery_stutter', pos = 'subjects' }; }; } end end end end; frame = { tungsten = { name = 'Quick Return'; desc = 'Use this amulet once to bind it to a particular place, then discharge its spell to translocate yourself rapidly back to that point from anywhere in the world.'; }; ................................................................................ name = 'Mass Banishment'; desc = 'Use this amulet once to bind it to a particular point in the world, then use it again to seize up everyone surrounding you in the grip of a fearsome magic that will deport them all in the blink of an eye to whatever destination you have chosen'; }; }; }; ruby = minetest.get_modpath('beds') and { name = 'Escape'; desc = 'Immediately transport yourself out of a dangerous situation back to the last place you slept'; cast = function(ctx) -- if not beds.spawns then beds.read_spawns() end local subjects = {ctx.caster} for _,s in pairs(subjects) do local spp = beds.spawn[ctx.caster:get_player_name()] if spp then local oldpos = s:get_pos() minetest.sound_play('sorcery_splunch', {pos=oldpos}, true) sorcery.vfx.body_sparkle(nil,sorcery.lib.color(244,38,131),2,oldpos) s:set_pos(spp) minetest.sound_play('sorcery_splunch', {pos=spp}, true) sorcery.vfx.body_sparkle(nil,sorcery.lib.color(244,38,89),2,spp) end -- TODO decide what happens to the people who don't have -- respawn points already set end end; frame = { cobalt = { ................................................................................ desc = 'Break up even the fiercest of quarrels by transporting yourself and everyone around you out of harms\' way and immediately back to the last place each slept'; }; }; }; diamond = { name = 'Elevation'; desc = 'Lift yourself and everything around you high up into the sky'; }; }; }; disjoin = { name = 'Disjoin'; tone = {159,235,0}; minpower = 4; rarity = 40; 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'; }; amethyst = { name = 'Purging'; ................................................................................ }; luxite = { name = 'Disjunctive Aura'; desc = 'For a time, all magic undertaken in your vicinity will fail totally — including your own'; cast = function(ctx) local h = ctx.heading.eyeheight*1.1 sorcery.spell.cast { caster = ctx.caster, attach = 'caster'; subjects = {{player=ctx.caster}}; disjunction = true, range = 4 + ctx.stats.power; duration = 10 + ctx.stats.power * 3; timeline = { [0] = function(s,_,tl) local ttns = 0.8 ................................................................................ }; ruby = { name = 'Liftoff'; desc = 'Lift yourself high into the air with a blast of violent repulsive force against the ground, and drift down safely to a position of your choice'; cast = function(ctx) local power = 14 * (1+(ctx.stats.power * 0.2)) minetest.sound_play('sorcery_hurl',{object=ctx.caster},true) sorcery.spell.cast { caster = ctx.caster; subjects = {{player=ctx.caster}}; duration = power * 0.30; timeline = { [0] = function(s,_,tl) sparktrail(s.visual_subjects,ctx.caster,sorcery.lib.color(255,252,93)) ctx.caster:add_velocity{y=power;x=0,z=0} s.affect { duration = power * 0.50; raise = 2; -- fall = (power * 0.25) * 0.3; impacts = { gravity = 0.1; }; } end; }; ................................................................................ desc = 'Toss an enemy violently into the air, and allow the inevitable impact to do your dirty work for you'; cast = function(ctx) if not (ctx.target and ctx.target.type == 'object') then return false end local tgt = ctx.target.ref local power = 16 * (1+(ctx.stats.power * 0.2)) minetest.sound_play('sorcery_hurl',{object=ctx.caster},true) sorcery.spell.cast { caster = ctx.caster; subjects = {{player=tgt}}; duration = 4; timeline = { [0] = function(s,_,tl) for _,sub in pairs(s.subjects) do local height = (sub.player:get_properties().eye_height or 1)*1.3 local scenter = vector.add(sub.player:get_pos(), {x=0,y=height/2,z=0}) for i=1,math.random(64,128) 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()*100, z=0} local po = vector.rotate({x=far,y=high,z=0}, yaw) local ppos = vector.add(po,sub.player:get_pos()) local dir = vector.direction(ppos,scenter) local vel = math.random() * 0.8 + 0.4 minetest.add_particle { pos = ppos; velocity = vector.multiply(dir,vel); expirationtime = far / vel; size = math.random()*2.4 + 0.6; texture = sorcery.lib.image('sorcery_sputter.png'):glow(sorcery.lib.color{ hue = math.random(41,63); saturation = 100; luminosity = 0.5 + math.random()*0.3; }):render(); glow = 14; animation = { type = 'vertical_frames', length = far/vel; aspect_w = 16, aspect_h = 16; }; } end end end; [0.3] = function(s,te,tl) sparktrail(s.visual_subjects,ctx.caster,sorcery.lib.color(255,252,93)) for _,sub in pairs(s.subjects) do sub.player:add_velocity{y=power;x=0,z=0} end end; [1] = (ctx.amulet.frame == 'cobalt') and function(s,te,tl) -- TODO add visuals ................................................................................ }; }; }; obliterate = { name = 'Obliterate'; tone = {255,0,10}; minpower = 5; rarity = 35; amulets = { amethyst = { name = 'Sapping'; desc = 'Punch a hole in enemy fortifications big enough to slip through but small enough to avoid immediate attention'; }; ruby = { name = 'Shattering'; ................................................................................ }; }; }; excavate = { name = 'Excavate'; tone = {0,68,235}; minpower = 3; rarity = 30; amulets = { luxite = { name = 'Stonestride'; desc = 'Rock walls will open up before you when you brandish this amulet before them, closing up again behind you without leaving a trace of your passage'; }; sapphire = { name = 'Tunnelling'; ................................................................................ sound='sorcery_crunch', where='pos'; ephemeral=true, gain = math.random(3,10) * 0.1; } tp = tp + (math.random(2,5) * 0.1) end sounds[1] = {sound='sorcery_powerdown', where='pos'} sorcery.spell.cast { caster = ctx.caster; duration = tp; timeline = timeline, sounds = sounds; -- spell state anchor = ctx.target.under; tunnel_angle = ctx.caster:get_look_horizontal(); tunnel_radius = math.floor(math.random(3,5) * (ctx.stats.power * 0.1)); ................................................................................ }; }; }; genesis = { name = 'Genesis'; tone = {235,0,175}; minpower = 5; rarity = 25; amulets = { mese = { mingrade = 4; name = 'Duplication'; desc = 'Generate a copy of any object or item, no matter how common or rare'; }; }; }; luminate = { name = 'Luminate'; tone = {255,194,0}; minpower = 1; rarity = 5; amulets = { luxite = { name = 'Glow'; desc = 'Swathe yourself in an aura of sparkling radiance, casting light upon all the dark places where you voyage'; }; diamond = { name = 'Radiance'; desc = 'Set the air around you alight with a mystic luminance, letting you see clearly a great distance in every direction for several minutes'; frame = { iridium = { name = 'Sunshine'; ................................................................................ }; }; }; dominate = { name = 'Dominate'; tone = {235,0,228}; minpower = 4; rarity = 20; amulets = { amethyst = { name = 'Suffocation'; desc = 'Wrap this spell tightly around your victim\'s throat, cutting off their oxygen until you release them.'; }; emerald = { name = 'Caging'; |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > | > > > > > > | > > | > | < < < < < < < < < < < < < < < < | | | | < < < < < < > | | < | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > | |
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 ... 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 ... 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 ... 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 ... 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 ... 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 ... 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 ... 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 ... 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 ... 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 ... 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 ... 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 |
texture = sorcery.vfx.glowspark(color):render(); animation = { type = 'vertical_frames', length = 5.1; aspect_w = 16, aspect_h = 16; }; }); end local teleport = function(ctx,subjects,delay,pos,color) if ctx.amulet.frame == 'tungsten' then delay = delay * 0.5 end color = color or sorcery.lib.color(29,205,247) local center = ctx.caster:get_pos() for _,sub in pairs(subjects) do local s = sub.ref local offset = vector.subtract(s:get_pos(), center) local pt = sorcery.lib.node.get_arrival_point(vector.add(pos,offset)) if pt then -- minetest.sound_play('sorcery_stutter', { -- object = s, gain = 0.8; -- },true) local mydelay = sub.delay or (delay + math.random(-10,10)*.1); local sh = s:get_properties().eye_height local color = sub.color or color sorcery.lib.node.preload(pt,s) sorcery.spell.cast { name = 'sorcery:translocate'; duration = mydelay; caster = ctx.caster; subjects = {{player=s,dest=sub.dest or pt}}; timeline = { [0] = function(sp,_,timeleft) sparkle(color,sp,timeleft*100, timeleft, 0.3,1.3, sh) sp.windup = (sp.play_now{ sound = 'sorcery_windup'; where = 'subjects'; gain = 0.4; fade = 1.5; })[1] end; [0.4] = function(sp,_,timeleft) sparkle(color,sp,timeleft*150, timeleft, 0.6,1.8, sh) end; [0.7] = function(sp,_,timeleft) sparkle(color,sp,timeleft*80, timeleft, 2,4, sh) end; [1] = function(sp) sp.silence(sp.windup) minetest.sound_play('sorcery_zap', { pos = pt, gain = 0.4 },true) minetest.sound_play('sorcery_zap', { pos = s:get_pos(), gain = 0.4 },true) sorcery.vfx.body_sparkle(nil,color:brighten(1.3),2,s:get_pos()) s:set_pos(pt) sorcery.vfx.body_sparkle(s,color:darken(0.3),2) end; }; sounds = { [0] = { sound = 'sorcery_stutter', pos = 'subjects' }; }; } end end end return { translocate = { name = 'Translocate'; tone = {0,235,233}; minpower = 3; rarity = 7; amulets = { amethyst = { name = 'Joining'; desc = 'Give this amulet to another and with a snap of their fingers they can arrive safely at your side from anywhere in the world — though returning whence they came may be a more difficult matter'; apply = function(ctx) local maker = ctx.user:get_player_name() ctx.meta:set_string('rune_join_target',maker) end; remove = function(ctx) ctx.meta:set_string('rune_join_target','') end; cast = function(ctx) local target = minetest.get_player_by_name(ctx.meta:get_string('rune_join_target')) if not target then return false end local subjects if ctx.amulet.frame == 'cobalt' then if ctx.target.type ~= 'object' then return false end subjects = {{ref=ctx.target.ref}} else subjects = {{ref=ctx.caster}} end local delay = math.max(5,11 - ctx.stats.power) + 2.3*(math.random()*2-1) local color = sorcery.lib.color(117,38,237) teleport(ctx,subjects,delay,target:get_pos(),color) if ctx.amulet.frame == 'gold' then teleport(ctx,{{ref=target}},delay,ctx.caster:get_pos()) else ctx.sparkle = false end end; frame = { tungsten = { name = 'Quick Joining'; desc = 'Give this amulet to another and they can arrive safely at your side almost instantaneously from anywhere in the world — though returning whence they came may be a more difficult matter'; }; gold = { name = 'Exchange'; desc = 'Give this amulet to another and they will be able to trade places with you no matter where in the world each of you might be.'; }; cobalt = { name = 'Sending'; desc = 'Give this amulet to another and by wielding this amulet against another they will be able to transport them instantly to your side'; ................................................................................ else local pos = minetest.string_to_pos(ctx.meta:get_string('rune_return_dest')) ctx.meta:set_string('rune_return_dest','') local subjects = { ctx.caster } local center = ctx.caster:get_pos() ctx.sparkle = false local delay = math.max(3,10 - ctx.stats.power) + 3*(math.random()*2-1) teleport(ctx,{{ref=ctx.caster}},delay,pos) end end; frame = { tungsten = { name = 'Quick Return'; desc = 'Use this amulet once to bind it to a particular place, then discharge its spell to translocate yourself rapidly back to that point from anywhere in the world.'; }; ................................................................................ name = 'Mass Banishment'; desc = 'Use this amulet once to bind it to a particular point in the world, then use it again to seize up everyone surrounding you in the grip of a fearsome magic that will deport them all in the blink of an eye to whatever destination you have chosen'; }; }; }; ruby = minetest.get_modpath('beds') and { name = 'Escape'; desc = 'Immediately transport yourself out of a dangerous situation back to the last place you slept, before anyone has time to net you in a disjunction'; cast = function(ctx) -- if not beds.spawns then beds.read_spawns() end local subjects = {ctx.caster} for _,s in pairs(subjects) do local spp = beds.spawn[ctx.caster:get_player_name()] if spp then local oldpos = s:get_pos() local jump = function() minetest.sound_play('sorcery_splunch', {pos=oldpos}, true) sorcery.vfx.body_sparkle(nil,sorcery.lib.color(244,38,131),2,oldpos) s:set_pos(spp) minetest.sound_play('sorcery_splunch', {pos=spp}, true) sorcery.vfx.body_sparkle(nil,sorcery.lib.color(244,38,89),2,spp) end if ctx.amulet.frame == 'cobalt' then sorcery.spell.cast { name = 'sorcery:escape'; caster = s; duration = random() * 0.4 + 0.3; timeline = { [0] = function() sorcery.vfx.imbue(sorcery.lib.color(244,38,131), s, 1.3) end; [1] = function(sp) local radius = 6 * ctx.stats.power local center = sp.caster:get_pos() local targets = minetest.get_objects_inside_radius(center, radius) jump() -- TODO: shockwave visuals for _,o in pairs(targets) do if not o:get_armor_groups().immortal then local distance = vector.distance(o:get_pos(), center) local dmg = (7 * ctx.stats.power) * (distance / radius) minetest.punch(ctx.caster, 1.0, { full_punch_interval = 1.0; damage_groups = { fleshy = dmg }; }, vector.direction(o:get_pos(), center)); end end end; } } else jump() end end -- TODO decide what happens to the people who don't have -- respawn points already set end end; frame = { cobalt = { ................................................................................ desc = 'Break up even the fiercest of quarrels by transporting yourself and everyone around you out of harms\' way and immediately back to the last place each slept'; }; }; }; diamond = { name = 'Elevation'; desc = 'Lift yourself and everything around you high up into the sky'; cast = function(ctx) local center = ctx.caster:get_pos() local up = ((ctx.stats.power * 7) + math.random(6,17)) * (math.random() * 0.4 + 0.4) if center.y > 0 then up = up + center.y end local newcenter = vector.new(center.x,up,center.z) if not sorcery.lib.node.get_arrival_point(newcenter) then return false end sorcery.lib.node.preload(newcenter,ctx.caster) local jmpcolor = sorcery.lib.color(0,255,144) if not ctx.amulet.frame == 'iridium' then local where = vector.offset(center,0,1,0) repeat local ok, nx = minetest.line_of_sight(where, newcenter) if ok then break end if minetest.get_node_or_nil(nx) == nil then minetest.load_area(nx) where = nx -- save some time else return false end until false end local lift = function(n) local dest = vector.new(n.pos.x, up + n.h, n.pos.z) if sorcery.lib.node.is_clear(dest) then minetest.set_node(dest, minetest.get_node(n.pos)) minetest.get_meta(dest):from_table(minetest.get_meta(n.pos):to_table()) if math.random(5) == 1 then minetest.set_node(n.pos, {name='sorcery:air_flash_' .. tostring(math.random(10))}) else minetest.remove_node(n.pos) end local obs = minetest.get_objects_inside_radius(n.pos, 1.5) if obs then for _,o in pairs(obs) do local pt = sorcery.lib.node.get_arrival_point(vector.add(dest, vector.subtract(o:get_pos(),n.pos))) if pt then o:set_pos(pt) sorcery.vfx.body_sparkle(o,jmpcolor:darken(0.3),2) end end end return true else return false end end local nodes,sparkles,tmap = {},{},{} local r = math.ceil((ctx.stats.power * 0.1) * 8 + 3) for x = -r,r do -- lazy hack to select a sphere for z = -r,r do local col = {} for y = -r,r do local ofs = vector.new(x,y,z) if sorcery.lib.math.vdcomp(r,ofs) <= 1 then local pos = vector.add(center, ofs) if sorcery.lib.node.is_air(pos) then if y > 0 then sparkles[#sparkles+1] = pos break -- levitation is a sin end else nodes[#nodes+1] = {pos=pos, h=y} col[#col+1] = {pos=pos, h=y} end end end if #col > 0 then local seq = math.floor(math.sqrt((x^2) + (z^2))) -- TODO find a way to optimise this shitshow if tmap[seq] then tmap[seq][#(tmap[seq])+1] = col else tmap[seq] = {col} end end end end -- for _,n in pairs(nodes) do -- local dest = vector.new(n.pos.x, up + n.h, n.pos.z) -- if sorcery.lib.node.is_clear(dest) then -- minetest.set_node(dest, minetest.get_node(n.pos)) -- minetest.get_meta(dest):from_table(minetest.get_meta(n.pos):to_table()) -- if math.random(5) == 1 then -- minetest.set_node(n.pos, {name='sorcery:air_flash_' .. tostring(math.random(10))}) -- else minetest.remove_node(n.pos) end -- end -- end local timeline, sounds = { [0] = function(s) -- sorcery.vfx.imbue(jmpcolor,s.caster,1) end; }, {}; local time = 0; for i=0,#tmap do local cols = tmap[i] if cols ~= nil then time = time + math.random()*0.2 + 0.1 local wh = {whence=0,secs=2+time} timeline[wh] = function(sp) for _,col in pairs(cols) do for _,n in pairs(col) do lift(n) end end end sounds[wh] = { sound = 'sorcery_zap'; gain = math.random() + 0.1; where = cols[1][1].pos; } end end sorcery.spell.cast { name = 'sorcery:elevate'; caster = ctx.caster; anchor = center, radius = r; duration = 2 + time; timeline = timeline, sounds = sounds; } end; frame = { iridium = { name = 'Ascension'; desc = 'Transport yourself and your surroundings high into the heavens, even if you are deep in the bowels of the earth'; }; }; }; }; }; disjoin = { name = 'Disjoin'; tone = {159,235,0}; minpower = 4; 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'; }; amethyst = { name = 'Purging'; ................................................................................ }; luxite = { name = 'Disjunctive Aura'; desc = 'For a time, all magic undertaken in your vicinity will fail totally — including your own'; cast = function(ctx) local h = ctx.heading.eyeheight*1.1 sorcery.spell.cast { name = 'sorcery:disjunctive-aura'; caster = ctx.caster, attach = 'caster'; subjects = {{player=ctx.caster}}; disjunction = true, range = 4 + ctx.stats.power; duration = 10 + ctx.stats.power * 3; timeline = { [0] = function(s,_,tl) local ttns = 0.8 ................................................................................ }; ruby = { name = 'Liftoff'; desc = 'Lift yourself high into the air with a blast of violent repulsive force against the ground, and drift down safely to a position of your choice'; cast = function(ctx) local power = 14 * (1+(ctx.stats.power * 0.2)) minetest.sound_play('sorcery_hurl',{object=ctx.caster},true) local oldsp = sorcery.spell.ensorcelled(ctx.caster, 'sorcery:liftoff') if oldsp then oldsp:cancel() end sorcery.spell.cast { name = 'sorcery:liftoff'; caster = ctx.caster; subjects = {{player=ctx.caster}}; duration = power * 0.30; timeline = { [0] = function(s,_,tl) sparktrail(s.visual_subjects,ctx.caster,sorcery.lib.color(255,252,93)) ctx.caster:add_velocity{y=power*1.2;x=0,z=0} end; [{whence=0, secs=1}] = function(s) s.affect { duration = power * 0.50; raise = 0.5; -- fall = (power * 0.25) * 0.3; impacts = { gravity = 0.1; }; } end; }; ................................................................................ desc = 'Toss an enemy violently into the air, and allow the inevitable impact to do your dirty work for you'; cast = function(ctx) if not (ctx.target and ctx.target.type == 'object') then return false end local tgt = ctx.target.ref local power = 16 * (1+(ctx.stats.power * 0.2)) minetest.sound_play('sorcery_hurl',{object=ctx.caster},true) sorcery.spell.cast { name = 'sorcery:flinging'; caster = ctx.caster; subjects = {{player=tgt}}; duration = 4; timeline = { [0] = function(s,_,tl) for _,sub in pairs(s.subjects) do sorcery.vfx.imbue(function() return sorcery.lib.color { hue = math.random(41,63); saturation = 100; luminosity = 0.5 + math.random()*0.3; } end, sub.player) end end; [{whence=0, secs=1}] = function(s,te,tl) sparktrail(s.visual_subjects,ctx.caster,sorcery.lib.color(255,252,93)) for _,sub in pairs(s.subjects) do sub.player:add_velocity{y=power;x=0,z=0} end end; [1] = (ctx.amulet.frame == 'cobalt') and function(s,te,tl) -- TODO add visuals ................................................................................ }; }; }; obliterate = { name = 'Obliterate'; tone = {255,0,10}; minpower = 5; rarity = 30; amulets = { amethyst = { name = 'Sapping'; desc = 'Punch a hole in enemy fortifications big enough to slip through but small enough to avoid immediate attention'; }; ruby = { name = 'Shattering'; ................................................................................ }; }; }; excavate = { name = 'Excavate'; tone = {0,68,235}; minpower = 3; rarity = 17; amulets = { luxite = { name = 'Stonestride'; desc = 'Rock walls will open up before you when you brandish this amulet before them, closing up again behind you without leaving a trace of your passage'; }; sapphire = { name = 'Tunnelling'; ................................................................................ sound='sorcery_crunch', where='pos'; ephemeral=true, gain = math.random(3,10) * 0.1; } tp = tp + (math.random(2,5) * 0.1) end sounds[1] = {sound='sorcery_powerdown', where='pos'} sorcery.spell.cast { name = 'sorcery:excavate'; caster = ctx.caster; duration = tp; timeline = timeline, sounds = sounds; -- spell state anchor = ctx.target.under; tunnel_angle = ctx.caster:get_look_horizontal(); tunnel_radius = math.floor(math.random(3,5) * (ctx.stats.power * 0.1)); ................................................................................ }; }; }; genesis = { name = 'Genesis'; tone = {235,0,175}; minpower = 5; rarity = 23; amulets = { 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'; 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) sndpos = 'subjects' sbj = {{player = ctx.target.ref}} local item = ItemStack(ctx.target.ref:get_luaentity().itemstring) local r = function() return math.random() * 2 - 1 end local putpos = vector.offset(ctx.target.ref:get_pos(), r(), 1, r()) dup = function() item:set_count(1) -- nice try bouge-san return minetest.add_item(putpos, item), false end elseif ctx.target.type == 'node' then ty = minetest.get_node(ctx.target.under).name sorcery.vfx.imbue(color, ctx.target.under) sndpos = 'pos'; anchor = ctx.target.under; dup = function() local origmeta = minetest.get_meta(ctx.target.under):to_table() origmeta.inventory = nil local npos do local vp = {} for _, of in pairs(sorcery.lib.node.offsets.neighbors) do local sum = vector.add(ctx.target.under, of) if sorcery.lib.node.is_clear(sum) then vp[#vp+1] = sum end end if #vp > 0 then npos=vp[math.random(#vp)] end end if npos then minetest.set_node(npos, minetest.get_node(ctx.target.under)) minetest.get_meta(npos):from_table(origmeta) return npos, true else local nstack = ItemStack(ty) nstack:get_meta():from_table(origmeta) local leftover = ctx.caster:get_inventory():add_item('main',nstack) if leftover and not leftover.is_empty() then minetest.add_item(ctx.caster:get_pos(), leftover) end end end else return false end if minetest.get_item_group(ty,'do_not_duplicate') ~= 0 then return true end sorcery.spell.cast { name = 'sorcery:duplicate'; 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) local mag = sbj and 0.5 or 0.7 local pv = sbj and vector.new(0,0,0) or ctx.target.under local vfn = (sbj and s.visual_subjects or s.visual) vfn { amount = tl * 30, time = tl; minpos = vector.offset(pv,-mag,-mag,-mag); maxpos = vector.offset(pv, mag, mag, mag); minsize = 0.5, maxsize = 2.3; minexptime = 1.0, maxexptime = 1.5; texture = sorcery.lib.image('sorcery_sputter.png'):glow(color):render(); animation = { type = 'vertical_frames', length = 1.6; aspect_w = 16, aspect_h = 16; }; } end; [1] = function(s,te) local where, node = dup() if where == nil then return end local pv = node and where or vector.new(0,0,0) local mp = (not node) and vector.new(0,0,0) or { x = 0.5, y = 0.5, z = 0.5 } minetest.add_particlespawner { amount = 170, time = 0.2; minpos = vector.subtract(pv,mp); maxpos = vector.add(pv,mp); attached = (not node) and where or nil; minvel = {x = -2.0, y = -1.8, z = -2.0}; maxvel = {x = 2.0, y = 0.2, z = 2.0}; minacc = {x = -0.0, y = -0.1, z = -0.0}; maxacc = {x = 0.0, y = -0.3, z = 0.0}; minsize = 0.3, maxsize = 2; minexptime = 1, maxexptime = 3.0; texture = sorcery.lib.image('sorcery_spark.png'):glow(color):render(); animation = { type = 'vertical_frames', length = 3.1; aspect_w = 16, aspect_h = 16; }; } end; }; sounds = { [0] = { sound = 'sorcery_duplicate_bg'; where = sndpos, stop = 1, fade = 2; }; [1] = { sound = 'sorcery_genesis'; where = sndpos, ephemeral = true; }; }; } end; }; }; }; luminate = { name = 'Luminate'; tone = {255,194,0}; minpower = 1; rarity = 5; amulets = { luxite = { name = 'Glow'; desc = 'Swathe yourself in an aura of sparkling radiance, casting light upon all the dark places where you voyage'; iridium = { name = 'Aura'; desc = 'Dazzling golden luminance emanates from the bodies of all those around you, and you walk in light even amid the darkest depths of the earth'; }; }; diamond = { name = 'Radiance'; desc = 'Set the air around you alight with a mystic luminance, letting you see clearly a great distance in every direction for several minutes'; frame = { iridium = { name = 'Sunshine'; ................................................................................ }; }; }; dominate = { name = 'Dominate'; tone = {235,0,228}; minpower = 4; rarity = 13; amulets = { amethyst = { name = 'Suffocation'; desc = 'Wrap this spell tightly around your victim\'s throat, cutting off their oxygen until you release them.'; }; emerald = { name = 'Caging'; |
Modified gems.lua from [42c4d86138] to [68440cd05e].
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
if not gem.foreign_amulet then local img = sorcery.lib.image local img_stone = img('sorcery_amulet.png'):multiply(sorcery.lib.color(gem.tone)) local img_sparkle = img('sorcery_amulet_sparkle.png') local useamulet = function(stack,user,target) local sp = sorcery.amulet.getspell(stack) if not sp or not sp.cast then return nil end local stats = sorcery.amulet.stats(stack) local probe = sorcery.spell.probe(user:get_pos()) -- amulets don't work in antimagic fields, though some may want to -- implement this logic themselves (for instance to check a range) if (probe.disjunction and not sp.ignore_disjunction) then return nil end local ctx = { caster = user; target = target; stats = stats; wield = stack; amulet = stack:get_definition()._sorcery.amulet; meta = stack:get_meta(); -- avoid spell boilerplate color = sorcery.lib.color(sp.tone); today = minetest.get_day_count(); probe = probe; heading = { pos = user:get_pos(); ................................................................................ pos = user:get_pos(); gain = 1; }) end if ctx.sparkle then sorcery.vfx.cast_sparkle(user, ctx.color, stats.power,0.5) end if res == nil then if not minetest.check_player_privs(user, 'sorcery:infinirune') then sorcery.amulet.setrune(stack) end end return ctx.wield end; minetest.register_craftitem(amuletname, { description = sorcery.lib.str.capitalize(name) .. ' amulet'; inventory_image = img_sparkle:blit(img_stone):render(); wield_scale = { x = 0.6, y = 0.6, z = 0.6 }; groups = { sorcery_amulet = 1 }; on_use = useamulet; |
<
>
>
>
>
>
>
>
>
|
>
<
|
|
<
>
|
>
>
>
>
>
>
>
>
>
>
|
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
if not gem.foreign_amulet then local img = sorcery.lib.image local img_stone = img('sorcery_amulet.png'):multiply(sorcery.lib.color(gem.tone)) local img_sparkle = img('sorcery_amulet_sparkle.png') local useamulet = function(stack,user,target) local sp = sorcery.amulet.getspell(stack) if not sp or not sp.cast then return nil end local usedamulet if stack:get_count() == 1 then usedamulet = stack else usedamulet = ItemStack(stack) usedamulet:set_count(1) end local probe = sorcery.spell.probe(user:get_pos()) -- amulets don't work in antimagic fields, though some may want to -- implement this logic themselves (for instance to check a range) if (probe.disjunction and not sp.ignore_disjunction) then return nil end local stats = sorcery.amulet.stats(usedamulet) local ctx = { caster = user; target = target; stats = stats; wield = usedamulet; amulet = stack:get_definition()._sorcery.amulet; meta = stack:get_meta(); -- avoid spell boilerplate color = sorcery.lib.color(sp.tone); today = minetest.get_day_count(); probe = probe; heading = { pos = user:get_pos(); ................................................................................ pos = user:get_pos(); gain = 1; }) end if ctx.sparkle then sorcery.vfx.cast_sparkle(user, ctx.color, stats.power,0.5) end local infinirune = minetest.check_player_privs(user, 'sorcery:infinirune') if res == nil then if not infinirune then sorcery.amulet.setrune(usedamulet) end end if stack:get_count() == 1 then return ctx.wield else if not infinirune then stack:take_item(1) local leftover = user:get_inventory():add_item('main',usedamulet) if leftover and leftover:get_count() > 0 then minetest.add_item(user:get_pos(), leftover) end end return stack end end; minetest.register_craftitem(amuletname, { description = sorcery.lib.str.capitalize(name) .. ' amulet'; inventory_image = img_sparkle:blit(img_stone):render(); wield_scale = { x = 0.6, y = 0.6, z = 0.6 }; groups = { sorcery_amulet = 1 }; on_use = useamulet; |
Modified init.lua from [c1be2dc670] to [98e23bb4d1].
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
-- convenience 'str', 'math'; -- serialization 'marshal', 'json'; -- data structures 'tbl', 'class'; -- wrappers 'color', 'image', 'ui'; -- game 'node', 'item'; } sorcery.stage('worldbuilding',data,root) root {'compat','matreg'} if not sorcery.stage('loadlore', data, root) then |
| |
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
-- convenience
'str', 'math';
-- serialization
'marshal', 'json';
-- data structures
'tbl', 'class';
-- wrappers
'color', 'image', 'ui', 'obj';
-- game
'node', 'item';
}
sorcery.stage('worldbuilding',data,root)
root {'compat','matreg'}
if not sorcery.stage('loadlore', data, root) then
|
Modified interop.lua from [913cc0d6da] to [66e6db7c2d].
41 42 43 44 45 46 47 48 49 |
{'top', 'sorcery:mill', 'output'};
{'side', 'sorcery:mill', 'grinder'};
{'bottom', 'sorcery:mill', 'input'};
{'bottom', 'sorcery:harvester', 'charge'};
-- output handled on our side
}
end
|
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
{'top', 'sorcery:mill', 'output'}; {'side', 'sorcery:mill', 'grinder'}; {'bottom', 'sorcery:mill', 'input'}; {'bottom', 'sorcery:harvester', 'charge'}; -- output handled on our side {'bottom', 'sorcery:runeforge', 'amulet'}; -- output handled on our side } end if minetest.get_modpath('mtg_craftguide') and minetest.get_modpath('sfinv') then -- the craft guide is handy, but not only is it glitched to the point of enabling -- trivial denial of service attacks against a server, it breaks some of the most -- basic mechanics of the sorcery mod. we disable it except for players with a -- specific debugging privilege. i suppose we could also add a 'potion of -- omniscience' that allows brief access, but i'm disinclined to; it feels gross. local pg = sfinv.pages['mtg_craftguide:craftguide'] local cb = pg.is_in_nav -- currently this isn't used by mtgcg, but doing this gives us some future- -- proofing, and keeps us from fucking up any competing access control that -- might be in use. pg.is_in_nav = function(self,player, ...) -- unfortunately, this is a purely cosmetic "access control" mechanism; -- sfinv doesn't actually check if a page is available to a player before -- showing it to them. ironic, given how the author specifically warns -- people in his modding tutorial that the client can submit any form it -- wants at any time… 🙄 if not minetest.check_player_privs(player, 'sorcery:omniscience') then return false end if cb then return cb(self,player,...) else return true end end end |
Modified lib/node.lua from [5b83bf6142] to [4b89fedafd].
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
is_air = function(pos) local n = sorcery.lib.node.force(pos) if n.name == 'air' then return true end local d = minetest.registered_nodes[n.name] if not d then return false end return not d.walkable end; get_arrival_point = function(pos) local air = sorcery.lib.node.is_air if air(pos) then local n = {x=0,y=1,z=0} if air(vector.add(pos,n)) then return pos end local down = vector.subtract(pos,n) if air(down) then return down end else return nil end end; amass = function(startpoint,names,directions) if not directions then directions = ofs.neighbors end local nodes, positions, checked = {},{},{} local checkedp = function(pos) for _,v in pairs(checked) do |
> > > > > > > > | | < < < < > > > | > > > > > > > |
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
is_air = function(pos) local n = sorcery.lib.node.force(pos) if n.name == 'air' then return true end local d = minetest.registered_nodes[n.name] if not d then return false end return not d.walkable end; is_clear = function(pos) if not sorcery.lib.node.is_air(pos) then return false end local ents = minetest.get_objects_inside_radius(pos,0.5) if #ents > 0 then return false end return true end; get_arrival_point = function(pos) local try = function(p) local air = sorcery.lib.node.is_clear if air(p) then if air(vector.offset(p,0,1,0)) then return p end if air(vector.offset(p,0,-1,0)) then return vector.offset(p,0,-1,0) end end return false end do local t = try(pos) if t then return t end end for _,o in pairs(ofs.neighbors) do local p = vector.add(pos, o) do local t = try(p) if t then return t end end end end; amass = function(startpoint,names,directions) if not directions then directions = ofs.neighbors end local nodes, positions, checked = {},{},{} local checkedp = function(pos) for _,v in pairs(checked) do |
Added lib/obj.lua version [42345da92a].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
-- functions for working with entities inexplicably missing -- from the game API local fn = {} -- WARNING: INEFFICIENT AS FUCK fn.identify = function(objref) --> objectid for _, o in pairs(minetest.get_connected_players()) do if objref == o then return o:get_player_name(), 'player' end end for id, le in pairs(minetest.luaentities) do if le.object == objref then return id, 'entity' end end end fn.handle = sorcery.lib.class { __newindex = function(self,key,newval) local hnd if self.player then hnd = minetest.get_player_by_name(self._id) else hnd = minetest.luaentities[self._id] end if key == 'id' then if type(newval) == 'string' then local p = minetest.get_player_by_name(newval) if p then self._id = newval self.player = true return end end if minetest.luaentities[newval] then self._id = newval self.player = false else error('attempted to assign invalid ID to entity handle') end elseif key == 'obj' then local no, kind = fn.identify(newval) if no then self._id = no if kind == 'player' then self.player = true else self.player = false end else error('attempted to assign invalid ObjectRef to entity handle') end elseif key == 'stack' and self.kind == 'item' then hnd:set_item(newval) end end; __index = function(self,key) local hnd if self.player then hnd = minetest.get_player_by_name(self._id) else hnd = minetest.luaentities[self._id] end if key == 'online' then return hnd ~= nil elseif key == 'id' then if self.player then return nil else return self._id end elseif key == 'obj' then if self.player then return hnd else return hnd.object end elseif key == 'kind' then if self.player then return 'player' elseif hnd.name == '__builtin:item' then return 'item' else return 'object' end elseif key == 'name' then if self.player then return self._id elseif self.kind == 'item' then return ItemStack(hnd.itemstring):get_name() else return hnd.name end elseif key == 'stack' and self.kind == 'item' then return ItemStack(hnd.itemstring) elseif key == 'height' then if kind == 'item' then return 0.5 elseif kind == 'player' then local eh = hnd.object:get_properties().eye_height return eh and (eh*1.2) or 1 else local box = hnd.object:get_properties().collisionbox if box then local miny,maxy = box[2], box[5] return maxy-miny, miny else return 0 end end end end; construct = function(h) local kind, id if type(h) == 'string' and minetest.get_player_by_name(h) ~= nil then kind = 'player'; id = h elseif minetest.luaentities[h] then kind = 'entity'; id = h else id, kind = fn.identify(h) end if not id then error('attempted to construct object handle from invalid value') end return { player = kind == 'player'; _id = id; } end; } return fn |
Modified privs.lua from [62e9e10513] to [7ff60372f5].
1 2 3 4 5 |
minetest.register_privilege('sorcery:infinirune', { description = "runes don't discharge upon use, for debugging use only"; give_to_singleplayer = false; give_to_admin = false; }) |
> > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
minetest.register_privilege('sorcery:infinirune', { description = "runes don't discharge upon use, for debugging use only"; give_to_singleplayer = false; give_to_admin = false; }) if minetest.get_modpath('mtg_craftguide') then minetest.register_privilege('sorcery:omniscience', { description = "access the all-knowing crafting guide"; give_to_singleplayer = false; give_to_admin = false; }) end |
Added sounds/sorcery_duplicate_bg.ogg version [].
Added sounds/sorcery_genesis.ogg version [8332db2096].
cannot compute difference between binary files
Modified spell.lua from [cfeba0d2de] to [92125c4f2c].
133 134 135 136 137 138 139 140 141 142 143 144 145 146 ... 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 ... 363 364 365 366 367 368 369 |
if s.i then sorcery.spell.active[s.i] = nil else for k,v in pairs(sorcery.spell.active) do if v == spell then sorcery.spell.active[k] = nil break end end end end end -- when a new spell is created, we analyze it and make the appropriate calls -- to minetest.after to queue up the events. each job returned needs to be -- saved in 'jobs' so they can be canceled if the spell is disjoined. no polling -- necessary :D sorcery.spell.cast = function(proto) ................................................................................ for _,j in ipairs(s.jobs) do j:cancel() end for _,v in ipairs(s.vfx) do minetest.delete_particlespawner(v.handle) end for _,i in ipairs(s.sfx) do s.silence(i) end for _,i in ipairs(s.impacts) do i.effect:stop() end end s.release_subject = function(si) local t = s.subjects[si] print('releasing against',si,t) for _,f in pairs(s.sfx) do if f.subject == t then s.silence(f) end end for _,f in pairs(s.impacts) do if f.subject == t then f.effect:stop() end end for _,f in pairs(s.vfx) do if f.subject == t then minetest.delete_particlespawner(f.handle) end end s.subjects[si] = nil end ................................................................................ if s.terminate then s:terminate() end sorcery.spell.active[myid] = nil end) end s.starttime = minetest.get_server_uptime() return s end |
> > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > |
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 ... 209 210 211 212 213 214 215 216 217 218 219 220 221 222 ... 388 389 390 391 392 393 394 395 396 397 398 |
if s.i then sorcery.spell.active[s.i] = nil else for k,v in pairs(sorcery.spell.active) do if v == spell then sorcery.spell.active[k] = nil break end end end end end sorcery.spell.ensorcelled = function(player,spell) if type(player) == 'string' then player = minetest.get_player_by_name(player) end for _,s in pairs(sorcery.spell.active) do if spell and (s.name ~= spell) then goto skip end for _,sub in pairs(s.subjects) do if sub.player == player then return s end end ::skip::end return false end sorcery.spell.each = function(player,spell) local idx = 0 return function() repeat idx = idx + 1 local sp = sorcery.spell.active[idx] if sp == nil then return nil end if spell == nil or sp.name == spell then for _,sub in pairs(sp.subjects) do if sub.player == player then return sp end end end until idx >= #sorcery.spell.active end end -- when a new spell is created, we analyze it and make the appropriate calls -- to minetest.after to queue up the events. each job returned needs to be -- saved in 'jobs' so they can be canceled if the spell is disjoined. no polling -- necessary :D sorcery.spell.cast = function(proto) ................................................................................ for _,j in ipairs(s.jobs) do j:cancel() end for _,v in ipairs(s.vfx) do minetest.delete_particlespawner(v.handle) end for _,i in ipairs(s.sfx) do s.silence(i) end for _,i in ipairs(s.impacts) do i.effect:stop() end end s.release_subject = function(si) local t = s.subjects[si] for _,f in pairs(s.sfx) do if f.subject == t then s.silence(f) end end for _,f in pairs(s.impacts) do if f.subject == t then f.effect:stop() end end for _,f in pairs(s.vfx) do if f.subject == t then minetest.delete_particlespawner(f.handle) end end s.subjects[si] = nil end ................................................................................ if s.terminate then s:terminate() end sorcery.spell.active[myid] = nil end) end s.starttime = minetest.get_server_uptime() return s end minetest.register_on_dieplayer(function(player) sorcery.spell.disjoin{target=player} end) |
Modified vfx.lua from [aaa85eb70f] to [423ee7c3cf].
104 105 106 107 108 109 110 111 112 |
}, acceleration = { x = 0, y = -1, z = 0 } } end end |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
}, 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 = target end height = height or 1 local scenter = vector.add(tpos, {x=0,y=height/2,z=0}) for i=1,math.random(64*(strength or 1),128*(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 minetest.add_particle { pos = ppos; velocity = vector.multiply(dir,vel); expirationtime = far / vel; size = math.random()*2.4 + 0.6; texture = sorcery.lib.image('sorcery_sputter.png'):glow(col):render(); glow = 14; animation = { type = 'vertical_frames', length = far/vel; aspect_w = 16, aspect_h = 16; }; } end end |