Overview
Comment: | add some more spells, add spell infrastructure to support metamagic, especially disjunction, various tweaks and bugfixes. [emergency commit] |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
00922196a9362e9cadeb8f074f7a9459 |
User & Date: | lexi on 2020-10-24 01:21:08 |
Other Links: | manifest | tags |
Context
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 | |
2020-10-24
| ||
01:21 | add some more spells, add spell infrastructure to support metamagic, especially disjunction, various tweaks and bugfixes. [emergency commit] check-in: 00922196a9 user: lexi tags: trunk | |
2020-10-23
| ||
00:08 | fix some showstopping bugs, more amulet spells, add sound effects, improve teleportation visuals check-in: 90e64c483c user: lexi tags: trunk | |
Changes
Modified data/runes.lua from [47aa367750] to [4908250df2].
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
Modified entities.lua from [2261aac8b4] to [9e257faffc].
3 3 age = u.marshal.t.u32; 4 4 lastemit = u.marshal.t.u32; 5 5 } 6 6 7 7 minetest.register_entity('sorcery:spell_projectile_flamebolt',{ 8 8 initial_properties = { 9 9 visual = "sprite"; 10 - -- use_texture_alpha = true; 10 + use_texture_alpha = true; 11 11 textures = {'sorcery_fireball.png'}; 12 - groups = {immortal = 1}; 13 12 visual_size = { x = 2, y = 2, z = 2 }; 14 13 physical = true; 15 14 collide_with_objects = true; 16 15 pointable = false; 17 16 glow = 14; 18 17 static_save = false; 18 + shaded = false; 19 19 }; 20 + on_activate = function(self) 21 + self.object:set_armor_groups{immortal = 1} 22 + end; 20 23 on_step = function(self,dtime,collision) 21 24 local pos = self.object:get_pos() 22 25 if not self._meta then 23 26 self._meta = { age = 0; lastemit = 0; emitters = {} } 24 27 goto emit 25 28 end 26 29 27 30 self._meta.age = self._meta.age + dtime 28 31 if self._meta.age >= 6 then 29 32 goto destroy 30 33 elseif (self._meta.age - self._meta.lastemit) < 3 then 31 34 goto collcheck 32 35 end 36 + 37 + -- fireballs dissipate when entering antimagic fields 38 + do local probe = sorcery.spell.probe(self.object:get_pos()) 39 + if probe.disjunction and not self._meta.ignore_disjunction then 40 + sorcery.vfx.cast_sparkle(nil,sorcery.lib.color(255,90,10),3,0.3,self.object:get_pos()) 41 + goto destroy 42 + end end 33 43 34 44 ::emit:: do 35 45 self._meta.lastemit = self._meta.age 36 46 local spawn = function(num, life_min, life_max, size_min, size_max, gl, speed, img) 37 47 table.insert(self._meta.emitters, minetest.add_particlespawner { 38 48 amount = num; 39 49 minexptime = life_min;
Modified gems.lua from [d9755d7797] to [58885d2bb0].
56 56 local img = sorcery.lib.image 57 57 local img_stone = img('sorcery_amulet.png'):multiply(sorcery.lib.color(gem.tone)) 58 58 local img_sparkle = img('sorcery_amulet_sparkle.png') 59 59 local useamulet = function(stack,user,target) 60 60 local sp = sorcery.amulet.getspell(stack) 61 61 if not sp or not sp.cast then return nil end 62 62 local stats = sorcery.amulet.stats(stack) 63 + local probe = sorcery.spell.probe(user:get_pos()) 64 + -- amulets don't work in antimagic fields, though some may want to 65 + -- implement this logic themselves (for instance to check a range) 66 + if (probe.disjunction and not sp.ignore_disjunction) then return nil end 63 67 64 68 local ctx = { 65 69 caster = user; 66 70 target = target; 67 71 stats = stats; 68 72 amulet = stack; 69 73 meta = stack:get_meta(); -- avoid spell boilerplate 70 74 color = sorcery.lib.color(sp.tone); 71 75 today = minetest.get_day_count(); 76 + probe = probe; 72 77 heading = { 73 78 pos = user:get_pos(); 74 79 yaw = user:get_look_dir(); 75 80 pitch = user:get_look_vertical(); 76 81 angle = user:get_look_horizontal(); 77 82 eyeheight = user:get_properties().eye_height; 78 83 };
Modified init.lua from [e9275c1995] to [c1be2dc670].
80 80 local data = sorcery.unit('data',nil,'lore') 81 81 local root = sorcery.unit() 82 82 sorcery.stage('bootstrap',data,root) 83 83 84 84 data {'ui'} 85 85 sorcery.unit('lib') { 86 86 -- convenience 87 - 'str'; 87 + 'str', 'math'; 88 88 -- serialization 89 89 'marshal', 'json'; 90 90 -- data structures 91 91 'tbl', 'class'; 92 92 -- wrappers 93 93 'color', 'image', 'ui'; 94 94 -- game ................................................................................ 118 118 sorcery.registry.mk(k,v) 119 119 end 120 120 end 121 121 end 122 122 123 123 sorcery.stage('startup',data) 124 124 for _,u in pairs { 125 - 'vfx'; 'attunement'; 'context'; 'itemclass'; 125 + 'vfx'; 'attunement'; 'context'; 'itemclass'; 'spell'; 126 126 'potions'; 'metal', 'gems'; 'leylines'; 'infuser'; 127 127 'altar'; 'wands'; 'tools', 'crafttools'; 'enchanter'; 128 128 'harvester'; 'metallurgy-hot', 'metallurgy-cold'; 129 129 'entities'; 'recipes'; 'coins'; 'interop'; 130 130 'tnodes'; 'forcefield'; 'farcaster'; 'portal'; 131 131 'cookbook', 'writing'; 'disassembly'; 'displacer'; 132 132 'gravitator'; 'precipitator'; 'calendar', 'astrolabe';
Modified lib/tbl.lua from [eefda5e589] to [6f943d189b].
86 86 local new = fn.copy(r1) 87 87 for i=1,#r2 do 88 88 new[#new + 1] = r2[i] 89 89 end 90 90 return new 91 91 end 92 92 93 -fn.capitalize = function(str) 94 - return string.upper(string.sub(str, 1,1)) .. string.sub(str, 2) 95 -end 96 - 97 93 fn.has = function(tbl,value,eqfn) 98 94 for k,v in pairs(tbl) do 99 95 if eqfn then 100 96 if eqfn(v,value,tbl) then return true, k end 101 97 else 102 98 if value == v then return true, k end 103 99 end ................................................................................ 142 138 table.sort(keys) 143 139 return fn.each(keys, function(k,i) 144 140 return f(tbl[k],k,i) 145 141 end) 146 142 end 147 143 148 144 fn.iter = function(tbl,fn) 149 - for i=1,#tbl do 150 - fn(tbl[i], i) 151 - end 145 + for i,v in ipairs(tbl) do fn(v, i) end 152 146 end 153 147 154 148 fn.map = function(tbl,fn) 155 149 local new = {} 156 150 for k,v in pairs(tbl) do 157 151 local nv, nk = fn(v, k) 158 152 new[nk or k] = nv ................................................................................ 162 156 163 157 fn.fold = function(tbl,fn,acc) 164 158 if #tbl == 0 then 165 159 fn.each_o(tbl, function(v) 166 160 acc = fn(acc, v, k) 167 161 end) 168 162 else 169 - for i=0,#tbl do 170 - acc = fn(acc,tbl[i],i) 163 + for i,v in ipairs(tbl) do 164 + acc = fn(acc,v,i) 171 165 end 172 166 end 173 167 return acc 174 168 end 175 169 176 170 fn.walk = function(tbl,path) 177 171 if type(path) == 'table' then
Modified sorcery.md from [246ca8c1c8] to [b729085cc9].
16 16 * **xdecor** for various tools and ingredients, especially honey and the hammer 17 17 * **basic_materials** for crafting ingredients 18 18 * **instant_ores** for ore generation. temporary, will be removed and replaced with home-grown mechanism soon 19 19 * **farming redo** for potion ingredients 20 20 * **late** for spell, potion, and gravitator effects 21 21 * **note**: in order for the gravitator to work, the late condition interval must be lowered from its default of 1.0 to 0.1. this currently can only be done by altering a variable at the top of `late/conditions.lua`, though a note in the source suggests a configuration option will be added eventually. hopefully this is so. 22 22 23 +## libraries 24 + * **luajit**, because `sorcery`'s code uses modern features not available in the ancient lua version bundled with minetest. alternately, it may be possible to build minetest against a more recent lua version if you're feeling masochistic; luajit will probably be faster tho and has first-party support 25 + 23 26 # interoperability 24 27 sorcery has special functionality to ensure it can cooperate with various other modules, although they are not necessarily required for it to function. 25 28 26 29 ## xdecor 27 30 by default, `sorcery` disables the xdecor enchanter, since `sorcery` offers its own, much more sophisticated enchantment mechanism. however, the two can coexist if you really want; a configuration flag can be used to prevent `sorcery` disabling the xdecor enchanter. 28 31 29 32 ## hopper
Modified vfx.lua from [343a5ccf55] to [aaa85eb70f].
1 1 sorcery.vfx = {} 2 + 3 +sorcery.vfx.glowspark = function(color) 4 + local spark = sorcery.lib.image('sorcery_spark.png') 5 + return spark:blit(spark:multiply(color)) 6 +end 2 7 3 8 sorcery.vfx.cast_sparkle = function(caster,color,strength,duration,pos) 4 9 local ofs = pos 5 10 and function(x) return vector.add(pos,x) end 6 11 or function(x) return x end 7 12 local height = caster:get_properties().eye_height 8 13 minetest.add_particlespawner {
Modified wands.lua from [b35b5eeee9] to [e661ef77a3].
207 207 end 208 208 209 209 local wand_cast = function(stack, user, target) 210 210 local meta = stack:get_meta() 211 211 local wand = sorcery.wands.util.getproto(stack) 212 212 if meta:contains('sorcery_wand_spell') == false then return nil end 213 213 local spell = meta:get_string('sorcery_wand_spell') 214 - local castfn = sorcery.data.spells[spell].cast 214 + local spelldata = sorcery.data.spells[spell] 215 + 216 + -- wands don't work in anti-magic fields 217 + local probe = sorcery.spell.probe(user:get_pos()) 218 + if probe.disjunction and not spelldata.ignore_disjunction then return nil end 219 + 220 + local castfn = spelldata.cast 215 221 if castfn == nil then return nil end 216 222 local matprops = sorcery.wands.util.matprops(wand) 217 223 if matprops.bond then 218 224 local userct, found = 0, false 219 225 for i=1,matprops.bond do 220 226 local prop = 'bound_user_' .. tostring(i) 221 227 if meta:contains(prop) then ................................................................................ 253 259 local context = { 254 260 base = wand; 255 261 stats = matprops; 256 262 meta = meta; 257 263 item = stack; 258 264 caster = user; 259 265 target = target; 266 + probe = probe; 260 267 today = minetest.get_day_count(); 261 268 heading = { 262 269 pos = user:get_pos(); 263 270 yaw = user:get_look_dir(); 264 271 pitch = user:get_look_vertical(); 265 272 angle = user:get_look_horizontal(); 266 273 eyeheight = uprops.eye_height;