Small grammar corrections

Small grammar corrections

-- Copyright (C) 2017-2018 DBot

-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at

--     http://www.apache.org/licenses/LICENSE-2.0

-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

-- editor stuffs

gui.ppm2.editor.eyes.separate = 'Getrennte Einstellungen für Augen verwenden'
gui.ppm2.editor.eyes.url = 'Augen URL Textur'
gui.ppm2.editor.eyes.url_desc = 'Bei Verwendung der Augen URL Textur; Die folgenden Optionen haben keine Auswirkung'

gui.ppm2.editor.eyes.lightwarp_desc = 'Lightwarp wirkt nur auf EyeRefract Augen'
gui.ppm2.editor.eyes.lightwarp = "Lightwarp"
gui.ppm2.editor.eyes.desc1 = "Lightwarp textur URL eingabe\nEs muss 256x16 sein!"
gui.ppm2.editor.eyes.desc2 = "Glanzstärke\nDieser Parameter erhöht die Stärke der Echtzeitreflexionen auf das Auge\nUm Änderungen zu sehen, setze ppm2_cl_reflections convar auf 1\nAndere Spieler würden Reflexionen nur sehen, wenn ppm2_cl_reflections auf 1 gesetzt ist\n0 - ist matt; 1 - ist spiegelnd"

for {tprefix, prefix} in *{{'def', ''}, {'left', 'Links '}, {'right', 'Rechts '}}
	gui.ppm2.editor.eyes[tprefix].lightwarp.shader = "#{prefix}EyeRefract Shader verwenden"
	gui.ppm2.editor.eyes[tprefix].lightwarp.cornera = "#{prefix}Eye Cornea diffus verwenden"
	gui.ppm2.editor.eyes[tprefix].lightwarp.glossiness = "#{prefix}Glanz"

	gui.ppm2.editor.eyes[tprefix].type = "#{prefix}Augentyp"
	gui.ppm2.editor.eyes[tprefix].reflection_type = "#{prefix}Augenreflexion typ"
	gui.ppm2.editor.eyes[tprefix].lines = "#{prefix}Augenlinien"
	gui.ppm2.editor.eyes[tprefix].derp = "#{prefix}Derp Augen"
	gui.ppm2.editor.eyes[tprefix].derp_strength = "#{prefix}Derp Augen Stärke"
	gui.ppm2.editor.eyes[tprefix].iris_size = "#{prefix}Augengröße"

	gui.ppm2.editor.eyes[tprefix].points_inside = "#{prefix}Augenlinien innen"
	gui.ppm2.editor.eyes[tprefix].width = "#{prefix}Augenbreite"
	gui.ppm2.editor.eyes[tprefix].height = "#{prefix}Augenhöhe"

	gui.ppm2.editor.eyes[tprefix].pupil.width = "#{prefix}Pupillenbreite"
	gui.ppm2.editor.eyes[tprefix].pupil.height = "#{prefix}Pupillenhöhe"
	gui.ppm2.editor.eyes[tprefix].pupil.size = "#{prefix}Pupillengröße"

	gui.ppm2.editor.eyes[tprefix].pupil.shift_x = "#{prefix}Pupillen Verschiebung X"
	gui.ppm2.editor.eyes[tprefix].pupil.shift_y = "#{prefix}Pupillen Verschiebung Y"
	gui.ppm2.editor.eyes[tprefix].pupil.rotation = "#{prefix}Augenrotation"

	gui.ppm2.editor.eyes[tprefix].background = "#{prefix}Augenhintergrund"
	gui.ppm2.editor.eyes[tprefix].pupil_size = "#{prefix}Pupille"
	gui.ppm2.editor.eyes[tprefix].top_iris = "#{prefix}Obere Augeniris"
	gui.ppm2.editor.eyes[tprefix].bottom_iris = "#{prefix}Untere Augeniris"
	gui.ppm2.editor.eyes[tprefix].line1 = "#{prefix}Augenlinie 1"
	gui.ppm2.editor.eyes[tprefix].line2 = "#{prefix}Augenlinie 2"
	gui.ppm2.editor.eyes[tprefix].reflection = "#{prefix}Augenreflexionseffekt"
	gui.ppm2.editor.eyes[tprefix].effect = "#{prefix}Augeneffekt"

gui.ppm2.editor.generic.title = 'PPM/2 Pony Editor'
gui.ppm2.editor.generic.title_file = '%q - PPM/2 Pony Editor'
gui.ppm2.editor.generic.title_file_unsaved = '%q* - PPM/2 Pony Editor (ungespeicherte Änderungen!)'

gui.ppm2.editor.generic.yes = 'Yas!'
gui.ppm2.editor.generic.no = 'Noh!'
gui.ppm2.editor.generic.ohno = 'Onoh!'
gui.ppm2.editor.generic.okay = 'Okai ;w;'
gui.ppm2.editor.generic.datavalue = '%s\nDatenwert: %q'
gui.ppm2.editor.generic.url = '%s\n\nLink geht zu: %s'
gui.ppm2.editor.generic.url_field = 'URL Feld'
gui.ppm2.editor.generic.spoiler = 'Geheimnisvoller Spoiler'

gui.ppm2.editor.generic.restart.needed = 'Neustart des Editors erforderlich'
gui.ppm2.editor.generic.restart.text = 'Sie sollten den Editor neu starten, um die Änderung zu übernehmen.\nJetzt neu starten?\nNicht gespeicherte Daten gehen verloren!'
gui.ppm2.editor.generic.fullbright = 'Volle Helligkeit'
gui.ppm2.editor.generic.wtf = 'Aus irgendeinem Grund hat Dein Player kein NetworkedPonyData. - Nichts zu bearbeiten!\nVersuche ppm2_reload in Deine Konsole einzugeben und versuche es erneut, den Editor zu öffnen.'

gui.ppm2.editor.io.random.title = 'Randomisieren!'
gui.ppm2.editor.io.newfile.title = 'Neue Datei'
gui.ppm2.editor.io.newfile.confirm = 'Willst Du wirklich eine neue Datei erstellen?'
gui.ppm2.editor.io.newfile.toptext = 'Zurücksetzen'
gui.ppm2.editor.io.delete.confirm = 'Willst Du diese Datei wirklich löschen?\nEs wird für immer weg sein!\n(eine lange Zeit!)'
gui.ppm2.editor.io.delete.title = 'Wirklich löschen?'

gui.ppm2.editor.io.filename = 'Dateiname'
gui.ppm2.editor.io.hint = 'Datei per Doppelklick öffnen'
gui.ppm2.editor.io.reload = 'Dateiliste neu laden'
gui.ppm2.editor.io.failed = 'Import fehlgeschlagen.'

gui.ppm2.editor.io.warn.oldfile = '!!! Es kann funktionieren oder auch nicht. Du wirst zerquetscht werden.'
gui.ppm2.editor.io.warn.text = "Derzeit hast du deine Änderungen nicht angegeben.\nWillst Du wirklich eine andere Datei öffnen?"
gui.ppm2.editor.io.warn.header = 'Nicht gespeicherte Änderungen!'
gui.ppm2.editor.io.save.button = 'Speichern'
gui.ppm2.editor.io.save.text = 'Dateiname ohne ppm2/ und .dat eingeben\nTipp: Um als Autoload zu speichern, schreibe "_current" (ohne ") ein.'
gui.ppm2.editor.io.wear = 'Änderungen übernehmen (Anziehen)'

gui.ppm2.editor.seq.standing = 'Stehen'
gui.ppm2.editor.seq.move = 'Bewegen'
gui.ppm2.editor.seq.walk = 'Gehen'
gui.ppm2.editor.seq.sit = 'Sitzen'
gui.ppm2.editor.seq.swim = 'Schwimmen'
gui.ppm2.editor.seq.run = 'Laufen'
gui.ppm2.editor.seq.duckwalk = 'Ducken gehen'
gui.ppm2.editor.seq.duck = 'Ducken'
gui.ppm2.editor.seq.jump = 'Springen'

gui.ppm2.editor.misc.race = 'Rasse'
gui.ppm2.editor.misc.weight = 'Gewicht'
gui.ppm2.editor.misc.size = 'Pony Größe'
gui.ppm2.editor.misc.hide_weapons = 'Waffen verstecken'
gui.ppm2.editor.misc.chest = 'Männliche Brust'
gui.ppm2.editor.misc.gender = 'Geschlecht'
gui.ppm2.editor.misc.wings = 'Flügel Typ'
gui.ppm2.editor.misc.flexes = 'Flex steuerung'
gui.ppm2.editor.misc.no_flexes2 = 'Kein Flex beim neuen Modell'
gui.ppm2.editor.misc.no_flexes_desc = 'Du kannst jeden Flex Zustandsregler separat deaktivieren.\nSo können diese Flexe mit Addons von Drittanbietern (wie PAC3) modifiziert werden.'

gui.ppm2.editor.misc.hide_pac3 = 'Entitys ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_mane = 'Mähne ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_tail = 'Schweif ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_socks = 'Socken ausblenden wenn PAC3 Entity verwendet wird'

gui.ppm2.editor.tabs.main = 'Allgemeines'
gui.ppm2.editor.tabs.files = 'Dateien'
gui.ppm2.editor.tabs.old_files = 'Alte Dateien'
gui.ppm2.editor.tabs.cutiemark = 'Schönheitsfleck'
gui.ppm2.editor.tabs.head = 'Kopfanatomie'
gui.ppm2.editor.tabs.eyes = 'Augen'
gui.ppm2.editor.tabs.face = 'Gesicht'
gui.ppm2.editor.tabs.mouth = 'Mund'
gui.ppm2.editor.tabs.left_eye = 'Linkes Auge'
gui.ppm2.editor.tabs.right_eye = 'Rechtes Auge'
gui.ppm2.editor.tabs.mane_horn = 'Mähne und Horn'
gui.ppm2.editor.tabs.mane = 'Mähne'
gui.ppm2.editor.tabs.details = 'Details'
gui.ppm2.editor.tabs.url_details = 'URL Details'
gui.ppm2.editor.tabs.url_separated_details = 'URL Getrennte Details'
gui.ppm2.editor.tabs.ears = 'Ohren'
gui.ppm2.editor.tabs.horn = 'Horn'
gui.ppm2.editor.tabs.back = 'Rücken'
gui.ppm2.editor.tabs.wings = 'Flügel'
gui.ppm2.editor.tabs.left = 'Links'
gui.ppm2.editor.tabs.right = 'Rechts'
gui.ppm2.editor.tabs.neck = 'Hals'
gui.ppm2.editor.tabs.body = 'Pony Körper'
gui.ppm2.editor.tabs.tattoos = 'Tätowierungen'
gui.ppm2.editor.tabs.tail = 'Schweif'
gui.ppm2.editor.tabs.hooves = 'Huf Anatomie'
gui.ppm2.editor.tabs.bottom_hoof = 'Huf unten'
gui.ppm2.editor.tabs.legs = 'Beine'
gui.ppm2.editor.tabs.socks = 'Socken'
gui.ppm2.editor.tabs.newsocks = 'Neue Socken'
gui.ppm2.editor.tabs.about = 'Über PPM/2'

gui.ppm2.editor.old_tabs.mane_tail = 'Mähne und Schweif'
gui.ppm2.editor.old_tabs.wings_and_horn_details = 'Flügel und Horn Details'
gui.ppm2.editor.old_tabs.wings_and_horn = 'Flügel und Horn'
gui.ppm2.editor.old_tabs.body_details = 'Körper Details'
gui.ppm2.editor.old_tabs.mane_tail_detals = 'Mähne und Schweif URL Details'

gui.ppm2.editor.cutiemark.display = 'Schönheitsfleck anzeigen'
gui.ppm2.editor.cutiemark.type = 'Schönheitsfleck typ'
gui.ppm2.editor.cutiemark.size = 'Schönheitsfleck größe'
gui.ppm2.editor.cutiemark.color = 'Schönheitsfleck Farbe'
gui.ppm2.editor.cutiemark.input = 'Schönheitsfleck URL bild Eingabefeld\nSollte PNG oder JPEG sein (funktioniert wie bei\nPAC3 URL texture)'

gui.ppm2.editor.face.eyelashes = 'Wimpern'
gui.ppm2.editor.face.eyelashes_color = 'Wimpern Farbe'
gui.ppm2.editor.face.eyebrows_color = 'Augenbrauen Farbe'
gui.ppm2.editor.face.new_muzzle = 'Neue Schnauze für das männliche Modell nutzen'

gui.ppm2.editor.face.nose = 'Nasenfarbe'
gui.ppm2.editor.face.lips = 'Lippen Farbe'
gui.ppm2.editor.face.eyelashes_separate_phong = 'Separate Wimpern Phong'
gui.ppm2.editor.face.eyebrows_glow = 'Leuchtende Augenbrauen'
gui.ppm2.editor.face.eyebrows_glow_strength = 'Augenbrauen leuchten Stärke'
gui.ppm2.editor.face.inherit.lips = 'Erbe Lippen Farbe vom Körper'
gui.ppm2.editor.face.inherit.nose = 'Erbe Nasen Farbe vom Körper'

gui.ppm2.editor.mouth.fangs = 'Fangzähne'
gui.ppm2.editor.mouth.alt_fangs = 'Alternative Fangzähne'
gui.ppm2.editor.mouth.claw = 'Klauenzähne'

gui.ppm2.editor.mouth.teeth = 'Zahnfarbe'
gui.ppm2.editor.mouth.mouth = 'Mundfarbe'
gui.ppm2.editor.mouth.tongue = 'Zungenfarbe'

gui.ppm2.editor.mane.type = 'Mähne Typ'
gui.ppm2.editor.mane.phong = 'Trenne Mähne phong Einstellungen vom Körper'
gui.ppm2.editor.mane.phong_sep = 'Trenne obere und untere Mähnenfarbe'
gui.ppm2.editor.mane.up.phong = 'Obere Mähne Phong Einstellungen'
gui.ppm2.editor.mane.down.phong = 'Untere Mähne Phong Einstellungen'
gui.ppm2.editor.mane.newnotice = 'Die nächsten Optionen wirken sich nur auf das neue Modell aus'

for i = 1, 2
	gui.ppm2.editor.mane['color' .. i] = "Mähnefarbe #{i}"
	gui.ppm2.editor.mane.up['color' .. i] = "Obere Mähnefarbe #{i}"
	gui.ppm2.editor.mane.down['color' .. i] = "Untere Mähnefarbe #{i}"

for i = 1, 6
	gui.ppm2.editor.mane['detail_color' .. i] = "Mähne Detail Farbe #{i}"
	gui.ppm2.editor.mane.up['detail_color' .. i] = "Obere Mähnefarbe #{i}"
	gui.ppm2.editor.mane.down['detail_color' .. i] = "Untere Mähnefarbe #{i}"

	gui.ppm2.editor.url_mane['desc' .. i] = "Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane['color' .. i] = "Mähne URL Detail Farbe #{i}"

	gui.ppm2.editor.url_mane.sep.up['desc' .. i] = "Obere Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane.sep.up['color' .. i] = "Obere Mähne URL Detail Farbe #{i}"

	gui.ppm2.editor.url_mane.sep.down['desc' .. i] = "Untere Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane.sep.down['color' .. i] = "Untere Mähne URL Detail Farbe #{i}"

gui.ppm2.editor.ears.bat = 'Bat pony Ohren'
gui.ppm2.editor.ears.size = 'Ohrengröße'

gui.ppm2.editor.horn.detail_color = 'Horn Detailfarbe'
gui.ppm2.editor.horn.glowing_detail = 'Horn leuchten Detail'
gui.ppm2.editor.horn.glow_strength = 'Horn leuchten Stärke'
gui.ppm2.editor.horn.separate_color = 'Trenne Horn Farbe vom Körper'
gui.ppm2.editor.horn.color = 'Horn Farbe'
gui.ppm2.editor.horn.magic = 'Horn magie Farbe'
gui.ppm2.editor.horn.separate_magic_color = 'Trenne magie Farbe vom Augen Farbe'
gui.ppm2.editor.horn.separate = 'Trenne Horn Farbe vom Körper'
gui.ppm2.editor.horn.separate_phong = 'Trenne Horn phong Einstellungen vom Körper'

for i = 1, 3
	gui.ppm2.editor.horn.detail['desc' .. i] = "Horn URL Detail #{i}"
	gui.ppm2.editor.horn.detail['color' .. i] = "URL Detail Farbe #{i}"

gui.ppm2.editor.wings.separate_color = 'Trenne Flügel Farbe vom Körper'
gui.ppm2.editor.wings.color = 'Flügel Farbe'
gui.ppm2.editor.wings.separate = 'Trenne Flügel Farbe vom Körper'
gui.ppm2.editor.wings.separate_phong = 'Trenne Flügel phong Einstellungen vom Körper'
gui.ppm2.editor.wings.bat_color = 'Bat Flügel Farbe'
gui.ppm2.editor.wings.bat_skin_color = 'Bat Flügel Haut Farbe'

gui.ppm2.editor.wings.normal = 'Normale Flügel'
gui.ppm2.editor.wings.bat = 'Bat Flügel'
gui.ppm2.editor.wings.bat_skin = 'Bat Flügel Haut'

gui.ppm2.editor.wings.left.size = 'Linker Flügel Größe'
gui.ppm2.editor.wings.left.fwd = 'Linker Flügel Vorwärts'
gui.ppm2.editor.wings.left.up = 'Linker Flügel Hoch'
gui.ppm2.editor.wings.left.inside = 'Linker Flügel Innen'

gui.ppm2.editor.wings.right.size = 'Rechter Flügel Größe'
gui.ppm2.editor.wings.right.fwd = 'Rechter Flügel Vorwärts'
gui.ppm2.editor.wings.right.up = 'Rechter Flügel Hoch'
gui.ppm2.editor.wings.right.inside = 'Rechter Flügel Innen'

for i = 1, 3
	gui.ppm2.editor.wings.details.def['detail' .. i] = "Flügel URL Detail #{i}"
	gui.ppm2.editor.wings.details.def['color' .. i] = "URL Detail Farbe #{i}"
	gui.ppm2.editor.wings.details.bat['detail' .. i] = "Bat Flügel URL Detail #{i}"
	gui.ppm2.editor.wings.details.bat['color' .. i] = "Bat Flügel URL Detail Farbe #{i}"
	gui.ppm2.editor.wings.details.batskin['detail' .. i] = "Bat Flügel Haut URL Detail #{i}"
	gui.ppm2.editor.wings.details.batskin['color' .. i] = "Bat Flügel Haut URL Detail Farbe #{i}"

gui.ppm2.editor.neck.height = 'Halshöhe'

gui.ppm2.editor.body.suit = 'Körperanzug'
gui.ppm2.editor.body.color = 'Korper Farbe'
gui.ppm2.editor.body.spine_length = 'Rückenlänge'
gui.ppm2.editor.body.url_desc = 'Körper Detail URL Bild Eingabefeld\nSollte PNG oder JPEG sein (funktioniert wie bei\nPAC3 URL textur)'

for i = 1, PPM2.MAX_BODY_DETAILS
	gui.ppm2.editor.body.detail['desc' .. i] = "Detail #{i}"
	gui.ppm2.editor.body.detail['color' .. i] = "Detail Farbe #{i}"
	gui.ppm2.editor.body.detail['glow' .. i] = "Detail #{i} leuchtet"
	gui.ppm2.editor.body.detail['glow_strength' .. i] = "Detail #{i} leuchtkraft"

	gui.ppm2.editor.body.detail.url['desc' .. i] = "Detail #{i}"
	gui.ppm2.editor.body.detail.url['color' .. i] = "Detail Farbe #{i}"

gui.ppm2.editor.tattoo.edit_keyboard = 'Bearbeiten mit der Tastatur'
gui.ppm2.editor.tattoo.type = 'Typ'
gui.ppm2.editor.tattoo.over = 'Tattoo über Körperdetails'
gui.ppm2.editor.tattoo.glow = 'Tattoo leuchtet'
gui.ppm2.editor.tattoo.glow_strength = 'Tattoo leuchtkraft'
gui.ppm2.editor.tattoo.color = 'Farbe der Tätowierung'

gui.ppm2.editor.tattoo.tweak.rotate = 'Rotation'
gui.ppm2.editor.tattoo.tweak.x = 'X Position'
gui.ppm2.editor.tattoo.tweak.y = 'Y Position'
gui.ppm2.editor.tattoo.tweak.width = 'Breite Skalierung'
gui.ppm2.editor.tattoo.tweak.height = 'Länge Skalierung'

for i = 1, PPM2.MAX_TATTOOS
	gui.ppm2.editor.tattoo['layer' .. i] = "Tattooschicht #{i}"

gui.ppm2.editor.tail.type = 'Schweif typ'
gui.ppm2.editor.tail.size = 'Schweif größe'
gui.ppm2.editor.tail.separate = 'Trenne Schweif phong Einstellungen vom Körper'

for i = 1, 2
	gui.ppm2.editor.tail['color' .. i] = 'Schweif Farbe ' .. i

for i = 1, 6
	gui.ppm2.editor.tail['detail' .. i] = "Schweif Detail Farbe #{i}"
	gui.ppm2.editor.tail.url['detail' .. i] = "Schweif URL Detail #{i}"
	gui.ppm2.editor.tail.url['color' .. i] = "Schweif URL Detail #{i}"

gui.ppm2.editor.hoof.fluffers = 'Huf flausch'

gui.ppm2.editor.legs.height = 'Bein höhe'
gui.ppm2.editor.legs.socks.simple = 'Socken (einfache Textur)'
gui.ppm2.editor.legs.socks.model = 'Socken (als Modell)'
gui.ppm2.editor.legs.socks.color = 'Socken Modell Farbe'
gui.ppm2.editor.legs.socks.texture = 'Socken Textur'
gui.ppm2.editor.legs.socks.url_texture = 'Socken URL textur'

for i = 1, 6
	gui.ppm2.editor.legs.socks['color' .. i] = 'Socken Detail Farbe ' .. i

gui.ppm2.editor.legs.newsocks.model = 'Socken (als neues Modell)'

for i = 1, 3
	gui.ppm2.editor.legs.newsocks['color' .. i] = 'Neue Socken Farbe ' .. i

gui.ppm2.editor.legs.newsocks.url = 'Neue Socken URL textur'

-- shared editor stuffs

gui.ppm2.editor.tattoo.help = "Um den Bearbeitungsmodus zu verlassen, drücke Escape oder klicke irgendwo mit der Maus
Um das Tattoo zu bewegen, verwende WASD
Um höher/niedriger zu skalieren, verwende die Hoch/Runter Pfeiltasten
Um breiter/kleiner zu skalieren, verwende die Rechts/Links Pfeiltasten
Um links/rechts zu Drehen, verwende die Q/E Tasten"

for name, data in pairs PPM2.PonyDataRegistry
	gui.ppm2.editor.reset[data.getFunc\lower()] = 'Zurücksetzen ' .. data.getFunc

gui.ppm2.editor.phong.info = 'Mehr Infos über Phong im Wiki'
gui.ppm2.editor.phong.exponent = 'Phong Exponent - wie stark die reflektierende Eigenschaft\nvon der Ponyhaut ist\nSetze den Wert nahe Null, um dem Roboterlook zu bekommen\nPonyhaut'
gui.ppm2.editor.phong.exponent_text = 'Phong Exponent'
gui.ppm2.editor.phong.boost.title = 'Phong Verstärkung - multipliziert spiegelnde Kartenreflexionen'
gui.ppm2.editor.phong.boost.boost = 'Phong Verstärkung'
gui.ppm2.editor.phong.tint.title = 'Tönung Farbe - welche Farben reflektieren Spiegelkarte\nWeiß - Reflektiert alle Farben\n(Im weißen Raum - weiße Spiegelkarte)'
gui.ppm2.editor.phong.tint.tint = 'Tönung Farbe - welche Farben reflektieren Spiegelkarte\nWeiß - Reflektiert alle Farben\n(Im weißen Raum - weiße Spiegelkarte)'
gui.ppm2.editor.phong.frensel.front.title = 'Phong Front - Fresnel 0 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.front.front = 'Phong Front'
gui.ppm2.editor.phong.frensel.middle.title = 'Phong Mitte - Fresnel 45 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.middle.front = 'Phong Mitte'
gui.ppm2.editor.phong.frensel.sliding.title = 'Phong Gleitend - Fresnel 45 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.sliding.front = 'Phong Gleitend'
gui.ppm2.editor.phong.lightwarp = 'Lightwarp'
gui.ppm2.editor.phong.url_lightwarp = 'Lightwarp textur URL eingabe\nEs muss 256x16 sein!'
gui.ppm2.editor.phong.bumpmap = 'Bumpmap URL eingabe'

gui.ppm2.editor.info.discord = "Trete DBot's Discord bei!"
gui.ppm2.editor.info.ponyscape = "PPM/2 ist ein Ponyscape projekt"
gui.ppm2.editor.info.creator = "PPM/2 wurde von DBot erstellt und entwickelt"
gui.ppm2.editor.info.newmodels = "Neue Modelle wurden von Durpy erstellt"
gui.ppm2.editor.info.cppmmodels = "CPPM Modelle (einschließlich Ponyhände) gehören zu UnkN', 'http://steamcommunity.com/profiles/76561198084938735"
gui.ppm2.editor.info.oldmodels = "Alte Modelle gehören zu Scentus und den anderen."
gui.ppm2.editor.info.bugs = "Einen Fehler gefunden? Melde es hier!"
gui.ppm2.editor.info.sources = "Quellen findest du hier"
gui.ppm2.editor.info.githubsources = "Oder im GitHub mirror"
gui.ppm2.editor.info.thanks = "Besonderer Dank geht an alle, die kritisiert,\ngeholfen und PPM/2 getestet haben!"

-- other stuff

info.ppm2.fly.pegasus = 'Du musst eine Pegasus oder ein Alicorn sein, um zu fliegen!'
info.ppm2.fly.cannot = 'Du kannst nicht %s im Augenblick.'

gui.ppm2.emotes.sad = 'Traurig'
gui.ppm2.emotes.wild = 'Wild'
gui.ppm2.emotes.grin = 'Grinsen'
gui.ppm2.emotes.angry = 'Wütend'
gui.ppm2.emotes.tongue = ':P'
gui.ppm2.emotes.angrytongue = '>:P'
gui.ppm2.emotes.pff = 'Pffff!'
gui.ppm2.emotes.kitty = ':3'
gui.ppm2.emotes.owo = 'oWo'
gui.ppm2.emotes.ugh = 'Uuugh'
gui.ppm2.emotes.lips = 'Lippen lecken'
gui.ppm2.emotes.scrunch = 'Scrunch'
gui.ppm2.emotes.sorry = 'Entschuldigend'
gui.ppm2.emotes.wink = 'Zwinkern'
gui.ppm2.emotes.right_wink = 'Rechts Zwinkern'
gui.ppm2.emotes.licking = 'Lecken'
gui.ppm2.emotes.suggestive_lips = 'Anzüglich Lippen Lecken'
gui.ppm2.emotes.suggestive_no_tongue = 'Anzüglich ohne Zunge'
gui.ppm2.emotes.gulp = 'Schlucken'
gui.ppm2.emotes.blah = 'Blah blah blah'
gui.ppm2.emotes.happi = 'Glücklich'
gui.ppm2.emotes.happi_grin = 'Glückliches Grinsen'
gui.ppm2.emotes.duck = 'ENTE'
gui.ppm2.emotes.ducks = 'ENTEN WAHNSINN'
gui.ppm2.emotes.quack = 'QUACK'
gui.ppm2.emotes.suggestive = 'Anzüglich mit Zunge'

message.ppm2.emotes.invalid = 'Keine Emotion mit ID: %s'

gui.ppm2.editor.intro.text = "Grüße meinen neuen.... Robochirurgen für Ponys! Es erlaubt Dir....\n" ..
	"hmmm... ein Pony zu werden, und ja, dieser Prozess ist UNUMKEHRBAR! Aber mach dir keine Sorgen,\n" ..
	"Du verlierst keine deiner Gehirnzellen, in und nach der Operation, weil wir die Operation sehr schonend durchführen...\n\n" ..
	"Eigentlich habe ich keine Ahnung, du biologisches Wesen! Seine mechanischen Hände werden dich in die engsten Umarmungen hüllen, die du je bekommen hast!\n" ..
	"Und, bitte, sterbe nicht in diesen Prozess, denn dies würde dazu führen DAS ERLÖSCHEN DEINER LEBENSLANGEN GARANTIE... und Du wirst kein Pony sein!\n" ..
	"----\n\n\n" ..
	"Vorsicht: Nehme den Robochirurgen nicht auseinander.\nLege deine Hände/Hufe nicht in bewegliche Teile des Robochirurgen.\n" ..
	"Schalte es während des Betriebs nicht aus..\nVersuche nicht sich seinen Taten zu widersetzen.\n" ..
	"Sei immer sanft mit den Robochirurgen.\n" ..
	"Schlage den Robochirurgen nie auf sein Interface.\n" ..
	"DBot's DLibCo übernimmt keine Verantwortung für Schäden, die durch falsche Verwendung von Robochirurgen entstehen.\n" ..
	"Die Garantie erlischt, wenn der Benutzer stirbt.\n" ..
	"Keine Rückerstattung."
gui.ppm2.editor.intro.title = 'Willkommen hier, Mensch!'
gui.ppm2.editor.intro.okay = "Ok, ich werde diese Lizenz sowieso nie lesen."

-- Copyright (C) 2017-2018 DBot

-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at

--     http://www.apache.org/licenses/LICENSE-2.0

-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

-- editor stuffs

gui.ppm2.editor.eyes.separate = 'Getrennte Einstellungen für Augen verwenden'
gui.ppm2.editor.eyes.url = 'Augen URL Textur'
gui.ppm2.editor.eyes.url_desc = 'Bei Verwendung der Augen URL Textur; Die folgenden Optionen haben keine Auswirkung'

gui.ppm2.editor.eyes.lightwarp_desc = 'Lightwarp wirkt nur auf EyeRefract Augen'
gui.ppm2.editor.eyes.lightwarp = "Lightwarp"
gui.ppm2.editor.eyes.desc1 = "Lightwarp textur URL eingabe\nEs muss 256x16 sein!"
gui.ppm2.editor.eyes.desc2 = "Glanzstärke\nDieser Parameter erhöht die Stärke der Echtzeitreflexionen auf das Auge\nUm Änderungen zu sehen, setze ppm2_cl_reflections convar auf 1\nAndere Spieler würden Reflexionen nur sehen, wenn ppm2_cl_reflections auf 1 gesetzt ist\n0 - ist matt; 1 - ist spiegelnd"

for {tprefix, prefix} in *{{'def', ''}, {'left', 'Links '}, {'right', 'Rechts '}}
	gui.ppm2.editor.eyes[tprefix].lightwarp.shader = "#{prefix}EyeRefract Shader verwenden"
	gui.ppm2.editor.eyes[tprefix].lightwarp.cornera = "#{prefix}Eye Cornea diffus verwenden"
	gui.ppm2.editor.eyes[tprefix].lightwarp.glossiness = "#{prefix}Glanz"

	gui.ppm2.editor.eyes[tprefix].type = "#{prefix}Augentyp"
	gui.ppm2.editor.eyes[tprefix].reflection_type = "#{prefix}Augenreflexion typ"
	gui.ppm2.editor.eyes[tprefix].lines = "#{prefix}Augenlinien"
	gui.ppm2.editor.eyes[tprefix].derp = "#{prefix}Derp Augen"
	gui.ppm2.editor.eyes[tprefix].derp_strength = "#{prefix}Derp Augen Stärke"
	gui.ppm2.editor.eyes[tprefix].iris_size = "#{prefix}Augengröße"

	gui.ppm2.editor.eyes[tprefix].points_inside = "#{prefix}Augenlinien innen"
	gui.ppm2.editor.eyes[tprefix].width = "#{prefix}Augenbreite"
	gui.ppm2.editor.eyes[tprefix].height = "#{prefix}Augenhöhe"

	gui.ppm2.editor.eyes[tprefix].pupil.width = "#{prefix}Pupillenbreite"
	gui.ppm2.editor.eyes[tprefix].pupil.height = "#{prefix}Pupillenhöhe"
	gui.ppm2.editor.eyes[tprefix].pupil.size = "#{prefix}Pupillengröße"

	gui.ppm2.editor.eyes[tprefix].pupil.shift_x = "#{prefix}Pupillen Verschiebung X"
	gui.ppm2.editor.eyes[tprefix].pupil.shift_y = "#{prefix}Pupillen Verschiebung Y"
	gui.ppm2.editor.eyes[tprefix].pupil.rotation = "#{prefix}Augenrotation"

	gui.ppm2.editor.eyes[tprefix].background = "#{prefix}Augenhintergrund"
	gui.ppm2.editor.eyes[tprefix].pupil_size = "#{prefix}Pupille"
	gui.ppm2.editor.eyes[tprefix].top_iris = "#{prefix}Obere Augeniris"
	gui.ppm2.editor.eyes[tprefix].bottom_iris = "#{prefix}Untere Augeniris"
	gui.ppm2.editor.eyes[tprefix].line1 = "#{prefix}Augenlinie 1"
	gui.ppm2.editor.eyes[tprefix].line2 = "#{prefix}Augenlinie 2"
	gui.ppm2.editor.eyes[tprefix].reflection = "#{prefix}Augenreflexionseffekt"
	gui.ppm2.editor.eyes[tprefix].effect = "#{prefix}Augeneffekt"

gui.ppm2.editor.generic.title = 'PPM/2 Pony Editor'
gui.ppm2.editor.generic.title_file = '%q - PPM/2 Pony Editor'
gui.ppm2.editor.generic.title_file_unsaved = '%q* - PPM/2 Pony Editor (ungespeicherte Änderungen!)'

gui.ppm2.editor.generic.yes = 'Yas!'
gui.ppm2.editor.generic.no = 'Noh!'
gui.ppm2.editor.generic.ohno = 'Onoh!'
gui.ppm2.editor.generic.okay = 'Okai ;w;'
gui.ppm2.editor.generic.datavalue = '%s\nDatenwert: %q'
gui.ppm2.editor.generic.url = '%s\n\nLink geht zu: %s'
gui.ppm2.editor.generic.url_field = 'URL Feld'
gui.ppm2.editor.generic.spoiler = 'Geheimnisvoller Spoiler'

gui.ppm2.editor.generic.restart.needed = 'Neustart des Editors erforderlich'
gui.ppm2.editor.generic.restart.text = 'Sie sollten den Editor neu starten, um die Änderung zu übernehmen.\nJetzt neu starten?\nNicht gespeicherte Daten gehen verloren!'
gui.ppm2.editor.generic.fullbright = 'Volle Helligkeit'
gui.ppm2.editor.generic.wtf = 'Aus irgendeinem Grund hat Dein Player kein NetworkedPonyData. - Nichts zu bearbeiten!\nVersuche ppm2_reload in Deine Konsole einzugeben und versuche es erneut, den Editor zu öffnen.'

gui.ppm2.editor.io.random.title = 'Randomisieren!'
gui.ppm2.editor.io.newfile.title = 'Neue Datei'
gui.ppm2.editor.io.newfile.confirm = 'Willst Du wirklich eine neue Datei erstellen?'
gui.ppm2.editor.io.newfile.toptext = 'Zurücksetzen'
gui.ppm2.editor.io.delete.confirm = 'Willst Du Datei wirklich löschen?\nEs wird für immer weg sein.!\n(eine lange Zeit!)'
gui.ppm2.editor.io.delete.title = 'Wirklich löschen?'

gui.ppm2.editor.io.filename = 'Dateiname'
gui.ppm2.editor.io.hint = 'Datei per Doppelklick öffnen'
gui.ppm2.editor.io.reload = 'Dateiliste neu laden'
gui.ppm2.editor.io.failed = 'Import fehlgeschlagen.'

gui.ppm2.editor.io.warn.oldfile = '!!! Es kann funktionieren oder auch nicht. Du wirst zerquetscht werden.'
gui.ppm2.editor.io.warn.text = "Derzeit hast du deine Änderungen nicht angegeben.\nWillst Du wirklich eine andere Datei öffnen?"
gui.ppm2.editor.io.warn.header = 'Nicht gespeicherte Änderungen!'
gui.ppm2.editor.io.save.button = 'Speichern'
gui.ppm2.editor.io.save.text = 'Dateiname ohne ppm2/ und .dat eingeben\nTipp: Um als Autoload zu speichern, schreibe "_current" (ohne ") ein.'
gui.ppm2.editor.io.wear = 'Änderungen übernehmen (Anziehen)'

gui.ppm2.editor.seq.standing = 'Stehen'
gui.ppm2.editor.seq.move = 'Bewegend'
gui.ppm2.editor.seq.walk = 'Gehen'
gui.ppm2.editor.seq.sit = 'Sitzen'
gui.ppm2.editor.seq.swim = 'Schwimmen'
gui.ppm2.editor.seq.run = 'Laufen'
gui.ppm2.editor.seq.duckwalk = 'Ducken gehen'
gui.ppm2.editor.seq.duck = 'Ducken'
gui.ppm2.editor.seq.jump = 'Springen'

gui.ppm2.editor.misc.race = 'Rasse'
gui.ppm2.editor.misc.weight = 'Gewicht'
gui.ppm2.editor.misc.size = 'Pony Größe'
gui.ppm2.editor.misc.hide_weapons = 'Waffen verstecken'
gui.ppm2.editor.misc.chest = 'Männliche Brust'
gui.ppm2.editor.misc.gender = 'Geschlecht'
gui.ppm2.editor.misc.wings = 'Flügel Typ'
gui.ppm2.editor.misc.flexes = 'Flex steuerung'
gui.ppm2.editor.misc.no_flexes2 = 'Kein Flex beim neuen Modell'
gui.ppm2.editor.misc.no_flexes_desc = 'Du kannst jeden Flex Zustandsregler separat deaktivieren.\nSo können diese Flexe mit Addons von Drittanbietern (wie PAC3) modifiziert werden.'

gui.ppm2.editor.misc.hide_pac3 = 'Entitys ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_mane = 'Mähne ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_tail = 'Schweif ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_socks = 'Socken ausblenden wenn PAC3 Entity verwendet wird'

gui.ppm2.editor.tabs.main = 'Allgemeines'
gui.ppm2.editor.tabs.files = 'Dateien'
gui.ppm2.editor.tabs.old_files = 'Alte Dateien'
gui.ppm2.editor.tabs.cutiemark = 'Schönheitsfleck'
gui.ppm2.editor.tabs.head = 'Kopfanatomie'
gui.ppm2.editor.tabs.eyes = 'Augen'
gui.ppm2.editor.tabs.face = 'Gesicht'
gui.ppm2.editor.tabs.mouth = 'Mund'
gui.ppm2.editor.tabs.left_eye = 'Linkes Auge'
gui.ppm2.editor.tabs.right_eye = 'Rechtes Auge'
gui.ppm2.editor.tabs.mane_horn = 'Mähne und Horn'
gui.ppm2.editor.tabs.mane = 'Mähne'
gui.ppm2.editor.tabs.details = 'Details'
gui.ppm2.editor.tabs.url_details = 'URL Details'
gui.ppm2.editor.tabs.url_separated_details = 'URL Getrennte Details'
gui.ppm2.editor.tabs.ears = 'Ohren'
gui.ppm2.editor.tabs.horn = 'Ohren'
gui.ppm2.editor.tabs.back = 'Rücken'
gui.ppm2.editor.tabs.wings = 'Flügel'
gui.ppm2.editor.tabs.left = 'Links'
gui.ppm2.editor.tabs.right = 'Rechts'
gui.ppm2.editor.tabs.neck = 'Hals'
gui.ppm2.editor.tabs.body = 'Pony Körper'
gui.ppm2.editor.tabs.tattoos = 'Tätowierungen'
gui.ppm2.editor.tabs.tail = 'Schweif'
gui.ppm2.editor.tabs.hooves = 'Huf Anatomie'
gui.ppm2.editor.tabs.bottom_hoof = 'Huf unten'
gui.ppm2.editor.tabs.legs = 'Beine'
gui.ppm2.editor.tabs.socks = 'Socken'
gui.ppm2.editor.tabs.newsocks = 'Neue Socken'
gui.ppm2.editor.tabs.about = 'Über PPM/2'

gui.ppm2.editor.old_tabs.mane_tail = 'Mähne und Schweif'
gui.ppm2.editor.old_tabs.wings_and_horn_details = 'Flügel und Horn details'
gui.ppm2.editor.old_tabs.wings_and_horn = 'Flügel und Horn'
gui.ppm2.editor.old_tabs.body_details = 'Körper details'
gui.ppm2.editor.old_tabs.mane_tail_detals = 'Mähne und Schweif URL details'

gui.ppm2.editor.cutiemark.display = 'Schönheitsfleck anzeigen'
gui.ppm2.editor.cutiemark.type = 'Schönheitsfleck typ'
gui.ppm2.editor.cutiemark.size = 'Schönheitsfleck größe'
gui.ppm2.editor.cutiemark.color = 'Schönheitsfleck farbe'
gui.ppm2.editor.cutiemark.input = 'Schönheitsfleck URL bild Eingabefeld\nSollte PNG oder JPEG sein (funktioniert wie bei\nPAC3 URL texture)'

gui.ppm2.editor.face.eyelashes = 'Wimpern'
gui.ppm2.editor.face.eyelashes_color = 'Wimpern Farbe'
gui.ppm2.editor.face.eyebrows_color = 'Augenbrauen Farbe'
gui.ppm2.editor.face.new_muzzle = 'Neue Schnauze für das männliche Modell nutzen'

gui.ppm2.editor.face.nose = 'Nasenfarbe'
gui.ppm2.editor.face.lips = 'Lippen Farbe'
gui.ppm2.editor.face.eyelashes_separate_phong = 'Separate Wimpern Phong'
gui.ppm2.editor.face.eyebrows_glow = 'Leuchtende Augenbrauen'
gui.ppm2.editor.face.eyebrows_glow_strength = 'Augenbrauen leuchten Stärke'
gui.ppm2.editor.face.inherit.lips = 'Erbe Lippen Farbe vom Körper'
gui.ppm2.editor.face.inherit.nose = 'Erbe Nasen Farbe vom Körper'

gui.ppm2.editor.mouth.fangs = 'Fangzähne'
gui.ppm2.editor.mouth.alt_fangs = 'Alternative Fangzähne'
gui.ppm2.editor.mouth.claw = 'Klauenzähne'

gui.ppm2.editor.mouth.teeth = 'Zahnfarbe'
gui.ppm2.editor.mouth.mouth = 'Mundfarbe'
gui.ppm2.editor.mouth.tongue = 'Zungenfarbe'

gui.ppm2.editor.mane.type = 'Mähne Typ'
gui.ppm2.editor.mane.phong = 'Trenne Mähne phong Einstellungen vom Körper'
gui.ppm2.editor.mane.phong_sep = 'Trenne obere und untere Mähnenfarbe'
gui.ppm2.editor.mane.up.phong = 'Obere Mähne Phong Einstellungen'
gui.ppm2.editor.mane.down.phong = 'Untere Mähne Phong Einstellungen'
gui.ppm2.editor.mane.newnotice = 'Die nächsten Optionen wirken sich nur auf das neue Modell aus'

for i = 1, 2
	gui.ppm2.editor.mane['color' .. i] = "Mähnefarbe #{i}"
	gui.ppm2.editor.mane.up['color' .. i] = "Obere Mähnefarbe #{i}"
	gui.ppm2.editor.mane.down['color' .. i] = "Untere Mähnefarbe #{i}"

for i = 1, 6
	gui.ppm2.editor.mane['detail_color' .. i] = "Mähne Detail Farbe #{i}"
	gui.ppm2.editor.mane.up['detail_color' .. i] = "Obere Mähnefarbe #{i}"
	gui.ppm2.editor.mane.down['detail_color' .. i] = "Untere Mähnefarbe #{i}"

	gui.ppm2.editor.url_mane['desc' .. i] = "Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane['color' .. i] = "Mähne URL detail farbe #{i}"

	gui.ppm2.editor.url_mane.sep.up['desc' .. i] = "Obere Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane.sep.up['color' .. i] = "Obere Mähne URL detail farbe #{i}"

	gui.ppm2.editor.url_mane.sep.down['desc' .. i] = "Untere Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane.sep.down['color' .. i] = "Untere Mähne URL detail farbe #{i}"

gui.ppm2.editor.ears.bat = 'Bat pony Ohren'
gui.ppm2.editor.ears.size = 'Ohrengröße'

gui.ppm2.editor.horn.detail_color = 'Horn Detailfarbe'
gui.ppm2.editor.horn.glowing_detail = 'Horn leuchten Detail'
gui.ppm2.editor.horn.glow_strength = 'Horn leuchten Stärke'
gui.ppm2.editor.horn.separate_color = 'Trenne Horn farbe vom Körper'
gui.ppm2.editor.horn.color = 'Horn farbe'
gui.ppm2.editor.horn.magic = 'Horn magie farbe'
gui.ppm2.editor.horn.separate_magic_color = 'Trenne magie farbe vom Augen farbe'
gui.ppm2.editor.horn.separate = 'Trenne Horn farbe vom Körper'
gui.ppm2.editor.horn.separate_phong = 'Trenne Horn phong Einstellungen vom Körper'

for i = 1, 3
	gui.ppm2.editor.horn.detail['desc' .. i] = "Horn URL detail #{i}"
	gui.ppm2.editor.horn.detail['color' .. i] = "URL Detail farbe #{i}"

gui.ppm2.editor.wings.separate_color = 'Trenne Flügel farbe vom Körper'
gui.ppm2.editor.wings.color = 'Flügel farbe'
gui.ppm2.editor.wings.separate = 'Trenne Flügel farbe vom Körper'
gui.ppm2.editor.wings.separate_phong = 'Trenne Flügel phong Einstellungen vom Körper'
gui.ppm2.editor.wings.bat_color = 'Bat Flügel farbe'
gui.ppm2.editor.wings.bat_skin_color = 'Bat Flügel Haut farbe'

gui.ppm2.editor.wings.normal = 'Normale Flügel'
gui.ppm2.editor.wings.bat = 'Bat Flügel'
gui.ppm2.editor.wings.bat_skin = 'Bat Flügel Haut'

gui.ppm2.editor.wings.left.size = 'Linker Flügel Größe'
gui.ppm2.editor.wings.left.fwd = 'Linker Flügel Vorwärts'
gui.ppm2.editor.wings.left.up = 'Linker Flügel Hoch'
gui.ppm2.editor.wings.left.inside = 'Linker Flügel Innen'

gui.ppm2.editor.wings.right.size = 'Rechter Flügel Größe'
gui.ppm2.editor.wings.right.fwd = 'Rechter Flügel Vorwärts'
gui.ppm2.editor.wings.right.up = 'Rechter Flügel Hoch'
gui.ppm2.editor.wings.right.inside = 'Rechter Flügel Innen'

for i = 1, 3
	gui.ppm2.editor.wings.details.def['detail' .. i] = "Flügel URL detail #{i}"
	gui.ppm2.editor.wings.details.def['color' .. i] = "URL Detail farbe #{i}"
	gui.ppm2.editor.wings.details.bat['detail' .. i] = "Bat Flügel URL detail #{i}"
	gui.ppm2.editor.wings.details.bat['color' .. i] = "Bat Flügel URL Detail farbe #{i}"
	gui.ppm2.editor.wings.details.batskin['detail' .. i] = "Bat Flügel Haut URL detail #{i}"
	gui.ppm2.editor.wings.details.batskin['color' .. i] = "Bat Flügel Haut URL Detail farbe #{i}"

gui.ppm2.editor.neck.height = 'Halshöhe'

gui.ppm2.editor.body.suit = 'Körperanzug'
gui.ppm2.editor.body.color = 'Korper farbe'
gui.ppm2.editor.body.spine_length = 'Rückenlänge'
gui.ppm2.editor.body.url_desc = 'Körper detail URL Bild Eingabefeld\nSollte PNG oder JPEG sein (funktioniert wie bei\nPAC3 URL textur)'

for i = 1, PPM2.MAX_BODY_DETAILS
	gui.ppm2.editor.body.detail['desc' .. i] = "Detail #{i}"
	gui.ppm2.editor.body.detail['color' .. i] = "Detail farbe #{i}"
	gui.ppm2.editor.body.detail['glow' .. i] = "Detail #{i} leuchtet"
	gui.ppm2.editor.body.detail['glow_strength' .. i] = "Detail #{i} leuchtkraft"

	gui.ppm2.editor.body.detail.url['desc' .. i] = "Detail #{i}"
	gui.ppm2.editor.body.detail.url['color' .. i] = "Detail farbe #{i}"

gui.ppm2.editor.tattoo.edit_keyboard = 'Bearbeiten mit der Tastatur'
gui.ppm2.editor.tattoo.type = 'Typ'
gui.ppm2.editor.tattoo.over = 'Tattoo über Körperdetails'
gui.ppm2.editor.tattoo.glow = 'Tattoo leuchtet'
gui.ppm2.editor.tattoo.glow_strength = 'Tattoo leuchtkraft'
gui.ppm2.editor.tattoo.color = 'Farbe der Tätowierung'

gui.ppm2.editor.tattoo.tweak.rotate = 'Rotation'
gui.ppm2.editor.tattoo.tweak.x = 'X Position'
gui.ppm2.editor.tattoo.tweak.y = 'Y Position'
gui.ppm2.editor.tattoo.tweak.width = 'Breite Skalierung'
gui.ppm2.editor.tattoo.tweak.height = 'Länge Skalierung'

for i = 1, PPM2.MAX_TATTOOS
	gui.ppm2.editor.tattoo['layer' .. i] = "Tattooschicht #{i}"

gui.ppm2.editor.tail.type = 'Schweif typ'
gui.ppm2.editor.tail.size = 'Schweif größe'
gui.ppm2.editor.tail.separate = 'Trenne Schweif phong Einstellungen vom Körper'

for i = 1, 2
	gui.ppm2.editor.tail['color' .. i] = 'Schweif farbe ' .. i

for i = 1, 6
	gui.ppm2.editor.tail['detail' .. i] = "Schweif detail farbe #{i}"
	gui.ppm2.editor.tail.url['detail' .. i] = "Schweif URL detail #{i}"
	gui.ppm2.editor.tail.url['color' .. i] = "Schweif URL detail #{i}"

gui.ppm2.editor.hoof.fluffers = 'Huf flausch'

gui.ppm2.editor.legs.height = 'Bein höhe'
gui.ppm2.editor.legs.socks.simple = 'Socken (einfache Textur)'
gui.ppm2.editor.legs.socks.model = 'Socken (als Modell)'
gui.ppm2.editor.legs.socks.color = 'Socken Modell Farbe'
gui.ppm2.editor.legs.socks.texture = 'Socken Textur'
gui.ppm2.editor.legs.socks.url_texture = 'Socken URL textur'

for i = 1, 6
	gui.ppm2.editor.legs.socks['color' .. i] = 'Socken detail farbe ' .. i

gui.ppm2.editor.legs.newsocks.model = 'Socken (als neues Modell)'

for i = 1, 3
	gui.ppm2.editor.legs.newsocks['color' .. i] = 'Neue Socken Farbe ' .. i

gui.ppm2.editor.legs.newsocks.url = 'Neue Socken URL textur'

-- shared editor stuffs

gui.ppm2.editor.tattoo.help = "Um den Bearbeitungsmodus zu verlassen, drücke Escape oder klicke irgendwo mit der Maus
Um das Tattoo zu bewegen, verwende WASD
Um höher/niedriger zu skalieren, verwende die Hoch/Runter Pfeiltasten
Um breiter/kleiner zu skalieren, verwende die Rechts/Links Pfeiltasten
Um links/rechts zu Drehen, verwende die Q/E Tasten"

for name, data in pairs PPM2.PonyDataRegistry
	gui.ppm2.editor.reset[data.getFunc\lower()] = 'Zurücksetzen ' .. data.getFunc

gui.ppm2.editor.phong.info = 'Mehr Infos über Phong im Wiki'
gui.ppm2.editor.phong.exponent = 'Phong Exponent - wie stark die reflektierende Eigenschaft\nvon der Ponyhaut ist\nSetze den Wert nahe Null, um dem Roboterlook zu bekommen\nPonyhaut'
gui.ppm2.editor.phong.exponent_text = 'Phong Exponent'
gui.ppm2.editor.phong.boost.title = 'Phong Verstärkung - multipliziert spiegelnde Kartenreflexionen'
gui.ppm2.editor.phong.boost.boost = 'Phong Verstärkung'
gui.ppm2.editor.phong.tint.title = 'Tönung Farbe - welche Farben reflektieren Spiegelkarte\nWeiß - Reflektiert alle Farben\n(Im weißen Raum - weiße Spiegelkarte)'
gui.ppm2.editor.phong.tint.tint = 'Tönung Farbe - welche Farben reflektieren Spiegelkarte\nWeiß - Reflektiert alle Farben\n(Im weißen Raum - weiße Spiegelkarte)'
gui.ppm2.editor.phong.frensel.front.title = 'Phong Front - Fresnel 0 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.front.front = 'Phong Front'
gui.ppm2.editor.phong.frensel.middle.title = 'Phong Mitte - Fresnel 45 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.middle.front = 'Phong Mitte'
gui.ppm2.editor.phong.frensel.sliding.title = 'Phong Gleitend - Fresnel 45 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.sliding.front = 'Phong Gleitend'
gui.ppm2.editor.phong.lightwarp = 'Lightwarp'
gui.ppm2.editor.phong.url_lightwarp = 'Lightwarp textur URL eingabe\nEs muss 256x16 sein!'
gui.ppm2.editor.phong.bumpmap = 'Bumpmap URL eingabe'

gui.ppm2.editor.info.discord = "Trete DBot's Discord bei!"
gui.ppm2.editor.info.ponyscape = "PPM/2 ist ein Ponyscape projekt"
gui.ppm2.editor.info.creator = "PPM/2 wurde von DBot erstellt und entwickelt"
gui.ppm2.editor.info.newmodels = "Neue Modelle wurden von Durpy erstellt"
gui.ppm2.editor.info.cppmmodels = "CPPM Modelle (einschließlich Ponyhände) gehören zu UnkN', 'http://steamcommunity.com/profiles/76561198084938735"
gui.ppm2.editor.info.oldmodels = "Alte Modelle gehören zu Scentus und den anderen."
gui.ppm2.editor.info.bugs = "Einen Fehler gefunden? Melde es hier!"
gui.ppm2.editor.info.sources = "Quellen findest du hier"
gui.ppm2.editor.info.githubsources = "Oder im GitHub mirror"
gui.ppm2.editor.info.thanks = "Besonderer Dank geht an alle, die kritisiert,\ngeholfen und PPM/2 getestet haben!"

-- other stuff

info.ppm2.fly.pegasus = 'Du musst eine Pegasus oder ein Alicorn sein, um zu fliegen!'
info.ppm2.fly.cannot = 'Du kannst nicht %s im Augenblick.'

gui.ppm2.emotes.sad = 'Traurig'
gui.ppm2.emotes.wild = 'Wild'
gui.ppm2.emotes.grin = 'Grinsen'
gui.ppm2.emotes.angry = 'Wütend'
gui.ppm2.emotes.tongue = ':P'
gui.ppm2.emotes.angrytongue = '>:P'
gui.ppm2.emotes.pff = 'Pffff!'
gui.ppm2.emotes.kitty = ':3'
gui.ppm2.emotes.owo = 'oWo'
gui.ppm2.emotes.ugh = 'Uuugh'
gui.ppm2.emotes.lips = 'Lippen lecken'
gui.ppm2.emotes.scrunch = 'Scrunch'
gui.ppm2.emotes.sorry = 'Entschuldigend'
gui.ppm2.emotes.wink = 'Zwinkern'
gui.ppm2.emotes.right_wink = 'Rechts Zwinkern'
gui.ppm2.emotes.licking = 'Lecken'
gui.ppm2.emotes.suggestive_lips = 'Anzüglich Lippen Lecken'
gui.ppm2.emotes.suggestive_no_tongue = 'Anzüglich ohne Zunge'
gui.ppm2.emotes.gulp = 'Schlucken'
gui.ppm2.emotes.blah = 'Blah blah blah'
gui.ppm2.emotes.happi = 'Glücklich'
gui.ppm2.emotes.happi_grin = 'Glückliches Grinsen'
gui.ppm2.emotes.duck = 'ENTE'
gui.ppm2.emotes.ducks = 'ENTEN WAHNSINN'
gui.ppm2.emotes.quack = 'QUACK'
gui.ppm2.emotes.suggestive = 'Anzüglich mit Zunge'

message.ppm2.emotes.invalid = 'Keine Emotion mit ID: %s'

gui.ppm2.editor.intro.text = "Grüße meinen neuen.... Robochirurgen für Ponys! Es erlaubt Dir....\n" ..
	"hmmm... ein Pony zu werden, und ja, dieser Prozess ist UNUMKEHRBAR! Aber mach dir keine Sorgen,\n" ..
	"Du verlierst keine deiner Gehirnzellen, in und nach der Operation, weil wir die Operation sehr schonend durchführen...\n\n" ..
	"Eigentlich habe ich keine Ahnung, du biologisches Wesen! Seine mechanischen Hände werden dich in die engsten Umarmungen hüllen, die du je bekommen hast!\n" ..
	"Und, bitte, sterbe nicht in diesen Prozess, denn dies würde dazu führen DAS ERLÖSCHEN DEINER LEBENSLANGEN GARANTIE... und Du wirst kein Pony sein!\n" ..
	"----\n\n\n" ..
	"Vorsicht: Nehme den Robochirurgen nicht auseinander.\nLege deine Hände/Hufe nicht in bewegliche Teile des Robochirurgen.\n" ..
	"Schalte es während des Betriebs nicht aus..\nVersuche nicht sich seinen Taten zu widersetzen.\n" ..
	"Sei immer sanft mit den Robochirurgen.\n" ..
	"Schlage den Robochirurgen nie auf sein Interface.\n" ..
	"DBot's DLibCo übernimmt keine Verantwortung für Schäden, die durch falsche Verwendung von Robochirurgen entstehen.\n" ..
	"Die Garantie erlischt, wenn der Benutzer stirbt.\n" ..
	"Keine Rückerstattung."
gui.ppm2.editor.intro.title = 'Willkommen hier, Mensch!'
gui.ppm2.editor.intro.okay = "Ok, ich werde diese Lizenz sowieso nie lesen."


Documentation.

Documentation.

Class = require "nyaa.class"

local Array

---
-- @fixme Some of those operations are not very efficient.
Array = Class "Array",
	---
	-- Creates an array.
	--
	-- Continuous integer-indexed values will be used as initial stored values.
	-- If none, the created array will be empty.
	__init: (arg) =>
		for i = 1, #arg
			rawset self, i, arg[i]

	---
	-- Appends a value to the Array.
	--
	-- The value is always added as the last element.
	--
	-- @return (nil)
	push: (value) =>
		self[#self + 1] = value

	---
	-- Removes the last value from the array, and returns it.
	--
	-- @return (object)
	pop: =>
		last = self[#self]

		self[#self] = nil

		return last

	---
	-- Applies a function to all elements of the array, and returns a new array containing the results.
	--
	-- @param f (function)
	-- @return (Array)
	map: (f) =>
		Array [f x for x in ipairs self]

	---
	-- Applies a function to all elements of the array, and returns a new array containing the original values whose application through this function returned something truth-y.
	--
	-- @param filter (function)
	-- @return (Array)
	filter: (filter) =>
		newArray = Array {}

		for i = 1, #self
			value = self[i]

			if filter value
				newArray\push value

		newArray

	---
	-- Concatenates the stored values.
	--
	-- Throws errors if an element cannot be converted to a string.
	--
	-- @param separator (string) String to add between concatenated values.
	concat: (separator) =>
		table.concat self, separator

Array

Class = require "nyaa.class"

local Array

---
-- @fixme Some of those operations are not very efficient.
Array = Class "Array",
	__init: (arg) =>
		for i = 1, #arg
			rawset self, i, arg[i]

	push: (value) =>
		self[#self + 1] = value

	pop: =>
		last = self[#self]

		self[#self] = nil

		return last

	map: (f) =>
		Array [f x for x in ipairs self]

	filter: (filter) =>
		newArray = Array {}

		for i = 1, #self
			value = self[i]

			if filter value
				newArray\push value

		newArray

	concat: (separator) =>
		table.concat self, separator

Array


remove debug msg

remove debug msg

Wlan = require "wlan"
Req = require "req"
Res = require "res"
log = require "log"
app = require "app"

class Http
  @config: (opts = {}) ->
    with net.createServer net.TCP
      \listen 80, (conn) ->
        conn\on "receive", (client, data) ->
          req = Req client, data
          res = Res client
          client\on 'sent', ->
            client\close()
            req.client = nil
            req = nil
            res.client = nil
            res = nil
            data = nil
            client = nil
            conn = nil
            collectgarbage()
          app\process req, res

Http.config()

return Http

Wlan = require "wlan"
Req = require "req"
Res = require "res"
log = require "log"
app = require "app"

class Http
  @config: (opts = {}) ->
    with net.createServer net.TCP
      \listen 80, (conn) ->
        conn\on "receive", (client, data) ->
          req = Req client, data
          res = Res client
          client\on 'sent', ->
            log.debug 'sent'
            client\close()
            req.client = nil
            req = nil
            res.client = nil
            res = nil
            data = nil
            client = nil
            conn = nil
            collectgarbage()
          app\process req, res

Http.config()

return Http


fixed yubikey

fixed yubikey

http = require "lapis.nginx.http"
import render_html from require "lapis.html"
import from_json,to_json,slugify, escape from require "lapis.util"
import encode_base64,hmac_sha1 from require "lapis.util.encoding"

UsersModel=require "lazuli.modules.user_management.models.users"
YubiCloudModel=require "lazuli.modules.user_management.models.yubicloud"

frandom = nil
ranval=->
  frandom or= assert io.open "/dev/urandom", "rb"
  s = frandom\read 4
  assert s\len! == 4
  v = 0
  for i = 1, 4
    v = 256 * v + s\byte i
  return v

class YubiCloud
  new: (@app,@modulename,@config) =>
    @apiurl = @config.apiurl or "http://api.yubico.com/wsapi/2.0/verify?id=1&otp=$OTP$&nonce=$NONCE$&timestamp=1"
    @required = @config.required or false
    @takeControlOfForm = @takeControlOfForm or true

  fillUrl: (otp,nonce) =>
    @apiurl\gsub("%$OTP%$",escape otp)\gsub("%$NONCE%$",escape nonce)

  tryLogin: (params) =>
    if params.yubikey
      params.yubikey=params.yubikey\lower!
      nonce=@mkNonce!
      query=@fillUrl params.yubikey, nonce
      res=http.simple query
      if res and res\find "status=OK", 1, true and res\find "nonce="..nonce, 1, true
        idstr=params.yubikey\sub(1,-32)
        entry=YubiCloudModel\find idstr: idstr
        if entry
          user=entry\get_user!
          if user
            return user, res
          return nil, "OTP -> user resulution failed."
        return nil, "OTP unknown. idstr: "..idstr
      return nil, "invalid OTP, details: "..query.." => "..res
    if @required
      return nil, res
    return false, res

  mkNonce: =>
    now=os.time!
    hash=slugify now..encode_base64 hmac_sha1 now..ranval!,ranval!..now
    hash=hash\sub 1,20
    hash\lower!

  getLoginOkHtml:    => ""
  getLoginErrorHtml: => ""

  getLoginHtml: =>
    render_html ->
      div class: "pure-control-group yubigroup", ->
        label for: "yubikey", "YubiCloud:"
        input id: "yubikey", type: "yubikey", name: "yubikey", autocomplete: "off", placeholder: "YubiKey OTP", autofocus: @takeControlOfForm and "autofocus" or nil
        if @takeControlOfForm
          input type: "button", value: "X", id: "closeYubiKeyBtn"
      if @takeControlOfForm
        script ->
          raw [[
            document.getElementsByClassName("usernamegroup")[0].hidden=true
            document.getElementsByClassName("passwordgroup")[0].hidden=true
            document.getElementById("closeYubiKeyBtn").onclick=function(){
              document.getElementsByClassName("usernamegroup")[0].hidden=false
              document.getElementsByClassName("passwordgroup")[0].hidden=false
              document.getElementsByClassName("yubigroup")[0].hidden=true
              document.getElementById("username").focus()
            }
          ]]

http = require "lapis.nginx.http"
import render_html from require "lapis.html"
import from_json,to_json,slugify, escape from require "lapis.util"
import encode_base64,hmac_sha1 from require "lapis.util.encoding"

UsersModel=require "lazuli.modules.user_management.models.users"
YubiCloudModel=require "lazuli.modules.user_management.models.yubicloud"

frandom = nil
ranval=->
  frandom or= assert io.open "/dev/urandom", "rb"
  s = frandom\read 4
  assert s\len! == 4
  v = 0
  for i = 1, 4
    v = 256 * v + s\byte i
  return v

class YubiCloud
  new: (@app,@modulename,@config) =>
    @apiurl = @config.apiurl or "http://api.yubico.com/wsapi/2.0/verify?id=1&otp=$OTP$&nonce=$NONCE$&timestamp=1"
    @required = @config.required or false
    @takeControlOfForm = @takeControlOfForm or true

  fillUrl: (otp,nonce) =>
    @apiurl\gsub("%$OTP%$",escape otp)\gsub("%$NONCE%$",escape nonce)

  tryLogin: (params) =>
    if params.yubikey
      params.yubikey=params.yubikey\lower!
      nonce=@mkNonce!
      query=@fillUrl params.yubikey, nonce
      res=http.simple query
      if res and res\find "status=OK", 1, true and res\find "nonce="..nonce, 1, true
        entry=YubiCloudModel\find idstr: params.yubikey\sub(-32)
        if entry
          user=entry\get_user!
          if user
            return user, res
          return nil, "OTP -> user resulution failed."
        return nil, "OTP unknown. idstr: "..params.yubikey\sub(-32)
      return nil, "invalid OTP, details: "..query.." => "..res
    if @required
      return nil, res
    return false, res

  mkNonce: =>
    now=os.time!
    hash=slugify now..encode_base64 hmac_sha1 now..ranval!,ranval!..now
    hash=hash\sub 1,20
    hash\lower!

  getLoginOkHtml:    => ""
  getLoginErrorHtml: => ""

  getLoginHtml: =>
    render_html ->
      div class: "pure-control-group yubigroup", ->
        label for: "yubikey", "YubiCloud:"
        input id: "yubikey", type: "yubikey", name: "yubikey", autocomplete: "off", placeholder: "YubiKey OTP", autofocus: @takeControlOfForm and "autofocus" or nil
        if @takeControlOfForm
          input type: "button", value: "X", id: "closeYubiKeyBtn"
      if @takeControlOfForm
        script ->
          raw [[
            document.getElementsByClassName("usernamegroup")[0].hidden=true
            document.getElementsByClassName("passwordgroup")[0].hidden=true
            document.getElementById("closeYubiKeyBtn").onclick=function(){
              document.getElementsByClassName("usernamegroup")[0].hidden=false
              document.getElementsByClassName("passwordgroup")[0].hidden=false
              document.getElementsByClassName("yubigroup")[0].hidden=true
              document.getElementById("username").focus()
            }
          ]]


improved superroute

improved superroute

lapis = require "lapis"

import after_dispatch from require "lapis.nginx.context"
import to_json from require "lapis.util"
migrations=require "lapis.db.migrations"
config = (require "lapis.config").get!

class extends lapis.Application
  @before_filter =>
    if config.measure_performance
      after_dispatch ->
        print to_json ngx.ctx.performance
    @modules or={}
    @session.modules or={}
  "/_lazuli/console": if config.enable_console
    (require "lapis.console").make!
  else
    => "console disabled in config"
  "/_lazuli/migrate": if config.enable_web_migration
    =>
      migrations.create_migrations_table!
      migrations.run_migrations require "migrations"
  else
    => "web migration disabled in config"
  new: (...)=>
    @modules or={}
    super ...
  enable: (feature,forcelapis=false)=>
    if not forcelapis
      have,fn=pcall require, "lazuli.modules."..feature..".enabler"
      if have and type(fn)=="function"
        return fn @
    super feature
  superroute: (r,f)=>
    App=@__parent
    cache=nil
    name=next r
    cont=f or r[1]
    r[1]=nil
    @__base[r]= =>
      if not cache
        for app_route in pairs App.__base
          if type(app_route) == "table"
            app_route_name = next app_route
            if app_route_name == name
              cache=App.__base[app_route]
              break
      if cont
        cont @, cache(@)
      else
        cache(@)

lapis = require "lapis"

import after_dispatch from require "lapis.nginx.context"
import to_json from require "lapis.util"
migrations=require "lapis.db.migrations"
config = (require "lapis.config").get!

class extends lapis.Application
  @before_filter =>
    if config.measure_performance
      after_dispatch ->
        print to_json ngx.ctx.performance
    @modules or={}
    @session.modules or={}
  "/_lazuli/console": if config.enable_console
    (require "lapis.console").make!
  else
    => "console disabled in config"
  "/_lazuli/migrate": if config.enable_web_migration
    =>
      migrations.create_migrations_table!
      migrations.run_migrations require "migrations"
  else
    => "web migration disabled in config"
  new: (...)=>
    @modules or={}
    super ...
  enable: (feature,forcelapis=false)=>
    if not forcelapis
      have,fn=pcall require, "lazuli.modules."..feature..".enabler"
      if have and type(fn)=="function"
        return fn @
    super feature
  superroute: (r)=>
    App=@__parent
    cache=nil
    name=next r
    cont=r[1]
    r[1]=nil
    @__base[r]= =>
      if not cache
        for app_route in pairs App.__base
          if type(app_route) == "table"
            app_route_name = next app_route
            if app_route_name == name
              cache=App.__base[app_route]
              break
      if cont
        cont @, cache(@)
      else
        cache(@)


FIXED THE DOOR ISSUE.

FIXED THE DOOR ISSUE.

STI = require('vendor/sti/sti')
bump = require('vendor/bump/bump')
State = require('lib/states/state')
MapLayer = require('lib/map-layer')
PauseState = require('lib/states/pause')
entities = require('lib/entities')
DrawSystem = require('lib/systems/draw')
ControlSystem = require('lib/systems/control')
Color = require('lib/display/color')
Point = require('lib/geo/point')
Direction = require('lib/enums/direction')

class AreaState extends State
	new: (@mapName) =>
		super()

		@map = STI("assets/maps/#{@mapName}", { 'bump' })
		@world = bump.newWorld(@map.tilewidth)
		@map\bump_init(@world)
		@scale = 1

	enter: (previous, transition) =>
		super(previous)

		if transition.player
			@player = transition.player\clone(@)
		needsDoor = not not transition.player

		doors = {}
		for object in *@map.layers.entities.objects
			if object.type == 'Player' and @player then continue
			if object.type == 'zones/DoorZone' then _.push(doors, object)

			entity = entities[object.type](@, object)
			if object.type == 'Player'
				@player = entity
				continue
			@addEntityToWorld(entity, object)

		assert @player
		door = doors[1]
		if needsDoor
			@player.point.x = door.x
			@player.point.y = door.y

		-- Handle specific spawn points
		if toPoint = transition.toPoint
			if toPoint.x then @player.point.x = toPoint.x
			if toPoint.y then @player.point.y = toPoint.y
		-- Handle door offsets
		-- if transition.offset
		-- 	if transition.offset.x then @player.point.x += transition.offset.x
		-- 	if transition.offset.y then @player.point.y += transition.offset.y
		-- Handle direction
		if transition.direction
			if transition.direction == Direction.NORTH then @player.point.y += @player.data.height
			if transition.direction == Direction.SOUTH then @player.point.y -= @player.data.height
			if transition.direction == Direction.WEST then @player.point.x += @player.data.width
			if transition.direction == Direction.EAST then @player.point.x -= @player.data.width

		@addEntityToWorld(@player, @player\getData())

		entityLayer = MapLayer(@map, 'entities')
		entityLayer.engine\addSystem(DrawSystem())
		entityLayer.engine\addSystem(ControlSystem())
		entityLayer.engine\addEntity(@player)

	addEntityToWorld: (entity, data) =>
		@world\add(entity, data.x, data.y, data.width, data.height)

	update: (dt) =>
		super(dt)
		@map\update(dt)
		@windowWidth = love.graphics.getWidth()
		@windowHeight = love.graphics.getHeight()
		tx = math.floor(@player.point.x - @windowWidth / @scale / 2)
		ty = math.floor(@player.point.y - @windowHeight / @scale / 2)
		@translation = Point(tx, ty)

	draw: () =>
		love.graphics.push()
		love.graphics.scale(@scale)
		love.graphics.translate(-@translation.x, -@translation.y)
		@map\setDrawRange(@translation.x, @translation.y, @windowWidth, @windowHeight)
		@map\draw()

		if @DEBUG
			love.graphics.setColor(Color(255, 0, 0, 255))
			@map\bump_draw(@world)
			love.graphics.setColor(Color(255, 255, 255, 255))

		love.graphics.pop()

		super()

	keypressed: (key) =>
		super(key)
		if key == 'escape' then @push(PauseState())

	mousepressed: (x, y, button) =>
		if @DEBUG and button == 2
			eventPoint = Point(x, y)
			-- TODO: Make work not just for player
			point = eventPoint + @translation - Point(200, 150)

			items, len = @world\queryPoint(point\toTuple())
			if len > 0 then d items

STI = require('vendor/sti/sti')
bump = require('vendor/bump/bump')
State = require('lib/states/state')
MapLayer = require('lib/map-layer')
PauseState = require('lib/states/pause')
entities = require('lib/entities')
DrawSystem = require('lib/systems/draw')
ControlSystem = require('lib/systems/control')
Color = require('lib/display/color')
Point = require('lib/geo/point')
Direction = require('lib/enums/direction')

class AreaState extends State
	new: (@mapName) =>
		super()

		@map = STI("assets/maps/#{@mapName}", { 'bump' })
		@world = bump.newWorld(@map.tilewidth)
		@map\bump_init(@world)
		@scale = 1

	enter: (previous, transition) =>
		super(previous)

		if transition.player
			@player = transition.player\clone(@)
		needsDoor = not not transition.player

		doors = {}
		for object in *@map.layers.entities.objects
			if object.type == 'Player' and @player then continue
			if object.type == 'zones/DoorZone' then _.push(doors, object)

			entity = entities[object.type](@, object)
			if object.type == 'Player'
				@player = entity
				continue
			@addEntityToWorld(entity, object)

		assert @player
		playerData = @player\getData()
		door = doors[1]
		if needsDoor
			playerData.x = door.x
			playerData.y = door.y

		-- Handle specific spawn points
		if toPoint = transition.toPoint
			if toPoint.x then playerData.x = toPoint.x
			if toPoint.y then playerData.y = toPoint.y
		-- Handle door offsets
		-- if transition.offset
		-- 	if transition.offset.x then playerData.x += transition.offset.x
		-- 	if transition.offset.y then playerData.y += transition.offset.y
		-- Handle direction
		if transition.direction
			if transition.direction == Direction.NORTH then playerData.y -= playerData.height
			if transition.direction == Direction.SOUTH then playerData.y += playerData.height
			if transition.direction == Direction.WEST then playerData.x += playerData.width
			if transition.direction == Direction.EAST then playerData.x -= playerData.width

		@addEntityToWorld(@player, playerData)

		entityLayer = MapLayer(@map, 'entities')
		entityLayer.engine\addSystem(DrawSystem())
		entityLayer.engine\addSystem(ControlSystem())
		entityLayer.engine\addEntity(@player)

	addEntityToWorld: (entity, data) =>
		@world\add(entity, data.x, data.y, data.width, data.height)

	update: (dt) =>
		super(dt)
		@map\update(dt)
		@windowWidth = love.graphics.getWidth()
		@windowHeight = love.graphics.getHeight()
		tx = math.floor(@player.point.x - @windowWidth / @scale / 2)
		ty = math.floor(@player.point.y - @windowHeight / @scale / 2)
		@translation = Point(tx, ty)

	draw: () =>
		love.graphics.push()
		love.graphics.scale(@scale)
		love.graphics.translate(-@translation.x, -@translation.y)
		@map\setDrawRange(@translation.x, @translation.y, @windowWidth, @windowHeight)
		@map\draw()

		if @DEBUG
			love.graphics.setColor(Color(255, 0, 0, 255))
			@map\bump_draw(@world)
			love.graphics.setColor(Color(255, 255, 255, 255))

		love.graphics.pop()

		super()

	keypressed: (key) =>
		super(key)
		if key == 'escape' then @push(PauseState())

	mousepressed: (x, y, button) =>
		if @DEBUG and button == 2
			eventPoint = Point(x, y)
			-- TODO: Make work not just for player
			point = eventPoint + @translation - Point(200, 150)

			items, len = @world\queryPoint(point\toTuple())
			if len > 0 then d items


Import ecs

Import ecs

-- type checking for classes
_type = type
export type = (v) ->
  base = _type v
  if base == "table"
    cls = v.__class
    return cls.__name if cls
  base

sauce3.console = require "sauce3/core/console"
sauce3.java    = require "sauce3/core/java"

sauce3.File    = require "sauce3/utils/File"
sauce3.Font    = require "sauce3/utils/Font"
sauce3.Image   = require "sauce3/utils/Image"
sauce3.Quad    = require "sauce3/utils/Quad"

sauce3.file_system = require "sauce3/core/file_system"
sauce3.timer       = require "sauce3/core/timer"
sauce3.graphics    = require "sauce3/core/graphics"
sauce3.console     = require "sauce3/core/console"
sauce3.system      = require "sauce3/core/system"
sauce3.timer       = require "sauce3/core/timer"
sauce3.audio       = require "sauce3/core/audio"

sauce3.ecs         = require "sauce3/framework/ecs"

import key_codes, button_codes from require "sauce3/wrappers"

sauce3._key_pressed = (key) ->
  (sauce3.key_pressed key_codes[key]) if sauce3.key_pressed

sauce3._key_released = (key) ->
  (sauce3.key_released key_codes[key]) if sauce3.key_released

sauce3._mouse_pressed = (x, y, button) ->
  (sauce3.mouse_pressed button_codes[button]) if sauce3.mouse_pressed

sauce3._mouse_released = (x, y, button) ->
  (sauce3.mouse_released button_codes[button]) if sauce3.mouse_released

sauce3._quit = ->
  if sauce3.quit then sauce3.quit!
  sauce3.audio.stop_all!

sauce3.run = ->
  dt = sauce3.timer.get_delta!

  sauce3.update(dt) if sauce3.update

  sauce3.graphics.clear!
  sauce3.graphics.origin!

  sauce3.draw! if sauce3.draw

  sauce3.graphics.present!

require "main"

-- type checking for classes
_type = type
export type = (v) ->
  base = _type v
  if base == "table"
    cls = v.__class
    return cls.__name if cls
  base

sauce3.console = require "sauce3/core/console"
sauce3.java    = require "sauce3/core/java"

sauce3.File    = require "sauce3/utils/File"
sauce3.Font    = require "sauce3/utils/Font"
sauce3.Image   = require "sauce3/utils/Image"
sauce3.Quad    = require "sauce3/utils/Quad"

sauce3.file_system = require "sauce3/core/file_system"
sauce3.timer       = require "sauce3/core/timer"
sauce3.graphics    = require "sauce3/core/graphics"
sauce3.console     = require "sauce3/core/console"
sauce3.system      = require "sauce3/core/system"
sauce3.timer       = require "sauce3/core/timer"
sauce3.audio       = require "sauce3/core/audio"

import key_codes, button_codes from require "sauce3/wrappers"

sauce3._key_pressed = (key) ->
  (sauce3.key_pressed key_codes[key]) if sauce3.key_pressed

sauce3._key_released = (key) ->
  (sauce3.key_released key_codes[key]) if sauce3.key_released

sauce3._mouse_pressed = (x, y, button) ->
  (sauce3.mouse_pressed button_codes[button]) if sauce3.mouse_pressed

sauce3._mouse_released = (x, y, button) ->
  (sauce3.mouse_released button_codes[button]) if sauce3.mouse_released

sauce3._quit = ->
  if sauce3.quit then sauce3.quit!
  sauce3.audio.stop_all!

sauce3.run = ->
  dt = sauce3.timer.get_delta!

  sauce3.update(dt) if sauce3.update

  sauce3.graphics.clear!
  sauce3.graphics.origin!

  sauce3.draw! if sauce3.draw

  sauce3.graphics.present!

require "main"


location: use conf for api key, use new webserver

Former-commit-id: ec65219a3a54ddaf730f14fedba3a8c68d7d7d3b [formerly 2fef49a87b59f5c440a30eaf63b250c6ba2e52e2]
Former-commit-id: e9f35499ba8f3af05f60d364f99c9ef75a9e930a

{:simplehttp, :json, :urlEncode} = require'util'

on_finished = (req, resp) ->
  channel = req.url\match('channel=(.+)')
  unless channel
    html = 'Invalid channel'
    resp\set_status(404)
    resp\set_header('Content-Type', 'text/html')
    resp\set_header('Content-Length', #html)
    resp\set_body(html)
    resp\send()
    return
  else
    channel = '#'..channel

  html = [[
  <!DOCTYPE html>

  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
  <title>IRC member map</title>

  ]]
  --print('---- request finished, send response')
  resp\set_status(200)
  resp\set_header('Content-Type', 'text/html')
  resp\set_header('Content-Length', #html)
  resp\set_body(html)
  resp\send()

ivar2.webserver.regUrl "/location/(.*)$", on_finished

lookup = (address, cb) ->
  API_URL = 'http://maps.googleapis.com/maps/api/geocode/json'
  url = API_URL .. '?address=' .. urlEncode(address) .. '&sensor=false' .. '&language=en-GB'

  simplehttp url, (data) ->
      parsedData = json.decode data
      if parsedData.status ~= 'OK'
        return false, parsedData.status or 'unknown API error'

      location = parsedData.results[1]
      locality, country, adminArea

      findComponent = (field, ...) ->
        n = select('#', ...)
        for i=1, n
          searchType = select(i, ...)
          for _, component in ipairs(location.address_components)
            for _, type in ipairs(component.types)
              if type == searchType
                return component[field]

      locality = findComponent('long_name', 'locality', 'postal_town', 'route', 'establishment', 'natural_feature')
      adminArea = findComponent('short_name', 'administrative_area_level_1')
      country = findComponent('long_name', 'country') or 'Nowhereistan'

      if adminArea and #adminArea <= 5
        if not locality
          locality = adminArea
        else
          locality = locality..', '..adminArea

      locality = locality or 'Null'

      place = locality..', '..country

      cb place, location.geometry.location.lat..','..location.geometry.location.lng

PRIVMSG:
  '^%plocation set (.+)$': (source, destination, arg) =>
    lookup arg, (place, loc) ->
      nick = source.nick
      @.persist["location:place:#{nick}"] = place
      @.persist["location:coords:#{nick}"] = loc
      say '%s %s', place, loc
  '^%plocation map$': (source, destination, arg) =>
    channel = destination\sub(2)
    say "http://irc.lart.no:#{ivar2.config.webserverport}/location/?channel=#{channel}"

json = require'json'
simplehttp = require'simplehttp'
httpserver = require'handler.http.server'
ev = require'ev'

loop = ev.Loop.default

on_data = (req, resp, data) ->
  --print('---- start request body')
  --io.write(data) if data
  --print('---- end request body')
  return

on_finished = (req, resp) ->
  channel = req.url\match('channel=(.+)')
  unless channel
    html = 'Invalid channel'
    resp\set_status(404)
    resp\set_header('Content-Type', 'text/html')
    resp\set_header('Content-Length', #html)
    resp\set_body(html)
    resp\send()
    return
  else
    channel = '#'..channel

  html = [[
  <!DOCTYPE html>

  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
  <title>IRC member map</title>

  ]]
  --print('---- request finished, send response')
  resp\set_status(200)
  resp\set_header('Content-Type', 'text/html')
  resp\set_header('Content-Length', #html)
  resp\set_body(html)
  resp\send()

on_response_sent = (resp) ->
  return

on_request = (server, req, resp) ->
  --print('---- start request headers: method =' .. req.method .. ', url = ' .. req.url)
  --for k,v in pairs(req.headers)
  --  print(k .. ": " .. v)

  --print('---- end request headers')
  -- check for '/favicon.ico' requests.
  if req.url\lower() == '/favicon.ico'
    -- return 404 Not found error
    resp\set_status(404)
    resp\send()
    return
  -- add callbacks to request.
  req.on_data = on_data
  req.on_finished = on_finished
  -- add response callbacks.
  resp.on_response_sent = on_response_sent

-- Check for already running server
if ivar2.webserver == nil
  print '---- Starting webserver ---- '
  ivar2.webserver = httpserver.new loop, {
    name: "ivar2-HTTPServer/0.0.1",
    on_request: on_request,
    request_head_timeout: 1.0,
    request_body_timeout: 1.0,
    write_timeout: 10.0,
    keep_alive_timeout: 1.0,
    max_keep_alive_requests: 10,
  }

  ivar2.webserver\listen_uri "tcp://0.0.0.0:#{ivar2.config.webserverport}/"
else
  -- Swap out the request handler with the reloaded one
  ivar2.webserver.on_request = on_request

urlEncode = (str, space) ->
	space = space or '+'

	str = str\gsub '([^%w ])', (c) ->
        string.format  "%%%02X", string.byte(c)
	return str\gsub(' ', space)

lookup = (address, cb) ->
  API_URL = 'http://maps.googleapis.com/maps/api/geocode/json'
  url = API_URL .. '?address=' .. urlEncode(address) .. '&sensor=false' .. '&language=en-GB'

  simplehttp url, (data) ->
      parsedData = json.decode data
      if parsedData.status ~= 'OK'
        return false, parsedData.status or 'unknown API error'

      location = parsedData.results[1]
      locality, country, adminArea

      findComponent = (field, ...) ->
        n = select('#', ...)
        for i=1, n
          searchType = select(i, ...)
          for _, component in ipairs(location.address_components)
            for _, type in ipairs(component.types)
              if type == searchType
                return component[field]

      locality = findComponent('long_name', 'locality', 'postal_town', 'route', 'establishment', 'natural_feature')
      adminArea = findComponent('short_name', 'administrative_area_level_1')
      country = findComponent('long_name', 'country') or 'Nowhereistan'

      if adminArea and #adminArea <= 5
        if not locality
          locality = adminArea
        else
          locality = locality..', '..adminArea

      locality = locality or 'Null'

      place = locality..', '..country

      cb place, location.geometry.location.lat..','..location.geometry.location.lng

PRIVMSG:
  '^%plocation set (.+)$': (source, destination, arg) =>
    lookup arg, (place, loc) ->
      nick = source.nick
      @.persist["location:place:#{nick}"] = place
      @.persist["location:coords:#{nick}"] = loc
      say '%s %s', place, loc
  '^%plocation map$': (source, destination, arg) =>
    channel = destination\sub(2)
    say "http://irc.lart.no:#{ivar2.config.webserverport}/?channel=#{channel}"


Make logging more flexible

Make logging more flexible

-- Copyright (c) 2014 by Adam Hellberg <sharparam@sharparam.com>.
--
-- 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.

NAME, T = ...

db = T.database

import format from string

class T.Logger
    @levels:
        DEBUG: 0
        INFO: 1
        WARN: 2
        ERROR: 3
        FATAL: 4

    new: (@name) =>

    log: (level, ...) =>
        return if level == @@levels.DEBUG and (not db.loaded or not db\get 'debug', false)
        prefix = @@prefixes[level] or 'UNKNOWN'
        msg = '%s.%s: [%s] %s'\format NAME, @name, prefix, format ...
        DEFAULT_CHAT_FRAME\AddMessage msg

import Logger from T

Logger.prefixes = {v, k for k, v in pairs Logger.levels}

for id, level in pairs Logger.levels
    Logger.__base[id\lower!] = (...) => @log level, ...

T.log = Logger('main')

-- Copyright (c) 2014 by Adam Hellberg <sharparam@sharparam.com>.
--
-- 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.

NAME, T = ...

db = T.database

import format, upper from string

T.log =
    levels:
        DEBUG: 0
        INFO: 1
        WARN: 2
        ERROR: 3
        FATAL: 4

import log from T

log.prefixes = {v, k for k, v in pairs log.levels}

printf = (...) ->
    DEFAULT_CHAT_FRAME\AddMessage format '%s: %s', NAME, format ...

lprintf = (level, ...) ->
    error "Invalid log level: #{level}" unless log.prefixes[level]
    return if level == log.levels.DEBUG and (not db.loaded or not db\get 'debug', false)
    prefix = log.prefixes[level]
    msg = '[%s] %s'\format prefix, format ...
    printf msg

for id, level in pairs log.levels
    log[id\lower!] = (...) -> lprintf level, ...


imageupload: use full prefix for better URLs

Former-commit-id: a2c9623540279676d9e078d78ad0a51714659c84 [formerly a124aa2d91dff4397040f62810b5b430030ed153]
Former-commit-id: b89b3c6020e7a405bdc1ea91a9b1356c14714e21

{:simplehttp, :json, :urlEncode} = require'util'
html2unicode = require 'html'
nixio = require'nixio'
fs = require'nixio.fs'
ltn12 = require'ltn12'

urlbase = '/image/'

safe = (fn) ->
  f, ext = fn\match'^(.*)%.(.-)$'
  f = f\gsub '[^%w%-]', ''
  return f..'.'..ext

ivar2.webserver.regUrl "#{urlbase}(.*)$", (req, res) ->
  send = (body, code, content_type) ->
    if not code then code = 200
    if not content_type then content_type = 'text/html'
    res\set_status code
    res\set_header 'Content-Type', content_type
    res\set_header 'Content-Length', #body
    res\set_body body
    res\send!
    res.on_error = (res, req, err) ->
      ivar2\Log 'error', 'imageupload: error: %s', err

  file = req.url\match '/file/(.*)$'
  if file
    fn = "cache/images/#{safe file}"
    size = nixio.fs.stat(fn).size
    fd = ltn12.source.file(io.open fn, 'rb')
    --body = fd\read '*a'
    --fd\close!
    content_type = 'image/jpeg'
    if file\match '.png'
      content_type = 'image/png'
    if file\match '.svg'
      content_type = 'image/svg'
    if file\match '.mp4'
      content_type = 'video/mp4'
    if file\match '.mp3'
      content_type = 'audio/mp3'

    res\set_status code
    res\set_header 'Content-Type', content_type
    res\set_header 'Content-Length', size
    res\set_body fd
    res\send!
    ivar2\Log 'error', 'imageupload: serving: %s', fn

    return

  channel = req.url\match('channel=(.+)%s*')
  unless channel
    send 'Invalid channel', 404
    return

  channel = html2unicode channel
  unescaped_channel = channel\gsub '%%23', '#'

  html = [[
  <!DOCTYPE html>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="IRCSNAP ]]..unescaped_channel..[[">
  <meta name="theme-color" content="#8f4099">

  <title>IRCSNAP</title>

Share to IRC app - ]]..unescaped_channel..[[ edition

Fill in your nick/name and optional text and then click on one of the yellow buttons to attach image. It will appear instantly on IRC!

    <form>

        <input id="sender" type="text" required>

        Your name

        <input id="text" type="text" required>

        Attach text

    </form>

Uploading. Percent complete: 0 %

      📷 Snap picture! <input type="file" accept="image/*" id="capturei" capture="camera" style="visibility:hidden;">
      📂 Browse gallery! <input type="file" accept="image/*" id="capturef" style="visibility:hidden;">
      🎥 Capture video! <input type="file" accept="video/*" capture="camcorder" id="capturev" style="visibility:hidden;">
      🎥 Upload video! <input type="file" accept="video/*" id="capturevf" style="visibility:hidden;">

        What is this?

Share to IRC is a simple web app for sharing media directly from your device to an IRC channel.

Click Tools -> Add to homescreen to create a convenient shortcut for easy and fast access to sharing

  ]]
  if req.method == 'POST'
    fn = req.headers['X-Filename']
    sender = req.headers['X-Sender'] or ''
    text = req.headers['X-Text'] or ''
    file = req.filename
    ivar2\Log 'info', "imageupload: Recieved file name: <#{fn}> datalen: #{#file}, sender: <#{sender}> text: <#{text}>, channel: <#{unescaped_channel}>"
    if fn and file
      html = 'Ok'
      realfn = "#{os.time!}-#{safe fn}"
      save = ->
        --fd = nixio.open "cache/images/#{realfn}", 'w+', 0400
        --written = fd\write file
        --fd\sync!
        --fd\close!
        -- Move tempfile to real location
        fs.move req.filename, "cache/images/#{realfn}"
        if sender ~= ''
          sender = "<#{sender\sub(1, 100)}> "
        if text ~= ''
          text = text\sub(1, 100) .. ' '
        msg = "[IRCSNAP] #{sender}#{text}#{ivar2.config.webserverprefix}#{urlbase}file/#{realfn}"
        ivar2\Privmsg unescaped_channel, msg
      ok, err = pcall(save)
      unless ok
        ivar2\Log 'error', "imageupload: Error during saving upload: %s", err
    else
      html = 'Not OK'

  send html

-- Attempt to create the cache folder.
nixio.fs.mkdir('cache/images')

PRIVMSG:
  '^%pircsnap$': (source, destination) =>
    channel = urlEncode destination
    say "#{ivar2.config.webserverprefix}#{urlbase}?channel=#{channel}"

{:simplehttp, :json, :urlEncode} = require'util'
html2unicode = require 'html'
nixio = require'nixio'
fs = require'nixio.fs'
ltn12 = require'ltn12'

urlbase = '/image/'

safe = (fn) ->
  f, ext = fn\match'^(.*)%.(.-)$'
  f = f\gsub '[^%w%-]', ''
  return f..'.'..ext

ivar2.webserver.regUrl "#{urlbase}(.*)$", (req, res) ->
  send = (body, code, content_type) ->
    if not code then code = 200
    if not content_type then content_type = 'text/html'
    res\set_status code
    res\set_header 'Content-Type', content_type
    res\set_header 'Content-Length', #body
    res\set_body body
    res\send!
    res.on_error = (res, req, err) ->
      ivar2\Log 'error', 'imageupload: error: %s', err

  file = req.url\match '/file/(.*)$'
  if file
    fn = "cache/images/#{safe file}"
    size = nixio.fs.stat(fn).size
    fd = ltn12.source.file(io.open fn, 'rb')
    --body = fd\read '*a'
    --fd\close!
    content_type = 'image/jpeg'
    if file\match '.png'
      content_type = 'image/png'
    if file\match '.svg'
      content_type = 'image/svg'
    if file\match '.mp4'
      content_type = 'video/mp4'
    if file\match '.mp3'
      content_type = 'audio/mp3'

    res\set_status code
    res\set_header 'Content-Type', content_type
    res\set_header 'Content-Length', size
    res\set_body fd
    res\send!
    ivar2\Log 'error', 'imageupload: serving: %s', fn

    return

  channel = req.url\match('channel=(.+)')
  unless channel
    send 'Invalid channel', 404
    return

  channel = html2unicode channel
  unescaped_channel = channel\gsub '%%23', '#'

  html = [[
  <!DOCTYPE html>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="IRCSNAP ]]..unescaped_channel..[[">
  <meta name="theme-color" content="#8f4099">

  <title>IRCSNAP</title>

Share to IRC app - ]]..unescaped_channel..[[ edition

Fill in your nick/name and optional text and then click on one of the yellow buttons to attach image. It will appear instantly on IRC!

    <form>

        <input id="sender" type="text" required>

        Your name

        <input id="text" type="text" required>

        Attach text

    </form>

Uploading. Percent complete: 0 %

      📷 Snap picture! <input type="file" accept="image/*" id="capturei" capture="camera" style="visibility:hidden;">
      📂 Browse gallery! <input type="file" accept="image/*" id="capturef" style="visibility:hidden;">
      🎥 Capture video! <input type="file" accept="video/*" capture="camcorder" id="capturev" style="visibility:hidden;">
      🎥 Upload video! <input type="file" accept="video/*" id="capturevf" style="visibility:hidden;">

        What is this?

Share to IRC is a simple web app for sharing media directly from your device to an IRC channel.

Click Tools -> Add to homescreen to create a convenient shortcut for easy and fast access to sharing

  ]]
  if req.method == 'POST'
    fn = req.headers['X-Filename']
    sender = req.headers['X-Sender'] or ''
    text = req.headers['X-Text'] or ''
    file = req.filename
    ivar2\Log 'info', "imageupload: Recieved file name: <#{fn}> datalen: #{#file}, sender: <#{sender}> text: <#{text}>, channel: <#{unescaped_channel}>"
    if fn and file
      html = 'Ok'
      realfn = "#{os.time!}-#{safe fn}"
      save = ->
        --fd = nixio.open "cache/images/#{realfn}", 'w+', 0400
        --written = fd\write file
        --fd\sync!
        --fd\close!
        -- Move tempfile to real location
        fs.move req.filename, "cache/images/#{realfn}"
        if sender ~= ''
          sender = "<#{sender\sub(1, 100)}> "
        if text ~= ''
          text = text\sub(1, 100) .. ' '
        msg = "[IRCSNAP] #{sender}#{text}http://irc.lart.no:#{ivar2.config.webserverport}#{urlbase}file/#{realfn}"
        ivar2\Privmsg unescaped_channel, msg
      ok, err = pcall(save)
      unless ok
        ivar2\Log 'error', "imageupload: Error during saving upload: %s", err
    else
      html = 'Not OK'

  send html

-- Attempt to create the cache folder.
nixio.fs.mkdir('cache/images')

PRIVMSG:
  '^%pircsnap$': (source, destination) =>
    channel = urlEncode destination
    say "http://irc.lart.no:#{ivar2.config.webserverport}#{urlbase}?channel=#{channel}"


list: tune output

list: tune output

listModules = (source, destination, arg) =>
  out = {}
  for moduleName, moduleTable in next, @Events!['PRIVMSG']
    unless @IsModuleDisabled moduleName, destination
      table.insert out, moduleName
  if #out > 0
    say "Modules: %s", table.concat(out, ' ')

listPatterns = (source, destination, moduleName) =>
  moduleTable = @Events!['PRIVMSG'][moduleName]
  unless moduleTable
    return

  if @IsModuleDisabled moduleName, destination
    return

  out = {}
  for pattern, callback in next, moduleTable
    patt = @ChannelCommandPattern(pattern, moduleName, destination)
    -- Make them prettier
    patt = patt\gsub('%^%%p', '!')
    patt = patt\gsub('%$$', '')
    table.insert out, patt

  if #out > 0
    say "%s patterns: %s", moduleName, table.concat(out, ', ')

PRIVMSG:
  '^%plist$': listModules
  '^%plist (%w+)$': listPatterns

listModules = (source, destination, arg) =>
  out = {}
  for moduleName, moduleTable in next, @Events!['PRIVMSG']
    unless @IsModuleDisabled moduleName, destination
      table.insert out, moduleName
  if #out > 0
    say "Modules: %s", table.concat(out, ', ')

listPatterns = (source, destination, moduleName) =>
  moduleTable = @Events!['PRIVMSG'][moduleName]
  unless moduleTable
    return

  if @IsModuleDisabled moduleName, destination
    return

  out = {}
  for pattern, callback in next, moduleTable
    patt = @ChannelCommandPattern(pattern, moduleName, destination)
    table.insert out, patt

  if #out > 0
    say "%s patterns: %s", moduleName, table.concat(out, ', ')

PRIVMSG:
  '^%plist$': listModules
  '^%plist (%w+)$': listPatterns


Reorganize, fix argument info.

Reorganize, fix argument info.

rex = require "rex_posix"
cli = require "cliargs"
lfs = require "lfs"

import concat, insert from table

contains = (t, item) ->
	for k, v in pairs t
		return true if item == v
	false

local dir_list, dir_listing
dir_listing = (path, file) ->
	f = path .. '/' .. file
	attr = lfs.attributes(f).mode
	{file, attr, if attr == 'directory' then dir_list(f)}

dir_list = (path) ->
	[dir_listing(path, file) for file in lfs.dir(path) when not contains({'.', '..'}, file)]

print_listing = (filedata) ->
	for k, {file, mode, data} in pairs filedata
		print file, mode
		print_listing(data) if data

cli\set_name("regnamex.lua")
cli\add_argument("DIR", "directory to scan")
cli\optarg("IGNORED", "ignored files/directories")
cli\add_flag("-t, --type", "action to take: cp, mv, symlink, defaults to symlink", "symlink")
cli\add_flag("-v, --version", "prints the program version")
cli\add_flag("-d, --debug", "script will simulate excution, print actions.")

args = cli\parse_args()
return if not args

ignored_files = switch type(args["IGNORED"])
	when "string"
		{}
	else
		args["IGNORED"]

files = dir_list(args["DIR"])
print_listing(files)

rex = require "rex_posix"
cli = require "cliargs"
lfs = require "lfs"

import concat, insert from table

contains = (t, item) ->
	for k, v in pairs t
		return true if item == v
	false

local dir_list, dir_listing
dir_listing = (path, file) ->
	f = path .. '/' .. file
	attr = lfs.attributes(f).mode
	{file, attr, if attr == 'directory' then dir_list(f)}

dir_list = (path) ->
	[dir_listing(path, file) for file in lfs.dir(path) when not contains({'.', '..'}, file)]

cli\set_name("regnamex.lua")
cli\add_argument("DIR", "directory to scan")
cli\optarg("IGNORED", "ignored files/directories")
cli\add_flag("-t, --type", "takes copy, move, symlink, defaults to symlink.")
cli\add_flag("-v, --version", "prints the program version")
cli\add_flag("-d, --debug", "script will simulate excution, print actions.")

args = cli\parse_args()
return if not args

ignored_files = args["IGNORED"]
files = dir_list(args["DIR"])

print_listing = (filedata) ->
	for k, {file, mode, data} in pairs filedata
		print file, mode
		print_listing(data) if data

print_listing(files)


Fixed name.

Fixed name.

rex = require "rex_pcre"
cli = require "cliargs"
lfs = require "lfs"

import insert from table

contains = (t, item) ->
	return true if item == nil
	for k, v in pairs t
		return true if item == v
	false

file_ext = (str) ->
	return "." .. str\match(".([^%.]+)$")

local dir_list, dir_listing
dir_listing = (path, file) ->
	f = path .. '/' .. file
	attr = lfs.attributes(f).mode
	{file, attr, if attr == 'directory' then dir_list(f)}

dir_list = (path) ->
	[dir_listing(path, file) for file in lfs.dir(path) when not contains({'.', '..'}, file)]

map_listing = (structure, func) ->
	for k, v in pairs structure
		func(v, map_listing)

print_listing = (value, func) ->
	{file, mode, data} = value
	print file, mode
	func(data, print_listing) if data

dbg = (b) -> return "[DBG:" .. ((b and "Y") or "N") .. "]"

perform_action = (old, new, action = "hardlink") ->
	switch action
		when "copy"
			print "[cp]: Not yet implemented."
		when "move"
			print "[mv]: Not yet implemented."
		when "symlink", "hardlink"
			print dbg(debug), "[link]:", old, "to:", new
			lfs.link(old, new, action == "hardlink" or false) if not debug
		else
			print "Unknown action:", action

process_files = (filedata, regex, parent) ->
	for k, {file, mode, data} in pairs filedata
		if rex.gsub(file, regex, '')
			if mode == 'directory'
				f = rex.gsub(file, regex, '')\match("^%s*(.-)%s*$")
				f = parent.out .. '/' .. f
				lfs.mkdir(f)
				process_files(data, regex, {in: parent.in .. '/' .. file, out: f})
			else
				ext = file_ext(file)
				newer_f = rex.gsub(file, regex, '')
				new_f = rex.match(newer_f, [[(.*)(v\d)]]) or newer_f
				new_f = new_f\gsub('[ ]', '')
				new_f = rex.gsub(new_f, [[([^\d]*)(\d+)[^\d]*$]], [[%1_ep%2]], 1)
				old_f, new_f = parent.in .. '/' .. file, parent.out .. '/' .. new_f .. ext
				perform_action(old_f, new_f, args["a"] or nil)

		else
			print "ERR: ", file, mode

cli\set_name("regexulon.lua")
cli\add_argument("DIR", "directory to scan")
cli\add_argument("TARGET", "target directory")
cli\optarg("IGNORED", "ignored files/directories")
cli\add_flag("-a, --action", "action to take: cp, mv, symlink, hardlink, defaults to hardlink", "hardlink")
cli\add_flag("-v, --version", "prints the program version")
cli\add_flag("-d, --debug", "script will simulate excution, print actions.")
export args = cli\parse_args()
return if not args

ignored_files = switch type(args["IGNORED"])
	when "string"
		{}
	else
		args["IGNORED"]

inputdir = args["DIR"]
targetdir = args["TARGET"]
export debug = args["d"]

files = dir_list(args["DIR"])
regex = [==[\(.*?\)|\[.*?\]]==]

map_listing(files, print_listing)
processed = process_files(files, regex, {in: inputdir, out: targetdir})

rex = require "rex_pcre"
cli = require "cliargs"
lfs = require "lfs"

import insert from table

contains = (t, item) ->
	return true if item == nil
	for k, v in pairs t
		return true if item == v
	false

file_ext = (str) ->
	return "." .. str\match(".([^%.]+)$")

local dir_list, dir_listing
dir_listing = (path, file) ->
	f = path .. '/' .. file
	attr = lfs.attributes(f).mode
	{file, attr, if attr == 'directory' then dir_list(f)}

dir_list = (path) ->
	[dir_listing(path, file) for file in lfs.dir(path) when not contains({'.', '..'}, file)]

map_listing = (structure, func) ->
	for k, v in pairs structure
		func(v, map_listing)

print_listing = (value, func) ->
	{file, mode, data} = value
	print file, mode
	func(data, print_listing) if data

dbg = (b) -> return "[DBG:" .. ((b and "Y") or "N") .. "]"

perform_action = (old, new, action = "hardlink") ->
	switch action
		when "copy"
			print "[cp]: Not yet implemented."
		when "move"
			print "[mv]: Not yet implemented."
		when "symlink", "hardlink"
			print dbg(debug), "[link]:", old, "to:", new
			lfs.link(old, new, action == "hardlink" or false) if not debug
		else
			print "Unknown action:", action

process_files = (filedata, regex, parent) ->
	for k, {file, mode, data} in pairs filedata
		if rex.gsub(file, regex, '')
			if mode == 'directory'
				f = rex.gsub(file, regex, '')\match("^%s*(.-)%s*$")
				f = parent.out .. '/' .. f
				lfs.mkdir(f)
				process_files(data, regex, {in: parent.in .. '/' .. file, out: f})
			else
				ext = file_ext(file)
				newer_f = rex.gsub(file, regex, '')
				new_f = rex.match(newer_f, [[(.*)(v\d)]]) or newer_f
				new_f = new_f\gsub('[ ]', '')
				new_f = rex.gsub(new_f, [[([^\d]*)(\d+)[^\d]*$]], [[%1_ep%2]], 1)
				old_f, new_f = parent.in .. '/' .. file, parent.out .. '/' .. new_f .. ext
				perform_action(old_f, new_f, args["a"] or nil)

		else
			print "ERR: ", file, mode

cli\set_name("regnamex.lua")
cli\add_argument("DIR", "directory to scan")
cli\add_argument("TARGET", "target directory")
cli\optarg("IGNORED", "ignored files/directories")
cli\add_flag("-a, --action", "action to take: cp, mv, symlink, hardlink, defaults to hardlink", "hardlink")
cli\add_flag("-v, --version", "prints the program version")
cli\add_flag("-d, --debug", "script will simulate excution, print actions.")
export args = cli\parse_args()
return if not args

ignored_files = switch type(args["IGNORED"])
	when "string"
		{}
	else
		args["IGNORED"]

inputdir = args["DIR"]
targetdir = args["TARGET"]
export debug = args["d"]

files = dir_list(args["DIR"])
regex = [==[\(.*?\)|\[.*?\]]==]

map_listing(files, print_listing)
processed = process_files(files, regex, {in: inputdir, out: targetdir})


cubsensors: fix imports

cubsensors: fix imports

Former-commit-id: ffb70c5b3edefb6e2ca166617590b0e471885836
Former-commit-id: 8e128d14db8fd488aaf1c48bab6ea6cd646e2f1d

{:json, :simplehttp, :bold} = require'util'

round = (x) ->
  x + 0.5 - (x + 0.5) % 1

PRIVMSG:
  '^%pinnev.+r$': (source, destination, input) =>
    simplehttp ivar2.config.cubeapiurl, (data) ->
      result = json.decode data
      if result
        r = result[1]
        temp = r.rtemp
        noise = tonumber(r.noisedba) or 0
        say '\002%.1f\002 °C, \002%s\002 ppm, \002%s\002 %, \002%s\002 lux, \002%s\002 dBA', temp, round(r.voc), round(r.humidity), round(r.light), noise

round = (x) ->
  x + 0.5 - (x + 0.5) % 1

PRIVMSG:
  '^%pinnev.+r$': (source, destination, input) =>
    ivar2.util.simplehttp ivar2.config.cubeapiurl, (data) ->
      result = json.decode data
      if result
        r = result[1]
        temp = r.rtemp
        noise = tonumber(r.noisedba) or 0
        say '\002%.1f\002 °C, \002%s\002 luftkvalitet, \002%s\002 fukt, \002%s\002 lys, \002%s\002 dBA støynivå', temp, round(r.voc), round(r.humidity), round(r.light), noise


allow route with pattern like "PUT /motor/%a+/%d+"

class Router
  routes: {}

  new: (ctrls = {}) =>
    table.foreach ctrls, (k, v) ->
      ctls = require "controller"
      @routes[k] = ctls[v.controller][v.action]

  process: (req, res, next) =>
    for pattern, func in pairs @routes
      if "#{req.method} #{req.url}"\find(pattern) != nil
        return func req, res
    if next != nil
      next()

  METHOD: (method, path, mw) =>
    table.insert @routes, "#{method} #{path}": mw

  use: (path, mw) =>
    @METHOD 'GET', path, mw

  get: (path, mw) =>
    @METHOD 'GET', path, mw

return Router

class Router
  routes: {}

  new: (ctrls = {}) =>
    table.foreach ctrls, (k, v) ->
      ctls = require "controller"
      @routes[k] = ctls[v.controller][v.action]

  process: (req, res, next) =>
    if @routes["#{req.method} #{req.url}"] != nil
      @routes["#{req.method} #{req.url}"](req, res)
    else
      if next != nil
        next()

  METHOD: (method, path, mw) =>
    table.insert @routes, "#{method} #{path}": mw

  use: (path, mw) =>
    @METHOD 'GET', path, mw

  get: (path, mw) =>
    @METHOD 'GET', path, mw

return Router


Fix the mistake

Fix the mistake