forked from MTSR/techage_modpack
292 lines
8.0 KiB
Lua
292 lines
8.0 KiB
Lua
--[[
|
|
|
|
Minecart
|
|
========
|
|
|
|
Copyright (C) 2019-2020 Joachim Stolberg
|
|
|
|
MIT
|
|
See license.txt for more information
|
|
|
|
]]--
|
|
|
|
-- for lazy programmers
|
|
local M = minetest.get_meta
|
|
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
|
local S2P = minetest.string_to_pos
|
|
local S = minecart.S
|
|
|
|
local CartsOnRail = minecart.CartsOnRail
|
|
|
|
--
|
|
-- Helper functions
|
|
--
|
|
local function get_object_id(object)
|
|
for id, entity in pairs(minetest.luaentities) do
|
|
if entity.object == object then
|
|
return id
|
|
end
|
|
end
|
|
end
|
|
|
|
local function get_route_key(pos, player_name)
|
|
local pos1 = minetest.find_node_near(pos, 1, {"minecart:buffer"})
|
|
if pos1 then
|
|
local meta = minetest.get_meta(pos1)
|
|
if player_name == nil or player_name == meta:get_string("owner") then
|
|
return P2S(pos1)
|
|
end
|
|
end
|
|
end
|
|
|
|
--
|
|
-- Recording
|
|
--
|
|
function minecart.add_cart_to_monitoring(obj, owner, cargo)
|
|
--print("add_cart_to_monitoring", dump(cargo))
|
|
local self = obj:get_luaentity()
|
|
self.myID = get_object_id(obj)
|
|
self.owner = owner
|
|
local pos = self.object:get_pos()
|
|
CartsOnRail[self.myID] = {
|
|
start_key = get_route_key(pos),
|
|
start_pos = pos,
|
|
stopped = true,
|
|
owner = owner,
|
|
entity_name = self.name,
|
|
cargo = cargo,
|
|
}
|
|
return self.myID
|
|
end
|
|
|
|
function minecart.start_recording(self, pos, vel, puncher)
|
|
-- Player punches cart to start the trip
|
|
if puncher:get_player_name() == self.driver and vector.equals(vel, {x=0, y=0, z=0}) then
|
|
self.start_key = get_route_key(pos, self.driver)
|
|
if self.start_key then
|
|
self.waypoints = {}
|
|
self.junctions = {}
|
|
self.recording = true
|
|
self.next_time = minetest.get_us_time() + 1000000
|
|
minetest.chat_send_player(self.driver, S("[minecart] Start route recording!"))
|
|
end
|
|
end
|
|
end
|
|
|
|
function minecart.store_next_waypoint(self, pos, vel)
|
|
if self.start_key and self.recording and self.driver and
|
|
self.next_time < minetest.get_us_time() then
|
|
self.next_time = minetest.get_us_time() + 1000000
|
|
self.waypoints[#self.waypoints+1] = {P2S(vector.round(pos)), P2S(vector.round(vel))}
|
|
|
|
local dest_pos = get_route_key(pos, self.driver)
|
|
if vector.equals(vel, {x=0, y=0, z=0}) and dest_pos then
|
|
if self.start_key ~= dest_pos then
|
|
local route = {
|
|
waypoints = self.waypoints,
|
|
dest_pos = dest_pos,
|
|
junctions = self.junctions,
|
|
}
|
|
minecart.store_route(self.start_key, route)
|
|
minetest.chat_send_player(self.driver, S("[minecart] Route stored!"))
|
|
else
|
|
minetest.chat_send_player(self.driver, S("[minecart] Recording canceled!"))
|
|
end
|
|
self.recording = false
|
|
self.waypoints = nil
|
|
self.junctions = nil
|
|
end
|
|
elseif self.recording and not self.driver then
|
|
self.recording = false
|
|
self.waypoints = nil
|
|
self.junctions = nil
|
|
end
|
|
end
|
|
|
|
function minecart.set_junction(self, pos, dir, switch_keys)
|
|
local junctions = CartsOnRail[self.myID] and CartsOnRail[self.myID].junctions
|
|
if junctions then
|
|
if self.junctions then
|
|
self.junctions[minetest.pos_to_string(vector.round(pos))] = {dir, switch_keys}
|
|
end
|
|
end
|
|
end
|
|
|
|
function minecart.get_junction(self, pos, dir)
|
|
local junctions = CartsOnRail[self.myID] and CartsOnRail[self.myID].junctions
|
|
if junctions then
|
|
local data = junctions[minetest.pos_to_string(vector.round(pos))]
|
|
if data then
|
|
return data[1], data[2]
|
|
end
|
|
end
|
|
return dir
|
|
end
|
|
|
|
--
|
|
-- Normal operation
|
|
--
|
|
function minecart.start_run(self, pos, vel, driver)
|
|
if vector.equals(vel, {x=0, y=0, z=0}) then
|
|
local start_key = get_route_key(pos)
|
|
if not start_key then
|
|
if driver then -- Punched from inside the cart
|
|
-- Don't start the cart
|
|
self.velocity = {x=0, y=0, z=0}
|
|
minetest.chat_send_player(driver, S("[minecart] Please start at a Railway Buffer!"))
|
|
return
|
|
end
|
|
-- Add also carts without route to be able to restore last pos/vel
|
|
minetest.log("info", "[minecart] Cart "..self.myID.." started.")
|
|
--print("start_run", dump(CartsOnRail[self.myID]))
|
|
CartsOnRail[self.myID].stopped = false
|
|
else -- Add cart to monitoring
|
|
minetest.log("info", "[minecart] Cart "..self.myID.." started.")
|
|
--print("start_run", dump(CartsOnRail[self.myID]))
|
|
local item = CartsOnRail[self.myID]
|
|
item.start_time = minetest.get_gametime()
|
|
item.start_key = start_key
|
|
item.stopped = false
|
|
item.junctions = minecart.get_route(start_key).junctions
|
|
end
|
|
end
|
|
end
|
|
|
|
function minecart.attach_cargo(self, pos)
|
|
local data = CartsOnRail[self.myID]
|
|
if data then
|
|
data.attached_items = {}
|
|
for _, obj_ in pairs(minetest.get_objects_inside_radius(pos, 1)) do
|
|
local entity = obj_:get_luaentity()
|
|
if not obj_:is_player() and entity and entity.name == "__builtin:item" then
|
|
obj_:remove()
|
|
data.attached_items[#data.attached_items + 1] = entity.itemstring
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function minecart.detach_cargo(self, pos, data)
|
|
-- Spawn loaded items again
|
|
if data.attached_items then
|
|
for _,item in ipairs(data.attached_items) do
|
|
minetest.add_item(pos, ItemStack(item))
|
|
end
|
|
end
|
|
end
|
|
|
|
function minecart.stopped(self, pos)
|
|
local data = CartsOnRail[self.myID]
|
|
if data and not data.stopped then
|
|
minecart.detach_cargo(self, pos, data)
|
|
data.stopped = true
|
|
data.start_key = get_route_key(pos)
|
|
data.start_pos = pos
|
|
data.start_time = nil
|
|
minetest.log("info", "[minecart] Cart "..self.myID.." stopped.")
|
|
if self.sound_handle then
|
|
minetest.sound_stop(self.sound_handle)
|
|
end
|
|
return data.cargo or {} -- for node based carts
|
|
end
|
|
end
|
|
|
|
function minecart.on_dig(self)
|
|
if self and self.myID then
|
|
CartsOnRail[self.myID] = nil
|
|
end
|
|
end
|
|
|
|
--
|
|
-- Monitoring
|
|
--
|
|
local function spawn_cart(pos, vel, item)
|
|
-- local node = minetest.get_node(pos)
|
|
-- if not minetest.tValidCarts[node.name] then
|
|
local pos2 = vector.round(pos)
|
|
if carts:is_rail(pos2) or carts:is_rail({x = pos2.x, y = pos2.y-1, z = pos2.z}) then
|
|
local obj = minetest.add_entity(pos, item.entity_name or "minecart:cart", nil)
|
|
obj:set_velocity(vel)
|
|
local id = minecart.add_cart_to_monitoring(obj, item.owner)
|
|
minetest.log("info", "[minecart] Cart "..id.." spawned again.")
|
|
return id
|
|
end
|
|
end
|
|
|
|
local function calc_pos_and_vel(item)
|
|
if item.start_time and item.start_key then
|
|
local run_time = minetest.get_gametime() - item.start_time
|
|
local waypoints = minecart.get_route(item.start_key).waypoints
|
|
local waypoint = waypoints[run_time]
|
|
if waypoint then
|
|
return minetest.string_to_pos(waypoint[1]), minetest.string_to_pos(waypoint[2])
|
|
end
|
|
end
|
|
if item.last_pos then
|
|
return item.last_pos, item.last_vel
|
|
end
|
|
return item.start_pos, {x=0, y=0, z=0}
|
|
end
|
|
|
|
local function monitoring()
|
|
local to_be_added = {}
|
|
for key, item in pairs(CartsOnRail) do
|
|
--print("Cart:", key, P2S(item.start_pos), item.owner)
|
|
if not item.recording then
|
|
local entity = minetest.luaentities[key]
|
|
if entity then -- cart in loaded area
|
|
local pos = entity.object:get_pos()
|
|
local vel = entity.object:get_velocity()
|
|
if not minetest.get_node_or_nil(pos) then -- in unloaded area
|
|
minetest.log("info", "[minecart] Cart "..key.." virtualized.")
|
|
if entity.sound_handle then
|
|
minetest.sound_stop(entity.sound_handle)
|
|
end
|
|
if vector.equals(vel, {x=0, y=0, z=0}) then
|
|
minecart.attach_cargo(entity, pos)
|
|
end
|
|
entity.object:remove()
|
|
end
|
|
-- store last pos from cart without route
|
|
item.last_pos, item.last_vel = pos, vel
|
|
else -- cart in unloaded area
|
|
local pos, vel = calc_pos_and_vel(item)
|
|
if pos and vel then
|
|
if minetest.get_node_or_nil(pos) then -- in loaded area
|
|
local id = spawn_cart(pos, vel, item)
|
|
if id then
|
|
to_be_added[id] = table.copy(item)
|
|
CartsOnRail[key] = nil
|
|
end
|
|
end
|
|
else
|
|
CartsOnRail[key] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- table maintenance
|
|
for key,val in pairs(to_be_added) do
|
|
CartsOnRail[key] = val
|
|
end
|
|
minetest.after(1, monitoring)
|
|
end
|
|
minetest.after(1, monitoring)
|
|
|
|
minecart.calc_pos_and_vel = calc_pos_and_vel
|
|
|
|
--
|
|
-- API function to get a list of cart data with current position and speed.
|
|
--
|
|
function minecart.get_cart_list()
|
|
local tbl = {}
|
|
for id, item in pairs(CartsOnRail) do
|
|
local pos, speed = calc_pos_and_vel(item)
|
|
tbl[#tbl+1] = {pos = pos, speed = speed, id = id}
|
|
end
|
|
return tbl
|
|
end
|
|
|
|
-- minecart.get_route_key(pos, player_name)
|
|
minecart.get_route_key = get_route_key
|