Replace old cart mod on boost_cart

master
MoNTE48 2019-05-18 20:36:28 +02:00
parent 512c54072f
commit bf018c4521
84 changed files with 1123 additions and 1262 deletions

View File

@ -0,0 +1,57 @@
Minetest mod: boost_cart
==========================
Based on (and fully compatible with) the mod "carts" by PilzAdam
and the one contained in the subgame "minetest_game".
Target: Run smoothly as possible, even on laggy servers.
Features
----------
- A fast cart for your railway or roller coaster
- Easily configurable cart speed using the Advanced Settings
- Boost and brake rails
- By mesecons controlled Start-Stop rails
- Detector rails that send a mesecons signal when the cart drives over them
- Rail junction switching with the 'right/left' walking keys
- Handbrake with the 'back' key
- Support for non-minetest_game subgames
Settings
----------
This mod can be adjusted to fit the conditions of a player or server.
Use the Advanced Settings dialog in the main menu or tune your
minetest.conf file manually:
boost_cart.speed_max = 10
^ Possible values: 1 ... 100
^ Maximal speed of the cart in m/s
boost_cart.punch_speed_max = 7
^ Possible values: -1 ... 100
^ Maximal speed to which the driving player can accelerate the cart
by punching from inside the cart. -1 will disable this feature.
Carts, based almost entirely on the mod boost_cart [1], which
itself is based on (and fully compatible with) the carts mod [2].
The model was originally designed by stujones11 [3] (CC-0).
Cart textures are based on original work from PixelBOX by Gambit (permissive
license).
[1] https://github.com/SmallJoker/boost_cart/
[2] https://github.com/PilzAdam/carts/
[3] https://github.com/stujones11/railcart/
Authors
---------
Various authors
carts_rail_*.png
klankbeeld (CC-BY 3.0)
http://freesound.org/people/klankbeeld/sounds/174042/
cart_rail.*.ogg

View File

@ -0,0 +1,475 @@
local HAVE_MESECONS_ENABLED = minetest.global_exists("mesecon")
function boost_cart:on_rail_step(entity, pos, distance)
-- Play rail sound
if entity.sound_counter <= 0 then
minetest.sound_play("cart_rail", {
pos = pos,
max_hear_distance = 40,
gain = 0.5
})
entity.sound_counter = math.random(4, 15)
end
entity.sound_counter = entity.sound_counter - distance
if HAVE_MESECONS_ENABLED then
boost_cart:signal_detector_rail(pos)
end
end
local cart_entity = {
initial_properties = {
physical = false,
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
visual = "mesh",
mesh = "carts_cart.b3d",
visual_size = {x=1, y=1},
textures = {"carts_cart.png"},
},
driver = nil,
punched = false, -- used to re-send velocity and position
velocity = {x=0, y=0, z=0}, -- only used on punch
old_dir = {x=1, y=0, z=0}, -- random value to start the cart on punch
old_pos = nil,
old_switch = 0,
sound_counter = 0,
railtype = nil,
attached_items = {}
}
function cart_entity:on_rightclick(clicker)
if not clicker or not clicker:is_player() then
return
end
local player_name = clicker:get_player_name()
if self.driver and player_name == self.driver then
self.driver = nil
boost_cart:manage_attachment(clicker, nil)
elseif not self.driver then
self.driver = player_name
boost_cart:manage_attachment(clicker, self.object)
if default.player_set_animation then
-- player_api(/default) does not update the animation
-- when the player is attached, reset to default animation
default.player_set_animation(clicker, "stand")
end
end
end
function cart_entity:on_activate(staticdata, dtime_s)
self.object:set_armor_groups({immortal=1})
self.sound_counter = math.random(4, 15)
if string.sub(staticdata, 1, string.len("return")) ~= "return" then
return
end
local data = minetest.deserialize(staticdata)
if type(data) ~= "table" then
return
end
self.railtype = data.railtype
self.old_dir = data.old_dir or self.old_dir
self.old_pos = data.old_pos or self.old_pos
-- Correct the position when the cart drives further after the last 'step()'
if self.old_pos and boost_cart:is_rail(self.old_pos, self.railtype) then
self.object:set_pos(self.old_pos)
end
end
function cart_entity:get_staticdata()
return minetest.serialize({
railtype = self.railtype,
old_dir = self.old_dir,
old_pos = self.old_pos
})
end
-- 0.5.x and later: When the driver leaves
function cart_entity:on_detach_child(child)
if child and child:get_player_name() == self.driver then
self.driver = nil
end
end
function cart_entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
local pos = self.object:get_pos()
local vel = self.object:get_velocity()
if not self.railtype or vector.equals(vel, {x=0, y=0, z=0}) then
local node = minetest.get_node(pos).name
self.railtype = minetest.get_item_group(node, "connect_to_raillike")
end
if not puncher or not puncher:is_player() then
local cart_dir = boost_cart:get_rail_direction(pos, self.old_dir, nil, nil, self.railtype)
if vector.equals(cart_dir, {x=0, y=0, z=0}) then
return
end
self.velocity = vector.multiply(cart_dir, 3)
self.punched = true
return
end
if puncher:get_player_control().sneak then
-- Pick up cart: Drop all attachments
if self.driver then
if self.old_pos then
self.object:set_pos(self.old_pos)
end
local player = minetest.get_player_by_name(self.driver)
boost_cart:manage_attachment(player, nil)
end
for _, obj_ in pairs(self.attached_items) do
if obj_ then
obj_:set_detach()
end
end
local leftover = puncher:get_inventory():add_item("main", "carts:cart")
if not leftover:is_empty() then
minetest.add_item(pos, leftover)
end
self.object:remove()
return
end
-- Driver punches to accelerate the cart
if puncher:get_player_name() == self.driver then
if math.abs(vel.x + vel.z) > boost_cart.punch_speed_max then
return
end
end
local punch_dir = boost_cart:velocity_to_dir(puncher:get_look_dir())
punch_dir.y = 0
local cart_dir = boost_cart:get_rail_direction(pos, punch_dir, nil, nil, self.railtype)
if vector.equals(cart_dir, {x=0, y=0, z=0}) then
return
end
local punch_interval = 1
if tool_capabilities and tool_capabilities.full_punch_interval then
punch_interval = tool_capabilities.full_punch_interval
end
time_from_last_punch = math.min(time_from_last_punch or punch_interval, punch_interval)
local f = 3 * (time_from_last_punch / punch_interval)
self.velocity = vector.multiply(cart_dir, f)
self.old_dir = cart_dir
self.punched = true
end
-- sound refresh interval = 1.0sec
local function rail_sound(self, dtime)
if not self.sound_ttl then
self.sound_ttl = 1.0
return
elseif self.sound_ttl > 0 then
self.sound_ttl = self.sound_ttl - dtime
return
end
self.sound_ttl = 1.0
if self.sound_handle then
local handle = self.sound_handle
self.sound_handle = nil
minetest.after(0.2, minetest.sound_stop, handle)
end
local vel = self.object:get_velocity()
local speed = vector.length(vel)
if speed > 0 then
self.sound_handle = minetest.sound_play(
"carts_cart_moving", {
object = self.object,
gain = (speed / boost_cart.speed_max) / 2,
loop = true,
})
end
end
local v3_len = vector.length
function cart_entity:on_step(dtime)
rail_sound(self, dtime)
local vel = self.object:get_velocity()
if self.punched then
vel = vector.add(vel, self.velocity)
self.object:set_velocity(vel)
self.old_dir.y = 0
elseif vector.equals(vel, {x=0, y=0, z=0}) then
return
end
local pos = self.object:get_pos()
local cart_dir = boost_cart:velocity_to_dir(vel)
local same_dir = vector.equals(cart_dir, self.old_dir)
local update = {}
if self.old_pos and not self.punched and same_dir then
local flo_pos = vector.round(pos)
local flo_old = vector.round(self.old_pos)
if vector.equals(flo_pos, flo_old) then
-- Do not check one node multiple times
return
end
end
local ctrl, player
local distance = 1
-- Get player controls
if self.driver then
player = minetest.get_player_by_name(self.driver)
if player then
ctrl = player:get_player_control()
end
end
local stop_wiggle = false
if self.old_pos and same_dir then
-- Detection for "skipping" nodes (perhaps use average dtime?)
-- It's sophisticated enough to take the acceleration in account
local acc = self.object:get_acceleration()
distance = dtime * (v3_len(vel) + 0.5 * dtime * v3_len(acc))
local new_pos, new_dir = boost_cart:pathfinder(
pos, self.old_pos, self.old_dir, distance, ctrl,
self.old_switch, self.railtype
)
if new_pos then
-- No rail found: set to the expected position
pos = new_pos
update.pos = true
cart_dir = new_dir
end
elseif self.old_pos and self.old_dir.y ~= 1 and not self.punched then
-- Stop wiggle
stop_wiggle = true
end
-- dir: New moving direction of the cart
-- switch_keys: Currently pressed L(1) or R(2) key,
-- used to ignore the key on the next rail node
local dir, switch_keys = boost_cart:get_rail_direction(
pos, cart_dir, ctrl, self.old_switch, self.railtype
)
local dir_changed = not vector.equals(dir, self.old_dir)
local acc = 0
if stop_wiggle or vector.equals(dir, {x=0, y=0, z=0}) then
vel = {x=0, y=0, z=0}
local pos_r = vector.round(pos)
if not boost_cart:is_rail(pos_r, self.railtype)
and self.old_pos then
pos = self.old_pos
elseif not stop_wiggle then
pos = pos_r
else
pos.y = math.floor(pos.y + 0.5)
end
update.pos = true
update.vel = true
else
-- Direction change detected
if dir_changed then
vel = vector.multiply(dir, math.abs(vel.x + vel.z))
update.vel = true
if dir.y ~= self.old_dir.y then
pos = vector.round(pos)
update.pos = true
end
end
-- Center on the rail
if dir.z ~= 0 and math.floor(pos.x + 0.5) ~= pos.x then
pos.x = math.floor(pos.x + 0.5)
update.pos = true
end
if dir.x ~= 0 and math.floor(pos.z + 0.5) ~= pos.z then
pos.z = math.floor(pos.z + 0.5)
update.pos = true
end
-- Calculate current cart acceleration
acc = nil
local acc_meta = minetest.get_meta(pos):get_string("cart_acceleration")
if acc_meta == "halt" and not self.punched then
-- Stop rail
vel = {x=0, y=0, z=0}
acc = false
pos = vector.round(pos)
update.pos = true
update.vel = true
end
if acc == nil then
-- Meta speed modifier
local speed_mod = tonumber(acc_meta)
if speed_mod and speed_mod ~= 0 then
-- Try to make it similar to the original carts mod
acc = speed_mod * 10
end
end
if acc == nil and boost_cart.mtg_compat then
-- MTG Cart API adaption
local rail_node = minetest.get_node(vector.round(pos))
local railparam = carts.railparams[rail_node.name]
if railparam and railparam.acceleration then
acc = railparam.acceleration
end
end
if acc ~= false then
-- Handbrake
if ctrl and ctrl.down then
acc = (acc or 0) - 2
elseif acc == nil then
acc = -0.4
end
end
if acc then
-- Slow down or speed up, depending on Y direction
acc = acc + dir.y * -2.1
else
acc = 0
end
end
-- Limit cart speed
local vel_len = vector.length(vel)
if vel_len > boost_cart.speed_max then
vel = vector.multiply(vel, boost_cart.speed_max / vel_len)
update.vel = true
end
if vel_len >= boost_cart.speed_max and acc > 0 then
acc = 0
end
self.object:set_acceleration(vector.multiply(dir, acc))
self.old_pos = vector.round(pos)
local old_y_dir = self.old_dir.y
if not vector.equals(dir, {x=0, y=0, z=0}) and not stop_wiggle then
self.old_dir = dir
else
-- Cart stopped, set the animation to 0
self.old_dir.y = 0
end
self.old_switch = switch_keys
boost_cart:on_rail_step(self, self.old_pos, distance)
if self.punched then
-- Collect dropped items
for _, obj_ in pairs(minetest.get_objects_inside_radius(pos, 1)) do
if not obj_:is_player() and
obj_:get_luaentity() and
not obj_:get_luaentity().physical_state and
obj_:get_luaentity().name == "__builtin:item" then
obj_:set_attach(self.object, "", {x=0, y=0, z=0}, {x=0, y=0, z=0})
self.attached_items[#self.attached_items + 1] = obj_
end
end
self.punched = false
update.vel = true
end
if not (update.vel or update.pos) then
return
end
-- Re-use "dir", localize self.old_dir
dir = self.old_dir
local yaw = 0
if dir.x < 0 then
yaw = 0.5
elseif dir.x > 0 then
yaw = 1.5
elseif dir.z < 0 then
yaw = 1
end
self.object:set_yaw(yaw * math.pi)
local anim = {x=0, y=0}
if dir.y == -1 then
anim = {x=1, y=1}
elseif dir.y == 1 then
anim = {x=2, y=2}
end
self.object:set_animation(anim, 1, 0)
-- Change player model rotation, depending on the Y direction
if player and dir.y ~= old_y_dir then
local feet = {x=0, y=0, z=0}
local eye = {x=0, y=-4, z=0}
feet.y = boost_cart.old_player_model and 6 or -4
if dir.y ~= 0 then
-- TODO: Find a better way to calculate this
if boost_cart.old_player_model then
feet.y = feet.y + 2
feet.z = -dir.y * 6
else
feet.y = feet.y + 4
feet.z = -dir.y * 2
end
eye.z = -dir.y * 8
end
player:set_attach(self.object, "", feet,
{x=dir.y * -30, y=0, z=0})
player:set_eye_offset(eye, eye)
end
if update.vel then
self.object:set_velocity(vel)
end
if update.pos then
if dir_changed then
self.object:set_pos(pos)
else
self.object:move_to(pos)
end
end
end
minetest.register_entity(":carts:cart", cart_entity)
-- Register item to place the entity
if not boost_cart.mtg_compat then
minetest.register_craftitem(":carts:cart", {
description = "Cart (Sneak+Click to pick up)",
inventory_image = minetest.inventorycube(
"cart_top.png",
"cart_side.png",
"cart_side.png"
),
wield_image = "cart_side.png",
on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing.type == "node" then
return
end
if boost_cart:is_rail(pointed_thing.under) then
minetest.add_entity(pointed_thing.under, "carts:cart")
elseif boost_cart:is_rail(pointed_thing.above) then
minetest.add_entity(pointed_thing.above, "carts:cart")
else
return
end
if not minetest.settings:get_bool("creative_mode") then
itemstack:take_item()
end
return itemstack
end,
})
minetest.register_craft({
output = "carts:cart",
recipe = {
{"default:steel_ingot", "", "default:steel_ingot"},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
},
})
end

View File

@ -0,0 +1,4 @@
default
mesecons?
moreores?
carts?

View File

@ -0,0 +1 @@
This mod offers improved minecarts and a few more rail types.

View File

@ -0,0 +1,58 @@
local mesecons_rules = mesecon.rules.flat
function boost_cart:turnoff_detector_rail(pos)
local node = minetest.get_node(pos)
if minetest.get_item_group(node.name, "detector_rail") == 1 then
if node.name == "boost_cart:detectorrail_on" then --has not been dug
minetest.swap_node(pos, {name = "boost_cart:detectorrail", param2=node.param2})
end
mesecon.receptor_off(pos, mesecons_rules)
end
end
function boost_cart:signal_detector_rail(pos)
local node = minetest.get_node(pos)
if minetest.get_item_group(node.name, "detector_rail") ~= 1 then
return
end
if node.name == "boost_cart:detectorrail" then
minetest.swap_node(pos, {name = "boost_cart:detectorrail_on", param2=node.param2})
end
mesecon.receptor_on(pos, mesecons_rules)
minetest.after(0.5, boost_cart.turnoff_detector_rail, boost_cart, pos)
end
boost_cart:register_rail("boost_cart:detectorrail", {
description = "Detector rail",
tiles = {
"carts_rail_straight_dtc.png", "carts_rail_curved_dtc.png",
"carts_rail_t_junction_dtc.png", "carts_rail_crossing_dtc.png"
},
groups = boost_cart:get_rail_groups({detector_rail = 1}),
mesecons = {receptor = {state = "off", rules = mesecons_rules}},
})
boost_cart:register_rail("boost_cart:detectorrail_on", {
description = "Detector rail ON (you hacker you)",
tiles = {
"carts_rail_straight_dtc_on.png", "carts_rail_curved_dtc_on.png",
"carts_rail_t_junction_dtc_on.png", "carts_rail_crossing_dtc_on.png"
},
groups = boost_cart:get_rail_groups({
detector_rail = 1, not_in_creative_inventory = 1
}),
drop = "boost_cart:detectorrail",
mesecons = {receptor = {state = "on", rules = mesecons_rules}},
})
minetest.register_craft({
output = "boost_cart:detectorrail 6",
recipe = {
{"default:steel_ingot", "", "default:steel_ingot"},
{"default:steel_ingot", "mesecons_pressureplates:pressure_plate_stone_off", "default:steel_ingot"},
{"default:steel_ingot", "mesecons:wire_00000000_off", "default:steel_ingot"},
},
})

View File

@ -0,0 +1,269 @@
function boost_cart:get_sign(z)
if z == 0 then
return 0
else
return z / math.abs(z)
end
end
function boost_cart:manage_attachment(player, obj)
if not player then
return
end
local status = obj ~= nil
local player_name = player:get_player_name()
if default.player_attached[player_name] == status then
return
end
default.player_attached[player_name] = status
if status then
local y_pos = self.old_player_model and 6 or -4
if player:get_properties().visual == "upright_sprite" then
y_pos = -4
end
player:set_attach(obj, "", {x=0, y=y_pos, z=0}, {x=0, y=0, z=0})
player:set_eye_offset({x=0, y=-4, z=0},{x=0, y=-4, z=0})
else
player:set_detach()
player:set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0})
-- HACK in effect! Force updating the attachment rotation
player:set_properties({})
end
end
function boost_cart:velocity_to_dir(v)
if math.abs(v.x) > math.abs(v.z) then
return {x=self:get_sign(v.x), y=self:get_sign(v.y), z=0}
else
return {x=0, y=self:get_sign(v.y), z=self:get_sign(v.z)}
end
end
local get_node = minetest.get_node
local get_item_group = minetest.get_item_group
function boost_cart:is_rail(pos, railtype)
local node = get_node(pos).name
if node == "ignore" then
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(pos, pos)
local area = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax,
}
local data = vm:get_data()
local vi = area:indexp(pos)
node = minetest.get_name_from_content_id(data[vi])
end
if get_item_group(node, "rail") == 0 then
return false
end
if not railtype then
return true
end
return get_item_group(node, "connect_to_raillike") == railtype
end
function boost_cart:check_front_up_down(pos, dir_, check_up, railtype)
local dir = vector.new(dir_)
local cur = nil
-- Front
dir.y = 0
cur = vector.add(pos, dir)
if self:is_rail(cur, railtype) then
return dir
end
-- Up
if check_up then
dir.y = 1
cur = vector.add(pos, dir)
if self:is_rail(cur, railtype) then
return dir
end
end
-- Down
dir.y = -1
cur = vector.add(pos, dir)
if self:is_rail(cur, railtype) then
return dir
end
return nil
end
function boost_cart:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
local pos = vector.round(pos_)
local cur = nil
local left_check, right_check = true, true
-- Check left and right
local left = {x=0, y=0, z=0}
local right = {x=0, y=0, z=0}
if dir.z ~= 0 and dir.x == 0 then
left.x = -dir.z
right.x = dir.z
elseif dir.x ~= 0 and dir.z == 0 then
left.z = dir.x
right.z = -dir.x
end
local straight_priority = ctrl and dir.y ~= 0
-- Normal, to disallow rail switching up- & downhill
if straight_priority then
cur = self:check_front_up_down(pos, dir, true, railtype)
if cur then
return cur
end
end
if ctrl then
if old_switch == 1 then
left_check = false
elseif old_switch == 2 then
right_check = false
end
if ctrl.left and left_check then
cur = self:check_front_up_down(pos, left, false, railtype)
if cur then
return cur, 1
end
left_check = false
end
if ctrl.right and right_check then
cur = self:check_front_up_down(pos, right, false, railtype)
if cur then
return cur, 2
end
right_check = true
end
end
-- Normal
if not straight_priority then
cur = self:check_front_up_down(pos, dir, true, railtype)
if cur then
return cur
end
end
-- Left, if not already checked
if left_check then
cur = self:check_front_up_down(pos, left, false, railtype)
if cur then
return cur
end
end
-- Right, if not already checked
if right_check then
cur = self:check_front_up_down(pos, right, false, railtype)
if cur then
return cur
end
end
-- Backwards
if not old_switch then
cur = self:check_front_up_down(pos, {
x = -dir.x,
y = dir.y,
z = -dir.z
}, true, railtype)
if cur then
return cur
end
end
return {x=0, y=0, z=0}
end
function boost_cart:pathfinder(pos_, old_pos, old_dir, distance, ctrl,
pf_switch, railtype)
local pos = vector.round(pos_)
if vector.equals(old_pos, pos) then
return
end
local pf_pos = vector.round(old_pos)
local pf_dir = vector.new(old_dir)
distance = math.min(boost_cart.path_distance_max,
math.floor(distance + 1))
for i = 1, distance do
pf_dir, pf_switch = self:get_rail_direction(
pf_pos, pf_dir, ctrl, pf_switch or 0, railtype)
if vector.equals(pf_dir, {x=0, y=0, z=0}) then
-- No way forwards
return pf_pos, pf_dir
end
pf_pos = vector.add(pf_pos, pf_dir)
if vector.equals(pf_pos, pos) then
-- Success! Cart moved on correctly
return
end
end
-- Not found. Put cart to predicted position
return pf_pos, pf_dir
end
function boost_cart:boost_rail(pos, amount)
minetest.get_meta(pos):set_string("cart_acceleration", tostring(amount))
for _,obj_ in ipairs(minetest.get_objects_inside_radius(pos, 0.5)) do
if not obj_:is_player() and
obj_:get_luaentity() and
obj_:get_luaentity().name == "carts:cart" then
obj_:get_luaentity():on_punch()
end
end
end
function boost_cart:register_rail(name, def_overwrite)
local sound_func = default.node_sound_metal_defaults
or default.node_sound_defaults
local def = {
drawtype = "raillike",
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
walkable = false,
selection_box = {
type = "fixed",
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
},
sounds = sound_func()
}
for k, v in pairs(def_overwrite) do
def[k] = v
end
if not def.inventory_image then
def.wield_image = def.tiles[1]
def.inventory_image = def.tiles[1]
end
minetest.register_node(name, def)
end
function boost_cart:get_rail_groups(additional_groups)
-- Get the default rail groups and add more when a table is given
local groups = {
dig_immediate = 2,
attached_node = 1,
rail = 1,
connect_to_raillike = 1
}
if minetest.raillike_group then
groups.connect_to_raillike = minetest.raillike_group("rail")
end
if type(additional_groups) == "table" then
for k, v in pairs(additional_groups) do
groups[k] = v
end
end
return groups
end

View File

@ -0,0 +1,45 @@
boost_cart = {}
boost_cart.modpath = minetest.get_modpath("boost_cart")
local function getNum(setting)
return tonumber(minetest.settings:get(setting))
end
-- Maximal speed of the cart in m/s
boost_cart.speed_max = getNum("boost_cart.speed_max") or 12
-- Set to -1 to disable punching the cart from inside
boost_cart.punch_speed_max = getNum("boost_cart.punch_speed_max") or 8
-- Maximal distance for the path correction (for dtime peaks)
boost_cart.path_distance_max = 3
-- Support for non-default games
if not default.player_attached then
default.player_attached = {}
end
minetest.after(0, function()
boost_cart.old_player_model = not minetest.global_exists("player_api")
end)
dofile(boost_cart.modpath.."/functions.lua")
dofile(boost_cart.modpath.."/rails.lua")
if minetest.global_exists("mesecon") then
dofile(boost_cart.modpath.."/detector.lua")
--else
-- minetest.register_alias("carts:powerrail", "boost_cart:detectorrail")
-- minetest.register_alias("carts:powerrail", "boost_cart:detectorrail_on")
end
dofile(boost_cart.modpath.."/cart_entity.lua")
-- Aliases
minetest.register_alias("railcart:cart", "carts:cart")
minetest.register_alias("railcart:cart_entity", "carts:cart")
minetest.register_alias("default:rail", "boost_cart:rail")
minetest.register_alias("railtrack:powerrail", "carts:powerrail")
minetest.register_alias("railtrack:superrail", "carts:powerrail")
minetest.register_alias("railtrack:brakerail", "carts:brakerail")
minetest.register_alias("railtrack:switchrail", "boost_cart:startstoprail")
minetest.register_alias("railtrack:fixer", "default:stick")
minetest.register_alias("railtrack:inspector", "default:stick")

View File

@ -0,0 +1,54 @@
License of source code
----------------------
The MIT License (MIT)
Copyright (C) 2012-2016 PilzAdam
Copyright (C) 2014-2016 SmallJoker
Copyright (C) 2012-2016 Various Minetest developers and contributors
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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 OR COPYRIGHT HOLDERS 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.
For more details:
https://opensource.org/licenses/MIT
Licenses of media
-----------------
CC-0, see: https://creativecommons.org/share-your-work/public-domain/cc0/, except
if other license is mentioned.
Authors
---------
Originally from PixelBOX (Gambit):
carts_cart_side.png
carts_cart_top.png
carts_cart_front.png*
carts_cart.png*
sofar + stujones11:
carts_cart.b3d and carts_cart.blend
hexafraction, modified by sofar
carts_rail_*.png
http://www.freesound.org/people/YleArkisto/sounds/253159/ - YleArkisto - CC-BY-3.0
carts_cart_moving.*.ogg

View File

@ -0,0 +1 @@
name = boost_cart

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

View File

@ -0,0 +1,153 @@
-- Common rail registrations
boost_cart:register_rail("boost_cart:rail", {
description = "Rail",
tiles = {
"carts_rail_straight.png", "carts_rail_curved.png",
"carts_rail_t_junction.png", "carts_rail_crossing.png"
},
groups = boost_cart:get_rail_groups(),
}, {})
minetest.register_craft({
output = "boost_cart:rail 16",
recipe = {
{"default:steel_ingot", "", "default:steel_ingot"},
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
{"default:steel_ingot", "", "default:steel_ingot"},
}
})
--[[
-- Moreores' copper rail
if minetest.get_modpath("moreores") then
minetest.register_alias("carts:copperrail", "moreores:copper_rail")
if minetest.raillike_group then
-- Ensure that this rail uses the same connect_to_raillike
local new_groups = minetest.registered_nodes["moreores:copper_rail"].groups
new_groups.connect_to_raillike = minetest.raillike_group("rail")
minetest.override_item("moreores:copper_rail", {
groups = new_groups
})
end
else
boost_cart:register_rail(":carts:copperrail", {
description = "Copper rail",
tiles = {
"carts_rail_straight_cp.png", "carts_rail_curved_cp.png",
"carts_rail_t_junction_cp.png", "carts_rail_crossing_cp.png"
},
groups = boost_cart:get_rail_groups()
})
minetest.register_craft({
output = "carts:copperrail 12",
recipe = {
{"default:copper_ingot", "", "default:copper_ingot"},
{"default:copper_ingot", "group:stick", "default:copper_ingot"},
{"default:copper_ingot", "", "default:copper_ingot"},
}
})
end
]]
-- Power rail
boost_cart:register_rail(":carts:powerrail", {
description = "Powered rail",
tiles = {
"carts_rail_straight_pwr.png", "carts_rail_curved_pwr.png",
"carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"
},
groups = boost_cart:get_rail_groups(),
after_place_node = function(pos, placer, itemstack)
if not mesecon then
minetest.get_meta(pos):set_string("cart_acceleration", "0.5")
end
end,
mesecons = {
effector = {
action_on = function(pos, node)
boost_cart:boost_rail(pos, 0.5)
end,
action_off = function(pos, node)
minetest.get_meta(pos):set_string("cart_acceleration", "0")
end,
},
},
})
minetest.register_craft({
output = "carts:powerrail 6",
recipe = {
{"default:gold_ingot", "", "default:gold_ingot"},
{"default:gold_ingot", "group:stick", "default:gold_ingot"},
{"default:gold_ingot", "mesecons:wire_00000000_off", "default:gold_ingot"},
}
})
-- Brake rail
boost_cart:register_rail(":carts:brakerail", {
description = "Brake rail",
tiles = {
"carts_rail_straight_brk.png", "carts_rail_curved_brk.png",
"carts_rail_t_junction_brk.png", "carts_rail_crossing_brk.png"
},
groups = boost_cart:get_rail_groups(),
after_place_node = function(pos, placer, itemstack)
if not mesecon then
minetest.get_meta(pos):set_string("cart_acceleration", "-0.3")
end
end,
mesecons = {
effector = {
action_on = function(pos, node)
minetest.get_meta(pos):set_string("cart_acceleration", "-0.3")
end,
action_off = function(pos, node)
minetest.get_meta(pos):set_string("cart_acceleration", "0")
end,
},
},
})
minetest.register_craft({
output = "carts:brakerail 6",
recipe = {
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
}
})
boost_cart:register_rail("boost_cart:startstoprail", {
description = "Start-stop rail",
tiles = {
"carts_rail_straight_ss.png", "carts_rail_curved_ss.png",
"carts_rail_t_junction_ss.png", "carts_rail_crossing_ss.png"
},
groups = boost_cart:get_rail_groups(),
after_place_node = function(pos, placer, itemstack)
if not mesecon then
minetest.get_meta(pos):set_string("cart_acceleration", "halt")
end
end,
mesecons = {
effector = {
action_on = function(pos, node)
boost_cart:boost_rail(pos, 0.5)
end,
action_off = function(pos, node)
minetest.get_meta(pos):set_string("cart_acceleration", "halt")
end,
},
},
})
minetest.register_craft({
output = "boost_cart:startstoprail 2",
recipe = {
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
{"default:steel_ingot", "mesecons_torch:mesecon_torch_on", "default:steel_ingot"},
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
}
})

View File

@ -0,0 +1,6 @@
# Maximal speed of the cart in m/s (min=1, max=100)
boost_cart.speed_max (Maximal speed) int 10 1 100
# Maximal speed to which the driving player can accelerate the cart by punching
# from inside the cart. -1 will disable this feature. (min=-1, max=100)
boost_cart.punch_speed_max (Maximal punch speed) int 7 -1 100

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

View File

@ -1,21 +0,0 @@
Minetest Mod - Railcart [railcart]
==================================
License Source Code: LGPL v2.1
License of media (textures, sounds and models not otherwise specified): CC-0
Authors of media files:
-----------------------
HybridDog: WTFPL
cart.png
cart_bottom.png
cart_side.png
cart_top.png
Zeg9:
cart.x

View File

@ -1,30 +0,0 @@
Minetest Mod - Railcart [railcart]
==================================
Minetest version: 0.4.13
Depends: railtrack
Proof of concept ground up re-write of the carts mod. Currently uses media files
borrowed from the original carts mod by PilzAdam.
Please note, this mod makes heavy use of metadata so that carts are able to
travel through unloaded map chunks, therefor a 'carts' privilege is required
to place or pick up carts in multiplayer mode.
Crafting
--------
S = Steel Ingot [default:steel_ingot]
W = Wood [group:wood]
Railcart: [railcart:cart]
+---+---+---+
| S | | S |
+---+---+---+
| S | | S |
+---+---+---+
| W | S | W |
+---+---+---+

View File

@ -1 +0,0 @@
railtrack

View File

@ -1,191 +0,0 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(modpath.."/railcart.lua")
local worldpath = minetest.get_worldpath()
local function create_detached_inventory(id)
local inv = minetest.create_detached_inventory("railcart_"..tostring(id), {
on_put = function(inv, listname, index, stack, player)
railcart:save()
end,
on_take = function(inv, listname, index, stack, player)
railcart:save()
end,
allow_put = function(inv, listname, index, stack, player)
return 1
end,
allow_take = function(inv, listname, index, stack, player)
return stack:get_count()
end,
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
return count
end,
})
--inv:set_size("main", 32)
return inv
end
local input = io.open(worldpath.."/railcart.txt", "r")
if input then
local data = input:read('*all')
if data then
local carts = minetest.deserialize(data) or {}
for id, ref in pairs(carts) do
local cart = railcart.cart:new(ref)
if ref.inv then
local inv = create_detached_inventory(cart.id)
for i, stack in pairs(ref.inv) do
inv:set_stack("main", i, stack)
end
cart.inv = inv
end
railcart.allcarts[id] = cart
end
end
input = nil
end
local function is_valid_player(object)
if object then
return object:is_player()
end
end
railcart:register_entity("railcart:cart_entity", {
on_punch = function(self, puncher, _, _, direction)
if not is_valid_player(puncher) then
return
end
if puncher:get_player_control().sneak then
if self.cart then
if self.cart.id then
if self.cart.inv then
if not self.cart.inv:is_empty("main") then
return
end
end
railcart:remove_cart(self.cart.id)
end
end
self.object:remove()
local inv = puncher:get_inventory()
if minetest.setting_getbool("creative_mode") then
if not inv:contains_item("main", "railcart:cart") then
inv:add_item("main", "railcart:cart")
end
else
inv:add_item("main", "railcart:cart")
end
return
end
if self.cart and direction then
if self.driver then
direction = {x=0, y=0, z=0}
local ld = self.driver:get_look_dir()
if ld.y > -0.99 then
direction = {
x = railtrack:get_sign(ld.x),
z = railtrack:get_sign(ld.z),
y = self.cart.dir.y
}
end
end
local pos = vector.round(self.object:getpos())
local dir = vector.round(vector.normalize(direction))
local speed = railcart:velocity_to_speed(self.cart.vel)
self.timer = 0
self.cart.target = nil
self.cart.prev = pos
self.cart.vel = vector.multiply(dir, 5)
self.cart.accel = railtrack:get_acceleration(pos)
self.object:setvelocity(self.cart.vel)
end
end,
on_rightclick = function(self, clicker)
if not is_valid_player(clicker) then
return
end
if clicker:get_player_control().sneak then
local name = clicker:get_player_name()
local cart = self.cart or {}
if cart.id and name then
local formspec = "size[8,9]"..
--default.gui_bg..default.gui_bg_img..default.gui_slots..
"list[detached:railcart_"..cart.id..";main;0,0.3;8,4;]"..
"list[current_player;main;0,4.85;8,1;]"..
"list[current_player;main;0,6.08;8,3;8]"..
"listring[detached:railcart_"..cart.id..";main]"..
"listring[current_player;main]"..
--default.get_hotbar_bg(0,4.85)
minetest.show_formspec(name, "inventory", formspec)
end
return
end
if self.driver and clicker == self.driver then
self.driver = nil
clicker:set_detach()
elseif not self.driver then
self.driver = clicker
clicker:set_attach(self.object, "", {x=0,y=5,z=0}, {x=0,y=0,z=0})
end
end,
})
minetest.register_craftitem("railcart:cart", {
description = "Railcart",
inventory_image = "railcart_side.png",
wield_image = "railcart_side.png",
on_place = function(itemstack, placer, pointed_thing)
local name = placer:get_player_name()
if not name or pointed_thing.type ~= "node" then
return
end
if not minetest.is_singleplayer() then
if not minetest.check_player_privs(name, {carts=true}) then
minetest.chat_send_player(name, "Requires carts privilege")
return
end
end
local pos = pointed_thing.under
if not railtrack:is_railnode(pos) then
return
end
local carts = railcart:get_carts_in_radius(pos, 0.9)
if #carts > 0 then
return
end
local id = railcart:get_new_id()
local ref = {
id = id,
inv = create_detached_inventory(id),
pos = vector.new(pos),
prev = vector.new(pos),
accel = railtrack:get_acceleration(pos),
dir = railcart:get_rail_direction(pos),
}
local cart = railcart.cart:new(ref)
table.insert(railcart.allcarts, cart)
railcart:save()
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
return itemstack
end,
})
minetest.register_privilege("carts", "Player can pick-up and place carts.")
minetest.register_craft({
output = "railcart:cart",
recipe = {
{"default:steel_ingot", "", "default:steel_ingot"},
{"default:steel_ingot", "", "default:steel_ingot"},
{"group:wood", "default:steel_ingot", "group:wood"},
},
})
minetest.register_on_shutdown(function()
railcart:save()
end)

View File

@ -1,444 +0,0 @@
local ENTITY_UPDATE_TIME = 1
local OBJECT_UPDATE_TIME = 5
local OBJECT_SAVE_TIME = 10
local RELOAD_DISTANCE = 32
local SNAP_DISTANCE = 0.5
local SPEED_MIN = 0.1
local SPEED_MAX = 15
railcart = {
timer = 0,
allcarts = {},
default_entity = {
physical = false,
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
visual = "mesh",
mesh = "railcart.b3d",
visual_size = {x=1, y=1},
textures = {"railcart.png"},
cart = nil,
driver = nil,
timer = 0,
},
}
railcart.cart = {
id = nil,
pos = nil,
target = nil,
prev = nil,
accel = nil,
inv = nil,
dir = {x=0, y=0, z=0},
vel = {x=0, y=0, z=0},
acc = {x=0, y=0, z=0},
timer = 0,
name = "railcart:cart_entity",
}
function railcart.cart:new(obj)
obj = obj or {}
setmetatable(obj, self)
self.__index = self
return obj
end
function railcart.cart:is_loaded()
for _, player in pairs(minetest.get_connected_players()) do
local pos = player:get_pos()
if pos then
local dist = railtrack:get_distance(pos, self.pos)
if dist <= RELOAD_DISTANCE then
return true
end
end
end
return false
end
function railcart.cart:on_step(dtime)
self.timer = self.timer - dtime
if self.timer > 0 then
return
end
self.timer = OBJECT_UPDATE_TIME
local entity = railcart:get_cart_entity(self.id)
if entity.object then
return
end
if self:is_loaded() then
local object = minetest.add_entity(self.pos, self.name)
if object then
entity = object:get_luaentity() or {}
entity.cart = self
object:setvelocity(self.vel)
object:set_acceleration(self.acc)
end
else
self.timer = railcart:update(self, self.timer)
end
end
function railcart:register_entity(name, def)
local ref = {}
for k, v in pairs(railcart.default_entity) do
ref[k] = def[k] or railtrack:copy(v)
end
ref.on_activate = function(self, staticdata, dtime_s)
if type(def.on_activate) == "function" then
def.on_activate(self, staticdata, dtime_s)
end
self.object:set_armor_groups({immortal=1})
if staticdata == "expired" then
self.object:remove()
end
end
ref.on_step = function(self, dtime)
if type(def.on_step) == "function" then
def.on_step(self, dtime)
end
local cart = self.cart
local object = self.object
if not cart or not object then
return
end
self.timer = self.timer - dtime
if self.timer > 0 then
return
end
self.timer = railcart:update(cart, ENTITY_UPDATE_TIME, object)
if type(def.on_update) == "function" then
def.on_update(self)
end
end
ref.get_staticdata = function(self)
if type(def.get_staticdata) == "function" then
def.get_staticdata(self)
end
if self.cart then
if self.cart:is_loaded() == false then
self.cart.timer = 0
self.object:remove()
end
end
return "expired"
end
for k, v in pairs(def) do
ref[k] = ref[k] or v
end
minetest.register_entity(name, ref)
end
function railcart:save()
local carts = {}
for id, cart in pairs(railcart.allcarts) do
local ref = {}
for k, v in pairs(cart) do
ref[k] = v
end
local inv = {}
if ref.inv then
local list = ref.inv:get_list("main")
for i, stack in ipairs(list) do
inv[i] = stack:to_string()
end
end
ref.inv = inv
table.insert(carts, ref)
end
local output = io.open(minetest.get_worldpath().."/railcart.txt",'w')
if output then
output:write(minetest.serialize(carts))
io.close(output)
end
end
function railcart:remove_cart(id)
for i, cart in pairs(railcart.allcarts) do
if cart.id == id then
railcart.allcarts[i] = nil
railcart:save()
break
end
end
end
function railcart:get_rail_direction(pos)
local target = nil
local cons = railtrack:get_connections(pos)
local ymax = pos.y
for _, con in pairs(cons) do
if con.y >= ymax then
ymax = con.y
target = con
end
end
if target then
if #cons == 1 then
target.y = pos.y
end
return railtrack:get_direction(target, pos)
end
return {x=0, y=0, z=0}
end
function railcart:get_new_id()
local id = 0
for _, cart in pairs(railcart.allcarts) do
if cart.id > id then
id = cart.id
end
end
return id + 1
end
function railcart:get_cart_ref(id)
for _, cart in pairs(railcart.allcarts) do
if cart.id == id then
return cart
end
end
end
function railcart:get_cart_entity(id)
local cart_ref = {}
for _, ref in pairs(minetest.luaentities) do
if ref.cart then
if ref.cart.id == id then
cart_ref = ref
break
end
end
end
return cart_ref
end
function railcart:get_carts_in_radius(pos, rad)
local carts = {}
for _, cart in pairs(railcart.allcarts) do
local px = pos.x - cart.pos.x
local py = pos.y - cart.pos.y
local pz = pos.z - cart.pos.z
if (px * px) + (py * py) + (pz * pz) <= rad * rad then
table.insert(carts, cart)
end
end
return carts
end
function railcart:get_cart_in_sight(p1, p2)
local ref = nil
local dist = railtrack:get_distance(p1, p2) + 1
local dir = railtrack:get_direction(p2, p1)
local carts = railcart:get_carts_in_radius(p1, dist)
for _, cart in pairs(carts) do
if not vector.equals(p1, cart.pos) then
local dc = railtrack:get_direction(cart.pos, p1)
if vector.equals(dc, dir) then
local d = railtrack:get_distance(p1, cart.pos)
if d < dist then
dist = d
ref = cart
end
end
end
end
return ref
end
function railcart:get_delta_time(vel, acc, dist)
if vel > 0 then
if acc == 0 then
return dist / vel
end
local r = math.sqrt(vel * vel + 2 * acc * dist)
if r > 0 then
return (-vel + r) / acc
end
end
return 9999 --INF
end
function railcart:velocity_to_dir(v)
if math.abs(v.x) > math.abs(v.z) then
return {x=railtrack:get_sign(v.x), y=railtrack:get_sign(v.y), z=0}
else
return {x=0, y=railtrack:get_sign(v.y), z=railtrack:get_sign(v.z)}
end
end
function railcart:velocity_to_speed(vel)
local speed = math.max(math.abs(vel.x), math.abs(vel.z))
if speed < SPEED_MIN then
speed = 0
elseif speed > SPEED_MAX then
speed = SPEED_MAX
end
return speed
end
function railcart:get_target(pos, vel)
local meta = minetest.get_meta(vector.round(pos))
local dir = self:velocity_to_dir(vel)
local targets = {}
local rots = railtrack.rotations
local contype = meta:get_string("contype") or ""
local s_junc = meta:get_string("junctions") or ""
local s_cons = meta:get_string("connections") or ""
local s_rots = meta:get_string("rotations") or ""
if contype == "section" then
local junctions = minetest.deserialize(s_junc) or {}
for _, p in pairs(junctions) do
table.insert(targets, p)
end
else
local cons = minetest.deserialize(s_cons) or {}
for _, p in pairs(cons) do
table.insert(targets, p)
end
if s_rots ~= "" then
local fwd = false
for _, p in pairs(cons) do
if vector.equals(vector.add(pos, dir), p) then
fwd = true
end
end
if fwd == true or #cons == 1 then
rots = s_rots
end
end
end
local rotations = railtrack:get_rotations(rots, dir)
for _, r in ipairs(rotations) do
for _, t in pairs(targets) do
local d = railtrack:get_direction(t, pos)
if r.x == d.x and r.z == d.z then
return t
end
end
end
end
function railcart:update(cart, time, object)
if object then
cart.pos = object:getpos()
cart.vel = object:get_velocity()
end
if not cart.target then
cart.pos = vector.new(cart.prev)
cart.target = railcart:get_target(cart.pos, cart.vel)
if object then
object:moveto(cart.pos)
end
end
local speed = railcart:velocity_to_speed(cart.vel)
if cart.target then
cart.dir = railtrack:get_direction(cart.target, cart.pos)
else
speed = 0
end
if speed > SPEED_MIN then
local blocked = false
local cis = railcart:get_cart_in_sight(cart.pos, cart.target)
if cis then
if railcart:velocity_to_speed(cis.vel) == 0 then
cart.target = vector.subtract(cis.pos, cart.dir)
blocked = true
end
end
--[[
if object then
local p1 = vector.add(cart.pos, {x=0, y=1, z=0})
local p2 = vector.add(cart.target, {x=0, y=1, z=0})
if minetest.get_node_or_nil(p2) then
local los, bp = minetest.line_of_sight(p1, p2)
if los == false then
bp.y = bp.y - 1
cart.target = vector.subtract(bp, cart.dir)
blocked = true
end
end
end
]]--
local d1 = railtrack:get_distance(cart.prev, cart.target)
local d2 = railtrack:get_distance(cart.prev, cart.pos)
local dist = d1 - d2
if dist > SNAP_DISTANCE then
local accel = railtrack.accel_flat
if cart.dir.y == -1 then
accel = railtrack.accel_down
elseif cart.dir.y == 1 then
accel = railtrack.accel_up
end
accel = cart.accel or accel
if object then
dist = math.max(dist - SNAP_DISTANCE, 0)
end
local dt = railcart:get_delta_time(speed, accel, dist)
if dt < time then
time = dt
end
local dp = speed * time + 0.5 * accel * time * time
local vf = speed + accel * time
if object then
if vf <= 0 then
speed = 0
accel = 0
end
cart.vel = vector.multiply(cart.dir, speed)
cart.acc = vector.multiply(cart.dir, accel)
elseif dp > 0 then
cart.vel = vector.multiply(cart.dir, vf)
cart.pos = vector.add(cart.pos, vector.multiply(cart.dir, dp))
end
else
if blocked and vector.equals(cart.target, cart.prev) then
cart.vel = {x=0, y=0, z=0}
cart.acc = {x=0, y=0, z=0}
else
cart.pos = vector.new(cart.target)
cart.prev = vector.new(cart.target)
cart.accel = railtrack:get_acceleration(cart.target)
cart.target = nil
return 0
end
end
else
cart.dir = railcart:get_rail_direction(cart.pos)
cart.vel = {x=0, y=0, z=0}
cart.acc = {x=0, y=0, z=0}
end
if object then
if cart.dir.y == -1 then
object:set_animation({x=1, y=1}, 1, 0)
elseif cart.dir.y == 1 then
object:set_animation({x=2, y=2}, 1, 0)
else
object:set_animation({x=0, y=0}, 1, 0)
end
if cart.dir.x < 0 then
object:setyaw(math.pi / 2)
elseif cart.dir.x > 0 then
object:setyaw(3 * math.pi / 2)
elseif cart.dir.z < 0 then
object:setyaw(math.pi)
elseif cart.dir.z > 0 then
object:setyaw(0)
end
object:setvelocity(cart.vel)
object:set_acceleration(cart.acc)
end
return time
end
minetest.register_globalstep(function(dtime)
for _, cart in pairs(railcart.allcarts) do
cart:on_step(dtime)
end
railcart.timer = railcart.timer + dtime
if railcart.timer > OBJECT_SAVE_TIME then
railcart:save()
railcart.timer = 0
end
end)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 B

View File

@ -1,14 +0,0 @@
Minetest Mod - Railtrack [railtrack]
====================================
License Source Code: LGPL v2.1
License of media (textures, sounds and models not otherwise specified): CC-0
Authors of media files:
-----------------------
rarkenin:
cart_rail_*.png

View File

@ -1,72 +0,0 @@
Minetest Mod - Railtrack [railtrack]
====================================
Minetest version: 0.4.13
Depends: default
Proof of concept rail networking system which enables rail-carts to travel
through unloaded map chunks.
Please note, this mod makes heavy use of metadata so that much of the processing
gets done as tracks are laid, for this reason a 'rails' privilege is required in
order to place rails when using multiplayer mode.
Crafting
--------
R = Rail [default:rail]
F = Mese Crystal Fragment [default:mese_crystal_fragment]
C = Coal Lump [default:coal_lump]
D = Diamond [default:diamond]
M = Mese Block [default:mese]
Powered Rail: [railtrack:powerrail]
By default these rails apply a positive acceleration during the time a cart
remains on a given section of track. When used with the mesecons mod the rails
will require power from a mesecon source to become active.
+---+---+---+
| R | F | R |
+---+---+---+
Braking Rail: [railtrack:brakerail]
By default these rails apply a negative acceleration during the time a cart
remains on a given section of track. When used with the mesecons mod the rails
will require power from a mesecon source to become active.
+---+---+---+
| R | C | R |
+---+---+---+
Superconducting Rail: [railtrack:superrail]
Zero friction rails that are easier to manage and are also a little less
cpu-intensive by avoiding the use of square root calculations.
+---+---+---+
| R | D | R |
+---+---+---+
Switching Rail: [railtrack:switchrail]
Currently depends on mesecons to do much other than provide a convenient, zero
friction union to join long sections of superconducting rails.
+---+---+---+
| R | M | R |
+---+---+---+
Misc Items
----------
Rail Fixer: [railtrack:fixer]
Only available in creative menu or with give(me) privileges.
Rail Inspector [railtrack:inspector]
Only available in creative menu or with give(me) privileges.

View File

@ -1 +0,0 @@
default

View File

@ -1,200 +0,0 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(modpath.."/railtrack.lua")
railtrack:register_rail(":default:rail", {
description = "Rail",
tiles = {"default_rail.png", "default_rail_curved.png",
"default_rail_t_junction.png", "default_rail_crossing.png"},
railtype = "rail",
})
railtrack:register_rail("railtrack:superrail", {
description = "Superconducting Rail",
tiles = {"carts_rail_sup.png", "carts_rail_curved_sup.png",
"carts_rail_t_junction_sup.png", "carts_rail_crossing_sup.png"},
railtype = "superrail",
acceleration = 0,
})
railtrack:register_rail("railtrack:powerrail", {
description = "Powered Rail",
tiles = {"carts_rail_pwr.png", "carts_rail_curved_pwr.png",
"carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"},
railtype = "powerrail",
acceleration = 4,
mesecons = {
effector = {
action_on = function(pos, node)
railtrack:set_acceleration(pos, 4)
end,
action_off = function(pos, node)
railtrack:set_acceleration(pos, nil)
end,
},
},
})
railtrack:register_rail("railtrack:brakerail", {
description = "Braking Rail",
tiles = {"carts_rail_brk.png", "carts_rail_curved_brk.png",
"carts_rail_t_junction_brk.png", "carts_rail_crossing_brk.png"},
railtype = "brakerail",
acceleration = -4,
mesecons = {
effector = {
action_on = function(pos, node)
railtrack:set_acceleration(pos, -4)
end,
action_off = function(pos, node)
railtrack:set_acceleration(pos, nil)
end,
},
},
})
railtrack:register_rail("railtrack:switchrail", {
description = "Switching Rail",
tiles = {"carts_rail_swt.png", "carts_rail_curved_swt.png",
"carts_rail_t_junction_swt.png", "carts_rail_crossing_swt.png"},
railtype = "switchrail",
acceleration = 0,
mesecons = {
effector = {
action_on = function(pos, node)
local meta = minetest.get_meta(pos)
meta:set_string("rotations", "RFLB")
end,
action_off = function(pos, node)
local meta = minetest.get_meta(pos)
meta:set_string("rotations", nil)
end,
},
},
})
minetest.register_privilege("rails", "Player can dig and place rails.")
minetest.register_tool("railtrack:fixer", {
description = "Rail Fixer",
inventory_image = "railtrack_fixer.png",
wield_image = "railtrack_fixer.png",
on_use = function(itemstack, user, pointed_thing)
local pos = minetest.get_pointed_thing_position(pointed_thing, false)
local name = user:get_player_name()
if not pos or not name or pointed_thing.type ~= "node" then
return
end
if not railtrack:is_railnode(pos) then
minetest.chat_send_player(name, "Not a rail node!")
return
end
if not minetest.is_singleplayer() then
if not minetest.check_player_privs(name, {rails=true}) then
minetest.chat_send_player(name, "Requires rails privilege")
return
end
end
local node = minetest.get_node(pos)
if node then
minetest.remove_node(pos)
minetest.set_node(pos, node)
local def = minetest.registered_items[node.name] or {}
local itemstack = ItemStack(node.name)
if type(def.after_place_node) == "function" then
def.after_place_node(pos, user, itemstack)
end
end
end,
})
minetest.register_tool("railtrack:inspector", {
description = "Rail Inspector",
inventory_image = "railtrack_inspector.png",
wield_image = "railtrack_inspector.png",
on_use = function(itemstack, user, pointed_thing)
local name = user:get_player_name()
local pos = minetest.get_pointed_thing_position(pointed_thing, false)
if not name or not pos then
return
end
local node = minetest.get_node(pos) or {}
if not railtrack:is_railnode(pos) or not node.name then
minetest.chat_send_player(name, "Not a rail node!")
return
end
local ref = minetest.registered_items[node.name] or {}
local meta = minetest.get_meta(pos)
local form = ""
local size = 2.5
form = form.."label[0.5,0.5;POS: "..minetest.pos_to_string(pos).."]"
local railtype = ref.railtype or "NIL"
form = form.."label[0.5,1.0;RAILTYPE: "..railtype.."]"
local contype = meta:get_string("contype") or "NIL"
form = form.."label[0.5,1.5;CONTYPE: "..contype.."]"
local accel = meta:get_string("acceleration") or "NIL"
form = form.."label[0.5,2.0;ACCEL: "..accel.."]"
local s_junc = meta:get_string("junctions")
if s_junc then
local junctions = minetest.deserialize(s_junc)
if junctions then
form = form.."label[0.5,2.5;JUNCTIONS:]"
for i, p in ipairs(junctions) do
size = size + 0.5
form = form.."label[0.5,"..size..";#"..i.." "
..minetest.pos_to_string(p).."]"
end
end
end
local s_cons = meta:get_string("connections")
if s_cons then
local cons = minetest.deserialize(s_cons)
if cons then
size = size + 0.5
form = form.."label[0.5,"..size..";CONNECTIONS:]"
for i, p in pairs(cons) do
size = size + 0.5
form = form.."label[0.5,"..size..";#"..i.." "
..minetest.pos_to_string(p).."]"
end
end
end
local s_rots = meta:get_string("rotations")
if s_rots then
size = size + 0.5
form = form.."label[0.5,"..size..";ROTATIONS: "..s_rots.."]"
end
form = form.."button_exit[3.0,"..(size + 1)..";2,0.5;;Ok]"
form = "size[8,"..(size + 2).."]"..form
minetest.show_formspec(name, "info", form)
end,
})
minetest.register_craft({
output = "railtrack:powerrail 2",
recipe = {
{"default:rail", "default:mese_crystal_fragment", "default:rail"},
}
})
minetest.register_craft({
output = "railtrack:brakerail 2",
recipe = {
{"default:rail", "default:coal_lump", "default:rail"},
}
})
minetest.register_craft({
output = "railtrack:switchrail 2",
recipe = {
{"default:rail", "default:mese", "default:rail"},
}
})
minetest.register_craft({
output = "railtrack:superrail 2",
recipe = {
{"default:rail", "default:diamond", "default:rail"},
}
})

View File

@ -1,288 +0,0 @@
local MAX_SECTION_LEN = 20
railtrack = {
rotations = "FLR",
accel_flat = -0.5,
accel_up = -2,
accel_down = 2,
}
railtrack.default_rail = {
description = "Rail",
drawtype = "raillike",
tiles = {"default_rail.png", "default_rail_curved.png",
"default_rail_t_junction.png", "default_rail_crossing.png"},
paramtype = "light",
sunlight_propagates = true,
walkable = false,
is_ground_content = false,
selection_box = {
type = "fixed",
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
},
groups = {bendy = 2, dig_immediate = 2, attached_node = 1,
connect_to_raillike = minetest.raillike_group("rail")},
railtype = "rail",
after_place_node = function(pos, placer, itemstack)
local meta = minetest.get_meta(pos)
local def = itemstack:get_definition() or {}
if def.acceleration then
meta:set_string("acceleration", def.acceleration)
end
local junc = {}
local contype = meta:get_string("contype") or ""
local s_cons = meta:get_string("connections") or ""
if contype == "section" then
railtrack:limit_section_len(placer, pos, meta)
elseif s_cons ~= "" then
local cons = minetest.deserialize(s_cons)
for _, con in pairs(cons) do
if railtrack:limit_section_len(placer, con) then
break
end
end
end
end,
on_construct = function(pos)
railtrack:update_rails(pos)
end,
after_destruct = function(pos, oldnode)
local cons = railtrack:get_connections(pos)
for _, p in pairs(cons) do
railtrack:update_rails(p)
end
end,
}
function railtrack:copy(t)
if type(t) ~= "table" then
return t
end
local copy = {}
for k, v in pairs(t) do
copy[k] = v
end
return copy
end
function railtrack:register_rail(name, def)
local udef = {}
for k, v in pairs(railtrack.default_rail) do
if not def[k] then
def[k] = railtrack:copy(v)
end
udef[k] = railtrack:copy(def[k])
end
def.inventory_image = def.inventory_image or def.tiles[1]
def.wield_image = def.wield_image or def.tiles[1]
minetest.register_node(name, def)
udef.drop = name
udef.railtype = udef.railtype.."_union"
udef.groups.not_in_creative_inventory=1
minetest.register_node(name.."_union", udef)
end
function railtrack:limit_section_len(player, pos, meta)
meta = meta or minetest.get_meta(pos)
local contype = meta:get_string("contype") or ""
if contype == "section" then
local s_junc = meta:get_string("junctions") or ""
if s_junc ~= "" then
local junc = minetest.deserialize(s_junc)
if #junc == 2 then
local dist = railtrack:get_distance(junc[1], junc[2])
if dist > MAX_SECTION_LEN then
local node = minetest.get_node(pos) or {}
if node.name then
minetest.swap_node(pos, {name=node.name.."_union"})
railtrack:update_rails(pos)
end
end
end
end
end
end
function railtrack:is_railnode(pos)
local node = minetest.get_node(pos)
if node then
return minetest.get_item_group(node.name, "connect_to_raillike") > 0
end
end
function railtrack:get_sign(z)
if z == 0 then
return 0
else
return z / math.abs(z)
end
end
function railtrack:get_rotations(s_rots, dir)
local rots = {}
for i = 1, string.len(s_rots) do
local r = string.sub(s_rots, i, i)
local rot = nil
if r == "F" then
rot = {x=dir.x, z=dir.z}
elseif r == "L" then
rot = {x=-dir.z, z=dir.x}
elseif r == "R" then
rot = {x=dir.z, z=-dir.x}
elseif r == "B" then
rot = {x=-dir.x, z=-dir.z}
end
if rot then
table.insert(rots, rot)
end
end
return rots
end
function railtrack:get_acceleration(pos)
local meta = minetest.get_meta(pos)
local accel = meta:get_string("acceleration") or ""
if accel ~= "" then
return tonumber(accel)
end
end
function railtrack:get_direction(p1, p2)
local v = vector.subtract(p1, p2)
return {
x = railtrack:get_sign(v.x),
y = railtrack:get_sign(v.y),
z = railtrack:get_sign(v.z),
}
end
function railtrack:get_distance(p1, p2)
local dx = p1.x - p2.x
local dz = p1.z - p2.z
return math.abs(dx) + math.abs(dz)
end
function railtrack:get_railtype(pos)
local node = minetest.get_node(pos) or {}
if node.name then
local ref = minetest.registered_items[node.name] or {}
return ref.railtype
end
end
function railtrack:get_connection_type(pos, cons)
local railtype = railtrack:get_railtype(pos)
if #cons == 0 then
return "single"
elseif #cons == 1 then
return "junction"
elseif #cons == 2 then
if cons[1].x == cons[2].x or cons[1].z == cons[2].z then
if (cons[1].y == cons[2].y and cons[1].y == pos.y) or
(math.abs(cons[1].y - cons[2].y) == 2) then
if railtype == railtrack:get_railtype(cons[1]) and
railtype == railtrack:get_railtype(cons[2]) then
return "section"
end
end
end
end
return "junction"
end
function railtrack:get_connections(pos)
local connections = {}
for y = 1, -1, -1 do
for x = -1, 1 do
for z = -1, 1 do
if math.abs(x) ~= math.abs(z) then
local p = vector.add(pos, {x=x, y=y, z=z})
if railtrack:is_railnode(p) then
table.insert(connections, p)
end
end
end
end
end
return connections
end
function railtrack:get_junctions(pos, last_pos, junctions)
junctions = junctions or {}
local cons = railtrack:get_connections(pos)
local contype = railtrack:get_connection_type(pos, cons)
if contype == "junction" then
table.insert(junctions, pos)
elseif contype == "section" then
if last_pos then
for i, p in pairs(cons) do
if vector.equals(p, last_pos) then
cons[i] = nil
end
end
end
for _, p in pairs(cons) do
railtrack:get_junctions(p, pos, junctions)
end
end
return junctions
end
function railtrack:set_acceleration(pos, accel)
local meta = minetest.get_meta(pos)
local contype = meta:get_string("contype")
if contype == "section" then
local s_junc = meta:get_string("junctions") or ""
local junc = minetest.deserialize(s_junc) or {}
if #junc == 2 then
local p = vector.new(junc[2])
local dir = railtrack:get_direction(junc[1], junc[2])
local dist = railtrack:get_distance(junc[1], junc[2])
for i = 0, dist do
local m = minetest.get_meta(p)
if m then
m:set_string("acceleration", tostring(accel))
end
p = vector.add(dir, p)
end
end
else
meta:set_string("acceleration", tostring(accel))
end
end
function railtrack:update_rails(pos, last_pos, level)
local connections = {}
local junctions = {}
local meta = minetest.get_meta(pos)
local cons = railtrack:get_connections(pos)
local contype = railtrack:get_connection_type(pos, cons)
level = level or 0
for i, p in pairs(cons) do
connections[i] = p
end
if contype == "junction" then
level = level + 1
end
if contype == "section" or level < 2 then
if last_pos then
for i, p in pairs(cons) do
if vector.equals(p, last_pos) then
cons[i] = nil
end
end
end
for _, p in pairs(cons) do
railtrack:update_rails(p, pos, level)
end
end
if contype == "section" then
junctions = railtrack:get_junctions(pos)
connections = {}
end
meta:set_string("connections", minetest.serialize(connections))
meta:set_string("junctions", minetest.serialize(junctions))
meta:set_string("contype", contype)
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 B