VoxeLibre/mods/ENTITIES/mcl_minecarts/train.lua

156 lines
3.8 KiB
Lua

local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local mod = mcl_minecarts
-- Imports
local get_cart_data = mod.get_cart_data
local save_cart_data = mod.save_cart_data
local MAX_TRAIN_LENGTH = mod.MAX_TRAIN_LENGTH
-- Follow .behind to the back end of a train
local function find_back(start)
assert(start)
while start.behind do
local nxt = get_cart_data(start.behind)
if not nxt then return start end
start = nxt
end
return start
end
-- Iterate across all the cars in a train
local function train_cars(staticdata)
assert(staticdata)
local back = find_back(staticdata)
local limit = MAX_TRAIN_LENGTH
return function()
if not back or limit <= 0 then return end
limit = limit - 1
local ret = back
if back.ahead then
back = get_cart_data(back.ahead)
else
back = nil
end
return ret
end
end
function mod.train_length(cart)
local count = 0
for cart in train_cars(cart) do
count = count + 1
end
return count
end
function mod.is_in_same_train(anchor, other)
for cart in train_cars(anchor) do
if cart.uuid == other.uuid then return true end
end
return false
end
function mod.distance_between_cars(car1, car2)
if not car1.connected_at then return nil end
if not car2.connected_at then return nil end
if not car1.dir then car1.dir = vector.zero() end
if not car2.dir then car2.dir = vector.zero() end
local pos1 = vector.add(car1.connected_at, vector.multiply(car1.dir, car1.distance))
local pos2 = vector.add(car2.connected_at, vector.multiply(car2.dir, car2.distance))
return vector.distance(pos1, pos2)
end
local distance_between_cars = mod.distance_between_cars
local function break_train_at(cart)
if cart.ahead then
local ahead = get_cart_data(cart.ahead)
if ahead then
ahead.behind = nil
cart.ahead = nil
save_cart_data(ahead.uuid)
end
end
if cart.behind then
local behind = get_cart_data(cart.behind)
if behind then
behind.ahead = nil
cart.behind = nil
save_cart_data(behind.uuid)
end
end
save_cart_data(cart.uuid)
end
mod.break_train_at = break_train_at
function mod.update_train(staticdata)
--local staticdata = cart._staticdata
-- Only update from the back
if staticdata.behind or not staticdata.ahead then return end
--print("\nUpdating train")
-- Do no special processing if the cart is not part of a train
if not staticdata.ahead and not staticdata.behind then return end
-- Calculate the maximum velocity of all train cars
local velocity = 0
local count = 0
for cart in train_cars(staticdata) do
velocity = velocity + (cart.velocity or 0)
count = count + 1
end
velocity = velocity / count
--print("Using velocity "..tostring(velocity))
-- Set the entire train to the average velocity
local behind = nil
for c in train_cars(staticdata) do
local e = 0
local separation
local cart_velocity = velocity
if not c.connected_at then
break_train_at(c)
elseif behind then
separation = distance_between_cars(behind, c)
local e = 0
if not separation then
break_train_at(c)
elseif separation > 1.6 then
cart_velocity = velocity * 0.9
elseif separation > 2.5 then
break_train_at(c)
elseif separation < 1.15 then
cart_velocity = velocity * 1.1
end
end
--[[
print(tostring(c.behind).."->"..c.uuid.."->"..tostring(c.ahead).."("..tostring(separation)..") setting cart #"..
c.uuid.." velocity from "..tostring(c.velocity).." to "..tostring(cart_velocity))
]]
c.velocity = cart_velocity
behind = c
end
end
function mod.link_cart_ahead(staticdata, ca_staticdata)
minetest.log("action","Linking cart #"..staticdata.uuid.." to cart #"..ca_staticdata.uuid)
staticdata.ahead = ca_staticdata.uuid
ca_staticdata.behind = staticdata.uuid
end
function mod.reverse_train(cart)
for c in train_cars(cart) do
mod.reverse_cart_direction(c)
c.behind,c.ahead = c.ahead,c.behind
end
end