Replace old cart mod on boost_cart
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
default
|
||||
mesecons?
|
||||
moreores?
|
||||
carts?
|
|
@ -0,0 +1 @@
|
|||
This mod offers improved minecarts and a few more rail types.
|
|
@ -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"},
|
||||
},
|
||||
})
|
|
@ -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
|
|
@ -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")
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
name = boost_cart
|
After Width: | Height: | Size: 989 B |
|
@ -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"},
|
||||
}
|
||||
})
|
|
@ -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
|
After Width: | Height: | Size: 376 B |
After Width: | Height: | Size: 398 B |
After Width: | Height: | Size: 245 B |
After Width: | Height: | Size: 403 B |
After Width: | Height: | Size: 513 B |
After Width: | Height: | Size: 577 B |
After Width: | Height: | Size: 577 B |
After Width: | Height: | Size: 397 B |
After Width: | Height: | Size: 579 B |
After Width: | Height: | Size: 240 B |
After Width: | Height: | Size: 378 B |
After Width: | Height: | Size: 417 B |
After Width: | Height: | Size: 457 B |
After Width: | Height: | Size: 458 B |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 474 B |
After Width: | Height: | Size: 243 B |
After Width: | Height: | Size: 439 B |
After Width: | Height: | Size: 478 B |
After Width: | Height: | Size: 516 B |
After Width: | Height: | Size: 516 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 518 B |
After Width: | Height: | Size: 415 B |
After Width: | Height: | Size: 402 B |
After Width: | Height: | Size: 501 B |
After Width: | Height: | Size: 526 B |
After Width: | Height: | Size: 528 B |
After Width: | Height: | Size: 402 B |
After Width: | Height: | Size: 568 B |
|
@ -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
|
||||
|
|
@ -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 |
|
||||
+---+---+---+
|
||||
|
|
@ -1 +0,0 @@
|
|||
railtrack
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 112 B |
Before Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 409 B |
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -1 +0,0 @@
|
|||
default
|
|
@ -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"},
|
||||
}
|
||||
})
|
||||
|
|
@ -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
|
||||
|
Before Width: | Height: | Size: 395 B |
Before Width: | Height: | Size: 358 B |
Before Width: | Height: | Size: 363 B |
Before Width: | Height: | Size: 365 B |
Before Width: | Height: | Size: 357 B |
Before Width: | Height: | Size: 351 B |
Before Width: | Height: | Size: 355 B |
Before Width: | Height: | Size: 354 B |
Before Width: | Height: | Size: 350 B |
Before Width: | Height: | Size: 390 B |
Before Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 385 B |
Before Width: | Height: | Size: 360 B |
Before Width: | Height: | Size: 361 B |
Before Width: | Height: | Size: 362 B |
Before Width: | Height: | Size: 347 B |
Before Width: | Height: | Size: 242 B |
Before Width: | Height: | Size: 149 B |
Before Width: | Height: | Size: 416 B |