Carts: Merge boost_cart as "carts" mod
This is all the working code from SmallJoker's boost_cart, poored into a more suitable form for minetest_game. - Mesecons and moreores stuff was removed entirely. - Textures were all renamed and moved out of default/ - Updated license, readme.txt, attribution - Changed code license to MIT, left artwork at CC0 - removed default:rail and made aliases for it - :carts:rail is now carts:rail. - localized entity def - removed copper rail entirely - startstop rail was removed, as well as detector rail - remodeled to b3d using stujones11 excellent blend model, but sizes of cart adjusted to make pixel sizes consistent (0.625) everywhere. - slightly more complex texture map for the cart (front/side visibly different) - rail parameters are passed as a separate def table, and stored in a private list. This avoids having to call `get_meta` on every node. In return, we need the node name, though. - adds metal sounds (based on default metal sound function) and cart moving sound. - reduced cart speeds to max 7, 5 by pushing. - Added on_step() rail event handler, gets called when a cart is on a rail. - Added various rebased updates from upstream (thanks Krock) - Included a fix that removes the 'reverse jiggle' when stopping. - Included reworked textures by sofar. The mod namespace is still public, but I'm NOT declaring it an API. I'd rather see it localized instead, for now. Any public interface in this code is *experimental* at best, and should be considered non-stable and unsupported for now.
22
game_api.txt
|
@ -650,3 +650,25 @@ Trees
|
||||||
|
|
||||||
* `default.grow_new_snowy_pine_tree(pos)`
|
* `default.grow_new_snowy_pine_tree(pos)`
|
||||||
* Grows a new design snowy pine tree at pos
|
* Grows a new design snowy pine tree at pos
|
||||||
|
|
||||||
|
Carts
|
||||||
|
-----
|
||||||
|
|
||||||
|
carts.register_rail(
|
||||||
|
"mycarts:myrail", -- Rail name
|
||||||
|
nodedef, -- standard nodedef
|
||||||
|
railparams -- rail parameter struct (optional)
|
||||||
|
)
|
||||||
|
|
||||||
|
railparams = {
|
||||||
|
on_step(obj, dtime), -- Event handler called when
|
||||||
|
-- cart is on rail
|
||||||
|
acceleration, -- integer acceleration factor (negative
|
||||||
|
-- values to brake)
|
||||||
|
}
|
||||||
|
|
||||||
|
The event handler is called after all default calculations
|
||||||
|
are made, so the custom on_step handler can override things
|
||||||
|
like speed, acceleration, player attachment. The handler will
|
||||||
|
likely be called many times per second, so the function needs
|
||||||
|
to make sure that the event is handled properly.
|
||||||
|
|
20
mods/carts/README.txt
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Carts (formerly boost_cart)
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Cleaned up for merge based almost entirely on SmallJoker's boost_cart
|
||||||
|
mod (github.com/smalljoker/boost_cart).
|
||||||
|
|
||||||
|
That in turn was based on (and fully compatible with) the mod "carts"
|
||||||
|
by PilzAdam
|
||||||
|
|
||||||
|
The model was redone, but based on github.com/stujones11/railcart, CC-0
|
||||||
|
|
||||||
|
Cart Textures are based on original work from PixelBOX (WTFPL).
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
----------
|
||||||
|
- A fast cart for your railway or roller coaster (up to 7 m/s!)
|
||||||
|
- Boost and brake rails
|
||||||
|
- Rail junction switching with the 'right-left' walking keys
|
||||||
|
- Handbrake with the 'back' key
|
1
mods/carts/depends.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
default
|
221
mods/carts/functions.lua
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
function carts:get_sign(z)
|
||||||
|
if z == 0 then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return z / math.abs(z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function carts: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
|
||||||
|
player:set_attach(obj, "", {x=0, y=6, 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})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function carts:velocity_to_dir(v)
|
||||||
|
if math.abs(v.x) > math.abs(v.z) then
|
||||||
|
return {x=carts:get_sign(v.x), y=carts:get_sign(v.y), z=0}
|
||||||
|
else
|
||||||
|
return {x=0, y=carts:get_sign(v.y), z=carts:get_sign(v.z)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function carts:is_rail(pos, railtype)
|
||||||
|
local node = minetest.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 minetest.get_item_group(node, "rail") == 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not railtype then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return minetest.get_item_group(node, "connect_to_raillike") == railtype
|
||||||
|
end
|
||||||
|
|
||||||
|
function carts:check_front_up_down(pos, dir_, check_up, railtype)
|
||||||
|
local dir = vector.new(dir_)
|
||||||
|
local cur
|
||||||
|
|
||||||
|
-- Front
|
||||||
|
dir.y = 0
|
||||||
|
cur = vector.add(pos, dir)
|
||||||
|
if carts:is_rail(cur, railtype) then
|
||||||
|
return dir
|
||||||
|
end
|
||||||
|
-- Up
|
||||||
|
if check_up then
|
||||||
|
dir.y = 1
|
||||||
|
cur = vector.add(pos, dir)
|
||||||
|
if carts:is_rail(cur, railtype) then
|
||||||
|
return dir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Down
|
||||||
|
dir.y = -1
|
||||||
|
cur = vector.add(pos, dir)
|
||||||
|
if carts:is_rail(cur, railtype) then
|
||||||
|
return dir
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function carts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
|
local pos = vector.round(pos_)
|
||||||
|
local cur
|
||||||
|
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
|
||||||
|
|
||||||
|
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 = carts: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 = carts:check_front_up_down(pos, right, false, railtype)
|
||||||
|
if cur then
|
||||||
|
return cur, 2
|
||||||
|
end
|
||||||
|
right_check = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Normal
|
||||||
|
cur = carts:check_front_up_down(pos, dir, true, railtype)
|
||||||
|
if cur then
|
||||||
|
return cur
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Left, if not already checked
|
||||||
|
if left_check then
|
||||||
|
cur = carts: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 = carts:check_front_up_down(pos, right, false, railtype)
|
||||||
|
if cur then
|
||||||
|
return cur
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Backwards
|
||||||
|
if not old_switch then
|
||||||
|
cur = carts: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 carts:pathfinder(pos_, expected_pos, old_dir, ctrl, pf_switch, railtype)
|
||||||
|
local pos = vector.round(pos_)
|
||||||
|
local pf_pos = vector.round(expected_pos)
|
||||||
|
local pf_dir = vector.new(old_dir)
|
||||||
|
|
||||||
|
for i = 1, 3 do
|
||||||
|
if vector.equals(pf_pos, pos) then
|
||||||
|
-- Success! Cart moved on correctly
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
pf_dir, pf_switch = carts:get_rail_direction(pf_pos, pf_dir, ctrl, pf_switch, railtype)
|
||||||
|
if vector.equals(pf_dir, {x=0, y=0, z=0}) then
|
||||||
|
-- No way forwards
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
pf_pos = vector.add(pf_pos, pf_dir)
|
||||||
|
end
|
||||||
|
-- Cart not found
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function carts:register_rail(name, def, railparams)
|
||||||
|
local def_default = {
|
||||||
|
drawtype = "raillike",
|
||||||
|
paramtype = "light",
|
||||||
|
sunlight_propagates = true,
|
||||||
|
is_ground_content = true,
|
||||||
|
walkable = false,
|
||||||
|
selection_box = {
|
||||||
|
type = "fixed",
|
||||||
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
||||||
|
},
|
||||||
|
sounds = default.node_sound_metal_defaults()
|
||||||
|
}
|
||||||
|
for k, v in pairs(def_default) do
|
||||||
|
def[k] = v
|
||||||
|
end
|
||||||
|
if not def.inventory_image then
|
||||||
|
def.wield_image = def.tiles[1]
|
||||||
|
def.inventory_image = def.tiles[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
if railparams then
|
||||||
|
carts.railparams[name] = table.copy(railparams)
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_node(name, def)
|
||||||
|
end
|
||||||
|
|
||||||
|
function carts: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 type(additional_groups) == "table" then
|
||||||
|
for k, v in pairs(additional_groups) do
|
||||||
|
groups[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return groups
|
||||||
|
end
|
403
mods/carts/init.lua
Normal file
|
@ -0,0 +1,403 @@
|
||||||
|
|
||||||
|
carts = {}
|
||||||
|
carts.modpath = minetest.get_modpath("carts")
|
||||||
|
carts.railparams = {}
|
||||||
|
|
||||||
|
-- Maximal speed of the cart in m/s (min = -1)
|
||||||
|
carts.speed_max = 7
|
||||||
|
-- Set to -1 to disable punching the cart from inside (min = -1)
|
||||||
|
carts.punch_speed_max = 5
|
||||||
|
|
||||||
|
|
||||||
|
dofile(carts.modpath.."/functions.lua")
|
||||||
|
dofile(carts.modpath.."/rails.lua")
|
||||||
|
|
||||||
|
-- Support for non-default games
|
||||||
|
if not default.player_attached then
|
||||||
|
default.player_attached = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local cart_entity = {
|
||||||
|
physical = false, -- otherwise going uphill breaks
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
carts:manage_attachment(clicker, nil)
|
||||||
|
elseif not self.driver then
|
||||||
|
self.driver = player_name
|
||||||
|
carts:manage_attachment(clicker, self.object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cart_entity:on_activate(staticdata, dtime_s)
|
||||||
|
self.object:set_armor_groups({immortal=1})
|
||||||
|
if string.sub(staticdata, 1, string.len("return")) ~= "return" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local data = minetest.deserialize(staticdata)
|
||||||
|
if not data or type(data) ~= "table" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.railtype = data.railtype
|
||||||
|
if data.old_dir then
|
||||||
|
self.old_dir = data.old_dir
|
||||||
|
end
|
||||||
|
if data.old_vel then
|
||||||
|
self.old_vel = data.old_vel
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cart_entity:get_staticdata()
|
||||||
|
return minetest.serialize({
|
||||||
|
railtype = self.railtype,
|
||||||
|
old_dir = self.old_dir,
|
||||||
|
old_vel = self.old_vel
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function cart_entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
|
||||||
|
local pos = self.object:getpos()
|
||||||
|
if not self.railtype 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 = carts: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
|
||||||
|
if self.sound_handle then
|
||||||
|
minetest.sound_stop(self.sound_handle)
|
||||||
|
end
|
||||||
|
-- Pick up cart: Drop all attachments
|
||||||
|
if self.driver then
|
||||||
|
if self.old_pos then
|
||||||
|
self.object:setpos(self.old_pos)
|
||||||
|
end
|
||||||
|
local player = minetest.get_player_by_name(self.driver)
|
||||||
|
carts:manage_attachment(player, nil)
|
||||||
|
end
|
||||||
|
for _,obj_ in ipairs(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(self.object:getpos(), leftover)
|
||||||
|
end
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local vel = self.object:getvelocity()
|
||||||
|
if puncher:get_player_name() == self.driver then
|
||||||
|
if math.abs(vel.x + vel.z) > carts.punch_speed_max then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local punch_dir = carts:velocity_to_dir(puncher:get_look_dir())
|
||||||
|
punch_dir.y = 0
|
||||||
|
local cart_dir = carts: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 = 2 * (time_from_last_punch / punch_interval)
|
||||||
|
|
||||||
|
self.velocity = vector.multiply(cart_dir, f)
|
||||||
|
self.old_dir = cart_dir
|
||||||
|
self.punched = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rail_on_step_event(handler, obj, dtime)
|
||||||
|
if handler then
|
||||||
|
handler(obj, dtime)
|
||||||
|
end
|
||||||
|
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:getvelocity()
|
||||||
|
local speed = vector.length(vel)
|
||||||
|
if speed > 0 then
|
||||||
|
self.sound_handle = minetest.sound_play(
|
||||||
|
"carts_cart_moving", {
|
||||||
|
object = self.object,
|
||||||
|
gain = (speed / carts.speed_max) / 2,
|
||||||
|
loop = true,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rail_on_step(self, dtime)
|
||||||
|
local pos = self.object:getpos()
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
local railparams = carts.railparams[node.name] or {}
|
||||||
|
|
||||||
|
local vel = self.object:getvelocity()
|
||||||
|
local update = {}
|
||||||
|
if self.punched then
|
||||||
|
vel = vector.add(vel, self.velocity)
|
||||||
|
self.object:setvelocity(vel)
|
||||||
|
self.old_dir.y = 0
|
||||||
|
elseif vector.equals(vel, {x=0, y=0, z=0}) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- stop cart if velocity vector flips
|
||||||
|
if self.old_vel and (((self.old_vel.x * vel.x) < 0) or
|
||||||
|
((self.old_vel.z * vel.z) < 0)) and
|
||||||
|
(self.old_vel.y == 0) then
|
||||||
|
self.old_dir = {x = 0, y = 0, z = 0}
|
||||||
|
self.old_vel = {x = 0, y = 0, z = 0}
|
||||||
|
self.velocity = {x = 0, y = 0, z = 0}
|
||||||
|
self.old_pos = pos
|
||||||
|
self.object:setvelocity(vector.new())
|
||||||
|
self.object:setacceleration(vector.new())
|
||||||
|
rail_on_step_event(railparams.on_step, self, dtime)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.old_vel = vector.new(vel)
|
||||||
|
|
||||||
|
if self.old_pos and not self.punched 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
|
||||||
|
rail_on_step_event(railparams.on_step, self, dtime)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ctrl, player
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
if self.old_pos then
|
||||||
|
-- Detection for "skipping" nodes
|
||||||
|
local expected_pos = vector.add(self.old_pos, self.old_dir)
|
||||||
|
local found_path = carts:pathfinder(
|
||||||
|
pos, expected_pos, self.old_dir, ctrl, self.old_switch, self.railtype
|
||||||
|
)
|
||||||
|
|
||||||
|
if not found_path then
|
||||||
|
-- No rail found: reset back to the expected position
|
||||||
|
pos = vector.new(self.old_pos)
|
||||||
|
update.pos = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cart_dir = carts:velocity_to_dir(vel)
|
||||||
|
|
||||||
|
-- dir: New moving direction of the cart
|
||||||
|
-- switch_keys: Currently pressed L/R key, used to ignore the key on the next rail node
|
||||||
|
local dir, switch_keys = carts:get_rail_direction(
|
||||||
|
pos, cart_dir, ctrl, self.old_switch, self.railtype
|
||||||
|
)
|
||||||
|
|
||||||
|
local new_acc = {x=0, y=0, z=0}
|
||||||
|
if vector.equals(dir, {x=0, y=0, z=0}) then
|
||||||
|
vel = {x=0, y=0, z=0}
|
||||||
|
pos = vector.round(pos)
|
||||||
|
update.pos = true
|
||||||
|
update.vel = true
|
||||||
|
else
|
||||||
|
-- If the direction changed
|
||||||
|
if dir.x ~= 0 and self.old_dir.z ~= 0 then
|
||||||
|
vel.x = dir.x * math.abs(vel.z)
|
||||||
|
vel.z = 0
|
||||||
|
pos.z = math.floor(pos.z + 0.5)
|
||||||
|
update.pos = true
|
||||||
|
end
|
||||||
|
if dir.z ~= 0 and self.old_dir.x ~= 0 then
|
||||||
|
vel.z = dir.z * math.abs(vel.x)
|
||||||
|
vel.x = 0
|
||||||
|
pos.x = math.floor(pos.x + 0.5)
|
||||||
|
update.pos = true
|
||||||
|
end
|
||||||
|
-- Up, down?
|
||||||
|
if dir.y ~= self.old_dir.y then
|
||||||
|
vel.y = dir.y * math.abs(vel.x + vel.z)
|
||||||
|
pos = vector.round(pos)
|
||||||
|
update.pos = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Slow down or speed up..
|
||||||
|
local acc = dir.y * -4.0
|
||||||
|
|
||||||
|
-- no need to check for railparams == nil since we always make it exist.
|
||||||
|
local speed_mod = railparams.acceleration
|
||||||
|
if speed_mod and speed_mod ~= 0 then
|
||||||
|
-- Try to make it similar to the original carts mod
|
||||||
|
acc = acc + speed_mod
|
||||||
|
else
|
||||||
|
-- Handbrake
|
||||||
|
if ctrl and ctrl.down then
|
||||||
|
acc = acc - 1.6
|
||||||
|
else
|
||||||
|
acc = acc - 0.4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
new_acc = vector.multiply(dir, acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Limits
|
||||||
|
local max_vel = carts.speed_max
|
||||||
|
for _,v in ipairs({"x","y","z"}) do
|
||||||
|
if math.abs(vel[v]) > max_vel then
|
||||||
|
vel[v] = carts:get_sign(vel[v]) * max_vel
|
||||||
|
new_acc[v] = 0
|
||||||
|
update.vel = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.object:setacceleration(new_acc)
|
||||||
|
self.old_pos = vector.new(pos)
|
||||||
|
if not vector.equals(dir, {x=0, y=0, z=0}) then
|
||||||
|
self.old_dir = vector.new(dir)
|
||||||
|
end
|
||||||
|
self.old_switch = switch_keys
|
||||||
|
|
||||||
|
if self.punched then
|
||||||
|
-- Collect dropped items
|
||||||
|
for _,obj_ in ipairs(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
|
||||||
|
rail_on_step_event(railparams.on_step, self, dtime)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local yaw = 0
|
||||||
|
if self.old_dir.x < 0 then
|
||||||
|
yaw = 0.5
|
||||||
|
elseif self.old_dir.x > 0 then
|
||||||
|
yaw = 1.5
|
||||||
|
elseif self.old_dir.z < 0 then
|
||||||
|
yaw = 1
|
||||||
|
end
|
||||||
|
self.object:setyaw(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)
|
||||||
|
|
||||||
|
self.object:setvelocity(vel)
|
||||||
|
if update.pos then
|
||||||
|
self.object:setpos(pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- call event handler
|
||||||
|
rail_on_step_event(railparams.on_step, self, dtime)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cart_entity:on_step(dtime)
|
||||||
|
rail_on_step(self, dtime)
|
||||||
|
rail_sound(self, dtime)
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_entity("carts:cart", cart_entity)
|
||||||
|
|
||||||
|
minetest.register_craftitem("carts:cart", {
|
||||||
|
description = "Cart (Sneak+Click to pick up)",
|
||||||
|
inventory_image = minetest.inventorycube("carts_cart_top.png", "carts_cart_side.png", "carts_cart_side.png"),
|
||||||
|
wield_image = "carts_cart_side.png",
|
||||||
|
on_place = function(itemstack, placer, pointed_thing)
|
||||||
|
if not pointed_thing.type == "node" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if carts:is_rail(pointed_thing.under) then
|
||||||
|
minetest.add_entity(pointed_thing.under, "carts:cart")
|
||||||
|
elseif carts:is_rail(pointed_thing.above) then
|
||||||
|
minetest.add_entity(pointed_thing.above, "carts:cart")
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.sound_play({name = "default_place_node_metal", gain = 0.5},
|
||||||
|
{pos = pointed_thing.above})
|
||||||
|
|
||||||
|
if not minetest.setting_getbool("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"},
|
||||||
|
},
|
||||||
|
})
|
54
mods/carts/license.txt
Normal 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
|
BIN
mods/carts/models/carts_cart.b3d
Normal file
BIN
mods/carts/models/carts_cart.blend
Normal file
58
mods/carts/rails.lua
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
carts:register_rail("carts:rail", {
|
||||||
|
description = "Rail",
|
||||||
|
tiles = {
|
||||||
|
"carts_rail_straight.png", "carts_rail_curved.png",
|
||||||
|
"carts_rail_t_junction.png", "carts_rail_crossing.png"
|
||||||
|
},
|
||||||
|
inventory_image = "carts_rail_straight.png",
|
||||||
|
wield_image = "carts_rail_straight.png",
|
||||||
|
groups = carts:get_rail_groups(),
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "carts:rail 16",
|
||||||
|
recipe = {
|
||||||
|
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||||
|
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
|
||||||
|
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_alias("default:rail", "carts:rail")
|
||||||
|
|
||||||
|
carts: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 = carts:get_rail_groups(),
|
||||||
|
}, {acceleration = 4})
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "carts:powerrail 8",
|
||||||
|
recipe = {
|
||||||
|
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"},
|
||||||
|
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
|
||||||
|
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
carts: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 = carts:get_rail_groups(),
|
||||||
|
}, {acceleration = -3})
|
||||||
|
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "carts:brakerail 8",
|
||||||
|
recipe = {
|
||||||
|
{"default:steel_ingot", "default:coal_lump", "default:steel_ingot"},
|
||||||
|
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
|
||||||
|
{"default:steel_ingot", "default:coal_lump", "default:steel_ingot"},
|
||||||
|
}
|
||||||
|
})
|
BIN
mods/carts/sounds/carts_cart_moving.1.ogg
Normal file
BIN
mods/carts/sounds/carts_cart_moving.2.ogg
Normal file
BIN
mods/carts/sounds/carts_cart_moving.3.ogg
Normal file
BIN
mods/carts/textures/carts_cart.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
mods/carts/textures/carts_cart_front.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
mods/carts/textures/carts_cart_side.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
mods/carts/textures/carts_cart_top.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
mods/carts/textures/carts_rail_crossing.png
Normal file
After Width: | Height: | Size: 612 B |
BIN
mods/carts/textures/carts_rail_crossing_brk.png
Normal file
After Width: | Height: | Size: 684 B |
BIN
mods/carts/textures/carts_rail_crossing_pwr.png
Normal file
After Width: | Height: | Size: 676 B |
BIN
mods/carts/textures/carts_rail_curved.png
Normal file
After Width: | Height: | Size: 580 B |
BIN
mods/carts/textures/carts_rail_curved_brk.png
Normal file
After Width: | Height: | Size: 618 B |
BIN
mods/carts/textures/carts_rail_curved_pwr.png
Normal file
After Width: | Height: | Size: 614 B |
BIN
mods/carts/textures/carts_rail_straight.png
Normal file
After Width: | Height: | Size: 602 B |
BIN
mods/carts/textures/carts_rail_straight_brk.png
Normal file
After Width: | Height: | Size: 660 B |
BIN
mods/carts/textures/carts_rail_straight_pwr.png
Normal file
After Width: | Height: | Size: 661 B |
BIN
mods/carts/textures/carts_rail_t_junction.png
Normal file
After Width: | Height: | Size: 707 B |
BIN
mods/carts/textures/carts_rail_t_junction_brk.png
Normal file
After Width: | Height: | Size: 698 B |
BIN
mods/carts/textures/carts_rail_t_junction_pwr.png
Normal file
After Width: | Height: | Size: 697 B |
|
@ -22,7 +22,7 @@ minetest.register_alias("papyrus", "default:papyrus")
|
||||||
minetest.register_alias("bookshelf", "default:bookshelf")
|
minetest.register_alias("bookshelf", "default:bookshelf")
|
||||||
minetest.register_alias("glass", "default:glass")
|
minetest.register_alias("glass", "default:glass")
|
||||||
minetest.register_alias("wooden_fence", "default:fence_wood")
|
minetest.register_alias("wooden_fence", "default:fence_wood")
|
||||||
minetest.register_alias("rail", "default:rail")
|
minetest.register_alias("rail", "carts:rail")
|
||||||
minetest.register_alias("ladder", "default:ladder_wood")
|
minetest.register_alias("ladder", "default:ladder_wood")
|
||||||
minetest.register_alias("wood", "default:wood")
|
minetest.register_alias("wood", "default:wood")
|
||||||
minetest.register_alias("mese", "default:mese")
|
minetest.register_alias("mese", "default:mese")
|
||||||
|
|
|
@ -352,15 +352,6 @@ minetest.register_craft({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
|
||||||
output = 'default:rail 24',
|
|
||||||
recipe = {
|
|
||||||
{'default:steel_ingot', '', 'default:steel_ingot'},
|
|
||||||
{'default:steel_ingot', 'group:stick', 'default:steel_ingot'},
|
|
||||||
{'default:steel_ingot', '', 'default:steel_ingot'},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = 'default:chest',
|
output = 'default:chest',
|
||||||
recipe = {
|
recipe = {
|
||||||
|
|
|
@ -181,8 +181,6 @@ default:fence_aspen_wood
|
||||||
default:glass
|
default:glass
|
||||||
default:obsidian_glass
|
default:obsidian_glass
|
||||||
|
|
||||||
default:rail
|
|
||||||
|
|
||||||
default:brick
|
default:brick
|
||||||
|
|
||||||
default:meselamp
|
default:meselamp
|
||||||
|
@ -2058,27 +2056,6 @@ minetest.register_node("default:obsidian_glass", {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
minetest.register_node("default:rail", {
|
|
||||||
description = "Rail",
|
|
||||||
drawtype = "raillike",
|
|
||||||
tiles = {"default_rail.png", "default_rail_curved.png",
|
|
||||||
"default_rail_t_junction.png", "default_rail_crossing.png"},
|
|
||||||
inventory_image = "default_rail.png",
|
|
||||||
wield_image = "default_rail.png",
|
|
||||||
paramtype = "light",
|
|
||||||
sunlight_propagates = true,
|
|
||||||
walkable = false,
|
|
||||||
is_ground_content = false,
|
|
||||||
selection_box = {
|
|
||||||
type = "fixed",
|
|
||||||
-- but how to specify the dimensions for curved and sideways rails?
|
|
||||||
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
||||||
},
|
|
||||||
groups = {dig_immediate = 2, attached_node = 1,
|
|
||||||
connect_to_raillike = minetest.raillike_group("rail")},
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
minetest.register_node("default:brick", {
|
minetest.register_node("default:brick", {
|
||||||
description = "Brick Block",
|
description = "Brick Block",
|
||||||
paramtype2 = "facedir",
|
paramtype2 = "facedir",
|
||||||
|
|
Before Width: | Height: | Size: 242 B |
Before Width: | Height: | Size: 262 B |
Before Width: | Height: | Size: 241 B |
Before Width: | Height: | Size: 247 B |