starlit  Check-in [e829ca194a]

Overview
Comment:plant growth, edibles, fixes
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e829ca194a0a1cb867d8e29e733a69d13ce75e2697dc907bb03e0ea83c1ab2b0
User & Date: lexi on 2024-05-02 00:22:47
Other Links: manifest | tags
Context
2024-05-02
20:27
check in missing mod, add forest biome, racial powers, overlays (untested) check-in: 6deb9bedbc user: lexi tags: trunk
00:22
plant growth, edibles, fixes check-in: e829ca194a user: lexi tags: trunk
2024-05-01
16:25
fixes, sounds; add license info check-in: 0e67c606c9 user: lexi tags: trunk
Changes

Modified mods/starlit-electronics/sw.lua from [e776ef774d] to [eb892cff12].

   129    129   	kind = 'suitPower', powerKind = 'active';
   130    130   	desc = 'An open-source program used in its various forks and iterations all across human-inhabited space and beyond. Rumored to contain fragments of code stolen from the nanoware of the Greater Races by an elusive infoterrorist.';
   131    131   	size = 500e3;
   132    132   	cost = {
   133    133   		cycles = 100e6;
   134    134   		ram = 500e6;
   135    135   	};
   136         -	run = shredder{range=2, powerDraw=200};
          136  +	run = shredder{range=3, powerDraw=200};
   137    137   })
   138    138   
   139    139   starlit.item.sw.link('starlit_electronics:compile_commune', {
   140    140   	name = 'Compile Matter';
   141    141   	kind = 'suitPower', powerKind = 'direct';
   142    142   	desc = "A basic suit matter compiler program, rather slow but ruthlessly optimized for power- and memory-efficiency by some of the Commune's most fanatic coders.";
   143    143   	size = 700e3;

Modified mods/starlit/init.lua from [c40c4aaae1] to [e52f2bba90].

    46     46   	liveUI = {
    47     47   		-- cached subset of activeUI containing those UIs needing live updates
    48     48   	};
    49     49   
    50     50   	interface = lib.registry.mk 'starlit:interface';
    51     51   	item = {
    52     52   		food = lib.registry.mk 'starlit:food';
           53  +		seed = lib.registry.mk 'starlit:seed';
    53     54   	};
    54     55   
    55     56   	region = {
    56     57   		radiator = {
    57     58   			store = AreaStore();
    58         -			emitters = {}
           59  +			emitters = {};
    59     60   		};
    60     61   	};
    61     62   
    62     63   	-- standardized effects
    63     64   	fx = {};
    64     65   
    65     66   	type = {};
................................................................................
   386    387   })
   387    388   minetest.register_item("starlit:_hand_dig", {
   388    389   	type = "none",
   389    390   	wield_image = "wieldhand.png",
   390    391   	wield_scale = {x=1,y=1,z=2.5},
   391    392   	tool_capabilities = {
   392    393   		groupcaps = {
   393         -			plant = {maxlevel=1, times = {.50,.5,.5}};
   394         -			dirt = {maxlevel=1, times = {2.5,1,1}};
          394  +			plant = {maxlevel=1, times = {.50}};
          395  +			dirt = {maxlevel=1, times = {2.5}};
          396  +
          397  +			log = {maxlevel=1, times = {1}};
   395    398   		};
   396    399   	}
   397    400   })
   398    401   
   399    402   minetest.register_on_player_inventory_action(function(luser, act, inv, p)
   400    403   	local name = luser:get_player_name()
   401    404   	local user = starlit.activeUsers[name]
................................................................................
   418    421   		-- cranked by similarly
   419    422   	end
   420    423   	return delta
   421    424   end, true)
   422    425   
   423    426   function minetest.handle_node_drops(pos, drops, digger)
   424    427   	local function jitter(pos)
   425         -		local function r(x) return x+math.random(-0.2, 0.2) end
          428  +		local function r(x) return x+math.random(-0.01, 0.01) end
   426    429   		return vector.new(
   427    430   			r(pos.x),
   428    431   			r(pos.y),
   429    432   			r(pos.z)
   430    433   		)
   431    434   	end
   432    435   	for i, it in ipairs(drops) do
   433         -		minetest.add_item(jitter(pos), it)
          436  +		local it = minetest.add_item(jitter(pos), it)
          437  +		local dp = vector.new(0,0,0)
          438  +		if digger then dp = digger:get_pos() end
          439  +		local delta = dp - it:get_pos()
          440  +		it:add_velocity(vector.new(delta.x,0,delta.z));
   434    441   	end
   435    442   end
   436    443   
   437    444   
   438    445   -- TODO timer iterates live UI
   439    446   

Modified mods/starlit/interfaces.lua from [5877ccf3fb] to [497adf50ee].

   345    345   				local tb = {
   346    346   					kind = 'vert', mode = 'sw';
   347    347   					padding = 0.5, 
   348    348   					{kind = 'hztl', padding = 0.25;
   349    349   						{kind = 'label', text = 'Name', w = 2, h = barh};
   350    350   						{kind = 'label', text = user.persona.name, w = 4, h = barh}};
   351    351   				}
   352         -				local statBars = {'hunger', 'thirst', 'fatigue', 'morale', 'irradiation', 'illness'}
          352  +				local statBars = {'nutrition', 'hydration', 'fatigue', 'morale', 'irradiation', 'illness'}
   353    353   				for idx, id in ipairs(statBars) do
   354    354   					local s = starlit.world.stats[id]
   355    355   					local amt, sv = user:effectiveStat(id)
   356    356   					local min, max = starlit.world.species.statRange(user.persona.species, user.persona.speciesVariant, id)
   357    357   					local st = string.format('%s / %s', s.desc(amt, true), s.desc(max))
   358    358   					table.insert(tb, {kind = 'hztl', padding = 0.25;
   359    359   						{kind = 'label', w=2, h=barh, text = s.name};

Modified mods/starlit/species.lua from [642ae8f101] to [b30e9eb59a].

    45     45   
    46     46   					return {lining, plate, skin, skin, eye, hair}
    47     47   				end;
    48     48   				stats = {
    49     49   					psiRegen = 1.3;
    50     50   					psiPower = 1.2;
    51     51   					psi = 1.2;
    52         -					hunger = .8; -- women have smaller stomachs
    53         -					thirst = .8;
           52  +					nutrition = .8; -- women have smaller stomachs
           53  +					hydration = .8;
    54     54   					staminaRegen = 1.0;
    55     55   					morale = 0.8; -- you are not She-Bear Grylls
    56     56   				};
    57     57   				traits = {
    58     58   					health = 400;
    59     59   					lungCapacity = .6;
    60     60   					irradiation = 0.8; -- you are smaller, so it takes less rads to kill ya
    61     61   					sturdiness = 0; -- women are more fragile and thus susceptible to blunt force trauma
    62         -					metabolism = 1800e3 / 24 / 60 / 60; --kCal/s
           62  +					metabolism = .150; -- kCal/s
    63     63   					painTolerance = 0.4;
    64         -					dehydration = 3; -- mL/s
           64  +					dehydration = 10e-4; -- L/s
    65     65   				};
    66     66   			};
    67     67   			male = {
    68     68   				name = 'Human Male';
    69     69   				eyeHeight = 1.6;
    70     70   				stats = {
    71     71   					psiRegen = 1.0;
    72     72   					psiPower = 1.0;
    73     73   					psi = 1.0;
    74         -					hunger = 1.0;
    75         -					thirst = 1.0;
           74  +					nutrition = 1.0;
           75  +					hydration = 1.0;
    76     76   					staminaRegen = .7; -- men are strong but have inferior endurance
    77     77   				};
    78     78   				traits = {
    79     79   					health = 500;
    80     80   					painTolerance = 1.0;
    81     81   					lungCapacity = 1.0;
    82     82   					sturdiness = 0.3;
    83         -					metabolism = 2200e3 / 24 / 60 / 60; --Cal/s
    84         -					dehydration = 5; -- mL/s
           83  +					metabolism = .150; -- kCal/s
           84  +					dehydration = 15e-4; -- L/s
    85     85   				};
    86     86   			};
    87     87   		};
    88     88   		traits = {};
    89     89   	};
    90     90   }
    91     91   
................................................................................
   165    165   		if base == true then
   166    166   			base = max
   167    167   		elseif base == false then
   168    168   			base = min
   169    169   		end
   170    170   
   171    171   	end
   172         -	return min, max, base
          172  +	return min, max, base or 0
   173    173   end
   174    174   
   175    175   -- set the necessary properties and create a persona for a newspawned entity
   176    176   function starlit.world.species.birth(pSpecies, pVariant, entity, circumstances)
   177    177   	circumstances = circumstances or {}
   178    178   	local sp,var = spLookup(pSpecies, pVariant)
   179    179   
................................................................................
   184    184   	end
   185    185   	local ps = starlit.world.species.mkPersonaFor(pSpecies,pVariant)
   186    186   	local startingHP = pct('health', 1.0)
   187    187   	if circumstances.injured    then startingHP = pct('health', circumstances.injured) end
   188    188   	if circumstances.psiCharged then ps.statDeltas.psi = pct('psi', circumstances.psiCharged) end
   189    189   	for k,v in pairs(starlit.world.stats) do ps.statDeltas[k] = 0 end
   190    190   	ps.statDeltas.warmth = 20 -- don't instantly start dying of frostbite
          191  +	ps.statDeltas.nutrition = 2000 -- shoulda packed more MRE :c
          192  +	ps.statDeltas.hydration = 3 -- stay hydrated uwu
   191    193   
   192    194   	entity:set_properties{hp_max = var.traits.health or sp.traits.health}
   193    195   	entity:set_hp(startingHP, 'initial hp')
   194    196   	return ps
   195    197   end
   196    198   
   197    199   function starlit.world.species.paramsFromTable(pSpecies, tbl)

Modified mods/starlit/stats.lua from [523263fc9a] to [c485cf1b8a].

     1      1   local lib = starlit.mod.lib
            2  +local T,G = lib.marshal.t, lib.marshal.g
     2      3   
     3      4   local function U(unit, prec, fixed)
     4      5   	local trunc = 2
     5      6   	if fixed then
     6      7   		return function(amt, excludeUnit)
     7      8   			local ta = lib.math.trim(amt/prec, trunc)
     8      9   			if excludeUnit then return tostring(ta) end
     9         -			return string.format("%s %s", ta, unit)
           10  +			return string.format("%s%s", ta, unit)
    10     11   		end
    11     12   	else
    12     13   		return function(amt, excludeUnit)
    13     14   			local ta = lib.math.trim(amt/prec, trunc)
    14     15   			if excludeUnit then return tostring(ta) end
    15     16   			return lib.math.si(unit, amt/prec, nil, nil, trunc)
    16     17   		end
................................................................................
    20     21   local function C(h, s, l)
    21     22   	return lib.color {hue = h, sat = s or 1, lum = l or .7}
    22     23   end
    23     24   starlit.world.stats = {
    24     25   	psi        = {min = 0, max = 500, base = 0, desc = U('ψ', 10), color = C(320), name = 'Numina'};
    25     26   	-- numina is measured in daψ
    26     27   	warmth     = {min = -1000, max = 1000, base = 0, desc = U('°C', 10, true), color = C(5), name = 'Warmth'};
    27         -	-- warmth in measured in °C×10
           28  +	-- warmth in measured in d°C
    28     29   	fatigue    = {min = 0, max = 76 * 60, base = 0, desc = U('hr', 60, true), color = C(288,.3,.5), name = 'Fatigue'};
    29     30   	-- fatigue is measured in minutes one needs to sleep to cure it
    30     31   	stamina    = {min = 0, max = 20 * 100, base = true, desc = U('m', 100), color = C(88), name = 'Stamina'};
    31     32   	-- stamina is measured in how many 10th-nodes (== cm) one can sprint
    32         -	hunger     = {min = 0, max = 2000e3, base = 0, desc = U('kCal', 1000, true), color = C(43,.5,.4), name = 'Hunger'};
    33         -	-- hunger is measured in calories one must consume to cure it. at a 2kCal deficit, you start dying
    34         -	thirst     = {min = 0, max = 4e3, base = 0, desc = U('L', 1e3), color = C(217, .25,.4), name = 'Thirst'};
    35         -	-- thirst is measured in mL of H²O required to cure it
           33  +	nutrition  = {min = 0, max = 8000, base = 0, desc = U('kCal', 1, true), color = C(43,.5,.4), name = 'Nutrition', srzType = T.decimal};
           34  +	-- hunger is measured in kcalories one must consume to cure it. at 0, you start dying
           35  +	hydration  = {min = 0, max = 4, base = 0, desc = U('L', 1), color = C(217, .25,.4), name = 'Hydration', srzType = T.decimal};
           36  +	-- thirst is measured in L of H²O required to cure it
    36     37   	morale     = {min = 0, max = 24 * 60 * 10, base = true, desc = U('hr', 60, true), color = C(0,0,.8), name = 'Morale'};
    37     38   	-- morale is measured in minutes. e.g. at base rate morale degrades by
    38     39   	-- 60 points every hour. morale can last up to 10 days
    39         -	irradiation = {min = 0, max = 20000, base = 0, desc = U('Gy', 1000), color = C(141,1,.5), name = 'Irradiation'};
           40  +	irradiation = {min = 0, max = 10, base = 0, desc = U('Gy', 1), color = C(141,1,.5), name = 'Irradiation', srzType = T.decimal};
    40     41   	-- irrad is measured is milligreys
    41     42   	-- 1Gy counters natural healing
    42     43   	-- ~3Gy counters basic nanomedicine
    43     44   	-- 5Gy causes death within two weeks without nanomedicine
    44     45   	-- radiation speeds up psi regen
    45     46   	-- morale drain doubles with each 2Gy
    46     47   	illness    = {min = 0, max = 1000, base = 0, desc = U('%', 10, true), color = C(71,.4,.25), name = 'Illness'};
    47     48   	-- as illness increases, maximum stamina and health gain a corresponding limit
    48     49   	-- illness is increased by certain conditions, and decreases on its own as your
    49     50   	-- body heals when those conditions wear off. some drugs can lower accumulated illness
    50     51   	-- but illness-causing conditions require specific cures
    51     52   	-- illness also causes thirst and fatigue to increase proportionately
    52     53   }

Modified mods/starlit/terrain.lua from [43cb3c61b6] to [033036f747].

     4      4   starlit.terrain = {}
     5      5   local soilSounds = {
     6      6   	footstep = 'default-dirt-footstep';
     7      7   	dig = 'default-dig-crumbly';
     8      8   	dug = 'default-dug-node';
     9      9   }
    10     10   local sandSounds = {
    11         -	footstep = 'default-sand-footstep';
           11  +	footstep = {name='default-sand-footstep',gain=0.1};
    12     12   	dig = 'default-dig-crumbly';
    13     13   	dug = 'default-dug-node';
    14     14   }
    15     15   local grassSounds = {
    16     16   	footstep = 'default-grass-footstep';
    17     17   	dig = 'default-dig-crumbly';
    18     18   	dug = 'default-dug-node';
................................................................................
    21     21   minetest.register_node('starlit:soil', {
    22     22   	description = T 'Soil';
    23     23   	tiles = {'default_dirt.png'};
    24     24   	groups = {dirt = 1};
    25     25   	drop = '';
    26     26   	sounds = soilSounds;
    27     27   	_starlit = {
    28         -		onDestroy = function() end;
    29     28   		kind = 'block';
    30     29   		elements = {};
    31     30   	};
    32     31   })
    33     32   
    34     33   
    35     34   minetest.register_node('starlit:sand', {

Modified mods/starlit/user.lua from [a0e5424f99] to [fe75c1df99].

    72     72   		pushPersona = function(self)
    73     73   			local s = userStore(self.entity)
    74     74   			s.write('persona', self.persona)
    75     75   		end;
    76     76   		uiColor = function(self) return lib.color {hue=238,sat=.5,lum=.5} end;
    77     77   		statDelta = function(self, stat, d, cause, abs)
    78     78   			local dt = self.persona.statDeltas
    79         -			local base
           79  +			local min, max, base = self:statRange(stat)
    80     80   			if abs then
    81         -				local min, max
    82         -				min, max, base = self:statRange(stat)
    83     81   				if     d == true  then d = max
    84     82   				elseif d == false then d = min end
    85     83   			end
    86     84   			if stat == 'health' then
    87     85   				self.entity:set_hp(abs and d or (self.entity:get_hp() + d), cause)
    88     86   			elseif stat == 'breath' then
    89     87   				self.entity:set_breath(abs and d or (self.entity:get_breath() + d))
    90     88   			else
    91     89   				if abs then
    92     90   					dt[stat] = d - base
    93     91   				else
    94     92   					dt[stat] = dt[stat] + d
    95     93   				end
           94  +
           95  +				if     dt[stat]+base > max then dt[stat] = max-base
           96  +				elseif dt[stat]+base < min then dt[stat] = min-base end
    96     97   				self:pushPersona()
    97     98   			end
           99  +
          100  +
    98    101   			self:updateHUD()
    99    102   			-- TODO trigger relevant animations?
   100    103   		end;
   101    104   		lookupSpecies = function(self)
   102    105   			return starlit.world.species.lookup(self.persona.species, self.persona.speciesVariant)
   103    106   		end;
   104    107   		phenoTrait = function(self, trait, dflt)
................................................................................
   469    472   
   470    473   			giveGifts('main', gifts.carry)
   471    474   
   472    475   			self:reconfigureSuit()
   473    476   
   474    477   			-- i feel like there has to be a better way
   475    478   			local cx = math.random(-500,500)
   476         -			local startPoint
          479  +			local iter, startPoint = 1
   477    480   			repeat local temp = -100
   478    481   				local cz = math.random(-500,500)
   479    482   				local cy = minetest.get_spawn_level(cx, cz)
   480    483   				if cy then
   481    484   					startPoint = vector.new(cx,cy,cz)
   482    485   					temp = starlit.world.climate.eval(startPoint,.5,.5).surfaceTemp
   483    486   				end
   484         -				if cx > 10000 then break end -- avoid infiniloop in pathological conditions
          487  +				iter = iter + 1
          488  +				if iter > 100 then break end -- avoid infiniloop in pathological conditions
   485    489   			until temp > -2
   486    490   			self.entity:set_pos(startPoint)
   487    491   			meta:set_string('starlit_spawn', startPoint:to_string())
   488    492   		end;
   489    493   		onDie = function(self, reason)
   490    494   			local inv = self.entity:get_inventory()
   491    495   			local where = self.entity:get_pos()
................................................................................
   496    500   						minetest.item_drop(o, self.entity, where)
   497    501   					end
   498    502   				end
   499    503   				inv:set_list(lst, {})
   500    504   			end
   501    505   			dropInv 'main'
   502    506   			dropInv 'starlit_suit'
   503         -			self:statDelta('psi',     0, 'death', true)
   504         -			self:statDelta('hunger',  0, 'death', true)
   505         -			self:statDelta('thirst',  0, 'death', true)
   506         -			self:statDelta('fatigue', 0, 'death', true)
   507         -			self:statDelta('stamina', 0, 'death', true)
          507  +			self:statDelta('psi',          0, 'death', true)
          508  +			self:statDelta('nutrition', 1500, 'death', true)
          509  +			self:statDelta('hydration',    2, 'death', true)
          510  +			self:statDelta('fatigue',      0, 'death', true)
          511  +			self:statDelta('stamina',      0, 'death', true)
   508    512   			self:updateSuit()
   509    513   		end;
   510    514   		onRespawn = function(self)
   511    515   			local meta = self.entity:get_meta()
   512    516   			self.entity:set_pos(vector.from_string(meta:get_string'starlit_spawn'))
   513    517   			self:updateSuit()
   514    518   			return true
................................................................................
   840    844   		local bmr = p:trait 'metabolism' * biointerval
   841    845   		-- TODO apply modifiers
   842    846   
   843    847   		local dehydration = p:trait 'dehydration' * biointerval
   844    848   		-- you dehydrate faster in higher temp
   845    849   		dehydration = dehydration * math.max(1, starlit.world.climate.temp(u.entity:get_pos()) / 10)
   846    850   
   847         -		u:statDelta('hunger', bmr)
   848         -		u:statDelta('thirst', dehydration)
          851  +		u:statDelta('nutrition', -bmr)
          852  +		u:statDelta('hydration', -dehydration)
          853  +
          854  +		if u:effectiveStat 'nutrition' == 0 then
          855  +			-- starvation
          856  +		end
          857  +
          858  +		if u:effectiveStat 'hydration' == 0 then
          859  +			-- dying of thirst
          860  +		end
          861  +
          862  +		local rads = u:effectiveStat 'irradiation'
          863  +		if rads > 0 then
          864  +			u:statDelta('irradiation', -0.0001 * biointerval)
          865  +		end
          866  +
   849    867   	end
   850    868   end)
   851    869   
   852    870   local cbit = {
   853    871   	up   = 0x001;
   854    872   	down = 0x002;
   855    873   	left = 0x004;

Modified mods/starlit/world.lua from [c6867ae3a2] to [4823845880].

   137    137   			base.node_dig_prediction = stageID(st.swap)
   138    138   			function base.after_dig_node(pos, node, digger)
   139    139   				node.name = stageID(st.swap)
   140    140   				minetest.swap_node(pos, node)
   141    141   				return true
   142    142   			end
   143    143   		end
   144         -		if st.biolum then
   145         -			base.light_source = math.floor(st.biolum * (n/stageCt))
   146         -		end
          144  +		if st.biolum then base.light_source = st.biolum; end
   147    145   		return base
   148    146   	end
   149    147   	for i, v in ipairs(b.stages) do
   150    148   		local n = regStage(i, v)
   151         -		b.stageNodes[i] = n
   152    149   		minetest.register_node(stageID(i), n)
          150  +		b.stageNodes[i] = stageID(i)
   153    151   	end
   154    152   	b.fullyGrown = stageID(stageCt)
   155    153   
   156    154   	local dec = {
   157    155   		deco_type = 'simple';
   158         -		decoration = b.fullyGrown;
          156  +		decoration = b.stageNodes;
   159    157   		height = 1;
   160    158   		param2 = b.meshOpt or 0;
   161    159   	}
   162    160   	for k,v in pairs(b.decoration) do dec[k] = v end
   163    161   	b.decoration = minetest.register_decoration(dec)
   164    162   end)
   165    163   
................................................................................
   231    229   			end
   232    230   			-- for every degree of difference you suffer 2 points of damage/s
   233    231   			local dmg = math.ceil(dv * 2)
   234    232   			user:statDelta('health', -dmg)
   235    233   		end
   236    234   	end
   237    235   end)
          236  +
          237  +
          238  +world.ecology.trees.foreach('starlit:tree-gen', {}, function(id, t)
          239  +	local dec = {
          240  +		deco_type = 'lsystem';
          241  +		treedef = t.def;
          242  +	}
          243  +	for k,v in pairs(t.decorate) do dec[k]=v end
          244  +	minetest.register_decoration(dec)
          245  +end)
          246  +
          247  +minetest.register_abm {
          248  +	label = "plant growth";
          249  +	nodenames = {'group:plant_grow'};
          250  +	chance = 15;
          251  +	interval = 20;
          252  +	catch_up = true;
          253  +	action = function(pos, node)
          254  +		local def = minetest.registered_nodes[node.name]._starlit.plant
          255  +		local plant = starlit.world.ecology.plants.db[def.id]
          256  +		local nextStage = plant.stageNodes[def.stage + 1]
          257  +		minetest.swap_node(pos, {name=nextStage})
          258  +	end;
          259  +}

Modified mods/vtlib/marshal.lua from [500f446f0a] to [2822b4a0ba].

   134    134   -- generic type constructors --
   135    135   -------------------------------
   136    136   
   137    137   function G.int(bits,signed)
   138    138   	local bytes = math.ceil(bits / 8)
   139    139   	local max = 2 ^ bits
   140    140   	local spoint = math.floor(max/2)
   141         -	return {
   142         -		sz = bytes;
   143         -		name = string.format("%sint<%s>",
          141  +	local name = string.format("%sint<%s>",
   144    142   			signed and 's' or 'u', bits
   145    143   		);
          144  +	return {
          145  +		name = name;
          146  +		sz = bytes;
   146    147   		enc = function(obj)
          148  +			report('encoding %s value=%s', name, dump(obj))
   147    149   			obj = obj or 0
   148    150   			local val = math.abs(obj)
   149    151   			local str = ''
   150    152   			if signed then
   151    153   				local max = math.floor(max / 2)
   152    154   				if (obj > max) or (obj < (0-(max+1))) then
   153         -					return m.err.domain end
          155  +					return error('domain error') end
   154    156   				if obj < 0 then val = val + spoint end
   155    157   				-- e.g. for 8bit: 0x80 == -1; 0xFF = -128
   156    158   			else
   157         -				if val > max then return m.err.domain end
          159  +				if val > max then error('domain error') end
   158    160   			end
   159    161   			for i=1,bytes do
   160    162   				local n = math.fmod(val, 0x100)
   161    163   				str = str .. string.char(n)
   162    164   				val = math.floor(val / 0x100)
   163    165   			end
   164    166   			return str
................................................................................
   198    200   		def = ...
   199    201   	end
   200    202   	name = 'struct' .. (name and ':' .. name or '');
   201    203   	report('defining struct name=%q fields=%s', name, dump(def))
   202    204   	return {
   203    205   		name = name;
   204    206   		enc = function(obj)
          207  +		report('encoding struct name=%q vals=%s', name, dump(obj))
   205    208   			local enc = m.streamEncoder()
   206    209   			local n = 0
   207    210   			for k,ty in pairs(def) do n=n+1
   208    211   				if obj[k] == nil then error('missing key '..dump(k)..' for type '..ty.name) end
   209    212   				local encoded = ty.enc(obj[k])
   210    213   				enc.push(T.u8.enc(#k), size.enc(#encoded), k, encoded)
   211    214   			end

Modified mods/vtlib/ui.lua from [121fdb8a0d] to [765cced2b5].

    42     42   	};
    43     43   
    44     44   	tooltipper = function(dui)
    45     45   		-- takes a configuration table mapping affinities to colors.
    46     46   		-- 'neutral' is the only required affinity
    47     47   		return function(a)
    48     48   			local color = a.color and a.color:readable(0.65, 1.0)
    49         -			if color == nil then color = l.color(136,158,177) end
           49  +			if color == nil then color = l.color(.5,.5,.5) end
    50     50   			local str = a.title
    51     51   			if a.desc then
    52     52   				str = str .. '\n' .. color:fmt(minetest.wrap_text(a.desc,60))
    53     53   			end
    54     54   			if a.props then
    55     55   				-- str = str .. '\n'
    56     56   				for _,prop in pairs(a.props) do