Differences From
Artifact [196ad8b009]:
41 41 -- the item material and look up the slots offered by this
42 42 -- material. we iterate through the slot definitions to pick
43 43 -- out a candidate slot, criteria being that the slot
44 44 -- possesses the proper affinity, and that the corresponding
45 45 -- slot on the item enchantment record is empty, then pick
46 46 -- the final slot at random from this table if #candidates>0
47 47 -- (otherwise, we fail unceremoniously).
48 - --
48 +
49 + local current_enchant = sorcery.enchant.get(subj)
50 + local material, eligible_spells = sorcery.enchant.getsubj(subj)
51 + if eligible_spells == nil
52 + or #eligible_spells == 0
53 + or material == nil
54 + or material.data.slots == nil
55 + or #material.data.slots == 0
56 + then return false end
57 +
58 + -- determine the properties the enchantment will have
59 + local power = 10
60 + local energy = material.data.maxenergy
61 + local reliability = 100
62 + if ctx.base.gem == 'sapphire' then power = power + 5
63 + elseif ctx.base.gem == 'amethyst' then
64 + energy = energy * (math.random() * 0.7)
65 + elseif ctx.base.gem == 'diamond' then
66 + if math.random(5) == 1 then
67 + power = power * 2
68 + reliability = reliability - (reliability / 4)
69 + else
70 + power = power + 5
71 + reliability = reliability - 10
72 + end
73 + end
74 +
75 + local viable_slots = {}
76 + for i,slot in pairs(material.data.slots) do
77 + if sorcery.lib.tbl.has(slot.affinity,aff) then
78 + for _,spell in pairs(current_enchant.spells) do
79 + if spell.slot == i then goto nonviable end
80 + end
81 + viable_slots[#viable_slots + 1] = i
82 + end
83 + ::nonviable::end
84 + if #viable_slots == 0 then return false end
85 +
86 + local target_slot = viable_slots[math.random(#viable_slots)]
49 87 -- once we have selected an appropriate slot, we iterate
50 88 -- through the list of known enchantments to check the
51 89 -- affinity of each against `aff`. if the affinity matches
52 90 -- the wand, we then check whether the 'recipe' matches.
53 91 -- if so, we've found the enchantment -- apply it to the
54 92 -- object and update its enchantment data. otherwise, we
55 93 -- move on to the next. if no matching recipe is found,
56 - -- we give up and bail, returning 'nil' to signify that
94 + -- we give up and bail, returning 'false' to signify that
57 95 -- a spell was not cast.
58 96
59 - enchantment_sparkle(ctx,affcolor)
97 + local focus_match = function(spec,stack)
98 + local default_mode
99 + if spec.lens then
100 + default_mode = 'dmg'
101 + if minetest.get_item_group(stack:get_name(), 'sorcery_enchanting_lens') == 0
102 + then return false end
103 + local proto = stack:get_definition()._proto
104 + if proto.kind ~= spec.lens or proto.gem ~= spec.gem
105 + then return false end
106 + elseif spec.item then
107 + default_mode = 'consume'
108 + if stack:get_name() ~= spec.item then
109 + return false end
110 + else
111 + return false
112 + end
113 +
114 + local mode
115 + if spec.dmg then mode = 'dmg'
116 + elseif spec.consume then mode = 'consume'
117 + else mode = default_mode end
118 +
119 + if mode == 'dmg' then
120 + stack:add_wear((spec.dmg or 1) * 1000)
121 + return stack
122 + elseif mode == 'consume' then
123 + stack:take_item(spec.consume or 1)
124 + return stack
125 + end
126 + end
127 + for ench,data in pairs(sorcery.data.enchants) do
128 + if data.affinity ~= aff or data.recipe == nil then goto skip end
129 + local newinv = {}
130 + for s,v in pairs(data.recipe) do
131 + newinv[s] = focus_match(v,inv:get_stack('foci',s))
132 + if newinv[s] == false then goto skip end
133 + end
134 + -- is the tool compatible with the chosen spell? we check its groups to
135 + -- see if any of them grant it eligibility; if not, we skip to the next
136 + -- spell. NOTE: this means the same recipe could mean different things
137 + -- for different kinds of tools, if one were so inclined.
138 + for g,v in pairs(subj:get_definition().groups) do
139 + if v ~= 0 and sorcery.lib.tbl.has(data.groups, g)
140 + then goto enchant end
141 + end
142 + goto skip
143 + -- spell matches!
144 + ::enchant:: current_enchant.spells[#current_enchant.spells + 1] = {
145 + id = ench;
146 + slot = target_slot;
147 + boost = power;
148 + reliability = reliability;
149 + }
150 + current_enchant.energy = math.max(current_enchant.energy, energy)
151 +
152 + sorcery.enchant.set(subj, current_enchant)
153 + inv:set_stack('item',1,subj)
154 + for i,v in pairs(newinv) do inv:set_stack('foci',i,v) end
155 + sorcery.enchant.update_enchanter(ctx.target.under)
156 + enchantment_sparkle(ctx,affcolor)
157 + do return nil end
158 + ::skip::end
159 + return false
60 160 end
61 161 };
62 162 end
63 163 -- note: this was written before terminology was standardized,
64 164 -- and "leytype" corresponds to what is otherwise known as an
65 165 -- "affinity"; "affinity" after this comment is widely misused
66 166 return {
................................................................................
93 193 leytype = 'imperic';
94 194 affinity = {'pine','dark'};
95 195 cast = function(ctx)
96 196 if ctx.target == nil or ctx.target.type ~= 'node' then return false end
97 197 local meta = minetest.get_meta(ctx.target.under)
98 198 -- first we need to check if the wand has an identifying 'key' yet,
99 199 -- and set one if not.
100 - local wandmode = ctx.base.gem == 'amethyst'
200 + local wandmode = ctx.base.gem == 'sapphire'
101 201 local keycode
102 202 if ctx.meta:contains('sorcery_wand_key') then
103 203 keycode = ctx.meta:get_string('sorcery_wand_key')
104 204 else
105 205 keycode = sorcery.lib.str.rand(32)
106 206 ctx.meta:set_string('sorcery_wand_key', keycode)
207 + -- ctx.meta:mark_as_private('sorcery_wand_key')
107 208 end
108 209 if meta:contains('owner') then
109 210 -- owner is already set -- can we break the enchantment?
110 211 if meta:get_string('sorcery_wand_key') == keycode then
111 212 meta:set_string('owner','')
112 213 meta:set_string('sorcery_wand_key','')
113 214 meta:set_string('sorcery_seal_mode','')
114 215 enchantment_sparkle(ctx,sorcery.lib.color(101,255,142))
115 216 else return false end
116 217 else
117 218 meta:set_string('sorcery_wand_key',keycode)
219 + meta:mark_as_private('sorcery_wand_key')
118 220 meta:set_string('owner',ctx.caster:get_player_name())
119 221 if wandmode then
120 222 meta:set_string('sorcery_seal_mode','wand')
121 223 end
122 224 enchantment_sparkle(ctx,sorcery.lib.color(255,201,27))
123 225 end
124 226 end;
125 227 };
126 228 leyspark = {
127 229 name = 'Leyspark';
128 230 leytype = 'cognic';
231 + color = {255,246,142}; -- bright yellow
129 232 affinity = {'apple','silent'};
130 - uses = 64;
233 + uses = 128;
131 234 desc = 'Reveal the strength and affinities of the local leyline';
132 235 cast = function(ctx)
133 236 local color = ctx.base.gem == 'sapphire';
237 + local duration = (ctx.base.gem == 'amethyst' and 4) or 2;
134 238 local ley = sorcery.ley.estimate(ctx.caster:get_pos())
135 239 local emit = function(color,strength)
136 240 minetest.add_particlespawner {
137 241 amount = 70 * strength;
138 - time = 2 * strength;
242 + time = duration * strength;
139 243 attached = ctx.caster;
140 244 texture = sorcery.lib.image('sorcery_spark.png'):
141 - multiply(color):render();
142 - minpos = { x = 0.5, z = 0.0, y = 1.5};
143 - maxpos = { x = 0.5, z = 0.0, y = 1.5};
245 + multiply(color:brighten(1.3)):render();
246 + minpos = { x = -0.1, z = 0.5, y = 1.2};
247 + maxpos = { x = 0.1, z = 0.3, y = 1.6};
144 248 minvel = { x = -0.5, z = -0.5, y = -0.5};
145 249 maxvel = { x = 0.5, z = 0.5, y = 0.5};
146 250 minacc = { x = 0.0, z = 0.0, y = 0.5};
147 - minacc = { x = 0.0, z = 0.0, y = 0.5};
251 + maxacc = { x = 0.0, z = 0.0, y = 0.5};
148 252 minsize = 0.4, maxsize = 0.8;
149 253 minexptime = 1, maxexptime = 1;
150 254 glow = 14;
151 255 animation = {
152 256 type = 'vertical_frames';
153 257 aspect_w = 16;
154 258 aspect_h = 16;
................................................................................
168 272 end
169 273
170 274 end;
171 275 };
172 276 dowse = {
173 277 name = 'dowsing';
174 278 leytype = 'cognic';
279 + color = {65,116,255};
175 280 affinity = {'acacia','dark','silent'};
176 - uses = 128;
281 + uses = 176;
177 282 desc = 'Send up sparks of radia to indicate nearness or absence of attuned blocks';
178 283 };
179 284 verdant = {
180 285 name = 'verdant';
181 286 color = {16,29,255};
182 287 uses = 48;
183 288 leytype = 'imperic';
................................................................................
188 293 counterpraxic = anchorwand('counterpraxic',23, {'pine','shimmering','silent'});
189 294 entropic = anchorwand('entropic', 8, {'jungle','dark'});
190 295 syncretic = anchorwand('syncretic', 12, {'aspen','verdant','shimmering','blazing'});
191 296 cognic = anchorwand('cognic', 36, {'acacia','verdant','dark'});
192 297 shape = {
193 298 name = 'shaping';
194 299 uses = 24;
300 + color = {255,114,65};
301 + leytype = 'praxic';
195 302 affinity = {'apple','blazing'};
196 303 desc = 'With an enchanter, physically alter the mundane qualities of an object';
197 304 };
198 305 attune = {
199 306 name = 'attunement';
200 307 uses = 38;
308 + color = {255,65,207};
201 309 leytype = 'syncretic';
202 310 affinity = {'pine','verdant','dark'};
203 311 desc = 'Establish a connection between mystic mechanisms, like connecting two sides of a portal or impressing targets onto a dowsing wand in an enchanter';
204 312 };
205 313 meld = {
206 314 name = 'melding';
207 315 uses = 48;
208 316 leytype = 'syncretic';
317 + color = {172,65,255};
209 318 affinity = {'apple','verdant'};
210 319 desc = 'Meld the properties of three balanced items on an enchanter to create a new one with special properties, but destroying the old ones and losing two thirds of the mass in the process. The precise outcome is not always predictable.';
211 320 };
212 321 divide = {
213 322 name = 'division';
214 323 uses = 19;
215 324 leytype = 'syncretic';
325 + color = {255,65,121};
216 326 affinity = {'apple','shimmering'};
217 327 desc = 'Shatter an item on an enchanter, dividing its essence equally into three parts and precipitating it into new items embodying various properties of the destroyed item. The outcome is not always predictable.';
218 328 };
219 329 obliterate = {
220 330 name = 'obliteration';
221 331 uses = 129;
332 + color = {175,6,212};
222 333 affinity = {'aspen','dark'};
223 334 leytype = 'occlutic';
224 335 desc = 'Totally and irreversibly obliterate all items on an enchanter.';
225 336 };
226 337 sacrifice = {
227 338 name = 'sacrifice';
228 339 uses = 58;
340 + color = {212,6,63};
229 341 affinity = {'aspen','blazing'};
230 342 leytype = 'syncretic';
231 343 desc = 'Transform the matter of one to three items on an enchanter into energy and empower the item on the center of the enchanter with it. Useful to recharge wands in areas with weak leylines.';
232 344 };
233 345 transfer = {
234 346 name = 'transfer';
235 347 uses = 65;
348 + color = {6,212,121};
236 349 leytype = 'syncretic';
237 350 affinity = {'aspen','shimmering','silent'};
238 351 desc = 'Transfer ley-current from items on an enchanter into the item in the center, but at a 50% loss if they are of mismatched affinities. One third of maximum current is transferred, and when used on items with little power may destroy them or their enchantments';
239 352 };
353 + transmute = {
354 + name = 'transmutation';
355 + uses = 7;
356 + color = {255,90,18};
357 + leytype = 'imperic';
358 + affinity = {'aspen','shimmering','dark','blazing'};
359 + desc = 'Transmute three ingots into one of a different metal, determined by chance and influenced by configuration of the wand';
360 + };
240 361 disjoin = {
241 362 name = 'disjunction';
242 363 uses = 32;
364 + color = {17,6,212};
243 365 leytype = 'occlutic';
244 366 affinity = {'jungle','silent'};
245 367 desc = 'With an enchanter, disjoin the anchor holding a spell into an object so a new spell can instead be bound in';
368 + };
369 + luminate = {
370 + name = 'lumination';
371 + desc = 'Banish darkness all about you for a few moments';
372 + uses = 40;
373 + color = {244,255,157};
374 + affinity = {'acacia','shimmering','blazing'};
375 + leytype = 'cognic';
376 + cast = function(ctx)
377 + local center = ctx.heading.pos
378 + local maxpower = 20
379 + local power = (ctx.base.gem == 'sapphire' and maxpower) or maxpower/2
380 + local range = (ctx.base.gem == 'emerald' and 10) or 5
381 + local duration = (ctx.base.gem == 'amethyst' and 60) or 30
382 + if ctx.base.gem == 'diamond' then
383 + power = power * (math.random()*2)
384 + range = range * (math.random()*2)
385 + duration = duration * (math.random()*2)
386 + end
387 + local lum = math.ceil((power/maxpower) * minetest.LIGHT_MAX)
388 + print('setting lum',lum)
389 + for i=1,power do
390 + local pos = vector.add(center, {
391 + x = math.random(-range,range);
392 + z = math.random(-range,range);
393 + y = math.random(0,range/2);
394 + })
395 + local delta = vector.subtract(pos,center)
396 + local near = 1 - (delta.x^2 + delta.y^2 + delta.z^2)/(range^2)
397 + if minetest.get_node(pos).name == 'air' then
398 + minetest.set_node(pos,{name='sorcery:air_glimmer_' .. tostring(lum)})
399 + do local lm = minetest.get_meta(pos)
400 + lm:set_float('duration',duration)
401 + lm:set_float('timeleft',duration)
402 + lm:set_float('power',lum * near)
403 + end
404 + end
405 + end
406 + end;
246 407 };
247 408 }