celevator-cd2025/drive_entity.lua
cheapie 90e7f15112 Allow changing inspection speed
Can be set to 0.1-0.7m/s, cannot be higher than contract speed
2024-05-14 17:42:12 -05:00

1035 lines
37 KiB
Lua

celevator.drives.entity = {
name = "Drive",
description = "Normal entity-based drive",
nname = "celevator:drive",
buzzsoundhandles = {},
movementsoundhandles = {},
movementsoundstate = {},
carsoundhandles = {},
carsoundstate = {},
entityinfo = {},
sheaverefs = {},
}
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
local state = meta:get_string("state")
if state == "running" and vel > 0 then
status = string.format("Running: Up, %0.02f m/s",vel)
elseif state == "running" and vel < 0 then
status = string.format("Running: Down, %0.02f m/s",math.abs(vel))
elseif state == "fakerunning" and vel > 0 then
status = string.format("Running (simulated): Up, %0.02f m/s",vel)
elseif state == "fakerunning" and vel < 0 then
status = string.format("Running (simulated): Down, %0.02f m/s",math.abs(vel))
end
meta:set_string("infotext",string.format("Drive - %s - Position: %0.02f m",status,apos))
end
local function playbuzz(pos)
local hash = minetest.hash_node_position(pos)
if celevator.drives.entity.buzzsoundhandles[hash] == "cancel" then return end
celevator.drives.entity.buzzsoundhandles[hash] = minetest.sound_play("celevator_drive_run",{
pos = pos,
loop = true,
gain = 0.2,
})
end
local function startbuzz(pos)
local hash = minetest.hash_node_position(pos)
if celevator.drives.entity.buzzsoundhandles[hash] == "cancel" then
celevator.drives.entity.buzzsoundhandles[hash] = nil
return
end
if celevator.drives.entity.buzzsoundhandles[hash] then return end
celevator.drives.entity.buzzsoundhandles[hash] = "pending"
minetest.after(0.5,playbuzz,pos)
end
local function stopbuzz(pos)
local hash = minetest.hash_node_position(pos)
if not celevator.drives.entity.buzzsoundhandles[hash] then return end
if celevator.drives.entity.buzzsoundhandles[hash] == "pending" then
celevator.drives.entity.buzzsoundhandles[hash] = "cancel"
end
if type(celevator.drives.entity.buzzsoundhandles[hash]) ~= "string" then
minetest.sound_stop(celevator.drives.entity.buzzsoundhandles[hash])
celevator.drives.entity.buzzsoundhandles[hash] = nil
end
end
local function motorsound(pos,newstate)
local hash = minetest.hash_node_position(pos)
local oldstate = celevator.drives.entity.movementsoundstate[hash]
oldstate = oldstate or "idle"
if oldstate == newstate then return end
local carid = minetest.get_meta(pos):get_int("carid")
local carinfo = minetest.deserialize(celevator.storage:get_string(string.format("car%d",carid)))
if not (carinfo and carinfo.machinepos) then return end
local oldhandle = celevator.drives.entity.movementsoundhandles[hash]
if newstate == "slow" then
if oldstate == "idle" then
minetest.sound_play("celevator_brake_release",{
pos = carinfo.machinepos,
gain = 1,
},true)
celevator.drives.entity.movementsoundhandles[hash] = minetest.sound_play("celevator_motor_slow",{
pos = carinfo.machinepos,
loop = true,
gain = 1,
})
elseif oldstate == "accel" or oldstate == "fast" or oldstate == "decel" then
if oldhandle then minetest.sound_stop(oldhandle) end
celevator.drives.entity.movementsoundhandles[hash] = minetest.sound_play("celevator_motor_slow",{
pos = carinfo.machinepos,
loop = true,
gain = 1,
})
end
elseif newstate == "accel" then
if oldhandle then minetest.sound_stop(oldhandle) end
celevator.drives.entity.movementsoundhandles[hash] = minetest.sound_play("celevator_motor_accel",{
pos = carinfo.machinepos,
gain = 1,
})
elseif newstate == "fast" then
if oldhandle then minetest.sound_stop(oldhandle) end
celevator.drives.entity.movementsoundhandles[hash] = minetest.sound_play("celevator_motor_fast",{
pos = carinfo.machinepos,
loop = true,
gain = 1,
})
elseif newstate == "decel" then
if oldhandle then minetest.sound_stop(oldhandle) end
celevator.drives.entity.movementsoundhandles[hash] = minetest.sound_play("celevator_motor_decel",{
pos = carinfo.machinepos,
gain = 1,
})
elseif newstate == "idle" then
if oldhandle then minetest.sound_stop(oldhandle) end
minetest.sound_play("celevator_brake_apply",{
pos = carinfo.machinepos,
gain = 1,
},true)
end
celevator.drives.entity.movementsoundstate[hash] = newstate
end
local function carsound(pos,newstate,speed)
if speed < 0.5 then return end
local hash = minetest.hash_node_position(pos)
local oldstate = celevator.drives.entity.carsoundstate[hash]
oldstate = oldstate or "idle"
if oldstate == newstate then return end
if not celevator.drives.entity.entityinfo[hash] then return end
local eref = celevator.drives.entity.entityinfo[hash].handles[1]
if not eref:get_pos() then return end
local oldhandle = celevator.drives.entity.carsoundhandles[hash]
local gain = math.min(1,speed/6)
if newstate == "accel" then
if oldhandle then minetest.sound_stop(oldhandle) end
celevator.drives.entity.carsoundhandles[hash] = minetest.sound_play("celevator_car_start",{
object = eref,
gain = gain,
})
minetest.after(3,function()
if celevator.drives.entity.carsoundstate[hash] == "accel" then
carsound(pos,"run",speed)
end
end)
elseif newstate == "run" then
if oldhandle then minetest.sound_stop(oldhandle) end
celevator.drives.entity.carsoundhandles[hash] = minetest.sound_play("celevator_car_run",{
object = eref,
loop = true,
gain = gain,
})
elseif newstate == "decel" then
if oldhandle then minetest.sound_stop(oldhandle) end
celevator.drives.entity.carsoundhandles[hash] = minetest.sound_play("celevator_car_stop",{
object = eref,
gain = gain,
})
elseif newstate == "stopped" then
if oldhandle then minetest.sound_stop(oldhandle) end
end
celevator.drives.entity.carsoundstate[hash] = newstate
end
local function compareexchangesound(pos,compare,new)
local hash = minetest.hash_node_position(pos)
local oldstate = celevator.drives.entity.movementsoundstate[hash]
if oldstate == compare then
motorsound(pos,new)
end
end
local function accelsound(pos)
motorsound(pos,"slow")
minetest.after(1,compareexchangesound,pos,"slow","accel")
minetest.after(4,compareexchangesound,pos,"accel","fast")
end
local function decelsound(pos)
motorsound(pos,"decel")
minetest.after(2,compareexchangesound,pos,"decel","slow")
end
minetest.register_node("celevator:drive",{
description = "Elevator "..celevator.drives.entity.name,
groups = {
cracky = 1,
_celevator_drive = 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 = "entity",
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")
meta:set_string("state","uninit")
meta:set_string("startpos","0")
meta:set_string("doorstate","closed")
meta:mark_as_private({"apos","dpos","vel","maxvel","state","startpos","doorstate"})
update_ui(pos)
end,
on_destruct = stopbuzz,
})
minetest.register_entity("celevator:car_moving",{
initial_properties = {
visual = "wielditem",
visual_size = vector.new(0.667,0.667,0.667),
wield_item = "default:dirt",
static_save = false,
glow = minetest.LIGHT_MAX,
pointable = false,
},
})
function celevator.drives.entity.gathercar(pos,yaw,nodes)
if not nodes then nodes = {} end
local hash = minetest.hash_node_position(pos)
if nodes[hash] then return nodes end
nodes[hash] = true
if minetest.get_item_group(celevator.get_node(pos).name,"_connects_xp") == 1 then
celevator.drives.entity.gathercar(vector.add(pos,vector.rotate_around_axis(vector.new(1,0,0),vector.new(0,1,0),yaw)),yaw,nodes)
end
if minetest.get_item_group(celevator.get_node(pos).name,"_connects_xm") == 1 then
celevator.drives.entity.gathercar(vector.add(pos,vector.rotate_around_axis(vector.new(-1,0,0),vector.new(0,1,0),yaw)),yaw,nodes)
end
if minetest.get_item_group(celevator.get_node(pos).name,"_connects_yp") == 1 then
celevator.drives.entity.gathercar(vector.add(pos,vector.new(0,1,0)),yaw,nodes)
end
if minetest.get_item_group(celevator.get_node(pos).name,"_connects_ym") == 1 then
celevator.drives.entity.gathercar(vector.add(pos,vector.new(0,-1,0)),yaw,nodes)
end
if minetest.get_item_group(celevator.get_node(pos).name,"_connects_zp") == 1 then
celevator.drives.entity.gathercar(vector.add(pos,vector.rotate_around_axis(vector.new(0,0,1),vector.new(0,1,0),yaw)),yaw,nodes)
end
if minetest.get_item_group(celevator.get_node(pos).name,"_connects_zm") == 1 then
celevator.drives.entity.gathercar(vector.add(pos,vector.rotate_around_axis(vector.new(0,0,-1),vector.new(0,1,0),yaw)),yaw,nodes)
end
return nodes
end
function celevator.drives.entity.nodestoentities(nodes,ename)
local refs = {}
for _,pos in ipairs(nodes) do
local node = celevator.get_node(pos)
local attach = minetest.get_objects_inside_radius(pos,0.9)
local eref = minetest.add_entity(pos,(ename or "celevator:car_moving"))
eref:set_properties({
wield_item = node.name,
})
eref:set_yaw(minetest.dir_to_yaw(minetest.fourdir_to_dir(node.param2)))
table.insert(refs,eref)
if node.name == "celevator:car_021" or node.name == "celevator:car_122" then
local toppos = vector.add(pos,vector.new(0,1,0))
local topattach = minetest.get_objects_inside_radius(toppos,0.75)
for _,ref in pairs(topattach) do
table.insert(attach,ref)
end
end
if not ename then --If ename is set, something other than the car is moving
for _,attachref in ipairs(attach) do
local included = {
["celevator:incar_pi_entity"] = true,
["celevator:car_top_box"] = true,
["celevator:car_door"] = true,
["celevator:tapehead"] = true,
}
if attachref:get_luaentity() and included[attachref:get_luaentity().name] then
table.insert(refs,attachref)
else
local attachpos = attachref:get_pos()
local basepos = eref:get_pos()
local attachoffset = vector.multiply(vector.subtract(attachpos,basepos),30)
attachoffset = vector.rotate_around_axis(attachoffset,vector.new(0,-1,0),eref:get_yaw())
attachref:set_attach(eref,"",attachoffset)
end
end
end
minetest.remove_node(pos)
end
return refs
end
function celevator.drives.entity.entitiestonodes(refs,carid)
local ok = true
for _,eref in ipairs(refs) do
local pos = eref:get_pos()
local top = false
local ename = eref:get_luaentity() and eref:get_luaentity().name
if pos and (ename == "celevator:car_moving" or ename == "celevator:hwdoor_moving") then
pos = vector.round(pos)
local node = {
name = eref:get_properties().wield_item,
param2 = minetest.dir_to_fourdir(minetest.yaw_to_dir(eref:get_yaw()))
}
if minetest.get_item_group(eref:get_properties().wield_item,"_connects_yp") ~= 1 then top = true end
minetest.set_node(pos,node)
eref:remove()
if carid then celevator.get_meta(pos):set_int("carid",carid) end
elseif pos and ename == "celevator:incar_pi_entity" then
pos = vector.new(pos.x,math.floor(pos.y+0.5),pos.z)
eref:set_pos(pos)
elseif not ok then
eref:remove()
else
if not pos then ok = false end
end
if pos and ename == "celevator:car_moving" then
local rounded = {
["celevator:car_top_box"] = true,
["celevator:car_door"] = true,
["celevator:tapehead"] = true,
}
for _,i in ipairs(minetest.get_objects_inside_radius(pos,0.9)) do
i:set_velocity(vector.new(0,0,0))
if i:is_player() then
local ppos = i:get_pos()
ppos.y=ppos.y-0.4
if top then ppos.y = ppos.y+1.1 end
i:set_pos(ppos)
minetest.after(0.5,i.set_pos,i,ppos)
elseif i:get_luaentity() and rounded[i:get_luaentity().name] then
local epos = i:get_pos()
epos.y = math.floor(epos.y+0.5)
if i:get_luaentity() and i:get_luaentity().name == "celevator:car_top_box" then
epos.y = epos.y+0.1
end
i:set_pos(epos)
end
end
end
end
return ok
end
function celevator.drives.entity.step(dtime)
local entitydrives_running = minetest.deserialize(celevator.storage:get_string("entitydrives_running")) or {}
local save = false
for i,hash in ipairs(entitydrives_running) do
save = true
local pos = minetest.get_position_from_hash(hash)
local node = celevator.get_node(pos)
local sound = false
if node.name == "ignore" then
minetest.forceload_block(pos,true)
elseif node.name ~= "celevator:drive" then
table.remove(entitydrives_running,i)
else
local meta = celevator.get_meta(pos)
local carid = meta:get_int("carid")
local state = meta:get_string("state")
if not (state == "running" or state == "start" or state == "fakerunning") then
table.remove(entitydrives_running,i)
else
local dpos = tonumber(meta:get_string("dpos")) or 0
local maxvel = tonumber(meta:get_string("maxvel")) or 0.2
local startpos = tonumber(meta:get_string("startpos")) or 0
local inspection = meta:get_int("inspection") == 1
local origin = minetest.string_to_pos(meta:get_string("origin"))
if not origin then
minetest.log("error","[celevator] [entity drive] Invalid origin for drive at "..minetest.pos_to_string(pos))
meta:set_string("fault","badorigin")
table.remove(entitydrives_running,i)
return
end
if state == "start" then
if math.abs(dpos-startpos) > 0.1 then
sound = true
if not inspection then
accelsound(pos)
else
motorsound(pos,"slow")
end
end
local startv = vector.add(origin,vector.new(0,startpos,0))
local hashes = celevator.drives.entity.gathercar(startv,minetest.dir_to_yaw(minetest.fourdir_to_dir(celevator.get_node(startv).param2)))
local nodes = {}
for carhash in pairs(hashes) do
local carpos = minetest.get_position_from_hash(carhash)
if vector.equals(startv,carpos) then
table.insert(nodes,1,carpos) --0,0,0 node must be first in the list
else
table.insert(nodes,carpos)
end
end
local carparam2 = celevator.get_node(nodes[1]).param2
meta:set_int("carparam2",carparam2)
local handles = celevator.drives.entity.nodestoentities(nodes)
celevator.drives.entity.entityinfo[hash] = {
handles = handles,
}
carsound(pos,"accel",maxvel)
meta:set_string("state","running")
celevator.drives.entity.sheavetoentity(carid)
elseif state == "running" then
if not celevator.drives.entity.entityinfo[hash] then
meta:set_string("state","fakerunning")
return
end
local handles = celevator.drives.entity.entityinfo[hash].handles
if (not handles) or (not handles[1]:get_pos()) then
meta:set_string("state","fakerunning")
return
end
local apos = handles[1]:get_pos().y - origin.y
local sheaverefs = celevator.drives.entity.sheaverefs[carid]
if sheaverefs and sheaverefs[1] then
local rotation = sheaverefs[1]:get_rotation()
if rotation then
rotation.z = math.pi*apos*-1
sheaverefs[1]:set_rotation(rotation)
end
end
local dremain = math.abs(dpos-apos)
local dmoved = math.abs(apos-startpos)
local vel
if dremain < 0.01 then
vel = 0
meta:set_string("state","stopped")
motorsound(pos,"idle")
celevator.drives.entity.sheavetonode(carid)
local ok = celevator.drives.entity.entitiestonodes(handles,carid)
if not ok then
local carparam2 = meta:get_int("carparam2")
celevator.car.spawncar(vector.round(vector.add(origin,vector.new(0,apos,0))),minetest.dir_to_yaw(minetest.fourdir_to_dir(carparam2)),carid)
end
apos = math.floor(apos+0.5)
minetest.after(0.25,celevator.drives.entity.updatecopformspec,pos)
elseif dremain < 0.2 and not inspection then
vel = 0.2
elseif dremain < 2*maxvel and dremain < dmoved and not inspection then
vel = math.min(dremain,maxvel)
if celevator.drives.entity.movementsoundstate[hash] == "fast" or celevator.drives.entity.movementsoundstate[hash] == "accel" then
decelsound(pos)
carsound(pos,"decel",maxvel)
end
elseif dmoved+0.1 > maxvel or inspection then
vel = maxvel
else
vel = dmoved+0.1
end
if dpos < apos then vel = 0-vel end
for _,eref in ipairs(handles) do
eref:set_velocity(vector.new(0,vel,0))
end
meta:set_string("apos",tostring(apos))
sound = vel ~= 0
meta:set_string("vel",tostring(vel))
elseif state == "fakerunning" then
celevator.drives.entity.carsoundstate[hash] = "stopped"
local apos = tonumber(meta:get_string("apos")) or 0
local sheaverefs = celevator.drives.entity.sheaverefs[carid]
if sheaverefs and sheaverefs[1] then
local rotation = sheaverefs[1]:get_rotation()
if rotation then
rotation.z = math.pi*apos*-1
sheaverefs[1]:set_rotation(rotation)
end
end
local dremain = math.abs(dpos-apos)
local dmoved = math.abs(apos-startpos)
local vel
if dremain < 0.01 then
vel = 0
meta:set_string("state","stopped")
motorsound(pos,"idle")
celevator.drives.entity.sheavetonode(carid)
local carparam2 = meta:get_int("carparam2")
celevator.car.spawncar(vector.round(vector.add(origin,vector.new(0,apos,0))),minetest.dir_to_yaw(minetest.fourdir_to_dir(carparam2)),carid)
apos = math.floor(apos+0.5)
minetest.after(0.25,celevator.drives.entity.updatecopformspec,pos)
elseif dremain < 0.2 and not inspection then
vel = 0.2
elseif dremain < 2*maxvel and dremain < dmoved and not inspection then
vel = math.min(dremain,maxvel)
if celevator.drives.entity.movementsoundstate[hash] == "fast" or celevator.drives.entity.movementsoundstate[hash] == "accel" then
decelsound(pos)
end
elseif dmoved+0.1 > maxvel or inspection then
vel = maxvel
else
vel = dmoved+0.1
end
if dpos < apos then vel = 0-vel end
apos = apos+(vel*dtime)
meta:set_string("apos",tostring(apos))
sound = vel ~= 0
meta:set_string("vel",tostring(vel))
end
end
end
update_ui(pos)
if sound then
startbuzz(pos)
else
stopbuzz(pos)
end
end
if save then
celevator.storage:set_string("entitydrives_running",minetest.serialize(entitydrives_running))
end
end
minetest.register_globalstep(celevator.drives.entity.step)
function celevator.drives.entity.moveto(pos,target,inspection)
local meta = celevator.get_meta(pos)
meta:mark_as_private({"apos","dpos","vel","maxvel","state","startpos","doorstate"})
local carid = celevator.get_meta(pos):get_int("carid")
local carinfo = minetest.deserialize(celevator.storage:get_string(string.format("car%d",carid)))
if not (carinfo and carinfo.machinepos) then return end
local origin = minetest.string_to_pos(meta:get_string("origin"))
if not origin then
minetest.log("error","[celevator] [entity drive] Invalid origin for drive at "..minetest.pos_to_string(pos))
meta:set_string("fault","badorigin")
return
end
if target < 0 or origin.y + target > (carinfo.machinepos.y-3) then
meta:set_string("fault","outofbounds")
return
end
if meta:get_string("state") ~= "stopped" then
local apos = tonumber(meta:get_string("apos"))
local vel = tonumber(meta:get_string("vel"))
if vel > 0 then
if target < apos+(vel*2) then return end
elseif vel < 0 then
if target > apos-(vel*-2) then return end
else
return
end
end
meta:set_string("dpos",tostring(target))
if meta:get_string("state") == "stopped" then
meta:set_string("state","start")
meta:set_int("inspection",inspection and 1 or 0)
meta:set_string("startpos",meta:get_string("apos"))
local hash = minetest.hash_node_position(pos)
local entitydrives_running = minetest.deserialize(celevator.storage:get_string("entitydrives_running")) or {}
local running = false
for _,dhash in ipairs(entitydrives_running) do
if hash == dhash then
running = true
break
end
end
if not running then
table.insert(entitydrives_running,hash)
celevator.storage:set_string("entitydrives_running",minetest.serialize(entitydrives_running))
--Controller needs to see something so it knows the drive is running
local apos = tonumber(meta:get_string("apos"))
if apos and apos > target then
meta:set_string("vel","-0.0001")
else
meta:set_string("vel","0.0001")
end
end
end
end
function celevator.drives.entity.resetpos(pos)
celevator.drives.entity.moveto(pos,0)
end
function celevator.drives.entity.estop(pos)
local meta = celevator.get_meta(pos)
if meta:get_string("state") ~= "running" then return end
local apos = math.floor(tonumber(meta:get_string("apos"))+0.5)
meta:set_string("dpos",tostring(apos))
meta:set_string("apos",tostring(apos))
local hash = minetest.hash_node_position(pos)
local handles = celevator.drives.entity.entityinfo[hash].handles
meta:set_string("state","stopped")
meta:set_string("vel","0")
local carid = meta:get_int("carid")
celevator.drives.entity.entitiestonodes(handles,carid)
stopbuzz(pos)
motorsound(pos,"idle")
if carid ~= 0 then celevator.drives.entity.sheavetonode(carid) end
minetest.after(0.25,celevator.drives.entity.updatecopformspec,pos)
end
function celevator.drives.entity.setmaxvel(pos,maxvel)
local meta = celevator.get_meta(pos)
meta:set_string("maxvel",tostring(maxvel))
end
function celevator.drives.entity.rezero(pos)
celevator.drives.entity.moveto(pos,0)
end
function celevator.drives.entity.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.entity.get_status(pos,true)
elseif node.name ~= "celevator:drive" then
minetest.log("error","[celevator] [entity drive] Could not load drive status at "..minetest.pos_to_string(pos))
return {fault = "metaload"}
else
local meta = celevator.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.state = meta:get_string("state")
ret.doorstate = meta:get_string("doorstate")
ret.fault = meta:get_string("fault")
ret.neareststop = ret.apos+(ret.vel*2)
if ret.fault == "" then ret.fault = nil end
return ret
end
end
function celevator.drives.entity.movedoors(drivepos,direction,nudge)
local drivehash = minetest.hash_node_position(drivepos)
local entitydrives_running = minetest.deserialize(celevator.storage:get_string("entitydrives_running")) or {}
local drivemeta = celevator.get_meta(drivepos)
for _,hash in pairs(entitydrives_running) do
if drivehash == hash then
minetest.log("error","[celevator] [entity drive] Attempted to open doors while drive at "..minetest.pos_to_string(drivepos).." was still moving")
drivemeta:set_string("fault","doorinterlock")
return
end
end
local origin = minetest.string_to_pos(drivemeta:get_string("origin"))
if not origin then
minetest.log("error","[celevator] [entity drive] Invalid origin for drive at "..minetest.pos_to_string(drivepos))
drivemeta:set_string("fault","badorigin")
return
end
local apos = tonumber(drivemeta:get_string("apos")) or 0
local carpos = vector.add(origin,vector.new(0,apos,0))
local carnode = celevator.get_node(carpos)
local hwdoorpos = vector.add(carpos,vector.rotate_around_axis(minetest.fourdir_to_dir(carnode.param2),vector.new(0,1,0),math.pi))
if direction == "open" and (minetest.get_item_group(celevator.get_node(hwdoorpos).name,"_celevator_hwdoor_root") == 1 or drivemeta:get_string("doorstate") == "closing") then
celevator.doors.hwopen(hwdoorpos,drivepos)
drivemeta:set_string("doorstate","opening")
elseif direction == "close" and celevator.get_node(hwdoorpos).name == "celevator:hwdoor_placeholder" then
celevator.doors.hwclose(hwdoorpos,drivepos,nudge)
drivemeta:set_string("doorstate","closing")
end
end
function celevator.drives.entity.resetfault(pos)
celevator.get_meta(pos):set_string("fault","")
end
function celevator.drives.entity.pibeep(drivepos)
local drivemeta = celevator.get_meta(drivepos)
local origin = minetest.string_to_pos(drivemeta:get_string("origin"))
if not origin then
minetest.log("error","[celevator] [entity drive] Invalid origin for drive at "..minetest.pos_to_string(drivepos))
drivemeta:set_string("fault","badorigin")
return
end
local apos = tonumber(drivemeta:get_string("apos")) or 0
local beeppos = vector.add(origin,vector.new(0,apos+2,0))
minetest.sound_play("celevator_pi_beep",{
pos = beeppos,
gain = 1,
},true)
end
local function carsearch(pos)
for i=1,500,1 do
local searchpos = vector.subtract(pos,vector.new(0,i,0))
local node = celevator.get_node(searchpos)
if minetest.get_item_group(node.name,"_celevator_car") == 1 then
local yaw = minetest.dir_to_yaw(minetest.fourdir_to_dir(node.param2))
local offsettext = minetest.registered_nodes[node.name]._position
local xoffset = tonumber(string.sub(offsettext,1,1))
local yoffset = tonumber(string.sub(offsettext,2,2))
local zoffset = tonumber(string.sub(offsettext,3,3))
local offset = vector.new(xoffset,yoffset,zoffset)
offset = vector.rotate_around_axis(offset,vector.new(0,1,0),yaw)
return vector.subtract(searchpos,offset)
end
end
end
local function updatecarpos(pos)
local meta = celevator.get_meta(pos)
if meta:get_int("carid") == 0 then return end
local carpos = carsearch(pos)
if carpos then
meta:set_string("origin",minetest.pos_to_string(carpos))
celevator.get_meta(carpos):set_string("machinepos",minetest.pos_to_string(pos))
meta:set_string("infotext",string.format("Using car with origin %s",minetest.pos_to_string(carpos)))
local carid = meta:get_int("carid")
local carinfo = minetest.deserialize(celevator.storage:get_string(string.format("car%d",carid)))
if not carinfo then return end
carinfo.origin = carpos
celevator.storage:set_string(string.format("car%d",carid),minetest.serialize(carinfo))
local drivepos = celevator.controller.finddrive(carinfo.controllerpos)
if drivepos then
local drivemeta = celevator.get_meta(drivepos)
if drivemeta:get_string("state") == "uninit" then
drivemeta:set_string("origin",minetest.pos_to_string(carpos))
drivemeta:set_string("state","stopped")
drivemeta:set_int("carid",carid)
end
end
local caryaw = minetest.dir_to_yaw(minetest.fourdir_to_dir(celevator.get_node(carpos).param2))
local carnodes = celevator.drives.entity.gathercar(carpos,caryaw)
for hash in pairs(carnodes) do
local carmeta = celevator.get_meta(minetest.get_position_from_hash(hash))
carmeta:set_int("carid",carid)
end
else
meta:set_string("infotext","No car found! Punch to try again")
end
end
minetest.register_node("celevator:machine",{
description = "Elevator Hoist Machine",
groups = {
dig_immediate = 2,
_celevator_machine = 1,
},
paramtype = "light",
paramtype2 = "4dir",
tiles = {
"celevator_machine_top.png",
"celevator_machine_top.png",
"celevator_machine_sides.png",
"celevator_machine_sides.png",
"celevator_machine_front.png",
"celevator_machine_front.png",
},
inventory_image = "celevator_machine_inventory.png",
wield_image = "celevator_machine_inventory.png",
wield_scale = vector.new(1,1,3),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.3,-0.5,-0.2,0.3,0.4,0.2}, -- Main body
{-0.2,0.4,-0.2,0.2,0.5,0.2}, -- Top of circle
{-0.4,-0.1,-0.2,-0.3,0.3,0.2}, -- Left of circle
{0.3,-0.1,-0.2,0.4,0.3,0.2}, -- Right of circle
{-0.42,0.075,-0.22,0.42,0.125,0.22}, -- Sealing flanges
{0.3,-0.3,-0.1,0.35,-0.1,0.1}, -- Bearing cap opposite motor
{-0.35,-0.3,-0.1,-0.3,-0.1,0.1}, -- Bearing cap on motor side
{-0.1,0,-0.5,0.1,0.2,-0.2}, -- Shaft to sheave
{-0.15,-0.05,0.2,0.15,0.25,0.25}, -- Bearing cap opposite sheave
{-0.15,-0.05,-0.25,0.15,0.25,-0.2}, -- Bearing cap on sheave side
{-0.5,-0.25,-0.05,-0.35,-0.15,0.05} -- Shaft from motor
},
},
selection_box = {
type = "fixed",
fixed = {
{-1.5,-0.5,-0.5,0.5,0.5,0.5},
{-0.5,-0.5,-0.8,0.5,0.5,-0.5},
},
},
after_place_node = function(pos,player)
if not player:is_player() then
minetest.remove_node(pos)
return true
end
local newnode = minetest.get_node(pos)
local facedir = minetest.dir_to_yaw(minetest.fourdir_to_dir(newnode.param2))
local motorpos = vector.add(pos,vector.rotate_around_axis(vector.new(-1,0,0),vector.new(0,1,0),facedir))
local motorreplaces = minetest.get_node(motorpos).name
local sheavepos = vector.add(pos,vector.rotate_around_axis(vector.new(0,0,-1),vector.new(0,1,0),facedir))
local sheavereplaces = minetest.get_node(sheavepos).name
local name = player:get_player_name()
if not (minetest.registered_nodes[motorreplaces] and minetest.registered_nodes[motorreplaces].buildable_to) then
minetest.chat_send_player(name,"Can't place machine here - no room for the motor (to the left)!")
minetest.remove_node(pos)
return true
end
if minetest.is_protected(motorpos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.chat_send_player(name,"Can't place machine here - space for the motor (to the left) is protected!")
minetest.record_protection_violation(motorpos,name)
minetest.remove_node(pos)
return true
end
if not (minetest.registered_nodes[sheavereplaces] and minetest.registered_nodes[sheavereplaces].buildable_to) then
minetest.chat_send_player(name,"Can't place machine here - no room for the sheave (in front)!")
minetest.remove_node(pos)
return true
end
if minetest.is_protected(sheavepos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
minetest.chat_send_player(name,"Can't place machine here - space for the sheave (in front) is protected!")
minetest.record_protection_violation(sheavepos,name)
minetest.remove_node(pos)
return true
end
local meta = minetest.get_meta(pos)
meta:set_string("formspec","formspec_version[7]size[8,5]field[0.5,0.5;7,1;carid;Car ID;]button[3,3.5;2,1;save;Save]")
minetest.set_node(motorpos,{name="celevator:motor",param2=newnode.param2})
minetest.set_node(sheavepos,{name="celevator:sheave",param2=newnode.param2})
end,
after_dig_node = function(pos,node)
local facedir = minetest.dir_to_yaw(minetest.fourdir_to_dir(node.param2))
local motorpos = vector.add(pos,vector.rotate_around_axis(vector.new(-1,0,0),vector.new(0,1,0),facedir))
if minetest.get_node(motorpos).name == "celevator:motor" then
minetest.remove_node(motorpos)
end
local sheavepos = vector.add(pos,vector.rotate_around_axis(vector.new(0,0,-1),vector.new(0,1,0),facedir))
if minetest.get_node(sheavepos).name == "celevator:sheave" then
minetest.remove_node(sheavepos)
end
local erefs = minetest.get_objects_inside_radius(sheavepos,0.5)
for _,ref in pairs(erefs) do
if ref:get_luaentity() and ref:get_luaentity().name == "celevator:sheave_moving" then
ref:remove()
end
end
end,
on_punch = function(pos)
local meta = minetest.get_meta(pos)
if not minetest.string_to_pos(meta:get_string("origin")) then
updatecarpos(pos)
end
end,
on_receive_fields = function(pos,_,fields)
if tonumber(fields.carid) then
local carid = tonumber(fields.carid)
local carinfo = minetest.deserialize(celevator.storage:get_string(string.format("car%d",carid)))
if not carinfo then return end
local oldmachinepos = carinfo.machinepos
if oldmachinepos then
local oldmachine = celevator.get_node(oldmachinepos)
if oldmachine.name == "celevator:machine" then
return
end
end
carinfo.machinepos = pos
celevator.storage:set_string(string.format("car%d",carid),minetest.serialize(carinfo))
local meta = minetest.get_meta(pos)
meta:set_int("carid",carid)
meta:set_string("formspec","")
updatecarpos(pos)
end
end,
})
minetest.register_node("celevator:motor",{
description = "Hoist Motor (you hacker you!)",
groups = {
not_in_creative_inventory = 1,
},
drop = "",
paramtype = "light",
paramtype2 = "4dir",
tiles = {
"celevator_machine_top.png",
"celevator_machine_top.png",
"celevator_motor_sides.png",
"celevator_motor_sides.png",
"celevator_motor_back.png",
"celevator_motor_front.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.5,-0.5,-0.3,0.1,0.1,0.3}, -- Motor body
{0.1,-0.25,-0.05,0.5,-0.15,0.05}, -- Shaft
{0.3,-0.4,-0.2,0.35,0,0.2}, -- Brake disc
{0.275,-0.3,-0.1,0.375,-0.1,0.1}, -- Brake disc clamp
{0.2,-0.5,0.15,0.45,0.1,0.3}, -- Brake housing
{-0.4,0.1,-0.2,0,0.3,0.2}, -- Junction box
},
},
selection_box = {
type = "fixed",
fixed = {},
},
})
minetest.register_node("celevator:sheave",{
description = "Sheave (you hacker you!)",
groups = {
not_in_creative_inventory = 1,
},
drop = "",
paramtype = "light",
paramtype2 = "4dir",
tiles = {
"celevator_sheave_sides.png^[transformR90",
"celevator_sheave_sides.png^[transformR270",
"celevator_sheave_sides.png",
"celevator_sheave_sides.png^[transformR180",
"celevator_sheave_front.png",
"celevator_sheave_front.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.3,-0.2,0.2,0.3,0.4,0.5},
{-0.4,-0.1,0.2,-0.3,0.3,0.5},
{0.3,-0.1,0.2,0.4,0.3,0.5},
{-0.2,0.4,0.2,0.2,0.5,0.5},
{-0.2,-0.3,0.2,0.2,-0.2,0.5},
},
},
selection_box = {
type = "fixed",
fixed = {},
},
})
minetest.register_node("celevator:sheave_centered",{
description = "Centered Sheave (you hacker you!)",
groups = {
not_in_creative_inventory = 1,
},
drop = "",
paramtype = "light",
paramtype2 = "4dir",
tiles = {
"celevator_sheave_sides.png^[transformR90",
"celevator_sheave_sides.png^[transformR270",
"celevator_sheave_sides.png",
"celevator_sheave_sides.png^[transformR180",
"celevator_sheave_front_centered.png",
"celevator_sheave_front_centered.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.3,-0.3,0.2,0.3,0.3,0.5},
{-0.4,-0.2,0.2,-0.3,0.2,0.5},
{0.3,-0.2,0.2,0.4,0.2,0.5},
{-0.2,0.3,0.2,0.2,0.4,0.5},
{-0.2,-0.4,0.2,0.2,-0.3,0.5},
},
},
selection_box = {
type = "fixed",
fixed = {},
},
})
minetest.register_entity("celevator:sheave_moving",{
initial_properties = {
visual = "wielditem",
visual_size = vector.new(0.667,0.667,0.667),
wield_item = "celevator:sheave_centered",
static_save = false,
pointable = false,
},
})
function celevator.drives.entity.sheavetoentity(carid)
local carinfo = minetest.deserialize(celevator.storage:get_string(string.format("car%d",carid)))
if not (carinfo and carinfo.machinepos) then return end
local dir = minetest.fourdir_to_dir(celevator.get_node(carinfo.machinepos).param2)
local pos = vector.add(carinfo.machinepos,vector.multiply(dir,-1))
minetest.set_node(pos,{
name = "celevator:sheave",
param2 = minetest.dir_to_fourdir(dir),
})
local sheaverefs = celevator.drives.entity.nodestoentities({pos},"celevator:sheave_moving")
celevator.drives.entity.sheaverefs[carid] = sheaverefs
sheaverefs[1]:set_properties({wield_item = "celevator:sheave_centered"})
sheaverefs[1]:set_pos(vector.add(pos,vector.new(0,0.1,0)))
end
function celevator.drives.entity.sheavetonode(carid)
local carinfo = minetest.deserialize(celevator.storage:get_string(string.format("car%d",carid)))
if not (carinfo and carinfo.machinepos) then return end
local dir = minetest.fourdir_to_dir(celevator.get_node(carinfo.machinepos).param2)
local pos = vector.add(carinfo.machinepos,vector.multiply(dir,-1))
local erefs = celevator.drives.entity.sheaverefs[carid]
if erefs and erefs[1] then
erefs[1]:remove()
end
minetest.set_node(pos,{
name = "celevator:sheave",
param2 = minetest.dir_to_fourdir(dir),
})
end
function celevator.drives.entity.updatecopformspec(drivepos)
local entitydrives_running = minetest.deserialize(celevator.storage:get_string("entitydrives_running")) or {}
if entitydrives_running[minetest.hash_node_position(drivepos)] then return end
local drivemeta = celevator.get_meta(drivepos)
local carid = drivemeta:get_int("carid")
if carid == 0 then return end
local carinfo = minetest.deserialize(celevator.storage:get_string(string.format("car%d",carid)))
if not carinfo then return end
local copformspec = celevator.get_meta(carinfo.controllerpos):get_string("copformspec")
local switchformspec = celevator.get_meta(carinfo.controllerpos):get_string("switchformspec")
local origin = minetest.string_to_pos(drivemeta:get_string("origin"))
if not origin then
minetest.log("error","[celevator] [entity drive] Invalid origin for drive at "..minetest.pos_to_string(drivepos))
drivemeta:set_string("fault","badorigin")
return
end
local apos = tonumber(drivemeta:get_string("apos")) or 0
if apos == math.floor(apos) then
local carpos = vector.add(origin,vector.new(0,apos,0))
local carnodes = celevator.drives.entity.gathercar(carpos,minetest.dir_to_yaw(minetest.fourdir_to_dir(celevator.get_node(carpos).param2)))
for hash in pairs(carnodes) do
local piecepos = minetest.get_position_from_hash(hash)
local piece = celevator.get_node(piecepos)
if piece.name == "celevator:car_010" then
celevator.get_meta(piecepos):set_string("formspec",copformspec)
elseif piece.name == "celevator:car_000" then
celevator.get_meta(piecepos):set_string("formspec",switchformspec)
end
end
end
end