sorcery  Diff

Differences From Artifact [aa52733fca]:

To Artifact [112e3b2a50]:


     1      1   -- contains functions for determining information about
     2      2   -- the nearest leyline and its currents
     3      3   
     4      4   sorcery.ley = {}
     5      5   
     6         --- leylines are invisible force-currents that rise up from the core of the earth, carrying magical energy upwards. they weaken as they get closer to the surface. each leyline also has between one and three 'affinities', which control how easily they can be wielded to perform particular sorts of magic. for instance, telestratic-affine leylines will charge wands enchanted with telestratic spells more quickly than leylines lacking this affinity.
            6  +-- leylines are invisible force-currents that rise up from the core of the earth, carrying magical energy upwards. they weaken as they get closer to the surface. each leyline also has between one and three 'affinities', which control how easily they can be wielded to perform particular sorts of magic. for instance, praxic-affine leylines will charge wands enchanted with praxic spells more quickly than leylines lacking this affinity.
            7  +-- leylines are one of two mystic energy forms; the other is aetheric energy, which is beamed down from Heaven by the sun during the day and is the power wielded by the gods. mortals can make limited use of aetheric force by collecting it and beaming it from place to place -- see aether.lua (aether is stored and manipulated by use of diamond)
            8  +-- leylines are always available, unlike aetheric force, which can only be collected during the day. but aetheric force is accessible wherever one can see the sky, and the higher up you are, the more you can collect, whereas leylines vary randomly in strength and affinity by position.
     7      9   
     8     10   sorcery.ley.estimate = function(pos)
     9     11   	local affs = {
    10     12   		'praxic'; 'counterpraxic'; 'cognic';
    11     13   		'mandatic'; 'occlutic'; 'imperic';
    12     14   		'syncretic'; 'entropic';
    13     15   	};
................................................................................
    42     44   	privs = { server = true };
    43     45   	func = function(caller,params)
    44     46   		local pos = minetest.get_player_by_name(caller):get_pos()
    45     47   		local ley = sorcery.ley.estimate(pos)
    46     48   		minetest.chat_send_player(caller, 'Leyline force ' .. tostring(ley.force) .. ' with affinities ' .. table.concat(ley.aff, ','))
    47     49   	end;
    48     50   })
           51  +
           52  +-- leyline energy can be transmitted via a conduit from a leysink. however, it cannot be stored like aetheric energy can be; leyline energy must be drawn when needed unless it is bound up in an enchantment (which simply delays its expression). leysinks provide a constant source of ley-force.
           53  +-- there are two nodes for transmitting leyline energy, wires and conduits. wires transmit a limited amount of energy, but are cheap and small. conduits transmit much more, but are expensive and take up full blocks. both are composed of electrum, the carrier, and copper, which prevents the ley-force from leaking out as dangerous radiance.
           54  +
           55  +minetest.register_node('sorcery:conduit', {
           56  +	description = 'Conduit';
           57  +	tiles = {
           58  +		'sorcery_conduit_copper_top.png';
           59  +		'sorcery_conduit_copper_top.png';
           60  +		'sorcery_conduit_copper_side.png';
           61  +	};
           62  +	groups = {
           63  +		sorcery_ley_device = 1;
           64  +		cracky = 3;
           65  +	};
           66  +	_sorcery = {
           67  +		ley = { mode = 'signal'; power = 10 };
           68  +	};
           69  +})
           70  +minetest.register_craft {
           71  +	output = 'sorcery:conduit 4';
           72  +	recipe = {
           73  +		{'default:copper_ingot', 'default:copper_ingot',  'default:copper_ingot'};
           74  +		{'default:copper_ingot', 'sorcery:electrumblock', 'default:copper_ingot'};
           75  +		{'default:copper_ingot', 'default:copper_ingot',  'default:copper_ingot'};
           76  +	};
           77  +};
           78  +minetest.register_craft {
           79  +	output = 'sorcery:wire 4';
           80  +	recipe = {
           81  +		{'', 'basic_materials:copper_wire',''};
           82  +		{'', 'sorcery:fragment_electrum',  ''};
           83  +		{'', 'basic_materials:copper_wire',''};
           84  +	}
           85  +};
           86  +
           87  +sorcery.ley.field_to_current = function(strength,time)
           88  +	local ley_factor = 0.25
           89  +	-- a ley harvester will produce this much current with
           90  +	-- access to a full-strength leyline
           91  +	
           92  +	return strength * ley_factor * time;
           93  +end
           94  +
           95  +do -- register condenser
           96  +	local gem = sorcery.lib.image('default_diamond_block.png')
           97  +	local amethyst = gem:multiply(sorcery.lib.color(sorcery.data.gems.amethyst.tone))
           98  +	local emerald = gem:multiply(sorcery.lib.color(sorcery.data.gems.emerald.tone))
           99  +	local box = {
          100  +		type = 'fixed';
          101  +		fixed = {
          102  +			-0.5, -0.5, -0.5;
          103  +			 0.5,  1.2,  0.5;
          104  +		};
          105  +	};
          106  +	minetest.register_node('sorcery:condenser', {
          107  +		description = 'Condenser';
          108  +		drawtype = 'mesh';
          109  +		mesh = 'sorcery-condenser.obj';
          110  +		selection_box = box;
          111  +		collision_box = box;
          112  +		tiles = {
          113  +			amethyst:render();
          114  +			'sorcery_condenser.png';
          115  +			'default_tin_block.png';
          116  +			'default_stone.png';
          117  +			'default_copper_block.png';
          118  +			emerald:render();
          119  +		};
          120  +		groups = {
          121  +			cracky = 2;
          122  +			sorcery_ley_device = 1;
          123  +		};
          124  +		on_construct = function(pos)
          125  +			local meta = minetest.get_meta(pos)
          126  +			meta:set_string('infotext','Condenser')
          127  +		end;
          128  +		_sorcery = {
          129  +			ley = { mode = 'produce' };
          130  +			on_leycalc = function(pos,time)
          131  +				local l = sorcery.ley.estimate(pos)
          132  +				return {
          133  +					power = sorcery.ley.field_to_current(l.force, time);
          134  +					affinity = l.aff;
          135  +				}
          136  +			end;
          137  +		};
          138  +	})
          139  +end
          140  +
          141  +minetest.register_craft {
          142  +	output = 'sorcery:condenser';
          143  +	recipe = {
          144  +		{'sorcery:accumulator'};
          145  +		{'sorcery:conduit'};
          146  +	};
          147  +}
          148  +sorcery.ley.mapnet = function(startpos,power)
          149  +	-- this function returns a list of all the nodes accessible from
          150  +	-- a ley network and their associated positions
          151  +	local net = {}
          152  +	power = power or 0
          153  +	
          154  +	local devices = {
          155  +		consume = {};
          156  +		produce = {};
          157  +		signal = {};
          158  +	}
          159  +	local numfound = 0
          160  +	local maxconduct = 0
          161  +	local minconduct
          162  +	local foundp = function(p)
          163  +		for k in pairs(net) do
          164  +			if vector.equals(p,k) then return true end
          165  +		end
          166  +		return false
          167  +	end
          168  +	-- we're implementing this with a recursive function to start with
          169  +	-- but this could rapidly lead to stack overflows so we should
          170  +	-- replace it with a linear one at some point
          171  +	local function find(positions)
          172  +		local searchnext = {}
          173  +		for _,pos in pairs(positions) do
          174  +			for _,p in pairs {
          175  +				{x =  0, z =  0, y =  0};
          176  +				{x = -1, z =  0, y =  0};
          177  +				{x =  1, z =  0, y =  0};
          178  +				{x =  0, z = -1, y =  0};
          179  +				{x =  0, z =  1, y =  0};
          180  +				{x =  0, z =  0, y = -1};
          181  +				{x =  0, z =  0, y =  1};
          182  +			} do local sum = vector.add(pos,p)
          183  +				if not foundp(sum) then
          184  +					local nodename = minetest.get_node(sum).name
          185  +					if minetest.get_item_group(nodename,'sorcery_ley_device') ~= 0
          186  +					   or sorcery.data.compat.ley[nodename] then
          187  +						local d = sorcery.ley.sample(pos,1,nodename)
          188  +						assert(d.mode == 'signal'
          189  +						    or d.mode == 'consume'
          190  +						    or d.mode == 'produce')
          191  +						devices[d.mode][#(devices[d.mode]) + 1] = {
          192  +							id = nodename; pos = sum;
          193  +						}
          194  +						if d.mode == 'signal' then
          195  +							if d.power > power then
          196  +								if minconduct then
          197  +									if d.power < minconduct then
          198  +										minconduct = d.power
          199  +									end
          200  +								else minconduct = d.power end
          201  +								if d.power > maxconduct then
          202  +									maxconduct = d.power
          203  +								end
          204  +							end
          205  +						end
          206  +						numfound = numfound + 1;
          207  +						net[sum] = nodename;
          208  +						searchnext[#searchnext + 1] = sum;
          209  +					end
          210  +				end
          211  +			end
          212  +		end
          213  +		if #searchnext > 0 then find(searchnext) end
          214  +	end
          215  +
          216  +	find{startpos}
          217  +
          218  +	if numfound > 0 then
          219  +		return {
          220  +			count = numfound;
          221  +			map = net;
          222  +			devices = devices;
          223  +			conduct = {
          224  +				min = minconduct;
          225  +				max = maxconduct;
          226  +			};
          227  +		}
          228  +	else return nil end
          229  +end
          230  +
          231  +do local afftbl = {
          232  +		[1] = 'praxic';   [2] = 'counterpraxic';
          233  +		[3] = 'cognic';   [4] = 'syncretic';
          234  +		[5] = 'mandatic'; [6] = 'occlutic';
          235  +		[7] = 'imperic';  [8] = 'entropic';
          236  +	}
          237  +	local modetbl = {
          238  +		[0] = 'none';
          239  +		[1] = 'consume';
          240  +		[2] = 'produce';
          241  +		[3] = 'signal';
          242  +	}
          243  +	for i=1,#afftbl  do afftbl [afftbl [i]] = i end
          244  +	for i=1,#modetbl do modetbl[modetbl[i]] = i end
          245  +	local m = sorcery.lib.marshal
          246  +	local enc, dec = m.transcoder {
          247  +		mode = m.t.u8;
          248  +		power = m.t.u32; -- power generated/consumed * 10,000
          249  +		affinity = m.g.array(m.t.u8); -- indexes into afftbl
          250  +	}
          251  +	sorcery.ley.encode = function(l)
          252  +		local idxs = {}
          253  +		for _,k in pairs(l.affinity) do
          254  +			idxs[#idxs+1] = afftbl[k]
          255  +		end
          256  +		return meta_armor(enc {
          257  +			mode = modetbl[l.mode];
          258  +			power = l.power * 10000;
          259  +			affinity = idxs;
          260  +		}, true)
          261  +	end
          262  +	sorcery.ley.decode = function(str)
          263  +		local obj = dec(meta_dearmor(str,true))
          264  +		local affs = {}
          265  +		for _,k in pairs(obj.affinity) do
          266  +			affs[#affs+1] = afftbl[k]
          267  +		end
          268  +		return {
          269  +			mode = modetbl[obj.mode];
          270  +			power = obj.power / 10000.0;
          271  +			affinity = affs;
          272  +		}
          273  +	end
          274  +end
          275  +sorcery.ley.setnode = function(pos,l)
          276  +	local meta = minetest.get_node(pos)
          277  +	meta:set_string('sorcery:ley',sorcery.ley.encode(l))
          278  +end
          279  +
          280  +sorcery.ley.sample = function(pos,timespan,name)
          281  +	-- returns how much ley-force can be transmitted by a
          282  +	-- device over timespan
          283  +	name = name or minetest.get_node(pos).name
          284  +	local props = minetest.registered_nodes[name]._sorcery
          285  +	local callback = props and props.on_leycalc or nil
          286  +	local p,a,m
          287  +	if callback then
          288  +		local gen = callback(pos,timespan)
          289  +		p = gen.power
          290  +		a = gen.affinity
          291  +		m = gen.mode
          292  +	end
          293  +
          294  +	if not (p and a and m) then
          295  +		local nm = minetest.get_meta(pos)
          296  +		if nm:contains('sorcery:ley') then
          297  +			local l = sorcery.ley.decode(nm:get_string('sorcery:ley'))
          298  +			p = p or sorcery.ley.field_to_current(l.power,timespan)
          299  +			a = a or l.affinity
          300  +			m = m or l.mode
          301  +		end
          302  +	end
          303  +
          304  +	if (not (p and a and m)) and props and props.ley then
          305  +		p = p or sorcery.ley.field_to_current(props.ley.power,timespan)
          306  +		a = a or props.ley.affinity
          307  +		m = m or props.ley.mode
          308  +	end
          309  +
          310  +	if (not (p and a and m)) then
          311  +		local compat = sorcery.data.compat.ley[name] 
          312  +		if compat then
          313  +			p = p or sorcery.ley.field_to_current(compat.power,timespan)
          314  +			a = a or compat.affinity
          315  +			m = m or compat.mode
          316  +		end
          317  +	end
          318  +
          319  +	return {
          320  +		power = p or 0;
          321  +		mode = m or 'none';
          322  +		affinity = a or {};
          323  +	}
          324  +end
          325  +
          326  +sorcery.ley.netcaps = function(pos,timespan,exclude)
          327  +	local net = sorcery.ley.mapnet(pos)
          328  +	local maxpower = 0
          329  +	local freepower = 0
          330  +	local affs,usedaffs = {},{}
          331  +	for _,n in pairs(net.devices.produce) do
          332  +		if not exclude or not vector.equals(n.pos,exclude) then
          333  +			local ln = sorcery.ley.sample(n.pos,timespan,n.id)
          334  +			maxpower = maxpower + ln.power
          335  +			for _,a in pairs(ln.affinity) do
          336  +				affs[a] = (affs[a] or 0) + 1
          337  +			end
          338  +		end
          339  +	end
          340  +	freepower = maxpower;
          341  +	for _,n in pairs(net.devices.consume) do
          342  +		if not exclude or not vector.equals(n.pos,exclude) then
          343  +			local ln = sorcery.ley.sample(n.pos,timespan,n.id)
          344  +			freepower = freepower - ln.power
          345  +			for _,a in pairs(ln.affinity) do
          346  +				usedaffs[a] = (usedaffs[a] or 0) + 1
          347  +			end
          348  +		end
          349  +	end
          350  +	
          351  +	return {
          352  +		net = net;
          353  +		freepower = freepower;
          354  +		maxpower = maxpower;
          355  +		affinity = affs;
          356  +		affinity_balance = usedaffs;
          357  +	}
          358  +end