sorcery  Diff

Differences From Artifact [196ad8b009]:

To Artifact [3dd4d17be3]:


    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   }