Index: mods/starlit-eco/init.lua ================================================================== --- mods/starlit-eco/init.lua +++ mods/starlit-eco/init.lua @@ -10,12 +10,12 @@ 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; + heat_point = 20; + humidity_point = 35; }; }) world.ecology.biomes.link('starlit:forest', { nightTempDelta = -20; @@ -24,13 +24,13 @@ seasonalTemp = {-40, -8, 10, 10, -14, -40}; def = { node_top = 'starlit:greengraze', depth_top = 1; node_filler = 'starlit:soil', depth_filler = 4; node_riverbed = 'starlit:sand', depth_riverbed = 4; - y_min = -100; + y_min = 0; y_max = 256; - heat_point = 13; + heat_point = 50; humidity_point = 40; }; }) world.ecology.biomes.link('starlit:desert', { @@ -42,11 +42,11 @@ node_top = 'starlit:sand', depth_top = 1; node_filler = 'starlit:sand', depth_filler = 4; node_riverbed = 'starlit:sand', depth_riverbed = 4; y_min = 0; y_max = 512; - heat_point = 20; + heat_point = 70; humidity_point = 10; }; }) world.ecology.biomes.link('starlit:ocean', { @@ -54,17 +54,60 @@ waterTempDelta = 5; seasonalTemp = {0}; -- no seasonal variance def = { y_max = 3; y_min = -512; - heat_point = 15; - humidity_point = 50; + heat_point = 50; + humidity_point = 70; node_top = 'starlit:sand', depth_top = 1; node_filler = 'starlit:sand', depth_filler = 3; }; }) +world.ecology.biomes.link('starlit:shiverdeep', { + nightTempDelta = -25; + waterTempDelta = 5; + -- W Sp Su Au W + seasonalTemp = {-70, -30, 0, -60, -70}; + def = { + y_max = 70; + y_min = 1; + heat_point = 0; + humidity_point = 5; + node_water_top = 'starlit:ice', depth_water_top = 1; + node_top = 'starlit:undergloam', depth_top = 1; + node_filler = 'starlit:soil', depth_filler = 2; + }; +}) + +world.ecology.biomes.link('starlit:silthaven', { + nightTempDelta = -5; + waterTempDelta = 5; + -- W Sp Su Au W + seasonalTemp = {-15, 5, 15, 7, -15}; + def = { + y_max = 30; + y_min = 0; + heat_point = 15; + humidity_point = 35; +-- node_top = 'starlit:undergloam', depth_top = 1; + node_filler = 'starlit:lifesilt', depth_filler = 5; + }; +}) + +world.ecology.biomes.link('starlit:barrens', { + nightTempDelta = -20; + waterTempDelta = 5; + -- W Sp Su Au W + seasonalTemp = {-30, -20, 0, -20, -30}; + def = { + y_max = 512; + y_min = 0; + heat_point = 0; + humidity_point = 0; + }; +}) minetest.register_craftitem('starlit_eco:fiber', { description = "Plant Fiber"; groups = {fiber = 1}; inventory_image = lib.image('starlit-eco-plant-fiber.png'):shift(lib.color(0,1,0)):render(); _starlit = { Index: mods/starlit-eco/plants.lua ================================================================== --- mods/starlit-eco/plants.lua +++ mods/starlit-eco/plants.lua @@ -122,11 +122,11 @@ leaf = { color = lib.color(.6, .8, .8); drop = simpleDrop(2, 'starlit_eco:moondrop_petal'); }; berries = { - desc = "The fruits of the moondrop are not very nutritious, but their peculiar sweet-sour flavor profile makes them one Thousand Petal's great delicacies"; + desc = "The fruits of the moondrop are not very nutritious, but their peculiar sweet-sour flavor profile makes them one of Farthest Shadow's great delicacies"; color = lib.color(1,0,.4); nourish = 10; hydrate = 0.05; taste = 1 * 60; mass = 1; @@ -152,11 +152,11 @@ }; decoration = { place_on = 'starlit:greengraze'; fill_ratio = 0.03; biomes = {'starlit:forest'}; - y_min = -50; + y_min = 0; y_max = 50; }; } stalkPlantAuto { @@ -166,10 +166,10 @@ seed = {}; color = lib.color(.3, .2, .1); decoration = { place_on = 'starlit:sand'; fill_ratio = 0.03; - biomes = {'starlit:ocean', 'starlit:desert'}; - y_min = -30; + biomes = {'starlit:desert'}; + y_min = 0; y_max = 400; }; } Index: mods/starlit-eco/trees.lua ================================================================== --- mods/starlit-eco/trees.lua +++ mods/starlit-eco/trees.lua @@ -173,19 +173,19 @@ fruit = 'starlit_eco:lambent_pine_bulb'; fruit_chance = 0; }; decorate = { { biomes = {'starlit:forest'}; - place_on = 'starlit:greengraze'; + place_on = {'starlit:greengraze'}; fill_ratio = 0.004; - y_min = -30, y_max = 500; + y_min = 0, y_max = 500; seed = 0xe8190e; }; { biomes = {'starlit:steppe'}; - place_on = 'starlit:greengraze'; + place_on = {'starlit:greengraze'}; fill_ratio = 0.002; - y_min = -30, y_max = 500; + y_min = 0, y_max = 500; seed = 0xe8190e; }; }; }; @@ -207,11 +207,11 @@ }; decorate = { { biomes = {'starlit:forest'}; place_on = 'starlit:greengraze'; fill_ratio = 0.001; - y_min = -20, y_max = 512; + y_min = 0, y_max = 512; }; }; }; } Index: mods/starlit/species.lua ================================================================== --- mods/starlit/species.lua +++ mods/starlit/species.lua @@ -21,10 +21,44 @@ name = 'Sprint'; desc = 'Put on a short burst of speed at the cost of some stamina'; img = 'starlit-ui-icon-ability-sprint.png'; powerKind = 'maneuver'; run = function(user, ctx) + local cost = 10 + -- unfortunately stat writes are very expensive, so can't draw from stamina + -- every single frame + local function halt() + if user.action.prog.sprint then + user:statDelta('stamina', -user.action.prog.sprint.cb) + user:deleteOverlay(user.action.prog.sprint.id) + user.action.prog.sprint = nil + end + end + + if ctx.how.state == 'init' then + halt() + if user:effectiveStat 'stamina' > 0 then + user.action.prog.sprint = { + cb = 0; + id = user:overlay(function(phys) phys.speed = phys.speed * 2 end) + } + end + elseif ctx.how.state == 'prog' then + local d = ctx.how.delta + local p = user.action.prog.sprint + -- is the player currently holding any of WASD + local isMoving = bit.band(0x0f, user.entity:get_player_control_bits()) ~= 0 + if p and isMoving then + p.cb = p.cb + cost*d + if p.cb >= 10 then + user:statDelta('stamina', -10) + if user:effectiveStat 'stamina' < 10 then halt() end + end + end + elseif ctx.how.state == 'halt' then + halt() + end end; }; } local species = { @@ -61,11 +95,10 @@ psiRegen = 1.3; psiPower = 1.2; psi = 1.2; nutrition = .8; -- women have smaller stomachs hydration = .8; - staminaRegen = 1.0; morale = 0.8; -- you are not She-Bear Grylls }; traits = { health = 400; lungCapacity = .6; @@ -73,10 +106,11 @@ sturdiness = 0; -- women are more fragile and thus susceptible to blunt force trauma metabolism = .150; -- kCal/s painTolerance = 0.4; dehydration = 10e-4; -- L/s speed = 1.1; + staminaRegen = 30.0; }; }; male = { name = 'Human Male'; eyeHeight = 1.6; @@ -84,11 +118,11 @@ psiRegen = 1.0; psiPower = 1.0; psi = 1.0; nutrition = 1.0; hydration = 1.0; - staminaRegen = .7; -- men are strong but have inferior endurance + staminaRegen = 20; -- men are strong but have inferior endurance }; traits = { health = 500; painTolerance = 1.0; lungCapacity = 1.0; Index: mods/starlit/stats.lua ================================================================== --- mods/starlit/stats.lua +++ mods/starlit/stats.lua @@ -26,11 +26,11 @@ -- 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 d°C fatigue = {min = 0, max = 76 * 60, base = 0, desc = U('hr', 60, true), color = C(288,.3,.5), name = 'Fatigue', srzType = T.decimal}; -- 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 = {min = 0, max = 40 * 100, base = true, desc = U('m', 100), color = C(88), name = 'Stamina'}; -- stamina is measured in how many 10th-nodes (== cm) one can sprint nutrition = {min = 0, max = 8000, base = 0, desc = U('kCal', 1, true), color = C(43,.5,.4), name = 'Nutrition', srzType = T.decimal}; -- hunger is measured in kcalories one must consume to cure it. at 0, you start dying hydration = {min = 0, max = 4, base = 0, desc = U('L', 1), color = C(217, .25,.4), name = 'Hydration', srzType = T.decimal}; -- thirst is measured in L of H²O required to cure it Index: mods/starlit/terrain.lua ================================================================== --- mods/starlit/terrain.lua +++ mods/starlit/terrain.lua @@ -41,18 +41,31 @@ minetest.register_node('starlit:sand', { description = T 'Sand'; tiles = {'starlit-terrain-sand.png'}; - groups = {looseClump = 1, sand = 1}; + groups = {looseClump = 1, sand = 1, falling_node = 1}; drop = ''; sounds = sandSounds; _starlit = { kind = 'block'; fab = starlit.type.fab { element = { silicon = 25 } }; }; }) + +minetest.register_node('starlit:lifesilt', { + description = T 'Lifesilt'; + tiles = {'starlit-terrain-lifesilt.png'}; + groups = {looseClump = 1, lifesilt = 1, falling_node = 1}; + drop = ''; + sounds = sandSounds; + _starlit = { + kind = 'block'; + fab = starlit.type.fab { element = { carbon = 8, silicon = 4 } }; + }; +}) + minetest.register_craftitem('starlit:soil_clump', { short_description = T 'Soil'; description = starlit.ui.tooltip { title = T 'Soil'; desc = 'A handful of nutrient-packed soil, suitable for growing plants'; @@ -107,16 +120,25 @@ starlit.terrain.createGrass { name = 'starlit:greengraze'; desc = T 'Greengraze'; img = 'starlit-terrain-greengraze'; fab = starlit.type.fab { + element = { carbon = 12; }; + time = { shred = 2.5; }; + }; +} + +starlit.terrain.createGrass { + name = 'starlit:undergloam'; + desc = T 'Undergloam'; + -- fungal carpet + img = 'starlit-terrain-undergloam'; + fab = starlit.type.fab { element = { carbon = 12; }; - time = { - shred = 2.5; - }; + time = { shred = 2.5; }; }; } for _, w in pairs {false,true} do minetest.register_node('starlit:liquid_water' .. (w and '_flowing' or ''), { @@ -166,16 +188,20 @@ starlit.world.mineral.foreach('starlit:mineral_generate', {}, function(name,m) local node = string.format('starlit:mineral_%s', name) local grp = {mineral = 1} minetest.register_node(node, { - description = m.desc; + short_description = m.name; + description = starlit.ui.tooltip { + title = m.name; + desc = m.desc; + color = m.tone; + }; tiles = m.tiles or (m.tone and { - string.format('default_stone.png^[colorizehsl:%s:%s:%s', - m.tone.hue, m.tone.sat, m.tone.lum) - }) or {'default_stone.png'}; + lib.image'starlit-terrain-granite.png':shift(m.tone):render() + }) or nil; groups = grp; drop = m.rocks or ''; sounds = hardSounds; _starlit = { kind = 'block'; @@ -185,41 +211,64 @@ recover_vary = m.recover_vary; }; }) if not m.excludeOre then local seed = 0 - grp.ore = 1 +-- grp.ore = 1 for i = 1, #m.name do - seed = seed*50 + string.byte(name, i) + seed = seed*50 + string.byte(m.name, i) end minetest.register_ore { ore = node; ore_type = m.dist.kind; - wherein = {m.dist.among}; + wherein = m.dist.among; clust_scarcity = m.dist.rare; y_max = m.dist.height[1], y_min = m.dist.height[2]; + column_height_min = m.dist.sheetCols and m.dist.sheetCols[1] or nil; + column_height_max = m.dist.sheetCols and m.dist.sheetCols[2] or nil; noise_params = m.dist.noise or { - offset = 28; - scale = 16; + scale = 1; spread = vector.new(128,128,128); seed = seed; - octaves = 1; + octaves = 2; }; } end end) starlit.world.mineral.link('feldspar', { - desc = T 'Feldspar'; + name = T 'Feldspar'; + tiles = {'starlit-terrain-feldspar.png'}; excludeOre = true; recover = starlit.type.fab { - time = { - shred = 3; - }; - cost = { - shredPower = 3; + time = { shred = 3; }; + cost = { shredPower = 3; }; + }; + recover_vary = function(rng, ctx) + -- print('vary!', rng:int(), rng:int(0,10)) + return starlit.type.fab { + element = { + aluminum = rng:int(0,4); + potassium = rng:int(0,2); + calcium = rng:int(0,2); + } }; + end; +}) + +starlit.world.mineral.link('granite', { + name = T 'Granite'; + tiles = {'starlit-terrain-granite.png'}; + dist = { + kind = 'sheet'; + among = 'starlit:feldspar'; + height = {-200,30}; + sheetCols = {1, 16}; + }; + recover = starlit.type.fab { + time = { shred = 4; }; + cost = { shredPower = 8; }; }; recover_vary = function(rng, ctx) -- print('vary!', rng:int(), rng:int(0,10)) return starlit.type.fab { element = { Index: mods/starlit/user.lua ================================================================== --- mods/starlit/user.lua +++ mods/starlit/user.lua @@ -909,70 +909,74 @@ return stack end; }; } -local biointerval = 3.0 +local biointerval = 1.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 + if u:effectiveStat 'health' ~= 0 then + local bmr = u:phenoTrait 'metabolism' * biointerval + -- TODO apply modifiers + + local dehydration = u:phenoTrait 'dehydration' * biointerval + -- you dehydrate faster in higher temp + dehydration = dehydration * math.max(1, starlit.world.climate.temp(u.entity:get_pos()) / 10) + + u:statDelta('nutrition', -bmr) + u:statDelta('hydration', -dehydration) + + local moralePenalty = -1 -- 1min/min + local fatiguePenalty = 1 -- 1min/min + local heatPenalty = 1 -- stamina regen is divided by this + + do local warmth = u:effectiveStat 'warmth' + local tempRange = u:species().tempRange + local tComfMin, tComfMax = tempRange.comfort[1], tempRange.comfort[2] + local tempDiff = 0 + if warmth < tComfMin then + tempDiff = tComfMin - warmth + elseif warmth > tComfMax then + tempDiff = warmth-tComfMax + end +-- print('tempDiff', tComfMin, tComfMax, tempDiff) + local tempPenalty = tempDiff/3 + moralePenalty = moralePenalty + tempPenalty + heatPenalty = heatPenalty + tempPenalty + end + + -- penalize heavy phys. activity + local stamina, sp = u:effectiveStat 'stamina' + fatiguePenalty = fatiguePenalty * (1 + 9*(1-sp)) + + local food = u:effectiveStat 'nutrition' + local water = u:effectiveStat 'hydration' + local rads = u:effectiveStat 'irradiation' + if food < 1000 then moralePenalty = moralePenalty + (1 - (food/1000)) * 5 end + if water < 1 then moralePenalty = moralePenalty + (1 - (water/1)) * 10 end - 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) + if rads > 0 then + u:statDelta('irradiation', -0.0001 * biointerval) + local moraleDrainFac = 2^(rads / 2) + moralePenalty = moralePenalty * moraleDrainFac + end - u:statDelta('nutrition', -bmr) - u:statDelta('hydration', -dehydration) - - local moralePenalty = -1 -- 1min/min - local fatiguePenalty = 1 -- 1min/min - local heatPenalty = 1 -- stamina regen is divided by this + u:statDelta('morale', moralePenalty * biointerval) + u:statDelta('fatigue', fatiguePenalty * biointerval) - do local warmth = u:effectiveStat 'warmth' - local tempRange = u:species().tempRange - local tComfMin, tComfMax = tempRange.comfort[1], tempRange.comfort[2] - local tempDiff = 0 - if warmth < tComfMin then - tempDiff = math.abs(warmth-tComfMin) - elseif warmth > tComfMax then - tempDiff = math.abs(warmth-tComfMax) + if food == 0 then -- starvation + u:statDelta('health', -5*biointerval) end - moralePenalty = moralePenalty + tempDiff - heatPenalty = heatPenalty + tempDiff - end - -- penalize heavy phys. activity - local stamina, sp = u:effectiveStat 'stamina' - fatiguePenalty = fatiguePenalty * (1 + 9*(1-sp)) + if water == 0 then -- dying of thirst + u:statDelta('health', -20*biointerval) + end - local food = u:effectiveStat 'nutrition' - local water = u:effectiveStat 'hydration' - local rads = u:effectiveStat 'irradiation' - if food < 1000 then moralePenalty = moralePenalty + (1 - (food/1000)) * 5 end - if water < 1 then moralePenalty = moralePenalty + (1 - (water/1)) * 10 end - - if rads > 0 then - u:statDelta('irradiation', -0.0001 * biointerval) - local moraleDrainFac = 2^(rads / 2) - moralePenalty = moralePenalty * moraleDrainFac - end - - u:statDelta('morale', moralePenalty * biointerval) - u:statDelta('fatigue', fatiguePenalty * biointerval) - - if food == 0 then -- starvation - u:statDelta('health', -5*biointerval) - end - - if water == 0 then -- dying of thirst - u:statDelta('health', -20*biointerval) - end - - if sp < 1.0 then - u:statDelta('stamina', u:effectiveStat 'staminaRegen' / heatPenalty) + if sp < 1.0 then + u:statDelta('stamina', u:phenoTrait('staminaRegen',1) / heatPenalty) +-- print('stam', u:effectiveStat 'stamina', u:phenoTrait('staminaRegen',1) / heatPenalty, heatPenalty) + end end end end) local cbit = { Index: mods/starlit/world.lua ================================================================== --- mods/starlit/world.lua +++ mods/starlit/world.lua @@ -5,11 +5,11 @@ local days = minetest.get_day_count() local year = math.floor(days / world.planet.orbit); local day = days % world.planet.orbit; return { year = year, day = day; - season = day / world.planet.orbit; + season = day / world.planet.orbit + 0.5; -- begin summer } end local lerp = lib.math.lerp local function gradient(grad, pos) @@ -20,27 +20,32 @@ local t = op-idx return lerp(t, grad[1 + idx], grad[2 + idx]) end local altitudeCooling = 10 / 100 +local heatRange = {min = -70, max = 70} -- translate mt temps into real temps -- this function provides the basis for temperature calculation, -- which is performed by adding this value to the ambient temperature, -- determined by querying nearby group:heatSource items in accordance -- with the inverse-square law function world.climate.eval(pos, tod, season) local data = minetest.get_biome_data(pos) local biome = world.ecology.biomes.db[minetest.get_biome_name(data.biome)] local heat, humid = data.heat, data.humidity + heat = lerp(heat/100, heatRange.min, heatRange.max) tod = tod or minetest.get_timeofday() heat = lerp(math.abs(tod - 0.5)*2, heat, heat + biome.nightTempDelta) +-- print('base heat', heat) local td = world.date() heat = heat + gradient(biome.seasonalTemp, season or td.season) +-- print('seasonal heat', heat) if pos.y > 0 then heat = heat - pos.y*altitudeCooling end +-- print('altitude heat', heat) return { surfaceTemp = heat; waterTemp = heat + biome.waterTempDelta; surfaceHumid = humid; Index: starlit.ct ================================================================== --- starlit.ct +++ starlit.ct @@ -42,11 +42,11 @@ 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. ## gameplay the most important thing to understand about starlit is that is is [*mean], by design. -* 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. Thousand Petal doesn't care about your feelings. +* 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. * 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. starlit is somewhat unusual in how it uses the minetest engine. it's a voxel game but not of the minecraft variety. ### controls