local lib = starlit.mod.lib
local suitStore = starlit.store.suitMeta
starlit.item.suit = lib.registry.mk 'starlit:suits';
-- note that this cannot be persisted as a reference to a particular suit in the world
local function suitContainer(stack, inv)
return starlit.item.container(stack, inv, {
pfx = 'starlit_suit'
})
end
starlit.type.suit = lib.class {
name = 'starlit:suit';
construct = function(stack)
return {
item = stack;
inv = suitStore(stack);
}
end;
__index = {
powerState = function(self)
local s = self.item
if not s then return nil end
local m = s:get_meta():get_int('starlit:power_mode')
if m == 1 then return 'on'
elseif m == 2 then return 'powerSave'
else return 'off' end
end;
powerStateSet = function(self, state)
local s = self.item
if not s then return nil end
local m
if state == 'on' then m = 1 -- TODO check power level
elseif state == 'powerSave' then m = 2
else m = 0 end
if self:powerLeft() <= 0 then m = 0 end
s:get_meta():set_int('starlit:power_mode', m)
end;
powerLeft = function(self)
local batteries = self.inv.read 'batteries'
local power = 0
for idx, slot in pairs(batteries) do
power = power + starlit.mod.electronics.dynamo.totalPower(slot)
end
return power
end;
powerCapacity = function(self)
local batteries = self.inv.read 'batteries'
local power = 0
for idx, slot in pairs(batteries) do
power = power + starlit.mod.electronics.dynamo.initialPower(slot)
end
return power
end;
maxPowerUse = function(self)
local batteries = self.inv.read 'batteries'
local w = 0
for idx, slot in pairs(batteries) do
w = w + starlit.mod.electronics.dynamo.dischargeRate(slot)
end
return w
end;
onReconfigure = function(self, inv)
-- apply any changes to item metadata and export any subinventories
-- to the provided invref, as they may have changed
local sc = starlit.item.container(self.item, inv, {pfx = 'starlit_suit'})
sc:push()
-- self:pullCanisters(inv)
end;
onItemMove = function(self, user, list, act, what)
-- called when the suit inventory is changed
if act == 'put' then
if list == 'starlit_suit_bat' then
user:suitSound('starlit-suit-battery-in')
elseif list == 'starlit_suit_chips' then
user:suitSound('starlit-suit-chip-in')
elseif list == 'starlit_suit_canisters' then
user:suitSound('starlit-insert-snap')
end
elseif act == 'take' then
if list == 'starlit_suit_bat' then
user:suitSound('starlit-insert-snap')
elseif list == 'starlit_suit_chips' then
--user:suitSound('starlit-suit-chip-out')
elseif list == 'starlit_suit_canisters' then
user:suitSound('starlit-insert-snap')
end
end
end;
def = function(self)
return self.item:get_definition()._starlit.suit
end;
--[[
pullCanisters = function(self, inv)
starlit.item.container.dropPrefix(inv, 'starlit_canister')
self:forCanisters(inv, function(sc) sc:pull() end)
end;
pushCanisters = function(self, inv, st, i)
self:forCanisters(inv, function(sc)
sc:push()
return true
end)
end;
forCanisters = function(self, inv, fn)
local cans = inv:get_list 'starlit_suit_canisters'
if cans and next(cans) then for i, st in ipairs(cans) do
if not st:is_empty() then
local pfx = 'starlit_canister_' .. tostring(i)
local sc = starlit.item.container(st, inv, {pfx = pfx})
if fn(sc, st, i, pfx) then
inv:set_stack('starlit_suit_canisters', i, st)
end
end
end end
end;
]]
establishInventories = function(self, obj)
local inv = obj:get_inventory()
local ct = suitContainer(self.item, inv)
ct:pull()
--[[
self:pullCanisters(inv)
local def = self:def()
local sst = suitStore(self.item)
local function readList(listName, prop)
inv:set_size(listName, def.slots[prop])
if def.slots[prop] > 0 then
local lst = sst.read(prop)
inv:set_list(listName, lst)
end
end
readList('starlit_suit_chips', 'chips')
readList('starlit_suit_bat', 'batteries')
readList('starlit_suit_guns', 'guns')
readList('starlit_suit_elem', 'elements')
readList('starlit_suit_ammo', 'ammo')
]]
end;
};
}
-- TODO find a better place for this!
starlit.type.suit.purgeInventories = function(obj)
local inv = obj:get_inventory()
starlit.item.container.dropPrefix(inv, 'starlit_suit')
starlit.item.container.dropPrefix(inv, 'starlit_canister')
--[[inv:set_size('starlit_suit_bat', 0)
inv:set_size('starlit_suit_guns', 0)
inv:set_size('starlit_suit_chips', 0)
inv:set_size('starlit_suit_ammo', 0)
inv:set_size('starlit_suit_elem', 0)
]]
end
starlit.item.suit.foreach('starlit:suit-gen', {}, function(id, def)
local icon = lib.image(def.img or 'starlit-item-suit.png')
local iconColor = def.iconColor
if not iconColor then
iconColor = (def.tex and def.tex.plate and def.tex.plate.tint)
or def.defaultColor
iconColor = iconColor:to_hsl()
iconColor.lum = 0
end
if iconColor then icon = icon:shift(iconColor) end
if not def.adorn then
function def.adorn(a, item, persona)
local function imageFor(pfx)
return lib.image(string.format("%s-%s-%s.png", pfx, persona.species, persona.speciesVariant))
end
if not def.tex then return end
a.suit = {}
for name, t in pairs(def.tex) do
local img = imageFor(t.id)
local color
local cstr = item:get_meta():get_string('starlit:tint_suit_' .. name)
if cstr and cstr ~= '' then
color = lib.color.unmarshal(cstr)
elseif t.tint then
color = t.tint or def.defaultColor
end
if color then
local hsl = color:to_hsl()
local adjusted = {
hue = hsl.hue;
sat = hsl.sat * 2 - 1;
lum = hsl.lum * 2 - 1;
}
img = img:shift(adjusted)
end
a.suit[name] = img
end
end
end
minetest.register_tool(id, {
short_description = def.name;
description = starlit.ui.tooltip {
title = def.name;
desc = def.desc;
color = lib.color(.1, .7, 1);
};
groups = {
suit = 1;
inv = 1; -- has inventories
batteryPowered = 1; -- has a battery inv
programmable = 1; -- has a chip inv
};
on_use = function(st, luser, pointed)
local user = starlit.activeUsers[luser:get_player_name()]
if not user then return end
-- have mercy on users who've lost their suits and wound
-- up naked and dying of exposure
if user:naked() then
local ss = st:take_item(1)
user:changeSuit(starlit.type.suit(ss))
user:suitSound('starlit-suit-don')
return st
end
end;
inventory_image = icon:render();
_starlit = {
container = {
workbench = {
order = {'batteries','chips','guns','ammo'}
};
list = {
bat = {
key = 'starlit:suit_slots_bat';
accept = 'dynamo';
sz = def.slots.batteries;
};
chips = {
key = 'starlit:suit_slots_chips';
accept = 'chip';
sz = def.slots.chips;
};
canisters = {
key = 'starlit:suit_slots_canisters';
accept = 'canister';
sz = def.slots.canisters;
};
guns = {
key = 'starlit:suit_slots_gun';
accept = 'weapon';
workbench = {
label = 'Weapon';
icon = 'starlit-ui-icon-gun';
color = lib.color(1,0,0);
};
sz = def.slots.guns;
};
ammo = {
key = 'starlit:suit_slots_ammo';
accept = 'ammo';
workbench = {
label = 'Ammunition';
color = lib.color(1,.5,0);
easySlots = true; -- all slots accessible on the go
};
sz = def.slots.ammo;
};
};
};
event = {
create = function(st,how)
local s = suitStore(st)
-- make sure there's a defined powerstate
starlit.type.suit(st):powerStateSet 'off'
suitContainer(st):clear()
--[[ populate meta tables
s.write('batteries', {})
s.write('guns', {})
s.write('ammo', {})
s.write('elements', {})
s.write('chips', {})]]
end;
};
suit = def;
};
});
end)
local slotProps = {
starlit_cfg = {
itemClass = 'inv';
};
starlit_suit_bat = {
suitSlot = true;
powerLock = true;
itemClass = 'dynamo';
};
starlit_suit_chips = {
suitSlot = true;
powerLock = true;
itemClass = 'chip';
};
starlit_suit_guns = {
suitSlot = true;
maintenanceNode = '';
itemClass = 'suitWeapon';
};
starlit_suit_ammo = {
suitSlot = true;
maintenanceNode = '';
itemClass = 'suitAmmo';
};
starlit_suit_canisters = {
suitSlot = true;
itemClass = 'canister';
};
}
minetest.register_allow_player_inventory_action(function(luser, act, inv, p)
local user = starlit.activeUsers[luser:get_player_name()]
local function grp(i,g)
return minetest.get_item_group(i:get_name(), g) ~= 0
end
local function checkBaseRestrictions(list)
local restrictions = slotProps[list]
if not restrictions then return nil, true end
if restrictions.suitSlot then
if user:naked() then return restrictions, false end
end
if restrictions.powerLock then
if user:getSuit():powerState() ~= 'off' then return restrictions, false end
end
return restrictions, true
end
local function itemFits(item, list)
local rst, ok = checkBaseRestrictions(list)
if not ok then return false end
if rst == nil then return true end
if rst.itemClass and not grp(item, rst.itemClass) then
return false
end
if rst.maintenanceNode then return false end
-- FIXME figure out best way to identify when the player is using a maintenance node
if grp(item, 'specialInventory') then
end
return true
end
local function itemCanLeave(item, list)
local rst, ok = checkBaseRestrictions(list)
if not ok then return false end
if rst == nil then return true end
if minetest.get_item_group(item:get_name(), 'specialInventory') then
end
if rst.maintenanceNode then return false end
return true
end
if act == 'move' then
local item = inv:get_stack(p.from_list, p.from_index)
if not (itemFits(item, p.to_list) and itemCanLeave(item, p.from_list)) then
return 0
end
elseif act == 'put' then
if not itemFits(p.stack, p.listname) then return 0 end
elseif act == 'take' then
if not itemCanLeave(p.stack, p.listname) then return 0 end
end
return true
end)
minetest.register_on_player_inventory_action(function(luser, act, inv, p)
local user = starlit.activeUsers[luser:get_player_name()]
local function slotChange(slot,a,item)
local s = slotProps[slot]
if slot == 'starlit_suit' then
user:updateSuit()
if user:naked() then
starlit.type.suit.purgeInventories(user.entity)
user.power.nano = {}
end
elseif s and s.suitSlot then
local s = user:getSuit()
s:onItemMove(user, slot, a, item)
s:onReconfigure(user.entity:get_inventory())
user:setSuit(s)
else return end
user:updateHUD()
end
if act == 'put' or act == 'take' then
local item = p.stack
slotChange(p.listname, act, item)
elseif act == 'move' then
local item = inv:get_stack(p.to_list, p.to_index)
slotChange(p.from_list, 'take', item)
slotChange(p.to_list, 'put', item)
end
end)
local suitInterval = 2.0
starlit.startJob('starlit:suit-software', suitInterval, function(delta)
local runState = {
pgmsRun = {};
flags = {};
}
for id, u in pairs(starlit.activeUsers) do
if not u:naked() then
local reconfSuit = false
local inv = u.entity:get_inventory()
local chips = inv:get_list('starlit_suit_chips')
local suitprog = starlit.mod.electronics.chip.usableSoftware(chips)
for _, prop in pairs(suitprog) do
local s = prop.sw
if s.kind == 'suitPower' and (s.powerKind == 'passive' or s.bgProc) and (not runState.pgmsRun[s]) then
local conf = prop.file.body.conf
local enabled = true
for _, e in ipairs(conf) do
if e.key == 'disable' and e.value == 'yes' then
enabled = false
break
end
end
local fn if s.powerKind == 'passive'
then fn = s.run
else fn = s.bgProc
end
function prop.saveConf(cfg) cfg = cfg or conf
prop.fd:write(cfg)
inv:set_stack('starlit_suit_chips', prop.chipSlot, prop.fd.chip)
reconfSuit = true
end
function prop.giveItem(st)
u:thrustUpon(st)
end
if enabled and fn(u, prop, suitInterval, runState) then
runState.pgmsRun[s] = true
end
end
end
if reconfSuit then
u:reconfigureSuit()
end
end
end
end)