Cranks, shafts, and spruce trees.

This commit is contained in:
Signal 2026-05-09 16:01:10 -04:00
parent 4659a008ac
commit 9011835cf4
40 changed files with 1496 additions and 104 deletions

View file

@ -9,6 +9,8 @@ function extend(dst, src)
return dst return dst
end end
table.merge = extend
-- PHP-style helper verb -- PHP-style helper verb
function include(file) function include(file)
return dofile(minetest.get_modpath(minetest.get_current_modname()).."/"..file) return dofile(minetest.get_modpath(minetest.get_current_modname()).."/"..file)
@ -59,7 +61,7 @@ EventTarget = {
local l = e.listeners[channel] local l = e.listeners[channel]
if not l then return end if not l then return end
for i = 1, #l do for i = 1, #l do
l[i](...) if l[i] then l[i](...) end
end end
end end
} }
@ -70,15 +72,22 @@ setmetatable(EventTarget, {
StateMachine = { StateMachine = {
init = function(obj, states) init = function(obj, states)
local super = EventTarget() local super = EventTarget()
local e = { local e = table.merge(super, {
states = states, states = states,
active_states = {}, active_states = {},
obj = obj obj = obj
} })
return setmetatable(e, {__index = super}) return setmetatable(e, {__index = StateMachine})
end, end,
add_state = function(e, state) add_state = function(e, state)
if e.active_states[state] then return false end if e.active_states[state] then return false end
if e.states[state].excludes then
for i = 1, #e.states[state].excludes do
if e.active_states[e.states[state].excludes[i]] then
e:remove_state(e.states[state].excludes[i])
end
end
end
e.states[state]:add(e.obj) e.states[state]:add(e.obj)
e.active_states[state] = true e.active_states[state] = true
return true return true
@ -100,6 +109,40 @@ setmetatable(StateMachine, {
__index = EventTarget() __index = EventTarget()
}) })
ModifierStack = {
init = function(mode)
local e = EventTarget()
extend(e, {
mode = mode or "add",
value = 0
})
return setmetatable(e, {__index = ModifierStack})
end,
set = function(e, key, value)
if key == "value" then return end
if e[key] then
if e.mode == "multiply" then
e.value = e.value /e[key]
else
e.value = e.value -e[key]
end
end
e[key] = value
if e[key] then
if e.mode == "multiply" then
e.value = e.value *e[key]
else
e.value = e.value +e[key]
end
end
e:dispatch("change")
end,
}
setmetatable(ModifierStack, {
__call = function(_, ...) return ModifierStack.init(...) end,
__index = EventTarget
})
rgt = { rgt = {
adjacent_neighbor_offests = { adjacent_neighbor_offests = {
vector.new(0,0,1), vector.new(0,0,1),
@ -117,6 +160,14 @@ rgt = {
vector.new(1,0,0), vector.new(1,0,0),
vector.new(-1,0,0), vector.new(-1,0,0),
}, },
abutting_neighbor_offests = {
vector.new(0,0,1),
vector.new(0,0,-1),
vector.new(1,0,0),
vector.new(-1,0,0),
vector.new(0,1,0),
vector.new(0,-1,0),
},
nodes_to_content_ids = {}, nodes_to_content_ids = {},
content_ids_to_nodes = {}, content_ids_to_nodes = {},
vm_data = {}, vm_data = {},
@ -182,7 +233,11 @@ function ns.register_tool(name, def)
end end
function ns.register_entity(name, def) function ns.register_entity(name, def)
minetest.register_entity(name, def) def._name = name
if not name:find(":") then
name = "red_glazed_terracotta:"..name
end
minetest.register_entity(":"..name, def)
end end
-- Make node dig particles denser. -- Make node dig particles denser.
@ -207,6 +262,46 @@ minetest.register_on_dignode(function(pos, node, digger)
}) })
end) end)
-- HACK: Lookup table for getting a rotation from a
-- facedir (because Minetest doesn't have any way
-- to get this information normally)
local facedir_rotations = {
-- +Y
[0] = vector.new(0, 0, 0),
[1] = vector.new(0, math.pi * 1.5, 0),
[2] = vector.new(0, math.pi * 1.0, 0),
[3] = vector.new(0, math.pi * 0.5, 0),
-- +Z
[4] = vector.new(math.pi * 1.5, 0, 0),
[5] = vector.new(0, math.pi * 1.5, math.pi * 1.5),
[6] = vector.new(math.pi * 0.5, math.pi * 1.0, 0),
[7] = vector.new(0, math.pi * 0.5, math.pi * 0.5),
-- -Z
[8] = vector.new(math.pi * 0.5, 0, 0),
[9] = vector.new(0, math.pi * 1.5, math.pi * 0.5),
[10] = vector.new(math.pi * 1.5, math.pi * 1.0, 0),
[11] = vector.new(0, math.pi * 0.5, math.pi * 1.5),
-- +X
[12] = vector.new(0, 0, math.pi * 0.5),
[13] = vector.new(math.pi * 1.5, math.pi * 1.5, 0),
[14] = vector.new(0, math.pi * 1.0, math.pi * 1.5),
[15] = vector.new(math.pi * 0.5, math.pi * 0.5, 0),
-- -X
[16] = vector.new(0, 0, math.pi * 1.5),
[17] = vector.new(math.pi * 0.5, math.pi * 1.5, 0),
[18] = vector.new(0, math.pi * 1.0, math.pi * 0.5),
[19] = vector.new(math.pi * 1.5, math.pi * 0.5, 0),
-- -Y
[20] = vector.new(0, 0, math.pi * 1.0),
[21] = vector.new(0, math.pi * 0.5, math.pi * 1.0),
[22] = vector.new(0, math.pi * 1.0, math.pi * 1.0),
[23] = vector.new(0, math.pi * 1.5, math.pi * 1.0),
}
function ns.facedir_to_rotation(facedir)
return facedir_rotations[facedir] or minetest.facedir_to_dir(facedir):dir_to_rotation()
end
-- Fills the area from pos1 to pos2 with the node named `node`. -- Fills the area from pos1 to pos2 with the node named `node`.
function ns.fill_area(pos1, pos2, node) function ns.fill_area(pos1, pos2, node)
local minp = vector.new(math.min(pos1.x, pos2.x), math.min(pos1.y, pos2.y), math.min(pos1.z, pos2.z)) local minp = vector.new(math.min(pos1.x, pos2.x), math.min(pos1.y, pos2.y), math.min(pos1.z, pos2.z))
@ -255,6 +350,18 @@ function ns.get_node_meta(pos)
return setmetatable({_pos = tostring(minetest.hash_node_position(pos))}, NodeMetaRef) return setmetatable({_pos = tostring(minetest.hash_node_position(pos))}, NodeMetaRef)
end end
ns.clock = EventTarget()
local seconds = minetest.get_us_time()
minetest.register_globalstep(function(dtime)
ns.clock:dispatch("tick", dtime)
local time = minetest.get_us_time()
if time - seconds >= 1000000 then
ns.clock:dispatch("every_second", dtime)
seconds = time
end
end)
-- Allow nodes to provide a callback to run on activation without -- Allow nodes to provide a callback to run on activation without
-- needing to register a bunch of mostly identical LBMs. -- needing to register a bunch of mostly identical LBMs.
minetest.register_lbm { minetest.register_lbm {

View file

@ -58,7 +58,7 @@ function rgt.register_hand(name, caps, realname)
drawtype = "mesh", drawtype = "mesh",
mesh = "rgt_hand.gltf", mesh = "rgt_hand.gltf",
tiles = {"rgt_base_"..(realname or name or "placeholder")..".png"}, tiles = {"rgt_base_"..(realname or name or "placeholder")..".png"},
use_texture_alpha = "opaque", use_texture_alpha = "clip",
visual_scale = 1, visual_scale = 1,
wield_scale = vector.new(2,2,2), wield_scale = vector.new(2,2,2),
node_placement_prediction = "", node_placement_prediction = "",

View file

@ -305,6 +305,32 @@ ns.register_hud_type {
end end
} }
ns.register_hud_type {
name = "waypoint",
required_fields = {"world_pos"},
field_types = {
scale = "vec2"
},
defaults = {
scale = {x=1,y=1}
},
add = function(e, m)
e._id = m.object:hud_add {
type = "waypoint",
world_pos = e.world_pos,
text = e.text
}
end,
update = function(e, m, changes)
for k, v in pairs(changes) do
m.object:hud_change(e._id, k, v)
end
end,
remove = function(e, m)
m.object:hud_remove(e._id)
end
}
minetest.register_chatcommand("hudtest", { minetest.register_chatcommand("hudtest", {
func = function(name) func = function(name)
ns.hud_add(rgt.players[name], { ns.hud_add(rgt.players[name], {

View file

@ -9,13 +9,20 @@ local creative_inv = minetest.create_detached_inventory("rgt_creative_inv", {
local num_items = 0 local num_items = 0
minetest.register_on_mods_loaded(function() minetest.register_on_mods_loaded(function()
local items = {}
for name, def in pairs(minetest.registered_items) do for name, def in pairs(minetest.registered_items) do
if not def._variant then if not def._variant then
creative_inv:set_size("main", num_items +1) items[num_items +1] = name.." "..def.stack_max
creative_inv:set_stack("main", num_items, ItemStack(name.." "..def.stack_max))
num_items = num_items +1 num_items = num_items +1
end end
end end
table.sort(items)
for i, x in ipairs(items) do
creative_inv:set_size("main", num_items)
creative_inv:set_stack("main", i, ItemStack(x))
end
end) end)
Inventory = setmetatable({ Inventory = setmetatable({

View file

@ -4,4 +4,104 @@ rgt_machines = {
} }
local ns = rgt_machines local ns = rgt_machines
-- Recursively propagate a network reassignment to all connected nodes (ignoring positions in `ignore`).
local function propagate_network_update(pos, net, net_type, ignore)
ignore[minetest.hash_node_position(pos)] = true
-- Store the network to the target node.
local m = minetest.get_meta(pos)
m:set_string("network", net)
m:set_string("network_type", net_type)
-- Check each neighboring node for further propagation.
for _, x in ipairs(rgt.adjacent_neighbor_offests) do
-- If we already checked this node, we shouldn't consider it to avoid infinite recursion.
if not ignore[minetest.hash_node_position(pos +x)] then
local ncid, _, nparam2 = minetest.get_node_raw(pos.x +x.x, pos.y +x.y, pos.z +x.z)
local ndef = minetest.registered_nodes[rgt.content_ids_to_nodes[ncid]]
-- Ensure that this node can belong to a network and thus is a valid propagation target.
if ndef._network_can_accept and ndef._network_can_accept(pos, nparam2) then
propagate_network_update(pos +x, net, ignore)
end
end
end
end
-- Called when a pos is added to or removed from a network.
-- Whether an addition or deletion is being performed depends on whether the node at `pos` belongs to the `network_component` group.
function ns.update_network(pos, net_type)
local cid, _, param2 = minetest.get_node_raw(pos.x, pos.y, pos.z)
local def = minetest.registered_nodes[rgt.content_ids_to_nodes[cid]]
-- We're adding a node.
if def._network_neighbors then
-- Find all adjacent networks.
local net
local nets = {}
local num_nets = 0
for _, x in ipairs(def._network_neighbors(pos, param2)) do
local ncid, _, nparam2 = minetest.get_node_raw(pos.x +x.x, pos.y +x.y, pos.z +x.z)
local ndef = minetest.registered_nodes[rgt.content_ids_to_nodes[ncid]]
-- Ensure that this node can accept a network propagation from this position.
if ndef._network_can_accept and ndef._network_can_accept(pos, nparam2) then
local m = minetest.get_meta(pos +x)
local n = m:get("network")
local nt = m:get("network_type")
-- If the node doesn't have a network for some reason, we should give it one.
if nt == net_type and not n or table.indexof(nets, n) == -1 then
-- Set our network to the first one we find.
if not net then
net = n
-- Store the positions of any other adjacent networks, so we can merge them with ours.
elseif net ~= n then
nets[#nets +1] = x
end
num_nets = num_nets +1
end
end
end
-- Propagate our chosen network to adjacent networks with different IDs.
-- If we didn't find any adjacent networks, this will just do nothing.
local ignore = {[minetest.hash_node_position(pos)] = true}
for _, x in ipairs(nets) do
propagate_network_update(pos +x, net, net_type, ignore)
end
-- Create a new unique network ID if none of our neighbors have one.
if not net then
net = string.format("net_%d_%06d", minetest.get_us_time(), math.random(100000, 999999))
end
-- Save our chosen network ID.
local m = minetest.get_meta(pos)
m:set_string("network", net)
m:set_string("network_type", net_type)
-- We're removing a node.
else
-- Find all adjacent networks.
local net
local nets = {}
for _, x in ipairs(rgt.adjacent_neighbor_offests) do
local ncid, _, nparam2 = minetest.get_node_raw(pos.x +x.x, pos.y +x.y, pos.z +x.z)
local ndef = minetest.registered_nodes[rgt.content_ids_to_nodes[ncid]]
-- Ensure that this node can accept a network propagation from this position.
if ndef._network_can_accept and ndef._network_can_accept(pos, nparam2) then
local m = minetest.get_meta(pos +x)
local n = m:get("network")
local nt = m:get("network_type")
if nt == net_type then
nets[#nets +1] = x
end
end
end
-- We only need to ensure that these networks are unique, thus one of them can keep its original ID.
if #nets > 1 then table.remove(nets, 1) end
-- Create a new ID for each network and propagate it.
local pos_hash = minetest.hash_node_position(pos)
for _, x in ipairs(nets) do
net = string.format("net_%d_%06d", minetest.get_us_time(), math.random(100000, 999999))
-- Provide a different ignore table each time, to ensure that when the same network abuts this node on multiple edges, all touching nodes get the same network in the end.
propagate_network_update(pos +x, net, net_type, {[pos_hash] = true})
end
end
return true
end

View file

@ -70,7 +70,7 @@ end
-- Called when a pos is added to or removed from a network. -- Called when a pos is added to or removed from a network.
-- Whether an addition or deletion is being performed depends on whether the node at `pos` belongs to the `network_component` group. -- Whether an addition or deletion is being performed depends on whether the node at `pos` belongs to the `network_component` group.
function ns.update_network(pos) function ns.update_network_(pos)
-- We're adding a node. -- We're adding a node.
if minetest.get_item_group(minetest.get_node(pos).name, "network_component") > 0 then if minetest.get_item_group(minetest.get_node(pos).name, "network_component") > 0 then
-- Find all adjacent networks. -- Find all adjacent networks.

View file

@ -1,22 +1,631 @@
local ns = rgt_machines local ns = rgt_machines
ns.components = {} local db = minetest.get_mod_storage()
--[[ local shaft_networks = {}
{
name = "...", -- The component's name. local shafts = {}
attach_points
local ShaftNetwork
ShaftNetwork = {
init = function(id)
local e = {
id = id,
torque = ModifierStack(),
load = ModifierStack(),
inertia = ModifierStack(),
alpha = 0,
omega = 0,
angle = 0,
} }
--]] setmetatable(e, {__index = ShaftNetwork})
function ns.register_component(def) local saved = minetest.deserialize(db:get(id))
ns.components[def.name] = def if saved then
e:read(saved)
end
function e._update() e:update() end
e.torque:listen("change", e._update)
e.inertia:listen("change", e._update)
function e._tick(...) e:tick(...) end
rgt.clock:listen("tick", e._tick)
return e
end,
deinit = function(e)
e:save()
rgt.clock:unlisten("tick", e._tick)
end,
read = function(e, saved)
for k, v in pairs(saved.torque) do
e.torque:set(k, v)
end
for k, v in pairs(saved.load) do
e.load:set(k, v)
end
for k, v in pairs(saved.inertia) do
e.inertia:set(k, v)
end
e.alpha = saved.alpha
e.omega = saved.omega
end,
store = function(e)
local out = {
torque = {},
load = {},
inertia = {},
alpha = e.alpha,
omega = e.omega,
angle = e.angle,
}
for k, v in pairs(e.torque) do
if k ~= "value" and k ~= "mode" and k ~= "listeners" then out.torque[k] = v end
end
for k, v in pairs(e.load) do
if k ~= "value" and k ~= "mode" and k ~= "listeners" then out.load[k] = v end
end
for k, v in pairs(e.inertia) do
if k ~= "value" and k ~= "mode" and k ~= "listeners" then out.inertia[k] = v end
end
return out
end,
save = function(e)
db:set_string(e.id, minetest.serialize(e:store()))
end,
tick = function(e, dtime)
local constant_load = math.max(0, math.abs(e.torque.value) -e.load.value) *math.sign(e.torque.value)
local net_torque = constant_load -(5 *e.omega)
e.alpha = net_torque /e.inertia.value
e.omega = e.omega +(e.alpha *dtime)
if math.abs(e.omega) < 0.05 then
e.alpha = 0
e.omega = 0
end end
function ns.register_plate(name, def) e.angle = (e.angle +(e.omega *dtime)) %(math.pi *200)
rgt.register_item(name, def)
for i = 1, #e do
e[i]:set_angle(e.angle)
end
end,
update = function(e, dtime)
end,
}
setmetatable(ShaftNetwork, {
__call = function(_, ...)
return ShaftNetwork.init(...)
end
})
local function propagate_shaft_update(pos, net, ignore)
local cid, _, param2 = minetest.get_node_raw(pos.x, pos.y, pos.z)
local def = minetest.registered_nodes[rgt.content_ids_to_nodes[cid]]
local hashed_pos = minetest.hash_node_position(pos)
ignore[hashed_pos] = true
-- Store the network to the target node.
local m = minetest.get_meta(pos)
local old_net = m:get("network")
if shaft_networks[old_net] then
for i = 1, #shaft_networks[old_net] do
local e = shaft_networks[old_net][i]
if e and e.pos == pos then
if not shaft_networks[net] then
shaft_networks[net] = ShaftNetwork(net)
shaft_networks[net]:read(shaft_networks[old_net]:store())
end
e:change_network(net)
end
end
end
m:set_string("network", net)
-- Check each neighboring node for further propagation.
for _, x in ipairs(def._network_neighbors(pos, param2)) do
-- If we already checked this node, we shouldn't consider it to avoid infinite recursion.
if not ignore[minetest.hash_node_position(x)] then
local ncid, _, nparam2 = minetest.get_node_raw(x.x, x.y, x.z)
local ndef = minetest.registered_nodes[rgt.content_ids_to_nodes[ncid]]
-- Ensure that this node can belong to a network and thus is a valid propagation target.
if ndef and minetest.get_item_group(ndef.name, "shaft") > 0 and ndef._network_can_accept and ndef._network_can_accept(x, pos, nparam2) then
propagate_shaft_update(x, net, ignore)
end
end
end
end end
function ns.update_shaft(pos, removing, ignore)
local cid, _, param2 = minetest.get_node_raw(pos.x, pos.y, pos.z)
local def = minetest.registered_nodes[rgt.content_ids_to_nodes[cid]]
if not removing then
-- Find all adjacent networks.
local net
local nets = {}
local num_nets = 0
for _, x in ipairs(def._network_neighbors(pos, param2)) do
local ncid, _, nparam2 = minetest.get_node_raw(x.x, x.y, x.z)
local ndef = minetest.registered_nodes[rgt.content_ids_to_nodes[ncid]]
-- Ensure that this node can accept a network propagation from this position.
if ndef and minetest.get_item_group(ndef.name, "shaft") > 0 and ndef._network_can_accept and ndef._network_can_accept(x, pos, nparam2) then
local m = minetest.get_meta(x)
local n = m:get("network")
-- If the node doesn't have a network for some reason, we should give it one.
if not n or table.indexof(nets, n) == -1 then
-- Set our network to the first one we find.
if not net then
net = n
-- Store the positions of any other adjacent networks, so we can merge them with ours.
elseif net ~= n then
nets[#nets +1] = x
end
num_nets = num_nets +1
end
end
end
-- Create a new unique network ID if none of our neighbors have one.
if not net then
net = string.format("net_%d_%06d", minetest.get_us_time(), math.random(100000, 999999))
end
-- Propagate our chosen network to adjacent networks with different IDs.
-- If we didn't find any adjacent networks, this will just do nothing.
local ignore = {[minetest.hash_node_position(pos)] = true}
for _, x in ipairs(nets) do
propagate_shaft_update(x, net, ignore)
end
-- Save our chosen network ID.
local m = minetest.get_meta(pos)
m:set_string("network", net)
return net
else
-- Find all adjacent networks.
local net
local nets = {}
for _, x in ipairs(def._network_neighbors(pos, param2)) do
local ncid, _, nparam2 = minetest.get_node_raw(x.x, x.y, x.z)
local ndef = minetest.registered_nodes[rgt.content_ids_to_nodes[ncid]]
-- Ensure that this node can accept a network propagation from this position.
if ndef and minetest.get_item_group(ndef.name, "shaft") > 0 and ndef._network_can_accept and ndef._network_can_accept(x, pos, nparam2) then
local m = minetest.get_meta(x)
local n = m:get("network")
nets[#nets +1] = x
end
end
-- We only need to ensure that these networks are unique, thus one of them can keep its original ID.
-- if #nets > 0 then table.remove(nets, 1) end
-- Create a new ID for each network and propagate it.
local pos_hash = minetest.hash_node_position(pos)
for i, x in ipairs(nets) do
net = string.format("net_%d_%06d", minetest.get_us_time(), math.random(100000, 999999))
-- Provide a different ignore table each time, to ensure that when the same network abuts this node on multiple edges, all touching nodes get the same network in the end.
propagate_shaft_update(x, net, {[pos_hash] = true, color = i == 1 and "red" or "blue"})
end
end
end
rgt.register_entity("shaft", {
initial_properties = {
visual = "mesh",
mesh = "rgt_shaft.gltf",
textures = {"rgt_shaft.png"},
pointable = false,
},
on_activate = function(e, data)
e.pos = e.object:get_pos():round()
if shafts[minetest.hash_node_position(e.pos)] or not minetest.get_node(e.pos).name:find "shaft" then
e.object:remove()
return
end
extend(e, minetest.deserialize(data or "return {}"))
e.omega = 0
e.angle = e.angle or 0
e.object:set_armor_groups{immortal = 1}
e.object:set_rotation(e.rotation)
shafts[minetest.hash_node_position(e.pos)] = e
if not shaft_networks[e.net] then
shaft_networks[e.net] = ShaftNetwork(e.net)
end
e:set_angle(shaft_networks[e.net].angle)
shaft_networks[e.net].inertia:set(e.pos:to_string(), 1)
table.insert(shaft_networks[e.net], e)
end,
on_deactivate = function(e)
shafts[minetest.hash_node_position(e.pos)] = nil
if not shaft_networks[e.net][1] then
table.remove(shaft_networks[e.net], table.indexof(shaft_networks[e.net], e))
shaft_networks[e.net]:deinit()
shaft_networks[e.net] = nil
end
end,
get_staticdata = function(e)
return minetest.serialize{rotation = e.rotation, invert = e.invert, net = e.net}
end,
change_network = function(e, new_net)
say("Changed network at "..e.pos:to_string().." from "..e.net.." to "..new_net)
local net = shaft_networks[e.net]
net.inertia:set(e.pos:to_string(), nil)
table.remove(net, table.indexof(net, e))
e.net = new_net
if not shaft_networks[e.net] then
shaft_networks[e.net] = ShaftNetwork(e.net)
end
table.insert(shaft_networks[e.net], e)
shaft_networks[e.net].inertia:set(e.pos:to_string(), 1)
end,
set_angle = function(e, angle)
-- e.object:set_properties {
-- nametag = e.net
-- }
e.object:set_bone_override("root", {
rotation = {
vec = vector.new(0, 0, e.invert and angle or -angle),
interpolation = 0.1,
absolute = true
}
})
end
})
local shaft_box = {
type = "fixed",
fixed = {
-3/16, -3/16, -0.5,
3/16, 3/16, 0.5
}
}
rgt.register_node("shaft", {
drawtype = "airlike",
paramtype = "light",
paramtype2 = "facedir",
selection_box = shaft_box,
collision_box = shaft_box,
groups = {shaft = 1, dig_immediate = 3},
entities = shafts,
on_place = function(s, p, pt)
local under = minetest.get_node(pt.under)
if under.name == "red_glazed_terracotta:shaft" then
return minetest.item_place_node(s, p, pt, under.param2)
end
if not p:get_player_control().sneak then
local target = minetest.registered_nodes[under.name].buildable_to and pt.under or pt.above
for _, neighbor in ipairs(minetest.find_nodes_in_area(target:offset(-1,-1,-1), target:offset(1,1,1), "shaft")) do
if neighbor:distance(target) <= 1 then
return minetest.item_place_node(s, p, pt, minetest.get_node(neighbor).param2)
end
end
end
return minetest.item_place_node(s, p, pt)
end,
on_construct = function(pos)
local param2 = minetest.get_node(pos).param2
local dir = minetest.facedir_to_dir(param2)
local net = ns.update_shaft(pos)
minetest.add_entity(pos, "red_glazed_terracotta:shaft", minetest.serialize{rotation = rgt.facedir_to_rotation(param2), invert = dir.x < 0 or dir.z < 0, net = net})
end,
on_destruct = function(pos)
local e = shafts[minetest.hash_node_position(pos)]
shaft_networks[e.net].inertia:set(pos:to_string(), nil)
e.object:remove()
shafts[minetest.hash_node_position(pos)] = nil
ns.update_shaft(pos, true)
end,
_network_can_accept = function(pos, from, param2)
local dir = minetest.facedir_to_dir(param2)
return dir:abs() == from:direction(pos):abs()
end,
_network_neighbors = function(pos, param2)
local param2 = minetest.get_node(pos).param2
local dir = minetest.facedir_to_dir(param2)
return {pos +dir, pos -dir}
end,
})
local hand_cranks = {}
rgt.register_entity("hand_crank", {
initial_properties = {
visual = "mesh",
mesh = "rgt_hand_crank.gltf",
textures = {"rgt_hand_crank.png"},
pointable = false
},
on_activate = function(e, data)
e.pos = e.object:get_pos():round()
if hand_cranks[minetest.hash_node_position(e.pos)] or not minetest.get_node(e.object:get_pos()).name:find "hand_crank" then
e.object:remove()
return
end
extend(e, minetest.deserialize(data or "return {}"))
e.rotation = vector.new(e.rotation.x, e.rotation.y, e.rotation.z)
e.torque = 0
e.omega = 0
e.angle = e.angle or 0
e.object:set_armor_groups{immortal = 1}
e.object:set_rotation(e.rotation)
hand_cranks[minetest.hash_node_position(e.pos)] = e
if not shaft_networks[e.net] then
shaft_networks[e.net] = ShaftNetwork(e.net)
end
e:set_angle(shaft_networks[e.net].angle)
shaft_networks[e.net].inertia:set(e.pos:to_string(), 1)
table.insert(shaft_networks[e.net], e)
end,
on_deactivate = function(e)
hand_cranks[minetest.hash_node_position(e.pos)] = nil
table.remove(shaft_networks[e.net], table.indexof(shaft_networks[e.net], e))
if not shaft_networks[e.net][1] then
db:set_string(e.net, minetest.serialize(shaft_networks[e.net]))
shaft_networks[e.net] = nil
end
end,
get_staticdata = function(e)
return minetest.serialize{rotation = e.rotation, invert = e.invert, net = e.net}
end,
on_step = function(e, dtime)
if not e.cranking then
e:set_torque(math.max(0, e.torque -1))
end
end,
change_network = function(e, new_net)
local net = shaft_networks[e.net]
net.inertia:set(e.pos:to_string(), nil)
net.torque:set(e.pos:to_string(), nil)
table.remove(net, table.indexof(net, e))
e.net = new_net
if not shaft_networks[e.net] then
shaft_networks[e.net] = ShaftNetwork(e.net)
end
table.insert(shaft_networks[e.net], e)
shaft_networks[e.net].inertia:set(e.pos:to_string(), 1)
shaft_networks[e.net].torque:set(e.pos:to_string(), e.torque)
end,
add_torque = function(e)
e:set_torque(math.min(20, e.torque +1))
end,
set_torque = function(e, torque)
if torque ~= e.torque then
e.torque = torque
local net = shaft_networks[e.net]
net.torque:set(e.pos:to_string(), e.torque)
end
end,
set_angle = function(e, angle)
-- local net = shaft_networks[e.net]
-- e.object:set_properties {
-- nametag = string.format("Torque: %s\nLoad: %s\nI: %s\na: %s\nw: %s\nangle: %s", net.torque.value, net.load.value, net.inertia.value, net.alpha, net.omega, net.angle)
-- }
e.object:set_bone_override("root", {
rotation = {
vec = vector.new(0, 0, e.invert and angle or -angle),
interpolation = 0.1,
absolute = true
}
})
end,
})
local function do_crank(m)
local crank = hand_cranks[m.cranking]
if not crank then return m:unlisten("tick", do_crank) end
if not m.ctl.place or not m.pointed_node or m.pointed_node.under ~= crank.pos then
m:unlisten("tick", do_crank)
crank.cranking = nil
m.cranking = nil
return
end
crank:add_torque()
end
rgt.register_node("hand_crank", { rgt.register_node("hand_crank", {
mesh = "rgt_hand_crank.gltf" drawtype = "airlike",
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
selection_box = {
type = "fixed",
fixed = {
-0.5, -0.5, 0.5,
0.5, 0.5, 6/16
}
},
groups = {shaft = 2, dig_immediate = 3},
entities = hand_cranks,
on_construct = function(pos)
local param2 = minetest.get_node(pos).param2
local dir = minetest.facedir_to_dir(param2)
local angle
local back = minetest.get_node(pos +dir)
if back.name:find "shaft" then
angle = shafts[minetest.hash_node_position(pos +dir)].angle
end
minetest.add_entity(pos, "red_glazed_terracotta:hand_crank", minetest.serialize{rotation = rgt.facedir_to_rotation(minetest.get_node(pos).param2), angle = angle, invert = dir.x < 0 or dir.z < 0, net = ns.update_shaft(pos)})
end,
on_destruct = function(pos)
local e = hand_cranks[minetest.hash_node_position(pos)]
shaft_networks[e.net].inertia:set(e.pos:to_string(), nil)
e.object:remove()
hand_cranks[minetest.hash_node_position(pos)] = nil
ns.update_shaft(pos)
end,
on_rightclick = function(pos, node, p, s, pt)
local m = rgt.players[p:get_player_name()]
if m.cranking then return end
m.cranking = minetest.hash_node_position(pos)
hand_cranks[m.cranking].cranking = true
m:listen("tick", do_crank)
end,
_network_can_accept = function(pos, from, param2)
local dir = minetest.facedir_to_dir(param2)
return dir == pos:direction(from)
end,
_network_neighbors = function(pos, param2)
local param2 = minetest.get_node(pos).param2
local dir = minetest.facedir_to_dir(param2)
local angle
local back = minetest.get_node_raw(pos.x +dir.x, pos.y +dir.y, pos.z +dir.z)
if minetest.get_item_group(rgt.content_ids_to_nodes[back], "shaft") > 0 then
return {pos +dir}
end
return {}
end
})
local load_testers = {}
rgt.register_entity("shaft_load_tester", {
initial_properties = {
visual = "mesh",
mesh = "rgt_shaft.gltf",
textures = {"rgt_shaft.png"},
pointable = false,
},
on_activate = function(e, data)
e.pos = e.object:get_pos():round()
if load_testers[minetest.hash_node_position(e.pos)] or not minetest.get_node(e.pos).name:find "shaft_load_tester" then
e.object:remove()
return
end
extend(e, minetest.deserialize(data or "return {}"))
e.torque = 0
e.omega = 0
e.angle = e.angle or 0
e.object:set_armor_groups{immortal = 1}
e.object:set_rotation(e.rotation)
e.object:set_bone_override("root", {
rotation = {
vec = vector.new(0, 0, -e.angle),
interpolation = 0,
absolute = true
}
})
load_testers[minetest.hash_node_position(e.pos)] = e
if not shaft_networks[e.net] then
shaft_networks[e.net] = ShaftNetwork(e.net)
end
shaft_networks[e.net].inertia:set(e.pos:to_string(), 2)
shaft_networks[e.net].load:set(e.pos:to_string(), 4)
table.insert(shaft_networks[e.net], e)
end,
on_deactivate = function(e)
load_testers[minetest.hash_node_position(e.pos)] = nil
if not shaft_networks[e.net][1] then
table.remove(shaft_networks[e.net], table.indexof(shaft_networks[e.net], e))
shaft_networks[e.net]:save()
shaft_networks[e.net] = nil
end
end,
get_staticdata = function(e)
return minetest.serialize{rotation = e.rotation, angle = e.angle, net = e.net}
end,
on_step = function(e, dtime)
if e.omega == 0 then return end
minetest.after(0, function()
e.angle = (e.angle +(e.omega *dtime)) %(math.pi *2)
e.object:set_bone_override("root", {
rotation = {
vec = vector.new(0, 0, -e.angle),
interpolation = 0.1,
absolute = true
}
})
end)
end,
set_angular_velocity = function(e, w)
e.omega = w
end,
})
rgt.register_node("shaft_load_tester", {
drawtype = "airlike",
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
selection_box = {
type = "fixed",
fixed = {
-0.5, -0.5, 0.5,
0.5, 0.5, 6/16
}
},
groups = {shaft = 2, dig_immediate = 3},
entities = load_testers,
on_construct = function(pos)
local param2 = minetest.get_node(pos).param2
local dir = minetest.facedir_to_dir(param2)
local angle
local back = minetest.get_node(pos +dir)
if back.name:find "shaft" then
angle = shafts[minetest.hash_node_position(pos +dir)].angle
end
minetest.add_entity(pos, "red_glazed_terracotta:shaft_load_tester", minetest.serialize{rotation = rgt.facedir_to_rotation(minetest.get_node(pos).param2), angle = angle, invert = dir.z < 0 or dir.z < 0, net = ns.update_shaft(pos)})
end,
on_destruct = function(pos)
load_testers[minetest.hash_node_position(pos)].object:remove()
load_testers[minetest.hash_node_position(pos)] = nil
ns.update_shaft(pos)
end,
on_rightclick = function(pos, node, p, s, pt)
local m = rgt.players[p:get_player_name()]
if m.cranking then return end
m.cranking = minetest.hash_node_position(pos)
load_testers[m.cranking].cranking = true
m:listen("tick", do_crank)
end,
_network_can_accept = function(pos, from, param2)
local dir = minetest.facedir_to_dir(param2)
return dir == pos:direction(from)
end,
_network_neighbors = function(pos, param2)
local param2 = minetest.get_node(pos).param2
local dir = minetest.facedir_to_dir(param2)
local angle
local back = minetest.get_node_raw(pos.x +dir.x, pos.y +dir.y, pos.z +dir.z)
if minetest.get_item_group(rgt.content_ids_to_nodes[back], "shaft") > 0 then
return {pos +dir}
end
return {}
end
}) })

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"asset":{"version":"2.0","generator":"Blockbench 4.12.5 glTF exporter"},"scenes":[{"nodes":[2],"name":"blockbench_export"}],"scene":0,"nodes":[{"name":"cube","mesh":0},{"name":"root","children":[0]},{"children":[1]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":16},{"buffer":0,"byteOffset":856,"byteLength":64}],"buffers":[{"byteLength":920,"uri":"data:application/octet-stream;base64,AADwPwAA8D8AAKBAAADwPwAA8D8AAKDAAADwPwAA8L8AAKBAAADwPwAA8L8AAKDAAADwvwAA8D8AAKDAAADwvwAA8D8AAKBAAADwvwAA8L8AAKDAAADwvwAA8L8AAKBAAADwvwAA8D8AAKDAAADwPwAA8D8AAKDAAADwvwAA8D8AAKBAAADwPwAA8D8AAKBAAADwvwAA8L8AAKBAAADwPwAA8L8AAKBAAADwvwAA8L8AAKDAAADwPwAA8L8AAKDAAADwvwAA8D8AAKBAAADwPwAA8D8AAKBAAADwvwAA8L8AAKBAAADwPwAA8L8AAKBAAADwPwAA8D8AAKDAAADwvwAA8D8AAKDAAADwPwAA8L8AAKDAAADwvwAA8L8AAKDAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAA/AAAAAAAAAAAAAEA+AAAAPwAAQD4AAAAAAABAPgAAAD8AAEA+AAAAAAAAwD4AAAA/AADAPgAAQD4AAGA/AAAAAAAAYD8AAEA+AADAPgAAAAAAAMA+AADAPgAAwD4AAEA+AADAPgAAwD4AAGA/AABAPgAAYD8AAAA/AAAAAAAAMD8AAAAAAAAAPwAAQD4AADA/AABAPgAAwD4AAMA+AAAQPwAAwD4AAMA+AAAQPwAAED8AABA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAAKuqqj6rqio/AACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAANezXb8AAAA/AAAAAAAAAIDXs12/AAAAvwAAAAAAAACAMjENpQAAgL8="}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1.875,1.875,5],"min":[-1.875,-1.875,-5],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.6875,0.875],"min":[0,0],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":4,"max":[1],"min":[0],"type":"SCALAR"},{"bufferView":5,"componentType":5126,"count":4,"max":[0,0,0,1],"min":[0,0,-0.8660253882408142,-1],"type":"VEC4"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"rgt_shaft"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","name":"rgt_shaft.png","uri":"rgt_shaft.png"}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]}],"animations":[{"name":"animation","samplers":[{"input":4,"output":5,"interpolation":"LINEAR"}],"channels":[{"sampler":0,"target":{"node":1,"path":"rotation"}}]}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

View file

@ -6,7 +6,8 @@ Player = {
new = function(p) new = function(p)
local m = setmetatable({ local m = setmetatable({
name = p:get_player_name(), name = p:get_player_name(),
object = p object = p,
listeners = {}
}, {__index = Player}) }, {__index = Player})
m:set_hotbar_size(8) m:set_hotbar_size(8)
@ -57,7 +58,7 @@ Player = {
m.inv = Inventory(p) m.inv = Inventory(p)
m:dispatch("init") Player:dispatch("init")
return m return m
end, end,
@ -110,8 +111,8 @@ Player = {
shaded = false shaded = false
} }
end, end,
set_wielditem = function(m, def) set_wielditem = function(m, s)
if not def then def = {name = ""} end local def = s:get_definition()
if not (m.wielditem_display and m.wielditem_display:is_valid()) then if not (m.wielditem_display and m.wielditem_display:is_valid()) then
local mp = m.object:get_pos() or vector.zero() local mp = m.object:get_pos() or vector.zero()
m.wielditem_display = minetest.add_entity(mp, "display") m.wielditem_display = minetest.add_entity(mp, "display")
@ -144,7 +145,7 @@ Player = {
m.wielditem_display:set_properties { m.wielditem_display:set_properties {
visual = "item", visual = "item",
visual_size = scale, visual_size = scale,
wield_item = def.name wield_item = s:to_string()
} }
m.wielditem_display:set_attach(m.object, "RightArm", pos, rot) m.wielditem_display:set_attach(m.object, "RightArm", pos, rot)
-- Apparently this forces a resend so that properties and attachment position will sync up? -- Apparently this forces a resend so that properties and attachment position will sync up?
@ -405,7 +406,7 @@ Player = {
if onunselect then onunselect(m) end if onunselect then onunselect(m) end
end end
m.prev_wielditem = wname m.prev_wielditem = wname
m:set_wielditem(def) m:set_wielditem(w)
local onselect = def and def.on_wield local onselect = def and def.on_wield
if onselect then onselect(m, w) end if onselect then onselect(m, w) end
end end
@ -413,7 +414,7 @@ Player = {
local while_wielded = def and def.while_wielded local while_wielded = def and def.while_wielded
if while_wielded then while_wielded(m, w) end if while_wielded then while_wielded(m, w) end
m:dispatch("tick") m:dispatch("tick", m)
end, end,
set_hotbar_size = function(m, slots) set_hotbar_size = function(m, slots)
local p = m.object local p = m.object

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 B

After

Width:  |  Height:  |  Size: 147 B

Before After
Before After

View file

View file

@ -0,0 +1,92 @@
local lantern_box = {
type = "fixed",
fixed = {
{
-3/16, -0.5, -3/16,
3/16, -1/16, 3/16,
},
{
-2/16, -1/16, -2/16,
2/16, 1/16, 2/16
},
{
-1/16, 1/16, -1/16,
1/16, 3/16, 1/16
}
}
}
local hanging_lantern_box = {
type = "fixed",
fixed = {
{
-3/16, -7/16, -3/16,
3/16, 0, 3/16,
},
{
-2/16, 0, -2/16,
2/16, 2/16, 2/16
},
{
-1/16, 1/16, -1/16,
1/16, 0.5, 1/16
}
}
}
function rgt.register_lantern(type)
local function on_place(s, p, pt)
local out = ItemStack(s)
local target = minetest.registered_nodes[minetest.get_node(pt.under).name].buildable_to and pt.under or pt.above
local below = minetest.get_node(target:offset(0, -1, 0))
-- TODO: Check solidity, not just air-ness.
if below.name == "air" then
local above = minetest.get_node(target:offset(0, 1, 0))
if above.name == "air" then
return s
end
s:set_name("lantern_"..type.."_hanging")
else
s:set_name("lantern_"..type)
end
local stack = minetest.item_place_node(s, p, pt)
out:set_count(stack:get_count())
return out
end
rgt.register_node("lantern_"..type, {
drawtype = "mesh",
mesh = "rgt_lantern.gltf",
tiles = {"rgt_lantern_"..type..".png"},
paramtype = "light",
light_source = 11,
selection_box = lantern_box,
collision_box = lantern_box,
node_placement_prediction = "",
groups = {dig_immediate = 3},
on_place = on_place
})
rgt.register_node("lantern_"..type.."_hanging", {
drawtype = "mesh",
mesh = "rgt_lantern_hanging.gltf",
tiles = {"rgt_lantern_"..type..".png"},
paramtype = "light",
light_source = 11,
selection_box = hanging_lantern_box,
collision_box = hanging_lantern_box,
node_placement_prediction = "",
drop = "lantern_"..type,
groups = {dig_immediate = 3},
on_place = on_place
})
end
rgt.register_lantern("iron")
rgt.register_lantern("copper")

View file

@ -0,0 +1,2 @@
name = rgt_lights
depends = rgt_world

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

View file

@ -25,7 +25,7 @@ function ns.add_point(point, args)
min = vector.new(-1,-1,-1), min = vector.new(-1,-1,-1),
max = vector.new(1,1,1) max = vector.new(1,1,1)
}, },
texture = args and args.color or "[fill:1x1:0,0:#faa", texture = args and args.color and "[fill:1x1:0,0:"..args.color or args.texture or "[fill:1x1:0,0:#faa",
time = 10, time = 10,
amount = 50, amount = 50,
size = 5 size = 5

View file

View file

@ -0,0 +1,2 @@
name = rgt_vehicles
depends = rgt_base, rgt_player

View file

@ -76,6 +76,7 @@ for i = 1, 3 do
tiles = {"rgt_grass_"..i..".png"}, tiles = {"rgt_grass_"..i..".png"},
groups = {attached_node = 3, dig_immediate = 3}, groups = {attached_node = 3, dig_immediate = 3},
walkable = false, walkable = false,
buildable_to = true,
selection_box = { selection_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
@ -96,6 +97,7 @@ rgt.register_node("grass_tall_bottom", {
tiles = {"rgt_grass_tall.png^[verticalframe:2:1"}, tiles = {"rgt_grass_tall.png^[verticalframe:2:1"},
groups = {attached_node = 3, dig_immediate = 3}, groups = {attached_node = 3, dig_immediate = 3},
walkable = false, walkable = false,
buildable_to = true,
selection_box = { selection_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
@ -147,6 +149,7 @@ rgt.register_node("grass_tall_top", {
} }
}, },
drop = "grass_tall_bottom", drop = "grass_tall_bottom",
buildable_to = true,
after_destruct = function(pos) after_destruct = function(pos)
local below = pos:offset(0, -1, 0) local below = pos:offset(0, -1, 0)
local nb = minetest.get_node(below) local nb = minetest.get_node(below)
@ -191,57 +194,7 @@ rgt.register_node("sand", {
groups = {dig_immediate = 3} groups = {dig_immediate = 3}
}) })
include "wood.lua"
rgt.register_node("oak_log", {
tiles = {"rgt_oak_log_top.png", "rgt_oak_log_top.png", "rgt_oak_log_side.png"},
groups = {dig_immediate = 3},
paramtype2 = "facedir"
})
rgt.register_node("oak_leaves", {
drawtype = "allfaces",
tiles = {"rgt_oak_leaves.png"},
use_texture_alpha = "clip",
groups = {dig_immediate = 3},
})
rgt.register_node("oak_planks", {
tiles = {{name = "rgt_oak_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("dark_planks", {
tiles = {{name = "rgt_dark_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("spruce_planks", {
tiles = {{name = "rgt_spruce_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("acacia_planks", {
tiles = {{name = "rgt_acacia_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("redwood_planks", {
tiles = {{name = "rgt_redwood_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("birch_planks", {
tiles = {{name = "rgt_birch_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("glass", { rgt.register_node("glass", {
drawtype = "glasslike", drawtype = "glasslike",
@ -266,6 +219,19 @@ rgt.register_node("basalt", {
groups = {dig_immediate = 3}, groups = {dig_immediate = 3},
}) })
rgt.register_node("basalt_tile", {
tiles = {{name = "rgt_basalt_tile.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("basalt_brick_large", {
tiles = {{name = "rgt_basalt_brick_large.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("water", { rgt.register_node("water", {
tiles = {"[fill:16x16:0,0:#2d5a7c77^[fill:14x14:1,1:#2d5a7c33"}, tiles = {"[fill:16x16:0,0:#2d5a7c77^[fill:14x14:1,1:#2d5a7c33"},
@ -443,26 +409,26 @@ minetest.register_decoration {
fill_ratio = 0.2, fill_ratio = 0.2,
} }
minetest.override_item("", { --minetest.override_item("", {
on_place = function(s, p, pt) -- on_place = function(s, p, pt)
if minetest.get_node(pt.under).name:find "dirt_grass" then -- if minetest.get_node(pt.under).name:find "dirt_grass" then
minetest.set_node(pt.under, {name = "path_grass"}) -- minetest.set_node(pt.under, {name = "path_grass"})
end -- end
-- minetest.spawn_tree(pt.above, { ---- minetest.spawn_tree(pt.above, {
-- axiom = "TF[FFA]", ---- axiom = "TF[FFA]",
-- rules_a = "F", ---- rules_a = "F",
-- trunk = "oak_log", ---- trunk = "oak_log",
-- leaves = "oak_leaves", ---- leaves = "oak_leaves",
-- angle = 30, ---- angle = 30,
-- iterations = 2, ---- iterations = 2,
-- random_level = 0, ---- random_level = 0,
-- trunk_type = "single", ---- trunk_type = "single",
---- thin_branches = true, ------ thin_branches = true,
-- fruit_chance = 0, ---- fruit_chance = 0,
-- fruit = "stone_brick" ---- fruit = "stone_brick"
---- })
-- end
--}) --})
end
})
minetest.register_chatcommand("biome", { minetest.register_chatcommand("biome", {
func = function(name) func = function(name)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 320 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 B

After

Width:  |  Height:  |  Size: 214 B

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 316 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 293 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 313 B

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

After

Width:  |  Height:  |  Size: 306 B

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 306 B

Before After
Before After

View file

@ -5,6 +5,7 @@ function ns.register_slab(def)
def._variants = nil def._variants = nil
rgt.register_node(def._name.."_slab", extend(def, { rgt.register_node(def._name.."_slab", extend(def, {
_base_node = def._name,
drawtype = "nodebox", drawtype = "nodebox",
node_box = { node_box = {
type = "fixed", type = "fixed",
@ -49,6 +50,92 @@ function ns.update_stair(pos, basename, leaf)
end end
end end
local function get_connection_dirs(dir, variant, orientation)
if variant == "stair_inner" then
if orientation == "back" then
return dir:rotate(vector.new(0, math.pi, 0)), dir:rotate(vector.new(0, -math.pi /2, 0))
else
return dir:rotate(vector.new(0, math.pi /2, 0)), dir
end
elseif variant == "stair_outer" then
if orientation == "back" then
return dir:rotate(vector.new(0, math.pi /2, 0)), dir
else
return dir:rotate(vector.new(0, math.pi, 0)), dir:rotate(vector.new(0, -math.pi /2, 0))
end
elseif variant == "stair" then
return dir:rotate(vector.new(0, math.pi /2, 0)), dir:rotate(vector.new(0, -math.pi /2, 0))
end
return vector.zero(), vector.zero()
end
local function place_stair(s, p, pt)
local m = rgt.players[p:get_player_name()]
local def = s:get_definition()
local out = ItemStack(s)
local variant = def._variant
local param2
s:set_name(def._base_node.."_stair")
local invert = m.pointed_node.intersection_point.y -m.pointed_node.under.y > 0.5
local adjusted_yaw = (math.pi *2 -m.yaw) +(math.pi /4)
if adjusted_yaw > math.pi *2 then
adjusted_yaw = adjusted_yaw -(math.pi *2)
end
local yaw = math.floor(adjusted_yaw /(math.pi /2))
local dir = vector.new(yaw == 1 and 1 or yaw == 3 and -1 or 0, 0, yaw == 0 and 1 or yaw == 2 and -1 or 0)
local dir_left = dir:rotate(vector.new(0, -math.pi /2, 0))
local dir_right = dir_left:rotate(vector.new(0, math.pi /2, 0))
-- local obj = minetest.add_entity(pt.above +dir, "display")
-- obj:set_properties {
-- visual = "cube",
-- textures = {"rgt_glass.png^[multiply:#f99", "rgt_glass.png^[multiply:#f99", "rgt_glass.png^[multiply:#f99", "rgt_glass.png^[multiply:#f99", "rgt_glass.png^[multiply:#f99", "rgt_glass.png^[multiply:#f99"}
-- }
-- minetest.after(10, function() obj:remove() end)
local front = minetest.get_node(pt.above +dir)
local front_def = minetest.registered_nodes[front.name]
local back = minetest.get_node(pt.above -dir)
local back_def = minetest.registered_nodes[back.name]
local left = minetest.get_node(pt.above +dir_left)
local left_def = minetest.registered_nodes[left.name]
local right = minetest.get_node(pt.above +dir_right)
if (front_def._variant or "none"):find "stair" then
local front_dir = minetest.facedir_to_dir(front.param2)
local dir_a, dir_b = get_connection_dirs(front_dir, front_def._variant)
if dir_a == dir then
s:set_name(def._base_node.."_stair_outer")
param2 = minetest.dir_to_facedir(dir_left)
elseif dir_b == dir then
s:set_name(def._base_node.."_stair_outer")
param2 = minetest.dir_to_facedir(dir_right)
end
elseif (back_def._variant or "none"):find "stair" then
local back_dir = minetest.facedir_to_dir(back.param2)
local dir_a, dir_b = get_connection_dirs(back_dir, back_def._variant, "back")
if dir_a == dir then
s:set_name(def._base_node.."_stair_inner")
param2 = minetest.dir_to_facedir(dir_left)
elseif dir_b == dir then
s:set_name(def._base_node.."_stair_inner")
param2 = minetest.dir_to_facedir(dir_right)
end
end
local stack = minetest.item_place_node(s, p, pt, param2)
out:set_count(stack:get_count())
return out
end
function ns.register_stair(def) function ns.register_stair(def)
def = table.copy(def) def = table.copy(def)
def._variants = nil def._variants = nil
@ -56,36 +143,75 @@ function ns.register_stair(def)
def.groups[def._name.."_stair"] = 1 def.groups[def._name.."_stair"] = 1
rgt.register_node(def._name.."_stair", extend(table.copy(def), { rgt.register_node(def._name.."_stair", extend(table.copy(def), {
_base_node = def._name,
drawtype = "nodebox", drawtype = "nodebox",
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = {{-0.5, -0.5, -0.5, 0.5, 0, 0.5}, {-0.5, -0.5, 0, 0.5, 0.5, 0.5}} fixed = {
{
-0.5, -0.5, -0.5,
0.5, 0, 0.5
},
{
-0.5, -0.5, 0,
0.5, 0.5, 0.5
},
}
}, },
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
_variant = "stair", _variant = "stair",
on_place = place_stair,
})) }))
rgt.register_node(def._name.."_stair_inner", extend(table.copy(def), { rgt.register_node(def._name.."_stair_inner", extend(table.copy(def), {
_base_node = def._name,
drawtype = "nodebox", drawtype = "nodebox",
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = {{-0.5, -0.5, -0.5, 0.5, 0, 0.5}, {-0.5, -0.5, 0, 0.5, 0.5, 0.5}, {-0.5, -0.5, -0.5, 0, 0.5, 0.5}} fixed = {
{
-0.5, -0.5, -0.5,
0.5, 0, 0.5
},
{
-0.5, -0.5, -0.5,
0, 0.5, 0.5
},
{
-0.5, -0.5, 0,
0.5, 0.5, 0.5
},
}
}, },
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
_variant = "stair_inner", _variant = "stair_inner",
on_place = place_stair,
drop = def._name.."_stair",
})) }))
rgt.register_node(def._name.."_stair_outer", extend(def, { rgt.register_node(def._name.."_stair_outer", extend(def, {
_base_node = def._name,
drawtype = "nodebox", drawtype = "nodebox",
node_box = { node_box = {
type = "fixed", type = "fixed",
fixed = {{-0.5, -0.5, -0.5, 0.5, 0, 0.5}, {-0.5, -0.5, 0.5, 0, 0.5, 0}} fixed = {
{
-0.5, -0.5, -0.5,
0.5, 0, 0.5
},
{
-0.5, -0.5, 0.5,
0, 0.5, 0
}
}
}, },
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
_variant = "stair_outer", _variant = "stair_outer",
on_place = place_stair,
drop = def._name.."_stair",
})) }))
end end

350
mods/rgt_world/wood.lua Normal file
View file

@ -0,0 +1,350 @@
-- MARK: Helpers
local fence_nodebox = {
type = "connected",
fixed = {
-2/16, -0.5, -2/16,
2/16, 0.5, 2/16
},
connect_front = {
{
-1/16, -6/16, -0.5,
1/16, -2/16, 0
},
{
-1/16, 2/16, -0.5,
1/16, 6/16, 0
}
},
connect_back = {
{
-1/16, -6/16, 0,
1/16, -2/16, 0.5
},
{
-1/16, 2/16, 0,
1/16, 6/16, 0.5
}
},
connect_left = {
{
-0.5, -6/16, -1/16,
0, -2/16, 1/16
},
{
-0.5, 2/16, -1/16,
0, 6/16, 1/16
}
},
connect_right = {
{
0, -6/16, -1/16,
0.5, -2/16, 1/16
},
{
0, 2/16, -1/16,
0.5, 6/16, 1/16
}
},
}
local fence_gate_nodebox = {
type = "fixed",
fixed = {
{
-8/16, -7/16, -1/16,
-6/16, 7/16, 1/16
},
{
8/16, -7/16, -1/16,
6/16, 7/16, 1/16
},
{
-6/16, -6/16, -1/16,
6/16, -2/16, 1/16
},
{
-6/16, 2/16, -1/16,
6/16, 6/16, 1/16
}
}
}
local fence_gate_open_nodebox = {
type = "fixed",
fixed = {
{
-8/16, -7/16, -1/16,
-6/16, 7/16, 1/16
},
{
8/16, -7/16, -1/16,
6/16, 7/16, 1/16
},
{
-8/16, -6/16, 1/16,
-6/16, -2/16, 0.5
},
{
-8/16, 2/16, 1/16,
-6/16, 6/16, 0.5
},
{
8/16, -6/16, 1/16,
6/16, -2/16, 0.5
},
{
8/16, 2/16, 1/16,
6/16, 6/16, 0.5
},
}
}
local ladder_nodebox = {
type = "fixed",
fixed = {
{
-7/16, -0.5, 0.5,
-5/16, 0.5, 6/16
},
{
5/16, -0.5, 0.5,
7/16, 0.5, 6/16
},
{
-5/16, -7/16, 7.5/16,
5/16, -5/16, 6.5/16
},
{
-5/16, -3/16, 7.5/16,
5/16, -1/16, 6.5/16
},
{
-5/16, 1/16, 7.5/16,
5/16, 3/16, 6.5/16
},
{
-5/16, 5/16, 7.5/16,
5/16, 7/16, 6.5/16
},
}
}
local log_nodebox = {
type = "fixed",
fixed = {
{
-6/16, -0.5, -0.5,
6/16, 0.5, 0.5,
},
{
-0.5, -0.5, -6/16,
0.5, 0.5, 6/16,
}
}
}
function rgt.register_fence(name, texture)
rgt.register_node(name.."_fence", {
drawtype = "nodebox",
node_box = fence_nodebox,
connects_to = {"group:fence"},
paramtype = "light",
sunlight_propagates = true,
tiles = {{name = texture or "rgt_"..name.."_planks.png", align_style = "world"}},
groups = {dig_immediate = 3, fence = 1},
})
local function fence_gate_swap(pos, node, p)
if node.name:find "fence_gate_open" then
node.name = node.name:gsub("fence_gate_open", "fence_gate")
elseif node.name:find "fence_gate" then
node.name = node.name:gsub("fence_gate", "fence_gate_open")
local rot = minetest.fourdir_to_dir(node.param2)
-- Get the axis on which the gate will open.
local axis = rot.x ~= 0 and "x" or "z"
local dir = p:get_pos():direction(pos)
-- If the gate will open in the opposite direction from the player's facing direction, flip it.
if math.sign(dir[axis]) ~= math.sign(rot[axis]) then
node.param2 = (node.param2 +2) %4
end
end
minetest.swap_node(pos, node)
end
rgt.register_node(name.."_fence_gate", {
drawtype = "nodebox",
node_box = fence_gate_nodebox,
connects_to = {"group:fence"},
connect_sides = {"left", "right"},
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "4dir",
tiles = {{name = "rgt_"..name.."_planks.png", align_style = "world"}},
groups = {dig_immediate = 3, fence = 1},
on_rightclick = fence_gate_swap,
})
rgt.register_node(name.."_fence_gate_open", {
drawtype = "nodebox",
node_box = fence_gate_open_nodebox,
connects_to = {"group:fence"},
connect_sides = {"left", "right"},
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "4dir",
tiles = {{name = "rgt_"..name.."_planks.png", align_style = "world"}},
groups = {dig_immediate = 3, fence = 1},
on_rightclick = fence_gate_swap,
})
end
function rgt.register_ladder(name)
rgt.register_node(name.."_ladder", {
drawtype = "nodebox",
node_box = ladder_nodebox,
selection_box = {
type = "fixed",
fixed = {-7/16, -0.5, 0.5, 7/16, 0.5, 6/16}
},
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "4dir",
climbable = true,
tiles = {{name = "rgt_"..name.."_planks.png", align_style = "world"}},
groups = {dig_immediate = 3, ladder = 1},
on_place = function(s, p, pt)
local under = minetest.get_node(pt.under)
-- If placing a ladder against a ladder, attempt to extend the pointed ladder rather than naively placing against it.
if under.name:find "ladder" then
local old_pt = table.copy(pt)
-- This will blow up for touchscreen users, but there's no way around it because they don't obey crosshair restrictions.
local iy = rgt.players[p:get_player_name()].pointed_node
-- `iy` can be nil in edge cases where the client clicked the node momentarily but is no longer pointing at the
-- node on the server when the message arrives and the server responds. In these cases, we just don't place anything
-- because we cannot determine the direction in which the player wishes to extend the ladder.
if not iy then return end
iy = iy.intersection_point.y
-- Try extending in the pointed direction first.
pt.above = pt.under:offset(0, iy -pt.under.y > 0 and 1 or -1, 0)
local s, success = minetest.item_place_node(s, p, pt, under.param2)
if success then return s end
-- If we can't, try extending in the other direction.
pt.above = pt.under:offset(0, iy -pt.under.y > 0 and -1 or 1, 0)
s, success = minetest.item_place_node(s, p, pt, under.param2)
if success then return s end
-- If that too fails, don't place anything. We could fall back to default placement, but that would make rapid laddering more annoying.
else
return minetest.item_place_node(s, p, pt)
end
end
})
end
-- MARK: Oak
rgt.register_node("oak_log", {
drawtype = "nodebox",
node_box = log_nodebox,
tiles = {"rgt_oak_log_top.png", "rgt_oak_log_top.png", "rgt_oak_log_side.png"},
groups = {dig_immediate = 3},
paramtype2 = "facedir",
})
rgt.register_node("oak_leaves", {
drawtype = "allfaces",
tiles = {"rgt_oak_leaves.png"},
use_texture_alpha = "clip",
groups = {dig_immediate = 3},
})
rgt.register_node("oak_planks", {
tiles = {{name = "rgt_oak_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_fence("oak")
rgt.register_ladder("oak")
-- MARK: Dark
rgt.register_node("dark_planks", {
tiles = {{name = "rgt_dark_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_fence("dark")
rgt.register_ladder("dark")
-- MARK: Spruce
rgt.register_node("spruce_log", {
drawtype = "nodebox",
node_box = log_nodebox,
tiles = {"rgt_spruce_log_top.png", "rgt_spruce_log_top.png", "rgt_spruce_log_side.png"},
groups = {dig_immediate = 3},
paramtype2 = "facedir",
})
rgt.register_node("spruce_planks", {
tiles = {{name = "rgt_spruce_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_node("spruce_leaves", {
drawtype = "allfaces",
tiles = {"rgt_spruce_leaves.png"},
use_texture_alpha = "clip",
groups = {dig_immediate = 3},
})
rgt.register_fence("spruce")
rgt.register_ladder("spruce")
-- MARK: Acacia
rgt.register_node("acacia_planks", {
tiles = {{name = "rgt_acacia_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_fence("acacia")
rgt.register_ladder("acacia")
-- MARK: Redwood
rgt.register_node("redwood_planks", {
tiles = {{name = "rgt_redwood_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_fence("redwood")
rgt.register_ladder("redwood")
-- MARK: Birch
rgt.register_node("birch_planks", {
tiles = {{name = "rgt_birch_planks.png", align_style = "world"}},
_variants = "all",
groups = {dig_immediate = 3},
})
rgt.register_fence("birch")
rgt.register_ladder("birch")