sorcery  Diff

Differences From Artifact [a02ac21c24]:

To Artifact [04ca2e071b]:


     1      1   -- liquid.lua
     2      2   -- the liquid registry is used to keep track of abstract liquids,
     3      3   -- their properties, and their representation in-game.
     4      4   
     5      5   sorcery.registry.mk('liquid', false)
     6         -sorcery.liquid = {}
            6  +sorcery.liquid = {
            7  +	constants = {
            8  +		drams_per_glass = 64;
            9  +		glasses_per_bottle = 3;
           10  +		bottles_per_bucket = 3;
           11  +		bottles_per_trough = 6;
           12  +	}
           13  +}
           14  +local constants = sorcery.liquid.constants
           15  +
           16  +local L = sorcery.lib
           17  +local log = sorcery.logger('liquid')
           18  +
           19  +sorcery.liquid.fill_from_basin = function(ctr, liquid, basin)
           20  +	local liq = sorcery.register.liquid.db[liquid]
           21  +	local filled
           22  +	if type(ctr) == 'string'
           23  +		then filled = liq.containers[ctr] ctr=ItemStack(ctr)
           24  +		else filled = liq.containers[ctr:get_name()]
           25  +	end
           26  +	if type(filled) == 'string' then
           27  +		local fs = sorcery.itemclass.get(filled, 'container')
           28  +		if not fs then log.err(filled,'is named as filled container but does not have the required itemclass definition') return end
           29  +
           30  +		local item_name = filled
           31  +		filled = {
           32  +			min = fs.charge, max = fs.charge, res = 1;
           33  +			make = function(amt,ct) return ItemStack{
           34  +				name = item_name, count = ct
           35  +			} end
           36  +		}
           37  +	end
           38  +	if not filled then return nil end
           39  +
           40  +	local num_ctrs = ctr:get_count()
           41  +	local res = filled.res or 1
           42  +	local qty = math.min(
           43  +		math.max((filled.min or 1)*num_ctrs, basin),
           44  +		(filled.max or 1)*num_ctrs)
           45  +	
           46  +	if basin >= qty then
           47  +		return filled.make(qty / num_ctrs, num_ctrs), basin - qty
           48  +	end
           49  +end
           50  +
           51  +sorcery.liquid.mktrough = function(liq)
           52  +	-- troughs are used for collecting liquid from the environment,
           53  +	-- like rainwater and tree sap. they hold twice as much as a bucket
           54  +	local Q = constants.glasses_per_bottle
           55  +	local trough_mkid = function(l,i)
           56  +		if type(l) == 'string' then l = sorcery.register.liquid.db[l] end
           57  +		if not l or not i then return 'sorcery:trough' end
           58  +		return string.format('%s:trough_%s_%u', l.mod,l.sid,i)
           59  +	end
           60  +	local lid = function(l) return trough_mkid(liq, l) end
           61  +
           62  +	local M = constants.bottles_per_trough
           63  +	local mkbox = function(lvl)
           64  +		local pxl = function(tbl) -- for mapping to txcoords
           65  +			return L.tbl.map(tbl, function(x)
           66  +				return (1/16 * x) - 0.5
           67  +			end)
           68  +		end
           69  +		local h = 12
           70  +		local geom = {
           71  +			pxl {2,0,2; 14, 2, 14};
           72  +			pxl {2,2,2; 4,h,14};
           73  +			pxl {2,2,2; 14,h,4};
           74  +
           75  +			pxl {12,2,2; 14,h,14};
           76  +			pxl {2,2,12; 14,h,14};
           77  +
           78  +		}
           79  +		if lvl > 0 then
           80  +			local fac = lvl / M
           81  +			return L.tbl.append({
           82  +				pxl {4,2,4; 12, 2 + ((h-3)*fac), 12};
           83  +			}, geom)
           84  +		else return geom end
           85  +	end
           86  +	local f = liq and 1 or 0
           87  +	for i = 1*f,M*f do
           88  +		local top = L.image('sorcery_trough_top_overlay.png')
           89  +		if liq then top = top:blit( 
           90  +			L.image('sorcery_node_liquid.png'):multiply(L.color(liq.color))
           91  +		) else top=top:blit(
           92  +			L.image('sorcery_trough_bottom.png')
           93  +		) end
           94  +		local trough_title = liq and string.format('%s Trough', L.str.capitalize(liq.name))
           95  +		local trough_content = liq and string.format('%s of %s', liq.measure(i * Q), liq.name)
           96  +		local function trough_caption(pos,i) 
           97  +			minetest.get_meta(pos):set_string('infotext', i > 0 and string.format(
           98  +				'%s\n(%s)', trough_title, trough_content
           99  +			) or 'Empty Trough')
          100  +		end
          101  +		sorcery.register.residue.link(lid(i),lid(0))
          102  +		minetest.register_node(':'..lid(i), {
          103  +			description = liq and L.ui.tooltip {
          104  +				title = trough_title;
          105  +				color = L.color(liq.color);
          106  +				desc = trough_content;
          107  +			} or 'Trough';
          108  +			short_description = liq and string.format('%s Trough', L.str.capitalize(liq.name)) or 'Trough';
          109  +			drawtype = 'nodebox';
          110  +			paramtype = 'light';
          111  +			groups = {
          112  +				dig_immediate = 3; not_in_creative_inventory = liq and 1;
          113  +				attached_node = 1;
          114  +				sorcery_trough = 1; sorcery_container = 2; metal = 1;
          115  +				sorcery_collect_rainwater = (liq == nil or (liq.collect_rainwater and i ~= M)) and 1 or nil;
          116  +			};
          117  +			on_construct = function(pos)
          118  +				trough_caption(pos,i)
          119  +			end;
          120  +			on_rightclick = i > 0 and function(pos, node, who, stack)
          121  +				if not stack or stack:is_empty() then return end
          122  +				if liq then
          123  +					local filled, amtleft = sorcery.liquid.fill_from_basin(stack, liq.id, i * Q)
          124  +					if filled then
          125  +						sorcery.liquid.sound_dip(i - amtleft, i, pos)
          126  +						minetest.swap_node(pos, {name = lid(amtleft / Q)})
          127  +						trough_caption(pos,amtleft/Q)
          128  +						return filled
          129  +					end
          130  +				end
          131  +			end;
          132  +			node_box = { type = 'fixed', fixed = mkbox(i) };
          133  +			tiles = {
          134  +				top:render();
          135  +				'sorcery_trough_side.png';
          136  +				'sorcery_trough_bottom.png';
          137  +			};
          138  +			_sorcery = {
          139  +				container = {
          140  +					type = 'bucket';
          141  +					hold = 'liquid';
          142  +					has = liq and liq.id;
          143  +					charge = liq and Q * i;
          144  +					empty = 'sorcery:trough';
          145  +					max = constants.bottles_per_trough * Q;
          146  +					set_node_vol = liq and function(pos, vol)
          147  +						vol = math.min(M, math.max(0, math.floor(vol / Q)))
          148  +						minetest.swap_node(pos, {name = lid(vol)})
          149  +						trough_caption(pos, vol)
          150  +					end;
          151  +					set_node_liq = function(pos, liq, vol)
          152  +						log.act('adding', vol, 'to trough at', liq)
          153  +						vol = vol or Q * i
          154  +						local idx = math.min(M, math.floor(vol/Q))
          155  +						minetest.swap_node(pos, {name = trough_mkid(liq, idx)})
          156  +						trough_caption(pos, idx)
          157  +					end
          158  +				}
          159  +			};
          160  +		})
          161  +	end
          162  +end
          163  +sorcery.liquid.mktrough()
          164  +
          165  +sorcery.liquid.measure_default = function(amt)
          166  +	return string.format('%s drams', amt*constants.drams_per_glass)
          167  +end
          168  +sorcery.liquid.register = function(liq)
          169  +	local fmt = string.format
          170  +	local Q = constants.glasses_per_bottle
          171  +	liq.sid = liq.sid or liq.id:gsub('^[^:]+:','')
          172  +	liq.mod = liq.mod or liq.id:gsub('^([^:]+):.*','%1')
          173  +	if not liq.measure then
          174  +		liq.measure = sorcery.liquid.measure_default
          175  +	end
          176  +	if liq.autogen then
          177  +		local glass = fmt('%s:liquid_%s_glass', liq.mod, liq.sid);
          178  +		local bottle = fmt('%s:liquid_%s_bottle', liq.mod, liq.sid);
          179  +		liq.containers = liq.containers or {}
          180  +		-- liq.containers['vessels:drinking_glass'] = glass;
          181  +		liq.containers['vessels:glass_bottle'] = bottle;
          182  +
          183  +		local img_bottle = liq.img_bottle or L.image('vessels_glass_bottle.png'):blit(
          184  +				L.image(fmt('sorcery_liquid_%s.png', liq.imgvariant or 'dull'))
          185  +					:multiply(L.color(liq.color))):render()
          186  +
          187  +		-- local img_glass = L.image('vessels_drinking_glass.png'):blit(
          188  +		-- 		L.image(fmt('sorcery_liquid_glass_%s.png', liq.imgvariant or 'dull'))
          189  +		-- 			:multiply(L.color(liq.color)))
          190  +
          191  +		minetest.register_node(':'..bottle, {
          192  +			description = liq.desc_bottle or fmt('%s Bottle', L.str.capitalize(liq.name));
          193  +			inventory_image = img_bottle;
          194  +			drawtype = 'plantlike', tiles = {img_bottle};
          195  +			is_ground_content = false, walkable = false;
          196  +			sunlight_propagates = true, paramtype = 'light';
          197  +			light_source = liq.glow or 0;
          198  +			selection_box = { type = 'fixed', fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25} };
          199  +			sounds = default.node_sound_glass_defaults();
          200  +			groups = L.tbl.merge({dig_immediate = 3; attached_node = 1; vessel = 1}, liq.bottle_groups or {});
          201  +			_sorcery = {
          202  +				container = {
          203  +					type = 'vessel', hold = 'liquid';
          204  +					has = liq.id;
          205  +					empty = 'vessels:glass_bottle';
          206  +					charge = Q;
          207  +				}
          208  +			};
          209  +		})
          210  +	end
          211  +
          212  +	sorcery.register.liquid.link(liq.id, liq)
          213  +
          214  +	if liq.usetrough then
          215  +		sorcery.liquid.mktrough(liq)
          216  +		liq.containers = liq.containers or {}
          217  +		liq.containers['sorcery:trough'] = {
          218  +			max = constants.bottles_per_trough * Q, res = Q;
          219  +			make = function(amt,ct)
          220  +				return ItemStack{
          221  +					name = string.format('%s:trough_%s_%u', liq.mod, liq.sid, math.min(amt/Q, constants.bottles_per_trough));
          222  +					count = ct;
          223  +				}
          224  +			end;
          225  +		}
          226  +	end
          227  +end;
          228  +
          229  +sorcery.liquid.sound_pour = function(amt_input, amt_basin, pos)
          230  +	log.act('playing sound at',pos)
          231  +	minetest.sound_play('default_water_footstep', {
          232  +		gain = math.min(0.5 + amt_input / 9.0,3.5);
          233  +		-- pitch = 1.0;
          234  +		pos = pos;
          235  +	}, true)
          236  +end;
          237  +
          238  +sorcery.liquid.sound_dip = function(amt_output, amt_basin, pos)
          239  +	sorcery.liquid.sound_pour(amt_output, amt_basin, pos)
          240  +end;
     7    241   
     8    242   -- pre-register basic liquids used in Sorcery and common ones sorcery depends on
     9    243   
    10         -sorcery.register.liquid.link('default:water', {
          244  +sorcery.liquid.register{
          245  +	id = 'default:water';
    11    246   	name = 'water';
    12    247   	kind = 'default:drink';
    13    248   	color = {10,85,255};
    14    249   	proto = nil;
    15    250   	src = 'default:water_source';
          251  +	usetrough = true;
          252  +	collect_rainwater = true;
    16    253   	containers = {
    17    254   		['vessels:glass_bottle'] = 'sorcery:potion_water';
    18    255   		['bucket:bucket_empty'] = 'bucket:bucket_water';
    19    256   	};
    20         -})
          257  +}
    21    258   
    22         -sorcery.register.liquid.link('farming:ethanol', {
          259  +sorcery.liquid.register {
          260  +	id = 'farming:ethanol';
    23    261   	name = 'ethanol';
    24    262   	kind = 'default:fuel';
    25    263   	color = {175,185,130};
    26    264   	proto = nil;
    27    265   	measure = function(u) return string.format('%s pints', u * 5) end;
    28    266   	containers = {
    29    267   		['vessels:glass_bottle'] = 'farming:ethanol_bottle';
    30    268   	};
    31         -})
          269  +}
    32    270   
    33         -sorcery.register.liquid.link('sorcery:blood', {
          271  +sorcery.liquid.register {
          272  +	id = 'sorcery:blood';
    34    273   	name = 'blood';
    35    274   	kind = 'sorcery:reagent';
    36    275   	color = {255,10,30};
    37    276   	proto = nil;
          277  +	usetrough = true;
    38    278   	measure = function(u) return string.format('%s cc', u * 236.5) end;
    39    279   	containers = {
    40    280   		['vessels:glass_bottle'] = 'sorcery:blood';
    41    281   	};
    42         -})
          282  +}
          283  +
          284  +minetest.register_abm {
          285  +	label = 'Rainfall';
          286  +	nodenames = {'group:sorcery_collect_rainwater'};
          287  +	interval =  230;
          288  +	chance = 40;
          289  +	min_y = -400;
          290  +	catch_up = true;
          291  +	action = function(pos, node)
          292  +		-- TODO vary by season and biome?
          293  +		if minetest.get_natural_light(vector.offset(pos,0,1,0), 0.5) >= 15 then
          294  +			if node.name == 'sorcery:trough' then
          295  +				node.name = 'default:trough_water_1'
          296  +			else
          297  +				local lvl = minetest.registered_nodes[node.name]._sorcery.container.charge / constants.glasses_per_bottle
          298  +				node.name = 'default:trough_water_' .. tostring(lvl+1)
          299  +			end
          300  +			minetest.set_node(pos, node)
          301  +		end
          302  +	end;
          303  +}