local lib = starlit.mod.lib
local E = {}
starlit.mod.electronics = E
---------------------
-- item registries --
---------------------
-- a dynamo is any item that produces power and can be slotted into a power
-- source slot. this includes batteries, but also things like radiothermal
-- dynamos.
starlit.item.dynamo = lib.registry.mk 'starlit_electronics:dynamo'
-- batteries hold a charge of power (measured in kJ). how much they can hold
-- (and how much power they can discharge?) depends on their quality
starlit.item.battery = lib.registry.mk 'starlit_electronics:battery'
-- a battery has the properties:
-- class
-- |- capacity (J ): amount of energy the battery can hold
-- |- dischargeRate (W ): rate at which battery can supply power/be charged
-- |- decay (J/J): rate at which the battery capacity degrades while
-- discharging. decay=0 batteries require no maintenance;
-- decay=1 batteries are effectively disposable
-- |- leak (fac): charging inefficiency. depends on the energy storage
-- technology. when N J are drawn from a power source,
-- only (N*leak) J actually make it into the battery.
-- leak=0 is a supercapacitor, leak=1 is /dev/null
-- |- size (m): each suit has a limit to how big of a battery it can take
-- instance
-- |- degrade (mJ): how much the battery has degraded. instance max charge is
-- | determined by $capacity - @degrade
-- |- %wear ÷ 2¹⁶ : used as a factor to determine battery charge
-- chips are standardized data storage hardware that can contain a certain amount
-- of software. in addition to their flash storage, they also provide a given amount
-- of working memory and processor power. processor power speeds up operations like
-- crafting, while programs require a certain amount of memory.
-- chips have a variable number of program slots and a single bootloader slot
--
starlit.item.chip = lib.registry.mk 'starlit_electronics:chip'
-- software is of one of the following types:
-- schematic: program for your matter compiler that enables crafting a given item.
-- output (convertible to ItemStack): the result
-- driver: inserted into a Core to control attached hardware
-- suitPower: provides suit functionality like nanoshredding or healing
-- passive powers are iterated on suit application/configuration and upon fst-tick
-- cost: what the software needs to run. some fields are fab-specific
-- energy: for fab, total energy cost of process in joules
-- for suitPassive, added suit power consumption in watts
starlit.item.sw = lib.registry.mk 'starlit_electronics:sw'
-- chip = lib.color(0, 0, .3);
E.schematicGroups = lib.registry.mk 'starlit_electronics:schematicGroups'
E.schematicGroupMembers = {}
E.schematicGroups.foreach('starlit_electronics:ensure-memlist', {}, function(id,g)
E.schematicGroupMembers[id] = {}
end)
function E.schematicGroupLink(group, item)
table.insert(E.schematicGroupMembers[group], item)
end
E.schematicGroups.link('starlit_electronics:chip', {
title = 'Chip', icon = 'starlit-item-chip.png';
description = 'Standardized data storage and compute modules';
})
E.schematicGroups.link('starlit_electronics:battery', {
title = 'Battery', icon = 'starlit-item-battery.png';
description = 'Portable power storage cells are essential to all aspects of survival';
})
E.schematicGroups.link('starlit_electronics:decayCell', {
title = 'Decay Cell', icon = 'starlit-item-decaycell.png';
description = "Radioisotope generators can pack much more power into a smaller amount of space than conventional batteries, but they can't be recharged, dump power and heat whether they're in use or not, and their power yield drops towards zero over their usable lifetime.";
})
-------------------------
-- batteries & dynamos --
-------------------------
E.battery = {}
local function accessor(ty, fn)
return function(stack, ...)
local function fail()
error(string.format('object %q is not a %s', stack:get_name(), ty))
end
if not stack or stack:is_empty() then fail() end
if minetest.get_item_group(stack:get_name(), ty) == 0 then fail() end
return fn(stack,
stack:get_definition()._starlit[ty],
stack:get_meta(), ...)
end
end
-- return a wear level that won't destroy the item
-- local function safeWear(fac) return math.min(math.max(fac,0),1) * 0xFFFE end
-- local function safeWearToFac(w) return w/0xFFFE end
E.battery.update = accessor('battery', function(stack, batClass, meta)
-- local cap = E.battery.capacity(stack)
local charge = meta:get_float 'starlit_electronics:battery_charge'
meta:set_string('count_meta', string.format('%s%%', math.floor(charge * 100)))
meta:set_int('count_alignment', 14)
end)
-- E.battery.capacity(bat) --> charge (J)
E.battery.capacity = accessor('battery', function(stack, batClass, meta)
local dmg = meta:get_int 'starlit_electronics:battery_degrade' -- µJ/μW
local dmg_J = dmg / 1000
return (batClass.capacity - dmg_J)
end)
-- E.battery.charge(bat) --> charge (J)
E.battery.charge = accessor('battery', function(stack, batClass, meta)
local fac = meta:get_float 'starlit_electronics:battery_charge'
-- local fac = 1 - safeWearToFac(stack:get_wear())
return E.battery.capacity(stack) * fac
end)
-- E.battery.dischargeRate(bat) --> dischargeRate (W)
E.battery.dischargeRate = accessor('battery', function(stack, batClass, meta)
local dmg = meta:get_int 'starlit_electronics:battery_degrade' -- µJ/μW
local dmg_W = dmg / 1000
return batClass.dischargeRate - dmg_W
end);
-- E.battery.drawCurrent(bat, power, time, test) --> supply (J), wasteHeat (J)
-- bat = battery stack
-- power J = joules of energy user wishes to consume
-- time s = the amount of time available for this transaction
-- supply J = how much power was actually provided in $time seconds
-- wasteHeat J = how heat is generated in the process
-- test = if true, the battery is not actually modified
E.battery.drawCurrent = accessor('battery', function(s, bc, m, power, time, test)
local ch = E.battery.charge(s)
local maxPower = math.min(E.battery.dischargeRate(s)*time, power, ch)
ch = ch - maxPower
if not test then
local degrade = m:get_int 'starlit_electronics:battery_degrade' or 0
degrade = degrade + maxPower * bc.decay
-- for each joule of power drawn, capacity degrades by `decay` J
-- this should ordinarily be on the order of mJ or smaller
m:set_int('starlit_electronics:battery_degrade', degrade)
-- s:set_wear(safeWear(1 - (ch / E.battery.capacity(s))))
m:set_float('starlit_electronics:battery_charge', ch / E.battery.capacity(s))
E.battery.update(s)
end
return maxPower, 0 -- FIXME specify waste heat
end)
-- E.battery.recharge(bat, power, time) --> draw (J)
-- bat = battery stack
-- power J = joules of energy user wishes to charge the battery with
-- time s = the amount of time available for this transaction
-- draw J = how much power was actually drawn in $time seconds
E.battery.recharge = accessor('battery', function(s, bc, m, power, time)
local ch = E.battery.charge(s)
local cap = E.battery.capacity(s)
local maxPower = math.min(E.battery.dischargeRate(s)*time, power)
local total = math.min(ch + maxPower, cap)
-- s:set_wear(safeWear(1 - (total/cap)))
m:set_float('starlit_electronics:battery_charge', total/cap)
E.battery.update(s)
return maxPower, 0 -- FIXME
end)
E.battery.setCharge = accessor('battery', function(s, bc, m, newPower)
local cap = E.battery.capacity(s)
local power = math.min(cap, newPower)
-- s:set_wear(safeWear(1 - (power/cap)))
m:set_float('starlit_electronics:battery_charge', power/cap)
E.battery.update(s)
end)
E.battery.setChargeF = accessor('battery', function(s, bc, m, newPowerF)
local power = math.min(1.0, newPowerF)
m:set_float('starlit_electronics:battery_charge', power)
E.battery.update(s)
end)
E.dynamo = { kind = {} }
E.dynamo.drawCurrent = accessor('dynamo', function(s,c,m, power, time, test)
return c.vtable.drawCurrent(s, power, time, test)
end)
E.dynamo.totalPower = accessor('dynamo', function(s,c,m) return c.vtable.totalPower(s) end)
E.dynamo.dischargeRate = accessor('dynamo', function(s,c,m) return c.vtable.dischargeRate (s) end)
E.dynamo.initialPower = accessor('dynamo', function(s,c,m) return c.vtable.initialPower(s) end)
E.dynamo.wasteHeat = accessor('dynamo', function(s,c,m) return c.vtable.wasteHeat(s) end)
-- baseline waste heat, produced whether or not power is being drawn. for batteries this is 0, but for
-- radiothermal generators it may be high
E.dynamo.kind.battery = {
drawCurrent = E.battery.drawCurrent;
totalPower = E.battery.charge;
initialPower = E.battery.capacity;
dischargeRate = E.battery.dischargeRate;
wasteHeat = function() return 0 end;
};
starlit.item.battery.foreach('starlit_electronics:battery-gen', {}, function(id, def)
minetest.register_tool(id, {
short_description = def.name;
groups = { battery = 1; dynamo = 1; electronic = 1; };
inventory_image = def.img or 'starlit-item-battery.png';
description = starlit.ui.tooltip {
title = def.name;
desc = def.desc;
color = lib.color(0,.2,1);
props = {
{ title = 'Optimal Capacity', affinity = 'info';
desc = lib.math.siUI('J', def.capacity) };
{ title = 'Discharge Rate', affinity = 'info';
desc = lib.math.siUI('W', def.dischargeRate) };
{ title = 'Charge Efficiency', affinity = 'info';
desc = string.format('%s%%', (1-def.leak) * 100) };
{ title = 'Size', affinity = 'info';
desc = lib.math.siUI('m', def.fab.size.print) };
};
};
_starlit = {
event = {
create = function(st, how)
--[[if not how.gift then -- cheap hack to make starting batteries fully charged
E.battery.setCharge(st, 0)
end]]
E.battery.update(st)
end;
};
fab = def.fab;
dynamo = {
vtable = E.dynamo.kind.battery;
};
battery = def;
};
})
end)
-- to use the power functions, consider the following situation. you have
-- a high-tier battery charger that can draw 100kW. (for simplicity, assume
-- it supports only one battery). if you install a low-tier battery, and
-- the charging callback is called every five seconds, you might use
-- a `recharge` call that looks like
--
-- starlit.mod.electronics.battery.recharge(bat, 5 * 100*1e4, 5)
--
-- this would offer the battery 500kJ over five seconds. the battery will
-- determine how much power it can actually make use of in 5 five seconds,
-- and then return that amount.
--
-- always remember to save the battery back to its inventory slot after
-- modifying its ItemStack with one of these functions!
-- battery types
-- supercapacitor: low capacity, no degrade, high dischargeRate, no leak
-- chemical: high capacity, high degrade, mid dischargeRate, low leak
-- battery tiers
-- makeshift: cheap, weak, low quality
-- imperial ("da red wunz go fasta"): powerful, low quality
-- commune ("snooty sophisticates"): limited power, high quality, expensive
-- usukwinya ("value engineering"): high power, mid quality, affordable
-- eluthrai ("uncompromising"): high power, high quality, wildly expensive
-- firstborn ("god-tier"): exceptional
local batteryTiers = {
makeshift = {
name = 'Makeshift'; capacity = .5, decay = 3, leak = 2, dischargeRate = 1,
fab = starlit.type.fab {
element = {copper=10};
};
desc = "Every attosecond this electrical abomination doesn't explode in your face is but the unearned grace of the Wild Gods.";
complexity = 1;
sw = {rarity = 1};
};
imperial = {
name = 'Imperial'; capacity = 2, decay = 2, leak = 2, dischargeRate = 2;
fab = starlit.type.fab {
element = {copper=15, iron = 20};
size = { print = 0.1 };
};
desc = "The Empire's native technology is a lumbering titan: bulky, inefficient, unreliable, ugly, and awesomely powerful. Their batteries are no exception, with raw capacity and throughput that exceed even Usukinwya designs.";
drm = 1;
complexity = 2;
sw = {rarity = 2};
};
commune = {
name = 'Commune'; capacity = 1, decay = .5, leak = .2, dischargeRate = 1;
fab = starlit.type.fab {
element = {vanadium = 50};
metal = {steel=10};
size = { print = 0.05 };
};
desc = "The Commune's proprietary battery designs prioritize reliability, compactness, and maintenance concerns above raw throughput, with an elegance of engineering and design that would make a Su'ikuri cry.";
complexity = 5;
sw = {rarity = 3};
};
usukwinya = {
name = 'Usukwinya'; capacity = 2, decay = 1, leak = 1, dischargeRate = 1.5,
fab = starlit.type.fab {
element = {argon=10};
metal = {vanadium=30};
size = { print = 0.07 };
};
desc = "A race of consummate value engineers, the Usukwinya have spent thousands of years refining their tech to be as cheap to build as possible, without compromising much on quality. The Tradebirds drive an infamously hard bargain, but their batteries are more than worth their meagre cost.";
drm = 2;
sw = {rarity = 10};
complexity = 15;
};
eluthrai = {
name = 'Eluthrai'; capacity = 3, decay = .4, leak = .1, dischargeRate = 1.5,
fab = starlit.type.fab {
element = {beryllium=20, platinum=20, technetium = 1};
metal = {cinderstone = 10};
size = { print = 0.03 };
};
desc = "The uncompromising Eluthrai are never satisfied until every quantifiable characteristic of their tech is maximally optimised down to the picoscale. Their batteries are some of the best in the Reach, and unquestionably the most expensive -- especially for those lesser races trying to copy the designs without the benefit of the sublime autofabricator ecosystem of the Eluthrai themselves.";
complexity = 200;
sw = {rarity = 0}; -- you think you're gonna buy eluthran schematics on SuperDiscountNanoWare.space??
};
firstborn = {
name = 'Firstborn'; capacity = 5, decay = 0.1, leak = 0, dischargeRate = 3;
fab = starlit.type.fab {
element = {neodymium=20, xenon=150, technetium=5};
metal = {sunsteel = 10};
crystal = {astrite = 1};
size = { print = 0.05 };
};
desc = "Firstborn engineering seamlessly merges psionic effects with a mastery of the physical universe unattained by even the greatest of the living Starsouls. Their batteries reach levels of performance that strongly imply Quantum Gravity Theory -- and several major holy books -- need to be rewritten. From the ground up.";
complexity = 1000;
sw = {rarity = 0}; -- lol no
};
}
local batterySizes = {
small = {name = 'Small', capacity = .5, dischargeRate = .5, complexity = 1, matMult = .5, fab = starlit.type.fab {size={print=0.1}}};
mid = { capacity = 1, dischargeRate = 1, complexity = 1, matMult = 1, fab = starlit.type.fab {size={print=0.3}}};
large = {name = 'Large', capacity = 2, dischargeRate = 1.5, complexity = 1, matMult = 1.5, fab = starlit.type.fab {size={print=0.5}}};
huge = {name = 'Huge', capacity = 3, dischargeRate = 2, complexity = 1, matMult = 2, fab = starlit.type.fab {size={print=0.8}}};
}
local batteryTypes = {
supercapacitor = {
name = 'Supercapacitor';
desc = 'Room-temperature superconductors make for very reliable, high-dischargeRate, but low-capacity batteries.';
fab = starlit.type.fab {
metal = { enodium = 5 };
size = {print=0.8};
};
sw = {
cost = {
cycles = 5e9; -- 5 bil cycles
ram = 10e9; -- 10GB
};
pgmSize = 2e9; -- 2GB
rarity = 5;
};
capacity = 50e3, dischargeRate = 1000;
leak = 0, decay = 1e-6;
complexity = 3;
};
chemical = {
name = 'Chemical';
desc = '';
fab = starlit.type.fab {
element = { lithium = 3 };
size = {print=1.0};
};
sw = {
cost = {
cycles = 1e9; -- 1 bil cycles
ram = 2e9; -- 2GB
};
pgmSize = 512e6; -- 512MB
rarity = 2;
};
capacity = 200e3, dischargeRate = 200;
leak = 0.2, decay = 1e-2;
complexity = 1;
};
carbon = {
name = 'Carbon';
desc = 'Carbon nanotubes form the basis of many important metamaterials, chief among them power-polymer.';
capacity = 1;
fab = starlit.type.fab {
element = { carbon = 40 };
size = {print=0.5};
};
sw = {
cost = {
cycles = 50e9; -- 50 bil cycles
ram = 64e9; -- 64GB
};
pgmSize = 1e9; -- 1GB
rarity = 10;
};
capacity = 100e3, dischargeRate = 500;
leak = 0.1, decay = 1e-3;
complexity = 10;
};
hybrid = {
name = 'Hybrid';
desc = '';
capacity = 1;
fab = starlit.type.fab {
element = {
lithium = 10;
carbon = 20;
};
size = {print=1.5};
};
sw = {
cost = {
cycles = 65e9; -- 65 bil cycles
ram = 96e9; -- 96GB
};
pgmSize = 5e9; -- 5GB
rarity = 15;
};
capacity = 300e3, dischargeRate = 350;
leak = 0.3, decay = 1e-5;
complexity = 30;
};
}
local function elemath(dest, src, mult)
dest = dest or {}
for k,v in pairs(src) do
if not dest[k] then dest[k] = 0 end
dest[k] = dest[k] + v*mult
end
return dest
end
for bTypeName, bType in pairs(batteryTypes) do
for bTierName, bTier in pairs(batteryTiers) do
for bSizeName, bSize in pairs(batterySizes) do
-- elemath(elementCost, bType.fab.element or {}, bSize.matMult)
-- elemath(elementCost, bTier.fab.element or {}, bSize.matMult)
-- elemath(metalCost, bType.fab.metal or {}, bSize.matMult)
-- elemath(metalCost, bTier.fab.metal or {}, bSize.matMult)
local fab = bType.fab + bTier.fab + bSize.fab + starlit.type.fab {
element = {copper = 10, silicon = 5};
}
local baseID = string.format('battery_%s_%s_%s',
bTypeName, bTierName, bSizeName)
local id = 'starlit_electronics:'..baseID
local name = string.format('%s %s Battery', bTier.name, bType.name)
if bSize.name then name = bSize.name .. ' ' .. name end
local function batStat(s)
if s == 'size' then
--return bType.fab[s] * (bTier.fab[s] or 1) * (bSize.fab[s] or 1)
return fab.size and fab.size.print or 1
else
return bType[s] * (bTier[s] or 1) * (bSize[s] or 1)
end
end
local swID = 'starlit_electronics:schematic_'..baseID
fab.reverseEngineer = {
complexity = bTier.complexity * bSize.complexity * bType.complexity;
sw = swID;
}
fab.flag = {print=true}
starlit.item.battery.link(id, {
name = name;
desc = table.concat({
bType.desc or '';
bTier.desc or '';
bSize.desc or '';
}, ' ');
fab = fab;
capacity = batStat 'capacity';
dischargeRate = batStat 'dischargeRate';
leak = batStat 'leak';
decay = batStat 'decay';
})
local rare
if bType.sw.rarity == 0 or bTier.sw.rarity == 0 then
-- rarity is measured such that the player has a 1/r
-- chance of finding a given item, or if r=0, no chance
-- whatsoever (the sw must be obtained e.g. by reverse-
-- engineering alien tech)
rare = 0
else
rare = bType.sw.rarity + bTier.sw.rarity
end
starlit.item.sw.link(swID, {
kind = 'schematic';
name = name .. ' Schematic';
input = fab;
output = id;
size = bType.sw.pgmSize;
cost = bType.sw.cost;
rarity = rare;
})
E.schematicGroupLink('starlit_electronics:battery', swID)
end end end
-----------
-- chips --
-----------
E.sw = {}
function E.sw.findSchematicFor(item)
local id = ItemStack(item):get_name()
local fm = minetest.registered_items[id]._starlit
if not (fm and fm.fab and fm.fab.reverseEngineer) then return nil end
local id = fm.fab.reverseEngineer.sw
return id, starlit.item.sw.db[id]
end
E.chip = { file = {} }
do local T,G = lib.marshal.t, lib.marshal.g
-- love too reinvent unions from first principles
E.chip.data = G.struct {
label = T.str;
uuid = T.u64;
files = G.array(16, G.class(G.struct {
kind = G.enum {
'sw'; -- a piece of installed software
'note'; -- a user-readable text file
'research'; -- saved RE progress
'genome'; -- for use with plant biosequencer?
'blob'; -- opaque binary blob, so 3d-pty mods can use the
-- file mechanism to store arbirary data.
};
drm = T.u8; -- inhibit copying
name = T.str;
body = T.text;
}, function(file) -- enc
local b = E.chip.file[file.kind].enc(file.body)
return {
kind = file.kind;
drm = file.drm;
name = file.name;
body = b;
}
end, function(file) -- dec
local f, ns = E.chip.file[file.kind].dec(file.body)
file.body = f
return file, ns
end));
bootSlot = T.u8; -- indexes into files; 0 = no bootloader
}
E.chip.file.sw = G.struct {
pgmId = T.str;
conf = G.array(16, G.struct {
key = T.str, value = T.str;
});
}
E.chip.file.note = G.struct {
author = T.str;
entries = G.array(16, G.struct {
title = T.str;
body = T.str;
});
}
E.chip.file.research = G.struct {
itemId = T.str;
progress = T.clamp;
}
E.chip.file.blob = G.struct {
kind = T.str; -- MT ID that identifies a blob file type belonging to an external mod
size = T.u8; -- this must be manually reported since we don't know how to evaluate it
data = T.text;
}
function E.chip.fileSize(file)
-- boy howdy
if file.kind == 'blob' then
return file.body.size
elseif file.kind == 'note' then
local sz = 0x10 + #file.body.author
for _, e in pairs(file.body.entries) do
sz = sz + #e.title + #e.body + 0x10 -- header overhead
end
return sz
elseif file.kind == 'research' then
local re = assert(minetest.registered_items[file.body.itemId]._starlit.fab.reverseEngineer)
return starlit.item.sw.db[re.sw].size * file.body.progress
elseif file.kind == 'sw' then
return starlit.item.sw.db[file.body.pgmId].size
elseif file.kind == 'genome' then
return 0 -- TODO
end
end
local metaKey = 'starlit_electronics:chip'
function E.chip.read(chip)
local m = chip:get_meta()
local blob = m:get_string(metaKey)
if blob and blob ~= '' then
return E.chip.data.dec(lib.str.meta_dearmor(blob))
else -- prepare to format the chip
return {
label = '';
bootSlot = 0;
uuid = math.floor(math.random(0,2^32));
files = {};
}
end
end
function E.chip.write(chip, data)
local m = chip:get_meta()
m:set_string(metaKey, lib.str.meta_armor(E.chip.data.enc(data)))
E.chip.update(chip)
end
function E.chip.fileOpen(chip, inode, fn)
local c = E.chip.read(chip)
if fn(c.files[inode]) then
E.chip.write(chip, c)
return true
end
return false
end
function E.chip.fileWrite(chip, inode, file)
local c = E.chip.read(chip)
c.files[inode] = file
E.chip.write(chip, c)
end
function E.chip.usedSpace(chip, d)
d = d or E.chip.read(chip)
local sz = 0
for _, f in pairs(d.files) do
sz = sz + E.chip.fileSize(f)
end
return sz
end
function E.chip.freeSpace(chip, d)
local used = E.chip.usedSpace(chip,d)
local max = assert(chip:get_definition()._starlit.chip.flash)
return max - used
end
function E.chip.install(chip, file)
-- remember to write out the itemstack after using this function!
local d = E.chip.read(chip)
if E.chip.freeSpace(chip, d) - E.chip.fileSize(file) >= 0 then
table.insert(d.files, file)
E.chip.write(chip, d)
return true
else
return false
end
end
end
function E.chip.files(ch)
local m = ch:get_meta()
if not m:contains 'starlit_electronics:chip' then
return nil
end
local data = E.chip.read(ch)
local f = 0
return function()
f = f + 1
return data.files[f], f
end
end
function E.chip.describe(ch, defOnly)
local def, data if defOnly then
def, data = ch, {}
else
def = ch:get_definition()
local m = ch:get_meta()
if m:contains 'starlit_electronics:chip' then
data = E.chip.read(ch)
else
data = {}
defOnly = true
end
def = assert(def._starlit.chip)
end
local props = {
{title = 'Clock Rate', affinity = 'info';
desc = lib.math.siUI('Hz', def.clockRate)};
{title = 'RAM', affinity = 'info';
desc = lib.math.siUI('B', def.ram)};
}
if not defOnly then
table.insert(props, {
title = 'Free Storage', affinity = 'info';
desc = lib.math.siUI('B', E.chip.freeSpace(ch, data)) .. ' / '
.. lib.math.siUI('B', def.flash);
})
local swAffMap = {
schematic = 'schematic';
suitPower = 'ability';
driver = 'driver';
}
for i, e in ipairs(data.files) do
local aff = 'neutral'
local name = e.name
local disabled = false
if e.kind == 'sw' then
for _,cf in pairs(e.body.conf) do
if cf.key == 'disable' and cf.value == 'yes' then
disabled = true
break
end
end
local sw = starlit.item.sw.db[e.body.pgmId]
aff = swAffMap[sw.kind] or 'good'
if name == '' then name = sw.name end
end
name = name or '<???>'
table.insert(props, disabled and {
title = name;
affinity = aff;
desc = '<off>';
} or {
--title = name;
affinity = aff;
desc = name;
})
end
else
table.insert(props, {
title = 'Flash Storage', affinity = 'info';
desc = lib.math.siUI('B', def.flash);
})
end
return starlit.ui.tooltip {
title = data.label and data.label~='' and string.format('<%s>', data.label) or def.name;
color = lib.color(.6,.6,.6);
desc = def.desc;
props = props;
};
end
function E.chip.update(chip)
chip:get_meta():set_string('description', E.chip.describe(chip))
end
starlit.item.chip.foreach('starlit_electronics:chip-gen', {}, function(id, def)
minetest.register_craftitem(id, {
short_description = def.name;
description = E.chip.describe(def, true);
inventory_image = def.img or 'starlit-item-chip.png';
groups = {chip = 1};
_starlit = {
fab = def.fab;
chip = def;
};
})
end)
-- in case other mods want to define their own tiers
E.chip.tiers = lib.registry.mk 'starlit_electronics:chipTiers'
E.chip.tiers.meld {
-- GP chips
tiny = {name = 'Tiny Chip', clockRate = 512e3, flash = 4096, ram = 1024, powerEfficiency = 1e9, size = 1};
small = {name = 'Small Chip', clockRate = 128e6, flash = 512e6, ram = 512e6, powerEfficiency = 1e8, size = 3};
med = {name = 'Chip', clockRate = 1e9, flash = 4e9, ram = 4e9, powerEfficiency = 1e7, size = 6};
large = {name = 'Large Chip', clockRate = 2e9, flash = 8e9, ram = 8e9, powerEfficiency = 1e6, size = 8};
-- specialized chips
compute = {name = 'Compute Chip', clockRate = 4e9, flash = 24e6, ram = 64e9, powerEfficiency = 1e8, size = 4};
data = {name = 'Data Chip', clockRate = 128e3, flash = 2e12, ram = 32e3, powerEfficiency = 1e5, size = 4};
lp = {name = 'Low-Power Chip', clockRate = 128e6, flash = 64e6, ram = 1e9, powerEfficiency = 1e10, size = 4};
carbon = {name = 'Carbon Chip', clockRate = 64e6, flash = 32e6, ram = 2e6, powerEfficiency = 2e9, size = 2, circ='carbon'};
}
E.chip.tiers.foreach('starlit_electronics:genChips', {}, function(id, t)
id = t.id or string.format('%s:chip_%s', minetest.get_current_modname(), id)
local circMat = t.circ or 'silicon';
starlit.item.chip.link(id, {
name = t.name;
clockRate = t.clockRate;
flash = t.flash;
ram = t.ram;
powerEfficiency = t.powerEfficiency; -- cycles per joule
fab = starlit.type.fab {
flag = {
silicompile = true;
};
time = {
silicompile = t.size * 24*60;
};
cost = {
energy = 50e3 + t.size * 15e2;
};
element = {
[circMat] = 50 * t.size;
copper = 30;
gold = 15;
};
};
})
end)
function E.chip.findBest(test, ...)
local chip, bestFitness
for id, c in pairs(starlit.item.chip.db) do
local fit, fitness = test(c, ...)
if fit and (bestFitness == nil or fitness > bestFitness) then
chip, bestFitness = id, fitness
end
end
return chip, starlit.item.chip.db[chip], bestFitness
end
function E.chip.findForStorage(sz)
return E.chip.findBest(function(c)
return c.flash >= sz, -math.abs(c.flash - sz)
end)
end
function E.chip.sumCompute(chips)
local c = {
cycles = 0;
ram = 0;
flashFree = 0;
powerEfficiency = 0;
}
local n = 0
for _, e in pairs(chips) do
n = n + 1
if not e:is_empty() then
local ch = e:get_definition()._starlit.chip
c.cycles = c.cycles + ch.clockRate
c.ram = c.ram + ch.clockRate
c.flashFree = c.flashFree + E.chip.freeSpace(e)
c.powerEfficiency = c.powerEfficiency + ch.powerEfficiency
end
end
if n > 0 then c.powerEfficiency = c.powerEfficiency / n end
return c
end
E.chip.fileHandle = lib.class {
__name = 'starlit_electronics:chip.fileHandle';
construct = function(chip, inode) -- stack, int --> fd
return { chip = chip, inode = inode }
end;
__index = {
read = function(self)
local dat = E.chip.read(self.chip)
return dat.files[self.inode]
end;
write = function(self,data)
-- print('writing', self.chip, self.inode)
return E.chip.fileWrite(self.chip, self.inode, data)
end;
erase = function(self)
local dat = E.chip.read(self.chip)
table.remove(dat.files, self.inode)
E.chip.write(self.chip, dat)
self.inode = nil
end;
open = function(self,fn)
return E.chip.fileOpen(self.chip, self.inode, fn)
end;
};
}
function E.chip.usableSoftware(chips,pgm,pred)
pred = pred or function() return true end
local comp = E.chip.sumCompute(chips)
local r = {}
local unusable = {}
local sw if pgm then
if type(pgm) == 'string' then
pgm = {starlit.item.sw.db[pgm]}
end
sw = pgm
else
sw = {}
for i, e in ipairs(chips) do
if (not e:is_empty())
and minetest.get_item_group(e:get_name(), 'chip') ~= 0
then
for fl, inode in E.chip.files(e) do
if fl.kind == 'sw' then
local s = starlit.item.sw.db[fl.body.pgmId]
table.insert(sw, {
sw = s, chip = e, chipSlot = i;
file = fl, inode = inode;
})
end
end
end
end
end
for _, s in pairs(sw) do
if s.sw.cost.ram <= comp.ram and pred(s) then
table.insert(r, {
sw = s.sw;
chip = s.chip, chipSlot = s.chipSlot;
file = s.file;
fd = E.chip.fileHandle(s.chip, s.inode);
speed = s.sw.cost.cycles / comp.cycles;
powerCost = s.sw.cost.cycles / comp.powerEfficiency;
comp = comp;
})
else
table.insert(unusable, {
sw = s.sw;
chip = s.chip;
ramNeeded = s.sw.cost.ram - comp.ram;
})
end
end
return r, unusable
end
starlit.include 'sw'