aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeha <shacknetisp@mail.com>2017-03-07 12:00:22 -0500
committerBeha <shacknetisp@mail.com>2017-03-07 12:00:22 -0500
commit1aa6dfd3149c66e231e154c1072e44c08a291aef (patch)
tree7c584d0c3c6c8864459f0ff27de9433e1b0c6ff2
downloadelevator-1aa6dfd3149c66e231e154c1072e44c08a291aef.tar.gz
elevator-1aa6dfd3149c66e231e154c1072e44c08a291aef.tar.bz2
elevator-1aa6dfd3149c66e231e154c1072e44c08a291aef.zip
initial commit
-rw-r--r--depends.txt1
-rw-r--r--init.lua560
-rw-r--r--models/carts_cart.b3dbin0 -> 3008 bytes
-rw-r--r--models/elevator_shaft.blendbin0 -> 488904 bytes
-rw-r--r--models/elevator_shaft.obj194
-rw-r--r--models/travelnet_elevator.obj64
-rw-r--r--textures/elevator_box.pngbin0 -> 290 bytes
-rw-r--r--textures/elevator_cart.pngbin0 -> 694 bytes
-rw-r--r--textures/elevator_shaft.pngbin0 -> 267 bytes
-rw-r--r--textures/travelnet_elevator_door_glass.pngbin0 -> 952 bytes
-rw-r--r--textures/travelnet_elevator_front.pngbin0 -> 756 bytes
-rw-r--r--textures/travelnet_elevator_inside_ceiling.pngbin0 -> 292 bytes
-rw-r--r--textures/travelnet_elevator_inside_controls.pngbin0 -> 832 bytes
-rw-r--r--textures/travelnet_elevator_inside_floor.pngbin0 -> 102 bytes
-rw-r--r--textures/travelnet_elevator_inv.pngbin0 -> 4946 bytes
-rw-r--r--textures/travelnet_elevator_sides_outside.pngbin0 -> 347 bytes
16 files changed, 819 insertions, 0 deletions
diff --git a/depends.txt b/depends.txt
new file mode 100644
index 0000000..4ad96d5
--- /dev/null
+++ b/depends.txt
@@ -0,0 +1 @@
+default
diff --git a/init.lua b/init.lua
new file mode 100644
index 0000000..5c82e8e
--- /dev/null
+++ b/init.lua
@@ -0,0 +1,560 @@
+--GPLv3, derived from Travelnet by Sokomine
+local SPEED = 10
+local ACCEL = 1
+local VERSION = 5
+
+local elevator = {
+ motors = {},
+}
+local boxes = {}
+local formspecs = {}
+local elevator_file = minetest.get_worldpath() .. "/elevator"
+
+local function load_elevator()
+ local file = io.open(elevator_file)
+ if file then
+ elevator = minetest.deserialize(file:read("*all")) or {}
+ file:close()
+ end
+end
+
+local function save_elevator()
+ local f = io.open(elevator_file .. ".tmp", "w")
+ f:write(minetest.serialize(elevator))
+ f:close()
+ os.rename(elevator_file .. ".tmp", elevator_file)
+end
+
+load_elevator()
+
+local function phash(pos)
+ return minetest.pos_to_string(pos)
+end
+
+local function locate_motor(pos)
+ local p = vector.new(pos)
+ while true do
+ local node = technic.get_or_load_node(p) or technic.get_or_load_node(p)
+ if node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
+ p.y = p.y + 2
+ elseif node.name == "elevator:shaft" then
+ p.y = p.y + 1
+ elseif node.name == "elevator:motor" then
+ return phash(p)
+ else
+ return nil
+ end
+ end
+end
+
+local function build_motor(hash)
+ local need_saving = false
+ local motor = elevator.motors[hash]
+ if not motor then
+ return
+ end
+ local p = minetest.string_to_pos(hash)
+ local node = technic.get_or_load_node(p) or technic.get_or_load_node(p)
+ if node.name ~= "elevator:motor" then
+ return
+ end
+ p.y = p.y - 1
+ motor.elevators = {}
+ motor.pnames = {}
+ while true do
+ local node = technic.get_or_load_node(p) or technic.get_or_load_node(p)
+ if node.name == "elevator:shaft" then
+ p.y = p.y - 1
+ else
+ p.y = p.y - 1
+ local node = technic.get_or_load_node(p) or technic.get_or_load_node(p)
+ if node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
+ table.insert(motor.elevators, phash(p))
+ table.insert(motor.pnames, tostring(p.y))
+ p.y = p.y - 1
+ need_saving = true
+ else
+ break
+ end
+ end
+ end
+ for i,m in ipairs(motor.elevators) do
+ local tpnames = {}
+ local pos = minetest.string_to_pos(m)
+ local meta = minetest.get_meta(pos)
+ for ji,jv in ipairs(motor.pnames) do
+ if ji ~= i then
+ table.insert(tpnames, jv)
+ end
+ end
+ meta:set_string("elevator_formspec", "size[3,2]"
+ .."dropdown[0,0;3;target;"..table.concat(tpnames, ",")..";1]"
+ .."button_exit[0,1;3,1;go;Go]")
+ meta:set_int("version", VERSION)
+ if meta:get_string("motor") ~= hash then
+ build_motor(meta:get_string("motor"))
+ end
+ meta:set_string("motor", hash)
+ end
+ if need_saving then
+ save_elevator()
+ end
+end
+
+local function unbuild(pos, add)
+ local need_saving = false
+ local p = table.copy(pos)
+ p.y = p.y - 1
+ while true do
+ local node = technic.get_or_load_node(p) or technic.get_or_load_node(p)
+ if node.name == "elevator:shaft" then
+ p.y = p.y - 1
+ else
+ p.y = p.y - 1
+ local node = technic.get_or_load_node(p) or technic.get_or_load_node(p)
+ if node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
+ local meta = minetest.get_meta(p)
+ meta:set_string("motor", "")
+ p.y = p.y - 1
+ else
+ break
+ end
+ end
+ end
+ minetest.after(0.01, function(p2)
+ p2.y = p2.y + add
+ build_motor(locate_motor(p2))
+ end, table.copy(pos))
+end
+
+minetest.register_node("elevator:motor", {
+ description = "Elevator Motor",
+ tiles = { "technic_wrought_iron_block.png^homedecor_motor.png" },
+ groups = {cracky=1},
+ sounds = default.node_sound_stone_defaults(),
+ after_place_node = function(pos, placer, itemstack)
+ elevator.motors[phash(pos)] = {
+ elevators = {},
+ pnames = {},
+ }
+ save_elevator()
+ build_motor(phash(pos))
+ end,
+ on_destruct = function(pos)
+ if boxes[phash(pos)] then
+ boxes[phash(pos)]:remove()
+ boxes[phash(pos)] = nil
+ end
+ elevator.motors[phash(pos)] = nil
+ save_elevator()
+ end,
+})
+
+for _,mode in ipairs({"on", "off"}) do
+local nodename = "elevator:elevator_"..mode
+local on = (mode == "on")
+local box
+if on then
+ box = {
+
+ { 0.48, -0.5,-0.5, 0.5, 1.5, 0.5},
+ {-0.5 , -0.5, 0.48, 0.48, 1.5, 0.5},
+ {-0.5, -0.5,-0.5 ,-0.48, 1.5, 0.5},
+
+ --groundplate to stand on
+ { -0.5,-0.5,-0.5,0.5,-0.48, 0.5},
+ { -0.5, 1.45,-0.5,0.5, 1.5, 0.5},
+ }
+else
+ box = {
+
+ { 0.48, -0.5,-0.5, 0.5, 1.5, 0.5},
+ {-0.5 , -0.5, 0.48, 0.48, 1.5, 0.5},
+ {-0.5, -0.5,-0.5 ,-0.48, 1.5, 0.5},
+ {-0.5 , -0.5, -0.48, 0.5, 1.5, -0.5},
+ --{-0.5 , -0.5, -0.48, -0.4, 1.5, -0.5},
+ --{-0.2 , -0.5, -0.48, -0.1, 1.5, -0.5},
+ --{0.1 , -0.5, -0.48, 0.2, 1.5, -0.5},
+ --{0.4 , -0.5, -0.48, 0.5, 1.5, -0.5},
+
+ --groundplate to stand on
+ { -0.5,-0.5,-0.5,0.5,-0.48, 0.5},
+ { -0.5, 1.45,-0.5,0.5, 1.5, 0.5},
+ }
+end
+minetest.register_node(nodename, {
+ description = "Elevator",
+ drawtype = (on and "mesh" or "nodebox"),
+ mesh = "travelnet_elevator.obj",
+ --drawtype = "nodebox",
+ sunlight_propagates = false,
+ paramtype = 'light',
+ paramtype2 = "facedir",
+ --wield_scale = {x=0.6, y=0.6, z=0.6},
+
+ selection_box = {
+ type = "fixed",
+ fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 }
+ },
+
+ collision_box = {
+ type = "fixed",
+ fixed = box,
+ },
+
+ node_box = {
+ type = "fixed",
+ fixed = box,
+ },
+
+ tiles = on and {
+ "elevator_box.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ "default_steel_block.png",
+ "default_steel_block.png",
+ } or {
+ "elevator_box.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ },
+ --inventory_image = "travelnet_elevator_inv.png",
+ groups = {cracky=1, choppy=1, snappy=1},
+ drop = "elevator:elevator_off",
+
+ light_source = (on and 4 or nil),
+
+ after_place_node = function(pos, placer, itemstack)
+ local meta = minetest.get_meta(pos)
+ meta:set_int("version", VERSION)
+ local p = {x=pos.x, y=pos.y+1, z=pos.z}
+ local p2 = minetest.dir_to_facedir(placer:get_look_dir())
+ minetest.set_node(p, {name="homedecor:expansion_placeholder", paramtype2="facedir", param2=p2})
+ local motor = locate_motor(pos)
+ if motor then
+ build_motor(motor)
+ end
+ end,
+
+ after_dig_node = function(pos, node, meta, digger)
+ unbuild(pos, 2)
+ end,
+
+ on_place = function(itemstack, placer, pointed_thing)
+ local pos = pointed_thing.above
+ local node = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z})
+ if( node ~= nil and node.name ~= "air" and node.name ~= 'homedecor:expansion_placeholder') then
+ return
+ end
+ return minetest.item_place(itemstack, placer, pointed_thing);
+ end,
+
+ on_rightclick = function(pos, node, sender)
+ local meta = minetest.get_meta(pos)
+ if on then
+ if vector.distance(sender:get_pos(), pos) > 1 or minetest.get_node(sender:get_pos()).name ~= nodename then
+ minetest.chat_send_player(sender:get_player_name(), "You are not inside the booth.")
+ return
+ end
+ formspecs[sender:get_player_name()] = pos
+ minetest.show_formspec(sender:get_player_name(), "elevator:elevator", meta:get_string("elevator_formspec"))
+ elseif not elevator.motors[meta:get_string("motor")] then
+ minetest.chat_send_player(sender:get_player_name(), "This elevator is inactive.")
+ elseif boxes[meta:get_string("motor")] then
+ minetest.chat_send_player(sender:get_player_name(), "This elevator is in use.")
+ end
+ end,
+
+ on_destruct = function(pos)
+ local p = {x=pos.x, y=pos.y+1, z=pos.z}
+ minetest.remove_node(p)
+ end,
+})
+end
+
+minetest.register_on_player_receive_fields(function(sender, formname, fields)
+ if formname ~= "elevator:elevator" then
+ return
+ end
+ local pos = formspecs[sender:get_player_name()]
+ if not pos then
+ return
+ end
+ if fields.go then
+ local meta = minetest.get_meta(pos)
+ local motorhash = meta:get_string("motor")
+ local motor = elevator.motors[motorhash]
+ if not motor then
+ motorhash = locate_motor(pos)
+ motor = elevator.motors[motorhash]
+ if motor then
+ meta:set_string("motor", "")
+ build_motor(motorhash)
+ minetest.chat_send_player(sender:get_player_name(), "Recalibrated to a new motor, please try again.")
+ return true
+ end
+ end
+ if not motor then
+ minetest.chat_send_player(sender:get_player_name(), "This elevator is not attached to a motor.")
+ return true
+ end
+ local target = nil
+ for i,v in ipairs(motor.pnames) do
+ if v == fields.target then
+ target = minetest.string_to_pos(motor.elevators[i])
+ end
+ end
+ if target then
+ if boxes[motorhash] then
+ minetest.chat_send_player(sender:get_player_name(), "This elevator is in use.")
+ return true
+ end
+ local obj = minetest.add_entity(pos, "elevator:box")
+ obj:set_pos(pos)
+ sender:set_pos(pos)
+ sender:set_attach(obj, "", {x=0, y=0, z=0}, {x=0, y=0, z=0})
+ sender:set_eye_offset({x=0, y=-9, z=0},{x=0, y=-9, z=0})
+ obj:get_luaentity().motor = motorhash
+ obj:get_luaentity().attached = sender:get_player_name()
+ obj:get_luaentity().start = pos
+ obj:get_luaentity().target = target
+ obj:get_luaentity().halfway = {x=pos.x, y=(pos.y+target.y)/2, z=pos.z}
+ obj:get_luaentity().vmult = (target.y < pos.y) and -1 or 1
+ obj:setvelocity({x=0, y=SPEED*obj:get_luaentity().vmult, z=0})
+ obj:setacceleration({x=0, y=ACCEL*obj:get_luaentity().vmult, z=0})
+ boxes[motorhash] = obj
+ else
+ minetest.chat_send_player(sender:get_player_name(), "This target is invalid.")
+ return true
+ end
+ return true
+ end
+ return true
+end)
+
+minetest.register_alias("elevator:elevator", "elevator:elevator_off")
+
+local offabm = function(pos, node)
+ local meta = minetest.get_meta(pos)
+ if meta:get_int("version") ~= VERSION then
+ minetest.log("action", "[elevator] Updating elevator with old version at "..minetest.pos_to_string(pos))
+ minetest.after(0, function(pos) build_motor(locate_motor(pos)) end, pos)
+ meta:set_int("version", VERSION)
+ meta:set_string("formspec", "")
+ end
+ if not boxes[meta:get_string("motor")] and elevator.motors[meta:get_string("motor")] then
+ node.name = "elevator:elevator_on"
+ minetest.swap_node(pos, node)
+ end
+end
+
+minetest.register_abm({
+ nodenames = {"elevator:elevator_off"},
+ interval = 1,
+ chance = 1,
+ action = offabm,
+})
+
+minetest.register_abm({
+ nodenames = {"elevator:elevator_on"},
+ interval = 1,
+ chance = 1,
+ action = function(pos, node)
+ local meta = minetest.get_meta(pos)
+ if meta:get_int("version") ~= VERSION then
+ minetest.log("action", "[elevator] Updating elevator with old version at "..minetest.pos_to_string(pos))
+ minetest.after(0, function(pos) build_motor(locate_motor(pos)) end, pos)
+ meta:set_int("version", VERSION)
+ meta:set_string("formspec", "")
+ end
+ if boxes[meta:get_string("motor")] or not elevator.motors[meta:get_string("motor")] then
+ node.name = "elevator:elevator_off"
+ minetest.swap_node(pos, node)
+ end
+ end,
+})
+
+minetest.register_node("elevator:shaft", {
+ description = "Elevator Shaft",
+ tiles = { "elevator_shaft.png" },
+ drawtype = "nodebox",
+ paramtype = "light",
+ sunlight_propagates = true,
+ groups = {cracky=2, oddly_breakable_by_hand=1},
+ sounds = default.node_sound_stone_defaults(),
+ node_box = {
+ type = "fixed",
+ fixed = {
+ {-8/16,-8/16,-8/16,-7/16,8/16,8/16},
+ {7/16,-8/16,-8/16,8/16,8/16,8/16},
+ {-7/16,-8/16,-8/16,7/16,8/16,-7/16},
+ {-7/16,-8/16,8/16,7/16,8/16,7/16},
+ },
+ },
+ collisionbox = {
+ type = "fixed",
+ fixed = {
+ {-8/16,-8/16,-8/16,-7/16,8/16,8/16},
+ {7/16,-8/16,-8/16,8/16,8/16,8/16},
+ {-7/16,-8/16,-8/16,7/16,8/16,-7/16},
+ {-7/16,-8/16,8/16,7/16,8/16,7/16},
+ },
+ },
+ after_place_node = function(pos)
+ build_motor(locate_motor(pos))
+ end,
+ on_destruct = function(pos)
+ unbuild(pos, 1)
+ end,
+})
+
+local box = {
+ { 0.48, -0.5,-0.5, 0.5, 1.5, 0.5},
+ {-0.5 , -0.5, 0.48, 0.48, 1.5, 0.5},
+ {-0.5, -0.5,-0.5 ,-0.48, 1.5, 0.5},
+ {-0.5 , -0.5, -0.48, 0.5, 1.5, -0.5},
+ --{-0.5 , -0.5, -0.48, -0.4, 1.5, -0.5},
+ --{-0.2 , -0.5, -0.48, -0.1, 1.5, -0.5},
+ --{0.1 , -0.5, -0.48, 0.2, 1.5, -0.5},
+ --{0.4 , -0.5, -0.48, 0.5, 1.5, -0.5},
+
+ --groundplate to stand on
+ { -0.5,-0.5,-0.5,0.5,-0.48, 0.5},
+ { -0.5, 1.45,-0.5,0.5, 1.5, 0.5},
+}
+
+minetest.register_node("elevator:elevator_box", {
+ description = "Elevator",
+ drawtype = ("nodebox"),
+ paramtype = 'light',
+ paramtype2 = "facedir",
+ wield_scale = {x=0.6, y=0.6, z=0.6},
+
+ selection_box = {
+ type = "fixed",
+ fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 }
+ },
+
+ collision_box = {
+ type = "fixed",
+ fixed = box,
+ },
+
+ node_box = {
+ type = "fixed",
+ fixed = box,
+ },
+
+ tiles = {
+ "default_steel_block.png",
+ "default_steel_block.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ "elevator_box.png",
+ },
+ groups = {not_in_creative_inventory = 1},
+
+ light_source = 4,
+})
+
+local box_entity = {
+ physical = false,
+ --collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
+ collisionbox = {0,0,0,0,0,0},
+ visual = "wielditem",
+ mesh = "carts_cart.b3d",
+ visual_size = {x=1, y=1},
+ textures = {"elevator:elevator_box"},
+
+ attached = "",
+ motor = false,
+ target = false,
+
+ start = false,
+ lastpos = false,
+ halfway = false,
+ vmult = 0,
+
+ on_activate = function(self)
+ self.object:set_armor_groups({immortal=1})
+ end,
+
+ on_step = function(self, dtime)
+ if not minetest.get_player_by_name(self.attached) then
+ self.object:remove()
+ boxes[self.motor] = nil
+ return
+ end
+ local pos = self.object:getpos()
+ self.lastpos = self.lastpos or pos
+ for y=self.lastpos.y,pos.y,((self.lastpos.y > pos.y) and -1 or 1) do
+ local p = vector.round({x=pos.x, y=y, z=pos.z})
+ --local above = vector.add(p, {x=0,y=1,z=0})
+ local below = vector.add(p, {x=0,y=-1,z=0})
+ local node = technic.get_or_load_node(p) or technic.get_or_load_node(p)
+ if node.name == "elevator:shaft" then
+ -- Nothing
+ elseif node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
+ if vector.distance(p, self.target) < 1 then
+ self.object:remove()
+ minetest.get_player_by_name(self.attached):set_detach()
+ minetest.get_player_by_name(self.attached):set_pos(vector.add(self.target, {x=0, y=-0.4, z=0}))
+ minetest.get_player_by_name(self.attached):set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0})
+ boxes[self.motor] = nil
+ offabm(self.target, node)
+ return
+ end
+ else
+ --local abovenode = technic.get_or_load_node(above) or technic.get_or_load_node(above)
+ local belownode = technic.get_or_load_node(below) or technic.get_or_load_node(below)
+ if belownode.name ~= "elevator:elevator_on" and belownode.name ~= "elevator:elevator_off" then
+ boxes[self.motor] = nil
+ self.object:remove()
+ minetest.get_player_by_name(self.attached):set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0})
+ return
+ end
+ end
+ end
+ self.lastpos = pos
+ if vector.distance(pos, self.target) < 32 and self.object:getvelocity().y > SPEED/2 then
+ self.object:setacceleration({x=0, y=((SPEED)*self.vmult*-1), z=0})
+ elseif self.object:getvelocity().y > SPEED and vector.distance(pos, self.target) < math.min(100, vector.distance(pos, self.start)) then
+ self.object:setacceleration({x=0, y=((SPEED/10)*self.vmult*-1), z=0})
+ elseif self.object:getvelocity().y <= SPEED/1.5 then
+ self.object:setacceleration({x=0, y=((SPEED/10)*self.vmult), z=0})
+ end
+ end,
+}
+
+minetest.register_entity("elevator:box", box_entity)
+
+minetest.register_craft({
+ output = "elevator:elevator",
+ recipe = {
+ {"technic:cast_iron_ingot", "chains:chain", "technic:cast_iron_ingot"},
+ {"technic:cast_iron_ingot", "default:mese_crystal", "technic:cast_iron_ingot"},
+ {"technic:stainless_steel_ingot", "default:glass", "technic:stainless_steel_ingot"},
+ },
+})
+
+minetest.register_craft({
+ output = "elevator:shaft",
+ recipe = {
+ {"technic:cast_iron_ingot", "default:glass"},
+ {"default:glass", "homedecor:chainlink_steel"},
+ },
+})
+
+minetest.register_craft({
+ output = "elevator:motor",
+ recipe = {
+ {"default:diamond", "technic:control_logic_unit", "default:diamond"},
+ {"default:steelblock", "technic:motor", "default:steelblock"},
+ {"chains:chain", "default:diamond", "chains:chain"}
+ },
+})
diff --git a/models/carts_cart.b3d b/models/carts_cart.b3d
new file mode 100644
index 0000000..4e7eba3
--- /dev/null
+++ b/models/carts_cart.b3d
Binary files differ
diff --git a/models/elevator_shaft.blend b/models/elevator_shaft.blend
new file mode 100644
index 0000000..55a82f0
--- /dev/null
+++ b/models/elevator_shaft.blend
Binary files differ
diff --git a/models/elevator_shaft.obj b/models/elevator_shaft.obj
new file mode 100644
index 0000000..0d7c6a1
--- /dev/null
+++ b/models/elevator_shaft.obj
@@ -0,0 +1,194 @@
+# Blender v2.76 (sub 0) OBJ File: 'elevator_shaft.blend'
+# www.blender.org
+o Cube.011_Cube.002
+v -0.220327 -0.438894 0.489317
+v -0.220327 0.511106 0.489317
+v -0.362827 -0.438894 0.489317
+v -0.362827 0.511106 0.489317
+v -0.220327 -0.438894 0.441817
+v -0.220327 0.511106 0.441817
+v -0.362827 -0.438894 0.441817
+v -0.362827 0.511106 0.441817
+s off
+f 2 4 3 1
+f 4 8 7 3
+f 8 6 5 7
+f 6 2 1 5
+f 1 3 7 5
+f 6 8 4 2
+o Cube.010_Cube.002
+v 0.349673 -0.438894 0.489317
+v 0.349673 0.511106 0.489317
+v 0.207173 -0.438894 0.489317
+v 0.207173 0.511106 0.489317
+v 0.349673 -0.438894 0.441817
+v 0.349673 0.511106 0.441817
+v 0.207173 -0.438894 0.441817
+v 0.207173 0.511106 0.441817
+s off
+f 10 12 11 9
+f 12 16 15 11
+f 16 14 13 15
+f 14 10 9 13
+f 9 11 15 13
+f 14 16 12 10
+o Cube.009_Cube.002
+v 0.349673 -0.438894 -0.460683
+v 0.349673 0.511106 -0.460683
+v 0.207173 -0.438894 -0.460683
+v 0.207173 0.511106 -0.460683
+v 0.349673 -0.438894 -0.508183
+v 0.349673 0.511106 -0.508183
+v 0.207173 -0.438894 -0.508183
+v 0.207173 0.511106 -0.508183
+s off
+f 18 20 19 17
+f 20 24 23 19
+f 24 22 21 23
+f 22 18 17 21
+f 17 19 23 21
+f 22 24 20 18
+o Cube.008_Cube.002
+v -0.220327 -0.438894 -0.460683
+v -0.220327 0.511106 -0.460683
+v -0.362827 -0.438894 -0.460683
+v -0.362827 0.511106 -0.460683
+v -0.220327 -0.438894 -0.508183
+v -0.220327 0.511106 -0.508183
+v -0.362827 -0.438894 -0.508183
+v -0.362827 0.511106 -0.508183
+s off
+f 26 28 27 25
+f 28 32 31 27
+f 32 30 29 31
+f 30 26 25 29
+f 25 27 31 29
+f 30 32 28 26
+o Cube.007_Cube.002
+v 0.444673 -0.438894 0.346817
+v 0.444673 0.511106 0.346817
+v 0.444673 -0.438894 0.204317
+v 0.444673 0.511106 0.204317
+v 0.492173 -0.438894 0.346817
+v 0.492173 0.511106 0.346817
+v 0.492173 -0.438894 0.204317
+v 0.492173 0.511106 0.204317
+s off
+f 34 36 35 33
+f 36 40 39 35
+f 40 38 37 39
+f 38 34 33 37
+f 33 35 39 37
+f 38 40 36 34
+o Cube.006_Cube.002
+v 0.444673 -0.438894 -0.223183
+v 0.444673 0.511106 -0.223183
+v 0.444673 -0.438894 -0.365683
+v 0.444673 0.511106 -0.365683
+v 0.492173 -0.438894 -0.223183
+v 0.492173 0.511106 -0.223183
+v 0.492173 -0.438894 -0.365683
+v 0.492173 0.511106 -0.365683
+s off
+f 42 44 43 41
+f 44 48 47 43
+f 48 46 45 47
+f 46 42 41 45
+f 41 43 47 45
+f 46 48 44 42
+o Cube.005_Cube.002
+v -0.505327 -0.438894 0.346817
+v -0.505327 0.511106 0.346817
+v -0.505327 -0.438894 0.204317
+v -0.505327 0.511106 0.204317
+v -0.457827 -0.438894 0.346817
+v -0.457827 0.511106 0.346817
+v -0.457827 -0.438894 0.204317
+v -0.457827 0.511106 0.204317
+s off
+f 50 52 51 49
+f 52 56 55 51
+f 56 54 53 55
+f 54 50 49 53
+f 49 51 55 53
+f 54 56 52 50
+o Cube.004_Cube.002
+v -0.505327 -0.438894 -0.223183
+v -0.505327 0.511106 -0.223183
+v -0.505327 -0.438894 -0.365683
+v -0.505327 0.511106 -0.365683
+v -0.457827 -0.438894 -0.223183
+v -0.457827 0.511106 -0.223183
+v -0.457827 -0.438894 -0.365683
+v -0.457827 0.511106 -0.365683
+s off
+f 58 60 59 57
+f 60 64 63 59
+f 64 62 61 63
+f 62 58 57 61
+f 57 59 63 61
+f 62 64 60 58
+o Cube.003_Cube.002
+v 0.064673 -0.438894 -0.460683
+v 0.064673 0.511106 -0.460683
+v -0.077827 -0.438894 -0.460683
+v -0.077827 0.511106 -0.460683
+v 0.064673 -0.438894 -0.508183
+v 0.064673 0.511106 -0.508183
+v -0.077827 -0.438894 -0.508183
+v -0.077827 0.511106 -0.508183
+s off
+f 66 68 67 65
+f 68 72 71 67
+f 72 70 69 71
+f 70 66 65 69
+f 65 67 71 69
+f 70 72 68 66
+o Cube.002
+v 0.064673 -0.438894 0.489317
+v 0.064673 0.511106 0.489317
+v -0.077827 -0.438894 0.489317
+v -0.077827 0.511106 0.489317
+v 0.064673 -0.438894 0.441817
+v 0.064673 0.511106 0.441817
+v -0.077827 -0.438894 0.441817
+v -0.077827 0.511106 0.441817
+s off
+f 74 76 75 73
+f 76 80 79 75
+f 80 78 77 79
+f 78 74 73 77
+f 73 75 79 77
+f 78 80 76 74
+o Cube.001_Cube.002
+v -0.505327 -0.438894 0.061817
+v -0.505327 0.511106 0.061817
+v -0.505327 -0.438894 -0.080683
+v -0.505327 0.511106 -0.080683
+v -0.457827 -0.438894 0.061817
+v -0.457827 0.511106 0.061817
+v -0.457827 -0.438894 -0.080683
+v -0.457827 0.511106 -0.080683
+s off
+f 82 84 83 81
+f 84 88 87 83
+f 88 86 85 87
+f 86 82 81 85
+f 81 83 87 85
+f 86 88 84 82
+o Cube_Cube.002
+v 0.444673 -0.438894 0.061817
+v 0.444673 0.511106 0.061817
+v 0.444673 -0.438894 -0.080683
+v 0.444673 0.511106 -0.080683
+v 0.492173 -0.438894 0.061817
+v 0.492173 0.511106 0.061817
+v 0.492173 -0.438894 -0.080683
+v 0.492173 0.511106 -0.080683
+s off
+f 90 92 91 89
+f 92 96 95 91
+f 96 94 93 95
+f 94 90 89 93
+f 89 91 95 93
+f 94 96 92 90
diff --git a/models/travelnet_elevator.obj b/models/travelnet_elevator.obj
new file mode 100644
index 0000000..cc006e2
--- /dev/null
+++ b/models/travelnet_elevator.obj
@@ -0,0 +1,64 @@
+# Blender v2.73 (sub 0) OBJ File: 'travelnet_elevator.blend'
+# www.blender.org
+o Cylinder
+v -0.499016 -0.499034 0.499022
+v -0.499016 -0.499034 -0.498989
+v 0.499035 -0.499034 -0.498989
+v 0.499035 -0.499034 0.499022
+v -0.499016 1.498990 0.499022
+v -0.499016 1.498990 -0.498989
+v 0.499035 1.498990 -0.498989
+v 0.499035 1.498990 0.499022
+v 0.437500 -0.437500 0.437500
+v -0.499016 1.437500 -0.498989
+v 0.499035 1.437500 -0.498989
+v -0.437500 -0.437500 0.437500
+v 0.437500 1.437500 0.437500
+v -0.499016 -0.437500 -0.498989
+v 0.499035 -0.437500 -0.498989
+v -0.437500 1.437500 0.437500
+v -0.437500 -0.437500 -0.498989
+v 0.437500 -0.437500 -0.498989
+v -0.437500 1.437500 -0.498989
+v 0.437500 1.437500 -0.498989
+vt 0.000000 0.968750
+vt 1.000000 0.968750
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vt 0.062500 0.031250
+vt 0.062500 0.968750
+vt 0.000000 0.031250
+vt 0.937500 0.031250
+vt 1.000000 0.031250
+vt 0.937500 0.968750
+vt 1.000000 -0.000000
+vt 0.000000 -0.000000
+vt 0.062500 -0.000000
+vt 0.937500 -0.000000
+vt 0.937500 0.937500
+vt 0.062500 0.937500
+vt 0.062500 1.000000
+vt 0.062500 0.062500
+vt 0.937500 0.062500
+vt 0.937500 1.000000
+g Cylinder_Cylinder_front
+s off
+f 11/1 10/2 6/3 7/4
+f 18/5 20/6 11/1 15/7
+f 17/8 14/9 10/2 19/10
+f 13/6 9/5 12/8 16/10
+f 2/11 14/9 15/7 3/12
+f 17/7 19/1 16/10 12/8
+g Cylinder_Cylinder_controls
+f 18/7 9/8 13/10 20/1
+g Cylinder_Cylinder_outside
+f 8/3 4/11 3/12 7/4
+f 6/4 2/12 1/11 5/3
+f 8/4 5/3 1/11 4/12
+g Cylinder_Cylinder_ceiling
+f 19/13 20/14 13/15 16/16
+g Cylinder_Cylinder_floor
+f 17/17 12/18 9/19 18/20
+g Cylinder_Cylinder_top-bottom
+f 5/12 8/11 7/3 6/4
+f 2/12 3/11 4/3 1/4
diff --git a/textures/elevator_box.png b/textures/elevator_box.png
new file mode 100644
index 0000000..714adf0
--- /dev/null
+++ b/textures/elevator_box.png
Binary files differ
diff --git a/textures/elevator_cart.png b/textures/elevator_cart.png
new file mode 100644
index 0000000..f10fea2
--- /dev/null
+++ b/textures/elevator_cart.png
Binary files differ
diff --git a/textures/elevator_shaft.png b/textures/elevator_shaft.png
new file mode 100644
index 0000000..ce9c097
--- /dev/null
+++ b/textures/elevator_shaft.png
Binary files differ
diff --git a/textures/travelnet_elevator_door_glass.png b/textures/travelnet_elevator_door_glass.png
new file mode 100644
index 0000000..ae775c4
--- /dev/null
+++ b/textures/travelnet_elevator_door_glass.png
Binary files differ
diff --git a/textures/travelnet_elevator_front.png b/textures/travelnet_elevator_front.png
new file mode 100644
index 0000000..60ec49e
--- /dev/null
+++ b/textures/travelnet_elevator_front.png
Binary files differ
diff --git a/textures/travelnet_elevator_inside_ceiling.png b/textures/travelnet_elevator_inside_ceiling.png
new file mode 100644
index 0000000..f35dd07
--- /dev/null
+++ b/textures/travelnet_elevator_inside_ceiling.png
Binary files differ
diff --git a/textures/travelnet_elevator_inside_controls.png b/textures/travelnet_elevator_inside_controls.png
new file mode 100644
index 0000000..725c39b
--- /dev/null
+++ b/textures/travelnet_elevator_inside_controls.png
Binary files differ
diff --git a/textures/travelnet_elevator_inside_floor.png b/textures/travelnet_elevator_inside_floor.png
new file mode 100644
index 0000000..7874bac
--- /dev/null
+++ b/textures/travelnet_elevator_inside_floor.png
Binary files differ
diff --git a/textures/travelnet_elevator_inv.png b/textures/travelnet_elevator_inv.png
new file mode 100644
index 0000000..a390d75
--- /dev/null
+++ b/textures/travelnet_elevator_inv.png
Binary files differ
diff --git a/textures/travelnet_elevator_sides_outside.png b/textures/travelnet_elevator_sides_outside.png
new file mode 100644
index 0000000..82c0a03
--- /dev/null
+++ b/textures/travelnet_elevator_sides_outside.png
Binary files differ