built on 14/05/2021 18:50:16
@ -184,7 +184,9 @@ local function update_node(pos)
|
||||
nnode = minetest.get_node(npos)
|
||||
if NodeTbl1[nnode.name] and NodeTbl3[node.name] then
|
||||
node.name = node.name .. "1"
|
||||
if minetest.registered_nodes[node.name] then
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
return
|
||||
end
|
||||
-- check case 2
|
||||
@ -192,7 +194,9 @@ local function update_node(pos)
|
||||
nnode = minetest.get_node(npos)
|
||||
if NodeTbl2[nnode.name] then
|
||||
node.name = string.sub(node.name,1,-1) .. "2"
|
||||
if minetest.registered_nodes[node.name] then
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
return
|
||||
end
|
||||
-- check case 3
|
||||
@ -203,7 +207,9 @@ local function update_node(pos)
|
||||
if NodeTbl1[nnode.name] and NodeTbl3[node.name] then
|
||||
node.name = node.name .. "1"
|
||||
node.param2 = 3
|
||||
if minetest.registered_nodes[node.name] then
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
return
|
||||
end
|
||||
-- check case 4
|
||||
@ -212,7 +218,9 @@ local function update_node(pos)
|
||||
if NodeTbl2[nnode.name] then
|
||||
node.name = string.sub(node.name,1,-1) .. "2"
|
||||
node.param2 = 3
|
||||
if minetest.registered_nodes[node.name] then
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
8
basic_materials/.gitlab-ci.yml
Normal file
@ -0,0 +1,8 @@
|
||||
stages:
|
||||
- test
|
||||
|
||||
luacheck:
|
||||
stage: test
|
||||
image: pipelinecomponents/luacheck:latest
|
||||
script:
|
||||
- luacheck .
|
@ -2,7 +2,7 @@ local S = minetest.get_translator("compost")
|
||||
|
||||
compost = {}
|
||||
|
||||
local CYCLE_TIME = 10
|
||||
local CYCLE_TIME = 30
|
||||
|
||||
-- Version for compatibility checks
|
||||
compost.version = 1.0
|
||||
|
@ -11,8 +11,7 @@ It is the fast and modern way of travelling.
|
||||
* It can be used even on small servers without lagging
|
||||
* No configuration or programming of the tube network is necessary (only the station names have to be entered)
|
||||
|
||||
|
||||
**![See Wiki Page for more info](https://github.com/joe7575/Minetest-Hyperloop/wiki)**
|
||||
**[See Wiki Page for more info](https://github.com/joe7575/Minetest-Hyperloop/wiki)**
|
||||
|
||||
![screenshot](https://github.com/joe7575/Minetest-Hyperloop/blob/master/screenshot.png)
|
||||
|
||||
@ -33,9 +32,9 @@ The mod includes many different kind of blocks:
|
||||
..and more.
|
||||
|
||||
|
||||
Browse on: ![GitHub](https://github.com/joe7575/Minetest-Hyperloop)
|
||||
Browse on: [GitHub](https://github.com/joe7575/Minetest-Hyperloop)
|
||||
|
||||
Download: ![GitHub](https://github.com/joe7575/Minetest-Hyperloop/archive/master.zip)
|
||||
Download: [GitHub](https://github.com/joe7575/Minetest-Hyperloop/archive/master.zip)
|
||||
|
||||
|
||||
## Migration from v1 to v2
|
||||
@ -59,7 +58,7 @@ has some risks. Therefore:
|
||||
|
||||
## Introduction
|
||||
|
||||
**![See Wiki Page for more info](https://github.com/joe7575/Minetest-Hyperloop/wiki)**
|
||||
**[See Wiki Page for more info](https://github.com/joe7575/Minetest-Hyperloop/wiki)**
|
||||
|
||||
|
||||
## Configuration
|
||||
@ -68,23 +67,25 @@ The following can be changed in the minetest menu (Settings -> Advanced Settings
|
||||
* "WiFi block crafting enabled" - To enable the crafting of WiFi blocks (default: false)
|
||||
* "free tube placement enabled" - If enabled Hyperloop Tubes and Elevator Shafts can be build in all directions (default: true)
|
||||
When this option is disabled, Hyperloop tubes can only be built in the horizontal direction and elevator shafts in the vertical direction.
|
||||
* "enable building of subnets" - If enabled the ticket block has an additional field for specifying a subnet name. Stations with the same subnet name (optional) represent an isolated subnet within the Hyperloop network.
|
||||
|
||||
Example for 'minetest.conf':
|
||||
```LUA
|
||||
hyperloop_wifi_enabled = true
|
||||
hyperloop_wifi_crafting_enabled = false
|
||||
hyperloop_free_tube_placement_enabled = true
|
||||
hyperloop_wifi_enabled = true -- WiFi block enabled
|
||||
hyperloop_wifi_crafting_enabled = false -- WiFi block crafting enabled
|
||||
hyperloop_free_tube_placement_enabled = true -- free tube placement enabled
|
||||
hyperloop_subnet_enabled = true -- enable building of subnets
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
tubelib2 (![GitHub](https://github.com/joe7575/tubelib2))
|
||||
tubelib2 ([GitHub](https://github.com/joe7575/tubelib2))
|
||||
default
|
||||
intllib
|
||||
optional: worldedit, techage
|
||||
|
||||
|
||||
# License
|
||||
Copyright (C) 2017,2020 Joachim Stolberg
|
||||
Copyright (C) 2017,2021 Joachim Stolberg
|
||||
Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt
|
||||
Textures: CC0
|
||||
Display: Derived from the work of kaeza, sofar and others (digilines) LGPLv2.1+
|
||||
|
@ -75,35 +75,72 @@ local function remove_junctions(sortedList)
|
||||
return tbl
|
||||
end
|
||||
|
||||
local function station_list_as_string(pos)
|
||||
-- Generate a distance sorted list of all connected stations
|
||||
local sortedList = Stations:station_list(pos, pos, "dist")
|
||||
-- Delete the own station from list
|
||||
table.remove(sortedList, 1)
|
||||
local function filter_subnet(sortedList, subnet)
|
||||
if hyperloop.subnet_enabled then
|
||||
if subnet == "" then
|
||||
subnet = nil
|
||||
end
|
||||
|
||||
local tbl = {}
|
||||
for idx,item in ipairs(sortedList) do
|
||||
if item.subnet == subnet then
|
||||
tbl[#tbl+1] = item
|
||||
end
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
return sortedList
|
||||
end
|
||||
|
||||
-- Used to update the station list for booking machine
|
||||
-- and teleport list.
|
||||
local function station_list_as_string(pos, subnet)
|
||||
local meta = M(pos)
|
||||
-- Generate a name sorted list of all connected stations
|
||||
local sortedList = Stations:station_list(pos, pos, "name")
|
||||
-- remove all junctions from the list
|
||||
sortedList = remove_junctions(sortedList)
|
||||
-- use subnet pattern to reduce the list
|
||||
sortedList = filter_subnet(sortedList, subnet)
|
||||
-- store the list for later use
|
||||
store_station_list(pos, sortedList)
|
||||
-- Generate the formspec string
|
||||
return generate_string(sortedList)
|
||||
end
|
||||
|
||||
local naming_formspec = nil
|
||||
|
||||
local function naming_formspec(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local formspec = "size[6,4]"..
|
||||
if hyperloop.subnet_enabled then
|
||||
naming_formspec = function(pos)
|
||||
local meta = M(pos)
|
||||
local formspec = "size[7,5.4]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[0,0;"..S("Please enter the station name to\nwhich this booking machine belongs.").."]" ..
|
||||
"field[0.5,1.5;5,1;name;"..S("Station name")..";MyTown]" ..
|
||||
"field[0.5,2.7;5,1;info;"..S("Additional station information")..";]" ..
|
||||
"button_exit[2,3.6;2,1;exit;Save]"
|
||||
"field[0.2,1.5;7.1,1;name;"..S("Station name")..";MyTown]" ..
|
||||
"field[0.2,2.7;7.1,1;info;"..S("Additional station information")..";]" ..
|
||||
"field[0.2,3.9;7.1,1;subnet;"..S("Subnet name (optional)")..";]" ..
|
||||
"button_exit[2.5,4.7;2,1;exit;Save]"
|
||||
meta:set_string("formspec", formspec)
|
||||
meta:set_int("change_counter", 0)
|
||||
end
|
||||
else
|
||||
naming_formspec = function(pos)
|
||||
local meta = M(pos)
|
||||
local formspec = "size[7,4.4]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[0,0;"..S("Please enter the station name to\nwhich this booking machine belongs.").."]" ..
|
||||
"field[0.2,1.5;7.1,1;name;"..S("Station name")..";MyTown]" ..
|
||||
"field[0.2,2.7;7.1,1;info;"..S("Additional station information")..";]" ..
|
||||
"button_exit[2.5,3.7;2,1;exit;Save]"
|
||||
meta:set_string("formspec", formspec)
|
||||
meta:set_int("change_counter", 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function booking_machine_update(pos)
|
||||
local meta = M(pos)
|
||||
local sStationPos = meta:get_string("sStationPos")
|
||||
@ -111,25 +148,19 @@ local function booking_machine_update(pos)
|
||||
local station_pos = P(sStationPos)
|
||||
local counter = meta:get_int("change_counter") or 0
|
||||
local changed, newcounter = Stations:changed(counter)
|
||||
if changed then
|
||||
meta:set_string("formspec", station_list_as_string(station_pos))
|
||||
if changed or not tStationList[sStationPos] then
|
||||
local subnet = meta:get_string("subnet")
|
||||
meta:set_string("formspec", station_list_as_string(station_pos, subnet))
|
||||
meta:set_int("change_counter", newcounter)
|
||||
end
|
||||
if not tStationList[sStationPos] then
|
||||
local sortedList = Stations:station_list(station_pos, station_pos, "dist")
|
||||
-- Delete the own station from list
|
||||
table.remove(sortedList, 1)
|
||||
-- remove all junctions from the list
|
||||
sortedList = remove_junctions(sortedList)
|
||||
-- store the list for later use
|
||||
store_station_list(station_pos, sortedList)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_rightclick(pos)
|
||||
booking_machine_update(pos)
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
booking_machine_update(pos)
|
||||
-- station name entered?
|
||||
if fields.name ~= nil then
|
||||
local station_name = string.trim(fields.name)
|
||||
@ -142,17 +173,25 @@ local function on_receive_fields(pos, formname, fields, player)
|
||||
hyperloop.chat(player, S("Station has already a booking machine!"))
|
||||
return
|
||||
end
|
||||
-- add subnet name if available
|
||||
local subnet = string.trim(fields.subnet or "")
|
||||
if subnet == "" then
|
||||
subnet = nil
|
||||
end
|
||||
-- store meta and generate station formspec
|
||||
Stations:update(stationPos, {
|
||||
name = station_name,
|
||||
booking_pos = pos,
|
||||
booking_info = string.trim(fields.info),
|
||||
subnet = subnet,
|
||||
})
|
||||
|
||||
local meta = M(pos)
|
||||
meta:set_string("sStationPos", SP(stationPos))
|
||||
meta:set_string("infotext", "Station: "..station_name)
|
||||
meta:set_string("formspec", station_list_as_string(stationPos))
|
||||
meta:set_string("subnet", string.trim(fields.subnet or ""))
|
||||
meta:set_int("change_counter", 0) -- force update
|
||||
booking_machine_update(pos)
|
||||
else
|
||||
hyperloop.chat(player, S("Invalid station name!"))
|
||||
end
|
||||
@ -233,6 +272,7 @@ minetest.register_node("hyperloop:booking", {
|
||||
on_rotate = screwdriver.disallow,
|
||||
on_receive_fields = on_receive_fields,
|
||||
on_destruct = on_destruct,
|
||||
on_rightclick = on_rightclick,
|
||||
|
||||
paramtype = 'light',
|
||||
light_source = 2,
|
||||
@ -279,10 +319,4 @@ minetest.register_node("hyperloop:booking_ground", {
|
||||
})
|
||||
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "[Hyperloop] Booking machine update",
|
||||
name = "hyperloop:update",
|
||||
nodenames = {"hyperloop:booking", "hyperloop:booking_ground"},
|
||||
run_at_every_load = true,
|
||||
action = booking_machine_update
|
||||
})
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
v2.06 by JoSt
|
||||
|
||||
Copyright (C) 2017-2019 Joachim Stolberg
|
||||
Copyright (C) 2017-2021 Joachim Stolberg
|
||||
|
||||
LGPLv2.1+
|
||||
See LICENSE.txt for more information
|
||||
@ -34,6 +34,7 @@
|
||||
2020-01-03 v2.04 Elevator door bugfix (MT 5+)
|
||||
2020-03-12 v2.05 minetest translator added (thanks to acmgit/Clyde)
|
||||
2020-06-14 v2.06 The default value for `hyperloop_free_tube_placement_enabled` is now true
|
||||
2021-02-07 v2.07 tube_crowbar: Add tube length check
|
||||
|
||||
]]--
|
||||
|
||||
@ -65,6 +66,7 @@ else
|
||||
hyperloop.wifi_enabled = minetest.settings:get_bool("hyperloop_wifi_enabled")
|
||||
hyperloop.wifi_crafting_enabled = minetest.settings:get_bool("hyperloop_wifi_crafting_enabled")
|
||||
hyperloop.free_tube_placement_enabled = minetest.settings:get_bool("hyperloop_free_tube_placement_enabled", true)
|
||||
hyperloop.subnet_enabled = minetest.settings:get_bool("hyperloop_subnet_enabled")
|
||||
|
||||
dofile(minetest.get_modpath("hyperloop") .. "/network.lua")
|
||||
dofile(minetest.get_modpath("hyperloop") .. "/data_base.lua")
|
||||
|
@ -96,6 +96,15 @@ local function sort_based_on_distance(tStations, pos)
|
||||
return lStations
|
||||
end
|
||||
|
||||
-- Return a list with sorted stations
|
||||
local function sort_based_on_name(tStations, pos)
|
||||
local lStations = table_to_list(table.copy(tStations))
|
||||
-- Add distance
|
||||
lStations = add_distance_to_list(lStations, pos)
|
||||
table.sort(lStations, function(a,b) return a.name < b.name end)
|
||||
return lStations
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Class Network
|
||||
@ -233,8 +242,12 @@ function Network:station_list(pos, station_pos, sorted)
|
||||
end
|
||||
if sorted == "dist" then
|
||||
lStations = sort_based_on_distance(tStations, pos)
|
||||
else
|
||||
elseif sorted == "level" then
|
||||
lStations = sort_based_on_level(tStations)
|
||||
else
|
||||
-- delete own station from list
|
||||
tStations[S(station_pos)] = nil
|
||||
lStations = sort_based_on_name(tStations, pos)
|
||||
end
|
||||
return lStations
|
||||
end
|
||||
|
@ -8,3 +8,8 @@ hyperloop_wifi_crafting_enabled (WiFi block crafting enabled) bool false
|
||||
# If disabled, connected stations have to be on one level,
|
||||
# typically underground.
|
||||
hyperloop_free_tube_placement_enabled (free tube placement enabled) bool false
|
||||
|
||||
# The ticket block has an additional field for specifying a subnet name.
|
||||
# Stations with the same subnet name (optional) represent an isolated
|
||||
# subnet within the Hyperloop network.
|
||||
hyperloop_subnet_enabled (enable building of subnets) bool false
|
@ -4,9 +4,9 @@ Minecart
|
||||
**Minecart, the lean railway transportation automation system**
|
||||
|
||||
|
||||
Browse on: ![GitHub](https://github.com/joe7575/minecart)
|
||||
Browse on: [GitHub](https://github.com/joe7575/minecart)
|
||||
|
||||
Download: ![GitHub](https://github.com/joe7575/minecart/archive/master.zip)
|
||||
Download: [GitHub](https://github.com/joe7575/minecart/archive/master.zip)
|
||||
|
||||
![minecart](https://github.com/joe7575/minecart/blob/master/screenshot.png)
|
||||
|
||||
@ -26,21 +26,15 @@ license).
|
||||
3. https://github.com/stujones11/railcart/
|
||||
|
||||
|
||||
Original Cart Features
|
||||
----------------------
|
||||
|
||||
- A fast cart for your railway or roller coaster (up to 7 m/s!)
|
||||
- Boost and brake rails
|
||||
- Rail junction switching with the 'right-left' walking keys
|
||||
- Handbrake with the 'back' key
|
||||
|
||||
|
||||
Minecart Features
|
||||
-----------------
|
||||
|
||||
The mod Minecart has its own cart (called Minecart) in addition to the standard cart.
|
||||
Minecarts are used for automated item transport on private and public rail networks.
|
||||
The mod features are:
|
||||
- a fast cart for your railway or roller coaster (up to 8 m/s!)
|
||||
- boost rails and speed limit signs
|
||||
- rail junction switching with the 'right-left' walking keys
|
||||
- configurable timetables and routes for Minecarts
|
||||
- automated loading/unloading of Minecarts by means of a Minecart Hopper
|
||||
- rail network protection based on protection blocks called Land Marks
|
||||
@ -73,17 +67,16 @@ Introduction
|
||||
5. Drive from buffer to buffer in both directions using the Minecart(!) to record the
|
||||
routes (use 'right-left' keys to control the Minecart)
|
||||
6. Punch the buffers to check the connection data (e.g. "Oxford: connected to Cambridge")
|
||||
7. Optional: Configure the Minecart stop time in one or both buffers. The Minecart
|
||||
7. Optional: Configure the Minecart waiting time in both buffers. The Minecart
|
||||
will then start automatically after the configured time
|
||||
8. Optional: Protect your rail network with the Protection Landmarks (one Landmark
|
||||
at least every 16 nodes/meters)
|
||||
9. Place a Minecart in front of the buffer and check whether it starts after the
|
||||
configured time
|
||||
10. Check the cart state via the chat command: /mycart <num>
|
||||
'<num>' is the cart number
|
||||
'<num>' is the cart number, or get a list of carts with /mycart
|
||||
11. Drop items into the Minecart and punch the cart to start it, or "sneak+click" the
|
||||
Minecart to get the items back
|
||||
12. Dig the empty cart with a second "sneak+click" (as usual)
|
||||
Minecart to get cart and items back
|
||||
|
||||
|
||||
Hopper
|
||||
@ -97,6 +90,43 @@ to/from Minecarts. To unload a Minecart place the hopper below the rail.
|
||||
To load the Minecart, place the hopper right next to the Minecart.
|
||||
|
||||
|
||||
Cart Pusher
|
||||
-----------
|
||||
|
||||
Used to push a cart if the cart does not stop directly at a buffer.
|
||||
The block has to be placed below the rail.
|
||||
|
||||
|
||||
Cart Speed / Speed Limit Signs
|
||||
------------------------------
|
||||
|
||||
As before, the speed of the carts is also influenced by power rails.
|
||||
Brake rails are irrelevant, the cart does not brake here.
|
||||
The maximum speed is 8 m/s. This assumes a ratio of power rails
|
||||
to normal rails of 1 to 4 on a flat section of rail. A rail section is a
|
||||
series of rail nodes without a change of direction. After every curve / kink,
|
||||
the speed for the next section of the route is newly determined,
|
||||
taking into account the swing of the cart. This means that a cart can
|
||||
roll over short rail sections without power rails.
|
||||
|
||||
In order to additionally brake the cart at certain points
|
||||
(at switches or in front of a buffer), speed limit signs can be placed
|
||||
on the track. With these signs the speed can be reduced to 4, 2, or 1 m / s.
|
||||
The "No speed limit" sign can be used to remove the speed limit.
|
||||
|
||||
The speed limit signs must be placed next to the track so that they can
|
||||
be read from the cart. This allows different speeds in each direction of travel.
|
||||
|
||||
|
||||
Migration to v2
|
||||
---------------
|
||||
|
||||
The way how carts are monitored and the cart speed is calculated has changed.
|
||||
Therefore, it is necessary that all carts are repositioned and the
|
||||
recording is repeated.
|
||||
Rails and buffers are not affected and can be kept unchanged.
|
||||
|
||||
|
||||
History
|
||||
-------
|
||||
|
||||
@ -118,3 +148,5 @@ History
|
||||
2020-07-24 V1.08 Adapted to new techage ICTA style
|
||||
2020-08-14 V1.09 Hopper support for digtron, protector:chest and default:furnace added
|
||||
2020-11-12 V1.10 Make carts more robust against server lag
|
||||
2021-04-10 V2.00 Complete revision to make carts robust against server load/lag,
|
||||
Speed limit signs and cart terminal added
|
||||
|
52
minecart/api.lua
Normal file
@ -0,0 +1,52 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
--
|
||||
-- API functions
|
||||
--
|
||||
|
||||
-- 'pos' is the position of the puncher/sensor, the cart
|
||||
-- position will be determined by means of 'param2' and 'radius'
|
||||
function minecart.is_cart_available(pos, param2, radius)
|
||||
local pos2 = minecart.get_nodecart_nearby(pos, param2, radius)
|
||||
if pos2 then
|
||||
return true
|
||||
end
|
||||
-- The entity check is needed for a cart with driver
|
||||
local entity = minecart.get_entitycart_nearby(pos, param2, radius)
|
||||
if entity then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.is_nodecart_available(pos, param2, radius)
|
||||
local pos2 = minecart.get_nodecart_nearby(pos, param2, radius)
|
||||
if pos2 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- 'pos' is the position of the puncher/sensor, the cart
|
||||
-- position will be determined by means of 'param2' and 'radius'
|
||||
function minecart.punch_cart(pos, param2, radius, punch_dir)
|
||||
local pos2, node = minecart.get_nodecart_nearby(pos, param2, radius)
|
||||
if pos2 then
|
||||
minecart.start_nodecart(pos2, node.name, nil, punch_dir)
|
||||
return true
|
||||
end
|
||||
-- The entity check is needed for a cart with driver
|
||||
local entity = minecart.get_entitycart_nearby(pos, param2, radius)
|
||||
if entity and entity.driver then
|
||||
minecart.push_entitycart(entity, punch_dir)
|
||||
return true
|
||||
end
|
||||
end
|
360
minecart/baselib.lua
Normal file
@ -0,0 +1,360 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local P2H = minetest.hash_node_position
|
||||
local H2P = minetest.get_position_from_hash
|
||||
|
||||
local param2_to_dir = {[0]=
|
||||
{x=0, y=0, z=1},
|
||||
{x=1, y=0, z=0},
|
||||
{x=0, y=0, z=-1},
|
||||
{x=-1, y=0, z=0},
|
||||
{x=0, y=-1, z=0},
|
||||
{x=0, y=1, z=0}
|
||||
}
|
||||
|
||||
-- Registered carts
|
||||
minecart.tNodeNames = {} -- [<cart_node_name>] = <cart_entity_name>
|
||||
minecart.tEntityNames = {} -- [<cart_entity_name>] = true
|
||||
minecart.lCartNodeNames = {} -- {<cart_node_name>, <cart_node_name>, ...}
|
||||
minecart.tCartTypes = {}
|
||||
|
||||
function minecart.param2_to_dir(param2)
|
||||
return param2_to_dir[param2 % 6]
|
||||
end
|
||||
|
||||
function minecart.get_node_lvm(pos)
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node then
|
||||
return node
|
||||
end
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
|
||||
local data = vm:get_data()
|
||||
local param2_data = vm:get_param2_data()
|
||||
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
||||
local idx = area:indexp(pos)
|
||||
if data[idx] and param2_data[idx] then
|
||||
return {
|
||||
name = minetest.get_name_from_content_id(data[idx]),
|
||||
param2 = param2_data[idx]
|
||||
}
|
||||
end
|
||||
return {name="ignore", param2=0}
|
||||
end
|
||||
|
||||
function minecart.find_node_near_lvm(pos, radius, items)
|
||||
local npos = minetest.find_node_near(pos, radius, items)
|
||||
if npos then
|
||||
return npos
|
||||
end
|
||||
local tItems = {}
|
||||
for _,v in ipairs(items) do
|
||||
tItems[v] = true
|
||||
end
|
||||
local pos1 = {x = pos.x - radius, y = pos.y - radius, z = pos.z - radius}
|
||||
local pos2 = {x = pos.x + radius, y = pos.y + radius, z = pos.z + radius}
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local MinEdge, MaxEdge = vm:read_from_map(pos1, pos2)
|
||||
local data = vm:get_data()
|
||||
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
||||
for x = pos1.x, pos2.x do
|
||||
for y = pos1.y, pos2.y do
|
||||
for z = pos1.z, pos2.z do
|
||||
local idx = area:indexp({x = x, y = y, z = z})
|
||||
if minetest.get_name_from_content_id(data[idx]) then
|
||||
return {x = x, y = y, z = z}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Marker entities for debugging purposes
|
||||
function minecart.set_marker(pos, text, size, ttl)
|
||||
local marker = minetest.add_entity(pos, "minecart:marker_cube")
|
||||
if marker ~= nil then
|
||||
marker:set_nametag_attributes({color = "#FFFFFF", text = text})
|
||||
size = size or 1
|
||||
marker:set_properties({visual_size = {x = size, y = size}})
|
||||
if ttl then
|
||||
minetest.after(ttl, marker.remove, marker)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity(":minecart:marker_cube", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
textures = {
|
||||
"minecart_marker_cube.png",
|
||||
"minecart_marker_cube.png",
|
||||
"minecart_marker_cube.png",
|
||||
"minecart_marker_cube.png",
|
||||
"minecart_marker_cube.png",
|
||||
"minecart_marker_cube.png",
|
||||
},
|
||||
physical = false,
|
||||
visual_size = {x = 1, y = 1},
|
||||
collisionbox = {-0.25,-0.25,-0.25, 0.25,0.25,0.25},
|
||||
glow = 8,
|
||||
static_save = false,
|
||||
},
|
||||
on_punch = function(self)
|
||||
self.object:remove()
|
||||
end,
|
||||
})
|
||||
|
||||
function minecart.is_air_like(name)
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if ndef and ndef.buildable_to then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function minecart.range(val, min, max)
|
||||
val = tonumber(val)
|
||||
if val < min then return min end
|
||||
if val > max then return max end
|
||||
return val
|
||||
end
|
||||
|
||||
function minecart.get_next_node(pos, param2)
|
||||
local pos2 = param2 and vector.add(pos, param2_to_dir[param2]) or pos
|
||||
local node = minetest.get_node(pos2)
|
||||
return pos2, node
|
||||
end
|
||||
|
||||
function minecart.get_object_id(object)
|
||||
for id, entity in pairs(minetest.luaentities) do
|
||||
if entity.object == object then
|
||||
return id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.is_owner(player, owner)
|
||||
if not player or not player:is_player() or not owner or owner == "" then
|
||||
return true
|
||||
end
|
||||
|
||||
local name = player:get_player_name()
|
||||
if minetest.check_player_privs(name, "minecart") then
|
||||
return true
|
||||
end
|
||||
return name == owner
|
||||
end
|
||||
|
||||
function minecart.get_buffer_pos(pos, player_name)
|
||||
local pos1 = minecart.find_node_near_lvm(pos, 1, {"minecart:buffer"})
|
||||
if pos1 then
|
||||
local meta = minetest.get_meta(pos1)
|
||||
if player_name == nil or player_name == meta:get_string("owner") then
|
||||
return pos1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.get_buffer_name(pos)
|
||||
local pos1 = minecart.find_node_near_lvm(pos, 1, {"minecart:buffer"})
|
||||
if pos1 then
|
||||
local name = M(pos1):get_string("name")
|
||||
if name ~= "" then
|
||||
return name
|
||||
end
|
||||
return P2S(pos1)
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.manage_attachment(player, entity, get_on)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
local player_name = player:get_player_name()
|
||||
if player_api.player_attached[player_name] == get_on then
|
||||
return
|
||||
end
|
||||
player_api.player_attached[player_name] = get_on
|
||||
|
||||
local obj = entity.object
|
||||
if get_on then
|
||||
player:set_attach(obj, "", {x=0, y=-4.5, z=-4}, {x=0, y=0, z=0})
|
||||
player:set_eye_offset({x=0, y=-6, z=0},{x=0, y=-6, z=0})
|
||||
player:set_properties({visual_size = {x = 2.5, y = 2.5}})
|
||||
player_api.set_animation(player, "sit")
|
||||
entity.driver = player:get_player_name()
|
||||
else
|
||||
player:set_detach()
|
||||
player:set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0})
|
||||
player:set_properties({visual_size = {x = 1, y = 1}})
|
||||
player_api.set_animation(player, "stand")
|
||||
entity.driver = nil
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.register_cart_names(node_name, entity_name, cart_type)
|
||||
minecart.tNodeNames[node_name] = entity_name
|
||||
minecart.tEntityNames[entity_name] = true
|
||||
minecart.lCartNodeNames[#minecart.lCartNodeNames+1] = node_name
|
||||
minecart.add_raillike_nodes(node_name)
|
||||
minecart.tCartTypes[node_name] = cart_type
|
||||
end
|
||||
|
||||
function minecart.add_nodecart(pos, node_name, param2, cargo, owner, userID)
|
||||
if pos and node_name and param2 and cargo and owner and userID then
|
||||
local pos2
|
||||
if not minecart.is_rail(pos) then
|
||||
pos2 = minetest.find_node_near(pos, 1, minecart.lRails)
|
||||
if not pos2 or not minecart.is_rail(pos2) then
|
||||
pos2 = minetest.find_node_near(pos, 2, minecart.lRails)
|
||||
if not pos2 or not minecart.is_rail(pos2) then
|
||||
pos2 = minetest.find_node_near(pos, 2, {"air"})
|
||||
end
|
||||
end
|
||||
else
|
||||
pos2 = vector.new(pos)
|
||||
end
|
||||
if pos2 then
|
||||
local node = minetest.get_node(pos2)
|
||||
local ndef = minetest.registered_nodes[node_name]
|
||||
local rail = node.name
|
||||
minetest.swap_node(pos2, {name = node_name, param2 = param2})
|
||||
local meta = M(pos2)
|
||||
meta:set_string("removed_rail", rail)
|
||||
meta:set_string("owner", owner)
|
||||
meta:set_int("userID", userID)
|
||||
meta:set_string("infotext", owner .. ": " .. userID)
|
||||
|
||||
if cargo and ndef.set_cargo then
|
||||
ndef.set_cargo(pos2, cargo)
|
||||
end
|
||||
if ndef.after_place_node then
|
||||
ndef.after_place_node(pos2)
|
||||
end
|
||||
return pos2
|
||||
else
|
||||
minetest.add_item(pos, ItemStack({name = node_name}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.add_entitycart(pos, node_name, entity_name, vel, cargo, owner, userID)
|
||||
local obj = minetest.add_entity(pos, entity_name)
|
||||
local objID = minecart.get_object_id(obj)
|
||||
|
||||
if objID then
|
||||
local entity = obj:get_luaentity()
|
||||
entity.start_pos = pos
|
||||
entity.owner = owner
|
||||
entity.node_name = node_name
|
||||
entity.userID = userID
|
||||
entity.objID = objID
|
||||
entity.cargo = cargo
|
||||
obj:set_nametag_attributes({color = "#ffff00", text = owner..": "..userID})
|
||||
obj:set_velocity(vel)
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.start_entitycart(self, pos)
|
||||
local route = {}
|
||||
|
||||
self.is_running = true
|
||||
self.arrival_time = 0
|
||||
self.start_pos = minecart.get_buffer_pos(pos, self.owner)
|
||||
if self.start_pos then
|
||||
-- Read buffer route for the junction info
|
||||
route = minecart.get_route(self.start_pos) or {}
|
||||
self.junctions = route and route.junctions
|
||||
end
|
||||
-- If set the start waypoint will be deleted
|
||||
self.no_normal_start = self.start_pos == nil
|
||||
if self.driver == nil then
|
||||
minecart.start_monitoring(self.owner, self.userID, pos, self.objID,
|
||||
route.checkpoints, route.junctions, self.cargo or {})
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.remove_nodecart(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
local meta = M(pos)
|
||||
local rail = meta:get_string("removed_rail")
|
||||
if rail == "" then rail = "air" end
|
||||
local userID = meta:get_int("userID")
|
||||
local owner = meta:get_string("owner")
|
||||
meta:set_string("infotext", "")
|
||||
meta:set_string("formspec", "")
|
||||
local cargo = ndef.get_cargo and ndef.get_cargo(pos) or {}
|
||||
minetest.swap_node(pos, {name = rail})
|
||||
return cargo, owner, userID
|
||||
end
|
||||
|
||||
function minecart.node_to_entity(pos, node_name, entity_name)
|
||||
-- Remove node
|
||||
local cargo, owner, userID = minecart.remove_nodecart(pos)
|
||||
local obj = minecart.add_entitycart(pos, node_name, entity_name,
|
||||
{x = 0, y = 0, z = 0}, cargo, owner, userID)
|
||||
if obj then
|
||||
return obj
|
||||
else
|
||||
print("Entity has no ID")
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.entity_to_node(pos, entity)
|
||||
-- Stop sound
|
||||
if entity.sound_handle then
|
||||
minetest.sound_stop(entity.sound_handle)
|
||||
entity.sound_handle = nil
|
||||
end
|
||||
|
||||
local rot = entity.object:get_rotation()
|
||||
local dir = minetest.yaw_to_dir(rot.y)
|
||||
local facedir = minetest.dir_to_facedir(dir)
|
||||
minecart.stop_recording(entity, pos)
|
||||
entity.object:remove()
|
||||
local pos2 = minecart.add_nodecart(pos, entity.node_name, facedir, entity.cargo, entity.owner, entity.userID)
|
||||
minecart.stop_monitoring(entity.owner, entity.userID, pos2)
|
||||
end
|
||||
|
||||
function minecart.add_node_to_player_inventory(pos, player, node_name)
|
||||
local inv = player:get_inventory()
|
||||
if not (creative and creative.is_enabled_for
|
||||
and creative.is_enabled_for(player:get_player_name()))
|
||||
or not inv:contains_item("main", node_name) then
|
||||
local leftover = inv:add_item("main", node_name)
|
||||
-- If no room in inventory, drop the cart
|
||||
if not leftover:is_empty() then
|
||||
minetest.add_item(pos, leftover)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Player removes the node
|
||||
function minecart.remove_entity(self, pos, player)
|
||||
-- Stop sound
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
end
|
||||
minecart.add_node_to_player_inventory(pos, player, self.node_name or "minecart:cart")
|
||||
minecart.stop_monitoring(self.owner, self.userID, pos)
|
||||
minecart.stop_recording(self, pos)
|
||||
minecart.monitoring_remove_cart(self.owner, self.userID)
|
||||
self.object:remove()
|
||||
end
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
@ -23,21 +23,17 @@ local StopTime = {}
|
||||
local function formspec(pos)
|
||||
local name = M(pos):get_string("name")
|
||||
local time = M(pos):get_int("time")
|
||||
local s = "size[4,4.2]" ..
|
||||
return "size[4,4.2]" ..
|
||||
"label[0,0;Configuration]" ..
|
||||
"field[0.5,1.2;3.6,1;name;"..S("Station name")..":;"..name.."]"..
|
||||
"button_exit[1,3.4;2,1;exit;Save]"
|
||||
if minecart.hopper_enabled then
|
||||
return s.."field[0.5,2.5;3.6,1;time;"..S("Stop time/sec")..":;"..time.."]"
|
||||
end
|
||||
return s
|
||||
"button_exit[1,3.4;2,1;exit;Save]"..
|
||||
"field[0.5,2.5;3.6,1;time;"..S("Waiting time/sec")..":;"..time.."]"
|
||||
end
|
||||
|
||||
local function remote_station_name(pos)
|
||||
local route = minecart.get_route(P2S(pos))
|
||||
local route = minecart.get_route(pos)
|
||||
if route and route.dest_pos then
|
||||
local pos2 = S2P(route.dest_pos)
|
||||
return M(pos2):get_string("name")
|
||||
return M(route.dest_pos):get_string("name")
|
||||
end
|
||||
return "none"
|
||||
end
|
||||
@ -46,23 +42,21 @@ local function on_punch(pos, node, puncher)
|
||||
local name = M(pos):get_string("name")
|
||||
M(pos):set_string("infotext", name..": "..S("connected to").." "..remote_station_name(pos))
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
if minecart.hopper_enabled then
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
end
|
||||
|
||||
-- Optional Teleport function
|
||||
if not minecart.teleport_enabled then return end
|
||||
local route = minecart.get_route(P2S(pos))
|
||||
local route = minecart.get_route(pos)
|
||||
if route and route.dest_pos and puncher and puncher:is_player() then
|
||||
|
||||
-- only teleport if the user is not pressing shift
|
||||
if not puncher:get_player_control()['sneak'] then
|
||||
local playername = puncher:get_player_name()
|
||||
local pos = S2P(route.dest_pos)
|
||||
|
||||
local teleport = function()
|
||||
-- Make sure the player object still exists
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
if player and pos then player:set_pos(pos) end
|
||||
if player then player:set_pos(route.dest_pos) end
|
||||
end
|
||||
minetest.after(0.25, teleport)
|
||||
end
|
||||
@ -95,24 +89,21 @@ minetest.register_node("minecart:buffer", {
|
||||
},
|
||||
after_place_node = function(pos, placer)
|
||||
M(pos):set_string("owner", placer:get_player_name())
|
||||
minecart.del_route(minetest.pos_to_string(pos))
|
||||
minecart.del_route(pos)
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
if minecart.hopper_enabled then
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
end
|
||||
end,
|
||||
on_timer = function(pos, elapsed)
|
||||
local time = M(pos):get_int("time")
|
||||
if time > 0 then
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
local param2 = (minetest.get_node(pos).param2 + 2) % 4
|
||||
if minecart.check_cart_for_pushing(pos, param2) then
|
||||
if minecart.is_cart_available(pos, param2, 0.5) then
|
||||
if StopTime[hash] then
|
||||
if StopTime[hash] < minetest.get_gametime() then
|
||||
StopTime[hash] = nil
|
||||
local node = minetest.get_node(pos)
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
minecart.punch_cart(pos, param2, 0, dir)
|
||||
local dir = minetest.facedir_to_dir(param2)
|
||||
minecart.punch_cart(pos, param2, 0.5, dir)
|
||||
end
|
||||
else
|
||||
StopTime[hash] = minetest.get_gametime() + time
|
||||
@ -124,7 +115,7 @@ minetest.register_node("minecart:buffer", {
|
||||
return true
|
||||
end,
|
||||
after_dig_node = function(pos)
|
||||
minecart.del_route(minetest.pos_to_string(pos))
|
||||
minecart.del_route(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
StopTime[hash] = nil
|
||||
end,
|
||||
@ -137,6 +128,7 @@ minetest.register_node("minecart:buffer", {
|
||||
M(pos):set_int("time", tonumber(fields.time) or 0)
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
M(pos):set_string("infotext", fields.name.." "..S("connected to").." "..remote_station_name(pos))
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
end
|
||||
end,
|
||||
on_punch = on_punch,
|
||||
@ -156,3 +148,15 @@ minetest.register_craft({
|
||||
{"default:steel_ingot", "default:junglewood", "default:steel_ingot"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "Delete waiting times",
|
||||
name = "minecart:del_time",
|
||||
nodenames = {"minecart:buffer"},
|
||||
run_at_every_load = false,
|
||||
action = function(pos, node)
|
||||
-- delete old data
|
||||
minecart.get_route(pos)
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
end,
|
||||
})
|
||||
|
@ -1,397 +0,0 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
Cart library functions (level 1)
|
||||
|
||||
]]--
|
||||
|
||||
-- Notes:
|
||||
-- 1) Only the owner can punch der cart
|
||||
-- 2) Only the owner can start the recording
|
||||
-- 3) But any player can act as cargo, cart punched by owner or buffer
|
||||
|
||||
local SLOPE_ACCELERATION = 3
|
||||
local MAX_SPEED = 7
|
||||
local PUNCH_SPEED = 3
|
||||
local SLOWDOWN = 0.4
|
||||
local RAILTYPE = minetest.get_item_group("carts:rail", "connect_to_raillike")
|
||||
local Y_OFFS_ON_SLOPES = 0.5
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = minecart.S
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local MP = minetest.get_modpath("minecart")
|
||||
local D = function(pos) return minetest.pos_to_string(vector.round(pos)) end
|
||||
|
||||
local tRails = {
|
||||
["carts:rail"] = true,
|
||||
["carts:powerrail"] = true,
|
||||
["carts:brakerail"] = true,
|
||||
}
|
||||
|
||||
local lRails = {"carts:rail", "carts:powerrail", "carts:brakerail"}
|
||||
|
||||
local function get_rail_node(pos)
|
||||
local rail_pos = vector.round(pos)
|
||||
local node = minecart.get_node_lvm(rail_pos)
|
||||
if tRails[node.name] then
|
||||
return rail_pos, node
|
||||
end
|
||||
end
|
||||
|
||||
local function find_rail_node(pos)
|
||||
local rail_pos = vector.round(pos)
|
||||
local node = get_rail_node(rail_pos)
|
||||
if node then
|
||||
return rail_pos, node
|
||||
end
|
||||
local pos1 = {x=rail_pos.x-1, y=rail_pos.y-1, z=rail_pos.z-1}
|
||||
local pos2 = {x=rail_pos.x+1, y=rail_pos.y+1, z=rail_pos.z+1}
|
||||
for _,pos3 in ipairs(minetest.find_nodes_in_area(pos1, pos2, lRails)) do
|
||||
--print("invalid position1", D(pos), D(pos3))
|
||||
return pos3, minecart.get_node_lvm(pos3)
|
||||
end
|
||||
--print("invalid position2", D(pos))
|
||||
end
|
||||
|
||||
local function get_pitch(dir)
|
||||
local pitch = 0
|
||||
if dir.y == -1 then
|
||||
pitch = -math.pi/4
|
||||
elseif dir.y == 1 then
|
||||
pitch = math.pi/4
|
||||
end
|
||||
return pitch * (dir.z == 0 and -1 or 1)
|
||||
end
|
||||
|
||||
local function get_yaw(dir)
|
||||
local yaw = 0
|
||||
if dir.x < 0 then
|
||||
yaw = math.pi/2*3
|
||||
elseif dir.x > 0 then
|
||||
yaw = math.pi/2
|
||||
elseif dir.z < 0 then
|
||||
yaw = math.pi
|
||||
end
|
||||
return yaw
|
||||
end
|
||||
|
||||
local function push_cart(self, pos, punch_dir, puncher)
|
||||
local vel = self.object:get_velocity()
|
||||
punch_dir = punch_dir or carts:velocity_to_dir(puncher:get_look_dir())
|
||||
punch_dir.y = 0
|
||||
local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, RAILTYPE)
|
||||
|
||||
-- Always start in horizontal direction
|
||||
cart_dir.y = 0
|
||||
|
||||
if vector.equals(cart_dir, {x=0, y=0, z=0}) then return end
|
||||
|
||||
local speed = vector.multiply(cart_dir, PUNCH_SPEED)
|
||||
local new_vel = vector.add(vel, speed)
|
||||
local yaw = get_yaw(cart_dir)
|
||||
local pitch = get_pitch(cart_dir)
|
||||
|
||||
self.object:set_rotation({x = pitch, y = yaw, z = 0})
|
||||
self.object:set_velocity(new_vel)
|
||||
|
||||
self.old_pos = vector.round(pos)
|
||||
self.stopped = false
|
||||
end
|
||||
|
||||
local api = {}
|
||||
|
||||
function api:init(is_node_cart)
|
||||
local lib
|
||||
|
||||
if is_node_cart then
|
||||
lib = dofile(MP.."/cart_lib2n.lua")
|
||||
else
|
||||
lib = dofile(MP.."/cart_lib2e.lua")
|
||||
end
|
||||
|
||||
-- add lib to local api
|
||||
for k,v in pairs(lib) do
|
||||
api[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- Player get on / off
|
||||
function api:on_rightclick(clicker)
|
||||
if not clicker or not clicker:is_player() then
|
||||
return
|
||||
end
|
||||
local player_name = clicker:get_player_name()
|
||||
if self.driver and player_name == self.driver then
|
||||
self.driver = nil
|
||||
carts:manage_attachment(clicker, nil)
|
||||
elseif not self.driver then
|
||||
self.driver = player_name
|
||||
carts:manage_attachment(clicker, self.object)
|
||||
|
||||
-- player_api does not update the animation
|
||||
-- when the player is attached, reset to default animation
|
||||
player_api.set_animation(clicker, "stand")
|
||||
end
|
||||
end
|
||||
|
||||
function api:on_activate(staticdata, dtime_s)
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
end
|
||||
|
||||
function api:on_detach_child(child)
|
||||
if child and child:get_player_name() == self.driver then
|
||||
self.driver = nil
|
||||
end
|
||||
end
|
||||
|
||||
function api:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
|
||||
local pos = self.object:get_pos()
|
||||
local vel = self.object:get_velocity()
|
||||
local stopped = vector.equals(vel, {x=0, y=0, z=0})
|
||||
local is_minecart = self.node_name == nil
|
||||
local node_name = self.node_name or "minecart:cart"
|
||||
local puncher_name = puncher and puncher:is_player() and puncher:get_player_name()
|
||||
local puncher_is_owner = puncher_name and (not self.owner or self.owner == "" or
|
||||
puncher_name == self.owner or
|
||||
minetest.check_player_privs(puncher_name, "minecart"))
|
||||
local puncher_is_driver = self.driver and self.driver == puncher_name
|
||||
local sneak_punch = puncher_name and puncher:get_player_control().sneak
|
||||
local no_cargo = next(self.cargo or {}) == nil
|
||||
|
||||
-- driver wants to leave/remove the empty Minecart by sneak-punch
|
||||
if is_minecart and sneak_punch and puncher_is_driver and no_cargo then
|
||||
if puncher_is_owner then
|
||||
api.remove_cart(self, pos, puncher)
|
||||
end
|
||||
carts:manage_attachment(puncher, nil)
|
||||
return
|
||||
end
|
||||
|
||||
-- Punched by non-authorized player
|
||||
if puncher_name and not puncher_is_owner then
|
||||
minetest.chat_send_player(puncher_name, S("[minecart] Cart is protected by ")..(self.owner or ""))
|
||||
return
|
||||
end
|
||||
|
||||
-- Punched by non-player
|
||||
if not puncher_name then
|
||||
local cart_dir = carts:get_rail_direction(pos, direction, nil, nil, RAILTYPE)
|
||||
if vector.equals(cart_dir, {x=0, y=0, z=0}) then
|
||||
return
|
||||
end
|
||||
api.load_cargo(self, pos)
|
||||
push_cart(self, pos, cart_dir)
|
||||
minecart.start_cart(pos, self.myID)
|
||||
return
|
||||
end
|
||||
|
||||
-- Sneak-punched by owner
|
||||
if sneak_punch then
|
||||
-- Unload the cargo
|
||||
if api.add_cargo_to_player_inv(self, pos, puncher) then
|
||||
return
|
||||
end
|
||||
-- detach driver
|
||||
if self.driver then
|
||||
carts:manage_attachment(puncher, nil)
|
||||
end
|
||||
-- Pick up cart
|
||||
api.remove_cart(self, pos, puncher)
|
||||
return
|
||||
end
|
||||
|
||||
-- Cart with driver punched to start recording
|
||||
if puncher_is_driver then
|
||||
minecart.start_recording(self, pos, vel, puncher)
|
||||
else
|
||||
minecart.start_cart(pos, self.myID)
|
||||
end
|
||||
|
||||
api.load_cargo(self, pos)
|
||||
|
||||
push_cart(self, pos, nil, puncher)
|
||||
end
|
||||
|
||||
-- sound refresh interval = 1.0sec
|
||||
local function rail_sound(self, dtime)
|
||||
if not self.sound_ttl then
|
||||
self.sound_ttl = 1.0
|
||||
return
|
||||
elseif self.sound_ttl > 0 then
|
||||
self.sound_ttl = self.sound_ttl - dtime
|
||||
return
|
||||
end
|
||||
self.sound_ttl = 1.0
|
||||
if self.sound_handle then
|
||||
local handle = self.sound_handle
|
||||
self.sound_handle = nil
|
||||
minetest.after(0.2, minetest.sound_stop, handle)
|
||||
end
|
||||
if not self.stopped then
|
||||
local vel = self.object:get_velocity() or {x=0, y=0, z=0}
|
||||
local speed = vector.length(vel)
|
||||
self.sound_handle = minetest.sound_play(
|
||||
"carts_cart_moving", {
|
||||
object = self.object,
|
||||
gain = (speed / carts.speed_max) / 2,
|
||||
loop = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function rail_on_step(self)
|
||||
-- Check if same position as before
|
||||
local pos = self.object:get_pos()
|
||||
local rot = self.object:get_rotation()
|
||||
local on_slope = rot.x ~= 0
|
||||
--print("rail_on_step_new", P2S(pos), rot.x)
|
||||
|
||||
-- cart position correction on slopes
|
||||
if on_slope then
|
||||
pos.y = pos.y - Y_OFFS_ON_SLOPES
|
||||
end
|
||||
|
||||
-- Used as fallback position
|
||||
self.old_pos = self.old_pos or pos
|
||||
local pos_rounded = vector.round(pos)
|
||||
-- Same pos as before
|
||||
if vector.equals(pos_rounded, self.old_pos) then
|
||||
return -- nothing todo
|
||||
end
|
||||
|
||||
-- Check if stopped
|
||||
local vel = self.object:get_velocity()
|
||||
local stopped = not on_slope and minecart.stopped(vel)
|
||||
local is_minecart = self.node_name == nil
|
||||
local recording = is_minecart and self.driver == self.owner
|
||||
|
||||
if stopped then
|
||||
if not self.stopped then
|
||||
local param2 = minetest.dir_to_facedir(self.old_dir)
|
||||
api.stop_cart(pos, self, self.node_name or "minecart:cart", param2)
|
||||
if recording then
|
||||
minecart.stop_recording(self, pos_rounded, vel, self.driver)
|
||||
end
|
||||
api.unload_cargo(self, pos)
|
||||
self.stopped = true
|
||||
end
|
||||
self.old_pos = pos_rounded
|
||||
return -- nothing todo
|
||||
end
|
||||
|
||||
-- Check if invalid position (not on rail anymore)
|
||||
local rail_pos, node = get_rail_node(pos)
|
||||
if not node then
|
||||
rail_pos, node = find_rail_node(self.old_pos)
|
||||
if rail_pos then
|
||||
pos_rounded = rail_pos
|
||||
if on_slope then
|
||||
self.object:set_pos({x=rail_pos.x, y=rail_pos.y + Y_OFFS_ON_SLOPES, z=rail_pos.z})
|
||||
else
|
||||
self.object:set_pos(rail_pos)
|
||||
end
|
||||
else
|
||||
self.object:set_pos(pos)
|
||||
minetest.log("error", "[minecart] No valid position "..(P2S(pos) or "nil"))
|
||||
return -- no valid position
|
||||
end
|
||||
end
|
||||
|
||||
-- Calc speed (value)
|
||||
local speed = math.sqrt((vel.x+vel.z)^2 + vel.y^2)
|
||||
-- Check if slope position
|
||||
if pos_rounded.y > self.old_pos.y then
|
||||
speed = speed - SLOPE_ACCELERATION
|
||||
elseif pos_rounded.y < self.old_pos.y then
|
||||
speed = speed + SLOPE_ACCELERATION
|
||||
else
|
||||
speed = speed - SLOWDOWN
|
||||
end
|
||||
-- Add power/brake rail acceleration
|
||||
local acc = (carts.railparams[node.name] or {}).acceleration or 0
|
||||
speed = speed + acc
|
||||
|
||||
-- Determine new direction
|
||||
local dir = carts:velocity_to_dir(vel)
|
||||
if speed < 0 then
|
||||
if on_slope then
|
||||
dir = vector.multiply(dir, -1)
|
||||
-- start with a value > 0
|
||||
speed = 0.5
|
||||
else
|
||||
speed = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- Get player controls
|
||||
local ctrl, player
|
||||
if recording then
|
||||
player = minetest.get_player_by_name(self.driver)
|
||||
if player then
|
||||
ctrl = player:get_player_control()
|
||||
end
|
||||
end
|
||||
|
||||
-- new_dir: New moving direction of the cart
|
||||
-- keys: Currently pressed L/R key, used to ignore the key on the next rail node
|
||||
local new_dir, keys = carts:get_rail_direction(rail_pos, dir, ctrl, self.old_keys, RAILTYPE)
|
||||
|
||||
-- handle junctions
|
||||
if recording and keys then
|
||||
minecart.set_junction(self, rail_pos, new_dir, keys)
|
||||
else -- normal run
|
||||
new_dir, keys = minecart.get_junction(self, rail_pos, new_dir)
|
||||
end
|
||||
self.old_keys = keys
|
||||
|
||||
-- Detect U-turn
|
||||
if (dir.x ~= 0 and dir.x == -new_dir.x) or (dir.z ~= 0 and dir.z == -new_dir.z) then
|
||||
-- Stop the cart
|
||||
self.object:set_velocity({x=0, y=0, z=0})
|
||||
self.object:move_to(pos_rounded)
|
||||
return
|
||||
-- New direction
|
||||
elseif not vector.equals(dir, new_dir) then
|
||||
if new_dir.y ~= 0 then
|
||||
self.object:set_pos({x=pos_rounded.x, y=pos_rounded.y + Y_OFFS_ON_SLOPES, z=pos_rounded.z})
|
||||
else
|
||||
self.object:set_pos(pos_rounded)
|
||||
end
|
||||
end
|
||||
|
||||
-- Set velocity and rotation
|
||||
local new_vel = vector.multiply(new_dir, math.min(speed, MAX_SPEED))
|
||||
local yaw = get_yaw(new_dir)
|
||||
local pitch = get_pitch(new_dir)
|
||||
|
||||
self.object:set_rotation({x = pitch, y = yaw, z = 0})
|
||||
self.object:set_velocity(new_vel)
|
||||
|
||||
|
||||
if recording then
|
||||
minecart.store_next_waypoint(self, rail_pos, vel)
|
||||
end
|
||||
|
||||
self.old_pos = pos_rounded
|
||||
end
|
||||
|
||||
function api:on_step(dtime)
|
||||
self.delay = (self.delay or 0) + dtime
|
||||
if self.delay > 0.09 then
|
||||
rail_on_step(self)
|
||||
rail_sound(self, self.delay)
|
||||
self.delay = 0
|
||||
end
|
||||
end
|
||||
|
||||
return api
|
@ -1,150 +0,0 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
Cart library functions for entity based carts (level 2)
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = minecart.S
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local MP = minetest.get_modpath("minecart")
|
||||
|
||||
local api = dofile(MP.."/cart_lib3.lua")
|
||||
|
||||
-- Add node, set metadata, and load carge
|
||||
local function add_cart(pos, node_name, param2, owner, userID, cargo)
|
||||
local obj = minetest.add_entity(pos, node_name)
|
||||
local myID = api.get_object_id(obj)
|
||||
if myID then
|
||||
-- Copy item data to cart entity
|
||||
local entity = obj:get_luaentity()
|
||||
entity.owner = owner
|
||||
entity.userID = userID
|
||||
entity.cargo = cargo
|
||||
entity.myID = myID
|
||||
obj:set_nametag_attributes({color = "#FFFF00", text = owner..": "..userID})
|
||||
minecart.add_to_monitoring(obj, myID, owner, userID)
|
||||
return myID
|
||||
else
|
||||
print("Entity has no ID")
|
||||
end
|
||||
end
|
||||
|
||||
function api.stop_cart(pos, entity, node_name, param2)
|
||||
-- Stop sound
|
||||
if entity.sound_handle then
|
||||
minetest.sound_stop(entity.sound_handle)
|
||||
entity.sound_handle = nil
|
||||
end
|
||||
minecart.stop_cart(pos, entity.myID)
|
||||
end
|
||||
|
||||
|
||||
-- Player adds the node
|
||||
function api.add_cart(itemstack, placer, pointed_thing, node_name)
|
||||
local owner = placer:get_player_name()
|
||||
local meta = placer:get_meta()
|
||||
local param2 = minetest.dir_to_facedir(placer:get_look_dir())
|
||||
local userID = 0
|
||||
local cargo = {}
|
||||
|
||||
-- Add node
|
||||
if carts:is_rail(pointed_thing.under) then
|
||||
add_cart(pointed_thing.under, node_name, param2, owner, userID, cargo)
|
||||
meta:set_string("cart_pos", P2S(pointed_thing.under))
|
||||
elseif carts:is_rail(pointed_thing.above) then
|
||||
add_cart(pointed_thing.above, node_name, param2, owner, userID, cargo)
|
||||
meta:set_string("cart_pos", P2S(pointed_thing.above))
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
minetest.sound_play({name = "default_place_node_metal", gain = 0.5},
|
||||
{pos = pointed_thing.above})
|
||||
|
||||
if not (creative and creative.is_enabled_for
|
||||
and creative.is_enabled_for(placer:get_player_name())) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
minetest.show_formspec(owner, "minecart:userID_entity",
|
||||
"size[4,3]" ..
|
||||
"label[0,0;Enter cart number:]" ..
|
||||
"field[1,1;3,1;userID;;]" ..
|
||||
"button_exit[1,2;2,1;exit;Save]")
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Player removes the node
|
||||
function api.remove_cart(self, pos, player)
|
||||
-- Add cart to player inventory
|
||||
local inv = player:get_inventory()
|
||||
if not (creative and creative.is_enabled_for
|
||||
and creative.is_enabled_for(player:get_player_name()))
|
||||
or not inv:contains_item("main", "minecart:cart") then
|
||||
local leftover = inv:add_item("main", "minecart:cart")
|
||||
-- If no room in inventory add a replacement cart to the world
|
||||
if not leftover:is_empty() then
|
||||
minetest.add_item(pos, leftover)
|
||||
end
|
||||
end
|
||||
minecart.remove_from_monitoring(self.myID)
|
||||
self.object:remove()
|
||||
-- Stop sound
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function api.load_cargo(self, pos)
|
||||
self.cargo = self.cargo or {}
|
||||
for _, obj_ in pairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||
local entity = obj_:get_luaentity()
|
||||
if not obj_:is_player() and entity and entity.name == "__builtin:item" then
|
||||
obj_:remove()
|
||||
self.cargo[#self.cargo + 1] = entity.itemstring
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function api.unload_cargo(self, pos)
|
||||
-- Spawn loaded items again
|
||||
for _,item in ipairs(self.cargo or {}) do
|
||||
minetest.add_item(pos, ItemStack(item))
|
||||
end
|
||||
self.cargo = {}
|
||||
end
|
||||
|
||||
-- in the case the owner punches the cart
|
||||
function api.add_cargo_to_player_inv(self, pos, puncher)
|
||||
local added = false
|
||||
local inv = puncher:get_inventory()
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||
local entity = obj:get_luaentity()
|
||||
if not obj:is_player() and entity and entity.name == "__builtin:item" then
|
||||
obj:remove()
|
||||
local item = ItemStack(entity.itemstring)
|
||||
local leftover = inv:add_item("main", item)
|
||||
if leftover:get_count() > 0 then
|
||||
minetest.add_item(pos, leftover)
|
||||
end
|
||||
added = true -- don't dig the cart
|
||||
end
|
||||
end
|
||||
return added
|
||||
end
|
||||
|
||||
return api
|
@ -1,198 +0,0 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
Cart library functions for node based carts (level 2)
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = minecart.S
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local MP = minetest.get_modpath("minecart")
|
||||
|
||||
local api = dofile(MP.."/cart_lib3.lua")
|
||||
|
||||
-- Add node, set metadata, and load carge
|
||||
local function add_cart(pos, node_name, param2, owner, userID, cargo)
|
||||
local ndef = minetest.registered_nodes[node_name]
|
||||
local node = minetest.get_node(pos)
|
||||
local meta = M(pos)
|
||||
local rail = node.name
|
||||
minetest.add_node(pos, {name = node_name, param2 = param2})
|
||||
meta:set_string("removed_rail", rail)
|
||||
meta:set_string("owner", owner)
|
||||
meta:set_string("userID", userID)
|
||||
meta:set_string("infotext", minetest.get_color_escape_sequence("#FFFF00")..owner..": "..userID)
|
||||
if ndef.after_place_node then
|
||||
ndef.after_place_node(pos)
|
||||
end
|
||||
if cargo and ndef.set_cargo then
|
||||
ndef.set_cargo(pos, cargo)
|
||||
end
|
||||
end
|
||||
|
||||
-- called after punch cart
|
||||
local function start_cart(pos, node_name, entity_name, puncher, dir)
|
||||
-- Read node metadata
|
||||
local ndef = minetest.registered_nodes[node_name]
|
||||
if ndef then
|
||||
local meta = M(pos)
|
||||
local rail = meta:get_string("removed_rail")
|
||||
local userID = meta:get_int("userID")
|
||||
local cart_owner = meta:get_string("owner")
|
||||
local cargo = ndef.get_cargo and ndef.get_cargo(pos) or {}
|
||||
-- swap node to rail
|
||||
minetest.remove_node(pos)
|
||||
minetest.add_node(pos, {name = rail})
|
||||
-- Add entity
|
||||
local obj = minetest.add_entity(pos, entity_name)
|
||||
-- Determine ID
|
||||
local myID = api.get_object_id(obj)
|
||||
if myID then
|
||||
-- Copy metadata to cart entity
|
||||
local entity = obj:get_luaentity()
|
||||
entity.owner = cart_owner
|
||||
entity.userID = userID
|
||||
entity.cargo = cargo
|
||||
entity.myID = myID
|
||||
obj:set_nametag_attributes({color = "#ffff00", text = cart_owner..": "..userID})
|
||||
minecart.add_to_monitoring(obj, myID, cart_owner, userID)
|
||||
minecart.node_at_station(cart_owner, userID, nil)
|
||||
-- punch cart to prevent the stopped handling
|
||||
obj:punch(puncher or obj, 1, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = 1},
|
||||
}, dir)
|
||||
return myID
|
||||
else
|
||||
print("Entity has no ID")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function api.stop_cart(pos, entity, node_name, param2)
|
||||
-- rail buffer reached?
|
||||
if api.get_route_key(pos) then
|
||||
-- Read entity data
|
||||
local owner = entity.owner or ""
|
||||
local userID = entity.userID or 0
|
||||
local cargo = entity.cargo or {}
|
||||
-- Remove entity
|
||||
minecart.remove_from_monitoring(entity.myID)
|
||||
minecart.node_at_station(owner, userID, pos)
|
||||
entity.object:remove()
|
||||
-- Add cart node
|
||||
add_cart(pos, node_name, param2, owner, userID, cargo)
|
||||
end
|
||||
-- Stop sound
|
||||
if entity.sound_handle then
|
||||
minetest.sound_stop(entity.sound_handle)
|
||||
entity.sound_handle = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Player adds the node
|
||||
function api.add_cart(itemstack, placer, pointed_thing, node_name)
|
||||
local owner = placer:get_player_name()
|
||||
local meta = placer:get_meta()
|
||||
local param2 = minetest.dir_to_facedir(placer:get_look_dir())
|
||||
local userID = 0
|
||||
local cargo = {}
|
||||
|
||||
-- Add node
|
||||
if carts:is_rail(pointed_thing.under) then
|
||||
add_cart(pointed_thing.under, node_name, param2, owner, userID, cargo)
|
||||
meta:set_string("cart_pos", P2S(pointed_thing.under))
|
||||
elseif carts:is_rail(pointed_thing.above) then
|
||||
add_cart(pointed_thing.above, node_name, param2, owner, userID, cargo)
|
||||
meta:set_string("cart_pos", P2S(pointed_thing.above))
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
minetest.sound_play({name = "default_place_node_metal", gain = 0.5},
|
||||
{pos = pointed_thing.above})
|
||||
|
||||
if not (creative and creative.is_enabled_for
|
||||
and creative.is_enabled_for(placer:get_player_name())) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
minetest.show_formspec(owner, "minecart:userID_node",
|
||||
"size[4,3]" ..
|
||||
"label[0,0;Enter cart number:]" ..
|
||||
"field[1,1;3,1;userID;;]" ..
|
||||
"button_exit[1,2;2,1;exit;Save]")
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function api.node_on_punch(pos, node, puncher, pointed_thing, entity_name, dir)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
-- Player digs cart by sneak-punch
|
||||
if puncher and puncher:get_player_control().sneak then
|
||||
api.remove_cart(nil, pos, puncher)
|
||||
return
|
||||
end
|
||||
start_cart(pos, node.name, entity_name, puncher, dir)
|
||||
end
|
||||
|
||||
local function add_to_player_inventory(pos, player, node_name)
|
||||
local inv = player:get_inventory()
|
||||
if not (creative and creative.is_enabled_for
|
||||
and creative.is_enabled_for(player:get_player_name()))
|
||||
or not inv:contains_item("main", node_name) then
|
||||
local leftover = inv:add_item("main", node_name)
|
||||
-- If no room in inventory add a replacement cart to the world
|
||||
if not leftover:is_empty() then
|
||||
minetest.add_item(pos, leftover)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Player removes the node
|
||||
function api.remove_cart(self, pos, player)
|
||||
if self then -- cart is still an entity
|
||||
add_to_player_inventory(pos, player, self.node_name or "minecart:cart")
|
||||
minecart.remove_from_monitoring(self.myID)
|
||||
self.object:remove()
|
||||
else
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef.can_dig and ndef.can_dig(pos, player) then
|
||||
add_to_player_inventory(pos, player, node.name)
|
||||
node.name = M(pos):get_string("removed_rail")
|
||||
if node.name == "" then
|
||||
node.name = "carts:rail"
|
||||
end
|
||||
minetest.remove_node(pos)
|
||||
minetest.add_node(pos, node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function api.load_cargo()
|
||||
-- nothing to load
|
||||
end
|
||||
|
||||
function api.unload_cargo()
|
||||
-- nothing to unload
|
||||
end
|
||||
|
||||
function api.add_cargo_to_player_inv()
|
||||
-- nothing to do
|
||||
end
|
||||
|
||||
-- needed by minecart.punch_cart and node carts
|
||||
minecart.node_on_punch = api.node_on_punch
|
||||
|
||||
return api
|
@ -1,90 +0,0 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
Cart library base functions (level 3)
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = minecart.S
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
|
||||
local api = {}
|
||||
|
||||
function api.get_object_id(object)
|
||||
for id, entity in pairs(minetest.luaentities) do
|
||||
if entity.object == object then
|
||||
return id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function api.get_route_key(pos, player_name)
|
||||
local pos1 = minetest.find_node_near(pos, 1, {"minecart:buffer"})
|
||||
if pos1 then
|
||||
local meta = minetest.get_meta(pos1)
|
||||
if player_name == nil or player_name == meta:get_string("owner") then
|
||||
return P2S(pos1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function api.get_station_name(pos)
|
||||
local pos1 = minetest.find_node_near(pos, 1, {"minecart:buffer"})
|
||||
if pos1 then
|
||||
local name = M(pos1):get_string("name")
|
||||
if name ~= "" then
|
||||
return name
|
||||
end
|
||||
return "-"
|
||||
end
|
||||
end
|
||||
|
||||
function api.load_cart(pos, vel, pitch, yaw, item)
|
||||
-- Add cart to map
|
||||
local obj = minetest.add_entity(pos, item.entity_name or "minecart:cart", nil)
|
||||
-- Determine ID
|
||||
local myID = api.get_object_id(obj)
|
||||
if myID then
|
||||
-- Copy item data to cart entity
|
||||
local entity = obj:get_luaentity()
|
||||
entity.owner = item.owner or ""
|
||||
entity.userID = item.userID or 0
|
||||
entity.cargo = item.cargo or {}
|
||||
entity.myID = myID
|
||||
obj:set_nametag_attributes({color = "#FFFF00", text = entity.owner..": "..entity.userID})
|
||||
-- Update item data
|
||||
item.owner = entity.owner
|
||||
item.cargo = nil
|
||||
-- Start cart
|
||||
obj:set_velocity(vel)
|
||||
obj:set_rotation({x = pitch or 0, y = yaw or 0, z = 0})
|
||||
return myID
|
||||
else
|
||||
print("Entity has no ID")
|
||||
end
|
||||
end
|
||||
|
||||
function api.unload_cart(pos, vel, entity, item)
|
||||
-- Copy entity data to item
|
||||
item.cargo = entity.cargo
|
||||
item.entity_name = entity.object:get_entity_name()
|
||||
-- Remove entity from map
|
||||
entity.object:remove()
|
||||
-- Stop sound
|
||||
if entity.sound_handle then
|
||||
minetest.sound_stop(entity.sound_handle)
|
||||
entity.sound_handle = nil
|
||||
end
|
||||
end
|
||||
|
||||
return api
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
@ -29,15 +29,15 @@ local summary_doc = table.concat({
|
||||
S("4. Place a Minecart at a buffer and give it a cart number (1..999)"),
|
||||
S("5. Drive from buffer to buffer in both directions using the Minecart(!) to record the routes (use 'right-left' keys to control the Minecart)."),
|
||||
S("6. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge')."),
|
||||
S("7. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time."),
|
||||
S("7. Optional: Configure the Minecart waiting time in both buffers. The Minecart will then start automatically after the configured time."),
|
||||
S("8. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters)."),
|
||||
S("9. Place a Minecart in front of the buffer and check whether it starts after the configured time."),
|
||||
S("10. Check the cart state via the chat command: /mycart <num>\n '<num>' is the cart number"),
|
||||
S("11. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back."),
|
||||
S("12. Dig the empty cart with a second 'sneak+click' (as usual)."),
|
||||
S("11. Drop items into the Minecart and punch the cart to start it."),
|
||||
S("12. Dig the cart with 'sneak+click' (as usual). The items will be drop down."),
|
||||
}, "\n")
|
||||
|
||||
local cart_doc = S("Primary used to transport items. You can drop items into the Minecart and punch the cart to get started. Sneak+click the cart to get the items back")
|
||||
local cart_doc = S("Primary used to transport items. You can drop items into the Minecart and punch the cart to get started. Sneak+click the cart to get cart and items back")
|
||||
|
||||
local buffer_doc = S("Used as buffer on both rail ends. Needed to be able to record the cart routes")
|
||||
|
||||
@ -45,6 +45,29 @@ local landmark_doc = S("Protect your rails with the Landmarks (one Landmark at l
|
||||
|
||||
local hopper_doc = S("Used to load/unload Minecart. The Hopper can push/pull items to/from chests and drop/pickup items to/from Minecarts. To unload a Minecart place the hopper below the rail. To load the Minecart, place the hopper right next to the Minecart.")
|
||||
|
||||
local pusher_doc = S([[If several carts are running on one route,
|
||||
it can happen that a buffer position is already occupied and one cart therefore stops earlier.
|
||||
In this case, the cart pusher is used to push the cart towards the buffer again.
|
||||
This block must be placed under the rail at a distance of 2 m in front of the buffer.]])
|
||||
|
||||
local speed_doc = S([[Limit the cart speed with speed limit signs.
|
||||
|
||||
As before, the speed of the carts is also influenced by power rails.
|
||||
Brake rails are irrelevant, the cart does not brake here.
|
||||
The maximum speed is 8 m/s. This assumes a ratio of power rails
|
||||
to normal rails of 1 to 4 on a flat section of rail. A rail section is a
|
||||
series of rail nodes without a change of direction. After every curve / kink,
|
||||
the speed for the next section of the route is newly determined,
|
||||
taking into account the swing of the cart. This means that a cart can
|
||||
roll over short rail sections without power rails.
|
||||
|
||||
In order to additionally brake the cart at certain points
|
||||
(at switches or in front of a buffer), speed limit signs can be placed
|
||||
on the track. With these signs the speed can be reduced to 4, 2, or 1 m / s.
|
||||
The "No speed limit" sign can be used to remove the speed limit.
|
||||
|
||||
The speed limit signs must be placed next to the track so that they can
|
||||
be read from the cart. This allows different speeds in each direction of travel.]])
|
||||
|
||||
local function formspec(data)
|
||||
if data.image then
|
||||
@ -90,6 +113,16 @@ doc.add_entry("minecart", "landmark", {
|
||||
data = {text = landmark_doc, item="minecart:landmark"},
|
||||
})
|
||||
|
||||
doc.add_entry("minecart", "speed signs", {
|
||||
name = S("Minecart Speed Signs"),
|
||||
data = {text = speed_doc, item="minecart:speed4"},
|
||||
})
|
||||
|
||||
doc.add_entry("minecart", "cart pusher", {
|
||||
name = S("Cart Pusher"),
|
||||
data = {text = pusher_doc, item="minecart:cart_pusher"},
|
||||
})
|
||||
|
||||
if minecart.hopper_enabled then
|
||||
doc.add_entry("minecart", "hopper", {
|
||||
name = S("Minecart Hopper"),
|
||||
|
318
minecart/entitylib.lua
Normal file
@ -0,0 +1,318 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P2H = minetest.hash_node_position
|
||||
local H2P = minetest.get_position_from_hash
|
||||
local MAX_SPEED = minecart.MAX_SPEED
|
||||
local dot2dir = minecart.dot2dir
|
||||
local get_waypoint = minecart.get_waypoint
|
||||
local recording_waypoints = minecart.recording_waypoints
|
||||
local recording_junctions = minecart.recording_junctions
|
||||
local set_junctions = minecart.set_junctions
|
||||
local player_ctrl = minecart.player_ctrl
|
||||
local tEntityNames = minecart.tEntityNames
|
||||
|
||||
local function stop_cart(self, cart_pos)
|
||||
self.is_running = false
|
||||
self.arrival_time = 0
|
||||
|
||||
if self.driver then
|
||||
local player = minetest.get_player_by_name(self.driver)
|
||||
if player then
|
||||
minecart.stop_recording(self, cart_pos)
|
||||
minecart.manage_attachment(player, self, false)
|
||||
end
|
||||
end
|
||||
if not minecart.get_buffer_pos(cart_pos, self.owner) then
|
||||
-- Probably somewhere in the pampas
|
||||
minecart.delete_cart_waypoint(cart_pos)
|
||||
end
|
||||
minecart.entity_to_node(cart_pos, self)
|
||||
end
|
||||
|
||||
local function get_ctrl(self, pos)
|
||||
-- Use player ctrl or junction data from recorded routes
|
||||
return (self.driver and self.ctrl) or (self.junctions and self.junctions[P2H(pos)]) or {}
|
||||
end
|
||||
|
||||
local function new_speed(self, new_dir)
|
||||
self.cart_speed = self.cart_speed or 0
|
||||
local rail_speed = (self.waypoint.speed or 0) / 10
|
||||
|
||||
if rail_speed <= 0 then
|
||||
rail_speed = math.max(self.cart_speed + rail_speed, 0)
|
||||
elseif rail_speed <= self.cart_speed then
|
||||
rail_speed = math.max((self.cart_speed + rail_speed) / 2, 0)
|
||||
end
|
||||
|
||||
-- Speed corrections
|
||||
if new_dir.y == 1 then
|
||||
if rail_speed < 1 then rail_speed = 0 end
|
||||
else
|
||||
if rail_speed < 0.4 then rail_speed = 0 end
|
||||
end
|
||||
|
||||
self.cart_speed = rail_speed -- store for next cycle
|
||||
return rail_speed
|
||||
end
|
||||
|
||||
local function running(self)
|
||||
local rot = self.object:get_rotation()
|
||||
local dir = minetest.yaw_to_dir(rot.y)
|
||||
dir.y = math.floor((rot.x / (math.pi/4)) + 0.5)
|
||||
dir = vector.round(dir)
|
||||
local facedir = minetest.dir_to_facedir(dir)
|
||||
local cart_pos, wayp_pos, is_junction
|
||||
|
||||
if self.reenter then -- through monitoring
|
||||
cart_pos = H2P(self.reenter[1])
|
||||
wayp_pos = cart_pos
|
||||
is_junction = false
|
||||
self.waypoint = {pos = H2P(self.reenter[2]), power = 0, dot = self.reenter[4]}
|
||||
self.cart_speed = self.reenter[3]
|
||||
self.speed_limit = MAX_SPEED
|
||||
self.reenter = nil
|
||||
elseif not self.waypoint then
|
||||
-- get waypoint
|
||||
cart_pos = vector.round(self.object:get_pos())
|
||||
wayp_pos = cart_pos
|
||||
is_junction = false
|
||||
self.waypoint = get_waypoint(cart_pos, facedir, get_ctrl(self, cart_pos), true)
|
||||
if self.no_normal_start then
|
||||
-- Probably somewhere in the pampas
|
||||
minecart.delete_waypoint(cart_pos)
|
||||
self.no_normal_start = nil
|
||||
end
|
||||
self.cart_speed = 2 -- push speed
|
||||
self.speed_limit = MAX_SPEED
|
||||
else
|
||||
-- next waypoint
|
||||
cart_pos = vector.new(self.waypoint.cart_pos or self.waypoint.pos)
|
||||
wayp_pos = self.waypoint.pos
|
||||
local vel = self.object:get_velocity()
|
||||
self.waypoint, is_junction = get_waypoint(wayp_pos, facedir, get_ctrl(self, wayp_pos), self.cart_speed < 0.1)
|
||||
end
|
||||
|
||||
if not self.waypoint then
|
||||
stop_cart(self, wayp_pos)
|
||||
return
|
||||
end
|
||||
|
||||
if is_junction then
|
||||
if self.is_recording then
|
||||
set_junctions(self, wayp_pos)
|
||||
end
|
||||
self.ctrl = nil
|
||||
end
|
||||
|
||||
--print("dist", P2S(cart_pos), P2S(self.waypoint.pos), P2S(self.waypoint.cart_pos), self.waypoint.dot)
|
||||
local dist = vector.distance(cart_pos, self.waypoint.cart_pos or self.waypoint.pos)
|
||||
local new_dir = dot2dir(self.waypoint.dot)
|
||||
local new_speed = new_speed(self, new_dir)
|
||||
local straight_ahead = vector.equals(new_dir, dir)
|
||||
-- If straight_ahead, then it's probably a speed limit sign
|
||||
if straight_ahead then
|
||||
self.speed_limit = minecart.get_speedlimit(wayp_pos, facedir) or self.speed_limit
|
||||
end
|
||||
new_speed = math.min(new_speed, self.speed_limit)
|
||||
|
||||
local new_cart_pos, extra_cycle = minecart.get_current_cart_pos_correction(
|
||||
wayp_pos, facedir, dir.y, self.waypoint.dot) -- TODO: Why has self.waypoint no dot?
|
||||
if extra_cycle and not vector.equals(cart_pos, new_cart_pos) then
|
||||
self.waypoint = {pos = wayp_pos, cart_pos = new_cart_pos}
|
||||
new_dir = vector.direction(cart_pos, new_cart_pos)
|
||||
dist = vector.distance(cart_pos, new_cart_pos)
|
||||
--print("extra_cycle", P2S(cart_pos), P2S(wayp_pos), P2S(new_cart_pos), new_speed)
|
||||
end
|
||||
|
||||
-- Slope corrections
|
||||
--print("Slope corrections", P2S(new_dir), P2S(cart_pos))
|
||||
if new_dir.y ~= 0 then
|
||||
cart_pos.y = cart_pos.y + 0.2
|
||||
end
|
||||
|
||||
-- Calc velocity, rotation and arrival_time
|
||||
local yaw = minetest.dir_to_yaw(new_dir)
|
||||
local pitch = new_dir.y * math.pi/4
|
||||
--print("new_speed", new_speed / (new_dir.y ~= 0 and 1.41 or 1))
|
||||
local vel = vector.multiply(new_dir, new_speed / ((new_dir.y ~= 0) and 1.41 or 1))
|
||||
self.arrival_time = self.timebase + (dist / new_speed)
|
||||
-- needed for recording
|
||||
self.curr_speed = new_speed
|
||||
self.num_sections = (self.num_sections or 0) + 1
|
||||
|
||||
-- Got stuck somewhere
|
||||
if new_speed < 0.1 or dist < 0 then
|
||||
print("Got stuck somewhere", new_speed, dist)
|
||||
stop_cart(self, wayp_pos)
|
||||
return
|
||||
end
|
||||
|
||||
self.object:set_pos(cart_pos)
|
||||
self.object:set_rotation({x = pitch, y = yaw, z = 0})
|
||||
self.object:set_velocity(vel)
|
||||
return
|
||||
end
|
||||
|
||||
local function play_sound(self)
|
||||
if self.sound_handle then
|
||||
local handle = self.sound_handle
|
||||
self.sound_handle = nil
|
||||
minetest.after(0.2, minetest.sound_stop, handle)
|
||||
end
|
||||
if self.object then
|
||||
self.sound_handle = minetest.sound_play(
|
||||
"carts_cart_moving", {
|
||||
object = self.object,
|
||||
gain = self.curr_speed / MAX_SPEED,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local function on_step(self, dtime)
|
||||
self.timebase = (self.timebase or 0) + dtime
|
||||
|
||||
if self.is_running then
|
||||
if self.arrival_time <= self.timebase then
|
||||
running(self)
|
||||
end
|
||||
|
||||
if (self.sound_ttl or 0) <= self.timebase then
|
||||
play_sound(self)
|
||||
self.sound_ttl = self.timebase + 1.0
|
||||
end
|
||||
else
|
||||
if self.sound_handle then
|
||||
minetest.sound_stop(self.sound_handle)
|
||||
self.sound_handle = nil
|
||||
end
|
||||
end
|
||||
|
||||
if self.driver then
|
||||
if self.is_recording then
|
||||
if self.rec_time <= self.timebase then
|
||||
recording_waypoints(self)
|
||||
self.rec_time = self.rec_time + 2.0
|
||||
end
|
||||
recording_junctions(self)
|
||||
else
|
||||
player_ctrl(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_entitycard_activate(self, staticdata, dtime_s)
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
end
|
||||
|
||||
-- Start the entity cart (or dig by shift+leftclick)
|
||||
local function on_entitycard_punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
|
||||
if minecart.is_owner(puncher, self.owner) then
|
||||
if puncher:get_player_control().sneak then
|
||||
if not self.only_dig_if_empty or not next(self.cargo) then
|
||||
-- drop items
|
||||
local pos = vector.round(self.object:get_pos())
|
||||
for _,item in ipairs(self.cargo or {}) do
|
||||
minetest.add_item(pos, ItemStack(item))
|
||||
end
|
||||
-- Dig cart
|
||||
if self.driver then
|
||||
-- remove cart as driver
|
||||
minecart.stop_recording(self, pos)
|
||||
minecart.monitoring_remove_cart(self.owner, self.userID)
|
||||
minecart.remove_entity(self, pos, puncher)
|
||||
minecart.manage_attachment(puncher, self, false)
|
||||
else
|
||||
-- remove cart from outside
|
||||
minecart.monitoring_remove_cart(self.owner, self.userID)
|
||||
minecart.remove_entity(self, pos, puncher)
|
||||
end
|
||||
end
|
||||
elseif not self.is_running then
|
||||
-- start the cart
|
||||
local pos = vector.round(self.object:get_pos())
|
||||
if puncher then
|
||||
local yaw = puncher:get_look_horizontal()
|
||||
self.object:set_rotation({x = 0, y = yaw, z = 0})
|
||||
end
|
||||
minecart.start_entitycart(self, pos)
|
||||
minecart.start_recording(self, pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Player get on / off
|
||||
local function on_entitycard_rightclick(self, clicker)
|
||||
if clicker and clicker:is_player() and self.driver_allowed then
|
||||
-- Get on / off
|
||||
if self.driver then
|
||||
-- get off
|
||||
local pos = vector.round(self.object:get_pos())
|
||||
minecart.manage_attachment(clicker, self, false)
|
||||
minecart.entity_to_node(pos, self)
|
||||
else
|
||||
-- get on
|
||||
local pos = vector.round(self.object:get_pos())
|
||||
minecart.stop_recording(self, pos)
|
||||
minecart.manage_attachment(clicker, self, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_entitycard_detach_child(self, child)
|
||||
if child and child:get_player_name() == self.driver then
|
||||
self.driver = nil
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.get_entitycart_nearby(pos, param2, radius)
|
||||
local pos2 = param2 and vector.add(pos, minecart.param2_to_dir(param2)) or pos
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos2, radius or 0.5)) do
|
||||
local entity = object:get_luaentity()
|
||||
if entity and entity.name and tEntityNames[entity.name] then
|
||||
local vel = object:get_velocity()
|
||||
if vector.equals(vel, {x=0, y=0, z=0}) then -- still standing?
|
||||
return entity
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.push_entitycart(self, punch_dir)
|
||||
--print("push_entitycart")
|
||||
local vel = self.object:get_velocity()
|
||||
punch_dir.y = 0
|
||||
local yaw = minetest.dir_to_yaw(punch_dir)
|
||||
self.object:set_rotation({x = 0, y = yaw, z = 0})
|
||||
self.is_running = true
|
||||
self.arrival_time = 0
|
||||
end
|
||||
|
||||
function minecart.register_cart_entity(entity_name, node_name, cart_type, entity_def)
|
||||
entity_def.entity_name = entity_name
|
||||
entity_def.node_name = node_name
|
||||
entity_def.on_activate = on_entitycard_activate
|
||||
entity_def.on_punch = on_entitycard_punch
|
||||
entity_def.on_step = on_step
|
||||
entity_def.on_rightclick = on_entitycard_rightclick
|
||||
entity_def.on_detach_child = on_entitycard_detach_child
|
||||
|
||||
entity_def.owner = nil
|
||||
entity_def.driver = nil
|
||||
entity_def.cargo = {}
|
||||
|
||||
minetest.register_entity(entity_name, entity_def)
|
||||
-- register node for punching
|
||||
minecart.register_cart_names(node_name, entity_name, cart_type)
|
||||
end
|
||||
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
@ -155,6 +155,7 @@ minetest.register_node("minecart:hopper", {
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
use_texture_alpha = minecart.CLIP,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
|
141
minecart/hopperlib.lua
Normal file
@ -0,0 +1,141 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
|
||||
local RegisteredInventories = {}
|
||||
|
||||
-- Take the given number of items from the inv.
|
||||
-- Returns nil if ItemList is empty.
|
||||
function minecart.inv_take_items(inv, listname, num)
|
||||
if inv:is_empty(listname) then
|
||||
return nil
|
||||
end
|
||||
local size = inv:get_size(listname)
|
||||
for idx = 1, size do
|
||||
local items = inv:get_stack(listname, idx)
|
||||
if items:get_count() > 0 then
|
||||
local taken = items:take_item(num)
|
||||
inv:set_stack(listname, idx, items)
|
||||
return taken
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function minecart.take_items(pos, param2, num)
|
||||
local npos, node
|
||||
if param2 then
|
||||
npos, node = minecart.get_next_node(pos, (param2 + 2) % 4)
|
||||
else
|
||||
npos, node = pos, minetest.get_node(pos)
|
||||
end
|
||||
local def = RegisteredInventories[node.name]
|
||||
local owner = M(pos):get_string("owner")
|
||||
local inv = minetest.get_inventory({type="node", pos=npos})
|
||||
|
||||
if def and inv and def.take_listname and (not def.allow_take or def.allow_take(npos, nil, owner)) then
|
||||
return minecart.inv_take_items(inv, def.take_listname, num)
|
||||
elseif def and def.take_item then
|
||||
return def.take_item(npos, num, owner)
|
||||
else
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.minecart_hopper_takeitem then
|
||||
return ndef.minecart_hopper_takeitem(npos, num)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.put_items(pos, param2, stack)
|
||||
local npos, node = minecart.get_next_node(pos, param2)
|
||||
local def = RegisteredInventories[node.name]
|
||||
local owner = M(pos):get_string("owner")
|
||||
local inv = minetest.get_inventory({type="node", pos=npos})
|
||||
|
||||
if def and inv and def.put_listname and (not def.allow_put or def.allow_put(npos, stack, owner)) then
|
||||
local leftover = inv:add_item(def.put_listname, stack)
|
||||
if leftover:get_count() > 0 then
|
||||
return leftover
|
||||
end
|
||||
elseif def and def.put_item then
|
||||
return def.put_item(npos, stack, owner)
|
||||
elseif minecart.is_air_like(node.name) or minecart.is_nodecart_available(npos) then
|
||||
minetest.add_item(npos, stack)
|
||||
else
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.minecart_hopper_additem then
|
||||
local leftover = ndef.minecart_hopper_additem(npos, stack)
|
||||
if leftover:get_count() > 0 then
|
||||
return leftover
|
||||
end
|
||||
else
|
||||
return stack
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.untake_items(pos, param2, stack)
|
||||
local npos, node
|
||||
if param2 then
|
||||
npos, node = minecart.get_next_node(pos, (param2 + 2) % 4)
|
||||
else
|
||||
npos, node = pos, minetest.get_node(pos)
|
||||
end
|
||||
local def = RegisteredInventories[node.name]
|
||||
local inv = minetest.get_inventory({type="node", pos=npos})
|
||||
|
||||
if def and inv and def.put_listname then
|
||||
return inv:add_item(def.put_listname, stack)
|
||||
elseif def and def.untake_item then
|
||||
return def.untake_item(npos, stack)
|
||||
else
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.minecart_hopper_untakeitem then
|
||||
return ndef.minecart_hopper_untakeitem(npos, stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Register inventory node for hopper access
|
||||
-- (for example, see below)
|
||||
function minecart.register_inventory(node_names, def)
|
||||
for _, name in ipairs(node_names) do
|
||||
RegisteredInventories[name] = {
|
||||
allow_put = def.put and def.put.allow_inventory_put,
|
||||
put_listname = def.put and def.put.listname,
|
||||
allow_take = def.take and def.take.allow_inventory_take,
|
||||
take_listname = def.take and def.take.listname,
|
||||
put_item = def.put and def.put.put_item,
|
||||
take_item = def.take and def.take.take_item,
|
||||
untake_item = def.take and def.take.untake_item,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Allow the hopper the access to itself
|
||||
minecart.register_inventory({"minecart:hopper"}, {
|
||||
put = {
|
||||
allow_inventory_put = function(pos, stack, player_name)
|
||||
local owner = M(pos):get_string("owner")
|
||||
return owner == player_name
|
||||
end,
|
||||
listname = "main",
|
||||
},
|
||||
take = {
|
||||
allow_inventory_take = function(pos, stack, player_name)
|
||||
local owner = M(pos):get_string("owner")
|
||||
return owner == player_name
|
||||
end,
|
||||
listname = "main",
|
||||
},
|
||||
})
|
488
minecart/i18n.py
@ -1,79 +1,469 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Script to generate the template file and update the translation files.
|
||||
# Copy the script into the mod or modpack root folder and run it there.
|
||||
#
|
||||
# Copyright (C) 2019 Joachim Stolberg
|
||||
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
|
||||
# LGPLv2.1+
|
||||
#
|
||||
# Copy the script into the mod root folder and adapt the last code lines to you needs.
|
||||
# See https://github.com/minetest-tools/update_translations for
|
||||
# potential future updates to this script.
|
||||
|
||||
from __future__ import print_function
|
||||
import os, fnmatch, re, shutil
|
||||
import os, fnmatch, re, shutil, errno
|
||||
from sys import argv as _argv
|
||||
from sys import stderr as _stderr
|
||||
|
||||
pattern_lua = re.compile(r'[ \.=^\t]S\("(.+?)"\)', re.DOTALL)
|
||||
pattern_tr = re.compile(r'(.+?[^@])=(.+)')
|
||||
# Running params
|
||||
params = {"recursive": False,
|
||||
"help": False,
|
||||
"mods": False,
|
||||
"verbose": False,
|
||||
"folders": [],
|
||||
"no-old-file": False,
|
||||
"break-long-lines": False,
|
||||
"sort": False,
|
||||
"print-source": False
|
||||
}
|
||||
# Available CLI options
|
||||
options = {"recursive": ['--recursive', '-r'],
|
||||
"help": ['--help', '-h'],
|
||||
"mods": ['--installed-mods', '-m'],
|
||||
"verbose": ['--verbose', '-v'],
|
||||
"no-old-file": ['--no-old-file', '-O'],
|
||||
"break-long-lines": ['--break-long-lines', '-b'],
|
||||
"sort": ['--sort', '-s'],
|
||||
"print-source": ['--print-source', '-p']
|
||||
}
|
||||
|
||||
def gen_template(templ_file, lkeyStrings):
|
||||
lOut = []
|
||||
lkeyStrings.sort()
|
||||
for s in lkeyStrings:
|
||||
lOut.append("%s=" % s)
|
||||
open(templ_file, "wt").write("\n".join(lOut))
|
||||
# Strings longer than this will have extra space added between
|
||||
# them in the translation files to make it easier to distinguish their
|
||||
# beginnings and endings at a glance
|
||||
doublespace_threshold = 80
|
||||
|
||||
def set_params_folders(tab: list):
|
||||
'''Initialize params["folders"] from CLI arguments.'''
|
||||
# Discarding argument 0 (tool name)
|
||||
for param in tab[1:]:
|
||||
stop_param = False
|
||||
for option in options:
|
||||
if param in options[option]:
|
||||
stop_param = True
|
||||
break
|
||||
if not stop_param:
|
||||
params["folders"].append(os.path.abspath(param))
|
||||
|
||||
def set_params(tab: list):
|
||||
'''Initialize params from CLI arguments.'''
|
||||
for option in options:
|
||||
for option_name in options[option]:
|
||||
if option_name in tab:
|
||||
params[option] = True
|
||||
break
|
||||
|
||||
def print_help(name):
|
||||
'''Prints some help message.'''
|
||||
print(f'''SYNOPSIS
|
||||
{name} [OPTIONS] [PATHS...]
|
||||
DESCRIPTION
|
||||
{', '.join(options["help"])}
|
||||
prints this help message
|
||||
{', '.join(options["recursive"])}
|
||||
run on all subfolders of paths given
|
||||
{', '.join(options["mods"])}
|
||||
run on locally installed modules
|
||||
{', '.join(options["no-old-file"])}
|
||||
do not create *.old files
|
||||
{', '.join(options["sort"])}
|
||||
sort output strings alphabetically
|
||||
{', '.join(options["break-long-lines"])}
|
||||
add extra line breaks before and after long strings
|
||||
{', '.join(options["verbose"])}
|
||||
add output information
|
||||
''')
|
||||
|
||||
|
||||
def main():
|
||||
'''Main function'''
|
||||
set_params(_argv)
|
||||
set_params_folders(_argv)
|
||||
if params["help"]:
|
||||
print_help(_argv[0])
|
||||
elif params["recursive"] and params["mods"]:
|
||||
print("Option --installed-mods is incompatible with --recursive")
|
||||
else:
|
||||
# Add recursivity message
|
||||
print("Running ", end='')
|
||||
if params["recursive"]:
|
||||
print("recursively ", end='')
|
||||
# Running
|
||||
if params["mods"]:
|
||||
print(f"on all locally installed modules in {os.path.expanduser('~/.minetest/mods/')}")
|
||||
run_all_subfolders(os.path.expanduser("~/.minetest/mods"))
|
||||
elif len(params["folders"]) >= 2:
|
||||
print("on folder list:", params["folders"])
|
||||
for f in params["folders"]:
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(f)
|
||||
else:
|
||||
update_folder(f)
|
||||
elif len(params["folders"]) == 1:
|
||||
print("on folder", params["folders"][0])
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(params["folders"][0])
|
||||
else:
|
||||
update_folder(params["folders"][0])
|
||||
else:
|
||||
print("on folder", os.path.abspath("./"))
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(os.path.abspath("./"))
|
||||
else:
|
||||
update_folder(os.path.abspath("./"))
|
||||
|
||||
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
|
||||
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
|
||||
pattern_lua_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
|
||||
pattern_lua_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
|
||||
pattern_lua_bracketed_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
|
||||
pattern_lua_bracketed_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
|
||||
|
||||
# Handles "concatenation" .. " of strings"
|
||||
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
|
||||
|
||||
pattern_tr = re.compile(r'(.*?[^@])=(.*)')
|
||||
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
|
||||
pattern_tr_filename = re.compile(r'\.tr$')
|
||||
pattern_po_language_code = re.compile(r'(.*)\.po$')
|
||||
|
||||
#attempt to read the mod's name from the mod.conf file or folder name. Returns None on failure
|
||||
def get_modname(folder):
|
||||
try:
|
||||
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
|
||||
for line in mod_conf:
|
||||
match = pattern_name.match(line)
|
||||
if match:
|
||||
return match.group(1)
|
||||
except FileNotFoundError:
|
||||
if not os.path.isfile(os.path.join(folder, "modpack.txt")):
|
||||
folder_name = os.path.basename(folder)
|
||||
# Special case when run in Minetest's builtin directory
|
||||
if folder_name == "builtin":
|
||||
return "__builtin"
|
||||
else:
|
||||
return folder_name
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
#If there are already .tr files in /locale, returns a list of their names
|
||||
def get_existing_tr_files(folder):
|
||||
out = []
|
||||
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
|
||||
for name in files:
|
||||
if pattern_tr_filename.search(name):
|
||||
out.append(name)
|
||||
return out
|
||||
|
||||
# A series of search and replaces that massage a .po file's contents into
|
||||
# a .tr file's equivalent
|
||||
def process_po_file(text):
|
||||
# The first three items are for unused matches
|
||||
text = re.sub(r'#~ msgid "', "", text)
|
||||
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
|
||||
text = re.sub(r'"\n#~ msgstr "', "=", text)
|
||||
# comment lines
|
||||
text = re.sub(r'#.*\n', "", text)
|
||||
# converting msg pairs into "=" pairs
|
||||
text = re.sub(r'msgid "', "", text)
|
||||
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
|
||||
text = re.sub(r'"\nmsgstr "', "=", text)
|
||||
# various line breaks and escape codes
|
||||
text = re.sub(r'"\n"', "", text)
|
||||
text = re.sub(r'"\n', "\n", text)
|
||||
text = re.sub(r'\\"', '"', text)
|
||||
text = re.sub(r'\\n', '@n', text)
|
||||
# remove header text
|
||||
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
|
||||
# remove double-spaced lines
|
||||
text = re.sub(r'\n\n', '\n', text)
|
||||
return text
|
||||
|
||||
# Go through existing .po files and, if a .tr file for that language
|
||||
# *doesn't* exist, convert it and create it.
|
||||
# The .tr file that results will subsequently be reprocessed so
|
||||
# any "no longer used" strings will be preserved.
|
||||
# Note that "fuzzy" tags will be lost in this process.
|
||||
def process_po_files(folder, modname):
|
||||
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
|
||||
for name in files:
|
||||
code_match = pattern_po_language_code.match(name)
|
||||
if code_match == None:
|
||||
continue
|
||||
language_code = code_match.group(1)
|
||||
tr_name = f'{modname}.{language_code}.tr'
|
||||
tr_file = os.path.join(root, tr_name)
|
||||
if os.path.exists(tr_file):
|
||||
if params["verbose"]:
|
||||
print(f"{tr_name} already exists, ignoring {name}")
|
||||
continue
|
||||
fname = os.path.join(root, name)
|
||||
with open(fname, "r", encoding='utf-8') as po_file:
|
||||
if params["verbose"]:
|
||||
print(f"Importing translations from {name}")
|
||||
text = process_po_file(po_file.read())
|
||||
with open(tr_file, "wt", encoding='utf-8') as tr_out:
|
||||
tr_out.write(text)
|
||||
|
||||
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
|
||||
# Creates a directory if it doesn't exist, silently does
|
||||
# nothing if it already exists
|
||||
def mkdir_p(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as exc: # Python >2.5
|
||||
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else: raise
|
||||
|
||||
# Converts the template dictionary to a text to be written as a file
|
||||
# dKeyStrings is a dictionary of localized string to source file sets
|
||||
# dOld is a dictionary of existing translations and comments from
|
||||
# the previous version of this text
|
||||
def strings_to_text(dkeyStrings, dOld, mod_name, header_comments):
|
||||
lOut = [f"# textdomain: {mod_name}"]
|
||||
if header_comments is not None:
|
||||
lOut.append(header_comments)
|
||||
|
||||
dGroupedBySource = {}
|
||||
|
||||
for key in dkeyStrings:
|
||||
sourceList = list(dkeyStrings[key])
|
||||
if params["sort"]:
|
||||
sourceList.sort()
|
||||
sourceString = "\n".join(sourceList)
|
||||
listForSource = dGroupedBySource.get(sourceString, [])
|
||||
listForSource.append(key)
|
||||
dGroupedBySource[sourceString] = listForSource
|
||||
|
||||
lSourceKeys = list(dGroupedBySource.keys())
|
||||
lSourceKeys.sort()
|
||||
for source in lSourceKeys:
|
||||
localizedStrings = dGroupedBySource[source]
|
||||
if params["sort"]:
|
||||
localizedStrings.sort()
|
||||
if params["print-source"]:
|
||||
if lOut[-1] != "":
|
||||
lOut.append("")
|
||||
lOut.append(source)
|
||||
for localizedString in localizedStrings:
|
||||
val = dOld.get(localizedString, {})
|
||||
translation = val.get("translation", "")
|
||||
comment = val.get("comment")
|
||||
if params["break-long-lines"] and len(localizedString) > doublespace_threshold and not lOut[-1] == "":
|
||||
lOut.append("")
|
||||
if comment != None and comment != "" and not comment.startswith("# textdomain:"):
|
||||
lOut.append(comment)
|
||||
lOut.append(f"{localizedString}={translation}")
|
||||
if params["break-long-lines"] and len(localizedString) > doublespace_threshold:
|
||||
lOut.append("")
|
||||
|
||||
|
||||
unusedExist = False
|
||||
for key in dOld:
|
||||
if key not in dkeyStrings:
|
||||
val = dOld[key]
|
||||
translation = val.get("translation")
|
||||
comment = val.get("comment")
|
||||
# only keep an unused translation if there was translated
|
||||
# text or a comment associated with it
|
||||
if translation != None and (translation != "" or comment):
|
||||
if not unusedExist:
|
||||
unusedExist = True
|
||||
lOut.append("\n\n##### not used anymore #####\n")
|
||||
if params["break-long-lines"] and len(key) > doublespace_threshold and not lOut[-1] == "":
|
||||
lOut.append("")
|
||||
if comment != None:
|
||||
lOut.append(comment)
|
||||
lOut.append(f"{key}={translation}")
|
||||
if params["break-long-lines"] and len(key) > doublespace_threshold:
|
||||
lOut.append("")
|
||||
return "\n".join(lOut) + '\n'
|
||||
|
||||
# Writes a template.txt file
|
||||
# dkeyStrings is the dictionary returned by generate_template
|
||||
def write_template(templ_file, dkeyStrings, mod_name):
|
||||
# read existing template file to preserve comments
|
||||
existing_template = import_tr_file(templ_file)
|
||||
|
||||
text = strings_to_text(dkeyStrings, existing_template[0], mod_name, existing_template[2])
|
||||
mkdir_p(os.path.dirname(templ_file))
|
||||
with open(templ_file, "wt", encoding='utf-8') as template_file:
|
||||
template_file.write(text)
|
||||
|
||||
|
||||
# Gets all translatable strings from a lua file
|
||||
def read_lua_file_strings(lua_file):
|
||||
lOut = []
|
||||
text = open(lua_file).read()
|
||||
for s in pattern_lua.findall(text):
|
||||
with open(lua_file, encoding='utf-8') as text_file:
|
||||
text = text_file.read()
|
||||
#TODO remove comments here
|
||||
|
||||
text = re.sub(pattern_concat, "", text)
|
||||
|
||||
strings = []
|
||||
for s in pattern_lua_s.findall(text):
|
||||
strings.append(s[1])
|
||||
for s in pattern_lua_bracketed_s.findall(text):
|
||||
strings.append(s)
|
||||
for s in pattern_lua_fs.findall(text):
|
||||
strings.append(s[1])
|
||||
for s in pattern_lua_bracketed_fs.findall(text):
|
||||
strings.append(s)
|
||||
|
||||
for s in strings:
|
||||
s = re.sub(r'"\.\.\s+"', "", s)
|
||||
s = re.sub("@[^@=n]", "@@", s)
|
||||
s = re.sub("@[^@=0-9]", "@@", s)
|
||||
s = s.replace('\\"', '"')
|
||||
s = s.replace("\\'", "'")
|
||||
s = s.replace("\n", "@n")
|
||||
s = s.replace("\\n", "@n")
|
||||
s = s.replace("=", "@=")
|
||||
lOut.append(s)
|
||||
return lOut
|
||||
|
||||
def inport_tr_file(tr_file):
|
||||
# Gets strings from an existing translation file
|
||||
# returns both a dictionary of translations
|
||||
# and the full original source text so that the new text
|
||||
# can be compared to it for changes.
|
||||
# Returns also header comments in the third return value.
|
||||
def import_tr_file(tr_file):
|
||||
dOut = {}
|
||||
text = None
|
||||
header_comment = None
|
||||
if os.path.exists(tr_file):
|
||||
for line in open(tr_file, "r").readlines():
|
||||
s = line.strip()
|
||||
if s == "" or s[0] == "#":
|
||||
continue
|
||||
match = pattern_tr.match(s)
|
||||
if match:
|
||||
dOut[match.group(1)] = match.group(2)
|
||||
return dOut
|
||||
with open(tr_file, "r", encoding='utf-8') as existing_file :
|
||||
# save the full text to allow for comparison
|
||||
# of the old version with the new output
|
||||
text = existing_file.read()
|
||||
existing_file.seek(0)
|
||||
# a running record of the current comment block
|
||||
# we're inside, to allow preceeding multi-line comments
|
||||
# to be retained for a translation line
|
||||
latest_comment_block = None
|
||||
for line in existing_file.readlines():
|
||||
line = line.rstrip('\n')
|
||||
if line.startswith("###"):
|
||||
if header_comment is None and not latest_comment_block is None:
|
||||
# Save header comments
|
||||
header_comment = latest_comment_block
|
||||
# Strip textdomain line
|
||||
tmp_h_c = ""
|
||||
for l in header_comment.split('\n'):
|
||||
if not l.startswith("# textdomain:"):
|
||||
tmp_h_c += l + '\n'
|
||||
header_comment = tmp_h_c
|
||||
|
||||
def generate_template(templ_file):
|
||||
lOut = []
|
||||
for root, dirs, files in os.walk('./'):
|
||||
# Reset comment block if we hit a header
|
||||
latest_comment_block = None
|
||||
continue
|
||||
elif line.startswith("#"):
|
||||
# Save the comment we're inside
|
||||
if not latest_comment_block:
|
||||
latest_comment_block = line
|
||||
else:
|
||||
latest_comment_block = latest_comment_block + "\n" + line
|
||||
continue
|
||||
match = pattern_tr.match(line)
|
||||
if match:
|
||||
# this line is a translated line
|
||||
outval = {}
|
||||
outval["translation"] = match.group(2)
|
||||
if latest_comment_block:
|
||||
# if there was a comment, record that.
|
||||
outval["comment"] = latest_comment_block
|
||||
latest_comment_block = None
|
||||
dOut[match.group(1)] = outval
|
||||
return (dOut, text, header_comment)
|
||||
|
||||
# Walks all lua files in the mod folder, collects translatable strings,
|
||||
# and writes it to a template.txt file
|
||||
# Returns a dictionary of localized strings to source file sets
|
||||
# that can be used with the strings_to_text function.
|
||||
def generate_template(folder, mod_name):
|
||||
dOut = {}
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for name in files:
|
||||
if fnmatch.fnmatch(name, "*.lua"):
|
||||
fname = os.path.join(root, name)
|
||||
found = read_lua_file_strings(fname)
|
||||
print(fname, len(found))
|
||||
lOut.extend(found)
|
||||
lOut = list(set(lOut))
|
||||
lOut.sort()
|
||||
gen_template(templ_file, lOut)
|
||||
return lOut
|
||||
if params["verbose"]:
|
||||
print(f"{fname}: {str(len(found))} translatable strings")
|
||||
|
||||
def update_tr_file(lNew, mod_name, tr_file):
|
||||
lOut = ["# textdomain: %s\n" % mod_name]
|
||||
if os.path.exists(tr_file):
|
||||
shutil.copyfile(tr_file, tr_file+".old")
|
||||
dOld = inport_tr_file(tr_file)
|
||||
for key in lNew:
|
||||
val = dOld.get(key, "")
|
||||
lOut.append("%s=%s" % (key, val))
|
||||
lOut.append("##### not used anymore #####")
|
||||
for key in dOld:
|
||||
if key not in lNew:
|
||||
lOut.append("%s=%s" % (key, dOld[key]))
|
||||
open(tr_file, "w").write("\n".join(lOut))
|
||||
for s in found:
|
||||
sources = dOut.get(s, set())
|
||||
sources.add(f"### {os.path.basename(fname)} ###")
|
||||
dOut[s] = sources
|
||||
|
||||
data = generate_template("./locale/template.txt")
|
||||
update_tr_file(data, "minecart", "./locale/minecart.de.tr")
|
||||
print("Done.\n")
|
||||
if len(dOut) == 0:
|
||||
return None
|
||||
templ_file = os.path.join(folder, "locale/template.txt")
|
||||
write_template(templ_file, dOut, mod_name)
|
||||
return dOut
|
||||
|
||||
# Updates an existing .tr file, copying the old one to a ".old" file
|
||||
# if any changes have happened
|
||||
# dNew is the data used to generate the template, it has all the
|
||||
# currently-existing localized strings
|
||||
def update_tr_file(dNew, mod_name, tr_file):
|
||||
if params["verbose"]:
|
||||
print(f"updating {tr_file}")
|
||||
|
||||
tr_import = import_tr_file(tr_file)
|
||||
dOld = tr_import[0]
|
||||
textOld = tr_import[1]
|
||||
|
||||
textNew = strings_to_text(dNew, dOld, mod_name, tr_import[2])
|
||||
|
||||
if textOld and textOld != textNew:
|
||||
print(f"{tr_file} has changed.")
|
||||
if not params["no-old-file"]:
|
||||
shutil.copyfile(tr_file, f"{tr_file}.old")
|
||||
|
||||
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
|
||||
new_tr_file.write(textNew)
|
||||
|
||||
# Updates translation files for the mod in the given folder
|
||||
def update_mod(folder):
|
||||
modname = get_modname(folder)
|
||||
if modname is not None:
|
||||
process_po_files(folder, modname)
|
||||
print(f"Updating translations for {modname}")
|
||||
data = generate_template(folder, modname)
|
||||
if data == None:
|
||||
print(f"No translatable strings found in {modname}")
|
||||
else:
|
||||
for tr_file in get_existing_tr_files(folder):
|
||||
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
|
||||
else:
|
||||
print(f"\033[31mUnable to find modname in folder {folder}.\033[0m", file=_stderr)
|
||||
exit(1)
|
||||
|
||||
# Determines if the folder being pointed to is a mod or a mod pack
|
||||
# and then runs update_mod accordingly
|
||||
def update_folder(folder):
|
||||
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
|
||||
if is_modpack:
|
||||
subfolders = [f.path for f in os.scandir(folder) if f.is_dir() and not f.name.startswith('.')]
|
||||
for subfolder in subfolders:
|
||||
update_mod(subfolder)
|
||||
else:
|
||||
update_mod(folder)
|
||||
print("Done.")
|
||||
|
||||
def run_all_subfolders(folder):
|
||||
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir() and not f.name.startswith('.')]:
|
||||
update_folder(modfolder)
|
||||
|
||||
|
||||
main()
|
||||
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
@ -13,24 +13,37 @@
|
||||
minecart = {}
|
||||
|
||||
-- Version for compatibility checks, see readme.md/history
|
||||
minecart.version = 1.10
|
||||
minecart.version = 2.00
|
||||
|
||||
minecart.hopper_enabled = minetest.settings:get_bool("minecart_hopper_enabled") ~= false
|
||||
minecart.teleport_enabled = minetest.settings:get_bool("minecart_teleport_enabled") == true
|
||||
-- Test for MT 5.4 new string mode
|
||||
minecart.CLIP = minetest.features.use_texture_alpha_string_modes and "clip" or false
|
||||
|
||||
|
||||
minecart.S = minetest.get_translator("minecart")
|
||||
local MP = minetest.get_modpath("minecart")
|
||||
dofile(MP.."/storage.lua")
|
||||
dofile(MP.."/lib.lua")
|
||||
dofile(MP.."/monitoring.lua")
|
||||
dofile(MP.."/recording.lua")
|
||||
dofile(MP.."/minecart.lua")
|
||||
dofile(MP.."/buffer.lua")
|
||||
dofile(MP.."/protection.lua")
|
||||
dofile(MP .. "/baselib.lua")
|
||||
dofile(MP .. "/storage.lua")
|
||||
dofile(MP .. "/rails.lua")
|
||||
dofile(MP .. "/monitoring.lua")
|
||||
dofile(MP .. "/recording.lua")
|
||||
dofile(MP .. "/hopperlib.lua")
|
||||
dofile(MP .. "/nodelib.lua")
|
||||
dofile(MP .. "/entitylib.lua")
|
||||
dofile(MP .. "/api.lua")
|
||||
dofile(MP .. "/minecart.lua")
|
||||
dofile(MP .. "/buffer.lua")
|
||||
dofile(MP .. "/protection.lua")
|
||||
--dofile(MP .. "/tool.lua") # for debugging only
|
||||
dofile(MP .. "/signs.lua")
|
||||
dofile(MP .. "/terminal.lua")
|
||||
dofile(MP .. "/pusher.lua")
|
||||
|
||||
if minecart.hopper_enabled then
|
||||
dofile(MP.."/hopper.lua")
|
||||
dofile(MP.."/mods_support.lua")
|
||||
dofile(MP .. "/hopper.lua")
|
||||
dofile(MP .. "/mods_support.lua")
|
||||
end
|
||||
dofile(MP.."/doc.lua")
|
||||
|
||||
dofile(MP .. "/doc.lua")
|
||||
minetest.log("info", "[MOD] Minecart loaded")
|
||||
|
326
minecart/lib.lua
@ -1,326 +0,0 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local S = minecart.S
|
||||
|
||||
local RegisteredInventories = {}
|
||||
|
||||
local param2_to_dir = {[0]=
|
||||
{x=0, y=0, z=1},
|
||||
{x=1, y=0, z=0},
|
||||
{x=0, y=0, z=-1},
|
||||
{x=-1, y=0, z=0},
|
||||
{x=0, y=-1, z=0},
|
||||
{x=0, y=1, z=0}
|
||||
}
|
||||
|
||||
-- Registered carts
|
||||
local tValidCarts = {} -- [<cart_name_stopped>] = <cart_name_running>
|
||||
local lValidCartNodes = {}
|
||||
local tValidCartEntities = {}
|
||||
|
||||
minetest.tValidCarts = tValidCarts
|
||||
|
||||
function minecart.register_cart_names(cart_name_stopped, cart_name_running)
|
||||
tValidCarts[cart_name_stopped] = cart_name_running
|
||||
|
||||
if minetest.registered_nodes[cart_name_stopped] then
|
||||
lValidCartNodes[#lValidCartNodes+1] = cart_name_stopped
|
||||
end
|
||||
if minetest.registered_nodes[cart_name_running] then
|
||||
lValidCartNodes[#lValidCartNodes+1] = cart_name_running
|
||||
end
|
||||
if minetest.registered_entities[cart_name_stopped] then
|
||||
tValidCartEntities[cart_name_stopped] = true
|
||||
end
|
||||
if minetest.registered_entities[cart_name_running] then
|
||||
tValidCartEntities[cart_name_running] = true
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.get_node_lvm(pos)
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node then
|
||||
return node
|
||||
end
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
|
||||
local data = vm:get_data()
|
||||
local param2_data = vm:get_param2_data()
|
||||
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
||||
local idx = area:indexp(pos)
|
||||
if data[idx] and param2_data[idx] then
|
||||
return {
|
||||
name = minetest.get_name_from_content_id(data[idx]),
|
||||
param2 = param2_data[idx]
|
||||
}
|
||||
end
|
||||
return {name="ignore", param2=0}
|
||||
end
|
||||
|
||||
function minecart.stopped(vel, tolerance)
|
||||
tolerance = tolerance or 0.05
|
||||
return math.abs(vel.x) < tolerance and math.abs(vel.z) < tolerance
|
||||
end
|
||||
|
||||
local function is_air_like(name)
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if ndef and ndef.buildable_to then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function minecart.range(val, min, max)
|
||||
val = tonumber(val)
|
||||
if val < min then return min end
|
||||
if val > max then return max end
|
||||
return val
|
||||
end
|
||||
|
||||
function minecart.get_next_node(pos, param2)
|
||||
local pos2 = param2 and vector.add(pos, param2_to_dir[param2]) or pos
|
||||
local node = minetest.get_node(pos2)
|
||||
return pos2, node
|
||||
end
|
||||
|
||||
local function get_cart_object(pos, radius)
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos, radius or 0.5)) do
|
||||
if tValidCartEntities[object:get_entity_name()] then
|
||||
local vel = object:get_velocity()
|
||||
if vector.equals(vel, {x=0, y=0, z=0}) then -- still standing?
|
||||
return object
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- check if cart can be pushed
|
||||
function minecart.check_cart_for_pushing(pos, param2, radius)
|
||||
local pos2 = param2 and vector.add(pos, param2_to_dir[param2]) or pos
|
||||
|
||||
if minetest.find_node_near(pos2, radius or 0.5, lValidCartNodes, true) then
|
||||
return true
|
||||
end
|
||||
|
||||
return get_cart_object(pos2, radius) ~= nil
|
||||
end
|
||||
|
||||
-- check if cargo can be loaded
|
||||
function minecart.check_cart_for_loading(pos, param2, radius)
|
||||
local pos2 = param2 and vector.add(pos, param2_to_dir[param2]) or pos
|
||||
|
||||
if minetest.find_node_near(pos2, radius or 0.5, lValidCartNodes, true) then
|
||||
return true
|
||||
end
|
||||
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos2, radius or 0.5)) do
|
||||
if object:get_entity_name() == "minecart:cart" then
|
||||
local vel = object:get_velocity()
|
||||
if vector.equals(vel, {x=0, y=0, z=0}) then -- still standing?
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local get_next_node = minecart.get_next_node
|
||||
local check_cart_for_loading = minecart.check_cart_for_loading
|
||||
local check_cart_for_pushing = minecart.check_cart_for_pushing
|
||||
|
||||
-- Take the given number of items from the inv.
|
||||
-- Returns nil if ItemList is empty.
|
||||
function minecart.inv_take_items(inv, listname, num)
|
||||
if inv:is_empty(listname) then
|
||||
return nil
|
||||
end
|
||||
local size = inv:get_size(listname)
|
||||
for idx = 1, size do
|
||||
local items = inv:get_stack(listname, idx)
|
||||
if items:get_count() > 0 then
|
||||
local taken = items:take_item(num)
|
||||
inv:set_stack(listname, idx, items)
|
||||
return taken
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function minecart.take_items(pos, param2, num)
|
||||
local npos, node
|
||||
if param2 then
|
||||
npos, node = get_next_node(pos, (param2 + 2) % 4)
|
||||
else
|
||||
npos, node = pos, minetest.get_node(pos)
|
||||
end
|
||||
local def = RegisteredInventories[node.name]
|
||||
local owner = M(pos):get_string("owner")
|
||||
local inv = minetest.get_inventory({type="node", pos=npos})
|
||||
|
||||
if def and inv and def.take_listname and (not def.allow_take or def.allow_take(npos, nil, owner)) then
|
||||
return minecart.inv_take_items(inv, def.take_listname, num)
|
||||
elseif def and def.take_item then
|
||||
return def.take_item(npos, num, owner)
|
||||
else
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.minecart_hopper_takeitem then
|
||||
return ndef.minecart_hopper_takeitem(npos, num)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.put_items(pos, param2, stack)
|
||||
local npos, node = get_next_node(pos, param2)
|
||||
local def = RegisteredInventories[node.name]
|
||||
local owner = M(pos):get_string("owner")
|
||||
local inv = minetest.get_inventory({type="node", pos=npos})
|
||||
|
||||
if def and inv and def.put_listname and (not def.allow_put or def.allow_put(npos, stack, owner)) then
|
||||
local leftover = inv:add_item(def.put_listname, stack)
|
||||
if leftover:get_count() > 0 then
|
||||
return leftover
|
||||
end
|
||||
elseif def and def.put_item then
|
||||
return def.put_item(npos, stack, owner)
|
||||
elseif is_air_like(node.name) or check_cart_for_loading(npos) then
|
||||
minetest.add_item(npos, stack)
|
||||
else
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.minecart_hopper_additem then
|
||||
local leftover = ndef.minecart_hopper_additem(npos, stack)
|
||||
if leftover:get_count() > 0 then
|
||||
return leftover
|
||||
end
|
||||
else
|
||||
return stack
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.untake_items(pos, param2, stack)
|
||||
local npos, node
|
||||
if param2 then
|
||||
npos, node = get_next_node(pos, (param2 + 2) % 4)
|
||||
else
|
||||
npos, node = pos, minetest.get_node(pos)
|
||||
end
|
||||
local def = RegisteredInventories[node.name]
|
||||
local inv = minetest.get_inventory({type="node", pos=npos})
|
||||
|
||||
if def and inv and def.put_listname then
|
||||
return inv:add_item(def.put_listname, stack)
|
||||
elseif def and def.untake_item then
|
||||
return def.untake_item(npos, stack)
|
||||
else
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.minecart_hopper_untakeitem then
|
||||
return ndef.minecart_hopper_untakeitem(npos, stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.punch_cart(pos, param2, radius, dir)
|
||||
local pos2 = param2 and vector.add(pos, param2_to_dir[param2]) or pos
|
||||
|
||||
local pos3 = minetest.find_node_near(pos2, radius or 0.5, lValidCartNodes, true)
|
||||
if pos3 then
|
||||
local node = minetest.get_node(pos3)
|
||||
--print(node.name)
|
||||
minecart.node_on_punch(pos3, node, nil, nil, tValidCarts[node.name], dir)
|
||||
return true
|
||||
end
|
||||
|
||||
local obj = get_cart_object(pos2, radius)
|
||||
if obj then
|
||||
obj:punch(obj, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = 1},
|
||||
}, dir)
|
||||
end
|
||||
end
|
||||
|
||||
-- Register inventory node for hopper access
|
||||
-- (for examples, see below)
|
||||
function minecart.register_inventory(node_names, def)
|
||||
for _, name in ipairs(node_names) do
|
||||
RegisteredInventories[name] = {
|
||||
allow_put = def.put and def.put.allow_inventory_put,
|
||||
put_listname = def.put and def.put.listname,
|
||||
allow_take = def.take and def.take.allow_inventory_take,
|
||||
take_listname = def.take and def.take.listname,
|
||||
put_item = def.put and def.put.put_item,
|
||||
take_item = def.take and def.take.take_item,
|
||||
untake_item = def.take and def.take.untake_item,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.register_cart_entity(entity_name, node_name, entity_def)
|
||||
entity_def.velocity = {x=0, y=0, z=0} -- only used on punch
|
||||
entity_def.old_dir = {x=1, y=0, z=0} -- random value to start the cart on punch
|
||||
entity_def.old_pos = nil
|
||||
entity_def.old_switch = 0
|
||||
entity_def.node_name = node_name
|
||||
minetest.register_entity(entity_name, entity_def)
|
||||
-- register node for punching
|
||||
minecart.register_cart_names(node_name, entity_name)
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == "minecart:userID_node" then
|
||||
if fields.exit == "Save" or fields.key_enter == "true" then
|
||||
local cart_pos = S2P(player:get_meta():get_string("cart_pos"))
|
||||
local userID = tonumber(fields.userID) or 0
|
||||
M(cart_pos):set_int("userID", userID)
|
||||
M(cart_pos):set_string("infotext", minetest.get_color_escape_sequence("#FFFF00")..player:get_player_name()..": "..userID)
|
||||
minecart.node_at_station(player:get_player_name(), userID, cart_pos)
|
||||
end
|
||||
return true
|
||||
end
|
||||
if formname == "minecart:userID_entity" then
|
||||
if fields.exit == "Save" or fields.key_enter == "true" then
|
||||
local cart_pos = S2P(player:get_meta():get_string("cart_pos"))
|
||||
local obj = get_cart_object(cart_pos)
|
||||
if obj then
|
||||
local entity = obj:get_luaentity()
|
||||
entity.userID = tonumber(fields.userID) or 0
|
||||
obj:set_nametag_attributes({color = "#ffff00", text = entity.owner..": "..entity.userID})
|
||||
minecart.update_userID(entity.myID, entity.userID)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
minecart.register_inventory({"minecart:hopper"}, {
|
||||
put = {
|
||||
allow_inventory_put = function(pos, stack, player_name)
|
||||
local owner = M(pos):get_string("owner")
|
||||
return owner == player_name
|
||||
end,
|
||||
listname = "main",
|
||||
},
|
||||
take = {
|
||||
allow_inventory_take = function(pos, stack, player_name)
|
||||
local owner = M(pos):get_string("owner")
|
||||
return owner == player_name
|
||||
end,
|
||||
listname = "main",
|
||||
},
|
||||
})
|
@ -1,37 +1,58 @@
|
||||
# textdomain: minecart
|
||||
|
||||
Station name=Stationsname
|
||||
Waiting time/sec=Wartezeit/s
|
||||
connected to=verbunden mit
|
||||
Minecart Railway Buffer=Minecart Prellbock
|
||||
Summary=Zusammenfassung
|
||||
1. Place your rails and build a route with two endpoints. Junctions are allowed as long as each route has its own start and endpoint.=1. Baue eine Schienenstrecke mit zwei Enden. Kreuzungen sind zulässig, solange jede Route ihre eigenen Start- und Endpunkte hat.
|
||||
10. Check the cart state via the chat command: /mycart <num>@n '<num>' is the cart number=Prüfe den Status des Wagen mit dem Chat Kommando: /mycart <num>@n <num> ist die Wagennummer
|
||||
11. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back.=11: Lege Gegenstände in ein Wagen (Taste Q) und starte dann den Wagen durch Anklicken. Klicke mit gedrückter Shift-Taste auf den Wagen, um Gegenstände wieder auszuladen.
|
||||
12. Dig the empty cart with a second 'sneak+click' (as usual).=10. Klicke erneut mit gedrückter Shift-Taste auf den Wagen, um diesen zu entfernen.
|
||||
2. Place a Railway Buffer at both endpoints (buffers are always needed, they store the route and timing information).=2. Platziere einen Prellbock an beide Schienenenden (Prellböcke sind zwingend notwendig, sie speichern die Routen- und Zeit-Informationen).
|
||||
3. Give both Railway Buffers unique station names, like Oxford and Cambridge.=3. Gib beiden Prellböcken eindeutige Stationsnamen wie: Stuttgart und München.
|
||||
4. Place a Minecart at a buffer and give it a cart number (1..999)=4. Platziere einen Minecart Wagen an einem Prellbock und gib dem Wagen eine Wagennummer (1..999)
|
||||
5. Drive from buffer to buffer in both directions using the Minecart(!) to record the routes (use 'right-left' keys to control the Minecart).=5. Um eine Route aufzuzeichnen, fahre die Route in beide Richtungen von Prellbock zu Prellbock mit dem Minecart Wagen(!). Nutze 'links-rechts' Tasten zur Steuerung.
|
||||
6. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge').=6. Schlage auf die Prellböcke um die Verbindungsdaten zu prüfen (bspw.: 'München: verbunden mit Stuttgart')
|
||||
7. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time.=7. Optional: Konfiguriere die Wagenwartezeit in einem oder in beiden Prellböcken. Der Wagen startet dann nach dieser Zeit automatisch.
|
||||
7. Optional: Configure the Minecart waiting time in both buffers. The Minecart will then start automatically after the configured time.=7. Optional: Konfiguriere die Wagenwartezeit in beiden Prellböcken. Der Wagen startet dann nach dieser Zeit automatisch.
|
||||
8. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters).=8. Optional: Schütze deine Schienen mit Hilfe der Meilensteine (ein Meilenstein mindestens alle 16 Blöcke).
|
||||
9. Place a Minecart in front of the buffer and check whether it starts after the configured time.=9. Platziere einen Wagen direkt vor einem Prellbock und prüfe, ob er nach der konfigurierten Zeit startet.
|
||||
Allow to dig/place rails in Minecart Landmark areas=Erlaubt dir, Schienen in Meilensteinbereichen zu setzen/zu entfernen
|
||||
Minecart=Minecart
|
||||
Minecart (Sneak+Click to pick up)=Minecart (Shift+Klick zum Entfernen des Carts)
|
||||
Minecart Cart=Wagen
|
||||
Minecart Hopper=Minecart Hopper
|
||||
Minecart Landmark=Minecart Meilenstein
|
||||
Minecart Railway Buffer=Minecart Prellbock
|
||||
Minecart, the lean railway transportation automation system=Minecart, das schlanke Schienentransport Automatisierungssystem
|
||||
Primary used to transport items. You can drop items into the Minecart and punch the cart to get started. Sneak+click the cart to get the items back=Primär für den Transport von Gegenständen genutzt. Du kannst Gegenstände in ein Cart legen (Taste Q) und dann den Wagen durch Anklicken starten. Klicke mit gedrückter Shift-Taste auf den Wagen, um die Gegenstände wieder auszuladen
|
||||
Protect your rails with the Landmarks (one Landmark at least every 16 blocks near the rail)=Schütze deine Schienen mit Hilfe der Meilensteine (ein Meilenstein mindestens alle 16 Blöcke der Strecke entlang)
|
||||
Station name=Stationsname
|
||||
Stop time/sec=Haltezeit/s
|
||||
Summary=Zusammenfassung
|
||||
10. Check the cart state via the chat command: /mycart <num>@n '<num>' is the cart number=Prüfe den Status des Wagen mit dem Chat Kommando: /mycart <num>@n <num> ist die Wagennummer
|
||||
11. Drop items into the Minecart and punch the cart to start it.=11: Lege Gegenstände in ein Wagen (Taste Q) und starte dann den Wagen durch Anklicken.
|
||||
12. Dig the cart with 'sneak+click' (as usual). The items will be drop down.=10. Klicke mit gedrückter Shift-Taste auf den Wagen, um diesen zu entfernen. Die Gegenstände fallen dann zu Boden.
|
||||
Primary used to transport items. You can drop items into the Minecart and punch the cart to get started. Sneak+click the cart to get cart and items back=Primär für den Transport von Gegenständen genutzt. Du kannst Gegenstände in ein Cart legen (Taste Q) und dann den Wagen durch Anklicken starten. Klicke mit gedrückter Shift-Taste auf den Wagen, um Cart und Gegenstände zurückzuerhalten
|
||||
Used as buffer on both rail ends. Needed to be able to record the cart routes=Preckblöcke müssen an beiden Schienenenden platziert sein, so dass Aufzeichnungen der Strecke gemacht werden können.
|
||||
Protect your rails with the Landmarks (one Landmark at least every 16 blocks near the rail)=Schütze deine Schienen mit Hilfe der Meilensteine (ein Meilenstein mindestens alle 16 Blöcke der Strecke entlang)
|
||||
Used to load/unload Minecart. The Hopper can push/pull items to/from chests and drop/pickup items to/from Minecarts. To unload a Minecart place the hopper below the rail. To load the Minecart, place the hopper right next to the Minecart.=Um Wagen zu be- und entladen. Der Hopper kann Gegenstände aus Kisten Holen und legen, sowie diese in Wagen fallen lassen bzw. aus Wagen entnehmen. Um einen Wagen zu entladen, muss der Hopper unter die Schiene platziert werden. Um einen Wagen zu beladen, muss der Hopper direkt neben die Schiene platziert werden.
|
||||
Minecart=Minecart
|
||||
Minecart, the lean railway transportation automation system=Minecart, das schlanke Schienentransport Automatisierungssystem
|
||||
Minecart Cart=Wagen
|
||||
Minecart Speed Signs=Geschwindigkeitsbegrenzungszeichen
|
||||
If several carts are running on one route,@nit can happen that a buffer position is already occupied and one cart therefore stops earlier.@nIn this case, the cart pusher is used to push the cart towards the buffer again.@nThis block must be placed under the rail at a distance of 2 m in front of the buffer.=Wenn mehrere Wagen auf einer Route fahren, kann es vorkommen,@ndass eine Prellbock Position bereits belegt ist und ein Wagen daher früher anhält.@nDer Cart Anschieber dient in diesem Fall dazu, die Wagen wieder in Richtung Prellbock anzuschieben.@nDieser Block muss unter der Schiene mit 2 m Abstand vor dem Prellbock platziert werden.
|
||||
Limit the cart speed with speed limit signs.@n@nAs before, the speed of the carts is also influenced by power rails.@nBrake rails are irrelevant, the cart does not brake here.@nThe maximum speed is 8 m/s. This assumes a ratio of power rails@nto normal rails of 1 to 4 on a flat section of rail. A rail section is a@nseries of rail nodes without a change of direction. After every curve / kink,@nthe speed for the next section of the route is newly determined,@ntaking into account the swing of the cart. This means that a cart can@nroll over short rail sections without power rails.@n@nIn order to additionally brake the cart at certain points@n(at switches or in front of a buffer), speed limit signs can be placed@non the track. With these signs the speed can be reduced to 4, 2, or 1 m / s.@nThe "No speed limit" sign can be used to remove the speed limit.@n@nThe speed limit signs must be placed next to the track so that they can@nbe read from the cart. This allows different speeds in each direction of travel.=Begrenze die Geschwindigkeit der Wagen mit Geschwindigkeitsbegrenzungszeichen@n@nDie Geschwindigkeit der Carts wird wie bisher auch über "power rails" beeinflusst. "Brake rails" sind ohne Bedeutung, das Cart bremst hier nicht. Die maximale Geschwindigkeit beträgt 8 m/s. Dies setzt eine Verhältnis von "power rails" zu "normal rails" von 1 zu 4 auf einem ebenen Streckenabschnitt voraus. Ein Streckenabschnitt ist dabei ein Reihe von Schienenblöcken ohne Richtungsänderung. Nach jeder Kurve/Knick wird die Geschwindigkeit für den nächsten Streckenabschnitt neu bestimmt, wobei hier der Schwung des Carts mit berücksichtigt wird. So kann ein Cart auch über kurze Streckenabschnitt ohne "power rails" rollen.@n@nUm das Cart zusätzlich an bestimmten Stellen abzubremsen (an Weichen oder vor einen Puffer), können Geschwindigkeitsbegrenzungszeichen an der Strecke platziert werden. Durch diese Zeichen kann die Geschwindigkeit auf 4, 2, oder 1 m/s reduziert werden. Durch das Aufhebungszeichen kann die Geschwindigkeitsbegrenzung wieder aufgehoben werden.@n@nDie Geschwindigkeitsbegrenzungszeichen müssen so neben die Strecke platziert werden, dass sie vom Cart ablesbar sind. Dies erlaubt damit unterschiedliche Geschwindigkeiten pro Fahrtrichtung.
|
||||
Minecart Hopper=Minecart Hopper
|
||||
Minecart (Sneak+Click to pick up)=Minecart (Shift+Klick zum Entfernen des Carts)
|
||||
Output cart state and position, or a list of carts, if no cart number is given.=Gibt Status und Position des Wagens, oder eine Liste aller Wagen aus, wenn keine Wagennummer angegeben ist.
|
||||
List of carts=Liste aller Wagen
|
||||
Enter cart number=Gebe Cart Nummer ein
|
||||
Save=Speichern
|
||||
[minecart] Area is protected!=[minecart] Bereich ist geschützt!
|
||||
[minecart] Cart is protected by = Wagen ist geschützt durch
|
||||
[minecart] Recording canceled!=[minecart] Aufzeichnung abgebrochen!
|
||||
Allow to dig/place rails in Minecart Landmark areas=Erlaubt dir, Schienen in Meilensteinbereichen zu setzen/zu entfernen
|
||||
Minecart Landmark=Minecart Meilenstein
|
||||
Cart Pusher=Wagen Anschieber
|
||||
left=links
|
||||
right=rechts
|
||||
straight=geradeaus
|
||||
Recording=Aufzeichnung
|
||||
speed=Tempo
|
||||
next junction=nächste Weiche
|
||||
Travel time=Fahrzeit
|
||||
[minecart] Route stored!=[minecart] Strecke gespeichert
|
||||
[minecart] Start route recording!=[minecart] Starte die Streckenaufzeichnung!
|
||||
connected to=verbunden mit
|
||||
[minecart] Speed @= %u m/s, Time @= %u s, Route length @= %u m=[minecart] Geschw. @= %u m/s, Zeit @= %u s, Routenlänge @= %u m
|
||||
Speed "1"=Tempo "1"
|
||||
Speed "2"=Tempo "2"
|
||||
Speed "4"=Tempo "4"
|
||||
No speed limit=Keine Geschwindigkeitsbegrenzung
|
||||
Cart List=Cart Liste
|
||||
Cart Terminal=Cart Terminal
|
||||
|
||||
|
||||
##### not used anymore #####
|
||||
|
||||
Used to push a cart if the cart does not stop directly at a buffer. Block has to be placed below the rail.=Wird verwendet, um einen Wagen anzuschieben, wenn der Wagen nicht direkt an einem Puffer anhält. Der Block muss unter der Schiene platziert werden.
|
||||
|
@ -1,33 +1,53 @@
|
||||
# textdomain: minecart
|
||||
Station name=
|
||||
Waiting time/sec=
|
||||
connected to=
|
||||
Minecart Railway Buffer=
|
||||
Summary=
|
||||
1. Place your rails and build a route with two endpoints. Junctions are allowed as long as each route has its own start and endpoint.=
|
||||
10. Check the cart state via the chat command: /mycart <num>@n '<num>' is the cart number=
|
||||
11. Drop items into the Minecart and punch the cart to start it, or 'sneak+click' the Minecart to get the items back.=
|
||||
12. Dig the empty cart with a second 'sneak+click' (as usual).=
|
||||
2. Place a Railway Buffer at both endpoints (buffers are always needed, they store the route and timing information).=
|
||||
3. Give both Railway Buffers unique station names, like Oxford and Cambridge.=
|
||||
4. Place a Minecart at a buffer and give it a cart number (1..999)=
|
||||
5. Drive from buffer to buffer in both directions using the Minecart(!) to record the routes (use 'right-left' keys to control the Minecart).=
|
||||
6. Punch the buffers to check the connection data (e.g. 'Oxford: connected to Cambridge').=
|
||||
7. Optional: Configure the Minecart stop time in one or both buffers. The Minecart will then start automatically after the configured time.=
|
||||
7. Optional: Configure the Minecart waiting time in both buffers. The Minecart will then start automatically after the configured time.=
|
||||
8. Optional: Protect your rail network with the Protection Landmarks (one Landmark at least every 16 nodes/meters).=
|
||||
9. Place a Minecart in front of the buffer and check whether it starts after the configured time.=
|
||||
Allow to dig/place rails in Minecart Landmark areas=
|
||||
Minecart=
|
||||
Minecart (Sneak+Click to pick up)=
|
||||
Minecart Cart=
|
||||
Minecart Hopper=
|
||||
Minecart Landmark=
|
||||
Minecart Railway Buffer=
|
||||
Minecart, the lean railway transportation automation system=
|
||||
Primary used to transport items. You can drop items into the Minecart and punch the cart to get started. Sneak+click the cart to get the items back=
|
||||
Protect your rails with the Landmarks (one Landmark at least every 16 blocks near the rail)=
|
||||
Station name=
|
||||
Stop time/sec=
|
||||
Summary=
|
||||
10. Check the cart state via the chat command: /mycart <num>@n '<num>' is the cart number=
|
||||
11. Drop items into the Minecart and punch the cart to start it.=
|
||||
12. Dig the cart with 'sneak+click' (as usual). The items will be drop down.=
|
||||
Primary used to transport items. You can drop items into the Minecart and punch the cart to get started. Sneak+click the cart to get cart and items back=
|
||||
Used as buffer on both rail ends. Needed to be able to record the cart routes=
|
||||
Protect your rails with the Landmarks (one Landmark at least every 16 blocks near the rail)=
|
||||
Used to load/unload Minecart. The Hopper can push/pull items to/from chests and drop/pickup items to/from Minecarts. To unload a Minecart place the hopper below the rail. To load the Minecart, place the hopper right next to the Minecart.=
|
||||
Minecart=
|
||||
Minecart, the lean railway transportation automation system=
|
||||
Minecart Cart=
|
||||
Minecart Speed Signs=
|
||||
If several carts are running on one route,@nit can happen that a buffer position is already occupied and one cart therefore stops earlier.@nIn this case, the cart pusher is used to push the cart towards the buffer again.@nThis block must be placed under the rail at a distance of 2 m in front of the buffer.=
|
||||
Limit the cart speed with speed limit signs.@n@nAs before, the speed of the carts is also influenced by power rails.@nBrake rails are irrelevant, the cart does not brake here.@nThe maximum speed is 8 m/s. This assumes a ratio of power rails@nto normal rails of 1 to 4 on a flat section of rail. A rail section is a@nseries of rail nodes without a change of direction. After every curve / kink,@nthe speed for the next section of the route is newly determined,@ntaking into account the swing of the cart. This means that a cart can@nroll over short rail sections without power rails.@n@nIn order to additionally brake the cart at certain points@n(at switches or in front of a buffer), speed limit signs can be placed@non the track. With these signs the speed can be reduced to 4, 2, or 1 m / s.@nThe "No speed limit" sign can be used to remove the speed limit.@n@nThe speed limit signs must be placed next to the track so that they can@nbe read from the cart. This allows different speeds in each direction of travel.=
|
||||
Minecart Hopper=
|
||||
Minecart (Sneak+Click to pick up)=
|
||||
Output cart state and position, or a list of carts, if no cart number is given.=
|
||||
List of carts=
|
||||
Enter cart number=
|
||||
Save=
|
||||
[minecart] Area is protected!=
|
||||
[minecart] Cart is protected by =
|
||||
[minecart] Recording canceled!=
|
||||
Allow to dig/place rails in Minecart Landmark areas=
|
||||
Minecart Landmark=
|
||||
Cart Pusher=
|
||||
left=
|
||||
right=
|
||||
straight=
|
||||
Recording=
|
||||
speed=
|
||||
next junction=
|
||||
Travel time=
|
||||
[minecart] Route stored!=
|
||||
[minecart] Start route recording!=
|
||||
connected to=
|
||||
[minecart] Speed @= %u m/s, Time @= %u s, Route length @= %u m=
|
||||
Speed "1"=
|
||||
Speed "2"=
|
||||
Speed "4"=
|
||||
No speed limit=
|
||||
Cart List=
|
||||
Cart Terminal=
|
||||
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
@ -11,69 +11,92 @@
|
||||
]]--
|
||||
|
||||
local S = minecart.S
|
||||
local MP = minetest.get_modpath("minecart")
|
||||
local lib = dofile(MP.."/cart_lib1.lua")
|
||||
local M = minetest.get_meta
|
||||
|
||||
lib:init(false)
|
||||
|
||||
local cart_entity = {
|
||||
initial_properties = {
|
||||
physical = false, -- otherwise going uphill breaks
|
||||
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
visual = "mesh",
|
||||
mesh = "carts_cart.b3d",
|
||||
visual_size = {x=1, y=1},
|
||||
textures = {"carts_cart.png^minecart_cart.png"},
|
||||
static_save = false,
|
||||
},
|
||||
------------------------------------ changed
|
||||
owner = nil,
|
||||
------------------------------------ changed
|
||||
driver = nil,
|
||||
punched = false, -- used to re-send velocity and position
|
||||
velocity = {x=0, y=0, z=0}, -- only used on punch
|
||||
old_dir = {x=1, y=0, z=0}, -- random value to start the cart on punch
|
||||
old_pos = nil,
|
||||
old_switch = 0,
|
||||
railtype = nil,
|
||||
cargo = {},
|
||||
on_rightclick = lib.on_rightclick,
|
||||
on_activate = lib.on_activate,
|
||||
on_detach_child = lib.on_detach_child,
|
||||
on_punch = lib.on_punch,
|
||||
on_step = lib.on_step,
|
||||
}
|
||||
|
||||
|
||||
minetest.register_entity("minecart:cart", cart_entity)
|
||||
|
||||
minecart.register_cart_names("minecart:cart", "minecart:cart")
|
||||
|
||||
|
||||
minetest.register_craftitem("minecart:cart", {
|
||||
minetest.register_node("minecart:cart", {
|
||||
description = S("Minecart (Sneak+Click to pick up)"),
|
||||
inventory_image = minetest.inventorycube("carts_cart_top.png", "carts_cart_side.png^minecart_logo.png", "carts_cart_side.png^minecart_logo.png"),
|
||||
wield_image = "carts_cart_side.png",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
-- use cart as tool
|
||||
local under = pointed_thing.under
|
||||
local node = minetest.get_node(under)
|
||||
local udef = minetest.registered_nodes[node.name]
|
||||
if udef and udef.on_rightclick and
|
||||
not (placer and placer:is_player() and
|
||||
placer:get_player_control().sneak) then
|
||||
return udef.on_rightclick(under, node, placer, itemstack,
|
||||
pointed_thing) or itemstack
|
||||
end
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"carts_cart_top.png^minecart_appl_cart_top.png",
|
||||
"carts_cart_top.png",
|
||||
"carts_cart_side.png^minecart_logo.png",
|
||||
"carts_cart_side.png^minecart_logo.png",
|
||||
"carts_cart_side.png^minecart_logo.png",
|
||||
"carts_cart_side.png^minecart_logo.png",
|
||||
},
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-8/16,-8/16,-8/16, 8/16, 8/16,-7/16},
|
||||
{-8/16,-8/16, 7/16, 8/16, 8/16, 8/16},
|
||||
{-8/16,-8/16,-8/16, -7/16, 8/16, 8/16},
|
||||
{ 7/16,-8/16,-8/16, 8/16, 8/16, 8/16},
|
||||
{-8/16,-8/16,-8/16, 8/16,-6/16, 8/16},
|
||||
},
|
||||
},
|
||||
-- collision_box = {
|
||||
-- type = "fixed",
|
||||
-- fixed = {
|
||||
-- {-8/16,-8/16,-8/16, 8/16,-4/16, 8/16},
|
||||
-- },
|
||||
-- },
|
||||
paramtype2 = "facedir",
|
||||
paramtype = "light",
|
||||
use_texture_alpha = minecart.CLIP,
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 2, crumbly = 2, choppy = 2},
|
||||
node_placement_prediction = "",
|
||||
diggable = false,
|
||||
|
||||
if not pointed_thing.type == "node" then
|
||||
return
|
||||
end
|
||||
on_place = minecart.on_nodecart_place,
|
||||
on_punch = minecart.on_nodecart_punch,
|
||||
|
||||
return lib.add_cart(itemstack, placer, pointed_thing, "minecart:cart")
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
if clicker and clicker:is_player() then
|
||||
if M(pos):get_int("userID") ~= 0 then
|
||||
-- enter the cart
|
||||
local object = minecart.node_to_entity(pos, "minecart:cart", "minecart:cart_entity")
|
||||
minecart.manage_attachment(clicker, object:get_luaentity(), true)
|
||||
else
|
||||
minecart.show_formspec(pos, clicker)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
set_cargo = function(pos, data)
|
||||
for _,item in ipairs(data or {}) do
|
||||
minetest.add_item(pos, ItemStack(item))
|
||||
end
|
||||
end,
|
||||
|
||||
get_cargo = function(pos)
|
||||
local data = {}
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||
local entity = obj:get_luaentity()
|
||||
if not obj:is_player() and entity and entity.name == "__builtin:item" then
|
||||
obj:remove()
|
||||
data[#data + 1] = entity.itemstring
|
||||
end
|
||||
end
|
||||
return data
|
||||
end,
|
||||
})
|
||||
|
||||
minecart.register_cart_entity("minecart:cart_entity", "minecart:cart", "default", {
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
visual = "wielditem",
|
||||
textures = {"minecart:cart"},
|
||||
visual_size = {x=0.66, y=0.66, z=0.66},
|
||||
static_save = false,
|
||||
},
|
||||
driver_allowed = true,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "minecart:cart",
|
||||
recipe = {
|
||||
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
@ -3,274 +3,271 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- Some notes:
|
||||
-- 1) Entity IDs are volatile. For each server restart all carts get new IDs.
|
||||
-- 2) Monitoring is performed for entities only. Stopped carts in form of
|
||||
-- real nodes need no monitoring.
|
||||
-- 3) But nodes at stations have to call 'node_at_station' to be "visible"
|
||||
-- for the chat commands
|
||||
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local P2H = minetest.hash_node_position
|
||||
local H2P = minetest.get_position_from_hash
|
||||
local S = minecart.S
|
||||
local MP = minetest.get_modpath("minecart")
|
||||
local lib = dofile(MP.."/cart_lib3.lua")
|
||||
|
||||
local CartsOnRail = minecart.CartsOnRail -- from storage.lua
|
||||
local get_route = minecart.get_route -- from storage.lua
|
||||
local NodesAtStation = {}
|
||||
local tCartsOnRail = minecart.CartsOnRail
|
||||
local Queue = {}
|
||||
local first = 0
|
||||
local last = -1
|
||||
|
||||
--
|
||||
-- Helper functions
|
||||
--
|
||||
local function get_pos_vel_pitch_yaw(item)
|
||||
if item.start_time and item.start_key then -- cart on recorded route
|
||||
local run_time = minetest.get_gametime() - item.start_time
|
||||
local waypoints = get_route(item.start_key).waypoints
|
||||
local waypoint = waypoints[run_time]
|
||||
if waypoint then
|
||||
return S2P(waypoint[1]), S2P(waypoint[2]), 0, 0
|
||||
end
|
||||
end
|
||||
if item.last_pos then
|
||||
item.last_pos = vector.round(item.last_pos)
|
||||
if carts:is_rail(item.last_pos, minetest.raillike_group("rail")) then
|
||||
return item.last_pos, item.last_vel, item.last_pitch or 0, item.last_yaw or 0
|
||||
end
|
||||
item.last_pos.y = item.last_pos.y - 1
|
||||
if carts:is_rail(item.last_pos, minetest.raillike_group("rail")) then
|
||||
return item.last_pos, item.last_vel, item.last_pitch or 0, item.last_yaw or 0
|
||||
end
|
||||
end
|
||||
return item.start_pos, {x=0, y=0, z=0}, 0, 0
|
||||
local function push(cycle, item)
|
||||
last = last + 1
|
||||
item.cycle = cycle
|
||||
Queue[last] = item
|
||||
end
|
||||
|
||||
--
|
||||
-- Monitoring of cart entities
|
||||
--
|
||||
function minecart.add_to_monitoring(obj, myID, owner, userID)
|
||||
local pos = vector.round(obj:get_pos())
|
||||
CartsOnRail[myID] = {
|
||||
start_key = lib.get_route_key(pos),
|
||||
start_pos = pos,
|
||||
owner = owner, -- needed for query API
|
||||
userID = userID, -- needed for query API
|
||||
stopped = true,
|
||||
entity_name = obj:get_entity_name()
|
||||
}
|
||||
end
|
||||
|
||||
-- Called after cart number formspec is closed
|
||||
function minecart.update_userID(myID, userID)
|
||||
if CartsOnRail[myID] then
|
||||
CartsOnRail[myID].userID = userID
|
||||
local function pop(cycle)
|
||||
if first > last then return end
|
||||
local item = Queue[first]
|
||||
if item.cycle < cycle then
|
||||
Queue[first] = nil -- to allow garbage collection
|
||||
first = first + 1
|
||||
return item
|
||||
end
|
||||
end
|
||||
|
||||
-- When cart entity is removed
|
||||
function minecart.remove_from_monitoring(myID)
|
||||
if myID then
|
||||
CartsOnRail[myID] = nil
|
||||
minecart.store_carts()
|
||||
end
|
||||
end
|
||||
|
||||
-- For node carts at stations
|
||||
function minecart.node_at_station(owner, userID, pos)
|
||||
NodesAtStation[owner] = NodesAtStation[owner] or {}
|
||||
NodesAtStation[owner][userID] = pos
|
||||
end
|
||||
|
||||
function minecart.start_cart(pos, myID)
|
||||
local item = CartsOnRail[myID]
|
||||
if item and item.stopped then
|
||||
item.stopped = false
|
||||
item.start_pos = pos
|
||||
item.start_time = nil
|
||||
-- cart started from a buffer?
|
||||
local start_key = lib.get_route_key(pos)
|
||||
if start_key then
|
||||
item.start_time = minetest.get_gametime()
|
||||
item.start_key = start_key
|
||||
item.junctions = minecart.get_route(start_key).junctions
|
||||
minecart.store_carts()
|
||||
local function is_player_nearby(pos)
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos, 64)) do
|
||||
if object:is_player() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function minecart.stop_cart(pos, myID)
|
||||
local item = CartsOnRail[myID]
|
||||
if item and not item.stopped then
|
||||
item.start_time = nil
|
||||
item.start_key = nil
|
||||
item.start_pos = nil
|
||||
item.junctions = nil
|
||||
item.stopped = true
|
||||
minecart.store_carts()
|
||||
return true
|
||||
local function zombie_to_entity(pos, cart, checkpoint)
|
||||
local vel = {x = 0, y = 0, z = 0}
|
||||
local obj = minecart.add_entitycart(pos, cart.node_name, cart.entity_name,
|
||||
vel, cart.cargo, cart.owner, cart.userID)
|
||||
if obj then
|
||||
local entity = obj:get_luaentity()
|
||||
entity.reenter = checkpoint
|
||||
entity.junctions = cart.junctions
|
||||
entity.is_running = true
|
||||
entity.arrival_time = 0
|
||||
cart.objID = entity.objID
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function monitoring()
|
||||
local to_be_added = {}
|
||||
for key, item in pairs(CartsOnRail) do
|
||||
local entity = minetest.luaentities[key]
|
||||
--print("Cart:", key, item.owner, item.userID, item.stopped)
|
||||
local function get_checkpoint(cart)
|
||||
local cp = cart.checkpoints[cart.idx]
|
||||
if not cp then
|
||||
cart.idx = #cart.checkpoints
|
||||
cp = cart.checkpoints[cart.idx]
|
||||
end
|
||||
local pos = H2P(cp[1])
|
||||
-- if M(pos):contains("waypoints") then
|
||||
-- print("get_checkpoint", P2S(H2P(cp[1])), P2S(H2P(cp[2])))
|
||||
-- end
|
||||
return cp, cart.idx == #cart.checkpoints
|
||||
end
|
||||
|
||||
-- Function returns the cart state ("running" / "stopped") and
|
||||
-- the station name or position string, or if cart is running,
|
||||
-- the distance to the query_pos.
|
||||
local function get_cart_state_and_loc(name, userID, query_pos)
|
||||
if tCartsOnRail[name] and tCartsOnRail[name][userID] then
|
||||
local cart = tCartsOnRail[name][userID]
|
||||
local pos = cart.last_pos or cart.pos
|
||||
local loc = minecart.get_buffer_name(cart.pos) or
|
||||
math.floor(vector.distance(pos, query_pos))
|
||||
if cart.objID == 0 then
|
||||
return "stopped", minecart.get_buffer_name(cart.pos) or
|
||||
math.floor(vector.distance(pos, query_pos)), cart.node_name
|
||||
else
|
||||
return "running", math.floor(vector.distance(pos, query_pos)), cart.node_name
|
||||
end
|
||||
end
|
||||
return "unknown", 0, "unknown"
|
||||
end
|
||||
|
||||
local function get_cart_info(owner, userID, query_pos)
|
||||
local state, loc, name = get_cart_state_and_loc(owner, userID, query_pos)
|
||||
local cart_type = minecart.tCartTypes[name] or "unknown"
|
||||
if type(loc) == "number" then
|
||||
return "Cart #" .. userID .. " (" .. cart_type .. ") " .. state .. " " .. loc .. " m away "
|
||||
else
|
||||
return "Cart #" .. userID .. " (" .. cart_type .. ") " .. state .. " at ".. loc .. " "
|
||||
end
|
||||
end
|
||||
|
||||
local function monitoring(cycle)
|
||||
local cart = pop(cycle)
|
||||
|
||||
while cart do
|
||||
-- All running cars
|
||||
if cart.objID and cart.objID ~= 0 then
|
||||
cart.idx = cart.idx + 1
|
||||
local entity = minetest.luaentities[cart.objID]
|
||||
if entity then -- cart entity running
|
||||
local pos = entity.object:get_pos()
|
||||
local vel = entity.object:get_velocity()
|
||||
local rot = entity.object:get_rotation()
|
||||
if pos and vel and rot then
|
||||
if not minetest.get_node_or_nil(pos) then -- unloaded area
|
||||
lib.unload_cart(pos, vel, entity, item)
|
||||
item.stopped = minecart.stopped(vel)
|
||||
end
|
||||
-- store last pos from cart
|
||||
item.last_pos, item.last_vel, item.last_pitch, item.last_yaw = pos, vel, rot.x, rot.y
|
||||
end
|
||||
else -- no cart running
|
||||
local pos, vel, pitch, yaw = get_pos_vel_pitch_yaw(item)
|
||||
if pos and vel then
|
||||
if minetest.get_node_or_nil(pos) then -- loaded area
|
||||
if pitch > 0 then
|
||||
pos.y = pos.y + 0.5
|
||||
end
|
||||
local myID = lib.load_cart(pos, vel, pitch, yaw, item)
|
||||
if myID then
|
||||
item.stopped = minecart.stopped(vel)
|
||||
to_be_added[myID] = table.copy(item)
|
||||
CartsOnRail[key] = nil -- invalid old ID
|
||||
end
|
||||
end
|
||||
item.last_pos, item.last_vel, item.last_pitch, item.last_yaw = pos, vel, pitch, yaw
|
||||
if pos then
|
||||
cart.last_pos = vector.round(pos)
|
||||
--print("entity card " .. cart.userID .. " at " .. P2S(cart.last_pos))
|
||||
else
|
||||
-- should never happen
|
||||
minetest.log("error", "[minecart] Cart of owner "..(item.owner or "nil").." got lost")
|
||||
CartsOnRail[key] = nil
|
||||
print("entity card without pos!")
|
||||
end
|
||||
push(cycle, cart)
|
||||
elseif cart.checkpoints then
|
||||
local cp, last_cp = get_checkpoint(cart)
|
||||
if cp then
|
||||
cart.last_pos = H2P(cp[1])
|
||||
--print("zombie " .. cart.userID .. " at " .. P2S(cart.last_pos))
|
||||
if is_player_nearby(cart.last_pos) or last_cp then
|
||||
zombie_to_entity(cart.last_pos, cart, cp)
|
||||
end
|
||||
push(cycle, cart)
|
||||
else
|
||||
print("zombie got lost")
|
||||
end
|
||||
-- table maintenance
|
||||
local is_changed = false
|
||||
for key,val in pairs(to_be_added) do
|
||||
CartsOnRail[key] = val
|
||||
is_changed = true
|
||||
else
|
||||
local pos = cart.last_pos or cart.pos
|
||||
pos = minecart.add_nodecart(pos, cart.node_name, 0, cart.cargo, cart.owner, cart.userID)
|
||||
cart.objID = 0
|
||||
cart.pos = pos
|
||||
--print("cart to node", cycle, cart.userID, P2S(pos))
|
||||
end
|
||||
if is_changed then
|
||||
elseif cart and not cart.objID and tCartsOnRail[cart.owner] then
|
||||
-- Delete carts marked as "to be deleted"
|
||||
tCartsOnRail[cart.owner][cart.userID] = nil
|
||||
end
|
||||
cart = pop(cycle)
|
||||
end
|
||||
minetest.after(2, monitoring, cycle + 1)
|
||||
end
|
||||
|
||||
minetest.after(5, monitoring, 2)
|
||||
|
||||
|
||||
function minecart.monitoring_add_cart(owner, userID, pos, node_name, entity_name)
|
||||
--print("monitoring_add_cart", owner, userID)
|
||||
tCartsOnRail[owner] = tCartsOnRail[owner] or {}
|
||||
tCartsOnRail[owner][userID] = {
|
||||
owner = owner,
|
||||
userID = userID,
|
||||
objID = 0,
|
||||
pos = pos,
|
||||
idx = 0,
|
||||
node_name = node_name,
|
||||
entity_name = entity_name,
|
||||
}
|
||||
minecart.store_carts()
|
||||
end
|
||||
|
||||
function minecart.start_monitoring(owner, userID, pos, objID, checkpoints, junctions, cargo)
|
||||
--print("start_monitoring", owner, userID)
|
||||
if tCartsOnRail[owner] and tCartsOnRail[owner][userID] then
|
||||
tCartsOnRail[owner][userID].pos = pos
|
||||
tCartsOnRail[owner][userID].objID = objID
|
||||
tCartsOnRail[owner][userID].checkpoints = checkpoints
|
||||
tCartsOnRail[owner][userID].junctions = junctions
|
||||
tCartsOnRail[owner][userID].cargo = cargo
|
||||
tCartsOnRail[owner][userID].idx = 0
|
||||
push(0, tCartsOnRail[owner][userID])
|
||||
minecart.store_carts()
|
||||
end
|
||||
minetest.after(1, monitoring)
|
||||
end
|
||||
-- delay the start to prevent cart disappear into nirvana
|
||||
minetest.register_on_mods_loaded(function()
|
||||
minetest.after(10, monitoring)
|
||||
end)
|
||||
|
||||
function minecart.stop_monitoring(owner, userID, pos)
|
||||
--print("stop_monitoring", owner, userID)
|
||||
if tCartsOnRail[owner] and tCartsOnRail[owner][userID] then
|
||||
tCartsOnRail[owner][userID].pos = pos
|
||||
tCartsOnRail[owner][userID].objID = 0
|
||||
minecart.store_carts()
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.monitoring_remove_cart(owner, userID)
|
||||
--print("monitoring_remove_cart", owner, userID)
|
||||
if tCartsOnRail[owner] and tCartsOnRail[owner][userID] then
|
||||
tCartsOnRail[owner][userID].objID = nil
|
||||
tCartsOnRail[owner][userID] = nil
|
||||
minecart.store_carts()
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.monitoring_valid_cart(owner, userID, pos, node_name)
|
||||
if tCartsOnRail[owner] and tCartsOnRail[owner][userID] then
|
||||
return vector.equals(tCartsOnRail[owner][userID].pos, pos) and
|
||||
tCartsOnRail[owner][userID].node_name == node_name
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.userID_available(owner, userID)
|
||||
return not tCartsOnRail[owner] or tCartsOnRail[owner][userID] == nil
|
||||
end
|
||||
|
||||
function minecart.get_cart_monitoring_data(owner, userID)
|
||||
if tCartsOnRail[owner] then
|
||||
return tCartsOnRail[owner][userID]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- API functions
|
||||
--
|
||||
|
||||
-- Return a list of carts with current position and speed.
|
||||
function minecart.get_cart_list()
|
||||
local tbl = {}
|
||||
for id, item in pairs(CartsOnRail) do
|
||||
local pos, speed = calc_pos_and_vel(item)
|
||||
tbl[#tbl+1] = {pos = pos, speed = speed, id = id}
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
local function get_cart_pos(query_pos, cart_pos)
|
||||
local dist = math.floor(vector.distance(cart_pos, query_pos))
|
||||
local station = lib.get_station_name(cart_pos)
|
||||
return station or dist
|
||||
end
|
||||
|
||||
local function get_cart_state(name, userID)
|
||||
for id, item in pairs(CartsOnRail) do
|
||||
if item.owner == name and item.userID == userID then
|
||||
return item.stopped and "stopped" or "running", item.last_pos
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
-- Needed by storage to re-construct the queue after server start
|
||||
minecart.push = push
|
||||
|
||||
minetest.register_chatcommand("mycart", {
|
||||
params = "<cart-num>",
|
||||
description = "Output cart state and position, or a list of carts, if no cart number is given.",
|
||||
func = function(name, param)
|
||||
description = S("Output cart state and position, or a list of carts, if no cart number is given."),
|
||||
func = function(owner, param)
|
||||
local userID = tonumber(param)
|
||||
local query_pos = minetest.get_player_by_name(name):get_pos()
|
||||
local query_pos = minetest.get_player_by_name(owner):get_pos()
|
||||
|
||||
if userID then
|
||||
-- First check if it is a node cart at a station
|
||||
local cart_pos = NodesAtStation[name] and NodesAtStation[name][userID]
|
||||
if cart_pos then
|
||||
local pos = get_cart_pos(query_pos, cart_pos)
|
||||
return true, "Cart #"..userID.." stopped at "..pos.." "
|
||||
end
|
||||
-- Check all running carts
|
||||
local state, cart_pos = get_cart_state(name, userID)
|
||||
if state and cart_pos then
|
||||
local pos = get_cart_pos(query_pos, cart_pos)
|
||||
if type(pos) == "string" then
|
||||
return true, "Cart #"..userID.." stopped at "..pos.." "
|
||||
elseif state == "running" then
|
||||
return true, "Cart #"..userID.." running "..pos.." m away "
|
||||
else
|
||||
return true, "Cart #"..userID.." stopped "..pos.." m away "
|
||||
end
|
||||
end
|
||||
return false, "Cart #"..userID.." is unknown"
|
||||
else
|
||||
return true, get_cart_info(owner, userID, query_pos)
|
||||
elseif tCartsOnRail[owner] then
|
||||
-- Output a list with all numbers
|
||||
local tbl = {}
|
||||
for userID, pos in pairs(NodesAtStation[name] or {}) do
|
||||
for userID, cart in pairs(tCartsOnRail[owner]) do
|
||||
tbl[#tbl + 1] = userID
|
||||
end
|
||||
for id, item in pairs(CartsOnRail) do
|
||||
if item.owner == name then
|
||||
tbl[#tbl + 1] = item.userID
|
||||
end
|
||||
end
|
||||
return true, "List of carts: "..table.concat(tbl, ", ").." "
|
||||
return true, S("List of carts") .. ": "..table.concat(tbl, ", ").." "
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
function minecart.cmnd_cart_state(name, userID)
|
||||
-- First check if it is a node cart at a station
|
||||
local pos = NodesAtStation[name] and NodesAtStation[name][userID]
|
||||
if pos then
|
||||
return "stopped"
|
||||
end
|
||||
return get_cart_state(name, userID)
|
||||
local state, loc = get_cart_state_and_loc(name, userID, {x=0, y=0, z=0})
|
||||
return state
|
||||
end
|
||||
|
||||
function minecart.cmnd_cart_location(name, userID, query_pos)
|
||||
-- First check if it is a node cart at a station
|
||||
local station = NodesAtStation[name] and NodesAtStation[name][userID]
|
||||
if station then
|
||||
return station
|
||||
local state, loc = get_cart_state_and_loc(name, userID, query_pos)
|
||||
return loc
|
||||
end
|
||||
|
||||
function minecart.get_cart_list(pos, name)
|
||||
local userIDs = {}
|
||||
local carts = {}
|
||||
|
||||
for userID, cart in pairs(tCartsOnRail[name] or {}) do
|
||||
userIDs[#userIDs + 1] = userID
|
||||
end
|
||||
local state, cart_pos = get_cart_state(name, userID)
|
||||
if state then
|
||||
return get_cart_pos(query_pos, cart_pos)
|
||||
|
||||
table.sort(userIDs, function(a,b) return a < b end)
|
||||
|
||||
for _, userID in ipairs(userIDs) do
|
||||
carts[#carts + 1] = get_cart_info(name, userID, pos)
|
||||
end
|
||||
|
||||
return table.concat(carts, "\n")
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
@ -357,4 +354,3 @@ minetest.register_on_mods_loaded(function()
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
|
139
minecart/nodelib.lua
Normal file
@ -0,0 +1,139 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = minecart.S
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
|
||||
function minecart.get_nodecart_nearby(pos, param2, radius)
|
||||
local pos2 = param2 and vector.add(pos, minecart.param2_to_dir(param2)) or pos
|
||||
local pos3 = minetest.find_node_near(pos2, radius or 0.5, minecart.lCartNodeNames, true)
|
||||
if pos3 then
|
||||
return pos3, minetest.get_node(pos3)
|
||||
end
|
||||
end
|
||||
|
||||
-- Convert node to entity and start cart
|
||||
function minecart.start_nodecart(pos, node_name, puncher, punch_dir)
|
||||
local owner = M(pos):get_string("owner")
|
||||
local userID = M(pos):get_int("userID")
|
||||
-- check if valid cart
|
||||
if not minecart.monitoring_valid_cart(owner, userID, pos, node_name) then
|
||||
--print("invalid cart", owner, userID, P2S(pos), node_name)
|
||||
M(pos):set_string("infotext",
|
||||
minetest.get_color_escape_sequence("#FFFF00") .. owner .. ": 0")
|
||||
return
|
||||
end
|
||||
-- Only the owner or a noplayer can start the cart, but owner has to be online
|
||||
if minecart.is_owner(puncher, owner) and minetest.get_player_by_name(owner) and
|
||||
userID ~= 0 then
|
||||
local entity_name = minecart.tNodeNames[node_name]
|
||||
local obj = minecart.node_to_entity(pos, node_name, entity_name)
|
||||
if obj then
|
||||
local entity = obj:get_luaentity()
|
||||
if puncher then
|
||||
local yaw = puncher:get_look_horizontal()
|
||||
entity.object:set_rotation({x = 0, y = yaw, z = 0})
|
||||
elseif punch_dir then
|
||||
local yaw = minetest.dir_to_yaw(punch_dir)
|
||||
entity.object:set_rotation({x = 0, y = yaw, z = 0})
|
||||
end
|
||||
minecart.start_entitycart(entity, pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.show_formspec(pos, clicker)
|
||||
local owner = M(pos):get_string("owner")
|
||||
if minecart.is_owner(clicker, owner) then
|
||||
clicker:get_meta():set_string("cart_pos", P2S(pos))
|
||||
minetest.show_formspec(owner, "minecart:userID_node",
|
||||
"size[4,3]" ..
|
||||
"label[0,0;" .. S("Enter cart number") .. ":]" ..
|
||||
"field[1,1;3,1;userID;;]" ..
|
||||
"button_exit[1,2;2,1;exit;" .. S("Save") .. "]")
|
||||
end
|
||||
end
|
||||
|
||||
-- Player places the node
|
||||
function minecart.on_nodecart_place(itemstack, placer, pointed_thing)
|
||||
local node_name = itemstack:get_name()
|
||||
local param2 = minetest.dir_to_facedir(placer:get_look_dir())
|
||||
local owner = placer:get_player_name()
|
||||
|
||||
-- Add node
|
||||
if minecart.is_rail(pointed_thing.under) then
|
||||
minecart.add_nodecart(pointed_thing.under, node_name, param2, {}, owner, 0)
|
||||
placer:get_meta():set_string("cart_pos", P2S(pointed_thing.under))
|
||||
minecart.show_formspec(pointed_thing.under, placer)
|
||||
elseif minecart.is_rail(pointed_thing.above) then
|
||||
minecart.add_nodecart(pointed_thing.above, node_name, param2, {}, owner, 0)
|
||||
placer:get_meta():set_string("cart_pos", P2S(pointed_thing.above))
|
||||
minecart.show_formspec(pointed_thing.above, placer)
|
||||
else
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.sound_play({name = "default_place_node_metal", gain = 0.5},
|
||||
{pos = pointed_thing.above})
|
||||
|
||||
if not (creative and creative.is_enabled_for
|
||||
and creative.is_enabled_for(placer:get_player_name())) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Start the node cart (or dig by shift+leftclick)
|
||||
function minecart.on_nodecart_punch(pos, node, puncher, pointed_thing)
|
||||
--print("on_nodecart_punch")
|
||||
local owner = M(pos):get_string("owner")
|
||||
local userID = M(pos):get_int("userID")
|
||||
if minecart.is_owner(puncher, owner) then
|
||||
if puncher:get_player_control().sneak then
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if not ndef.has_cargo or not ndef.has_cargo(pos) then
|
||||
minecart.remove_nodecart(pos)
|
||||
minecart.add_node_to_player_inventory(pos, puncher, node.name)
|
||||
minecart.monitoring_remove_cart(owner, userID)
|
||||
end
|
||||
else
|
||||
minecart.start_nodecart(pos, node.name, puncher)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == "minecart:userID_node" then
|
||||
if fields.exit or fields.key_enter == "true" then
|
||||
local cart_pos = S2P(player:get_meta():get_string("cart_pos"))
|
||||
local owner = M(cart_pos):get_string("owner")
|
||||
if minecart.is_owner(player, owner) then
|
||||
local userID = tonumber(fields.userID) or 0
|
||||
if minecart.userID_available(owner, userID) then
|
||||
M(cart_pos):set_int("userID", userID)
|
||||
M(cart_pos):set_string("infotext",
|
||||
minetest.get_color_escape_sequence("#FFFF00") ..
|
||||
player:get_player_name() .. ": " .. userID)
|
||||
local node = minetest.get_node(cart_pos)
|
||||
local entity_name = minecart.tNodeNames[node.name]
|
||||
minecart.monitoring_add_cart(owner, userID, cart_pos, node.name, entity_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
@ -198,3 +198,8 @@ minecart.register_protected_node("minecart:buffer")
|
||||
minecart.register_protected_node("minecart:ballast")
|
||||
minecart.register_protected_node("minecart:ballast_slope")
|
||||
minecart.register_protected_node("minecart:ballast_ramp")
|
||||
minecart.register_protected_node("minecart:speed1")
|
||||
minecart.register_protected_node("minecart:speed2")
|
||||
minecart.register_protected_node("minecart:speed4")
|
||||
minecart.register_protected_node("minecart:speed8")
|
||||
|
||||
|
67
minecart/pusher.lua
Normal file
@ -0,0 +1,67 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = minecart.S
|
||||
local CYCLE_TIME = 4
|
||||
|
||||
local function node_timer(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
minecart.punch_cart({x = pos.x, y = pos.y + 1, z = pos.z}, nil, 1, dir)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local function after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
end
|
||||
|
||||
minetest.register_node("minecart:cart_pusher", {
|
||||
description = S("Cart Pusher"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-8/16,-8/16,-8/16, 8/16, 8/16, 8/16},
|
||||
{-1/16, 8/16,-4/16, 1/16, 10/16, 4/16},
|
||||
},
|
||||
},
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"default_steel_block.png^minecart_pusher_top.png",
|
||||
"default_steel_block.png",
|
||||
"default_steel_block.png^minecart_pusher.png",
|
||||
"default_steel_block.png^minecart_pusher.png",
|
||||
"default_steel_block.png^minecart_pusher.png",
|
||||
"default_steel_block.png^minecart_pusher.png",
|
||||
},
|
||||
after_place_node = function(pos)
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
end,
|
||||
|
||||
on_timer = node_timer,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = true,
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "minecart:cart_pusher",
|
||||
recipe = {
|
||||
{"dye:black", "default:steel_ingot", "dye:yellow"},
|
||||
{"default:steel_ingot", "default:mese_crystal", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
},
|
||||
})
|
548
minecart/rails.lua
Normal file
@ -0,0 +1,548 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P2H = minetest.hash_node_position
|
||||
|
||||
local get_node_lvm = minecart.get_node_lvm
|
||||
|
||||
local MAX_SPEED = 8
|
||||
local SLOWDOWN = 0.3
|
||||
local MAX_NODES = 100
|
||||
|
||||
--waypoint = {
|
||||
-- dot = travel direction,
|
||||
-- pos = destination pos,
|
||||
-- speed = 10 times the section speed (as int),
|
||||
-- limit = 10 times the speed limit (as int),
|
||||
--}
|
||||
--
|
||||
-- waypoints = {facedir = waypoint,...}
|
||||
|
||||
local tWaypoints = {} -- {pos_hash = waypoints, ...}
|
||||
|
||||
local tRailsPower = {
|
||||
["carts:rail"] = 0,
|
||||
["carts:powerrail"] = 1,
|
||||
["minecart:rail"] = 0,
|
||||
["minecart:powerrail"] = 1,
|
||||
["carts:brakerail"] = 0,
|
||||
}
|
||||
-- Real rails from the mod carts
|
||||
local tRails = {
|
||||
["carts:rail"] = true,
|
||||
["carts:powerrail"] = true,
|
||||
["carts:brakerail"] = true,
|
||||
["minecart:rail"] = true,
|
||||
["minecart:powerrail"] = true,
|
||||
}
|
||||
-- Rails plus node carts. Used to find waypoints. Added via add_raillike_nodes
|
||||
local tRailsExt = {
|
||||
["carts:rail"] = true,
|
||||
["carts:powerrail"] = true,
|
||||
["carts:brakerail"] = true,
|
||||
["minecart:rail"] = true,
|
||||
["minecart:powerrail"] = true,
|
||||
}
|
||||
|
||||
local tSigns = {
|
||||
["minecart:speed1"] = 1,
|
||||
["minecart:speed2"] = 2,
|
||||
["minecart:speed4"] = 4,
|
||||
["minecart:speed8"] = 8,
|
||||
}
|
||||
|
||||
-- Real rails from the mod carts
|
||||
local lRails = {"carts:rail", "carts:powerrail", "carts:brakerail", "minecart:rail", "minecart:powerrail"}
|
||||
-- Rails plus node carts used to find waypoints, , added via add_raillike_nodes
|
||||
local lRailsExt = {"carts:rail", "carts:powerrail", "carts:brakerail", "minecart:rail", "minecart:powerrail"}
|
||||
|
||||
minecart.MAX_SPEED = MAX_SPEED
|
||||
minecart.lRails = lRails
|
||||
minecart.tRails = tRails
|
||||
minecart.tRailsExt = tRailsExt
|
||||
minecart.lRailsExt = lRailsExt
|
||||
|
||||
local Dot2Dir = {}
|
||||
local Dir2Dot = {}
|
||||
local Facedir2Dir = {[0] =
|
||||
{x= 0, y=0, z= 1},
|
||||
{x= 1, y=0, z= 0},
|
||||
{x= 0, y=0, z=-1},
|
||||
{x=-1, y=0, z= 0},
|
||||
{x= 0, y=-1, z= 0},
|
||||
{x= 0, y=1, z= 0},
|
||||
}
|
||||
|
||||
local flip = {
|
||||
[0] = 2,
|
||||
[1] = 3,
|
||||
[2] = 0,
|
||||
[3] = 1,
|
||||
[4] = 5,
|
||||
[5] = 4,
|
||||
}
|
||||
|
||||
-- facedir = math.floor(dot / 4)
|
||||
-- y = (dot % 4) - 1
|
||||
|
||||
-- Create helper tables
|
||||
for facedir = 0,3 do
|
||||
for y = -1,1 do
|
||||
local dot = 1 + facedir * 4 + y
|
||||
local dir = vector.new(Facedir2Dir[facedir])
|
||||
dir.y = y
|
||||
Dot2Dir[dot] = dir
|
||||
Dir2Dot[P2H(dir)] = dot
|
||||
end
|
||||
end
|
||||
|
||||
local function dot2dir(dot) return vector.new(Dot2Dir[dot]) end
|
||||
local function facedir2dir(fd) return vector.new(Facedir2Dir[fd]) end
|
||||
|
||||
minecart.dot2dir = dot2dir
|
||||
minecart.facedir2dir = facedir2dir
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- waypoint metadata
|
||||
-------------------------------------------------------------------------------
|
||||
local function has_metadata(pos)
|
||||
return M(pos):contains("waypoints")
|
||||
end
|
||||
|
||||
local function get_metadata(pos)
|
||||
local hash = P2H(pos)
|
||||
if tWaypoints[hash] then
|
||||
return tWaypoints[hash]
|
||||
end
|
||||
local s = M(pos):get_string("waypoints")
|
||||
if s ~= "" then
|
||||
tWaypoints[hash] = minetest.deserialize(s)
|
||||
return tWaypoints[hash]
|
||||
end
|
||||
end
|
||||
|
||||
local function get_oldmetadata(meta)
|
||||
local s = meta:get_string("waypoints")
|
||||
if s ~= "" then
|
||||
return minetest.deserialize(s)
|
||||
end
|
||||
end
|
||||
|
||||
local function set_metadata(pos, t)
|
||||
local hash = P2H(pos)
|
||||
tWaypoints[hash] = t
|
||||
local s = minetest.serialize(t)
|
||||
M(pos):set_string("waypoints", s)
|
||||
-- visualization
|
||||
local name = get_node_lvm(pos).name
|
||||
if name == "carts:rail" then
|
||||
minetest.swap_node(pos, {name = "minecart:rail"})
|
||||
elseif name == "carts:powerrail" then
|
||||
minetest.swap_node(pos, {name = "minecart:powerrail"})
|
||||
end
|
||||
end
|
||||
|
||||
local function del_metadata(pos)
|
||||
local hash = P2H(pos)
|
||||
tWaypoints[hash] = nil
|
||||
local meta = M(pos)
|
||||
if meta:contains("waypoints") then
|
||||
meta:set_string("waypoints", "")
|
||||
-- visualization
|
||||
local name = get_node_lvm(pos).name
|
||||
if name == "minecart:rail" then
|
||||
minetest.swap_node(pos, {name = "carts:rail"})
|
||||
elseif name == "minecart:powerrail" then
|
||||
minetest.swap_node(pos, {name = "carts:powerrail"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- find_next_waypoint
|
||||
-------------------------------------------------------------------------------
|
||||
local function check_right(pos, facedir)
|
||||
local fdr = (facedir + 1) % 4 -- right
|
||||
local new_pos = vector.add(pos, facedir2dir(fdr))
|
||||
|
||||
local name = get_node_lvm(new_pos).name
|
||||
if tRailsExt[name] or tSigns[name] then
|
||||
return true
|
||||
end
|
||||
new_pos.y = new_pos.y - 1
|
||||
if tRailsExt[get_node_lvm(new_pos).name] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function check_left(pos, facedir)
|
||||
local fdl = (facedir + 3) % 4 -- left
|
||||
local new_pos = vector.add(pos, facedir2dir(fdl))
|
||||
|
||||
local name = get_node_lvm(new_pos).name
|
||||
if tRailsExt[name] or tSigns[name] then
|
||||
return true
|
||||
end
|
||||
new_pos.y = new_pos.y - 1
|
||||
if tRailsExt[get_node_lvm(new_pos).name] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function get_next_pos(pos, facedir, y)
|
||||
local new_pos = vector.add(pos, facedir2dir(facedir))
|
||||
new_pos.y = new_pos.y + y
|
||||
local name = get_node_lvm(new_pos).name
|
||||
return tRailsExt[name] ~= nil, new_pos, tRailsPower[name] or 0
|
||||
end
|
||||
|
||||
local function is_ramp(pos)
|
||||
return tRailsExt[get_node_lvm({x = pos.x, y = pos.y + 1, z = pos.z}).name] ~= nil
|
||||
end
|
||||
|
||||
-- Check also the next position to detect a ramp
|
||||
local function slope_detection(pos, facedir)
|
||||
local is_rail, new_pos = get_next_pos(pos, facedir, 0)
|
||||
if not is_rail then
|
||||
return is_ramp(new_pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function find_next_waypoint(pos, facedir, y)
|
||||
local cnt = 0
|
||||
local name = get_node_lvm(pos).name
|
||||
local speed = tRailsPower[name] or 0
|
||||
local is_rail, new_pos, _speed
|
||||
|
||||
while cnt < MAX_NODES do
|
||||
is_rail, new_pos, _speed = get_next_pos(pos, facedir, y)
|
||||
speed = speed + _speed
|
||||
if not is_rail then
|
||||
return pos, y == 0 and is_ramp(new_pos), speed
|
||||
end
|
||||
if y == 0 then -- no slope
|
||||
if check_right(new_pos, facedir) then
|
||||
return new_pos, slope_detection(new_pos, facedir), speed
|
||||
elseif check_left(new_pos, facedir) then
|
||||
return new_pos, slope_detection(new_pos, facedir), speed
|
||||
end
|
||||
end
|
||||
pos = new_pos
|
||||
cnt = cnt + 1
|
||||
end
|
||||
return new_pos, false, speed
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- find_all_next_waypoints
|
||||
-------------------------------------------------------------------------------
|
||||
local function check_front_up_down(pos, facedir)
|
||||
local new_pos = vector.add(pos, facedir2dir(facedir))
|
||||
|
||||
if tRailsExt[get_node_lvm(new_pos).name] then
|
||||
return 0
|
||||
end
|
||||
new_pos.y = new_pos.y - 1
|
||||
if tRailsExt[get_node_lvm(new_pos).name] then
|
||||
return -1
|
||||
end
|
||||
new_pos.y = new_pos.y + 2
|
||||
if tRailsExt[get_node_lvm(new_pos).name] then
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Search for rails in all 4 directions
|
||||
local function find_all_rails_nearby(pos)
|
||||
--print("find_all_rails_nearby")
|
||||
local tbl = {}
|
||||
for fd = 0, 3 do
|
||||
tbl[#tbl + 1] = check_front_up_down(pos, fd, true)
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- Recalc the value based on waypoint length and slope
|
||||
local function recalc_speed(num_pow_rails, pos1, pos2, y)
|
||||
local num_norm_rails = vector.distance(pos1, pos2) - num_pow_rails
|
||||
local ratio, speed
|
||||
|
||||
if y ~= 0 then
|
||||
num_norm_rails = math.floor(num_norm_rails / 1.41 + 0.5)
|
||||
end
|
||||
|
||||
if y ~= -1 then
|
||||
if num_pow_rails == 0 then
|
||||
return num_norm_rails * -SLOWDOWN
|
||||
else
|
||||
ratio = math.floor(num_norm_rails / num_pow_rails)
|
||||
ratio = minecart.range(ratio, 0, 11)
|
||||
end
|
||||
else
|
||||
ratio = 3 + num_norm_rails * SLOWDOWN + num_pow_rails
|
||||
end
|
||||
|
||||
if y == 1 then
|
||||
speed = 7 - ratio
|
||||
elseif y == -1 then
|
||||
speed = 15 - ratio
|
||||
else
|
||||
speed = 11 - ratio
|
||||
end
|
||||
|
||||
return minecart.range(speed, 0, 8)
|
||||
end
|
||||
|
||||
local function find_all_next_waypoints(pos)
|
||||
local wp = {}
|
||||
local dots = {}
|
||||
|
||||
for facedir = 0,3 do
|
||||
local y = check_front_up_down(pos, facedir)
|
||||
if y then
|
||||
local new_pos, is_ramp, speed = find_next_waypoint(pos, facedir, y)
|
||||
--print("find_all_next_waypoints", P2S(new_pos), is_ramp, speed)
|
||||
local dot = 1 + facedir * 4 + y
|
||||
speed = recalc_speed(speed, pos, new_pos, y) * 10
|
||||
wp[facedir] = {dot = dot, pos = new_pos, speed = speed, is_ramp = is_ramp}
|
||||
end
|
||||
end
|
||||
|
||||
return wp
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- get_waypoint
|
||||
-------------------------------------------------------------------------------
|
||||
-- If ramp, stop 0.5 nodes earlier or later
|
||||
local function ramp_correction(pos, wp, facedir)
|
||||
if wp.is_ramp or pos.y < wp.pos.y then -- ramp detection
|
||||
local dir = facedir2dir(facedir)
|
||||
local pos = wp.pos
|
||||
|
||||
wp.cart_pos = {
|
||||
x = pos.x - dir.x / 2,
|
||||
y = pos.y,
|
||||
z = pos.z - dir.z / 2}
|
||||
elseif pos.y > wp.pos.y then
|
||||
local dir = facedir2dir(facedir)
|
||||
local pos = wp.pos
|
||||
|
||||
wp.cart_pos = {
|
||||
x = pos.x + dir.x / 2,
|
||||
y = pos.y,
|
||||
z = pos.z + dir.z / 2}
|
||||
end
|
||||
return wp
|
||||
end
|
||||
|
||||
-- Returns waypoint and is_junction
|
||||
function minecart.get_waypoint(pos, facedir, ctrl, uturn)
|
||||
local t = get_metadata(pos)
|
||||
if not t then
|
||||
t = find_all_next_waypoints(pos)
|
||||
set_metadata(pos, t)
|
||||
end
|
||||
|
||||
local left = (facedir + 3) % 4
|
||||
local right = (facedir + 1) % 4
|
||||
local back = (facedir + 2) % 4
|
||||
|
||||
if ctrl.right and t[right] then return t[right], t[facedir] ~= nil or t[left] ~= nil end
|
||||
if ctrl.left and t[left] then return t[left] , t[facedir] ~= nil or t[right] ~= nil end
|
||||
|
||||
if t[facedir] then return ramp_correction(pos, t[facedir], facedir), false end
|
||||
if t[right] then return ramp_correction(pos, t[right], right), false end
|
||||
if t[left] then return ramp_correction(pos, t[left], left), false end
|
||||
|
||||
if uturn and t[back] then return t[back], false end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- delete waypoints
|
||||
-------------------------------------------------------------------------------
|
||||
local function delete_counterpart_metadata(pos, wp)
|
||||
for facedir = 0,3 do
|
||||
if wp[facedir] then
|
||||
del_metadata(wp[facedir].pos)
|
||||
end
|
||||
end
|
||||
del_metadata(pos)
|
||||
end
|
||||
|
||||
local function delete_next_metadata(pos, facedir, y)
|
||||
local cnt = 0
|
||||
while cnt <= MAX_NODES do
|
||||
local is_rail, new_pos = get_next_pos(pos, facedir, y)
|
||||
if not is_rail then
|
||||
return
|
||||
end
|
||||
|
||||
if has_metadata(new_pos) then
|
||||
del_metadata(new_pos)
|
||||
end
|
||||
|
||||
pos = new_pos
|
||||
cnt = cnt + 1
|
||||
end
|
||||
if has_metadata(pos) then
|
||||
del_metadata(pos)
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.delete_waypoint(pos)
|
||||
if has_metadata(pos) then
|
||||
local wp = get_metadata(pos)
|
||||
delete_counterpart_metadata(pos, wp)
|
||||
return
|
||||
end
|
||||
|
||||
for facedir = 0,3 do
|
||||
local y = check_front_up_down(pos, facedir)
|
||||
if y then
|
||||
local new_pos = vector.add(pos, facedir2dir(facedir))
|
||||
new_pos.y = new_pos.y + y
|
||||
if has_metadata(new_pos) then
|
||||
local wp = get_metadata(new_pos)
|
||||
delete_counterpart_metadata(new_pos, wp)
|
||||
else
|
||||
delete_next_metadata(pos, facedir, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
carts:register_rail("minecart:rail", {
|
||||
description = "Rail",
|
||||
tiles = {
|
||||
"carts_rail_straight.png^minecart_waypoint.png", "carts_rail_curved.png^minecart_waypoint.png",
|
||||
"carts_rail_t_junction.png^minecart_waypoint.png", "carts_rail_crossing.png^minecart_waypoint.png"
|
||||
},
|
||||
inventory_image = "carts_rail_straight.png",
|
||||
wield_image = "carts_rail_straight.png",
|
||||
groups = carts:get_rail_groups({not_in_creative_inventory = 1}),
|
||||
drop = "carts:rail",
|
||||
}, {})
|
||||
|
||||
carts:register_rail("minecart:powerrail", {
|
||||
description = "Powered Rail",
|
||||
tiles = {
|
||||
"carts_rail_straight_pwr.png^minecart_waypoint.png", "carts_rail_curved_pwr.png^minecart_waypoint.png",
|
||||
"carts_rail_t_junction_pwr.png^minecart_waypoint.png", "carts_rail_crossing_pwr.png^minecart_waypoint.png"
|
||||
},
|
||||
inventory_image = "carts_rail_straight.png",
|
||||
wield_image = "carts_rail_straight.png",
|
||||
groups = carts:get_rail_groups({not_in_creative_inventory = 1}),
|
||||
drop = "carts:powerrail",
|
||||
}, {})
|
||||
|
||||
for name,_ in pairs(tRails) do
|
||||
minetest.override_item(name, {
|
||||
after_destruct = minecart.delete_waypoint,
|
||||
after_place_node = minecart.delete_waypoint,
|
||||
})
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- API functions
|
||||
-------------------------------------------------------------------------------
|
||||
-- Return new cart pos and if an extra move cycle is needed
|
||||
function minecart.get_current_cart_pos_correction(curr_pos, curr_fd, curr_y, new_dot)
|
||||
if new_dot then
|
||||
local new_y = (new_dot % 4) - 1
|
||||
local new_fd = math.floor(new_dot / 4)
|
||||
|
||||
if curr_y == -1 or new_y == -1 then
|
||||
local new_fd = math.floor(new_dot / 4)
|
||||
local dir = facedir2dir(new_fd)
|
||||
return {
|
||||
x = curr_pos.x + dir.x / 2,
|
||||
y = curr_pos.y,
|
||||
z = curr_pos.z + dir.z / 2}, new_y == -1
|
||||
elseif curr_y == 1 and curr_fd ~= new_fd then
|
||||
local dir = facedir2dir(new_fd)
|
||||
return {
|
||||
x = curr_pos.x + dir.x / 2,
|
||||
y = curr_pos.y,
|
||||
z = curr_pos.z + dir.z / 2}, true
|
||||
elseif curr_y == 1 or new_y == 1 then
|
||||
local dir = facedir2dir(curr_fd)
|
||||
return {
|
||||
x = curr_pos.x - dir.x / 2,
|
||||
y = curr_pos.y,
|
||||
z = curr_pos.z - dir.z / 2}, false
|
||||
end
|
||||
end
|
||||
return curr_pos, false
|
||||
end
|
||||
|
||||
-- Called by carts, returns the speed value or nil
|
||||
function minecart.get_speedlimit(pos, facedir)
|
||||
local fd = (facedir + 1) % 4 -- right
|
||||
local new_pos = vector.add(pos, facedir2dir(fd))
|
||||
local node = get_node_lvm(new_pos)
|
||||
if tSigns[node.name] and node.param2 == facedir then
|
||||
return tSigns[node.name]
|
||||
end
|
||||
|
||||
fd = (facedir + 3) % 4 -- left
|
||||
new_pos = vector.add(pos, facedir2dir(fd))
|
||||
node = get_node_lvm(new_pos)
|
||||
if tSigns[node.name] and node.param2 == facedir then
|
||||
return tSigns[node.name]
|
||||
end
|
||||
end
|
||||
|
||||
-- Called by carts, to delete temporarily created waypoints
|
||||
function minecart.delete_cart_waypoint(pos)
|
||||
del_metadata(pos)
|
||||
end
|
||||
|
||||
-- Called by signs, to delete the rail waypoints nearby
|
||||
function minecart.delete_signs_waypoint(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local facedir = (node.param2 + 1) % 4 -- right
|
||||
local new_pos = vector.add(pos, facedir2dir(facedir))
|
||||
if tRailsExt[get_node_lvm(new_pos).name] then
|
||||
minecart.delete_waypoint(new_pos)
|
||||
end
|
||||
|
||||
facedir = (node.param2 + 3) % 4 -- left
|
||||
new_pos = vector.add(pos, facedir2dir(facedir))
|
||||
if tRailsExt[get_node_lvm(new_pos).name] then
|
||||
minecart.delete_waypoint(new_pos)
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.is_rail(pos)
|
||||
return tRails[get_node_lvm(pos).name] ~= nil
|
||||
end
|
||||
|
||||
-- To register node cart names
|
||||
function minecart.add_raillike_nodes(name)
|
||||
tRailsExt[name] = true
|
||||
lRailsExt[#lRailsExt + 1] = name
|
||||
end
|
||||
|
||||
--minetest.register_lbm({
|
||||
-- label = "Delete waypoints",
|
||||
-- name = "minecart:del_meta",
|
||||
-- nodenames = {"carts:brakerail"},
|
||||
-- run_at_every_load = true,
|
||||
-- action = function(pos, node)
|
||||
-- del_metadata(pos)
|
||||
-- end,
|
||||
--})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
@ -14,76 +14,172 @@
|
||||
local M = minetest.get_meta
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local P2H = minetest.hash_node_position
|
||||
local H2P = minetest.get_position_from_hash
|
||||
local S = minecart.S
|
||||
local MP = minetest.get_modpath("minecart")
|
||||
local lib = dofile(MP.."/cart_lib3.lua")
|
||||
|
||||
local CartsOnRail = minecart.CartsOnRail -- from storage.lua
|
||||
local get_route = minecart.get_route -- from storage.lua
|
||||
local function dashboard_destroy(self)
|
||||
if self.driver and self.hud_id then
|
||||
local player = minetest.get_player_by_name(self.driver)
|
||||
if player then
|
||||
player:hud_remove(self.hud_id)
|
||||
self.hud_id = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function dashboard_create(self)
|
||||
if self.driver then
|
||||
local player = minetest.get_player_by_name(self.driver)
|
||||
if player then
|
||||
dashboard_destroy(self)
|
||||
self.hud_id = player:hud_add({
|
||||
name = "minecart",
|
||||
hud_elem_type = "text",
|
||||
position = {x = 0.4, y = 0.25},
|
||||
scale = {x=100, y=100},
|
||||
text = "Recording:",
|
||||
number = 0xFFFFFF,
|
||||
size = {x = 1},
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function dashboard_update(self)
|
||||
if self.driver and self.hud_id then
|
||||
local player = minetest.get_player_by_name(self.driver)
|
||||
if player then
|
||||
local time = self.runtime or 0
|
||||
local dir = (self.ctrl and self.ctrl.left and S("left")) or
|
||||
(self.ctrl and self.ctrl.right and S("right")) or S("straight")
|
||||
local speed = math.floor((self.curr_speed or 0) + 0.5)
|
||||
local s = string.format(S("Recording") ..
|
||||
" | " .. S("speed") ..
|
||||
": %.1f | " .. S("next junction") ..
|
||||
": %-8s | " .. S("Travel time") .. ": %.1f s",
|
||||
speed, dir, time)
|
||||
player:hud_change(self.hud_id, "text", s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function check_waypoint(self, pos)
|
||||
-- If next waypoint already reached but not handled
|
||||
-- determine next waypoint
|
||||
if vector.equals(pos, self.waypoint.pos) then
|
||||
local rot = self.object:get_rotation()
|
||||
local dir = minetest.yaw_to_dir(rot.y)
|
||||
dir.y = math.floor((rot.x / (math.pi/4)) + 0.5)
|
||||
local facedir = minetest.dir_to_facedir(dir)
|
||||
local waypoint = minecart.get_waypoint(pos, facedir, self.ctrl or {}, false)
|
||||
if waypoint then
|
||||
return waypoint.pos
|
||||
end
|
||||
end
|
||||
return self.waypoint.pos
|
||||
end
|
||||
|
||||
--
|
||||
-- Route recording
|
||||
--
|
||||
function minecart.start_recording(self, pos)
|
||||
self.start_key = lib.get_route_key(pos, self.driver)
|
||||
if self.start_key then
|
||||
self.waypoints = {}
|
||||
--print("start_recording")
|
||||
if self.driver then
|
||||
self.start_pos = minecart.get_buffer_pos(pos, self.driver)
|
||||
if self.start_pos then
|
||||
self.checkpoints = {} -- {cart_pos, next_waypoint_pos, speed, dot}
|
||||
self.junctions = {}
|
||||
self.recording = true
|
||||
self.next_time = minetest.get_us_time() + 1000000
|
||||
minetest.chat_send_player(self.driver, S("[minecart] Start route recording!"))
|
||||
self.is_recording = true
|
||||
self.rec_time = self.timebase
|
||||
self.hud_time = self.timebase
|
||||
self.runtime = 0
|
||||
self.num_sections = 0
|
||||
self.sum_speed = 0
|
||||
self.ctrl = {}
|
||||
dashboard_create(self)
|
||||
dashboard_update(self, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.store_next_waypoint(self, pos, vel)
|
||||
if self.start_key and self.recording and self.driver and
|
||||
self.next_time < minetest.get_us_time() then
|
||||
self.next_time = minetest.get_us_time() + 1000000
|
||||
self.waypoints[#self.waypoints+1] = {P2S(vector.round(pos)), P2S(vector.round(vel))}
|
||||
elseif self.recording and not self.driver then
|
||||
self.recording = false
|
||||
self.waypoints = nil
|
||||
self.junctions = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- destination reached(speed == 0)
|
||||
function minecart.stop_recording(self, pos, vel, puncher)
|
||||
local dest_pos = lib.get_route_key(pos, self.driver)
|
||||
if dest_pos then
|
||||
if self.start_key and self.start_key ~= dest_pos then
|
||||
function minecart.stop_recording(self, pos)
|
||||
--print("stop_recording")
|
||||
if self.driver and self.is_recording then
|
||||
local dest_pos = minecart.get_buffer_pos(pos, self.driver)
|
||||
local player = minetest.get_player_by_name(self.driver)
|
||||
if dest_pos and player and #self.checkpoints > 3 then
|
||||
-- Remove last checkpoint, because it is potentially too close to the dest_pos
|
||||
table.remove(self.checkpoints)
|
||||
if self.start_pos then
|
||||
local route = {
|
||||
waypoints = self.waypoints,
|
||||
dest_pos = dest_pos,
|
||||
checkpoints = self.checkpoints,
|
||||
junctions = self.junctions,
|
||||
}
|
||||
minecart.store_route(self.start_key, route)
|
||||
minecart.store_route(self.start_pos, route)
|
||||
minetest.chat_send_player(self.driver, S("[minecart] Route stored!"))
|
||||
else
|
||||
minetest.chat_send_player(self.driver, S("[minecart] Recording canceled!"))
|
||||
local speed = self.sum_speed / #self.checkpoints
|
||||
local length = speed * self.runtime
|
||||
local fmt = S("[minecart] Speed = %u m/s, Time = %u s, Route length = %u m")
|
||||
minetest.chat_send_player(self.driver, string.format(fmt, speed, self.runtime, length))
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(self.driver, S("[minecart] Recording canceled!"))
|
||||
end
|
||||
self.recording = false
|
||||
dashboard_destroy(self)
|
||||
end
|
||||
self.is_recording = false
|
||||
self.checkpoints = nil
|
||||
self.waypoints = nil
|
||||
self.junctions = nil
|
||||
end
|
||||
|
||||
function minecart.set_junction(self, pos, dir, switch_keys)
|
||||
if self.junctions then
|
||||
self.junctions[P2S(vector.round(pos))] = {dir, switch_keys}
|
||||
function minecart.recording_waypoints(self)
|
||||
local pos = vector.round(self.object:get_pos())
|
||||
-- hier müsste überprüfung dest_pos rein
|
||||
self.sum_speed = self.sum_speed + self.curr_speed
|
||||
local wp_pos = check_waypoint(self, pos)
|
||||
self.checkpoints[#self.checkpoints+1] = {
|
||||
-- cart_pos, next_waypoint_pos, speed, dot
|
||||
P2H(pos),
|
||||
P2H(wp_pos),
|
||||
math.floor(self.curr_speed + 0.5),
|
||||
self.waypoint.dot
|
||||
}
|
||||
end
|
||||
|
||||
function minecart.recording_junctions(self)
|
||||
local player = minetest.get_player_by_name(self.driver)
|
||||
if player then
|
||||
local ctrl = player:get_player_control()
|
||||
if ctrl.left then
|
||||
self.ctrl = {left = true}
|
||||
elseif ctrl.right then
|
||||
self.ctrl = {right = true}
|
||||
elseif ctrl.up or ctrl.down then
|
||||
self.ctrl = nil
|
||||
end
|
||||
end
|
||||
if self.hud_time <= self.timebase then
|
||||
dashboard_update(self)
|
||||
self.hud_time = self.timebase + 0.5
|
||||
self.runtime = self.runtime + 0.5
|
||||
end
|
||||
end
|
||||
|
||||
function minecart.get_junction(self, pos, dir)
|
||||
local junctions = CartsOnRail[self.myID] and CartsOnRail[self.myID].junctions
|
||||
if junctions then
|
||||
local data = junctions[P2S(vector.round(pos))]
|
||||
if data then
|
||||
return data[1], data[2]
|
||||
function minecart.set_junctions(self, wayp_pos)
|
||||
if self.ctrl then
|
||||
self.junctions[P2H(wayp_pos)] = self.ctrl
|
||||
end
|
||||
end
|
||||
return dir
|
||||
end
|
||||
|
||||
function minecart.player_ctrl(self)
|
||||
local player = minetest.get_player_by_name(self.driver)
|
||||
if player then
|
||||
local ctrl = player:get_player_control()
|
||||
if ctrl.left then
|
||||
self.ctrl = {left = true}
|
||||
elseif ctrl.right then
|
||||
self.ctrl = {right = true}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
107
minecart/signs.lua
Normal file
@ -0,0 +1,107 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg 4
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
local S = minecart.S
|
||||
|
||||
|
||||
local function register_sign(def)
|
||||
minetest.register_node("minecart:"..def.name, {
|
||||
description = def.description,
|
||||
inventory_image = def.image,
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -1/16, -8/16, -1/16, 1/16,-2/16, 1/16},
|
||||
{ -5/16, -2/16, -1/32, 5/16, 8/16, 1/32},
|
||||
},
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
tiles = {
|
||||
"default_steel_block.png",
|
||||
"default_steel_block.png",
|
||||
"default_steel_block.png",
|
||||
"default_steel_block.png",
|
||||
"default_steel_block.png",
|
||||
"default_steel_block.png^"..def.image,
|
||||
},
|
||||
|
||||
after_place_node = minecart.delete_signs_waypoint,
|
||||
preserve_metadata = minecart.delete_signs_waypoint,
|
||||
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = minecart.CLIP,
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2, minecart_sign = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
end
|
||||
|
||||
register_sign({
|
||||
name = "speed1",
|
||||
description = S('Speed "1"'),
|
||||
image = "minecart_sign1.png",
|
||||
})
|
||||
|
||||
register_sign({
|
||||
name = "speed2",
|
||||
description = S('Speed "2"'),
|
||||
image = "minecart_sign2.png",
|
||||
})
|
||||
|
||||
register_sign({
|
||||
name = "speed4",
|
||||
description = S('Speed "4"'),
|
||||
image = "minecart_sign4.png",
|
||||
})
|
||||
|
||||
register_sign({
|
||||
name = "speed8",
|
||||
description = S('No speed limit'),
|
||||
image = "minecart_sign8.png",
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "minecart:speed8 8",
|
||||
recipe = {
|
||||
{"default:tin_ingot", "dye:red", "default:tin_ingot"},
|
||||
{"", "default:steel_ingot", ""},
|
||||
{"", "default:steel_ingot", ""}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "minecart:speed4",
|
||||
recipe = {"minecart:speed8"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "minecart:speed2",
|
||||
recipe = {"minecart:speed4"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "minecart:speed1",
|
||||
recipe = {"minecart:speed2"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "minecart:speed8",
|
||||
recipe = {"minecart:speed1"}
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
@ -14,24 +14,75 @@
|
||||
local M = minetest.get_meta
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local P2H = minetest.hash_node_position
|
||||
local H2P = minetest.get_position_from_hash
|
||||
local S = minecart.S
|
||||
|
||||
local storage = minetest.get_mod_storage()
|
||||
|
||||
local function place_carts(t)
|
||||
local Carts = {
|
||||
["minecart:cart"] = "minecart:cart",
|
||||
["techage:tank_cart_entity"] = "techage:tank_cart",
|
||||
["techage:chest_cart_entity"] = "techage:chest_cart",
|
||||
}
|
||||
for id, item in pairs(t) do
|
||||
local pos = vector.round((item.start_pos or item.last_pos))
|
||||
local name = Carts[item.entity_name] or "minecart:cart"
|
||||
--print(P2S(pos), name, item.owner, item.userID)
|
||||
if minetest.registered_nodes[name] then
|
||||
minecart.add_nodecart(pos, name, 0, {}, item.owner or "", item.userID or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Store data of running carts
|
||||
-------------------------------------------------------------------------------
|
||||
minecart.CartsOnRail = {}
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
for key,val in pairs(minetest.deserialize(storage:get_string("CartsOnRail")) or {}) do
|
||||
-- use invalid keys to force the cart spawning
|
||||
minecart.CartsOnRail[-key] = val
|
||||
local version = storage:get_int("version")
|
||||
if version < 2 then
|
||||
local t = minetest.deserialize(storage:get_string("CartsOnRail")) or {}
|
||||
minetest.after(5, place_carts, t)
|
||||
storage:set_int("version", 2)
|
||||
else
|
||||
local t = minetest.deserialize(storage:get_string("CartsOnRail")) or {}
|
||||
for owner, carts in pairs(t) do
|
||||
minecart.CartsOnRail[owner] = {}
|
||||
for userID, cart in pairs(carts) do
|
||||
print("reload cart", owner, userID, cart.objID)
|
||||
minecart.CartsOnRail[owner][userID] = cart
|
||||
-- mark all entity carts as zombified
|
||||
if cart.objID and cart.objID ~= 0 then
|
||||
cart.objID = -1
|
||||
minecart.push(1, cart)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.after(10, function()
|
||||
for owner, carts in pairs(minecart.CartsOnRail) do
|
||||
for userID, cart in pairs(carts) do
|
||||
-- Remove node carts that are not available anymore
|
||||
if cart.objID == 0 or not cart.objID then
|
||||
local node = minecart.get_node_lvm(cart.pos)
|
||||
if not minecart.tNodeNames[node.name] then
|
||||
-- Mark as "to be deleted"
|
||||
print("Node cart deleted", owner, userID)
|
||||
minecart.CartsOnRail[owner][userID] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
storage:set_string("CartsOnRail", minetest.serialize(minecart.CartsOnRail))
|
||||
print("minecart shutdown finished!!!")
|
||||
end)
|
||||
|
||||
function minecart.store_carts()
|
||||
@ -39,69 +90,31 @@ function minecart.store_carts()
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Store routes
|
||||
-- Store routes (in buffers)
|
||||
-------------------------------------------------------------------------------
|
||||
-- All positions as "pos_to_string" string
|
||||
--Routes = {
|
||||
-- start_pos = {
|
||||
-- waypoints = {{spos, svel}, {spos, svel}, ...},
|
||||
-- dest_pos = spos,
|
||||
-- junctions = {
|
||||
-- {spos = num},
|
||||
-- {spos = num},
|
||||
-- },
|
||||
-- },
|
||||
-- start_pos = {...},
|
||||
--}
|
||||
local Routes = {}
|
||||
local NEW_ROUTE = {waypoints = {}, junctions = {}}
|
||||
|
||||
function minecart.store_route(key, route)
|
||||
if key and route then
|
||||
Routes[key] = route
|
||||
local meta = M(S2P(key))
|
||||
if meta then
|
||||
meta:set_string("route", minetest.serialize(route))
|
||||
function minecart.store_route(pos, route)
|
||||
if pos and route then
|
||||
M(pos):set_string("route", minetest.serialize(route))
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function minecart.get_route(key)
|
||||
if not Routes[key] then
|
||||
local s = M(S2P(key)):get_string("route")
|
||||
function minecart.get_route(pos)
|
||||
if pos then
|
||||
local s = M(pos):get_string("route")
|
||||
if s ~= "" then
|
||||
Routes[key] = minetest.deserialize(s) or NEW_ROUTE
|
||||
else
|
||||
Routes[key] = NEW_ROUTE
|
||||
end
|
||||
end
|
||||
return Routes[key]
|
||||
end
|
||||
|
||||
function minecart.del_route(key)
|
||||
Routes[key] = nil -- remove from memory
|
||||
M(S2P(key)):set_string("route", "") -- and as metadata
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Convert data to v2
|
||||
-------------------------------------------------------------------------------
|
||||
minetest.after(5, function()
|
||||
local tbl = storage:to_table()
|
||||
for key,s in pairs(tbl.fields) do
|
||||
if key ~= "CartsOnRail" then
|
||||
local route = minetest.deserialize(s)
|
||||
if route.waypoints and route.junctions then
|
||||
if minecart.store_route(key, route) then
|
||||
storage:set_string(key, "")
|
||||
if route.waypoints then
|
||||
M(pos):set_string("route", "")
|
||||
M(pos):set_int("time", 0)
|
||||
return
|
||||
end
|
||||
else
|
||||
storage:set_string(key, "")
|
||||
return minetest.deserialize(s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
function minecart.del_route(pos)
|
||||
M(pos):set_string("route", "")
|
||||
end
|
||||
|
90
minecart/terminal.lua
Normal file
@ -0,0 +1,90 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = minecart.S
|
||||
|
||||
local function is_player_nearby(pos)
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos, 6)) do
|
||||
if object:is_player() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function formspec(pos, text)
|
||||
text = minetest.formspec_escape(text)
|
||||
text = text:gsub("\n", ",")
|
||||
|
||||
return "size[11,9]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"box[0,-0.1;10.8,0.5;#c6e8ff]"..
|
||||
"label[4.5,-0.1;"..minetest.colorize( "#000000", S("Cart List")).."]"..
|
||||
"style_type[table,field;font=mono]"..
|
||||
"table[0,0.5;10.8,8.6;output;"..text..";200]"
|
||||
end
|
||||
|
||||
minetest.register_node("minecart:terminal", {
|
||||
description = S("Cart Terminal"),
|
||||
inventory_image = "minecart_terminal_front.png",
|
||||
tiles = {
|
||||
"minecart_terminal_top.png",
|
||||
"minecart_terminal_top.png",
|
||||
"minecart_terminal_side.png",
|
||||
"minecart_terminal_side.png",
|
||||
"minecart_terminal_back.png",
|
||||
"minecart_terminal_front.png",
|
||||
},
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -8/16, -8/16, 0/16, 8/16, 8/16, 8/16},
|
||||
},
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
meta:set_string("formspec", formspec(pos, ""))
|
||||
minetest.get_node_timer(pos):start(2)
|
||||
end,
|
||||
|
||||
on_timer = function(pos, elapsed)
|
||||
if is_player_nearby(pos) then
|
||||
local text = minecart.get_cart_list(pos, M(pos):get_string("owner"))
|
||||
M(pos):set_string("formspec", formspec(pos, text))
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
paramtype2 = "facedir",
|
||||
paramtype = "light",
|
||||
use_texture_alpha = minecart.CLIP,
|
||||
on_rotate = screwdriver.disallow,
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 2, level = 2},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "minecart:terminal",
|
||||
recipe = {
|
||||
{"", "default:obsidian_glass", "default:steel_ingot"},
|
||||
{"", "default:obsidian_glass", "default:copper_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
},
|
||||
})
|
BIN
minecart/textures/minecart_appl_cart_top.png
Normal file
After Width: | Height: | Size: 108 B |
Before Width: | Height: | Size: 159 B After Width: | Height: | Size: 138 B |
Before Width: | Height: | Size: 169 B After Width: | Height: | Size: 148 B |
Before Width: | Height: | Size: 203 B After Width: | Height: | Size: 182 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 572 B |
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 222 B |
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 263 B After Width: | Height: | Size: 201 B |
BIN
minecart/textures/minecart_marker_cube.png
Normal file
After Width: | Height: | Size: 195 B |
Before Width: | Height: | Size: 266 B After Width: | Height: | Size: 128 B |
BIN
minecart/textures/minecart_pusher.png
Normal file
After Width: | Height: | Size: 111 B |
BIN
minecart/textures/minecart_pusher_top.png
Normal file
After Width: | Height: | Size: 126 B |
BIN
minecart/textures/minecart_sign1.png
Normal file
After Width: | Height: | Size: 141 B |
BIN
minecart/textures/minecart_sign2.png
Normal file
After Width: | Height: | Size: 154 B |
BIN
minecart/textures/minecart_sign4.png
Normal file
After Width: | Height: | Size: 151 B |
BIN
minecart/textures/minecart_sign8.png
Normal file
After Width: | Height: | Size: 174 B |
BIN
minecart/textures/minecart_terminal_back.png
Normal file
After Width: | Height: | Size: 123 B |
BIN
minecart/textures/minecart_terminal_front.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
minecart/textures/minecart_terminal_side.png
Normal file
After Width: | Height: | Size: 166 B |
BIN
minecart/textures/minecart_terminal_top.png
Normal file
After Width: | Height: | Size: 126 B |
BIN
minecart/textures/minecart_tool.png
Normal file
After Width: | Height: | Size: 202 B |
BIN
minecart/textures/minecart_waypoint.png
Normal file
After Width: | Height: | Size: 111 B |
@ -1,16 +0,0 @@
|
||||
import os, fnmatch
|
||||
|
||||
|
||||
print ">>> Convert"
|
||||
for filename in os.listdir("./"):
|
||||
if fnmatch.fnmatch(filename, "*.png"):
|
||||
print(filename)
|
||||
os.system("pngquant --skip-if-larger --quality=8-32 --output ./%s.new ./%s" % (filename, filename))
|
||||
|
||||
print "\n>>> Copy"
|
||||
for filename in os.listdir("./"):
|
||||
if fnmatch.fnmatch(filename, "*.new"):
|
||||
print(filename)
|
||||
os.remove("./" + filename[:-4])
|
||||
os.rename("./" + filename, "./" + filename[:-4])
|
||||
|
2
minecart/textures/shrink.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
pngquant --skip-if-larger --quality=80 --strip *.png --ext .png --force
|
101
minecart/tool.lua
Normal file
@ -0,0 +1,101 @@
|
||||
--[[
|
||||
|
||||
Minecart
|
||||
========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
MIT
|
||||
See license.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = minecart.S
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local P2H = minetest.hash_node_position
|
||||
|
||||
local sDir = {[0] = "north", "east", "south", "west"}
|
||||
|
||||
local function DOTS(dots)
|
||||
if dots then
|
||||
return table.concat(dots, ", ")
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
local old_pos
|
||||
|
||||
local function test_get_route(pos, node, player)
|
||||
local yaw = player:get_look_horizontal()
|
||||
local dir = minetest.yaw_to_dir(yaw)
|
||||
local facedir = minetest.dir_to_facedir(dir)
|
||||
local route = minecart.get_waypoint(pos, facedir, {})
|
||||
if route then
|
||||
-- print(dump(route))
|
||||
minecart.set_marker(route.pos, "pos", 0.3, 10)
|
||||
if route.cart_pos then
|
||||
minecart.set_marker(route.cart_pos, "cart", 0.3, 10)
|
||||
end
|
||||
|
||||
-- determine some kind of current y
|
||||
old_pos = old_pos or pos
|
||||
local curr_y = pos.y > old_pos.y and 1 or pos.y < old_pos.y and -1 or 0
|
||||
|
||||
local cart_pos, extra_cycle = minecart.get_current_cart_pos_correction(pos, facedir, curr_y, route.dot)
|
||||
minecart.set_marker(cart_pos, "curr", 0.3, 10)
|
||||
old_pos = pos
|
||||
print(string.format("Route: dist = %u, dot = %u, speed = %d, extra cycle = %s",
|
||||
vector.distance(pos, route.pos), route.dot, route.speed or 0, extra_cycle))
|
||||
end
|
||||
end
|
||||
|
||||
local function test_get_connections(pos, node, player, ctrl)
|
||||
local wp = minecart.get_waypoints(pos)
|
||||
for i = 0,3 do
|
||||
if wp[i] then
|
||||
local dir = minecart.Dot2Dir[ wp[i].dot]
|
||||
print(sDir[i], vector.distance(pos, wp[i].pos), dir.y)
|
||||
end
|
||||
end
|
||||
print(dump(M(pos):to_table()))
|
||||
end
|
||||
|
||||
local function click_left(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.under
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == "carts:rail" or node.name == "carts:powerrail" then
|
||||
test_get_route(pos, node, placer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function click_right(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.under
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == "carts:rail" or node.name == "carts:powerrail" then
|
||||
test_get_connections(pos, node, placer)
|
||||
elseif node.name == "minecart:buffer" then
|
||||
local route = minecart.get_route(P2S(pos))
|
||||
print(dump(route))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("minecart:tool", {
|
||||
description = "Tool",
|
||||
inventory_image = "minecart_tool.png",
|
||||
wield_image = "minecart_tool.png",
|
||||
liquids_pointable = true,
|
||||
use_texture_alpha = true,
|
||||
groups = {cracky=1, book=1},
|
||||
on_use = click_left,
|
||||
on_place = click_right,
|
||||
node_placement_prediction = "",
|
||||
stack_max = 1,
|
||||
})
|
@ -96,6 +96,7 @@ For all Inventory commands applies: If the inventory stack specified by <slot> i
|
||||
pattern - save the blocks behind the shield (up to 5x3x3) as template
|
||||
copy <size> - make a copy of "pattern". Size is e.g. 3x3 (see ingame help)
|
||||
punch_cart - Punch a rail cart to start it
|
||||
print <text> - Output chat message for debug purposes
|
||||
|
||||
#### Flow control commands
|
||||
|
||||
@ -169,4 +170,7 @@ optional: farming redo, node_io, doc, techage, minecart
|
||||
- 2020-06-21 v1.03 * Interpreter bugfixes, node and crop sensors changed
|
||||
- 2020-10-01 v1.04 * Many improvements and bugfixes (Thanks to Thomas-S)
|
||||
- 2021-01-30 v1.05 * Many improvements and bugfixes
|
||||
- 2021-03-14 v1.06 * Switch translation from intllib to minetest.translator
|
||||
- 2021-04-24 v1.07 * Adapted to minecart v2.0
|
||||
- 2021-05-04 v1.08 * Add print command, improve error msg
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,13 +13,10 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -112,16 +109,33 @@ local function preassigned_slots(pos)
|
||||
return table.concat(tbl, "")
|
||||
end
|
||||
|
||||
local function status(mem)
|
||||
if mem.error then
|
||||
if type(mem.error) == "string" then
|
||||
return mem.error
|
||||
else
|
||||
return dump(mem.error)
|
||||
end
|
||||
end
|
||||
if mem.running then
|
||||
return S("running")
|
||||
end
|
||||
if mem.charging then
|
||||
return S("charging")
|
||||
end
|
||||
return S("stopped")
|
||||
end
|
||||
|
||||
local function formspec(pos, mem)
|
||||
mem.running = mem.running or false
|
||||
local cmnd = mem.running and "stop;"..I("Off") or "start;"..I("On")
|
||||
local cmnd = mem.running and "stop;"..S("Off") or "start;"..S("On")
|
||||
local bot = not mem.running and "image[0.6,0;1,1;signs_bot_bot_inv.png]" or ""
|
||||
local current_capa = mem.capa or (signs_bot.MAX_CAPA * 0.9)
|
||||
return "size[9,7.6]"..
|
||||
return "size[9,8.2]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[2.1,0;"..I("Signs").."]label[5.3,0;"..I("Other items").."]"..
|
||||
"label[2.1,0;"..S("Signs").."]label[5.3,0;"..S("Other items").."]"..
|
||||
"image[0.6,0;1,1;signs_bot_form_mask.png]"..
|
||||
bot..
|
||||
preassigned_slots(pos)..
|
||||
@ -132,24 +146,27 @@ local function formspec(pos, mem)
|
||||
"label[5.3,0.5;1]label[6.3,0.5;2]label[7.3,0.5;3]label[8.3,0.5;4]"..
|
||||
"list[context;main;5,1;4,2;]"..
|
||||
"label[5.3,3;5]label[6.3,3;6]label[7.3,3;7]label[8.3,3;8]"..
|
||||
"button[0.2,1;1.5,1;config;"..I("Config").."]"..
|
||||
"button[0.2,1;1.5,1;config;"..S("Config").."]"..
|
||||
"button[0.2,2;1.5,1;"..cmnd.."]"..
|
||||
"list[current_player;main;0.5,3.8;8,4;]"..
|
||||
"label[1,3.6;"..status(mem).."]"..
|
||||
"list[current_player;main;0.5,4.4;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function formspec_cfg(pos, mem)
|
||||
return "size[9,7.6]"..
|
||||
local function formspec_cfg()
|
||||
return "size[9,8.2]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[5.3,0;"..I("Preassign slots items").."]"..
|
||||
"label[5.3,0;"..S("Preassign slots items").."]"..
|
||||
"label[5.3,0.5;1]label[6.3,0.5;2]label[7.3,0.5;3]label[8.3,0.5;4]"..
|
||||
"list[context;filter;5,1;4,2;]"..
|
||||
"label[5.3,3;5]label[6.3,3;6]label[7.3,3;7]label[8.3,3;8]"..
|
||||
"button[0.2,1;1.5,1;back;"..I("Back").."]"..
|
||||
"list[current_player;main;0.5,3.8;8,4;]"
|
||||
"button[0.2,1;1.5,1;back;"..S("Back").."]"..
|
||||
"list[current_player;main;0.5,4.4;8,4;]"..
|
||||
"listring[context;filter]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function get_capa(itemstack)
|
||||
@ -166,7 +183,7 @@ local function set_capa(pos, oldnode, digger, capa)
|
||||
capa = techage.power.percent(signs_bot.MAX_CAPA, capa)
|
||||
capa = (math.floor((capa or 0) / 5)) * 5
|
||||
meta:set_int("capa", capa)
|
||||
local text = I("Robot Box ").." ("..capa.." %)"
|
||||
local text = S("Robot Box").." ("..capa.." %)"
|
||||
meta:set_string("description", text)
|
||||
local inv = minetest.get_inventory({type="player", name=digger:get_player_name()})
|
||||
local left_over = inv:add_item("main", node)
|
||||
@ -179,7 +196,7 @@ function signs_bot.infotext(pos, state)
|
||||
local meta = M(pos)
|
||||
local number = meta:get_string("number")
|
||||
state = state or "<unknown>"
|
||||
meta:set_string("infotext", I("Robot Box ")..number..": "..state)
|
||||
meta:set_string("infotext", S("Robot Box").." "..number..": "..state)
|
||||
end
|
||||
|
||||
local function reset_robot(pos, mem)
|
||||
@ -205,7 +222,7 @@ function signs_bot.start_robot(base_pos)
|
||||
mem.capa = nil
|
||||
end
|
||||
meta:set_string("formspec", formspec(base_pos, mem))
|
||||
signs_bot.infotext(base_pos, I("running"))
|
||||
signs_bot.infotext(base_pos, S("running"))
|
||||
reset_robot(base_pos, mem)
|
||||
minetest.get_node_timer(base_pos):start(CYCLE_TIME)
|
||||
return true
|
||||
@ -224,9 +241,9 @@ function signs_bot.stop_robot(base_pos, mem)
|
||||
mem.charging = false
|
||||
end
|
||||
if mem.power_available then
|
||||
signs_bot.infotext(base_pos, I("charging"))
|
||||
signs_bot.infotext(base_pos, S("charging"))
|
||||
else
|
||||
signs_bot.infotext(base_pos, I("stopped"))
|
||||
signs_bot.infotext(base_pos, S("stopped"))
|
||||
end
|
||||
meta:set_string("formspec", formspec(base_pos, mem))
|
||||
signs_bot.remove_robot(mem)
|
||||
@ -285,7 +302,7 @@ local function on_receive_fields(pos, formname, fields, player)
|
||||
if fields.update then
|
||||
meta:set_string("formspec", formspec(pos, mem))
|
||||
elseif fields.config then
|
||||
meta:set_string("formspec", formspec_cfg(pos, mem))
|
||||
meta:set_string("formspec", formspec_cfg())
|
||||
elseif fields.back then
|
||||
meta:set_string("formspec", formspec(pos, mem))
|
||||
elseif fields.start then
|
||||
@ -373,6 +390,7 @@ end
|
||||
local function on_power(pos)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
mem.power_available = true
|
||||
mem.charging = true
|
||||
signs_bot.infotext(pos, S("charging"))
|
||||
end
|
||||
|
||||
@ -383,7 +401,7 @@ local function on_nopower(pos)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:box", {
|
||||
description = I("Signs Bot Box"),
|
||||
description = S("Signs Bot Box"),
|
||||
stack_max = 1,
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
@ -417,7 +435,7 @@ minetest.register_node("signs_bot:box", {
|
||||
meta:set_string("formspec", formspec(pos, mem))
|
||||
meta:set_string("signs_bot_cmnd", "turn_off")
|
||||
meta:set_int("err_code", 0)
|
||||
signs_bot.infotext(pos, I("stopped"))
|
||||
signs_bot.infotext(pos, S("stopped"))
|
||||
if minetest.global_exists("techage") then
|
||||
techage.ElectricCable:after_place_node(pos)
|
||||
mem.capa = get_capa(itemstack)
|
||||
@ -473,12 +491,12 @@ minetest.register_node("signs_bot:box", {
|
||||
on_power = function(pos)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
mem.power_available = true
|
||||
signs_bot.infotext(pos, I("charging"))
|
||||
signs_bot.infotext(pos, S("charging"))
|
||||
end,
|
||||
on_nopower = function(pos)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
mem.power_available = false
|
||||
signs_bot.infotext(pos, I("no power"))
|
||||
signs_bot.infotext(pos, S("no power"))
|
||||
end,
|
||||
nominal = PWR_NEEDED,
|
||||
}
|
||||
@ -516,22 +534,22 @@ end
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "box", {
|
||||
name = I("Signs Bot Box"),
|
||||
name = S("Signs Bot Box"),
|
||||
data = {
|
||||
item = "signs_bot:box",
|
||||
text = table.concat({
|
||||
I("The Box is the housing of the bot."),
|
||||
I("Place the box and start the bot by means of the 'On' button."),
|
||||
I("If the mod techage is installed, the bot needs electrical power."),
|
||||
S("The Box is the housing of the bot."),
|
||||
S("Place the box and start the bot by means of the 'On' button."),
|
||||
S("If the mod techage is installed, the bot needs electrical power."),
|
||||
"",
|
||||
I("The bot leaves the box on the right side."),
|
||||
I("It will not start, if this position is blocked."),
|
||||
S("The bot leaves the box on the right side."),
|
||||
S("It will not start, if this position is blocked."),
|
||||
"",
|
||||
I("To stop and remove the bot, press the 'Off' button."),
|
||||
S("To stop and remove the bot, press the 'Off' button."),
|
||||
"",
|
||||
I("The box inventory simulates the inventory of the bot."),
|
||||
I("You will not be able to access the inventory, if the bot is running."),
|
||||
I("The bot can carry up to 8 stacks and 6 signs with it."),
|
||||
S("The box inventory simulates the inventory of the bot."),
|
||||
S("You will not be able to access the inventory, if the bot is running."),
|
||||
S("The bot can carry up to 8 stacks and 6 signs with it."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPLv3
|
||||
See LICENSE.txt for more information
|
||||
@ -12,14 +12,8 @@
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local function formspec(cmnd)
|
||||
cmnd = minetest.formspec_escape(cmnd)
|
||||
@ -28,7 +22,7 @@ local function formspec(cmnd)
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[0.3,0.3;"..cmnd.."]"..
|
||||
"button_exit[2.5,5.5;2,1;exit;"..I("Exit").."]"
|
||||
"button_exit[2.5,5.5;2,1;exit;"..S("Exit").."]"
|
||||
end
|
||||
|
||||
local commands = [[dig_sign 6
|
||||
@ -37,7 +31,7 @@ place_sign_behind 6
|
||||
]]
|
||||
|
||||
minetest.register_node("signs_bot:bot_flap", {
|
||||
description = "Bot Flap",
|
||||
description = S("Bot Flap"),
|
||||
paramtype2 = "facedir",
|
||||
tiles = {
|
||||
"signs_bot_bot_flap_top.png",
|
||||
@ -69,13 +63,13 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "bot_flap", {
|
||||
name = I("Bot Flap"),
|
||||
name = S("Bot Flap"),
|
||||
data = {
|
||||
item = "signs_bot:bot_flap",
|
||||
text = table.concat({
|
||||
I("The flap is a simple block used as door for the bot."),
|
||||
I("Place the flap in any wall, and the bot will automatically open"),
|
||||
I("and close the flap as it passes through it."),
|
||||
S("The flap is a simple block used as door for the bot."),
|
||||
S("Place the flap in any wall, and the bot will automatically open"),
|
||||
S("and close the flap as it passes through it."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,22 +13,19 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
|
||||
local lib = signs_bot.lib
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local function update_infotext(pos, dest_pos, cmnd)
|
||||
M(pos):set_string("infotext", I("Bot Sensor: Connected with ")..S(dest_pos).." / "..cmnd)
|
||||
M(pos):set_string("infotext", S("Bot Sensor: Connected with").." "..P2S(dest_pos).." / "..cmnd)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:bot_sensor", {
|
||||
description = I("Bot Sensor"),
|
||||
description = S("Bot Sensor"),
|
||||
inventory_image = "signs_bot_sensor_bot_inv.png",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
@ -49,7 +46,7 @@ minetest.register_node("signs_bot:bot_sensor", {
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
meta:set_string("infotext", I("Bot Sensor: Not connected"))
|
||||
meta:set_string("infotext", S("Bot Sensor: Not connected"))
|
||||
end,
|
||||
|
||||
update_infotext = update_infotext,
|
||||
@ -57,13 +54,14 @@ minetest.register_node("signs_bot:bot_sensor", {
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
is_ground_content = false,
|
||||
groups = {sign_bot_sensor = 1, cracky = 1},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:bot_sensor_on", {
|
||||
description = I("Bot Sensor"),
|
||||
description = S("Bot Sensor"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
@ -100,6 +98,7 @@ minetest.register_node("signs_bot:bot_sensor_on", {
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
is_ground_content = false,
|
||||
diggable = false,
|
||||
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
|
||||
@ -118,13 +117,13 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "bot_sensor", {
|
||||
name = I("Bot Sensor"),
|
||||
name = S("Bot Sensor"),
|
||||
data = {
|
||||
item = "signs_bot:bot_sensor",
|
||||
text = table.concat({
|
||||
I("The Bot Sensor detects any bot and sends a signal, if a bot is nearby."),
|
||||
I("the sensor range is one node/meter."),
|
||||
I("The sensor direction does not care."),
|
||||
S("The Bot Sensor detects any bot and sends a signal, if a bot is nearby."),
|
||||
S("the sensor range is one node/meter."),
|
||||
S("The sensor direction does not care."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,20 +13,19 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
local CYCLE_TIME = 2
|
||||
|
||||
local function update_infotext(pos, dest_pos, dest_idx)
|
||||
M(pos):set_string("infotext", I("Cart Sensor: Connected with ")..S(dest_pos).." / "..dest_idx)
|
||||
M(pos):set_string("infotext", S("Cart Sensor: Connected with").." "..P2S(dest_pos).." / "..dest_idx)
|
||||
end
|
||||
|
||||
local function swap_node(pos, name)
|
||||
@ -39,18 +38,8 @@ local function swap_node(pos, name)
|
||||
return true
|
||||
end
|
||||
|
||||
local function check_cart(pos)
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||
if object:get_entity_name() == "minecart:cart" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function node_timer(pos)
|
||||
local pos1 = lib.next_pos(pos, M(pos):get_int("param2"))
|
||||
if check_cart(pos1) then
|
||||
if minecart.is_cart_available(pos, M(pos):get_int("param2"), 1) then
|
||||
if swap_node(pos, "signs_bot:cart_sensor_on") then
|
||||
signs_bot.send_signal(pos)
|
||||
signs_bot.lib.activate_extender_nodes(pos, true)
|
||||
@ -62,7 +51,7 @@ local function node_timer(pos)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:cart_sensor", {
|
||||
description = I("Cart Sensor"),
|
||||
description = S("Cart Sensor"),
|
||||
inventory_image = "signs_bot_sensor_cart_inv.png",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
@ -83,7 +72,7 @@ minetest.register_node("signs_bot:cart_sensor", {
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
meta:set_string("infotext", "Cart Sensor: Not connected")
|
||||
meta:set_string("infotext", S("Cart Sensor: Not connected"))
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
local node = minetest.get_node(pos)
|
||||
meta:set_int("param2", (node.param2 + 2) % 4)
|
||||
@ -93,6 +82,7 @@ minetest.register_node("signs_bot:cart_sensor", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -101,7 +91,7 @@ minetest.register_node("signs_bot:cart_sensor", {
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:cart_sensor_on", {
|
||||
description = I("Cart Sensor"),
|
||||
description = S("Cart Sensor"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
@ -123,6 +113,7 @@ minetest.register_node("signs_bot:cart_sensor_on", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -156,13 +147,13 @@ minetest.register_lbm({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "cart_sensor", {
|
||||
name = I("Cart Sensor"),
|
||||
name = S("Cart Sensor"),
|
||||
data = {
|
||||
item = "signs_bot:cart_sensor",
|
||||
text = table.concat({
|
||||
I("The Cart Sensor detects and sends a signal, if a cart (Minecart) is nearby."),
|
||||
I("the sensor range is one node/meter."),
|
||||
I("The sensor has an active side (red) that must point to the rail/cart."),
|
||||
S("The Cart Sensor detects and sends a signal, if a cart (Minecart) is nearby."),
|
||||
S("the sensor range is one node/meter."),
|
||||
S("The sensor has an active side (red) that must point to the rail/cart."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,13 +13,10 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -34,7 +31,7 @@ local formspec = "size[8,7]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[1,1.3;"..I("Signs:").."]"..
|
||||
"label[1,1.3;"..S("Signs:").."]"..
|
||||
"label[2.6,0.7;1]label[5.1,0.7;2]"..
|
||||
"list[context;sign;3,0.5;2,2;]"..
|
||||
"label[2.6,1.7;3]label[5.1,1.7;4]"..
|
||||
@ -100,10 +97,15 @@ local function allow_metadata_inventory()
|
||||
return 0
|
||||
end
|
||||
|
||||
local function can_dig(pos)
|
||||
local inv = minetest.get_inventory({type="node", pos=pos})
|
||||
return inv:is_empty("sign")
|
||||
end
|
||||
|
||||
for idx = 1,4 do
|
||||
local not_in_inv = idx == 1 and 0 or 1
|
||||
minetest.register_node("signs_bot:changer"..idx, {
|
||||
description = I("Bot Control Unit"),
|
||||
description = S("Bot Control Unit"),
|
||||
inventory_image = "signs_bot_ctrl_unit_inv.png",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
@ -139,9 +141,11 @@ for idx = 1,4 do
|
||||
allow_metadata_inventory_put = allow_metadata_inventory,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory,
|
||||
on_punch = swap_node,
|
||||
can_dig = can_dig,
|
||||
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -162,18 +166,18 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "changer", {
|
||||
name = I("Bot Control Unit"),
|
||||
name = S("Bot Control Unit"),
|
||||
data = {
|
||||
item = "signs_bot:changer1",
|
||||
text = table.concat({
|
||||
I("The Bot Control Unit is used to lead the bot by means of signs."),
|
||||
I("The unit can be loaded with up to 4 different signs and can be programmed by means of sensors."),
|
||||
S("The Bot Control Unit is used to lead the bot by means of signs."),
|
||||
S("The unit can be loaded with up to 4 different signs and can be programmed by means of sensors."),
|
||||
"",
|
||||
I("To load the unit, place a sign on the red side of the unit and click on the unit."),
|
||||
I("The sign disappears / is moved to the inventory of the unit."),
|
||||
I("This can be repeated 3 times."),
|
||||
S("To load the unit, place a sign on the red side of the unit and click on the unit."),
|
||||
S("The sign disappears / is moved to the inventory of the unit."),
|
||||
S("This can be repeated 3 times."),
|
||||
"",
|
||||
I("Use the connection tool to connect up to 4 sensors with the Bot Control Unit."),
|
||||
S("Use the connection tool to connect up to 4 sensors with the Bot Control Unit."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-0221 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,13 +13,12 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local NODE_IO = minetest.global_exists("node_io")
|
||||
|
||||
@ -50,7 +49,7 @@ end
|
||||
local function update_infotext(pos, dest_pos, cmnd)
|
||||
local meta = M(pos)
|
||||
local state = get_inv_state(pos)
|
||||
meta:set_string("infotext", I("Bot Chest: Sends signal to ")..S(dest_pos).." / "..cmnd..", if "..state)
|
||||
meta:set_string("infotext", S("Bot Chest: Sends signal to").." "..P2S(dest_pos).." / "..cmnd..", if "..state)
|
||||
meta:set_string("state", state)
|
||||
|
||||
end
|
||||
@ -68,7 +67,7 @@ end
|
||||
|
||||
if NODE_IO then
|
||||
minetest.register_node("signs_bot:chest", {
|
||||
description = I("Signs Bot Chest"),
|
||||
description = S("Signs Bot Chest"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
'signs_bot_chest_top.png',
|
||||
@ -91,7 +90,7 @@ if NODE_IO then
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
meta:set_string("formspec", formspec(pos, mem))
|
||||
meta:set_string("infotext", "Bot Chest: Not connected")
|
||||
meta:set_string("infotext", S("Bot Chest: Not connected"))
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
@ -158,7 +157,7 @@ if NODE_IO then
|
||||
})
|
||||
else
|
||||
minetest.register_node("signs_bot:chest", {
|
||||
description = I("Signs Bot Chest"),
|
||||
description = S("Signs Bot Chest"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
'signs_bot_chest_top.png',
|
||||
@ -181,7 +180,7 @@ else
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
meta:set_string("formspec", formspec(pos, mem))
|
||||
meta:set_string("infotext", "Bot Chest: Not connected")
|
||||
meta:set_string("infotext", S("Bot Chest: Not connected"))
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
@ -245,15 +244,15 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "chest", {
|
||||
name = I("Signs Bot Chest"),
|
||||
name = S("Signs Bot Chest"),
|
||||
data = {
|
||||
item = "signs_bot:chest",
|
||||
text = table.concat({
|
||||
I("The Signs Bot Chest is a special chest with sensor function."),
|
||||
I("It sends a signal depending on the chest state."),
|
||||
I("Possible states are 'empty', 'not empty', 'almost full'"),
|
||||
S("The Signs Bot Chest is a special chest with sensor function."),
|
||||
S("It sends a signal depending on the chest state."),
|
||||
S("Possible states are 'empty', 'not empty', 'almost full'"),
|
||||
"",
|
||||
I("A typical use case is to turn off the bot, when the chest is almost full or empty."),
|
||||
S("A typical use case is to turn off the bot, when the chest is almost full or empty."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -11,14 +11,8 @@
|
||||
Bot farming commands
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -65,7 +59,7 @@ signs_bot.register_botcommand("sow_seed", {
|
||||
mod = "farming",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Sow farming seeds\nin front of the robot"),
|
||||
description = S("Sow farming seeds\nin front of the robot"),
|
||||
check = function(slot)
|
||||
slot = tonumber(slot)
|
||||
return slot and slot > 0 and slot < 9
|
||||
@ -97,7 +91,10 @@ local function harvesting(base_pos, mem)
|
||||
-- Do not cache the result of get_node_drops; it is a probabilistic function!
|
||||
local drops = minetest.get_node_drops(node.name)
|
||||
for _,itemstring in ipairs(drops) do
|
||||
bot_inv_put_item(base_pos, 0, ItemStack(itemstring))
|
||||
local leftover = bot_inv_put_item(base_pos, 0, ItemStack(itemstring))
|
||||
if leftover and leftover:get_count() > 0 then
|
||||
signs_bot.lib.drop_items(mem.robot_pos, leftover)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -107,7 +104,7 @@ signs_bot.register_botcommand("harvest", {
|
||||
mod = "farming",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Harvest farming products\nin front of the robot\non a 3x3 field."),
|
||||
description = S("Harvest farming products\nin front of the robot\non a 3x3 field."),
|
||||
cmnd = function(base_pos, mem)
|
||||
if not mem.steps then
|
||||
mem.pos_tbl = signs_bot.lib.gen_position_table(mem.robot_pos, mem.robot_param2, 3, 3, 0)
|
||||
@ -145,7 +142,7 @@ signs_bot.register_botcommand("plant_sapling", {
|
||||
mod = "farming",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Plant a sapling\nin front of the robot"),
|
||||
description = S("Plant a sapling\nin front of the robot"),
|
||||
check = function(slot)
|
||||
slot = tonumber(slot)
|
||||
return slot and slot > 0 and slot < 9
|
||||
@ -168,7 +165,7 @@ turn_around]]
|
||||
|
||||
signs_bot.register_sign({
|
||||
name = "farming",
|
||||
description = I('Sign "farming"'),
|
||||
description = S('Sign "farming"'),
|
||||
commands = CMD,
|
||||
image = "signs_bot_sign_farming.png",
|
||||
})
|
||||
@ -184,14 +181,14 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "farming", {
|
||||
name = I("Sign 'farming'"),
|
||||
name = S("Sign 'farming'"),
|
||||
data = {
|
||||
item = "signs_bot:farming",
|
||||
text = table.concat({
|
||||
I("Used to harvest and seed a 3x3 field."),
|
||||
I("Place the sign in front of the field."),
|
||||
I("The seed to be placed has to be in the first inventory slot of the bot."),
|
||||
I("When finished, the bot turns."),
|
||||
S("Used to harvest and seed a 3x3 field."),
|
||||
S("Place the sign in front of the field."),
|
||||
S("The seed to be placed has to be in the first inventory slot of the bot."),
|
||||
S("When finished, the bot turns."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -11,14 +11,8 @@
|
||||
Bot flower cutting command
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -48,16 +42,28 @@ minetest.after(1, function()
|
||||
end
|
||||
end)
|
||||
|
||||
local function is_tree(node)
|
||||
if minetest.get_item_group(node.name, "tree") == 1 then
|
||||
return signs_bot.handle_drop_like_a_player(node)
|
||||
end
|
||||
if minetest.get_item_group(node.name, "leaves") == 1 then
|
||||
return signs_bot.handle_drop_like_a_player(node)
|
||||
end
|
||||
end
|
||||
|
||||
local function harvesting(base_pos, mem)
|
||||
local pos = mem.pos_tbl and mem.pos_tbl[mem.steps]
|
||||
mem.steps = (mem.steps or 1) + 1
|
||||
|
||||
if pos and lib.not_protected(base_pos, pos) then
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
local drop = Flowers[node.name]
|
||||
local drop = Flowers[node.name] or is_tree(node)
|
||||
if drop then
|
||||
minetest.remove_node(pos)
|
||||
bot_inv_put_item(base_pos, 0, ItemStack(drop))
|
||||
local leftover = bot_inv_put_item(base_pos, 0, ItemStack(drop))
|
||||
if leftover and leftover:get_count() > 0 then
|
||||
signs_bot.lib.drop_items(mem.robot_pos, leftover)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -66,7 +72,7 @@ signs_bot.register_botcommand("cutting", {
|
||||
mod = "farming",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Cutting flowers\nin front of the robot\non a 3x3 field."),
|
||||
description = S("Cutting flowers, leaves and tree blocks\nin front of the robot\non a 3x3 field."),
|
||||
cmnd = function(base_pos, mem)
|
||||
if not mem.steps then
|
||||
mem.pos_tbl = signs_bot.lib.gen_position_table(mem.robot_pos, mem.robot_param2, 3, 3, 0)
|
||||
@ -91,7 +97,7 @@ turn_around]]
|
||||
|
||||
signs_bot.register_sign({
|
||||
name = "flowers",
|
||||
description = I('Sign "flowers"'),
|
||||
description = S('Sign "flowers"'),
|
||||
commands = CMD,
|
||||
image = "signs_bot_sign_flowers.png",
|
||||
})
|
||||
@ -107,13 +113,13 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "flowers", {
|
||||
name = I("Sign 'flowers'"),
|
||||
name = S("Sign 'flowers'"),
|
||||
data = {
|
||||
item = "signs_bot:flowers",
|
||||
text = table.concat({
|
||||
I("Used to cut flowers on a 3x3 field."),
|
||||
I("Place the sign in front of the field."),
|
||||
I("When finished, the bot turns."),
|
||||
S("Used to cut flowers on a 3x3 field."),
|
||||
S("Place the sign in front of the field."),
|
||||
S("When finished, the bot turns."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,13 +13,10 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -90,7 +87,7 @@ signs_bot.register_botcommand("take_item", {
|
||||
mod = "item",
|
||||
params = "<num> <slot>",
|
||||
num_param = 2,
|
||||
description = I("Take <num> items from a chest like node\nand put it into the item inventory.\n"..
|
||||
description = S("Take <num> items from a chest like node\nand put it into the item inventory.\n"..
|
||||
"<slot> is the inventory slot (1..8) or 0 for any one"),
|
||||
check = function(num, slot)
|
||||
num = tonumber(num) or 1
|
||||
@ -115,7 +112,7 @@ signs_bot.register_botcommand("add_item", {
|
||||
mod = "item",
|
||||
params = "<num> <slot>",
|
||||
num_param = 2,
|
||||
description = I("Add <num> items to a chest like node\ntaken from the item inventory.\n"..
|
||||
description = S("Add <num> items to a chest like node\ntaken from the item inventory.\n"..
|
||||
"<slot> is the inventory slot (1..8) or 0 for any one"),
|
||||
check = function(num, slot)
|
||||
num = tonumber(num) or 1
|
||||
@ -140,7 +137,7 @@ signs_bot.register_botcommand("add_fuel", {
|
||||
mod = "item",
|
||||
params = "<num> <slot>",
|
||||
num_param = 2,
|
||||
description = I("Add <num> fuel to a furnace like node\ntaken from the item inventory.\n"..
|
||||
description = S("Add <num> fuel to a furnace like node\ntaken from the item inventory.\n"..
|
||||
"<slot> is the inventory slot (1..8) or 0 for any one"),
|
||||
check = function(num, slot)
|
||||
num = tonumber(num) or 1
|
||||
@ -165,7 +162,7 @@ signs_bot.register_botcommand("cond_take_item", {
|
||||
mod = "item",
|
||||
params = "<num> <slot>",
|
||||
num_param = 2,
|
||||
description = I("deprecated, use bot inventory configuration instead"),
|
||||
description = S("deprecated, use bot inventory configuration instead"),
|
||||
check = function(num, slot)
|
||||
return false
|
||||
end,
|
||||
@ -178,7 +175,7 @@ signs_bot.register_botcommand("cond_add_item", {
|
||||
mod = "item",
|
||||
params = "<num> <slot>",
|
||||
num_param = 2,
|
||||
description = I("deprecated, use bot inventory configuration instead"),
|
||||
description = S("deprecated, use bot inventory configuration instead"),
|
||||
check = function(num, slot)
|
||||
return false
|
||||
end,
|
||||
@ -191,7 +188,7 @@ signs_bot.register_botcommand("pickup_items", {
|
||||
mod = "item",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Pick up all objects\n"..
|
||||
description = S("Pick up all objects\n"..
|
||||
"in a 3x3 field.\n"..
|
||||
"<slot> is the inventory slot (1..8) or 0 for any one"),
|
||||
check = function(slot)
|
||||
@ -219,7 +216,7 @@ signs_bot.register_botcommand("drop_items", {
|
||||
mod = "item",
|
||||
params = "<num> <slot>",
|
||||
num_param = 2,
|
||||
description = I("Drop items in front of the bot.\n"..
|
||||
description = S("Drop items in front of the bot.\n"..
|
||||
"<slot> is the inventory slot (1..8) or 0 for any one"),
|
||||
check = function(num, slot)
|
||||
num = tonumber(num) or 1
|
||||
@ -246,18 +243,10 @@ signs_bot.register_botcommand("punch_cart", {
|
||||
mod = "item",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Punch a rail cart to start it"),
|
||||
description = S("Punch a rail cart to start it"),
|
||||
cmnd = function(base_pos, mem)
|
||||
local pos = lib.dest_pos(mem.robot_pos, mem.robot_param2, {0})
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos, 2)) do
|
||||
if object:get_entity_name() == "minecart:cart" then
|
||||
object:punch(object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = 1},
|
||||
}, minetest.facedir_to_dir(mem.robot_param2))
|
||||
break -- start only one cart
|
||||
end
|
||||
end
|
||||
local punch_dir = minetest.facedir_to_dir(mem.robot_param2)
|
||||
minecart.punch_cart(mem.robot_pos, mem.robot_param2, 1, punch_dir)
|
||||
return signs_bot.DONE
|
||||
end,
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -12,14 +12,8 @@
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
local get_node_lvm = tubelib2.get_node_lvm
|
||||
@ -135,7 +129,7 @@ signs_bot.register_botcommand("backward", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Move the robot one step back"),
|
||||
description = S("Move the robot one step back"),
|
||||
cmnd = function(base_pos, mem)
|
||||
local new_pos = backward_robot(mem)
|
||||
if new_pos then -- not blocked?
|
||||
@ -160,7 +154,7 @@ signs_bot.register_botcommand("turn_left", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Turn the robot to the left"),
|
||||
description = S("Turn the robot to the left"),
|
||||
cmnd = function(base_pos, mem)
|
||||
mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "L")
|
||||
return signs_bot.DONE
|
||||
@ -171,7 +165,7 @@ signs_bot.register_botcommand("turn_right", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Turn the robot to the right"),
|
||||
description = S("Turn the robot to the right"),
|
||||
cmnd = function(base_pos, mem)
|
||||
mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R")
|
||||
return signs_bot.DONE
|
||||
@ -182,7 +176,7 @@ signs_bot.register_botcommand("turn_around", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Turn the robot around"),
|
||||
description = S("Turn the robot around"),
|
||||
cmnd = function(base_pos, mem)
|
||||
mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R")
|
||||
mem.robot_param2 = turn_robot(mem.robot_pos, mem.robot_param2, "R")
|
||||
@ -217,7 +211,7 @@ signs_bot.register_botcommand("move_up", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Move the robot upwards"),
|
||||
description = S("Move the robot upwards"),
|
||||
cmnd = function(base_pos, mem)
|
||||
local new_pos = robot_up(mem.robot_pos, mem.robot_param2)
|
||||
if new_pos then -- not blocked?
|
||||
@ -251,7 +245,7 @@ signs_bot.register_botcommand("move_down", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Move the robot down"),
|
||||
description = S("Move the robot down"),
|
||||
cmnd = function(base_pos, mem)
|
||||
local new_pos = robot_down(mem.robot_pos, mem.robot_param2)
|
||||
if new_pos then -- not blocked?
|
||||
@ -265,14 +259,14 @@ signs_bot.register_botcommand("pause", {
|
||||
mod = "move",
|
||||
params = "<sec>",
|
||||
num_param = 1,
|
||||
description = I("Stop the robot for <sec> seconds\n(1..9999)"),
|
||||
description = S("Stop the robot for <sec> seconds\n(1..9999)"),
|
||||
check = function(sec)
|
||||
sec = tonumber(sec) or 1
|
||||
return sec and sec > 0 and sec < 10000
|
||||
end,
|
||||
cmnd = function(base_pos, mem, sec)
|
||||
if not mem.steps then
|
||||
mem.steps = tonumber(sec or 1)
|
||||
mem.steps = tonumber(sec) or 1
|
||||
end
|
||||
mem.steps = mem.steps - 1
|
||||
if mem.steps == 0 then
|
||||
@ -290,7 +284,7 @@ signs_bot.register_botcommand("stop", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Stop the robot."),
|
||||
description = S("Stop the robot."),
|
||||
cmnd = function(base_pos, mem, slot)
|
||||
if mem.capa then
|
||||
mem.capa = mem.capa + 2
|
||||
@ -303,7 +297,7 @@ signs_bot.register_botcommand("turn_off", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Turn the robot off\n"..
|
||||
description = S("Turn the robot off\n"..
|
||||
"and put it back in the box."),
|
||||
cmnd = function(base_pos, mem)
|
||||
signs_bot.stop_robot(base_pos, mem)
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -12,14 +12,8 @@
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -117,7 +111,7 @@ signs_bot.register_botcommand("pattern", {
|
||||
mod = "copy",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I("Store pattern to be cloned."),
|
||||
description = S("Store pattern to be cloned."),
|
||||
cmnd = function(base_pos, mem)
|
||||
mem.pttrn_pos = lib.next_pos(mem.robot_pos, mem.robot_param2)
|
||||
mem.pttrn_param2 = mem.robot_param2
|
||||
@ -129,7 +123,7 @@ signs_bot.register_botcommand("copy", {
|
||||
mod = "copy",
|
||||
params = "<size> <lvl>",
|
||||
num_param = 2,
|
||||
description = I("Copy the nodes from\n"..
|
||||
description = S("Copy the nodes from\n"..
|
||||
"the stored pattern position\n"..
|
||||
"<size> is: 3x1, 3x2, 3x3,\n"..
|
||||
"5x1, 5x2, 5x3 (wide x deep)\n"..
|
||||
@ -178,7 +172,7 @@ minetest.register_node("signs_bot:missing", {
|
||||
|
||||
signs_bot.register_sign({
|
||||
name = "pattern",
|
||||
description = I('Sign "pattern"'),
|
||||
description = S('Sign "pattern"'),
|
||||
commands = "pattern\nturn_around",
|
||||
image = "signs_bot_sign_pattern.png",
|
||||
})
|
||||
@ -207,7 +201,7 @@ turn_around]]
|
||||
|
||||
signs_bot.register_sign({
|
||||
name = "copy3x3x3",
|
||||
description = I('Sign "copy 3x3x3"'),
|
||||
description = S('Sign "copy 3x3x3"'),
|
||||
commands = CMND,
|
||||
image = "signs_bot_sign_copy3x3x3.png",
|
||||
})
|
||||
@ -223,14 +217,14 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "pattern", {
|
||||
name = I("Sign 'pattern'"),
|
||||
name = S("Sign 'pattern'"),
|
||||
data = {
|
||||
item = "signs_bot:pattern",
|
||||
text = table.concat({
|
||||
I("Used to make a copy of a 3x3x3 cube."),
|
||||
I("Place the sign in front of the pattern to be copied."),
|
||||
I("Use the copy sign to make the copy of this pattern on a different location."),
|
||||
I("The bot must first reach the pattern sign, then the copy sign."),
|
||||
S("Used to make a copy of a 3x3x3 cube."),
|
||||
S("Place the sign in front of the pattern to be copied."),
|
||||
S("Use the copy sign to make the copy of this pattern on a different location."),
|
||||
S("The bot must first reach the pattern sign, then the copy sign."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
@ -238,13 +232,13 @@ end
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "copy3x3x3", {
|
||||
name = I("Sign 'copy3x3x3'"),
|
||||
name = S("Sign 'copy3x3x3'"),
|
||||
data = {
|
||||
item = "signs_bot:copy3x3x3",
|
||||
text = table.concat({
|
||||
I("Used to make a copy of a 3x3x3 cube."),
|
||||
I("Place the sign in front of the location, where the copy should be made."),
|
||||
I("Use the pattern sign to mark the pattern."),
|
||||
S("Used to make a copy of a 3x3x3 cube."),
|
||||
S("Place the sign in front of the location, where the copy should be made."),
|
||||
S("Use the pattern sign to mark the pattern."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -12,14 +12,8 @@
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
local bot_inv_take_item = signs_bot.bot_inv_take_item
|
||||
@ -32,13 +26,23 @@ end
|
||||
local tValidLevels = {[-1] = -1, [0] = 0, [1] = 1}
|
||||
|
||||
-- for items with paramtype2 = "facedir"
|
||||
local tRotations = {
|
||||
[0] = {8,20,4},
|
||||
[1] = {16,20,12},
|
||||
[2] = {4,20,8},
|
||||
[3] = {12,20,16},
|
||||
local tRotations = {}
|
||||
|
||||
local Rotations = {
|
||||
{0,8,22,4},
|
||||
{1,17,21,13},
|
||||
{2,6,20,10},
|
||||
{3,15,23,19},
|
||||
}
|
||||
|
||||
for _,v in ipairs(Rotations) do
|
||||
local t = table.copy(v)
|
||||
for i = 1,4 do
|
||||
table.insert(t, 1, table.remove(t))
|
||||
tRotations[t[1]] = {t[2], t[3], t[4]}
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Place/dig items
|
||||
--
|
||||
@ -46,7 +50,7 @@ local function place_item(base_pos, robot_pos, param2, slot, route, level)
|
||||
local pos1, p2 = lib.dest_pos(robot_pos, param2, route)
|
||||
pos1.y = pos1.y + level
|
||||
if not lib.not_protected(base_pos, pos1) then
|
||||
return signs_bot.ERROR, I("Error: Position protected")
|
||||
return signs_bot.ERROR, S("Error: Position protected")
|
||||
end
|
||||
if lib.is_air_like(pos1) then
|
||||
local taken = signs_bot.bot_inv_take_item(base_pos, slot, 1)
|
||||
@ -74,7 +78,7 @@ signs_bot.register_botcommand("place_front", {
|
||||
mod = "place",
|
||||
params = "<slot> <lvl>",
|
||||
num_param = 2,
|
||||
description = I("Place a block in front of the robot\n"..
|
||||
description = S("Place a block in front of the robot\n"..
|
||||
"<slot> is the inventory slot (1..8)\n"..
|
||||
"<lvl> is one of: -1 0 +1"),
|
||||
check = function(slot, lvl)
|
||||
@ -95,7 +99,7 @@ signs_bot.register_botcommand("place_left", {
|
||||
mod = "place",
|
||||
params = "<slot> <lvl>",
|
||||
num_param = 2,
|
||||
description = I("Place a block on the left side\n"..
|
||||
description = S("Place a block on the left side\n"..
|
||||
"<slot> is the inventory slot (1..8)\n"..
|
||||
"<lvl> is one of: -1 0 +1"),
|
||||
check = function(slot, lvl)
|
||||
@ -116,7 +120,7 @@ signs_bot.register_botcommand("place_right", {
|
||||
mod = "place",
|
||||
params = "<slot> <lvl>",
|
||||
num_param = 2,
|
||||
description = I("Place a block on the right side\n"..
|
||||
description = S("Place a block on the right side\n"..
|
||||
"<slot> is the inventory slot (1..8)\n"..
|
||||
"<lvl> is one of: -1 0 +1"),
|
||||
check = function(slot, lvl)
|
||||
@ -136,7 +140,7 @@ signs_bot.register_botcommand("place_right", {
|
||||
local function place_item_below(base_pos, robot_pos, param2, slot)
|
||||
local pos1 = {x=robot_pos.x,y=robot_pos.y-1,z=robot_pos.z}
|
||||
if not lib.not_protected(base_pos, pos1) then
|
||||
return signs_bot.ERROR, I("Error: Position protected")
|
||||
return signs_bot.ERROR, S("Error: Position protected")
|
||||
end
|
||||
local node = tubelib2.get_node_lvm(pos1)
|
||||
if node.name == "signs_bot:robot_foot" then
|
||||
@ -155,7 +159,7 @@ signs_bot.register_botcommand("place_below", {
|
||||
mod = "place",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Place a block under the robot.\n"..
|
||||
description = S("Place a block under the robot.\n"..
|
||||
"Hint: use 'move_up' first.\n"..
|
||||
"<slot> is the inventory slot (1..8)"),
|
||||
check = function(slot)
|
||||
@ -171,7 +175,7 @@ signs_bot.register_botcommand("place_below", {
|
||||
local function place_item_above(base_pos, robot_pos, param2, slot)
|
||||
local pos1 = {x=robot_pos.x,y=robot_pos.y+1,z=robot_pos.z}
|
||||
if not lib.not_protected(base_pos, pos1) then
|
||||
return signs_bot.ERROR, I("Error: Position protected")
|
||||
return signs_bot.ERROR, S("Error: Position protected")
|
||||
end
|
||||
if lib.is_air_like(pos1) then
|
||||
local taken = bot_inv_take_item(base_pos, slot, 1)
|
||||
@ -189,7 +193,7 @@ signs_bot.register_botcommand("place_above", {
|
||||
mod = "place",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Place a block above the robot.\n"..
|
||||
description = S("Place a block above the robot.\n"..
|
||||
"<slot> is the inventory slot (1..8)"),
|
||||
check = function(slot)
|
||||
slot = tonumber(slot) or 0
|
||||
@ -207,13 +211,13 @@ local function dig_item(base_pos, robot_pos, param2, slot, route, level)
|
||||
local node = tubelib2.get_node_lvm(pos1)
|
||||
local dug_name = lib.is_simple_node(node)
|
||||
if not lib.not_protected(base_pos, pos1) then
|
||||
return signs_bot.ERROR, I("Error: Position protected")
|
||||
return signs_bot.ERROR, S("Error: Position protected")
|
||||
end
|
||||
if dug_name then
|
||||
if bot_inv_put_item(base_pos, slot, ItemStack(dug_name)) then
|
||||
minetest.remove_node(pos1)
|
||||
else
|
||||
return signs_bot.ERROR, I("Error: No free inventory space")
|
||||
return signs_bot.ERROR, S("Error: No free inventory space")
|
||||
end
|
||||
end
|
||||
return signs_bot.DONE
|
||||
@ -223,7 +227,7 @@ signs_bot.register_botcommand("dig_front", {
|
||||
mod = "place",
|
||||
params = "<slot> <lvl>",
|
||||
num_param = 2,
|
||||
description = I("Dig the block in front of the robot\n"..
|
||||
description = S("Dig the block in front of the robot\n"..
|
||||
"<slot> is the inventory slot (1..8)\n"..
|
||||
"<lvl> is one of: -1 0 +1"),
|
||||
check = function(slot, lvl)
|
||||
@ -245,7 +249,7 @@ signs_bot.register_botcommand("dig_left", {
|
||||
mod = "place",
|
||||
params = "<slot> <lvl>",
|
||||
num_param = 2,
|
||||
description = I("Dig the block on the left side\n"..
|
||||
description = S("Dig the block on the left side\n"..
|
||||
"<slot> is the inventory slot (1..8)\n"..
|
||||
"<lvl> is one of: -1 0 +1"),
|
||||
check = function(slot, lvl)
|
||||
@ -267,7 +271,7 @@ signs_bot.register_botcommand("dig_right", {
|
||||
mod = "place",
|
||||
params = "<slot> <lvl>",
|
||||
num_param = 2,
|
||||
description = I("Dig the block on the right side\n"..
|
||||
description = S("Dig the block on the right side\n"..
|
||||
"<slot> is the inventory slot (1..8)\n"..
|
||||
"<lvl> is one of: -1 0 +1"),
|
||||
check = function(slot, lvl)
|
||||
@ -290,13 +294,13 @@ local function dig_item_below(base_pos, robot_pos, param2, slot)
|
||||
local node = tubelib2.get_node_lvm(pos1)
|
||||
local dug_name = lib.is_simple_node(node)
|
||||
if not lib.not_protected(base_pos, pos1) then
|
||||
return signs_bot.ERROR, I("Error: Position protected")
|
||||
return signs_bot.ERROR, S("Error: Position protected")
|
||||
end
|
||||
if dug_name then
|
||||
if bot_inv_put_item(base_pos, slot, ItemStack(dug_name)) then
|
||||
minetest.set_node(pos1, {name="signs_bot:robot_foot"})
|
||||
else
|
||||
return signs_bot.ERROR, I("Error: No free inventory space")
|
||||
return signs_bot.ERROR, S("Error: No free inventory space")
|
||||
end
|
||||
end
|
||||
return signs_bot.DONE
|
||||
@ -306,7 +310,7 @@ signs_bot.register_botcommand("dig_below", {
|
||||
mod = "place",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Dig the block under the robot.\n"..
|
||||
description = S("Dig the block under the robot.\n"..
|
||||
"<slot> is the inventory slot (1..8)"),
|
||||
check = function(slot)
|
||||
slot = tonumber(slot) or 0
|
||||
@ -324,13 +328,13 @@ local function dig_item_above(base_pos, robot_pos, param2, slot)
|
||||
local node = tubelib2.get_node_lvm(pos1)
|
||||
local dug_name = lib.is_simple_node(node)
|
||||
if not lib.not_protected(base_pos, pos1) then
|
||||
return signs_bot.ERROR, I("Error: Position protected")
|
||||
return signs_bot.ERROR, S("Error: Position protected")
|
||||
end
|
||||
if dug_name then
|
||||
if bot_inv_put_item(base_pos, slot, ItemStack(dug_name)) then
|
||||
minetest.remove_node(pos1)
|
||||
else
|
||||
return signs_bot.ERROR, I("Error: No free inventory space")
|
||||
return signs_bot.ERROR, S("Error: No free inventory space")
|
||||
end
|
||||
end
|
||||
return signs_bot.DONE
|
||||
@ -340,7 +344,7 @@ signs_bot.register_botcommand("dig_above", {
|
||||
mod = "place",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Dig the block above the robot.\n"..
|
||||
description = S("Dig the block above the robot.\n"..
|
||||
"<slot> is the inventory slot (1..8)"),
|
||||
check = function(slot)
|
||||
slot = tonumber(slot) or 0
|
||||
@ -358,7 +362,7 @@ local function rotate_item(base_pos, robot_pos, param2, route, level, steps)
|
||||
pos1.y = pos1.y + level
|
||||
local node = tubelib2.get_node_lvm(pos1)
|
||||
if not lib.not_protected(base_pos, pos1) then
|
||||
return signs_bot.ERROR, I("Error: Position protected")
|
||||
return signs_bot.ERROR, S("Error: Position protected")
|
||||
end
|
||||
if lib.is_simple_node(node) then
|
||||
local p2 = tRotations[node.param2] and tRotations[node.param2][steps]
|
||||
@ -372,7 +376,8 @@ end
|
||||
signs_bot.register_botcommand("rotate_item", {
|
||||
mod = "place",
|
||||
params = "<lvl> <steps>",
|
||||
description = I("Rotate the block in front of the robot\n"..
|
||||
num_param = 2,
|
||||
description = S("Rotate the block in front of the robot\n"..
|
||||
"<lvl> is one of: -1 0 +1\n"..
|
||||
"<steps> is one of: 1 2 3"),
|
||||
check = function(lvl, steps)
|
||||
@ -391,7 +396,7 @@ signs_bot.register_botcommand("rotate_item", {
|
||||
|
||||
-- Simplified torch which can be placed w/o a fake player
|
||||
minetest.register_node("signs_bot:torch", {
|
||||
description = "Bot torch",
|
||||
description = S("Bot torch"),
|
||||
inventory_image = "default_torch_on_floor.png",
|
||||
wield_image = "default_torch_on_floor.png",
|
||||
drawtype = "nodebox",
|
||||
@ -428,6 +433,7 @@ minetest.register_node("signs_bot:torch", {
|
||||
"group:bakedclay", "group:soil"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
liquids_pointable = false,
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,13 +13,10 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -48,12 +45,12 @@ local function formspec1(meta)
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"style_type[textarea,table;font=mono]"..
|
||||
"tabheader[0,0;tab;"..I("Commands,Help")..";1;;true]"..
|
||||
"field[0.3,0.5;9,1;name;"..I("Sign name:")..";"..name.."]"..
|
||||
"tabheader[0,0;tab;"..S("Commands,Help")..";1;;true]"..
|
||||
"field[0.3,0.5;9,1;name;"..S("Sign name:")..";"..name.."]"..
|
||||
"textarea[0.3,1.2;9,7.2;cmnd;;"..cmnd.."]"..
|
||||
"label[0.3,7.5;"..err_msg.."]"..
|
||||
"button_exit[5,7.5;2,1;cancel;"..I("Cancel").."]"..
|
||||
"button[7,7.5;2,1;check;"..I("Check").."]"
|
||||
"button_exit[5,7.5;2,1;cancel;"..S("Cancel").."]"..
|
||||
"button[7,7.5;2,1;check;"..S("Check").."]"
|
||||
end
|
||||
|
||||
local function formspec2(pos, text)
|
||||
@ -62,10 +59,10 @@ local function formspec2(pos, text)
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"style_type[textarea,table;font=mono]"..
|
||||
"tabheader[0,0;tab;"..I("Commands,Help")..";2;;true]"..
|
||||
"tabheader[0,0;tab;"..S("Commands,Help")..";2;;true]"..
|
||||
"table[0.1,0;8.6,4;command;"..sCmnds..";"..pos.."]"..
|
||||
"textarea[0.3,4.5;9,3.5;help;Help:;"..text.."]"..
|
||||
"button[3,7.5;3,1;copy;"..I("Copy Cmnd").."]"
|
||||
"textarea[0.3,4.5;9,3.5;help;"..S("Help")..":;"..text.."]"..
|
||||
"button[3,7.5;3,1;copy;"..S("Copy Cmnd").."]"
|
||||
end
|
||||
|
||||
local function add_arrow(text, line_num)
|
||||
@ -94,7 +91,7 @@ local function append_line(pos, meta, line)
|
||||
local text = meta:get_string("signs_bot_cmnd").."\n"..line
|
||||
meta:set_string("signs_bot_cmnd", text)
|
||||
meta:set_int("err_code", 1) -- zero means OK
|
||||
meta:set_string("err_msg", "please check the added line(s)")
|
||||
meta:set_string("err_msg", S("please check the added line(s)"))
|
||||
end
|
||||
|
||||
local function check_and_store(pos, meta, fields)
|
||||
@ -106,7 +103,7 @@ local function check_and_store(pos, meta, fields)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:sign_cmnd", {
|
||||
description = I('Sign "command"'),
|
||||
description = S('Sign "command"'),
|
||||
drawtype = "nodebox",
|
||||
inventory_image = "signs_bot_sign_cmnd.png",
|
||||
node_box = {
|
||||
@ -134,8 +131,8 @@ minetest.register_node("signs_bot:sign_cmnd", {
|
||||
nmeta:set_string("err_msg", imeta:get_string("err_msg"))
|
||||
nmeta:set_int("err_code", imeta:get_int("err_code"))
|
||||
else
|
||||
nmeta:set_string("sign_name", I('Sign "command"'))
|
||||
nmeta:set_string("signs_bot_cmnd", I("-- enter or copy commands from help page"))
|
||||
nmeta:set_string("sign_name", S('Sign "command"'))
|
||||
nmeta:set_string("signs_bot_cmnd", S("-- enter or copy commands from help page"))
|
||||
nmeta:set_int("err_code", 0)
|
||||
end
|
||||
nmeta:set_string("infotext", nmeta:get_string("sign_name"))
|
||||
@ -175,6 +172,7 @@ minetest.register_node("signs_bot:sign_cmnd", {
|
||||
|
||||
after_dig_node = lib.after_dig_sign_node,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
drop = "",
|
||||
@ -215,18 +213,18 @@ local function place_sign(base_pos, robot_pos, param2, slot)
|
||||
lib.place_sign(pos1, sign, param2)
|
||||
return signs_bot.DONE
|
||||
else
|
||||
return signs_bot.ERROR, I("Error: Signs inventory empty")
|
||||
return signs_bot.ERROR, S("Error: Signs inventory empty")
|
||||
end
|
||||
end
|
||||
end
|
||||
return signs_bot.ERROR, I("Error: Position protected or occupied")
|
||||
return signs_bot.ERROR, S("Error: Position protected or occupied")
|
||||
end
|
||||
|
||||
signs_bot.register_botcommand("place_sign", {
|
||||
mod = "sign",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Place a sign in front of the robot\ntaken from the signs inventory\n"..
|
||||
description = S("Place a sign in front of the robot\ntaken from the signs inventory\n"..
|
||||
"<slot> is the inventory slot (1..6)"),
|
||||
check = function(slot)
|
||||
slot = tonumber(slot) or 1
|
||||
@ -247,18 +245,18 @@ local function place_sign_behind(base_pos, robot_pos, param2, slot)
|
||||
lib.place_sign(pos1, sign, param2)
|
||||
return signs_bot.DONE
|
||||
else
|
||||
return signs_bot.ERROR, I("Error: Signs inventory empty")
|
||||
return signs_bot.ERROR, S("Error: Signs inventory empty")
|
||||
end
|
||||
end
|
||||
end
|
||||
return signs_bot.ERROR, I("Error: Position protected or occupied")
|
||||
return signs_bot.ERROR, S("Error: Position protected or occupied")
|
||||
end
|
||||
|
||||
signs_bot.register_botcommand("place_sign_behind", {
|
||||
mod = "sign",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Place a sign behind the robot\ntaken from the signs inventory\n"..
|
||||
description = S("Place a sign behind the robot\ntaken from the signs inventory\n"..
|
||||
"<slot> is the inventory slot (1..6)"),
|
||||
check = function(slot)
|
||||
slot = tonumber(slot) or 1
|
||||
@ -277,7 +275,7 @@ local function dig_sign(base_pos, robot_pos, param2, slot)
|
||||
local err_code = meta:get_int("err_code")
|
||||
local name = meta:get_string("sign_name")
|
||||
if cmnd == "" then
|
||||
return signs_bot.ERROR, I("Error: No sign available")
|
||||
return signs_bot.ERROR, S("Error: No sign available")
|
||||
end
|
||||
if lib.not_protected(base_pos, pos1) then
|
||||
local node = tubelib2.get_node_lvm(pos1)
|
||||
@ -289,18 +287,18 @@ local function dig_sign(base_pos, robot_pos, param2, slot)
|
||||
minetest.remove_node(pos1)
|
||||
if not put_inv_sign(base_pos, slot, sign) then
|
||||
signs_bot.lib.drop_items(robot_pos, sign)
|
||||
return signs_bot.ERROR, I("Error: Signs inventory slot is occupied")
|
||||
return signs_bot.ERROR, S("Error: Signs inventory slot is occupied")
|
||||
end
|
||||
return signs_bot.DONE
|
||||
end
|
||||
return signs_bot.ERROR, I("Error: Position is protected")
|
||||
return signs_bot.ERROR, S("Error: Position is protected")
|
||||
end
|
||||
|
||||
signs_bot.register_botcommand("dig_sign", {
|
||||
mod = "sign",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Dig the sign in front of the robot\n"..
|
||||
description = S("Dig the sign in front of the robot\n"..
|
||||
"and add it to the signs inventory.\n"..
|
||||
"<slot> is the inventory slot (1..6)"),
|
||||
check = function(slot)
|
||||
@ -317,23 +315,26 @@ local function trash_sign(base_pos, robot_pos, param2, slot)
|
||||
local pos1 = lib.dest_pos(robot_pos, param2, {0})
|
||||
local cmnd = M(pos1):get_string("signs_bot_cmnd")
|
||||
if cmnd == "" then
|
||||
return signs_bot.ERROR, I("Error: No sign available")
|
||||
return signs_bot.ERROR, S("Error: No sign available")
|
||||
end
|
||||
if lib.not_protected(base_pos, pos1) then
|
||||
local node = tubelib2.get_node_lvm(pos1)
|
||||
local sign = ItemStack("signs_bot:sign_cmnd")
|
||||
minetest.remove_node(pos1)
|
||||
signs_bot.bot_inv_put_item(base_pos, slot, sign)
|
||||
local leftover = signs_bot.bot_inv_put_item(base_pos, slot, sign)
|
||||
if leftover and leftover:get_count() > 0 then
|
||||
signs_bot.lib.drop_items(robot_pos, leftover)
|
||||
end
|
||||
return signs_bot.DONE
|
||||
end
|
||||
return signs_bot.ERROR, I("Error: Position is protected")
|
||||
return signs_bot.ERROR, S("Error: Position is protected")
|
||||
end
|
||||
|
||||
signs_bot.register_botcommand("trash_sign", {
|
||||
mod = "sign",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = I("Dig the sign in front of the robot\n"..
|
||||
description = S("Dig the sign in front of the robot\n"..
|
||||
"and add the cleared sign to\nthe item iventory.\n"..
|
||||
"<slot> is the inventory slot (1..8)"),
|
||||
check = function(slot)
|
||||
@ -358,14 +359,14 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "sign_cmnd", {
|
||||
name = I("Sign 'command'"),
|
||||
name = S("Sign 'command'"),
|
||||
data = {
|
||||
item = "signs_bot:sign_cmnd",
|
||||
text = table.concat({
|
||||
I("The 'command' sign can be programmed by the player."),
|
||||
I("Place the sign in front of you and use the node menu to program your sequence of bot commands."),
|
||||
I("The menu has an edit field for your commands and a help page with all available commands."),
|
||||
I("The help page has a copy button to simplify the programming."),
|
||||
S("The 'command' sign can be programmed by the player."),
|
||||
S("Place the sign in front of you and use the node menu to program your sequence of bot commands."),
|
||||
S("The menu has an edit field for your commands and a help page with all available commands."),
|
||||
S("The help page has a copy button to simplify the programming."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,13 +13,12 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
local ci = dofile(MP.."/interpreter.lua")
|
||||
|
||||
local lib = signs_bot.lib
|
||||
@ -73,7 +72,7 @@ end
|
||||
function signs_bot.get_commands()
|
||||
local tbl = {}
|
||||
for _,mod in ipairs(SortedMods) do
|
||||
tbl[#tbl+1] = mod.." "..I("commands:")
|
||||
tbl[#tbl+1] = mod.." "..S("commands:")
|
||||
for _,cmnd in ipairs(SortedKeys[mod]) do
|
||||
local item = tCommands[cmnd]
|
||||
tbl[#tbl+1] = " "..item.name.." "..item.params
|
||||
@ -90,7 +89,7 @@ function signs_bot.get_help_text(cmnd)
|
||||
return item.description
|
||||
end
|
||||
end
|
||||
return I("unknown command")
|
||||
return S("unknown command")
|
||||
end
|
||||
|
||||
function signs_bot.check_commands(pos, text)
|
||||
@ -154,12 +153,16 @@ local function activate_sensor(pos, param2)
|
||||
end
|
||||
end
|
||||
|
||||
local function bot_error(base_pos, mem, err)
|
||||
local function bot_error(base_pos, mem, err, cmd)
|
||||
minetest.sound_play('signs_bot_error', {pos = base_pos})
|
||||
minetest.sound_play('signs_bot_error', {pos = mem.robot_pos})
|
||||
print(err)
|
||||
if cmd then
|
||||
signs_bot.infotext(base_pos, err .. ":\n'" .. cmd .. "'")
|
||||
mem.error = err .. ": '" .. cmd .. "'"
|
||||
else
|
||||
signs_bot.infotext(base_pos, err)
|
||||
mem.error = true
|
||||
mem.error = err
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
@ -176,9 +179,9 @@ local function power_consumption(mem, cmnd)
|
||||
end
|
||||
|
||||
function signs_bot.run_next_command(base_pos, mem)
|
||||
local res, err = ci.run_script(base_pos, mem)
|
||||
local res, err, cmd = ci.run_script(base_pos, mem)
|
||||
if res == ci.ERROR then
|
||||
return bot_error(base_pos, mem, err)
|
||||
return bot_error(base_pos, mem, err, cmd)
|
||||
elseif res == ci.EXIT then
|
||||
signs_bot.stop_robot(base_pos, mem)
|
||||
return false
|
||||
@ -198,31 +201,31 @@ end
|
||||
signs_bot.register_botcommand("repeat", {
|
||||
mod = "core",
|
||||
params = "<num>",
|
||||
description = I("start of a 'repeat..end' block"),
|
||||
description = S("start of a 'repeat..end' block"),
|
||||
})
|
||||
|
||||
signs_bot.register_botcommand("end", {
|
||||
mod = "core",
|
||||
params = "",
|
||||
description = I("end command of a 'repeat..end' block"),
|
||||
description = S("end command of a 'repeat..end' block"),
|
||||
})
|
||||
|
||||
signs_bot.register_botcommand("call", {
|
||||
mod = "core",
|
||||
params = "<label>",
|
||||
description = I("call a subroutine (with 'return' statement)"),
|
||||
description = S("call a subroutine (with 'return' statement)"),
|
||||
})
|
||||
|
||||
signs_bot.register_botcommand("return", {
|
||||
mod = "core",
|
||||
params = "",
|
||||
description = I("return from a subroutine"),
|
||||
description = S("return from a subroutine"),
|
||||
})
|
||||
|
||||
signs_bot.register_botcommand("jump", {
|
||||
mod = "core",
|
||||
params = "<label>",
|
||||
description = I("jump to a label"),
|
||||
description = S("jump to a label"),
|
||||
})
|
||||
|
||||
local function move(mem, any_sensor)
|
||||
@ -242,7 +245,7 @@ signs_bot.register_botcommand("move", {
|
||||
mod = "move",
|
||||
params = "<steps>",
|
||||
num_param = 1,
|
||||
description = I([[Move the robot 1..999 steps forward
|
||||
description = S([[Move the robot 1..999 steps forward
|
||||
without paying attention to any signs.
|
||||
Up and down movements also become
|
||||
counted as steps.]]),
|
||||
@ -262,7 +265,7 @@ signs_bot.register_botcommand("cond_move", {
|
||||
mod = "move",
|
||||
params = "",
|
||||
num_param = 0,
|
||||
description = I([[Walk until a sign or obstacle is
|
||||
description = S([[Walk until a sign or obstacle is
|
||||
reached. Then continue with the next command.
|
||||
When a sign has been reached,
|
||||
the current program is ended
|
||||
@ -280,4 +283,22 @@ new program from the sign]]),
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
signs_bot.register_botcommand("print", {
|
||||
mod = "debug",
|
||||
params = "<text>",
|
||||
num_param = 1,
|
||||
description = S([[Print given text as chat message.
|
||||
For two or more words, use the '*' character
|
||||
instead of spaces, like "Hello*world"]]),
|
||||
check = function(text)
|
||||
return text ~= ""
|
||||
end,
|
||||
cmnd = function(base_pos, mem, text)
|
||||
text = text:gsub("*", " ")
|
||||
local owner = M(base_pos):get_string("owner")
|
||||
if owner ~= "" and text ~= "" then
|
||||
minetest.chat_send_player(owner, "Bot: " .. text)
|
||||
end
|
||||
return signs_bot.DONE
|
||||
end,
|
||||
})
|
||||
|
101
signs_bot/compost.lua
Normal file
@ -0,0 +1,101 @@
|
||||
--[[
|
||||
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Signs Bot: Commands for the compost mod
|
||||
|
||||
]]--
|
||||
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local NUM_LEAVES = 2
|
||||
|
||||
-- we reuse the minecart hopper API here
|
||||
local function additem(mem, stack)
|
||||
local pos = signs_bot.lib.next_pos(mem.robot_pos, mem.robot_param2)
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef.minecart_hopper_additem then
|
||||
return ndef.minecart_hopper_additem(pos, stack)
|
||||
end
|
||||
|
||||
pos = {x = pos.x, y = pos.y - 1, z = pos.z}
|
||||
node = minetest.get_node(pos)
|
||||
ndef = minetest.registered_nodes[node.name]
|
||||
if ndef.minecart_hopper_additem then
|
||||
return ndef.minecart_hopper_additem(pos, stack)
|
||||
end
|
||||
|
||||
return stack
|
||||
end
|
||||
|
||||
local function takeitem(mem)
|
||||
local pos = signs_bot.lib.next_pos(mem.robot_pos, mem.robot_param2)
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef.minecart_hopper_takeitem then
|
||||
return ndef.minecart_hopper_takeitem(pos, 1)
|
||||
end
|
||||
|
||||
pos = {x = pos.x, y = pos.y - 1, z = pos.z}
|
||||
node = minetest.get_node(pos)
|
||||
ndef = minetest.registered_nodes[node.name]
|
||||
if ndef.minecart_hopper_takeitem then
|
||||
return ndef.minecart_hopper_takeitem(pos, 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if minetest.global_exists("signs_bot") then
|
||||
|
||||
signs_bot.register_botcommand("add_compost", {
|
||||
mod = "compost",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = S("Put 2 leaves into the compost barrel\n"..
|
||||
"<slot> is the bot inventory slot (1..8)\n"..
|
||||
"with the leaves."),
|
||||
check = function(slot)
|
||||
slot = tonumber(slot) or 0
|
||||
return slot > 0 and slot < 9
|
||||
end,
|
||||
cmnd = function(base_pos, mem, slot)
|
||||
slot = tonumber(slot) or 0
|
||||
local taken = signs_bot.bot_inv_take_item(base_pos, slot, NUM_LEAVES)
|
||||
local leftover = additem(mem, taken)
|
||||
if leftover and leftover:get_count() > 0 then
|
||||
signs_bot.bot_inv_put_item(base_pos, slot, leftover)
|
||||
end
|
||||
return signs_bot.DONE
|
||||
end,
|
||||
})
|
||||
|
||||
signs_bot.register_botcommand("take_compost", {
|
||||
mod = "compost",
|
||||
params = "<slot>",
|
||||
num_param = 1,
|
||||
description = S("Take a compost item from the barrel.\n"..
|
||||
"<slot> (1..8 or 0 for the first free slot) is the bot\n"..
|
||||
"slot for the compost item."),
|
||||
check = function(num, slot)
|
||||
slot = tonumber(slot) or 0
|
||||
return slot >= 0 and slot < 9
|
||||
end,
|
||||
cmnd = function(base_pos, mem, slot)
|
||||
slot = tonumber(slot) or 0
|
||||
local taken = takeitem(mem)
|
||||
local leftover = signs_bot.bot_inv_put_item(base_pos, slot, taken)
|
||||
if leftover and leftover:get_count() > 0 then
|
||||
signs_bot.lib.drop_items(mem.robot_pos, leftover)
|
||||
end
|
||||
return signs_bot.DONE
|
||||
end,
|
||||
})
|
||||
end
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,20 +13,18 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
local CYCLE_TIME = 4
|
||||
|
||||
local function update_infotext(pos, dest_pos, dest_idx)
|
||||
M(pos):set_string("infotext", I("Crop Sensor: Connected with ")..S(dest_pos).." / "..dest_idx)
|
||||
M(pos):set_string("infotext", S("Crop Sensor: Connected with").." "..P2S(dest_pos).." / "..dest_idx)
|
||||
end
|
||||
|
||||
local function swap_node(pos, name)
|
||||
@ -56,7 +54,7 @@ local function node_timer(pos)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:crop_sensor", {
|
||||
description = I("Crop Sensor"),
|
||||
description = S("Crop Sensor"),
|
||||
inventory_image = "signs_bot_sensor_crop_inv.png",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
@ -77,7 +75,7 @@ minetest.register_node("signs_bot:crop_sensor", {
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
meta:set_string("infotext", "Crop Sensor: Not connected")
|
||||
meta:set_string("infotext", S("Crop Sensor: Not connected"))
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
local node = minetest.get_node(pos)
|
||||
meta:set_int("param2", (node.param2 + 2) % 4)
|
||||
@ -87,6 +85,7 @@ minetest.register_node("signs_bot:crop_sensor", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -95,7 +94,7 @@ minetest.register_node("signs_bot:crop_sensor", {
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:crop_sensor_on", {
|
||||
description = I("Crop Sensor"),
|
||||
description = S("Crop Sensor"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
@ -117,6 +116,7 @@ minetest.register_node("signs_bot:crop_sensor_on", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -150,13 +150,13 @@ minetest.register_lbm({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "crop_sensor", {
|
||||
name = I("Crop Sensor"),
|
||||
name = S("Crop Sensor"),
|
||||
data = {
|
||||
item = "signs_bot:crop_sensor",
|
||||
text = table.concat({
|
||||
I("The Crop Sensor sends cyclical signals when, for example, wheat is fully grown."),
|
||||
I("The sensor range is one node/meter."),
|
||||
I("The sensor has an active side (red) that must point to the crop/field."),
|
||||
S("The Crop Sensor sends cyclical signals when, for example, wheat is fully grown."),
|
||||
S("The sensor range is one node/meter."),
|
||||
S("The sensor has an active side (red) that must point to the crop/field."),
|
||||
|
||||
}, "\n")
|
||||
},
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,20 +13,17 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local CYCLE_TIME = 2
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
local function update_infotext(pos, dest_pos, cmnd)
|
||||
M(pos):set_string("infotext", I("Signal Delayer: Connected with ")..S(dest_pos).." / "..cmnd)
|
||||
M(pos):set_string("infotext", S("Signal Delayer: Connected with").." "..P2S(dest_pos).." / "..cmnd)
|
||||
end
|
||||
|
||||
local function infotext(pos)
|
||||
@ -34,19 +31,19 @@ local function infotext(pos)
|
||||
local dest_pos = meta:get_string("signal_pos")
|
||||
local signal = meta:get_string("signal_data")
|
||||
if dest_pos ~= "" and signal ~= "" then
|
||||
update_infotext(pos, P(dest_pos), signal)
|
||||
update_infotext(pos, S2P(dest_pos), signal)
|
||||
end
|
||||
end
|
||||
|
||||
local function formspec(meta)
|
||||
local label = minetest.formspec_escape(I("Delay time [sec]:"))
|
||||
local label = minetest.formspec_escape(S("Delay time [sec]:"))
|
||||
local value = minetest.formspec_escape(meta:get_int("time"))
|
||||
return "size[4,3]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"field[0.3,1;4,1;time;"..label..";"..value.."]"..
|
||||
"button_exit[1,2.2;2,1;start;"..I("Start").."]"
|
||||
"button_exit[1,2.2;2,1;start;"..S("Start").."]"
|
||||
end
|
||||
|
||||
-- Used by the pairing tool
|
||||
@ -141,7 +138,7 @@ local function on_receive_fields(pos, formname, fields, player)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:delayer", {
|
||||
description = I("Signal Delayer"),
|
||||
description = S("Signal Delayer"),
|
||||
inventory_image = "signs_bot_delayer_inv.png",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
@ -172,6 +169,7 @@ minetest.register_node("signs_bot:delayer", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -180,7 +178,7 @@ minetest.register_node("signs_bot:delayer", {
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:delayer_loaded", {
|
||||
description = I("Signal Delayer"),
|
||||
description = S("Signal Delayer"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
@ -203,6 +201,7 @@ minetest.register_node("signs_bot:delayer_loaded", {
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
is_ground_content = false,
|
||||
diggable = false,
|
||||
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
|
||||
@ -210,7 +209,7 @@ minetest.register_node("signs_bot:delayer_loaded", {
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:delayer_on", {
|
||||
description = I("Signal Delayer"),
|
||||
description = S("Signal Delayer"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
@ -230,6 +229,7 @@ minetest.register_node("signs_bot:delayer_on", {
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
is_ground_content = false,
|
||||
diggable = false,
|
||||
groups = {sign_bot_sensor = 1, not_in_creative_inventory = 1},
|
||||
@ -261,12 +261,12 @@ minetest.register_lbm({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "delayer", {
|
||||
name = I("Signal Delayer"),
|
||||
name = S("Signal Delayer"),
|
||||
data = {
|
||||
item = "signs_bot:delayer",
|
||||
text = table.concat({
|
||||
I("Signals are forwarded delayed. Subsequent signals are queued."),
|
||||
I("The delay time can be configured."),
|
||||
S("Signals are forwarded delayed. Subsequent signals are queued."),
|
||||
S("The delay time can be configured."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -1,13 +1,26 @@
|
||||
--[[
|
||||
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPLv3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Signs Bot: Bot Flap
|
||||
|
||||
]]--
|
||||
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
signs_bot.doc = {}
|
||||
|
||||
if not minetest.get_modpath("doc") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
|
||||
local function formspec(data)
|
||||
if data.image then
|
||||
local image = "image["..(doc.FORMSPEC.ENTRY_WIDTH - 3)..",0;3,2;"..data.image.."]"
|
||||
@ -24,112 +37,113 @@ local function formspec(data)
|
||||
end
|
||||
|
||||
local start_doc = table.concat({
|
||||
I("After you have placed the Signs Bot Box, you can start the bot by means of the 'On' button in the box menu."),
|
||||
I("The bot then runs straight up until it reaches an obstacle (a step with two or more blocks up or down or a sign.)"),
|
||||
I("If the bot first reaches a sign it will execute the commands on the sign."),
|
||||
I("If the command(s) on the sign is e.g. 'turn_around', the bot turns and goes back."),
|
||||
I("In this case, the bot reaches his box again and turns off."),
|
||||
S("After you have placed the Signs Bot Box, you can start the bot by means of the 'On' button in the box menu."),
|
||||
S("If the bot returns to its box right away, you will likely need to charge it with electrical energy (techage) first."),
|
||||
S("The bot then runs straight up until it reaches an obstacle (a step with two or more blocks up or down or a sign.)"),
|
||||
S("If the bot first reaches a sign it will execute the commands on the sign."),
|
||||
S("If the command(s) on the sign is e.g. 'turn_around', the bot turns and goes back."),
|
||||
S("In this case, the bot reaches his box again and turns off."),
|
||||
"",
|
||||
I("The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for other items (to be placed/dug by the bot)."),
|
||||
I("This inventory simulates the bot internal inventory."),
|
||||
I("That means you will only have access to the inventory if the bot is turned off ('sitting' in his box)."),
|
||||
S("The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for other items (to be placed/dug by the bot)."),
|
||||
S("This inventory simulates the bot internal inventory."),
|
||||
S("That means you will only have access to the inventory if the bot is turned off ('sitting' in his box)."),
|
||||
}, "\n")
|
||||
|
||||
local control_doc = table.concat({
|
||||
I("You simply control the direction of the bot by means of the 'turn left' and 'turn right' signs (signs with the arrow)."),
|
||||
I("The bot can run over steps (one block up/down). But there are also commands to move the bot up and down."),
|
||||
S("You simply control the direction of the bot by means of the 'turn left' and 'turn right' signs (signs with the arrow)."),
|
||||
S("The bot can run over steps (one block up/down). But there are also commands to move the bot up and down."),
|
||||
"",
|
||||
I("It is not necessary to mark a way back to the box."),
|
||||
I("With the command 'turn_off' the bot will turn off and be back in his box from every position."),
|
||||
I("The same applies if you turn off the bot by the box menu."),
|
||||
I("If the bot reaches a sign from the wrong direction (from back or sides) the sign will be ignored."),
|
||||
I("The bot will walk over."),
|
||||
S("It is not necessary to mark a way back to the box."),
|
||||
S("With the command 'turn_off' the bot will turn off and be back in his box from every position."),
|
||||
S("The same applies if you turn off the bot by the box menu."),
|
||||
S("If the bot reaches a sign from the wrong direction (from back or sides) the sign will be ignored."),
|
||||
S("The bot will walk over."),
|
||||
"",
|
||||
I("All predefined signs have a menu with a list of the bot commands."),
|
||||
I("These signs can't be changed, but you can craft and program your own signs."),
|
||||
I("For this you have to use the 'command' sign."),
|
||||
I("This sign has an edit field for your commands and a help page with all available commands."),
|
||||
I("The help page has a copy button to simplify the programming."),
|
||||
S("All predefined signs have a menu with a list of the bot commands."),
|
||||
S("These signs can't be changed, but you can craft and program your own signs."),
|
||||
S("For this you have to use the 'command' sign."),
|
||||
S("This sign has an edit field for your commands and a help page with all available commands."),
|
||||
S("The help page has a copy button to simplify the programming."),
|
||||
"",
|
||||
I("Also for your own signs it is important to know:"),
|
||||
I("After the execution of the last command of the sign, the bot falls back into its default behaviour and runs in its taken direction."),
|
||||
S("Also for your own signs it is important to know:"),
|
||||
S("After the execution of the last command of the sign, the bot falls back into its default behaviour and runs in its taken direction."),
|
||||
"",
|
||||
I("A standard job for the bot is to move items from one chest to another"),
|
||||
I("(chest or node with a chest like inventory)."),
|
||||
I("This can be done by means of the two signs 'take item' and 'add item'."),
|
||||
I("These signs have to be placed on top of chest nodes."),
|
||||
S("A standard job for the bot is to move items from one chest to another"),
|
||||
S("(chest or node with a chest like inventory)."),
|
||||
S("This can be done by means of the two signs 'take item' and 'add item'."),
|
||||
S("These signs have to be placed on top of chest nodes."),
|
||||
}, "\n")
|
||||
|
||||
local sensor_doc = table.concat({
|
||||
I("In addition to the signs the bot can be controlled by means of sensors."),
|
||||
I("Sensors like the Bot Sensor have two states: on and off."),
|
||||
I("If the Bot Sensor detects a bot it will switch to the state 'on' and"),
|
||||
I("sends a signal to a connected block, called an actuator."),
|
||||
S("In addition to the signs the bot can be controlled by means of sensors."),
|
||||
S("Sensors like the Bot Sensor have two states: on and off."),
|
||||
S("If the Bot Sensor detects a bot it will switch to the state 'on' and"),
|
||||
S("sends a signal to a connected block, called an actuator."),
|
||||
"",
|
||||
I("Sensors are:"),
|
||||
I("- Bot Sensor: Sends a signal when the robot passes by"),
|
||||
I("- Node Sensor: Sends a signal when it detects any node"),
|
||||
I("- Crop Sensor: Sends a signal when, for example wheat is fully grown"),
|
||||
I("- Bot Chest: Sends a signal depending on the chest state (empty, full)"),
|
||||
S("Sensors are:"),
|
||||
S("- Bot Sensor: Sends a signal when the robot passes by"),
|
||||
S("- Node Sensor: Sends a signal when it detects any node"),
|
||||
S("- Crop Sensor: Sends a signal when, for example wheat is fully grown"),
|
||||
S("- Bot Chest: Sends a signal depending on the chest state (empty, full)"),
|
||||
"",
|
||||
I("Actuators are:"),
|
||||
I("- Signs Bot Box: Can turn the bot off and on"),
|
||||
I("- Control Unit: Can be used to exchange the sign to lead the bot"),
|
||||
S("Actuators are:"),
|
||||
S("- Signs Bot Box: Can turn the bot off and on"),
|
||||
S("- Control Unit: Can be used to exchange the sign to lead the bot"),
|
||||
"",
|
||||
I("Additional sensors and actuator can be added by other mods."),
|
||||
S("Additional sensors and actuator can be added by other mods."),
|
||||
}, "\n")
|
||||
|
||||
|
||||
local tool_doc = table.concat({
|
||||
I("To send a signal from a sensor to an actuator, the sensor has to be connected (paired) with actuator."),
|
||||
I("To connect sensor and actuator, the Sensor Connection Tool has to be used."),
|
||||
I("Simply click with the tool on both blocks and the sensor will be connected with the actuator."),
|
||||
I("A successful connection is indicated by a ping/pong noise."),
|
||||
S("To send a signal from a sensor to an actuator, the sensor has to be connected (paired) with actuator."),
|
||||
S("To connect sensor and actuator, the Sensor Connection Tool has to be used."),
|
||||
S("Simply click with the tool on both blocks and the sensor will be connected with the actuator."),
|
||||
S("A successful connection is indicated by a ping/pong noise."),
|
||||
"",
|
||||
I("Before you connect sensor with actuator, take care that the actuator is in the requested state."),
|
||||
I("For example: If you want to start the Bot with a sensor, connect the sensor with the Bot Box,"),
|
||||
I("when the Bot is in the state 'on'. Otherwise the sensor signal will stop the Bot,"),
|
||||
I("instead of starting it."),
|
||||
S("Before you connect sensor with actuator, take care that the actuator is in the requested state."),
|
||||
S("For example: If you want to start the Bot with a sensor, connect the sensor with the Bot Box,"),
|
||||
S("when the Bot is in the state 'on'. Otherwise the sensor signal will stop the Bot,"),
|
||||
S("instead of starting it."),
|
||||
}, "\n")
|
||||
|
||||
|
||||
local inventory_doc = table.concat({
|
||||
I("The following applies to all commands that are used to place items in the bot inventory, like:"),
|
||||
S("The following applies to all commands that are used to place items in the bot inventory, like:"),
|
||||
"",
|
||||
I("- take_item <num> <slot>"),
|
||||
I("- pickup_items <slot>"),
|
||||
I("- trash_sign <slot>"),
|
||||
I("- harvest <slot>"),
|
||||
I("- dig_front <slot> <lvl>"),
|
||||
I("- dig_left <slot> <lvl>"),
|
||||
I("- dig_right <slot> <lvl>"),
|
||||
I("- dig_below <slot> <lvl>"),
|
||||
I("- dig_above <slot> <lvl>"),
|
||||
S("- take_item <num> <slot>"),
|
||||
S("- pickup_items <slot>"),
|
||||
S("- trash_sign <slot>"),
|
||||
S("- harvest <slot>"),
|
||||
S("- dig_front <slot> <lvl>"),
|
||||
S("- dig_left <slot> <lvl>"),
|
||||
S("- dig_right <slot> <lvl>"),
|
||||
S("- dig_below <slot> <lvl>"),
|
||||
S("- dig_above <slot> <lvl>"),
|
||||
"",
|
||||
I("If no slot or slot 0 was specified with the command (case A), all 8 slots of the bot inventory "),
|
||||
I("are checked one after the other. If a slot was specified (case B), only this slot is checked."),
|
||||
I("In both cases the following applies: If the slot is preconfigured and fits the item, "),
|
||||
I("or if the slot is not configured and empty, or is only partially filled with the item type "),
|
||||
I("(which should be added), then the items are added."),
|
||||
I("If not all items can be added, the remaining slots will be tried out in case A."),
|
||||
I("Anything that could not be added to your own inventory goes back."),
|
||||
S("If no slot or slot 0 was specified with the command (case A), all 8 slots of the bot inventory "),
|
||||
S("are checked one after the other. If a slot was specified (case B), only this slot is checked."),
|
||||
S("In both cases the following applies: If the slot is preconfigured and fits the item, "),
|
||||
S("or if the slot is not configured and empty, or is only partially filled with the item type "),
|
||||
S("(which should be added), then the items are added."),
|
||||
S("If not all items can be added, the remaining slots will be tried out in case A."),
|
||||
S("Anything that could not be added to your own inventory goes back."),
|
||||
"",
|
||||
I("The following applies to all commands that are used to take items from the bot inventory, like:"),
|
||||
S("The following applies to all commands that are used to take items from the bot inventory, like:"),
|
||||
"",
|
||||
I("- add_item <num> <slot>"),
|
||||
S("- add_item <num> <slot>"),
|
||||
"",
|
||||
I("It doesn't matter whether a slot is configured or not. The bot takes the first stack that "),
|
||||
I("it can find from its own inventory and tries to use it."),
|
||||
I("If a slot is specified, it only takes this, if no slot has been specified, it checks all of "),
|
||||
I("them one after the other, starting from slot 1 until it finds something."),
|
||||
I("If the number found is smaller than requested, he tries to take the rest out of any slot."),
|
||||
S("It doesn't matter whether a slot is configured or not. The bot takes the first stack that "),
|
||||
S("it can find from its own inventory and tries to use it."),
|
||||
S("If a slot is specified, it only takes this, if no slot has been specified, it checks all of "),
|
||||
S("them one after the other, starting from slot 1 until it finds something."),
|
||||
S("If the number found is smaller than requested, he tries to take the rest out of any slot."),
|
||||
}, "\n")
|
||||
|
||||
|
||||
doc.add_category("signs_bot",
|
||||
{
|
||||
name = I("Signs Bot"),
|
||||
description = I("A robot controlled by signs, used for automated work"),
|
||||
name = S("Signs Bot"),
|
||||
description = S("A robot controlled by signs, used for automated work"),
|
||||
sorting = "custom",
|
||||
sorting_data = {"start", "control", "sensor_doc", "tool",
|
||||
"box", "bot_flap", "duplicator",
|
||||
@ -141,26 +155,26 @@ doc.add_category("signs_bot",
|
||||
})
|
||||
|
||||
doc.add_entry("signs_bot", "start", {
|
||||
name = I("Start the Bot"),
|
||||
name = S("Start the Bot"),
|
||||
data = {text = start_doc, image = "signs_bot_doc_image.png"},
|
||||
})
|
||||
|
||||
doc.add_entry("signs_bot", "control", {
|
||||
name = I("Control the Bot"),
|
||||
name = S("Control the Bot"),
|
||||
data = {text = control_doc, image = "signs_bot_doc_image.png"},
|
||||
})
|
||||
|
||||
doc.add_entry("signs_bot", "sensor_doc", {
|
||||
name = I("Sensors and Actuators"),
|
||||
name = S("Sensors and Actuators"),
|
||||
data = {text = sensor_doc, image = "signs_bot_doc_image.png"},
|
||||
})
|
||||
|
||||
doc.add_entry("signs_bot", "tool", {
|
||||
name = I("Connecting sensors and actuator"),
|
||||
name = S("Connecting sensors and actuator"),
|
||||
data = {text = tool_doc, image = "signs_bot_doc_image.png"},
|
||||
})
|
||||
|
||||
doc.add_entry("signs_bot", "tool", {
|
||||
name = I("Bot inventory behavior"),
|
||||
name = S("Bot inventory behavior"),
|
||||
data = {text = inventory_doc, image = "signs_bot_doc_image.png"},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,13 +13,10 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -27,15 +24,15 @@ local formspec = "size[8,7.3]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[0.3,0;"..I("Input:").."]"..
|
||||
"list[context;inp;3,0;1,1;]"..
|
||||
"label[0.3,1;"..I("Template:").."]"..
|
||||
"list[context;temp;3,1;1,1;]"..
|
||||
"label[0.3,2;"..I("Output:").."]"..
|
||||
"list[context;outp;3,2;1,1;]"..
|
||||
"label[4,0;"..I("1. Place one 'cmnd' sign to be\n used as template.\n")..
|
||||
I("2. Add 'blank signs' to\n the input inventory.\n")..
|
||||
I("3. Take the copies\n from the output inventory.").."]"..
|
||||
"label[0.1,0.2;"..S("Template:").."]"..
|
||||
"list[context;temp;2,0;1,1;]"..
|
||||
"label[0.1,1.2;"..S("Input:").."]"..
|
||||
"list[context;inp;2,1;1,1;]"..
|
||||
"label[0.1,2.2;"..S("Output:").."]"..
|
||||
"list[context;outp;2,2;1,1;]"..
|
||||
"label[3,0.2;"..S("1. Place one 'cmnd' sign").."]"..
|
||||
"label[3,1.2;"..S("2. Add 'blank signs'").."]"..
|
||||
"label[3,2.2;"..S("3. Take the copies").."]"..
|
||||
"list[current_player;main;0,3.5;8,4;]"..
|
||||
"listring[context;inp]"..
|
||||
"listring[current_player;main]"..
|
||||
@ -108,7 +105,7 @@ local function on_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:duplicator", {
|
||||
description = I("Signs Duplicator"),
|
||||
description = S("Signs Duplicator"),
|
||||
stack_max = 1,
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
@ -167,7 +164,7 @@ local function formspec_user(cmnd)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:sign_user", {
|
||||
description = I('Sign "user"'),
|
||||
description = S('Sign "user"'),
|
||||
drawtype = "nodebox",
|
||||
inventory_image = "signs_bot_sign_user.png",
|
||||
node_box = {
|
||||
@ -210,7 +207,7 @@ minetest.register_node("signs_bot:sign_user", {
|
||||
|
||||
signs_bot.register_sign({
|
||||
name = "sign_blank",
|
||||
description = I('Sign "blank"'),
|
||||
description = S('Sign "blank"'),
|
||||
commands = "",
|
||||
image = "signs_bot_sign_blank.png",
|
||||
})
|
||||
@ -226,17 +223,17 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "duplicator", {
|
||||
name = I("Signs Duplicator"),
|
||||
name = S("Signs Duplicator"),
|
||||
data = {
|
||||
item = "signs_bot:duplicator",
|
||||
text = table.concat({
|
||||
I("The Duplicator can be used to make copies of signs."),
|
||||
I("1. Put one 'cmnd' sign to be used as template into the 'Template' inventory"),
|
||||
I("2. Add one or several 'blank signs' to the 'Input' inventory."),
|
||||
I("3. Take the copies from the 'Output' inventory."),
|
||||
S("The Duplicator can be used to make copies of signs."),
|
||||
S("1. Put one 'cmnd' sign to be used as template into the 'Template' inventory"),
|
||||
S("2. Add one or several 'blank signs' to the 'Input' inventory."),
|
||||
S("3. Take the copies from the 'Output' inventory."),
|
||||
"",
|
||||
I("Written books [default:book_written] can alternatively be used as template"),
|
||||
I("Already written signs can be used as input, too."),
|
||||
S("Written books [default:book_written] can alternatively be used as template"),
|
||||
S("Already written signs can be used as input, too."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
@ -244,10 +241,10 @@ end
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "sign_blank", {
|
||||
name = I('Sign "blank"'),
|
||||
name = S('Sign "blank"'),
|
||||
data = {
|
||||
item = "signs_bot:sign_blank",
|
||||
text = I("Needed as input for the Duplicator.")
|
||||
text = S("Needed as input for the Duplicator.")
|
||||
},
|
||||
})
|
||||
end
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,22 +13,18 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
|
||||
local lib = signs_bot.lib
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local function update_infotext(pos, dest_pos, cmnd)
|
||||
M(pos):set_string("infotext", I("Sensor Extender: Connected with ")..S(dest_pos).." / "..cmnd)
|
||||
M(pos):set_string("infotext", S("Sensor Extender: Connected with").." "..P2S(dest_pos).." / "..cmnd)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:sensor_extender", {
|
||||
description = I("Sensor Extender"),
|
||||
description = S("Sensor Extender"),
|
||||
inventory_image = "signs_bot_extender_inv.png",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
@ -54,12 +50,13 @@ minetest.register_node("signs_bot:sensor_extender", {
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
meta:set_string("infotext", I("Sensor Extender: Not connected"))
|
||||
meta:set_string("infotext", S("Sensor Extender: Not connected"))
|
||||
end,
|
||||
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -68,7 +65,7 @@ minetest.register_node("signs_bot:sensor_extender", {
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:sensor_extender_on", {
|
||||
description = I("Sensor Extender"),
|
||||
description = S("Sensor Extender"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "connected",
|
||||
@ -108,6 +105,7 @@ minetest.register_node("signs_bot:sensor_extender_on", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -127,13 +125,13 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "sensor_extender", {
|
||||
name = I("Sensor Extender"),
|
||||
name = S("Sensor Extender"),
|
||||
data = {
|
||||
item = "signs_bot:sensor_extender",
|
||||
text = table.concat({
|
||||
I("With the Sensor Extender, sensor signals can be sent to more than one actuator."),
|
||||
I("Place one or more extender nearby the sensor and connect each extender"),
|
||||
I("with one further actuator by means of the Connection Tool."),
|
||||
S("With the Sensor Extender, sensor signals can be sent to more than one actuator."),
|
||||
S("Place one or more extender nearby the sensor and connect each extender"),
|
||||
S("with one further actuator by means of the Connection Tool."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
458
signs_bot/i18n.py
Normal file
@ -0,0 +1,458 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Script to generate the template file and update the translation files.
|
||||
# Copy the script into the mod or modpack root folder and run it there.
|
||||
#
|
||||
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
|
||||
# LGPLv2.1+
|
||||
#
|
||||
# See https://github.com/minetest-tools/update_translations for
|
||||
# potential future updates to this script.
|
||||
|
||||
from __future__ import print_function
|
||||
import os, fnmatch, re, shutil, errno
|
||||
from sys import argv as _argv
|
||||
from sys import stderr as _stderr
|
||||
|
||||
# Running params
|
||||
params = {"recursive": False,
|
||||
"help": False,
|
||||
"mods": False,
|
||||
"verbose": False,
|
||||
"folders": [],
|
||||
"no-old-file": False,
|
||||
"break-long-lines": False,
|
||||
"sort": False
|
||||
}
|
||||
# Available CLI options
|
||||
options = {"recursive": ['--recursive', '-r'],
|
||||
"help": ['--help', '-h'],
|
||||
"mods": ['--installed-mods', '-m'],
|
||||
"verbose": ['--verbose', '-v'],
|
||||
"no-old-file": ['--no-old-file', '-O'],
|
||||
"break-long-lines": ['--break-long-lines', '-b'],
|
||||
"sort": ['--sort', '-s']
|
||||
}
|
||||
|
||||
# Strings longer than this will have extra space added between
|
||||
# them in the translation files to make it easier to distinguish their
|
||||
# beginnings and endings at a glance
|
||||
doublespace_threshold = 80
|
||||
|
||||
def set_params_folders(tab: list):
|
||||
'''Initialize params["folders"] from CLI arguments.'''
|
||||
# Discarding argument 0 (tool name)
|
||||
for param in tab[1:]:
|
||||
stop_param = False
|
||||
for option in options:
|
||||
if param in options[option]:
|
||||
stop_param = True
|
||||
break
|
||||
if not stop_param:
|
||||
params["folders"].append(os.path.abspath(param))
|
||||
|
||||
def set_params(tab: list):
|
||||
'''Initialize params from CLI arguments.'''
|
||||
for option in options:
|
||||
for option_name in options[option]:
|
||||
if option_name in tab:
|
||||
params[option] = True
|
||||
break
|
||||
|
||||
def print_help(name):
|
||||
'''Prints some help message.'''
|
||||
print(f'''SYNOPSIS
|
||||
{name} [OPTIONS] [PATHS...]
|
||||
DESCRIPTION
|
||||
{', '.join(options["help"])}
|
||||
prints this help message
|
||||
{', '.join(options["recursive"])}
|
||||
run on all subfolders of paths given
|
||||
{', '.join(options["mods"])}
|
||||
run on locally installed modules
|
||||
{', '.join(options["no-old-file"])}
|
||||
do not create *.old files
|
||||
{', '.join(options["sort"])}
|
||||
sort output strings alphabetically
|
||||
{', '.join(options["break-long-lines"])}
|
||||
add extra line breaks before and after long strings
|
||||
{', '.join(options["verbose"])}
|
||||
add output information
|
||||
''')
|
||||
|
||||
|
||||
def main():
|
||||
'''Main function'''
|
||||
set_params(_argv)
|
||||
set_params_folders(_argv)
|
||||
if params["help"]:
|
||||
print_help(_argv[0])
|
||||
elif params["recursive"] and params["mods"]:
|
||||
print("Option --installed-mods is incompatible with --recursive")
|
||||
else:
|
||||
# Add recursivity message
|
||||
print("Running ", end='')
|
||||
if params["recursive"]:
|
||||
print("recursively ", end='')
|
||||
# Running
|
||||
if params["mods"]:
|
||||
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
|
||||
run_all_subfolders("~/.minetest/mods")
|
||||
elif len(params["folders"]) >= 2:
|
||||
print("on folder list:", params["folders"])
|
||||
for f in params["folders"]:
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(f)
|
||||
else:
|
||||
update_folder(f)
|
||||
elif len(params["folders"]) == 1:
|
||||
print("on folder", params["folders"][0])
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(params["folders"][0])
|
||||
else:
|
||||
update_folder(params["folders"][0])
|
||||
else:
|
||||
print("on folder", os.path.abspath("./"))
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(os.path.abspath("./"))
|
||||
else:
|
||||
update_folder(os.path.abspath("./"))
|
||||
|
||||
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
|
||||
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
|
||||
pattern_lua_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
|
||||
pattern_lua_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
|
||||
pattern_lua_bracketed_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
|
||||
pattern_lua_bracketed_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
|
||||
|
||||
# Handles "concatenation" .. " of strings"
|
||||
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
|
||||
|
||||
pattern_tr = re.compile(r'(.*?[^@])=(.*)')
|
||||
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
|
||||
pattern_tr_filename = re.compile(r'\.tr$')
|
||||
pattern_po_language_code = re.compile(r'(.*)\.po$')
|
||||
|
||||
#attempt to read the mod's name from the mod.conf file. Returns None on failure
|
||||
def get_modname(folder):
|
||||
try:
|
||||
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
|
||||
for line in mod_conf:
|
||||
match = pattern_name.match(line)
|
||||
if match:
|
||||
return match.group(1)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return None
|
||||
|
||||
#If there are already .tr files in /locale, returns a list of their names
|
||||
def get_existing_tr_files(folder):
|
||||
out = []
|
||||
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
|
||||
for name in files:
|
||||
if pattern_tr_filename.search(name):
|
||||
out.append(name)
|
||||
return out
|
||||
|
||||
# A series of search and replaces that massage a .po file's contents into
|
||||
# a .tr file's equivalent
|
||||
def process_po_file(text):
|
||||
# The first three items are for unused matches
|
||||
text = re.sub(r'#~ msgid "', "", text)
|
||||
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
|
||||
text = re.sub(r'"\n#~ msgstr "', "=", text)
|
||||
# comment lines
|
||||
text = re.sub(r'#.*\n', "", text)
|
||||
# converting msg pairs into "=" pairs
|
||||
text = re.sub(r'msgid "', "", text)
|
||||
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
|
||||
text = re.sub(r'"\nmsgstr "', "=", text)
|
||||
# various line breaks and escape codes
|
||||
text = re.sub(r'"\n"', "", text)
|
||||
text = re.sub(r'"\n', "\n", text)
|
||||
text = re.sub(r'\\"', '"', text)
|
||||
text = re.sub(r'\\n', '@n', text)
|
||||
# remove header text
|
||||
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
|
||||
# remove double-spaced lines
|
||||
text = re.sub(r'\n\n', '\n', text)
|
||||
return text
|
||||
|
||||
# Go through existing .po files and, if a .tr file for that language
|
||||
# *doesn't* exist, convert it and create it.
|
||||
# The .tr file that results will subsequently be reprocessed so
|
||||
# any "no longer used" strings will be preserved.
|
||||
# Note that "fuzzy" tags will be lost in this process.
|
||||
def process_po_files(folder, modname):
|
||||
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
|
||||
for name in files:
|
||||
code_match = pattern_po_language_code.match(name)
|
||||
if code_match == None:
|
||||
continue
|
||||
language_code = code_match.group(1)
|
||||
tr_name = modname + "." + language_code + ".tr"
|
||||
tr_file = os.path.join(root, tr_name)
|
||||
if os.path.exists(tr_file):
|
||||
if params["verbose"]:
|
||||
print(f"{tr_name} already exists, ignoring {name}")
|
||||
continue
|
||||
fname = os.path.join(root, name)
|
||||
with open(fname, "r", encoding='utf-8') as po_file:
|
||||
if params["verbose"]:
|
||||
print(f"Importing translations from {name}")
|
||||
text = process_po_file(po_file.read())
|
||||
with open(tr_file, "wt", encoding='utf-8') as tr_out:
|
||||
tr_out.write(text)
|
||||
|
||||
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
|
||||
# Creates a directory if it doesn't exist, silently does
|
||||
# nothing if it already exists
|
||||
def mkdir_p(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as exc: # Python >2.5
|
||||
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else: raise
|
||||
|
||||
# Converts the template dictionary to a text to be written as a file
|
||||
# dKeyStrings is a dictionary of localized string to source file sets
|
||||
# dOld is a dictionary of existing translations and comments from
|
||||
# the previous version of this text
|
||||
def strings_to_text(dkeyStrings, dOld, mod_name, header_comments):
|
||||
lOut = [f"# textdomain: {mod_name}\n"]
|
||||
if header_comments is not None:
|
||||
lOut.append(header_comments)
|
||||
|
||||
dGroupedBySource = {}
|
||||
|
||||
for key in dkeyStrings:
|
||||
sourceList = list(dkeyStrings[key])
|
||||
if params["sort"]:
|
||||
sourceList.sort()
|
||||
sourceString = "\n".join(sourceList)
|
||||
listForSource = dGroupedBySource.get(sourceString, [])
|
||||
listForSource.append(key)
|
||||
dGroupedBySource[sourceString] = listForSource
|
||||
|
||||
lSourceKeys = list(dGroupedBySource.keys())
|
||||
lSourceKeys.sort()
|
||||
for source in lSourceKeys:
|
||||
localizedStrings = dGroupedBySource[source]
|
||||
if params["sort"]:
|
||||
localizedStrings.sort()
|
||||
lOut.append("")
|
||||
lOut.append(source)
|
||||
lOut.append("")
|
||||
for localizedString in localizedStrings:
|
||||
val = dOld.get(localizedString, {})
|
||||
translation = val.get("translation", "")
|
||||
comment = val.get("comment")
|
||||
if params["break-long-lines"] and len(localizedString) > doublespace_threshold and not lOut[-1] == "":
|
||||
lOut.append("")
|
||||
if comment != None:
|
||||
lOut.append(comment)
|
||||
lOut.append(f"{localizedString}={translation}")
|
||||
if params["break-long-lines"] and len(localizedString) > doublespace_threshold:
|
||||
lOut.append("")
|
||||
|
||||
|
||||
unusedExist = False
|
||||
for key in dOld:
|
||||
if key not in dkeyStrings:
|
||||
val = dOld[key]
|
||||
translation = val.get("translation")
|
||||
comment = val.get("comment")
|
||||
# only keep an unused translation if there was translated
|
||||
# text or a comment associated with it
|
||||
if translation != None and (translation != "" or comment):
|
||||
if not unusedExist:
|
||||
unusedExist = True
|
||||
lOut.append("\n\n##### not used anymore #####\n")
|
||||
if params["break-long-lines"] and len(key) > doublespace_threshold and not lOut[-1] == "":
|
||||
lOut.append("")
|
||||
if comment != None:
|
||||
lOut.append(comment)
|
||||
lOut.append(f"{key}={translation}")
|
||||
if params["break-long-lines"] and len(key) > doublespace_threshold:
|
||||
lOut.append("")
|
||||
return "\n".join(lOut) + '\n'
|
||||
|
||||
# Writes a template.txt file
|
||||
# dkeyStrings is the dictionary returned by generate_template
|
||||
def write_template(templ_file, dkeyStrings, mod_name):
|
||||
# read existing template file to preserve comments
|
||||
existing_template = import_tr_file(templ_file)
|
||||
|
||||
text = strings_to_text(dkeyStrings, existing_template[0], mod_name, existing_template[2])
|
||||
mkdir_p(os.path.dirname(templ_file))
|
||||
with open(templ_file, "wt", encoding='utf-8') as template_file:
|
||||
template_file.write(text)
|
||||
|
||||
|
||||
# Gets all translatable strings from a lua file
|
||||
def read_lua_file_strings(lua_file):
|
||||
lOut = []
|
||||
with open(lua_file, encoding='utf-8') as text_file:
|
||||
text = text_file.read()
|
||||
#TODO remove comments here
|
||||
|
||||
text = re.sub(pattern_concat, "", text)
|
||||
|
||||
strings = []
|
||||
for s in pattern_lua_s.findall(text):
|
||||
strings.append(s[1])
|
||||
for s in pattern_lua_bracketed_s.findall(text):
|
||||
strings.append(s)
|
||||
for s in pattern_lua_fs.findall(text):
|
||||
strings.append(s[1])
|
||||
for s in pattern_lua_bracketed_fs.findall(text):
|
||||
strings.append(s)
|
||||
|
||||
for s in strings:
|
||||
s = re.sub(r'"\.\.\s+"', "", s)
|
||||
s = re.sub("@[^@=0-9]", "@@", s)
|
||||
s = s.replace('\\"', '"')
|
||||
s = s.replace("\\'", "'")
|
||||
s = s.replace("\n", "@n")
|
||||
s = s.replace("\\n", "@n")
|
||||
s = s.replace("=", "@=")
|
||||
lOut.append(s)
|
||||
return lOut
|
||||
|
||||
# Gets strings from an existing translation file
|
||||
# returns both a dictionary of translations
|
||||
# and the full original source text so that the new text
|
||||
# can be compared to it for changes.
|
||||
# Returns also header comments in the third return value.
|
||||
def import_tr_file(tr_file):
|
||||
dOut = {}
|
||||
text = None
|
||||
header_comment = None
|
||||
if os.path.exists(tr_file):
|
||||
with open(tr_file, "r", encoding='utf-8') as existing_file :
|
||||
# save the full text to allow for comparison
|
||||
# of the old version with the new output
|
||||
text = existing_file.read()
|
||||
existing_file.seek(0)
|
||||
# a running record of the current comment block
|
||||
# we're inside, to allow preceeding multi-line comments
|
||||
# to be retained for a translation line
|
||||
latest_comment_block = None
|
||||
for line in existing_file.readlines():
|
||||
line = line.rstrip('\n')
|
||||
if line[:3] == "###":
|
||||
if header_comment is None:
|
||||
# Save header comments
|
||||
header_comment = latest_comment_block
|
||||
# Stip textdomain line
|
||||
tmp_h_c = ""
|
||||
for l in header_comment.split('\n'):
|
||||
if not l.startswith("# textdomain:"):
|
||||
tmp_h_c += l + '\n'
|
||||
header_comment = tmp_h_c
|
||||
|
||||
# Reset comment block if we hit a header
|
||||
latest_comment_block = None
|
||||
continue
|
||||
if line[:1] == "#":
|
||||
# Save the comment we're inside
|
||||
if not latest_comment_block:
|
||||
latest_comment_block = line
|
||||
else:
|
||||
latest_comment_block = latest_comment_block + "\n" + line
|
||||
continue
|
||||
match = pattern_tr.match(line)
|
||||
if match:
|
||||
# this line is a translated line
|
||||
outval = {}
|
||||
outval["translation"] = match.group(2)
|
||||
if latest_comment_block:
|
||||
# if there was a comment, record that.
|
||||
outval["comment"] = latest_comment_block
|
||||
latest_comment_block = None
|
||||
dOut[match.group(1)] = outval
|
||||
return (dOut, text, header_comment)
|
||||
|
||||
# Walks all lua files in the mod folder, collects translatable strings,
|
||||
# and writes it to a template.txt file
|
||||
# Returns a dictionary of localized strings to source file sets
|
||||
# that can be used with the strings_to_text function.
|
||||
def generate_template(folder, mod_name):
|
||||
dOut = {}
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for name in files:
|
||||
if fnmatch.fnmatch(name, "*.lua"):
|
||||
fname = os.path.join(root, name)
|
||||
found = read_lua_file_strings(fname)
|
||||
if params["verbose"]:
|
||||
print(f"{fname}: {str(len(found))} translatable strings")
|
||||
|
||||
for s in found:
|
||||
sources = dOut.get(s, set())
|
||||
sources.add(f"### {os.path.basename(fname)} ###")
|
||||
dOut[s] = sources
|
||||
|
||||
if len(dOut) == 0:
|
||||
return None
|
||||
templ_file = os.path.join(folder, "locale/template.txt")
|
||||
write_template(templ_file, dOut, mod_name)
|
||||
return dOut
|
||||
|
||||
# Updates an existing .tr file, copying the old one to a ".old" file
|
||||
# if any changes have happened
|
||||
# dNew is the data used to generate the template, it has all the
|
||||
# currently-existing localized strings
|
||||
def update_tr_file(dNew, mod_name, tr_file):
|
||||
if params["verbose"]:
|
||||
print(f"updating {tr_file}")
|
||||
|
||||
tr_import = import_tr_file(tr_file)
|
||||
dOld = tr_import[0]
|
||||
textOld = tr_import[1]
|
||||
|
||||
textNew = strings_to_text(dNew, dOld, mod_name, tr_import[2])
|
||||
|
||||
if textOld and textOld != textNew:
|
||||
print(f"{tr_file} has changed.")
|
||||
if not params["no-old-file"]:
|
||||
shutil.copyfile(tr_file, f"{tr_file}.old")
|
||||
|
||||
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
|
||||
new_tr_file.write(textNew)
|
||||
|
||||
# Updates translation files for the mod in the given folder
|
||||
def update_mod(folder):
|
||||
modname = get_modname(folder)
|
||||
if modname is not None:
|
||||
process_po_files(folder, modname)
|
||||
print(f"Updating translations for {modname}")
|
||||
data = generate_template(folder, modname)
|
||||
if data == None:
|
||||
print(f"No translatable strings found in {modname}")
|
||||
else:
|
||||
for tr_file in get_existing_tr_files(folder):
|
||||
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
|
||||
else:
|
||||
print(f"\033[31mUnable to find modname in folder {folder}.\033[0m", file=_stderr)
|
||||
exit(1)
|
||||
|
||||
# Determines if the folder being pointed to is a mod or a mod pack
|
||||
# and then runs update_mod accordingly
|
||||
def update_folder(folder):
|
||||
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
|
||||
if is_modpack:
|
||||
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
|
||||
for subfolder in subfolders:
|
||||
update_mod(subfolder + "/")
|
||||
else:
|
||||
update_mod(folder)
|
||||
print("Done.")
|
||||
|
||||
def run_all_subfolders(folder):
|
||||
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
|
||||
update_folder(modfolder + "/")
|
||||
|
||||
|
||||
main()
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -15,7 +15,10 @@
|
||||
signs_bot = {}
|
||||
|
||||
-- Version for compatibility checks, see readme.md/history
|
||||
signs_bot.version = 1.05
|
||||
signs_bot.version = 1.08
|
||||
|
||||
-- Test for MT 5.4 new string mode
|
||||
signs_bot.CLIP = minetest.features.use_texture_alpha_string_modes and "clip" or true
|
||||
|
||||
if minetest.global_exists("techage") and techage.version < 0.25 then
|
||||
error("[signs_bot] Signs Bot requires techage version 0.25 or newer!")
|
||||
@ -25,8 +28,15 @@ if tubelib2.version < 1.9 then
|
||||
error("[signs_bot] Signs Bot requires tubelib2 version 1.9 or newer!")
|
||||
end
|
||||
|
||||
if minetest.global_exists("minecart") and minecart.version < 2.0 then
|
||||
error("[signs_bot] Signs Bot requires minecart version 2.0 or newer!")
|
||||
end
|
||||
|
||||
-- Load support for I18n.
|
||||
signs_bot.S = minetest.get_translator("signs_bot")
|
||||
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
|
||||
dofile(MP.."/doc.lua")
|
||||
dofile(MP.."/random.lua")
|
||||
dofile(MP.."/lib.lua")
|
||||
@ -60,5 +70,6 @@ dofile(MP.."/techage.lua")
|
||||
dofile(MP.."/timer.lua")
|
||||
dofile(MP.."/delayer.lua")
|
||||
dofile(MP.."/logic_and.lua")
|
||||
dofile(MP.."/compost.lua")
|
||||
|
||||
dofile(MP.."/tool.lua")
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -12,9 +12,8 @@
|
||||
|
||||
]]--
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local MAX_SIZE = 1000 -- max number of tokens
|
||||
|
||||
@ -128,6 +127,19 @@ local function compile(script)
|
||||
return pass2(tokens)
|
||||
end
|
||||
|
||||
local function gen_string_cmnd(code, pc, num_param, script)
|
||||
local tokens = tokenizer(script)
|
||||
if num_param == 0 then
|
||||
return tokens[pc]
|
||||
elseif num_param == 1 then
|
||||
return tokens[pc] .. " " .. (tokens[pc+1] or "")
|
||||
elseif num_param == 2 then
|
||||
return tokens[pc] .. " " .. (tokens[pc+1] or "") .. " " .. (tokens[pc+2] or "")
|
||||
else
|
||||
return tokens[pc] .. " " .. (tokens[pc+1] or "") .. " " .. (tokens[pc+2] or "") .. " " .. (tokens[pc+3] or "")
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Commands
|
||||
-------------------------------------------------------------------------------
|
||||
@ -235,29 +247,29 @@ function api.check_script(script)
|
||||
if tCmdDef[cmnd] then
|
||||
num_token = num_token + 1 + tCmdDef[cmnd].num_param
|
||||
if num_token > MAX_SIZE then
|
||||
return false, I("Maximum programm size exceeded"), idx
|
||||
return false, S("Maximum programm size exceeded"), idx
|
||||
end
|
||||
param1 = tonumber(param1) or param1
|
||||
param2 = tonumber(param2) or param2
|
||||
param3 = tonumber(param3) or param3
|
||||
local num_param = (param1 and 1 or 0) + (param2 and 1 or 0) + (param3 and 1 or 0)
|
||||
if tCmdDef[cmnd].num_param < num_param then
|
||||
return false, I("Too many parameters"), idx
|
||||
return false, S("Too many parameters"), idx
|
||||
end
|
||||
if tCmdDef[cmnd].num_param > 0 and not tCmdDef[cmnd].check(param1, param2, param3) then
|
||||
return false, I("Parameter error"), idx
|
||||
return false, S("Parameter error"), idx
|
||||
end
|
||||
elseif not cmnd:find("%w+:") then
|
||||
return false, I("Command error"), idx
|
||||
return false, S("Command error"), idx
|
||||
end
|
||||
tbl[cmnd] = (tbl[cmnd] or 0) + 1
|
||||
end
|
||||
if (tbl["end"] or 0) > (tbl["repeat"] or 0) then
|
||||
return false, I("'repeat' missing"), 0
|
||||
return false, S("'repeat' missing"), 0
|
||||
elseif (tbl["end"] or 0) < (tbl["repeat"] or 0) then
|
||||
return false, I("'end' missing"), 0
|
||||
return false, S("'end' missing"), 0
|
||||
end
|
||||
return true, I("Checked and approved"), 0
|
||||
return true, S("Checked and approved"), 0
|
||||
end
|
||||
|
||||
-- function returns: true/false, error-string
|
||||
@ -281,7 +293,7 @@ function api.run_script(base_pos, mem)
|
||||
mem.pc = 1
|
||||
mem.Stack = {}
|
||||
end
|
||||
return res, err
|
||||
return res, err, gen_string_cmnd(code, mem.pc, num_param, mem.script)
|
||||
end
|
||||
return api.EXIT
|
||||
end
|
||||
|
@ -1,45 +0,0 @@
|
||||
|
||||
-- Fallback functions for when `intllib` is not installed.
|
||||
-- Code released under Unlicense <http://unlicense.org>.
|
||||
|
||||
-- Get the latest version of this file at:
|
||||
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
|
||||
|
||||
local function format(str, ...)
|
||||
local args = { ... }
|
||||
local function repl(escape, open, num, close)
|
||||
if escape == "" then
|
||||
local replacement = tostring(args[tonumber(num)])
|
||||
if open == "" then
|
||||
replacement = replacement..close
|
||||
end
|
||||
return replacement
|
||||
else
|
||||
return "@"..open..num..close
|
||||
end
|
||||
end
|
||||
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
|
||||
end
|
||||
|
||||
local gettext, ngettext
|
||||
if minetest.get_modpath("intllib") then
|
||||
if intllib.make_gettext_pair then
|
||||
-- New method using gettext.
|
||||
gettext, ngettext = intllib.make_gettext_pair()
|
||||
else
|
||||
-- Old method using text files.
|
||||
gettext = intllib.Getter()
|
||||
end
|
||||
end
|
||||
|
||||
-- Fill in missing functions.
|
||||
|
||||
gettext = gettext or function(msgid, ...)
|
||||
return format(msgid, ...)
|
||||
end
|
||||
|
||||
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
|
||||
return format(n==1 and msgid or msgid_plural, ...)
|
||||
end
|
||||
|
||||
return gettext, ngettext
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
../intllib/tools/xgettext.sh ./basis.lua ./bot_flap.lua ./bot_sensor.lua ./cart_sensor.lua ./changer.lua ./chest.lua ./cmd_farming.lua ./cmd_flowers.lua ./cmd_item.lua ./cmd_move.lua ./cmd_pattern.lua ./cmd_place.lua ./cmd_sign.lua ./commands.lua ./crop_sensor.lua ./doc.lua ./duplicator.lua ./extender.lua ./init.lua ./lib.lua ./node_sensor.lua ./nodes.lua ./robot.lua ./signal.lua ./signs.lua ./tool.lua ./timer.lua ./delayer.lua ./logic_and.lua ./interpreter.lua
|
||||
../intllib/tools/xgettext.sh ./basis.lua ./bot_flap.lua ./bot_sensor.lua ./cart_sensor.lua ./changer.lua ./chest.lua ./cmd_farming.lua ./cmd_flowers.lua ./cmd_item.lua ./cmd_move.lua ./cmd_pattern.lua ./cmd_place.lua ./cmd_sign.lua ./commands.lua ./crop_sensor.lua ./doc.lua ./duplicator.lua ./extender.lua ./init.lua ./lib.lua ./node_sensor.lua ./nodes.lua ./robot.lua ./signal.lua ./signs.lua ./tool.lua ./timer.lua ./delayer.lua ./logic_and.lua ./interpreter.lua ./compost.lua
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,8 +13,6 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
signs_bot.register_inventory({"default:chest", "default:chest_open"}, {
|
||||
@ -63,3 +61,21 @@ signs_bot.register_inventory({"default:furnace", "default:furnace_active"}, {
|
||||
},
|
||||
})
|
||||
|
||||
signs_bot.register_inventory({"mobs:beehive"}, {
|
||||
put = {
|
||||
listname = "beehive",
|
||||
},
|
||||
take = {
|
||||
listname = "beehive",
|
||||
},
|
||||
})
|
||||
|
||||
signs_bot.register_inventory({"xdecor:hive"}, {
|
||||
put = {
|
||||
listname = "honey",
|
||||
},
|
||||
take = {
|
||||
listname = "honey",
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,8 +13,6 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
signs_bot.lib = {}
|
||||
@ -91,6 +89,13 @@ local function handle_drop(drop)
|
||||
end
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
function signs_bot.handle_drop_like_a_player(node)
|
||||
local drops = minetest.get_node_drops(node)
|
||||
if #drops >= 1 then
|
||||
return drops[1]
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
@ -213,7 +218,6 @@ function signs_bot.lib.after_dig_sign_node(pos, oldnode, oldmetadata, digger)
|
||||
smeta:set_string("err_msg", oldmetadata.fields.err_msg or "")
|
||||
end
|
||||
local player_name = digger:get_player_name()
|
||||
-- See https://github.com/minetest/minetest/blob/34e3ede8eeb05e193e64ba3d055fc67959d87d86/doc/lua_api.txt#L6222
|
||||
if player_name == "" then
|
||||
minetest.add_item(pos, sign)
|
||||
else
|
||||
|
413
signs_bot/locale/signs_bot.de.tr
Normal file
@ -0,0 +1,413 @@
|
||||
# textdomain: signs_bot
|
||||
|
||||
|
||||
|
||||
### basis.lua ###
|
||||
|
||||
running=läuft
|
||||
charging=aufladen
|
||||
stopped=gestoppt
|
||||
Off=Aus
|
||||
On=An
|
||||
Signs=Zeichen
|
||||
Other items=Andere Gegenstände
|
||||
Config=Konfig.
|
||||
Preassign slots items=Vorbelegungen
|
||||
Back=Zurück
|
||||
Robot Box=Roboterbox
|
||||
no power=kein Strom
|
||||
Signs Bot Box=Roboter Box
|
||||
The Box is the housing of the bot.=Die Box ist das Gehäuse des Roboters.
|
||||
Place the box and start the bot by means of the 'On' button.=Platziere die Box und starte den Roboter über den "An" Button.
|
||||
If the mod techage is installed, the bot needs electrical power.=Wenn die Mod techage installiert ist, benötigt der Roboter elektrischen Strom.
|
||||
The bot leaves the box on the right side.=Der Roboter verlässt die Box auf der rechten Seite.
|
||||
It will not start, if this position is blocked.=Er startet nicht, wenn diese Position belegt ist.
|
||||
To stop and remove the bot, press the 'Off' button.=Um den Roboter zu stoppen bzw. zu entfernen, drücke den "Aus" Button.
|
||||
The box inventory simulates the inventory of the bot.=Das Inventar der Box simuliert das Roboter Inventar.
|
||||
You will not be able to access the inventory, if the bot is running.=Du hast keinen Zugriff auf das Inventar, sofern der Roboter unterwegs ist.
|
||||
The bot can carry up to 8 stacks and 6 signs with it.=Der Roboter kann 8 Stapel von Blöcken und 6 Zeichen transportieren.
|
||||
|
||||
### bot_flap.lua ###
|
||||
|
||||
Exit=Beenden
|
||||
Bot Flap=Roboterklappe
|
||||
The flap is a simple block used as door for the bot.=Die Klappe ist ein einfacher Block, welcher vom Roboter als Tür genutzt wird.
|
||||
Place the flap in any wall, and the bot will automatically open=Platziere die Klappe in eine Mauer und der Roboter wird die Klappe automatisch öffnen
|
||||
and close the flap as it passes through it.=und wieder schließen, wenn er durchgegangen ist.
|
||||
|
||||
### bot_sensor.lua ###
|
||||
|
||||
Bot Sensor: Connected with=Bot Sensor: Verbunden mit
|
||||
Bot Sensor=Bot Sensor
|
||||
Bot Sensor: Not connected=Bot Sensor: Nicht verbunden
|
||||
The Bot Sensor detects any bot and sends a signal, if a bot is nearby.=Der Roboter Sensor entdeckt jeden Roboter und sendet ein Signal, sofern ein Roboter in der Nähe ist.
|
||||
The sensor direction does not care.=Die Ausrichtung des Sensor spielt keine Rolle.
|
||||
|
||||
### cart_sensor.lua ###
|
||||
|
||||
Cart Sensor: Connected with=Wagen Sensor: Verbunden mit
|
||||
Cart Sensor=Wagen Sensor
|
||||
Cart Sensor: Not connected=Wagen Sensor: Nicht verbunden
|
||||
The Cart Sensor detects and sends a signal, if a cart (Minecart) is nearby.=Der Wagen Sensor sendet ein Signal, sofern ein Wagen in der Nähe ist.
|
||||
The sensor has an active side (red) that must point to the rail/cart.=Der Sensor hat eine aktive Seite (rot), welche zu den Schienen zeigen muss.
|
||||
|
||||
### cart_sensor.lua ###
|
||||
### bot_sensor.lua ###
|
||||
|
||||
the sensor range is one node/meter.=Der Sensorbereich ist einen Block/Meter groß.
|
||||
|
||||
### changer.lua ###
|
||||
|
||||
Signs:=Zeichen:
|
||||
Bot Control Unit=Roboter Steuerungseinheit
|
||||
The Bot Control Unit is used to lead the bot by means of signs.=Die Roboter Steuerungseinheit dient zur Steuerung des Roboters über Zeichen.
|
||||
The unit can be loaded with up to 4 different signs and can be programmed by means of sensors.=Die Einheit kann mit bis zu 4 verschiedenen Zeichen geladen und über Sensoren programmiert werden.
|
||||
To load the unit, place a sign on the red side of the unit and click on the unit.=Um die Steuerungseinheit zu laden, platziere ein Zeichen auf die rote Seite der Einheit und klicke auf die Einheit.
|
||||
The sign disappears / is moved to the inventory of the unit.=Das Zeichen verschwindet/ist in die Steuerungseinheit verschoben.
|
||||
This can be repeated 3 times.=Dies kann bis zu 3 mal wiederholt werden.
|
||||
Use the connection tool to connect up to 4 sensors with the Bot Control Unit.=Benutze das Verbinde-Werkzeug um die bis zu 4 Sensoren mit der Steuerungseinheit zu verbinden.
|
||||
|
||||
### chest.lua ###
|
||||
|
||||
Bot Chest: Sends signal to=Bot Kiste: Sende Signal zu
|
||||
Signs Bot Chest=Signs Bot Kiste
|
||||
Bot Chest: Not connected=Bot Kiste: nicht verbunden
|
||||
The Signs Bot Chest is a special chest with sensor function.=Die Roboter Kiste ist eine spezielle Kiste mit zusätzlicher Sensor-Funktion.
|
||||
It sends a signal depending on the chest state.=Sie sendet ein Signal abhängig vom Zustand der Kiste.
|
||||
Possible states are 'empty', 'not empty', 'almost full'=Mögliche Zustände sind "leer", "nicht leer" und "fast voll"
|
||||
A typical use case is to turn off the bot, when the chest is almost full or empty.=Ein typischer Anwendungsfall ist den Roboter zu stoppen, wenn die Kiste fast voll oder leer ist.
|
||||
|
||||
### cmd_farming.lua ###
|
||||
|
||||
Sow farming seeds@nin front of the robot=Sähe Saatgut@nvor dem Roboter
|
||||
Harvest farming products@nin front of the robot@non a 3x3 field.=Ernte Früchte/Getreide@nin einem 3x3 großem Feld@nvor dem Roboter.
|
||||
Plant a sapling@nin front of the robot=Pflanze einen Setzling@nvor den Roboter
|
||||
Sign "farming"=Zeichen "Farming"
|
||||
Sign 'farming'=Zeichen 'Farming'
|
||||
Used to harvest and seed a 3x3 field.=Benötigt um ein 3x3 Feld zu ernten und wieder zu sähen.
|
||||
The seed to be placed has to be in the first inventory slot of the bot.=Das Saatgut, dass gesät werden soll, muss sich an der 1. Position im Inventar befinden.
|
||||
|
||||
### cmd_farming.lua ###
|
||||
### cmd_flowers.lua ###
|
||||
|
||||
Place the sign in front of the field.=Platziere das Zeichen vor das Feld.
|
||||
When finished, the bot turns.=Der Roboter dreht um, wenn er fertig ist.
|
||||
|
||||
### cmd_flowers.lua ###
|
||||
|
||||
Cutting flowers, leaves and tree blocks@nin front of the robot@non a 3x3 field.=Schneide Blumen, Blätter und Baumblöcke@nin einem 3x3 großem Feld@nvor dem Roboter.
|
||||
Sign "flowers"=Zeichen "Blumen"
|
||||
Sign 'flowers'=Zeichen 'Blumen'
|
||||
Used to cut flowers on a 3x3 field.=Benötigt um ein 3x3 Blumenfeld zu ernten.
|
||||
|
||||
### cmd_item.lua ###
|
||||
|
||||
Take <num> items from a chest like node@nand put it into the item inventory.@n<slot> is the inventory slot (1..8) or 0 for any one=Nehme <num> Gegenstände aus der@nKiste oder dem Kisten-ähnlichen Block@nund tue diese in das eigene Inventar@nan der Position <slot>. Slot = (1..8)@noder 0 für irgend eine Position
|
||||
Add <num> items to a chest like node@ntaken from the item inventory.@n<slot> is the inventory slot (1..8) or 0 for any one=Lege <num> Gegenstände aus dem@neigenen Inventar in die andere Kiste.@n<slot> ist die Position im@neigenen Inventar (1--8).@noder 0 für irgend eine Position
|
||||
Add <num> fuel to a furnace like node@ntaken from the item inventory.@n<slot> is the inventory slot (1..8) or 0 for any one=Lege <num> Brennstoffe aus dem@neigenen Inventar in den anderen Block.@n<slot> ist die Position im@neigenen Inventar (1--8).@noder 0 für irgend eine Position
|
||||
deprecated, use bot inventory configuration instead=veraltet, benutze stattdessen die Inventar Konfigurationsmöglichkeit
|
||||
Pick up all objects@nin a 3x3 field.@n<slot> is the inventory slot (1..8) or 0 for any one=Hebe alle Objekte in einem@n3x3 Blöcke großen Feld auf@nund lege diese in das eigene@nInventar an Position <slot> (1-8)@noder 0 für irgend eine Position
|
||||
Drop items in front of the bot.@n<slot> is the inventory slot (1..8) or 0 for any one=Lasse ein Objekt aus dem eigenen@nInventar vor dem Roboter fallen.@n<slot> ist die Position im@neigenen Inventar (1--8).@noder 0 für irgend eine Position
|
||||
Punch a rail cart to start it=Schlage den Wagen um ihn zu starten
|
||||
|
||||
### cmd_move.lua ###
|
||||
|
||||
Move the robot one step back=Bewege den Roboter@neinen Schritt zurück
|
||||
Turn the robot to the left=Drehe den Roboter@nnach links
|
||||
Turn the robot to the right=Drehe den Roboter@nnach rechts
|
||||
Turn the robot around=Drehe den Roboter@num (180 Grad)
|
||||
Move the robot upwards=Bewege den Roboter@nnach oben
|
||||
Move the robot down=Bewege den Roboter@nnach unten
|
||||
Stop the robot for <sec> seconds@n(1..9999)=Stoppe den Roboter@nfür <sec> Sekunden (1.9999)
|
||||
Stop the robot.=Stoppe den Roboter.
|
||||
Turn the robot off@nand put it back in the box.=Schalte den Roboter aus und@nsetze ihn damit zurück in@nseine Box.
|
||||
|
||||
### cmd_pattern.lua ###
|
||||
|
||||
Store pattern to be cloned.=Speichere die Vorlage@ndie kopiert werden soll.
|
||||
Copy the nodes from@nthe stored pattern position@n<size> is: 3x1, 3x2, 3x3,@n5x1, 5x2, 5x3 (wide x deep)@n<lvl> pattern level offset (0..4)=Kopiere die Blöcke von der@n"Vorlage" Position.@n<size> ist: 3x1, 3x2, 3x3,@n5x1, 5x2, 5x3 (Breite x Tiefe)@n<lvl> Vorlagenebene (0..4)
|
||||
Sign "pattern"=Zeichen "Vorlage"
|
||||
Sign "copy 3x3x3"=Zeichen "kopiere 3x3x3"
|
||||
Sign 'pattern'=Zeichen 'Vorlage'
|
||||
Used to make a copy of a 3x3x3 cube.=Benötigt um eine Kopie eines 3x3x3 Quadrats zu machen.
|
||||
Place the sign in front of the pattern to be copied.=Platziere das Zeichen vor die Vorlage, die kopiert werden soll.
|
||||
Use the copy sign to make the copy of this pattern on a different location.=Benutze das Kopier-Zeichen, um eine Kopie dieser Vorlage an einer anderen Stelle zu machen.
|
||||
The bot must first reach the pattern sign, then the copy sign.=Der Roboter muss zuerst das Vorlage-Zeichen und dann das Kopier-Zeichen erreichen.
|
||||
Sign 'copy3x3x3'=Zeichen 'kopiere 3x3x3'
|
||||
Place the sign in front of the location, where the copy should be made.=Platziere das Zeichen vor die Stelle, wo die Kopie hergestellt werden soll.
|
||||
Use the pattern sign to mark the pattern.=Benutze das Vorlage-Zeichen und die Vorlage zu markieren.
|
||||
|
||||
### cmd_place.lua ###
|
||||
|
||||
Error: Position protected=Fehler: Position geschützt
|
||||
Place a block in front of the robot@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Setze einen Block vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
|
||||
Place a block on the left side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Setze einen Block links vorne.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
|
||||
Place a block on the right side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Setze einen Block rechts vorne.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
|
||||
Place a block under the robot.@nHint: use 'move_up' first.@n<slot> is the inventory slot (1..8)=Setze einen Block unter den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--8).
|
||||
Place a block above the robot.@n<slot> is the inventory slot (1..8)=Setze einen Block über den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--8).
|
||||
Error: No free inventory space=Fehler: Kein freier Inventarplatz
|
||||
Dig the block in front of the robot@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Entferne einen Block vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
|
||||
Dig the block on the left side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Entferne einen links vorne.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
|
||||
Dig the block on the right side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=Entferne einen rechts vorne.@n<slot> ist die Position im@neigenen Inventar (1--8).@nFür <lvl> ist zulässig: -1 0 +1
|
||||
Dig the block under the robot.@n<slot> is the inventory slot (1..8)=Entferne einen Block unter dem@nRoboter.@n<slot> ist die Position im@neigenen Inventar (1--8).
|
||||
Dig the block above the robot.@n<slot> is the inventory slot (1..8)=Entferne einen Block über dem@nRoboter.@n<slot> ist die Position im@neigenen Inventar (1--8).
|
||||
Rotate the block in front of the robot@n<lvl> is one of: -1 0 +1@n<steps> is one of: 1 2 3=Rotiere den Block vor dem Roboter.@nFür <lvl> ist zulässig: -1 0 +1@nFür <steps> ist zulässig: 1 2 3
|
||||
Bot torch=Bot Fackel
|
||||
|
||||
### cmd_sign.lua ###
|
||||
|
||||
Commands,Help=Kommandos,Hilfe
|
||||
Sign name:=Zeichenname:
|
||||
Cancel=Abbruch
|
||||
Check=Prüfen
|
||||
Help=Hilfe
|
||||
Copy Cmnd=Kopiere Kommando
|
||||
please check the added line(s)=bitte prüfe die neue(n) Zeile(n)
|
||||
Sign "command"=Zeichen "Kommando"
|
||||
-- enter or copy commands from help page=-- Kommandos eingeben oder von der Hilfeseite kopieren
|
||||
Error: Signs inventory empty=Fehler: Zeichen Inventar ist leer
|
||||
Error: Position protected or occupied=Fehler: Position ist geschützt oder belegt
|
||||
Place a sign in front of the robot@ntaken from the signs inventory@n<slot> is the inventory slot (1..6)=Setze ein Zeichen vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--6).
|
||||
Place a sign behind the robot@ntaken from the signs inventory@n<slot> is the inventory slot (1..6)=Setze ein Zeichen hinter den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--6).
|
||||
Error: No sign available=Fehler: Kein Zeichen verfügar
|
||||
Error: Signs inventory slot is occupied=Fehler: Die Zeicheninventar Position ist belegt
|
||||
Error: Position is protected=Fehler: Die Position ist geschützt
|
||||
Dig the sign in front of the robot@nand add it to the signs inventory.@n<slot> is the inventory slot (1..6)=Entferne das Zeichen vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--6).
|
||||
Dig the sign in front of the robot@nand add the cleared sign to@nthe item iventory.@n<slot> is the inventory slot (1..8)=Entferne das Zeichen vor den Roboter.@n<slot> ist die Position im@neigenen Inventar (1--6).
|
||||
Sign 'command'=Zeichen 'Kommando'
|
||||
The 'command' sign can be programmed by the player.=Das 'Kommando' Zeichen kann vom Spieler programmiert werden.
|
||||
Place the sign in front of you and use the node menu to program your sequence of bot commands.=Platziere das Zeichen vor dir und nutze das Zeichen-Menü, um die Kommando-Sequenz zu programmieren.
|
||||
The menu has an edit field for your commands and a help page with all available commands.=Das Menü hat ein Eingabefeld für deine Kommandos und eine Hilfeseite zu allen Kommandos.
|
||||
|
||||
### commands.lua ###
|
||||
|
||||
commands:=Kommandos:
|
||||
unknown command=unbekanntes Kommando
|
||||
start of a 'repeat..end' block=Anfang eines 'repeat..end' Blocks
|
||||
end command of a 'repeat..end' block=Ende Kommando eines 'repeat..end' Blocks
|
||||
call a subroutine (with 'return' statement)=Aufruf einer Unterfunktion (mit 'return' Anweisung)
|
||||
return from a subroutine=Rückkehr von einer Unterfunktion
|
||||
jump to a label=Sprung zu einer Marke
|
||||
Move the robot 1..999 steps forward@nwithout paying attention to any signs.@nUp and down movements also become@ncounted as steps.=Bewege den Roboter 1..999 Schritte@nvorwärts ohne auf Zeichen zu achten.@nAuf- und Ab-Bewegungen werden auch@nals Schritte gezählt.
|
||||
Walk until a sign or obstacle is@nreached. Then continue with the next command.@nWhen a sign has been reached, @nthe current program is ended@nand the bot executes the@nnew program from the sign=Gehe bis ein Zeichen oder Hindernis@nerreicht wurde. Führe dann das nächste@nKommando aus. @nWurde ein Zeichen erreicht, so arbeite@ndie Kommandos des Zeichens als@nUnter-Prozess ab
|
||||
Print given text as chat message.@nFor two or more words, use the '*' character @ninstead of spaces, like "Hello*world"=Gebe den angegebenen Text als Chat-Nachricht aus.@nFür zwei oder mehr Wörter verwende das Zeichen '*' @nanstelle von Leerzeichen, z. B. "Hallo*Welt".
|
||||
|
||||
### compost.lua ###
|
||||
|
||||
Put 2 leaves into the compost barrel@n<slot> is the bot inventory slot (1..8)@nwith the leaves.=Lege 2 Blätter in den Kompostbehälter@n<slot> ist die Position im@nBot Inventar (1..8)@nmit den Blättern.
|
||||
Take a compost item from the barrel.@n<slot> (1..8 or 0 for the first free slot) is the bot@nslot for the compost item.=Nimm einen Kompostblock aus dem@nKompostbehälter. <slot> ist die Position im@nBot Inventar für den Block.@nWerte für <slot>: 0..8, oder 0 für die erste@nfreie Inventarposition.
|
||||
|
||||
### crop_sensor.lua ###
|
||||
|
||||
Crop Sensor: Connected with=Ernte Sensor: Verbunden mit
|
||||
Crop Sensor=Ernte Sensor
|
||||
Crop Sensor: Not connected=Ernte Sensor: Nicht verbunden
|
||||
The Crop Sensor sends cyclical signals when, for example, wheat is fully grown.=Der Ernte Sensor sendet zyklisch ein Signal, wenn bspw. der Weizen voll ausgewachsen ist.
|
||||
The sensor range is one node/meter.=Der Sensorbereich beträgt einen Block/Meter.
|
||||
The sensor has an active side (red) that must point to the crop/field.=Der Sensor hat eine aktive Seite (rot), welche zur der Pflanze zeigen muss.
|
||||
|
||||
### delayer.lua ###
|
||||
|
||||
Signal Delayer: Connected with=Signal Verzögerer: Verbunden mit
|
||||
Delay time [sec]:=Verzögerungszeit [s]:
|
||||
Signal Delayer=Signal Verzögerer
|
||||
Signals are forwarded delayed. Subsequent signals are queued.=Signale werden verzögert weitergeleitet. Nachfolgende Signale werden in die Warteschlange gestellt.
|
||||
The delay time can be configured.=Die Verzögerungszeit kann eingestellt werden.
|
||||
|
||||
### doc.lua ###
|
||||
|
||||
After you have placed the Signs Bot Box, you can start the bot by means of the 'On' button in the box menu.=Nachdem du die Roboter-Kiste platziert hast, kannst du den Roboter über den "An" Button im Kistenmenü starten.
|
||||
If the bot returns to its box right away, you will likely need to charge it with electrical energy (techage) first.=Wenn der Bot sofort in seine Box zurückkehrt, musst du ihn wahrscheinlich zuerst mit elektrischer Energie (Techage) aufladen.
|
||||
The bot then runs straight up until it reaches an obstacle (a step with two or more blocks up or down or a sign.)=Der Roboter läuft dann geradeaus, bis er ein Hindernis erreicht (eine Stufe mit zwei oder mehr Blöcken, auf oder ab).
|
||||
If the bot first reaches a sign it will execute the commands on the sign.=Falls der Roboter zuerst ein Zeichen erreicht, wird er die Kommandos auf dem Zeichen ausführen.
|
||||
If the command(s) on the sign is e.g. 'turn_around', the bot turns and goes back.=Falls das Kommandos auf dem Zeichen bspw. ein 'turn_around' ist, dreht der Roboter um und geht zurück.
|
||||
In this case, the bot reaches his box again and turns off.=In diesem Fall erreicht der Roboter wieder seine Box und schaltet sich ab.
|
||||
The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for other items (to be placed/dug by the bot).=Die Roboter Box hat ein Inventar für 6 Stapel Zeichen und 8 Stapel für andere Gegenstände (welche platziert oder eingesammelt werden können).
|
||||
This inventory simulates the bot internal inventory.=Das Inventar der Box simuliert das Roboter Inventar.
|
||||
That means you will only have access to the inventory if the bot is turned off ('sitting' in his box).=Das bedeutet, du hast nur Zugriff auf das Inventar, sofern der Roboter ausgeschaltet ist.
|
||||
You simply control the direction of the bot by means of the 'turn left' and 'turn right' signs (signs with the arrow).=Du kannst die Richtung des Roboters ganz einfach über 'turn_left' (drehe nach links) oder 'turn_right' (drehe nach rechts) Zeichen verändern.
|
||||
The bot can run over steps (one block up/down). But there are also commands to move the bot up and down.=Der Roboter kann auch Stufen überwinden (eine Block hoch oder runter). Aber es gibt auch Kommandos, um den Roboter nach oben oder unten zu bewegen.
|
||||
It is not necessary to mark a way back to the box.=Es ist nicht notwendig, dem Roboter einen Weg zurück zu seiner Box zu markieren.
|
||||
With the command 'turn_off' the bot will turn off and be back in his box from every position.=Mit dem Kommando "turn_off" wird der Roboter ausgeschaltet und befindet sich danach wieder in seiner Box, egal wo er war.
|
||||
The same applies if you turn off the bot by the box menu.=Das gleiche gilt, wenn du den Roboter über den 'Aus' Button ausschaltest.
|
||||
If the bot reaches a sign from the wrong direction (from back or sides) the sign will be ignored.=Wenn der Roboter ein Zeichen von der falschen Richtung erreicht (von der Seite oder von hinten), wird das Zeichen ignoriert.
|
||||
The bot will walk over.=Der Roboter läuft einfach hinüber.
|
||||
All predefined signs have a menu with a list of the bot commands.=Alle vordefinierten Zeichen haben ein Menü mit der Liste der Kommandos.
|
||||
These signs can't be changed, but you can craft and program your own signs.=Diese Zeichen können nicht geändert werden, aber du kannst eigene Zeichen herstellen und programmieren.
|
||||
For this you have to use the 'command' sign.=Dafür kannst du das 'command' Zeichen verwenden.
|
||||
This sign has an edit field for your commands and a help page with all available commands.=Das Zeichen hat ein Eingabefeld für deine Kommandos und eine Hilfeseite zu allen Kommandos.
|
||||
Also for your own signs it is important to know:=Es ist auch für eigene Zeichen wichtig zu wissen:
|
||||
After the execution of the last command of the sign, the bot falls back into its default behaviour and runs in its taken direction.=Nach der Ausführung des letzten Kommandos eines Zeichens fällt der Roboter immer zurück in sein Standardverhalten und läuft in die eingeschlagene Richtung weiter.
|
||||
A standard job for the bot is to move items from one chest to another=Eine Standardaufgabe für den Roboter ist, Gegenstände von einer Kiste in eine andere zu tun
|
||||
(chest or node with a chest like inventory).=(Kiste oder Block mit Kisten-ähnlichem Inventar).
|
||||
This can be done by means of the two signs 'take item' and 'add item'.=Das kann mit Hilfe von zwei Zeichen realisiert werden: 'take_item' und 'add_item'.
|
||||
These signs have to be placed on top of chest nodes.=Diese Zeichen müssen auf den Kisten platziert werden.
|
||||
In addition to the signs the bot can be controlled by means of sensors.=Zusätzlich zu den Zeichen kann der Roboter auch über Sensoren gesteuert werden.
|
||||
Sensors like the Bot Sensor have two states: on and off.=Sensoren wie der Roboter-Sensor haben zwei Zustande: on' (an) und 'off' (aus).
|
||||
If the Bot Sensor detects a bot it will switch to the state 'on' and=Wenn der Roboter-Sensor einen Roboter erkennt geht er in den Zustand 'on' und
|
||||
sends a signal to a connected block, called an actuator.=sendet ein Signal zu einem verbundenen Block, einem Aktor.
|
||||
Sensors are:=Sensoren sind:
|
||||
- Bot Sensor: Sends a signal when the robot passes by=- Roboter Sensor: Sendet ein Signal wenn ein Roboter vorbei geht
|
||||
- Node Sensor: Sends a signal when it detects any node=- Block Sensor: Sendet ein Signal bei einer Veränderung von Blocken vor sich
|
||||
- Crop Sensor: Sends a signal when, for example wheat is fully grown=- Ernte Sensor: Sendet ein Signal, wenn bspw. der Weizen voll ausgewachsen ist
|
||||
- Bot Chest: Sends a signal depending on the chest state (empty, full)=- Roboter-Kiste: Sendet ein Signal abhängig vom Zustand der Kiste (voll, leer)
|
||||
Actuators are:=Aktoren sind:
|
||||
- Signs Bot Box: Can turn the bot off and on=- Roboter-Box: Kann den Roboter ein- und ausschalten
|
||||
- Control Unit: Can be used to exchange the sign to lead the bot=- Steuerungseinheit: Wird genutzt um Zeichen auszutauschen und damit den Roboter zu steuern
|
||||
Additional sensors and actuator can be added by other mods.=Weitere Sensoren und Aktoren können von weiteren Mods hinzugefügt werden.
|
||||
To send a signal from a sensor to an actuator, the sensor has to be connected (paired) with actuator.=Um ein Signal von einem Sensor zu einem Aktor senden zu können, muss der Sensor mit dem Aktor verbunden werden (Pairing).
|
||||
To connect sensor and actuator, the Sensor Connection Tool has to be used.=Das Sensor Verbinde-Werkzeug wir genutzt, um Sensoren mit Aktoren zu verbinden.
|
||||
Simply click with the tool on both blocks and the sensor will be connected with the actuator.=Klicke einfach mit dem Werkzeug nacheinander auf beide Blöcke und der Sensor wird mit dem Aktor verbunden.
|
||||
A successful connection is indicated by a ping/pong noise.=Eine erfolgreiche Verbindung wird über ein Ping/Pong-Geräusch angezeigt.
|
||||
Before you connect sensor with actuator, take care that the actuator is in the requested state.=Bevor du einen Sensor mit einem Aktor verbindest, achte darauf, dass sich der Aktor im richtigen Zustand befindet.
|
||||
For example: If you want to start the Bot with a sensor, connect the sensor with the Bot Box,=Zum Beispiel: Wenn du den Roboter über einen Sensor starten willst, verbinde Sensor und Roboter-Kiste nur,
|
||||
when the Bot is in the state 'on'. Otherwise the sensor signal will stop the Bot,=wenn der Roboter an ist (im Zustand 'on'). Anderenfalls würde der Sensor den Roboter ausschalten,
|
||||
instead of starting it.=anstatt ihn zu starten.
|
||||
The following applies to all commands that are used to place items in the bot inventory, like:=Das folgende gilt für alle Kommandos, um Items in das Roboter Inventar zu legen, wie:
|
||||
- take_item <num> <slot>=- take_item <num> <slot>
|
||||
- pickup_items <slot>=- pickup_items <slot>
|
||||
- trash_sign <slot>=- trash_sign <slot>
|
||||
- harvest <slot>=- harvest <slot>
|
||||
- dig_front <slot> <lvl>=- dig_front <slot> <lvl>
|
||||
- dig_left <slot> <lvl>=- dig_left <slot> <lvl>
|
||||
- dig_right <slot> <lvl>=- dig_right <slot> <lvl>
|
||||
- dig_below <slot> <lvl>=- dig_below <slot> <lvl>
|
||||
- dig_above <slot> <lvl>=- dig_above <slot> <lvl>
|
||||
If no slot or slot 0 was specified with the command (case A), all 8 slots of the bot inventory =Wurde beim Kommando kein Slot oder Slot 0 angegeben (Fall A), werden nacheinander alle
|
||||
are checked one after the other. If a slot was specified (case B), only this slot is checked.=8 Slots des Bot Inventars geprüft, Wurde ein Slot angegeben (Fall B), wird nur dieser geprüft.
|
||||
In both cases the following applies: If the slot is preconfigured and fits the item, =In beiden Fällen gilt: Ist der Slot vorkonfiguriert und passt das Item dazu, oder ist der Slot
|
||||
or if the slot is not configured and empty, or is only partially filled with the item type =nicht konfiguriert und leer, oder mit dem Item-Typ (das hinzu gefügt werden soll) nur
|
||||
(which should be added), then the items are added.=teilweise gefüllt, dann werden die Items hinzugefügt.
|
||||
If not all items can be added, the remaining slots will be tried out in case A.=Können nicht alle Items hinzugefügt werden, werden im Falle A die restlichen Slots weiter durchprobiert.
|
||||
Anything that could not be added to your own inventory goes back.=Was nicht dem eigenen Inventar hinzugefügt werden konnte, geht zurück.
|
||||
The following applies to all commands that are used to take items from the bot inventory, like:=Das folgende gilt für alle Kommandos, um Items aus dem Roboter Inventar zu nehmen, wie:
|
||||
- add_item <num> <slot>=- add_item <num> <slot>
|
||||
It doesn't matter whether a slot is configured or not. The bot takes the first stack that =Hier ist es egal, ob ein Slot konfiguriert ist, oder nicht. Der Bot nimmt den ersten Stack, den
|
||||
it can find from its own inventory and tries to use it.=er finden kann, aus dem eigenen Inventar und versucht diesen zu nutzen.
|
||||
If a slot is specified, it only takes this, if no slot has been specified, it checks all of =Ist ein Slot angegeben, nimmt er nur diesen, wurde kein Slot angegeben, prüft er alle
|
||||
them one after the other, starting from slot 1 until it finds something.=nacheinander, von Slot 1 beginnend, bis er etwas findet. Ist die gefundene Anzahl kleiner als
|
||||
If the number found is smaller than requested, he tries to take the rest out of any slot.=gefordert, versucht er den Rest aus irgend einem Slot zu nehmen.
|
||||
Signs Bot=Signs Bot
|
||||
A robot controlled by signs, used for automated work=Ein Roboter, gesteuert über Zeichen, für Automatisierungsaufgaben
|
||||
Start the Bot=Starte den Roboter
|
||||
Control the Bot=Steuere den Roboter
|
||||
Sensors and Actuators=Sensoren und Aktoren
|
||||
Connecting sensors and actuator=Verbinde Sensor mit Aktor
|
||||
Bot inventory behavior=Verhalten beim Roboter Inventar
|
||||
|
||||
### doc.lua ###
|
||||
### cmd_sign.lua ###
|
||||
|
||||
The help page has a copy button to simplify the programming.=Die Hilfeseite hat einen Kopier-Button um die Programmierung zu erleichtern.
|
||||
|
||||
### duplicator.lua ###
|
||||
|
||||
Template:=Vorlage:
|
||||
Input:=Eingabe:
|
||||
Output:=Ausgabe:
|
||||
1. Place one 'cmnd' sign= 1. Kommando Zeichen einlegen
|
||||
2. Add 'blank signs'=2. Füge 'leere Zeichen' hinzu
|
||||
3. Take the copies=3. Entnehme die Kopien
|
||||
Signs Duplicator=Zeichen Kopierer
|
||||
Sign "user"=Zeichen "Benutzer"
|
||||
Sign "blank"="Leeres" Zeichen
|
||||
The Duplicator can be used to make copies of signs.=Der Zeichen Kopierer kann zur Herstellung von Kopien eines Zeichens genutzt werden.
|
||||
1. Put one 'cmnd' sign to be used as template into the 'Template' inventory=1. Lege ein Kommando-Zeichen als Vorlage in das 'Vorlage' Inventar
|
||||
2. Add one or several 'blank signs' to the 'Input' inventory.=2. Füge ein oder mehrere 'leere Zeichen' als Eingabe hinzu.
|
||||
3. Take the copies from the 'Output' inventory.=3. Entnehme die Kopieren aus der Ausgabe.
|
||||
Written books [default:book_written] can alternatively be used as template=Alternativ können auch beschriebene Bücher [default:book_written] als Vorlage verwendet werden
|
||||
Already written signs can be used as input, too.=Bereits beschriebene Zeichen können auch als Eingabe-Zeichen genutzt werden.
|
||||
Needed as input for the Duplicator.=Wird als Eingabe für den Zeichen Kopierer benötigt.
|
||||
|
||||
### extender.lua ###
|
||||
|
||||
Sensor Extender: Connected with=Sensor Erweiterung: Verbunden mit
|
||||
Sensor Extender=Sensor Erweiterung
|
||||
Sensor Extender: Not connected=Sensor Erweiterung: Nicht verbunden
|
||||
With the Sensor Extender, sensor signals can be sent to more than one actuator.=Mit Hilfe der Sensor-Erweiterung können weitere Aktoren mit dem Sensor verbunden werden.
|
||||
Place one or more extender nearby the sensor and connect each extender=Platziere ein oder mehrere Sensor-Erweiterungen neben einen Sensor und verbinde diese
|
||||
with one further actuator by means of the Connection Tool.=mit weiteren Aktoren mit Hilfe des Verbinde-Werkzeuges.
|
||||
|
||||
### interpreter.lua ###
|
||||
|
||||
Maximum programm size exceeded=Maximale Programmlänge überschritten
|
||||
Too many parameters=Zu viele Parameter
|
||||
Parameter error=Parameter Fehler
|
||||
Command error=Kommandozeilen Fehler
|
||||
'repeat' missing=Es fehlt ein 'repeat'
|
||||
'end' missing=Es fehlt ein 'end'
|
||||
Checked and approved=Geprüft und genehmigt
|
||||
|
||||
### logic_and.lua ###
|
||||
|
||||
Signal AND with=Signal UND mit
|
||||
inputs=Eingängen
|
||||
Signal AND=Signal UND
|
||||
Signal is sent, if all input signals are received.=Signal wird gesendet, wenn all Eingangssignale empfangen wurden.
|
||||
|
||||
### node_sensor.lua ###
|
||||
|
||||
Node Sensor: Connected with =Block Sensor: Verbunden mit
|
||||
added=hinzukommen
|
||||
removed=fehlen
|
||||
added or removed=hinzukommen oder fehlen
|
||||
Send signal if nodes have been:=Sende ein Signal wenn Blöcke:
|
||||
accept=übernehmen
|
||||
Node Sensor=Block Sensor
|
||||
Node Sensor: Not connected=Block Sensor: Nicht verbunden
|
||||
The node sensor sends cyclical signals when it detects that nodes have appeared or disappeared,=Der Block Sensor sendet zyklisch ein Signal, wenn er eine Veränderung von Blöcken vor sich entdeckt (ein Block erscheint oder verschwindet),
|
||||
but has to be configured accordingly.=aber muss entsprechend konfiguriert werden.
|
||||
Valid nodes are all kind of blocks and plants.=Gültig sind alle Arten von Blöcken oder Pflanzen.
|
||||
The sensor range is 3 nodes/meters in one direction.=Die Sensor-Reichweite beträgt 3 Blöcke/Meter in eine Richtung.
|
||||
The sensor has an active side (red) that must point to the observed area.=Der Sensor hat eine aktive Seite (rot) welche zu dem zu überwachenden Bereich zeigen muss.
|
||||
|
||||
### signs.lua ###
|
||||
|
||||
Sign "turn right"=Zeichen "rechts drehen"
|
||||
Sign "turn left"=Zeichen "links drehen"
|
||||
Sign "take item"=Zeichen "Nehme Gegenstand"
|
||||
Sign "add item"=Zeichen "Lege Gegenstand"
|
||||
Sign "stop"=Zeichen "Stopp"
|
||||
Sign "add to cart"=Zeichen "Lege in den Wagen"
|
||||
Sign "take from cart"=Zeichen "Nehme aus dem Wagen"
|
||||
The Bot turns right when it detects this sign in front of it.=Der Roboter dreht nach rechts, wenn er dieses Zeichen vor sich hat.
|
||||
The Bot turns left when it detects this sign in front of it.=Der Roboter dreht nach links, wenn er dieses Zeichen vor sich hat.
|
||||
The Bot takes items out of a chest in front of it and then turns around.=Der Roboter nimmt Gegenstände aus einer Kiste vor sich und dreht dann um.
|
||||
This sign has to be placed on top of the chest.=Das Zeichen muss auf der Kiste platziert werden.
|
||||
The Bot puts items into a chest in front of it and then turns around.=Der Roboter legt Gegenstände in eine Kiste vor sich und dreht dann um.
|
||||
The Bot will stop in front of this sign until the sign is removed or the bot is turned off.=Der Roboter stoppt vor diesem Zeichen, bis das Zeichen entfernt, oder der Roboter ausgeschaltet wird.
|
||||
The Bot puts items into a minecart in front of it, pushes the cart and then turns around.=Der Roboter legt Gegenstände in den Wagen vor sich, startet den Wagen und dreht dann um.
|
||||
This sign has to be placed on top of the rail at the cart end position.=Das Zeichen muss auf dem Gleis und damit über dem Wagen platziert werden.
|
||||
The Bot takes items out of a minecart in front of it, pushes the cart and then turns around.=Der Roboter nimmt Gegenstände aus dem Wagen vor sich, startet den Wagen und dreht dann um.
|
||||
|
||||
### techage.lua ###
|
||||
|
||||
Ignite the techage charcoal lighter=Zünde den Holzkohle-Anzünder an
|
||||
Turns the bot off if the@nbattery power is below the@ngiven value in percent (1..99)=Schalte den Bot aus,@nwenn die Batterieladung kleiner@nist als der angegebene Wert@nin Prozent (1.99)
|
||||
fully charged=voll geladen
|
||||
Sends a techage command@nto a given node. @nReceiver is addressed by@nthe techage node number.@nFor commands with two or more @nwords, use the '*' character @ninstead of spaces, e.g.: @nsend_cmnd 3465 pull*default:dirt*2=Sende ein techage Kommando@nan einen Block mit der@nangegebenen Blocknummer.@nFür Kommandos mit zwei oder mehr @nWörtern verwende das Zeichen '*' @nanstelle des Leerzeichens, wie bspw.:@nsend_cmnd 3465 pull*default:dirt*2
|
||||
|
||||
### timer.lua ###
|
||||
|
||||
Bot Timer=Roboter Timer
|
||||
Cycle time [min]:=Zykluszeit [min]:
|
||||
Bot Timer: Not connected=Roboter Timer: Nicht verbunden
|
||||
Special kind of sensor.=Spezielle Form eines Sensors.
|
||||
Can be programmed with a time in seconds, e.g. to start the bot cyclically.=Kann mit einer Zeit in Sekunden programmiert werden, um bspw. den Roboter zyklisch zu starten.
|
||||
|
||||
### timer.lua ###
|
||||
### delayer.lua ###
|
||||
|
||||
Start=Start
|
||||
|
||||
### timer.lua ###
|
||||
### logic_and.lua ###
|
||||
|
||||
Connected with=Verbunden mit
|
||||
|
||||
### tool.lua ###
|
||||
|
||||
Sensor Connection Tool=Sensor Verbindungswerkzeug
|
||||
|
||||
|
||||
##### not used anymore #####
|
410
signs_bot/locale/template.txt
Normal file
@ -0,0 +1,410 @@
|
||||
# textdomain: signs_bot
|
||||
|
||||
|
||||
|
||||
### basis.lua ###
|
||||
|
||||
running=
|
||||
charging=
|
||||
stopped=
|
||||
Off=
|
||||
On=
|
||||
Signs=
|
||||
Other items=
|
||||
Config=
|
||||
Preassign slots items=
|
||||
Back=
|
||||
Robot Box=
|
||||
no power=
|
||||
Signs Bot Box=
|
||||
The Box is the housing of the bot.=
|
||||
Place the box and start the bot by means of the 'On' button.=
|
||||
If the mod techage is installed, the bot needs electrical power.=
|
||||
The bot leaves the box on the right side.=
|
||||
It will not start, if this position is blocked.=
|
||||
To stop and remove the bot, press the 'Off' button.=
|
||||
The box inventory simulates the inventory of the bot.=
|
||||
You will not be able to access the inventory, if the bot is running.=
|
||||
The bot can carry up to 8 stacks and 6 signs with it.=
|
||||
|
||||
### bot_flap.lua ###
|
||||
|
||||
Exit=
|
||||
Bot Flap=
|
||||
The flap is a simple block used as door for the bot.=
|
||||
Place the flap in any wall, and the bot will automatically open=
|
||||
and close the flap as it passes through it.=
|
||||
|
||||
### bot_sensor.lua ###
|
||||
|
||||
Bot Sensor: Connected with=
|
||||
Bot Sensor=
|
||||
Bot Sensor: Not connected=
|
||||
The Bot Sensor detects any bot and sends a signal, if a bot is nearby.=
|
||||
The sensor direction does not care.=
|
||||
|
||||
### cart_sensor.lua ###
|
||||
|
||||
Cart Sensor: Connected with=
|
||||
Cart Sensor=
|
||||
Cart Sensor: Not connected=
|
||||
The Cart Sensor detects and sends a signal, if a cart (Minecart) is nearby.=
|
||||
The sensor has an active side (red) that must point to the rail/cart.=
|
||||
|
||||
### cart_sensor.lua ###
|
||||
### bot_sensor.lua ###
|
||||
|
||||
the sensor range is one node/meter.=
|
||||
|
||||
### changer.lua ###
|
||||
|
||||
Signs:=
|
||||
Bot Control Unit=
|
||||
The Bot Control Unit is used to lead the bot by means of signs.=
|
||||
The unit can be loaded with up to 4 different signs and can be programmed by means of sensors.=
|
||||
To load the unit, place a sign on the red side of the unit and click on the unit.=
|
||||
The sign disappears / is moved to the inventory of the unit.=
|
||||
This can be repeated 3 times.=
|
||||
Use the connection tool to connect up to 4 sensors with the Bot Control Unit.=
|
||||
|
||||
### chest.lua ###
|
||||
|
||||
Bot Chest: Sends signal to=
|
||||
Signs Bot Chest=
|
||||
Bot Chest: Not connected=
|
||||
The Signs Bot Chest is a special chest with sensor function.=
|
||||
It sends a signal depending on the chest state.=
|
||||
Possible states are 'empty', 'not empty', 'almost full'=
|
||||
A typical use case is to turn off the bot, when the chest is almost full or empty.=
|
||||
|
||||
### cmd_farming.lua ###
|
||||
|
||||
Sow farming seeds@nin front of the robot=
|
||||
Harvest farming products@nin front of the robot@non a 3x3 field.=
|
||||
Plant a sapling@nin front of the robot=
|
||||
Sign "farming"=
|
||||
Sign 'farming'=
|
||||
Used to harvest and seed a 3x3 field.=
|
||||
The seed to be placed has to be in the first inventory slot of the bot.=
|
||||
|
||||
### cmd_farming.lua ###
|
||||
### cmd_flowers.lua ###
|
||||
|
||||
Place the sign in front of the field.=
|
||||
When finished, the bot turns.=
|
||||
|
||||
### cmd_flowers.lua ###
|
||||
|
||||
Cutting flowers, leaves and tree blocks@nin front of the robot@non a 3x3 field.=
|
||||
Sign "flowers"=
|
||||
Sign 'flowers'=
|
||||
Used to cut flowers on a 3x3 field.=
|
||||
|
||||
### cmd_item.lua ###
|
||||
|
||||
Take <num> items from a chest like node@nand put it into the item inventory.@n<slot> is the inventory slot (1..8) or 0 for any one=
|
||||
Add <num> items to a chest like node@ntaken from the item inventory.@n<slot> is the inventory slot (1..8) or 0 for any one=
|
||||
Add <num> fuel to a furnace like node@ntaken from the item inventory.@n<slot> is the inventory slot (1..8) or 0 for any one=
|
||||
deprecated, use bot inventory configuration instead=
|
||||
Pick up all objects@nin a 3x3 field.@n<slot> is the inventory slot (1..8) or 0 for any one=
|
||||
Drop items in front of the bot.@n<slot> is the inventory slot (1..8) or 0 for any one=
|
||||
Punch a rail cart to start it=
|
||||
|
||||
### cmd_move.lua ###
|
||||
|
||||
Move the robot one step back=
|
||||
Turn the robot to the left=
|
||||
Turn the robot to the right=
|
||||
Turn the robot around=
|
||||
Move the robot upwards=
|
||||
Move the robot down=
|
||||
Stop the robot for <sec> seconds@n(1..9999)=
|
||||
Stop the robot.=
|
||||
Turn the robot off@nand put it back in the box.=
|
||||
|
||||
### cmd_pattern.lua ###
|
||||
|
||||
Store pattern to be cloned.=
|
||||
Copy the nodes from@nthe stored pattern position@n<size> is: 3x1, 3x2, 3x3,@n5x1, 5x2, 5x3 (wide x deep)@n<lvl> pattern level offset (0..4)=
|
||||
Sign "pattern"=
|
||||
Sign "copy 3x3x3"=
|
||||
Sign 'pattern'=
|
||||
Used to make a copy of a 3x3x3 cube.=
|
||||
Place the sign in front of the pattern to be copied.=
|
||||
Use the copy sign to make the copy of this pattern on a different location.=
|
||||
The bot must first reach the pattern sign, then the copy sign.=
|
||||
Sign 'copy3x3x3'=
|
||||
Place the sign in front of the location, where the copy should be made.=
|
||||
Use the pattern sign to mark the pattern.=
|
||||
|
||||
### cmd_place.lua ###
|
||||
|
||||
Error: Position protected=
|
||||
Place a block in front of the robot@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
|
||||
Place a block on the left side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
|
||||
Place a block on the right side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
|
||||
Place a block under the robot.@nHint: use 'move_up' first.@n<slot> is the inventory slot (1..8)=
|
||||
Place a block above the robot.@n<slot> is the inventory slot (1..8)=
|
||||
Error: No free inventory space=
|
||||
Dig the block in front of the robot@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
|
||||
Dig the block on the left side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
|
||||
Dig the block on the right side@n<slot> is the inventory slot (1..8)@n<lvl> is one of: -1 0 +1=
|
||||
Dig the block under the robot.@n<slot> is the inventory slot (1..8)=
|
||||
Dig the block above the robot.@n<slot> is the inventory slot (1..8)=
|
||||
Rotate the block in front of the robot@n<lvl> is one of: -1 0 +1@n<steps> is one of: 1 2 3=
|
||||
Bot torch=
|
||||
|
||||
### cmd_sign.lua ###
|
||||
|
||||
Commands,Help=
|
||||
Sign name:=
|
||||
Cancel=
|
||||
Check=
|
||||
Help=
|
||||
Copy Cmnd=
|
||||
please check the added line(s)=
|
||||
Sign "command"=
|
||||
-- enter or copy commands from help page=
|
||||
Error: Signs inventory empty=
|
||||
Error: Position protected or occupied=
|
||||
Place a sign in front of the robot@ntaken from the signs inventory@n<slot> is the inventory slot (1..6)=
|
||||
Place a sign behind the robot@ntaken from the signs inventory@n<slot> is the inventory slot (1..6)=
|
||||
Error: No sign available=
|
||||
Error: Signs inventory slot is occupied=
|
||||
Error: Position is protected=
|
||||
Dig the sign in front of the robot@nand add it to the signs inventory.@n<slot> is the inventory slot (1..6)=
|
||||
Dig the sign in front of the robot@nand add the cleared sign to@nthe item iventory.@n<slot> is the inventory slot (1..8)=
|
||||
Sign 'command'=
|
||||
The 'command' sign can be programmed by the player.=
|
||||
Place the sign in front of you and use the node menu to program your sequence of bot commands.=
|
||||
The menu has an edit field for your commands and a help page with all available commands.=
|
||||
|
||||
### commands.lua ###
|
||||
|
||||
commands:=
|
||||
unknown command=
|
||||
start of a 'repeat..end' block=
|
||||
end command of a 'repeat..end' block=
|
||||
call a subroutine (with 'return' statement)=
|
||||
return from a subroutine=
|
||||
jump to a label=
|
||||
Move the robot 1..999 steps forward@nwithout paying attention to any signs.@nUp and down movements also become@ncounted as steps.=
|
||||
Walk until a sign or obstacle is@nreached. Then continue with the next command.@nWhen a sign has been reached, @nthe current program is ended@nand the bot executes the@nnew program from the sign=
|
||||
Print given text as chat message.@nFor two or more words, use the '*' character @ninstead of spaces, like "Hello*world"=
|
||||
|
||||
### compost.lua ###
|
||||
|
||||
Put 2 leaves into the compost barrel@n<slot> is the bot inventory slot (1..8)@nwith the leaves.=
|
||||
Take a compost item from the barrel.@n<slot> (1..8 or 0 for the first free slot) is the bot@nslot for the compost item.=
|
||||
|
||||
### crop_sensor.lua ###
|
||||
|
||||
Crop Sensor: Connected with=
|
||||
Crop Sensor=
|
||||
Crop Sensor: Not connected=
|
||||
The Crop Sensor sends cyclical signals when, for example, wheat is fully grown.=
|
||||
The sensor range is one node/meter.=
|
||||
The sensor has an active side (red) that must point to the crop/field.=
|
||||
|
||||
### delayer.lua ###
|
||||
|
||||
Signal Delayer: Connected with=
|
||||
Delay time [sec]:=
|
||||
Signal Delayer=
|
||||
Signals are forwarded delayed. Subsequent signals are queued.=
|
||||
The delay time can be configured.=
|
||||
|
||||
### doc.lua ###
|
||||
|
||||
After you have placed the Signs Bot Box, you can start the bot by means of the 'On' button in the box menu.=
|
||||
If the bot returns to its box right away, you will likely need to charge it with electrical energy (techage) first.=
|
||||
The bot then runs straight up until it reaches an obstacle (a step with two or more blocks up or down or a sign.)=
|
||||
If the bot first reaches a sign it will execute the commands on the sign.=
|
||||
If the command(s) on the sign is e.g. 'turn_around', the bot turns and goes back.=
|
||||
In this case, the bot reaches his box again and turns off.=
|
||||
The Signs Bot Box has an inventory with 6 stacks for signs and 8 stacks for other items (to be placed/dug by the bot).=
|
||||
This inventory simulates the bot internal inventory.=
|
||||
That means you will only have access to the inventory if the bot is turned off ('sitting' in his box).=
|
||||
You simply control the direction of the bot by means of the 'turn left' and 'turn right' signs (signs with the arrow).=
|
||||
The bot can run over steps (one block up/down). But there are also commands to move the bot up and down.=
|
||||
It is not necessary to mark a way back to the box.=
|
||||
With the command 'turn_off' the bot will turn off and be back in his box from every position.=
|
||||
The same applies if you turn off the bot by the box menu.=
|
||||
If the bot reaches a sign from the wrong direction (from back or sides) the sign will be ignored.=
|
||||
The bot will walk over.=
|
||||
All predefined signs have a menu with a list of the bot commands.=
|
||||
These signs can't be changed, but you can craft and program your own signs.=
|
||||
For this you have to use the 'command' sign.=
|
||||
This sign has an edit field for your commands and a help page with all available commands.=
|
||||
Also for your own signs it is important to know:=
|
||||
After the execution of the last command of the sign, the bot falls back into its default behaviour and runs in its taken direction.=
|
||||
A standard job for the bot is to move items from one chest to another=
|
||||
(chest or node with a chest like inventory).=
|
||||
This can be done by means of the two signs 'take item' and 'add item'.=
|
||||
These signs have to be placed on top of chest nodes.=
|
||||
In addition to the signs the bot can be controlled by means of sensors.=
|
||||
Sensors like the Bot Sensor have two states: on and off.=
|
||||
If the Bot Sensor detects a bot it will switch to the state 'on' and=
|
||||
sends a signal to a connected block, called an actuator.=
|
||||
Sensors are:=
|
||||
- Bot Sensor: Sends a signal when the robot passes by=
|
||||
- Node Sensor: Sends a signal when it detects any node=
|
||||
- Crop Sensor: Sends a signal when, for example wheat is fully grown=
|
||||
- Bot Chest: Sends a signal depending on the chest state (empty, full)=
|
||||
Actuators are:=
|
||||
- Signs Bot Box: Can turn the bot off and on=
|
||||
- Control Unit: Can be used to exchange the sign to lead the bot=
|
||||
Additional sensors and actuator can be added by other mods.=
|
||||
To send a signal from a sensor to an actuator, the sensor has to be connected (paired) with actuator.=
|
||||
To connect sensor and actuator, the Sensor Connection Tool has to be used.=
|
||||
Simply click with the tool on both blocks and the sensor will be connected with the actuator.=
|
||||
A successful connection is indicated by a ping/pong noise.=
|
||||
Before you connect sensor with actuator, take care that the actuator is in the requested state.=
|
||||
For example: If you want to start the Bot with a sensor, connect the sensor with the Bot Box,=
|
||||
when the Bot is in the state 'on'. Otherwise the sensor signal will stop the Bot,=
|
||||
instead of starting it.=
|
||||
The following applies to all commands that are used to place items in the bot inventory, like:=
|
||||
- take_item <num> <slot>=
|
||||
- pickup_items <slot>=
|
||||
- trash_sign <slot>=
|
||||
- harvest <slot>=
|
||||
- dig_front <slot> <lvl>=
|
||||
- dig_left <slot> <lvl>=
|
||||
- dig_right <slot> <lvl>=
|
||||
- dig_below <slot> <lvl>=
|
||||
- dig_above <slot> <lvl>=
|
||||
If no slot or slot 0 was specified with the command (case A), all 8 slots of the bot inventory =
|
||||
are checked one after the other. If a slot was specified (case B), only this slot is checked.=
|
||||
In both cases the following applies: If the slot is preconfigured and fits the item, =
|
||||
or if the slot is not configured and empty, or is only partially filled with the item type =
|
||||
(which should be added), then the items are added.=
|
||||
If not all items can be added, the remaining slots will be tried out in case A.=
|
||||
Anything that could not be added to your own inventory goes back.=
|
||||
The following applies to all commands that are used to take items from the bot inventory, like:=
|
||||
- add_item <num> <slot>=
|
||||
It doesn't matter whether a slot is configured or not. The bot takes the first stack that =
|
||||
it can find from its own inventory and tries to use it.=
|
||||
If a slot is specified, it only takes this, if no slot has been specified, it checks all of =
|
||||
them one after the other, starting from slot 1 until it finds something.=
|
||||
If the number found is smaller than requested, he tries to take the rest out of any slot.=
|
||||
Signs Bot=
|
||||
A robot controlled by signs, used for automated work=
|
||||
Start the Bot=
|
||||
Control the Bot=
|
||||
Sensors and Actuators=
|
||||
Connecting sensors and actuator=
|
||||
Bot inventory behavior=
|
||||
|
||||
### doc.lua ###
|
||||
### cmd_sign.lua ###
|
||||
|
||||
The help page has a copy button to simplify the programming.=
|
||||
|
||||
### duplicator.lua ###
|
||||
|
||||
Template:=
|
||||
Input:=
|
||||
Output:=
|
||||
1. Place one 'cmnd' sign=
|
||||
2. Add 'blank signs'=
|
||||
3. Take the copies=
|
||||
Signs Duplicator=
|
||||
Sign "user"=
|
||||
Sign "blank"=
|
||||
The Duplicator can be used to make copies of signs.=
|
||||
1. Put one 'cmnd' sign to be used as template into the 'Template' inventory=
|
||||
2. Add one or several 'blank signs' to the 'Input' inventory.=
|
||||
3. Take the copies from the 'Output' inventory.=
|
||||
Written books [default:book_written] can alternatively be used as template=
|
||||
Already written signs can be used as input, too.=
|
||||
Needed as input for the Duplicator.=
|
||||
|
||||
### extender.lua ###
|
||||
|
||||
Sensor Extender: Connected with=
|
||||
Sensor Extender=
|
||||
Sensor Extender: Not connected=
|
||||
With the Sensor Extender, sensor signals can be sent to more than one actuator.=
|
||||
Place one or more extender nearby the sensor and connect each extender=
|
||||
with one further actuator by means of the Connection Tool.=
|
||||
|
||||
### interpreter.lua ###
|
||||
|
||||
Maximum programm size exceeded=
|
||||
Too many parameters=
|
||||
Parameter error=
|
||||
Command error=
|
||||
'repeat' missing=
|
||||
'end' missing=
|
||||
Checked and approved=
|
||||
|
||||
### logic_and.lua ###
|
||||
|
||||
Signal AND with=
|
||||
inputs=
|
||||
Signal AND=
|
||||
Signal is sent, if all input signals are received.=
|
||||
|
||||
### node_sensor.lua ###
|
||||
|
||||
Node Sensor: Connected with =
|
||||
added=
|
||||
removed=
|
||||
added or removed=
|
||||
Send signal if nodes have been:=
|
||||
accept=
|
||||
Node Sensor=
|
||||
Node Sensor: Not connected=
|
||||
The node sensor sends cyclical signals when it detects that nodes have appeared or disappeared,=
|
||||
but has to be configured accordingly.=
|
||||
Valid nodes are all kind of blocks and plants.=
|
||||
The sensor range is 3 nodes/meters in one direction.=
|
||||
The sensor has an active side (red) that must point to the observed area.=
|
||||
|
||||
### signs.lua ###
|
||||
|
||||
Sign "turn right"=
|
||||
Sign "turn left"=
|
||||
Sign "take item"=
|
||||
Sign "add item"=
|
||||
Sign "stop"=
|
||||
Sign "add to cart"=
|
||||
Sign "take from cart"=
|
||||
The Bot turns right when it detects this sign in front of it.=
|
||||
The Bot turns left when it detects this sign in front of it.=
|
||||
The Bot takes items out of a chest in front of it and then turns around.=
|
||||
This sign has to be placed on top of the chest.=
|
||||
The Bot puts items into a chest in front of it and then turns around.=
|
||||
The Bot will stop in front of this sign until the sign is removed or the bot is turned off.=
|
||||
The Bot puts items into a minecart in front of it, pushes the cart and then turns around.=
|
||||
This sign has to be placed on top of the rail at the cart end position.=
|
||||
The Bot takes items out of a minecart in front of it, pushes the cart and then turns around.=
|
||||
|
||||
### techage.lua ###
|
||||
|
||||
Ignite the techage charcoal lighter=
|
||||
Turns the bot off if the@nbattery power is below the@ngiven value in percent (1..99)=
|
||||
fully charged=
|
||||
Sends a techage command@nto a given node. @nReceiver is addressed by@nthe techage node number.@nFor commands with two or more @nwords, use the '*' character @ninstead of spaces, e.g.: @nsend_cmnd 3465 pull*default:dirt*2=
|
||||
|
||||
### timer.lua ###
|
||||
|
||||
Bot Timer=
|
||||
Cycle time [min]:=
|
||||
Bot Timer: Not connected=
|
||||
Special kind of sensor.=
|
||||
Can be programmed with a time in seconds, e.g. to start the bot cyclically.=
|
||||
|
||||
### timer.lua ###
|
||||
### delayer.lua ###
|
||||
|
||||
Start=
|
||||
|
||||
### timer.lua ###
|
||||
### logic_and.lua ###
|
||||
|
||||
Connected with=
|
||||
|
||||
### tool.lua ###
|
||||
|
||||
Sensor Connection Tool=
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,13 +13,12 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
@ -38,12 +37,12 @@ end
|
||||
local function update_infotext(pos, dest_pos, cmnd)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
local text = table.concat({
|
||||
I("Signal AND with"),
|
||||
S("Signal AND with"),
|
||||
#mem.inputs or 0,
|
||||
I("inputs"),
|
||||
S("inputs"),
|
||||
":",
|
||||
I("Connected with"),
|
||||
S(dest_pos),
|
||||
S("Connected with"),
|
||||
P2S(dest_pos),
|
||||
"/",
|
||||
cmnd,
|
||||
":",
|
||||
@ -80,7 +79,7 @@ local function infotext(pos)
|
||||
local dest_pos = meta:get_string("signal_pos")
|
||||
local signal = meta:get_string("signal_data")
|
||||
if dest_pos ~= "" and signal ~= "" then
|
||||
update_infotext(pos, P(dest_pos), signal)
|
||||
update_infotext(pos, S2P(dest_pos), signal)
|
||||
end
|
||||
end
|
||||
|
||||
@ -143,7 +142,7 @@ local function signs_bot_on_signal(pos, node, signal)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:and1", {
|
||||
description = I("Signal AND"),
|
||||
description = S("Signal AND"),
|
||||
inventory_image = "signs_bot_and_inv.png",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
@ -170,6 +169,7 @@ minetest.register_node("signs_bot:and1", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -178,7 +178,7 @@ minetest.register_node("signs_bot:and1", {
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:and2", {
|
||||
description = I("Signal AND"),
|
||||
description = S("Signal AND"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
@ -197,6 +197,7 @@ minetest.register_node("signs_bot:and2", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -206,7 +207,7 @@ minetest.register_node("signs_bot:and2", {
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:and3", {
|
||||
description = I("Signal AND"),
|
||||
description = S("Signal AND"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
@ -223,6 +224,7 @@ minetest.register_node("signs_bot:and3", {
|
||||
update_infotext = update_infotext,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -242,11 +244,11 @@ minetest.register_craft({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "and", {
|
||||
name = I("Signal AND"),
|
||||
name = S("Signal AND"),
|
||||
data = {
|
||||
item = "signs_bot:and1",
|
||||
text = table.concat({
|
||||
I("Signal is sent, if all input signals are received."),
|
||||
S("Signal is sent, if all input signals are received."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -13,20 +13,18 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath("signs_bot")
|
||||
local I,_ = dofile(MP.."/intllib.lua")
|
||||
-- Load support for I18n.
|
||||
local S = signs_bot.S
|
||||
|
||||
local lib = signs_bot.lib
|
||||
|
||||
local CYCLE_TIME = 2
|
||||
|
||||
local function update_infotext(pos, dest_pos, cmnd)
|
||||
M(pos):set_string("infotext", I("Node Sensor: Connected with ")..S(dest_pos).." / "..cmnd)
|
||||
M(pos):set_string("infotext", S("Node Sensor: Connected with ")..P2S(dest_pos).." / "..cmnd)
|
||||
end
|
||||
|
||||
local function swap_node(pos, name)
|
||||
@ -44,20 +42,20 @@ end
|
||||
|
||||
|
||||
local DropdownValues = {
|
||||
[I("added")] = 1,
|
||||
[I("removed")] = 2,
|
||||
[I("added or removed")] = 3,
|
||||
[S("added")] = 1,
|
||||
[S("removed")] = 2,
|
||||
[S("added or removed")] = 3,
|
||||
}
|
||||
|
||||
local function formspec(mem)
|
||||
local label = I("added")..","..I("removed")..","..I("added or removed")
|
||||
local label = S("added")..","..S("removed")..","..S("added or removed")
|
||||
return "size[6,3]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[0.2,0.4;"..I("Send signal if nodes have been:").."]"..
|
||||
"label[0.2,0.4;"..S("Send signal if nodes have been:").."]"..
|
||||
"dropdown[0.2,1;6,1;mode;"..label..";"..(mem.mode or 3).."]"..
|
||||
"button_exit[1.5,2.2;3,1;accept;"..I("accept").."]"
|
||||
"button_exit[1.5,2.2;3,1;accept;"..S("accept").."]"
|
||||
end
|
||||
|
||||
local function any_node_changed(pos)
|
||||
@ -115,7 +113,7 @@ local function node_timer(pos)
|
||||
end
|
||||
|
||||
minetest.register_node("signs_bot:node_sensor", {
|
||||
description = I("Node Sensor"),
|
||||
description = S("Node Sensor"),
|
||||
inventory_image = "signs_bot_sensor_node_inv.png",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
@ -137,7 +135,7 @@ minetest.register_node("signs_bot:node_sensor", {
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
local mem = tubelib2.init_mem(pos)
|
||||
meta:set_string("infotext", "Node Sensor: Not connected")
|
||||
meta:set_string("infotext", S("Node Sensor: Not connected"))
|
||||
mem.mode = 3 -- default legacy mode
|
||||
meta:set_string("formspec", formspec(mem))
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
@ -149,6 +147,7 @@ minetest.register_node("signs_bot:node_sensor", {
|
||||
on_receive_fields = on_receive_fields,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -157,7 +156,7 @@ minetest.register_node("signs_bot:node_sensor", {
|
||||
})
|
||||
|
||||
minetest.register_node("signs_bot:node_sensor_on", {
|
||||
description = I("Node Sensor"),
|
||||
description = S("Node Sensor"),
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
@ -180,6 +179,7 @@ minetest.register_node("signs_bot:node_sensor_on", {
|
||||
on_receive_fields = on_receive_fields,
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
use_texture_alpha = signs_bot.CLIP,
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
@ -213,15 +213,15 @@ minetest.register_lbm({
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry("signs_bot", "node_sensor", {
|
||||
name = I("Node Sensor"),
|
||||
name = S("Node Sensor"),
|
||||
data = {
|
||||
item = "signs_bot:node_sensor",
|
||||
text = table.concat({
|
||||
I("The node sensor sends cyclical signals when it detects that nodes have appeared or disappeared,"),
|
||||
I("but has to be configured accordingly."),
|
||||
I("Valid nodes are all kind of blocks and plants."),
|
||||
I("The sensor range is 3 nodes/meters in one direction."),
|
||||
I("The sensor has an active side (red) that must point to the observed area."),
|
||||
S("The node sensor sends cyclical signals when it detects that nodes have appeared or disappeared,"),
|
||||
S("but has to be configured accordingly."),
|
||||
S("Valid nodes are all kind of blocks and plants."),
|
||||
S("The sensor range is 3 nodes/meters in one direction."),
|
||||
S("The sensor has an active side (red) that must point to the observed area."),
|
||||
}, "\n")
|
||||
},
|
||||
})
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -69,7 +69,7 @@ if farming.mod == "redo" then
|
||||
fp("farming:cocoa_beans", "farming:cocoa_1", "farming:cocoa_4")
|
||||
fp("farming:garlic_clove", "farming:garlic_1", "farming:garlic_5")
|
||||
fp("farming:onion", "farming:onion_1", "farming:onion_5")
|
||||
fp("farming:peas", "farming:pea_1", "farming:pea_5")
|
||||
fp("farming:pea_pod", "farming:pea_1", "farming:pea_5")
|
||||
fp("farming:peppercorn", "farming:pepper_1", "farming:pepper_5")
|
||||
fp("farming:pineapple_top", "farming:pineapple_1", "farming:pineapple_8")
|
||||
end
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
|