starlit  Check-in [511814aace]

Overview
Comment:add primitive thermal hazard LEDs; further documentation
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 511814aace3b6e9dd57ec362b2f17c7092904b5e739530038bc6261790f214a9
User & Date: lexi on 2024-05-03 21:00:04
Other Links: manifest | tags
Context
2024-05-04
02:18
fix colors, add grass check-in: 9d4ddb7701 user: lexi tags: trunk
2024-05-03
21:00
add primitive thermal hazard LEDs; further documentation check-in: 511814aace user: lexi tags: trunk
15:31
genericize impacts check-in: 467a75e0dc user: lexi tags: trunk
Changes

Modified mods/starlit/init.lua from [435c654ad9] to [d6e7e6c59c].

    28     28   			brightest = 14; -- only sun and growlights
    29     29   		};
    30     30   		heat = { -- celsius
    31     31   			freezing = 0;
    32     32   			safe = 4;
    33     33   			overheat = 32;
    34     34   			boiling = 100;
           35  +			thermalConductivity = 0.05; -- κ
    35     36   		};
    36     37   		rad = {
    37     38   		};
    38     39   	};
    39     40   
    40     41   	activeUsers = {
    41     42   		-- map of username -> user object

Modified mods/starlit/user.lua from [788077fd8e] to [7074af2318].

    37     37   		end
    38     38   		return {
    39     39   			entity = luser;
    40     40   			name = name;
    41     41   			hud = {
    42     42   				elt = {};
    43     43   				bar = {};
           44  +				alarm = {};
    44     45   			};
    45     46   			tree = {};
    46     47   			action = {
    47     48   				bits = 0; -- for control deltas
    48     49   				prog = {}; -- for recording action progress on a node; reset on refocus
    49     50   				tgt = {type='nothing'};
    50     51   				sfx = {};
................................................................................
    59     60   			};
    60     61   			pref = {
    61     62   				calendar = 'commune';
    62     63   			};
    63     64   			overlays = {};
    64     65   			cooldownTimes = {
    65     66   				stamina = 0;
           67  +				alarm = 0;
    66     68   			};
    67     69   		}
    68     70   	end;
    69     71   	__index = {
    70     72   		--------------
    71     73   		-- overlays --
    72     74   		--------------
................................................................................
   188    190   		end;
   189    191   
   190    192   		---------
   191    193   		-- HUD --
   192    194   		---------
   193    195   		attachImage = function(self, def)
   194    196   			local user = self.entity
   195         -			local img = {}
          197  +			local img = {def = def}
   196    198   			img.id = user:hud_add {
   197    199   				type = 'image';
   198    200   				text = def.tex;
   199    201   				scale = def.scale;
   200    202   				alignment = def.align;
   201    203   				position = def.pos;
   202    204   				offset = def.ofs;
................................................................................
   209    211   					end, def)
   210    212   				end
   211    213   			end
   212    214   			return img
   213    215   		end;
   214    216   		attachMeter = function(self, def)
   215    217   			local luser = self.entity
   216         -			local m = {}
          218  +			local m = {def = def}
   217    219   			local w = def.size or 80
   218    220   			local szf = w / 80
   219    221   			local h = szf * 260
   220    222   			m.meter = luser:hud_add {
   221    223   				type = 'image';
   222    224   				scale = {x = szf, y = szf};
   223    225   				alignment = def.align;
................................................................................
   263    265   					luser:hud_change(m.readout, 'number', txtcolor:hex())
   264    266   				end
   265    267   			end
   266    268   			return m
   267    269   		end;
   268    270   		attachTextBox = function(self, def)
   269    271   			local luser = self.entity
   270         -			local box = {}
          272  +			local box = {def = def}
   271    273   			box.id = luser:hud_add {
   272    274   				type = 'text';
   273    275   				text = '';
   274    276   				alignment = def.align;
   275    277   				number = def.color and def.color:int24() or 0xFFffFF;
   276    278   				scale = def.bound;
   277    279   				size = {x = def.size, y=0};
................................................................................
   286    288   					luser:hud_change(box.id, 'number', color:int24())
   287    289   				end
   288    290   			end
   289    291   			return box
   290    292   		end;
   291    293   		attachStatBar = function(self, def)
   292    294   			local luser = self.entity
   293         -			local bar = {}
          295  +			local bar = {def = def}
   294    296   			local img = lib.image 'starlit-ui-bar.png'
   295    297   			local colorized = img
   296    298   			if type(def.color) ~= 'function' then
   297    299   				colorized = colorized:shift(def.color)
   298    300   			end
   299    301   
   300    302   			bar.id = luser:hud_add {
................................................................................
   446    448   					set('text', hudAdjustBacklight(hudCenterBG):render())
   447    449   				end;
   448    450   			};
   449    451   			self:updateHUD()
   450    452   		end;
   451    453   		deleteHUD = function(self)
   452    454   			for name, e in pairs(self.hud.elt) do
   453         -				self:hud_delete(e.id)
          455  +				self.entity:hud_remove(e.id)
   454    456   			end
   455    457   		end;
   456    458   		updateHUD = function(self)
   457    459   			for name, e in pairs(self.hud.elt) do
   458    460   				if e.update then e.update() end
   459    461   			end
   460    462   		end;
................................................................................
   499    501   		-- intel-gathering --
   500    502   		---------------------
   501    503   		clientInfo = function(self)
   502    504   			return minetest.get_player_information(self.name)
   503    505   		end;
   504    506   		species = function(self)
   505    507   			return starlit.world.species.index[self.persona.species]
          508  +		end;
          509  +		-- can the suit heater sustain its current internal temperature in an area of t°C
          510  +		tempCanSustain = function(self, t)
          511  +			if self:naked() then return false end
          512  +			local s = self:getSuit()
          513  +			if s:powerState() == 'off' then return false end
          514  +			local sd = s:def()
          515  +			local w = self:effectiveStat 'warmth'
          516  +			local kappa = starlit.constant.heat.thermalConductivity
          517  +			local insul = sd.temp.insulation
          518  +			local dt = (kappa * (1-insul)) * (t - w)
          519  +			print('dt', dt, dump(sd.temp))
          520  +			if (dt > 0 and          dt  > sd.temp.maxCool)
          521  +			or (dt < 0 and math.abs(dt) > sd.temp.maxHeat) then return false end
          522  +			return true
          523  +		end;
          524  +		-- will exposure to temperature t cause the player eventual harm
          525  +		tempHazard = function(self, t)
          526  +			local tr = self:species().tempRange.survivable
          527  +			if t >= tr[1] and t <= tr[2] then return nil end
          528  +			if self:tempCanSustain(t)    then return nil end
          529  +
          530  +			if t < tr[1] then return 'cold' end
          531  +			return 'hot'
   506    532   		end;
   507    533   
   508    534   		--------------------
   509    535   		-- event handlers --
   510    536   		--------------------
   511    537   		onSignup = function(self)
   512    538   			local meta = self.entity:get_meta()
................................................................................
   872    898   			end
   873    899   			if run then
   874    900   				run(self, ctx)
   875    901   				return true
   876    902   			end
   877    903   			return false
   878    904   		end;
          905  +
          906  +		alarm = function(self, urgency, kind, freq, where)
          907  +			freq = freq or 3
          908  +			local urgencies = {
          909  +				[1] = {sound = 'starlit-alarm'};
          910  +				[2] = {sound = 'starlit-alarm-urgent'};
          911  +			}
          912  +		   local gt = minetest.get_gametime()
          913  +		   local urg = urgencies[urgency] or urgencies[#urgencies]
          914  +
          915  +		   if gt - self.cooldownTimes.alarm < freq then return end
          916  +
          917  +		   self.cooldownTimes.alarm = gt
          918  +		   self:suitSound(urg.sound)
          919  +
          920  +		   if where then
          921  +			   local elt = {
          922  +				   tex = where.tex or 'starlit-ui-alert.png';
          923  +				   scale = {x=1, y=1};
          924  +				   align = table.copy(where.elt.def.align);
          925  +				   pos = table.copy(where.elt.def.pos);
          926  +				   ofs = table.copy(where.elt.def.ofs);
          927  +			   }
          928  +			   elt.ofs.x = elt.ofs.x + where.ofs.x
          929  +			   elt.ofs.y = elt.ofs.y + where.ofs.y
          930  +			   local attached = self:attachImage(elt)
          931  +				table.insert(self.hud.alarm, attached)
          932  +
          933  +			   -- HATE. HATE. HAAAAAAAAAAATE
          934  +			   minetest.after(freq/2, function()
          935  +				   for k,v in pairs(self.hud.alarm) do
          936  +					   self.entity:hud_remove(v.id)
          937  +				   end
          938  +				   self.hud.alarm={}
          939  +			   end)
          940  +		   end
          941  +	   end;
   879    942   
   880    943   		-------------
   881    944   		-- weather --
   882    945   		-------------
   883    946   		updateWeather = function(self)
   884    947   		end;
   885    948   
................................................................................
   933    996   
   934    997   local clockInterval = 1.0
   935    998   starlit.startJob('starlit:clock', clockInterval, function(delta)
   936    999   	for id, u in pairs(starlit.activeUsers) do
   937   1000   		u.hud.elt.time:update()
   938   1001   	end
   939   1002   end)
         1003  +
         1004  +-- performs a general HUD refresh, mainly to update the HUD backlight brightness
         1005  +local hudInterval = 10
         1006  +starlit.startJob('starlit:hud-refresh', hudInterval, function(delta)
         1007  +	for id, u in pairs(starlit.activeUsers) do u:updateHUD() end
         1008  +end)
   940   1009   
   941   1010   local biointerval = 1.0
   942   1011   starlit.startJob('starlit:bio', biointerval, function(delta)
   943   1012   	for id, u in pairs(starlit.activeUsers) do
   944   1013   		if u:effectiveStat 'health' ~= 0 then
   945   1014   			local bmr = u:phenoTrait 'metabolism' * biointerval
   946   1015   			-- TODO apply modifiers

Modified mods/starlit/world.lua from [d79dc9efc2] to [a2ac450e1c].

   176    176   	-- player is in -30°C weather, and has an internal temperature of
   177    177   	-- 10°C. then:
   178    178   	--   κ  = .1°C/C/s (which is apparently 100mHz)
   179    179   	--   Tₚ =  10°C
   180    180   	--   Tₑ = -30°C
   181    181   	--   d  = Tₑ − Tₚ = -40°C
   182    182   	--   ΔT = κ×d = -.4°C/s
          183  +	-- too cold:
          184  +	--		x = beginning of danger zone
          185  +	--    κ × (x - Tₚ) = y where y < Tₚ
   183    186   	-- our final change in temperature is computed as tΔC where t is time
   184         -	local kappa = .05
          187  +	local kappa = starlit.constant.heat.thermalConductivity
   185    188   	for name,user in pairs(starlit.activeUsers) do
   186    189   		local tr = user:species().tempRange
   187    190   		local t = starlit.world.climate.temp(user.entity:get_pos())
          191  +
          192  +		do -- this bit probably belongs in starlit:bio but we do it here in order
          193  +		   -- to spare ourselves another call into the dark swamp of climate.temp
          194  +		   local urg = 1
          195  +		   local function alarm(kind)
          196  +			   user:alarm(urg, kind, nil, {
          197  +				   elt = user.hud.elt.temp, ofs = {x=100,y=0};
          198  +				   tex = 'starlit-ui-alert-'..kind..'.png';
          199  +			   })
          200  +		   end
          201  +		   local hz = user:tempHazard(t)
          202  +			local tr = user:species().tempRange.survivable
          203  +		   if hz == 'cold' then
          204  +			   if tr[1] - t > 7 then urg = 2 end
          205  +			   alarm 'temp-cold'
          206  +		   elseif hz == 'hot' then
          207  +			   if t - tr[2] > 7 then urg = 2 end
          208  +			   alarm 'temp-hot'
          209  +		   end
          210  +		end
          211  +
   188    212   		local insul = 0
   189    213   		local naked = user:naked()
   190    214   		local suitDef
   191    215   		if not naked then
   192    216   			suitDef = user:suitStack():get_definition()
   193    217   			insul = suitDef._starlit.suit.temp.insulation
   194    218   		end
................................................................................
   223    247   			end
   224    248   		end
   225    249   
   226    250   		user:statDelta('warmth', tgt - warm) -- dopey but w/e
   227    251   
   228    252   		warm = tgt -- for the sake of readable code
   229    253   
          254  +		-- does this belong in starlit:bio? unsure tbh
   230    255   		if warm < tSafeMin or warm > tSafeMax then
   231    256   			local dv
   232    257   			if warm < tSafeMin then
   233    258   				dv = math.abs(warm - tSafeMin)
   234    259   			else
   235    260   				dv = math.abs(warm - tSafeMax)
   236    261   			end

Modified starlit.ct from [415d01ad10] to [8d6b5074b6].

     1      1   # starlit
     2      2   [*starlit] is a sci-fi survival game. you play the survivor of a disaster in space, stranded on a just-barely-habitable world with nothing but your escape pod, your survival suit, and some handy new psionic powers that the accident seems to have unlocked in you. scavenge resources, search alien ruins, and build a base where you can survive indefinitely, but be careful: winter approaches, and your starting suit heater is already struggling to keep you alive in the comparatively warm days of "summer".
     3      3   
     4         -## story
     5         -### "Imperial Expat" background
            4  +##story story
     6      5   about a month ago, you woke up to unexpected good news. your application to join the new Commune colony at Thousand Petal, submitted in a moment of utter existential weariness and almost in jest, was actually accepted. your skillset was a "perfect match" for the budding colony's needs, claimed the Population Control Authority, and you'd earned yourself a free trip to your new home -- on a swanky state transport, no less.
     7      6   
     8      7   it took a few discreet threats and bribes from Commune diplomats, but after a week of wrangling with the surly Crown Service for Comings & Goings -- whose bureaucrats seemed outright [!offended] that you actually managed to find a way off that hateful rock -- you secured grudging clearance to depart. you celebrated by telling your slackjawed boss exactly what you thought of him in a meeting room packed with his fellow parasites -- and left House Taladran with a new appreciation for the value of your labor, as the nobs found themselves desperately scrabbling for a replacement on short notice.
     9      8   
    10      9   you almost couldn't believe it when the Commune ship -- a sleek, solid piece of engineering whose graceful descent onto the landing pad seemed to sneer at the lurching rattletraps arrayed all around it -- actually showed up. in a daze you handed over your worldly possessions -- all three of them -- to a valet with impeccable manners, and climbed up out of the wagie nightmare into high orbit around your homeworld. the mercenary psion aboard, a preening Usukwinti with her very own luxury suite, tore a bleeding hole in the spacetime metric, and five hundred hopeful souls dove through towards fancied salvation. "sure," you thought to yourself as you slipped into your sleek new nanotech environment suit, itself worth more than the sum total of your earnings on Flame of Unyielding Purification, "life won't be easy -- but damn it, it'll [!mean] something out there."
    11     10   
    12     11   a free life on the wild frontier with a nation of comrades to have your back, with the best tech humans can make, fresh, clean water that isn't laced with compliance serum, and -- best of all -- never having to worry about paying rent again. it was too good to be true, you mused.
................................................................................
    38     37   
    39     38   	p11143: https://github.com/minetest/minetest/pull/11143.diff
    40     39   
    41     40   ### shadows
    42     41   i was delighted to see dynamic shadows land in minetest, and i hope the implementation will eventually mature. however, as it stands, there are severe issues with shadows that make them essentially incompatible with complex meshes like the Starlit player character meshes. for the sake of those who don't mind these glitches, Starlit does enable shadows, but i unfortunately have to recommend that you disable them until the minetest devs get their act together on this feature.
    43     42   
    44     43   ## gameplay
           44  +starlit is somewhat unusual in how it uses the minetest engine. it's a voxel game but not of the minecraft variety.
           45  +
    45     46   the most important thing to understand about starlit is that is is [*mean], by design.
    46     47   
    47     48   * chance plays an important role. your escape pod might land in the midst of a lush, temperate forest with plenty of nearby shipwrecks to scavenge. or it might land in the exact geographic center of a vast, harsh desert that your suit's cooling systems can't protect you from, ten klicks from anything of value. "unfair", you say? tough. Farthest Shadow doesn't care about your feelings.
    48     49   * death is much worse than a slap on the wrist. when you die, you drop your possessions and your suit, and respawn naked at your spawn point. this is a serious danger, as you might be kilometers away from your spawn point -- and there's no guarantee someone else won't take your suit before you can find your way back to it. good luck crossing long distances without climate control! if you haven't carefully prepared for this eventuality by keeping a spare suit by your spawn point, death can be devastating, to the point of making the game unsurvivable without another player's help. 
    49     50   
    50         -starlit is somewhat unusual in how it uses the minetest engine. it's a voxel game but not of the minecraft variety.
           51  +### scenarios
           52  +your starting character configuration depends on the scenario you select. (right now this is configured in minetest settings, which is intensely awkward, but i don't have a better solution). the scenario controls your species, sex, and starting inventory. [*neither species nor sex is cosmetic]; e.g. human females are physically weaker but psionically stronged than males. the current playable scenarios are:
           53  +
           54  +#### Imperial Expat
           55  +[*phenotype]: human female
           56  +[*starting gear]: Commune survival kit
           57  +> 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.
           58  +> At least you got some handy psionic powers out of this whole clusterfuck. Hopefully they're safe to use.
           59  +
           60  +#### Gentleman Adventurer [!(unimplemented)]
           61  +[*phenotype]: human male
           62  +[*starting gear]: Imperial survival kit
           63  +> Tired of the same-old-same-old, sick of your idiot contemporaries, exasperated with the shallow soul-rotting luxury of life as landless lordling, and earnestly eager to enrage your father, you resolved to see the Reach in all her splendor. Deftly evading the usual tourist traps, you finagled your way into the confidence of the Commune ambassador with a few modest infusions of Father's money -- now [!that] should pop his monocle -- and secured yourself a seat on a ride to their brand-new colony at Thousand Petal. How exciting -- a genuine frontier outing!
           64  +
           65  +#### Terrorist Tagalong
           66  +[*phenotype]: human female
           67  +[*starting gear]: star merc combat kit
           68  +> It turns out there's a *reason* Crown jobs pay so well.
           69  +
           70  +#### Tradebird Bodyguard [!(unimplemented)]
           71  +[*phenotype]: male Usukwinti
           72  +[*starting gear]: Usuk survival kit
           73  +> You've never understood why astropaths of all people [!insist] on bodyguards. This one could probably make hash of a good-sized human batallion, if her feathers were sufficiently ruffled. Perhaps it's a status thing. Whatever the case, it's easy money.
           74  +> At least, it was supposed to be.
    51     75   
    52     76   ### controls
    53     77   summon your Suit Interface by pressing the [*E] / [*Inventory] key. this will allow you to move items around in your inventory, but more importantly, it also allows you select or configure your Interaction Mode.
    54     78   
    55     79   the top three buttons can be used to select (or deactivate) an Interaction Mode. an Interaction Mode can be configured by pressing the button immediately below. the active Interaction Mode controls the behavior of the mouse buttons when no item is selected in the hotbar.
    56     80   
    57     81   the modes are:
................................................................................
    67     91   
    68     92   to use a tool, select it in the hotbar. even if an Interaction Mode is active, the tool will take priority. press [*Left Button] / [*Punch] to use the tool on a block; for instance, to break a stone with a jackhammer. to configure a tool or use its secondary functions, if any, press [*Right Button] / [*Place].
    69     93   
    70     94   hold [*Aux1] to activate your selected Maneuver. by default this is Sprint, which will consume stamina to allow you to run much faster. certain suits offer the Flight ability, which allows slow, mid-range flight. you can also unlock the psionic ability Lift, which allows very rapid flight but consumes psi at a prodigious rate.
    71     95   
    72     96   you can only have one Maneuver active at a time, whether this is a Psi Maneuver (consuming psi), a Suit Maneuver (consuming battery), or a Body Maneuver (consuming stamina). Maneuvers are activated in their respective panel.
    73     97   
           98  +### stats
           99  +to survive and thrive on Farthest Shadow, you need to manage your stats carefully. stats impact gameplay and other stats in various ways.
          100  +* [*health]: self-explanatory. it hits zero, you die. measured in hit points (hp)
          101  + ** regenerates very slowly, though nanomedical suites improve this considerably
          102  +* [*stamina]: how long you can sustain intense physical activity. measured in sprinting meters (m)
          103  +** regenerates quickly, depending on [*fatigue]
          104  +** consumed by biological abilities such as sprinting or Usuk flight.
          105  +** affects [*fatigue]: the lower your stamina bar, the faster your fatigue builds
          106  +* [*energy]: the amount of electrical power available to your environment suit. measured in joules (J)
          107  +** does not regenerate, but empty batteries can be swapped out to replenish energy in the field
          108  +** consumed by all suit abilities, including nanotech and weapons systems
          109  +* [*numina]: a measure of your available psionic power. measured in psi (ψ)
          110  +** accumulates slowly, depending on [*morale]
          111  +* [*nutrition]: how much energy your body has stored. measured in kCal. basal metabolic rate depends on species and sex
          112  +** affects [*morale]: if you're starving, you bleed morale more quickly
          113  +** affects [*health]: you will die if you do not eat enough
          114  +* [*hydration]: stay hydrated uwu. measured in liters (L)
          115  +** affects [*morale]: if you're parched, you bleed morale much more quickly
          116  +** affects [*health]: you will die very quickly if you do not drink enough
          117  +* [*warmth]: maintaining a healthy body temperature is crucially important on Farthest Shadow, which does not have many places a human can be comfortable without a powered environment suit. if you're outside your species' comfort range, your stamina regen and morale expenditure will be impacted. if you're outside your species' [*survival] range, you will start dying of hypo/hyperthermia. most environment suits have built-in thermoelectrics that will try to keep you at a comfortable (or merely survivable, in power save mode) temperature, but this can drain your suit batteries very quickly at extreme temperatures.
          118  +** affects [*morale], [*stamina], [*health]
          119  +* [*fatigue]: how long you've gone without sleep.
          120  +** affects [*stamina] regen: the higher your [*fatigue], the more slowly you regnerate stamina. if you are fully fatigued (exhausted), you do not regenerate stamina
          121  +* [*morale]: how happy you are. measured in the number of hours you can continue functioning emotionally
          122  +** diminishes over time. can be replenished by various activities, such as eating tasty food
          123  +** affects [*numina] regen: the lower your morale, the more slowly you accumulate numina. if you are completely out of morale (depressed), you do not accumulate any numina
          124  +* [*irradation]: how much ionizing radiation you've soaked up. measured in grays (Gy)
          125  +** your environment suit provides some protection against environmental radiation. how much depends on your model of suit.
          126  +** affects [*health]: slows natural healing. at values over 2Gy, you will start to hemorrhage health, though a capable nanomedical system can compensate up to a point
          127  +** affects [*numina]: you accumulate numina more quickly in high-rad areas
          128  +** naturally diminishes, but very slowly
          129  +* [*illness]: not everything on Farthest Shadow's ecosystem is, shall we say, biocompatible. measured in percent
          130  +** affects [*morale]: the higher your illness, the more quickly you bleed morale
          131  +
          132  +your primary stats are shown on your HUD. ancillary stats can be viewed in the "body" panel.
          133  +
    74    134   ### psionics
    75    135   there are four types of psionic abilities: Manual, Maneuver, Ritual, and Contextual.
    76    136   
    77    137   you can assign two Manual abilities at any given time and access them with the mouse buttons in Psionics mode.
    78    138   
    79    139   you can select a Psi Maneuver in the Psionics panel and activate it by holding [*Aux1].
    80    140