Add forcefields, and start building the map.

This commit is contained in:
Signal 2025-11-20 03:15:30 -05:00
parent 5fd67703c0
commit dd73665a23
19 changed files with 511 additions and 15 deletions

View file

@ -23,11 +23,14 @@ function ns.do_whack(m)
node = {name = node.name}, node = {name = node.name},
time = 0.1 time = 0.1
} }
if artifact.on_whacked then
artifact.on_whacked("node", m.pointed_node)
end
elseif m.pointed_obj and m.pointed_obj.on_whack then elseif m.pointed_obj and m.pointed_obj.on_whack then
if m.pointed_obj:on_whack() then if m.pointed_obj:on_whack() then
-- For use in artifact_help. -- For use in artifact_help.
if artifact.on_whacked then if artifact.on_whacked then
artifact.on_whacked() artifact.on_whacked("object", m.pointed_obj)
end end
end end
end end

View file

@ -3,6 +3,12 @@ local ns = artifact
local hud_pop_times = {} local hud_pop_times = {}
function ns.push_chat_message(msg, sender, splash, duration) function ns.push_chat_message(msg, sender, splash, duration)
-- Special handling for the character names (and if sent by a player, colorize based on character).
if sender == "Key" or artifact.players[sender] and artifact.players[sender].character == "key" then
sender = minetest.colorize("#284", sender)
elseif sender == "Vix" or artifact.players[sender] and artifact.players[sender].character == "vix" then
sender = minetest.colorize("#f5dd66", sender)
end
for name, m in pairs(artifact.players) do for name, m in pairs(artifact.players) do
local box = {} local box = {}
local w = minetest.get_player_window_information(name).size.x local w = minetest.get_player_window_information(name).size.x
@ -146,4 +152,9 @@ if artifact.debug then
end end
}) })
minetest.register_chatcommand("color", {
func = function(name, args)
ns.push_chat_message(minetest.colorize(args, "This is a colored message"), "Server")
end
})
end end

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

View file

@ -165,6 +165,22 @@ minetest.register_entity(":artifact:door", {
collisionbox = box collisionbox = box
} }
end, end,
on_hover = function(e, m)
if e.type == "wood" and e._locked then
m._whack_hud = m.object:hud_add {
type = "image_waypoint",
world_pos = e.object:get_pos():offset(0, 0.5, 0),
scale = {x=3,y=3},
text = "artifact_icon_whack.png"
}
end
end,
on_unhover = function(e, m)
if m._whack_hud then
m.object:hud_remove(m._whack_hud)
m._whack_hud = nil
end
end,
on_whack = function(e) on_whack = function(e)
if e.type == "wood" then if e.type == "wood" then
local pos = e.object:get_pos():round() local pos = e.object:get_pos():round()

View file

@ -0,0 +1,346 @@
artifact.register_node("forcefield", {
drawtype = "glasslike_framed",
use_texture_alpha = "blend",
light_source = 6,
tiles = {"artifact_forcefield_border.png", "artifact_forcefield_inner.png"},
visual_scale = 0.75,
diggable = false
})
local vm_data = {}
local vm_p2_data = {}
local c_air = minetest.get_content_id("air")
local c_forcefield = minetest.get_content_id("forcefield")
function artifact.place_forcefield(pos, def)
if def.type == "cube" then
if def.offset then pos = pos:add(def.offset) end
local vm = minetest.get_voxel_manip(pos:offset(-def.radius,-def.radius,-def.radius), pos:offset(def.radius,def.radius,def.radius))
local va = VoxelArea(vm:get_emerged_area())
vm:get_data(vm_data)
vm:get_param2_data(vm_p2_data)
for x = pos.x -def.radius, pos.x +def.radius, def.radius *2 do
for y = pos.y -def.radius, pos.y +def.radius do
for z = pos.z -def.radius, pos.z +def.radius do
local idx = va:index(x, y, z)
if vm_data[idx] == c_air then
vm_data[idx] = c_forcefield
vm_p2_data[idx] = 1
elseif vm_data[idx] == c_forcefield then
vm_p2_data[idx] = vm_p2_data[idx] +1
end
end
end
end
for y = pos.y -def.radius, pos.y +def.radius, def.radius *2 do
for x = pos.x -def.radius, pos.x +def.radius do
for z = pos.z -def.radius, pos.z +def.radius do
local idx = va:index(x, y, z)
if vm_data[idx] == c_air then
vm_data[idx] = c_forcefield
vm_p2_data[idx] = 1
elseif vm_data[idx] == c_forcefield then
vm_p2_data[idx] = vm_p2_data[idx] +1
end
end
end
end
for z = pos.z -def.radius, pos.z +def.radius, def.radius *2 do
for y = pos.y -def.radius, pos.y +def.radius do
for x = pos.x -def.radius, pos.x +def.radius do
local idx = va:index(x, y, z)
if vm_data[idx] == c_air then
vm_data[idx] = c_forcefield
vm_p2_data[idx] = 1
elseif vm_data[idx] == c_forcefield then
vm_p2_data[idx] = vm_p2_data[idx] +1
end
end
end
end
vm:set_data(vm_data)
vm:set_param2_data(vm_p2_data)
vm:write_to_map()
elseif def.type == "plane" then
local vm = minetest.get_voxel_manip(pos:offset(-def.radius,-def.radius,-def.radius), pos:offset(def.radius,def.radius,def.radius))
local va = VoxelArea(vm:get_emerged_area())
vm:get_data(vm_data)
vm:get_param2_data(vm_p2_data)
local x
local y
local z
if def.axis == "x" then
local x = pos.x
for y = pos.y -def.radius, pos.y +def.radius do
for z = pos.z -def.radius, pos.z +def.radius do
local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
if vm_data[idx] == c_air then
vm_data[idx] = c_forcefield
vm_p2_data[idx] = 1
elseif vm_data[idx] == c_forcefield and vm_p2_data[idx] > 0 then
vm_p2_data[idx] = vm_p2_data[idx] +1
end
end
end
elseif def.axis == "y" then
local y = pos.y
for x = pos.x -def.radius, pos.x +def.radius do
for z = pos.z -def.radius, pos.z +def.radius do
local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
if vm_data[idx] == c_air then
vm_data[idx] = c_forcefield
vm_p2_data[idx] = 1
elseif vm_data[idx] == c_forcefield and vm_p2_data[idx] > 0 then
vm_p2_data[idx] = vm_p2_data[idx] +1
end
end
end
elseif def.axis == "z" then
local z = pos.z
for y = pos.y -def.radius, pos.y +def.radius do
for x = pos.x -def.radius, pos.x +def.radius do
local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
if vm_data[idx] == c_air then
vm_data[idx] = c_forcefield
vm_p2_data[idx] = 1
elseif vm_data[idx] == c_forcefield and vm_p2_data[idx] > 0 then
vm_p2_data[idx] = vm_p2_data[idx] +1
end
end
end
end
vm:set_data(vm_data)
vm:set_param2_data(vm_p2_data)
vm:write_to_map()
end
end
function artifact.remove_forcefield(pos, def)
if def.type == "cube" then
if def.offset then pos = pos:add(def.offset) end
local vm = minetest.get_voxel_manip(pos:offset(-def.radius,-def.radius,-def.radius), pos:offset(def.radius,def.radius,def.radius))
local va = VoxelArea(vm:get_emerged_area())
vm:get_data(vm_data)
vm:get_param2_data(vm_p2_data)
for x = pos.x -def.radius, pos.x +def.radius, def.radius *2 do
for y = pos.y -def.radius, pos.y +def.radius do
for z = pos.z -def.radius, pos.z +def.radius do
local idx = va:index(x, y, z)
if vm_data[idx] == c_forcefield then
if vm_p2_data[idx] <= 1 then
vm_data[idx] = c_air
vm_p2_data[idx] = 0
else
vm_p2_data[idx] = vm_p2_data[idx] -1
end
end
end
end
end
for y = pos.y -def.radius, pos.y +def.radius, def.radius *2 do
for x = pos.x -def.radius, pos.x +def.radius do
for z = pos.z -def.radius, pos.z +def.radius do
local idx = va:index(x, y, z)
if vm_data[idx] == c_forcefield then
if vm_p2_data[idx] <= 1 then
vm_data[idx] = c_air
vm_p2_data[idx] = 0
else
vm_p2_data[idx] = vm_p2_data[idx] -1
end
end
end
end
end
for z = pos.z -def.radius, pos.z +def.radius, def.radius *2 do
for y = pos.y -def.radius, pos.y +def.radius do
for x = pos.x -def.radius, pos.x +def.radius do
local idx = va:index(x, y, z)
if vm_data[idx] == c_forcefield then
if vm_p2_data[idx] <= 1 then
vm_data[idx] = c_air
vm_p2_data[idx] = 0
else
vm_p2_data[idx] = vm_p2_data[idx] -1
end
end
end
end
end
vm:set_data(vm_data)
vm:set_param2_data(vm_p2_data)
vm:write_to_map()
elseif def.type == "plane" then
local vm = minetest.get_voxel_manip(pos:offset(-def.radius,-def.radius,-def.radius), pos:offset(def.radius,def.radius,def.radius))
local va = VoxelArea(vm:get_emerged_area())
vm:get_data(vm_data)
vm:get_param2_data(vm_p2_data)
local x
local y
local z
if def.axis == "x" then
local x = pos.x
for y = pos.y -def.radius, pos.y +def.radius do
for z = pos.z -def.radius, pos.z +def.radius do
local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
if vm_data[idx] == c_forcefield then
if vm_p2_data[idx] <= 1 then
vm_data[idx] = c_air
vm_p2_data[idx] = 0
else
vm_p2_data[idx] = vm_p2_data[idx] -1
end
end
end
end
elseif def.axis == "y" then
local y = pos.y
for x = pos.x -def.radius, pos.x +def.radius do
for z = pos.z -def.radius, pos.z +def.radius do
local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
if vm_data[idx] == c_forcefield then
if vm_p2_data[idx] <= 1 then
vm_data[idx] = c_air
vm_p2_data[idx] = 0
else
vm_p2_data[idx] = vm_p2_data[idx] -1
end
end
end
end
elseif def.axis == "z" then
local z = pos.z
for y = pos.y -def.radius, pos.y +def.radius do
for x = pos.x -def.radius, pos.x +def.radius do
local idx = va:index(x +def.offset.x, y +def.offset.y, z +def.offset.z)
if vm_data[idx] == c_forcefield then
if vm_p2_data[idx] <= 1 then
vm_data[idx] = c_air
vm_p2_data[idx] = 0
else
vm_p2_data[idx] = vm_p2_data[idx] -1
end
end
end
end
end
vm:set_data(vm_data)
vm:set_param2_data(vm_p2_data)
vm:write_to_map()
end
end
local function onload(pos)
local m = minetest.get_meta(pos)
if not m:contains("initialized") then
local conf = minetest.deserialize(m:get("config") or "return nil") or {type = "plane", radius = 5, axis = "x", offset = vector.new(3,0,0)}
m:set_string("_config", minetest.serialize(conf))
m:set_string("config", minetest.serialize(conf))
artifact.place_forcefield(pos, conf)
end
end
artifact.register_node("forcefield_generator", {
drawtype = "mesh",
mesh = "artifact_forcefield_generator.gltf",
tiles = {"artifact_forcefield_generator.png"},
paramtype = "light",
light_source = 10,
paramtype2 = "facedir",
groups = {whackable = 1},
collision_box = {
type = "fixed",
fixed = {
-1, -0.5, -0.5,
1, 0.5, 0.5
}
},
selection_box = {
type = "fixed",
fixed = {
-1, -0.5, -0.5,
1, 0.5, 0.5
}
},
on_construct = onload,
on_load = onload,
on_rightclick = artifact.debug and function(pos)
local m = minetest.get_meta(pos)
artifact.remove_forcefield(pos, minetest.deserialize(m:get("_config") or "return nil") or {type = "plane", radius = 5, axis = "x", offset = vector.new(3,0,0)})
local conf = minetest.deserialize(m:get("config") or "return nil") or {type = "plane", radius = 5, axis = "x", offset = 3}
m:set_string("_config", minetest.serialize(conf))
artifact.place_forcefield(pos, conf)
end or nil,
on_destruct = function(pos)
local m = minetest.get_meta(pos)
local conf = minetest.deserialize(m:get("config") or "return nil") or {type = "cube", radius = 5}
artifact.remove_forcefield(pos, conf)
local node = minetest.get_node(pos)
node.name = "forcefield_generator_off"
minetest.after(0, function()
minetest.set_node(pos, node)
end)
minetest.add_particlespawner {
pos = pos,
vel = vector.zero(),
acc = vector.new(0, -9.81, 0),
drag = vector.new(3, 0, 3),
radius = 0.1,
attract = {
kind = "point",
origin = pos,
strength = -50
},
texture = {
name = "artifact_light_gold.png",
alpha_tween = {1, 0}
},
size_tween = {
{
min = 3,
max = 6
},
{
min = 1,
max = 2
}
},
time = 0.3,
amount = 100,
}
end
})
artifact.register_node("forcefield_generator_off", {
drawtype = "mesh",
mesh = "artifact_forcefield_generator.gltf",
tiles = {"artifact_forcefield_generator_off.png"},
paramtype = "light",
light_source = 6,
paramtype2 = "facedir",
collision_box = {
type = "fixed",
fixed = {
-1, -0.5, -0.5,
1, 0.5, 0.5
}
},
selection_box = {
type = "fixed",
fixed = {
-1, -0.5, -0.5,
1, 0.5, 0.5
}
},
on_impact = artifact.debug and function(pos)
local node = minetest.get_node(pos)
node.name = "forcefield_generator"
minetest.set_node(pos, node)
end or nil,
})

View file

@ -35,10 +35,12 @@ include "doors.lua"
include "colors.lua" include "colors.lua"
include "large_doors.lua" include "large_doors.lua"
include "chest.lua" include "chest.lua"
include "forcefields.lua"
function artifact.load_schematic(dst, path, rot) function artifact.load_schematic(dst, path, rot)
minetest.place_schematic(dst, path..".mts", rot or "0") -- There's not a very good way to export without the forcefields' param2, but we can _import_ without them.
minetest.place_schematic(dst, path..".mts", rot or "0", {["artifact:forcefield"] = "air"})
local f = io.open(path..".json") local f = io.open(path..".json")
local meta = minetest.parse_json(f:read("a")) local meta = minetest.parse_json(f:read("a"))
f:close() f:close()

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

View file

@ -129,7 +129,7 @@ Player = setmetatable({
-- MARK: Pointing callbacks -- MARK: Pointing callbacks
local pointed_found = nil local pointed_found = nil
m.pointed_node = nil local pointed_node_found = nil
for x in minetest.raycast(m.pos, m.pos +(dir *5)) do for x in minetest.raycast(m.pos, m.pos +(dir *5)) do
-- We should ignore all objects when placing a grabbed node. -- We should ignore all objects when placing a grabbed node.
if x and x.type == "object" and not m._grabbed_item then if x and x.type == "object" and not m._grabbed_item then
@ -171,6 +171,24 @@ Player = setmetatable({
end end
end end
elseif x and x.type == "node" then elseif x and x.type == "node" then
pointed_node_found = true
x.node_under = minetest.get_node(x.under)
local was_whackable = m.pointed_node and minetest.registered_nodes[m.pointed_node.node_under.name].groups.whackable
local whackable = m.character == "key" and minetest.registered_nodes[x.node_under.name].groups.whackable
if whackable and not was_whackable then
m.whack_hud = m.object:hud_add {
type = "image_waypoint",
world_pos = x.under,
scale = {x=3,y=3},
text = "artifact_icon_whack.png"
}
elseif whackable and x.under ~= (m.pointed_node and m.pointed_node.under) then
m.object:hud_change(m.whack_hud, "world_pos", x.under)
elseif not whackable and m.whack_hud then
m.object:hud_remove(m.whack_hud)
m.whack_hud = nil
end
m.pointed_node = x m.pointed_node = x
if m.pointed_obj then if m.pointed_obj then
if m.pointed_obj.on_unhover then if m.pointed_obj.on_unhover then
@ -197,6 +215,14 @@ Player = setmetatable({
end end
m.pointed_obj = nil m.pointed_obj = nil
end end
if not pointed_node_found then
m.pointed_node = nil
if m.whack_hud then
m.object:hud_remove(m.whack_hud)
m.whack_hud = nil
end
end
local ctl = m.object:get_player_control() local ctl = m.object:get_player_control()
-- MARK: Animations -- MARK: Animations
@ -314,6 +340,8 @@ Player = setmetatable({
-- MARK: Radial menu handling -- MARK: Radial menu handling
--[[ Disabled for the present due to a dearth of usecases...
-- This should only work once we have Vix, since we can't use it without her. -- This should only work once we have Vix, since we can't use it without her.
if state >= artifact.story.states.main and ctl.place and not m.ctl.place and wi:get_name():find "artifact:input" and (not m.pointed_obj or not m.pointed_obj.on_interact or m.pointed_obj._no_interact) then if state >= artifact.story.states.main and ctl.place and not m.ctl.place and wi:get_name():find "artifact:input" and (not m.pointed_obj or not m.pointed_obj.on_interact or m.pointed_obj._no_interact) then
artifact.show_radial_menu(m, { artifact.show_radial_menu(m, {
@ -389,6 +417,8 @@ Player = setmetatable({
end end
end end
--]]
-- MARK: Health regen -- MARK: Health regen
if m.next_regen and time -m.next_regen >= 0 then if m.next_regen and time -m.next_regen >= 0 then

View file

@ -25,6 +25,35 @@ function ns.enter_init_state()
ns.play_intro_cutscene() ns.play_intro_cutscene()
end end
local vix_scene
minetest.register_entity(":artifact:vix_scene", {
initial_properties = {
visual = "mesh",
mesh = "artifact_scene_vix.gltf",
textures = {"artifact_vix.png"}
},
on_activate = function(e)
if state > ns.states.pre_vix then
e.object:remove()
end
e.object:set_armor_groups{immortal = 1}
e.object:set_animation({x=0,y=2}, 0.5, 0.1, true)
vix_scene = e
end,
on_deactivate = function(e)
vix_scene = nil
end,
on_step = function(e)
if e._can_check then
for _, m in pairs(artifact.players) do
if m.pos:distance(e.object:get_pos()) < 15 and m.dir:distance(m.pos:direction(e.object:get_pos())) < 0.2 then
artifact.push_chat_message("Hmm...", "Key", "artifact_key_splash_low.png")
e._can_check = false
end
end
end
end
})
function ns.enter_pre_vix_state() function ns.enter_pre_vix_state()
for _, m in pairs(artifact.players) do for _, m in pairs(artifact.players) do
@ -36,20 +65,49 @@ function ns.enter_pre_vix_state()
m.object:set_pos(artifact.origin:offset(0, -73.5, -4)) m.object:set_pos(artifact.origin:offset(0, -73.5, -4))
m:set_spawnpoint(artifact.origin:offset(0, -73.5, -4)) m:set_spawnpoint(artifact.origin:offset(0, -73.5, -4))
end end
minetest.after(5, function() minetest.add_entity(artifact.origin:offset(-16.5, -72.5, -17), "artifact:vix_scene")
local help = minetest.after(15, function()
for _, m in pairs(artifact.players) do for _, m in pairs(artifact.players) do
artifact.show_help_message(m, "Certain nodes can be broken by punching them with the blackrod.", "info") artifact.show_help_message(m, "Certain nodes can be broken by punching them with the blackrod.", "info")
end end
end) end)
db:set_string("checkpoint:pre_vix", "begin")
artifact.on_whacked = function(type, target)
local checkpoint = db:get("checkpoint:pre_vix")
if checkpoint == "begin" then -- We're still in the start closet.
if type == "object" then
help:cancel()
vix_scene._can_check = true
end
db:set_string("checkpoint:pre_vix", "in_room")
elseif checkpoint == "in_room" then -- We're actually in the room.
if target.node_under.name:find "forcefield_generator" then
local num = db:get_int("checkpoint:pre_vix_fields_broken") +1
db:set_int("checkpoint:pre_vix_fields_broken", num)
if num == 1 then -- Key breaks his first generator.
minetest.after(1, function()
artifact.push_chat_message("Hehe.", "Key", "artifact_key_splash_low.png")
end)
elseif num == 5 then -- All generators are down.
vix_scene._can_check = nil
end
end
end
end
end
function ns.enter_main_state()
vix_scene.object:remove()
end end
function ns.enter_state(to) function ns.enter_state(to)
state = to state = to
minetest.log("State changed to "..to..".")
if state == ns.states.init then if state == ns.states.init then
ns.enter_init_state() ns.enter_init_state()
elseif state == ns.states.pre_vix then elseif state == ns.states.pre_vix then
ns.enter_pre_vix_state() ns.enter_pre_vix_state()
elseif state == ns.states.main then
ns.enter_main_state()
end end
db:set_int("state", state) db:set_int("state", state)
end end

File diff suppressed because one or more lines are too long

View file

@ -257,7 +257,7 @@ artifact.register_node("vines", {
type = "fixed", type = "fixed",
fixed = { fixed = {
-0.5, -0.5, 0.5, -0.5, -0.5, 0.5,
0.5, 0.5, 0.45 0.5, 0.5, 0.35
} }
}, },
paramtype = "light", paramtype = "light",
@ -279,7 +279,7 @@ artifact.register_node("vines_dry", {
type = "fixed", type = "fixed",
fixed = { fixed = {
-0.5, -0.5, 0.5, -0.5, -0.5, 0.5,
0.5, 0.5, 0.45 0.5, 0.5, 0.35
} }
}, },
paramtype = "light", paramtype = "light",
@ -354,7 +354,16 @@ local function register_lamp(color, brightness)
2/16, 4/16, 2/16 2/16, 4/16, 2/16
} }
} }
},
selection_box = {
type = "fixed",
fixed = {
{
-2/16,0,-2/16,
2/16, 4/16, 2/16
} }
}
},
}) })
artifact.register_node("lamp_"..color.."_wall", { artifact.register_node("lamp_"..color.."_wall", {
drawtype = "mesh", drawtype = "mesh",
@ -376,6 +385,19 @@ local function register_lamp(color, brightness)
1/16, 6/16, 8/16 1/16, 6/16, 8/16
}, },
} }
},
selection_box = {
type = "fixed",
fixed = {
{
-2/16,-1/16,-1/16,
2/16, 3/16, 3/16
},
{
-1/16,4/16,-1/16,
1/16, 6/16, 8/16
},
}
} }
}) })
artifact.register_node("lamp_"..color.."_hanging", { artifact.register_node("lamp_"..color.."_hanging", {
@ -393,13 +415,23 @@ local function register_lamp(color, brightness)
2/16, 4/16, 2/16 2/16, 4/16, 2/16
} }
} }
},
selection_box = {
type = "fixed",
fixed = {
{
-2/16,0,-2/16,
2/16, 4/16, 2/16
}
}
} }
}) })
end end
register_lamp("red", 6) register_lamp("red", 6)
register_lamp("gold", 8) register_lamp("green", 8)
register_lamp("blue", 10) register_lamp("blue", 10)
register_lamp("gold", 12)
artifact.register_node("light", { artifact.register_node("light", {
@ -420,7 +452,7 @@ if artifact.debug then
minetest.register_decoration { minetest.register_decoration {
deco_type = "simple", deco_type = "simple",
decoration = "lamp_blue", decoration = "lamp_gold",
place_on = "stone", place_on = "stone",
fill_ratio = 0.02, fill_ratio = 0.02,
} }

View file

@ -1,4 +0,0 @@
# Made in Blockbench 4.12.5
newmtl m_e3e69eb0-b6c9-b9c2-690f-4d510df185b2
map_Kd artifact_torch.png
newmtl none

View file

@ -1 +1 @@
{"(45, 21, 52)":{"fields":null,"inventory":null}} {"(24, 24, 41)":{"fields":{"_config":"return {type=\"cube\",offset={z=1,x=4,y=0},radius=3,axis=\"x\"}","config":"return {radius=3,axis=\"x\",offset={z=1,x=4,y=0},type=\"cube\"}"},"inventory":null},"(24, 24, 43)":{"fields":{"_config":"return {type=\"cube\",offset={z=-1,x=4,y=0},radius=3,axis=\"x\"}","config":"return {radius=3,axis=\"x\",offset={z=-1,x=4,y=0},type=\"cube\"}"},"inventory":null},"(39, 33, 33)":{"fields":null,"inventory":null},"(39, 34, 52)":{"fields":{"inverted":"true"},"inventory":null},"(41, 33, 34)":{"fields":{"_config":"return {type=\"plane\",offset={z=12,x=-6,y=0},radius=15,axis=\"x\"}","config":"return {radius=15,axis=\"x\",offset={z=12,x=-6,y=0},type=\"plane\"}"},"inventory":null},"(43, 33, 33)":{"fields":{"inverted":"true"},"inventory":null},"(43, 34, 52)":{"fields":null,"inventory":null},"(44, 24, 33)":{"fields":{"open":"false"},"inventory":null},"(44, 24, 38)":{"fields":{"_config":"return {type=\"plane\",radius=15,offset={z=0,x=-8,y=0},axis=\"x\"}","config":"return {offset={z=0,x=-8,y=0},axis=\"x\",radius=15,type=\"plane\"}"},"inventory":null},"(44, 24, 47)":{"fields":{"_config":"return {type=\"plane\",radius=15,offset={z=0,x=-7,y=0},axis=\"x\"}","config":"return {offset={z=0,x=-7,y=0},axis=\"x\",radius=15,type=\"plane\"}"},"inventory":null},"(44, 24, 52)":{"fields":{"locked":"true","open":"false"},"inventory":null},"(62, 24, 42)":{"fields":null,"inventory":null},"(62, 24, 43)":{"fields":{"inverted":"true"},"inventory":null},"(62, 34, 42)":{"fields":null,"inventory":null},"(62, 34, 43)":{"fields":{"inverted":"true"},"inventory":null}}

Binary file not shown.