minetest-go_play_modpack/carts/init.lua

822 lines
21 KiB
Lua

--=========
-- Maximum speed of the cart
--=========
local MAX_SPEED = 4.5
--=========
-- Transport the player like a normal item
-- Note: This is extremly laggy <- FIXME
--=========
TRANSPORT_PLAYER = true
--=========
-- The name of the Soundfile
--=========
SOUND_FILES = {
{"carts_curved_rails", 2},
{"carts_railway_crossover", 2},
{"carts_straight_rails", 1},
}
--=========
-- The sound gain
SOUND_GAIN = 0.8
--=========
--=========
-- Raillike nodes
--=========
RAILS = {"default:rail", "carts:meseconrail_off", "carts:meseconrail_on", "carts:meseconrail_stop_off", "carts:meseconrail_stop_on"}
dofile(minetest.get_modpath("carts").."/box.lua")
local cart = {
physical = true,
collisionbox = {-0.425, -0.425, -0.425, 0.425, 0.425, 0.425},
visual = "wielditem",
textures = {"carts:cart_box"},
visual_size = {x=0.85*2/3, y=0.85*2/3},
--Variables
fahren = false, -- true when the cart drives
fallen = false, -- true when the cart drives downhill
bremsen = false, -- true when the cart brakes
dir = nil, -- direction of the cart
old_dir = nil, -- saves the direction when the cart stops
items = {}, -- list with transported items
weiche = {x=nil, y=nil, z=nil}, -- saves the position of the railroad switch (to prevent double direction changes)
sound_handler = nil, -- soundhandler
}
-- Returns the current speed of the cart
function cart:get_speed()
if self.dir == "x+" then
return self.object:getvelocity().x
elseif self.dir == "x-" then
return -1*self.object:getvelocity().x
elseif self.dir == "z+" then
return self.object:getvelocity().z
elseif self.dir == "z-" then
return -1*self.object:getvelocity().z
end
return 0
end
-- Sets the current speed of the cart
function cart:set_speed(speed)
local newsp = {x=0, y=0, z=0}
if self.dir == "x+" then
newsp.x = speed
elseif self.dir == "x-" then
newsp.x = -1*speed
elseif self.dir == "z+" then
newsp.z = speed
elseif self.dir == "z-" then
newsp.z = -1*speed
end
self.object:setvelocity(newsp)
end
-- Sets the acceleration of the cart
function cart:set_acceleration(staerke)
if self.dir == "x+" then
self.object:setacceleration({x=staerke, y=-10, z=0})
elseif self.dir == "x-" then
self.object:setacceleration({x=-staerke, y=-10, z=0})
elseif self.dir == "z+" then
self.object:setacceleration({x=0, y=-10, z=staerke})
elseif self.dir == "z-" then
self.object:setacceleration({x=0, y=-10, z=-staerke})
end
end
-- Stops the cart
function cart:stop()
self.fahren = false
self.bremsen = false
self.items = {}
self.fallen = false
self.object:setacceleration({x = 0, y = -10, z = 0})
self:set_speed(0)
-- stop sound
self:sound("stop")
end
function cart:sound(arg)
if arg == "stop" then
if self.sound_handler ~= nil then
minetest.sound_stop(self.sound_handler)
self.sound_handler = nil
end
elseif arg == "continue" then
if self.sound_handler == nil then
return
end
minetest.sound_stop(self.sound_handler)
local sound = SOUND_FILES[math.random(1, #SOUND_FILES)]
self.sound_handler = minetest.sound_play(sound[1], {
object = self.object,
gain = SOUND_GAIN,
})
minetest.after(sound[2], function()
self:sound("continue")
end)
elseif arg == "start" then
local sound = SOUND_FILES[math.random(1, #SOUND_FILES)]
self.sound_handler = minetest.sound_play(sound[1], {
object = self.object,
gain = SOUND_GAIN,
})
minetest.after(sound[2], function()
self:sound("continue")
end)
end
end
-- Returns the direction the cart has to drive
function cart:get_new_direction(pos)
if pos == nil then
pos = self.object:getpos()
end
if self.dir == nil then
return nil
end
pos.x = math.floor(0.5+pos.x)
pos.y = math.floor(0.5+pos.y)
pos.z = math.floor(0.5+pos.z)
if self.fallen then
for i,rail in ipairs(RAILS) do
if minetest.env:get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == rail then
return "y-"
end
end
end
if self.dir == "x+" then
pos.x = pos.x+1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
pos.x = pos.x-1
local meta = minetest.env:get_meta(pos)
if meta:get_string("rail_direction") == "right" and not pos_equals(pos, self.weiche) then
pos.z = pos.z+1
for i,rail1 in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail1 then
self.weiche = {x=pos.x, y=pos.y, z=pos.z-1}
return "z+"
end
end
pos.z = pos.z-1
elseif meta:get_string("rail_direction") == "left" and not pos_equals(pos, self.weiche) then
pos.z = pos.z-1
for i,rail1 in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail1 then
self.weiche = {x=pos.x, y=pos.y, z=pos.z+1}
return "z-"
end
end
pos.z = pos.z+1
end
return "x+"
end
end
pos.y = pos.y-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "y-"
end
end
pos.y = pos.y+2
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "y+"
end
end
pos.y = pos.y-1
pos.x = pos.x-1
local tmp = minetest.env:get_meta(pos):get_string("rail_direction")
if tmp == "left" then
pos.z = pos.z+1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "z+"
end
end
pos.z = pos.z-1
elseif tmp == "right" then
pos.z = pos.z-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "z-"
end
end
pos.z = pos.z+1
end
pos.z = pos.z-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "z-"
end
end
pos.z = pos.z+2
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "z+"
end
end
pos.z = pos.z-1
elseif self.dir == "x-" then
pos.x = pos.x-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
pos.x = pos.x+1
local meta = minetest.env:get_meta(pos)
if meta:get_string("rail_direction") == "left" and not pos_equals(pos, self.weiche) then
pos.z = pos.z+1
for i,rail1 in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail1 then
self.weiche = {x=pos.x, y=pos.y, z=pos.z-1}
return "z+"
end
end
pos.z = pos.z-1
elseif meta:get_string("rail_direction") == "right" and not pos_equals(pos, self.weiche) then
pos.z = pos.z-1
for i,rail1 in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail1 then
self.weiche = {x=pos.x, y=pos.y, z=pos.z+1}
return "z-"
end
end
pos.z = pos.z+1
end
return "x-"
end
end
pos.y = pos.y-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "y-"
end
end
pos.y = pos.y+2
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "y+"
end
end
pos.y = pos.y-1
pos.x = pos.x+1
local tmp = minetest.env:get_meta(pos):get_string("rail_direction")
if tmp == "left" then
pos.z = pos.z-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "z-"
end
end
pos.z = pos.z+1
elseif tmp == "right" then
pos.z = pos.z+1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "z+"
end
end
pos.z = pos.z-1
end
pos.z = pos.z+1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "z+"
end
end
pos.z = pos.z-2
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "z-"
end
end
pos.z = pos.z+1
elseif self.dir == "z+" then
pos.z = pos.z+1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
pos.z = pos.z-1
local meta = minetest.env:get_meta(pos)
if meta:get_string("rail_direction") == "left" and not pos_equals(pos, self.weiche) then
pos.x = pos.x+1
for i,rail1 in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail1 then
self.weiche = {x=pos.x-1, y=pos.y, z=pos.z}
return "x+"
end
end
pos.x = pos.x-1
elseif meta:get_string("rail_direction") == "right" and not pos_equals(pos, self.weiche) then
pos.x = pos.x-1
for i,rail1 in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail1 then
self.weiche = {x=pos.x+1, y=pos.y, z=pos.z}
return "x-"
end
end
pos.x = pos.x+1
end
return "z+"
end
end
pos.y = pos.y-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "y-"
end
end
pos.y = pos.y+2
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "y+"
end
end
pos.y = pos.y-1
pos.z = pos.z-1
local tmp = minetest.env:get_meta(pos):get_string("rail_direction")
if tmp == "left" then
pos.x = pos.x-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "x-"
end
end
pos.x = pos.x+1
elseif tmp == "right" then
pos.x = pos.x+1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "x+"
end
end
pos.x = pos.x-1
end
pos.x = pos.x+1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "x+"
end
end
pos.x = pos.x-2
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "x-"
end
end
pos.x = pos.x+1
elseif self.dir == "z-" then
pos.z = pos.z-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
pos.z = pos.z+1
local meta = minetest.env:get_meta(pos)
if meta:get_string("rail_direction") == "right" and not pos_equals(pos, self.weiche) then
pos.x = pos.x+1
for i,rail1 in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail1 then
self.weiche = {x=pos.x-1, y=pos.y, z=pos.z}
return "x+"
end
end
pos.x = pos.x-1
elseif meta:get_string("rail_direction") == "left" and not pos_equals(pos, self.weiche) then
pos.x = pos.x-1
for i,rail1 in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail1 then
self.weiche = {x=pos.x+1, y=pos.y, z=pos.z}
return "x-"
end
end
pos.x = pos.x+1
end
return "z-"
end
end
pos.y = pos.y-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "y-"
end
end
pos.y = pos.y+2
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "y+"
end
end
pos.y = pos.y-1
pos.z = pos.z+1
local tmp = minetest.env:get_meta(pos):get_string("rail_direction")
if tmp == "left" then
pos.x = pos.x+1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "x+"
end
end
pos.x = pos.x-1
elseif tmp == "right" then
pos.x = pos.x-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "x-"
end
end
pos.x = pos.x+1
end
pos.x = pos.x-1
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "x-"
end
end
pos.x = pos.x+2
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
return "x+"
end
end
pos.x = pos.x-1
end
return nil
end
-- This method does several things.
function cart:on_step(dtime)
-- if the cart dont drives set gravity and return
if not self.fahren then
self.object:setacceleration({x=0, y=-10, z=0})
return
end
local newdir = self:get_new_direction()
if newdir == "x+" then
self.object:setyaw(0)
elseif newdir == "x-" then
self.object:setyaw(math.pi)
elseif newdir == "z+" then
self.object:setyaw(math.pi/2)
elseif newdir == "z-" then
self.object:setyaw(math.pi*3/2)
end
if newdir == nil and not self.fallen then
-- end of rail
-- chek if the cart derailed
local pos = self.object:getpos()
if self.dir == "x+" then
pos.x = pos.x-1
elseif self.dir == "x-" then
pos.x = pos.x+1
elseif self.dir == "z+" then
pos.z = pos.z-1
elseif self.dir == "z-" then
pos.z = pos.z+1
end
local checkdir = self:get_new_direction(pos)
if checkdir ~= self.dir and checkdir ~= nil then
self.object:setpos(pos)
self.dir = checkdir
self.old_dir = checkdir
-- change direction
local speed = self:get_speed()
self:set_speed(speed)
else
-- stop
self:stop()
local pos = self.object:getpos()
pos.x = math.floor(0.5+pos.x)
pos.z = math.floor(0.5+pos.z)
self.object:setpos(pos)
end
elseif newdir == "y+" then
-- uphill
self.fallen = false
local vel = self.object:getvelocity()
vel.y = MAX_SPEED
self.object:setvelocity(vel)
elseif newdir == "y-" then
-- downhill
local vel = self.object:getvelocity()
vel.y = -2*MAX_SPEED
self.object:setvelocity(vel)
self.fallen = true
elseif newdir ~= self.dir then
-- curve
self.fallen = false
local pos = self.object:getpos()
-- wait until the cart is nearly on the cornernode
if equals(pos.x, math.floor(0.5+pos.x)) and equals(pos.y, math.floor(0.5+pos.y)) and equals(pos.z, math.floor(0.5+pos.z)) then
-- "jump" exacly on the cornernode
pos.x = math.floor(0.5+pos.x)
pos.z = math.floor(0.5+pos.z)
self.object:setpos(pos)
-- change direction
local speed = self:get_speed()
self.dir = newdir
self.old_dir = newdir
self:set_speed(speed)
end
end
-- control speed and acceleration
if self.bremsen then
if not equals(self:get_speed(), 0) then
-- if the cart is still driving -> brake
self:set_acceleration(-10)
else
-- if the cart stand still -> stop
self:stop()
end
else
if self.fahren and self:get_speed() < MAX_SPEED then
-- if the cart is too slow -> accelerate
self:set_acceleration(10)
else
self:set_acceleration(0)
end
end
-- move items
for i,item in ipairs(self.items) do
if item:is_player() then
-- if the item is a player move him 0.5 blocks lowlier
local pos = self.object:getpos()
pos.y = pos.y-0.5
item:setpos(pos)
else
item:setpos(self.object:getpos())
if item:get_luaentity() ~= nil then
item:setvelocity(self.object:getvelocity())
end
end
end
-- if the cart isnt on a railroad switch reset the variable
local pos_tmp = self.object:getpos()
pos_tmp.x = math.floor(0.5+pos_tmp.x)
pos_tmp.y = math.floor(0.5+pos_tmp.y)
pos_tmp.z = math.floor(0.5+pos_tmp.z)
if not pos_equals(pos_tmp, self.weiche) then
self.weiche = {x=nil, y=nil, z=nil}
end
-- search for chests
for d=-1,1 do
local pos = {x=self.object:getpos().x+d, y=self.object:getpos().y, z=self.object:getpos().z}
local name1 = minetest.env:get_node(pos).name
pos = {x=self.object:getpos().x, y=self.object:getpos().y, z=self.object:getpos().z+d}
local name2 = minetest.env:get_node(pos).name
if name1 == "carts:chest" then
pos = {x=self.object:getpos().x+d, y=self.object:getpos().y, z=self.object:getpos().z}
elseif name2 == "carts:chest" then
pos = {x=self.object:getpos().x, y=self.object:getpos().y, z=self.object:getpos().z+d}
else
name1 = nil
end
if name1 ~= nil then
pos.x = math.floor(0.5+pos.x)
pos.y = math.floor(0.5+pos.y)
pos.z = math.floor(0.5+pos.z)
local inv = minetest.env:get_meta(pos):get_inventory()
-- drop items
local items_tmp = {}
local inv = minetest.env:get_meta(pos):get_inventory()
for i,item in ipairs(self.items) do
if not item:is_player() and item:get_luaentity().itemstring ~= nil and item:get_luaentity().itemstring ~= "" and inv:room_for_item("in", ItemStack(item:get_luaentity().itemstring)) then
if item:get_luaentity().pickup == nil or not pos_equals(pos, item:get_luaentity().pickup) then
inv:add_item("in", ItemStack(item:get_luaentity().itemstring))
item:remove()
else
table.insert(items_tmp, item)
end
else
table.insert(items_tmp, item)
end
end
self.items = items_tmp
--pick up items
for i=1,inv:get_size("out") do
local stack = inv:get_stack("out", i)
if not stack:is_empty() then
local item = minetest.env:add_entity(self.object:getpos(), "__builtin:item")
item:get_luaentity():set_item(stack:get_name().." "..stack:get_count())
item:get_luaentity().pickup = pos
table.insert(self.items, item)
inv:remove_item("out", stack)
end
end
end
end
-- mesecons functions
if minetest.get_modpath("mesecons") ~= nil then
local pos = self.object:getpos()
pos.x = math.floor(0.5+pos.x)
pos.y = math.floor(0.5+pos.y)
pos.z = math.floor(0.5+pos.z)
local name = minetest.env:get_node(pos).name
if name == "carts:meseconrail_off" then
minetest.env:set_node(pos, {name="carts:meseconrail_on"})
if mesecon ~= nil then
mesecon:receptor_on(pos)
end
end
if name == "carts:meseconrail_stop_on" then
self:stop()
local pos = self.object:getpos()
pos.x = math.floor(0.5+pos.x)
pos.z = math.floor(0.5+pos.z)
self.object:setpos(pos)
end
end
end
-- rightclick starts/stops the cart
function cart:on_rightclick(clicker)
if self.fahren then
self.bremsen = true
else
-- find out the direction
local pos_cart = self.object:getpos()
local pos_player = clicker:getpos()
local res = {x=pos_cart.x-pos_player.x, z=pos_cart.z-pos_player.z}
if math.abs(res.x) > math.abs(res.z) then
if res.x < 0 then
self.dir = "x-"
self.old_dir = "x-"
if self:get_new_direction() ~= "x-" then
if res.z < 0 then
self.dir = "z-"
self.old_dir = "z-"
else
self.dir = "z+"
self.old_dir = "z+"
end
if self:get_new_direction() ~= self.dir then
self.dir = "x-"
self.old_dir = "x-"
end
end
else
self.dir = "x+"
self.old_dir = "x+"
if self:get_new_direction() ~= "x+" then
if res.z < 0 then
self.dir = "z-"
self.old_dir = "z-"
else
self.dir = "z+"
self.old_dir = "z+"
end
if self:get_new_direction() ~= self.dir then
self.dir = "x+"
self.old_dir = "x+"
end
end
end
else
if res.z < 0 then
self.dir = "z-"
self.old_dir = "z-"
if self:get_new_direction() ~= "z-" then
if res.x < 0 then
self.dir = "x-"
self.old_dir = "x-"
else
self.dir = "x+"
self.old_dir = "x+"
end
if self:get_new_direction() ~= self.dir then
self.dir = "z-"
self.old_dir = "z-"
end
end
else
self.dir = "z+"
self.old_dir = "z+"
if self:get_new_direction() ~= "z+" then
if res.x < 0 then
self.dir = "x-"
self.old_dir = "x-"
else
self.dir = "x+"
self.old_dir = "x+"
end
if self:get_new_direction() ~= self.dir then
self.dir = "z+"
self.old_dir = "z+"
end
end
end
end
-- detect items
local tmp = minetest.env:get_objects_inside_radius(self.object:getpos(), 1)
for i,item in ipairs(tmp) do
if not item:is_player() and item:get_luaentity().name ~= "carts:cart" then
table.insert(self.items, item)
elseif item:is_player() and TRANSPORT_PLAYER then
table.insert(self.items, item)
end
end
-- start sound
self:sound("start")
self.fahren = true
end
end
-- remove the cart and place it in the inventory
function cart:on_punch(hitter)
-- stop sound
self:sound("stop")
self.object:remove()
hitter:get_inventory():add_item("main", "carts:cart")
end
-- save the probprties of the cart if unloaded
function cart:get_staticdata()
local str = tostring(self.fahren)
str = str..","
if self.fahren then
str = str..self.dir
end
self.object:setvelocity({x=0, y=0, z=0})
return str
end
-- set gravity
function cart:on_activate(staticdata)
self.object:setacceleration({x = 0, y = -10, z = 0})
self.items = {}
if staticdata ~= nil then
-- if the cart was unloaded
if string.find(staticdata, ",") ~= nil then
-- restore the probprties
if string.sub(staticdata, 1, string.find(staticdata, ",")-1)=="true" then
self.dir = string.sub(staticdata, string.find(staticdata, ",")+1)
self.old_dir = dir
self.fahren = true
end
end
end
end
minetest.register_entity("carts:cart", cart)
-- inventoryitem
minetest.register_craftitem("carts:cart", {
description = "Cart",
image = minetest.inventorycube("carts_cart_top.png", "carts_cart_side.png", "carts_cart_side.png"),
wield_image = "carts_cart_top.png",
stack_max = 1,
-- replace it with the object
on_place = function(itemstack, placer, pointed)
local pos = pointed.under
local bool = false
for i,rail in ipairs(RAILS) do
if minetest.env:get_node(pos).name == rail then
bool = true
end
end
if not bool then
pos = pointed.above
end
pos = {x = math.floor(0.5+pos.x), y = math.floor(0.5+pos.y), z = math.floor(0.5+pos.z)}
minetest.env:add_entity(pos, "carts:cart")
itemstack:take_item(1)
return itemstack
end,
})
minetest.register_craft({
output = '"carts:cart" 1',
recipe = {
{'default:steel_ingot', '', 'default:steel_ingot'},
{'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'}
}
})
dofile(minetest.get_modpath("carts").."/switches.lua")
dofile(minetest.get_modpath("carts").."/mesecons.lua")
dofile(minetest.get_modpath("carts").."/chest.lua")
dofile(minetest.get_modpath("carts").."/functions.lua")