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.
|
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.**
|
**The moved/copied nodes will not have valid node numbers, which could lead to a server crash.**
|
||||||
|
|
||||||
TechPack provides:
|
TechPack provides:
|
||||||
|
|
||||||
- lumber tubes to connect 2 nodes
|
- lumber tubes to connect 2 nodes
|
||||||
- a Pusher node to pull/push items through tubes
|
- a Pusher node to pull/push items through tubes
|
||||||
- a Distributor node with 4 output channels to sort incoming items
|
- 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
|
- a Display node for text outputs of the Controller
|
||||||
- Metal ladders, stairways, and bridges
|
- Metal ladders, stairways, and bridges
|
||||||
- Warehouse Boxes in steel, copper, and gold
|
- Warehouse Boxes in steel, copper, and gold
|
||||||
|
- A chest cart for the mod minecart
|
||||||
|
|
||||||
|
|
||||||
TechPack supports the following mods:
|
TechPack supports the following mods:
|
||||||
|
|
||||||
- Farming Redo (Harvester, Fermenter)
|
- Farming Redo (Harvester, Fermenter)
|
||||||
- Ethereal (Harvester, Quarry, Fermenter)
|
- Ethereal (Harvester, Quarry, Fermenter)
|
||||||
- Pipeworks (Gravel Sieve)
|
- Pipeworks (Gravel Sieve)
|
||||||
@ -75,12 +78,14 @@ TechPack supports the following mods:
|
|||||||
|
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
The following can be changed in the minetest menu (Settings -> Advanced Settings -> Mods -> tubelib) or directly in 'minetest.conf'
|
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
|
- Maximum number of Forceload Blocks per player
|
||||||
- Enable Basalt Stone (and disable ore generation via Cobblestone generator)
|
- Enable Basalt Stone (and disable ore generation via Cobblestone generator)
|
||||||
- Machine aging value to calculate the lifetime of machines
|
- Machine aging value to calculate the lifetime of machines
|
||||||
|
|
||||||
Example for 'minetest.conf':
|
Example for 'minetest.conf':
|
||||||
|
|
||||||
```LUA
|
```LUA
|
||||||
tubelib_basalt_stone_enabled = false
|
tubelib_basalt_stone_enabled = false
|
||||||
tubelib_max_num_forceload_blocks = 12
|
tubelib_max_num_forceload_blocks = 12
|
||||||
@ -88,6 +93,7 @@ tubelib_machine_aging_value = 200
|
|||||||
```
|
```
|
||||||
|
|
||||||
Example for a v1 compatible 'minetest.conf':
|
Example for a v1 compatible 'minetest.conf':
|
||||||
|
|
||||||
```LUA
|
```LUA
|
||||||
tubelib_basalt_stone_enabled = false
|
tubelib_basalt_stone_enabled = false
|
||||||
tubelib_max_num_forceload_blocks = 0
|
tubelib_max_num_forceload_blocks = 0
|
||||||
@ -96,38 +102,64 @@ tubelib_machine_aging_value = 999999
|
|||||||
|
|
||||||
|
|
||||||
#### Maximum number of Forceload Blocks per player
|
#### Maximum number of Forceload Blocks per player
|
||||||
|
|
||||||
Default value is 12.
|
Default value is 12.
|
||||||
I higher number allows to build larger farms and machines which keep loaded, but increases the server load, too.
|
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.
|
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.
|
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)
|
#### Enable Basalt Stone (and disable ore generation via Cobblestone generator)
|
||||||
|
|
||||||
The lava/water Cobblestone generator allows to produce infinite Cobblestone. By means of Quarry,
|
The lava/water Cobblestone generator allows to produce infinite Cobblestone. By means of Quarry,
|
||||||
Grinder, and Gravel Sieve it allows to infinite generate ores.
|
Grinder, and Gravel Sieve it allows to infinite generate ores.
|
||||||
This can be disabled by means of the setting parameter. If enabled, the Cobblestone
|
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.
|
generator generates Basalt instead, which only can be used for building purposes.
|
||||||
|
|
||||||
|
|
||||||
#### Machine aging value to calculate the lifetime of machines
|
#### Machine aging value to calculate the lifetime of machines
|
||||||
|
|
||||||
Default value is 200.
|
Default value is 200.
|
||||||
This aging value is used to calculate the lifetime of machines before they go defect.
|
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).
|
The value 200 (default) results in a lifetime for standard machines of about 2000 - 8000 item processing cycles (~2-4 hours).
|
||||||
|
|
||||||
|
|
||||||
### License
|
### 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
|
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
|
### Dependencies
|
||||||
|
|
||||||
default, doors, intllib, basic_materials
|
default, doors, intllib, basic_materials
|
||||||
tubelib2 ()
|
tubelib2 ()
|
||||||
Tubelib Color Lamps optional: unifieddyes
|
Tubelib Color Lamps optional: unifieddyes
|
||||||
SmartLine Controller optional: mail
|
SmartLine Controller optional: mail
|
||||||
Gravelsieve optional: moreores, hopper, pipeworks
|
Gravelsieve optional: moreores, hopper, pipeworks
|
||||||
tubelib_addons1 optional: unified_inventory
|
tubelib_addons1 optional: unified_inventory
|
||||||
|
tubelib_addons13 optional: minecart
|
||||||
|
|
||||||
|
|
||||||
### History
|
### History
|
||||||
|
|
||||||
- 2018-03-18 V1.00 * Tubelib, tubelib_addons1, tubelib_addons2, smartline, and gravelsieve combined to one modpack.
|
- 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-24 V1.01 * Support for Ethereal added
|
||||||
- 2018-03-27 V1.02 * Timer improvements for unloaded areas
|
- 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-27 V2.01 * SaferLua Controller Terminal added
|
||||||
- 2019-01-28 V2.02 * Logic Not added, output reduction on Harvester, Fermenter, and Gravel Sieve
|
- 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
|
- 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)
|
## 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.
|
- 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 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.
|
- 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)
|
## 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
|
- 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
|
- the new mods 'techpack_stairway' and 'techpack_warehouse' have to be enabled
|
||||||
- TechPack depends now on the mod 'basic_materials' and 'tubelib2' ()
|
- 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)
|
gravelsieve.disallow = function(pos, node, user, mode, new_param2)
|
||||||
return false
|
return false
|
||||||
@ -40,7 +42,7 @@ gravelsieve.handler = function(itemstack, user, pointed_thing)
|
|||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_tool("gravelsieve:hammer", {
|
minetest.register_tool("gravelsieve:hammer", {
|
||||||
description = "Hammer converts Cobblestone into Gravel",
|
description = S("Hammer converts Cobblestone into Gravel"),
|
||||||
inventory_image = "gravelsieve_hammer.png",
|
inventory_image = "gravelsieve_hammer.png",
|
||||||
on_use = function(itemstack, user, pointed_thing)
|
on_use = function(itemstack, user, pointed_thing)
|
||||||
return gravelsieve.handler(itemstack, user, pointed_thing)
|
return gravelsieve.handler(itemstack, user, pointed_thing)
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
Derived from the work of celeron55, Perttu Ahola (furnace)
|
Derived from the work of celeron55, Perttu Ahola (furnace)
|
||||||
Pipeworks support added by FiftySix
|
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 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||||
Copyright (C) 2011-2016 Various Minetest developers and contributors
|
Copyright (C) 2011-2016 Various Minetest developers and contributors
|
||||||
|
|
||||||
LGPLv2.1+
|
AGPL v3
|
||||||
See LICENSE.txt for more information
|
See LICENSE.txt for more information
|
||||||
|
|
||||||
History:
|
History:
|
||||||
@ -42,13 +42,17 @@
|
|||||||
gravelsieve = {
|
gravelsieve = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Load support for I18n
|
||||||
|
gravelsieve.S = minetest.get_translator("gravelsieve")
|
||||||
|
local S = gravelsieve.S
|
||||||
|
|
||||||
dofile(minetest.get_modpath("gravelsieve") .. "/hammer.lua")
|
dofile(minetest.get_modpath("gravelsieve") .. "/hammer.lua")
|
||||||
|
|
||||||
local settings_get
|
local settings_get
|
||||||
if minetest.setting_get then
|
if minetest.setting_get then
|
||||||
settings_get = minetest.setting_get
|
settings_get = minetest.setting_get
|
||||||
else
|
else
|
||||||
settings_get = function(...) endminetest.settings:get(...) end
|
settings_get = function(...) return minetest.settings:get(...) end
|
||||||
end
|
end
|
||||||
gravelsieve.ore_rarity = tonumber(settings_get("gravelsieve_ore_rarity")) or 1.16
|
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
|
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)
|
local y_spread = math.max(1 + gravelsieve.ore_max_elevation - gravelsieve.ore_min_elevation, 1)
|
||||||
|
|
||||||
-- Increase the probability over the natural occurrence
|
-- 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
|
-- tubelib aging feature
|
||||||
local AGING_LEVEL1 = nil
|
local AGING_LEVEL1 = nil
|
||||||
@ -70,6 +76,8 @@ end
|
|||||||
gravelsieve.ore_probability = {
|
gravelsieve.ore_probability = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gravelsieve.process_probabilities = {}
|
||||||
|
|
||||||
|
|
||||||
-- Pipeworks support
|
-- Pipeworks support
|
||||||
local pipeworks_after_dig = nil
|
local pipeworks_after_dig = nil
|
||||||
@ -80,10 +88,6 @@ if minetest.get_modpath("pipeworks") and pipeworks ~= nil then
|
|||||||
pipeworks_after_place = pipeworks.after_place
|
pipeworks_after_place = pipeworks.after_place
|
||||||
end
|
end
|
||||||
|
|
||||||
local function harmonic_sum(a, b)
|
|
||||||
return 1 / ((1 / a) + (1 / b))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function calculate_probability(item)
|
local function calculate_probability(item)
|
||||||
local ymax = math.min(item.y_max, gravelsieve.ore_max_elevation)
|
local ymax = math.min(item.y_max, gravelsieve.ore_max_elevation)
|
||||||
local ymin = math.max(item.y_min, gravelsieve.ore_min_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))
|
item.clust_scarcity / (item.clust_num_ores * ((ymax - ymin) / y_spread))
|
||||||
end
|
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
|
-- collect all registered ores and calculate the probability
|
||||||
local function add_ores()
|
local function add_ores()
|
||||||
for _,item in pairs(minetest.registered_ores) do
|
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_scarcity ~= nil and item.clust_scarcity > 0
|
||||||
and item.clust_num_ores ~= nil and item.clust_num_ores > 0
|
and item.clust_num_ores ~= nil and item.clust_num_ores > 0
|
||||||
and item.y_max ~= nil and item.y_min ~= nil then
|
and item.y_max ~= nil and item.y_min ~= nil then
|
||||||
|
local count
|
||||||
|
drop, count = parse_drop(drop)
|
||||||
|
|
||||||
local probability = calculate_probability(item)
|
local probability = calculate_probability(item)
|
||||||
if probability > 0 then
|
if probability > 0 then
|
||||||
|
local probabilityFraction = count / probability
|
||||||
local cur_probability = gravelsieve.ore_probability[drop]
|
local cur_probability = gravelsieve.ore_probability[drop]
|
||||||
if cur_probability then
|
if cur_probability then
|
||||||
gravelsieve.ore_probability[drop] = harmonic_sum(cur_probability, probability)
|
gravelsieve.ore_probability[drop] = cur_probability+probabilityFraction
|
||||||
else
|
else
|
||||||
gravelsieve.ore_probability[drop] = probability
|
gravelsieve.ore_probability[drop] = probabilityFraction
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
minetest.log("action", "[gravelsieve] ore probabilties:")
|
||||||
local overall_probability = 0.0
|
local overall_probability = 0.0
|
||||||
for name,probability in pairs(gravelsieve.ore_probability) do
|
for name,probability in pairs_by_values(gravelsieve.ore_probability) do
|
||||||
minetest.log("info", ("[gravelsieve] %-32s %.02f"):format(name, probability))
|
minetest.log("action", ("[gravelsieve] %-32s: 1 / %.02f"):format(name, 1.0/probability))
|
||||||
overall_probability = overall_probability + 1.0/probability
|
overall_probability = overall_probability + probability
|
||||||
end
|
end
|
||||||
minetest.log("info", ("[gravelsieve] Overall probability %f"):format(overall_probability))
|
minetest.log("action", ("[gravelsieve] Overall probability %f"):format(overall_probability))
|
||||||
end
|
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 =
|
local sieve_formspec =
|
||||||
"size[8,8]"..
|
"size[8,8]"..
|
||||||
@ -197,48 +345,35 @@ local function swap_node(pos, meta, start)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- place ores to dst according to the calculated probability
|
-- place ores to dst according to the calculated probability
|
||||||
local function random_ore(inv, src)
|
local function move_random_ore(inv, item)
|
||||||
local num
|
local running_total = 0
|
||||||
for ore, probability in pairs(gravelsieve.ore_probability) do
|
local probabilities = gravelsieve.process_probabilities[item]
|
||||||
if math.random(probability) == 1 then
|
local chosen = math.random()
|
||||||
local item = ItemStack(ore)
|
for ore, probability in pairs(probabilities) do
|
||||||
if inv:room_for_item("dst", item) then
|
running_total = running_total + probability
|
||||||
inv:add_item("dst", item)
|
if chosen < running_total then
|
||||||
return true -- ore placed
|
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
|
||||||
end
|
end
|
||||||
end
|
return false -- Failure, this shouldn't really happen but might due to floating point errors
|
||||||
return false -- gravel has to be moved
|
|
||||||
end
|
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
|
-- 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
|
if inv:room_for_item("dst", dst) and inv:contains_item("src", src) then
|
||||||
local res = swap_node(pos, meta, false)
|
local res = swap_node(pos, meta, false)
|
||||||
if res then -- time to move one item?
|
if res then -- time to move one item?
|
||||||
if src:get_name() == "default:gravel" then -- will we find ore?
|
local processed = move_random_ore(inv, item)
|
||||||
if not random_ore(inv, src) then -- no ore found?
|
if processed then
|
||||||
add_gravel_to_dst(meta, inv)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
inv:add_item("dst", ItemStack("gravelsieve:sieved_gravel")) -- add to dest
|
|
||||||
end
|
|
||||||
inv:remove_item("src", src)
|
inv:remove_item("src", src)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
return true -- process finished
|
return true -- process finished
|
||||||
end
|
end
|
||||||
return false -- process still running
|
return false -- process still running
|
||||||
@ -248,19 +383,16 @@ end
|
|||||||
local function sieve_node_timer(pos, elapsed)
|
local function sieve_node_timer(pos, elapsed)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local inv = meta:get_inventory()
|
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)
|
aging(pos, meta)
|
||||||
return true
|
return true
|
||||||
elseif move_src2dst(meta, pos, inv, gravel_sieved) then
|
end
|
||||||
aging(pos, meta)
|
end
|
||||||
return true
|
|
||||||
else
|
|
||||||
minetest.get_node_timer(pos):stop()
|
minetest.get_node_timer(pos):stop()
|
||||||
return false
|
return false
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -281,7 +413,7 @@ for idx = 0,4 do
|
|||||||
local tube_info
|
local tube_info
|
||||||
if automatic == 0 then
|
if automatic == 0 then
|
||||||
node_name = "gravelsieve:sieve"
|
node_name = "gravelsieve:sieve"
|
||||||
description = "Gravel Sieve"
|
description = S("Gravel Sieve")
|
||||||
tiles_data = {
|
tiles_data = {
|
||||||
-- up, down, right, left, back, front
|
-- up, down, right, left, back, front
|
||||||
"gravelsieve_gravel.png",
|
"gravelsieve_gravel.png",
|
||||||
@ -293,7 +425,7 @@ for idx = 0,4 do
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
node_name = "gravelsieve:auto_sieve"
|
node_name = "gravelsieve:auto_sieve"
|
||||||
description = "Automatic Gravel Sieve"
|
description = S("Automatic Gravel Sieve")
|
||||||
tiles_data = {
|
tiles_data = {
|
||||||
-- up, down, right, left, back, front
|
-- up, down, right, left, back, front
|
||||||
"gravelsieve_gravel.png",
|
"gravelsieve_gravel.png",
|
||||||
@ -313,7 +445,7 @@ for idx = 0,4 do
|
|||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
swap_node(pos, meta, true)
|
swap_node(pos, meta, true)
|
||||||
else
|
else
|
||||||
minetest.get_node_timer(pos):start(1.0)
|
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||||
end
|
end
|
||||||
return inv:add_item("src", stack)
|
return inv:add_item("src", stack)
|
||||||
end,
|
end,
|
||||||
@ -327,6 +459,7 @@ for idx = 0,4 do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local not_in_creative_inventory
|
||||||
if idx == 3 then
|
if idx == 3 then
|
||||||
tiles_data[1] = "gravelsieve_top.png"
|
tiles_data[1] = "gravelsieve_top.png"
|
||||||
not_in_creative_inventory = 0
|
not_in_creative_inventory = 0
|
||||||
@ -381,7 +514,7 @@ for idx = 0,4 do
|
|||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
swap_node(pos, meta, true)
|
swap_node(pos, meta, true)
|
||||||
else
|
else
|
||||||
minetest.get_node_timer(pos):start(1.0)
|
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
@ -396,7 +529,7 @@ for idx = 0,4 do
|
|||||||
meta:set_int("gravel_cnt", 0)
|
meta:set_int("gravel_cnt", 0)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
minetest.get_node_timer(pos):start(1.0)
|
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
@ -405,7 +538,7 @@ for idx = 0,4 do
|
|||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
swap_node(pos, meta, true)
|
swap_node(pos, meta, true)
|
||||||
else
|
else
|
||||||
minetest.get_node_timer(pos):start(1.0)
|
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
@ -483,7 +616,7 @@ if minetest.global_exists("tubelib") then
|
|||||||
|
|
||||||
after_place_node = function(pos, placer)
|
after_place_node = function(pos, placer)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
meta:set_string("infotext", "Gravel Sieve")
|
meta:set_string("infotext", S("Gravel Sieve"))
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_dig = function(pos, node, puncher, pointed_thing)
|
on_dig = function(pos, node, puncher, pointed_thing)
|
||||||
@ -494,6 +627,10 @@ if minetest.global_exists("tubelib") then
|
|||||||
end
|
end
|
||||||
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",
|
paramtype = "light",
|
||||||
sounds = default.node_sound_wood_defaults(),
|
sounds = default.node_sound_wood_defaults(),
|
||||||
paramtype2 = "facedir",
|
paramtype2 = "facedir",
|
||||||
@ -515,7 +652,7 @@ if minetest.global_exists("tubelib") then
|
|||||||
return tubelib.get_item(meta, "dst")
|
return tubelib.get_item(meta, "dst")
|
||||||
end,
|
end,
|
||||||
on_push_item = function(pos, side, item)
|
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)
|
local meta = minetest.get_meta(pos)
|
||||||
return tubelib.put_item(meta, "src", item)
|
return tubelib.put_item(meta, "src", item)
|
||||||
end,
|
end,
|
||||||
@ -524,7 +661,7 @@ if minetest.global_exists("tubelib") then
|
|||||||
return tubelib.put_item(meta, "dst", item)
|
return tubelib.put_item(meta, "dst", item)
|
||||||
end,
|
end,
|
||||||
on_node_load = function(pos)
|
on_node_load = function(pos)
|
||||||
minetest.get_node_timer(pos):start(1.0)
|
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||||
end,
|
end,
|
||||||
on_node_repair = function(pos)
|
on_node_repair = function(pos)
|
||||||
local meta = minetest.get_meta(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('src', 1)
|
||||||
inv:set_size('dst', 16)
|
inv:set_size('dst', 16)
|
||||||
swap_node(pos, meta, false)
|
swap_node(pos, meta, false)
|
||||||
minetest.get_node_timer(pos):start(1.0)
|
minetest.get_node_timer(pos):start(STEP_DELAY)
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_node("gravelsieve:sieved_gravel", {
|
minetest.register_node("gravelsieve:sieved_gravel", {
|
||||||
description = "Sieved Gravel",
|
description = S("Sieved Gravel"),
|
||||||
tiles = {"default_gravel.png^[brighten"},
|
tiles = {"default_gravel.png^[brighten"},
|
||||||
groups = {crumbly=2, falling_node=1, not_in_creative_inventory=1},
|
groups = {crumbly=2, falling_node=1, not_in_creative_inventory=1},
|
||||||
sounds = default.node_sound_gravel_defaults(),
|
sounds = default.node_sound_gravel_defaults(),
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_node("gravelsieve:compressed_gravel", {
|
minetest.register_node("gravelsieve:compressed_gravel", {
|
||||||
description = "Compressed Gravel",
|
description = S("Compressed Gravel"),
|
||||||
tiles = {"gravelsieve_compressed_gravel.png"},
|
tiles = {"gravelsieve_compressed_gravel.png"},
|
||||||
groups = {cracky=2, crumbly = 2, cracky = 2},
|
groups = {cracky=2, crumbly = 2, cracky = 2},
|
||||||
sounds = default.node_sound_gravel_defaults(),
|
sounds = default.node_sound_gravel_defaults(),
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "gravelsieve:sieve",
|
output = "gravelsieve:sieve3",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"group:wood", "", "group:wood"},
|
{"group:wood", "", "group:wood"},
|
||||||
{"group:wood", "default:steel_ingot", "group:wood"},
|
{"group:wood", "default:steel_ingot", "group:wood"},
|
||||||
@ -565,10 +702,10 @@ minetest.register_craft({
|
|||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "gravelsieve:auto_sieve",
|
output = "gravelsieve:auto_sieve3",
|
||||||
type = "shapeless",
|
type = "shapeless",
|
||||||
recipe = {
|
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
|
if minetest.get_modpath("moreblocks") then
|
||||||
|
|
||||||
stairsplus:register_all("gravelsieve", "compressed_gravel", "gravelsieve:compressed_gravel", {
|
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},
|
groups={cracky=2, crumbly=2, choppy=2, not_in_creative_inventory=1},
|
||||||
tiles = {"gravelsieve_compressed_gravel.png"},
|
tiles = {"gravelsieve_compressed_gravel.png"},
|
||||||
sounds = default.node_sound_stone_defaults(),
|
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 |