local constants = {
xmit_wattage = 0.4;
-- the amount of power per second needed to transmit an item from
-- one displacer to another
rcpt_wattage = 0.15;
-- the amount of power needed to broadcast a receptor's availability
}
local gettxr = function(pos)
local txrcomps = {
'sorcery:displacer';
'sorcery:displacer_transmit_gem';
'sorcery:displacer_transmit_attune';
'sorcery:displacer_receive_gem';
'sorcery:displacer_receive_attune';
}
local devs = sorcery.lib.node.amass(pos,txrcomps,sorcery.lib.node.offsets.neighbors)
local r = {
receptacles = {};
connections = {};
counts = {
receptacles = 0;
transmitters = 0;
receptors = 0;
};
}
local getcode = function(pos)
local inv = minetest.get_meta(pos):get_inventory()
local code = {}
local empty = true
for i=1,inv:get_size('code') do
if not inv:get_stack('code',i):is_empty()
then empty = false end
code[i] = inv:get_stack('code',i):get_name()
end
if empty then return nil
else return code end
end
for pos, dev in pairs(devs) do
if dev == 'sorcery:displacer_receive_gem' then
r.counts.receptors = r.counts.receptors + 1
r.connections[#r.connections+1] = {
pos = pos; mode = 'receive';
code = getcode(pos); -- TODO retrieve code
}
elseif dev == 'sorcery:displacer_receive_attune' then
local tune = sorcery.attunement.verify(pos)
if tune then
r.counts.receptors = r.counts.receptors + 1
r.connections[#r.connections+1] = {
pos = pos; mode = 'receive';
partner = tune.partner;
}
end
elseif dev == 'sorcery:displacer_transmit_gem' then
r.counts.transmitters = r.counts.transmitters + 1
r.connections[#r.connections+1] = {
pos = pos; mode = 'transmit';
code = getcode(pos); -- TODO retrieve code
}
elseif dev == 'sorcery:displacer_transmit_attune' then
local tune = sorcery.attunement.verify(pos)
if tune then
r.counts.transmitters = r.counts.transmitters + 1
r.connections[#r.connections+1] = {
pos = pos; mode = 'transmit';
partner = tune.partner;
}
end
elseif dev == 'sorcery:displacer' then
r.counts.receptacles = r.counts.receptacles + 1
r.receptacles[#r.receptacles+1] = pos
end
end
return r
end
local autoselect = function(pos)
local dev = gettxr(pos)
local active
if dev.counts.receptors == 0 and dev.counts.transmitters == 1 then
active = dev.connections[1].pos
end
for _,rcp in pairs(dev.receptacles) do
local meta = minetest.get_meta(rcp)
meta:set_string('active-device',active and minetest.pos_to_string(active) or '')
end
return active ~= nil
end
minetest.register_node('sorcery:displacer', {
description = 'Displacer Receptacle';
paramtype2 = 'facedir';
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
minetest.get_node_timer(pos):start(1)
inv:set_size('cache', 6)
meta:set_string('infotext','displacer')
meta:set_string('active-device','')
meta:set_string('formspec', [[
formspec_version[3] size[10.25,8]
list[context;cache;3.125,0.25;3,2]
list[current_player;main;0.25,3;8,4]
listring[]
]])
end;
-- vararg wrapping necessary to discard the return value,
-- as a return value of true will prevent the item from
-- being removed from the user's inventory after placement
after_place_node = function(...) autoselect(...) end;
after_dig_node = function(...)
autoselect(...)
sorcery.lib.node.purge_container(...)
end;
on_metadata_inventory_put = function(pos)
minetest.get_node_timer(pos):start(1)
end;
on_timer = function(pos,delta)
local meta = minetest.get_meta(pos)
if not meta:contains('active-device') then return false end
local probe = sorcery.spell.probe(pos)
if probe.disjunction then return true end
local inv = meta:get_inventory()
if inv:is_empty('cache') then return false end
local dev = gettxr(pos)
local active = minetest.string_to_pos(meta:get_string('active-device'))
local ad
for _,d in pairs(dev.connections) do
if vector.equals(d.pos, active) then ad = d break end
end
if not ad then
meta:set_string('active-device','')
return false
end
local remote
if ad.partner then
remote = gettxr(ad.partner)
elseif ad.code then
local net = sorcery.farcaster.junction(pos,constants.xmit_wattage)
for _,n in pairs(net) do
for _,d in pairs(n.caps.net.devices.consume) do
if d.id == 'sorcery:displacer' then
local dp = sorcery.spell.probe(d.pos)
if not dp.disjunction then
local t = gettxr(d.pos)
for _,d in pairs(t.connections) do
if d.mode == 'receive' and d.code then
local match = true
for i=1,#d.code do
if d.code[i] ~= ad.code[i] then
match = false break
end
end
if match then
remote = t
break
end
end
end
end
end
if remote then break end
end
if remote then break end
end
end
if not remote then return false end
local n = sorcery.ley.netcaps(pos,delta,nil,constants.xmit_wattage)
if n.self.powerdraw == n.self.maxpower then
-- fully powered for transmission; find an object to transmit
local transmission
for i=1,inv:get_size('cache') do
local s = inv:get_stack('cache',i)
if not (s:is_empty() or minetest.get_item_group(s:get_name(), 'sorcery_nontranslocatable') ~= 0) then
local quantity = 1
local tq = minetest.get_item_group(s:get_name(), 'sorcery_translocate_pack')
if tq ~= 0 then quantity = math.min(tq, s:get_count()) end
transmission = s:take_item(quantity)
inv:set_stack('cache',i,s)
break
end
end
if not transmission then return false end
-- iterate through available receptacles and see if there's room
-- in any of them. otherwise, fail
for _,r in pairs(remote.receptacles) do
local i = minetest.get_meta(r):get_inventory()
transmission = i:add_item('cache',transmission)
minetest.sound_play('sorcery_zap', { gain = 0.5, pos = r })
if transmission:is_empty() then break end
end
if not transmission:is_empty() then inv:add_item('cache',transmission) else
minetest.sound_play('sorcery_zap', { gain = 0.5, pos = pos })
end
-- TODO add particle fx as well
return true
elseif n.maxpower >= n.self.maxpower then
-- other devices are currently drawing power and might stop,
-- making enough available for us; keep iterating just in case
return true
else
-- the system does not have the capability to generate
-- sufficient power, no point in continuing to fuck around
return false
end
end;
groups = {
cracky = 2;
sorcery_ley_device = 1;
sorcery_magitech = 1;
};
tiles = {
'sorcery_displacer_top.png';
'sorcery_displacer_top.png';
'sorcery_displacer_side.png';
'sorcery_displacer_side.png';
'sorcery_displacer_side.png';
'sorcery_displacer_front.png';
};
_sorcery = {
on_leychange = function(pos)
minetest.get_node_timer(pos):start(1)
end;
ley = {
mode = 'consume', affinity = {'mandatic'};
power = function(pos,time)
local meta = minetest.get_meta(pos)
local power = 0
if meta:contains('active-device') then
power = constants.xmit_wattage
end
local dev = gettxr(pos)
power = power + constants.rcpt_wattage * dev.counts.receptors
return (power / dev.counts.receptacles) * time
end;
};
};
})
for mode,m in pairs {
gem={
desc = 'Gem-Coded';
construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size('code',6)
meta:set_string('formspec', [[
formspec_version[3] size[10.25,7]
list[context;code;1.5,0.25;6,1]
list[current_player;main;0.25,1.75;8,4]
listring[]
]])
end;
allowput = function(pos,list,idx,stack)
if list == 'code' then
if sorcery.itemclass.get(stack:get_name(),'gem') then return 1 end
end
return 0
end;
};
attune={
desc = 'Attuned';
};
} do for kind,n in pairs {
transmit = {
name = 'Transmission';
button = function(pos)
minetest.sound_play('doors_steel_door_open', {pos = pos})
local n = minetest.get_node(pos)
local dev = gettxr(pos)
if dev.counts.receptacles > 0 then
for _,r in pairs(dev.receptacles) do
local m = minetest.get_meta(r)
m:set_string('active-device',minetest.pos_to_string(pos))
minetest.get_node_timer(r):start(1)
end
end
end;
attune = {
target = true, accepts = 'sorcery:displacer';
reciprocal = true;
};
};
receive = {
name = 'Reception';
attune = {
source = true, class = 'sorcery:displacer';
reciprocal = true;
}
};
} do local id = 'sorcery:displacer_' .. kind .. '_' .. mode
minetest.register_node(id, {
description = m.desc .. ' ' .. n.name .. ' Module';
paramtype2 = 'facedir';
tiles = {
'sorcery_displacer_top.png';
'sorcery_displacer_top.png';
'sorcery_displacer_side.png';
'sorcery_displacer_side.png';
'sorcery_displacer_side.png';
'sorcery_displacer_module_' .. kind .. '.png';
};
on_construct = m.construct;
on_rightclick = mode ~= 'gem' and n.button or nil;
on_punch = n.button and function(pos,node,puncher)
if puncher and puncher:get_wielded_item():is_empty() then
n.button(pos)
end
end or nil;
after_place_node = function(...)
autoselect(...)
-- these are not ley devices but they still affect the
-- energy consumption of the ley device they are attached
-- to, so we need to manually run the notification routine
-- when they are placed or dug
sorcery.ley.notify(...)
end;
allow_metadata_inventory_put = m.allowput;
_sorcery = {
attune = (mode == 'attune') and n.attune or nil;
};
after_dig_node = function(...)
autoselect(...)
sorcery.lib.node.purge_container(...)
sorcery.lib.node.notifyneighbors(...)
end;
groups = {
cracky = 2;
sorcery_magitech = 1;
};
})
end
end