starlit  Check-in [a810a756ce]

Overview
Comment:cleanups, fixes, begin canister rework, begin ecology
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a810a756ce2ab6fe9009feee5e3634704d85eecb804d1ed7fd8f3ee3d0b55af6
User & Date: lexi on 2024-05-01 13:46:45
Other Links: manifest | tags
Context
2024-05-01
16:25
fixes, sounds; add license info check-in: 0e67c606c9 user: lexi tags: trunk
13:46
cleanups, fixes, begin canister rework, begin ecology check-in: a810a756ce user: lexi tags: trunk
2024-04-29
22:49
rename again check-in: 187bf04c87 user: lexi tags: trunk
Changes

Modified mods/starlit-electronics/sw.lua from [288b4b5859] to [d777bc86b5].

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


20
21

22
23
24


25
26


27
28
29
30
31
32
33
34
...
101
102
103
104
105
106
107
108
109

110
111
112
113
114
115
116
--  🄯 EUPL v1.2
--  ? 

-------------------------------
-- basic suit nano abilities --
-------------------------------
local function shredder(prop)
	local function getItemsForFab(fab)
		local elt
		if fab then
			elt = fab:elementalize()
		else
			elt = {}
		end
		local items = {}
		if elt.element then
			for k,v in pairs(elt.element) do


				local st = ItemStack {
					name = starlit.world.material.element.db[k].form.element;

					count = v;
				}
				table.insert(items, st)


			end
		end


		return items
	end

	return function(user, ctx)
		local function cleanup()
			user.action.prog.shred = nil
			if user.action.sfx.shred then
				minetest.sound_fade(user.action.sfx.shred, 1, 0)
................................................................................
					user:suitSound 'starlit-success'
					if fab then
						local vf = fab
						if vary then
							local rng = (starlit.world.seedbank+0xa891f62)[minetest.hash_node_position(what)]
							vf = vf + vary(rng, {})
						end
						local items = getItemsForFab(vf)
						for i, it in ipairs(items) do user:give(it) end

					end
				else
					user:suitSound 'starlit-error'
				end
				cleanup()
			end
		elseif ctx.how.state == 'halt' then







|






|


>
>
|
<
>
|
|
|
>
>
|
|
>
>
|







 







|

>







3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
--  🄯 EUPL v1.2
--  ? 

-------------------------------
-- basic suit nano abilities --
-------------------------------
local function shredder(prop)
	local function fabToItemsAndCharges(fab)
		local elt
		if fab then
			elt = fab:elementalize()
		else
			elt = {}
		end
		local items,charges = {},{}
		if elt.element then
			for k,v in pairs(elt.element) do
				local forms =  starlit.world.material.element.db[k].form
				if forms.brick then
					local st = ItemStack {

						name = forms.brick;
						count = math.floor(v);
					}
					table.insert(items, st)
				else -- gas, liquid
					table.insert(charges, {id = k, mass = v})
				end
			end
		end
		print(dump(items))
		return items, charges
	end

	return function(user, ctx)
		local function cleanup()
			user.action.prog.shred = nil
			if user.action.sfx.shred then
				minetest.sound_fade(user.action.sfx.shred, 1, 0)
................................................................................
					user:suitSound 'starlit-success'
					if fab then
						local vf = fab
						if vary then
							local rng = (starlit.world.seedbank+0xa891f62)[minetest.hash_node_position(what)]
							vf = vf + vary(rng, {})
						end
						local items, charges = fabToItemsAndCharges(vf)
						for i, it in ipairs(items) do user:give(it) end
						-- TODO give gasses, liquids
					end
				else
					user:suitSound 'starlit-error'
				end
				cleanup()
			end
		elseif ctx.how.state == 'halt' then

Modified mods/starlit-material/elements.lua from [5740080b38] to [2a0859e47b].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
local lib = starlit.mod.lib
local W = starlit.world
local M = W.material

M.element.meld {
	hydrogen = {
		name = 'hydrogen', sym = 'H', n = 1;
		gas = true;
		color = lib.color(1,0.8,.3);
	};
	beryllium = {
		name = 'Beryllium', sym = 'Be', n = 4;
		metal = true; -- rare emerald-stuff
		color = lib.color(0.2,1,0.2);
	};
	oxygen = {
		name = 'oxygen', sym = 'O', n = 8;
		gas = true;
		color = lib.color(.2,1,.2);
	};
	carbon = {
		name = 'carbon', sym = 'C', n = 6;
		color = lib.color(.7,.2,.1);
	};
	silicon = {
		name = 'silicon', sym = 'Si', n = 14;
		metal = true; -- can be forged into an ingot
		color = lib.color(.6,.6,.4);
	};
	potassium = {
		name = 'potassium', sym = 'K', n = 19;
		-- potassium is technically a metal but it's so soft
		-- it can be easily nanoworked without high temps, so
		-- ingots make no sense
		color = lib.color(1,.8,0.1);
	};
	calcium = {
		name = 'calcium', sym = 'Ca', n = 20;
		metal = true;
		color = lib.color(1,1,0.7);
	};
	aluminum = {
		name = 'aluminum', sym = 'Al', n = 13;
		metal = true;
		color = lib.color(0.9,.95,1);
	};
	iron = {
		name = 'iron', sym = 'Fe', n = 26;
		metal = true;
		color = lib.color(.3,.3,.3);
	};
	copper = {
		name = 'copper', sym = 'Cu', n = 29;
		metal = true;
		color = lib.color(.8,.4,.1);
	};
	lithium = {
		name = 'lithium', sym = 'Li', n = 3;
		-- i think lithium is considered a metal but we don't mark it as
		-- one here because making a 'lithium ingot' is insane (even possible?)
		color = lib.color(1,0.8,.3);
	};
	titanium = {
		name = 'titanium', sym = 'Ti', n = 22;
		metal = true;
		color = lib.color(.7,.7,.7);
	};
	vanadium = {
		name = 'vanadium', sym = 'V', n = 23;
		metal = true;
		color = lib.color(.3,0.5,.3);
	};
	xenon = {
		name = 'xenon', sym = 'Xe', n = 54;
		gas = true;
		color = lib.color(.5,.1,1);
	};
	argon = {
		name = 'argon', sym = 'Ar', n = 18;
		gas = true;
		color = lib.color(0,0.1,.9);
	};
	osmium = {
		name = 'osmium', sym = 'Os', n = 76;
		metal = true;
		color = lib.color(.8,.1,1);
	};
	iridium = {
		name = 'iridium', sym = 'Ir', n = 77;
		metal = true;
		color = lib.color(.8,0,.5);
	};
	technetium = {
		name = 'technetium', sym = 'Tc', n = 43;
		desc = 'Prized by the higher Powers for subtle interactions that elude mere human scholars, technetium is of particular use in nuclear nanobatteries.';
		metal = true;
		color = lib.color(.2,0.2,1);
	};
	uranium = {
		name = 'uranium', sym = 'U', n = 92;
		desc = 'A weak but relatively plentiful nuclear fuel.';
		metal = true;
		color = lib.color(.2,.7,0);
	};
	thorium = {
		name = 'thorium', sym = 'Th', n = 90;
		desc = 'A frighteningly powerful nuclear fuel.';
		metal = true;
		color = lib.color(.7,.3,.1);
	};
	silver = {
		name = 'silver', sym = 'Ag', n = 47;
		metal = true;
		color = lib.color(.7,.7,.8);
	};
	gold = {
		name = 'gold', sym = 'Au', n = 79;
		metal = true;
		color = lib.color(1,.8,0);
	};
}






|




|




|




|



|




|






|




|




|




|




|





|




|




|




|




|




|




|





|





|





|




|




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
local lib = starlit.mod.lib
local W = starlit.world
local M = W.material

M.element.meld {
	hydrogen = {
		name = 'hydrogen', sym = 'H', n = 1;  density = 8.988e-5;
		gas = true;
		color = lib.color(1,0.8,.3);
	};
	beryllium = {
		name = 'beryllium', sym = 'Be', n = 4;  density = 0;
		metal = true; -- rare emerald-stuff
		color = lib.color(0.2,1,0.2);
	};
	oxygen = {
		name = 'oxygen', sym = 'O', n = 8;  density = 0.001429;
		gas = true;
		color = lib.color(.2,1,.2);
	};
	carbon = {
		name = 'carbon', sym = 'C', n = 6, density = 2.266; -- g/cm³
		color = lib.color(.7,.2,.1);
	};
	silicon = {
		name = 'silicon', sym = 'Si', n = 14, density = 2.329;
		metal = true; -- can be forged into an ingot
		color = lib.color(.6,.6,.4);
	};
	potassium = {
		name = 'potassium', sym = 'K', n = 19, density = 0.862;
		-- potassium is technically a metal but it's so soft
		-- it can be easily nanoworked without high temps, so
		-- ingots make no sense
		color = lib.color(1,.8,0.1);
	};
	calcium = {
		name = 'calcium', sym = 'Ca', n = 20; density = 1.55;
		metal = true;
		color = lib.color(1,1,0.7);
	};
	aluminum = {
		name = 'aluminum', sym = 'Al', n = 13;  density = 2.7;
		metal = true;
		color = lib.color(0.9,.95,1);
	};
	iron = {
		name = 'iron', sym = 'Fe', n = 26;  density = 7.874;
		metal = true;
		color = lib.color(.3,.3,.3);
	};
	copper = {
		name = 'copper', sym = 'Cu', n = 29;  density = 8.96;
		metal = true;
		color = lib.color(.8,.4,.1);
	};
	lithium = {
		name = 'lithium', sym = 'Li', n = 3;  density = 0.534;
		-- i think lithium is considered a metal but we don't mark it as
		-- one here because making a 'lithium ingot' is insane (even possible?)
		color = lib.color(1,0.8,.3);
	};
	titanium = {
		name = 'titanium', sym = 'Ti', n = 22;  density = 4.506;
		metal = true;
		color = lib.color(.7,.7,.7);
	};
	vanadium = {
		name = 'vanadium', sym = 'V', n = 23; density = 6;
		metal = true;
		color = lib.color(.3,0.5,.3);
	};
	xenon = {
		name = 'xenon', sym = 'Xe', n = 54;  density = 0.005894;
		gas = true;
		color = lib.color(.5,.1,1);
	};
	argon = {
		name = 'argon', sym = 'Ar', n = 18;  density = 0.001784;
		gas = true;
		color = lib.color(0,0.1,.9);
	};
	osmium = {
		name = 'osmium', sym = 'Os', n = 76;  density = 22.59;
		metal = true;
		color = lib.color(.8,.1,1);
	};
	iridium = {
		name = 'iridium', sym = 'Ir', n = 77; density = 22.56;
		metal = true;
		color = lib.color(.8,0,.5);
	};
	technetium = {
		name = 'technetium', sym = 'Tc', n = 43;  density = 11;
		desc = 'Prized by the higher Powers for subtle interactions that elude mere human scholars, technetium is of particular use in nuclear nanobatteries.';
		metal = true;
		color = lib.color(.2,0.2,1);
	};
	uranium = {
		name = 'uranium', sym = 'U', n = 92;  density = 19.1;
		desc = 'A weak but relatively plentiful nuclear fuel.';
		metal = true;
		color = lib.color(.2,.7,0);
	};
	thorium = {
		name = 'thorium', sym = 'Th', n = 90;  density = 11.7;
		desc = 'A frighteningly powerful nuclear fuel.';
		metal = true;
		color = lib.color(.7,.3,.1);
	};
	silver = {
		name = 'silver', sym = 'Ag', n = 47;  density = 10.49;
		metal = true;
		color = lib.color(.7,.7,.8);
	};
	gold = {
		name = 'gold', sym = 'Au', n = 79;  density = 19.30;
		metal = true;
		color = lib.color(1,.8,0);
	};
}

Modified mods/starlit-material/init.lua from [fffa847df5] to [b9ae2a76fd].

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


21

22
local M = {
	canisterSizes = lib.registry.mk 'starlit_material:canister-size';
}
M.canisterSizes.foreach('starlit_material:canister_link', {}, function(id, sz)
	starlit.item.canister.link(minetest.get_current_modname() .. ':canister_' .. id, {
		name = sz.name;
		slots = sz.slots;
		vol = 0.1; -- too big for suit?
		desc = sz.desc;
	})
end)
M.canisterSizes.meld {
	tiny = {name = 'Tiny Canister', slots = 1, vol = 0.05};
	small = {name = 'Small Canister', slots = 3, vol = 0.2};
	mid = {name = 'Canister', slots = 5, vol = 0.5};
	large = {name = 'Large Canister', slots = 10, vol = 1.0};
	storage = {name = 'Storage Canister', slots = 50, vol = 5.0};
}



starlit.include 'elements'









|




|
|
|
|
|


>
>

>

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
local M = {
	canisterSizes = lib.registry.mk 'starlit_material:canister-size';
}
M.canisterSizes.foreach('starlit_material:canister_link', {}, function(id, sz)
	starlit.item.canister.link(minetest.get_current_modname() .. ':canister_' .. id, {
		name = sz.name;
		slots = sz.slots;
		vol = sz.vol; -- too big for suit?
		desc = sz.desc;
	})
end)
M.canisterSizes.meld {
	tiny    = {name = 'Tiny Canister',    vol = 1.0};
	small   = {name = 'Small Canister',   vol = 2.0};
	mid     = {name = 'Canister',         vol = 4.0};
	large   = {name = 'Large Canister',   vol = 8.0};
	storage = {name = 'Storage Canister', vol = 16.0};
}

starlit.mod.material = M

starlit.include 'elements'
starlit.include 'liquids'

Modified mods/starlit-scenario/init.lua from [bf764e9a62] to [2d3d2d3760].

1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
..
60
61
62
63
64
65
66
























67
68
69
70
71
72
73
..
91
92
93
94
95
96
97
98

99
100
101
102
103
104
105
local lib = starlit.mod.lib
local scenario = starlit.world.scenario


local function makeChip(label, schem, sw)
	local E = starlit.mod.electronics
	local files = {}
	local sz = 0
	for _, e in ipairs(schem) do
		local p = E.sw.findSchematicFor(e[1])
		if p then
			local file = {
				kind = 'sw', name = '', drm = e[2];
				body = {pgmId = p};
			}
			table.insert(files, file)
			sz = sz + E.chip.fileSize(file)
		end
	end
	for _, e in ipairs(sw) do
		local file = {
			kind = 'sw', name = '', drm = e[2];
			body = {pgmId = e[1]};
		}
		table.insert(files, file)
		sz = sz + E.chip.fileSize(file)
	end
	local chip = ItemStack(assert(E.chip.findBest(function(c)
		return c.flash >= sz, c.ram + c.clockRate
	end)))
................................................................................

local battery = function(name)
	local s = ItemStack(name)
	starlit.mod.electronics.battery.setChargeF(s, 1.0)
	return s
end


























table.insert(scenario, {
	id = 'starlit_scenario:imperialExpat';
	name = 'Imperial Expat';
	desc = "Hoping to escape a miserable life deep in the grinding gears of the capitalist machine for the bracing freedom of the frontier, you sought entry as a colonist to the new Commune world of Thousand Petal. Fate -- which is to say, terrorists -- intervened, and you wound up stranded on Farthest Shadow with little more than the nanosuit on your back, ship blown to tatters and your soul thoroughly mauled by the explosion of a twisted alien artifact -- which SOMEONE neglected to inform you your ride would be carrying.\nAt least you got some nifty psionic powers out of this whole clusterfuck. Hopefully they're safe to use.";

	species = 'human';
................................................................................
			-- came with this chip already plugged in. it's apparently true
			-- what they say: the Commune is always prepared for everything.
			-- E V E R Y T H I N G.
		};
		suitGuns = {};
		suitAmmo = {};
		suitCans = {
			ItemStack('starlit_material:canister_small');

		};
		carry = {
			chipLibrary.compendium;
			-- you bought this on a whim before you left the Empire, and
			-- just happened to still have it on your person when everything
			-- went straight to the Wild Gods' privy
		};


>










|








|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
local lib = starlit.mod.lib
local scenario = starlit.world.scenario
local log = starlit.logger 'scenario'

local function makeChip(label, schem, sw)
	local E = starlit.mod.electronics
	local files = {}
	local sz = 0
	for _, e in ipairs(schem) do
		local p = E.sw.findSchematicFor(e[1])
		if p then
			local file = {
				kind = 'sw', name = '', drm = e[2];
				body = {pgmId = p, conf = {}};
			}
			table.insert(files, file)
			sz = sz + E.chip.fileSize(file)
		end
	end
	for _, e in ipairs(sw) do
		local file = {
			kind = 'sw', name = '', drm = e[2];
			body = {pgmId = e[1], conf = {}};
		}
		table.insert(files, file)
		sz = sz + E.chip.fileSize(file)
	end
	local chip = ItemStack(assert(E.chip.findBest(function(c)
		return c.flash >= sz, c.ram + c.clockRate
	end)))
................................................................................

local battery = function(name)
	local s = ItemStack(name)
	starlit.mod.electronics.battery.setChargeF(s, 1.0)
	return s
end

local function volume(kind,name,mass)
	local sorted = {}
	for k,v in pairs(starlit.item.canister.db) do
		table.insert(sorted, {id=k, can=v})
	end
	table.sort(sorted, function(a,b) return a.can.vol < b.can.vol end)

	local liq = starlit.world.material[kind].db[name]

	local can
	for i, v in ipairs(sorted) do
		if v.can.vol <= liq.density * mass then
			can = ItemStack(i)
			break
		end
	end
	if can == nil then log.fatal('failed to find canister size for gift %s', kind) end

	local st = starlit.item.canister.meta(can)
	st.write('contents', {kind = kind, id = name, mass = mass})

	return can
end


table.insert(scenario, {
	id = 'starlit_scenario:imperialExpat';
	name = 'Imperial Expat';
	desc = "Hoping to escape a miserable life deep in the grinding gears of the capitalist machine for the bracing freedom of the frontier, you sought entry as a colonist to the new Commune world of Thousand Petal. Fate -- which is to say, terrorists -- intervened, and you wound up stranded on Farthest Shadow with little more than the nanosuit on your back, ship blown to tatters and your soul thoroughly mauled by the explosion of a twisted alien artifact -- which SOMEONE neglected to inform you your ride would be carrying.\nAt least you got some nifty psionic powers out of this whole clusterfuck. Hopefully they're safe to use.";

	species = 'human';
................................................................................
			-- came with this chip already plugged in. it's apparently true
			-- what they say: the Commune is always prepared for everything.
			-- E V E R Y T H I N G.
		};
		suitGuns = {};
		suitAmmo = {};
		suitCans = {
-- 			ItemStack('starlit_material:canister_small');
			volume('liquid', 'water', 5);
		};
		carry = {
			chipLibrary.compendium;
			-- you bought this on a whim before you left the Empire, and
			-- just happened to still have it on your person when everything
			-- went straight to the Wild Gods' privy
		};

Modified mods/starlit-secrets/init.lua from [0a228a51be] to [50a2d37741].

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
				'the firstborn smonked hella weed';
			};
			-- body can also be a function(user,secret)
		}
   }
}

TODO would it be useful to impl horn clauses and a general fact database?
     is that level of flexibility meaningful? or are simply flags better

a secret can be a single piece of information predicated
on a fact, in which case the secret and fact should share
the same ID. the ID should be as non-indicative as possible
to avoid spoilers for devs of unrelated code.

a secret can also be manually unlocked e.g. by using an item

]==]--

function sec.prereqCheck(user, pr)
end







<
<
<











38
39
40
41
42
43
44



45
46
47
48
49
50
51
52
53
54
55
				'the firstborn smonked hella weed';
			};
			-- body can also be a function(user,secret)
		}
   }
}




a secret can be a single piece of information predicated
on a fact, in which case the secret and fact should share
the same ID. the ID should be as non-indicative as possible
to avoid spoilers for devs of unrelated code.

a secret can also be manually unlocked e.g. by using an item

]==]--

function sec.prereqCheck(user, pr)
end

Modified mods/starlit/effect.lua from [eed7790b6a] to [9a580a99bf].

307
308
309
310
311
312
313

314
315
316
317
318
319
320
				s.queue(spec.stop, function()
					for _,snd in pairs(snds) do s.silence(snd) end
				end)
			end
		end)
	end
	s.silence = function(sound)

		if sound.ctl.fade == 0 then minetest.sound_stop(sound.handle)
		else minetest.sound_fade(sound.handle,sound.ctl.fade or 1,0) end
	end
	local startqueued, termqueued = false, false
	local myid = #starlit.effect.active+1
	s.cancel = function()
		s.abort()







>







307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
				s.queue(spec.stop, function()
					for _,snd in pairs(snds) do s.silence(snd) end
				end)
			end
		end)
	end
	s.silence = function(sound)
		if not sound.handle then return end
		if sound.ctl.fade == 0 then minetest.sound_stop(sound.handle)
		else minetest.sound_fade(sound.handle,sound.ctl.fade or 1,0) end
	end
	local startqueued, termqueued = false, false
	local myid = #starlit.effect.active+1
	s.cancel = function()
		s.abort()

Modified mods/starlit/element.lua from [db8d1e2ad1] to [e6ca28c621].

12
13
14
15
16
17
18

19
20
21
22
23

24
25
26
27
28
29
30
..
63
64
65
66
67
68
69




70

















71







72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
161
162
163
164
165
166
167






168
169
170
171
172
173
174

175
176
177
178
179
180
181
182












183
184






185
186
187
188
189
190
191
192
193
194
...
212
213
214
215
216
217
218


			-- for the object in question; e.g a normal chunk will be
			-- 100 $element, an ingot will be 1000 $element
		})
	elseif m.gas then
		M.gas.link(id, {
			name = m.name;
			composition = starlit.type.fab{element = {[id] = 1}};

		})
	elseif m.liquid then
		M.liquid.link(id, {
			name = m.name;
			composition = starlit.type.fab{element = {[id] = 1}};

		})
	end
end)

local F = string.format

local function mkEltIndicator(composition)
................................................................................
			props = {
				{title = 'Mass', desc = lib.math.si('g', g), affinity='info'}
			}
		}
	end
	local comp = {[id] = 1}
	local iblit = mkEltIndicator(comp)




	m.form = m.form or {}

















	m.form.element = eltID









	local powder = F('starlit-element-%s-powder.png', id);
	minetest.register_craftitem(eltID, {
		short_description = eltName;
		description = tt(eltName, F('Elemental %s kept in suspension by a nanide storage system, ready to be worked by a cold matter compiler', m.name), 1);
		inventory_image = iblit(powder);
		wield_image = powder;
		stack_max = 1000; -- 1kg
		groups = {element = 1, powder = 1, specialInventory = 1};
		_starlit = {
			mass = 1;
			material = {
				kind = 'element';
				element = id;
			};
			fab = starlit.type.fab {
				element = comp;
			};
		};
	});

end)


M.metal.foreach('starlit:gen-forms', {}, function(id, m)
	local baseID = F('%s:metal_%s_', minetest.get_current_modname(), id)
	local brickID, ingotID = baseID .. 'brick', baseID .. 'ingot'
	local brickName, ingotName =
		F('%s Brick', lib.str.capitalize(m.name)),
		F('%s Ingot', lib.str.capitalize(m.name))
	m.form = m.form or {}
	m.form.brick = brickID
	m.form.ingot = ingotID
	local tt = function(t, d, g)
		return starlit.ui.tooltip {
			title = t, desc = d;
			color = lib.color(0.1,0.1,0.1);
			props = {
				{title = 'Mass', desc = lib.math.si('g', g), affinity='info'}
................................................................................
		return t
	end
	local iblit = mkEltIndicator(mcomp)
	local function img(s)
		return iblit(s:colorize(m.color):render())
	end

	minetest.register_craftitem(brickID, {
		short_description = brickName;
		description = tt(brickName, F('A solid brick of %s, ready to be worked by a matter compiler', m.name), 100);
		inventory_image = img(lib.image 'starlit-item-brick.png');
		wield_image = lib.image 'starlit-item-brick.png':colorize(m.color):render();
		stack_max = 10;
		groups = {metal = 1, ingot = 1};
		_starlit = {
			mass = 100;
			material = {
				kind = 'metal';
				metal = id;
			};
			fab = starlit.type.fab {
				flag = {smelt= true};
				element = comp(1e2);
			};
		};
	});

	minetest.register_craftitem(ingotID, {
		short_description = ingotName;
		description = tt(ingotName, F('A solid ingot of %s, ready to be worked by a large matter compiler', m.name), 1e3);
		inventory_image = img(lib.image('starlit-item-ingot.png'));
		wield_image = lib.image 'starlit-item-ingot.png':colorize(m.color):render();
		groups = {metal = 1, ingot = 1};
		stack_max = 5;
................................................................................
				element = comp(1e3);
			};
		};
	});


end)







local function canisterDesc(stack, def)
	def = def or stack:get_definition()._starlit.canister
	local props = {
		{title = 'Charge Slots', affinity = 'info', desc = tostring(def.slots)};
	};
	if stack then

		local inv = starlit.item.container(stack)
		for i,e in ipairs(inv:list 'elem') do
			local comp = e:get_definition()._starlit.fab
			table.insert(props, {
				title = comp:formula();
				desc = lib.math.si('g', e:get_count());
				affinity = 'good';
			})












		end
		-- TODO list masses






	end
	return starlit.ui.tooltip {
		title = def.name, desc = def.desc or 'A canister that can store a charge of elemental powder, gas, or liquid';
		color = lib.color(0.2,0.1,0.1);
		props = props;
	};	
end

starlit.item.canister = lib.registry.mk 'starlit:canister';
starlit.item.canister.foreach('starlit:item-gen', {}, function(id, c)
................................................................................
						sz = c.slots;
					};
				};
			};
		};
	})
end)









>





>







 







>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>

>
|


|
|


|











>





|
<
<
|

<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>




|


>








>
>
>
>
>
>
>
>
>
>
>
>

<
>
>
>
>
>
>


|







 







>
>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129


130
131

132
133
134
135
136
137
138
...
148
149
150
151
152
153
154




















155
156
157
158
159
160
161
...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
...
245
246
247
248
249
250
251
252
253
			-- for the object in question; e.g a normal chunk will be
			-- 100 $element, an ingot will be 1000 $element
		})
	elseif m.gas then
		M.gas.link(id, {
			name = m.name;
			composition = starlit.type.fab{element = {[id] = 1}};
			density = m.density;
		})
	elseif m.liquid then
		M.liquid.link(id, {
			name = m.name;
			composition = starlit.type.fab{element = {[id] = 1}};
			density = m.density;
		})
	end
end)

local F = string.format

local function mkEltIndicator(composition)
................................................................................
			props = {
				{title = 'Mass', desc = lib.math.si('g', g), affinity='info'}
			}
		}
	end
	local comp = {[id] = 1}
	local iblit = mkEltIndicator(comp)
	local function img(s)
		return iblit(s:colorize(m.color):render())
	end

	m.form = m.form or {}


	if not (m.gas or m.liquid) then
		local brickID = eltID .. '_brick'
		local brickName = F('%s Brick', lib.str.capitalize(m.name))
		m.form.brick = brickID
		minetest.register_craftitem(brickID, {
			short_description = brickName;
			description = tt(brickName, F('A small brick of %s, ready to be worked by a matter compiler', m.name), 1);
			inventory_image = img(lib.image 'starlit-item-brick.png');
			wield_image = lib.image 'starlit-item-brick.png':colorize(m.color):render();
			stack_max = 500;
			groups = {element=1, brick=1};
			_starlit = {
				mass = 1;
				material = {
					kind = 'element';
					element = id;
				};
				fab = starlit.type.fab {
					element = comp;
				};
			};
		});
	end

	--[[
	local chunk = F('starlit-element-%s-powder.png', id);
	minetest.register_craftitem(eltID, {
		short_description = eltName;
		description = tt(eltName, F('A 1g chunk of elemental %s, ready to be worked by a cold matter compiler', m.name), 1);
		inventory_image = iblit(chunk);
		wield_image = powder;
		stack_max = 1000; -- 1kg
		groups = {element = 1, chunk = 1};
		_starlit = {
			mass = 1;
			material = {
				kind = 'element';
				element = id;
			};
			fab = starlit.type.fab {
				element = comp;
			};
		};
	});
	]]
end)


M.metal.foreach('starlit:gen-forms', {}, function(id, m)
	local baseID = F('%s:metal_%s_', minetest.get_current_modname(), id)
	local ingotID = baseID .. 'ingot'


	local ingotName = F('%s Ingot', lib.str.capitalize(m.name))
	m.form = m.form or {}

	m.form.ingot = ingotID
	local tt = function(t, d, g)
		return starlit.ui.tooltip {
			title = t, desc = d;
			color = lib.color(0.1,0.1,0.1);
			props = {
				{title = 'Mass', desc = lib.math.si('g', g), affinity='info'}
................................................................................
		return t
	end
	local iblit = mkEltIndicator(mcomp)
	local function img(s)
		return iblit(s:colorize(m.color):render())
	end





















	minetest.register_craftitem(ingotID, {
		short_description = ingotName;
		description = tt(ingotName, F('A solid ingot of %s, ready to be worked by a large matter compiler', m.name), 1e3);
		inventory_image = img(lib.image('starlit-item-ingot.png'));
		wield_image = lib.image 'starlit-item-ingot.png':colorize(m.color):render();
		groups = {metal = 1, ingot = 1};
		stack_max = 5;
................................................................................
				element = comp(1e3);
			};
		};
	});


end)

local function canisterMeta(stack)
	return lib.marshal.metaStore {
		contents = {key = 'starlit:canister_contents', type = starlit.store.volume};
	} (stack)
end

local function canisterDesc(stack, def)
	def = def or stack:get_definition()._starlit.canister
	local props = {
		{title = 'Volume', affinity = 'info', desc = lib.math.si('L', def.vol,nil,nil,2)};
	};
	if stack then
	--[[
		local inv = starlit.item.container(stack)
		for i,e in ipairs(inv:list 'elem') do
			local comp = e:get_definition()._starlit.fab
			table.insert(props, {
				title = comp:formula();
				desc = lib.math.si('g', e:get_count());
				affinity = 'good';
			})
		end ]]
		local itemMeta = canisterMeta(stack)
		local e = itemMeta.read 'contents'
		local mass = lib.math.si('g', e.mass, nil, nil, 2)
		local def, meas
		if e.kind == 'liquid' then
			def = M.liquid.db[e.id]
			local vol =  lib.math.si('L', e.mass * def.composition.density, nil, nil, 2)
			meas = string.format("%s %s (%s %s)", vol, def.name, e.mass, def.composition:formula())
		elseif e.kind == 'gas' then
			def = M.gas.db[e.id]
			meas = string.format("%s %s (%s)", mass, def.name, def.composition:formula())
		end

		local comp = def.composition
		table.insert(props, {
			title = meas;
			desc = def.desc;
			affinity = 'info';
		})
	end
	return starlit.ui.tooltip {
		title = def.name, desc = def.desc or 'A canister that can store a charge of gas or liquid';
		color = lib.color(0.2,0.1,0.1);
		props = props;
	};	
end

starlit.item.canister = lib.registry.mk 'starlit:canister';
starlit.item.canister.foreach('starlit:item-gen', {}, function(id, c)
................................................................................
						sz = c.slots;
					};
				};
			};
		};
	})
end)

starlit.item.canister.meta = canisterMeta

Modified mods/starlit/init.lua from [1f09b99871] to [6f8dfbcd79].

45
46
47
48
49
50
51

52
53
54
55
56
57
58
..
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
248
249
250
251
252
253
254

255
256
257
258
259
260
261
...
356
357
358
359
360
361
362

363
364
365
366
367
368
369





















370
371
372
373
374
375
376
	};
	liveUI = {
		-- cached subset of activeUI containing those UIs needing live updates
	};

	interface = lib.registry.mk 'starlit:interface';
	item = {

	};

	region = {
		radiator = {
			store = AreaStore();
			emitters = {}
		};
................................................................................
		fact = lib.registry.mk 'starlit:fact';
		time = {
			calendar = {
				empire  = {
					name = 'Imperial Regnal Calendar';
					year = function(t, long)
						local reigns = {
							-- if anyone actually makes it to his Honor & Glory Unfailing Persigan I i will be
							-- exceptionally flattered
							{4, 'Emperor', 'Atavarka', 'the Bold'}; -- died at war
							{9, 'Emperor', 'Vatikserka', 'the Unconquered'}; -- died at war
							{22, 'Emperor', 'Rusifend', 'the Wise'}; -- poisoned at diplomacy
							{61, 'Empress', 'Tafseshendi', 'the Great'}; -- died of an 'insurrection of the innards' after a celebrated reign
							{291, 'Emperor', 'Treptebaska', 'the Unwise'}; -- murdered by his wife in short order
							{292, 'Empress', 'Vilintalti', 'the Impious'}; -- removed by the praetorian elite
							{298, 'Emperor', 'Radavan', 'the Reckless'}; -- died at war
							{316, 'Emperor', 'Suldibrand', 'the Forsaken of Men'}; -- fucked around. found out.
							{320, 'Emperor', 'Persigan', 'the Deathless'};
						}
						local year, r = math.floor(t / 414)
						for i=1, #reigns do if reigns[i+1][1] < year then r = reigns[i+1] end end
						local reignBegin, title, name, epithet = lib.tbl.unpack(r)
						local ry = 1 + (year - reignBegin)
						return long and string.format('Year %s of the Reign of HH&GU %s %s %s',
							ry, title, name, epithet) or string.format('Y. %s %s', name, ry)
................................................................................
		end)
	end
	start()
end

starlit.include 'stats'
starlit.include 'world'

starlit.include 'fab'
starlit.include 'tiers'
starlit.include 'species'

starlit.include 'store'

starlit.include 'ui'
................................................................................
		user.action.bits = bit.bor(user.action.bits, 0x100)
		--return user:trigger('secondary', {state = 'prog', delta = 0})
	elseif pointChanged(oldTgt, point) then
		user:trigger('retarget', {oldTgt = oldTgt})
	end
end
-- sigh

core.noneitemdef_default.on_place = function(...)
	if not triggerPower(...) then
		minetest.item_place(...)
	end
end
core.noneitemdef_default.on_use           = function(...) triggerPower(...) end
core.noneitemdef_default.on_secondary_use = function(...) triggerPower(...) end






















minetest.register_on_player_inventory_action(function(luser, act, inv, p)
	local name = luser:get_player_name()
	local user = starlit.activeUsers[name]
	-- allow UIs to update on UI changes
	local state = starlit.activeUI[name]
	if state then







>







 







|









|







 







>







 







>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
	};
	liveUI = {
		-- cached subset of activeUI containing those UIs needing live updates
	};

	interface = lib.registry.mk 'starlit:interface';
	item = {
		food = lib.registry.mk 'starlit:food';
	};

	region = {
		radiator = {
			store = AreaStore();
			emitters = {}
		};
................................................................................
		fact = lib.registry.mk 'starlit:fact';
		time = {
			calendar = {
				empire  = {
					name = 'Imperial Regnal Calendar';
					year = function(t, long)
						local reigns = {
							-- if anyone actually makes it to his Honor & Glory Unfailing Persivan I i will be
							-- exceptionally flattered
							{4, 'Emperor', 'Atavarka', 'the Bold'}; -- died at war
							{9, 'Emperor', 'Vatikserka', 'the Unconquered'}; -- died at war
							{22, 'Emperor', 'Rusifend', 'the Wise'}; -- poisoned at diplomacy
							{61, 'Empress', 'Tafseshendi', 'the Great'}; -- died of an 'insurrection of the innards' after a celebrated reign
							{291, 'Emperor', 'Treptebaska', 'the Unwise'}; -- murdered by his wife in short order
							{292, 'Empress', 'Vilintalti', 'the Impious'}; -- removed by the praetorian elite
							{298, 'Emperor', 'Radavan', 'the Reckless'}; -- died at war
							{316, 'Emperor', 'Suldibrand', 'the Forsaken of Men'}; -- fucked around. found out.
							{350, 'Emperor', 'Persivan', 'the Deathless'};
						}
						local year, r = math.floor(t / 414)
						for i=1, #reigns do if reigns[i+1][1] < year then r = reigns[i+1] end end
						local reignBegin, title, name, epithet = lib.tbl.unpack(r)
						local ry = 1 + (year - reignBegin)
						return long and string.format('Year %s of the Reign of HH&GU %s %s %s',
							ry, title, name, epithet) or string.format('Y. %s %s', name, ry)
................................................................................
		end)
	end
	start()
end

starlit.include 'stats'
starlit.include 'world'
starlit.include 'food'
starlit.include 'fab'
starlit.include 'tiers'
starlit.include 'species'

starlit.include 'store'

starlit.include 'ui'
................................................................................
		user.action.bits = bit.bor(user.action.bits, 0x100)
		--return user:trigger('secondary', {state = 'prog', delta = 0})
	elseif pointChanged(oldTgt, point) then
		user:trigger('retarget', {oldTgt = oldTgt})
	end
end
-- sigh
--[[
core.noneitemdef_default.on_place = function(...)
	if not triggerPower(...) then
		minetest.item_place(...)
	end
end
core.noneitemdef_default.on_use           = function(...) triggerPower(...) end
core.noneitemdef_default.on_secondary_use = function(...) triggerPower(...) end
]]
print(dump(core.noneitemdef_default))
minetest.register_item(":", {
	type = "none",
	wield_image = "wieldhand.png",
	wield_scale = {x=1,y=1,z=2.5},
	on_secondary_use = function(...) triggerPower(...) end;
-- 	on_use = function(...) print'base' end;
	after_use = function(...) triggerPower(...) end;
})
minetest.register_item("starlit:_hand_dig", {
	type = "none",
	wield_image = "wieldhand.png",
	wield_scale = {x=1,y=1,z=2.5},
	tool_capabilities = {
		groupcaps = {
			plant = {maxlevel=1, times = {.50,.5,.5}};
			dirt = {maxlevel=1, times = {2.5,1,1}};
		};
	}
})

minetest.register_on_player_inventory_action(function(luser, act, inv, p)
	local name = luser:get_player_name()
	local user = starlit.activeUsers[name]
	-- allow UIs to update on UI changes
	local state = starlit.activeUI[name]
	if state then

Modified mods/starlit/interfaces.lua from [1cb802f20f] to [f0b5b90e43].

298
299
300
301
302
303
304

305
306
307
308
309
310
311
312
313
314
315
316
317

318
319
320
321
322
323
324
...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
						}) end
					end
				end
				local menu = { kind = 'vert', mode = 'sw', padding = 0.5 }
				if swm then table.insert(menu, abilityMenu(swm)) end

				local inv = user.entity:get_inventory()

				local cans = inv:get_list 'starlit_suit_canisters'
				if cans and next(cans) then for i, st in ipairs(cans) do
					local id = string.format('starlit_canister_%u_elem', i)
					local esz = inv:get_size(id)
					if esz > 0 then
						local eltW, eltH = listWrap(esz, 5)
						table.insert(menu, {kind = 'hztl',
							{kind = 'img', desc='Elements', img = 'starlit-ui-icon-element.png', w=1,h=1};
							{kind = 'list', target = 'current_player', inv = id,
								listContent = 'element', w = eltW, h = eltH, spacing = 0.1};
						})
					end
				end end


				if #menu == 0 then
					table.insert(menu, {
						kind = 'img';
						img = 'starlit-ui-alert.png';
						w=2, h=2;
					})
................................................................................
				local tb = {
					kind = 'vert', mode = 'sw';
					padding = 0.5, 
					{kind = 'hztl', padding = 0.25;
						{kind = 'label', text = 'Name', w = 2, h = barh};
						{kind = 'label', text = user.persona.name, w = 4, h = barh}};
				}
				local statBars = {'hunger', 'thirst', 'fatigue', 'morale'}
				for idx, id in ipairs(statBars) do
					local s = starlit.world.stats[id]
					local amt, sv = user:effectiveStat(id)
					local min, max = starlit.world.species.statRange(user.persona.species, user.persona.speciesVariant, id)
					local st = string.format('%s / %s', s.desc(amt, true), s.desc(max))
					table.insert(tb, {kind = 'hztl', padding = 0.25;
						{kind = 'label', w=2, h=barh, text = s.name};







>













>







 







|







298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
...
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
						}) end
					end
				end
				local menu = { kind = 'vert', mode = 'sw', padding = 0.5 }
				if swm then table.insert(menu, abilityMenu(swm)) end

				local inv = user.entity:get_inventory()
				--[[
				local cans = inv:get_list 'starlit_suit_canisters'
				if cans and next(cans) then for i, st in ipairs(cans) do
					local id = string.format('starlit_canister_%u_elem', i)
					local esz = inv:get_size(id)
					if esz > 0 then
						local eltW, eltH = listWrap(esz, 5)
						table.insert(menu, {kind = 'hztl',
							{kind = 'img', desc='Elements', img = 'starlit-ui-icon-element.png', w=1,h=1};
							{kind = 'list', target = 'current_player', inv = id,
								listContent = 'element', w = eltW, h = eltH, spacing = 0.1};
						})
					end
				end end
				]]

				if #menu == 0 then
					table.insert(menu, {
						kind = 'img';
						img = 'starlit-ui-alert.png';
						w=2, h=2;
					})
................................................................................
				local tb = {
					kind = 'vert', mode = 'sw';
					padding = 0.5, 
					{kind = 'hztl', padding = 0.25;
						{kind = 'label', text = 'Name', w = 2, h = barh};
						{kind = 'label', text = user.persona.name, w = 4, h = barh}};
				}
				local statBars = {'hunger', 'thirst', 'fatigue', 'morale', 'irradiation', 'illness'}
				for idx, id in ipairs(statBars) do
					local s = starlit.world.stats[id]
					local amt, sv = user:effectiveStat(id)
					local min, max = starlit.world.species.statRange(user.persona.species, user.persona.speciesVariant, id)
					local st = string.format('%s / %s', s.desc(amt, true), s.desc(max))
					table.insert(tb, {kind = 'hztl', padding = 0.25;
						{kind = 'label', w=2, h=barh, text = s.name};

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

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
..
55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81

82
83
84
85
86
87

88
89
90
91
92

















93
94
95
96
97
98
99
...
108
109
110
111
112
113
114

115
116
117
118
119
120
121
...
160
161
162
163
164
165
166

167
168
169
170
171
172
173

-- constants
local animationFrameRate = 60

local species = {
	human = {
		name = 'Human';
		desc = 'The weeds of the galactic flowerbed. Humans are one of the Lesser Races, excluded from the ranks of the Greatest Races by souls that lack, in normal circumstances, external psionic channels. Their mastery of the universe cut unexpectedly short, forever locked out of FTL travel, short-lived without augments, and alternately pitied or scorned by the lowest of the low, humans flourish nonetheless due to a capacity for adaptation unmatched among the Thinking Few, terrifyingly rapid reproductive cycles -- and a keen facility for bribery. While the lack of human psions remains a sensitive topic, humans (unlike the bitter and emotional Kruthandi) are practical enough to hire the talent they cannot possess, and have even built a small number of symbiotic civilizations with the more indulging of the Powers. In a galaxy where nearly all sophont life is specialized to a fault, humans have found the unique niche of occupying no particular niche.';
		scale = 1.0;
		params = {
			{'eyeColor',  'Eye Color',  'tone', {hue=327, sat=0, lum=0}};
			{'hairColor', 'Hair Color', 'tone', {hue=100, sat=0, lum=0}};
			{'skinTone',  'Skin Tone',  'tone', {hue=  0, sat=0, lum=0}};
		};
		tempRange = {
................................................................................
					morale = 0.8; -- you are not She-Bear Grylls
				};
				traits = {
					health = 400;
					lungCapacity = .6;
					irradiation = 0.8; -- you are smaller, so it takes less rads to kill ya
					sturdiness = 0; -- women are more fragile and thus susceptible to blunt force trauma
					metabolism = 1800; --Cal
					painTolerance = 0.4;

				};
			};
			male = {
				name = 'Human Male';
				eyeHeight = 1.6;
				stats = {
					psiRegen = 1.0;
					psiPower = 1.0;
					psi = 1.0;
					hunger = 1.0;

					staminaRegen = .7; -- men are strong but have inferior endurance
				};
				traits = {
					health = 500;
					painTolerance = 1.0;
					lungCapacity = 1.0;
					sturdiness = 0.3;
					metabolism = 2200; --Cal

				};
			};
		};
		traits = {};
	};
}


starlit.world.species = {
	index = species;
	paramTypes = paramTypes;
}


















function starlit.world.species.mkDefaultParamsTable(pSpecies, pVariant)
	local sp = species[pSpecies]
	local var = sp.variants[pVariant]
	local vpd = var.defaults or {}
	local tbl = {}
	for _, p in pairs(sp.params) do
................................................................................
	return {
		species = pSpecies;
		speciesVariant = pVariant;
		bodyParams = starlit.world.species.paramsFromTable(pSpecies,
			starlit.world.species.mkDefaultParamsTable(pSpecies, pVariant)
		);
		statDeltas = {};

	}
end

local function spLookup(pSpecies, pVariant)
	local sp = species[pSpecies]
	local var = sp.variants[pVariant or next(sp.variants)]
	return sp, var
................................................................................
		local delta = max - min
		return min + delta*p
	end
	local ps = starlit.world.species.mkPersonaFor(pSpecies,pVariant)
	local startingHP = pct('health', 1.0)
	if circumstances.injured    then startingHP = pct('health', circumstances.injured) end
	if circumstances.psiCharged then ps.statDeltas.psi = pct('psi', circumstances.psiCharged) end

	ps.statDeltas.warmth = 20 -- don't instantly start dying of frostbite

	entity:set_properties{hp_max = var.traits.health or sp.traits.health}
	entity:set_hp(startingHP, 'initial hp')
	return ps
end








|







 







|

>










>







|
>






>





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
..
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

-- constants
local animationFrameRate = 60

local species = {
	human = {
		name = 'Human';
		desc = 'The weeds of the galactic flowerbed. Humans are one of the Lesser Races, excluded from the ranks of the Starlit by souls that lack, in normal circumstances, external psionic channels. Their mastery of the universe cut unexpectedly short, forever locked out of FTL travel, short-lived without augments, and alternately pitied or scorned by the lowest of the low, humans flourish nonetheless due to a capacity for adaptation unmatched among the Thinking Few, terrifyingly rapid reproductive cycles -- and a keen facility for bribery. While the lack of human psions remains a sensitive topic, humans (unlike the bitter and emotional Kruthandi) are practical enough to hire the talent they cannot possess, and have even built a small number of symbiotic civilizations with the more indulging of the Powers. In a galaxy where nearly all sophont life is specialized to a fault, humans have found the unique niche of occupying no particular niche.';
		scale = 1.0;
		params = {
			{'eyeColor',  'Eye Color',  'tone', {hue=327, sat=0, lum=0}};
			{'hairColor', 'Hair Color', 'tone', {hue=100, sat=0, lum=0}};
			{'skinTone',  'Skin Tone',  'tone', {hue=  0, sat=0, lum=0}};
		};
		tempRange = {
................................................................................
					morale = 0.8; -- you are not She-Bear Grylls
				};
				traits = {
					health = 400;
					lungCapacity = .6;
					irradiation = 0.8; -- you are smaller, so it takes less rads to kill ya
					sturdiness = 0; -- women are more fragile and thus susceptible to blunt force trauma
					metabolism = 1800e3 / 24 / 60 / 60; --kCal/s
					painTolerance = 0.4;
					dehydration = 3; -- mL/s
				};
			};
			male = {
				name = 'Human Male';
				eyeHeight = 1.6;
				stats = {
					psiRegen = 1.0;
					psiPower = 1.0;
					psi = 1.0;
					hunger = 1.0;
					thirst = 1.0;
					staminaRegen = .7; -- men are strong but have inferior endurance
				};
				traits = {
					health = 500;
					painTolerance = 1.0;
					lungCapacity = 1.0;
					sturdiness = 0.3;
					metabolism = 2200e3 / 24 / 60 / 60; --Cal/s
					dehydration = 5; -- mL/s
				};
			};
		};
		traits = {};
	};
}


starlit.world.species = {
	index = species;
	paramTypes = paramTypes;
}

starlit.world.species.pheno = lib.class {
	name = 'starlit:species.pheno';
	construct = function(pSp, pVar)
		local sp, var = starlit.world.species.lookup(pSp, pVar)
		return {
			species = sp, variant = var;
			pSpecies = pSp, pVariant = pVar;
		};
	end;
	__index = {
		trait = function(me, st, dflt)
			local v = me.variant.traits[st] or me.species.traits[st]
			return v or dflt
		end;
	};
}

function starlit.world.species.mkDefaultParamsTable(pSpecies, pVariant)
	local sp = species[pSpecies]
	local var = sp.variants[pVariant]
	local vpd = var.defaults or {}
	local tbl = {}
	for _, p in pairs(sp.params) do
................................................................................
	return {
		species = pSpecies;
		speciesVariant = pVariant;
		bodyParams = starlit.world.species.paramsFromTable(pSpecies,
			starlit.world.species.mkDefaultParamsTable(pSpecies, pVariant)
		);
		statDeltas = {};
		facts = {};
	}
end

local function spLookup(pSpecies, pVariant)
	local sp = species[pSpecies]
	local var = sp.variants[pVariant or next(sp.variants)]
	return sp, var
................................................................................
		local delta = max - min
		return min + delta*p
	end
	local ps = starlit.world.species.mkPersonaFor(pSpecies,pVariant)
	local startingHP = pct('health', 1.0)
	if circumstances.injured    then startingHP = pct('health', circumstances.injured) end
	if circumstances.psiCharged then ps.statDeltas.psi = pct('psi', circumstances.psiCharged) end
	for k,v in pairs(starlit.world.stats) do ps.statDeltas[k] = 0 end
	ps.statDeltas.warmth = 20 -- don't instantly start dying of frostbite

	entity:set_properties{hp_max = var.traits.health or sp.traits.health}
	entity:set_hp(startingHP, 'initial hp')
	return ps
end

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

1
2
3

4
5

6
7
8
9
10

11
12
13
14
15
16
17
18
19
..
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
local lib = starlit.mod.lib

local function U(unit, prec, fixed)

	if fixed then
		return function(amt, excludeUnit)

			if excludeUnit then return tostring(amt/prec) end
			return string.format("%s %s", amt/prec, unit)
		end
	else
		return function(amt, excludeUnit)

			if excludeUnit then return tostring(amt/prec) end
			return lib.math.si(unit, amt/prec)
		end
	end
end

local function C(h, s, l)
	return lib.color {hue = h, sat = s or 1, lum = l or .7}
end
................................................................................
	-- numina is measured in daψ
	warmth     = {min = -1000, max = 1000, base = 0, desc = U('°C', 10, true), color = C(5), name = 'Warmth'};
	-- warmth in measured in °C×10
	fatigue    = {min = 0, max = 76 * 60, base = 0, desc = U('hr', 60, true), color = C(288,.3,.5), name = 'Fatigue'};
	-- fatigue is measured in minutes one needs to sleep to cure it
	stamina    = {min = 0, max = 20 * 100, base = true, desc = U('m', 100), color = C(88), name = 'Stamina'};
	-- stamina is measured in how many 10th-nodes (== cm) one can sprint
	hunger     = {min = 0, max = 20000, base = 0, desc = U('Cal', 1), color = C(43,.5,.4), name = 'Hunger'};
	-- hunger is measured in calories one must consume to cure it
	thirst     = {min = 0, max = 1600, base = 0, desc = U('l', 100), color = C(217, .25,.4), name = 'Thirst'};
	-- thirst is measured in centiliters of H²O required to cure it
	morale     = {min = 0, max = 24 * 60 * 10, base = true, desc = U('hr', 60, true), color = C(0,0,.8), name = 'Morale'};
	-- morale is measured in minutes. e.g. at base rate morale degrades by
	-- 60 points every hour. morale can last up to 10 days
	irradiation = {min = 0, max = 20000, base = 0, desc = U('Gy', 1000), color = C(141,1,.5), name = 'Irradiation'};
	-- irrad is measured is milligreys
	-- 1Gy counters natural healing
	-- ~3Gy counters basic nanomedicine



>


>
|
|



>
|
|







 







|
|
|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
local lib = starlit.mod.lib

local function U(unit, prec, fixed)
	local trunc = 2
	if fixed then
		return function(amt, excludeUnit)
			local ta = lib.math.trim(amt/prec, trunc)
			if excludeUnit then return tostring(ta) end
			return string.format("%s %s", ta, unit)
		end
	else
		return function(amt, excludeUnit)
			local ta = lib.math.trim(amt/prec, trunc)
			if excludeUnit then return tostring(ta) end
			return lib.math.si(unit, amt/prec, nil, nil, trunc)
		end
	end
end

local function C(h, s, l)
	return lib.color {hue = h, sat = s or 1, lum = l or .7}
end
................................................................................
	-- numina is measured in daψ
	warmth     = {min = -1000, max = 1000, base = 0, desc = U('°C', 10, true), color = C(5), name = 'Warmth'};
	-- warmth in measured in °C×10
	fatigue    = {min = 0, max = 76 * 60, base = 0, desc = U('hr', 60, true), color = C(288,.3,.5), name = 'Fatigue'};
	-- fatigue is measured in minutes one needs to sleep to cure it
	stamina    = {min = 0, max = 20 * 100, base = true, desc = U('m', 100), color = C(88), name = 'Stamina'};
	-- stamina is measured in how many 10th-nodes (== cm) one can sprint
	hunger     = {min = 0, max = 2000e3, base = 0, desc = U('kCal', 1000, true), color = C(43,.5,.4), name = 'Hunger'};
	-- hunger is measured in calories one must consume to cure it. at a 2kCal deficit, you start dying
	thirst     = {min = 0, max = 4e3, base = 0, desc = U('L', 1e3), color = C(217, .25,.4), name = 'Thirst'};
	-- thirst is measured in mL of H²O required to cure it
	morale     = {min = 0, max = 24 * 60 * 10, base = true, desc = U('hr', 60, true), color = C(0,0,.8), name = 'Morale'};
	-- morale is measured in minutes. e.g. at base rate morale degrades by
	-- 60 points every hour. morale can last up to 10 days
	irradiation = {min = 0, max = 20000, base = 0, desc = U('Gy', 1000), color = C(141,1,.5), name = 'Irradiation'};
	-- irrad is measured is milligreys
	-- 1Gy counters natural healing
	-- ~3Gy counters basic nanomedicine

Modified mods/starlit/store.lua from [73c6f814ce] to [9cc49bcaa7].

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
47
48
49
50
51
52
53






local T,G = lib.marshal.t, lib.marshal.g
starlit.store = {} -- the serialization equivalent of .type

-------------
-- persona --
------------- -----------------------------------------------
-- a Persona is a structure that defines the nature of     --
-- an (N)PC and how it interacts with the Starsoul-managed --
-- portion of the game world -- things like name, species, --
-- stat values, physical characteristics, and so forth     --

local statStructFields = {}
for k,v in pairs(starlit.world.stats) do
	statStructFields[k] = v.srzType or (
		(v.base == true or v.base > 0) and T.s16 or T.u16
................................................................................
starlit.store.suitMeta = lib.marshal.metaStore {
	batteries = {key = 'starlit:suit_slots_bat', type = T.inventoryList};
	chips = {key = 'starlit:suit_slots_chips', type = T.inventoryList};
	elements = {key = 'starlit:suit_slots_elem', type = T.inventoryList};
	guns = {key = 'starlit:suit_slots_gun', type = T.inventoryList};
	ammo = {key = 'starlit:suit_slots_ammo', type = T.inventoryList};
}













|







 







>
>
>
>
>
>
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
47
48
49
50
51
52
53
54
55
56
57
58
59
local T,G = lib.marshal.t, lib.marshal.g
starlit.store = {} -- the serialization equivalent of .type

-------------
-- persona --
------------- -----------------------------------------------
-- a Persona is a structure that defines the nature of     --
-- an (N)PC and how it interacts with the Starlit-managed  --
-- portion of the game world -- things like name, species, --
-- stat values, physical characteristics, and so forth     --

local statStructFields = {}
for k,v in pairs(starlit.world.stats) do
	statStructFields[k] = v.srzType or (
		(v.base == true or v.base > 0) and T.s16 or T.u16
................................................................................
starlit.store.suitMeta = lib.marshal.metaStore {
	batteries = {key = 'starlit:suit_slots_bat', type = T.inventoryList};
	chips = {key = 'starlit:suit_slots_chips', type = T.inventoryList};
	elements = {key = 'starlit:suit_slots_elem', type = T.inventoryList};
	guns = {key = 'starlit:suit_slots_gun', type = T.inventoryList};
	ammo = {key = 'starlit:suit_slots_ammo', type = T.inventoryList};
}

starlit.store.volume = G.struct {
	kind = T.str;
	id = T.str;
	mass = T.decimal;
}

Modified mods/starlit/suit.lua from [7112e6c94b] to [7f11ba51a8].

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
..
86
87
88
89
90
91
92

93
94
95
96
97
98
99
...
108
109
110
111
112
113
114

115
116
117
118

119
120
121
122
123
124
125
126
127
128
...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
			return w
		end;
		onReconfigure = function(self, inv)
			-- apply any changes to item metadata and export any subinventories
			-- to the provided invref, as they may have changed
			local sc = starlit.item.container(self.item, inv, {pfx = 'starlit_suit'})
			sc:push()
			self:pullCanisters(inv)
		end;
		onItemMove = function(self, user, list, act, what)
			-- called when the suit inventory is changed
			if act == 'put' then
				if list == 'starlit_suit_bat' then
					user:suitSound('starlit-suit-battery-in')
				elseif list == 'starlit_suit_chips' then
................................................................................
					user:suitSound('starlit-insert-snap')
				end
			end
		end;
		def = function(self)
			return self.item:get_definition()._starlit.suit
		end;

		pullCanisters = function(self, inv)
			starlit.item.container.dropPrefix(inv, 'starlit_canister')
			self:forCanisters(inv, function(sc) sc:pull() end)
		end;
		pushCanisters = function(self, inv, st, i)
			self:forCanisters(inv, function(sc)
				sc:push()
................................................................................
					local sc = starlit.item.container(st, inv, {pfx = pfx})
					if fn(sc, st, i, pfx) then
						inv:set_stack('starlit_suit_canisters', i, st)
					end
				end
			end end
		end;

		establishInventories = function(self, obj)
			local inv = obj:get_inventory()
			local ct = suitContainer(self.item, inv)
			ct:pull()

			self:pullCanisters(inv)

			--[[
			local def = self:def()
			local sst = suitStore(self.item)
			local function readList(listName, prop)
				inv:set_size(listName, def.slots[prop])
				if def.slots[prop] > 0 then
					local lst = sst.read(prop)
					inv:set_list(listName, lst)
................................................................................
		on_use = function(st, luser, pointed)
			local user = starlit.activeUsers[luser:get_player_name()]
			if not user then return end
			-- have mercy on users who've lost their suits and wound
			-- up naked and dying of exposure
			if user:naked() then
				local ss = st:take_item(1)
				user:setSuit(starlit.type.suit(ss))
				user:suitSound('starlit-suit-don')
				return st
			end
		end;
		inventory_image = icon:render();
		_starlit = {
			container = {







|







 







>







 







>




>


<







 







|







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
			return w
		end;
		onReconfigure = function(self, inv)
			-- apply any changes to item metadata and export any subinventories
			-- to the provided invref, as they may have changed
			local sc = starlit.item.container(self.item, inv, {pfx = 'starlit_suit'})
			sc:push()
-- 			self:pullCanisters(inv)
		end;
		onItemMove = function(self, user, list, act, what)
			-- called when the suit inventory is changed
			if act == 'put' then
				if list == 'starlit_suit_bat' then
					user:suitSound('starlit-suit-battery-in')
				elseif list == 'starlit_suit_chips' then
................................................................................
					user:suitSound('starlit-insert-snap')
				end
			end
		end;
		def = function(self)
			return self.item:get_definition()._starlit.suit
		end;
		--[[
		pullCanisters = function(self, inv)
			starlit.item.container.dropPrefix(inv, 'starlit_canister')
			self:forCanisters(inv, function(sc) sc:pull() end)
		end;
		pushCanisters = function(self, inv, st, i)
			self:forCanisters(inv, function(sc)
				sc:push()
................................................................................
					local sc = starlit.item.container(st, inv, {pfx = pfx})
					if fn(sc, st, i, pfx) then
						inv:set_stack('starlit_suit_canisters', i, st)
					end
				end
			end end
		end;
		]]
		establishInventories = function(self, obj)
			local inv = obj:get_inventory()
			local ct = suitContainer(self.item, inv)
			ct:pull()
			--[[
			self:pullCanisters(inv)


			local def = self:def()
			local sst = suitStore(self.item)
			local function readList(listName, prop)
				inv:set_size(listName, def.slots[prop])
				if def.slots[prop] > 0 then
					local lst = sst.read(prop)
					inv:set_list(listName, lst)
................................................................................
		on_use = function(st, luser, pointed)
			local user = starlit.activeUsers[luser:get_player_name()]
			if not user then return end
			-- have mercy on users who've lost their suits and wound
			-- up naked and dying of exposure
			if user:naked() then
				local ss = st:take_item(1)
				user:changeSuit(starlit.type.suit(ss))
				user:suitSound('starlit-suit-don')
				return st
			end
		end;
		inventory_image = icon:render();
		_starlit = {
			container = {

Modified mods/starlit/terrain.lua from [5a8b3b76d0] to [b5b4e3205a].

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
			def.img .. '.png';
			'default_dirt.png';
			{
				name = 'default_dirt.png^' .. def.img ..'_side.png';
				tileable_vertical = false;
			};
		};
		groups = {grass = 1, sub_walk = 1};
		drop = '';
		sounds = grassSounds;
		_starlit = grassfst(2);
	})
	for i=2,0,-1 do
		local opacity = tostring((i/2.0) * 255)








|







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
			def.img .. '.png';
			'default_dirt.png';
			{
				name = 'default_dirt.png^' .. def.img ..'_side.png';
				tileable_vertical = false;
			};
		};
		groups = {grass = 1, dirt = 1, sub_walk = 1};
		drop = '';
		sounds = grassSounds;
		_starlit = grassfst(2);
	})
	for i=2,0,-1 do
		local opacity = tostring((i/2.0) * 255)

Modified mods/starlit/user.lua from [aee7410825] to [43bf528964].

63
64
65
66
67
68
69

70
71
72
73
74
75
76
..
96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
...
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
...
375
376
377
378
379
380
381










382
383
384
385
386
387
388
389
390
391
392

393
394
395
396
397
398
399
...
415
416
417
418
419
420
421

422
423
424
425
426
427
428
...
499
500
501
502
503
504
505

506
507
508
509
510
511
512
...
781
782
783
784
785
786
787

788
789
790
791
792
793
794
795
796
797
798

799
800
801
802
803
804
805
806
807
808








809
810
811
812
813
814



815






816
817
818
819
820
821
822
	end;
	__index = {
		pullPersona = function(self)
			-- if later records are added in public updates, extend this function to merge them
			-- into one object
			local s = userStore(self.entity)
			self.persona = s.read 'persona'

		end;
		pushPersona = function(self)
			local s = userStore(self.entity)
			s.write('persona', self.persona)
		end;
		uiColor = function(self) return lib.color {hue=238,sat=.5,lum=.5} end;
		statDelta = function(self, stat, d, cause, abs)
................................................................................
			end
			self:updateHUD()
			-- TODO trigger relevant animations?
		end;
		lookupSpecies = function(self)
			return starlit.world.species.lookup(self.persona.species, self.persona.speciesVariant)
		end;
		phenoTrait = function(self, trait)
			local s,v = self:lookupSpecies()
			return v.traits[trait] or s.traits[trait] or 0

		end;
		statRange = function(self, stat) --> min, max, base
			return starlit.world.species.statRange(
				self.persona.species, self.persona.speciesVariant, stat)
		end;
		effectiveStat = function(self, stat)
			local val
................................................................................
					local hot = self:effectiveStat 'irradiation'
					local color = self:uiColor():lerp(lib.color(0.3, 1, 0), math.min(1, hot/5))
					local txt = string.format("%sGy", math.floor(hot))
					return (hot/5), txt, color
				end;
			}
			self.hud.elt.crosshair = self:attachImage {
				name = 'crosshair ';
				tex = '';
				pos = {x=.5, y=.5};
				scale = {x=1,y=1};
				ofs = {x=0, y=0};
				align = {x=0, y=0};
				update = function(user, set)
					local imgs = {
................................................................................
				align = {x=0, y=-1};
				z = -1;
				update = function(user, set)
					set('text', hudAdjustBacklight(hudCenterBG):render())
				end;
			};
		end;










		onModeChange = function(self, oldMode, silent)
			self.hud.elt.crosshair.update()
			if not silent then
				local sfxt = {
					off = 'starlit-mode-off';
					nano = 'starlit-mode-nano';
					psi = 'starlit-mode-psi';
					weapon = 'starlit-mode-weapon';
				}
				local sfx = self.actMode and sfxt[self.actMode] or sfxt.off
				self:suitSound(sfx)

			end
		end;
		actModeSet = function(self, mode, silent)
			if not mode then mode = 'off' end
			local oldMode = self.actMode
			self.actMode = mode
			self:onModeChange(oldMode, silent)
................................................................................
			return minetest.get_player_information(self.name)
		end;
		onSignup = function(self)
			local meta = self.entity:get_meta()
			local inv = self.entity:get_inventory()
			-- the sizes indicated here are MAXIMA. limitations on e.g. the number of elements that may be carried are defined by your suit and enforced through callbacks and UI generation code, not inventory size
			inv:set_size('main', 6) -- carried items and tools. main hotbar.


			inv:set_size('starlit_suit', 1) -- your environment suit (change at wardrobe)
			inv:set_size('starlit_cfg', 1) -- the item you're reconfiguring / container you're accessing

			local scenario
			for _, e in pairs(starlit.world.scenario) do
				if e.id == starlit.world.defaultScenario then
................................................................................
			self:updateSuit()
			return true
		end;
		onJoin = function(self)
			local me = self.entity
			local meta = me:get_meta()
			self:pullPersona()


			-- formspec_version and real_coordinates are apparently just
			-- completely ignored here
			me:set_formspec_prepend [[
				bgcolor[#00000000;true]
				style_type[button,button_exit,image_button,item_image_button;border=false]
				style_type[button;bgimg=starlit-ui-button-hw.png;bgimg_middle=8;content_offset=0,-2]
................................................................................
		give = function(self, item)
			local inv = self.entity:get_inventory()
			local function is(grp)
				return minetest.get_item_group(item:get_name(), grp) ~= 0
			end
			-- TODO notif popups
			if is 'specialInventory' then

				if is 'powder' then
					if self:naked() then return item end
					local cans = inv:get_list 'starlit_suit_canisters'
					if cans and next(cans) then for i, st in ipairs(cans) do
						local lst = string.format('starlit_canister_%u_elem', i)
						item = inv:add_item(lst, item)
						if item:is_empty() then break end
					end end
					self:forSuit(function(x) x:pushCanisters(inv) end)
				end
				return item

			else
				return inv:add_item('main', item)
			end
		end;
		thrustUpon = function(self, item)
			local r = self:give(st)
			if not r:is_empty() then
				return minetest.add_item(self.entity:get_pos(), r)
			end
		end;








	};
}

local biointerval = 3.0
starlit.startJob('starlit:bio', biointerval, function(delta)
	for id, u in pairs(starlit.activeUsers) do










	end
end)

local cbit = {
	up   = 0x001;
	down = 0x002;
	left = 0x004;







>







 







|
|
|
>







 







|







 







>
>
>
>
>
>
>
>
>
>











>







 







>







 







>







 







>











>










>
>
>
>
>
>
>
>






>
>
>

>
>
>
>
>
>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
...
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
...
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
...
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
	end;
	__index = {
		pullPersona = function(self)
			-- if later records are added in public updates, extend this function to merge them
			-- into one object
			local s = userStore(self.entity)
			self.persona = s.read 'persona'
			self.pheno = starlit.world.species.pheno(self.persona.species, self.persona.speciesVariant)
		end;
		pushPersona = function(self)
			local s = userStore(self.entity)
			s.write('persona', self.persona)
		end;
		uiColor = function(self) return lib.color {hue=238,sat=.5,lum=.5} end;
		statDelta = function(self, stat, d, cause, abs)
................................................................................
			end
			self:updateHUD()
			-- TODO trigger relevant animations?
		end;
		lookupSpecies = function(self)
			return starlit.world.species.lookup(self.persona.species, self.persona.speciesVariant)
		end;
		phenoTrait = function(self, trait, dflt)
-- 			local s,v = self:lookupSpecies()
-- 			return v.traits[trait] or s.traits[trait] or 0
			return self.pheno:trait(trait, dflt)
		end;
		statRange = function(self, stat) --> min, max, base
			return starlit.world.species.statRange(
				self.persona.species, self.persona.speciesVariant, stat)
		end;
		effectiveStat = function(self, stat)
			local val
................................................................................
					local hot = self:effectiveStat 'irradiation'
					local color = self:uiColor():lerp(lib.color(0.3, 1, 0), math.min(1, hot/5))
					local txt = string.format("%sGy", math.floor(hot))
					return (hot/5), txt, color
				end;
			}
			self.hud.elt.crosshair = self:attachImage {
				name = 'crosshair';
				tex = '';
				pos = {x=.5, y=.5};
				scale = {x=1,y=1};
				ofs = {x=0, y=0};
				align = {x=0, y=0};
				update = function(user, set)
					local imgs = {
................................................................................
				align = {x=0, y=-1};
				z = -1;
				update = function(user, set)
					set('text', hudAdjustBacklight(hudCenterBG):render())
				end;
			};
		end;
		-- horrible horrible HACK
		setModeHand = function(self)
			local inv = self.entity:get_inventory()
			local hnd
			if self.actMode == 'off'
				then hnd = ItemStack('starlit:_hand_dig')
				else hnd = ItemStack()
			end
			inv:set_stack('hand', 1, hnd)
		end;
		onModeChange = function(self, oldMode, silent)
			self.hud.elt.crosshair.update()
			if not silent then
				local sfxt = {
					off = 'starlit-mode-off';
					nano = 'starlit-mode-nano';
					psi = 'starlit-mode-psi';
					weapon = 'starlit-mode-weapon';
				}
				local sfx = self.actMode and sfxt[self.actMode] or sfxt.off
				self:suitSound(sfx)
				self:setModeHand()
			end
		end;
		actModeSet = function(self, mode, silent)
			if not mode then mode = 'off' end
			local oldMode = self.actMode
			self.actMode = mode
			self:onModeChange(oldMode, silent)
................................................................................
			return minetest.get_player_information(self.name)
		end;
		onSignup = function(self)
			local meta = self.entity:get_meta()
			local inv = self.entity:get_inventory()
			-- the sizes indicated here are MAXIMA. limitations on e.g. the number of elements that may be carried are defined by your suit and enforced through callbacks and UI generation code, not inventory size
			inv:set_size('main', 6) -- carried items and tools. main hotbar.
			inv:set_size('hand', 1) -- horrible hack to allow both tools and intrinsics

			inv:set_size('starlit_suit', 1) -- your environment suit (change at wardrobe)
			inv:set_size('starlit_cfg', 1) -- the item you're reconfiguring / container you're accessing

			local scenario
			for _, e in pairs(starlit.world.scenario) do
				if e.id == starlit.world.defaultScenario then
................................................................................
			self:updateSuit()
			return true
		end;
		onJoin = function(self)
			local me = self.entity
			local meta = me:get_meta()
			self:pullPersona()
			self:setModeHand()

			-- formspec_version and real_coordinates are apparently just
			-- completely ignored here
			me:set_formspec_prepend [[
				bgcolor[#00000000;true]
				style_type[button,button_exit,image_button,item_image_button;border=false]
				style_type[button;bgimg=starlit-ui-button-hw.png;bgimg_middle=8;content_offset=0,-2]
................................................................................
		give = function(self, item)
			local inv = self.entity:get_inventory()
			local function is(grp)
				return minetest.get_item_group(item:get_name(), grp) ~= 0
			end
			-- TODO notif popups
			if is 'specialInventory' then
			--[[
				if is 'powder' then
					if self:naked() then return item end
					local cans = inv:get_list 'starlit_suit_canisters'
					if cans and next(cans) then for i, st in ipairs(cans) do
						local lst = string.format('starlit_canister_%u_elem', i)
						item = inv:add_item(lst, item)
						if item:is_empty() then break end
					end end
					self:forSuit(function(x) x:pushCanisters(inv) end)
				end
				return item
				]]
			else
				return inv:add_item('main', item)
			end
		end;
		thrustUpon = function(self, item)
			local r = self:give(st)
			if not r:is_empty() then
				return minetest.add_item(self.entity:get_pos(), r)
			end
		end;
		consume = function(self, stack, n)
			n = n or 1
			if n == 0 then n = stack:get_count() end
			local fd = stack:take_item(n)
			local stats = starlit.world.food.effectiveStats(fd)

			return stack
		end;
	};
}

local biointerval = 3.0
starlit.startJob('starlit:bio', biointerval, function(delta)
	for id, u in pairs(starlit.activeUsers) do
		local p = u.pheno
		local bmr = p:trait 'metabolism' * biointerval
		-- TODO apply modifiers

		local dehydration = p:trait 'dehydration' * biointerval
		-- you dehydrate faster in higher temp
		dehydration = dehydration * math.max(1, starlit.world.climate.temp(u.entity:get_pos()) / 10)

		u:statDelta('hunger', bmr)
		u:statDelta('thirst', dehydration)
	end
end)

local cbit = {
	up   = 0x001;
	down = 0x002;
	left = 0x004;

Modified mods/starlit/world.lua from [830720f731] to [d9bc181e37].

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106






107
108

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124



























125
126
127
128
129
130
131
end

world.ecology.biomes.foreach('starlit:biome-gen', {}, function(id, b)
	b.def.name = id
	minetest.register_biome(b.def)
end)

world.ecology.biomes.link('starlit:steppe', {
	nightTempDelta = -30;
	waterTempDelta = 0;
	--               W    Sp   Su    Au   W
	seasonalTemp = {-50, -10, 5, 5, -20, -50};
	def = {
		node_top      = 'starlit:greengraze', depth_top = 1;
		node_filler   = 'starlit:soil',    depth_filler = 4;
		node_riverbed = 'starlit:sand',  depth_riverbed = 4;
		y_min = 0;
		y_max = 512;






		heat_point = 10;
		humidity_point = 30;

	};
})
	
world.ecology.biomes.link('starlit:ocean', {
	nightTempDelta = -35;
	waterTempDelta = 5;
	seasonalTemp = {0}; -- no seasonal variance
	def = {
		y_max = 3;
		y_min = -512;
		heat_point = 15;
		humidity_point = 50;
		node_top    = 'starlit:sand', depth_top    = 1;
		node_filler = 'starlit:sand', depth_filler = 3;
	};
})




























local toward = lib.math.toward
local hfinterval = 1.5
starlit.startJob('starlit:heatflow', hfinterval, function(delta)

	-- our base thermal conductivity (κ) is measured in °C/°C/s. say the
	-- player is in -30°C weather, and has an internal temperature of







|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
|
<
>
|
<
<
<
<
<
<
|
|
|
|
<
<
<
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

114
115






116
117
118
119



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
end

world.ecology.biomes.foreach('starlit:biome-gen', {}, function(id, b)
	b.def.name = id
	minetest.register_biome(b.def)
end)

world.ecology.plants.foreach('starlit:plant-gen', {}, function(id, b)
	local stageCt = #b.stages
	local function stageID(n)
		if n == stageCt then return id end
		return id .. string.format('_stage_%s', n)
	end
	b.stageNodes = {}
	local function regStage(n, st)
		local base = {
			description = b.name;
			drawtype = "plantlike";
			tiles = { tostring(st.tex) };
			paramtype = "light";
			paramtype2 = "meshoptions";
			walkable = false;
			buildable_to = true;
			groups = {
				plant = 1;

				plant_grow = stageCt ~= n and 1 or 0;
			};






			drop = st.drop;
			_starlit = {
				plant = {
					id = id, stage = n;



				};
			};
		}
		if st.swap then
			base.node_dig_prediction = stageID(st.swap)
			function base.on_dig(pos, node, digger)
				node.name = stageID(st.swap)
				minetest.swap_node(pos, node)
				return true
			end
		end
		return base
	end
	for i, v in ipairs(b.stages) do
		local n = regStage(i, v)
		b.stageNodes[i] = n
		minetest.register_node(stageID(i), n)
	end
	b.fullyGrown = stageID(stageCt)

	local dec = {
		deco_type = 'simple';
		decoration = b.fullyGrown;
		height = 1;
		param2 = 0;
	}
	for k,v in pairs(b.decoration) do dec[k] = v end
	b.decoration = minetest.register_decoration(dec)
end)

local toward = lib.math.toward
local hfinterval = 1.5
starlit.startJob('starlit:heatflow', hfinterval, function(delta)

	-- our base thermal conductivity (κ) is measured in °C/°C/s. say the
	-- player is in -30°C weather, and has an internal temperature of

Modified mods/vtlib/image.lua from [479480cc25] to [9575362639].

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
					str = '(' .. i.img:render() .. ')^' .. str
				end
				if str ~= '' then
					str = str .. '('
					bracket = true
				end
				str = str .. self.string
				end
			for _,e in pairs(self.fx) do
				str = str .. '^[' .. e
				-- be sure to escape ones that take arguments
				-- correctly!
			end
			if bracket then str = str .. ')' end
			return str
................................................................................
					color = lib.color(color)
				end
				color = color:to_hsl()
			end
			return image.change(self, {
				fx = lib.tbl.append(self.fx, {
					string.format('hsl:%s:%s:%s',
						color.hue, color.sat*100, color.lum*100)
				})
			})
		end;

		rehue = function(self, hue)
			return self.shift{hue=hue, sat=0, lum=0}
		end;







|







 







|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
					str = '(' .. i.img:render() .. ')^' .. str
				end
				if str ~= '' then
					str = str .. '('
					bracket = true
				end
				str = str .. self.string
			end
			for _,e in pairs(self.fx) do
				str = str .. '^[' .. e
				-- be sure to escape ones that take arguments
				-- correctly!
			end
			if bracket then str = str .. ')' end
			return str
................................................................................
					color = lib.color(color)
				end
				color = color:to_hsl()
			end
			return image.change(self, {
				fx = lib.tbl.append(self.fx, {
					string.format('hsl:%s:%s:%s',
						color.hue, color.sat, color.lum)
				})
			})
		end;

		rehue = function(self, hue)
			return self.shift{hue=hue, sat=0, lum=0}
		end;

Modified mods/vtlib/marshal.lua from [ec9d9f2682] to [670a4be42e].

201
202
203
204
205
206
207

208
209
210
211
212
213
214
	report('defining struct name=%q fields=%s', name, dump(def))
	return {
		name = name;
		enc = function(obj)
			local enc = m.streamEncoder()
			local n = 0
			for k,ty in pairs(def) do n=n+1

				local encoded = ty.enc(obj[k])
				enc.push(T.u8.enc(#k), size.enc(#encoded), k, encoded)
			end
			return size.enc(n) .. enc.peek()
		end;
		dec = debugger.wrap(function(blob)
			if blob == '' then







>







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
	report('defining struct name=%q fields=%s', name, dump(def))
	return {
		name = name;
		enc = function(obj)
			local enc = m.streamEncoder()
			local n = 0
			for k,ty in pairs(def) do n=n+1
				if obj[k] == nil then error('missing key '..dump(k)..' for type '..ty.name) end
				local encoded = ty.enc(obj[k])
				enc.push(T.u8.enc(#k), size.enc(#encoded), k, encoded)
			end
			return size.enc(n) .. enc.peek()
		end;
		dec = debugger.wrap(function(blob)
			if blob == '' then

Modified mods/vtlib/math.lua from [28fc5b216f] to [988785061c].

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
45
46
47
48
49
50
51


52
53
54
55
56
57
58
59


60
61
62
63
64
65
66
67
68
69
	return dsq / (dist^2)
	-- [0,1) == less then
	-- 1 == equal
	-- >1 == greater than
end

-- produce an SI expression for a quantity
fn.si = function(unit, val, full, uncommonScales)
	if val == 0 then return '0 ' .. unit end
	local scales = {
		{30, 'Q', 'quetta',true,  'q', 'quecto',true};
		{27, 'R', 'ronna', true,  'r', 'ronto', true};
		{24, 'Y', 'yotta', true,  'y', 'yocto', true};
		{21, 'Z', 'zetta', true,  'z', 'zepto', true};
		{18, 'E', 'exa',   true,  'a', 'atto',  true};
................................................................................
	for i, s in ipairs(scales) do
		local amt, smaj, pmaj, cmaj,
		           smin, pmin, cmin = lib.tbl.unpack(s)

		if math.abs(val) > 1 then
			if uncommonScales or cmaj then
				local denom = 10^amt


				if math.abs(val) >= (10^(amt)) then
					return string.format("%s %s%s",
						val / denom, (full and pmaj or smaj), unit)
				end
			end
		elseif math.abs(val) < 1 then
			if uncommonScales or cmin then
				local denom = 10^-amt


				if math.abs(val) <= (10^-(amt-1)) then
					return string.format("%s %s%s",
						val / denom, (full and pmin or smin), unit)
				end
			end
		end
	end

	return string.format("%s %s", val, unit)
end







|







 







>
>


|





>
>


|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
	return dsq / (dist^2)
	-- [0,1) == less then
	-- 1 == equal
	-- >1 == greater than
end

-- produce an SI expression for a quantity
fn.si = function(unit, val, full, uncommonScales, prec)
	if val == 0 then return '0 ' .. unit end
	local scales = {
		{30, 'Q', 'quetta',true,  'q', 'quecto',true};
		{27, 'R', 'ronna', true,  'r', 'ronto', true};
		{24, 'Y', 'yotta', true,  'y', 'yocto', true};
		{21, 'Z', 'zetta', true,  'z', 'zepto', true};
		{18, 'E', 'exa',   true,  'a', 'atto',  true};
................................................................................
	for i, s in ipairs(scales) do
		local amt, smaj, pmaj, cmaj,
		           smin, pmin, cmin = lib.tbl.unpack(s)

		if math.abs(val) > 1 then
			if uncommonScales or cmaj then
				local denom = 10^amt
				local vd = val/denom
				if prec then vd = lib.math.trim(vd, prec) end
				if math.abs(val) >= (10^(amt)) then
					return string.format("%s %s%s",
						vd, (full and pmaj or smaj), unit)
				end
			end
		elseif math.abs(val) < 1 then
			if uncommonScales or cmin then
				local denom = 10^-amt
				local vd = val/denom
				if prec then vd = lib.math.trim(vd, prec) end
				if math.abs(val) <= (10^-(amt-1)) then
					return string.format("%s %s%s",
						vd, (full and pmin or smin), unit)
				end
			end
		end
	end

	return string.format("%s %s", val, unit)
end