mods that hopefully work for a TechAge-based pack
This commit is contained in:
@@ -0,0 +1,440 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2023 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 P2H = minetest.hash_node_position
|
||||
local H2P = minetest.get_position_from_hash
|
||||
local S = minecart.S
|
||||
|
||||
local tCartsOnRail = minecart.CartsOnRail
|
||||
local Queue = {}
|
||||
local first = 0
|
||||
local last = -1
|
||||
|
||||
local function push(cycle, item)
|
||||
last = last + 1
|
||||
item.cycle = cycle
|
||||
Queue[last] = item
|
||||
end
|
||||
|
||||
local function pop(cycle)
|
||||
if first > last then return end
|
||||
local item = Queue[first]
|
||||
if item.cycle < cycle then
|
||||
Queue[first] = nil -- to allow garbage collection
|
||||
first = first + 1
|
||||
return item
|
||||
end
|
||||
end
|
||||
|
||||
local function is_player_nearby(pos)
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos, 64)) do
|
||||
if object:is_player() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function zombie_to_entity(pos, cart, checkpoint)
|
||||
local vel = {x = 0, y = 0, z = 0}
|
||||
local obj = minecart.add_entitycart(pos, cart.node_name, cart.entity_name,
|
||||
vel, cart.cargo, cart.owner, cart.userID)
|
||||
if obj then
|
||||
local entity = obj:get_luaentity()
|
||||
entity.reenter = checkpoint
|
||||
entity.junctions = cart.junctions
|
||||
entity.is_running = true
|
||||
entity.arrival_time = 0
|
||||
cart.objID = entity.objID
|
||||
end
|
||||
end
|
||||
|
||||
local function get_checkpoint(cart)
|
||||
local cp = cart.checkpoints[cart.idx]
|
||||
if not cp then
|
||||
cart.idx = #cart.checkpoints
|
||||
cp = cart.checkpoints[cart.idx]
|
||||
end
|
||||
local pos = H2P(cp[1])
|
||||
return cp, cart.idx == #cart.checkpoints
|
||||
end
|
||||
|
||||
-- Function returns the cart state ("running" / "stopped") and
|
||||
-- the station name or position string, or if cart is running,
|
||||
-- the distance to the query_pos.
|
||||
local function get_cart_state_and_loc(name, userID, query_pos)
|
||||
if tCartsOnRail[name] and tCartsOnRail[name][userID] then
|
||||
local cart = tCartsOnRail[name][userID]
|
||||
local pos = cart.last_pos or cart.pos
|
||||
if pos then
|
||||
local loc = minecart.get_buffer_name(cart.pos) or
|
||||
math.floor(vector.distance(pos, query_pos))
|
||||
if cart.objID == 0 then
|
||||
return "stopped", minecart.get_buffer_name(cart.pos) or
|
||||
math.floor(vector.distance(pos, query_pos)), cart.node_name
|
||||
else
|
||||
return "running", math.floor(vector.distance(pos, query_pos)), cart.node_name
|
||||
end
|
||||
end
|
||||
end
|
||||
return "unknown", 0, "unknown"
|
||||
end
|
||||
|
||||
-- Return the cart distance to the query_pos.
|
||||
local function get_cart_distance(name, userID, query_pos)
|
||||
if tCartsOnRail[name] and tCartsOnRail[name][userID] then
|
||||
local cart = tCartsOnRail[name][userID]
|
||||
if cart.last_pos or cart.pos then
|
||||
if cart.objID == 0 then -- stopped
|
||||
return math.floor(vector.distance(cart.pos or cart.last_pos, query_pos))
|
||||
else
|
||||
return math.floor(vector.distance(cart.last_pos or cart.pos, query_pos))
|
||||
end
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function get_cart_info(owner, userID, query_pos)
|
||||
local state, loc, name = get_cart_state_and_loc(owner, userID, query_pos)
|
||||
local cart_type = minecart.tCartTypes[name] or "unknown"
|
||||
if type(loc) == "number" then
|
||||
return "Cart #" .. userID .. " (" .. cart_type .. ") " .. state .. " " .. loc .. " m away "
|
||||
else
|
||||
return "Cart #" .. userID .. " (" .. cart_type .. ") " .. state .. " at ".. loc .. " "
|
||||
end
|
||||
end
|
||||
|
||||
local function logging(cart, err)
|
||||
local s = string.format("[Minecart] Cart %s/%u %s!", cart.owner, cart.userID, err)
|
||||
minetest.log("warning", s)
|
||||
end
|
||||
|
||||
-- check cart data
|
||||
local function valid_cart(cart)
|
||||
if cart.objID == nil or cart.objID == 0 then
|
||||
return false
|
||||
end
|
||||
if tCartsOnRail[cart.owner] and tCartsOnRail[cart.owner][cart.userID] then
|
||||
return true
|
||||
end
|
||||
logging(cart, "with invalid data")
|
||||
local entity = minetest.luaentities[cart.objID]
|
||||
if entity then
|
||||
entity.object:remove()
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function monitoring(cycle)
|
||||
local cart = pop(cycle)
|
||||
|
||||
-- All running cars
|
||||
while cart do
|
||||
if valid_cart(cart) then
|
||||
cart.idx = cart.idx + 1
|
||||
local entity = minetest.luaentities[cart.objID]
|
||||
if entity then -- cart entity running
|
||||
local pos = entity.object:get_pos()
|
||||
if pos then
|
||||
cart.last_pos = vector.round(pos)
|
||||
else
|
||||
logging(cart, "without pos")
|
||||
end
|
||||
push(cycle, cart)
|
||||
elseif cart.checkpoints then
|
||||
local cp, last_cp = get_checkpoint(cart)
|
||||
if cp then
|
||||
cart.last_pos = H2P(cp[1])
|
||||
--print("zombie " .. cart.userID .. " at " .. P2S(cart.last_pos))
|
||||
if is_player_nearby(cart.last_pos) or last_cp then
|
||||
zombie_to_entity(cart.last_pos, cart, cp)
|
||||
end
|
||||
push(cycle, cart)
|
||||
else
|
||||
logging(cart, "as zombie got lost")
|
||||
end
|
||||
else
|
||||
local pos = cart.last_pos or cart.pos
|
||||
pos = minecart.add_nodecart(pos, cart.node_name, 0, cart.cargo, cart.owner, cart.userID)
|
||||
minecart.stop_monitoring(cart.owner, cart.userID, pos)
|
||||
logging(cart, "stopped at " .. (P2S(pos) or "unknown"))
|
||||
end
|
||||
elseif not cart.objID and tCartsOnRail[cart.owner] then
|
||||
-- Delete carts marked as "to be deleted"
|
||||
tCartsOnRail[cart.owner][cart.userID] = nil
|
||||
end
|
||||
cart = pop(cycle)
|
||||
end
|
||||
minetest.after(2, monitoring, cycle + 1)
|
||||
end
|
||||
|
||||
minetest.after(5, monitoring, 2)
|
||||
|
||||
|
||||
function minecart.monitoring_add_cart(owner, userID, pos, node_name, entity_name)
|
||||
--print("monitoring_add_cart", owner, userID)
|
||||
tCartsOnRail[owner] = tCartsOnRail[owner] or {}
|
||||
tCartsOnRail[owner][userID] = {
|
||||
owner = owner,
|
||||
userID = userID,
|
||||
objID = 0,
|
||||
pos = pos,
|
||||
idx = 0,
|
||||
node_name = node_name,
|
||||
entity_name = entity_name,
|
||||
}
|
||||
minecart.store_carts()
|
||||
end
|
||||
|
||||
function minecart.start_monitoring(owner, userID, pos, objID, checkpoints, junctions, cargo)
|
||||
--print("start_monitoring", owner, userID)
|
||||
if tCartsOnRail[owner] and tCartsOnRail[owner][userID] then
|
||||
tCartsOnRail[owner][userID].pos = pos
|
||||
tCartsOnRail[owner][userID].objID = objID
|
||||
tCartsOnRail[owner][userID].checkpoints = checkpoints
|
||||
tCartsOnRail[owner][userID].junctions = junctions
|
||||
tCartsOnRail[owner][userID].cargo = cargo
|
||||
tCartsOnRail[owner][userID].idx = 0
|
||||
push(0, tCartsOnRail[owner][userID])
|
||||
minecart.store_carts()
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.stop_monitoring(owner, userID, pos)
|
||||
--print("stop_monitoring", owner, userID)
|
||||
if tCartsOnRail[owner] and tCartsOnRail[owner][userID] then
|
||||
tCartsOnRail[owner][userID].pos = pos
|
||||
-- Mark as "stopped"
|
||||
tCartsOnRail[owner][userID].objID = 0
|
||||
minecart.store_carts()
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.monitoring_remove_cart(owner, userID)
|
||||
--print("monitoring_remove_cart", owner, userID)
|
||||
if tCartsOnRail[owner] and tCartsOnRail[owner][userID] then
|
||||
-- Cart stopped?
|
||||
if tCartsOnRail[owner][userID].objID == 0 then
|
||||
-- Mark as "to be deleted" by monitoring (if part of monitoring)
|
||||
tCartsOnRail[owner][userID].objID = nil
|
||||
-- And delete directly in addition
|
||||
tCartsOnRail[owner][userID] = nil
|
||||
else -- Cart running
|
||||
-- Mark as "to be deleted" by monitoring
|
||||
tCartsOnRail[owner][userID].objID = nil
|
||||
end
|
||||
minecart.store_carts()
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.monitoring_valid_cart(owner, userID, pos, node_name)
|
||||
if tCartsOnRail[owner] and tCartsOnRail[owner][userID] and tCartsOnRail[owner][userID].pos then
|
||||
return vector.equals(tCartsOnRail[owner][userID].pos, pos) and
|
||||
tCartsOnRail[owner][userID].node_name == node_name
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.userID_available(owner, userID)
|
||||
return not tCartsOnRail[owner] or tCartsOnRail[owner][userID] == nil
|
||||
end
|
||||
|
||||
function minecart.get_cart_monitoring_data(owner, userID)
|
||||
if tCartsOnRail[owner] then
|
||||
return tCartsOnRail[owner][userID]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- API functions
|
||||
--
|
||||
|
||||
-- Needed by storage to re-construct the queue after server start
|
||||
minecart.push = push
|
||||
|
||||
minetest.register_chatcommand("mycart", {
|
||||
params = "<cart-num>",
|
||||
description = S("Output cart state and position, or a list of carts, if no cart number is given."),
|
||||
func = function(owner, param)
|
||||
local userID = tonumber(param)
|
||||
local query_pos = minetest.get_player_by_name(owner):get_pos()
|
||||
|
||||
if userID then
|
||||
return true, get_cart_info(owner, userID, query_pos)
|
||||
elseif tCartsOnRail[owner] then
|
||||
-- Output a list with all numbers
|
||||
local tbl = {}
|
||||
for userID, cart in pairs(tCartsOnRail[owner]) do
|
||||
tbl[#tbl + 1] = userID
|
||||
end
|
||||
return true, S("List of carts") .. ": "..table.concat(tbl, ", ").." "
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("stopcart", {
|
||||
params = "<cart-num>",
|
||||
description = S("Stop and return/drop a missing/running cart."),
|
||||
func = function(owner, param)
|
||||
local userID = tonumber(param)
|
||||
local player_pos = minetest.get_player_by_name(owner):get_pos()
|
||||
if userID then
|
||||
local data = minecart.get_cart_monitoring_data(owner, userID)
|
||||
if data and data.objID then
|
||||
local entity = minetest.luaentities[data.objID]
|
||||
--print("stopcart", userID, data.pos, data.objID, entity)
|
||||
if data.objID == 0 then
|
||||
-- Cart as node
|
||||
if data.pos then
|
||||
local meta = M(data.pos)
|
||||
if owner == meta:get_string("owner") and userID == meta:get_int("userID") then
|
||||
minecart.remove_nodecart(data.pos)
|
||||
end
|
||||
end
|
||||
elseif entity then
|
||||
-- Cart as entity
|
||||
minecart.remove_entity(entity, data.pos)
|
||||
else
|
||||
-- Cart as zombie/invalid/corrupted
|
||||
minetest.log("warning", "[Minecart] data.objID ~= 0, but no entity available!")
|
||||
end
|
||||
minetest.add_item(player_pos, ItemStack({name = data.node_name}))
|
||||
minecart.monitoring_remove_cart(owner, userID)
|
||||
return true, S("Cart") .. " " .. userID .. " " .. S("dropped")
|
||||
else
|
||||
return false, S("Cart") .. " " .. userID .. " " .. S("is not existing!")
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
function minecart.cmnd_cart_state(name, userID)
|
||||
local state, loc = get_cart_state_and_loc(name, userID, {x=0, y=0, z=0})
|
||||
return state
|
||||
end
|
||||
|
||||
function minecart.cmnd_cart_location(name, userID, query_pos)
|
||||
local state, loc = get_cart_state_and_loc(name, userID, query_pos)
|
||||
return loc
|
||||
end
|
||||
|
||||
function minecart.cmnd_cart_distance(name, userID, query_pos)
|
||||
return get_cart_distance(name, userID, query_pos)
|
||||
end
|
||||
|
||||
function minecart.get_cart_list(pos, name)
|
||||
local userIDs = {}
|
||||
local carts = {}
|
||||
|
||||
for userID, cart in pairs(tCartsOnRail[name] or {}) do
|
||||
userIDs[#userIDs + 1] = userID
|
||||
end
|
||||
|
||||
table.sort(userIDs, function(a,b) return a < b end)
|
||||
|
||||
for _, userID in ipairs(userIDs) do
|
||||
carts[#carts + 1] = get_cart_info(name, userID, pos)
|
||||
end
|
||||
|
||||
return table.concat(carts, "\n")
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
if minetest.get_modpath("techage") then
|
||||
techage.icta_register_condition("cart_state", {
|
||||
title = "read cart state",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
name = "number",
|
||||
label = "cart number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read state from one of your carts",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
local number = tonumber(data.number) or 0
|
||||
return 'cart_state('..number..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
local number = tonumber(data.number) or 0
|
||||
return minecart.cmnd_cart_state(environ.owner, number)
|
||||
end
|
||||
local result = function(val)
|
||||
return val ~= 0
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
techage.icta_register_condition("cart_location", {
|
||||
title = "read cart location",
|
||||
formspec = {
|
||||
{
|
||||
type = "digits",
|
||||
name = "number",
|
||||
label = "cart number",
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "label",
|
||||
name = "lbl",
|
||||
label = "Read location from one of your carts",
|
||||
},
|
||||
},
|
||||
button = function(data, environ) -- default button label
|
||||
local number = tonumber(data.number) or 0
|
||||
return 'cart_loc('..number..')'
|
||||
end,
|
||||
code = function(data, environ)
|
||||
local condition = function(env, idx)
|
||||
local number = tonumber(data.number) or 0
|
||||
return minecart.cmnd_cart_location(environ.owner, number, env.pos)
|
||||
end
|
||||
local result = function(val)
|
||||
return val ~= 0
|
||||
end
|
||||
return condition, result
|
||||
end,
|
||||
})
|
||||
techage.lua_ctlr.register_function("cart_state", {
|
||||
cmnd = function(self, num)
|
||||
num = tonumber(num) or 0
|
||||
return minecart.cmnd_cart_state(self.meta.owner, num)
|
||||
end,
|
||||
help = " $cart_state(num)\n"..
|
||||
" Read state from one of your carts.\n"..
|
||||
' "num" is the cart number\n'..
|
||||
' example: sts = $cart_state(2)'
|
||||
})
|
||||
techage.lua_ctlr.register_function("cart_location", {
|
||||
cmnd = function(self, num)
|
||||
num = tonumber(num) or 0
|
||||
return minecart.cmnd_cart_location(self.meta.owner, num, self.meta.pos)
|
||||
end,
|
||||
help = " $cart_location(num)\n"..
|
||||
" Read location from one of your carts.\n"..
|
||||
' "num" is the cart number\n'..
|
||||
' example: sts = $cart_location(2)'
|
||||
})
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user