update techpack to 2.06

This commit is contained in:
Sergei Mozhaisky 2021-12-28 18:38:34 +00:00
parent 5bffc33c4e
commit c9a7f0e002
451 changed files with 4608 additions and 1553 deletions

View File

@ -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

View File

@ -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/

File diff suppressed because it is too large Load Diff

View File

@ -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.
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,
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.
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
Textures: CC BY-SA 3.0
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
default, doors, intllib, basic_materials
tubelib2 (![GitHub](https://github.com/joe7575/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' (![GitHub](https://github.com/joe7575/tubelib2))

View File

@ -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)

View File

@ -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,47 +345,34 @@ 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 -- gravel has to be moved
return false -- Failure, this shouldn't really happen but might due to floating point errors
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
local processed = move_random_ore(inv, item)
if processed then
inv:remove_item("src", src)
end
inv:remove_item("src", src)
end
return true -- process finished
end
@ -248,19 +383,16 @@ 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
aging(pos, meta)
return true
elseif move_src2dst(meta, pos, inv, gravel_sieved) then
aging(pos, meta)
return true
else
minetest.get_node_timer(pos):stop()
return false
for item,probabilities in pairs(gravelsieve.process_probabilities) do
if probabilities and move_src2dst(meta, pos, inv, item) then
aging(pos, meta)
return true
end
end
minetest.get_node_timer(pos):stop()
return false
end
@ -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(),

View 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

View 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=

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 B

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 934 B

450
techpack_modpack/i18n.py Normal file
View 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()

View File

@ -0,0 +1 @@
name=lcdlib

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 B

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 B

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 B

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 B

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 B

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 B

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 B

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 B

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 B

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 B

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 B

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 B

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 B

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 B

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 B

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 B

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 B

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 B

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 B

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 B

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 B

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 B

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 B

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 B

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 B

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 B

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 B

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 B

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 B

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 B

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 B

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 B

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 B

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 B

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 B

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 B

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 B

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 B

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 B

After

Width:  |  Height:  |  Size: 303 B

Some files were not shown because too many files have changed in this diff Show More