Add bits that are done so far
This includes: * Controller (runs and responds to calls placed on the screen, parameter editing and switches work) * Null Drive (simulates motion so the controller can run, no actual movement yet) * Call Buttons (lights can be toggled with right-click, no communication yet)
12
.luacheckrc
Normal file
@ -0,0 +1,12 @@
|
||||
max_line_length = 200
|
||||
|
||||
globals = {
|
||||
"celevator",
|
||||
}
|
||||
|
||||
read_globals = {
|
||||
"DIR_DELIM",
|
||||
"vector",
|
||||
"screwdriver",
|
||||
"minetest",
|
||||
}
|
22
COPYING
Normal file
@ -0,0 +1,22 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
152
callbuttons.lua
Normal file
@ -0,0 +1,152 @@
|
||||
local function makebuttontex(dir,upon,downon)
|
||||
local tex = "[combine:64x64:0,0=celevator_cabinet_sides.png:32,0=celevator_cabinet_sides.png:0,32=celevator_cabinet_sides.png:32,32=celevator_cabinet_sides.png:22,24=celevator_callbutton_panel.png"
|
||||
if dir == "up" then
|
||||
tex = tex..":24,35=celevator_callbutton_up.png"
|
||||
if upon then
|
||||
tex = tex..":33,36=celevator_callbutton_light.png"
|
||||
end
|
||||
elseif dir == "down" then
|
||||
tex = tex..":24,35=celevator_callbutton_down.png"
|
||||
if downon then
|
||||
tex = tex..":33,36=celevator_callbutton_light.png"
|
||||
end
|
||||
elseif dir == "both" then
|
||||
tex = tex..":24,28=celevator_callbutton_up.png:24,43=celevator_callbutton_down.png"
|
||||
if upon then
|
||||
tex = tex..":33,29=celevator_callbutton_light.png"
|
||||
end
|
||||
if downon then
|
||||
tex = tex..":33,44=celevator_callbutton_light.png"
|
||||
end
|
||||
end
|
||||
return(tex)
|
||||
end
|
||||
|
||||
local validstates = {
|
||||
{"up",false,false,"Up"},
|
||||
{"up",true,false,"Up"},
|
||||
{"down",false,false,"Down"},
|
||||
{"down",false,true,"Down"},
|
||||
{"both",false,false,"Up and Down"},
|
||||
{"both",true,false,"Up and Down"},
|
||||
{"both",false,true,"Up and Down"},
|
||||
{"both",true,true,"Up and Down"},
|
||||
}
|
||||
|
||||
local function setlight(pos,dir,newstate)
|
||||
local node = minetest.get_node(pos)
|
||||
if minetest.get_item_group(node.name,"_celevator_callbutton") ~= 1 then return end
|
||||
if dir == "up" then
|
||||
if minetest.get_item_group(node.name,"_celevator_callbutton_has_up") ~= 1 then return end
|
||||
local lit = minetest.get_item_group(node.name,"_celevator_callbutton_up_lit") == 1
|
||||
if lit == newstate then return end
|
||||
local newname = "celevator:callbutton_"
|
||||
if minetest.get_item_group(node.name,"_celevator_callbutton_has_down") == 1 then
|
||||
newname = newname.."both"
|
||||
else
|
||||
newname = newname.."up"
|
||||
end
|
||||
if newstate then newname = newname.."_upon" end
|
||||
if minetest.get_item_group(node.name,"_celevator_callbutton_down_lit") == 1 then
|
||||
newname = newname.."_downon"
|
||||
end
|
||||
node.name = newname
|
||||
minetest.swap_node(pos,node)
|
||||
elseif dir == "down" then
|
||||
if minetest.get_item_group(node.name,"_celevator_callbutton_has_down") ~= 1 then return end
|
||||
local lit = minetest.get_item_group(node.name,"_celevator_callbutton_down_lit") == 1
|
||||
if lit == newstate then return end
|
||||
local newname = "celevator:callbutton_"
|
||||
if minetest.get_item_group(node.name,"_celevator_callbutton_has_up") == 1 then
|
||||
newname = newname.."both"
|
||||
else
|
||||
newname = newname.."down"
|
||||
end
|
||||
if minetest.get_item_group(node.name,"_celevator_callbutton_up_lit") == 1 then
|
||||
newname = newname.."_upon"
|
||||
end
|
||||
if newstate then newname = newname.."_downon" end
|
||||
node.name = newname
|
||||
minetest.swap_node(pos,node)
|
||||
end
|
||||
end
|
||||
|
||||
local function disambiguatedir(pos,player)
|
||||
if player and not player.is_fake_player then
|
||||
local eyepos = vector.add(player:get_pos(),vector.add(player:get_eye_offset(),vector.new(0,1.5,0)))
|
||||
local lookdir = player:get_look_dir()
|
||||
local distance = vector.distance(eyepos,pos)
|
||||
local endpos = vector.add(eyepos,vector.multiply(lookdir,distance+1))
|
||||
local ray = minetest.raycast(eyepos,endpos,true,false)
|
||||
local pointed,button,hitpos
|
||||
repeat
|
||||
pointed = ray:next()
|
||||
if pointed and pointed.type == "node" then
|
||||
local node = minetest.get_node(pointed.under)
|
||||
if node.name and (minetest.get_item_group(node.name,"_celevator_callbutton") == 1) then
|
||||
button = pointed.under
|
||||
hitpos = vector.subtract(pointed.intersection_point,button)
|
||||
end
|
||||
end
|
||||
until button or not pointed
|
||||
if not hitpos then return end
|
||||
hitpos.y = -1*hitpos.y
|
||||
hitpos.y = math.floor((hitpos.y+0.5)*64+0.5)+1
|
||||
return hitpos.y >= 40 and "down" or "up"
|
||||
end
|
||||
end
|
||||
|
||||
for _,state in ipairs(validstates) do
|
||||
local boringside = "[combine:64x64:0,0=celevator_cabinet_sides.png:32,0=celevator_cabinet_sides.png:0,32=celevator_cabinet_sides.png:32,32=celevator_cabinet_sides.png"
|
||||
local nname = "celevator:callbutton_"..state[1]
|
||||
local dropname = nname
|
||||
if state[2] then nname = nname.."_upon" end
|
||||
if state[3] then nname = nname.."_downon" end
|
||||
local idle = not (state[2] or state[3])
|
||||
local description = string.format("%s Call Button%s",state[4],(idle and "" or " (on state, you hacker you!)"))
|
||||
minetest.register_node(nname,{
|
||||
description = description,
|
||||
groups = {
|
||||
dig_immediate = 2,
|
||||
not_in_creative_inventory = (idle and 0 or 1),
|
||||
_celevator_callbutton = 1,
|
||||
_celevator_callbutton_has_up = (state[1] == "down" and 0 or 1),
|
||||
_celevator_callbutton_has_down = (state[1] == "up" and 0 or 1),
|
||||
_celevator_callbutton_up_lit = (state[2] and 1 or 0),
|
||||
_celevator_callbutton_down_lit = (state[3] and 1 or 0),
|
||||
},
|
||||
drop = dropname,
|
||||
tiles = {
|
||||
boringside,
|
||||
boringside,
|
||||
boringside,
|
||||
boringside,
|
||||
boringside,
|
||||
makebuttontex(state[1],state[2],state[3])
|
||||
},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5 },
|
||||
{-0.16, -0.37,-0.59, 0.17, 0.13,-0.5 },
|
||||
},
|
||||
},
|
||||
on_rightclick = function(pos,_,clicker)
|
||||
if state[1] == "up" then
|
||||
setlight(pos,"up",not state[2])
|
||||
elseif state[1] == "down" then
|
||||
setlight(pos,"down",not state[3])
|
||||
elseif state[1] == "both" then
|
||||
local dir = disambiguatedir(pos,clicker)
|
||||
if dir == "up" then
|
||||
setlight(pos,"up",not state[2])
|
||||
elseif dir == "down" then
|
||||
setlight(pos,"down",not state[3])
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
492
controller.lua
Normal file
@ -0,0 +1,492 @@
|
||||
celevator.controller = {}
|
||||
|
||||
celevator.controller.iqueue = minetest.deserialize(celevator.storage:get_string("controller_iqueue")) or {}
|
||||
|
||||
celevator.controller.equeue = minetest.deserialize(celevator.storage:get_string("controller_equeue")) or {}
|
||||
|
||||
celevator.controller.running = {}
|
||||
|
||||
local fw,err = loadfile(minetest.get_modpath("celevator")..DIR_DELIM.."controllerfw.lua")
|
||||
if not fw then error(err) end
|
||||
|
||||
minetest.register_chatcommand("celevator_reloadcontroller",{
|
||||
params = "",
|
||||
description = "Reload celevator controller firmware from disk",
|
||||
privs = {server = true},
|
||||
func = function()
|
||||
local newfw,loaderr = loadfile(minetest.get_modpath("celevator")..DIR_DELIM.."controllerfw.lua")
|
||||
if newfw then
|
||||
fw = newfw
|
||||
return true,"Firmware reloaded successfully"
|
||||
else
|
||||
return false,loaderr
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local function after_place(pos,placer)
|
||||
local node = minetest.get_node(pos)
|
||||
local toppos = {x=pos.x,y=pos.y + 1,z=pos.z}
|
||||
local topnode = minetest.get_node(toppos)
|
||||
local placername = placer:get_player_name()
|
||||
if topnode.name ~= "air" then
|
||||
if placer:is_player() then
|
||||
minetest.chat_send_player(placername,"Can't place cabinet - no room for the top half!")
|
||||
end
|
||||
minetest.set_node(pos,{name="air"})
|
||||
return true
|
||||
end
|
||||
if minetest.is_protected(toppos,placername) and not minetest.check_player_privs(placername,{protection_bypass=true}) then
|
||||
if placer:is_player() then
|
||||
minetest.chat_send_player(placername,"Can't place cabinet - top half is protected!")
|
||||
minetest.record_protection_violation(toppos,placername)
|
||||
end
|
||||
minetest.set_node(pos,{name="air"})
|
||||
return true
|
||||
end
|
||||
node.name = "celevator:controller_top"
|
||||
minetest.set_node(toppos,node)
|
||||
end
|
||||
|
||||
local function ondestruct(pos)
|
||||
pos.y = pos.y + 1
|
||||
local topnode = minetest.get_node(pos)
|
||||
local controllertops = {
|
||||
["celevator:controller_top"] = true,
|
||||
["celevator:controller_top_running"] = true,
|
||||
["celevator:controller_top_open"] = true,
|
||||
["celevator:controller_top_open_running"] = true,
|
||||
}
|
||||
if controllertops[topnode.name] then
|
||||
minetest.set_node(pos,{name="air"})
|
||||
end
|
||||
celevator.controller.equeue[minetest.hash_node_position(pos)] = nil
|
||||
celevator.storage:set_string("controller_equeue",minetest.serialize(celevator.controller.equeue))
|
||||
end
|
||||
|
||||
local function onrotate(controllerpos,node,user,mode,new_param2)
|
||||
if not minetest.global_exists("screwdriver") then
|
||||
return false
|
||||
end
|
||||
local ret = screwdriver.rotate_simple(controllerpos,node,user,mode,new_param2)
|
||||
minetest.after(0,function(pos)
|
||||
local newnode = minetest.get_node(pos)
|
||||
local param2 = newnode.param2
|
||||
pos.y = pos.y + 1
|
||||
local topnode = minetest.get_node(pos)
|
||||
topnode.param2 = param2
|
||||
minetest.set_node(pos,topnode)
|
||||
end,controllerpos)
|
||||
return ret
|
||||
end
|
||||
|
||||
local function handlefields(pos,_,fields,sender)
|
||||
local playername = sender and sender:get_player_name() or ""
|
||||
local event = {}
|
||||
event.type = "ui"
|
||||
event.fields = fields
|
||||
event.sender = playername
|
||||
celevator.controller.run(pos,event)
|
||||
end
|
||||
|
||||
local function controllerleds(pos,running)
|
||||
local toppos = vector.add(pos,vector.new(0,1,0))
|
||||
local node = minetest.get_node(toppos)
|
||||
local sparams = {
|
||||
pos = toppos,
|
||||
}
|
||||
if node.name == "celevator:controller_top_open" and running then
|
||||
node.name = "celevator:controller_top_open_running"
|
||||
minetest.swap_node(toppos,node)
|
||||
minetest.sound_play("celevator_controller_start",sparams,true)
|
||||
elseif node.name == "celevator:controller_top" and running then
|
||||
node.name = "celevator:controller_top_running"
|
||||
minetest.swap_node(toppos,node)
|
||||
minetest.sound_play("celevator_controller_start",sparams,true)
|
||||
elseif node.name == "celevator:controller_top_open_running" and not running then
|
||||
node.name = "celevator:controller_top_open"
|
||||
minetest.swap_node(toppos,node)
|
||||
minetest.sound_play("celevator_controller_stop",sparams,true)
|
||||
elseif node.name == "celevator:controller_top_running" and not running then
|
||||
node.name = "celevator:controller_top"
|
||||
minetest.swap_node(toppos,node)
|
||||
minetest.sound_play("celevator_controller_stop",sparams,true)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("celevator:controller",{
|
||||
description = "Controller",
|
||||
groups = {
|
||||
cracky = 1,
|
||||
},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5,-0.5,0,0.5,0.5,0.5},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5,-0.5,0,0.5,1.5,0.5},
|
||||
},
|
||||
},
|
||||
tiles = {
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_front_bottom.png",
|
||||
},
|
||||
after_place_node = after_place,
|
||||
on_destruct = ondestruct,
|
||||
on_rotate = onrotate,
|
||||
on_receive_fields = handlefields,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("mem",minetest.serialize({}))
|
||||
local event = {}
|
||||
event.type = "program"
|
||||
celevator.controller.run(pos,event)
|
||||
end,
|
||||
on_punch = function(pos,node,puncher)
|
||||
if not puncher:is_player() then
|
||||
return
|
||||
end
|
||||
local name = puncher:get_player_name()
|
||||
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
|
||||
minetest.chat_send_player(name,"Can't open cabinet - cabinet is locked.")
|
||||
minetest.record_protection_violation(pos,name)
|
||||
return
|
||||
end
|
||||
node.name = "celevator:controller_open"
|
||||
minetest.swap_node(pos,node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec",meta:get_string("formspec_hidden"))
|
||||
pos.y = pos.y + 1
|
||||
node = minetest.get_node(pos)
|
||||
if node.name == "celevator:controller_top_running" then
|
||||
node.name = "celevator:controller_top_open_running"
|
||||
else
|
||||
node.name = "celevator:controller_top_open"
|
||||
end
|
||||
minetest.swap_node(pos,node)
|
||||
minetest.sound_play("doors_steel_door_open",{
|
||||
pos = pos,
|
||||
gain = 0.5,
|
||||
max_hear_distance = 10
|
||||
},true)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("celevator:controller_open",{
|
||||
description = "Controller (door open - you hacker you!)",
|
||||
groups = {
|
||||
cracky = 1,
|
||||
not_in_creative_inventory = 1,
|
||||
},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
drop = "celevator:controller",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5,-0.5,0,0.5,0.5,0.5},
|
||||
{-0.5,-0.5,-0.5,-0.45,0.5,0},
|
||||
{0.45,-0.5,-0.5,0.5,0.5,0},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5,-0.5,-0.5,0.5,1.5,0.5},
|
||||
},
|
||||
},
|
||||
tiles = {
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_front_bottom_open_rside.png",
|
||||
"celevator_cabinet_front_bottom_open_lside.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_front_bottom_open.png",
|
||||
},
|
||||
after_place_node = after_place,
|
||||
on_destruct = ondestruct,
|
||||
on_rotate = onrotate,
|
||||
on_receive_fields = handlefields,
|
||||
on_punch = function(pos,node,puncher)
|
||||
if not puncher:is_player() then
|
||||
return
|
||||
end
|
||||
node.name = "celevator:controller"
|
||||
minetest.swap_node(pos,node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec","")
|
||||
pos.y = pos.y + 1
|
||||
node = minetest.get_node(pos)
|
||||
if node.name == "celevator:controller_top_open_running" then
|
||||
node.name = "celevator:controller_top_running"
|
||||
else
|
||||
node.name = "celevator:controller_top"
|
||||
end
|
||||
minetest.swap_node(pos,node)
|
||||
minetest.sound_play("doors_steel_door_close",{
|
||||
pos = pos,
|
||||
gain = 0.5,
|
||||
max_hear_distance = 10
|
||||
},true)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("celevator:controller_top",{
|
||||
description = "Controller (top section - you hacker you!)",
|
||||
groups = {
|
||||
not_in_creative_inventory = 1,
|
||||
},
|
||||
drop = "",
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5,-0.5,0,0.5,0.5,0.5},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{0,0,0,0,0,0},
|
||||
},
|
||||
},
|
||||
tiles = {
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_front_top.png",
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_node("celevator:controller_top_running",{
|
||||
description = "Controller (top section, car in motion - you hacker you!)",
|
||||
groups = {
|
||||
not_in_creative_inventory = 1,
|
||||
},
|
||||
drop = "",
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5,-0.5,0,0.5,0.5,0.5},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{0,0,0,0,0,0},
|
||||
},
|
||||
},
|
||||
tiles = {
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_front_top.png",
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_node("celevator:controller_top_open",{
|
||||
description = "Controller (top section, open - you hacker you!)",
|
||||
groups = {
|
||||
not_in_creative_inventory = 1,
|
||||
},
|
||||
drop = "",
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5,-0.5,0,0.5,0.5,0.5},
|
||||
{-0.5,-0.5,-0.5,-0.45,0.5,0},
|
||||
{0.45,-0.5,-0.5,0.5,0.5,0},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{0,0,0,0,0,0},
|
||||
},
|
||||
},
|
||||
tiles = {
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_front_top_open_rside.png",
|
||||
"celevator_cabinet_front_top_open_lside.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
{
|
||||
name="celevator_cabinet_front_top_open_stopped.png",
|
||||
animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=2},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_node("celevator:controller_top_open_running",{
|
||||
description = "Controller (top section, open, car in motion - you hacker you!)",
|
||||
groups = {
|
||||
not_in_creative_inventory = 1,
|
||||
},
|
||||
drop = "",
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5,-0.5,0,0.5,0.5,0.5},
|
||||
{-0.5,-0.5,-0.5,-0.45,0.5,0},
|
||||
{0.45,-0.5,-0.5,0.5,0.5,0},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{0,0,0,0,0,0},
|
||||
},
|
||||
},
|
||||
tiles = {
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_front_top_open_rside.png",
|
||||
"celevator_cabinet_front_top_open_lside.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
{
|
||||
name="celevator_cabinet_front_top_open_running.png",
|
||||
animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=2},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
function celevator.controller.iscontroller(pos,call2)
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == "ignore" and not call2 then
|
||||
minetest.forceload_block(pos)
|
||||
return celevator.controller.iscontroller(pos,true)
|
||||
elseif node.name == "celevator:controller" or node.name == "celevator:controller_open" then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function celevator.controller.finddrive(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
local drivepos = vector.add(pos,vector.new(0,1,0))
|
||||
drivepos = vector.add(drivepos,vector.rotate_around_axis(dir,vector.new(0,-1,0),math.pi/2))
|
||||
drivepos = vector.round(drivepos)
|
||||
local drivename = minetest.get_node(drivepos).name
|
||||
return drivepos,minetest.registered_nodes[drivename]._celevator_drive_type
|
||||
end
|
||||
|
||||
function celevator.controller.finish(pos,mem)
|
||||
if not celevator.controller.iscontroller(pos) then
|
||||
return
|
||||
else
|
||||
local drivepos,drivetype = celevator.controller.finddrive(pos)
|
||||
if drivetype then
|
||||
for _,command in ipairs(mem.drive.commands) do
|
||||
if command.command == "moveto" then
|
||||
celevator.drives[drivetype].moveto(drivepos,command.pos)
|
||||
elseif command.command == "setmaxvel" then
|
||||
celevator.drives[drivetype].setmaxvel(drivepos,command.maxvel)
|
||||
elseif command.command == "resetpos" then
|
||||
celevator.drives[drivetype].resetpos(drivepos)
|
||||
elseif command.command == "estop" then
|
||||
celevator.drives[drivetype].estop(drivepos)
|
||||
end
|
||||
end
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
meta:set_string("mem",minetest.serialize(mem))
|
||||
if node.name == "celevator:controller_open" then meta:set_string("formspec",mem.formspec or "") end
|
||||
meta:set_string("formspec_hidden",mem.formspec or "")
|
||||
meta:set_string("infotext",mem.infotext or "")
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
celevator.controller.iqueue[hash] = mem.interrupts
|
||||
celevator.storage:set_string("controller_iqueue",minetest.serialize(celevator.controller.iqueue))
|
||||
controllerleds(pos,mem.showrunning)
|
||||
celevator.controller.running[hash] = nil
|
||||
if #celevator.controller.equeue[hash] > 0 then
|
||||
local event = celevator.controller.equeue[hash][1]
|
||||
table.remove(celevator.controller.equeue[hash],1)
|
||||
celevator.storage:set_string("controller_equeue",minetest.serialize(celevator.controller.equeue))
|
||||
celevator.controller.run(pos,event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function celevator.controller.run(pos,event)
|
||||
if not celevator.controller.iscontroller(pos) then
|
||||
return
|
||||
else
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if not celevator.controller.equeue[hash] then
|
||||
celevator.controller.equeue[hash] = {}
|
||||
celevator.storage:set_string("controller_equeue",minetest.serialize(celevator.controller.equeue))
|
||||
end
|
||||
if celevator.controller.running[hash] then
|
||||
table.insert(celevator.controller.equeue[hash],event)
|
||||
celevator.storage:set_string("controller_equeue",minetest.serialize(celevator.controller.equeue))
|
||||
if #celevator.controller.equeue[hash] > 5 then
|
||||
minetest.log("warning","[celevator] [controller] Async process for controller at %s is falling behind, %d events in queue",minetest.pos_to_string(pos),#celevator.controller.equeue[hash])
|
||||
end
|
||||
return
|
||||
end
|
||||
celevator.controller.running[hash] = true
|
||||
local meta = minetest.get_meta(pos)
|
||||
local mem = minetest.deserialize(meta:get_string("mem"))
|
||||
if not mem then
|
||||
minetest.log("error","[celevator] [controller] Failed to load controller memory at "..minetest.pos_to_string(pos))
|
||||
mem = {}
|
||||
end
|
||||
mem.drive = {}
|
||||
mem.drive.commands = {}
|
||||
local drivepos,drivetype = celevator.controller.finddrive(pos)
|
||||
if drivetype then
|
||||
mem.drive.type = drivetype
|
||||
mem.drive.status = celevator.drives[drivetype].getstatus(drivepos)
|
||||
end
|
||||
mem.interrupts = celevator.controller.iqueue[minetest.hash_node_position(pos)] or {}
|
||||
minetest.handle_async(fw,celevator.controller.finish,pos,event,mem)
|
||||
end
|
||||
end
|
||||
|
||||
function celevator.controller.checkiqueue(dtime)
|
||||
for hash,iqueue in pairs(celevator.controller.iqueue) do
|
||||
local pos = minetest.get_position_from_hash(hash)
|
||||
for iid,time in pairs(iqueue) do
|
||||
iqueue[iid] = time-dtime
|
||||
if iqueue[iid] < 0 then
|
||||
iqueue[iid] = nil
|
||||
local event = {}
|
||||
event.type = "interrupt"
|
||||
event.iid = iid
|
||||
celevator.controller.run(pos,event)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_globalstep(celevator.controller.checkiqueue)
|
707
controllerfw.lua
Normal file
@ -0,0 +1,707 @@
|
||||
local pos,event,mem = ...
|
||||
|
||||
local function fault(ftype,fatal)
|
||||
if fatal then mem.fatalfault = true end
|
||||
if not mem.activefaults then mem.activefaults = {} end
|
||||
if not mem.faultlog then mem.faultlog = {} end
|
||||
if mem.activefaults[ftype] then return end
|
||||
mem.activefaults[ftype] = true
|
||||
table.insert(mem.faultlog,{ftype = ftype,timestamp = os.time()})
|
||||
end
|
||||
|
||||
if not mem.drive.status then
|
||||
fault("drivecomm",true)
|
||||
mem.drive.status = {
|
||||
apos = 0,
|
||||
dpos = 0,
|
||||
vel = 0,
|
||||
maxvel = 0,
|
||||
}
|
||||
end
|
||||
|
||||
local juststarted = false
|
||||
|
||||
local modenames = {
|
||||
normal = "Normal Operation",
|
||||
uninit = "Uninitialized",
|
||||
resync = "Position Sync - Floor",
|
||||
bfdemand = "Position Sync - Terminal",
|
||||
fault = "Fault",
|
||||
stop = "Emergency Stop",
|
||||
mrinspect = "Machine Room Inspection",
|
||||
carinspect = "Car Top Inspection",
|
||||
inspconflict = "Inspection Conflict",
|
||||
fs1 = "Fire Service - Phase 1",
|
||||
fs2 = "Fire Service - Phase 2",
|
||||
indep = "Independent Service",
|
||||
capture = "Captured",
|
||||
test = "Test Mode",
|
||||
}
|
||||
|
||||
local doorstates = {
|
||||
open = "Open",
|
||||
opening = "Opening",
|
||||
closing = "Closing",
|
||||
closed = "Closed",
|
||||
testtiming = "Closed",
|
||||
}
|
||||
|
||||
local faultnames = {
|
||||
drivecomm = "Lost Communication With Drive",
|
||||
}
|
||||
|
||||
local function drivecmd(command)
|
||||
table.insert(mem.drive.commands,command)
|
||||
end
|
||||
|
||||
local function interrupt(time,iid)
|
||||
mem.interrupts[iid] = time
|
||||
end
|
||||
|
||||
local function getpos()
|
||||
local ret = 0
|
||||
for k,v in ipairs(mem.params.floorheights) do
|
||||
ret = ret+v
|
||||
if ret > mem.drive.status.apos then return k end
|
||||
end
|
||||
return mem.params.floorheights[#mem.params.floorheights]
|
||||
end
|
||||
|
||||
local function gettarget(floor)
|
||||
local target = 0
|
||||
if floor == 1 then return 0 end
|
||||
for i=1,floor-1,1 do
|
||||
target = target+mem.params.floorheights[i]
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
local function gotofloor(floor)
|
||||
mem.carmotion = true
|
||||
drivecmd({
|
||||
command = "setmaxvel",
|
||||
maxvel = mem.params.contractspeed
|
||||
})
|
||||
drivecmd({
|
||||
command = "moveto",
|
||||
pos = gettarget(floor)
|
||||
})
|
||||
interrupt(0,"checkdrive")
|
||||
juststarted = true
|
||||
end
|
||||
|
||||
local function getnextcallabove(dir)
|
||||
for i=getpos(),#mem.params.floorheights,1 do
|
||||
if not dir then
|
||||
if mem.carcalls[i] then
|
||||
return i,"car"
|
||||
elseif mem.upcalls[i] then
|
||||
return i,"up"
|
||||
elseif mem.dncalls[i] then
|
||||
return i,"down"
|
||||
end
|
||||
elseif dir == "up" then
|
||||
if mem.carcalls[i] then
|
||||
return i,"car"
|
||||
elseif mem.upcalls[i] then
|
||||
return i,"up"
|
||||
end
|
||||
elseif dir == "down" then
|
||||
if mem.carcalls[i] then
|
||||
return i,"car"
|
||||
elseif mem.dncalls[i] then
|
||||
return i,"down"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getnextcallbelow(dir)
|
||||
for i=getpos(),1,-1 do
|
||||
if not dir then
|
||||
if mem.carcalls[i] then
|
||||
return i,"car"
|
||||
elseif mem.upcalls[i] then
|
||||
return i,"up"
|
||||
elseif mem.dncalls[i] then
|
||||
return i,"down"
|
||||
end
|
||||
elseif dir == "up" then
|
||||
if mem.carcalls[i] then
|
||||
return i,"car"
|
||||
elseif mem.upcalls[i] then
|
||||
return i,"up"
|
||||
end
|
||||
elseif dir == "down" then
|
||||
if mem.carcalls[i] then
|
||||
return i,"car"
|
||||
elseif mem.dncalls[i] then
|
||||
return i,"down"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getlowestupcall()
|
||||
for i=1,#mem.params.floornames,1 do
|
||||
if mem.upcalls[i] then return i end
|
||||
end
|
||||
end
|
||||
|
||||
local function gethighestdowncall()
|
||||
for i=#mem.params.floornames,1,-1 do
|
||||
if mem.dncalls[i] then return i end
|
||||
end
|
||||
end
|
||||
|
||||
local function open()
|
||||
--TODO: Door operator interface
|
||||
mem.doorstate = "opening"
|
||||
interrupt(2,"opened")
|
||||
end
|
||||
|
||||
local function close()
|
||||
--TODO: Door operator interface
|
||||
mem.doorstate = "closing"
|
||||
interrupt(2,"closed")
|
||||
end
|
||||
|
||||
mem.formspec = ""
|
||||
|
||||
local function fs(element)
|
||||
mem.formspec = mem.formspec..element
|
||||
end
|
||||
|
||||
if event.type == "program" then
|
||||
mem.carstate = "uninit"
|
||||
mem.editingfloor = 1
|
||||
mem.doorstate = "closed"
|
||||
mem.carmotion = false
|
||||
mem.carcalls = {}
|
||||
mem.upcalls = {}
|
||||
mem.dncalls = {}
|
||||
mem.screenpage = 1
|
||||
mem.scrollfollowscar = true
|
||||
mem.controllerstopsw = false
|
||||
mem.controllerinspectsw = false
|
||||
mem.cartopinspectsw = false
|
||||
mem.capturesw = false
|
||||
mem.testsw = false
|
||||
mem.activefaults = {}
|
||||
mem.faultlog = {}
|
||||
mem.fatalfault = false
|
||||
if not mem.params then
|
||||
mem.state = "unconfigured"
|
||||
mem.screenstate = "oobe_welcome"
|
||||
mem.params = {
|
||||
contractspeed = 1,
|
||||
floorheights = {5,5,5},
|
||||
floornames = {"1","2","3"},
|
||||
doortimer = 5,
|
||||
groupmode = "simplex",
|
||||
}
|
||||
end
|
||||
elseif event.type == "ui" then
|
||||
if mem.screenstate == "oobe_welcome" then
|
||||
if event.fields.license then
|
||||
mem.screenstate = "oobe_license"
|
||||
elseif event.fields.next then
|
||||
mem.screenstate = "oobe_groupmode"
|
||||
end
|
||||
elseif mem.screenstate == "oobe_license" then
|
||||
if event.fields.back then
|
||||
mem.screenstate = "oobe_welcome"
|
||||
end
|
||||
elseif mem.screenstate == "oobe_groupmode" then
|
||||
if event.fields.back then
|
||||
mem.screenstate = "oobe_welcome"
|
||||
elseif event.fields.simplex then
|
||||
mem.screenstate = "oobe_floortable"
|
||||
mem.params.groupmode = "simplex"
|
||||
elseif event.fields.group then
|
||||
mem.screenstate = "oobe_dispatcherconnect"
|
||||
mem.params.groupmode = "group"
|
||||
end
|
||||
elseif mem.screenstate == "oobe_dispatcherconnect" then
|
||||
if event.fields.back then
|
||||
mem.screenstate = "oobe_groupmode"
|
||||
end
|
||||
elseif mem.screenstate == "oobe_floortable" or mem.screenstate == "floortable" then
|
||||
local exp = event.fields.floor and minetest.explode_textlist_event(event.fields.floor) or {}
|
||||
if event.fields.back then
|
||||
mem.screenstate = "oobe_groupmode"
|
||||
elseif event.fields.next then
|
||||
if mem.screenstate == "oobe_floortable" then
|
||||
mem.activefaults = {}
|
||||
mem.faultlog = {}
|
||||
mem.fatalfault = false
|
||||
end
|
||||
mem.state = "configured"
|
||||
mem.screenstate = (mem.screenstate == "oobe_floortable" and "status" or "parameters")
|
||||
mem.screenpage = 1
|
||||
mem.carstate = "bfdemand"
|
||||
if mem.doorstate == "closed" then
|
||||
drivecmd({
|
||||
command = "setmaxvel",
|
||||
maxvel = mem.params.contractspeed,
|
||||
})
|
||||
drivecmd({command = "resetpos"})
|
||||
interrupt(0.1,"checkdrive")
|
||||
mem.carmotion = true
|
||||
juststarted = true
|
||||
else
|
||||
close()
|
||||
end
|
||||
elseif exp.type == "CHG" then
|
||||
mem.editingfloor = #mem.params.floornames-exp.index+1
|
||||
elseif exp.type == "DCL" then
|
||||
mem.editingfloor = #mem.params.floornames-exp.index+1
|
||||
mem.screenstate = (mem.screenstate == "oobe_floortable" and "oobe_floortable_edit" or "floortable_edit")
|
||||
elseif event.fields.edit then
|
||||
mem.screenstate = (mem.screenstate == "oobe_floortable" and "oobe_floortable_edit" or "floortable_edit")
|
||||
elseif event.fields.add then
|
||||
table.insert(mem.params.floorheights,5)
|
||||
table.insert(mem.params.floornames,tostring(#mem.params.floornames+1))
|
||||
elseif event.fields.remove then
|
||||
table.remove(mem.params.floorheights,mem.editingfloor)
|
||||
table.remove(mem.params.floornames,mem.editingfloor)
|
||||
mem.editingfloor = math.max(1,mem.editingfloor-1)
|
||||
elseif event.fields.moveup then
|
||||
local height = mem.params.floorheights[mem.editingfloor]
|
||||
local name = mem.params.floornames[mem.editingfloor]
|
||||
table.remove(mem.params.floorheights,mem.editingfloor)
|
||||
table.remove(mem.params.floornames,mem.editingfloor)
|
||||
table.insert(mem.params.floorheights,mem.editingfloor+1,height)
|
||||
table.insert(mem.params.floornames,mem.editingfloor+1,name)
|
||||
mem.editingfloor = mem.editingfloor + 1
|
||||
elseif event.fields.movedown then
|
||||
local height = mem.params.floorheights[mem.editingfloor]
|
||||
local name = mem.params.floornames[mem.editingfloor]
|
||||
table.remove(mem.params.floorheights,mem.editingfloor)
|
||||
table.remove(mem.params.floornames,mem.editingfloor)
|
||||
table.insert(mem.params.floorheights,mem.editingfloor-1,height)
|
||||
table.insert(mem.params.floornames,mem.editingfloor-1,name)
|
||||
mem.editingfloor = mem.editingfloor - 1
|
||||
end
|
||||
elseif mem.screenstate == "oobe_floortable_edit" or mem.screenstate == "floortable_edit" then
|
||||
if event.fields.back then
|
||||
mem.screenstate = (mem.screenstate == "oobe_floortable_edit" and "oobe_floortable" or "floortable")
|
||||
local height = tonumber(event.fields.height)
|
||||
if height then
|
||||
height = math.floor(height+0.5)
|
||||
mem.params.floorheights[mem.editingfloor] = math.max(0,height)
|
||||
end
|
||||
mem.params.floornames[mem.editingfloor] = string.sub(event.fields.name,1,256)
|
||||
end
|
||||
elseif mem.screenstate == "parameters" then
|
||||
if event.fields.save then
|
||||
mem.screenstate = "status"
|
||||
local doortimer = tonumber(event.fields.doortimer)
|
||||
if doortimer and doortimer > 0 and doortimer <= 30 then
|
||||
mem.params.doortimer = doortimer
|
||||
end
|
||||
local contractspeed = tonumber(event.fields.contractspeed)
|
||||
if contractspeed and contractspeed >= 0.1 and contractspeed <= 20 then
|
||||
mem.params.contractspeed = contractspeed
|
||||
end
|
||||
elseif event.fields.floortable then
|
||||
mem.screenstate = "floortable"
|
||||
elseif event.fields.cancel then
|
||||
mem.screenstate = "status"
|
||||
end
|
||||
elseif mem.screenstate == "status" then
|
||||
for i=1,#mem.params.floornames,1 do
|
||||
if event.fields[string.format("carcall%d",i)] and (mem.carstate == "normal" or mem.carstate == "test" or mem.carstate == "capture") then
|
||||
mem.carcalls[i] = true
|
||||
elseif event.fields[string.format("upcall%d",i)] and mem.carstate == "normal" and not mem.capturesw then
|
||||
mem.upcalls[i] = true
|
||||
elseif event.fields[string.format("downcall%d",i)] and mem.carstate == "normal" and not mem.capturesw then
|
||||
mem.dncalls[i] = true
|
||||
end
|
||||
end
|
||||
if event.fields.scrollup then
|
||||
mem.screenpage = mem.screenpage + 1
|
||||
mem.scrollfollowscar = false
|
||||
elseif event.fields.scrolldown then
|
||||
mem.screenpage = mem.screenpage - 1
|
||||
mem.scrollfollowscar = false
|
||||
elseif event.fields.scrollfollowscar then
|
||||
mem.scrollfollowscar = (event.fields.scrollfollowscar == "true")
|
||||
elseif event.fields.stopsw then
|
||||
mem.controllerstopsw = not mem.controllerstopsw
|
||||
elseif event.fields.inspectsw then
|
||||
mem.controllerinspectsw = not mem.controllerinspectsw
|
||||
elseif event.fields.capturesw then
|
||||
mem.capturesw = not mem.capturesw
|
||||
elseif event.fields.testsw then
|
||||
mem.testsw = not mem.testsw
|
||||
elseif event.fields.inspectup and mem.carstate == "mrinspect" and mem.doorstate == "closed" and getpos() < #mem.params.floornames then
|
||||
mem.carmotion = true
|
||||
juststarted = true
|
||||
drivecmd({
|
||||
command = "setmaxvel",
|
||||
maxvel = 0.2,
|
||||
})
|
||||
drivecmd({
|
||||
command = "moveto",
|
||||
pos = math.floor(mem.drive.status.apos)+1
|
||||
})
|
||||
elseif event.fields.inspectdown and mem.carstate == "mrinspect" and mem.doorstate == "closed" and mem.drive.status.apos-1 >= 0 then
|
||||
mem.carmotion = true
|
||||
juststarted = true
|
||||
drivecmd({
|
||||
command = "setmaxvel",
|
||||
maxvel = 0.2,
|
||||
})
|
||||
drivecmd({
|
||||
command = "moveto",
|
||||
pos = math.floor(mem.drive.status.apos)-1
|
||||
})
|
||||
elseif event.fields.parameters then
|
||||
mem.screenstate = "parameters"
|
||||
elseif event.fields.faults then
|
||||
mem.screenstate = "faults"
|
||||
end
|
||||
elseif mem.screenstate == "faults" then
|
||||
if event.fields.back then
|
||||
mem.screenstate = "status"
|
||||
elseif event.fields.clear then
|
||||
mem.faultlog = {}
|
||||
mem.activefaults = {}
|
||||
mem.fatalfault = false
|
||||
end
|
||||
end
|
||||
elseif event.iid == "opened" and mem.doorstate == "opening" then
|
||||
mem.doorstate = "open"
|
||||
if mem.carstate == "normal" then
|
||||
interrupt(mem.params.doortimer,"close")
|
||||
end
|
||||
elseif event.iid == "close" and mem.doorstate == "open" then
|
||||
close()
|
||||
elseif event.iid == "closed" and (mem.doorstate == "closing" or mem.doorstate == "testtiming") then
|
||||
mem.doorstate = "closed"
|
||||
if mem.carstate == "bfdemand" then
|
||||
drivecmd({
|
||||
command = "setmaxvel",
|
||||
maxvel = mem.params.contractspeed,
|
||||
})
|
||||
drivecmd({command = "resetpos"})
|
||||
interrupt(0.1,"checkdrive")
|
||||
mem.carmotion = true
|
||||
juststarted = true
|
||||
elseif mem.carstate == "resync" then
|
||||
gotofloor(getpos())
|
||||
interrupt(0.1,"checkdrive")
|
||||
mem.carmotion = true
|
||||
juststarted = true
|
||||
end
|
||||
end
|
||||
|
||||
local oldstate = mem.carstate
|
||||
|
||||
if mem.fatalfault then
|
||||
mem.carstate = "fault"
|
||||
drivecmd({command="estop"})
|
||||
mem.carcalls = {}
|
||||
mem.upcalls = {}
|
||||
mem.dncalls = {}
|
||||
mem.direction = nil
|
||||
elseif mem.controllerstopsw or mem.screenstate == "floortable" or mem.screenstate == "floortable_edit" then
|
||||
mem.carstate = "stop"
|
||||
drivecmd({command="estop"})
|
||||
mem.carcalls = {}
|
||||
mem.upcalls = {}
|
||||
mem.dncalls = {}
|
||||
mem.direction = nil
|
||||
elseif mem.controllerinspectsw and not mem.cartopinspectsw then
|
||||
mem.carstate = "mrinspect"
|
||||
mem.carcalls = {}
|
||||
mem.upcalls = {}
|
||||
mem.dncalls = {}
|
||||
mem.direction = nil
|
||||
if oldstate ~= "mrinspect" then drivecmd({command="estop"}) end
|
||||
elseif mem.testsw then
|
||||
mem.upcalls = {}
|
||||
mem.dncalls = {}
|
||||
mem.carstate = "test"
|
||||
elseif mem.capturesw then
|
||||
mem.upcalls = {}
|
||||
mem.dncalls = {}
|
||||
if not mem.direction then mem.carstate = "capture" end
|
||||
else
|
||||
if oldstate == "stop" or oldstate == "mrinspect" or oldstate == "fault" then
|
||||
mem.carstate = "resync"
|
||||
gotofloor(getpos())
|
||||
elseif oldstate == "test" or oldstate == "capture" then
|
||||
mem.carstate = "normal"
|
||||
end
|
||||
end
|
||||
|
||||
if mem.carmotion then
|
||||
mem.carmotion = (mem.drive.status.vel ~= 0) or juststarted
|
||||
if mem.carmotion then
|
||||
interrupt(0.1,"checkdrive")
|
||||
else
|
||||
if mem.carstate == "normal" then
|
||||
mem.carcalls[getpos()] = nil
|
||||
if mem.direction == "up" then
|
||||
mem.upcalls[getpos()] = nil
|
||||
elseif mem.direction == "down" then
|
||||
mem.dncalls[getpos()] = nil
|
||||
end
|
||||
if getpos() >= #mem.params.floornames then
|
||||
mem.direction = "down"
|
||||
elseif getpos() <= 1 then
|
||||
mem.direction = "up"
|
||||
end
|
||||
open()
|
||||
elseif mem.carstate == "test" then
|
||||
mem.carcalls[getpos()] = nil
|
||||
mem.doorstate = "testtiming"
|
||||
interrupt(5,"closed")
|
||||
if getpos() >= #mem.params.floornames then
|
||||
mem.direction = "down"
|
||||
elseif getpos() <= 1 then
|
||||
mem.direction = "up"
|
||||
end
|
||||
elseif mem.carstate == "bfdemand" or mem.carstate == "resync" then
|
||||
mem.carstate = "normal"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (mem.carstate == "normal" or mem.carstate == "capture" or mem.carstate == "test") and mem.doorstate == "closed" and not mem.carmotion then
|
||||
if mem.direction == "up" then
|
||||
if getnextcallabove("up") then
|
||||
mem.direction = "up"
|
||||
gotofloor(getnextcallabove("up"))
|
||||
elseif gethighestdowncall() then
|
||||
mem.direction = "down"
|
||||
gotofloor(gethighestdowncall())
|
||||
elseif getlowestupcall() then
|
||||
gotofloor(getlowestupcall())
|
||||
elseif getnextcallbelow("down") then
|
||||
mem.direction = "down"
|
||||
gotofloor(getnextcallbelow("down"))
|
||||
else
|
||||
mem.direction = nil
|
||||
end
|
||||
elseif mem.direction == "down" then
|
||||
if getnextcallbelow("down") then
|
||||
gotofloor(getnextcallbelow("down"))
|
||||
elseif getlowestupcall() then
|
||||
mem.direction = "up"
|
||||
gotofloor(getlowestupcall())
|
||||
elseif gethighestdowncall() then
|
||||
gotofloor(gethighestdowncall())
|
||||
elseif getnextcallabove("up") then
|
||||
mem.direction = "up"
|
||||
gotofloor(getnextcallabove())
|
||||
else
|
||||
mem.direction = nil
|
||||
end
|
||||
else
|
||||
if getnextcallabove("up") then
|
||||
mem.direction = "up"
|
||||
gotofloor(getnextcallabove())
|
||||
elseif getnextcallbelow("down") then
|
||||
mem.direction = "down"
|
||||
gotofloor(getnextcallbelow("down"))
|
||||
elseif getlowestupcall() then
|
||||
mem.direction = "up"
|
||||
gotofloor(getlowestupcall())
|
||||
elseif gethighestdowncall() then
|
||||
mem.direction = "down"
|
||||
gotofloor(gethighestdowncall())
|
||||
end
|
||||
end
|
||||
if mem.carstate == "normal" and mem.capturesw and not mem.direction then
|
||||
mem.upcalls = {}
|
||||
mem.dncalls = {}
|
||||
mem.carstate = "capture"
|
||||
elseif mem.carstate == "capture" and mem.direction then
|
||||
mem.carstate = "normal"
|
||||
end
|
||||
end
|
||||
|
||||
if mem.scrollfollowscar and mem.screenstate == "status" then
|
||||
mem.screenpage = math.floor((getpos()-1)/10)+1
|
||||
end
|
||||
|
||||
fs("formspec_version[6]")
|
||||
fs("size[16,12]")
|
||||
fs("background9[0,0;16,12;celevator_fs_bg.png;true;3]")
|
||||
if mem.screenstate == "oobe_welcome" then
|
||||
fs("image[6,1;4,2;celevator_logo.png]")
|
||||
fs("label[1,4;Welcome to your new MTronic XT elevator controller!]")
|
||||
fs("label[1,4.5;This setup wizard is designed to get your elevator up and running as quickly as possible.]")
|
||||
fs("label[1,5.5;Press Next to begin.]")
|
||||
fs("button[1,10;2,1;license;License Info]")
|
||||
fs("button[13,10;2,1;next;Next >]")
|
||||
elseif mem.screenstate == "oobe_license" then
|
||||
local licensefile = io.open(minetest.get_modpath("celevator")..DIR_DELIM.."COPYING")
|
||||
local license = minetest.formspec_escape(licensefile:read("*all"))
|
||||
licensefile:close()
|
||||
fs("textarea[1,1;14,8;license;This applies to the whole celevator mod\\, not just this controller:;"..license.."]")
|
||||
fs("button[7,10.5;2,1;back;OK]")
|
||||
elseif mem.screenstate == "oobe_groupmode" then
|
||||
fs("button[1,10;2,1;back;< Back]")
|
||||
fs("label[1,1;Select a group operation mode:]")
|
||||
fs("button[1,3;2,1;simplex;Simplex]")
|
||||
fs("label[1,4.5;This will be the only elevator in the group. Hall calls will be handled by this controller.]")
|
||||
fs("button[1,6;2,1;group;Group]")
|
||||
fs("label[1,7.5;This elevator will participate in a group with others. Hall calls will be handled by a dispatcher. (not implemented)]")
|
||||
elseif mem.screenstate == "oobe_dispatcherconnect" then
|
||||
fs("button[1,10;2,1;back;< Back]")
|
||||
fs("label[1,1;Not yet implemented. Press Back.]")
|
||||
elseif mem.screenstate == "oobe_floortable" or mem.screenstate == "floortable" then
|
||||
if mem.screenstate == "oobe_floortable" then
|
||||
fs("label[1,1;Enter details of all floors this elevator will serve, then press Done.]")
|
||||
fs("button[1,10;2,1;back;< Back]")
|
||||
fs("button[13,10;2,1;next;Done >]")
|
||||
else
|
||||
fs("label[1,1;EDIT FLOOR TABLE]")
|
||||
fs("button[1,10;2,1;next;Done]")
|
||||
end
|
||||
fs("textlist[1,2;6,7;floor;")
|
||||
for i=#mem.params.floornames,1,-1 do
|
||||
fs(minetest.formspec_escape(string.format("%d - Height: %d - PI: %s",i,mem.params.floorheights[i],mem.params.floornames[i]))..(i==1 and "" or ","))
|
||||
end
|
||||
fs(";"..tostring(#mem.params.floornames-mem.editingfloor+1)..";false]")
|
||||
fs("button[8,2;2,1;add;New Floor]")
|
||||
fs("button[8,3.5;2,1;edit;Edit Floor]")
|
||||
if #mem.params.floornames > 2 then fs("button[8,5;2,1;remove;Remove Floor]") end
|
||||
if mem.editingfloor < #mem.params.floornames then fs("button[8,6.5;2,1;moveup;Move Up]") end
|
||||
if mem.editingfloor > 1 then fs("button[8,8;2,1;movedown;Move Down") end
|
||||
elseif mem.screenstate == "oobe_floortable_edit" or mem.screenstate == "floortable_edit" then
|
||||
if mem.screenstate == "oobe_floortable_edit" then
|
||||
fs("button[7,10.5;2,1;back;OK]")
|
||||
fs("label[1,5;The Floor Height is the distance (in meters/nodes) from the floor level of this floor to the floor level of the next floor.]")
|
||||
fs("label[1,5.5;(not used at the highest floor)]")
|
||||
fs("label[1,6.5;The Floor Name is how the floor will be displayed on the position indicators.]")
|
||||
else
|
||||
fs("button[7,10.5;2,1;save;Save]")
|
||||
end
|
||||
fs("label[1,1;Editing floor "..tostring(mem.editingfloor).."]")
|
||||
fs("field[1,3;3,1;height;Floor Height;"..tostring(mem.params.floorheights[mem.editingfloor]).."]")
|
||||
fs("field[5,3;3,1;name;Floor Name;"..minetest.formspec_escape(mem.params.floornames[mem.editingfloor]).."]")
|
||||
elseif mem.screenstate == "status" then
|
||||
fs("style_type[image_button;font=mono;font_size=*0.75]")
|
||||
fs("box[12,2.5;0.1,9;#AAAAAAFF]")
|
||||
fs("box[13.12,2.5;0.05,9;#AAAAAAFF]")
|
||||
fs("box[14.12,2.5;0.05,9;#AAAAAAFF]")
|
||||
fs("box[15.25,2.5;0.1,9;#AAAAAAFF]")
|
||||
fs("label[12.5,2;UP]")
|
||||
fs("label[13.38,2;CAR]")
|
||||
fs("label[14.25,2;DOWN]")
|
||||
local maxfloor = #mem.params.floornames
|
||||
local bottom = (mem.screenpage-1)*10+1
|
||||
for i=0,9,1 do
|
||||
local ypos = 11-(i*0.9)
|
||||
local floornum = bottom+i
|
||||
if floornum > maxfloor then break end
|
||||
fs(string.format("label[11.25,%f;%s]",ypos,mem.params.floornames[floornum]))
|
||||
local ccdot = mem.carcalls[floornum] and "*" or ""
|
||||
if getpos() == floornum then
|
||||
local cargraphics = {
|
||||
open = "\\[ \\]",
|
||||
opening = "\\[< >\\]",
|
||||
closing = "\\[> <\\]",
|
||||
closed = "\\[ | \\]",
|
||||
testtiming = "\\[ | \\]",
|
||||
}
|
||||
ccdot = cargraphics[mem.doorstate]
|
||||
if mem.direction == "up" then
|
||||
ccdot = minetest.colorize("#55FF55",ccdot)
|
||||
elseif mem.direction == "down" then
|
||||
ccdot = minetest.colorize("#FF5555",ccdot)
|
||||
end
|
||||
end
|
||||
fs(string.format("image_button[13.25,%f;0.75,0.75;celevator_fs_bg.png;carcall%d;%s]",ypos-0.25,floornum,ccdot))
|
||||
if floornum < maxfloor then
|
||||
local arrow = mem.upcalls[floornum] and minetest.colorize("#55FF55","^") or ""
|
||||
fs(string.format("image_button[12.25,%f;0.75,0.75;celevator_fs_bg.png;upcall%d;%s]",ypos-0.25,floornum,arrow))
|
||||
end
|
||||
if floornum > 1 then
|
||||
local arrow = mem.dncalls[floornum] and minetest.colorize("#FF5555","v") or ""
|
||||
fs(string.format("image_button[14.25,%f;0.75,0.75;celevator_fs_bg.png;downcall%d;%s]",ypos-0.25,floornum,arrow))
|
||||
end
|
||||
end
|
||||
if maxfloor > 10 then
|
||||
fs(string.format("checkbox[13,1.25;scrollfollowscar;Follow Car;%s]",tostring(mem.scrollfollowscar)))
|
||||
if bottom+9 < maxfloor then
|
||||
fs("image_button[12.75,0.25;0.75,0.75;celevator_menu_arrow.png;scrollup;;false;false;celevator_menu_arrow.png]")
|
||||
end
|
||||
if bottom > 1 then
|
||||
fs("image_button[13.87,0.25;0.75,0.75;celevator_menu_arrow.png^\\[transformFY;scrolldown;;false;false;celevator_menu_arrow.png^\\[transformFY]")
|
||||
end
|
||||
end
|
||||
fs("label[1,1;CAR STATUS]")
|
||||
fs(string.format("label[1,2;%s]",modenames[mem.carstate]))
|
||||
fs(string.format("label[1,2.5;Doors %s]",doorstates[mem.doorstate]))
|
||||
fs(string.format("label[1,3;Position: %0.02fm Speed: %+0.02fm/s PI: %s]",mem.drive.status.apos,mem.drive.status.vel,minetest.formspec_escape(mem.params.floornames[getpos()])))
|
||||
if #mem.faultlog > 0 then
|
||||
fs("label[1,3.5;Fault(s) Active]")
|
||||
else
|
||||
fs("label[1,3.5;No Current Faults]")
|
||||
end
|
||||
fs("button[1,10;3,1;faults;Fault History]")
|
||||
fs("button[4.5,10;3,1;parameters;Edit Parameters]")
|
||||
fs("style[*;font=mono]")
|
||||
local stopswimg = "celevator_toggle_switch.png"..(mem.controllerstopsw and "^\\[transformFY" or "")
|
||||
fs(string.format("image_button[1,5;1,1.33;%s;stopsw;;false;false;%s]",stopswimg,stopswimg))
|
||||
fs("label[1.3,4.75;RUN]")
|
||||
fs("label[1.2,6.6;STOP]")
|
||||
local captureswimg = "celevator_toggle_switch.png"..(mem.capturesw and "" or "^\\[transformFY")
|
||||
fs(string.format("image_button[3,5;1,1.33;%s;capturesw;;false;false;%s]",captureswimg,captureswimg))
|
||||
fs("label[3,4.75;CAPTURE]")
|
||||
local testswimg = "celevator_toggle_switch.png"..(mem.testsw and "" or "^\\[transformFY")
|
||||
fs(string.format("image_button[5,5;1,1.33;%s;testsw;;false;false;%s]",testswimg,testswimg))
|
||||
fs("label[5.23,4.75;TEST]")
|
||||
local inspectswimg = "celevator_toggle_switch.png"..(mem.controllerinspectsw and "" or "^\\[transformFY")
|
||||
fs(string.format("image_button[1,8;1,1.33;%s;inspectsw;;false;false;%s]",inspectswimg,inspectswimg))
|
||||
fs("label[1.05,7.75;INSPECT]")
|
||||
fs("label[1.1,9.6;NORMAL]")
|
||||
fs(string.format("image_button[3,8.25;1,1;%s;inspectup;;false;false;%s]","celevator_button_black.png","celevator_button_black.png"))
|
||||
fs("label[3.4,7.75;UP]")
|
||||
fs(string.format("image_button[5,8.25;1,1;%s;inspectdown;;false;false;%s]","celevator_button_black.png","celevator_button_black.png"))
|
||||
fs("label[5.25,7.75;DOWN]")
|
||||
elseif mem.screenstate == "parameters" then
|
||||
fs("label[1,1;EDIT PARAMETERS]")
|
||||
fs("button[1,10;3,1;save;Save]")
|
||||
fs("button[4.5,10;3,1;cancel;Cancel]")
|
||||
fs("button[8,10;3,1;floortable;Edit Floor Table]")
|
||||
fs(string.format("field[1,3;3,1;doortimer;Door Dwell Timer;%0.1f]",mem.params.doortimer))
|
||||
fs(string.format("field[1,5;3,1;contractspeed;Contract Speed (m/s);%0.1f]",mem.params.contractspeed))
|
||||
elseif mem.screenstate == "faults" then
|
||||
fs("label[1,1;FAULT HISTORY]")
|
||||
if #mem.faultlog > 0 then
|
||||
for i=0,9,1 do
|
||||
if #mem.faultlog-i >= 1 then
|
||||
local currfault = mem.faultlog[#mem.faultlog-i]
|
||||
local date = os.date("*t",currfault.timestamp)
|
||||
fs(string.format("label[1,%0.1f;%04d-%02d-%02d %02d:%02d:%02d - %s]",2+i,date.year,date.month,date.day,date.hour,date.min,date.sec,faultnames[currfault.ftype]))
|
||||
end
|
||||
end
|
||||
else
|
||||
fs("label[1,2;No Current Faults]")
|
||||
end
|
||||
fs("button[1,10;3,1;back;Back]")
|
||||
fs("button[4.5,10;3,1;clear;Clear]")
|
||||
end
|
||||
|
||||
local arrow = " "
|
||||
if mem.drive.status.dpos > mem.drive.status.apos then
|
||||
arrow = "^"
|
||||
elseif mem.drive.status.dpos < mem.drive.status.apos then
|
||||
arrow = "v"
|
||||
end
|
||||
mem.infotext = string.format("Floor %s %s - %s - Doors %s",mem.params.floornames[getpos()],arrow,modenames[mem.carstate],doorstates[mem.doorstate])
|
||||
|
||||
if mem.drive.type then
|
||||
mem.showrunning = mem.drive.status.vel ~= 0
|
||||
else
|
||||
mem.showrunning = false
|
||||
end
|
||||
|
||||
return pos,mem
|
206
drive_null.lua
Normal file
@ -0,0 +1,206 @@
|
||||
celevator.drives.null = {
|
||||
name = "Null Drive",
|
||||
description = "Simulation only, no movement, for testing and demonstration",
|
||||
nname = "celevator:drive_null",
|
||||
soundhandles = {},
|
||||
}
|
||||
|
||||
local function update_ui(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local apos = tonumber(meta:get_string("apos")) or 0
|
||||
local status = "Idle"
|
||||
local vel = tonumber(meta:get_string("vel")) or 0
|
||||
if vel > 0 then
|
||||
status = string.format("Running: Up, %0.02f m/s",vel)
|
||||
elseif vel < 0 then
|
||||
status = string.format("Running: Down, %0.02f m/s",math.abs(vel))
|
||||
end
|
||||
meta:set_string("infotext",string.format("Null Drive - %s - Position: %0.02f m",status,apos))
|
||||
end
|
||||
|
||||
local function playbuzz(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if celevator.drives.null.soundhandles[hash] == "cancel" then return end
|
||||
celevator.drives.null.soundhandles[hash] = minetest.sound_play("celevator_drive_run",{
|
||||
pos = pos,
|
||||
loop = true,
|
||||
gain = 0.4,
|
||||
})
|
||||
end
|
||||
|
||||
local function startbuzz(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if celevator.drives.null.soundhandles[hash] == "cancel" then
|
||||
celevator.drives.null.soundhandles[hash] = nil
|
||||
return
|
||||
end
|
||||
if celevator.drives.null.soundhandles[hash] then return end
|
||||
celevator.drives.null.soundhandles[hash] = "pending"
|
||||
minetest.after(0.5,playbuzz,pos)
|
||||
end
|
||||
|
||||
local function stopbuzz(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if not celevator.drives.null.soundhandles[hash] then return end
|
||||
if celevator.drives.null.soundhandles[hash] == "pending" then
|
||||
celevator.drives.null.soundhandles[hash] = "cancel"
|
||||
end
|
||||
if type(celevator.drives.null.soundhandles[hash]) ~= "string" then
|
||||
minetest.sound_stop(celevator.drives.null.soundhandles[hash])
|
||||
celevator.drives.null.soundhandles[hash] = nil
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("celevator:drive_null",{
|
||||
description = celevator.drives.null.name,
|
||||
groups = {
|
||||
cracky = 1,
|
||||
},
|
||||
tiles = {
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_cabinet_sides.png",
|
||||
"celevator_drive_front.png",
|
||||
},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.4,-0.4,-0.1,0.4,0.4,0.5},
|
||||
{-0.5,-0.3,0.4,-0.4,-0.22,0.32},
|
||||
{-0.5,0.22,0.4,-0.4,0.3,0.32},
|
||||
},
|
||||
},
|
||||
_celevator_drive_type = "null",
|
||||
after_place_node = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("apos","0")
|
||||
meta:set_string("dpos","0")
|
||||
meta:set_string("vel","0")
|
||||
meta:set_string("maxvel","0.2")
|
||||
update_ui(pos)
|
||||
end,
|
||||
on_destruct = stopbuzz,
|
||||
})
|
||||
|
||||
function celevator.drives.null.step(dtime)
|
||||
local nulldrives_running = minetest.deserialize(celevator.storage:get_string("nulldrives_running")) or {}
|
||||
local save = false
|
||||
for i,hash in ipairs(nulldrives_running) do
|
||||
save = true
|
||||
local pos = minetest.get_position_from_hash(hash)
|
||||
local node = minetest.get_node(pos)
|
||||
local sound = false
|
||||
if node.name == "ignore" then
|
||||
minetest.forceload_block(pos,true)
|
||||
elseif node.name ~= "celevator:drive_null" then
|
||||
table.remove(nulldrives_running,i)
|
||||
else
|
||||
local meta = minetest.get_meta(pos)
|
||||
local apos = tonumber(meta:get_string("apos")) or 0
|
||||
local dpos = tonumber(meta:get_string("dpos")) or 0
|
||||
local maxvel = tonumber(meta:get_string("maxvel")) or 0.2
|
||||
local dremain = math.abs(dpos-apos)
|
||||
local vel = maxvel
|
||||
if dremain < 0.5 then vel = math.min(0.2,vel) end
|
||||
local stepdist = vel*dtime
|
||||
if dpos > apos then
|
||||
local newpos = apos + stepdist
|
||||
if newpos < dpos then
|
||||
meta:set_string("apos",tostring(newpos))
|
||||
meta:set_string("vel",vel)
|
||||
sound = true
|
||||
else
|
||||
meta:set_string("apos",tostring(dpos))
|
||||
meta:set_string("vel",0)
|
||||
sound = false
|
||||
end
|
||||
elseif dpos < apos then
|
||||
local newpos = apos - stepdist
|
||||
if newpos > dpos then
|
||||
meta:set_string("apos",tostring(newpos))
|
||||
meta:set_string("vel",0-vel)
|
||||
sound = true
|
||||
else
|
||||
meta:set_string("apos",tostring(dpos))
|
||||
meta:set_string("vel",0)
|
||||
sound = false
|
||||
end
|
||||
else
|
||||
table.remove(nulldrives_running,i)
|
||||
end
|
||||
end
|
||||
update_ui(pos)
|
||||
if sound then
|
||||
startbuzz(pos)
|
||||
else
|
||||
stopbuzz(pos)
|
||||
end
|
||||
end
|
||||
if save then
|
||||
celevator.storage:set_string("nulldrives_running",minetest.serialize(nulldrives_running))
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_globalstep(celevator.drives.null.step)
|
||||
|
||||
function celevator.drives.null.moveto(pos,target)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("dpos",tostring(target))
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
local nulldrives_running = minetest.deserialize(celevator.storage:get_string("nulldrives_running")) or {}
|
||||
local running = false
|
||||
for _,dhash in ipairs(nulldrives_running) do
|
||||
if hash == dhash then
|
||||
running = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not running then
|
||||
table.insert(nulldrives_running,hash)
|
||||
celevator.storage:set_string("nulldrives_running",minetest.serialize(nulldrives_running))
|
||||
end
|
||||
end
|
||||
|
||||
function celevator.drives.null.resetpos(pos)
|
||||
celevator.drives.null.moveto(pos,0)
|
||||
end
|
||||
|
||||
function celevator.drives.null.estop(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("dpos",meta:get_string("apos"))
|
||||
meta:set_string("vel","0")
|
||||
end
|
||||
|
||||
function celevator.drives.null.setmaxvel(pos,maxvel)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("maxvel",tostring(maxvel))
|
||||
end
|
||||
|
||||
function celevator.drives.null.rezero(pos)
|
||||
celevator.drives.null.moveto(pos,0)
|
||||
end
|
||||
|
||||
function celevator.drives.null.getstatus(pos,call2)
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == "ignore" and not call2 then
|
||||
minetest.forceload_block(pos,true)
|
||||
return celevator.drives.null.get_status(pos,true)
|
||||
elseif node.name ~= "celevator:drive_null" then
|
||||
minetest.log("error","[celevator] [null drive] Could not load drive status at "..minetest.pos_to_string(pos))
|
||||
return
|
||||
else
|
||||
local meta = minetest.get_meta(pos)
|
||||
local ret = {}
|
||||
ret.apos = tonumber(meta:get_string("apos")) or 0
|
||||
ret.dpos = tonumber(meta:get_string("dpos")) or 0
|
||||
ret.vel = tonumber(meta:get_string("vel")) or 0
|
||||
ret.maxvel = tonumber(meta:get_string("maxvel")) or 0.2
|
||||
ret.neareststop = ret.apos
|
||||
return ret
|
||||
end
|
||||
end
|
4
framework.lua
Normal file
@ -0,0 +1,4 @@
|
||||
celevator = {
|
||||
drives = {},
|
||||
storage = minetest.get_mod_storage(),
|
||||
}
|
10
init.lua
Normal file
@ -0,0 +1,10 @@
|
||||
local components = {
|
||||
"framework",
|
||||
"drive_null",
|
||||
"controller",
|
||||
"callbuttons",
|
||||
}
|
||||
|
||||
for _,v in ipairs(components) do
|
||||
dofile(string.format("%s%s%s.lua",minetest.get_modpath("celevator"),DIR_DELIM,v))
|
||||
end
|
BIN
sounds/celevator_controller_start.ogg
Normal file
BIN
sounds/celevator_controller_stop.ogg
Normal file
BIN
sounds/celevator_drive_run.ogg
Normal file
BIN
textures/celevator_button_black.png
Normal file
After Width: | Height: | Size: 598 B |
BIN
textures/celevator_cabinet_front_bottom.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
textures/celevator_cabinet_front_bottom_open.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
textures/celevator_cabinet_front_bottom_open_lside.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
textures/celevator_cabinet_front_bottom_open_rside.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
textures/celevator_cabinet_front_top.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
textures/celevator_cabinet_front_top_open_lside.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
textures/celevator_cabinet_front_top_open_rside.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
textures/celevator_cabinet_front_top_open_running.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
textures/celevator_cabinet_front_top_open_stopped.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
textures/celevator_cabinet_sides.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
textures/celevator_callbutton_down.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
textures/celevator_callbutton_light.png
Normal file
After Width: | Height: | Size: 222 B |
BIN
textures/celevator_callbutton_panel.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
textures/celevator_callbutton_up.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
textures/celevator_drive_front.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
textures/celevator_fs_bg.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
textures/celevator_logo.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
textures/celevator_menu_arrow.png
Normal file
After Width: | Height: | Size: 676 B |
BIN
textures/celevator_toggle_switch.png
Normal file
After Width: | Height: | Size: 681 B |