update techpack to 2.06
49
techpack_modpack/.gitignore
vendored
@ -1,49 +0,0 @@
|
||||
# Compiled Lua sources
|
||||
luac.out
|
||||
|
||||
# luarocks build files
|
||||
*.src.rock
|
||||
*.zip
|
||||
*.tar.gz
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.os
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
*.def
|
||||
*.exp
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
.buildpath
|
||||
.project
|
||||
org.eclipse.*
|
||||
*.lua.new
|
||||
|
||||
test_*.lua
|
||||
|
||||
shrink.py
|
@ -1,28 +0,0 @@
|
||||
The TechPack Modpack for Minetest is
|
||||
|
||||
Copyright (C) 2017-2018 Joachim Stolberg
|
||||
|
||||
License of source code
|
||||
----------------------
|
||||
|
||||
This program is free software; you can redistribute and/or
|
||||
modify it under the terms of the GNU Lesser General Public License version 2.1 or later
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
License of media (textures, sounds and documentation)
|
||||
-----------------------------------------------------
|
||||
|
||||
All textures, sounds and documentation files are licensed under the
|
||||
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
@ -1,4 +1,4 @@
|
||||
# TechPack V2.02
|
||||
# TechPack V2.06
|
||||
|
||||
TechPack, a Mining, Crafting, & Farming Modpack for Minetest.
|
||||
|
||||
@ -27,6 +27,7 @@ TechPack is a collection of following Mods:
|
||||
**The moved/copied nodes will not have valid node numbers, which could lead to a server crash.**
|
||||
|
||||
TechPack provides:
|
||||
|
||||
- lumber tubes to connect 2 nodes
|
||||
- a Pusher node to pull/push items through tubes
|
||||
- a Distributor node with 4 output channels to sort incoming items
|
||||
@ -64,9 +65,11 @@ TechPack provides:
|
||||
- a Display node for text outputs of the Controller
|
||||
- Metal ladders, stairways, and bridges
|
||||
- Warehouse Boxes in steel, copper, and gold
|
||||
- A chest cart for the mod minecart
|
||||
|
||||
|
||||
TechPack supports the following mods:
|
||||
|
||||
- Farming Redo (Harvester, Fermenter)
|
||||
- Ethereal (Harvester, Quarry, Fermenter)
|
||||
- Pipeworks (Gravel Sieve)
|
||||
@ -75,12 +78,14 @@ TechPack supports the following mods:
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
The following can be changed in the minetest menu (Settings -> Advanced Settings -> Mods -> tubelib) or directly in 'minetest.conf'
|
||||
- Maximum number of Forceload Blocks per player
|
||||
- Enable Basalt Stone (and disable ore generation via Cobblestone generator)
|
||||
- Machine aging value to calculate the lifetime of machines
|
||||
|
||||
Example for 'minetest.conf':
|
||||
|
||||
```LUA
|
||||
tubelib_basalt_stone_enabled = false
|
||||
tubelib_max_num_forceload_blocks = 12
|
||||
@ -88,6 +93,7 @@ tubelib_machine_aging_value = 200
|
||||
```
|
||||
|
||||
Example for a v1 compatible 'minetest.conf':
|
||||
|
||||
```LUA
|
||||
tubelib_basalt_stone_enabled = false
|
||||
tubelib_max_num_forceload_blocks = 0
|
||||
@ -96,38 +102,64 @@ tubelib_machine_aging_value = 999999
|
||||
|
||||
|
||||
#### Maximum number of Forceload Blocks per player
|
||||
|
||||
Default value is 12.
|
||||
I higher number allows to build larger farms and machines which keep loaded, but increases the server load, too.
|
||||
But the areas are only loaded when the player is online.
|
||||
To be able to use e.g. 12 forceloaded blocks per player, the pararamter 'max_forceloaded_blocks' in 'minetest.conf' has to be ajusted.
|
||||
|
||||
|
||||
#### Enable Basalt Stone (and disable ore generation via Cobblestone generator)
|
||||
|
||||
The lava/water Cobblestone generator allows to produce infinite Cobblestone. By means of Quarry,
|
||||
Grinder, and Gravel Sieve it allows to infinite generate ores.
|
||||
This can be disabled by means of the setting parameter. If enabled, the Cobblestone
|
||||
generator generates Basalt instead, which only can be used for building purposes.
|
||||
|
||||
|
||||
#### Machine aging value to calculate the lifetime of machines
|
||||
|
||||
Default value is 200.
|
||||
This aging value is used to calculate the lifetime of machines before they go defect.
|
||||
The value 200 (default) results in a lifetime for standard machines of about 2000 - 8000 item processing cycles (~2-4 hours).
|
||||
|
||||
|
||||
### License
|
||||
Copyright (C) 2017-2019 Joachim Stolberg
|
||||
Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt
|
||||
|
||||
Copyright (C) 2017-2021 Joachim Stolberg
|
||||
Code: Licensed under the GNU AGPL version 3 or later. See LICENSE.txt
|
||||
Textures: CC BY-SA 3.0
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
|
||||
### Contributors
|
||||
|
||||
- oversword (PR #43, #57, #58, #59, #60, #62, #68, #74, #76, and many more)
|
||||
- afkplayer5000 (PR #70, #71)
|
||||
- andrenete (PR #37, #66)
|
||||
- fluxionary (PR #27, #28, #30, #31, #34, #54)
|
||||
- Arigatas (PR #51, #53)
|
||||
- realmicu (PR #6, #8, #12)
|
||||
- theFox6 (PR #3, #4)
|
||||
- superfloh247 (PR #89, #88, #87)
|
||||
- SciFurz (via forum)
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
default, doors, intllib, basic_materials
|
||||
tubelib2 ()
|
||||
Tubelib Color Lamps optional: unifieddyes
|
||||
SmartLine Controller optional: mail
|
||||
Gravelsieve optional: moreores, hopper, pipeworks
|
||||
tubelib_addons1 optional: unified_inventory
|
||||
tubelib_addons13 optional: minecart
|
||||
|
||||
|
||||
### History
|
||||
|
||||
- 2018-03-18 V1.00 * Tubelib, tubelib_addons1, tubelib_addons2, smartline, and gravelsieve combined to one modpack.
|
||||
- 2018-03-24 V1.01 * Support for Ethereal added
|
||||
- 2018-03-27 V1.02 * Timer improvements for unloaded areas
|
||||
@ -151,9 +183,14 @@ tubelib_addons1 optional: unified_inventory
|
||||
- 2019-01-27 V2.01 * SaferLua Controller Terminal added
|
||||
- 2019-01-28 V2.02 * Logic Not added, output reduction on Harvester, Fermenter, and Gravel Sieve
|
||||
- 2019-04-23 V2.03 * Piston/WorldEdit/replacer detection added, farming and grinder recipes added
|
||||
- 2020-11-20 V2.04 * Switch to AGPL v3, adapt to minetest 5.3, add translation support, fix minor bugs
|
||||
- 2021-01-24 V2.05 * PR #74, #76: Implement checks for valid connection sides for many nodes
|
||||
- 2021-06-06 V2.06 * PR #78 - #89, chest cart added
|
||||
- 2021-09-03 V2.07 * FR #103, Add Altitude to harvester menu
|
||||
|
||||
|
||||
## New in v2 (from players point of view)
|
||||
|
||||
- Almost all machines break after a certain amount of time (switch into the state 'defect') and have to be repaired.
|
||||
- A Repair Kit is available to repair defect machines.
|
||||
- A Forceload block (16x16x16) is added which keeps the corresponding area loaded and the machines operational as far as the player is logged in.
|
||||
@ -164,6 +201,7 @@ tubelib_addons1 optional: unified_inventory
|
||||
|
||||
|
||||
## New in v2 (from admins point of view)
|
||||
|
||||
- settingtypes introduced with the following settings: tubelib_max_num_forceload_blocks, tubelib_basalt_stone_enabled, tubelib_machine_aging_value
|
||||
- the new mods 'techpack_stairway' and 'techpack_warehouse' have to be enabled
|
||||
- TechPack depends now on the mod 'basic_materials' and 'tubelib2' ()
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
]]--
|
||||
|
||||
-- Load support for I18n
|
||||
local S = gravelsieve.S
|
||||
|
||||
gravelsieve.disallow = function(pos, node, user, mode, new_param2)
|
||||
return false
|
||||
@ -40,7 +42,7 @@ gravelsieve.handler = function(itemstack, user, pointed_thing)
|
||||
end
|
||||
|
||||
minetest.register_tool("gravelsieve:hammer", {
|
||||
description = "Hammer converts Cobblestone into Gravel",
|
||||
description = S("Hammer converts Cobblestone into Gravel"),
|
||||
inventory_image = "gravelsieve_hammer.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
return gravelsieve.handler(itemstack, user, pointed_thing)
|
||||
|
@ -7,11 +7,11 @@
|
||||
Derived from the work of celeron55, Perttu Ahola (furnace)
|
||||
Pipeworks support added by FiftySix
|
||||
|
||||
Copyright (C) 2017-2018 Joachim Stolberg
|
||||
Copyright (C) 2017-2020 Joachim Stolberg
|
||||
Copyright (C) 2011-2016 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2011-2016 Various Minetest developers and contributors
|
||||
|
||||
LGPLv2.1+
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
History:
|
||||
@ -42,13 +42,17 @@
|
||||
gravelsieve = {
|
||||
}
|
||||
|
||||
-- Load support for I18n
|
||||
gravelsieve.S = minetest.get_translator("gravelsieve")
|
||||
local S = gravelsieve.S
|
||||
|
||||
dofile(minetest.get_modpath("gravelsieve") .. "/hammer.lua")
|
||||
|
||||
local settings_get
|
||||
if minetest.setting_get then
|
||||
settings_get = minetest.setting_get
|
||||
else
|
||||
settings_get = function(...) endminetest.settings:get(...) end
|
||||
settings_get = function(...) return minetest.settings:get(...) end
|
||||
end
|
||||
gravelsieve.ore_rarity = tonumber(settings_get("gravelsieve_ore_rarity")) or 1.16
|
||||
gravelsieve.ore_max_elevation = tonumber(settings_get("gravelsieve_ore_max_elevation")) or 0
|
||||
@ -56,7 +60,9 @@ gravelsieve.ore_min_elevation = tonumber(settings_get("gravelsieve_ore_min_eleva
|
||||
local y_spread = math.max(1 + gravelsieve.ore_max_elevation - gravelsieve.ore_min_elevation, 1)
|
||||
|
||||
-- Increase the probability over the natural occurrence
|
||||
local PROBABILITY_FACTOR = 3
|
||||
local PROBABILITY_FACTOR = tonumber(settings_get("gravelsieve_probability_factor")) or 3
|
||||
|
||||
local STEP_DELAY = tonumber(settings_get("gravelsieve_step_delay")) or 1.0
|
||||
|
||||
-- tubelib aging feature
|
||||
local AGING_LEVEL1 = nil
|
||||
@ -70,6 +76,8 @@ end
|
||||
gravelsieve.ore_probability = {
|
||||
}
|
||||
|
||||
gravelsieve.process_probabilities = {}
|
||||
|
||||
|
||||
-- Pipeworks support
|
||||
local pipeworks_after_dig = nil
|
||||
@ -80,10 +88,6 @@ if minetest.get_modpath("pipeworks") and pipeworks ~= nil then
|
||||
pipeworks_after_place = pipeworks.after_place
|
||||
end
|
||||
|
||||
local function harmonic_sum(a, b)
|
||||
return 1 / ((1 / a) + (1 / b))
|
||||
end
|
||||
|
||||
local function calculate_probability(item)
|
||||
local ymax = math.min(item.y_max, gravelsieve.ore_max_elevation)
|
||||
local ymin = math.max(item.y_min, gravelsieve.ore_min_elevation)
|
||||
@ -91,6 +95,41 @@ local function calculate_probability(item)
|
||||
item.clust_scarcity / (item.clust_num_ores * ((ymax - ymin) / y_spread))
|
||||
end
|
||||
|
||||
local function pairs_by_values(t, f)
|
||||
if not f then
|
||||
f = function(a, b) return a > b end
|
||||
end
|
||||
local s = {}
|
||||
for k, v in pairs(t) do
|
||||
table.insert(s, {k, v})
|
||||
end
|
||||
table.sort(s, function(a, b)
|
||||
return f(a[2], b[2])
|
||||
end)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
local v = s[i]
|
||||
if v then
|
||||
return unpack(v)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function parse_drop(drop)
|
||||
local d, count = drop:match("^%s*(%S+)%s+(%d+)%s*$")
|
||||
if d and count then
|
||||
return d, count
|
||||
end
|
||||
d, count = drop:match("%s*craft%s+\"?([^%s\"]+)\"?%s+(%d+)%s*")
|
||||
if d and count then
|
||||
return d, count
|
||||
end
|
||||
return drop, 1
|
||||
end
|
||||
|
||||
-- collect all registered ores and calculate the probability
|
||||
local function add_ores()
|
||||
for _,item in pairs(minetest.registered_ores) do
|
||||
@ -104,27 +143,136 @@ local function add_ores()
|
||||
and item.clust_scarcity ~= nil and item.clust_scarcity > 0
|
||||
and item.clust_num_ores ~= nil and item.clust_num_ores > 0
|
||||
and item.y_max ~= nil and item.y_min ~= nil then
|
||||
local count
|
||||
drop, count = parse_drop(drop)
|
||||
|
||||
local probability = calculate_probability(item)
|
||||
if probability > 0 then
|
||||
local probabilityFraction = count / probability
|
||||
local cur_probability = gravelsieve.ore_probability[drop]
|
||||
if cur_probability then
|
||||
gravelsieve.ore_probability[drop] = harmonic_sum(cur_probability, probability)
|
||||
gravelsieve.ore_probability[drop] = cur_probability+probabilityFraction
|
||||
else
|
||||
gravelsieve.ore_probability[drop] = probability
|
||||
gravelsieve.ore_probability[drop] = probabilityFraction
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.log("action", "[gravelsieve] ore probabilties:")
|
||||
local overall_probability = 0.0
|
||||
for name,probability in pairs(gravelsieve.ore_probability) do
|
||||
minetest.log("info", ("[gravelsieve] %-32s %.02f"):format(name, probability))
|
||||
overall_probability = overall_probability + 1.0/probability
|
||||
for name,probability in pairs_by_values(gravelsieve.ore_probability) do
|
||||
minetest.log("action", ("[gravelsieve] %-32s: 1 / %.02f"):format(name, 1.0/probability))
|
||||
overall_probability = overall_probability + probability
|
||||
end
|
||||
minetest.log("info", ("[gravelsieve] Overall probability %f"):format(overall_probability))
|
||||
minetest.log("action", ("[gravelsieve] Overall probability %f"):format(overall_probability))
|
||||
end
|
||||
|
||||
minetest.after(1, add_ores)
|
||||
local function default_configuration()
|
||||
local normal_gravel = "default:gravel"
|
||||
local sieved_gravel = "gravelsieve:sieved_gravel"
|
||||
local gravel_probabilities = table.copy(gravelsieve.ore_probability)
|
||||
local overall_probability = 0
|
||||
for _,v in pairs(gravel_probabilities) do
|
||||
overall_probability = overall_probability+v
|
||||
end
|
||||
local remainder_probability = 0
|
||||
if overall_probability < 1 then
|
||||
remainder_probability = 1-overall_probability
|
||||
end
|
||||
gravel_probabilities[normal_gravel] = remainder_probability/2.0
|
||||
gravel_probabilities[sieved_gravel] = remainder_probability/2.0
|
||||
|
||||
return {
|
||||
[normal_gravel] = gravel_probabilities,
|
||||
[sieved_gravel] = {
|
||||
[sieved_gravel] = 1
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function normalize_probabilities(conf)
|
||||
local total = 0
|
||||
for _,val in pairs(conf) do
|
||||
if val >= 0 then
|
||||
total = total + val
|
||||
end
|
||||
end
|
||||
local normalized = {}
|
||||
for key,val in pairs(conf) do
|
||||
if val >= 0 then
|
||||
normalized[key] = val/total
|
||||
end
|
||||
end
|
||||
return normalized
|
||||
end
|
||||
|
||||
local function normalize_config(current_config)
|
||||
local normalized_config = {}
|
||||
-- Normalize all inputs so their output probabilities always add up to 1
|
||||
for input, output_probabilities in pairs(current_config) do
|
||||
if output_probabilities then
|
||||
normalized_config[input] = normalize_probabilities(output_probabilities)
|
||||
end
|
||||
end
|
||||
return normalized_config
|
||||
end
|
||||
|
||||
local function merge_config(def_conf, new_conf)
|
||||
local result_conf = table.copy(def_conf)
|
||||
for key,val in pairs(new_conf) do
|
||||
if type(val) == 'table' and type(result_conf[key]) == 'table' then
|
||||
result_conf[key] = merge_config(result_conf[key], val)
|
||||
else
|
||||
result_conf[key] = val
|
||||
end
|
||||
end
|
||||
return result_conf
|
||||
end
|
||||
|
||||
local function configure_probabilities_step(current_config, funct_or_table)
|
||||
local var_type = type(funct_or_table)
|
||||
local conf
|
||||
if var_type == 'function' then
|
||||
conf = funct_or_table()
|
||||
elseif var_type == 'table' then
|
||||
conf = funct_or_table
|
||||
end
|
||||
if conf then
|
||||
return merge_config(current_config, conf)
|
||||
end
|
||||
return current_config
|
||||
end
|
||||
|
||||
local configured = false
|
||||
local set_probabilities = {default_configuration}
|
||||
|
||||
function gravelsieve.set_probabilities(funct_or_table)
|
||||
if configured then
|
||||
-- This is here so you can do hard overrides after everything has loaded if you need to
|
||||
-- Otherwise the order mods are loaded may cause them to override your configs
|
||||
local current_config = gravelsieve.process_probabilities
|
||||
current_config = configure_probabilities_step(current_config, funct_or_table)
|
||||
gravelsieve.process_probabilities = normalize_config(current_config)
|
||||
else
|
||||
-- Build up a list of callbacks to be run after all mods are loaded
|
||||
table.insert(set_probabilities, funct_or_table)
|
||||
end
|
||||
end
|
||||
|
||||
local function configure_probabilities()
|
||||
configured = true
|
||||
add_ores()
|
||||
local current_config = {}
|
||||
|
||||
-- Run through all configs in order and merge them
|
||||
for _,funct_or_table in ipairs(set_probabilities) do
|
||||
current_config = configure_probabilities_step(current_config, funct_or_table)
|
||||
end
|
||||
gravelsieve.process_probabilities = normalize_config(current_config)
|
||||
end
|
||||
|
||||
minetest.after(1, configure_probabilities)
|
||||
|
||||
local sieve_formspec =
|
||||
"size[8,8]"..
|
||||
@ -197,48 +345,35 @@ local function swap_node(pos, meta, start)
|
||||
end
|
||||
|
||||
-- place ores to dst according to the calculated probability
|
||||
local function random_ore(inv, src)
|
||||
local num
|
||||
for ore, probability in pairs(gravelsieve.ore_probability) do
|
||||
if math.random(probability) == 1 then
|
||||
local item = ItemStack(ore)
|
||||
if inv:room_for_item("dst", item) then
|
||||
inv:add_item("dst", item)
|
||||
return true -- ore placed
|
||||
local function move_random_ore(inv, item)
|
||||
local running_total = 0
|
||||
local probabilities = gravelsieve.process_probabilities[item]
|
||||
local chosen = math.random()
|
||||
for ore, probability in pairs(probabilities) do
|
||||
running_total = running_total + probability
|
||||
if chosen < running_total then
|
||||
local ore_item = ItemStack(ore)
|
||||
if not inv:room_for_item("dst", ore_item) then
|
||||
return false
|
||||
end
|
||||
inv:add_item("dst", ore_item)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false -- Failure, this shouldn't really happen but might due to floating point errors
|
||||
end
|
||||
return false -- gravel has to be moved
|
||||
end
|
||||
|
||||
|
||||
local function add_gravel_to_dst(meta, inv)
|
||||
-- maintain a counter for gravel kind selection
|
||||
local gravel_cnt = meta:get_int("gravel_cnt") + 1
|
||||
meta:set_int("gravel_cnt", gravel_cnt)
|
||||
|
||||
if (gravel_cnt % 2) == 0 then -- gravel or sieved gravel?
|
||||
inv:add_item("dst", ItemStack("default:gravel")) -- add to dest
|
||||
else
|
||||
inv:add_item("dst", ItemStack("gravelsieve:sieved_gravel")) -- add to dest
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- move gravel and ores to dst
|
||||
local function move_src2dst(meta, pos, inv, src, dst)
|
||||
local function move_src2dst(meta, pos, inv, item, dst)
|
||||
local src = ItemStack(item)
|
||||
if inv:room_for_item("dst", dst) and inv:contains_item("src", src) then
|
||||
local res = swap_node(pos, meta, false)
|
||||
if res then -- time to move one item?
|
||||
if src:get_name() == "default:gravel" then -- will we find ore?
|
||||
if not random_ore(inv, src) then -- no ore found?
|
||||
add_gravel_to_dst(meta, inv)
|
||||
end
|
||||
else
|
||||
inv:add_item("dst", ItemStack("gravelsieve:sieved_gravel")) -- add to dest
|
||||
end
|
||||
local processed = move_random_ore(inv, item)
|
||||
if processed then
|
||||
inv:remove_item("src", src)
|
||||
end
|
||||
end
|
||||
return true -- process finished
|
||||
end
|
||||
return false -- process still running
|
||||
@ -248,20 +383,17 @@ end
|
||||
local function sieve_node_timer(pos, elapsed)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local gravel = ItemStack("default:gravel")
|
||||
local gravel_sieved = ItemStack("gravelsieve:sieved_gravel")
|
||||
|
||||
if move_src2dst(meta, pos, inv, gravel) then
|
||||
for item,probabilities in pairs(gravelsieve.process_probabilities) do
|
||||
if probabilities and move_src2dst(meta, pos, inv, item) then
|
||||
aging(pos, meta)
|
||||
return true
|
||||
elseif move_src2dst(meta, pos, inv, gravel_sieved) then
|
||||
aging(pos, meta)
|
||||
return true
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
minetest.get_node_timer(pos):stop()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for automatic = 0,1 do
|
||||
@ -281,7 +413,7 @@ for idx = 0,4 do
|
||||
local tube_info
|
||||
if automatic == 0 then
|
||||
node_name = "gravelsieve:sieve"
|
||||
description = "Gravel Sieve"
|
||||
description = S("Gravel Sieve")
|
||||
tiles_data = {
|
||||
-- up, down, right, left, back, front
|
||||
"gravelsieve_gravel.png",
|
||||
@ -293,7 +425,7 @@ for idx = 0,4 do
|
||||
}
|
||||
else
|
||||
node_name = "gravelsieve:auto_sieve"
|
||||
description = "Automatic Gravel Sieve"
|
||||
description = S("Automatic Gravel Sieve")
|
||||
tiles_data = {
|
||||
-- up, down, right, left, back, front
|
||||
"gravelsieve_gravel.png",
|
||||
@ -313,7 +445,7 @@ for idx = 0,4 do
|
||||
local meta = minetest.get_meta(pos)
|
||||
swap_node(pos, meta, true)
|
||||
else
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||
end
|
||||
return inv:add_item("src", stack)
|
||||
end,
|
||||
@ -327,6 +459,7 @@ for idx = 0,4 do
|
||||
}
|
||||
end
|
||||
|
||||
local not_in_creative_inventory
|
||||
if idx == 3 then
|
||||
tiles_data[1] = "gravelsieve_top.png"
|
||||
not_in_creative_inventory = 0
|
||||
@ -381,7 +514,7 @@ for idx = 0,4 do
|
||||
local meta = minetest.get_meta(pos)
|
||||
swap_node(pos, meta, true)
|
||||
else
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||
end
|
||||
end,
|
||||
|
||||
@ -396,7 +529,7 @@ for idx = 0,4 do
|
||||
meta:set_int("gravel_cnt", 0)
|
||||
end
|
||||
else
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||
end
|
||||
end,
|
||||
|
||||
@ -405,7 +538,7 @@ for idx = 0,4 do
|
||||
local meta = minetest.get_meta(pos)
|
||||
swap_node(pos, meta, true)
|
||||
else
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||
end
|
||||
end,
|
||||
|
||||
@ -483,7 +616,7 @@ if minetest.global_exists("tubelib") then
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Gravel Sieve")
|
||||
meta:set_string("infotext", S("Gravel Sieve"))
|
||||
end,
|
||||
|
||||
on_dig = function(pos, node, puncher, pointed_thing)
|
||||
@ -494,6 +627,10 @@ if minetest.global_exists("tubelib") then
|
||||
end
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
|
||||
paramtype = "light",
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
paramtype2 = "facedir",
|
||||
@ -515,7 +652,7 @@ if minetest.global_exists("tubelib") then
|
||||
return tubelib.get_item(meta, "dst")
|
||||
end,
|
||||
on_push_item = function(pos, side, item)
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return tubelib.put_item(meta, "src", item)
|
||||
end,
|
||||
@ -524,7 +661,7 @@ if minetest.global_exists("tubelib") then
|
||||
return tubelib.put_item(meta, "dst", item)
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||
end,
|
||||
on_node_repair = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
@ -535,28 +672,28 @@ if minetest.global_exists("tubelib") then
|
||||
inv:set_size('src', 1)
|
||||
inv:set_size('dst', 16)
|
||||
swap_node(pos, meta, false)
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_node("gravelsieve:sieved_gravel", {
|
||||
description = "Sieved Gravel",
|
||||
description = S("Sieved Gravel"),
|
||||
tiles = {"default_gravel.png^[brighten"},
|
||||
groups = {crumbly=2, falling_node=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_gravel_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gravelsieve:compressed_gravel", {
|
||||
description = "Compressed Gravel",
|
||||
description = S("Compressed Gravel"),
|
||||
tiles = {"gravelsieve_compressed_gravel.png"},
|
||||
groups = {cracky=2, crumbly = 2, cracky = 2},
|
||||
sounds = default.node_sound_gravel_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "gravelsieve:sieve",
|
||||
output = "gravelsieve:sieve3",
|
||||
recipe = {
|
||||
{"group:wood", "", "group:wood"},
|
||||
{"group:wood", "default:steel_ingot", "group:wood"},
|
||||
@ -565,10 +702,10 @@ minetest.register_craft({
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "gravelsieve:auto_sieve",
|
||||
output = "gravelsieve:auto_sieve3",
|
||||
type = "shapeless",
|
||||
recipe = {
|
||||
"gravelsieve:sieve", "default:mese_crystal", "default:mese_crystal",
|
||||
"gravelsieve:sieve3", "default:mese_crystal", "default:mese_crystal",
|
||||
},
|
||||
})
|
||||
|
||||
@ -615,7 +752,7 @@ end
|
||||
if minetest.get_modpath("moreblocks") then
|
||||
|
||||
stairsplus:register_all("gravelsieve", "compressed_gravel", "gravelsieve:compressed_gravel", {
|
||||
description="Compressed Gravel",
|
||||
description=S("Compressed Gravel"),
|
||||
groups={cracky=2, crumbly=2, choppy=2, not_in_creative_inventory=1},
|
||||
tiles = {"gravelsieve_compressed_gravel.png"},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
|
14
techpack_modpack/gravelsieve/locale/gravelsieve.de.tr
Normal file
@ -0,0 +1,14 @@
|
||||
# textdomain: gravelsieve
|
||||
|
||||
|
||||
|
||||
### hammer.lua ###
|
||||
|
||||
Hammer converts Cobblestone into Gravel=Hammer, Zertrümmert Pflasterstein in Kies
|
||||
|
||||
### init.lua ###
|
||||
|
||||
Automatic Gravel Sieve=Automatisches Kiessieb
|
||||
Compressed Gravel=Komprimiertes Kies
|
||||
Gravel Sieve=Kiessieb
|
||||
Sieved Gravel=Gesiebtes Kies
|
14
techpack_modpack/gravelsieve/locale/template.txt
Normal file
@ -0,0 +1,14 @@
|
||||
# textdomain: gravelsieve
|
||||
|
||||
|
||||
|
||||
### hammer.lua ###
|
||||
|
||||
Hammer converts Cobblestone into Gravel=
|
||||
|
||||
### init.lua ###
|
||||
|
||||
Automatic Gravel Sieve=
|
||||
Compressed Gravel=
|
||||
Gravel Sieve=
|
||||
Sieved Gravel=
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 713 B After Width: | Height: | Size: 753 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 491 B After Width: | Height: | Size: 934 B |
450
techpack_modpack/i18n.py
Normal file
@ -0,0 +1,450 @@
|
||||
#!/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
|
||||
}
|
||||
# Available CLI options
|
||||
options = {"recursive": ['--recursive', '-r'],
|
||||
"help": ['--help', '-h'],
|
||||
"mods": ['--installed-mods'],
|
||||
"verbose": ['--verbose', '-v'],
|
||||
"no-old-file": ['--no-old-file']
|
||||
}
|
||||
|
||||
# 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 = 60
|
||||
|
||||
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["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])
|
||||
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]
|
||||
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 len(localizedString) > doublespace_threshold and not lOut[-1] == "":
|
||||
lOut.append("")
|
||||
if comment != None:
|
||||
lOut.append(comment)
|
||||
lOut.append(f"{localizedString}={translation}")
|
||||
if 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 len(key) > doublespace_threshold and not lOut[-1] == "":
|
||||
lOut.append("")
|
||||
if comment != None:
|
||||
lOut.append(comment)
|
||||
lOut.append(f"{key}={translation}")
|
||||
if 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):
|
||||
print(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()
|
||||
|
1
techpack_modpack/lcdlib/mod.conf
Normal file
@ -0,0 +1 @@
|
||||
name=lcdlib
|
Before Width: | Height: | Size: 91 B After Width: | Height: | Size: 145 B |
Before Width: | Height: | Size: 82 B After Width: | Height: | Size: 281 B |
Before Width: | Height: | Size: 87 B After Width: | Height: | Size: 299 B |
Before Width: | Height: | Size: 78 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 92 B After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 100 B After Width: | Height: | Size: 312 B |
Before Width: | Height: | Size: 107 B After Width: | Height: | Size: 319 B |
Before Width: | Height: | Size: 78 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 91 B After Width: | Height: | Size: 303 B |
Before Width: | Height: | Size: 89 B After Width: | Height: | Size: 301 B |
Before Width: | Height: | Size: 77 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 85 B After Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 78 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 75 B After Width: | Height: | Size: 287 B |
Before Width: | Height: | Size: 77 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 87 B After Width: | Height: | Size: 299 B |
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 302 B |
Before Width: | Height: | Size: 88 B After Width: | Height: | Size: 300 B |
Before Width: | Height: | Size: 100 B After Width: | Height: | Size: 312 B |
Before Width: | Height: | Size: 101 B After Width: | Height: | Size: 313 B |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 105 B After Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 100 B After Width: | Height: | Size: 312 B |
Before Width: | Height: | Size: 96 B After Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 88 B After Width: | Height: | Size: 300 B |
Before Width: | Height: | Size: 103 B After Width: | Height: | Size: 315 B |
Before Width: | Height: | Size: 77 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 80 B After Width: | Height: | Size: 292 B |
Before Width: | Height: | Size: 78 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 78 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 77 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 98 B After Width: | Height: | Size: 310 B |
Before Width: | Height: | Size: 111 B After Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 101 B After Width: | Height: | Size: 313 B |
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 302 B |
Before Width: | Height: | Size: 108 B After Width: | Height: | Size: 320 B |
Before Width: | Height: | Size: 96 B After Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 302 B |
Before Width: | Height: | Size: 93 B After Width: | Height: | Size: 305 B |
Before Width: | Height: | Size: 115 B After Width: | Height: | Size: 327 B |
Before Width: | Height: | Size: 92 B After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 80 B After Width: | Height: | Size: 292 B |
Before Width: | Height: | Size: 92 B After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 108 B After Width: | Height: | Size: 320 B |
Before Width: | Height: | Size: 86 B After Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 105 B After Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 103 B After Width: | Height: | Size: 315 B |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 97 B After Width: | Height: | Size: 309 B |
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 322 B |
Before Width: | Height: | Size: 91 B After Width: | Height: | Size: 303 B |
Before Width: | Height: | Size: 104 B After Width: | Height: | Size: 316 B |
Before Width: | Height: | Size: 89 B After Width: | Height: | Size: 301 B |
Before Width: | Height: | Size: 87 B After Width: | Height: | Size: 299 B |
Before Width: | Height: | Size: 94 B After Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 101 B After Width: | Height: | Size: 313 B |
Before Width: | Height: | Size: 96 B After Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 94 B After Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 95 B After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 85 B After Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 85 B After Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 86 B After Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 91 B After Width: | Height: | Size: 303 B |
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 286 B |
Before Width: | Height: | Size: 72 B After Width: | Height: | Size: 284 B |
Before Width: | Height: | Size: 104 B After Width: | Height: | Size: 316 B |
Before Width: | Height: | Size: 95 B After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 95 B After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 101 B After Width: | Height: | Size: 313 B |
Before Width: | Height: | Size: 94 B After Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 93 B After Width: | Height: | Size: 305 B |
Before Width: | Height: | Size: 80 B After Width: | Height: | Size: 292 B |
Before Width: | Height: | Size: 81 B After Width: | Height: | Size: 293 B |
Before Width: | Height: | Size: 99 B After Width: | Height: | Size: 311 B |
Before Width: | Height: | Size: 80 B After Width: | Height: | Size: 292 B |
Before Width: | Height: | Size: 94 B After Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 96 B After Width: | Height: | Size: 308 B |
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 302 B |
Before Width: | Height: | Size: 103 B After Width: | Height: | Size: 315 B |
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 91 B After Width: | Height: | Size: 303 B |