diff --git a/mods/artifact_base/init.lua b/mods/artifact_base/init.lua index b83fde1..e8ef0e0 100644 --- a/mods/artifact_base/init.lua +++ b/mods/artifact_base/init.lua @@ -45,6 +45,26 @@ function Promise(fn) } end +-- HACK: Lookup table for getting a rotation from a +-- facedir (because Minetest doesn't have any way +-- to get this) +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, 0), + [3] = vector.new(0, math.pi *0.5, 0), + + -- +Z + [4] = vector.new(math.pi *1.5, 0, 0), + [5] = vector.new(math.pi *1.5, math.pi *1.5, 0), + [6] = vector.new(math.pi *1.5, math.pi, 0), + [7] = vector.new(math.pi *1.5, math.pi *0.5, 0), +} +function artifact.facedir_to_rotation(facedir) + return facedir_rotations[facedir] or minetest.facedir_to_dir(facedir):dir_to_rotation() +end + minetest.register_lbm{ name = ":artifact:on_load", diff --git a/mods/artifact_characters/init.lua b/mods/artifact_characters/init.lua index 913dc36..4baa8e6 100644 --- a/mods/artifact_characters/init.lua +++ b/mods/artifact_characters/init.lua @@ -8,6 +8,8 @@ function ns.apply_key(m) eye_height = 1.6 } m.eye_height = 1.6 + -- Switch hand appearance. + m.inv:set_stack("main", 1, ItemStack("input_"..m.character)) end function ns.apply_vix(m) @@ -17,6 +19,8 @@ function ns.apply_vix(m) eye_height = 1.5 } m.eye_height = 1.5 + -- Switch hand appearance. + m.inv:set_stack("main", 1, ItemStack("input_"..m.character)) end function ns.swap_character(m) diff --git a/mods/artifact_characters/models/artifact_hand.gltf b/mods/artifact_characters/models/artifact_hand.gltf new file mode 100644 index 0000000..b3f1685 --- /dev/null +++ b/mods/artifact_characters/models/artifact_hand.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.11.0 glTF exporter"},"scenes":[{"nodes":[3],"name":"blockbench_export"}],"scene":0,"nodes":[{"translation":[-7.341186189470692,-10.221666105894709,3.75],"name":"Right Arm","mesh":0},{"translation":[-7.341186189470692,-10.221666105894709,3.75],"name":"Right Arm Layer","mesh":1},{"rotation":[0.9829629131445342,0.12940952255126037,-0.017037086855465768,0.12940952255126018],"name":"RightArm","children":[0,1]},{"children":[2]}],"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":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963}],"buffers":[{"byteLength":1680,"uri":"data:application/octet-stream;base64,AACMQAAAcEEAAHA/AACMQAAAcEEAAHC/AACMQAAA8EAAAHA/AACMQAAA8EAAAHC/AAAgQAAAcEEAAHC/AAAgQAAAcEEAAHA/AAAgQAAA8EAAAHC/AAAgQAAA8EAAAHA/AAAgQAAAcEEAAHC/AACMQAAAcEEAAHC/AAAgQAAAcEEAAHA/AACMQAAAcEEAAHA/AAAgQAAA8EAAAHA/AACMQAAA8EAAAHA/AAAgQAAA8EAAAHC/AACMQAAA8EAAAHC/AAAgQAAAcEEAAHA/AACMQAAAcEEAAHA/AAAgQAAA8EAAAHA/AACMQAAA8EAAAHA/AACMQAAAcEEAAHC/AAAgQAAAcEEAAHC/AACMQAAA8EAAAHC/AAAgQAAA8EAAAHC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ABAkPwAgoD4A8C8/ACCgPgAQJD8A4P8+APAvPwDg/z4AEDw/ACCgPgDwRz8AIKA+ABA8PwDg/z4A8Ec/AOD/PgDwOz8A4J8+ABAwPwDgnz4A8Ds/ACCIPgAQMD8AIIg+APBHPwAgiD4AEDw/ACCIPgDwRz8A4J8+ABA8PwDgnz4AEEg/ACCgPgDwUz8AIKA+ABBIPwDg/z4A8FM/AOD/PgAQMD8AIKA+APA7PwAgoD4AEDA/AOD/PgDwOz8A4P8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACRQACAckEAAIw/AACRQACAckEAAIy/AACRQAAA60AAAIw/AACRQAAA60AAAIy/AAAWQACAckEAAIy/AAAWQACAckEAAIw/AAAWQAAA60AAAIy/AAAWQAAA60AAAIw/AAAWQACAckEAAIy/AACRQACAckEAAIy/AAAWQACAckEAAIw/AACRQACAckEAAIw/AAAWQAAA60AAAIw/AACRQAAA60AAAIw/AAAWQAAA60AAAIy/AACRQAAA60AAAIy/AAAWQACAckEAAIw/AACRQACAckEAAIw/AAAWQAAA60AAAIw/AACRQAAA60AAAIw/AACRQACAckEAAIy/AAAWQACAckEAAIy/AACRQAAA60AAAIy/AAAWQAAA60AAAIy/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAkPwAAED8AADA/AAAQPwAAJD8AAEA/AAAwPwAAQD8AAEA/AAAQPwAATD8AABA/AABAPwAAQD8AAEw/AABAPwAAPD8AABA/AAAwPwAAED8AADw/AAAEPwAAMD8AAAQ/AABIPwAABD8AADw/AAAEPwAASD8AABA/AAA8PwAAED8AAEw/AAAQPwAAWD8AABA/AABMPwAAQD8AAFg/AABAPwAAMD8AABA/AAA8PwAAED8AADA/AABAPwAAPD8AAEA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUA"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[4.375,15,0.9375],"min":[2.5,7.5,-0.9375],"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.827880859375,0.499755859375],"min":[0.640869140625,0.265869140625],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[4.53125,15.15625,1.09375],"min":[2.34375,7.34375,-1.09375],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.84375,0.75],"min":[0.640625,0.515625],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"artifact_key.png"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png"}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]}]} \ No newline at end of file diff --git a/mods/artifact_mechanisms/basics.lua b/mods/artifact_mechanisms/basics.lua index a01304e..bfe5cf7 100644 --- a/mods/artifact_mechanisms/basics.lua +++ b/mods/artifact_mechanisms/basics.lua @@ -21,13 +21,14 @@ minetest.register_entity(":artifact:lever_display", { end e.object:set_armor_groups{immortal = 1} extend(e, minetest.deserialize(data) or {}) + if not e.rotation then e.rotation = vector.zero() end levers[e.object:get_pos():round():to_string()] = e end, on_deactivte = function(e) levers[e.object:get_pos():round():to_string()] = nil end, get_staticdata = function(e) - return {rotation = e.rotation} + return minetest.serialize{rotation = e.rotation} end, on_interact = function(e) if e._active then @@ -36,6 +37,7 @@ minetest.register_entity(":artifact:lever_display", { e.object:set_animation({x=1,y=2}, 2, 0.1, false) minetest.after(0.5, function() e._no_interact = nil + e:trigger(false) end) else e._active = true @@ -43,10 +45,21 @@ minetest.register_entity(":artifact:lever_display", { e.object:set_animation({x=0,y=1}, 2, 0.1, false) minetest.after(0.5, function() e._no_interact = nil + e:trigger(true) end) end end, + trigger = function(e, state) + local receivers = minetest.deserialize(minetest.get_meta(e.object:get_pos():round()):get("receivers") or "return nil") + if receivers then + artifact.dispatch_event(receivers, {type = state and "on" or "off"}) + end + end, rotate = function(e, rot) + if not rot then + say "!" + return + end e.object:set_rotation(rot) e.rotation = rot e.object:set_properties { @@ -58,6 +71,7 @@ minetest.register_entity(":artifact:lever_display", { end }) + artifact.register_node("lever", { drawtype = "airlike", paramtype = "light", @@ -66,9 +80,13 @@ artifact.register_node("lever", { pointable = false, on_construct = function(pos) local m = minetest.get_meta(pos) - m:set_string("initialized", "true") - local rot = minetest.facedir_to_dir(minetest.get_node(pos).param2):dir_to_rotation() - minetest.add_entity(pos, "artifact:lever_display", minetest.serialize{type = "wood"}):get_luaentity():rotate(rot) + local rot = artifact.facedir_to_rotation(minetest.get_node(pos).param2) + if m:contains("initialized") then + levers[pos:to_string()]:rotate(rot) + else + m:set_string("initialized", "true") + minetest.add_entity(pos, "artifact:lever_display", minetest.serialize{type = "wood"}):get_luaentity():rotate(rot) + end end, on_destruct = function(pos) levers[pos:to_string()].object:remove() @@ -79,15 +97,18 @@ artifact.register_node("lever", { -- Dynamically initialize doors that were mapgen'd in. if not m:contains("initialized") then m:set_string("initialized", "true") - local rot = minetest.facedir_to_dir(minetest.get_node(pos).param2):dir_to_rotation() + local rot = artifact.facedir_to_rotation(minetest.get_node(pos).param2) minetest.add_entity(pos, "artifact:lever_display", minetest.serialize{type = "wood"}):get_luaentity():rotate(rot) end end, + on_rightclick = function(pos) + local rot = artifact.facedir_to_rotation(minetest.get_node(pos).param2) + levers[pos:to_string()]:rotate(rot) + end, on_rotate = function(pos, node, p, click, param2) - local node = minetest.get_node(pos) node.param2 = param2 minetest.swap_node(pos, node) - local rot = minetest.facedir_to_dir(node.param2):dir_to_rotation() + local rot = artifact.facedir_to_rotation(param2) levers[pos:to_string()]:rotate(rot) return true end diff --git a/mods/artifact_mechanisms/colors.lua b/mods/artifact_mechanisms/colors.lua new file mode 100644 index 0000000..46754d0 --- /dev/null +++ b/mods/artifact_mechanisms/colors.lua @@ -0,0 +1,14 @@ + +artifact.register_entity("ccolor_swapper_display", { + initial_properties = { + visual = "mesh", + mesh = "artifact_color_swapper.gltf", + textures = {"artifact_color_swapper_gold.png"} + } +}) + +artifact.register_node("color_swapper", { + on_construct = function(pos) + + end, +}) diff --git a/mods/artifact_mechanisms/doors.lua b/mods/artifact_mechanisms/doors.lua index 88d48f2..a590722 100644 --- a/mods/artifact_mechanisms/doors.lua +++ b/mods/artifact_mechanisms/doors.lua @@ -25,14 +25,17 @@ minetest.register_entity(":artifact:door", { return (e._open and vector.new(-0.5, 0.5, 0) or vector.new(0, 0.5, 0.5)):rotate(e.object:get_rotation()) end, on_activate = function(e, data) - if not minetest.get_node(e.object:get_pos()).name:find "door" then + local node = minetest.get_node(e.object:get_pos()) + if not node.name:find "door" then e.object:remove() return end e.object:set_armor_groups{immortal = 1} extend(e, minetest.deserialize(data) or {}) - if e.type == "iron" and e._locked == nil then - e._locked = true + if e.type == "iron" then + if e._locked == nil then + e._locked = true + end e.object:set_properties { textures = {"artifact_door_iron.png"}, } @@ -45,50 +48,69 @@ minetest.register_entity(":artifact:door", { e._no_interact = true end e._name = ""..math.random() + local nm = minetest.get_meta(e.object:get_pos()) + if (node.name:find "_open") and not e._open then + e:open(true) + elseif not (node.name:find "_open") and e._open then + e:close(true) + end doors[e.object:get_pos():round():to_string()] = e end, on_deactivate = function(e) doors[e.object:get_pos():round():to_string()] = nil end, on_interact = function(e) + if e._locked then return end if e._open then - e._open = nil - e._no_interact = true - e._name = ""..math.random() - e.object:set_animation({x=0.5,y=1}, 1.5, 0.1, false) - minetest.after(0.1, function() - local pos = e.object:get_pos():round() - local node = minetest.get_node(pos) - node.name = "door_"..e.type - minetest.swap_node(pos, node) - e.object:set_properties { - selectionbox = artifact.rotate_selectionbox({ - -0.5, -0.5, -0.5, - 0.5, 1.5, -6/16 - }, e.rotation) - } - end) - minetest.after(0.25, function() + e:close() + else + e:open() + end + end, + open = function(e, snap) + if e._open then return end + e._open = true + e._no_interact = true + e._name = ""..math.random() + e.object:set_animation({x=snap and 0.5 or 0,y=0.5}, 1.5, 0.1, false) + minetest.after(snap and 0 or 0.1, function() + local pos = e.object:get_pos():round() + local node = minetest.get_node(pos) + node.name = "door_"..e.type.."_open" + minetest.swap_node(pos, node) + e.object:set_properties { + selectionbox = artifact.rotate_selectionbox({ + 0.5, -0.5, -0.5, + 6/16, 1.5, 0.5 + }, e.rotation) + } + end) + if not e._locked then + minetest.after(snap and 0 or 0.25, function() e._no_interact = nil end) - else - e._open = true - e._no_interact = true - e._name = ""..math.random() - e.object:set_animation({x=0,y=0.5}, 1.5, 0.1, false) - minetest.after(0.1, function() - local pos = e.object:get_pos():round() - local node = minetest.get_node(pos) - node.name = "door_"..e.type.."_open" - minetest.swap_node(pos, node) - e.object:set_properties { - selectionbox = artifact.rotate_selectionbox({ - 0.5, -0.5, -0.5, - 6/16, 1.5, 0.5 - }, e.rotation) - } - end) - minetest.after(0.25, function() + end + end, + close = function(e, snap) + if not e._open then return end + e._open = nil + e._no_interact = true + e._name = ""..math.random() + e.object:set_animation({x=snap and 1 or 0.5,y=1}, 1.5, 0.1, false) + minetest.after(snap and 0 or 0.1, function() + local pos = e.object:get_pos():round() + local node = minetest.get_node(pos) + node.name = "door_"..e.type + minetest.swap_node(pos, node) + e.object:set_properties { + selectionbox = artifact.rotate_selectionbox({ + -0.5, -0.5, -0.5, + 0.5, 1.5, -6/16 + }, e.rotation) + } + end) + if not e._locked then + minetest.after(snap and 0 or 0.25, function() e._no_interact = nil end) end @@ -113,6 +135,43 @@ minetest.register_entity(":artifact:door", { }) local function register_basic_door(type) + local function onload(pos) + local m = minetest.get_meta(pos) + -- Dynamically initialize doors that were mapgen'd in. + if not m:contains("initialized") then + m:set_string("initialized", "true") + local rot = minetest.facedir_to_dir(minetest.get_node(pos).param2):dir_to_rotation() + rot.y = rot.y -math.pi + minetest.add_entity(pos, "artifact:door", minetest.serialize{type = type}):get_luaentity():rotate(rot) + end + end + local function ondestruct(pos, reason) + if reason == "whack" then + -- TODO: Particles + end + doors[pos:to_string()].object:remove() + doors[pos:to_string()] = nil + end + local function onsignal(pos, event) + local door = doors[vector.to_string(pos)] + if event.type == "on" then + if door then + door:open() + else + local node = minetest.get_node(pos) + node.name = "door_"..e.type.."_open" + minetest.swap_node(pos, node) + end + elseif event.type == "off" then + if door then + door:close() + else + local node = minetest.get_node(pos) + node.name = "door_"..e.type.."_open" + minetest.swap_node(pos, node) + end + end + end artifact.register_node("door_"..type, { drawtype = "nodebox", node_box = { @@ -128,30 +187,10 @@ local function register_basic_door(type) paramtype = "light", pointable = false, groups = {call_on_load = 1, whackable = type == "wood" and 1 or nil}, - on_construct = function(pos) - local m = minetest.get_meta(pos) - m:set_string("initialized", "true") - local rot = minetest.facedir_to_dir(minetest.get_node(pos).param2):dir_to_rotation() - rot.y = rot.y -math.pi - minetest.add_entity(pos, "artifact:door", minetest.serialize{type = type}):get_luaentity():rotate(rot) - end, - on_destruct = function(pos, reason) - if reason == "whack" then - -- TODO: Particles - end - doors[pos:to_string()].object:remove() - doors[pos:to_string()] = nil - end, - on_load = function(pos) - local m = minetest.get_meta(pos) - -- Dynamically initialize doors that were mapgen'd in. - if not m:contains("initialized") then - m:set_string("initialized", "true") - local rot = minetest.facedir_to_dir(minetest.get_node(pos).param2):dir_to_rotation() - rot.y = rot.y -math.pi - minetest.add_entity(pos, "artifact:door", minetest.serialize{type = type}):get_luaentity():rotate(rot) - end - end + on_construct = onload, + on_destruct = ondestruct, + on_load = onload, + on_signal = onsignal }) artifact.register_node("door_"..type.."_open", { drawtype = "nodebox", @@ -168,27 +207,10 @@ local function register_basic_door(type) paramtype = "light", pointable = false, groups = {call_on_load = 1, whackable = type == "wood" and 1 or nil}, - on_construct = function(pos) - local m = minetest.get_meta(pos) - m:set_string("initialized", "true") - local rot = minetest.facedir_to_dir(minetest.get_node(pos).param2):dir_to_rotation() - rot.y = rot.y -math.pi - minetest.add_entity(pos, "artifact:door", minetest.serialize{type = type}):get_luaentity():rotate(rot) - end, - on_destruct = function(pos) - doors[pos:to_string()].object:remove() - doors[pos:to_string()] = nil - end, - on_load = function(pos) - local m = minetest.get_meta(pos) - -- Dynamically initialize doors that were mapgen'd in. - if not m:contains("initialized") then - m:set_string("initialized", "true") - local rot = minetest.facedir_to_dir(minetest.get_node(pos).param2):dir_to_rotation() - rot.y = rot.y -math.pi - minetest.add_entity(pos, "artifact:door", minetest.serialize{type = type}):get_luaentity():rotate(rot) - end - end + on_construct = onload, + on_destruct = ondestruct, + on_load = onload, + on_signal = onsignal }) end diff --git a/mods/artifact_mechanisms/init.lua b/mods/artifact_mechanisms/init.lua index e0858aa..97a7056 100644 --- a/mods/artifact_mechanisms/init.lua +++ b/mods/artifact_mechanisms/init.lua @@ -13,6 +13,239 @@ artifact.register_craftitem("cancel", { inventory_image = "artifact_cancel.png" }) +--- @param receivers: A list of node positions to notify. +--- @param event: The event to send. +function artifact.dispatch_event(receivers, event) + for _, x in ipairs(receivers) do + -- Ensure that nodes are available. + minetest.load_area(x.pos) + local node = minetest.get_node(x.pos) + local def = minetest.registered_nodes[node.name] + if def.on_signal then + def.on_signal(x.pos, event, x.channel or "gold") + end + end +end + + include "basics.lua" include "doors.lua" include "chest.lua" + + +function artifact.load_schematic(dst, path, rot) + minetest.place_schematic(dst, path..".mts", rot or "0") + local f = io.open(path..".json") + local meta = minetest.parse_json(f:read("a")) + f:close() + -- Load auxiliary metadata. + for p, m in pairs(meta or {}) do + local pos = dst +vector.from_string(p) + -- Transform all position fields back into global space. + if m.fields and m.fields.receivers then + local receivers = minetest.deserialize(m.fields.receivers) + for i, x in ipairs(receivers) do + x.pos = vector.add(x.pos, dst) + receivers[i] = x + end + m.fields.receivers = minetest.serialize(receivers) + end + minetest.get_meta(pos):from_table(m) + local def = minetest.registered_nodes[minetest.get_node(pos).name] + if def.on_construct then + def.on_construct(pos) + end + end +end + + +minetest.register_entity(":test", { + initial_properties = { + static_save = false, + visual = "mesh", + mesh = "artifact_character.gltf", + }, + on_activate = function(e) +-- e.object:set_bone_override("root", { +-- position = {vec = vector.new(15,15,15)} +-- }) + end, + on_rightclick = function(e, p) + local v = vector.new(0, p:get_properties().eye_height *10, 0) + p:set_eye_offset(v,v,v) + p:set_attach(e.object, "Head") + end, + on_punch = function(e, p) + p:set_detach() + end +}) + + +if artifact.debug then + + local link_colors = { + "gold", + "red", + "blue" + } + + if minetest.get_modpath("rhotator") then + minetest.override_item("rhotator:screwdriver", { + pointabilities = { + nodes = { + -- This gets added to everything in debug mode. + ["group:dig_immediate"] = true, + air = false, + }, + objects = { + -- The display entities should all be immortal. + ["group:immortal"] = false + } + }, + }) + minetest.override_item("testtools:param2tool", { + pointabilities = { + nodes = { + -- This gets added to everything in debug mode. + ["group:dig_immediate"] = true, + air = false, + }, + objects = { + -- The display entities should all be immortal. + ["group:immortal"] = false + } + }, + }) + end + + artifact.register_craftitem("linker_tool", { + inventory_image = "artifact_linker_tool.png", + stack_max = 1, + pointabilities = { + nodes = { + -- This gets added to everything in debug mode. + ["group:dig_immediate"] = true, + air = false, + }, + objects = { + -- The display entities should all be immortal. + ["group:immortal"] = false + } + }, + on_secondary_use = function(s, p, pt) + local m = s:get_meta() + -- Just cycle through the colors list. + local color = link_colors[(table.indexof(link_colors, m:get("color") or "gold") +1) %#link_colors +1] + m:set_string("color", color) + m:set_string("inventory_image", "[fill:16x16:0,0:"..color.."#00^artifact_linker_tool.png") + return s + end, + on_place = function(s, p, pt) + local m = artifact.players[p:get_player_name()] + if pt.type == "node" and m._linker_target then + local color = s:get_meta():get("color") or "gold" + local meta = minetest.get_meta(m._linker_target) + local receivers = minetest.deserialize(meta:get("receivers") or "return {}") + if m.ctl.sneak then + local idx = 0 + for i, x in ipairs(receivers) do + if vector.equals(x.pos, pt.under) then idx = i end + end + if idx > 0 then + table.remove(receivers, idx) + m.object:hud_remove(m._linker_receivers[idx]) + table.remove(m._linker_receivers, idx) + end + else + local idx = 0 + for i, x in ipairs(receivers) do + if vector.equals(x.pos, pt.under) then idx = i end + end + -- Ensure we haven't added this pos already. + if idx == 0 then + table.insert(receivers, {pos = pt.under, channel = color}) + table.insert(m._linker_receivers, m.object:hud_add { + type = "image_waypoint", + scale = {x=3, y=3}, + world_pos = pt.under, + text = "artifact_linker_tool.png^[colorize:"..color..":64" + }) + end + end + meta:set_string("receivers", minetest.serialize(receivers)) + end + end, + on_use = function(s, p, pt) + local m = artifact.players[p:get_player_name()] + if pt.type == "node" then + m._linker_target = pt.under + if m._linker_target_hud then + m.object:hud_remove(m._linker_target_hud) + end + m._linker_target_hud = m.object:hud_add { + type = "image_waypoint", + scale = {x=3, y=3}, + world_pos = pt.under, + text = "artifact_linker_tool.png" + } + if m._linker_receivers then + for _, x in ipairs(m._linker_receivers) do + m.object:hud_remove(x) + end + end + m._linker_receivers = {} + local receivers = minetest.deserialize(minetest.get_meta(m._linker_target):get("receivers") or "return {}") + for _, x in ipairs(receivers) do + table.insert(m._linker_receivers, m.object:hud_add { + type = "image_waypoint", + scale = {x=3, y=3}, + world_pos = x.pos, + text = "artifact_linker_tool.png^[colorize:"..x.channel..":64" + }) + end + end + end + }) + + -- To make life easier, simply require worldedit in order to export an area. + if minetest.global_exists("worldedit") then + minetest.mkdir(minetest.get_worldpath().."/schems") + minetest.register_chatcommand("export", { + privs = {server = true}, + params = "", + description = "Export the selected region as a schematic, with all node metadata stored in an adjacent JSON file.", + func = function(name, args) + local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] + print(dump(minetest.create_schematic(pos1, pos2, nil, minetest.get_worldpath().."/schems/"..args..".mts"))) + local minp = vector.sort(pos1, pos2) + local meta = {} + for _, pos in pairs(minetest.find_nodes_with_meta(pos1, pos2)) do + local mt = minetest.get_meta(pos):to_table() + mt.fields.initialized = nil + -- Transform all position fields into local coordinate space. + if mt.fields.receivers then + local receivers = minetest.deserialize(mt.fields.receivers) + for i, x in ipairs(receivers) do + x.pos = vector.subtract(x.pos, minp) + receivers[i] = x + end + mt.fields.receivers = minetest.serialize(receivers) + end + meta[(pos -minp):to_string()] = mt + end + local f = io.open(minetest.get_worldpath().."/schems/"..args..".json", "w") + f:write(minetest.write_json(meta)) + f:flush() + f:close() + end + }) + end + + minetest.register_chatcommand("load", { + privs = {server = true}, + func = function(name, args) + artifact.load_schematic(artifact.players[name].pos:round(), minetest.get_worldpath().."/schems/"..args) + end + }) +end + diff --git a/mods/artifact_player/init.lua b/mods/artifact_player/init.lua index 7fb4a54..fcbcc1f 100644 --- a/mods/artifact_player/init.lua +++ b/mods/artifact_player/init.lua @@ -18,7 +18,7 @@ Player = setmetatable({ m.character = m.meta:get("character") or "key" m.inv = p:get_inventory() - m.inv:set_stack("main", 1, ItemStack("input")) + m.inv:set_stack("main", 1, ItemStack("input_"..m.character)) -- Generic black sky, since the whole game takes place underground. p:set_sky{ @@ -219,7 +219,7 @@ Player = setmetatable({ -- MARK: Radial menu handling - if ctl.place and not m.ctl.place and wi:get_name() == "artifact:input" then + if ctl.place and not m.ctl.place and wi:get_name():find "artifact:input" and (not m.pointed_obj or m.pointed_obj._no_interact) then artifact.show_radial_menu(m, { name = "construct", "test", @@ -228,7 +228,7 @@ Player = setmetatable({ "test4", "test5" }) - elseif m._menu and not (ctl.place and wi:get_name() == "artifact:input") then + elseif m._menu and not (ctl.place and wi:get_name():find "artifact:input") or (m.pointed_obj and not m.pointed_obj._no_interact) then artifact.dismiss_radial_menu(m, "construct") elseif m._menu then local dx = m.yaw -yaw @@ -324,17 +324,36 @@ Player = setmetatable({ end }) -artifact.register_craftitem("input", { - inventory_image = "artifact_rmb_100.png", - range = 0, - on_drop = function(s, p, pos) - local m = artifact.players[p:get_player_name()] - if artifact.debug or artifat.story.state > artifact.story.states.pre_vix then - artifact.swap_character(m) +local _hand = minetest.registered_items[""] + +function artifact.register_input(name) + artifact.register_node("input_"..name, { + inventory_image = "artifact_rmb_100.png", + description = "", + paramtype = "light", + drawtype = "mesh", + mesh = "artifact_hand.gltf", + tiles = {"artifact_"..name..".png"}, + use_texture_alpha = "opaque", + visual_scale = 1, + wield_scale = vector.new(2,2,2), + node_placement_prediction = "", + on_construct = function(pos) + minetest.remove_node(pos) + end, + drop = "", + range = 0, + on_drop = function(s, p, pos) + local m = artifact.players[p:get_player_name()] + if artifact.debug or artifat.story.state > artifact.story.states.pre_vix then + artifact.swap_character(m) + end + return s end - return s - end -}) + }) +end +artifact.register_input "key" +artifact.register_input "vix" minetest.register_globalstep(function() diff --git a/mods/artifact_player/radial_menu.lua b/mods/artifact_player/radial_menu.lua index 22c9674..2a5d934 100644 --- a/mods/artifact_player/radial_menu.lua +++ b/mods/artifact_player/radial_menu.lua @@ -38,7 +38,7 @@ end function ns.dismiss_radial_menu(m, name) -- This is in case we only want to close a specific menu while leaving others intact. - if name and m._menu.name ~= name then return end + if name and (not m._menu or m._menu.name ~= name) then return end for _, x in ipairs(m._menu) do x:remove(m) end