Index: mods/starlit-electronics/sw.lua ================================================================== --- mods/starlit-electronics/sw.lua +++ mods/starlit-electronics/sw.lua @@ -5,28 +5,34 @@ ------------------------------- -- basic suit nano abilities -- ------------------------------- local function shredder(prop) - local function getItemsForFab(fab) + local function fabToItemsAndCharges(fab) local elt if fab then elt = fab:elementalize() else elt = {} end - local items = {} + local items,charges = {},{} 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) + 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 - return items + print(dump(items)) + return items, charges end return function(user, ctx) local function cleanup() user.action.prog.shred = nil @@ -103,12 +109,13 @@ 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) + 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() Index: mods/starlit-material/elements.lua ================================================================== --- mods/starlit-material/elements.lua +++ mods/starlit-material/elements.lua @@ -2,120 +2,120 @@ local W = starlit.world local M = W.material M.element.meld { hydrogen = { - name = 'hydrogen', sym = 'H', n = 1; + 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; + 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; + name = 'oxygen', sym = 'O', n = 8; density = 0.001429; gas = true; color = lib.color(.2,1,.2); }; carbon = { - name = 'carbon', sym = 'C', n = 6; + name = 'carbon', sym = 'C', n = 6, density = 2.266; -- g/cm³ color = lib.color(.7,.2,.1); }; silicon = { - name = 'silicon', sym = 'Si', n = 14; + 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; + 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; + 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; + 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; + name = 'iron', sym = 'Fe', n = 26; density = 7.874; metal = true; color = lib.color(.3,.3,.3); }; copper = { - name = 'copper', sym = 'Cu', n = 29; + name = 'copper', sym = 'Cu', n = 29; density = 8.96; metal = true; color = lib.color(.8,.4,.1); }; lithium = { - name = 'lithium', sym = 'Li', n = 3; + 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; + name = 'titanium', sym = 'Ti', n = 22; density = 4.506; metal = true; color = lib.color(.7,.7,.7); }; vanadium = { - name = 'vanadium', sym = 'V', n = 23; + name = 'vanadium', sym = 'V', n = 23; density = 6; metal = true; color = lib.color(.3,0.5,.3); }; xenon = { - name = 'xenon', sym = 'Xe', n = 54; + name = 'xenon', sym = 'Xe', n = 54; density = 0.005894; gas = true; color = lib.color(.5,.1,1); }; argon = { - name = 'argon', sym = 'Ar', n = 18; + 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; + name = 'osmium', sym = 'Os', n = 76; density = 22.59; metal = true; color = lib.color(.8,.1,1); }; iridium = { - name = 'iridium', sym = 'Ir', n = 77; + name = 'iridium', sym = 'Ir', n = 77; density = 22.56; metal = true; color = lib.color(.8,0,.5); }; technetium = { - name = 'technetium', sym = 'Tc', n = 43; + 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; + 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; + 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; + name = 'silver', sym = 'Ag', n = 47; density = 10.49; metal = true; color = lib.color(.7,.7,.8); }; gold = { - name = 'gold', sym = 'Au', n = 79; + name = 'gold', sym = 'Au', n = 79; density = 19.30; metal = true; color = lib.color(1,.8,0); }; } Index: mods/starlit-material/init.lua ================================================================== --- mods/starlit-material/init.lua +++ mods/starlit-material/init.lua @@ -4,19 +4,22 @@ } 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? + vol = sz.vol; -- 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}; + 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' Index: mods/starlit-scenario/init.lua ================================================================== --- mods/starlit-scenario/init.lua +++ mods/starlit-scenario/init.lua @@ -1,7 +1,8 @@ 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 @@ -8,20 +9,20 @@ 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}; + 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]}; + 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) @@ -62,10 +63,34 @@ 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."; @@ -93,11 +118,12 @@ -- E V E R Y T H I N G. }; suitGuns = {}; suitAmmo = {}; suitCans = { - ItemStack('starlit_material:canister_small'); +-- 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 Index: mods/starlit-secrets/init.lua ================================================================== --- mods/starlit-secrets/init.lua +++ mods/starlit-secrets/init.lua @@ -40,13 +40,10 @@ -- 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. Index: mods/starlit/effect.lua ================================================================== --- mods/starlit/effect.lua +++ mods/starlit/effect.lua @@ -309,10 +309,11 @@ 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 Index: mods/starlit/element.lua ================================================================== --- mods/starlit/element.lua +++ mods/starlit/element.lua @@ -14,15 +14,17 @@ }) 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 @@ -65,21 +67,50 @@ } } 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 {} - m.form.element = eltID - local powder = F('starlit-element-%s-powder.png', id); + + 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('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); + 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, powder = 1, specialInventory = 1}; + groups = {element = 1, chunk = 1}; _starlit = { mass = 1; material = { kind = 'element'; element = id; @@ -87,21 +118,19 @@ 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)) + local ingotID = baseID .. 'ingot' + local ingotName = 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); @@ -121,30 +150,10 @@ 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(); @@ -163,30 +172,54 @@ }; }); 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 = 'Charge Slots', affinity = 'info', desc = tostring(def.slots)}; + {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 - -- TODO list masses + 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 elemental powder, gas, or liquid'; + 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 @@ -214,5 +247,7 @@ }; }; }; }) end) + +starlit.item.canister.meta = canisterMeta Index: mods/starlit/init.lua ================================================================== --- mods/starlit/init.lua +++ mods/starlit/init.lua @@ -47,10 +47,11 @@ -- 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(); @@ -93,21 +94,21 @@ 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 + -- 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. - {320, 'Emperor', 'Persigan', 'the Deathless'}; + {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) @@ -250,10 +251,11 @@ start() end starlit.include 'stats' starlit.include 'world' +starlit.include 'food' starlit.include 'fab' starlit.include 'tiers' starlit.include 'species' starlit.include 'store' @@ -358,17 +360,39 @@ 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 Index: mods/starlit/interfaces.lua ================================================================== --- mods/starlit/interfaces.lua +++ mods/starlit/interfaces.lua @@ -300,10 +300,11 @@ 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 @@ -313,10 +314,11 @@ {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'; @@ -345,11 +347,11 @@ 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'} + 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)) Index: mods/starlit/species.lua ================================================================== --- mods/starlit/species.lua +++ mods/starlit/species.lua @@ -16,11 +16,11 @@ 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.'; + 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}}; @@ -57,12 +57,13 @@ 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 + metabolism = 1800e3 / 24 / 60 / 60; --kCal/s painTolerance = 0.4; + dehydration = 3; -- mL/s }; }; male = { name = 'Human Male'; eyeHeight = 1.6; @@ -69,29 +70,49 @@ 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 = 2200; --Cal + 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 {} @@ -110,10 +131,11 @@ 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] @@ -162,10 +184,11 @@ 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 Index: mods/starlit/stats.lua ================================================================== --- mods/starlit/stats.lua +++ mods/starlit/stats.lua @@ -1,17 +1,20 @@ local lib = starlit.mod.lib local function U(unit, prec, fixed) + local trunc = 2 if fixed then return function(amt, excludeUnit) - if excludeUnit then return tostring(amt/prec) end - return string.format("%s %s", amt/prec, unit) + 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) - if excludeUnit then return tostring(amt/prec) end - return lib.math.si(unit, amt/prec) + 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) @@ -24,14 +27,14 @@ -- 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 + 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 Index: mods/starlit/store.lua ================================================================== --- mods/starlit/store.lua +++ mods/starlit/store.lua @@ -10,11 +10,11 @@ ------------- -- persona -- ------------- ----------------------------------------------- -- a Persona is a structure that defines the nature of -- --- an (N)PC and how it interacts with the Starsoul-managed -- +-- 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 @@ -49,5 +49,11 @@ 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; +} Index: mods/starlit/suit.lua ================================================================== --- mods/starlit/suit.lua +++ mods/starlit/suit.lua @@ -63,11 +63,11 @@ 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) +-- 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 @@ -88,10 +88,11 @@ 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) @@ -110,17 +111,18 @@ 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 @@ -215,11 +217,11 @@ 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:changeSuit(starlit.type.suit(ss)) user:suitSound('starlit-suit-don') return st end end; inventory_image = icon:render(); Index: mods/starlit/terrain.lua ================================================================== --- mods/starlit/terrain.lua +++ mods/starlit/terrain.lua @@ -80,11 +80,11 @@ { name = 'default_dirt.png^' .. def.img ..'_side.png'; tileable_vertical = false; }; }; - groups = {grass = 1, sub_walk = 1}; + groups = {grass = 1, dirt = 1, sub_walk = 1}; drop = ''; sounds = grassSounds; _starlit = grassfst(2); }) for i=2,0,-1 do Index: mods/starlit/user.lua ================================================================== --- mods/starlit/user.lua +++ mods/starlit/user.lua @@ -65,10 +65,11 @@ 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; @@ -98,13 +99,14 @@ -- 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 + 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; @@ -347,11 +349,11 @@ local txt = string.format("%sGy", math.floor(hot)) return (hot/5), txt, color end; } self.hud.elt.crosshair = self:attachImage { - name = 'crosshair '; + name = 'crosshair'; tex = ''; pos = {x=.5, y=.5}; scale = {x=1,y=1}; ofs = {x=0, y=0}; align = {x=0, y=0}; @@ -377,10 +379,20 @@ 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'; @@ -388,10 +400,11 @@ 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 @@ -417,10 +430,11 @@ 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 @@ -501,10 +515,11 @@ 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] @@ -783,10 +798,11 @@ 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) @@ -794,10 +810,11 @@ 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) @@ -804,17 +821,34 @@ 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; Index: mods/starlit/world.lua ================================================================== --- mods/starlit/world.lua +++ mods/starlit/world.lua @@ -91,39 +91,63 @@ 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; - }; -}) +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) Index: mods/vtlib/image.lua ================================================================== --- mods/vtlib/image.lua +++ mods/vtlib/image.lua @@ -21,11 +21,11 @@ if str ~= '' then str = str .. '(' bracket = true end str = str .. self.string - end + end for _,e in pairs(self.fx) do str = str .. '^[' .. e -- be sure to escape ones that take arguments -- correctly! end @@ -84,11 +84,11 @@ 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) + color.hue, color.sat, color.lum) }) }) end; rehue = function(self, hue) Index: mods/vtlib/marshal.lua ================================================================== --- mods/vtlib/marshal.lua +++ mods/vtlib/marshal.lua @@ -203,10 +203,11 @@ 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; Index: mods/vtlib/math.lua ================================================================== --- mods/vtlib/math.lua +++ mods/vtlib/math.lua @@ -24,11 +24,11 @@ -- 1 == equal -- >1 == greater than end -- produce an SI expression for a quantity -fn.si = function(unit, val, full, uncommonScales) +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}; @@ -47,21 +47,25 @@ 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", - val / denom, (full and pmaj or smaj), unit) + 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", - val / denom, (full and pmin or smin), unit) + vd, (full and pmin or smin), unit) end end end end