Differences From
Artifact [47aa367750]:
1 1 -- a rune is an abstract object created by a runeforge, which can be
2 2 -- applied to an amulet in order to imbue that amulet with unique
3 3 -- and fearsome powers. the specific spell depends on the stone the
4 4 -- rune is applied to, and not all runes can necessarily be applied
5 5 -- to all stones.
6 6
7 +local sparkle = function(color, spell, amt,time,minsize,maxsize,sh)
8 + spell.visual_subjects {
9 + amount = amt, time = time, -- attached = s;
10 + minpos = { x = -0.3, y = -0.5, z = -0.3 };
11 + maxpos = { x = 0.3, y = sh*1.1, z = 0.3 };
12 + minvel = { x = -0.4, y = -0.2, z = -0.4 };
13 + maxvel = { x = 0.4, y = 0.2, z = 0.4 };
14 + minacc = { x = -0.5, y = -0.4, z = -0.5 };
15 + maxacc = { x = 0.5, y = 0.4, z = 0.5 };
16 + minexptime = 1.0, maxexptime = 2.0;
17 + minsize = minsize, maxsize = maxsize, glow = 14;
18 + texture = sorcery.vfx.glowspark(color):render();
19 + animation = {
20 + type = 'vertical_frames';
21 + aspect_w = 16, aspect_h = 16;
22 + };
23 + }
24 +end
25 +local sparktrail = function(fn,tgt,color)
26 + return (fn or minetest.add_particlespawner)({
27 + amount = 240, time = 1, attached = tgt;
28 + minpos = {x = -0.4, y = -0.5, z = -0.4};
29 + maxpos = {x = 0.4, y = tgt:get_properties().eye_height or 0.5, z = 0.4};
30 + minacc = {x = 0.0, y = 0.05, z = 0.0};
31 + maxacc = {x = 0.0, y = 0.15, z = 0.0};
32 + minexptime = 1.5, maxexptime = 5;
33 + minsize = 0.5, maxsize = 2.6, glow = 14;
34 + texture = sorcery.vfx.glowspark(color):render();
35 + animation = {
36 + type = 'vertical_frames', length = 5.1;
37 + aspect_w = 16, aspect_h = 16;
38 + };
39 + });
40 +end
7 41 return {
8 42 translocate = {
9 43 name = 'Translocate';
10 44 tone = {0,235,233};
11 45 minpower = 3;
12 46 rarity = 15;
13 47 amulets = {
................................................................................
48 82 else
49 83 local pos = minetest.string_to_pos(ctx.meta:get_string('rune_return_dest'))
50 84 ctx.meta:set_string('rune_return_dest','')
51 85 local subjects = { ctx.caster }
52 86 local center = ctx.caster:get_pos()
53 87 ctx.sparkle = false
54 88 local delay = math.max(3,10 - ctx.stats.power) + 3*(math.random()*2-1)
55 - print('teledelay',delay,ctx.stats.power)
56 89 for _,s in pairs(subjects) do
57 90 local offset = vector.subtract(s:get_pos(), center)
58 91 local pt = sorcery.lib.node.get_arrival_point(vector.add(pos,offset))
59 92 if pt then
60 - minetest.sound_play('sorcery_stutter', {
61 - object = s, gain = 0.8;
62 - },true)
63 - local windup = minetest.sound_play('sorcery_windup',{
64 - object = s, gain = 0.4;
65 - })
93 + -- minetest.sound_play('sorcery_stutter', {
94 + -- object = s, gain = 0.8;
95 + -- },true)
66 96 local mydelay = delay + math.random(-10,10)*.1;
67 - local spark = sorcery.lib.image('sorcery_spark.png')
68 97 local sh = s:get_properties().eye_height
69 - local sparkle = function(amt,time,minsize,maxsize)
70 - minetest.add_particlespawner {
71 - amount = amt, time = time, attached = s;
72 - minpos = { x = -0.3, y = -0.5, z = -0.3 };
73 - maxpos = { x = 0.3, y = sh*1.1, z = 0.3 };
74 - minvel = { x = -0.4, y = -0.2, z = -0.4 };
75 - maxvel = { x = 0.4, y = 0.2, z = 0.4 };
76 - minacc = { x = -0.5, y = -0.4, z = -0.5 };
77 - maxacc = { x = 0.5, y = 0.4, z = 0.5 };
78 - minexptime = 1.0, maxexptime = 2.0;
79 - minsize = minsize, maxsize = maxsize, glow = 14;
80 - texture = spark:blit(spark:multiply(sorcery.lib.color(29,205,247))):render();
81 - animation = {
82 - type = 'vertical_frames';
83 - aspect_w = 16, aspect_h = 16;
98 + local color = sorcery.lib.color(29,205,247)
99 + sorcery.lib.node.preload(pt,s)
100 + sorcery.spell.cast {
101 + duration = mydelay;
102 + caster = ctx.caster;
103 + subjects = {{player=s,dest=pt}};
104 + timeline = {
105 + [0] = function(sp,_,timeleft)
106 + sparkle(color,sp,timeleft*100, timeleft, 0.3,1.3, sh)
107 + sp.windup = (sp.play_now{
108 + sound = 'sorcery_windup';
109 + where = 'subjects';
110 + gain = 0.4;
111 + fade = 1.5;
112 + })[1]
113 + end;
114 + [0.4] = function(sp,_,timeleft)
115 + sparkle(color,sp,timeleft*150, timeleft, 0.6,1.8, sh)
116 + end;
117 + [0.7] = function(sp,_,timeleft)
118 + sparkle(color,sp,timeleft*80, timeleft, 2,4, sh)
119 + end;
120 + [1] = function(sp)
121 + sp.silence(sp.windup)
122 + minetest.sound_play('sorcery_zap', { pos = pt, gain = 0.4 },true)
123 + minetest.sound_play('sorcery_zap', { pos = s:get_pos(), gain = 0.4 },true)
124 + sorcery.vfx.body_sparkle(nil,sorcery.lib.color(20,255,120),2,s:get_pos())
125 + s:set_pos(pt)
126 + sorcery.vfx.body_sparkle(s,sorcery.lib.color(20,120,255),2)
127 + end;
128 + };
129 + sounds = {
130 + [0] = {
131 + pos = 'subjects';
132 + sound = 'sorcery_stutter';
84 133 };
85 - }
86 - end
87 - sparkle(mydelay*100,mydelay,0.3,1.3)
88 - minetest.after(mydelay*0.4, function()
89 - local timeleft = mydelay - (mydelay*0.4)
90 - sparkle(timeleft*150, timeleft, 0.6,1.8)
91 - end)
92 - minetest.after(mydelay*0.7, function()
93 - local timeleft = mydelay - (mydelay*0.7)
94 - sparkle(timeleft*80, timeleft, 2,4)
95 - end)
96 - sorcery.lib.node.preload(pt,s)
97 - minetest.after(mydelay, function()
98 - minetest.sound_stop(windup)
99 - minetest.sound_play('sorcery_zap', { pos = pt, gain = 0.4 },true)
100 - minetest.sound_play('sorcery_zap', { pos = s:get_pos(), gain = 0.4 },true)
101 - sorcery.vfx.body_sparkle(nil,sorcery.lib.color(20,255,120),2,s:get_pos())
102 - s:set_pos(pt)
103 - sorcery.vfx.body_sparkle(s,sorcery.lib.color(20,120,255),2)
104 - end)
134 + };
135 + }
105 136 end
106 137 end
107 138 end
108 139 end;
109 140 frame = {
110 141 iridium = {
111 142 name = 'Mass Return';
................................................................................
162 193 };
163 194 disjoin = {
164 195 name = 'Disjoin';
165 196 tone = {159,235,0};
166 197 minpower = 4;
167 198 rarity = 20;
168 199 amulets = {
169 - amethyst = {
200 + sapphire = {
170 201 name = 'Unsealing';
171 202 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';
203 + };
204 + amethyst = {
205 + name = 'Purging';
206 + 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';
207 + cast = function(ctx)
208 + local h = ctx.heading.eyeheight * 1.1
209 + minetest.add_particlespawner {
210 + time = 0.2, amount = math.random(200,250), attached = ctx.caster;
211 + glow = 14, texture = sorcery.vfx.glowspark(sorcery.lib.color(156,255,10)):render();
212 + minpos = {x = -0.3, y = -0.5, z = -0.3};
213 + maxpos = {x = 0.3, y = h, z = 0.3};
214 + minvel = {x = -1.8, y = -1.8, z = -1.8};
215 + maxvel = {x = 1.8, y = 1.8, z = 1.8};
216 + minsize = 0.2, maxsize = 5;
217 + animation = {
218 + type = 'vertical_frames', length = 4.1;
219 + aspect_w = 16, aspect_h = 16;
220 + };
221 + minexptime = 2, maxexptime = 4;
222 + }
223 + minetest.sound_play('sorcery_disjoin',{object=ctx.caster},true)
224 + sorcery.spell.disjoin{target=ctx.caster}
225 + end;
172 226 };
173 227 emerald = {
228 + name = 'Disjunction Field';
229 + desc = 'Render an area totally opaque to spellwork for a period of time, disrupting any existing spells and preventing further spellcasting therein';
230 + };
231 + ruby = {
232 + name = 'Disjunction';
233 + desc = 'Wield this amulet against a spellcaster to disrupt and abort all their spells in progress, perhaps to trap a foe intent on translocating away, or unleash its force upon the victim of a malign hex to free them from its clutches';
234 + frame = {
235 + iridium = {
236 + name = 'Nullification';
237 + desc = 'Not only will your victim\'s spells be nullified, but all enchanted objects they carry will be stripped of their power — or possibly even destroyed outright';
238 + };
239 + };
240 + };
241 + luxite = {
242 + name = 'Disjunctive Aura';
243 + desc = 'For a time, all magic undertaken in your vicinity will fail totally';
244 + cast = function(ctx)
245 + sorcery.spell.cast {
246 + caster = ctx.caster, attach = 'caster';
247 + disjunction = true, range = 4 + ctx.stats.power;
248 + duration = 10 + ctx.stats.power * 3;
249 + timeline = {
250 + [0] = function(s,_,tl)
251 + sparkle(sorcery.lib.color(120,255,30), s,
252 + 30 * tl, tl, 0.3,1.4, ctx.heading.eyeheight*1.1)
253 + end
254 + };
255 + sounds = {
256 + [0] = { sound = 'sorcery_disjoin', pos = 'caster' };
257 + [1] = { sound = 'sorcery_powerdown', pos = 'caster' };
258 + };
259 + }
260 + end
261 + };
262 + diamond = {
174 263 name = 'Mundanity';
175 264 desc = 'Strip away the effects of all active potions and spells in your immediate vicinity, leaving adversaries without their magicks to enhance and protect them, and allies free of any curses they may be hobbled by -- and, of course, vice versa';
265 + frame = {
266 + iridium = {
267 + name = 'Spellshatter';
268 + desc = 'Blast out a tidal wave of anti-magic that will nullify active spells, but also disenchant or destroy all magical items in range of its violently mundane grip';
269 + };
270 + };
176 271 };
177 272 }
178 273 };
179 274 repulse = {
180 275 name = 'Repulse';
181 276 tone = {0,180,235};
182 277 minpower = 1;
183 278 rarity = 7;
184 279 amulets = {
185 280 amethyst = {
186 281 name = 'Hurling';
187 282 desc = 'Wielding this amulet, a mere flick of your fingers will lift any target of your choice bodily into the air and press upon them with tremendous repulsive force, throwing them like a hapless ragdoll out of your path';
283 + cast = function(ctx)
284 + if not (ctx.target and ctx.target.type == 'object') then return false end
285 + local tgt = ctx.target.ref
286 + local line = vector.subtract(ctx.caster:get_pos(), tgt:get_pos())
287 + -- direction vector from target to caster
288 + print('line',dump(line))
289 + local dir,mag = sorcery.lib.math.vsep(line)
290 + if mag > 6 then return false end -- no cheating!
291 + local force = 20 + (ctx.stats.power * 2.5)
292 + minetest.sound_play('sorcery_hurl',{pos=tgt:get_pos()},true)
293 + local immortal = tgt:get_luaentity():get_armor_groups().immortal or 0
294 + if minetest.is_player(tgt) or immortal == 0 then
295 + tgt:punch(ctx.caster, 1, {
296 + full_punch_interval = 1;
297 + damage_groups = { fleshy = force / 10 };
298 + })
299 + end
300 + sparktrail(nil,tgt,sorcery.lib.color(101,226,255))
301 + if dir.y > 0 then dir.y = 0 end -- spell always lifts
302 + dir = vector.add(dir, {x=0,z=0,y=-0.25})
303 + local vel = vector.multiply(dir,0-force)
304 + tgt:add_velocity(vel)
305 + end;
306 + };
307 + ruby = {
308 + name = 'Liftoff';
309 + 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';
310 + cast = function(ctx)
311 + local power = 14 * (1+(ctx.stats.power * 0.2))
312 + minetest.sound_play('sorcery_hurl',{object=ctx.caster},true)
313 + sorcery.spell.cast {
314 + caster = ctx.caster;
315 + subjects = {{player=ctx.caster}};
316 + duration = power * 0.25;
317 + timeline = {
318 + [0] = function(s,_,tl)
319 + sparktrail(s.visual_subjects,ctx.caster,sorcery.lib.color(255,252,93))
320 + ctx.caster:add_velocity{y=power;x=0,z=0}
321 + s.affect {
322 + duration = power * 0.25;
323 + raise = 2;
324 + fall = (power * 0.25) * 0.3;
325 + impacts = {
326 + gravity = 0.1;
327 + };
328 + }
329 + end;
330 + };
331 + }
332 + end;
188 333 };
189 334 sapphire = {
190 335 name = 'Flinging';
191 336 desc = 'Toss an enemy violently into the air, and allow the inevitable impact to do your dirty work for you';
192 337 };
193 338 emerald = {
194 339 name = 'Shockwave';
................................................................................
212 357 amulets = {
213 358 amethyst = {
214 359 name = 'Sapping';
215 360 desc = 'Punch a hole in enemy fortifications big enough to slip through but small enough to avoid immediate attention';
216 361 };
217 362 ruby = {
218 363 name = 'Shattering';
219 - desc = 'Tear a violent wound in the earth with the destructive force of this amulet';
364 + desc = 'Tear a violent wound in the land with the destructive force of this amulet';
220 365 };
221 366 emerald = {
222 367 name = 'Detonate';
223 368 desc = 'Wielding this amulet, you can loose an extraordinarily powerful bolt of flame from your fingertips that will explode violently on impact, wreaking total havoc wherever it lands';
224 369 cast = function(ctx)
225 370 local speed = 40
226 371 local radius = math.random(math.floor(ctx.stats.power*0.5),math.ceil(ctx.stats.power))
................................................................................
233 378 bolt:set_velocity(vel)
234 379 end;
235 380 };
236 381 luxite = {
237 382 name = 'Lethal Aura';
238 383 desc = 'For a time, anyone who approaches you, whether friend or foe, will suffer immediate retaliation as they are quickly sapped of their life force';
239 384 };
385 + mese = {
386 + name = 'Cataclysm';
387 + desc = 'Use this amulet once to pick a target, then visit devastation upon it from afar with a mere snap of your fingers';
388 + };
240 389 diamond = {
241 390 name = 'Killing';
242 391 mingrade = 4;
243 392 desc = 'Wield this amulet against a foe to instantly snuff the life out of their mortal form, regardless of their physical protections.';
244 393 cast = function(ctx)
245 394 if not (ctx.target and ctx.target.type == 'object') then return false end
246 395 local tgt = ctx.target.ref