Initial commit
168
README.md
Normal file
@ -0,0 +1,168 @@
|
||||
Railcarts Mod
|
||||
-------------
|
||||
|
||||
This is a Minetest mod for rails and carts, which is inspired by the
|
||||
Monorail mod by Sapier, and also on the Carts mod by PilzAdam, yet
|
||||
completely different to both. The design goals of this mod are different
|
||||
and there are also various fundamental differences, such as the player
|
||||
interaction controls and default behaviours of rails and other components.
|
||||
In particular, note the absence of "power rails".
|
||||
|
||||
Additionally, this mod will probably generate FAR MORE network traffic
|
||||
than those two mods. You have been warned.
|
||||
|
||||
It is also incompatible with both of those mods - you can run it at the
|
||||
same time, but each of the three mods will only recognise its own carts
|
||||
and specialised rail/control items.
|
||||
|
||||
Author: Ciaran Gultnieks
|
||||
License: LGPL
|
||||
|
||||
data_storage.lua, by Sapier, taken from the monorail mod.
|
||||
|
||||
Textures/Models (both in big need of improvement):
|
||||
WTFPL - PilzAdam, Sapier, Ciaran Gultnieks
|
||||
|
||||
Cart
|
||||
----
|
||||
|
||||
Carts are placed on the rails with right-click. Right-clicking a cart lets
|
||||
you board it. When you're in a cart, right-clicking it lets you get out.
|
||||
|
||||
If a situation arises where the cart is going to continue, but the player
|
||||
will collide with a block above the track, the player is automatically
|
||||
ejected from the cart.
|
||||
|
||||
Left-clicking a cart pushes it. Left-clicking with the sneak key held down
|
||||
removes it from the rails (back into your inventory).
|
||||
|
||||
The whole cart system is really intended to be fully automated - i.e. you
|
||||
select a destination and board a cart, and get dropped out when you
|
||||
arrive at your destination. However, the standard move of pushing the
|
||||
cart and jumping in is supported, as is punching the cart while you're in
|
||||
it to control its movement.
|
||||
|
||||
Cargo Cart
|
||||
----------
|
||||
|
||||
A Cargo Cart is effectively a chest on wheels. You can't ride in it.
|
||||
|
||||
When below a Hopper, the cart will get loaded with items until it's full,
|
||||
unless it's above an AutoLauncher, in which case it will unload into the
|
||||
hopper, unless that's full.
|
||||
|
||||
Boring Cart
|
||||
-----------
|
||||
|
||||
A boring cart is for boring tunnels, laying rails, building bridges, etc.
|
||||
It's mostly like a cargo cart, but it has two inventories - one for stuff
|
||||
it digs up, and one for materials it uses.
|
||||
|
||||
In the materials inventory go:
|
||||
* rails
|
||||
* wood for bridging
|
||||
* a mese pickaxe if you want it to dig
|
||||
|
||||
Additionally on the right-click form you will find controls for setting
|
||||
the direction (level, down or up), and enabling/disabling the various
|
||||
functions.
|
||||
|
||||
It operates when it reaches the end of the rails it's on. Be careful
|
||||
with that! (Generally, it's a good idea to have failsafe limits of
|
||||
operation set up using digital control rails (see below) and lua
|
||||
controllers, such that boring carts are restricted from leaving the
|
||||
particular part of the rail network they're working on.
|
||||
|
||||
Switching Rails
|
||||
---------------
|
||||
|
||||
Rail switch junctions are changed from their default direction to the
|
||||
opposite with the application of a mesecons signal.
|
||||
|
||||
To permanently switch a junction, burying a mesecons:switch beside it
|
||||
and turning it on is a good option. Covering the switch with a slab to
|
||||
prevent it being accidentally changed is also a good idea.
|
||||
|
||||
If using automated rail switching (a common example is with the combination
|
||||
of a digital control rail to detect and identify a cart, and a lua controller
|
||||
wired to the switch to change it) be aware that if MESECONS_GLOBALSTEP is set
|
||||
to true (the default) there can be a significant delay before the rail
|
||||
actually switches - sometimes after the cart has gone past. I just set it to
|
||||
false.
|
||||
|
||||
Control Rail
|
||||
------------
|
||||
|
||||
Control rails alter the behaviour of a cart that passes over. They are
|
||||
normally switched on, but can be switched off with a mesecons signal.
|
||||
|
||||
Right-clicking brings up a dialog where you can set what they do. Any of the
|
||||
following are valid:
|
||||
* "none" - has no effect
|
||||
* "maxspeed" - set the cart to its maximum possible speed
|
||||
* "speedup" - go faster (1m/s more)
|
||||
* "slowdown" - go slower
|
||||
* "stop" - these are quite obvious
|
||||
* "reverse" - aren't they
|
||||
* "tag <tag>" - set the 'tag' on the cart - each cart has one tag, which can
|
||||
be read back by Digital Control Rails. They're good for many things,
|
||||
including setting cart destinations when they will travel over a large
|
||||
network with many junctions.
|
||||
* "speed <n>" - set the cart's speed to the given value
|
||||
|
||||
Digital Control Rail
|
||||
--------------------
|
||||
|
||||
A digital control rail is a digilines-compatible rail, that sets cart controls
|
||||
to the same specification as a Control Rail, but via a digiline message rather
|
||||
than being a fixed setting.
|
||||
|
||||
In addition, it sends a digilines event when a cart passes over it,
|
||||
which includes the speed, direction, type of cart, and tag.
|
||||
|
||||
Right-click to set the channel.
|
||||
|
||||
Cart Detector
|
||||
-------------
|
||||
|
||||
A cart detector emits a mesecons signal for two seconds when a cart
|
||||
passes by. It is only ever triggered once per cart.
|
||||
|
||||
It can be placed below the rail, in which case it will trigger for
|
||||
carts passing both ways, or it can be placed to the side of the
|
||||
rail, in which case it will only trigger for carts who pass with
|
||||
the detector on their left.
|
||||
|
||||
Launcher
|
||||
--------
|
||||
|
||||
Launchers are intended to be placed at the end of a track. Applying mesecons
|
||||
power will cause an adjacent cart to be accelerated away down the track.
|
||||
|
||||
Autolauncher
|
||||
------------
|
||||
|
||||
An autolauncher a) operates like a chest, although it only makes sense to
|
||||
put carts in it, b) when there are rails leading away from it, or below
|
||||
it, (with no cart there) and there is a cart inside, puts a cart on the
|
||||
rails, c) when there is a cart adjacent to it, and there is a player on
|
||||
board, accelerates that cart away down the track, d) grabs a cart when is
|
||||
stationary with no player or cargo inside, on top of it, or below and
|
||||
adjacent, e) if there is one below the rails, and the cart has a player
|
||||
in, the player is ejected (and cargo is unloaded, see Hoppers/Cargo Carts)
|
||||
|
||||
Hopper
|
||||
------
|
||||
|
||||
A hopper is for loading and unloading cargo carts. It should be placed
|
||||
above the rails, with either:
|
||||
* An Autolauncher below the rails, for unloading (after which, the cart
|
||||
will be grabbed by the Autolauncher, and can then be sent elsewhere)
|
||||
* An Autolauncher adjacent, for loading (in which case, once the cart
|
||||
is loaded, it will set off, to be replaced with another for the next
|
||||
batch of goods.
|
||||
This might not make sense until you try it.
|
||||
|
||||
Pipeworks tubes connect to the top for filling the hopper. Filters are
|
||||
used to empty it.
|
||||
|
298
boringcart.lua
Normal file
@ -0,0 +1,298 @@
|
||||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
boringcart = {}
|
||||
|
||||
-- A table the indexes players against the last boring cart they right
|
||||
-- clicked, so we can know which cart to apply changes to when form
|
||||
-- fields are clicked. This seems mighty messy, but I don't see a better
|
||||
-- way of doing it.
|
||||
-- A hidden form field could work (if such a thing was possible) but in
|
||||
-- any case, that would allow a client to subvert the contents and apply
|
||||
-- changes to a different cart!
|
||||
boringcart.lastcartformbyplayer = {}
|
||||
|
||||
minetest.register_on_player_receive_fields(
|
||||
function(player, formname, fields)
|
||||
if formname ~= "railcarts:boring_cart" then return end
|
||||
if fields.quit then return end
|
||||
|
||||
local playername = player:get_player_name()
|
||||
dbg.v3("Recieved boring cart form submit from "..playername.." with "..dump(fields))
|
||||
local cart = boringcart.lastcartformbyplayer[playername]
|
||||
if not cart then return end
|
||||
|
||||
if fields.dig then
|
||||
if fields.dig == "true" then
|
||||
cart.dig = true
|
||||
else
|
||||
cart.dig = false
|
||||
end
|
||||
end
|
||||
|
||||
if fields.bridge then
|
||||
if fields.bridge == "true" then
|
||||
cart.bridge = true
|
||||
else
|
||||
cart.bridge = false
|
||||
end
|
||||
end
|
||||
|
||||
if fields.lay then
|
||||
if fields.lay == "true" then
|
||||
cart.lay = true
|
||||
else
|
||||
cart.lay = false
|
||||
end
|
||||
end
|
||||
|
||||
if fields.slope then
|
||||
if fields.slope == "Level" then
|
||||
cart.digslope = 2
|
||||
elseif fields.slope == "Up" then
|
||||
cart.digslope = 1
|
||||
elseif fields.slope == "Down" then
|
||||
cart.digslope = 3
|
||||
end
|
||||
end
|
||||
dbg.v1(playername.." set boring cart parameters dig:"..tostring(cart.dig)..
|
||||
",bridge:"..tostring(cart.bridge)..",lay:"..
|
||||
tostring(cart.lay)..",slope:"..cart.digslope)
|
||||
|
||||
boringcart.show_form(cart, player)
|
||||
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
function boringcart.show_form(self, player)
|
||||
local playername = player:get_player_name()
|
||||
boringcart.lastcartformbyplayer[playername] = self
|
||||
minetest.show_formspec(playername, "railcarts:boring_cart",
|
||||
"size[10,12;]"..
|
||||
"label[0,0;Boring cart cargo:]" ..
|
||||
"list[detached:" .. self.inventoryname .. ";main;0,1;4,3;]"..
|
||||
"label[6,0;Boring cart materials:]" ..
|
||||
"list[detached:" .. self.inventoryname .. ";materials;6,1;4,3;]"..
|
||||
"list[current_player;main;1,8;8,4;]"..
|
||||
"checkbox[0,4;dig;Dig;"..tostring(self.dig).."]"..
|
||||
"checkbox[3,4;bridge;Bridge;"..tostring(self.bridge).."]"..
|
||||
"checkbox[6,4;lay;Lay;"..tostring(self.lay).."]"..
|
||||
"dropdown[0,5;3;slope;Up,Level,Down;"..self.digslope.."]")
|
||||
end
|
||||
|
||||
function boringcart.on_end_of_rails(self, current_state, axis, oaxis, xz)
|
||||
|
||||
local laypos = poscopy(current_state.pos)
|
||||
laypos[axis] = laypos [axis] + xz[axis]
|
||||
|
||||
-- Have we got a mese pick? Without one, there will be no digging.
|
||||
local has_mesepick = self.inventory:contains_item("materials", "default:pick_mese 1")
|
||||
|
||||
local digposl
|
||||
if self.dig and has_mesepick then
|
||||
digposl = {}
|
||||
local nn
|
||||
|
||||
nn = poscopy(laypos)
|
||||
nn[oaxis] = nn[oaxis] - 1
|
||||
if self.digslope ~= 1 then
|
||||
table.insert(digposl, nn)
|
||||
end
|
||||
nn = poscopy(nn)
|
||||
nn.y = nn.y + 1
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn.y = nn.y + 1
|
||||
table.insert(digposl, nn)
|
||||
|
||||
nn = poscopy(laypos)
|
||||
nn[oaxis] = nn[oaxis] + 1
|
||||
if self.digslope ~= 1 then
|
||||
table.insert(digposl, nn)
|
||||
end
|
||||
nn = poscopy(nn)
|
||||
nn.y = nn.y + 1
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn.y = nn.y + 1
|
||||
table.insert(digposl, nn)
|
||||
|
||||
nn = poscopy(laypos)
|
||||
if self.digslope ~= 1 then
|
||||
table.insert(digposl, nn)
|
||||
end
|
||||
nn = poscopy(nn)
|
||||
nn.y = nn.y + 1
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn.y = nn.y + 1
|
||||
table.insert(digposl, nn)
|
||||
|
||||
if self.digslope == 3 then
|
||||
nn = poscopy(laypos)
|
||||
nn.y = nn.y - 1
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn[oaxis] = nn[oaxis] + 1
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn[oaxis] = nn[oaxis] - 2
|
||||
table.insert(digposl, nn)
|
||||
end
|
||||
|
||||
if self.digslope ~= 2 then
|
||||
-- Extra space above on slopes
|
||||
nn = poscopy(laypos)
|
||||
nn.y = nn.y + 3
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn[oaxis] = nn[oaxis] + 1
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn[oaxis] = nn[oaxis] - 2
|
||||
table.insert(digposl, nn)
|
||||
if self.digslope == 1 then
|
||||
-- More extra space above on up slopes
|
||||
nn = poscopy(laypos)
|
||||
nn.y = nn.y + 4
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn[oaxis] = nn[oaxis] + 1
|
||||
table.insert(digposl, nn)
|
||||
nn = poscopy(nn)
|
||||
nn[oaxis] = nn[oaxis] - 2
|
||||
table.insert(digposl, nn)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if digposl then
|
||||
for _, digpos in ipairs(digposl) do
|
||||
dignode = minetest.get_node(digpos)
|
||||
if dignode.name ~= "air" then
|
||||
|
||||
minetest.set_node(digpos, {name="air"})
|
||||
|
||||
-- TODO api docs say get_node_drops will be removed - why and to be replaced by what?
|
||||
-- TODO also, can we calculate the right tool wear here?
|
||||
local itemstacks = minetest.get_node_drops(dignode.name, "default:pick_mese")
|
||||
local full = false
|
||||
for _, item in ipairs(itemstacks) do
|
||||
if self.inventory:room_for_item("main", item) then
|
||||
self.inventory:add_item("main", item)
|
||||
else
|
||||
minetest.item_drop(item, "", digpos)
|
||||
full = true
|
||||
end
|
||||
end
|
||||
dbg.v2("Boring cart dug "..dignode.name.. " at "..postostr(digpos))
|
||||
if full then
|
||||
dbg.v2("Boring cart full at "..postostr(digpos))
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.digslope == 3 then
|
||||
laypos.y = laypos.y - 1
|
||||
elseif self.digslope == 1 then
|
||||
laypos.y = laypos.y + 1
|
||||
end
|
||||
laynode = minetest.get_node(laypos)
|
||||
|
||||
if laynode.name ~= "air" then
|
||||
dbg.v2("Boring cart blocked by "..laynode.name.. " at "..postostr(laypos))
|
||||
return false
|
||||
end
|
||||
|
||||
local laybelowpos = poscopy(laypos)
|
||||
laybelowpos.y = laybelowpos.y -1
|
||||
lbnode = minetest.get_node(laybelowpos)
|
||||
|
||||
if minetest.registered_nodes[lbnode.name].walkable then
|
||||
if not self.lay then
|
||||
dbg.v2("Boring cart at end of rails, ready to lay, but disabled at "..postostr(laypos))
|
||||
return false
|
||||
end
|
||||
if self.inventory:remove_item("materials", "default:rail 1"):get_count() == 1 then
|
||||
minetest.place_node(laypos, {name="default:rail"})
|
||||
dbg.v2("Cart laid rail at "..postostr(laypos))
|
||||
return true
|
||||
else
|
||||
dbg.v2("Boring cart out of rails at "..postostr(laypos))
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if lbnode.name == "air" then
|
||||
if self.bridge then
|
||||
if self.inventory:remove_item("materials", "default:wood 1"):get_count() == 1 then
|
||||
dbg.v2("Cart laid bridge at "..postostr(laypos))
|
||||
minetest.place_node(laypos, {name="default:wood"})
|
||||
return true
|
||||
else
|
||||
dbg.v2("Boring cart needs to bridge but has no wood at "..postostr(laypos))
|
||||
return false
|
||||
end
|
||||
else
|
||||
dbg.v2("Boring cart cannot lay track on "..lbnode.name.." at "..postostr(laypos))
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
dbg.v2("Boring cart doesn't know what to do with "..lbnode.name.." below and in front")
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
|
||||
minetest.register_entity("railcarts:boring_cart_ent", {
|
||||
physical = true,
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
visual = "mesh",
|
||||
textures = { "railcarts_tex_boringcart.png" },
|
||||
mesh= "railcarts_transport_cart.x",
|
||||
visual_size = {x=1,y=1,z=1},
|
||||
groups = { immortal=1, },
|
||||
|
||||
carttype = "boring",
|
||||
getitem = "railcarts:boring_cart",
|
||||
|
||||
on_step = cartbase.on_step_handler,
|
||||
on_punch = cartbase.on_punch_handler,
|
||||
on_activate = cartbase.on_activate_handler,
|
||||
get_staticdata = cartbase.get_staticdata_handler,
|
||||
on_end_of_rails = boringcart.on_end_of_rails,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
boringcart.show_form(self, clicker)
|
||||
return true
|
||||
end
|
||||
|
||||
})
|
||||
|
||||
minetest.register_craftitem("railcarts:boring_cart", {
|
||||
description = "Boring Cart",
|
||||
image = minetest.inventorycube("railcarts_inv_transportcart_top.png",
|
||||
"railcarts_inv_cart_side.png",
|
||||
"railcarts_inv_cart_side.png"),
|
||||
|
||||
on_place = function(item, placer, pointed_thing)
|
||||
return cartbase.place_cart(item, pointed_thing, "railcarts:boring_cart_ent")
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:boring_cart",
|
||||
recipe = {
|
||||
{"", "", ""},
|
||||
{"default:steel_ingot", "default:mese_crystal", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
},
|
||||
})
|
||||
|
54
cargocart.lua
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
minetest.register_entity("railcarts:cargo_cart_ent", {
|
||||
physical = true,
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
visual = "mesh",
|
||||
textures = { "railcarts_tex_transportcart.png" },
|
||||
mesh= "railcarts_transport_cart.x",
|
||||
visual_size = {x=1,y=1,z=1},
|
||||
groups = { immortal=1, },
|
||||
|
||||
carttype = "cargo",
|
||||
getitem = "railcarts:cargo_cart",
|
||||
|
||||
on_step = cartbase.on_step_handler,
|
||||
on_punch = cartbase.on_punch_handler,
|
||||
on_activate = cartbase.on_activate_handler,
|
||||
get_staticdata = cartbase.get_staticdata_handler,
|
||||
|
||||
on_rightclick = function(self,clicker)
|
||||
local playername = clicker:get_player_name()
|
||||
minetest.show_formspec(playername, "cargo_cart_formspec",
|
||||
"size[8,9;]"..
|
||||
"label[0,0;Cargo cart content:]" ..
|
||||
"list[detached:" .. self.inventoryname .. ";main;2,1;4,3;]"..
|
||||
"list[current_player;main;0,5;8,4;]")
|
||||
return true
|
||||
end
|
||||
|
||||
})
|
||||
|
||||
minetest.register_craftitem("railcarts:cargo_cart", {
|
||||
description = "Cargo Cart",
|
||||
image = minetest.inventorycube("railcarts_inv_transportcart_top.png",
|
||||
"railcarts_inv_cart_side.png",
|
||||
"railcarts_inv_cart_side.png"),
|
||||
|
||||
on_place = function(item, placer, pointed_thing)
|
||||
return cartbase.place_cart(item, pointed_thing, "railcarts:cargo_cart_ent")
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:cargo_cart",
|
||||
recipe = {
|
||||
{"", "", ""},
|
||||
{"default:steel_ingot", "default:chest", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
},
|
||||
})
|
||||
|
||||
|
52
cart.lua
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
minetest.register_entity("railcarts:cart_ent", {
|
||||
physical = true,
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
visual = "mesh",
|
||||
textures = { "railcarts_tex_cart.png" },
|
||||
mesh= "railcarts_cart.x",
|
||||
visual_size = {x=1,y=1,z=1},
|
||||
groups = { immortal=1, },
|
||||
|
||||
carttype = "passenger",
|
||||
getitem = "railcarts:cart",
|
||||
|
||||
on_activate = cartbase.on_activate_handler,
|
||||
get_staticdata = cartbase.get_staticdata_handler,
|
||||
on_step = cartbase.on_step_handler,
|
||||
on_punch = cartbase.on_punch_handler,
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
if not self.linkedplayer then
|
||||
self.linkedplayer = clicker
|
||||
clicker:set_attach(self.object, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
|
||||
cartbase.setdirection(self, nil, self.direction)
|
||||
else
|
||||
self.linkedplayer = nil
|
||||
clicker:set_detach()
|
||||
end
|
||||
end
|
||||
|
||||
})
|
||||
|
||||
minetest.register_craftitem("railcarts:cart", {
|
||||
description = "Cart",
|
||||
image = minetest.inventorycube("railcarts_inv_cart_top.png",
|
||||
"railcarts_inv_cart_side.png",
|
||||
"railcarts_inv_cart_side.png"),
|
||||
|
||||
on_place = function(item, placer, pointed_thing)
|
||||
return cartbase.place_cart(item, pointed_thing, "railcarts:cart_ent")
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:cart",
|
||||
recipe = {
|
||||
{"", "", ""},
|
||||
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
},
|
||||
})
|
||||
|
||||
|
835
cartbase.lua
Normal file
@ -0,0 +1,835 @@
|
||||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
cartbase = {}
|
||||
|
||||
--- Set a new direction
|
||||
-- This updates the passed in state, but also sets the cart's orientation and
|
||||
-- modifies the linked player's view.
|
||||
-- @param state The current cart state, which will be updated.
|
||||
-- Only the direction component is relevant.
|
||||
-- If nil, the player's direction is set directly rather
|
||||
-- than amended (e.g. boarding cart)
|
||||
-- @param newdir The new direction
|
||||
function cartbase.setdirection(self, state, newdir)
|
||||
|
||||
self.object:setyaw(direction_to_yaw(newdir))
|
||||
|
||||
if self.linkedplayer then
|
||||
if state then
|
||||
local dd = newdir - state.direction
|
||||
self.addplayeryaw = self.addplayeryaw - dd * (math.pi / 2)
|
||||
else
|
||||
self.linkedplayer:set_look_yaw(direction_to_yaw(newdir))
|
||||
end
|
||||
end
|
||||
|
||||
if state then
|
||||
state.direction = newdir
|
||||
else
|
||||
self.direction = newdir
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Determine if the cart is empty
|
||||
-- Empty means it can be picked up, which is not the case if there's
|
||||
-- someone riding in it, or cargo on board, etc.
|
||||
-- @return True if it's empty
|
||||
function cartbase.is_empty(self)
|
||||
if self.linkedplayer ~= nil then return false end
|
||||
if self.carttype == "cargo" then
|
||||
if not self.inventory:is_empty("main") then return false end
|
||||
elseif self.carttype == "boring" then
|
||||
if not self.inventory:is_empty("main") then return false end
|
||||
if not self.inventory:is_empty("materials") then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--- Handle step processing for a cart
|
||||
--@param self The cart
|
||||
--@param dtime Time since last call, in seconds
|
||||
function cartbase.on_step_handler(self, dtime)
|
||||
|
||||
if self.skipnextstep then
|
||||
self.skipnextstep = false
|
||||
self.dtime_debt = self.dtime_debt + dtime
|
||||
return
|
||||
end
|
||||
|
||||
-- No need to do any processing in a wait state.
|
||||
self.loadwait = self.loadwait + dtime
|
||||
if self.wait > 0 then
|
||||
self.wait = self.wait - dtime
|
||||
if self.wait >= 0 then return end
|
||||
-- Note, adding the negative remainder - we now process just the
|
||||
-- portion of the step left after the wait finished.
|
||||
dtime = dtime + self.wait
|
||||
self.wait = 0
|
||||
end
|
||||
|
||||
-- Add on 'dtime debt' (caused by us skipping slices on a previous
|
||||
-- step)
|
||||
dtime = dtime + self.dtime_debt
|
||||
self.dtime_debt = 0
|
||||
|
||||
self.lastmove = self.lastmove + dtime
|
||||
local oldpos
|
||||
if self.pos then
|
||||
oldpos = {x=self.pos.x, y=self.pos.y, z=self.pos.z}
|
||||
end
|
||||
|
||||
-- Keep track of this over all step slices and do it once, otherwise
|
||||
-- it goes wrong if it happens twice (i.e. very fast over two close
|
||||
-- together curves)
|
||||
self.addplayeryaw = 0
|
||||
|
||||
-- Maximum length of time processed in a singleslice. This is based on
|
||||
-- the maximum cart speed and ensures it doesn't move more than half (or
|
||||
-- exactly!) a node length in one slice call.
|
||||
-- Theoretically, the speed could increase during the first slice and
|
||||
-- make the movement too large in the second, but I'm not worrying about
|
||||
-- that for now. TODO
|
||||
local max_update_time = 1
|
||||
if self.speed > 0 then
|
||||
max_update_time = 0.45 / self.speed
|
||||
end
|
||||
|
||||
-- Do as many slices as we need to to keep the maximum distance moved
|
||||
-- below the threshold
|
||||
local nomore = false
|
||||
while(dtime > max_update_time and not nomore) do
|
||||
nomore = cartbase.update(self, max_update_time)
|
||||
dtime = dtime - max_update_time
|
||||
end
|
||||
if dtime >= 0 and not nomore then
|
||||
cartbase.update(self, dtime)
|
||||
dtime = 0
|
||||
end
|
||||
if nomore then
|
||||
-- TODO - seeing if skipping an extra step resolves the mesecons
|
||||
-- delay which occasionally causes rails to not be switched
|
||||
-- in time
|
||||
self.skipnextstep = true
|
||||
end
|
||||
self.dtime_debt = dtime
|
||||
|
||||
if self.addplayeryaw ~= 0 and self.linkedplayer then
|
||||
local curyaw = self.linkedplayer:get_look_yaw()
|
||||
if curyaw then
|
||||
-- Correct for api insanity
|
||||
curyaw = curyaw - math.pi / 2
|
||||
curyaw = curyaw + self.addplayeryaw
|
||||
local c = math.pi * 2
|
||||
while curyaw > c do curyaw = curyaw - c end
|
||||
while curyaw < 0 do curyaw = curyaw + c end
|
||||
self.linkedplayer:set_look_yaw(curyaw)
|
||||
end
|
||||
end
|
||||
|
||||
-- Call setpos if the position has changed
|
||||
if not oldpos or not v3equal(self.pos, oldpos) then
|
||||
self.object:setpos(self.pos)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Handle update for a cart.
|
||||
--@param self The cart
|
||||
--@param dtime Time since last call, in seconds, which should always be within
|
||||
-- low enough that the cart doesn't >= 0.5 nodes.
|
||||
--@return True if something happened which means no further step slices should
|
||||
-- be processed during this step. (Currently, this is specifically only
|
||||
-- when a digiline message is sent, because a common usage of that is
|
||||
-- to pass information to a luacontroller, which then uses mesecons to
|
||||
-- switch a rail. This will not necessarily have happened until the next
|
||||
-- server step - and hopefully by then!)
|
||||
function cartbase.update(self, dtime)
|
||||
|
||||
local no_more_slices = false
|
||||
|
||||
-- A little hack to stop double-grabbing. After self.object:remove() this
|
||||
-- handler can still get called, because of our step-slicing.
|
||||
if self.dead then return no_more_slices end
|
||||
|
||||
local state = {}
|
||||
if not self.pos then
|
||||
self.pos = self.object:getpos()
|
||||
end
|
||||
state.pos = self.pos
|
||||
|
||||
-- state.nodepos is the rounded node position, i.e. the centre of the node box
|
||||
state.nodepos = v3posround(state.pos)
|
||||
state.direction = self.direction
|
||||
state.speed = self.speed
|
||||
state.railstatus = get_railstatus(state.pos, state.direction, state.speed)
|
||||
-- Stop here if we're on an unloaded block. Hopefully it will load and we can just carry on.
|
||||
if is_unloaded(state.railstatus.railtype) then
|
||||
if self.speed == 0 then
|
||||
-- This can happen at speed 0, because we're inactive. Could have
|
||||
-- been placed by an active autolauncher, near the boundary with
|
||||
-- an inactive block, for example.
|
||||
self.wait = 2
|
||||
else
|
||||
-- It shouldn't ever happen when moving though, so long as we have
|
||||
-- autonomous support.
|
||||
dbg.v2("Waiting for current block load ("..state.railstatus.railtype..") at "..postostr(state.pos))
|
||||
self.wait = 1
|
||||
end
|
||||
return no_more_slices
|
||||
end
|
||||
|
||||
-- Eject a player if necessary
|
||||
if (state.railstatus.eject or state.railstatus.onautolauncher)
|
||||
and self.linkedplayer ~= nil then
|
||||
dbg.v1("Ejecting player from cart")
|
||||
self.linkedplayer:set_detach()
|
||||
self.linkedplayer:setpos(state.pos)
|
||||
self.linkedplayer = nil
|
||||
end
|
||||
|
||||
-- Grab the cart into something's inventory if necessary
|
||||
-- (only allowed when there is no passenger or cargo)
|
||||
if state.railstatus.grab and cartbase.is_empty(self) then
|
||||
dbg.v1("Node "..postostr(state.railstatus.grab).." grabbing "..self.carttype.." cart")
|
||||
local meta = minetest.get_meta(state.railstatus.grab)
|
||||
local inv = meta:get_inventory()
|
||||
if self.name == "railcarts:cart_ent" then
|
||||
inv:add_item("main", ItemStack("railcarts:cart"))
|
||||
elseif self.name == "railcarts:boring_cart_ent" then
|
||||
inv:add_item("main", ItemStack("railcarts:boring_cart"))
|
||||
else
|
||||
inv:add_item("main", ItemStack("railcarts:cargo_cart"))
|
||||
end
|
||||
self.object:remove()
|
||||
self.dead = true
|
||||
return no_more_slices
|
||||
end
|
||||
|
||||
-- Set speed/direction from launcher
|
||||
if state.railstatus.launch then
|
||||
cartbase.setdirection(self, state, state.railstatus.launch)
|
||||
state.speed = LAUNCH_CART_SPEED
|
||||
elseif state.railstatus.autolaunch and self.linkedplayer ~= nil then
|
||||
cartbase.setdirection(self, state, state.railstatus.autolaunch)
|
||||
state.speed = LAUNCH_CART_SPEED
|
||||
end
|
||||
|
||||
-- Handle loading/unloading from/to a hopper
|
||||
if (self.carttype == "cargo" or self.carttype == "boring") and state.railstatus.hopper and self.speed == 0 then
|
||||
local hpos = state.railstatus.hopper
|
||||
local meta = minetest.get_meta(hpos)
|
||||
if meta then
|
||||
local frominv = meta:get_inventory()
|
||||
local toinv = self.inventory
|
||||
local desc = "load"
|
||||
-- For now at least, loading is the default, and unloading happens
|
||||
-- when there is an autolauncher below.
|
||||
if state.railstatus.onautolauncher then
|
||||
local t = frominv
|
||||
frominv = toinv
|
||||
toinv = t
|
||||
desc = "unload"
|
||||
end
|
||||
if frominv:is_empty("main") then
|
||||
if self.loadwait > 600 and state.railstatus.autolaunch and desc == "load" and not toinv:is_empty("main") then
|
||||
cartbase.setdirection(self, state, state.railstatus.autolaunch)
|
||||
state.speed = LAUNCH_CART_SPEED
|
||||
dbg.v3("Cart at "..postostr(hpos).." waited 10 minutes for more cargo - launching")
|
||||
else
|
||||
if toinv:is_empty("main") then
|
||||
self.loadwait = 0
|
||||
dbg.v3("Hopper and cart at "..postostr(hpos).." are empty, waiting.")
|
||||
else
|
||||
dbg.v3("Hopper/cart at "..postostr(hpos).." is empty, waiting (for "..math.floor(self.loadwait).."s)")
|
||||
end
|
||||
self.wait = 5
|
||||
end
|
||||
else
|
||||
self.loadwait = 0
|
||||
for i, stack in ipairs(frominv:get_list("main")) do
|
||||
if stack:get_name() ~= "" then
|
||||
if toinv:room_for_item("main", stack) then
|
||||
-- Important to overwrite the slot here, not remove_item,
|
||||
-- otherwise if there are two items the same with metadata
|
||||
-- the wrong one can be removed.
|
||||
frominv:set_stack("main", i, ItemStack(nil))
|
||||
toinv:add_item("main", stack)
|
||||
dbg.v1("Cart "..desc.."ed "..stack:get_count().." "..stack:get_name().." at "..postostr(hpos))
|
||||
-- Counts as a move, for the purposes of remaining
|
||||
-- active
|
||||
self.lastmove = 0
|
||||
self.wait = 5
|
||||
break
|
||||
else
|
||||
if desc == "load" then
|
||||
if state.railstatus.autolaunch then
|
||||
cartbase.setdirection(self, state, state.railstatus.autolaunch)
|
||||
state.speed = LAUNCH_CART_SPEED
|
||||
dbg.v3("Cart at "..postostr(hpos).." is full - launching")
|
||||
else
|
||||
dbg.v3("Cart at "..postostr(hpos).." is full")
|
||||
self.wait = 30
|
||||
end
|
||||
else
|
||||
dbg.v3("Hopper at "..postostr(hpos).." is full, can't"..desc)
|
||||
self.wait = 30
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
self.loadwait = 0
|
||||
end
|
||||
|
||||
-- For safety - should never happen...
|
||||
if state.railstatus.railtype == "inv" and state.speed ~= 0 then
|
||||
dbg.v1("Stopping cart, it's off the rails at "..postostr(self.pos).." "..postostr(v3posround(self.pos)))
|
||||
state.speed = 0
|
||||
end
|
||||
|
||||
-- Move the cart
|
||||
if state.speed > 0 then
|
||||
|
||||
-- Get the status of the rails at our next location
|
||||
cartbase.get_next_railstatus(self, state, false)
|
||||
local drop = false
|
||||
if state.nextrailstatus.railtype == "inv" then
|
||||
-- If there's no next rail, maybe there's a drop (a.k.a a downward slope)
|
||||
cartbase.get_next_railstatus(self, state, true)
|
||||
if state.nextrailstatus.railtype ~= "inv" then drop = true end
|
||||
|
||||
end
|
||||
|
||||
-- Stop here if we're entering an unloaded block. Hopefully it will load
|
||||
-- and we can just carry on
|
||||
if is_unloaded(state.nextrailstatus.railtype) then
|
||||
dbg.v2("Waiting for next block load ("..state.nextrailstatus.railtype..") at "..
|
||||
postostr(state.nextrailstatus.unloadedpos)..
|
||||
" : block = "..
|
||||
math.floor(state.nextrailstatus.unloadedpos.x / 16)..","..
|
||||
math.floor(state.nextrailstatus.unloadedpos.y / 16)..","..
|
||||
math.floor(state.nextrailstatus.unloadedpos.z / 16))
|
||||
return no_more_slices
|
||||
end
|
||||
|
||||
local movedist = state.speed * dtime
|
||||
local xz = direction_to_xz(state.direction)
|
||||
local axis, oaxis
|
||||
if xz.x ~= 0 then
|
||||
axis = "x"
|
||||
oaxis = "z"
|
||||
else
|
||||
axis = "z"
|
||||
oaxis = "x"
|
||||
end
|
||||
local movein = false
|
||||
if (xz[axis] > 0 and state.pos[axis] < state.nodepos[axis]) or
|
||||
(xz[axis] < 0 and state.pos[axis] > state.nodepos[axis]) then
|
||||
movein = true
|
||||
end
|
||||
|
||||
dbg.v3("Cart on "..state.railstatus.railtype.." at "..postostr(state.pos)..
|
||||
", dir:"..state.direction.." speed:"..state.speed..
|
||||
" next rail:"..state.nextrailstatus.railtype..
|
||||
" "..(drop and "(drop)" or "")..
|
||||
" "..(movein and "(movein)" or "(moveout)"))
|
||||
|
||||
state.pos[axis] = state.pos[axis] + movedist * xz[axis]
|
||||
if movein then
|
||||
if (xz[axis] > 0 and state.pos[axis] >= state.nodepos[axis]) or
|
||||
(xz[axis] < 0 and state.pos[axis] <= state.nodepos[axis]) then
|
||||
|
||||
-- We were moving in to the centre, but now we've moved past it
|
||||
local past = math.abs(state.pos[axis] - state.nodepos[axis])
|
||||
|
||||
-- Trigger a detector?
|
||||
if state.railstatus.detector then
|
||||
local detpos = state.railstatus.detector
|
||||
minetest.add_node(detpos, {name="railcarts:cart_detector_on"})
|
||||
mesecon:receptor_on(detpos, mesecon.rules.default)
|
||||
dbg.v2("Triggered cart detector at "..postostr(newpos))
|
||||
end
|
||||
|
||||
-- Various control things. These are applied only at the centre
|
||||
-- of the node such that they only occur once.
|
||||
local con = state.railstatus.control
|
||||
if con then
|
||||
if con == "maxspeed" then
|
||||
dbg.v2("Max speed on control rail")
|
||||
state.speed = MAXIMUM_CART_SPEED
|
||||
elseif con == "speedup" then
|
||||
dbg.v2("Accelerating on control rail")
|
||||
state.speed = state.speed + 1
|
||||
elseif con == "slowdown" then
|
||||
dbg.v2("Decelerating on control rail")
|
||||
state.speed = state.speed - 1
|
||||
elseif con == "stop" then
|
||||
dbg.v2("Stopping on control rail")
|
||||
state.speed = 0
|
||||
elseif con == "reverse" then
|
||||
dbg.v2("Reversing on control rail")
|
||||
cartbase.setdirection(self, state,
|
||||
direction_reverse(state.direction))
|
||||
-- Move back to prevent re-trigger on the way back
|
||||
state.pos[axis] = state.pos[axis] - past
|
||||
elseif string.sub(con, 0, 6) == "speed " then
|
||||
local newspeed = tonumber(string.sub(con, 7))
|
||||
if newspeed then
|
||||
state.speed = newspeed
|
||||
dbg.v2("Setting speed to "..newspeed.." on control rail")
|
||||
else
|
||||
dbg.v1("Invalid speed from control rail")
|
||||
end
|
||||
elseif string.sub(con, 0, 4) == "tag " then
|
||||
self.tag = string.sub(con, 5)
|
||||
dbg.v2("Cart tagged '"..self.tag.."'")
|
||||
elseif con == "none" then
|
||||
-- doesn't do anything
|
||||
else
|
||||
dbg.v1("Invalid control rail setting")
|
||||
end
|
||||
|
||||
-- Cap speed appropriately
|
||||
if state.speed < 0 then state.speed = 0 end
|
||||
if state.speed > MAXIMUM_CART_SPEED then state.speed = MAXIMUM_CART_SPEED end
|
||||
end
|
||||
|
||||
-- Send digiline signal if required
|
||||
if digiline and state.railstatus.digiline then
|
||||
local dpos = state.railstatus.digiline
|
||||
local channel = minetest.get_meta(dpos):get_string("channel")
|
||||
if channel and channel ~= "" then
|
||||
msg = {}
|
||||
msg.tag = self.tag
|
||||
msg.speed = self.speed
|
||||
msg.direction = self.direction
|
||||
if self.linkedplayer then
|
||||
msg.passenger = self.linkedplayer:get_player_name()
|
||||
else
|
||||
msg.passenger = nil
|
||||
end
|
||||
msg.carttype = self.carttype
|
||||
dbg.v2("Sending digiline message on channel "..channel)
|
||||
digiline:receptor_send(dpos, digiline.rules.default, channel, msg)
|
||||
no_more_slices = true
|
||||
end
|
||||
end
|
||||
|
||||
if state.nextrailstatus.railtype == "inv" then
|
||||
|
||||
-- End of rails, so stop the cart (or do a custom action)
|
||||
state.pos[axis] = state.nodepos[axis]
|
||||
state.speed = 0
|
||||
|
||||
if self.on_end_of_rails then
|
||||
if self.on_end_of_rails(self, state, axis, oaxis, xz) then
|
||||
self.wait = 2
|
||||
state.speed = 1
|
||||
-- Back up a bit so we come back here for the next action
|
||||
state.pos[axis] = state.pos[axis] - (0.4 * xz[axis])
|
||||
end
|
||||
else
|
||||
dbg.v2("Cart reached end of rails and stopped at "..postostr(state.pos))
|
||||
end
|
||||
|
||||
elseif is_curve(state.railstatus.railtype) then
|
||||
|
||||
state.pos[axis] = state.nodepos[axis]
|
||||
local newdir
|
||||
if state.railstatus.railtype == "x+" then
|
||||
if axis == "x" then
|
||||
newdir = 2
|
||||
past = -past
|
||||
else
|
||||
newdir = 1
|
||||
end
|
||||
elseif state.railstatus.railtype == "x-" then
|
||||
if axis == "x" then
|
||||
newdir = 2
|
||||
past = -past
|
||||
else
|
||||
newdir = 3
|
||||
past = -past
|
||||
end
|
||||
elseif state.railstatus.railtype == "z+" then
|
||||
if axis == "x" then
|
||||
newdir = 0
|
||||
else
|
||||
newdir = 1
|
||||
end
|
||||
else
|
||||
if axis == "x" then
|
||||
newdir = 0
|
||||
else
|
||||
newdir = 3
|
||||
past = -past
|
||||
end
|
||||
end
|
||||
cartbase.setdirection(self, state, newdir)
|
||||
state.pos[oaxis] = state.pos[oaxis] + past
|
||||
dbg.v2("Cart turned a corner")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
--Moving out
|
||||
|
||||
local newnodepos = v3posround(state.pos)
|
||||
if state.nodepos[axis] ~= newnodepos[axis] then
|
||||
dbg.v3("New nodepos "..newnodepos[axis]..", old "..state.nodepos[axis])
|
||||
-- We've arrived in the new node
|
||||
|
||||
if drop then
|
||||
state.pos.y = state.pos.y - 1
|
||||
elseif state.direction == state.railstatus.slope then
|
||||
state.pos.y = state.pos.y + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Set ourselves autonomous if necessary (requires minetest patch)
|
||||
if state.speed > 0 then
|
||||
self.lastmove = 0
|
||||
end
|
||||
local autonomous = 0
|
||||
if self.lastmove < 10 then
|
||||
autonomous = 1
|
||||
end
|
||||
self.object:set_autonomous(autonomous)
|
||||
|
||||
-- Write things back. Ultimately the position will get 'broken' when core
|
||||
-- turns it from a double to a float. We don't care, because we use our
|
||||
-- own internally stored position as the reference.
|
||||
self.pos = state.pos
|
||||
self.speed = state.speed
|
||||
self.direction = state.direction
|
||||
|
||||
return no_more_slices
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Get the status of the rails at the next node
|
||||
--@param state The current state, which will have a next_railstatus
|
||||
-- field added. (And only railstatus, pos, speed and direction are relevant)
|
||||
--@param below True to look below instead of at the same level
|
||||
function cartbase.get_next_railstatus(self, state, below)
|
||||
|
||||
if state.speed == 0 then
|
||||
state.nextrailstatus = {railtype="inv"}
|
||||
return
|
||||
end
|
||||
|
||||
local xz, nexty
|
||||
if below then
|
||||
nexty = -1
|
||||
else
|
||||
if state.railstatus.slope then
|
||||
if state.railstatus.slope == state.direction then
|
||||
nexty = 1
|
||||
else
|
||||
nexty = 0
|
||||
end
|
||||
else
|
||||
nexty = 0
|
||||
end
|
||||
end
|
||||
|
||||
if is_curve(state.railstatus.railtype) then
|
||||
if state.railstatus.railtype == "x+" then
|
||||
if state.direction == 0 or state.direction == 1 then
|
||||
xz = {x=1,z=0}
|
||||
else
|
||||
xz = {x=0,z=-1}
|
||||
end
|
||||
elseif state.railstatus.railtype == "x-" then
|
||||
if state.direction == 0 or state.direction == 3 then
|
||||
xz = {x=-1,z=0}
|
||||
else
|
||||
xz = {x=0,z=-1}
|
||||
end
|
||||
elseif state.railstatus.railtype == "z+" then
|
||||
if state.direction == 2 or state.direction == 1 then
|
||||
xz = {x=1,z=0}
|
||||
else
|
||||
xz = {x=0,z=1}
|
||||
end
|
||||
else
|
||||
if state.direction == 2 or state.direction == 3 then
|
||||
xz = {x=-1,z=0}
|
||||
else
|
||||
xz = {x=0,z=1}
|
||||
end
|
||||
end
|
||||
else
|
||||
xz = direction_to_xz(state.direction)
|
||||
end
|
||||
|
||||
nextpos = v3posround(state.pos)
|
||||
nextpos.x = nextpos.x + xz.x
|
||||
nextpos.y = nextpos.y + nexty
|
||||
nextpos.z = nextpos.z + xz.z
|
||||
dbg.v3("Checking nextpos "..postostr(nextpos))
|
||||
state.nextrailstatus = get_railstatus(nextpos)
|
||||
end
|
||||
|
||||
|
||||
--- Handle cart being punched by a player
|
||||
--
|
||||
function cartbase.punch_move(self, own_pos, hitterpos)
|
||||
|
||||
self.wait = 0
|
||||
|
||||
local railstatus = get_railstatus(self.pos)
|
||||
-- Only when on rails...
|
||||
if railstatus.railtype == "inv" then return end
|
||||
|
||||
local xd = own_pos.x - hitterpos.x
|
||||
local zd = own_pos.z - hitterpos.z
|
||||
dbg.v2("Player punched cart with xd="..xd..",zd="..zd)
|
||||
|
||||
local newdir
|
||||
if railstatus.railtype == "x" then
|
||||
if xd < 0 then
|
||||
newdir = 3
|
||||
else
|
||||
newdir = 1
|
||||
end
|
||||
elseif railstatus.railtype == "z" then
|
||||
if zd < 0 then
|
||||
newdir = 2
|
||||
else
|
||||
newdir = 0
|
||||
end
|
||||
elseif railstatus.railtype == "x+" then
|
||||
if math.abs(zd) > math.abs(xd) and zd < 0 then
|
||||
newdir = 2
|
||||
elseif math.abs(xd) > math.abs(zd) and xd > 0 then
|
||||
newdir = 1
|
||||
end
|
||||
end
|
||||
-- TODO - the rest of the curves!
|
||||
if newdir then
|
||||
|
||||
-- Punching a moving cart in the same direction speeds it up
|
||||
if self.speed ~= 0 then
|
||||
if newdir == self.direction then
|
||||
self.speed = self.speed + 1
|
||||
if self.speed > MAXIMUM_CART_SPEED then
|
||||
self.speed = MAXIMUM_CART_SPEED
|
||||
end
|
||||
else
|
||||
-- otherwise it just stops...
|
||||
self.speed = 0
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
state = {direction=newdir, speed=1, railstatus=railstatus, pos=self.pos}
|
||||
cartbase.get_next_railstatus(self, state, false)
|
||||
if state.nextrailstatus.railtype == "inv" then
|
||||
-- check below, so we can push down a hill
|
||||
cartbase.get_next_railstatus(self, state, true)
|
||||
end
|
||||
if state.nextrailstatus.railtype ~= "inv" and
|
||||
not is_unloaded(state.nextrailstatus.railtype) then
|
||||
cartbase.setdirection(self, nil, newdir)
|
||||
self.speed = 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Handler for on_punch
|
||||
-- @@param hitter The player that hit it
|
||||
function cartbase.on_punch_handler(self, hitter)
|
||||
|
||||
if hitter:get_player_control().sneak and cartbase.is_empty(self) then
|
||||
hitter:get_inventory():add_item("main", self.getitem)
|
||||
self.object:remove()
|
||||
else
|
||||
local own_pos = self.object:getpos()
|
||||
local hitterpos = hitter:getpos()
|
||||
cartbase.punch_move(self,own_pos,hitterpos)
|
||||
end
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
--- Handler for on_activate
|
||||
-- @param staticdata Saved data if restoring a deactivated cart
|
||||
function cartbase.on_activate_handler(self, staticdata)
|
||||
|
||||
self.direction = 0
|
||||
self.speed = 0
|
||||
self.pos = nil
|
||||
self.tag = nil
|
||||
self.wait = 0
|
||||
self.loadwait = 0
|
||||
self.skipnextstep = false
|
||||
self.lastmove = 0
|
||||
self.dtime_debt = 0
|
||||
|
||||
-- These are parameters for the boring cart only...
|
||||
self.dig = false
|
||||
self.bridge = false
|
||||
self.lay = false
|
||||
self.digslope = 2
|
||||
|
||||
self.object:set_armor_groups(self.groups)
|
||||
|
||||
-- Set up detached inventories if required
|
||||
if self.carttype == "cargo" or self.carttype == "boring" then
|
||||
-- Unique name needed for detached inventory
|
||||
-- TODO - is there a better way of getting this unique ID?
|
||||
local uid
|
||||
for k, v in pairs(minetest.luaentities) do
|
||||
if v == self then
|
||||
uid = k
|
||||
break
|
||||
end
|
||||
end
|
||||
if not uid then
|
||||
dbg.v1("WARNING - unique ID was not generated - cart inventories may be shared")
|
||||
uuid = tostring(math.random())
|
||||
end
|
||||
self.inventoryname = uid
|
||||
dbg.v3("Creating new cart, inventory name is "..self.inventoryname)
|
||||
|
||||
self.inventory = minetest.create_detached_inventory(self.inventoryname, nil)
|
||||
self.inventory:set_size("main",12)
|
||||
if self.carttype == "boring" then
|
||||
self.inventory:set_size("materials",12)
|
||||
end
|
||||
end
|
||||
|
||||
local restored = minetest.deserialize(staticdata)
|
||||
|
||||
if restored ~= nil then
|
||||
|
||||
if restored.pos then
|
||||
self.pos = restored.pos
|
||||
else
|
||||
self.pos = self.object:getpos()
|
||||
end
|
||||
if restored.speed then
|
||||
self.speed = restored.speed
|
||||
end
|
||||
if restored.direction then
|
||||
self.direction = restored.direction
|
||||
end
|
||||
if restored.tag then
|
||||
self.tag = restored.tag
|
||||
end
|
||||
if restored.wait then
|
||||
self.wait = restored.wait
|
||||
end
|
||||
if restored.loadwait then
|
||||
self.loadwait = restored.loadwait
|
||||
end
|
||||
if restored.dig then
|
||||
self.dig = restored.dig
|
||||
end
|
||||
if restored.bridge then
|
||||
self.bridge = restored.bridge
|
||||
end
|
||||
if restored.lay then
|
||||
self.lay = restored.lay
|
||||
end
|
||||
if restored.digslope then
|
||||
self.digslope = restored.digslope
|
||||
end
|
||||
|
||||
local inv_main
|
||||
if restored.stacks then
|
||||
-- TODO legacy support, can remove in a bit
|
||||
inv_main = restored.stacks
|
||||
end
|
||||
inv_main = restored.inv_main
|
||||
if inv_main then
|
||||
for i=1,#inv_main,1 do
|
||||
self.inventory:set_stack("main",i,inv_main[i])
|
||||
end
|
||||
end
|
||||
|
||||
local inv_materials = restored.inv_materials
|
||||
if inv_materials then
|
||||
for i=1,#inv_materials,1 do
|
||||
self.inventory:set_stack("materials",i,inv_materials[i])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
self.object:set_armor_groups(self.groups)
|
||||
|
||||
end
|
||||
|
||||
--- Get static data for saving state on deactivation
|
||||
function cartbase.get_staticdata_handler(self)
|
||||
local tostore = {}
|
||||
|
||||
tostore.pos = self.pos
|
||||
tostore.speed = self.speed
|
||||
tostore.direction = self.direction
|
||||
tostore.tag = self.tag
|
||||
tostore.wait = self.wait
|
||||
tostore.loadwait = self.loadwait
|
||||
|
||||
if self.carttype == "cargo" or self.carttype == "boring" then
|
||||
tostore.inv_main = cartbase.get_inv(self, "main")
|
||||
end
|
||||
if self.carttype == "boring" then
|
||||
tostore.inv_materials = cartbase.get_inv(self, "materials")
|
||||
tostore.dig = self.dig
|
||||
tostore.bridge = self.bridge
|
||||
tostore.lay = self.lay
|
||||
tostore.digslope = self.digslope
|
||||
end
|
||||
|
||||
return minetest.serialize(tostore)
|
||||
|
||||
end
|
||||
|
||||
function cartbase.get_inv(self, name)
|
||||
local st = {}
|
||||
local list = self.inventory:get_list(name)
|
||||
for i=1,#list,1 do
|
||||
table.insert(st,list[i]:to_string())
|
||||
end
|
||||
return st
|
||||
end
|
||||
|
||||
|
||||
function cartbase.place_cart(item, pointed_thing, ent)
|
||||
if pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.above
|
||||
pos.y = pos.y - 1
|
||||
local railtype = get_railstatus(pos).railtype
|
||||
if railtype ~= "inv" then
|
||||
|
||||
local obj = minetest.add_entity(pos, ent)
|
||||
if obj then
|
||||
if railtype == "x" then
|
||||
obj:setyaw(0)
|
||||
elseif railtype == "z" then
|
||||
obj:setyaw(0 + math.pi/2)
|
||||
end
|
||||
item:take_item()
|
||||
end
|
||||
end
|
||||
end
|
||||
return item
|
||||
end
|
||||
|
98
controlrail.lua
Normal file
@ -0,0 +1,98 @@
|
||||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:controlrail 12",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:stick", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("railcarts:controlrail", {
|
||||
description = "Control Rail",
|
||||
drawtype = "raillike",
|
||||
tiles = {"railcarts_pa_carts_rail_pwr.png",
|
||||
"railcarts_pa_carts_rail_curved_pwr.png",
|
||||
"railcarts_pa_carts_rail_t_junction_pwr.png",
|
||||
"railcarts_pa_carts_rail_crossing_pwr.png"},
|
||||
inventory_image = "railcarts_pa_carts_rail_pwr.png",
|
||||
wield_image = "railcarts_pa_carts_rail_pwr.png",
|
||||
paramtype = "light",
|
||||
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},
|
||||
},
|
||||
groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},
|
||||
|
||||
mesecons = {
|
||||
effector = {
|
||||
action_on = function(pos, node)
|
||||
node.name = "railcarts:controlrail_off"
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
}
|
||||
},
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"field[fn;Control;${infotext}]")
|
||||
meta:set_string("infotext", "speedup")
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
if fields and fields.fn then
|
||||
dbg.v2("Control rail at "..postostr(pos).." set to "..fields.fn.." by "..(sender:get_player_name() or ""))
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", fields.fn)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_node("railcarts:controlrail_off", {
|
||||
description = "Control Rail",
|
||||
drawtype = "raillike",
|
||||
tiles = {"railcarts_pa_carts_rail_brk.png",
|
||||
"railcarts_pa_carts_rail_curved_brk.png",
|
||||
"railcarts_pa_carts_rail_t_junction_brk.png",
|
||||
"railcarts_pa_carts_rail_crossing_brk.png"},
|
||||
drop = 'railcarts:controlrail 1',
|
||||
paramtype = "light",
|
||||
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},
|
||||
},
|
||||
groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},
|
||||
|
||||
mesecons = {
|
||||
effector = {
|
||||
action_off = function(pos, node)
|
||||
node.name = "railcarts:controlrail"
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
}
|
||||
},
|
||||
on_construct = controlrail_construct,
|
||||
on_receive_fields = controlrail_receive
|
||||
})
|
||||
|
||||
function controlrail_construct(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"field[fn;Control;${infotext}]")
|
||||
meta:set_string("infotext", "speedup")
|
||||
end
|
||||
|
||||
function controlrail_receive(pos, formname, fields, sender)
|
||||
if fields and fields.fn then
|
||||
dbg.v2("Control rail at "..postostr(pos).." set to "..fields.fn.." by "..(sender:get_player_name() or ""))
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", fields.fn)
|
||||
end
|
||||
end
|
||||
|
109
data_storage.lua
Normal file
@ -0,0 +1,109 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- Monorail Mod by Sapier
|
||||
--
|
||||
-- You may copy, use, modify or do nearly anything except removing this
|
||||
-- copyright notice.
|
||||
-- And of course you are NOT allow to pretend you have written it.
|
||||
--
|
||||
--! @file data_storage.lua
|
||||
--! @brief generic functions used in many different places
|
||||
--! @copyright Sapier
|
||||
--! @author Sapier
|
||||
--! @date 2013-02-04
|
||||
--!
|
||||
-- Contact sapier a t gmx net
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- name: railcarts_get_current_time()
|
||||
--
|
||||
--! @brief alias to get current time
|
||||
--
|
||||
--! @return current time in seconds
|
||||
-------------------------------------------------------------------------------
|
||||
function railcarts_get_current_time()
|
||||
return os.time(os.date('*t'))
|
||||
--return minetest.get_time()
|
||||
end
|
||||
-------------------------------------------------------------------------------
|
||||
-- name: railcarts_global_data_store(value)
|
||||
--
|
||||
--! @brief save data and return unique identifier
|
||||
--
|
||||
--! @param value to save
|
||||
--
|
||||
--! @return unique identifier
|
||||
-------------------------------------------------------------------------------
|
||||
railcarts_global_data_identifier = 0
|
||||
railcarts_global_data = {}
|
||||
railcarts_global_data.cleanup_index = 0
|
||||
railcarts_global_data.last_cleanup = railcarts_get_current_time()
|
||||
function railcarts_global_data_store(value)
|
||||
|
||||
local current_id = railcarts_global_data_identifier
|
||||
|
||||
railcarts_global_data_identifier = railcarts_global_data_identifier + 1
|
||||
|
||||
railcarts_global_data[current_id] = {
|
||||
value = value,
|
||||
added = railcarts_get_current_time(),
|
||||
}
|
||||
return current_id
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- name: railcarts_global_data_store(value)
|
||||
--
|
||||
--! @brief pop data from global store
|
||||
--
|
||||
--! @param id to pop
|
||||
--
|
||||
--! @return stored value
|
||||
-------------------------------------------------------------------------------
|
||||
function railcarts_global_data_get(id)
|
||||
|
||||
local dataid = tonumber(id)
|
||||
|
||||
if dataid == nil or
|
||||
railcarts_global_data[dataid] == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local retval = railcarts_global_data[dataid].value
|
||||
railcarts_global_data[dataid] = nil
|
||||
return retval
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- name: railcarts_global_data_store(value)
|
||||
--
|
||||
--! @brief pop data from global store
|
||||
--
|
||||
--! @param id to pop
|
||||
--
|
||||
--! @return stored value
|
||||
-------------------------------------------------------------------------------
|
||||
function railcarts_global_data_cleanup(id)
|
||||
|
||||
if railcarts_global_data.last_cleanup + 500 < railcarts_get_current_time() then
|
||||
|
||||
for i=1,50,1 do
|
||||
if railcarts_global_data[railcarts_global_data.cleanup_index] ~= nil then
|
||||
if railcarts_global_data[railcarts_global_data.cleanup_index].added <
|
||||
railcarts_get_current_time() - 300 then
|
||||
|
||||
railcarts_global_data[railcarts_global_data.cleanup_index] = nil
|
||||
end
|
||||
railcarts_global_data.cleanup_index = railcarts_global_data.cleanup_index +1
|
||||
|
||||
if railcarts_global_data.cleanup_index > #railcarts_global_data then
|
||||
railcarts_global_data.cleanup_index = 0
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
railcarts_global_data.last_cleanup = railcarts_get_current_time()
|
||||
end
|
||||
end
|
70
defaultrail.lua
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
local railrules = {
|
||||
{x = 0, y = -1, z = 0},
|
||||
{x = -1, y = 0, z = 0},
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = 0, y = 0, z = 1},
|
||||
{x = 0, y = 0, z = -1},
|
||||
{x = -1, y = -1, z = 0},
|
||||
{x = 1, y = -1, z = 0},
|
||||
{x = 0, y = -1, z = 1},
|
||||
{x = 0, y = -1, z = -1},
|
||||
}
|
||||
|
||||
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",
|
||||
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},
|
||||
},
|
||||
groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},
|
||||
mesecons = {
|
||||
effector = {
|
||||
rules = railrules,
|
||||
action_on =
|
||||
function(pos, node)
|
||||
node.name = "railcarts:rail_switched"
|
||||
minetest.swap_node(pos, node)
|
||||
dbg.v2("Rails switched at "..postostr(pos))
|
||||
end
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("railcarts:rail_switched", {
|
||||
description = "Rail",
|
||||
drawtype = "raillike",
|
||||
drop = 'default:rail 1',
|
||||
tiles = {"default_rail.png", "default_rail_curved.png", "railcarts_rail_t_junction_switched.png", "default_rail_crossing.png"},
|
||||
paramtype = "light",
|
||||
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},
|
||||
},
|
||||
groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},
|
||||
mesecons = {
|
||||
effector = {
|
||||
rules = railrules,
|
||||
action_off =
|
||||
function(pos, node)
|
||||
node.name = "default:rail"
|
||||
minetest.swap_node(pos, node)
|
||||
dbg.v2("Rails switched back at "..postostr(pos))
|
||||
end
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
5
depends.txt
Normal file
@ -0,0 +1,5 @@
|
||||
default
|
||||
mesecons
|
||||
digilines
|
||||
pipeworks
|
||||
moddebug?
|
7
description.txt
Normal file
@ -0,0 +1,7 @@
|
||||
Version 1.0.0
|
||||
Railcarts mod - carts and rails, designed for automation and autonomy
|
||||
-Passenger cart
|
||||
-Cargo cart
|
||||
-Boring cart (tunnel and rail construction)
|
||||
-Mesecon integration (optional)
|
||||
-Digiline integration (optional)
|
47
detector.lua
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:cart_detector",
|
||||
recipe = {
|
||||
{"", "", ""},
|
||||
{"default:mese_crystal", "", "default:mese_crystal"},
|
||||
{"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"},
|
||||
},
|
||||
}
|
||||
)
|
||||
minetest.register_node("railcarts:cart_detector", {
|
||||
description = "Cart Detector",
|
||||
tiles = {"railcarts_cart_detector.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, mesecon=2},
|
||||
drop = 'railcarts:cart_detector 1',
|
||||
mesecons = { receptor = {
|
||||
state = "off"
|
||||
} }
|
||||
})
|
||||
|
||||
minetest.register_node("railcarts:cart_detector_on", {
|
||||
description = "Cart Detector",
|
||||
tiles = {"railcarts_cart_detector.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, mesecon=2},
|
||||
drop = 'railcarts:cart_detector 1',
|
||||
mesecons = { receptor = {
|
||||
state = "on"
|
||||
} }
|
||||
})
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"railcarts:cart_detector_on"},
|
||||
interval = 2,
|
||||
chance = 1,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
minetest.add_node(pos,{name="railcarts:cart_detector"})
|
||||
mesecon:receptor_off(pos, mesecon.rules.default)
|
||||
dbg.v2("Cart detector off at: "..postostr(pos))
|
||||
end
|
||||
})
|
||||
|
||||
|
50
digicontrol.lua
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:digicontrol 12",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "digilines:wire_std_00000000", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:stick", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("railcarts:digicontrol", {
|
||||
description = "Digital Control Rail",
|
||||
drawtype = "raillike",
|
||||
tiles = {"railcarts_pa_carts_rail_digi.png",
|
||||
"railcarts_pa_carts_rail_curved_digi.png",
|
||||
"railcarts_pa_carts_rail_t_junction_digi.png",
|
||||
"railcarts_pa_carts_rail_crossing_digi.png"},
|
||||
inventory_image = "railcarts_pa_carts_rail_digi.png",
|
||||
wield_image = "railcarts_pa_carts_rail_digi.png",
|
||||
paramtype = "light",
|
||||
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},
|
||||
},
|
||||
groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", "field[channel;Channel;${channel}]")
|
||||
meta:set_string("infotext", "none")
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("channel", fields.channel)
|
||||
end,
|
||||
digiline =
|
||||
{
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = function(pos, node, channel, msg)
|
||||
local setchan = minetest.get_meta(pos):get_string("channel")
|
||||
if setchan ~= channel then return end
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("infotext", msg)
|
||||
end
|
||||
},
|
||||
},
|
||||
})
|
||||
|
61
direction.lua
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
-- Directions are used throughout to specify movement along an axis. A
|
||||
-- direction is an integer from 0 to 3, defined as follows:
|
||||
--
|
||||
-- 0 = +z
|
||||
-- 1 = +x
|
||||
-- 2 = +z
|
||||
-- 3 = +x
|
||||
--
|
||||
-- Thus, incrementing the direction is a clockwise rotation, decrementing
|
||||
-- is an anticlockwise one.
|
||||
--
|
||||
-- This file provides utility functions for converting and manipulating
|
||||
-- them.
|
||||
|
||||
|
||||
--- Convert a direction to an xz vector of length 1
|
||||
--@param direction A direction, 0-3
|
||||
--@return {x,z}
|
||||
function direction_to_xz(direction)
|
||||
if direction == 0 then return {x=0,z=1} end
|
||||
if direction == 1 then return {x=1,z=0} end
|
||||
if direction == 2 then return {x=0,z=-1} end
|
||||
return {x=-1,z=0}
|
||||
end
|
||||
|
||||
--- Convert an xz vector to a direction.
|
||||
-- @param xz An xz vector, which had better be one of those returned by
|
||||
-- direction_to_xz, or at least a multiple of, otherwise the results will
|
||||
-- be nonsensical.
|
||||
-- @return The direction
|
||||
function xz_to_direction(xz)
|
||||
if xz.z > 0 then return 0 end
|
||||
if xz.x > 0 then return 1 end
|
||||
if xz.z < 0 then return 2 end
|
||||
return 3
|
||||
end
|
||||
|
||||
--- Get the yaw for a direction
|
||||
-- @param direction The direction
|
||||
-- @return The yaw in radians
|
||||
function direction_to_yaw(direction)
|
||||
if direction == 3 then
|
||||
direction = 1
|
||||
elseif direction == 1 then
|
||||
direction = 3
|
||||
end
|
||||
return direction * (math.pi / 2)
|
||||
end
|
||||
|
||||
--- Get the opposite of a direction
|
||||
-- @param direction The direction
|
||||
-- @return The opposite direction
|
||||
function direction_reverse(direction)
|
||||
if direction == 0 then return 2 end
|
||||
if direction == 1 then return 3 end
|
||||
if direction == 2 then return 0 end
|
||||
return 1
|
||||
end
|
||||
|
||||
|
43
hopper.lua
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:hopper 1",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||
{"", "default:steel_ingot", ""},
|
||||
}
|
||||
})
|
||||
local hopper = pipeworks.clone_node("default:chest")
|
||||
hopper.description = "Hopper"
|
||||
hopper.tiles[1] = "pipeworks_filter_side.png"
|
||||
hopper.tiles[2] = "pipeworks_filter_side.png"
|
||||
hopper.tiles[3] = "railcarts_hopper_side.png"
|
||||
hopper.tiles[4] = "railcarts_hopper_side.png"
|
||||
hopper.tiles[5] = "railcarts_hopper_side.png"
|
||||
hopper.tiles[6] = "railcarts_hopper_side.png"
|
||||
hopper.groups.tubedevice = 1
|
||||
hopper.groups.tubedevice_receiver = 1
|
||||
hopper.tube = {
|
||||
insert_object = function(pos, node, stack, direction)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return inv:add_item("main", stack)
|
||||
end,
|
||||
can_insert = function(pos, node, stack, direction)
|
||||
if direction.y ~= -1 then return false end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return inv:room_for_item("main", stack)
|
||||
end,
|
||||
input_inventory = "main",
|
||||
connect_sides = {top=1}
|
||||
}
|
||||
hopper.after_place_node = function(pos)
|
||||
pipeworks.scan_for_tube_objects(pos)
|
||||
end
|
||||
hopper.after_dig_node = function(pos)
|
||||
pipeworks.scan_for_tube_objects(pos)
|
||||
end
|
||||
|
||||
minetest.register_node("railcarts:hopper", hopper)
|
||||
|
23
init.lua
Normal file
@ -0,0 +1,23 @@
|
||||
local version = "1.0.0"
|
||||
|
||||
LAUNCH_CART_SPEED = 2
|
||||
MAXIMUM_CART_SPEED = 7
|
||||
|
||||
local railcarts_modpath = minetest.get_modpath("railcarts")
|
||||
|
||||
dofile(railcarts_modpath .. "/util.lua")
|
||||
dofile(railcarts_modpath .. "/direction.lua")
|
||||
dofile(railcarts_modpath .. "/defaultrail.lua")
|
||||
dofile(railcarts_modpath .. "/controlrail.lua")
|
||||
dofile(railcarts_modpath .. "/digicontrol.lua")
|
||||
dofile(railcarts_modpath .. "/launchers.lua")
|
||||
dofile(railcarts_modpath .. "/detector.lua")
|
||||
dofile(railcarts_modpath .. "/hopper.lua")
|
||||
dofile(railcarts_modpath .. "/rail.lua")
|
||||
dofile(railcarts_modpath .. "/cartbase.lua")
|
||||
dofile(railcarts_modpath .. "/cargocart.lua")
|
||||
dofile(railcarts_modpath .. "/boringcart.lua")
|
||||
dofile(railcarts_modpath .. "/cart.lua")
|
||||
|
||||
print("railcarts mod " .. version .. " loaded")
|
||||
|
154
items.lua
Normal file
@ -0,0 +1,154 @@
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:cart",
|
||||
recipe = {
|
||||
{"", "", ""},
|
||||
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "railcarts:cargo_cart",
|
||||
recipe = {
|
||||
{"", "", ""},
|
||||
{"default:steel_ingot", "default:chest", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
},
|
||||
})
|
||||
|
||||
-- TODO : get rid of these when conversion is no longer needed
|
||||
minetest.register_alias("railcarts:powerrail", "railcarts:controlrail")
|
||||
minetest.register_alias("railcarts:powerrail_off", "railcarts:controlrail_off")
|
||||
minetest.register_alias("railcarts:brakerail", "railcarts:controlrail")
|
||||
minetest.register_alias("railcarts:brakerail_off", "railcarts:controlrail_off")
|
||||
minetest.register_alias("railcarts:transport_cart", "railcarts:cargo_cart")
|
||||
minetest.register_alias("railcarts:freight_cart", "railcarts:cargo_cart")
|
||||
|
||||
|
||||
|
||||
minetest.register_craftitem("railcarts:cart", {
|
||||
description = "Cart",
|
||||
image = minetest.inventorycube("railcarts_inv_cart_top.png",
|
||||
"railcarts_inv_cart_side.png",
|
||||
"railcarts_inv_cart_side.png"),
|
||||
|
||||
on_place = function(item, placer, pointed_thing)
|
||||
if pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.above
|
||||
pos.y = pos.y - 1
|
||||
local railtype = get_railstatus(pos).railtype
|
||||
if railtype ~= "inv" then
|
||||
|
||||
minetest.env:add_entity(pos,"railcarts:cart_ent")
|
||||
|
||||
if new_object ~= nil then
|
||||
if railtype == "x" then
|
||||
new_object:setyaw(0)
|
||||
elseif railtype == "z" then
|
||||
new_object:setyaw(0 + math.pi/2)
|
||||
end
|
||||
end
|
||||
item:take_item()
|
||||
end
|
||||
end
|
||||
return item
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_craftitem("railcarts:cargo_cart", {
|
||||
description = "Cargo Cart",
|
||||
image = minetest.inventorycube("railcarts_inv_transportcart_top.png",
|
||||
"railcarts_inv_cart_side.png",
|
||||
"railcarts_inv_cart_side.png"),
|
||||
|
||||
on_place = function(item, placer, pointed_thing)
|
||||
if pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.above
|
||||
pos.y = pos.y - 1
|
||||
local railtype = get_railstatus(pos).railtype
|
||||
if railtype ~= "inv" then
|
||||
|
||||
minetest.env:add_entity(pos,"railcarts:cargo_cart_ent")
|
||||
|
||||
if new_object ~= nil then
|
||||
if railtype == "x" then
|
||||
new_object:setyaw(0)
|
||||
elseif railtype == "z" then
|
||||
new_object:setyaw(0 + math.pi/2)
|
||||
end
|
||||
end
|
||||
item:take_item()
|
||||
end
|
||||
end
|
||||
return item
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
local railrules = {
|
||||
{x = 0, y = -1, z = 0},
|
||||
{x = -1, y = 0, z = 0},
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = 0, y = 0, z = 1},
|
||||
{x = 0, y = 0, z = -1},
|
||||
{x = -1, y = -1, z = 0},
|
||||
{x = 1, y = -1, z = 0},
|
||||
{x = 0, y = -1, z = 1},
|
||||
{x = 0, y = -1, z = -1},
|
||||
}
|
||||
|
||||
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",
|
||||
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},
|
||||
},
|
||||
groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},
|
||||
mesecons = {
|
||||
effector = {
|
||||
rules = railrules,
|
||||
action_on =
|
||||
function(pos, node)
|
||||
node.name = "railcarts:rail_switched"
|
||||
minetest.swap_node(pos, node)
|
||||
debug.v2("Rails switched at "..postostr(pos))
|
||||
end
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("railcarts:rail_switched", {
|
||||
description = "Rail",
|
||||
drawtype = "raillike",
|
||||
drop = 'default:rail 1',
|
||||
tiles = {"default_rail.png", "default_rail_curved.png", "railcarts_rail_t_junction_switched.png", "default_rail_crossing.png"},
|
||||
paramtype = "light",
|
||||
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},
|
||||
},
|
||||
groups = {bendy=2,snappy=1,dig_immediate=2,attached_node=1,rail=1,connect_to_raillike=1},
|
||||
mesecons = {
|
||||
effector = {
|
||||
rules = railrules,
|
||||
action_off =
|
||||
function(pos, node)
|
||||
node.name = "default:rail"
|
||||
minetest.swap_node(pos, node)
|
||||
debug.v2("Rails switched back at "..postostr(pos))
|
||||
end
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
150
launchers.lua
Normal file
@ -0,0 +1,150 @@
|
||||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'node "railcarts:launcher_off" 5',
|
||||
recipe = {
|
||||
{"default:cobble", "default:mese_crystal_fragment","default:cobble"},
|
||||
{"default:mese_crystal_fragment", "default:glass", "default:mese_crystal_fragment"},
|
||||
{"default:cobble", "default:mese_crystal_fragment","default:cobble"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'node "railcarts:cart_detector_off" 5',
|
||||
recipe = {
|
||||
{"default:cobble", "default:glass","default:cobble"},
|
||||
{"default:glass", "default:mese_crystal_fragment", "default:glass"},
|
||||
{"default:cobble", "default:glass","default:cobble"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'node "railcarts:autolauncher" 2',
|
||||
recipe = {
|
||||
{"default:cobble", "default:mese_crystal_fragment","default:cobble"},
|
||||
{"default:mese_crystal_fragment", "default:chest", "default:mese_crystal_fragment"},
|
||||
{"default:cobble", "default:mese_crystal_fragment","default:cobble"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'node "railcarts:launcher_off" 2',
|
||||
recipe = {
|
||||
{"default:cobble", "default:mese_crystal_fragment","default:cobble"},
|
||||
{"default:mese_crystal_fragment", "default:sand", "default:mese_crystal_fragment"},
|
||||
{"default:cobble", "default:mese_crystal_fragment","default:cobble"},
|
||||
}
|
||||
})
|
||||
minetest.register_node("railcarts:launcher_off", {
|
||||
description = "Cart Launcher",
|
||||
tiles ={"railcarts_launcher_off.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, mesecon=2},
|
||||
|
||||
mesecons = { conductor = {
|
||||
state = "off",
|
||||
onstate = "railcarts:launcher_on",
|
||||
} }
|
||||
})
|
||||
|
||||
minetest.register_node("railcarts:launcher_on", {
|
||||
description = "Cart Launcher",
|
||||
tiles ={"railcarts_launcher_on.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, mesecon=2},
|
||||
drop = 'railcarts:launcher_off 1',
|
||||
|
||||
mesecons = { conductor = {
|
||||
state = "on",
|
||||
offstate = "railcarts:launcher_off",
|
||||
} }
|
||||
|
||||
})
|
||||
|
||||
minetest.register_node("railcarts:autolauncher", {
|
||||
description = "Cart Autolauncher",
|
||||
tiles ={"railcarts_autolauncher.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
drop = 'railcarts:autolauncher 1',
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"size[8,8]"..
|
||||
"list[current_name;main;0,0;8,3;]"..
|
||||
"list[current_player;main;0,4;8,4;]")
|
||||
meta:set_string("infotext", "Autolauncher")
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 24)
|
||||
end,
|
||||
can_dig = function(pos,player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"railcarts:autolauncher"},
|
||||
neighbors = {},
|
||||
interval = 5.0,
|
||||
chance = 1,
|
||||
action =
|
||||
function(pos, node, active_object_count, active_object_count_wider)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
tests = {{x=pos.x+1,y=pos.y,z=pos.z},
|
||||
{x=pos.x-1,y=pos.y,z=pos.z},
|
||||
{x=pos.x,y=pos.y,z=pos.z+1},
|
||||
{x=pos.x,y=pos.y,z=pos.z-1},
|
||||
{x=pos.x,y=pos.y-1,z=pos.z}}
|
||||
|
||||
for _, testpos in ipairs(tests) do
|
||||
|
||||
if is_rail(minetest.get_node(testpos)) then
|
||||
|
||||
-- Check there's not a cart (or anything else, like a mob) in the way
|
||||
-- TODO: I think the block can be loaded, but the objects not active,
|
||||
-- in which case this will drop another cart on top of an existing
|
||||
-- one ad infinitum - needs fixing!
|
||||
already = minetest.get_objects_inside_radius(testpos, 1)
|
||||
if table.maxn(already) == 0 then
|
||||
|
||||
-- Get a cart from the autolauncher's inventory
|
||||
local stack = inv:remove_item("main", ItemStack("railcarts:cart"))
|
||||
local placecart = "railcarts:cart_ent"
|
||||
if stack:is_empty() then
|
||||
stack = inv:remove_item("main", ItemStack("railcarts:cargo_cart"))
|
||||
if stack:is_empty() then
|
||||
stack = inv:remove_item("main", ItemStack("railcarts:boring_cart"))
|
||||
if stack:is_empty() then
|
||||
dbg.v1("Autolauncher at "..postostr(pos).." is empty")
|
||||
return
|
||||
end
|
||||
placecart = "railcarts:boring_cart_ent"
|
||||
else
|
||||
placecart = "railcarts:cargo_cart_ent"
|
||||
end
|
||||
end
|
||||
|
||||
dbg.v1("Autolauncher placing "..placecart.." at"..postostr(testpos))
|
||||
|
||||
local object = minetest.add_entity(testpos, placecart)
|
||||
if object then
|
||||
local ent = object:get_luaentity()
|
||||
local newdir = xz_to_direction({x=testpos.x-pos.x, z=testpos.z-pos.z})
|
||||
current_state = {direction=newdir}
|
||||
cartbase.setdirection(ent, current_state, newdir)
|
||||
ent.direction = newdir
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end,
|
||||
})
|
||||
|
||||
|
BIN
models/cart.blend
Normal file
BIN
models/railcarts_cart.b3d
Normal file
339
models/railcarts_cart.x
Normal file
@ -0,0 +1,339 @@
|
||||
xof 0303txt 0032
|
||||
|
||||
Frame Root {
|
||||
FrameTransformMatrix {
|
||||
1.000000, 0.000000, 0.000000, 0.000000,
|
||||
0.000000, 0.000000, 1.000000, 0.000000,
|
||||
0.000000, 1.000000,-0.000000, 0.000000,
|
||||
0.000000, 0.000000, 0.000000, 1.000000;;
|
||||
}
|
||||
Frame Cube {
|
||||
FrameTransformMatrix {
|
||||
5.000000, 0.000000,-0.000000, 0.000000,
|
||||
-0.000000, 3.535534, 3.535534, 0.000000,
|
||||
0.000000,-3.535534, 3.535534, 0.000000,
|
||||
0.000000,-3.000000, 3.000000, 1.000000;;
|
||||
}
|
||||
Mesh { //Cube_001 Mesh
|
||||
72;
|
||||
-1.000000; 1.000000;-1.000000;,
|
||||
-1.000000;-1.000000;-1.000000;,
|
||||
1.000000;-1.000000;-1.000000;,
|
||||
1.000000; 1.000000;-1.000000;,
|
||||
-0.833334;-1.000000; 1.000000;,
|
||||
-1.000000;-1.000000; 1.000000;,
|
||||
-1.000000;-0.833333; 1.000000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
-1.000000;-1.000000;-1.000000;,
|
||||
-1.000000;-1.000000; 1.000000;,
|
||||
0.999999;-1.000001; 1.000000;,
|
||||
1.000000;-1.000000;-1.000000;,
|
||||
0.999999;-1.000001; 1.000000;,
|
||||
0.833332;-1.000000; 1.000000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
1.000000;-0.833334; 1.000000;,
|
||||
0.833332;-1.000000; 1.000000;,
|
||||
-0.833334;-1.000000; 1.000000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
1.000000; 0.833333; 1.000000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
0.833334; 1.000000; 1.000000;,
|
||||
1.000000; 0.999999; 1.000000;,
|
||||
1.000000;-0.833334; 1.000000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
1.000000; 0.833333; 1.000000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
-0.833333; 1.000000; 1.000000;,
|
||||
0.833334; 1.000000; 1.000000;,
|
||||
0.833334; 0.833333;-0.800000;,
|
||||
-0.833333; 0.833333;-0.800000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
-1.000000; 0.833333; 1.000000;,
|
||||
-1.000000; 1.000000; 1.000000;,
|
||||
-0.833333; 1.000000; 1.000000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
-1.000000;-0.833333; 1.000000;,
|
||||
-1.000000; 0.833333; 1.000000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
0.833333;-0.833334;-0.800000;,
|
||||
-0.833334;-0.833333;-0.800000;,
|
||||
-0.833333; 0.833333;-0.800000;,
|
||||
0.833334; 0.833333;-0.800000;,
|
||||
-0.833333; 0.833333;-0.800000;,
|
||||
-0.833334;-0.833333;-0.800000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
-0.833334;-0.833333;-0.800000;,
|
||||
0.833333;-0.833334;-0.800000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
0.833333;-0.833334;-0.800000;,
|
||||
0.833334; 0.833333;-0.800000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
-1.000000; 1.000000;-1.000000;,
|
||||
-1.000000; 1.000000; 1.000000;,
|
||||
-1.000000;-1.000000; 1.000000;,
|
||||
-1.000000;-1.000000;-1.000000;,
|
||||
-1.000000; 1.000000; 1.000000;,
|
||||
-1.000000; 1.000000;-1.000000;,
|
||||
1.000000; 1.000000;-1.000000;,
|
||||
1.000000; 0.999999; 1.000000;,
|
||||
1.000000;-1.000000;-1.000000;,
|
||||
0.999999;-1.000001; 1.000000;,
|
||||
1.000000; 0.999999; 1.000000;,
|
||||
1.000000; 1.000000;-1.000000;;
|
||||
18;
|
||||
4;0;1;2;3;,
|
||||
4;4;5;6;7;,
|
||||
4;8;9;10;11;,
|
||||
4;12;13;14;15;,
|
||||
4;16;17;18;19;,
|
||||
4;20;21;22;23;,
|
||||
4;24;25;26;27;,
|
||||
4;28;29;30;31;,
|
||||
4;32;33;34;35;,
|
||||
4;36;37;38;39;,
|
||||
4;40;41;42;43;,
|
||||
4;44;45;46;47;,
|
||||
4;48;49;50;51;,
|
||||
4;52;53;54;55;,
|
||||
4;56;57;58;59;,
|
||||
4;60;61;62;63;,
|
||||
4;64;65;66;67;,
|
||||
4;68;69;70;71;;
|
||||
MeshNormals { //Cube_001 Normals
|
||||
72;
|
||||
0.000000; 0.000000;-1.000000;,
|
||||
0.000000; 0.000000;-1.000000;,
|
||||
0.000000; 0.000000;-1.000000;,
|
||||
0.000000; 0.000000;-1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
-0.000000;-1.000000;-0.000000;,
|
||||
-0.000000;-1.000000;-0.000000;,
|
||||
-0.000000;-1.000000;-0.000000;,
|
||||
-0.000000;-1.000000;-0.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
-0.000000;-1.000000; 0.000000;,
|
||||
-0.000000;-1.000000; 0.000000;,
|
||||
-0.000000;-1.000000; 0.000000;,
|
||||
-0.000000;-1.000000; 0.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
-1.000000; 0.000000; 0.000000;,
|
||||
-1.000000; 0.000000; 0.000000;,
|
||||
-1.000000; 0.000000; 0.000000;,
|
||||
-1.000000; 0.000000; 0.000000;,
|
||||
-1.000000; 0.000000;-0.000000;,
|
||||
-1.000000; 0.000000;-0.000000;,
|
||||
-1.000000; 0.000000;-0.000000;,
|
||||
-1.000000; 0.000000;-0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;;
|
||||
18;
|
||||
4;0;1;2;3;,
|
||||
4;4;5;6;7;,
|
||||
4;8;9;10;11;,
|
||||
4;12;13;14;15;,
|
||||
4;16;17;18;19;,
|
||||
4;20;21;22;23;,
|
||||
4;24;25;26;27;,
|
||||
4;28;29;30;31;,
|
||||
4;32;33;34;35;,
|
||||
4;36;37;38;39;,
|
||||
4;40;41;42;43;,
|
||||
4;44;45;46;47;,
|
||||
4;48;49;50;51;,
|
||||
4;52;53;54;55;,
|
||||
4;56;57;58;59;,
|
||||
4;60;61;62;63;,
|
||||
4;64;65;66;67;,
|
||||
4;68;69;70;71;;
|
||||
} //End of Cube_001 Normals
|
||||
MeshMaterialList { //Cube_001 Material List
|
||||
1;
|
||||
18;
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0;;
|
||||
Material Material {
|
||||
0.640000; 0.640000; 0.640000; 1.000000;;
|
||||
96.078431;
|
||||
0.500000; 0.500000; 0.500000;;
|
||||
0.000000; 0.000000; 0.000000;;
|
||||
TextureFilename {"cart.png";}
|
||||
}
|
||||
} //End of Cube_001 Material List
|
||||
MeshTextureCoords { //Cube_001 UV Coordinates
|
||||
72;
|
||||
0.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 1.000000;,
|
||||
0.000000; 1.000000;,
|
||||
0.031250; 0.500000;,
|
||||
-0.000000; 0.500000;,
|
||||
-0.000000; 0.468750;,
|
||||
0.031250; 0.468750;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.468750; 0.468750;,
|
||||
0.500000; 0.468750;,
|
||||
0.500000; 0.500000;,
|
||||
0.468750; 0.500000;,
|
||||
0.031250; 0.468750;,
|
||||
0.468750; 0.468750;,
|
||||
0.468750; 0.500000;,
|
||||
0.031250; 0.500000;,
|
||||
0.468750; 0.000000;,
|
||||
0.500000; 0.000000;,
|
||||
0.500000; 0.031250;,
|
||||
0.468750; 0.031250;,
|
||||
0.468750; 0.031250;,
|
||||
0.500000; 0.031250;,
|
||||
0.500000; 0.468750;,
|
||||
0.468750; 0.468750;,
|
||||
0.468750; 0.031250;,
|
||||
0.031250; 0.031250;,
|
||||
0.031250; 0.000000;,
|
||||
0.468750; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
0.031250; 0.031250;,
|
||||
0.000000; 0.031250;,
|
||||
0.000000; 0.000000;,
|
||||
0.031250; 0.000000;,
|
||||
0.031250; 0.468750;,
|
||||
-0.000000; 0.468750;,
|
||||
0.000000; 0.031250;,
|
||||
0.031250; 0.031250;,
|
||||
0.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 1.000000;,
|
||||
0.000000; 1.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;;
|
||||
} //End of Cube_001 UV Coordinates
|
||||
} //End of Cube_001 Mesh
|
||||
} //End of Cube
|
||||
} //End of Root Frame
|
||||
AnimationSet {
|
||||
Animation {
|
||||
{Cube}
|
||||
AnimationKey { //Position
|
||||
2;
|
||||
4;
|
||||
0;3; 0.000000, 0.000000, 0.000000;;,
|
||||
1;3; 0.000000, 3.000000, 3.000000;;,
|
||||
2;3; 0.000000,-3.000000, 3.000000;;,
|
||||
3;3; 0.000000,-3.000000, 3.000000;;;
|
||||
}
|
||||
AnimationKey { //Rotation
|
||||
0;
|
||||
4;
|
||||
0;4; -1.000000, 0.000000, 0.000000, 0.000000;;,
|
||||
1;4; -0.923880,-0.382683,-0.000000, 0.000000;;,
|
||||
2;4; -0.923880, 0.382683, 0.000000, 0.000000;;,
|
||||
3;4; -0.923880, 0.382683, 0.000000, 0.000000;;;
|
||||
}
|
||||
AnimationKey { //Scale
|
||||
1;
|
||||
4;
|
||||
0;3; 5.000000, 5.000000, 5.000000;;,
|
||||
1;3; 5.000000, 5.000000, 5.000000;;,
|
||||
2;3; 5.000000, 5.000000, 5.000000;;,
|
||||
3;3; 5.000000, 5.000000, 5.000000;;;
|
||||
}
|
||||
}
|
||||
} //End of AnimationSet
|
BIN
models/railcarts_transport_cart.b3d
Normal file
339
models/railcarts_transport_cart.x
Normal file
@ -0,0 +1,339 @@
|
||||
xof 0303txt 0032
|
||||
|
||||
Frame Root {
|
||||
FrameTransformMatrix {
|
||||
1.000000, 0.000000, 0.000000, 0.000000,
|
||||
0.000000, 0.000000, 1.000000, 0.000000,
|
||||
0.000000, 1.000000,-0.000000, 0.000000,
|
||||
0.000000, 0.000000, 0.000000, 1.000000;;
|
||||
}
|
||||
Frame Cube {
|
||||
FrameTransformMatrix {
|
||||
5.000000, 0.000000,-0.000000, 0.000000,
|
||||
-0.000000, 3.535534, 3.535534, 0.000000,
|
||||
0.000000,-3.535534, 3.535534, 0.000000,
|
||||
0.000000,-3.000000, 3.000000, 1.000000;;
|
||||
}
|
||||
Mesh { //Cube_001 Mesh
|
||||
72;
|
||||
-1.000000; 1.000000;-1.000000;,
|
||||
-1.000000;-1.000000;-1.000000;,
|
||||
1.000000;-1.000000;-1.000000;,
|
||||
1.000000; 1.000000;-1.000000;,
|
||||
-0.833334;-1.000000; 1.000000;,
|
||||
-1.000000;-1.000000; 1.000000;,
|
||||
-1.000000;-0.833333; 1.000000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
-1.000000;-1.000000;-1.000000;,
|
||||
-1.000000;-1.000000; 1.000000;,
|
||||
0.999999;-1.000001; 1.000000;,
|
||||
1.000000;-1.000000;-1.000000;,
|
||||
0.999999;-1.000001; 1.000000;,
|
||||
0.833332;-1.000000; 1.000000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
1.000000;-0.833334; 1.000000;,
|
||||
0.833332;-1.000000; 1.000000;,
|
||||
-0.833334;-1.000000; 1.000000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
1.000000; 0.833333; 1.000000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
0.833334; 1.000000; 1.000000;,
|
||||
1.000000; 0.999999; 1.000000;,
|
||||
1.000000;-0.833334; 1.000000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
1.000000; 0.833333; 1.000000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
-0.833333; 1.000000; 1.000000;,
|
||||
0.833334; 1.000000; 1.000000;,
|
||||
0.833334; 0.833333;-0.800000;,
|
||||
-0.833333; 0.833333;-0.800000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
-1.000000; 0.833333; 1.000000;,
|
||||
-1.000000; 1.000000; 1.000000;,
|
||||
-0.833333; 1.000000; 1.000000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
-1.000000;-0.833333; 1.000000;,
|
||||
-1.000000; 0.833333; 1.000000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
0.833333;-0.833334;-0.800000;,
|
||||
-0.833334;-0.833333;-0.800000;,
|
||||
-0.833333; 0.833333;-0.800000;,
|
||||
0.833334; 0.833333;-0.800000;,
|
||||
-0.833333; 0.833333;-0.800000;,
|
||||
-0.833334;-0.833333;-0.800000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
-0.833333; 0.833333; 1.000000;,
|
||||
-0.833334;-0.833333;-0.800000;,
|
||||
0.833333;-0.833334;-0.800000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
-0.833334;-0.833333; 1.000000;,
|
||||
0.833333;-0.833334;-0.800000;,
|
||||
0.833334; 0.833333;-0.800000;,
|
||||
0.833334; 0.833333; 1.000000;,
|
||||
0.833333;-0.833334; 1.000000;,
|
||||
-1.000000; 1.000000;-1.000000;,
|
||||
-1.000000; 1.000000; 1.000000;,
|
||||
-1.000000;-1.000000; 1.000000;,
|
||||
-1.000000;-1.000000;-1.000000;,
|
||||
-1.000000; 1.000000; 1.000000;,
|
||||
-1.000000; 1.000000;-1.000000;,
|
||||
1.000000; 1.000000;-1.000000;,
|
||||
1.000000; 0.999999; 1.000000;,
|
||||
1.000000;-1.000000;-1.000000;,
|
||||
0.999999;-1.000001; 1.000000;,
|
||||
1.000000; 0.999999; 1.000000;,
|
||||
1.000000; 1.000000;-1.000000;;
|
||||
18;
|
||||
4;0;1;2;3;,
|
||||
4;4;5;6;7;,
|
||||
4;8;9;10;11;,
|
||||
4;12;13;14;15;,
|
||||
4;16;17;18;19;,
|
||||
4;20;21;22;23;,
|
||||
4;24;25;26;27;,
|
||||
4;28;29;30;31;,
|
||||
4;32;33;34;35;,
|
||||
4;36;37;38;39;,
|
||||
4;40;41;42;43;,
|
||||
4;44;45;46;47;,
|
||||
4;48;49;50;51;,
|
||||
4;52;53;54;55;,
|
||||
4;56;57;58;59;,
|
||||
4;60;61;62;63;,
|
||||
4;64;65;66;67;,
|
||||
4;68;69;70;71;;
|
||||
MeshNormals { //Cube_001 Normals
|
||||
72;
|
||||
0.000000; 0.000000;-1.000000;,
|
||||
0.000000; 0.000000;-1.000000;,
|
||||
0.000000; 0.000000;-1.000000;,
|
||||
0.000000; 0.000000;-1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
-0.000000;-1.000000;-0.000000;,
|
||||
-0.000000;-1.000000;-0.000000;,
|
||||
-0.000000;-1.000000;-0.000000;,
|
||||
-0.000000;-1.000000;-0.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
-0.000000;-1.000000; 0.000000;,
|
||||
-0.000000;-1.000000; 0.000000;,
|
||||
-0.000000;-1.000000; 0.000000;,
|
||||
-0.000000;-1.000000; 0.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
0.000000;-0.000000; 1.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
-1.000000; 0.000000; 0.000000;,
|
||||
-1.000000; 0.000000; 0.000000;,
|
||||
-1.000000; 0.000000; 0.000000;,
|
||||
-1.000000; 0.000000; 0.000000;,
|
||||
-1.000000; 0.000000;-0.000000;,
|
||||
-1.000000; 0.000000;-0.000000;,
|
||||
-1.000000; 0.000000;-0.000000;,
|
||||
-1.000000; 0.000000;-0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
0.000000; 1.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;,
|
||||
1.000000;-0.000000; 0.000000;;
|
||||
18;
|
||||
4;0;1;2;3;,
|
||||
4;4;5;6;7;,
|
||||
4;8;9;10;11;,
|
||||
4;12;13;14;15;,
|
||||
4;16;17;18;19;,
|
||||
4;20;21;22;23;,
|
||||
4;24;25;26;27;,
|
||||
4;28;29;30;31;,
|
||||
4;32;33;34;35;,
|
||||
4;36;37;38;39;,
|
||||
4;40;41;42;43;,
|
||||
4;44;45;46;47;,
|
||||
4;48;49;50;51;,
|
||||
4;52;53;54;55;,
|
||||
4;56;57;58;59;,
|
||||
4;60;61;62;63;,
|
||||
4;64;65;66;67;,
|
||||
4;68;69;70;71;;
|
||||
} //End of Cube_001 Normals
|
||||
MeshMaterialList { //Cube_001 Material List
|
||||
1;
|
||||
18;
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0;;
|
||||
Material Material {
|
||||
0.640000; 0.640000; 0.640000; 1.000000;;
|
||||
96.078431;
|
||||
0.500000; 0.500000; 0.500000;;
|
||||
0.000000; 0.000000; 0.000000;;
|
||||
TextureFilename {"cart.png";}
|
||||
}
|
||||
} //End of Cube_001 Material List
|
||||
MeshTextureCoords { //Cube_001 UV Coordinates
|
||||
72;
|
||||
0.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 1.000000;,
|
||||
0.000000; 1.000000;,
|
||||
0.031250; 0.500000;,
|
||||
-0.000000; 0.500000;,
|
||||
-0.000000; 0.468750;,
|
||||
0.031250; 0.468750;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.468750; 0.468750;,
|
||||
0.500000; 0.468750;,
|
||||
0.500000; 0.500000;,
|
||||
0.468750; 0.500000;,
|
||||
0.031250; 0.468750;,
|
||||
0.468750; 0.468750;,
|
||||
0.468750; 0.500000;,
|
||||
0.031250; 0.500000;,
|
||||
0.468750; 0.000000;,
|
||||
0.500000; 0.000000;,
|
||||
0.500000; 0.031250;,
|
||||
0.468750; 0.031250;,
|
||||
0.468750; 0.031250;,
|
||||
0.500000; 0.031250;,
|
||||
0.500000; 0.468750;,
|
||||
0.468750; 0.468750;,
|
||||
0.468750; 0.031250;,
|
||||
0.031250; 0.031250;,
|
||||
0.031250; 0.000000;,
|
||||
0.468750; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
0.031250; 0.031250;,
|
||||
0.000000; 0.031250;,
|
||||
0.000000; 0.000000;,
|
||||
0.031250; 0.000000;,
|
||||
0.031250; 0.468750;,
|
||||
-0.000000; 0.468750;,
|
||||
0.000000; 0.031250;,
|
||||
0.031250; 0.031250;,
|
||||
0.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 1.000000;,
|
||||
0.000000; 1.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
0.500000; 0.500000;,
|
||||
0.500000; 0.000000;,
|
||||
1.000000; 0.000000;,
|
||||
1.000000; 0.500000;;
|
||||
} //End of Cube_001 UV Coordinates
|
||||
} //End of Cube_001 Mesh
|
||||
} //End of Cube
|
||||
} //End of Root Frame
|
||||
AnimationSet {
|
||||
Animation {
|
||||
{Cube}
|
||||
AnimationKey { //Position
|
||||
2;
|
||||
4;
|
||||
0;3; 0.000000, 0.000000, 0.000000;;,
|
||||
1;3; 0.000000, 3.000000, 3.000000;;,
|
||||
2;3; 0.000000,-3.000000, 3.000000;;,
|
||||
3;3; 0.000000,-3.000000, 3.000000;;;
|
||||
}
|
||||
AnimationKey { //Rotation
|
||||
0;
|
||||
4;
|
||||
0;4; -1.000000, 0.000000, 0.000000, 0.000000;;,
|
||||
1;4; -0.923880,-0.382683,-0.000000, 0.000000;;,
|
||||
2;4; -0.923880, 0.382683, 0.000000, 0.000000;;,
|
||||
3;4; -0.923880, 0.382683, 0.000000, 0.000000;;;
|
||||
}
|
||||
AnimationKey { //Scale
|
||||
1;
|
||||
4;
|
||||
0;3; 5.000000, 5.000000, 5.000000;;,
|
||||
1;3; 5.000000, 5.000000, 5.000000;;,
|
||||
2;3; 5.000000, 5.000000, 5.000000;;,
|
||||
3;3; 5.000000, 5.000000, 5.000000;;;
|
||||
}
|
||||
}
|
||||
} //End of AnimationSet
|
BIN
models/transport_cart.blend
Normal file
513
rail.lua
Normal file
@ -0,0 +1,513 @@
|
||||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
--- Determine whether the given node is a rail.
|
||||
-- @param node The node to check (which can be nil)
|
||||
-- @return true if it's a rail
|
||||
function is_rail(node)
|
||||
if node == nil then return false end
|
||||
name = node.name
|
||||
if name == "default:rail" or
|
||||
name == "railcarts:controlrail" or
|
||||
name == "railcarts:controlrail_off" or
|
||||
name == "railcarts:digicontrol" or
|
||||
name == "railcarts:rail_switched" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Determine whether a railtype is 'unloaded'
|
||||
-- @param railtype A railtype (as per get_rail_status)
|
||||
-- @return true if unloaded
|
||||
function is_unloaded(railtype)
|
||||
if railtype == "unloaded" then return true end
|
||||
if railtype == "unloaded-near" then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
---Determine if a node is a grabber of carts
|
||||
--@param node The node to check (which can be nil)
|
||||
--@return True if it is
|
||||
function is_grabber(node)
|
||||
if node == nil then return false end
|
||||
if node.name == "railcarts:autolauncher" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---Get rail control information coming from a node.
|
||||
-- Only switched on control rails are considered, since if they're switched
|
||||
-- off they should behave exactly like a normal rail.
|
||||
--@param node The node to check (which can be nil)
|
||||
--@param pos The position to check
|
||||
--@param status The status to update.
|
||||
--The control field might be added, containing any of the specifications
|
||||
--as described in the README.
|
||||
-- The digiline field might be set to the pos that should send a signal
|
||||
function get_railcontrol(node, pos, status)
|
||||
if node and (node.name == "railcarts:controlrail"
|
||||
or node.name == "railcarts:digicontrol") then
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
local it = meta:get_string("infotext")
|
||||
if it and it ~= "" then
|
||||
status.control = it
|
||||
end
|
||||
end
|
||||
if node.name == "railcarts:digicontrol" then
|
||||
status.digiline = pos
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Determine if a node is a launcher
|
||||
-- @param node The node (which can be nil)
|
||||
-- @return True if it is
|
||||
function is_launcher(node)
|
||||
|
||||
if node == nil then return false end
|
||||
if node.name == "railcarts:launcher_on" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Determine if a node is an autolauncher
|
||||
-- @param node The node (which can be nil)
|
||||
-- @return True if it is
|
||||
function is_autolauncher(node)
|
||||
|
||||
if node == nil then return false end
|
||||
if node.name == "railcarts:autolauncher" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Get status of rails
|
||||
-- @param fppos The position to check at
|
||||
-- @param direction Movement direction (can be nil, in which case some checks
|
||||
-- are not possible).
|
||||
-- @param speed Movement speed (only relevant if direction is not nil)
|
||||
-- checks can be completed.
|
||||
-- @return A table which always contains railtype. One of:
|
||||
-- "inv" - invalid position
|
||||
-- "unloaded" or "unloaded-near" - entering as-yet-unloaded territory
|
||||
-- use is_unloaded() to check this!
|
||||
-- "x", "z" - straight track in that direction (can be sloped)
|
||||
-- "x+", x-", "z+", "z-" - curve (see check_curve ireturn value)
|
||||
-- It can also optionally contain:
|
||||
-- unloadedpos, when railtype is unloaded or unloaded-near, is the position
|
||||
-- that caused the unloaded status
|
||||
-- slope, if the rail is sloped, the up direction, 0-3 (and railtype can only
|
||||
-- be "x" or "z"
|
||||
-- eject, which if true means any player in the cart should be
|
||||
-- grab, which if it exists is the position of a node which should 'grab' the
|
||||
-- cart into its inventory (i.e. an autolauncher)
|
||||
-- launch, which is a launch direction
|
||||
-- autolaunch, which is an autolaunch direction
|
||||
-- control, usually nil, otherwise control information coming from the current
|
||||
-- rail - values as per get_controlrail()
|
||||
-- onautolauncher, which if true means there is an autolauncher below the rails
|
||||
-- detectors, if present, is the pos for a cart detector to trigger
|
||||
-- hopper, if present, is the pos for a hopper above the cart
|
||||
-- only detected when stationary
|
||||
function get_railstatus(fppos, direction, speed)
|
||||
|
||||
if fppos == nil then
|
||||
dbg.v2("get_railstatus for nil pos!?")
|
||||
return {railtype="inv"}
|
||||
end
|
||||
|
||||
-- Use rounded position for all lookups and calculations in here
|
||||
pos = v3posround(fppos)
|
||||
|
||||
local current_node = minetest.get_node(pos)
|
||||
|
||||
if current_node.name == "ignore" then
|
||||
return {railtype="unloaded", unloadedpos=pos}
|
||||
end
|
||||
|
||||
if not is_rail(current_node) then
|
||||
return {railtype="inv"}
|
||||
end
|
||||
|
||||
status = {}
|
||||
|
||||
-- Get all surrounding nodes. This builds a little cache of all the
|
||||
-- surrounding nodes we might need to look at.
|
||||
-- TODO: make this lazily get positions only when requested, because
|
||||
-- sometimes we don't need them all
|
||||
-- TODO: make this shared across a whole step (because getrailstatus
|
||||
-- can be called twice, with overlap, within a step)
|
||||
-- BUT!!! digirail triggers, mid-step, can change the rails,
|
||||
-- although that shouldn't matter - but think about it!
|
||||
-- TODO: make it less messy!
|
||||
-- TODO: we're only storing the position for reading it back to debug
|
||||
-- the 'unloaded' problem, we could lose it when that's fixed
|
||||
local getsur = function(name, pos, surrounds)
|
||||
surrounds[name] = minetest.get_node(pos)
|
||||
surrounds[name.."_pos"] = pos
|
||||
end
|
||||
surrounds = {}
|
||||
getsur("x_prev", {x=pos.x-1,y=pos.y,z=pos.z}, surrounds)
|
||||
getsur("x_next", {x=pos.x+1,y=pos.y,z=pos.z}, surrounds)
|
||||
getsur("z_prev", {x=pos.x,y=pos.y,z=pos.z-1}, surrounds)
|
||||
getsur("z_next", {x=pos.x,y=pos.y,z=pos.z+1}, surrounds)
|
||||
getsur("x_prev_above", {x=pos.x-1,y=pos.y+1,z=pos.z}, surrounds)
|
||||
getsur("x_next_above", {x=pos.x+1,y=pos.y+1,z=pos.z}, surrounds)
|
||||
getsur("z_prev_above", {x=pos.x,y=pos.y+1,z=pos.z-1}, surrounds)
|
||||
getsur("z_next_above", {x=pos.x,y=pos.y+1,z=pos.z+1}, surrounds)
|
||||
getsur("x_prev_below", {x=pos.x-1,y=pos.y-1,z=pos.z}, surrounds)
|
||||
getsur("x_next_below", {x=pos.x+1,y=pos.y-1,z=pos.z}, surrounds)
|
||||
getsur("z_prev_below", {x=pos.x,y=pos.y-1,z=pos.z-1}, surrounds)
|
||||
getsur("z_next_below", {x=pos.x,y=pos.y-1,z=pos.z+1}, surrounds)
|
||||
getsur("below", {x=pos.x,y=pos.y-1,z=pos.z}, surrounds)
|
||||
getsur("above", {x=pos.x,y=pos.y+1,z=pos.z}, surrounds)
|
||||
|
||||
-- We need to know what all the surrounding blocks are to be able to move
|
||||
-- properly (e.g. consider a curve or switch on a block boundary) so if
|
||||
-- we don't have that information we need to wait.
|
||||
for k, nn in pairs(surrounds) do
|
||||
if string.sub(k, -4) ~= "_pos" then
|
||||
if nn.name == "ignore" then
|
||||
return {railtype="unloaded-near", unloadedpos=surrounds[k.."_pos"]}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get_railcontrol(current_node, pos, status)
|
||||
|
||||
local railtype
|
||||
|
||||
-- Check for slopes. The order of these is the same as the order of
|
||||
-- priority used by the rail drawing code (as determined by experimenation)
|
||||
if is_rail(surrounds.x_next_above) then
|
||||
railtype = "x"
|
||||
status.slope=1
|
||||
elseif is_rail(surrounds.x_prev_above) then
|
||||
railtype = "x"
|
||||
status.slope=3
|
||||
elseif is_rail(surrounds.z_prev_above) then
|
||||
railtype = "z"
|
||||
status.slope=2
|
||||
elseif is_rail(surrounds.z_next_above) then
|
||||
railtype = "z"
|
||||
status.slope=0
|
||||
end
|
||||
|
||||
-- Check for a crossing
|
||||
if not railtype then
|
||||
railtype = check_crossing(direction, surrounds)
|
||||
end
|
||||
|
||||
-- Check for a switch
|
||||
if (not railtype) and direction then
|
||||
railtype = check_switch(current_node, pos, direction, surrounds)
|
||||
end
|
||||
|
||||
-- Check for a curve
|
||||
if not railtype then
|
||||
railtype = check_curve(surrounds)
|
||||
end
|
||||
|
||||
-- There can only be straight rails left
|
||||
if (not railtype) and (is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below) or
|
||||
is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) then
|
||||
railtype = "x"
|
||||
end
|
||||
|
||||
if (not railtype) and (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below) or
|
||||
is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) then
|
||||
railtype = "z"
|
||||
end
|
||||
|
||||
if not railtype then
|
||||
railtype = "inv"
|
||||
else
|
||||
|
||||
-- Check for hoppers
|
||||
if is_hopper(surrounds.above) then
|
||||
status.hopper = {x=pos.x,y=pos.y+1,z=pos.z}
|
||||
end
|
||||
|
||||
if direction then
|
||||
-- Check if a player should be ejected
|
||||
if speed > 0 then
|
||||
if direction == 1 and is_ejector(surrounds.x_next_above) and is_rail(surrounds.x_next) then
|
||||
status.eject = true
|
||||
elseif direction == 3 and is_ejector(surrounds.x_prev_above) and is_rail(surrounds.x_prev) then
|
||||
status.eject = true
|
||||
elseif direction == 0 and is_ejector(surrounds.z_next_above) and is_rail(surrounds.z_next) then
|
||||
status.eject = true
|
||||
elseif direction == 2 and is_ejector(surrounds.z_prev_above) and is_rail(surrounds.z_prev) then
|
||||
status.eject = true
|
||||
end
|
||||
|
||||
-- Check for detectors - below the cart, or to the left
|
||||
local detpos
|
||||
if is_detector(surrounds.below) then
|
||||
detpos = {x=pos.x,y=pos.y-1,z=pos.z}
|
||||
else
|
||||
if direction == 0 and is_detector(surrounds.x_prev) then
|
||||
detpos = {x=pos.x-1,y=pos.y,z=pos.z}
|
||||
elseif direction == 1 and is_detector(surrounds.z_next) then
|
||||
detpos = {x=pos.x,y=pos.y,z=pos.z+1}
|
||||
elseif direction == 2 and is_detector(surrounds.x_next) then
|
||||
detpos = {x=pos.x+1,y=pos.y,z=pos.z}
|
||||
elseif direction == 1 and is_detector(surrounds.z_prev) then
|
||||
detpos = {x=pos.x,y=pos.y,z=pos.z-1}
|
||||
end
|
||||
end
|
||||
if detpos then
|
||||
status.detector = detpos
|
||||
end
|
||||
end
|
||||
|
||||
if speed == 0 then
|
||||
|
||||
-- Check if the cart should be grabbed
|
||||
if is_grabber(surrounds.x_next_above) then
|
||||
status.grab = {x=pos.x+1,y=pos.y+1,z=pos.z}
|
||||
elseif is_grabber(surrounds.x_prev_above) then
|
||||
status.grab = {x=pos.x-1,y=pos.y+1,z=pos.z}
|
||||
elseif is_grabber(surrounds.z_prev_above) then
|
||||
status.grab = {x=pos.x,y=pos.y+1,z=pos.z-1}
|
||||
elseif is_grabber(surrounds.z_next_above) then
|
||||
status.grab = {x=pos.x,y=pos.y+1,z=pos.z+1}
|
||||
elseif is_grabber(surrounds.below) then
|
||||
status.grab = {x=pos.x,y=pos.y-1,z=pos.z}
|
||||
end
|
||||
|
||||
-- Check for being next to a launcher (only stationary carts)
|
||||
check_launcher(surrounds, status)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
if surrounds.below and surrounds.below.name == "railcarts:autolauncher" then
|
||||
status.onautolauncher = true
|
||||
end
|
||||
|
||||
status.railtype = railtype
|
||||
return status
|
||||
end
|
||||
|
||||
|
||||
--- Check if the given node should cause player ejection.
|
||||
-- This is currently any walkable node.
|
||||
-- @param node The node to check (can be nil)
|
||||
function is_ejector(node)
|
||||
if not node then return false end
|
||||
nd = minetest.registered_nodes[node.name]
|
||||
return nd.walkable
|
||||
end
|
||||
|
||||
--- Check if the given node is a cart detector.
|
||||
-- Only unactivated detectors are relevant.
|
||||
-- @param node The node to check (can be nil)
|
||||
function is_detector(node)
|
||||
if not node then return false end
|
||||
return node.name == "railcarts:cart_detector"
|
||||
end
|
||||
|
||||
--- Check if the given node is a hopper.
|
||||
-- @param node The node to check (can be nil)
|
||||
function is_hopper(node)
|
||||
if not node then return false end
|
||||
return node.name == "railcarts:hopper"
|
||||
end
|
||||
|
||||
--- Check for a launcher adjacent to a position, with a rail in the other
|
||||
-- direction.
|
||||
-- @param surrounds The surrounding nodes
|
||||
-- @param result, into which launch and autolaunch fields are inserted if
|
||||
-- necessary
|
||||
function check_launcher(surrounds, result)
|
||||
|
||||
if is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below) then
|
||||
if is_launcher(surrounds.x_next) then result.launch = 3 end
|
||||
if is_autolauncher(surrounds.x_next) then result.autolaunch = 3 end
|
||||
end
|
||||
|
||||
if is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below) then
|
||||
if is_launcher(surrounds.x_prev) then result.launch = 1 end
|
||||
if is_autolauncher(surrounds.x_prev) then result.autolaunch = 1 end
|
||||
end
|
||||
|
||||
if is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below) then
|
||||
if is_launcher(surrounds.z_next) then result.launch = 2 end
|
||||
if is_autolauncher(surrounds.z_next) then result.autolaunch = 2 end
|
||||
end
|
||||
|
||||
if is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below) then
|
||||
if is_launcher(surrounds.z_prev) then result.launch = 0 end
|
||||
if is_autolauncher(surrounds.z_prev) then result.autolaunch = 0 end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Check for a crossing at the given location.
|
||||
-- @param direction The cart direction
|
||||
-- @param surrounds The surrounding nodes
|
||||
--
|
||||
-- @return If there's no corssing at the current position, nil is returned.
|
||||
-- Otherwise, "x" or "z" is returned.
|
||||
function check_crossing(direction, surrounds)
|
||||
|
||||
if (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) and
|
||||
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) and
|
||||
(is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) and
|
||||
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) then
|
||||
if direction == 1 or direction == 3 then
|
||||
return "x"
|
||||
else
|
||||
return "z"
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check for a switch at the given location.
|
||||
-- @param node The node to check at
|
||||
-- @param pos The position of the node
|
||||
-- @param direction The cart direction
|
||||
-- @param surrounds The surrounding nodes
|
||||
--
|
||||
-- @return If there's no switch at the current position, nil is returned.
|
||||
-- Otherwise, "x" or "z" is returned for a switch which is in a straight
|
||||
-- configuration (according to the current direction), and for a curve
|
||||
-- one of the "x+", "x-", "z+" or "z-" curve designations, as returned
|
||||
-- by check_curve.
|
||||
function check_switch(node, pos, direction, surrounds)
|
||||
|
||||
-- junction : Z-, Z+, X+
|
||||
if (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) and
|
||||
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) and
|
||||
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) then
|
||||
|
||||
if node.name == "railcarts:rail_switched" then
|
||||
if direction == 0 then
|
||||
return "z"
|
||||
else
|
||||
return "z+"
|
||||
end
|
||||
else
|
||||
if direction == 2 then
|
||||
return "z"
|
||||
else
|
||||
return "x+"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- junction : Z-, Z+, X-
|
||||
if (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) and
|
||||
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) and
|
||||
(is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) then
|
||||
|
||||
if node.name == "railcarts:rail_switched" then
|
||||
if direction == 2 then
|
||||
return "z"
|
||||
else
|
||||
return "x-"
|
||||
end
|
||||
else
|
||||
if direction == 0 then
|
||||
return "z"
|
||||
else
|
||||
return "z-"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- junction : X-, X+, Z-
|
||||
if (is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) and
|
||||
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) and
|
||||
(is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) then
|
||||
if node.name == "railcarts:rail_switched" then
|
||||
if direction == 1 then
|
||||
return "x"
|
||||
else
|
||||
return "x+"
|
||||
end
|
||||
else
|
||||
if direction == 3 then
|
||||
return "x"
|
||||
else
|
||||
return "x-"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- junction : X-, X+, Z-
|
||||
if (is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) and
|
||||
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) and
|
||||
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) then
|
||||
if node.name == "railcarts:rail_switched" then
|
||||
if direction == 3 then
|
||||
return "x"
|
||||
else
|
||||
return "z-"
|
||||
end
|
||||
else
|
||||
if direction == 1 then
|
||||
return "x"
|
||||
else
|
||||
return "z+"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Check if the given railtype is a curve
|
||||
-- @param railtype A railtype (see get_rail_status)
|
||||
-- @return True if it's a curve
|
||||
function is_curve(railtype)
|
||||
if railtype == "x+" then return true end
|
||||
if railtype == "x-" then return true end
|
||||
if railtype == "z+" then return true end
|
||||
if railtype == "z-" then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check for a curve at the given position.
|
||||
-- @param surrounds The surrounding nodes
|
||||
-- @return Nil if there is no curve, otherwise one of four curve designations.
|
||||
-- "x+" has rails at x+1 and z-1, "x-" has rails at x-1 and z-1, "z+" has rails
|
||||
-- at x+1 and z+1, and "z-" has nodes at x-1 and z+1.
|
||||
function check_curve(surrounds)
|
||||
|
||||
if (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) and
|
||||
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) then
|
||||
return "x+"
|
||||
end
|
||||
|
||||
if (is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) and
|
||||
(is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) then
|
||||
return "x-"
|
||||
end
|
||||
|
||||
if (is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) and
|
||||
(is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) then
|
||||
return "z-"
|
||||
end
|
||||
|
||||
if (is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) and
|
||||
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) then
|
||||
return "z+"
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
BIN
screenshot.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
textures/railcarts_autolauncher.png
Normal file
After Width: | Height: | Size: 730 B |
BIN
textures/railcarts_cart.png
Normal file
After Width: | Height: | Size: 208 B |
BIN
textures/railcarts_cart_detector.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
textures/railcarts_hopper_side.png
Normal file
After Width: | Height: | Size: 312 B |
BIN
textures/railcarts_inv_cart_side.png
Normal file
After Width: | Height: | Size: 192 B |
BIN
textures/railcarts_inv_cart_top.png
Normal file
After Width: | Height: | Size: 262 B |
BIN
textures/railcarts_inv_transportcart_top.png
Normal file
After Width: | Height: | Size: 302 B |
BIN
textures/railcarts_launcher_off.png
Normal file
After Width: | Height: | Size: 747 B |
BIN
textures/railcarts_launcher_on.png
Normal file
After Width: | Height: | Size: 741 B |
BIN
textures/railcarts_pa_carts_rail_brk.png
Normal file
After Width: | Height: | Size: 524 B |
BIN
textures/railcarts_pa_carts_rail_crossing_brk.png
Normal file
After Width: | Height: | Size: 498 B |
BIN
textures/railcarts_pa_carts_rail_crossing_digi.png
Normal file
After Width: | Height: | Size: 619 B |
BIN
textures/railcarts_pa_carts_rail_crossing_pwr.png
Normal file
After Width: | Height: | Size: 604 B |
BIN
textures/railcarts_pa_carts_rail_curved_brk.png
Normal file
After Width: | Height: | Size: 555 B |
BIN
textures/railcarts_pa_carts_rail_curved_digi.png
Normal file
After Width: | Height: | Size: 568 B |
BIN
textures/railcarts_pa_carts_rail_curved_pwr.png
Normal file
After Width: | Height: | Size: 577 B |
BIN
textures/railcarts_pa_carts_rail_digi.png
Normal file
After Width: | Height: | Size: 549 B |
BIN
textures/railcarts_pa_carts_rail_pwr.png
Normal file
After Width: | Height: | Size: 533 B |
BIN
textures/railcarts_pa_carts_rail_t_junction_brk.png
Normal file
After Width: | Height: | Size: 548 B |
BIN
textures/railcarts_pa_carts_rail_t_junction_digi.png
Normal file
After Width: | Height: | Size: 603 B |
BIN
textures/railcarts_pa_carts_rail_t_junction_pwr.png
Normal file
After Width: | Height: | Size: 584 B |
BIN
textures/railcarts_rail_t_junction_switched.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
textures/railcarts_tex_boringcart.png
Normal file
After Width: | Height: | Size: 568 B |
BIN
textures/railcarts_tex_cart.png
Normal file
After Width: | Height: | Size: 422 B |
BIN
textures/railcarts_tex_transportcart.png
Normal file
After Width: | Height: | Size: 490 B |
BIN
textures/railcarts_transport_cart.png
Normal file
After Width: | Height: | Size: 196 B |
27
util.lua
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
--- Convert a pos to a human-readable string
|
||||
-- @param pos The position
|
||||
-- @return A readable string of the form (x,y,z)
|
||||
function postostr(pos)
|
||||
if pos == nil then return "(nil)" end
|
||||
return "("..pos.x..","..pos.y..","..pos.z..")"
|
||||
end
|
||||
|
||||
--- Get a copy of a position
|
||||
-- @param pos The position
|
||||
-- @return An identical position
|
||||
function poscopy(pos)
|
||||
return {x=pos.x,y=pos.y,z=pos.z}
|
||||
end
|
||||
|
||||
--- Check if two 3d vectors are equal
|
||||
function v3equal(v1, v2)
|
||||
return v1.x == v2.x and v1.y == v2.y and v1.z == v2.z
|
||||
end
|
||||
|
||||
function v3posround(v1)
|
||||
local posround = function(n)
|
||||
return math.floor(n+0.5)
|
||||
end
|
||||
return {x=posround(v1.x), y=posround(v1.y), z=posround(v1.z)}
|
||||
end
|