code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: 2020-03-30 23:09+0000\n"
"Last-Translator: Liet Kynes <sissi90@gmail.com>\n"
"Language-Team: Norwegian Bokmål <https://hosted.weblate.org/projects/"
"minetest/mod-storage-drawers/nb_NO/>\n"
"Language: nb_NO\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.0-dev\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr "Tom"

#: lua/api.lua init.lua
msgid "Wooden"
msgstr "Tre"

#: lua/api.lua
msgid "@1 Drawer"
msgstr "@1 Skuff"

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr "@1 Skuffer (1x2)"

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr "@1 Skuffer (2x2)"

#: lua/controller.lua
msgid "Drawer Controller"
msgstr "Skuffekontrollør"

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr "@1 (@2% full)"

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr "@1 @2 (@3% full)"

#: init.lua
msgid "Acacia Wood"
msgstr "Akasie"

#: init.lua
msgid "Aspen Wood"
msgstr "Osp"

#: init.lua
msgid "Junglewood"
msgstr "Regnskogtømmer"

#: init.lua
msgid "Pine Wood"
msgstr "Furu"

#: init.lua
msgid "Oak Wood"
msgstr "Eik"

#: init.lua
msgid "Birch Wood"
msgstr "Bjørk"

#: init.lua
msgid "Dark Oak Wood"
msgstr "Mørk eik"

#: init.lua
msgid "Spruce Wood"
msgstr "Gran"

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr "Stålskuffoppgradering (x2)"

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr "Gullskuffoppgradering (x3)"

#: init.lua
#, fuzzy
msgid "Obsidian Drawer Upgrade (x4)"
msgstr "Obsidianskuffoppgradering (x5)"

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr "Diamantskuffoppgradering (x8)"

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr "Jernskuffoppgradering (x2)"

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr "Smaragdskuffoppgradering (x13)"

#: init.lua
#, fuzzy
msgid "Mithril Drawer Upgrade (x13)"
msgstr "Smaragdskuffoppgradering (x13)"

#: init.lua
msgid "Wooden Trim"
msgstr "Kledning i tre"

#: init.lua
msgid "Drawer Upgrade Template"
msgstr "Mal for oppgradering av skuffer"

#~ msgid "Current State: "
#~ msgstr "Nåværende tilstand: "

#~ msgid "Stopped"
#~ msgstr "Stanset"

#~ msgid "Running"
#~ msgstr "Kjører"

#~ msgid "Jammed"
#~ msgstr "Sitter fast"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: 2020-05-28 16:41+0000\n"
"Last-Translator: Miniontoby <tobias.gaarenstroom@gmail.com>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/minetest/mod-"
"storage-drawers/nl/>\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.1-dev\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr "Leeg"

#: lua/api.lua init.lua
msgid "Wooden"
msgstr "Houten"

#: lua/api.lua
msgid "@1 Drawer"
msgstr "@1 Lade"

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr "@1 Lades (1x2)"

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr "@1 Lades (2x2)"

#: lua/controller.lua
msgid "Drawer Controller"
msgstr ""

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr "@1 (@2% vol)"

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr "@1 @2 (@3% vol)"

#: init.lua
msgid "Acacia Wood"
msgstr "Acacia Hout"

#: init.lua
msgid "Aspen Wood"
msgstr "Espenhout"

#: init.lua
msgid "Junglewood"
msgstr "Junglehout"

#: init.lua
msgid "Pine Wood"
msgstr "Dennenhout"

#: init.lua
msgid "Oak Wood"
msgstr "Eikenhout"

#: init.lua
msgid "Birch Wood"
msgstr "Berken hout"

#: init.lua
msgid "Dark Oak Wood"
msgstr "Donker Eikenhout"

#: init.lua
msgid "Spruce Wood"
msgstr "Sparrenhout"

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr "Stalen Lade Upgrade (x2)"

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr "Gouden Lade Upgrade (x3)"

#: init.lua
#, fuzzy
msgid "Obsidian Drawer Upgrade (x4)"
msgstr "Obsidianen Lade Upgrade (x5)"

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr "Diamanten Lade Upgrade (x8)"

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr "Ijzeren Lade Upgrade (x2)"

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr "Smaragden Lade upgrade (x13)"

#: init.lua
#, fuzzy
msgid "Mithril Drawer Upgrade (x13)"
msgstr "Smaragden Lade upgrade (x13)"

#: init.lua
msgid "Wooden Trim"
msgstr "Houten bekleding"

#: init.lua
msgid "Drawer Upgrade Template"
msgstr "Lade Upgrade Template"

repo_name: korkut/MeseCraft

license: mit


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: 2019-09-11 16:23+0000\n"
"Last-Translator: Mateusz Mendel <mendelm9@gmail.com>\n"
"Language-Team: Polish <https://hosted.weblate.org/projects/minetest/mod-"
"storage-drawers/pl/>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 3.9-dev\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr "Pusta"

#: lua/api.lua init.lua
msgid "Wooden"
msgstr "Drewniany"

#: lua/api.lua
msgid "@1 Drawer"
msgstr "@1 Szuflada"

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr "@1 Szuflady (1x2)"

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr "@1 Szuflady (2x2)"

#: lua/controller.lua
msgid "Drawer Controller"
msgstr "Kontroler szuflady"

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr "@1 (@2% napełnione)"

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr "@1 @2 (@3% napełnione)"

#: init.lua
msgid "Acacia Wood"
msgstr "Drewno akacjowe"

#: init.lua
msgid "Aspen Wood"
msgstr "Drewno osikowe"

#: init.lua
msgid "Junglewood"
msgstr "Drewno jungli"

#: init.lua
msgid "Pine Wood"
msgstr "Drewno sosnowe"

#: init.lua
msgid "Oak Wood"
msgstr "Drewno dębowe"

#: init.lua
msgid "Birch Wood"
msgstr "Drewno brzozowe"

#: init.lua
msgid "Dark Oak Wood"
msgstr "Drewno ciemne dębowe"

#: init.lua
msgid "Spruce Wood"
msgstr "Drwno świerkowe"

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr "Ulepszenie stalowej szuflady(x2)"

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr "Ulepszenie złotej szuflady (x3)"

#: init.lua
#, fuzzy
msgid "Obsidian Drawer Upgrade (x4)"
msgstr "Ulepszenie obsydianowej szuflady (x5)"

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr "Ulepszenie diamentowej szuflady (x8)"

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr "Żelazna aktualizacja szuflady (x2)"

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr "Ulepszenie szmaragdowej szuflady (x13)"

#: init.lua
#, fuzzy
msgid "Mithril Drawer Upgrade (x13)"
msgstr "Ulepszenie szmaragdowej szuflady (x13)"

#: init.lua
msgid "Wooden Trim"
msgstr "Drewniane wykończenia"

#: init.lua
msgid "Drawer Upgrade Template"
msgstr "Szablon ulepszenia szuflady"

#~ msgid "Current State: "
#~ msgstr "Stan aktulany: "

#~ msgid "Stopped"
#~ msgstr "Zatrzymany"

#~ msgid "Running"
#~ msgstr "Uruchomiona"

#~ msgid "Jammed"
#~ msgstr "Zablokowany"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: 2020-08-30 19:37+0000\n"
"Last-Translator: ssantos <ssantos@web.de>\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/minetest/mod-"
"storage-drawers/pt/>\n"
"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.2.1-dev\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr "Vazio"

#: lua/api.lua init.lua
msgid "Wooden"
msgstr "Madeira"

#: lua/api.lua
msgid "@1 Drawer"
msgstr "Armário @1"

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr "Armários @1 (1x2)"

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr "Armários @1 (2x2)"

#: lua/controller.lua
msgid "Drawer Controller"
msgstr "Controlador de Armários"

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr "@1 (@2% cheio)"

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr "@1 @2 (@3% cheio)"

#: init.lua
msgid "Acacia Wood"
msgstr "Lenha de Acácia"

#: init.lua
msgid "Aspen Wood"
msgstr "Lenha de Álamo"

#: init.lua
msgid "Junglewood"
msgstr "Lenha da selva"

#: init.lua
msgid "Pine Wood"
msgstr "Lenha de Pinheiro"

#: init.lua
msgid "Oak Wood"
msgstr "Lenha de Carvalho"

#: init.lua
msgid "Birch Wood"
msgstr "Lenha de Bétula"

#: init.lua
msgid "Dark Oak Wood"
msgstr "Lenha de Carvalho Escuro"

#: init.lua
msgid "Spruce Wood"
msgstr "Lenha de Abeto"

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr "Melhoria de Armário de Aço (x2)"

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr "Melhoria de Armário de Ouro (x3)"

#: init.lua
#, fuzzy
msgid "Obsidian Drawer Upgrade (x4)"
msgstr "Melhoria de Armário de Obsidiana (x5)"

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr "Melhoria de Armário de Diamante (x8)"

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr "Melhoria de Armário de Ferro (x2)"

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr "Melhoria de Armário de Esmeralda (x13)"

#: init.lua
#, fuzzy
msgid "Mithril Drawer Upgrade (x13)"
msgstr "Melhoria de Armário de Esmeralda (x13)"

#: init.lua
msgid "Wooden Trim"
msgstr "Guarnição de Madeira"

#: init.lua
msgid "Drawer Upgrade Template"
msgstr "Modelo de Melhoria de Armário"

#~ msgid "Current State: "
#~ msgstr "Estado atual: "

#~ msgid "Stopped"
#~ msgstr "Parado"

#~ msgid "Running"
#~ msgstr "Ativo"

#~ msgid "Jammed"
#~ msgstr "Obstruído"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: 2019-08-03 04:11+0000\n"
"Last-Translator: Wilker Santana da Silva <wilker@tutamail.com>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"minetest/mod-storage-drawers/pt_BR/>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 3.8-dev\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr "Vazio"

#: lua/api.lua init.lua
msgid "Wooden"
msgstr "Madeira"

#: lua/api.lua
msgid "@1 Drawer"
msgstr "Armário @1"

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr "Armários @1 (1x2)"

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr "Armários @1 (2x2)"

#: lua/controller.lua
msgid "Drawer Controller"
msgstr "Controlador de Armários"

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr "@1 (@2% cheio)"

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr "@1 @2 (@3% cheio)"

#: init.lua
msgid "Acacia Wood"
msgstr "Lenha de Acácia"

#: init.lua
msgid "Aspen Wood"
msgstr "Lenha de Álamo"

#: init.lua
msgid "Junglewood"
msgstr "Lenha da selva"

#: init.lua
msgid "Pine Wood"
msgstr "Lenha de Pinheiro"

#: init.lua
msgid "Oak Wood"
msgstr "Lenha de Carvalho"

#: init.lua
msgid "Birch Wood"
msgstr "Lenha de Bétula"

#: init.lua
msgid "Dark Oak Wood"
msgstr "Lenha de Carvalho Escuro"

#: init.lua
msgid "Spruce Wood"
msgstr "Lenha de Abeto"

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr "Melhoria de Armário de Aço (x2)"

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr "Melhoria de Armário de Ouro (x3)"

#: init.lua
#, fuzzy
msgid "Obsidian Drawer Upgrade (x4)"
msgstr "Melhoria de Armário de Obsidiana (x5)"

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr "Melhoria de Armário de Diamante (x8)"

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr "Melhoria de Armário de Ferro (x2)"

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr "Melhoria de Armário de Esmeralda (x13)"

#: init.lua
#, fuzzy
msgid "Mithril Drawer Upgrade (x13)"
msgstr "Melhoria de Armário de Esmeralda (x13)"

#: init.lua
msgid "Wooden Trim"
msgstr "Guarnição de Madeira"

#: init.lua
msgid "Drawer Upgrade Template"
msgstr "Modelo de Melhoria de Armário"

#~ msgid "Current State: "
#~ msgstr "Estado atual: "

#~ msgid "Stopped"
#~ msgstr "Parado"

#~ msgid "Running"
#~ msgstr "Ativo"

#~ msgid "Jammed"
#~ msgstr "Obstruído"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: 2019-12-05 03:05+0000\n"
"Last-Translator: Andrei Stepanov <adem4ik@gmail.com>\n"
"Language-Team: Russian <https://hosted.weblate.org/projects/minetest/mod-"
"storage-drawers/ru/>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 3.10-dev\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr "Пусто"

#: lua/api.lua init.lua
msgid "Wooden"
msgstr "Деревянный"

#: lua/api.lua
msgid "@1 Drawer"
msgstr "@1 Ящик"

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr "@1 Ящики (1x2)"

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr "@1 Ящики (2x2)"

#: lua/controller.lua
msgid "Drawer Controller"
msgstr "Контролер ящика"

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr "@1 (@2% полон)"

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr "@1 @2 (@3% полон)"

#: init.lua
msgid "Acacia Wood"
msgstr "Акация"

#: init.lua
msgid "Aspen Wood"
msgstr "Осина"

#: init.lua
msgid "Junglewood"
msgstr "Тропическое дерево"

#: init.lua
msgid "Pine Wood"
msgstr "Сосна"

#: init.lua
msgid "Oak Wood"
msgstr "Дуб"

#: init.lua
msgid "Birch Wood"
msgstr "Берёза"

#: init.lua
msgid "Dark Oak Wood"
msgstr "Тёмный дуб"

#: init.lua
msgid "Spruce Wood"
msgstr "Ель"

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr "Стальной ящик Улучшение (x2)"

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr "Золотой ящик Улучшение (x3)"

#: init.lua
#, fuzzy
msgid "Obsidian Drawer Upgrade (x4)"
msgstr "Обсидиановый ящик Улучшение (x5)"

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr "Алмазный ящик Улучшение (x8)"

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr "Железный ящик Улучшение (x2)"

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr "Изумрудный ящик Улучшение (x13)"

#: init.lua
#, fuzzy
msgid "Mithril Drawer Upgrade (x13)"
msgstr "Изумрудный ящик Улучшение (x13)"

#: init.lua
msgid "Wooden Trim"
msgstr "Деревянная отделка"

#: init.lua
msgid "Drawer Upgrade Template"
msgstr "Шаблон улучшения ящика"

#~ msgid "Current State: "
#~ msgstr "Текущее состояние: "

#~ msgid "Stopped"
#~ msgstr "остановлен"

#~ msgid "Running"
#~ msgstr "в действии"

#~ msgid "Jammed"
#~ msgstr "замкнут"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr ""

#: lua/api.lua init.lua
msgid "Wooden"
msgstr ""

#: lua/api.lua
msgid "@1 Drawer"
msgstr ""

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr ""

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr ""

#: lua/controller.lua
msgid "Drawer Controller"
msgstr ""

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr ""

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr ""

#: init.lua
msgid "Acacia Wood"
msgstr ""

#: init.lua
msgid "Aspen Wood"
msgstr ""

#: init.lua
msgid "Junglewood"
msgstr ""

#: init.lua
msgid "Pine Wood"
msgstr ""

#: init.lua
msgid "Oak Wood"
msgstr ""

#: init.lua
msgid "Birch Wood"
msgstr ""

#: init.lua
msgid "Dark Oak Wood"
msgstr ""

#: init.lua
msgid "Spruce Wood"
msgstr ""

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr ""

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr ""

#: init.lua
msgid "Obsidian Drawer Upgrade (x4)"
msgstr ""

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr ""

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr ""

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr ""

#: init.lua
msgid "Mithril Drawer Upgrade (x13)"
msgstr ""

#: init.lua
msgid "Wooden Trim"
msgstr ""

#: init.lua
msgid "Drawer Upgrade Template"
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: 2018-12-09 11:08+0000\n"
"Last-Translator: monolifed <monolifed@gmail.com>\n"
"Language-Team: Turkish <https://hosted.weblate.org/projects/minetest/mod-"
"storage-drawers/tr/>\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 3.4-dev\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr "Boş"

#: lua/api.lua init.lua
msgid "Wooden"
msgstr "Ahşap"

#: lua/api.lua
msgid "@1 Drawer"
msgstr "@1 Çekmece"

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr "@1 Çekmece (1x2)"

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr "@1 Çekmece (2x2)"

#: lua/controller.lua
msgid "Drawer Controller"
msgstr "Çekmece Denetleyicisi"

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr "@1 (%@2 dolu)"

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr "@1 @2 (%@3 dolu)"

#: init.lua
msgid "Acacia Wood"
msgstr "Akasya Ahşap"

#: init.lua
msgid "Aspen Wood"
msgstr "Kavak Ahşap"

#: init.lua
msgid "Junglewood"
msgstr "Cangıl Ahşap"

#: init.lua
msgid "Pine Wood"
msgstr "Çam Ahşap"

#: init.lua
msgid "Oak Wood"
msgstr "Meşe Ahşap"

#: init.lua
msgid "Birch Wood"
msgstr "Huş Ahşap"

#: init.lua
msgid "Dark Oak Wood"
msgstr "Koyu Meşe Ahşap"

#: init.lua
msgid "Spruce Wood"
msgstr "Ladin Ahşap"

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr "Çelik Çekmece Yükseltme (x2)"

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr "Altın Çekmece Yükseltme (x3)"

#: init.lua
#, fuzzy
msgid "Obsidian Drawer Upgrade (x4)"
msgstr "Obsidyen Çekmece Yükseltme (x5)"

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr "Elmas Çekmece Yükseltme (x8)"

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr "Demir Çekmece Yükseltme (x2)"

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr "Zümrüt Çekmece Yükseltme (x13)"

#: init.lua
#, fuzzy
msgid "Mithril Drawer Upgrade (x13)"
msgstr "Zümrüt Çekmece Yükseltme (x13)"

#: init.lua
msgid "Wooden Trim"
msgstr "Ahşap Döşeme"

#: init.lua
msgid "Drawer Upgrade Template"
msgstr "Çekmece Yükseltme Şablonu"

#~ msgid "Current State: "
#~ msgstr "Şu Anki Durum: "

#~ msgid "Stopped"
#~ msgstr "Durdu"

#~ msgid "Running"
#~ msgstr "Çalışıyor"

#~ msgid "Jammed"
#~ msgstr "Sıkıştı"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: 2019-07-18 17:04+0000\n"
"Last-Translator: Elizabeth Sherrock <lizzyd710@gmail.com>\n"
"Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
"minetest/mod-storage-drawers/zh_CMN-HANT/>\n"
"Language: zh_CMN-HANT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 3.8-dev\n"

#: lua/visual.lua lua/api.lua
#, fuzzy
msgid "Empty"
msgstr "空的"

#: lua/api.lua init.lua
#, fuzzy
msgid "Wooden"
msgstr "木制"

#: lua/api.lua
msgid "@1 Drawer"
msgstr ""

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr ""

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr ""

#: lua/controller.lua
msgid "Drawer Controller"
msgstr ""

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr ""

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr ""

#: init.lua
msgid "Acacia Wood"
msgstr "金合欢木"

#: init.lua
msgid "Aspen Wood"
msgstr ""

#: init.lua
msgid "Junglewood"
msgstr "丛林木"

#: init.lua
msgid "Pine Wood"
msgstr ""

#: init.lua
msgid "Oak Wood"
msgstr "橡木"

#: init.lua
msgid "Birch Wood"
msgstr "白桦木"

#: init.lua
msgid "Dark Oak Wood"
msgstr "深色橡木"

#: init.lua
msgid "Spruce Wood"
msgstr "云杉木"

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr ""

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr ""

#: init.lua
msgid "Obsidian Drawer Upgrade (x4)"
msgstr ""

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr ""

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr ""

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr ""

#: init.lua
msgid "Mithril Drawer Upgrade (x13)"
msgstr ""

#: init.lua
msgid "Wooden Trim"
msgstr ""

#: init.lua
msgid "Drawer Upgrade Template"
msgstr ""

#~ msgid "Current State: "
#~ msgstr "当前状态： "

#~ msgid "Stopped"
#~ msgstr "已停止"

#~ msgid "Running"
#~ msgstr "正在运行"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: zh_Hans\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr ""

#: lua/api.lua init.lua
msgid "Wooden"
msgstr ""

#: lua/api.lua
msgid "@1 Drawer"
msgstr ""

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr ""

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr ""

#: lua/controller.lua
msgid "Drawer Controller"
msgstr ""

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr ""

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr ""

#: init.lua
msgid "Acacia Wood"
msgstr ""

#: init.lua
msgid "Aspen Wood"
msgstr ""

#: init.lua
msgid "Junglewood"
msgstr ""

#: init.lua
msgid "Pine Wood"
msgstr ""

#: init.lua
msgid "Oak Wood"
msgstr ""

#: init.lua
msgid "Birch Wood"
msgstr ""

#: init.lua
msgid "Dark Oak Wood"
msgstr ""

#: init.lua
msgid "Spruce Wood"
msgstr ""

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr ""

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr ""

#: init.lua
msgid "Obsidian Drawer Upgrade (x4)"
msgstr ""

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr ""

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr ""

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr ""

#: init.lua
msgid "Mithril Drawer Upgrade (x13)"
msgstr ""

#: init.lua
msgid "Wooden Trim"
msgstr ""

#: init.lua
msgid "Drawer Upgrade Template"
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-27 18:43+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: zh_Hant\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: lua/visual.lua lua/api.lua
msgid "Empty"
msgstr ""

#: lua/api.lua init.lua
msgid "Wooden"
msgstr ""

#: lua/api.lua
msgid "@1 Drawer"
msgstr ""

#: lua/api.lua
msgid "@1 Drawers (1x2)"
msgstr ""

#: lua/api.lua
msgid "@1 Drawers (2x2)"
msgstr ""

#: lua/controller.lua
msgid "Drawer Controller"
msgstr ""

#: lua/controller.lua
msgid "Digiline Channel"
msgstr ""

#: lua/controller.lua
msgid "Save"
msgstr ""

#: lua/helpers.lua
msgid "@1 (@2% full)"
msgstr ""

#: lua/helpers.lua
msgid "@1 @2 (@3% full)"
msgstr ""

#: init.lua
msgid "Acacia Wood"
msgstr ""

#: init.lua
msgid "Aspen Wood"
msgstr ""

#: init.lua
msgid "Junglewood"
msgstr ""

#: init.lua
msgid "Pine Wood"
msgstr ""

#: init.lua
msgid "Oak Wood"
msgstr ""

#: init.lua
msgid "Birch Wood"
msgstr ""

#: init.lua
msgid "Dark Oak Wood"
msgstr ""

#: init.lua
msgid "Spruce Wood"
msgstr ""

#: init.lua
msgid "Steel Drawer Upgrade (x2)"
msgstr ""

#: init.lua
msgid "Gold Drawer Upgrade (x3)"
msgstr ""

#: init.lua
msgid "Obsidian Drawer Upgrade (x4)"
msgstr ""

#: init.lua
msgid "Diamond Drawer Upgrade (x8)"
msgstr ""

#: init.lua
msgid "Iron Drawer Upgrade (x2)"
msgstr ""

#: init.lua
msgid "Emerald Drawer Upgrade (x13)"
msgstr ""

#: init.lua
msgid "Mithril Drawer Upgrade (x13)"
msgstr ""

#: init.lua
msgid "Wooden Trim"
msgstr ""

#: init.lua
msgid "Drawer Upgrade Template"
msgstr ""


code:
--[[
Minetest Mod Storage Drawers - A Mod adding storage drawers

Copyright (C) 2017-2020 Linus Jahn <lnj@kaidan.im>
Copyright (C) 2016 Mango Tango <mtango688@gmail.com>

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]

-- Load support for intllib.
local MP = core.get_modpath(core.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")

drawers.node_box_simple = {
	{-0.5, -0.5, -0.4375, 0.5, 0.5, 0.5},
	{-0.5, -0.5, -0.5, -0.4375, 0.5, -0.4375},
	{0.4375, -0.5, -0.5, 0.5, 0.5, -0.4375},
	{-0.4375, 0.4375, -0.5, 0.4375, 0.5, -0.4375},
	{-0.4375, -0.5, -0.5, 0.4375, -0.4375, -0.4375},
}

drawers.drawer_formspec = "size[9,7]" ..
	"list[context;upgrades;2,0.5;5,1;]" ..
	"list[current_player;main;0,3;9,4;]" ..
	"listring[context;upgrades]" ..
	"listring[current_player;main]" ..
	drawers.gui_bg ..
	drawers.gui_bg_img ..
	drawers.gui_slots ..
	drawers.get_upgrade_slots_bg(2, 0.5)

-- construct drawer
function drawers.drawer_on_construct(pos)
	local node = core.get_node(pos)
	local ndef = core.registered_nodes[node.name]
	local drawerType = ndef.groups.drawer

	local base_stack_max = core.nodedef_default.stack_max or 99
	local stack_max_factor = ndef.drawer_stack_max_factor or 24 -- 3x8
	stack_max_factor = math.floor(stack_max_factor / drawerType) -- drawerType => number of drawers in node

	-- meta
	local meta = core.get_meta(pos)

	local i = 1
	while i <= drawerType do
		local vid = i
		-- 1x1 drawers don't have numbers in the meta fields
		if drawerType == 1 then vid = "" end
		meta:set_string("name"..vid, "")
		meta:set_int("count"..vid, 0)
		meta:set_int("max_count"..vid, base_stack_max * stack_max_factor)
		meta:set_int("base_stack_max"..vid, base_stack_max)
		meta:set_string("entity_infotext"..vid, drawers.gen_info_text(S("Empty"), 0,
			stack_max_factor, base_stack_max))
		meta:set_int("stack_max_factor"..vid, stack_max_factor)

		i = i + 1
	end

	-- spawn all visuals
	drawers.spawn_visuals(pos)

	-- create drawer upgrade inventory
	meta:get_inventory():set_size("upgrades", 5)

	-- set the formspec
	meta:set_string("formspec", drawers.drawer_formspec)
end

-- destruct drawer
function drawers.drawer_on_destruct(pos)
	drawers.remove_visuals(pos)

	-- clean up visual cache
	if drawers.drawer_visuals[core.hash_node_position(pos)] then
		drawers.drawer_visuals[core.hash_node_position(pos)] = nil
	end
end

-- drop all items
function drawers.drawer_on_dig(pos, node, player)
	local drawerType = 1
	if core.registered_nodes[node.name] then
		drawerType = core.registered_nodes[node.name].groups.drawer
	end
	if core.is_protected(pos,player:get_player_name()) then
	   core.record_protection_violation(pos,player:get_player_name())
	   return 0
	end
	local meta = core.get_meta(pos)

	local k = 1
	while k <= drawerType do
		-- don't add a number in meta fields for 1x1 drawers
		local vid = tostring(k)
		if drawerType == 1 then vid = "" end
		local count = meta:get_int("count"..vid)
		local name = meta:get_string("name"..vid)

		-- drop the items
		local stack_max = ItemStack(name):get_stack_max()

		local j = math.floor(count / stack_max) + 1
		local i = 1
		while i <= j do
			local rndpos = drawers.randomize_pos(pos)
			if not (i == j) then
				core.add_item(rndpos, name .. " " .. stack_max)
			else
				core.add_item(rndpos, name .. " " .. count % stack_max)
			end
			i = i + 1
		end
		k = k + 1
	end

	-- drop all drawer upgrades
	local upgrades = meta:get_inventory():get_list("upgrades")
	if upgrades then
		for _,itemStack in pairs(upgrades) do
			if itemStack:get_count() > 0 then
				local rndpos = drawers.randomize_pos(pos)
				core.add_item(rndpos, itemStack:get_name())
			end
		end
	end

	-- remove node
	core.node_dig(pos, node, player)
end

function drawers.drawer_allow_metadata_inventory_put(pos, listname, index, stack, player)
	if core.is_protected(pos,player:get_player_name()) then
	   core.record_protection_violation(pos,player:get_player_name())
	   return 0
	end
	if listname ~= "upgrades" then
		return 0
	end
	if stack:get_count() > 1 then
		return 0
	end
	if core.get_item_group(stack:get_name(), "drawer_upgrade") < 1 then
		return 0
	end
	return 1
end

function drawers.add_drawer_upgrade(pos, listname, index, stack, player)
	-- only do anything if adding to upgrades
	if listname ~= "upgrades" then return end

	drawers.update_drawer_upgrades(pos)
end

function drawers.remove_drawer_upgrade(pos, listname, index, stack, player)
	-- only do anything if adding to upgrades
	if listname ~= "upgrades" then return end

	drawers.update_drawer_upgrades(pos)
end

--[[
	Inserts an incoming stack into a specific slot of a drawer.
]]
function drawers.drawer_insert_object(pos, stack, visualid)
	local visual = drawers.get_visual(pos, visualid)
	if not visual then
		return stack
	end

	return visual:try_insert_stack(stack, true)
end

--[[
	Inserts an incoming stack into a drawer and uses all slots.
]]
function drawers.drawer_insert_object_from_tube(pos, node, stack, direction)
	local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)]
	if not drawer_visuals then
        return stack
    end

	-- first try to insert in the correct slot (if there are already items)
	local leftover = stack
	for _, visual in pairs(drawer_visuals) do
		if visual.itemName == stack:get_name() then
			leftover = visual:try_insert_stack(leftover, true)
		end
	end

	-- if there's still something left, also use other slots
	if leftover:get_count() > 0 then
		for _, visual in pairs(drawer_visuals) do
			leftover = visual:try_insert_stack(leftover, true)
		end
	end
	return leftover
end

--[[
	Returns how much (count) of a stack can be inserted to a drawer slot.
]]
function drawers.drawer_can_insert_stack(pos, stack, visualid)
	local visual = drawers.get_visual(pos, visualid)
	if not visual then
		return 0
	end

	return visual:can_insert_stack(stack)
end

--[[
	Returns whether a stack can be (partially) inserted to any slot of a drawer.
]]
function drawers.drawer_can_insert_stack_from_tube(pos, node, stack, direction)
	local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)]
	if not drawer_visuals then
		return false
	end

	for _, visual in pairs(drawer_visuals) do
	   if visual:can_insert_stack(stack) > 0 then
	      return true
	   end
	end
	return false
end

function drawers.drawer_take_item(pos, itemstack)
	local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)]

	if not drawer_visuals then
		return ItemStack("")
	end

	-- check for max count
	if itemstack:get_count() > itemstack:get_stack_max() then
		itemstack:set_count(itemstack:get_stack_max())
	end

	for _, visual in pairs(drawer_visuals) do
		if visual.itemName == itemstack:get_name() then
			return visual:take_items(itemstack:get_count())
		end
	end

	return ItemStack()
end

--[[
	Returns the content of a drawer slot.
]]
function drawers.drawer_get_content(pos, visualid)
	local drawer_meta = core.get_meta(pos)

	return {
		name = drawer_meta:get_string("name" .. visualid),
		count = drawer_meta:get_int("count" .. visualid),
		maxCount = drawer_meta:get_int("max_count" .. visualid)
	}
end

function drawers.register_drawer(name, def)
	def.description = def.description or S("Wooden")
	def.drawtype = "nodebox"
	def.node_box = {type = "fixed", fixed = drawers.node_box_simple}
	def.collision_box = {type = "regular"}
	def.selection_box = {type = "fixed", fixed = drawers.node_box_simple}
	def.paramtype = "light"
	def.paramtype2 = "facedir"
	def.legacy_facedir_simple = true
	def.groups = def.groups or {}
	def.drawer_stack_max_factor = def.drawer_stack_max_factor or 24

	-- events
	def.on_construct = drawers.drawer_on_construct
	def.on_destruct = drawers.drawer_on_destruct
	def.on_dig = drawers.drawer_on_dig
	def.allow_metadata_inventory_put = drawers.drawer_allow_metadata_inventory_put
	def.allow_metadata_inventory_take = drawers.drawer_allow_metadata_inventory_put
	def.on_metadata_inventory_put = drawers.add_drawer_upgrade
	def.on_metadata_inventory_take = drawers.remove_drawer_upgrade

	if minetest.get_modpath("screwdriver") and screwdriver then
		def.on_rotate = def.on_rotate or screwdriver.disallow
	end

	if minetest.get_modpath("pipeworks") and pipeworks then
		def.groups.tubedevice = 1
		def.groups.tubedevice_receiver = 1
		def.tube = def.tube or {}
		def.tube.insert_object = def.tube.insert_object or
		   drawers.drawer_insert_object_from_tube
		def.tube.can_insert = def.tube.can_insert or
		   drawers.drawer_can_insert_stack_from_tube

		def.tube.connect_sides = {left = 1, right = 1, back = 1, top = 1,
			bottom = 1}
		def.after_place_node = pipeworks.after_place
		def.after_dig_node = pipeworks.after_dig
	end

	local has_mesecons_mvps = minetest.get_modpath("mesecons_mvps")

	if drawers.enable_1x1 then
		-- normal drawer 1x1 = 1
		local def1 = table.copy(def)
		def1.description = S("@1 Drawer", def.description)
		def1.tiles = def.tiles or def.tiles1
		def1.tiles1 = nil
		def1.tiles2 = nil
		def1.tiles4 = nil
		def1.groups.drawer = 1
		core.register_node(name .. "1", def1)
		core.register_alias(name, name .. "1") -- 1x1 drawer is the default one
		if has_mesecons_mvps then
			-- don't let drawers be moved by pistons, visual glitches and
			-- possible duplication bugs occur otherwise
			mesecon.register_mvps_stopper(name .. "1")
		end
	end

	if drawers.enable_1x2 then
		-- 1x2 = 2
		local def2 = table.copy(def)
		def2.description = S("@1 Drawers (1x2)", def.description)
		def2.tiles = def.tiles2
		def2.tiles1 = nil
		def2.tiles2 = nil
		def2.tiles4 = nil
		def2.groups.drawer = 2
		core.register_node(name .. "2", def2)
		if has_mesecons_mvps then
			mesecon.register_mvps_stopper(name .. "2")
		end
	end

	if drawers.enable_2x2 then
		-- 2x2 = 4
		local def4 = table.copy(def)
		def4.description = S("@1 Drawers (2x2)", def.description)
		def4.tiles = def.tiles4
		def4.tiles1 = nil
		def4.tiles2 = nil
		def4.tiles4 = nil
		def4.groups.drawer = 4
		core.register_node(name .. "4", def4)
		if has_mesecons_mvps then
			mesecon.register_mvps_stopper(name .. "4")
		end
	end

	if (not def.no_craft) and def.material then
		if drawers.enable_1x1 then
			core.register_craft({
				output = name .. "1",
				recipe = {
					{def.material, def.material, def.material},
					{    "", drawers.CHEST_ITEMSTRING,  ""   },
					{def.material, def.material, def.material}
				}
			})
		end
		if drawers.enable_1x2 then
			core.register_craft({
				output = name .. "2 2",
				recipe = {
					{def.material, drawers.CHEST_ITEMSTRING, def.material},
					{def.material,       def.material,       def.material},
					{def.material, drawers.CHEST_ITEMSTRING, def.material}
				}
			})
		end
		if drawers.enable_2x2 then
			core.register_craft({
				output = name .. "4 4",
				recipe = {
					{drawers.CHEST_ITEMSTRING, def.material, drawers.CHEST_ITEMSTRING},
					{      def.material,       def.material,       def.material      },
					{drawers.CHEST_ITEMSTRING, def.material, drawers.CHEST_ITEMSTRING}
				}
			})
		end
	end
end

function drawers.register_drawer_upgrade(name, def)
	def.groups = def.groups or {}
	def.groups.drawer_upgrade = def.groups.drawer_upgrade or 100
	def.inventory_image = def.inventory_image or "drawers_upgrade_template.png"
	def.stack_max = 1

	local recipe_item = def.recipe_item or "air"
	def.recipe_item = nil

	core.register_craftitem(name, def)

	if not def.no_craft then
		core.register_craft({
			output = name,
			recipe = {
				{recipe_item, "group:stick", recipe_item},
				{"group:stick", "drawers:upgrade_template", "group:stick"},
				{recipe_item, "group:stick", recipe_item}
			}
		})
	end
end


code:
--[[
Minetest Mod Storage Drawers - A Mod adding storage drawers

Copyright (C) 2017-2020 Linus Jahn <lnj@kaidan.im>
Copyright (C) 2018 isaiah658

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]--

--[[ The gist of how the drawers mod stores data is that there are entities
and the drawer node itself. The entities are needed to allow having multiple
drawers in one node. The entities and node each store metadata about the item
counts and such. It is necessary to change both at once otherwise in some cases
the entity values are used and in other cases the node metadata is used.

The gist of how the controller works is this. The drawer controller scans the
adjacent tiles and puts the item names and other info such as coordinates and
the visualid of the entity in a table. That table is saved in the controllers
metadata. The table is used to help prevent needing to scan all the drawers to
deposit an item in certain situations. The table is only updated on an as needed
basis, not by a specific time/interval. Controllers that have no items will not
continue scanning drawers. ]]--

-- Load support for intllib.
local MP = core.get_modpath(core.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")

local default_loaded = core.get_modpath("default") and default
local mcl_loaded = core.get_modpath("mcl_core") and mcl_core
local pipeworks_loaded = core.get_modpath("pipeworks") and pipeworks
local digilines_loaded = core.get_modpath("digilines") and digilines

local function controller_formspec(pos)
	local formspec =
		"size[8,8.5]"..
		drawers.gui_bg..
		drawers.gui_bg_img..
		drawers.gui_slots..
		"label[0,0;" .. S("Drawer Controller") .. "]" ..
		"list[current_name;src;3.5,1.75;1,1;]"..
		"list[current_player;main;0,4.25;8,1;]"..
		"list[current_player;main;0,5.5;8,3;8]"..
		"listring[current_player;main]"..
		"listring[current_name;src]"..
		"listring[current_player;main]"

	if digilines_loaded and pipeworks_loaded then
		formspec = formspec .. "field[1,3.5;4,1;digilineChannel;" .. S("Digiline Channel") .. ";${digilineChannel}]"
		formspec = formspec .. "button_exit[5,3.2;2,1;saveChannel;" .. S("Save") .. "]"
	end

	return formspec
end

local function is_valid_drawer_index_slot(net_index, item_name)
	return net_index and
			net_index[item_name] and
			net_index[item_name].drawer_pos and
			net_index[item_name].drawer_pos.x and
			net_index[item_name].drawer_pos.y and
			net_index[item_name].drawer_pos.z and
			net_index[item_name].visualid
end

local function controller_index_slot(pos, visualid)
	return {
		drawer_pos = pos,
		visualid = visualid
	}
end

local function compare_pos(pos1, pos2)
	return pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z
end

local function contains_pos(list, p)
	for _,v in ipairs(list) do
		if compare_pos(v, p) then
			return true
		end
	end
	return false
end

-- iterator for iterating from 1 -> to
local function range(to)
	local i = 0
	return function()
		if i == to then
			return nil
		end
		i = i + 1
		return i, i
	end
end

local function pos_in_range(pos1, pos2)
	local diff = {
		pos1.x - pos2.x,
		pos1.y - pos2.y,
		pos1.z - pos2.z
	}
	for _,v in ipairs(diff) do
		if v < 0 then
			v = v * -1
		end
		if v > drawers.CONTROLLER_RANGE then
			return false
		end
	end
	return true
end

local function add_drawer_to_inventory(controllerInventory, pos)
	-- the number of slots is saved as drawer group
	local slots = core.get_item_group(core.get_node(pos).name, "drawer")
	if not slots then
		return
	end

	local meta = core.get_meta(pos)
	if not meta then
		return
	end

	local i = 1
	while i <= slots do
		-- nothing is appended in case the drawer has only one slot
		local slot_id = ""
		if slots ~= 1 then
			slot_id = tostring(i)
		end

		local item_id = meta:get_string("name" .. slot_id)
		local drawer_meta_entity_infotext = meta:get_string("entity_infotext" .. slot_id)

		if item_id == "" and not controllerInventory["empty"] then
			controllerInventory["empty"] = controller_index_slot(pos, slot_id)
		elseif item_id ~= "" then
			-- If we already indexed this item previously, check which drawer
			-- has the most space and have that one be the one indexed
			if controllerInventory[item_id] then
				local content = drawers.drawer_get_content(controllerInventory[item_id].drawer_pos, controllerInventory[item_id].visualid)
				local new_content = drawers.drawer_get_content(pos, slot_id)

				-- If the already indexed drawer has less space, we override the
				-- table index for that item with the new drawer
				if (new_content.maxCount - new_content.count) > (content.maxCount - content.count) then
					controllerInventory[item_id] = controller_index_slot(pos, slot_id)
				end
			else
				controllerInventory[item_id] = controller_index_slot(pos, slot_id)
			end
		end

		i = i + 1
	end
end

local function find_connected_drawers(controller_pos, pos, foundPositions)
	foundPositions = foundPositions or {}
	pos = pos or controller_pos

	local newPositions = core.find_nodes_in_area(
		{x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
		{x = pos.x + 1, y = pos.y + 1, z = pos.z + 1},
		{"group:drawer", "group:drawer_connector"}
	)

	for _,p in ipairs(newPositions) do
		-- check that this node hasn't been scanned yet
		if not compare_pos(pos, p) and not contains_pos(foundPositions, p)
		   and pos_in_range(controller_pos, pos) then
			-- add new position
			table.insert(foundPositions, p)
			-- search for other drawers from the new pos
			find_connected_drawers(controller_pos, p, foundPositions)
		end
	end

	return foundPositions
end

local function index_drawers(pos)
	--[[
	The pos parameter is the controllers position

	We store the item name as a string key and the value is a table with position x,
	position y, position z, and visualid. Those are all strings as well with the
	values assigned to them that way we don't need to worry about the ordering of
	the table. The count and max count are not stored as those values have a high
	potential of being outdated quickly. It's better to grab the values from the
	drawer when needed so you know you are working with accurate numbers.
	]]

	local controllerInventory = {}
	for _,drawerPos in ipairs(find_connected_drawers(pos)) do
		add_drawer_to_inventory(controllerInventory, drawerPos)
	end

	return controllerInventory
end

--[[
	Returns a table of all stored itemstrings in the drawer network with their
	drawer position and visualid.

	It uses the cached data, if possible, but if the itemstring is not contained
	the network is reindexed.
]]
local function controller_get_drawer_index(pos, itemstring)
	local meta = core.get_meta(pos)

	-- If the index has not been created, the item isn't in the index, the
	-- item in the drawer is no longer the same item in the index, or the item
	-- is in the index but it's full, run the index_drawers function.
	local drawer_net_index = core.deserialize(meta:get_string("drawers_table_index"))

	-- If the index has not been created
	-- If the item isn't in the index (or the index is corrupted)
	if not is_valid_drawer_index_slot(drawer_net_index, itemstring) then
		drawer_net_index = index_drawers(pos)
		meta:set_string("drawers_table_index", core.serialize(drawer_net_index))

	-- There is a valid entry in the index: check that the entry is still up-to-date
	else
		local content = drawers.drawer_get_content(
			drawer_net_index[itemstring].drawer_pos,
			drawer_net_index[itemstring].visualid)

		if content.name ~= itemstring or content.count >= content.maxCount then
			drawer_net_index = index_drawers(pos)
			meta:set_string("drawers_table_index", core.serialize(drawer_net_index))
		end
	end

	return drawer_net_index
end

local function controller_insert_to_drawers(pos, stack)
	-- Inizialize metadata
	local meta = core.get_meta(pos)
	local inv = meta:get_inventory()

	local drawer_net_index = controller_get_drawer_index(pos, stack:get_name())

	-- We check if there is a drawer with the item and it isn't full. We will
	-- put the items we can into it.
	if drawer_net_index[stack:get_name()] then
		local drawer_pos = drawer_net_index[stack:get_name()]["drawer_pos"]
		local visualid = drawer_net_index[stack:get_name()]["visualid"]
		local content = drawers.drawer_get_content(drawer_pos, visualid)

		-- If the the item in the drawer is the same as the one we are trying to
		-- store, the drawer is not full, and the drawer entity is loaded, we
		-- will put the items in the drawer
		if content.name == stack:get_name() and
				content.count < content.maxCount and
				drawers.drawer_visuals[core.hash_node_position(drawer_pos)] then
			return drawers.drawer_insert_object(drawer_pos, stack, visualid)
		end
	elseif drawer_net_index["empty"] then
		local drawer_pos = drawer_net_index["empty"]["drawer_pos"]
		local visualid = drawer_net_index["empty"]["visualid"]
		local content = drawers.drawer_get_content(drawer_pos, visualid)

		-- If the drawer is still empty and the drawer entity is loaded, we will
		-- put the items in the drawer
		if content.name == "" and drawers.drawer_visuals[core.hash_node_position(drawer_pos)] then
			local leftover = drawers.drawer_insert_object(drawer_pos, stack, visualid)

			-- Add the item to the drawers table index and set the empty one to nil
			drawer_net_index["empty"] = nil
			drawer_net_index[stack:get_name()] = controller_index_slot(drawer_pos, visualid)

			-- Set the controller metadata
			meta:set_string("drawers_table_index", core.serialize(drawer_net_index))

			return leftover
		end
	end

	return stack
end

local function controller_can_dig(pos, player)
	local meta = core.get_meta(pos);
	local inv = meta:get_inventory()
	return inv:is_empty("src")
end

local function controller_on_construct(pos)
	local meta = core.get_meta(pos)
	meta:set_string("drawers_table_index", "")
	meta:set_string("formspec", controller_formspec(pos))

	meta:get_inventory():set_size("src", 1)
end

local function controller_on_blast(pos)
	local drops = {}
	default.get_inventory_drops(pos, "src", drops)
	drops[#drops+1] = "drawers:controller"
	core.remove_node(pos)
	return drops
end

local function controller_allow_metadata_inventory_put(pos, listname, index, stack, player)
	if (player and core.is_protected(pos, player:get_player_name())) or listname ~= "src" then
		return 0
	end

	local drawer_net_index = controller_get_drawer_index(pos, stack:get_name())

	if drawer_net_index[stack:get_name()] then
		local drawer = drawer_net_index[stack:get_name()]

		if drawers.drawer_get_content(drawer.drawer_pos, drawer.visualid).name == stack:get_name() then
			return drawers.drawer_can_insert_stack(drawer.drawer_pos, stack, drawer["visualid"])
		end
	end

	if drawer_net_index["empty"] then
		local drawer = drawer_net_index["empty"]

		if drawers.drawer_get_content(drawer.drawer_pos, drawer.visualid).name == "" then
			return drawers.drawer_can_insert_stack(drawer.drawer_pos, stack, drawer.visualid)
		end
	end

	return 0
end

local function controller_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
	local meta = core.get_meta(pos)
	local inv = meta:get_inventory()
	local stack = inv:get_stack(from_list, from_index)
	return controller_allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
end

local function controller_allow_metadata_inventory_take(pos, listname, index, stack, player)
	if core.is_protected(pos, player:get_player_name()) then
		return 0
	end
	return stack:get_count()
end

local function controller_on_metadata_inventory_put(pos, listname, index, stack, player)
	if listname ~= "src" then
		return
	end

	local inv = core.get_meta(pos):get_inventory()

	local complete_stack = inv:get_stack("src", 1)
	local leftover = controller_insert_to_drawers(pos, complete_stack)
	inv:set_stack("src", 1, leftover)
end

local function controller_on_digiline_receive(pos, _, channel, msg)
	local meta = core.get_meta(pos)

	if channel ~= meta:get_string("digilineChannel") then
		return
	end

	local item = ItemStack(msg)
	local drawers_index = controller_get_drawer_index(pos, item:get_name())

	if not drawers_index[item:get_name()] then
		-- we can't do anything: the requested item doesn't exist
		return
	end

	local taken_stack = drawers.drawer_take_item(
		drawers_index[item:get_name()]["drawer_pos"], item)
	local dir = core.facedir_to_dir(core.get_node(pos).param2)

	-- prevent crash if taken_stack ended up with a nil value
	if taken_stack then
		pipeworks.tube_inject_item(pos, pos, dir, taken_stack:to_string())
	end
end

local function controller_on_receive_fields(pos, formname, fields, sender)
	if core.is_protected(pos, sender:get_player_name()) then
		return
	end
	local meta = core.get_meta(pos)
	if fields.saveChannel then
		meta:set_string("digilineChannel", fields.digilineChannel)
	end
end

-- Registers the drawer controller
local function register_controller()
	-- Set the controller definition using a table to allow for pipeworks and
	-- potentially other mod support
	local def = {}

	def.description = S("Drawer Controller")
	def.drawtype = "nodebox"
	def.node_box = { type = "fixed", fixed = drawers.node_box_simple }
	def.collision_box = { type = "regular" }
	def.selection_box = { type = "regular" }
	def.paramtype = "light"
	def.paramtype2 = "facedir"
	def.legacy_facedir_simple = true

	-- add pipe connectors, if pipeworks is enabled
	if pipeworks_loaded then
		def.tiles = {
			"drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
			"drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
			"drawers_controller_side.png^pipeworks_tube_connection_metallic.png",
			"drawers_controller_side.png^pipeworks_tube_connection_metallic.png",
			"drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
			"drawers_controller_front.png"
		}
	else
		def.tiles = {
			"drawers_controller_top.png",
			"drawers_controller_top.png",
			"drawers_controller_side.png",
			"drawers_controller_side.png",
			"drawers_controller_top.png",
			"drawers_controller_front.png"
		}
	end

	-- MCL2 requires a few different groups and parameters that MTG does not
	if mcl_loaded then
		def.groups = {
			pickaxey = 1, stone = 1, building_block = 1, material_stone = 1
		}
		def._mcl_blast_resistance = 30
		def._mcl_hardness = 1.5
	else
		def.groups = {
			cracky = 3, level = 2
		}
	end

	def.can_dig = controller_can_dig
	def.on_construct = controller_on_construct
	def.on_blast = controller_on_blast
	def.on_receive_fields = controller_on_receive_fields
	def.on_metadata_inventory_put = controller_on_metadata_inventory_put

	def.allow_metadata_inventory_put = controller_allow_metadata_inventory_put
	def.allow_metadata_inventory_move = controller_allow_metadata_inventory_move
	def.allow_metadata_inventory_take = controller_allow_metadata_inventory_take

	if pipeworks_loaded then
		def.groups.tubedevice = 1
		def.groups.tubedevice_receiver = 1

		def.tube = {}
		def.tube.insert_object = function(pos, node, stack, tubedir)
			return controller_insert_to_drawers(pos, stack)
		end

		def.tube.can_insert = function(pos, node, stack, tubedir)
			return controller_allow_metadata_inventory_put(pos, "src", nil, stack, nil) > 0
		end

		def.tube.connect_sides = {
			left = 1, right = 1, back = 1, top = 1, bottom = 1
		}

		def.after_place_node = pipeworks.after_place
		def.after_dig_node = pipeworks.after_dig
	end

	if digilines_loaded and pipeworks_loaded then
		def.digiline = {
			receptor = {},
			effector = {
				action = controller_on_digiline_receive
			},
		}
	end

	core.register_node("drawers:controller", def)
end

-- register drawer controller
register_controller()

if default_loaded then
	core.register_craft({
		output = 'drawers:controller',
		recipe = {
			{'default:steel_ingot', 'default:diamond', 'default:steel_ingot'},
			{'default:tin_ingot',    'group:drawer',   'default:copper_ingot'},
			{'default:steel_ingot', 'default:diamond', 'default:steel_ingot'},
		}
	})
elseif mcl_loaded then
	core.register_craft({
		output = 'drawers:controller',
		recipe = {
			{'mcl_core:iron_ingot', 'mcl_core:diamond', 'mcl_core:iron_ingot'},
			{'mcl_core:gold_ingot',   'group:drawer',   'mcl_core:gold_ingot'},
			{'mcl_core:iron_ingot', 'mcl_core:diamond', 'mcl_core:iron_ingot'},
		}
	})
else
	-- Because the rest of the drawers mod doesn't have a hard depend on
	-- default, I changed the recipe to have an alternative
	core.register_craft({
		output = 'drawers:controller',
		recipe = {
			{'group:stone', 'group:stone',  'group:stone'},
			{'group:stone', 'group:drawer', 'group:stone'},
			{'group:stone', 'group:stone',  'group:stone'},
		}
	})
end


code:
--[[
Minetest Mod Storage Drawers - A Mod adding storage drawers

Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>
Copyright (C) 2016 Mango Tango <mtango688@gmail.com>

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]

-- Load support for intllib.
local MP = core.get_modpath(core.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")

-- GUI
function drawers.get_upgrade_slots_bg(x,y)
	local out = ""
	for i = 0, 4, 1 do
		out = out .."image["..x+i..","..y..";1,1;drawers_upgrade_slot_bg.png]"
	end
	return out
end

function drawers.gen_info_text(basename, count, factor, stack_max)
	local maxCount = stack_max * factor
	local percent = count / maxCount * 100
	-- round the number (float -> int)
	percent = math.floor(percent + 0.5)

	if count == 0 then
		return S("@1 (@2% full)", basename, tostring(percent))
	else
		return S("@1 @2 (@3% full)", tostring(count), basename, tostring(percent))
	end
end

function drawers.get_inv_image(name)
	local texture = "blank.png"
	local def = core.registered_items[name]
	if not def then return end

	if def.inventory_image and #def.inventory_image > 0 then
		texture = def.inventory_image
	else
		if not def.tiles then return texture end
		local tiles = table.copy(def.tiles)

		for k,v in pairs(tiles) do
			if type(v) == "table" then
				tiles[k] = v.name
			end
		end

		-- tiles: up, down, right, left, back, front
		-- inventorycube: up, front, right
		if #tiles <= 2 then
			texture = core.inventorycube(tiles[1], tiles[1], tiles[1])
		elseif #tiles <= 5 then
			texture = core.inventorycube(tiles[1], tiles[3], tiles[3])
		else -- full tileset
			texture = core.inventorycube(tiles[1], tiles[6], tiles[3])
		end
	end

	return texture
end

function drawers.spawn_visuals(pos)
	local node = core.get_node(pos)
	local ndef = core.registered_nodes[node.name]
	local drawerType = ndef.groups.drawer

	-- data for the new visual
	drawers.last_drawer_pos = pos
	drawers.last_drawer_type = drawerType

	if drawerType == 1 then -- 1x1 drawer
		drawers.last_visual_id = ""
		drawers.last_texture = drawers.get_inv_image(core.get_meta(pos):get_string("name"))

		local bdir = core.facedir_to_dir(node.param2)
		local fdir = vector.new(-bdir.x, 0, -bdir.z)
		local pos2 = vector.add(pos, vector.multiply(fdir, 0.45))

		local obj = core.add_entity(pos2, "drawers:visual")
		if not obj then return end

		if bdir.x < 0 then obj:set_yaw(0.5 * math.pi) end
		if bdir.z < 0 then obj:set_yaw(math.pi) end
		if bdir.x > 0 then obj:set_yaw(1.5 * math.pi) end

		drawers.last_texture = nil
	elseif drawerType == 2 then
		local bdir = core.facedir_to_dir(node.param2)

		local fdir1
		local fdir2
		if node.param2 == 2 or node.param2 == 0 then
			fdir1 = vector.new(-bdir.x, 0.5, -bdir.z)
			fdir2 = vector.new(-bdir.x, -0.5, -bdir.z)
		else
			fdir1 = vector.new(-bdir.x, 0.5, -bdir.z)
			fdir2 = vector.new(-bdir.x, -0.5, -bdir.z)
		end

		local objs = {}

		drawers.last_visual_id = 1
		drawers.last_texture = drawers.get_inv_image(core.get_meta(pos):get_string("name1"))
		local pos1 = vector.add(pos, vector.multiply(fdir1, 0.45))
		objs[1] = core.add_entity(pos1, "drawers:visual")

		drawers.last_visual_id = 2
		drawers.last_texture = drawers.get_inv_image(core.get_meta(pos):get_string("name2"))
		local pos2 = vector.add(pos, vector.multiply(fdir2, 0.45))
		objs[2] = core.add_entity(pos2, "drawers:visual")

		for i,obj in pairs(objs) do
			if bdir.x < 0 then obj:set_yaw(0.5 * math.pi) end
			if bdir.z < 0 then obj:set_yaw(math.pi) end
			if bdir.x > 0 then obj:set_yaw(1.5 * math.pi) end
		end
	else -- 2x2 drawer
		local bdir = core.facedir_to_dir(node.param2)

		local fdir1
		local fdir2
		local fdir3
		local fdir4
		if node.param2 == 2 then
			fdir1 = vector.new(-bdir.x + 0.5, 0.5, -bdir.z)
			fdir2 = vector.new(-bdir.x - 0.5, 0.5, -bdir.z)
			fdir3 = vector.new(-bdir.x + 0.5, -0.5, -bdir.z)
			fdir4 = vector.new(-bdir.x - 0.5, -0.5, -bdir.z)
		elseif node.param2 == 0 then
			fdir1 = vector.new(-bdir.x - 0.5, 0.5, -bdir.z)
			fdir2 = vector.new(-bdir.x + 0.5, 0.5, -bdir.z)
			fdir3 = vector.new(-bdir.x - 0.5, -0.5, -bdir.z)
			fdir4 = vector.new(-bdir.x + 0.5, -0.5, -bdir.z)
		elseif node.param2 == 1 then
			fdir1 = vector.new(-bdir.x, 0.5, -bdir.z + 0.5)
			fdir2 = vector.new(-bdir.x, 0.5, -bdir.z - 0.5)
			fdir3 = vector.new(-bdir.x, -0.5, -bdir.z + 0.5)
			fdir4 = vector.new(-bdir.x, -0.5, -bdir.z - 0.5)
		else
			fdir1 = vector.new(-bdir.x, 0.5, -bdir.z - 0.5)
			fdir2 = vector.new(-bdir.x, 0.5, -bdir.z + 0.5)
			fdir3 = vector.new(-bdir.x, -0.5, -bdir.z - 0.5)
			fdir4 = vector.new(-bdir.x, -0.5, -bdir.z + 0.5)
		end

		local objs = {}

		drawers.last_visual_id = 1
		drawers.last_texture = drawers.get_inv_image(core.get_meta(pos):get_string("name1"))
		local pos1 = vector.add(pos, vector.multiply(fdir1, 0.45))
		objs[1] = core.add_entity(pos1, "drawers:visual")

		drawers.last_visual_id = 2
		drawers.last_texture = drawers.get_inv_image(core.get_meta(pos):get_string("name2"))
		local pos2 = vector.add(pos, vector.multiply(fdir2, 0.45))
		objs[2] = core.add_entity(pos2, "drawers:visual")

		drawers.last_visual_id = 3
		drawers.last_texture = drawers.get_inv_image(core.get_meta(pos):get_string("name3"))
		local pos3 = vector.add(pos, vector.multiply(fdir3, 0.45))
		objs[3] = core.add_entity(pos3, "drawers:visual")

		drawers.last_visual_id = 4
		drawers.last_texture = drawers.get_inv_image(core.get_meta(pos):get_string("name4"))
		local pos4 = vector.add(pos, vector.multiply(fdir4, 0.45))
		objs[4] = core.add_entity(pos4, "drawers:visual")

		for i,obj in pairs(objs) do
			if bdir.x < 0 then obj:set_yaw(0.5 * math.pi) end
			if bdir.z < 0 then obj:set_yaw(math.pi) end
			if bdir.x > 0 then obj:set_yaw(1.5 * math.pi) end
		end
	end
end

function drawers.remove_visuals(pos)
	local objs = core.get_objects_inside_radius(pos, 0.56)
	if not objs then return end

	for _, obj in pairs(objs) do
		if obj and obj:get_luaentity() and
				obj:get_luaentity().name == "drawers:visual" then
			obj:remove()
		end
	end
end

--[[
	Returns the visual object for the visualid of the drawer at pos.

	visualid can be: "", "1", "2", ... or 1, 2, ...
]]
function drawers.get_visual(pos, visualid)
	local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)]
	if not drawer_visuals then
		return nil
	end

	-- not a real index (starts with 1)
	local index = tonumber(visualid)
	if visualid == "" then
		index = 1
	end

	return drawer_visuals[index]
end

function drawers.update_drawer_upgrades(pos)
	local node = core.get_node(pos)
	local ndef = core.registered_nodes[node.name]
	local drawerType = ndef.groups.drawer

	-- default number of slots/stacks
	local stackMaxFactor = ndef.drawer_stack_max_factor

	-- storage percent with all upgrades
	local storagePercent = 100

	-- get info of all upgrades
	local inventory = core.get_meta(pos):get_inventory():get_list("upgrades")
	for _,itemStack in pairs(inventory) do
		local iname = itemStack:get_name()
		local idef = core.registered_items[iname]
		local addPercent = idef.groups.drawer_upgrade or 0

		storagePercent = storagePercent + addPercent
	end

	--						i.e.: 150% / 100 => 1.50
	stackMaxFactor = math.floor(stackMaxFactor * (storagePercent / 100))
	-- calculate stack_max factor for a single drawer
	stackMaxFactor = stackMaxFactor / drawerType

	-- set the new stack max factor in all visuals
	local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)]
	if not drawer_visuals then return end

	for _,visual in pairs(drawer_visuals) do
		visual:setStackMaxFactor(stackMaxFactor)
	end
end

function drawers.randomize_pos(pos)
	local rndpos = table.copy(pos)
	local x = math.random(-50, 50) * 0.01
	local z = math.random(-50, 50) * 0.01
	rndpos.x = rndpos.x + x
	rndpos.y = rndpos.y + 0.25
	rndpos.z = rndpos.z + z
	return rndpos
end

function drawers.node_tiles_front_other(front, other)
	return {other, other, other, other, other, front}
end


code:
--[[
Minetest Mod Storage Drawers - A Mod adding storage drawers

Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]

-- Load support for intllib.
local MP = core.get_modpath(core.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")

core.register_entity("drawers:visual", {
	initial_properties = {
		hp_max = 1,
		physical = false,
		collide_with_objects = false,
		collisionbox = {-0.4374, -0.4374, 0,  0.4374, 0.4374, 0}, -- for param2 0, 2
		visual = "upright_sprite", -- "wielditem" for items without inv img?
		visual_size = {x = 0.6, y = 0.6},
		textures = {"blank.png"},
		spritediv = {x = 1, y = 1},
		initial_sprite_basepos = {x = 0, y = 0},
		is_visible = true,
	},

	get_staticdata = function(self)
		return core.serialize({
			drawer_posx = self.drawer_pos.x,
			drawer_posy = self.drawer_pos.y,
			drawer_posz = self.drawer_pos.z,
			texture = self.texture,
			drawerType = self.drawerType,
			visualId = self.visualId
		})
	end,

	on_activate = function(self, staticdata, dtime_s)
		-- Restore data
		local data = core.deserialize(staticdata)
		if data then
			self.drawer_pos = {
				x = data.drawer_posx,
				y = data.drawer_posy,
				z = data.drawer_posz,
			}
			self.texture = data.texture
			self.drawerType = data.drawerType or 1
			self.visualId = data.visualId or ""

			-- backwards compatibility
			if self.texture == "drawers_empty.png" then
				self.texture = "blank.png"
			end
		else
			self.drawer_pos = drawers.last_drawer_pos
			self.texture = drawers.last_texture or "blank.png"
			self.visualId = drawers.last_visual_id
			self.drawerType = drawers.last_drawer_type
		end

		local node = minetest.get_node(self.object:get_pos())
		if core.get_item_group(node.name, "drawer") == 0 then
			self.object:remove()
			return
		end

		-- add self to public drawer visuals
		-- this is needed because there is no other way to get this class
		-- only the underlying LuaEntitySAO
		-- PLEASE contact me, if this is wrong
		local vId = self.visualId
		if vId == "" then vId = 1 end
		local posstr = core.hash_node_position(self.drawer_pos)
		if not drawers.drawer_visuals[posstr] then
			drawers.drawer_visuals[posstr] = {[vId] = self}
		else
			drawers.drawer_visuals[posstr][vId] = self
		end

		-- get meta
		self.meta = core.get_meta(self.drawer_pos)

		-- collisionbox
		node = core.get_node(self.drawer_pos)
		local colbox
		if self.drawerType ~= 2 then
			if node.param2 == 1 or node.param2 == 3 then
				colbox = {0, -0.4374, -0.4374,  0, 0.4374, 0.4374}
			else
				colbox = {-0.4374, -0.4374, 0,  0.4374, 0.4374, 0} -- for param2 = 0 or 2
			end
			-- only half the size if it's a small drawer
			if self.drawerType > 1 then
				for i,j in pairs(colbox) do
					colbox[i] = j * 0.5
				end
			end
		else
			if node.param2 == 1 or node.param2 == 3 then
				colbox = {0, -0.2187, -0.4374,  0, 0.2187, 0.4374}
			else
				colbox = {-0.4374, -0.2187, 0,  0.4374, 0.2187, 0} -- for param2 = 0 or 2
			end
		end

		-- visual size
		local visual_size = {x = 0.6, y = 0.6}
		if self.drawerType >= 2 then
			visual_size = {x = 0.3, y = 0.3}
		end

		-- drawer values
		local vid = self.visualId
		self.count = self.meta:get_int("count"..vid)
		self.itemName = self.meta:get_string("name"..vid)
		self.maxCount = self.meta:get_int("max_count"..vid)
		self.itemStackMax = self.meta:get_int("base_stack_max"..vid)
		self.stackMaxFactor = self.meta:get_int("stack_max_factor"..vid)

		-- infotext
		local infotext = self.meta:get_string("entity_infotext"..vid) .. "\n\n\n\n\n"

		self.object:set_properties({
			collisionbox = colbox,
			infotext = infotext,
			textures = {self.texture},
			visual_size = visual_size
		})

		-- make entity undestroyable
		self.object:set_armor_groups({immortal = 1})
	end,

	on_rightclick = function(self, clicker)
		if core.is_protected(self.drawer_pos, clicker:get_player_name()) then
			core.record_protection_violation(self.drawer_pos, clicker:get_player_name())
			return
		end

		-- used to check if we need to play a sound in the end
		local inventoryChanged = false

		-- When the player uses the drawer with their bare hand all
		-- stacks from the inventory will be added to the drawer.
		if self.itemName ~= "" and
		   clicker:get_wielded_item():get_name() == "" and
		   not clicker:get_player_control().sneak then
			-- try to insert all items from inventory
			local i = 0
			local inv = clicker:get_inventory()

			while i <= inv:get_size("main") do
				-- set current stack to leftover of insertion
				local leftover = self.try_insert_stack(
					self,
					inv:get_stack("main", i),
					true
				)

				-- check if something was added
				if leftover:get_count() < inv:get_stack("main", i):get_count() then
					inventoryChanged = true
				end

				-- set new stack
				inv:set_stack("main", i, leftover)
				i = i + 1
			end
		else
			-- try to insert wielded item only
			local leftover = self.try_insert_stack(
				self,
				clicker:get_wielded_item(),
				not clicker:get_player_control().sneak
			)

			-- check if something was added
			if clicker:get_wielded_item():get_count() > leftover:get_count() then
				inventoryChanged = true
			end
			-- set the leftover as new wielded item for the player
			clicker:set_wielded_item(leftover)
		end

		if inventoryChanged then
			self:play_interact_sound()
		end
	end,

	on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
		local node = minetest.get_node(self.object:get_pos())

		if core.get_item_group(node.name, "drawer") == 0 then
			self.object:remove()
			return
		end
		local add_stack = not puncher:get_player_control().sneak
		if core.is_protected(self.drawer_pos, puncher:get_player_name()) then
		   core.record_protection_violation(self.drawer_pos, puncher:get_player_name())
		   return
		end
		local inv = puncher:get_inventory()
		if inv == nil then
			return
		end
		local spaceChecker = ItemStack(self.itemName)
		if add_stack then
			spaceChecker:set_count(spaceChecker:get_stack_max())
		end
		if not inv:room_for_item("main", spaceChecker) then
			return
		end

		local stack
		if add_stack then
			stack = self:take_stack()
		else
			stack = self:take_items(1)
		end

		if stack ~= nil then
			-- add removed stack to player's inventory
			inv:add_item("main", stack)

			-- play the interact sound
			self:play_interact_sound()
		end
	end,

	take_items = function(self, removeCount)
		local meta = core.get_meta(self.drawer_pos)

		if self.count <= 0 then
			return
		end

		if removeCount > self.count then
			removeCount = self.count
		end

		local stack = ItemStack(self.itemName)
		stack:set_count(removeCount)

		-- update the drawer count
		self.count = self.count - removeCount

		self:updateInfotext()
		self:updateTexture()
		self:saveMetaData()

		-- return the stack that was removed from the drawer
		return stack
	end,

	take_stack = function(self)
		return self:take_items(ItemStack(self.itemName):get_stack_max())
	end,

	can_insert_stack = function(self, stack)
		if stack:get_name() == "" or stack:get_count() <= 0 then
			return 0
		end

		-- don't allow unstackable stacks
		if self.itemName == "" and stack:get_stack_max() ~= 1 then
			return stack:get_count()
		end

		if self.itemName ~= stack:get_name() then
			return 0
		end

		if (self.count + stack:get_count()) <= self.maxCount then
			return stack:get_count()
		end
		return self.maxCount - self.count
	end,

	try_insert_stack = function(self, itemstack, insert_all)
		local stackCount = itemstack:get_count()
		local stackName = itemstack:get_name()

		local insertCount = self:can_insert_stack(itemstack)

		if insertCount == 0 then
			return itemstack
		end

		-- only add one, if player holding sneak key
		if not insert_all then
			insertCount = 1
		end

		-- in case the drawer was empty, initialize count, itemName, maxCount
		if self.itemName == "" then
			self.count = 0
			self.itemName = itemstack:get_name()
			self.maxCount = itemstack:get_stack_max() * self.stackMaxFactor
			self.itemStackMax = itemstack:get_stack_max()
		end

		-- update everything
		self.count = self.count + insertCount
		self:updateInfotext()
		self:updateTexture()
		self:saveMetaData()

		-- return leftover
		itemstack:take_item(insertCount)
		if itemstack:get_count() == 0 then
			return ItemStack("")
		end
		return itemstack
	end,

	updateInfotext = function(self)
		local itemDescription = ""
		if core.registered_items[self.itemName] then
			itemDescription = core.registered_items[self.itemName].description
		end

		if self.count <= 0 then
			self.itemName = ""
			self.meta:set_string("name"..self.visualId, self.itemName)
			self.texture = "blank.png"
			itemDescription = S("Empty")
		end

		local infotext = drawers.gen_info_text(itemDescription,
			self.count, self.stackMaxFactor, self.itemStackMax)
		self.meta:set_string("entity_infotext"..self.visualId, infotext)

		self.object:set_properties({
			infotext = infotext .. "\n\n\n\n\n"
		})
	end,

	updateTexture = function(self)
		-- texture
		self.texture = drawers.get_inv_image(self.itemName)

		self.object:set_properties({
			textures = {self.texture}
		})
	end,

	dropStack = function(self, itemStack)
		-- print warning if dropping higher stack counts than allowed
		if itemStack:get_count() > itemStack:get_stack_max() then
			core.log("warning", "[drawers] Dropping item stack with higher count than allowed")
		end
		-- find a position containing air
		local dropPos = core.find_node_near(self.drawer_pos, 1, {"air"}, false)
		-- if no pos found then drop on the top of the drawer
		if not dropPos then
			dropPos = self.object:get_pos()
			dropPos.y = dropPos.y + 1
		end
		-- drop the item stack
		core.item_drop(itemStack, nil, dropPos)
	end,

	dropItemOverload = function(self)
		-- drop stacks until there are no more items than allowed
		while self.count > self.maxCount do
			-- remove the overflow
			local removeCount = self.count - self.maxCount
			-- if this is too much for a single stack, only take the
			-- stack limit
			if removeCount > self.itemStackMax then
				removeCount = self.itemStackMax
			end
			-- remove this count from the drawer
			self.count = self.count - removeCount
			-- create a new item stack having the size of the remove
			-- count
			local stack = ItemStack(self.itemName)
			stack:set_count(removeCount)
			print(stack:to_string())
			-- drop the stack
			self:dropStack(stack)
		end
	end,

	setStackMaxFactor = function(self, stackMaxFactor)
		self.stackMaxFactor = stackMaxFactor
		self.maxCount = self.stackMaxFactor * self.itemStackMax

		-- will drop possible overflowing items
		self:dropItemOverload()
		self:updateInfotext()
		self:saveMetaData()
	end,

	play_interact_sound = function(self)
		core.sound_play("drawers_interact", {
			pos = self.object:get_pos(),
			max_hear_distance = 6,
			gain = 2.0
		})
	end,

	saveMetaData = function(self, meta)
		self.meta:set_int("count"..self.visualId, self.count)
		self.meta:set_string("name"..self.visualId, self.itemName)
		self.meta:set_int("max_count"..self.visualId, self.maxCount)
		self.meta:set_int("base_stack_max"..self.visualId, self.itemStackMax)
		self.meta:set_int("stack_max_factor"..self.visualId, self.stackMaxFactor)
	end
})

core.register_lbm({
	name = "drawers:restore_visual",
	nodenames = {"group:drawer"},
	run_at_every_load = true,
	action  = function(pos, node)
		local meta = core.get_meta(pos)
		-- create drawer upgrade inventory
		meta:get_inventory():set_size("upgrades", 5)
		-- set the formspec
		meta:set_string("formspec", drawers.drawer_formspec)

		-- count the drawer visuals
		local drawerType = core.registered_nodes[node.name].groups.drawer
		local foundVisuals = 0
		local objs = core.get_objects_inside_radius(pos, 0.56)
		if objs then
			for _, obj in pairs(objs) do
				if obj and obj:get_luaentity() and
						obj:get_luaentity().name == "drawers:visual" then
					foundVisuals = foundVisuals + 1
				end
			end
		end
		-- if all drawer visuals were found, return
		if foundVisuals == drawerType then
			return
		end

		-- not enough visuals found, remove existing and create new ones
		drawers.remove_visuals(pos)
		drawers.spawn_visuals(pos)
	end
})


code:
name = drawers
description = Storage Drawers - A Mod adding simple storages for items and blocks, showing the item's inventory image in the front.
optional_depends = default, mcl_core, screwdriver, pipeworks, intllib, moreores, digilines, mesecons_mvps
min_minetest_version = 5.0


code:
#! /bin/bash

# To create a new translation:
#   msginit --locale=ll_CC -o locale/ll_CC.po -i locale/template.pot

cd "$(dirname "${BASH_SOURCE[0]}")/..";

# Extract translatable strings.
xgettext --from-code=UTF-8 \
		--keyword=S \
		--keyword=NS:1,2 \
		--keyword=N_ \
		--add-comments='Translators:' \
		--add-location=file \
		-o locale/template.pot \
		$(find . -name '*.lua')

# Update translations.
find locale -name '*.po' | while read -r file; do
	echo $file
	msgmerge --update $file locale/template.pot;
done


code:
mesecraft_bucket
default
vessels
stairs
hunger?
thirsty?
plantlife?
farming_plus?
crops?
farming?
minetest_doc_modpack?
fruits?
ethereal?


code: This mod adds a juice press and two juice storage containers. Most fruits and several veggies can be juiced. The resulting drinks can be 'ate' to gain health, or if you are using the thirsty mod, to regain hydration.