local large_doors = {} minetest.register_entity(":artifact:large_door_display", { initial_properties = { visual = "mesh", mesh = "artifact_door_large.gltf", textures = {"artifact_door_large.png"}, selectionbox = { -1.5, -0.5, -2/16, 1.5, 2.5, 2/16 }, pointable = artifact.debug }, on_activate = function(e, data) local pos = e.object:get_pos():round() local node = minetest.get_node(pos) if not node.name:find "large_door" then e.object:remove() return end e.object:set_armor_groups{immortal = 1} extend(e, minetest.deserialize(data) or {}) if e.rotation then e.rotation.y = e.rotation.y +math.pi e:rotate(e.rotation) else e.rotation = vector.zero() end e._name = ""..math.random() if e._locked == nil then e._locked = true end 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 -- In case our saved state differs from the node's stored state, e.g. if a trigger -- updated an effector in an unloaded area and couldn't get at the display entity, -- we should always be sure to match the node meta. if nm:get_string("locked") == "true" then e._locked = true end if nm:contains("locks") then e.locks = minetest.deserialize(nm:get_string("locks")) end if e.locks then e:set_locks(e.locks) end e.collider_a = minetest.add_entity(pos, "display") e.collider_a:set_properties { physical = true, collisionbox = artifact.rotate_selectionbox({ -0.75, -1.5, -2/16, 0.75, 1.5, 2/16 }, e.rotation) } e.collider_a:set_attach(e.object, "a") e.collider_b = minetest.add_entity(pos, "display") e.collider_b:set_properties { physical = true, collisionbox = artifact.rotate_selectionbox({ -0.75, -1.5, -2/16, 0.75, 1.5, 2/16 }, e.rotation) } e.collider_b:set_attach(e.object, "b") large_doors[e.object:get_pos():round():to_string()] = e end, on_deactivate = function(e) large_doors[e.object:get_pos():round():to_string()] = nil if e.collider_a then e.collider_a:remove() e.collider_a = nil end if e.collider_b then e.collider_b:remove() e.collider_b = nil end end, get_staticdata = function(e) return minetest.serialize{type = e.type, _locked = e._locked, rotation = e.rotation, locks = e.locks} end, open = function(e) -- We're already open, so there's nothing to do. if e._open then return end e._open = true e._animating = true e.object:set_animation({x=0,y=1}, 1.25, 0.1, false) local pos = e.object:get_pos():round() minetest.get_meta(pos):set_string("open", "true") minetest.after(0.75, function() e._animating = nil end) end, close = function(e) if not e._open then return end e._open = false e._animating = true e.object:set_animation({x=1,y=2}, 1.25, 0.1, false) local pos = e.object:get_pos():round() minetest.get_meta(pos):set_string("open", "false") minetest.after(0.75, function() e._animating = nil end) end, on_step = function(e) -- If we're locked or in a transition state, there's no need to run these checks. if e._locked or e._animating then return end -- We don't use objects_inside_radius to avoid wasting time finding and ignoring display entities. -- Instead, we just do a radius check on each player. local pos = e.object:get_pos() local found = artifact.sidekick.pos and artifact.sidekick.pos:distance(pos) < 4 for _, m in pairs(artifact.players) do if m.object:get_pos():distance(pos) < 4 then found = true break end end if found and not e._open then e:open() elseif not found and e._open then e:close() end end, rotate = function(e, rot) e.object:set_rotation(rot) e.rotation = rot e.object:set_properties { selectionbox = artifact.rotate_selectionbox({ -1.5, -0.5, -2/16, 1.5, 2.5, 2/16 }, e.rotation) } if e.collider_a then e.collider_a:set_properties { collisionbox = artifact.rotate_selectionbox({ -0.75, -1.5, -2/16, 0.75, 1.5, 2/16 }, e.rotation) } end if e.collider_b then e.collider_b:set_properties { collisionbox = artifact.rotate_selectionbox({ -0.75, -1.5, -2/16, 0.75, 1.5, 2/16 }, e.rotation) } end end, -- We include this so we can update the entity's locks without having to unload and reload them. on_punch = function(e) local locks = minetest.deserialize(minetest.get_meta(e.object:get_pos():round()):get_string("locks")) e:set_locks(locks) end, set_locks = function(e, locks) e.locks = locks local mod = "" local locked = false for i, color in ipairs(locks) do if not locks[color] then locked = true end mod = mod..":"..((i -1) *5 +74)..",0=artifact_lock_"..color.."_"..(locks[color] and "on" or "off")..".png" end e._locked = locked e.object:set_properties { textures = {"[combine:128x128:0,0=artifact_door_large.png"..mod} } end, }) 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 = artifact.facedir_to_rotation(minetest.get_node(pos).param2) local locks = {red = false, blue = false, green = false, "red", "green", "blue"} minetest.add_entity(pos, "artifact:large_door_display", minetest.serialize{locks = locks}):get_luaentity():rotate(rot) minetest.get_meta(pos):set_string("locks", minetest.serialize(locks)) end end local function ondestruct(pos) large_doors[pos:to_string()].object:remove() large_doors[pos:to_string()] = nil end local function onsignal(pos, event, channel) if event.type == "on" or event.type == "pulse" then local e = large_doors[vector.to_string(pos)] if e then local locks = e.locks if locks[channel] ~= nil then locks[channel] = true end e:set_locks(locks) minetest.get_meta(pos):set_string("locks", minetest.serialize(locks)) else local m = minetest.get_meta(pos) local locks = minetest.deserialize(m:get_string("locks")) if locks[channel] ~= nil then locks[channel] = true end e:set_locks(locks) m:set_string("locks", minetest.serialize(locks)) end end end artifact.register_node("large_door", { drawtype = "airlike", paramtype = "light", walkable = false, selection_box = { type = "fixed", fixed = { -1.5, -0.5, -2/16, 1.5, 3.5, 2/16 } }, paramtype2 = "facedir", groups = {call_on_load = 1}, on_construct = onload, on_load = onload, on_destruct = ondestruct, on_signal = onsignal })